10000 mimxrt/bootloader: Enable support for the UF2 bootloader. by robert-hh · Pull Request #15983 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

mimxrt/bootloader: Enable support for the UF2 bootloader. #15983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 10, 2025

Conversation

robert-hh
Copy link
Contributor
@robert-hh robert-hh commented Oct 9, 2024

Based on tinyuf2, which can be installed and used with MicroPython. The .uf2 file is created in addition to the .bin and .hex files allowing to use the latter ones without the bootloader for debugging and testing.

Changes:

  • Set the location of the ISR Vector and .text segment to 0x6000C000 and 0x6000C400.
  • Reserve an area at the start of ITCM for a copy of the interrupt vector table and copy the table on reset to this place.
  • Extend machine.bootloader() by setting the magic number to enable the bootloader on reset.
  • Create a .uf2 file which skips the segments below 0x6000C000.
  • Update the deploy instructions.
  • Change the autobuild script to include the .uf2 file.

The bootloader has to be installed as a preparation step using the board specific methods, but then the firmware's .uf2 file version can be installed using the bootloader. The bootloader can be invoked with:

  • double reset
  • calling machine.bootloader()
  • Using the touch1200 method

Tested with:

  • Adafruit Metro M7
  • MIMXRt1011-EVK
  • MIMXRT1015-EVK
  • MIMXRT1021-EVK
  • Olimex RT1011
  • Seeed ARCH MIX
  • Makediary RT1011 Nano Kit

The change is especially useful for boards which are delivered with a UF2 bootloader like the Adafruit Metro M7 or with no bootloader like the Olimex and Seeed boards. There, the more complicated initial install has to be done only once.
The bootloader needs 48kByte of flash memory. But the MIMXRT boards have plenty of flash space, making this additional 8000 flash use acceptable.

Some boards are excluded from the .uf2 build:

  • MIMXRT1050_EVK: The uf2 bootloader is built for the QSPI version of the board. MicroPyton supports the Hyperflash version.
  • TEENSY40, TEENSY41: There is trouble with the uf2 bootloader even when working with Circuitpython. The Bootloader can be installed and works once after installation. But it cannot be invoked by any of the above means.
  • MIMXRT1176_EVK: No support for this board yet, but it should be possible.

Copy link
codecov bot commented Oct 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.59%. Comparing base (b2ce9b6) to head (0a433a0).
Report is 6 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #15983   +/-   ##
=======================================
  Coverage   98.59%   98.59%           
=======================================
  Files         167      167           
  Lines       21599    21599           
=======================================
  Hits        21295    21295           
  Misses        304      304           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
github-actions bot commented Oct 9, 2024

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:  +420 +0.115% TEENSY40[incl +32(data)]
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@mattytrentini
Copy link
Contributor

Thanks for making this change @robert-hh, and I've also tested with the Makerdiary Nano Kit; appears to work fine (and with the default bootloader - based on TnyUF2 - shipped with the product).

@@ -467,7 +474,13 @@ $(BUILD)/lib/tinyusb/src/device/usbd.o: CFLAGS += -Wno-missing-braces
# Build targets
# =============================================================================

# Do not build the .uf2 target for MIMXRT1050_EVK, MIMXRT1170_EVK, Teensy40 and Teensy41
# These boards do not support the bootloader properly.
ifneq ($(BOARD), $(filter $(BOARD), MIMXRT1050_EVK TEENSY40 TEENSY41 MIMXRT1170_EVK))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding a make variable to those boards that use the bootloader, instead of testing for specific boards. Eg USE_BOOTLOADER = 1, and then here ifeq ($(USE_BOOTLOADER),1).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'll do. The default would be to use the bootloader.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author
@robert-hh robert-hh Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This switch only controls the creation of the .uf2 file. The memory layout is the same for boards which do not use the uf2 bootloader. That reminds me of adding the rule to add the .uf2 file to the download page. I assume that a .uf2 file is silently skipped if missing.

$(BUILD)/firmware.uf2: $(BUILD)/firmware.elf
$(Q)$(OBJCOPY) -O binary -R .stack -R .ivt -R .flash_config $^ $@-binpart
$(Q)$(PYTHON) $(TOP)/tools/uf2conv.py -b $(BOOTLOADER_SIZE) -f MIMXRT10XX -c -o $@ $@-binpart
$(Q)rm $@-binpart
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file lives in the build directory so there's no need to remove it here.

