Forums

CM3/stm32 SCB_VTOR

Started by kristoff April 20, 2016
hi,


I am doing some more work to get to know the STM32F103, now looking in 
the libopencm3 library and learning from the example code.
(using ubuntu as development platform)

I am looking at the demo application to create a usb-over-serial 
interface (usb_cdcacm), as found here:

https://github.com/libopencm3/libopencm3-examples/tree/master/examples/stm32/f1/stm32-maple/usb_cdcacm


When I take this code and modify the pin configuration for the maple 
mini, it works nicely.
However, when I start expanding the application (printing out a rolling 
text), the code does not even initialise the USB bus anymore.

dmesg gives this:
usb 3-3: new full-speed USB device number 110 using xhci_hcd
usb 3-3: device descriptor read/64, error -71


Now, one of the lines of code that I do not understand is this:
	SCB_VTOR = (uint32_t) 0x08005000;

(line 242 of cscacm.c. It is actually the first line of code executed in 
"main").


The VTOR seams to be the vector table offset register, which seams to be 
-if I am correct- the place where the interrupt vector table is stored 
in memory. (i.e. flash)

I am a bit surprised to see this line. Isn't this just some part of the 
memory-layout, so something that that linker must take care of.
(it is the linker that generates the hex-file and puts everything at the 
correct place in flash, no?)


Can somebody explain what is the exact use of this? Why would you need 
to use this in your code? (and what is it doing in this application?)

And do you need to tell the linker how to deal with this?




Cheerio! Kr. Bonne
On 2016-04-20, kristoff <kristoff@skypro.be> wrote:
> hi, > > I am doing some more work to get to know the STM32F103, now looking in > the libopencm3 library and learning from the example code. > (using ubuntu as development platform) > > I am looking at the demo application to create a usb-over-serial > interface (usb_cdcacm), as found here: > > https://github.com/libopencm3/libopencm3-examples/tree/master/examples/stm32/f1/stm32-maple/usb_cdcacm > > When I take this code and modify the pin configuration for the maple > mini, it works nicely. > However, when I start expanding the application (printing out a rolling > text), the code does not even initialise the USB bus anymore. > > dmesg gives this: > usb 3-3: new full-speed USB device number 110 using xhci_hcd > usb 3-3: device descriptor read/64, error -71 > > Now, one of the lines of code that I do not understand is this: > SCB_VTOR = (uint32_t) 0x08005000; > > (line 242 of cscacm.c. It is actually the first line of code executed in > "main"). > > The VTOR seams to be the vector table offset register, which seams to be > -if I am correct- the place where the interrupt vector table is stored > in memory. (i.e. flash) > > I am a bit surprised to see this line. Isn't this just some part of the > memory-layout, so something that that linker must take care of. > (it is the linker that generates the hex-file and puts everything at the > correct place in flash, no?) > > Can somebody explain what is the exact use of this? Why would you need > to use this in your code? (and what is it doing in this application?) > > And do you need to tell the linker how to deal with this? >
[Disclaimer: I've never used this exact MCU, so my comments are somewhat generic based on how I've handled this problem in the past.] When the vector table base can be moved around, the CPU does not have access to your linker script so you need to tell it which part of your program is the vector table. In that context, the above line of code is reasonable and required. What is unreasonable, IMHO, is that it's a hardcoded numerical constant. That vector base should be defined in the linker script in a variable and the variable exported so that the program can use it without having to hardcode a number. However, if you wish to continue with a hard coded value, then generate a linker map and make sure that your vector table base is at the expected address. If it's moved, then make sure any alignment requirements are taken care of and use the new address in the above code. Also, if the vector table is copied by the startup code to RAM, then make sure you choose the correct (RAM) address for the above register. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
Hi Simon,



