oskirby / tinydfu-bootloader

USB DFU Bootloader for small FPGAs
Apache License 2.0
6 stars 4 forks source link

New port to colorlight_i5 ecp5 board #5

Open bmentink opened 2 years ago

bmentink commented 2 years ago

Hi There,

I am trying to port your booloader to the above board. After tweaking the board pin constraints file to the new board, and a few other tweaks, I now have the bootloader running when I download directly to the FPGA. (Based my port on the logicbone_ecp5 target board)

I then see the dfu device and dfu-util -l reports the following:


dfu-util 0.10-dev

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Found DFU: [1d50:615d] ver=0000, devnum=3, cfg=1, intf=0, path="4-2", alt=5, name="Security Reg", serial="777777"
Found DFU: [1d50:615d] ver=0000, devnum=3, cfg=1, intf=0, path="4-2", alt=4, name="Security Reg", serial="777777"
Found DFU: [1d50:615d] ver=0000, devnum=3, cfg=1, intf=0, path="4-2", alt=3, name="Security Reg", serial="777777"
Found DFU: [1d50:615d] ver=0000, devnum=3, cfg=1, intf=0, path="4-2", alt=2, name="Bootloader", serial="777777"
Found DFU: [1d50:615d] ver=0000, devnum=3, cfg=1, intf=0, path="4-2", alt=1, name="User Data", serial="777777"
Found DFU: [1d50:615d] ver=0000, devnum=3, cfg=1, intf=0, path="4-2", alt=0, name="User Image", serial="777777"

I can use dfu-util to upload the bootloader *.bit file to the a2 partion, and that completes fine. I then upload my user code to a1 partion, and that also completes fine. (should it be a1 or a0?)

I then unplug my usb(s) and reboot using only the usb that is direct conneced to the FPGA and the bootloader runs.

My problem is that I can't see a way to boot my user code over the same direct connected USB interface. Does the pwr_button have something to do with that? (I had to set it to"0" to get everything working) Please explain what this button does, I don't have a user button on this board ..

Please explain how the bootloader decides what image to boot .. (bootloader at a2, or user image at a1)

Many Thanks for the great code ..

PS: I see to have 32Mb Flash on this colorlight module, but I still have the boardinfo.vh file, SPI_FLASH_SIZE still at 16Mb, is that an issue?

bmentink commented 2 years ago

Update: I added a push button. When the pwr_button io is "0" then I see the DFU devices, with it at '1' I don't see the devices any more, but also the user program does not run ....... ( I have the user program stored in a0)......

If I store the user program in a1, the user program starts on boot, but no way to get to the bootloader any more ...

Very puzzling ..

Update2: Hmmm seems my board has 32Mbit flash not BYTE, so that makes it 4Mbyte! I have adjusted the bardinfo.vh file accordingly ..

I have put the bootloader into A2 partition and the User code into A0 as that is the one at the bootaddr of 0x100000 .. Now the bootloader boots on powerup. The pwr_button does nothing but toggle DFU mode on/off .. still can't run my user image ... what am I missing? Some help would be appreciated .. is the problem related to issue #6 I posted?

bmentink commented 2 years ago

@oskirby
Hi, I was talking with you on the logicbone github.

Regarding what you said on the PROGRAMNpin, is this essential to have access to in order to have multi boot functionality? I ask, because with the Colorlight i5 board, there does not seem to be any access to that pin (W3). .. so at the moment, the resetnpin at top level, is not connected to anything.

oskirby commented 2 years ago

Hey @bmentink the ability to support multiple boot images on the ECP5 family is unfortunately rather crude. But hopefully this might help explain the boot process:

  1. The ECP5 bitstream format includes a "boot address" tag, which normally defaults to zero. This tag tells the FPGA which image should be booted next when a soft reset is encountered. We set this tag using the --bootaddr option when running ecppack
  2. The DFU bootloader is packed with a boot address pointing to the user image, and it should be equal to the value of USERPART_START in boardinfo.vh.
  3. On a POR/hard reset, the FPGA always loads the first image out of the SPI flash, which happens to be the DFU bootloader.
  4. The DFU bootloader checks the status of the boot switch during the first second or so, and takes one of two actions:
    • If the boot switch is depressed, keep running the DFU bootloader, enumerate by USB and handle the DFU command set, or
    • If the boot switch is not pressed within the timeout, it asserts PROGRAMn and triggers a soft reset.
  5. A soft reset clears the FPGA configuration memory, and loads an image out of the SPI flash and whatever address was set by the --bootaddr parameter used to pack the DFU bootloader. Hopefully this is the user image.

This reset logic can be chained as many times as you like. So the user image could set yet another --bootaddr and load a third image from somewhere else in SPI flash. Unfortunately, I couldn't find a way to change the --bootaddr at runtime. So a series of soft resets will always follow a fixed sequence of memory addresses.

The first three partitions in the SPI flash, starting from address zero, are ordered as followed:

  1. DFU bootloader (always the first thing booted).
  2. User bitstream/image (always the second thing booted)
  3. Everything else, which we decided to name "User Data"

However, the normal convention I've observed with the DFU protocol is that alternate mode zero (the default) should normally be the one that the user is expected to work with when updating their board. And for this reason I decided to use -a0 to point to the user image, despite it actually appearing second in the SPI flash. Otherwise a user who forgets to set the alt-mode would risk bricking their board by overwriting the bootloader.

I hope that help clears some things up.

bmentink commented 2 years ago

@oskirby Many Thanks for that, great explanation. Just one point of clarification, by the boot switch to you mean the top level input labeled pwr_button ? Also, if the boot switch is held low during POR will it also load the bootloader image? Or do you have to press it during the timeout?

If the pwr button is indeed the boot switch and is pulled high, then I am not seeing the user code get booted, it stays in bootloader image. Is this because I don't haveresetn tied to PROGRAMN? .. or is it another issue I am having.

If the lack of getting to PROGRAMN is the issue, is there another way to soft reset the FPGA that you know of?

( I have limited schematics for this board. It has been partially reverse-enginerred, but not everything, I suspect I will have to have the board x-rayed, or re-flow the BGA,to see where the W3 pin goes to .. it should in the very least go to a pull-up resistor)

bmentink commented 2 years ago

@oskirby ?

rowanG077 commented 1 year ago

I didn't test this but from reading the Lattice docs the equivalent of toggling PROGRAMN is issueing a JTAG REFRESH instruction. The fabric has access to the JTAG using the JTAGG primitive. This leads me to believe issuing a JTAG REFRESH should always be possible from the fabric. This should allow multi-boot without having access to the PROGRAMN pin.

vk2seb commented 1 year ago

Hey there @oskirby @bmentink - I came across this thread as I am also playing with multiboot on the colorlight i5/i9. I am using a different bootloader BUT I believe I found the PROGRAMN pin on the Colorlight i5 (board rev V7.0). By poking around (at risk of damage of course) I found it is exposed on an external pullup resistor.

R106, the pad closest to the ECP5 part --

image

I verified that it works by flashing 2 images with different LED blinking behaviours, the first one with bootaddr 0x100000 and the second one with bootaddr 0x0, and shorting this pin to ground seemed to switch between the two images correctly - which indicates it is PROGRAMN and not some other kind of reset. You have to be VERY careful shorting it, I found if you don't make good contact it would just restart back into the old image (probably some debouncing / minimum time required)

So in theory a rudimentary hack for the Colorlight i5 could be to add a bodge wire connecting this pad to some other pin. Just thought I'd share this to save someone else some time :)