You could also create a new rule for $(BUILD)/firmware.uf2_bin that has the objcopy line from this rule, and then make this rule depend on that one. Similar to how samd does it.

Also I suggest making a UF2CONV variable for uf2conv.py, same as in samd.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK for the non-remove. I'll look into using the variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -90,10 +92,9 @@ SECTIONS
. = ALIGN(4);
} > m_interrupts

__VECTOR_RAM = __Vectors;
__RAM_VECTOR_TABLE_SIZE_BYTES = 0x0;
__Vectors_RAM = itcm_start;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be __Vectors_RAM = ORIGIN(m_isr) instead? I know it's the same thing, but m_isr seems more logical, and the source of truth here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


1. Load the MicroPython firmware directly to the device. The MicroPython
firmware files for that method have the extension .bin or .hex and are available
at the MicroPython download site.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the .bin/.hex files work without having a bootloader installed? I don't think they will, because interrupts_start and text_start have been changed.

Copy link
Contributor Author
@robert-hh robert-hh Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. They work. I tested that by erasing the flash of a device and then installing the .bin and .hex files. At Teensy this is the default. But to be sure, I'll repeat the test.
Loading a .bin, .hex or .elf file overwrites the bootloader. Being able to use the .elf file is convenient for debugging.

@robert-hh
Copy link
Contributor Author
robert-hh commented Jan 24, 2025

@dpgeorge The changes you suggested are now implemented. I tested the PR again using:

  • MIMXRT1011 with the boards Adafruit Metro M7 and Makediary Nano Kit
  • MIMXRT1015 with the boards MIMXRT1015-EVK
  • MIMXRT1021 with the board MIMXRT1020-EVK
  • MIMXRT1052 with the board Seeed Arch Mix.
  • MIMXRT1062 with Teensy 4.1 (no uf2 bootloader)

I tested both using the UF2 bootloader for uploading the .uf2 file when supported and uploading the generated .hex resp. .elf file using a Segger adapter or the Teensy bootloader. The "standalone" versions of the firmware can still be uploaded and work, turning off the uf2 bootloader.

@dpgeorge
Copy link
Member

I tested both using the UF2 bootloader for uploading the .uf2 file when supported and uploading the generated .hex resp. .elf file using a Segger adapter or the Teensy bootloader. The "standalone" versions of the firmware can still be uploaded and work, turning off the uf2 bootloader.

Excellent. Thanks for retesting.

@robert-hh
Copy link
Contributor Author

I made a few little changes, enabling the bootloader on Teensy boards as a test for other MIMXRT106x boards. There was a problem that wit calling machine.bootloader() the ROM bootloader was called instead of the UF2 bootloader. That should have affected the other boards as well, and is changed now.
With Teensy both the teensy loader or the UF2 bootloader can be used. But using the teensy loader would disable the UF2 bootloader. The methods for starting the UF2 boatloader on Teensy are calling machine.reset() or the touch1200 method. Lacking a reset button, double reset does not work.

@@ -162,6 +163,13 @@ SRC_HAL_IMX_C += \
$(MCU_DIR)/drivers/fsl_romapi.c
endif

# If not empty, then it is 10xx.
ifneq ($(findstring MIMXRT10, $(MCU_SERIES)),)
BOOTLOADER_SIZE := 0x6000C000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this variable would be better called APPLICATION_ADDR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I can change that, because that's what it is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@robert-hh
Copy link
Contributor Author
robert-hh commented Jan 26, 2025

@dpgeorge With further testing of this PR it appears, that there is a problem with flash write access at some boards. The TinyUF2 bootloader re-configures the flash - IMHO without any need . I thought I had compensated for that, but it seems that not for each board or flash type.
So please wait with merging.

Edit: The problem occurs only with MIMXRT1010 boards, and happens when using mpremote to copy a file to the board, when the file does not yet exist. The directory entry is created, but on file copy the is an internal hard fault. If the file exists, even with 0 length, copy runs fine. Local file operation at the board succeed well. The same firmware loaded to the board directly without the UF2 bootloader works well. Stack trace on hard fault below. It looks as if file write and USB communication get in conflict.
Call Stack on Fault

The SCBDisableCache/EnableCache .... was already a point of discussion. If I remove it, I get a Python Exception with OSError: 36

