F4 DFU bootloader

Post here first, or if you can't find a relevant section!
User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Sun Aug 27, 2017 2:14 am

Victor.

My mistake

I flashed the old version.

One thing however... Its not jumping to the sketch code when DFU is finished.

Is there any way we can get it to do that ?

I did try the -R option to reset, but that didn't help

User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Sun Aug 27, 2017 11:32 am

Victor

FYI.

I looked at resetting the board when the Magic sequence is received via Serial USB, but the F4 USB code is completely different to the code in the F1 :-(

The F1 has a "rxhook" which is configured so that the rxhook() function in usb_serial.c is called each time something arrives via CDCACM
But the F4 doesnt have this, and the CDCACM files look a lot different to the F1 versions.

So I didnt implement that functionality yet.

I did try to see if the reset code from the F1 would work with the F1, but again, the F4 underlying code does not have the nvic reset function etc.
I tried taking the assembler code from the F1 and parts of the F1 nvic reset code e.g. from scb.h etc, and put it in a sketch, but as far as I could tell, the F1 code would not work correctly on the F4.

However its a bit difficult to know if it had jumped to the bootloader or not, as the bootloader does not currently flash the LED.

I tried toggling USB DP using the normal code, before running the reset code, but the bootloader did not seem to respond to dfu-util.

Anyway, if its any use to you, here is my attempt at a sketch that would reset the MCU

(On my board. PB0 is the LED, so the code flashes the led 10 times, then calls reset_to_bootloader

reset_to_bootloader() drives PA12 (USB DP) low and then sets it back to floating, in the hope that when the board resets, the USB Host PC, notices that the STM DFU device has now appeared.


Code: Select all

void setup() {
  // put your setup code here, to run once:
pinMode(PB0,OUTPUT);
}
int c=0;

#include <libmaple/scb.h>
#define SCB_AIRCR_VECTKEYSTAT           (0x5FA << 16)
#define SCB_AIRCR_VECTKEY               (0x5FA << 16)
#define SCB_AIRCR_ENDIANNESS            (1U << 15)
#define SCB_AIRCR_PRIGROUP              (0x3 << 8)
#define SCB_AIRCR_SYSRESETREQ           (1U << 2)
#define SCB_AIRCR_VECTCLRACTIVE         (1U << 1)
#define SCB_AIRCR_VECTRESET             (1U << 0)
void nvic_sys_reset() {
    uint32 prigroup = SCB_BASE->AIRCR & SCB_AIRCR_PRIGROUP;
    SCB_BASE->AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ | prigroup;
    asm volatile("dsb");
    while (1)
        ;
}

#define RESET_DELAY 100000
static void wait_reset(void) {
  delay_us(RESET_DELAY);
  nvic_sys_reset();
}
#define STACK_TOP 0x20000800
#define EXC_RETURN 0xFFFFFFF9
#define DEFAULT_CPSR 0x61000000
void reset_to_bootloader()
{
      //Reset the USB interface on generic boards - developed by Victor PV
      pinMode(PA12,OUTPUT);
      digitalWrite(PA12,LOW);
      
      for(volatile unsigned int i=0;i<512;i++);// Only small delay seems to be needed, and USB pins will get configured in Serial.begin
      pinMode(PA12,INPUT_FLOATING);
  
             uintptr_t target = (uintptr_t)wait_reset | 0x1;
            asm volatile("mov r0, %[stack_top]      \n\t" // Reset stack
                         "mov sp, r0                \n\t"
                         "mov r0, #1                \n\t"
                         "mov r1, %[target_addr]    \n\t"
                         "mov r2, %[cpsr]           \n\t"
                         "push {r2}                 \n\t" // Fake xPSR
                         "push {r1}                 \n\t" // PC target addr
                         "push {r0}                 \n\t" // Fake LR
                         "push {r0}                 \n\t" // Fake R12
                         "push {r0}                 \n\t" // Fake R3
                         "push {r0}                 \n\t" // Fake R2
                         "push {r0}                 \n\t" // Fake R1
                         "push {r0}                 \n\t" // Fake R0
                         "mov lr, %[exc_return]     \n\t"
                         "bx lr"
                         :
                         : [stack_top] "r" (STACK_TOP),
                           [target_addr] "r" (target),
                           [exc_return] "r" (EXC_RETURN),
                           [cpsr] "r" (DEFAULT_CPSR)
                         : "r0", "r1", "r2");
}


#define DELAY 250
void loop() {
  // put your main code here, to run repeatedly:
  for(int i=0;i<10;i++)
  {
    digitalWrite(PB0,HIGH);
    delay(DELAY);
    digitalWrite(PB0,LOW);
    delay(DELAY);
  }
  reset_to_bootloader();
}
Anyway. Basically the code is just taken from the F1, so I'm not sure if I should expect it to work on the F4 really.....

victor_pv
Posts: 1750
Joined: Mon Apr 27, 2015 12:12 pm

Re: F4 DFU bootloader

Post by victor_pv » Sun Aug 27, 2017 6:35 pm

I uploaded my fork here:
https://github.com/victorpv/F4Bootloader

This is the one that generates the BIN attached in the first post.
I compiled it using the latest Systemworkbench available from openstm32.org

The LED and button pins are defined in /inc/main.h

I probably wont have time to work on this any more today. If I get to do any progress I'll updating it.

User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Sun Aug 27, 2017 9:34 pm

Thanks

I will download System Workbench and see if I can build it

BTW.

The newer dfu-util which works with that bootloader does not seem to work correctly with the Maple bootloader.
It gets an error at the end.

So until I have time to fix the Maple bootloader we can't simply switch to using the new dfu-util for both F4 and F1

I think I will also look at STM32GENERIC and STMs F4 cores, because LibMaple F4 is lacking in a lot of features and I don't have time to update it :-(

User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Sun Aug 27, 2017 10:46 pm

Victor

Which version of gcc are you using to compile the bootloader?

I'm getting errors on many of the files e.g. main.c gpio.c and some of the HAL files.

Apart from Arduino I'm running gcc 5.4, (as I need this for other MCU's), but I'll try configuring so that it uses the same compiler as the Arduino IDE, (which I think is V 4.8)

Edit.

I removed gcc 5.4 from my main PATH and it now compiles OK

User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Mon Aug 28, 2017 12:01 am

Victor

I probably won't have time to try this until tonight, but looking at the code it has

Code: Select all

			  if(dfuActive(&USBD_Device) == 0) {
				  loops--;
			  }

I wonder if its possible to tell when DFU is complete, so we can jump on completion to the application (sketch)

I must admit, I've never really understood how the Maple bootloader does this.

But my best guess is that the Maple bootloader waits for a DFU reset rather than knowing when DFU is complete. However in that case, it would need to go though the same code that waits for DFU to start (for about a second) before jumping to the application code because DFU has not started.

In which case I don't see why they don't simply use the Reset command as a trigger to jump to the application code

Or perhaps DFU has some way to know when an upload is complete ? and as we only ever upload one binary per transaction, we may be able to cheat and always jump to the application code when an upload is complete.

User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Mon Aug 28, 2017 11:12 am

Victor

FYI.

I recompiled it to flash the LED that is on my board (PB0), and eventually got it to work, but for a while it would not flash because PORTB had not been init'ed at all.

The code should probably init all the ports or have a #ifdef to make sure the button and led port are fully init'ed, not just the pins

I also shorted the delay, but I think more work needs to be done with that, as there is no need to wait after upload is finished.

I thought the DFU was supposed to reset the bootloader after its complete, and there seems to be some code which is using the Backup resister to determine if its in DFU or not, but I don't know if thats the standard code from STM or something Chris added.

I have also been looking at integration with STM32Generic, as it already has the code for the F1 to reboot based on the magic sequence via USB Serial.
The problem however is the maple_loader.jar is not designed to work with the new version of dfu-util which needs the -s argument.
(Well I'm not sure if it is or not, as some of the comments metion commands for DFUse, but its not clear if thats the -s or something else

Either way the Maple bootloader and this F4 bootloader can't both use the newer dfu-util that supports DFUse, so we'd need a separate bat file / script for the f4_loader which calls into the new dfu-util sub folder.

We also need a way to send the magic sequence, but I think that perhaps this can just be done in a bat file or a linux script etc

e.g.

Code: Select all

ECHO "MAGIC" > \\.\com155:
Magic are hex codes, but I found an example bat file to generate chars from hex codes

Code: Select all

@echo off
setlocal

::Define a Linefeed variable
set LF=^


::above 2 blank lines are critical - do not remove.

::Create a TAB variable
call :hexprint "0x09" TAB

::Print a string with encoded TABs
call :hexprint "A0x09B0x09C"

::Create a string variable with encoded TABs
call :hexprint "A0x09B0x09C" var
set var

exit /b

:hexPrint  string  [rtnVar]
  for /f eol^=^%LF%%LF%^ delims^= %%A in (
    'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(%~1"'
  ) do if "%~2" neq "" (set %~2=%%A) else echo(%%A
exit /b

So perhaps we can combine the two and send the necessary hex codes for the magic number to the serial port and then run dfu-util




Anyway, I don't have any more time today, but I'll take a look at this tomorrow

victor_pv
Posts: 1750
Joined: Mon Apr 27, 2015 12:12 pm

Re: F4 DFU bootloader

Post by victor_pv » Mon Aug 28, 2017 3:15 pm

About GPIO, I think we should make it initialize the ports used by the button and the LED, but not all GPIO, since that would just increase the RAM and Flash needs for no benefit, I'd rather try to keep it small.

About checking the Backup registers, it's something Chriss added. That's part of the Generic core. I believe Daniel use the same registers with the same values that we use on the F1 bootloader. That's something you added and I didn't look at it in detail, so I can't confirm it they work exactly the same, but I believe they do since Daniel added it to his core to work with the F1 bootloader.

DFU has a reset command, but I think it doesn't work. Agreed we need to get that part working better, either manage the reset command, or reboot on uploaded completed it that can be confirmed at runtime, thru some parameters of the DFU transfer like the size of the upload, or perhaps the DFU tool sends something else once the upload is supposedly completed.

One other thing I noticed, is that I believe the code doesn't provide any protection for not overwriting the bootloader page. Instead it relies on the DFU tool to not try to write to that page. Since dfu-util seems to first check what pages are writable and only request that, at the moment it seems to work, but I think it's a risk that a tool may request to write to the bootloader page and the bootloader would allow it. I need to read more the code to confirm that, but is my suspicion so far.

One more thing I have been thinking about.
We could integrate presenting the device as a Flash media to the computer, and accept writing a file, similar to what the nucleo boards do. Needs additional code, and would grow the bootloader, possibly taking another 16KB, but seems like a nice feature given the most common F4 have enough flash.

User avatar
RogerClark
Posts: 7498
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: F4 DFU bootloader

Post by RogerClark » Mon Aug 28, 2017 10:14 pm

Thanks Victor

The backup register code is not really used in the Maple bootloader. Someone sent it to me, and I thought I may as well include it, but it also needed a change to the LibMaple core to set those registers prior to resetting - and I did not change LibMaple because from what I recall some people had concerns about the sketch using the backup registers ( as I think they used them in their sketch)- though I can't remember the details.

Re:Bootloader overwriting its self

Yes. That needs to be fixed.

Found this interesting page on DfuSe

http://dfu-util.sourceforge.net/dfuse.html


Re:jump to sketch after upload

I think somehow the built in bootloader runs the code (from 0x8000000) after I upload the bootloader ( as I upload it via its own internal dfu )

So it looks like there is a process to do this.

It could be the Reset command sent from the host that does this, but it needs more investigation.
If this is the case, I thought we could potentially use the Backup register so that when a reset command was received, it sets a flag value, and the MCU is reset.
Then we can add code at the very start of the bootloader, that checks for this flag, and jumps immediately to the sketch

PS.
I think the Backup reg flag values that are in the bootloader are different to the values in the Maple bootloader, which is why I wondered if they were part of the STM code rather than something Chris added

PPS.

It's strange if Chris wrote the bootloader to work with STM32GENERIC , because you can't build the F4 version of STM32GENERIC with an vector offset, and I have asked Daniel about this, and will submit a PR to fix this.

But strangely there is an unused linker file for the Black F4 board with offset at 0x4000, already in the repo

victor_pv
Posts: 1750
Joined: Mon Apr 27, 2015 12:12 pm

Re: F4 DFU bootloader

Post by victor_pv » Tue Aug 29, 2017 2:23 am

The 0x4000 offset if I remember right is for Aeroquad, but I do not know what kind of bootloader they used. I tried to find the source but was not able to.
Chriss offset is actually at 0x10000, which is the space reserved by STM example code. He didn't change that part.

Chriss mentioned he wrote it for STM32GENERIC. He may have modified the core enough to fit his need. The bootloader is mostly unmodified from STM example except that it jumps to the user code after a timeout period, and I believe I didn't see that in STM code.

I'm not 100 sure, but wouldn't the STM32GENERIC load the VTOR redirect with whatever base address the sketch was compiled for? in that case if the linker script is modified to link in say 08004000, wouldn't it load that address to the VTOR?
Otherwise he may have modified his copy of the core. In any case form what I saw in another thread, you have already identified what part of the core to change to allow for different offsets.

I think the parts we need to confirm/modify are:
1.- Ensure the bootloader doesn't wipe itself (by mass erase of by writing to it's page)
2.- Confirm it jumps to the sketch right after upload.
3.- Check and confirm if it supports the Reset command, and in that case that it will jump to the sketch.

Perhaps rather than use the backup registers, we could reserve the first word in RAM for a magic word.
We can do that thru the linker scripts, so variables, heap, etc start 1 word up. I don't think that would hurt anything, and it will be a word that no one ever should try to modify, since if it was not reserved it wouild belong to a variable. We can do that thru the linker scripts, and only access it with a pointer to that address.

Post Reply