Skip to content
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

UF2 Bootloader UART Support #42

Open
ghost opened this issue May 8, 2018 · 16 comments
Open

UF2 Bootloader UART Support #42

ghost opened this issue May 8, 2018 · 16 comments

Comments

@ghost
Copy link

ghost commented May 8, 2018

Thank you to everyone for the work on the UF2 bootloader.

I'm using a SAMD51 (SAMD51J20A) custom board. The USB CDC works perfectly with the Arduino IDE, however I've been having some trouble getting the UART support to work.

I powered the board without the native USB connection to prevent enumeration, put my custom board into bootloader mode with a double tap of the reset button, then attempted to upload a Blink sketch with the Arduino IDE, using an FTDI USB to serial board on a spare UART port, (in my case SERCOM5).

After looking at the code, I believe that line 128 of the "usart_sam_ba.c" file needs to be changed from:

// Baud rate 115200 - clock 8MHz -> BAUD value-50436
uart_basic_init(BOOT_USART_MODULE, 50436, BOOT_USART_PAD_SETTINGS);

to:

// Baud rate 115200 - clock 48MHz -> BAUD value-63018
uart_basic_init(BOOT_USART_MODULE, 63018, BOOT_USART_PAD_SETTINGS);

This is to account for GCLK0 now being set to 48MHz. The replacement line is taken from the Arduino Zero bootloader.

After this modification I'm now able to upload the sketch using the FTDI board, unfortunately though some timing issues still persist, as this only works on every second attempt.

@dhalbert
Copy link
Collaborator

dhalbert commented May 8, 2018

This was merged from https://github.com/adafruit/uf2-samd21, so we (Adafruit) will look at this. This looks a debugging leftover. @MartinL1, feel free to submit issues there related to SAMD51. I'll create new issues for us in our repo.

@dhalbert
Copy link
Collaborator

dhalbert commented May 8, 2018

@MartinL1 If you can continue to look at the timing issues, that would be very helpful. We don't normally use the UART support.

@matt448
Copy link

matt448 commented Dec 10, 2018

I ran into the same issue but with the SAMD21 chip. Making the code change that @MartinL1 recommended does get it to work but only on the second attempt which obviously isn't optimal. I discovered the first attempt to communicate with the chip over UART fails no matter what you ask it to do (info, read, erase, write). As a temporary work around I'm doing two info requests and then doing the write.

bossac --debug --port=tty.usbserial-X501XXXA --info
bossac --debug --port=tty.usbserial-X501XXXA --info
bossac --debug --port=tty.usbserial-X501XXXA --offset=0x2000 --erase --write --verify --reset BlinkSlow/BlinkSlow.ino.itsybitsy_m0.bin

mmoskal pushed a commit that referenced this issue Apr 16, 2019
Updated VID/PID for SparkFun SAMD21 Dev breakout
@alexwhittemore
Copy link
Contributor

How is baud_val calculated, anyway? I'm working on a custom SAMD21 board, where I need HW UART bootloader support. I threw a little line into the bootloader:

#if USE_UART
        usart_putdata("ready", 5); // My debugging addition
        /* Check if a '#' has been received */
        if (!main_b_cdc_enable && usart_sharp_received()) {
            RGBLED_set_color(COLOR_UART);
            sam_ba_monitor_init(SAM_BA_INTERFACE_USART);
            /* SAM-BA on UART loop */
            while (1) {
                sam_ba_monitor_run();
            }
        }
#endif

This spits out "ready" over and over again until USB enumerates.

With the original line,

uart_basic_init(BOOT_USART_MODULE, 50436, BOOT_USART_PAD_SETTINGS);

I get an output at 476baud. With the new line from above,

uart_basic_init(BOOT_USART_MODULE, 63018, BOOT_USART_PAD_SETTINGS);

that drops to 78baud.

I'm quite certain my system clock is at 48MHz, as that was a whole other thing.

@ghost
Copy link
Author

ghost commented Apr 18, 2019

@alexwhittemore

How is baud_val calculated, anyway?

The asynchronous arithmetic baud rate is calculated as follows:

baud rate = (f(ref) / s) * (1 - BAUD/65536)

where:
f(ref) = reference frequency = 48MHz
s = number of samples per bit = 16
BAUD = value in the BAUD register = 63018

baud rate = (48MHz / 16) * (1 - 63018 / 65536) = 115264bps = approx. 115200bps

The calculation is detailed on page 459 of the SAMD21 datasheet, or page 931 on the SAMD51's.

@matt448
Copy link

matt448 commented Apr 18, 2019

@alexwhittemore The Atmel ASF framework calculates it like this:

65536 - ((65536 * 16.0f * CONF_SERCOM_0_USART_BAUD) / CONF_GCLK_SERCOM0_CORE_FREQUENCY)

65536 - ((65536 * 16.0 * 115200) / 48000000) = 63019
65536 - ((65536 * 16.0 * 115200) / 8000000) = 50436

