cancel
Showing results for 
Search instead for 
Did you mean: 

Run code in SRAM in ASM

knabbers
Associate II
Posted on January 05, 2012 at 04:46

Hi, I'm quite new to the STM32 and am trying to run code in internal SRAM, but cannot find a lot of information about it.

This is what I have been doing:

1) Copy code block to SRAM (with LDR and STR) starting at 0x2000 0000

2) Branch to 0x2000 0000

The code should have turned on an LED, but it didn't. No interrupts will fire after reset, so I don't know why/if I need to copy the interrupt vector table as well as the code. I'm quite sure that the code block is copying correctly, as I have checked it in debug mode.

I am not using Keil software, so must program it directly in ASM. I would really appreciate some help on this one, as it seems to be the make-or-break thing needed to get my camera working.

Thanks in advance, James

#code-execute-sram
11 REPLIES 11
Posted on January 05, 2012 at 15:29

As it is 16-bit Thumb code, you'll need to jump to an ODD address, ie 0x20000001, otherwise the process will Hard Fault, it cannot run 32-bit ARM code.

It is not clear why you need to run it from RAM, or if you plan to have some code that interrupts, or not. You would generally want the vector table at the front, but it could remain in FLASH, and the stack at the end.

Consider GCC based compilers, there is/was a WinARM package setup for the STM32F1xx

Anyway, the copy/call method should work.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
infoinfo989
Associate III
Posted on January 05, 2012 at 16:48

As Clive said, what you're doing should work. As part of a bootloader we copy code to RAM and execute it from there.

Writing assembler can get old fast. For free GCC-based C compilers take a look at Yagarto or Code Sourcery (Mentor) G++ lite.

knabbers
Associate II
Posted on January 05, 2012 at 19:59

Thank you for your help - adding 1 to the SRAM branch address worked perfectly! However, I was also able to execute 32 bit instructions in the SRAM?!

The code I am working on is extremely time-sensitive, because my camera is quite high resolution. This is why I choose assembly over C (which has also been set up).

Do you know how to branch back to flash at a particular point from SRAM? Does the address offset have to be calculated manually (i.e. FLASH_LABEL - SRAM_BRANCH_INSTRUCTION)?

I am not using any interrupts. Code needs to run from SRAM because of the unpredictable timing behaviour of the flash prefetch buffer when branching (something to do with the alignment - but it seems quite random from my testing). From what I have heard, the SRAM can run code at 72MHz directly, so the branch timing should be constant. I will test that now.

Do you know if timer overflow interrupts have a deterministic latency between when the overflow actually happens and when the ISR code begins? This cannot be tested with the SYSTICK timer. If so, nothing would need to be run from SRAM.

Posted on January 05, 2012 at 21:24

Thumb(1/2) instructions that take two 16-bit words, aren't really classified as 32-bit ARM instructions, the Cortex-M3 cannot execute classic ARM 32-bit code.

Yes, running from SRAM should be single cycle (ie zero wait states), doing it out of flash exposes you to prefetch and flash-line issues, as well as pulling literal constants. The STM32F4 series has better abstraction of the flash, but the array is still running at ~42 ns. Interrupts get clouded by the tail-chaining, and push/pop multiple. Memory access gets clouded by DMA, crossing buses, and write buffers. For observing cycle level performance, use the core counter present within the trace unit included on the STM

// From http://forums.arm.com/index.php?showtopic=13949
volatile unsigned int *DWT_CYCCNT = (volatile unsigned int *)0xE0001004; //address of the register
volatile unsigned int *DWT_CONTROL = (volatile unsigned int *)0xE0001000; //address of the register
volatile unsigned int *SCB_DEMCR = (volatile unsigned int *)0xE000EDFC; //address of the register
*SCB_DEMCR = *SCB_DEMCR | 0x01000000;
*DWT_CYCCNT = 0; // reset the counter
*DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter
#define STOPWATCH_START { cyc[0] = *DWT_CYCCNT;}
#define STOPWATCH_STOP { cyc[1] = *DWT_CYCCNT; cyc[1] = cyc[1] - cyc[0]; }

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on January 06, 2012 at 00:07

''I am not using Keil software, so must program it directly in ASM''

 

That is a complete non-sequitur - there are very many high-level language providers other than just Keil!

eg,

http://www.st.com/internet/com/software/debug_tools.jsp

knabbers
Associate II
Posted on January 07, 2012 at 03:33

Yes OK, I meant that I need to actually program code, rather than just select 'run from SRAM' in so-and-so menu! I know that there is more than Keil out there :)

Posted on January 07, 2012 at 05:20

Do you know how to branch back to flash at a particular point from SRAM? Does the address offset have to be calculated manually (i.e. FLASH_LABEL - SRAM_BRANCH_INSTRUCTION)?

 

Well, I'd probably encode it with a literal register load with the address I wanted to go to and then branch to the register. You could also use the classic LR methods, or load things from a vector table. To be flexible you'll want to use the absolute addresses of the external routines you want to be calling.

The other thing to remember when copying stuff to RAM, is to flush the literal pool, and be sure that it is included in the data copied to RAM as it will be accessed in a PC relative fashion by the code.

Typically you'd also want to make sure you don't call outside functions, just those within the region you are copying.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mike-davies
Associate II
Posted on July 31, 2012 at 13:19

Hi Clive,

''The other thing to remember when copying stuff to RAM, is to flush the literal pool...''

Can you tell me what you mean by this ?  I know the literal pools are literal constants accessed during execution and that they may appear after the function return statement, but how do you flush them ?  I thought they were accessed relative to the PC so surely any jump or call has the effect of reloading the effective literal pool pointer ?

Thanks,

Mike

Posted on July 31, 2012 at 13:44

In ARM assembler this is normally done with LTORG

Literals don't always get placed at the end of a subroutine, the PC relative encoding has a limited scope, and there is a opportunity to fold those that are repeated. When marking areas of assembler for moving to RAM I'd use a LTORG before I start, and then place an end-of-scope marker after a final LTORG. You could perhaps park all this code/literals in a different section/segment, but I typically just in-line it so I don't have to worry where the heck the linker will place it.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..