There is another phenomenon: With the UF2 Bootloader active the execution speed on some boards is heavily reduced. No impact for Teensy 4.x, but for instance with the MIMXRT1020-EVK the speed is cut to halve. The bootloader configures flash write to single speed instead of quad speed, but on during the test there are no flash writes.
Anyhow, the whole flash sections needs a rework. I started it, but at low drive since flash access works.

@robert-hh robert-hh force-pushed the mimxrt_uf2_bl branch 2 times, most recently from 574e47e to ab0d051 Compare January 26, 2025 20:45
@robert-hh
Copy link
Contributor Author

I changed the low level functions for flash to reverse the order of disabling flash and disabling IRQ (and the oppostite), after which I could not provoke the flash write problem again. The commit is attached.

@iabdalkader is that some info for your recent reasoning, whether this SCB_CleanInvalidateDCache(); SCB_DisableDCache(); is needed or not?

@iabdalkader
Copy link
Contributor
iabdalkader commented Jan 26, 2025

@iabdalkader is that some info for your recent reasoning, whether this SCB_CleanInvalidateDCache(); SCB_DisableDCache(); is needed or not?

I'm not sure I understand the question, I never worked on this flash driver. However, note that SCB_DisableDCache already cleans and invalidates the cache, so you probably don't need SCB_CleanInvalidateDCache at all.

I'm actually more concerned about the flash changes: they seem to downgrade the flash from QUAD to single line, among other unknown changes. I'd at least make sure all changes are conditional on the bootloader being enabled, and document the changes in the commit.

EDIT: If you were asking if we needed the clean/invalidate, yes. The flash contents is going to change, so any cached reads need to be invalidated. I don't think there's any dirty lines (writes) to flush though.

@robert-hh
Copy link
Contributor Author

However, note that SCB_DisableDCache already cleans and invalidates the cache

Thanks. I looked into the code of SCB_DisableDCache and you're right. So I can remove the clearing.

I'm actually more concerned about the flash changes: they seem to downgrade the flash from QUAD to single line.

I'm not overall happy about it, even if write time is not increased a lot. The flash clock frequency for a iMXRT1011 board is 60MHz. So the data transfer time is 4.3µs (quad) or 17µs (single) compared to page program times of 600 to 2400 µs. In addition, if you program the device with a .bin or .hex file, the uf2-bootloader is bypassed. Looking at the timing at the flash chip with an oscilloscope, the flash read is still done in quad mode.

@robert-hh
Copy link
Contributor Author
robert-hh commented Jan 27, 2025

Comparing the flash frequencies, it seems that the UF2 bootloader reconfigures the flash clock from ~100Mhz to ~60MHz for iMXRT1011 and 30MHz for i.MXRT1021. That explains the speed difference. Have to switch that back or ask Hatach not to change the flash clock.

@robert-hh
Copy link
Contributor Author
robert-hh commented Jan 28, 2025

I looked to change back the flash config at runtime, but this is hardly feasible. Even at runtime of MicroPython, the flash configuration of the UF2 bootloader is used, and when trying to change that, the bootloader does not start the app. So I'll make a PR for the bootloader.
The genuine flash config of MicroPython will be used when the bootloader is overwritten. That set-up has to be reworked in a separate PR.
Edit: The PR in the UF2-bootloader repository is made. No further change needed so far here.

@robert-hh
Copy link
Contributor Author

@dpgeorge Meanwhile Adafruit has updated it's repository with the higher clock frequencies. Only that this is not yet in a release of binary images, which could be referenced. Scott Shawcroft was so kind to direct me to the place in the CircuitPython code where the flash clock is changed at runtime. I adapted that for the MIMXRT port and it works, at least for the mimxr10xx series. I can add that change to this PR, so we do not have to wait for the next release of the UF2-bootloader.

@robert-hh robert-hh marked this pull request as draft February 3, 2025 17:36
@robert-hh robert-hh marked this pull request as ready for review February 4, 2025 14:12
@robert-hh
Copy link
Contributor Author

@dpgeorge Quad mode page program is now enabled again as well. I added per-board settings to cover the different commands and arguments for each board. The flash chips differ by three parameters, frequency, command to enable Quad mode and Argument for that command. Adding these to the board .mk file seemed easier than adding a flash_config.c file to each board definition. I had prepared that option as well, but it required adding many files which were mostly identical. Tested for all board I have with both using the UF2 bootloader and loading the firmware directly. Test overview:

