CM3/stm32 SCB_VTOR

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:

formatting link

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

Reply to
kristoff
Loading thread data ...

[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
Reply to
Simon Clubley

Hi Simon,

(...)

(...)

no problem :-)

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.

(...)

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. :-(

Cheerio! Kr. Bonne

Reply to
kristoff

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.

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.

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.

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.

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
Reply to
Simon Clubley

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
Reply to
Waldek Hebisch

Hi Waldek,

(and also thanks to Simon) :-)

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)

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.

Reply to
kristoff

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.