That 50436 value was hard coded based on the clock running at 8MHz. It wasn't updated when the bootloader was changed to run at 48MHz.

@alexwhittemore
Copy link
Contributor

Thank you for the super detailed responses! The Arduino core code has the same lines in it, with a bit more in the way of comments including those formulas, which I noticed right before bed last night. Of course, the values above calculate out correctly. Being unable to achieve expected output baud rate, I just started stabbing in the dark. I eventually tried uart_basic_init(BOOT_USART_MODULE, 6, BOOT_USART_PAD_SETTINGS); or something like that, and the fastest output rate I was able to achieve was IIRC 2000 baud. So something weird is going on here.

Of course, the reason I'm interested at all is to get serial upload working on my SAMD21. There's a confounding variable that it seems PlatformIO is handling my serial adapter badly and I'm not sure why. I've verified my uploader never actually sends anything when it starts an upload (I think it's supposed to send a '#' to start the FW transaction?).

So serial upload is probably not working for me because the baud rate is still very incorrect, but also it's probably not working for me because my uploader simply isn't doing the right thing.

Oh - no. Update: I was getting no output on my FTDI because it was open in a serial monitor I was unaware of, and PIO doesn't complain about being unable to open the port - that's just a silent failure.

That said, now that I have output, I'm getting framing errors from both Arduino and PlatformIO, and no apparent '#' start byte. So I'm still not sure that the computer-side of this transaction is working right.

@matt448
Copy link

matt448 commented Apr 18, 2019

@alexwhittemore I would recommend you try using the BOSSA utility to confirm that serial firmware uploads work. That way you aren't troubleshooting your IDE at the same time. I would just try doing a --info and see if you can talk to the bootloader:

bossac --debug --port=tty.usbserial-XYZ123 --info
or
bossac --debug --port=COM45 --info

The other problem that @MartinL1 was reporting in this issue is that even with the uart baud set correctly the first request to the bootloader always fails. Subsequent requests will work. If you send two --info requests to the board you should get back info on the second try.

Another thing I ran into - I'm using an Adafruit ItsyBitsy M0 board right now and I'm powering it through its USB connector. If the USB is connected to a port on my computer the UART bootloader communication on the SAMD21 will not work. If I power the board with a power only USB port (like a phone charger) the UART bootloader works fine.

@alexwhittemore
Copy link
Contributor

@matt448 Thanks once again for the extremely useful suggestion! I'm doing some experimenting with bossa right now to test theories and baselines.

That's extremely interesting that your board of choice though is the ItsyBitsy M0 since it suffers most of the same problems/required modifications as my board. For instance - it looks like uf2, by default, uses SERCOM3 pads 1+2 for UART. So either you reconfigured the SERCOM with something like (from my board_config.h):

#define BOOT_USART_MODULE SERCOM0
#define BOOT_USART_PAD_SETTINGS UART_RX_PAD3_TX_PAD2
#define BOOT_USART_PAD3 PINMUX_PA11C_SERCOM0_PAD3
#define BOOT_USART_PAD2 PINMUX_PA10C_SERCOM0_PAD2
#define BOOT_USART_PAD1 PINMUX_UNUSED
#define BOOT_USART_PAD0 PINMUX_UNUSED

or you did the bare minimum to enable UART bootloading and you're using the ItsyBitsyM0's SDA+SCL pins (PA22+PA23, the default from uf2 main.h) - which of those is it, so I can try to recreate a successful baseline?

@matt448
Copy link

matt448 commented Apr 18, 2019

@alexwhittemore I reconfigured to use different pins. They are alternate SERCOM0 pins. I created a fork with my changes if you want to check it out: https://github.com/matt448/uf2-samdx1
I modified uf2.h and created a few different board definitions so I had some options for which pins to use with the uart bootloader. I also added some notes to the top of the README with example bossac commands.

If you pull down my fork and run make itsybitsy_m0_UART_SERCOM0_ALT2 it will build something that for sure works on an ItsyBitsy M0. In the boards directory I made four different itsybitsy_m0_UART_SERCOMx configs. I have tested all of them and they work.

#define BOOT_USART_MODULE                 SERCOM0
#define BOOT_USART_MASK                   APBAMASK
#define BOOT_USART_BUS_CLOCK_INDEX        MCLK_APBBMASK_SERCOM0
#define BOOT_USART_PAD_SETTINGS           UART_RX_PAD1_TX_PAD0
#define BOOT_USART_PAD3                   PINMUX_UNUSED
#define BOOT_USART_PAD2                   PINMUX_UNUSED
#define BOOT_USART_PAD1                   PINMUX_PA05D_SERCOM0_PAD1 //Pin A4 on ItsyBitsy M0 -> HOST TXO
#define BOOT_USART_PAD0                   PINMUX_PA04D_SERCOM0_PAD0 //Pin A3 on ItsyBitsy M0 -> HOST RXI

@alexwhittemore
Copy link
Contributor

You're the best! If this doesn't completely solve my problem, it'll at least get me super far.

#define BOOT_USART_MASK                   APBAMASK
#define BOOT_USART_BUS_CLOCK_INDEX        MCLK_APBBMASK_SERCOM0

That sure looks suspicious. I noticed those lines in the SAMD51 board_config.hs but I assumed they weren't relevant to the SAMD21 since the "default config" in main.h doesn't include them. But SERCOM clocking sure sounds like a relevant issue for me.

@alexwhittemore
Copy link
Contributor

alexwhittemore commented Apr 19, 2019

@matt448 Sorry for repaying your kindness with an inbox flood ;)