Board          Flash Chip      Timing  Freq     QE_CMD  QE_ARG   Build  UF2  Direct load
Adafruit MM7   W25Q64JV         3ns    133 MHz   0x31    0x02     OK     OK   OK
Makediary NK   W25Q128JV        3ns    133 MHz   0x31    0x02     OK     OK   OK
MIMXRT1010     AT25SF128A       5ns    100 MHz   0x31    0x02     OK     OK   OK
MIMXRT1015     AT25SF128A       5ns    100 MHz   0x31    0x02     OK     OK   OK
MIMXRT1020     IS25LP064        3ns    133 MHz   0x01    0x40     OK     OK   OK
MIMXRT1050 NOR IS25WP064        5ns    133 MHz   0x01    0x40     Same setup as Arch mix
MIMXRT1050 HPR S26KS512SDPBHI02 5ns    133 MHz                    OK     -    OK
MIMXRT1060     IS25WP064        5ns    133 MHz   0x01    0x40     No board for test
MIMXRT1064     W25Q32JV         3ns    100 MHz   0x31    0x02     No board for test
MIMXRT1176     IS25WP128-JBLE   3ns    133 MHz   0x31    0x02     OK     -    OK
Olimex RT1010  EN25Q16-104      3ns    100 MHz   0x01    0x40     OK     OK   OK 
SEEED ARCH MIX IS25WP064        5ns    133 MHz   0x01    0x40     OK     OK   OK
Teensy 4.0     W25Q16JVUXIM     5ns    133 MHz   0x31    0x02     OK     OK   OK
Teensy 4.1     W25Q64JVXGIM     3ns    133 MHz   0x31    0x02     OK     OK   OK

@iabdalkader Could you test that PR with a MIMXRT1064 board, if you still have access to it?

@iabdalkader
Copy link
Contributor

@iabdalkader Could you test that PR with a MIMXRT1064 board, if you still have access to it?

I'll see if I can find it.

@robert-hh
Copy link
Contributor Author

The commit that sets the flash clock frequency according to the configured value is added now. It may not be needed any more with the next release of the UF2 bootloader. But for all users with an older version of the bootloader this is useful. It applied only to i.mxrt10xx boards with QSPI flash. Tested with:

i.mxrt 1011 (MIMXRT1011-EVK)
i.mxrt 1015 (MIMXRT1015-EVK)
i.mxrt 1021 (MIMXRT1020-EVK)
i.mxrt 1052 (Seeed Arch Mix 1052)
i.mxrt 1062 (Teensy 4.1)

@robert-hh robert-hh force-pushed the mimxrt_uf2_bl branch 3 times, most recently from b40abbc to 95751b1 Compare February 6, 2025 18:09
@iabdalkader
Copy link
Contributor

I'm going to test this on the weekend, haven't forgotten about it.

@robert-hh
Copy link
Contributor Author

@iabdalkader OK. Don't worry.

@iabdalkader
Copy link
Contributor
iabdalkader commented Feb 8, 2025

@robert-hh I programmed the bootloader:

[10245.500663] scsi 0:0:0:0: Direct-Access     Adafruit UF2 Bootloader   1.0  PQ: 0 ANSI: 2
[10245.502161] sd 0:0:0:0: [sda] 65801 512-byte logical blocks: (33.7 MB/32.1 MiB)

Now when I copy firmware.uf2 to the MSC disk it seems to overwrite itself. Shouldn't the flash address, in the linker script, change when the bootloader is enabled?

@robert-hh
Copy link
Contributor Author

The question is:

a) does MicroPyhton run
b) can you restart the bootloader with machine.bootloader().

The code for text should start at flash_start + 32k. the firmware.bin and firmware-hex images still containc the ISR, such the these files can be loaded directly, overwriting the bootloader. But these parts of the firmware are stripped from the .uf2 file, avoiding to overwrite the bootloader.

@iabdalkader
Copy link
Contributor

The question is:

a) does MicroPyhton run b) can you restart the bootloader with machine.bootloader().

Not sure what happened the first time, but when I copy the firmware now it works. And yes, I can enter the bootloader again via machine.bootloader(), so all is good.

@robert-hh
Copy link
Contributor Author

Can you run at least twice the cycle:

  • start bootloader
  • upload firmware

touch1200 should work as well to start the bootlader, and reset twice. Although the timing for latter is a little bit tricky on the -EVK board. You have to push reset the second time JUST when the led close to the MCU goes off.