On 20-04-16 20:04, Simon Clubley wrote:
>> I am looking at the demo application to create a usb-over-serial >> interface (usb_cdcacm), as found here: >> https://github.com/libopencm3/libopencm3-examples/tree/master/examples/stm32/f1/stm32-maple/usb_cdcacm
(...)
>> Now, one of the lines of code that I do not understand is this: >> SCB_VTOR = (uint32_t) 0x08005000; >> (line 242 of cscacm.c. It is actually the first line of code executed in >> "main"). >> The VTOR seams to be the vector table offset register, which seams to be >> -if I am correct- the place where the interrupt vector table is stored >> in memory. (i.e. flash)
(...)
>> Can somebody explain what is the exact use of this? Why would you need >> to use this in your code? (and what is it doing in this application?)
> [Disclaimer: I've never used this exact MCU, so my comments are somewhat > generic based on how I've handled this problem in the past.]
no problem :-)
> When the vector table base can be moved around, the CPU does not have > access to your linker script so you need to tell it which part of your > program is the vector table. > In that context, the above line of code is reasonable and required.
I'm not really such an expert in gcc or the process behind it, but I did think this part of the code is executed before you even start the code in the "main" function. I try to understand how this really works. How is this implemented in an application that uses interrupts-service routine without this line of code? I understand that the vector lookup table contains the address of the interrupt service routines, so ... - either the lookup-table is copied to RAM so it can be modified at any point in the future. - or the compiler first scans the code for all interrupt related code, creates the vector table beforehand and stores it in flash. Setting the VTOR seams to be related to USB as all examples in libopencm3-examples that set this value in the code are related to USB. (or they just happen to written by the same person and this is related to the author, not the MPU :-) ) So perhaps there is something in the way USB is done that is different from other peripherals. BTW. When tracing this with gdb, I found out that the program -execution is restarted from "main" after this line of code. (...)
> However, if you wish to continue with a hard coded value, then generate > a linker map and make sure that your vector table base is at the expected > address. ...
Hihi .. I do not "wish" to do anything at this point. I would already be happy to understand what is going on. (and I would also be happy to understand why the USB device does not initialise in my code, just adding a buffer of 60 octets seams to be enough to cause this behaviour. :-(
> Simon.
Cheerio! Kr. Bonne
On 2016-04-21, kristoff <kristoff@skypro.be> wrote:
> Hi Simon, > > On 20-04-16 20:04, Simon Clubley wrote: > >> [Disclaimer: I've never used this exact MCU, so my comments are somewhat >> generic based on how I've handled this problem in the past.] > no problem :-) > >> When the vector table base can be moved around, the CPU does not have >> access to your linker script so you need to tell it which part of your >> program is the vector table. >> In that context, the above line of code is reasonable and required. > > I'm not really such an expert in gcc or the process behind it, but I did > think this part of the code is executed before you even start the code > in the "main" function. >
It depends on the startup code. If the startup code has provision for setting a custom base address based on a definition in the linker, then the startup code can do it for you. If not, then you have to do it in the program.
> > I try to understand how this really works. > > How is this implemented in an application that uses interrupts-service > routine without this line of code? >
It depends on the MCU and the toolchain/startup code. If the MCU has a usable default value for the register, then the linker script (maybe with some help from the startup code) can just place the vector table at that default address itself. If the default value is not usable, then _something_ has to setup the register. Note that these issues are different from the ones around actually setting up the _contents_ of the vector table itself. Some toolchains help this latter process along by using extensions to the language in use (usually C) to define the vector in the source code as part of the function definition; others may require you to use a specific name for the interrupt handler so the default definition can be overridden at link time. Still others may require you to place an entry in the vector table at run time.
> I understand that the vector lookup table contains the address of the > interrupt service routines, so ... > - either the lookup-table is copied to RAM so it can be modified at any > point in the future. > - or the compiler first scans the code for all interrupt related code, > creates the vector table beforehand and stores it in flash. >
Sometimes the compiler has extensions to the function definition syntax to support this, but the rest of the time this is a linker function along with maybe some support from the startup code.
> > Setting the VTOR seams to be related to USB as all examples in > libopencm3-examples that set this value in the code are related to USB. > (or they just happen to written by the same person and this is related > to the author, not the MPU :-) ) > > So perhaps there is something in the way USB is done that is different > from other peripherals. >
Unlikely. The other peripherals may just be running in polled mode. USB certainly has additional and unique requirements, but at this level it's just the same as defining an interrupt handler for (say) a serial port.
> >> However, if you wish to continue with a hard coded value, then generate >> a linker map and make sure that your vector table base is at the expected >> address. ... > > Hihi .. I do not "wish" to do anything at this point. I would already be > happy to understand what is going on. > > (and I would also be happy to understand why the USB device does not > initialise in my code, just adding a buffer of 60 octets seams to be > enough to cause this behaviour. :-( >
This may be because the additional space required by your new code has caused the linker to move the vector table to a new address and your hard coded address no longer reflects reality. I have not been much help above because I don't know your specific MCU, I don't know how you are using gcc to define and write your interrupt handlers and I don't know the capabilities of your startup code. However, I can give you one very specific piece of advice. Get the linker to generate a linker map and look at where your vector table base is currently in your new version of the code. If it's different from the hard coded address, then make sure that the vector table base is correctly aligned to meet the MCU requirements and then insert the new value into your source code. As I've mentioned however, a longer term solution is to define a symbol in the linker script which contains the base address of your vector table and which you can use in your source code. The answers above are pretty generic and I think I've reached the limit of the generic advice I can give you, but I hope some of the above helps you fix your problem. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
kristoff <kristoff@skypro.be> wrote:
> hi, > > > I am doing some more work to get to know the STM32F103, now looking in > the libopencm3 library and learning from the example code. > (using ubuntu as development platform) > > I am looking at the demo application to create a usb-over-serial > interface (usb_cdcacm), as found here: > > https://github.com/libopencm3/libopencm3-examples/tree/master/examples/stm32/f1/stm32-maple/usb_cdcacm > > > When I take this code and modify the pin configuration for the maple > mini, it works nicely. > However, when I start expanding the application (printing out a rolling > text), the code does not even initialise the USB bus anymore. > > dmesg gives this: > usb 3-3: new full-speed USB device number 110 using xhci_hcd > usb 3-3: device descriptor read/64, error -71 > > > Now, one of the lines of code that I do not understand is this: > SCB_VTOR = (uint32_t) 0x08005000; > > (line 242 of cscacm.c. It is actually the first line of code executed in > "main"). > > > The VTOR seams to be the vector table offset register, which seams to be > -if I am correct- the place where the interrupt vector table is stored > in memory. (i.e. flash) > > I am a bit surprised to see this line. Isn't this just some part of the > memory-layout, so something that that linker must take care of. > (it is the linker that generates the hex-file and puts everything at the > correct place in flash, no?) > > > Can somebody explain what is the exact use of this? Why would you need > to use this in your code? (and what is it doing in this application?) > > And do you need to tell the linker how to deal with this?
This line is specific to using Maple Mini. The first 20k of flash is reserved for Maple bootloader. Since flash starts at 0x08000000 the first address in user program is 0x08005000. Linker script for libmaple links user programs in such a way that interrupt vertors are at start of user program, that is 0x08005000. However, at runtime processor needs to know location of interrupt vectors. To tell processor where to find interrupt vectors you do assignmnet to SCB_VTOR. In principle linker script could inject a snippet of code to do this. However, normally linker scripts just decide which code should be included and where but do not inject new code. So doing this in linker script would be confusing. In other words this line is expected for your environment. P.S. I am using Maple Mini, libmaple and libopecm3 but did not look at the usb example, so can not say anything about your main problem. -- Waldek Hebisch
Hi Waldek,

(and also thanks to Simon) :-)


On 22-04-16 17:17, Waldek Hebisch wrote:
>> Now, one of the lines of code that I do not understand is this: >> SCB_VTOR = (uint32_t) 0x08005000;
>> (line 242 of cscacm.c. It is actually the first line of code executed in >> "main").
>> The VTOR seams to be the vector table offset register, which seams to be >> -if I am correct- the place where the interrupt vector table is stored >> in memory. (i.e. flash)
> This line is specific to using Maple Mini. The first 20k of > flash is reserved for Maple bootloader. Since flash starts > at 0x08000000 the first address in user program is 0x08005000. > Linker script for libmaple links user programs in such a > way that interrupt vertors are at start of user program, that > is 0x08005000. However, at runtime processor needs to know > location of interrupt vectors. To tell processor where to > find interrupt vectors you do assignmnet to SCB_VTOR.
Yes, that is it. As mentioned in my message to Simon, at first, I thought this was related to the fact that this was code accessing the USB interface, but after checking again, it is indeed more related to the maple (mini) and its bootloader. This SCB-VTOR is found in all the examples for the maple mini and that use interrupts! In fact, a couple of days ago, I had been playing around with a version of "blinky" that uses timer interrupts for "wait_ms" and I noticed that it did work when flashing the device with jtag, but not when using the boot-loader. Now I know why :-) I thought that the "rom (rx) : ORIGIN = 0x08002000, LENGTH = 120K" line in the linkerscript I used would have fixed everything. (I use the stm32duino bootloader which uses only 8 K instead of 20K)
> P.S. I am using Maple Mini, libmaple and libopecm3 but > did not look at the usb example, so can not say anything > about your main problem.
For the time being, I'm using the UART instead. I like the maple mini as it is easy in a breadboard. I like libopencm3 because it runs from a cli. Cheerio! Kr. Bonne.