So I went through your examples with a fine-toothed comb until they all worked and I understood each change pretty well. Then I adapted them to my own bootloader repo and...

EXACT same behavior. 78baud output from the microcontroller in BL mode.

Then I figured, the only reason I have my own clone of the repo in the first place was to add a boards/myvariant, so I just copied that folder into my clone of YOUR repo, and lo and behold, works perfectly. No idea what changed since I first cloned, but it works now.

EDIT: As an aside, I have no idea what the line
#define BOOT_USART_BUS_CLOCK_INDEX MCLK_APBBMASK_SERCOM0
is supposed to do.
MCLK_APBBMASK_SERCOM0 isn't defined anywhere at all, though
MCLK_APBAMASK_SERCOM0 is. It turns out, BOOT_USART_BUS_CLOCK_INDEX only applies to the SAMD51 anyway.

@alexwhittemore
Copy link
Contributor

@dhalbert FWIW, this issue seems pretty well characterized at this point and I'm quite confident the original correction is accurate and adequate. I'm seeing the same first-attempt-failure as @matt448 and don't quite know what's going on there, but I'm also pretty comfortable by now with this subsystem. Is resolution waiting only on a pull request? I'd love to put one together to fix this, and add some tested template code (built on Matt's work) for enabling serial on other SAMD boards.

Now that I'm all set, it's outside the scope of my client work, but I could probably get it out this weekend if that's all the issue needs.

@dhalbert
Copy link
Collaborator

@alexwhittemore Feel free to submit a PR either to our fork or microsoft/uf2-samdx1. We try to keep in sync with each other. Thanks to you and @mat448 for your thorough investigation.

@jpmeijers
Copy link

I'm using uf2-samdx1 on a Sodaq Explorer, testing bootloader firmware upload via the UART. This is to make sure everything works before finalising the schematic of my own custom SAMD21 based board.
Using:

#define BOOT_USART_MODULE SERCOM5
#define BOOT_USART_PAD_SETTINGS UART_RX_PAD1_TX_PAD0
#define BOOT_USART_PAD3 PINMUX_UNUSED
#define BOOT_USART_PAD2 PINMUX_UNUSED
#define BOOT_USART_PAD1 PINMUX_PB31D_SERCOM5_PAD1
#define BOOT_USART_PAD0 PINMUX_PB30D_SERCOM5_PAD0

Also applied the patch to usart_sam_ba.c:

    /* Baud rate 115200 - clock 8MHz -> BAUD value-50436 */
    //uart_basic_init(BOOT_USART_MODULE, 50436, BOOT_USART_PAD_SETTINGS);
    // Baud rate 115200 - clock 48MHz -> BAUD value-63018
    uart_basic_init(BOOT_USART_MODULE, 63018, BOOT_USART_PAD_SETTINGS);

After a manual double-tap reset I can upload firmware using Bossac. It's not that stable and a few retries are necessary. I also noted that the uart bootloader does not work when usb is enumerated.

Another issue I am stuck with now is that it does not seem like Bossac resets the board and does not put it into bootloader mode my itself. I expected Bossac to toggle the DTR line on my FTDI breakout so that a reset can be done via a capacitor, just like the Arduino Uno does. Is there a way to tell Bossac to reset the board, so that I do not need to manually double-tap reset the board every time I want to upload firmware?

My other 2 cents:
Uploading firmware via the UART is very useful when you try and build a board that needs to be reverse compatible with something like an Arduino Uno. Using the same connectors and interfaces to do the same job, but with a better MCU. It is also easier to bring out a UART on a connector rather than something sensitive to impedances like USB.

@elsp1991
Copy link

I also have a custom board with SAMD21 that I Want to program through Uart interface. I modified the bootloader to enable Uart in the correct pins and changed it to single press reset (have a look here @jpmeijers) with the idea to use the traditional arduino Uno reset method with the CTS/DTR signal from the FTDI. So when Bossac will open the serial port to talk to the bootloader will trigger a reset and bring the board to boot mode. The Idea works so it resets when bossac starts but unfortunately bossac is too early in trying to connect so it fails.
Is there a way to accelerate the boot time of the bootloader so Bossac is not too fast for it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants