Skip to content

Commit 630c935

Browse files
committed
Add LittleFS build support, move filesystem size parser code here
1 parent 3560177 commit 630c935

File tree

3 files changed

+133
-4
lines changed

3 files changed

+133
-4
lines changed

builder/main.py

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from platform import system
1717
from os import makedirs
1818
from os.path import isdir, join
19+
import re
1920

2021
from platformio.util import get_serial_ports
2122

@@ -67,6 +68,9 @@ def generate_uf2(target, source, env):
6768

6869
ARFLAGS=["rc"],
6970

71+
MKFSTOOL="mklittlefs",
72+
PICO_FS_IMAGE_NAME=env.get("PICO_FS_IMAGE_NAME", "littlefs"),
73+
7074
SIZEPROGREGEXP=r"^(?:\.text|\.data|\.rodata|\.text.align|\.ARM.exidx)\s+(\d+).*",
7175
SIZEDATAREGEXP=r"^(?:\.data|\.bss|\.noinit)\s+(\d+).*",
7276
SIZECHECKCMD="$SIZETOOL -A -d $SOURCES",
@@ -109,6 +113,100 @@ def generate_uf2(target, source, env):
109113
if not env.get("PIOFRAMEWORK"):
110114
env.SConscript("frameworks/_bare.py")
111115

116+
117+
def convert_size_expression_to_int(expression):
118+
conversion_factors = {
119+
"M": 1024*1024,
120+
"MB": 1024*1024,
121+
"K": 1024,
122+
"KB": 1024,
123+
"B": 1,
124+
"": 1 # giving no conversion factor is factor 1.
125+
}
126+
# match <floating pointer number><conversion factor>.
127+
extract_regex = r'^((?:[0-9]*[.])?[0-9]+)([mkbMKB]*)$'
128+
res = re.findall(extract_regex, expression)
129+
# unparsable expression? Warning.
130+
if len(res) == 0:
131+
sys.stderr.write(
132+
"Error: Could not parse filesystem size expression '%s'."
133+
" Will treat as size = 0.\n" % str(expression))
134+
return 0
135+
# access first result
136+
number, factor = res[0]
137+
number = float(number)
138+
number *= conversion_factors[factor.upper()]
139+
return int(number)
140+
141+
def fetch_fs_size(env):
142+
# follow generation formulas from makeboards.py for Earle Philhower core
143+
# given the total flash size, a user can specify
144+
# the amount for the filesystem (0MB, 2MB, 4MB, 8MB, 16MB)
145+
# via board_build.filesystem_size,
146+
# and we will calculate the flash size and eeprom size from that.
147+
flash_size = board.get("upload.maximum_size")
148+
filesystem_size = board.get("build.filesystem_size", "0MB")
149+
filesystem_size_int = convert_size_expression_to_int(filesystem_size)
150+
151+
maximum_size = flash_size - 4096 - filesystem_size_int
152+
153+
print("Flash size: %.2fMB" % (flash_size / 1024.0 / 1024.0))
154+
print("Sketch size: %.2fMB" % (maximum_size / 1024.0 / 1024.0))
155+
print("Filesystem size: %.2fMB" % (filesystem_size_int / 1024.0 / 1024.0))
156+
157+
flash_length = maximum_size
158+
eeprom_start = 0x10000000 + flash_size - 4096
159+
fs_start = 0x10000000 + flash_size - 4096 - filesystem_size_int
160+
fs_end = 0x10000000 + flash_size - 4096
161+
162+
if maximum_size <= 0:
163+
sys.stderr.write(
164+
"Error: Filesystem too large for given flash. "
165+
"Can at max be flash size - 4096 bytes. "
166+
"Available sketch size with current "
167+
"config would be %d bytes.\n" % maximum_size)
168+
sys.stderr.flush()
169+
env.Exit(-1)
170+
171+
env["PICO_FLASH_LENGTH"] = flash_length
172+
env["PICO_EEPROM_START"] = eeprom_start
173+
env["FS_START"] = fs_start
174+
env["FS_END"] = fs_end
175+
# LittleFS configuration paramters taken from
176+
# https://github.com/earlephilhower/arduino-pico-littlefs-plugin/blob/master/src/PicoLittleFS.java
177+
env["FS_PAGE"] = 256
178+
env["FS_BLOCK"] = 4096
179+
180+
print("Maximium size: %d Flash Length: %d "
181+
"EEPROM Start: %d Filesystem start %d "
182+
"Filesystem end %s" %
183+
(maximum_size,flash_length, eeprom_start, fs_start, fs_end))
184+
185+
186+
def __fetch_fs_size(target, source, env):
187+
fetch_fs_size(env)
188+
return (target, source)
189+
190+
env.Append(
191+
BUILDERS=dict(
192+
DataToBin=Builder(
193+
action=env.VerboseAction(" ".join([
194+
'"$MKFSTOOL"',
195+
"-c", "$SOURCES",
196+
"-p", "$FS_PAGE",
197+
"-b", "$FS_BLOCK",
198+
"-s", "${FS_END - FS_START}",
199+
"$TARGET"
200+
]), "Building file system image from '$SOURCES' directory to $TARGET"),
201+
emitter=__fetch_fs_size,
202+
source_factory=env.Dir,
203+
suffix=".bin"
204+
)
205+
)
206+
)
207+
208+
env["fetch_fs_size"] = fetch_fs_size
209+
112210
#
113211
# Target: Build executable and linkable firmware
114212
#
@@ -119,16 +217,37 @@ def generate_uf2(target, source, env):
119217
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
120218
else:
121219
target_elf = env.BuildProgram()
122-
target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
123-
env.Depends(target_firm, "checkprogsize")
124-
220+
if set(["buildfs", "uploadfs"]) & set(COMMAND_LINE_TARGETS):
221+
target_firm = env.DataToBin(
222+
join("$BUILD_DIR", "${PICO_FS_IMAGE_NAME}"), "$PROJECTDATA_DIR")
223+
AlwaysBuild(target_firm)
224+
else:
225+
target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
226+
env.Depends(target_firm, "checkprogsize")
227+
228+
env.AddPlatformTarget("buildfs", target_firm, target_firm, "Build Filesystem Image")
125229
AlwaysBuild(env.Alias("nobuild", target_firm))
126230
target_buildprog = env.Alias("buildprog", target_firm, target_firm)
127231

128232
env.AddPostAction(
129233
target_elf, env.VerboseAction(generate_uf2, "Generating UF2 image")
130234
)
131235

236+
def _update_max_upload_size(env):
237+
fetch_fs_size(env)
238+
env.BoardConfig().update("upload.maximum_size", env["PICO_FLASH_LENGTH"])
239+
240+
# update max upload size based on CSV file
241+
if env.get("PIOMAINPROG"):
242+
env.AddPreAction(
243+
"checkprogsize",
244+
env.VerboseAction(
245+
lambda source, target, env: _update_max_upload_size(env),
246+
"Retrieving maximum program size $SOURCE"))
247+
# remove after PIO Core 3.6 release
248+
elif set(["checkprogsize", "upload"]) & set(COMMAND_LINE_TARGETS):
249+
_update_max_upload_size(env)
250+
132251
#
133252
# Target: Print binary size
134253
#
@@ -232,7 +351,7 @@ def _jlink_cmd_script(env, source):
232351
sys.stderr.write("Warning! Unknown upload protocol %s\n" % upload_protocol)
233352

234353
AlwaysBuild(env.Alias("upload", upload_source, upload_actions))
235-
354+
env.AddPlatformTarget("uploadfs", target_firm, upload_actions, "Upload Filesystem Image")
236355
#
237356
# Default targets
238357
#

platform.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
"optional": true,
6666
"owner": "platformio",
6767
"version": "~1.72000.0"
68+
},
69+
"tool-mklittlefs": {
70+
"type": "uploader",
71+
"optional": true,
72+
"owner": "platformio",
73+
"version": "~1.203.0"
6874
}
6975
}
7076
}

platform.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ def configure_default_packages(self, variables, targets):
4646
sys.stderr.write(
4747
"Error! Unknown build.core value '%s'. Don't know which Arduino core package to use." % build_core)
4848

49+
# if we want to build a filesystem, we need the tools.
50+
if "buildfs" in targets:
51+
self.packages['tool-mklittlefs']['optional'] = False
52+
4953
# configure J-LINK tool
5054
jlink_conds = [
5155
"jlink" in variables.get(option, "")

0 commit comments

Comments
 (0)