diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | message.mk | 2 | ||||
| -rw-r--r-- | quantum/mcu_selection.mk | 24 | ||||
| -rw-r--r-- | tmk_core/rules.mk | 31 | ||||
| -rwxr-xr-x | util/uf2conv.py | 319 |
6 files changed, 373 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore index d6846cf63..518b2df83 100644 --- a/.gitignore +++ b/.gitignore | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | *.eep | 5 | *.eep |
| 6 | *.elf | 6 | *.elf |
| 7 | *.hex | 7 | *.hex |
| 8 | *.uf2 | ||
| 8 | *.qmk | 9 | *.qmk |
| 9 | !util/bootloader.hex | 10 | !util/bootloader.hex |
| 10 | !quantum/tools/eeprom_reset.hex | 11 | !quantum/tools/eeprom_reset.hex |
| @@ -93,8 +93,8 @@ clean: | |||
| 93 | 93 | ||
| 94 | .PHONY: distclean | 94 | .PHONY: distclean |
| 95 | distclean: clean | 95 | distclean: clean |
| 96 | echo -n 'Deleting *.bin and *.hex ... ' | 96 | echo -n 'Deleting *.bin, *.hex, and *.uf2 ... ' |
| 97 | rm -f *.bin *.hex | 97 | rm -f *.bin *.hex *.uf2 |
| 98 | echo 'done.' | 98 | echo 'done.' |
| 99 | 99 | ||
| 100 | #Compatibility with the old make variables, anything you specify directly on the command line | 100 | #Compatibility with the old make variables, anything you specify directly on the command line |
diff --git a/message.mk b/message.mk index 3240c041b..c3f391919 100644 --- a/message.mk +++ b/message.mk | |||
| @@ -47,10 +47,12 @@ MSG_SIZE_AFTER = Size after: | |||
| 47 | MSG_COFF = Converting to AVR COFF: | 47 | MSG_COFF = Converting to AVR COFF: |
| 48 | MSG_EXTENDED_COFF = Converting to AVR Extended COFF: | 48 | MSG_EXTENDED_COFF = Converting to AVR Extended COFF: |
| 49 | MSG_FLASH = Creating load file for flashing: | 49 | MSG_FLASH = Creating load file for flashing: |
| 50 | MSG_UF2 = Creating UF2 file for deployment: | ||
| 50 | MSG_EEPROM = Creating load file for EEPROM: | 51 | MSG_EEPROM = Creating load file for EEPROM: |
| 51 | MSG_BIN = Creating binary load file for flashing: | 52 | MSG_BIN = Creating binary load file for flashing: |
| 52 | MSG_EXTENDED_LISTING = Creating Extended Listing: | 53 | MSG_EXTENDED_LISTING = Creating Extended Listing: |
| 53 | MSG_SYMBOL_TABLE = Creating Symbol Table: | 54 | MSG_SYMBOL_TABLE = Creating Symbol Table: |
| 55 | MSG_EXECUTING = Executing: | ||
| 54 | MSG_LINKING = Linking: | 56 | MSG_LINKING = Linking: |
| 55 | MSG_COMPILING = Compiling: | 57 | MSG_COMPILING = Compiling: |
| 56 | MSG_COMPILING_CXX = Compiling: | 58 | MSG_COMPILING_CXX = Compiling: |
diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk index 53e03a805..2e4d25008 100644 --- a/quantum/mcu_selection.mk +++ b/quantum/mcu_selection.mk | |||
| @@ -139,6 +139,9 @@ ifneq ($(findstring STM32F042, $(MCU)),) | |||
| 139 | # Options to pass to dfu-util when flashing | 139 | # Options to pass to dfu-util when flashing |
| 140 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 140 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 141 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 141 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 142 | |||
| 143 | # UF2 settings | ||
| 144 | UF2_FAMILY ?= STM32F0 | ||
| 142 | endif | 145 | endif |
| 143 | 146 | ||
| 144 | ifneq ($(findstring STM32F072, $(MCU)),) | 147 | ifneq ($(findstring STM32F072, $(MCU)),) |
| @@ -172,6 +175,9 @@ ifneq ($(findstring STM32F072, $(MCU)),) | |||
| 172 | # Options to pass to dfu-util when flashing | 175 | # Options to pass to dfu-util when flashing |
| 173 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 176 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 174 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 177 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 178 | |||
| 179 | # UF2 settings | ||
| 180 | UF2_FAMILY ?= STM32F0 | ||
| 175 | endif | 181 | endif |
| 176 | 182 | ||
| 177 | ifneq ($(findstring STM32F103, $(MCU)),) | 183 | ifneq ($(findstring STM32F103, $(MCU)),) |
| @@ -205,6 +211,9 @@ ifneq ($(findstring STM32F103, $(MCU)),) | |||
| 205 | # Options to pass to dfu-util when flashing | 211 | # Options to pass to dfu-util when flashing |
| 206 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 212 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 207 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 213 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 214 | |||
| 215 | # UF2 settings | ||
| 216 | UF2_FAMILY ?= STM32F1 | ||
| 208 | endif | 217 | endif |
| 209 | 218 | ||
| 210 | ifneq ($(findstring STM32F303, $(MCU)),) | 219 | ifneq ($(findstring STM32F303, $(MCU)),) |
| @@ -238,6 +247,9 @@ ifneq ($(findstring STM32F303, $(MCU)),) | |||
| 238 | # Options to pass to dfu-util when flashing | 247 | # Options to pass to dfu-util when flashing |
| 239 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 248 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 240 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 249 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 250 | |||
| 251 | # UF2 settings | ||
| 252 | UF2_FAMILY ?= STM32F3 | ||
| 241 | endif | 253 | endif |
| 242 | 254 | ||
| 243 | ifneq ($(findstring STM32F401, $(MCU)),) | 255 | ifneq ($(findstring STM32F401, $(MCU)),) |
| @@ -271,6 +283,9 @@ ifneq ($(findstring STM32F401, $(MCU)),) | |||
| 271 | # Options to pass to dfu-util when flashing | 283 | # Options to pass to dfu-util when flashing |
| 272 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 284 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 273 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 285 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 286 | |||
| 287 | # UF2 settings | ||
| 288 | UF2_FAMILY ?= STM32F4 | ||
| 274 | endif | 289 | endif |
| 275 | 290 | ||
| 276 | ifneq ($(findstring STM32F411, $(MCU)),) | 291 | ifneq ($(findstring STM32F411, $(MCU)),) |
| @@ -304,6 +319,9 @@ ifneq ($(findstring STM32F411, $(MCU)),) | |||
| 304 | # Options to pass to dfu-util when flashing | 319 | # Options to pass to dfu-util when flashing |
| 305 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 320 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 306 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 321 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 322 | |||
| 323 | # UF2 settings | ||
| 324 | UF2_FAMILY ?= STM32F4 | ||
| 307 | endif | 325 | endif |
| 308 | 326 | ||
| 309 | ifneq ($(findstring STM32G431, $(MCU)),) | 327 | ifneq ($(findstring STM32G431, $(MCU)),) |
| @@ -337,6 +355,9 @@ ifneq ($(findstring STM32G431, $(MCU)),) | |||
| 337 | # Options to pass to dfu-util when flashing | 355 | # Options to pass to dfu-util when flashing |
| 338 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 356 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 339 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 357 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 358 | |||
| 359 | # UF2 settings | ||
| 360 | UF2_FAMILY ?= STM32G4 | ||
| 340 | endif | 361 | endif |
| 341 | 362 | ||
| 342 | ifneq ($(findstring STM32G474, $(MCU)),) | 363 | ifneq ($(findstring STM32G474, $(MCU)),) |
| @@ -370,6 +391,9 @@ ifneq ($(findstring STM32G474, $(MCU)),) | |||
| 370 | # Options to pass to dfu-util when flashing | 391 | # Options to pass to dfu-util when flashing |
| 371 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | 392 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave |
| 372 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 393 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 394 | |||
| 395 | # UF2 settings | ||
| 396 | UF2_FAMILY ?= STM32G4 | ||
| 373 | endif | 397 | endif |
| 374 | 398 | ||
| 375 | ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287)) | 399 | ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287)) |
diff --git a/tmk_core/rules.mk b/tmk_core/rules.mk index bbcfc1e4d..8dbed35fb 100644 --- a/tmk_core/rules.mk +++ b/tmk_core/rules.mk | |||
| @@ -223,6 +223,12 @@ ifneq ($(filter Darwin FreeBSD,$(shell uname -s)),) | |||
| 223 | MD5SUM = md5 | 223 | MD5SUM = md5 |
| 224 | endif | 224 | endif |
| 225 | 225 | ||
| 226 | # UF2 format settings | ||
| 227 | # To produce a UF2 file in your build, add to your keyboard's rules.mk: | ||
| 228 | # FIRMWARE_FORMAT = uf2 | ||
| 229 | UF2CONV = $(TOP_DIR)/util/uf2conv.py | ||
| 230 | UF2_FAMILY ?= 0x0 | ||
| 231 | |||
| 226 | # Compiler flags to generate dependency files. | 232 | # Compiler flags to generate dependency files. |
| 227 | #GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d | 233 | #GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d |
| 228 | GENDEPFLAGS = -MMD -MP -MF $(patsubst %.o,%.td,$@) | 234 | GENDEPFLAGS = -MMD -MP -MF $(patsubst %.o,%.td,$@) |
| @@ -255,6 +261,7 @@ DFU_SUFFIX_ARGS ?= | |||
| 255 | 261 | ||
| 256 | elf: $(BUILD_DIR)/$(TARGET).elf | 262 | elf: $(BUILD_DIR)/$(TARGET).elf |
| 257 | hex: $(BUILD_DIR)/$(TARGET).hex | 263 | hex: $(BUILD_DIR)/$(TARGET).hex |
| 264 | uf2: $(BUILD_DIR)/$(TARGET).uf2 | ||
| 258 | cpfirmware: $(FIRMWARE_FORMAT) | 265 | cpfirmware: $(FIRMWARE_FORMAT) |
| 259 | $(SILENT) || printf "Copying $(TARGET).$(FIRMWARE_FORMAT) to qmk_firmware folder" | $(AWK_CMD) | 266 | $(SILENT) || printf "Copying $(TARGET).$(FIRMWARE_FORMAT) to qmk_firmware folder" | $(AWK_CMD) |
| 260 | $(COPY) $(BUILD_DIR)/$(TARGET).$(FIRMWARE_FORMAT) $(TARGET).$(FIRMWARE_FORMAT) && $(PRINT_OK) | 267 | $(COPY) $(BUILD_DIR)/$(TARGET).$(FIRMWARE_FORMAT) $(TARGET).$(FIRMWARE_FORMAT) && $(PRINT_OK) |
| @@ -283,32 +290,44 @@ gccversion : | |||
| 283 | 290 | ||
| 284 | # Create final output files (.hex, .eep) from ELF output file. | 291 | # Create final output files (.hex, .eep) from ELF output file. |
| 285 | %.hex: %.elf | 292 | %.hex: %.elf |
| 286 | @$(SILENT) || printf "$(MSG_FLASH) $@" | $(AWK_CMD) | ||
| 287 | $(eval CMD=$(HEX) $< $@) | 293 | $(eval CMD=$(HEX) $< $@) |
| 294 | #@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n" | ||
| 295 | @$(SILENT) || printf "$(MSG_FLASH) $@" | $(AWK_CMD) | ||
| 296 | @$(BUILD_CMD) | ||
| 297 | |||
| 298 | %.uf2: %.hex | ||
| 299 | $(eval CMD=$(UF2CONV) $(BUILD_DIR)/$(TARGET).hex -o $(BUILD_DIR)/$(TARGET).uf2 -c -f $(UF2_FAMILY) >/dev/null 2>&1) | ||
| 300 | #@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n" | ||
| 301 | @$(SILENT) || printf "$(MSG_UF2) $@" | $(AWK_CMD) | ||
| 288 | @$(BUILD_CMD) | 302 | @$(BUILD_CMD) |
| 289 | 303 | ||
| 290 | %.eep: %.elf | 304 | %.eep: %.elf |
| 291 | @$(SILENT) || printf "$(MSG_EEPROM) $@" | $(AWK_CMD) | ||
| 292 | $(eval CMD=$(EEP) $< $@ || exit 0) | 305 | $(eval CMD=$(EEP) $< $@ || exit 0) |
| 306 | #@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n" | ||
| 307 | @$(SILENT) || printf "$(MSG_EEPROM) $@" | $(AWK_CMD) | ||
| 293 | @$(BUILD_CMD) | 308 | @$(BUILD_CMD) |
| 294 | 309 | ||
| 295 | # Create extended listing file from ELF output file. | 310 | # Create extended listing file from ELF output file. |
| 296 | %.lss: %.elf | 311 | %.lss: %.elf |
| 297 | @$(SILENT) || printf "$(MSG_EXTENDED_LISTING) $@" | $(AWK_CMD) | ||
| 298 | $(eval CMD=$(OBJDUMP) -h -S -z $< > $@) | 312 | $(eval CMD=$(OBJDUMP) -h -S -z $< > $@) |
| 313 | #@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n" | ||
| 314 | @$(SILENT) || printf "$(MSG_EXTENDED_LISTING) $@" | $(AWK_CMD) | ||
| 299 | @$(BUILD_CMD) | 315 | @$(BUILD_CMD) |
| 300 | 316 | ||
| 301 | # Create a symbol table from ELF output file. | 317 | # Create a symbol table from ELF output file. |
| 302 | %.sym: %.elf | 318 | %.sym: %.elf |
| 303 | @$(SILENT) || printf "$(MSG_SYMBOL_TABLE) $@" | $(AWK_CMD) | ||
| 304 | $(eval CMD=$(NM) -n $< > $@ ) | 319 | $(eval CMD=$(NM) -n $< > $@ ) |
| 320 | #@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n" | ||
| 321 | @$(SILENT) || printf "$(MSG_SYMBOL_TABLE) $@" | $(AWK_CMD) | ||
| 305 | @$(BUILD_CMD) | 322 | @$(BUILD_CMD) |
| 306 | 323 | ||
| 307 | %.bin: %.elf | 324 | %.bin: %.elf |
| 308 | @$(SILENT) || printf "$(MSG_BIN) $@" | $(AWK_CMD) | ||
| 309 | $(eval CMD=$(BIN) $< $@ || exit 0) | 325 | $(eval CMD=$(BIN) $< $@ || exit 0) |
| 326 | #@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n" | ||
| 327 | @$(SILENT) || printf "$(MSG_BIN) $@" | $(AWK_CMD) | ||
| 310 | @$(BUILD_CMD) | 328 | @$(BUILD_CMD) |
| 311 | if [ ! -z "$(DFU_SUFFIX_ARGS)" ]; then \ | 329 | if [ ! -z "$(DFU_SUFFIX_ARGS)" ]; then \ |
| 330 | #$(SILENT) || printf "$(MSG_EXECUTING) '$(DFU_SUFFIX) $(DFU_SUFFIX_ARGS) -a $(BUILD_DIR)/$(TARGET).bin 1>/dev/null':\n" ;\ | ||
| 312 | $(DFU_SUFFIX) $(DFU_SUFFIX_ARGS) -a $(BUILD_DIR)/$(TARGET).bin 1>/dev/null ;\ | 331 | $(DFU_SUFFIX) $(DFU_SUFFIX_ARGS) -a $(BUILD_DIR)/$(TARGET).bin 1>/dev/null ;\ |
| 313 | fi | 332 | fi |
| 314 | $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin; | 333 | $(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin; |
| @@ -476,7 +495,7 @@ $(eval $(foreach OUTPUT,$(OUTPUTS),$(shell mkdir -p $(OUTPUT) 2>/dev/null))) | |||
| 476 | 495 | ||
| 477 | # Listing of phony targets. | 496 | # Listing of phony targets. |
| 478 | .PHONY : all dump_vars finish sizebefore sizeafter qmkversion \ | 497 | .PHONY : all dump_vars finish sizebefore sizeafter qmkversion \ |
| 479 | gccversion build elf hex eep lss sym coff extcoff \ | 498 | gccversion build elf hex uf2 eep lss sym coff extcoff \ |
| 480 | clean clean_list debug gdb-config show_path \ | 499 | clean clean_list debug gdb-config show_path \ |
| 481 | program teensy dfu dfu-ee dfu-start \ | 500 | program teensy dfu dfu-ee dfu-start \ |
| 482 | flash dfu-split-left dfu-split-right \ | 501 | flash dfu-split-left dfu-split-right \ |
diff --git a/util/uf2conv.py b/util/uf2conv.py new file mode 100755 index 000000000..044a7f231 --- /dev/null +++ b/util/uf2conv.py | |||
| @@ -0,0 +1,319 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | import sys | ||
| 3 | import struct | ||
| 4 | import subprocess | ||
| 5 | import re | ||
| 6 | import os | ||
| 7 | import os.path | ||
| 8 | import argparse | ||
| 9 | |||
| 10 | |||
| 11 | UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" | ||
| 12 | UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected | ||
| 13 | UF2_MAGIC_END = 0x0AB16F30 # Ditto | ||
| 14 | |||
| 15 | families = { | ||
| 16 | 'SAMD21': 0x68ed2b88, | ||
| 17 | 'SAML21': 0x1851780a, | ||
| 18 | 'SAMD51': 0x55114460, | ||
| 19 | 'NRF52': 0x1b57745f, | ||
| 20 | 'STM32F0': 0x647824b6, | ||
| 21 | 'STM32F1': 0x5ee21072, | ||
| 22 | 'STM32F2': 0x5d1a0a2e, | ||
| 23 | 'STM32F3': 0x6b846188, | ||
| 24 | 'STM32F4': 0x57755a57, | ||
| 25 | 'STM32F7': 0x53b80f00, | ||
| 26 | 'STM32G0': 0x300f5633, | ||
| 27 | 'STM32G4': 0x4c71240a, | ||
| 28 | 'STM32H7': 0x6db66082, | ||
| 29 | 'STM32L0': 0x202e3a91, | ||
| 30 | 'STM32L1': 0x1e1f432d, | ||
| 31 | 'STM32L4': 0x00ff6919, | ||
| 32 | 'STM32L5': 0x04240bdf, | ||
| 33 | 'STM32WB': 0x70d16653, | ||
| 34 | 'STM32WL': 0x21460ff0, | ||
| 35 | 'ATMEGA32': 0x16573617, | ||
| 36 | 'MIMXRT10XX': 0x4FB2D5BD, | ||
| 37 | 'LPC55': 0x2abc77ec, | ||
| 38 | 'GD32F350': 0x31D228C6, | ||
| 39 | 'ESP32S2': 0xbfdd4eee, | ||
| 40 | 'RP2040': 0xe48bff56 | ||
| 41 | } | ||
| 42 | |||
| 43 | INFO_FILE = "/INFO_UF2.TXT" | ||
| 44 | |||
| 45 | appstartaddr = 0x2000 | ||
| 46 | familyid = 0x0 | ||
| 47 | |||
| 48 | |||
| 49 | def is_uf2(buf): | ||
| 50 | w = struct.unpack("<II", buf[0:8]) | ||
| 51 | return w[0] == UF2_MAGIC_START0 and w[1] == UF2_MAGIC_START1 | ||
| 52 | |||
| 53 | def is_hex(buf): | ||
| 54 | try: | ||
| 55 | w = buf[0:30].decode("utf-8") | ||
| 56 | except UnicodeDecodeError: | ||
| 57 | return False | ||
| 58 | if w[0] == ':' and re.match(b"^[:0-9a-fA-F\r\n]+$", buf): | ||
| 59 | return True | ||
| 60 | return False | ||
| 61 | |||
| 62 | def convert_from_uf2(buf): | ||
| 63 | global appstartaddr | ||
| 64 | numblocks = len(buf) // 512 | ||
| 65 | curraddr = None | ||
| 66 | outp = [] | ||
| 67 | for blockno in range(numblocks): | ||
| 68 | ptr = blockno * 512 | ||
| 69 | block = buf[ptr:ptr + 512] | ||
| 70 | hd = struct.unpack(b"<IIIIIIII", block[0:32]) | ||
| 71 | if hd[0] != UF2_MAGIC_START0 or hd[1] != UF2_MAGIC_START1: | ||
| 72 | print("Skipping block at " + ptr + "; bad magic") | ||
| 73 | continue | ||
| 74 | if hd[2] & 1: | ||
| 75 | # NO-flash flag set; skip block | ||
| 76 | continue | ||
| 77 | datalen = hd[4] | ||
| 78 | if datalen > 476: | ||
| 79 | assert False, "Invalid UF2 data size at " + ptr | ||
| 80 | newaddr = hd[3] | ||
| 81 | if curraddr == None: | ||
| 82 | appstartaddr = newaddr | ||
| 83 | curraddr = newaddr | ||
| 84 | padding = newaddr - curraddr | ||
| 85 | if padding < 0: | ||
| 86 | assert False, "Block out of order at " + ptr | ||
| 87 | if padding > 10*1024*1024: | ||
| 88 | assert False, "More than 10M of padding needed at " + ptr | ||
| 89 | if padding % 4 != 0: | ||
| 90 | assert False, "Non-word padding size at " + ptr | ||
| 91 | while padding > 0: | ||
| 92 | padding -= 4 | ||
| 93 | outp += b"\x00\x00\x00\x00" | ||
| 94 | outp.append(block[32 : 32 + datalen]) | ||
| 95 | curraddr = newaddr + datalen | ||
| 96 | return b"".join(outp) | ||
| 97 | |||
| 98 | def convert_to_carray(file_content): | ||
| 99 | outp = "const unsigned long bindata_len = %d;\n" % len(file_content) | ||
| 100 | outp += "const unsigned char bindata[] __attribute__((aligned(16))) = {" | ||
| 101 | for i in range(len(file_content)): | ||
| 102 | if i % 16 == 0: | ||
| 103 | outp += "\n" | ||
| 104 | outp += "0x%02x, " % file_content[i] | ||
| 105 | outp += "\n};\n" | ||
| 106 | return bytes(outp, "utf-8") | ||
| 107 | |||
| 108 | def convert_to_uf2(file_content): | ||
| 109 | global familyid | ||
| 110 | datapadding = b"" | ||
| 111 | while len(datapadding) < 512 - 256 - 32 - 4: | ||
| 112 | datapadding += b"\x00\x00\x00\x00" | ||
| 113 | numblocks = (len(file_content) + 255) // 256 | ||
| 114 | outp = [] | ||
| 115 | for blockno in range(numblocks): | ||
| 116 | ptr = 256 * blockno | ||
| 117 | chunk = file_content[ptr:ptr + 256] | ||
| 118 | flags = 0x0 | ||
| 119 | if familyid: | ||
| 120 | flags |= 0x2000 | ||
| 121 | hd = struct.pack(b"<IIIIIIII", | ||
| 122 | UF2_MAGIC_START0, UF2_MAGIC_START1, | ||
| 123 | flags, ptr + appstartaddr, 256, blockno, numblocks, familyid) | ||
| 124 | while len(chunk) < 256: | ||
| 125 | chunk += b"\x00" | ||
| 126 | block = hd + chunk + datapadding + struct.pack(b"<I", UF2_MAGIC_END) | ||
| 127 | assert len(block) == 512 | ||
| 128 | outp.append(block) | ||
| 129 | return b"".join(outp) | ||
| 130 | |||
| 131 | class Block: | ||
| 132 | def __init__(self, addr): | ||
| 133 | self.addr = addr | ||
| 134 | self.bytes = bytearray(256) | ||
| 135 | |||
| 136 | def encode(self, blockno, numblocks): | ||
| 137 | global familyid | ||
| 138 | flags = 0x0 | ||
| 139 | if familyid: | ||
| 140 | flags |= 0x2000 | ||
| 141 | hd = struct.pack("<IIIIIIII", | ||
| 142 | UF2_MAGIC_START0, UF2_MAGIC_START1, | ||
| 143 | flags, self.addr, 256, blockno, numblocks, familyid) | ||
| 144 | hd += self.bytes[0:256] | ||
| 145 | while len(hd) < 512 - 4: | ||
| 146 | hd += b"\x00" | ||
| 147 | hd += struct.pack("<I", UF2_MAGIC_END) | ||
| 148 | return hd | ||
| 149 | |||
| 150 | def convert_from_hex_to_uf2(buf): | ||
| 151 | global appstartaddr | ||
| 152 | appstartaddr = None | ||
| 153 | upper = 0 | ||
| 154 | currblock = None | ||
| 155 | blocks = [] | ||
| 156 | for line in buf.split('\n'): | ||
| 157 | if line[0] != ":": | ||
| 158 | continue | ||
| 159 | i = 1 | ||
| 160 | rec = [] | ||
| 161 | while i < len(line) - 1: | ||
| 162 | rec.append(int(line[i:i+2], 16)) | ||
| 163 | i += 2 | ||
| 164 | tp = rec[3] | ||
| 165 | if tp == 4: | ||
| 166 | upper = ((rec[4] << 8) | rec[5]) << 16 | ||
| 167 | elif tp == 2: | ||
| 168 | upper = ((rec[4] << 8) | rec[5]) << 4 | ||
| 169 | assert (upper & 0xffff) == 0 | ||
| 170 | elif tp == 1: | ||
| 171 | break | ||
| 172 | elif tp == 0: | ||
| 173 | addr = upper | (rec[1] << 8) | rec[2] | ||
| 174 | if appstartaddr == None: | ||
| 175 | appstartaddr = addr | ||
| 176 | i = 4 | ||
| 177 | while i < len(rec) - 1: | ||
| 178 | if not currblock or currblock.addr & ~0xff != addr & ~0xff: | ||
| 179 | currblock = Block(addr & ~0xff) | ||
| 180 | blocks.append(currblock) | ||
| 181 | currblock.bytes[addr & 0xff] = rec[i] | ||
| 182 | addr += 1 | ||
| 183 | i += 1 | ||
| 184 | numblocks = len(blocks) | ||
| 185 | resfile = b"" | ||
| 186 | for i in range(0, numblocks): | ||
| 187 | resfile += blocks[i].encode(i, numblocks) | ||
| 188 | return resfile | ||
| 189 | |||
| 190 | def to_str(b): | ||
| 191 | return b.decode("utf-8") | ||
| 192 | |||
| 193 | def get_drives(): | ||
| 194 | drives = [] | ||
| 195 | if sys.platform == "win32": | ||
| 196 | r = subprocess.check_output(["wmic", "PATH", "Win32_LogicalDisk", | ||
| 197 | "get", "DeviceID,", "VolumeName,", | ||
| 198 | "FileSystem,", "DriveType"]) | ||
| 199 | for line in to_str(r).split('\n'): | ||
| 200 | words = re.split('\s+', line) | ||
| 201 | if len(words) >= 3 and words[1] == "2" and words[2] == "FAT": | ||
| 202 | drives.append(words[0]) | ||
| 203 | else: | ||
| 204 | rootpath = "/media" | ||
| 205 | if sys.platform == "darwin": | ||
| 206 | rootpath = "/Volumes" | ||
| 207 | elif sys.platform == "linux": | ||
| 208 | tmp = rootpath + "/" + os.environ["USER"] | ||
| 209 | if os.path.isdir(tmp): | ||
| 210 | rootpath = tmp | ||
| 211 | for d in os.listdir(rootpath): | ||
| 212 | drives.append(os.path.join(rootpath, d)) | ||
| 213 | |||
| 214 | |||
| 215 | def has_info(d): | ||
| 216 | try: | ||
| 217 | return os.path.isfile(d + INFO_FILE) | ||
| 218 | except: | ||
| 219 | return False | ||
| 220 | |||
| 221 | return list(filter(has_info, drives)) | ||
| 222 | |||
| 223 | |||
| 224 | def board_id(path): | ||
| 225 | with open(path + INFO_FILE, mode='r') as file: | ||
| 226 | file_content = file.read() | ||
| 227 | return re.search("Board-ID: ([^\r\n]*)", file_content).group(1) | ||
| 228 | |||
| 229 | |||
| 230 | def list_drives(): | ||
| 231 | for d in get_drives(): | ||
| 232 | print(d, board_id(d)) | ||
| 233 | |||
| 234 | |||
| 235 | def write_file(name, buf): | ||
| 236 | with open(name, "wb") as f: | ||
| 237 | f.write(buf) | ||
| 238 | print("Wrote %d bytes to %s" % (len(buf), name)) | ||
| 239 | |||
| 240 | |||
| 241 | def main(): | ||
| 242 | global appstartaddr, familyid | ||
| 243 | def error(msg): | ||
| 244 | print(msg) | ||
| 245 | sys.exit(1) | ||
| 246 | parser = argparse.ArgumentParser(description='Convert to UF2 or flash directly.') | ||
| 247 | parser.add_argument('input', metavar='INPUT', type=str, nargs='?', | ||
| 248 | help='input file (HEX, BIN or UF2)') | ||
| 249 | parser.add_argument('-b' , '--base', dest='base', type=str, | ||
| 250 | default="0x2000", | ||
| 251 | help='set base address of application for BIN format (default: 0x2000)') | ||
| 252 | parser.add_argument('-o' , '--output', metavar="FILE", dest='output', type=str, | ||
| 253 | help='write output to named file; defaults to "flash.uf2" or "flash.bin" where sensible') | ||
| 254 | parser.add_argument('-d' , '--device', dest="device_path", | ||
| 255 | help='select a device path to flash') | ||
| 256 | parser.add_argument('-l' , '--list', action='store_true', | ||
| 257 | help='list connected devices') | ||
| 258 | parser.add_argument('-c' , '--convert', action='store_true', | ||
| 259 | help='do not flash, just convert') | ||
| 260 | parser.add_argument('-D' , '--deploy', action='store_true', | ||
| 261 | help='just flash, do not convert') | ||
| 262 | parser.add_argument('-f' , '--family', dest='family', type=str, | ||
| 263 | default="0x0", | ||
| 264 | help='specify familyID - number or name (default: 0x0)') | ||
| 265 | parser.add_argument('-C' , '--carray', action='store_true', | ||
| 266 | help='convert binary file to a C array, not UF2') | ||
| 267 | args = parser.parse_args() | ||
| 268 | appstartaddr = int(args.base, 0) | ||
| 269 | |||
| 270 | if args.family.upper() in families: | ||
| 271 | familyid = families[args.family.upper()] | ||
| 272 | else: | ||
| 273 | try: | ||
| 274 | familyid = int(args.family, 0) | ||
| 275 | except ValueError: | ||
| 276 | error("Family ID needs to be a number or one of: " + ", ".join(families.keys())) | ||
| 277 | |||
| 278 | if args.list: | ||
| 279 | list_drives() | ||
| 280 | else: | ||
| 281 | if not args.input: | ||
| 282 | error("Need input file") | ||
| 283 | with open(args.input, mode='rb') as f: | ||
| 284 | inpbuf = f.read() | ||
| 285 | from_uf2 = is_uf2(inpbuf) | ||
| 286 | ext = "uf2" | ||
| 287 | if args.deploy: | ||
| 288 | outbuf = inpbuf | ||
| 289 | elif from_uf2: | ||
| 290 | outbuf = convert_from_uf2(inpbuf) | ||
| 291 | ext = "bin" | ||
| 292 | elif is_hex(inpbuf): | ||
| 293 | outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8")) | ||
| 294 | elif args.carray: | ||
| 295 | outbuf = convert_to_carray(inpbuf) | ||
| 296 | ext = "h" | ||
| 297 | else: | ||
| 298 | outbuf = convert_to_uf2(inpbuf) | ||
| 299 | print("Converting to %s, output size: %d, start address: 0x%x" % | ||
| 300 | (ext, len(outbuf), appstartaddr)) | ||
| 301 | if args.convert or ext != "uf2": | ||
| 302 | drives = [] | ||
| 303 | if args.output == None: | ||
| 304 | args.output = "flash." + ext | ||
| 305 | else: | ||
| 306 | drives = get_drives() | ||
| 307 | |||
| 308 | if args.output: | ||
| 309 | write_file(args.output, outbuf) | ||
| 310 | else: | ||
| 311 | if len(drives) == 0: | ||
| 312 | error("No drive to deploy.") | ||
| 313 | for d in drives: | ||
| 314 | print("Flashing %s (%s)" % (d, board_id(d))) | ||
| 315 | write_file(d + "/NEW.UF2", outbuf) | ||
| 316 | |||
| 317 | |||
| 318 | if __name__ == "__main__": | ||
| 319 | main() | ||