@iabdalkader
Copy link
Contributor

Can you run at least twice the cycle:

I've already did, and it worked fine. I only tested machine.bootloader() nothing else.

@robert-hh
Copy link
Contributor Author

Thank you for testing and confirming. One last question: Is it possible to write to the file system with MicroPython? For instance using mpremote.
That confirms the quad mode is properly enabled for writing.

@iabdalkader
Copy link
Contributor

Yes, the filesystem is writable.

@robert-hh
Copy link
Contributor Author

Thanks again.

@dpgeorge
Copy link
Member
dpgeorge commented Feb 9, 2025

Thanks @robert-hh for improving this PR, and @iabdalkader for testing.

Is this now ready to be merged?

@robert-hh
Copy link
Contributor Author

I would say yes. The last commit will not be needed any more, when the UF2 bootloader site is updated. Anyhow, it does not hurt.

Allowing to use e.g. the Adafruit bootloaders with MicroPython.  The .uf2
file is created in addition to the .bin and .hex files allowing to use the
latter ones without the bootloader for debugging and testing.

Changes:

- Set the location of the ISR Vector and .text segment to 0x6000C000 and
  0x6000C400.
- Reserve an area at the start of ITCM for a copy of the interrupt vector
  table and copy the table on reset to this place.
- Extend `machine.bootloader()` by setting the magic number to enable the
  bootloader on reset.
- Create a .uf2 file which skips the segments below 0x6000C000.

The bootloader has to be installed as a preparation step using the board
specific methods, but then the firmware's .uf2 file version can be
installed using the bootloader.  The bootloader can be invoked with:

- double reset
- calling machine.bootloader()
- Using the touch1200 method

Double reset is hard to achieve on MIMXRT boards, since there is no clean
reset pin.  Some MIMXRT boards provide it by switching the power.

Some boards are excluded from the .uf2 build:

- MIMXRT1050_EVK: The uf2 bootloader is built for the QSPI version of the
  board.  MicroPython supports the Hyperflash version.
- MIMXRT1176_EVK: No support for this board yet, but it should be possible.

Signed-off-by: robert-hh <robert@hammelrath.com>
Changes:
- Change the LUT table ordering to be similar to the order of the
  UF2-Bootloader and fsl_romapi.h.
- Rewrite the LUT entry for PAGEPROGRAM_QUAD and update the LUT.  That
  enabled QUAD program again.

Signed-off-by: robert-hh <robert@hammelrath.com>
This change stopped problems with USB IRQ happening during flash writes.

Signed-off-by: robert-hh <robert@hammelrath.com>
Signed-off-by: robert-hh <robert@hammelrath.com>
And use these to initialize the LUT table properly for the various flash
types.  The different flash types differ by 3 parameters.  Thus it is
easier to just keep one copy of the qspiflash_config structure with the LUT
table and update it during flash initialisation as needed.

Signed-off-by: robert-hh <robert@hammelrath.com>
The flash clock frequency may have been set to a different value by a
bootloader.  Set the frequency according to the configured value.  Use a
table of pre-calculated dividers to get the closest value for the flash
frequency, achieving for MIMXRT10xx:

  30 -> 30.85 MHz
  50 -> 49.65 MHz
  60 -> 60 MHz
  75 -> 75.13 MHz
  80 -> 80 MHz
  100 -> 99.31 Mhz
  133 -> 132.92 MHz
  166  -> 166.15 MHz

for MIMXRT1176:

  30 -> 31 MHz
  50 -> 52.8 MJz
  60 -> 58.7 MHz
  75 -> 75.4 MHz
  80 -> 75.4 MHz
  100 -> 105.6 MHz
  133 -> 132 MHz
  166 -> 176 MHz

Signed-off-by: robert-hh <robert@hammelrath.com>
Copy link
Member
@dpgeorge dpgeorge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this on TEENSY40:

  • build and flash the non-uf2 firmware using Teensy Loader
  • flash tinyuf2 (using Teensy Loader)
  • deploy MicroPython uf2 via MSC
  • use machine.bootloader() to enter the bootloader
  • reflash non-uf2 version of MicroPython using Teensy Loader

All the above works! Very good.

@dpgeorge dpgeorge merged commit 0a433a0 into micropython:master Feb 10, 2025
65 checks passed
@robert-hh robert-hh deleted the mimxrt_uf2_bl branch February 11, 2025 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0