Hi again
I am still struggling with debugging my code in DDR3 memory using the Keil uLink2. I have created a .ini file for the uLink2 to execute which initializes the DDR3 for use, load my code to DDR3, and attempts to change the pc and sp to that memory. The last thing is not working. the code definitely is in the DDR3 memory OK. Below is the the script function.
The code was linked to 0xA0000000
This is a microsemi cortex m3 FPGA arm.
Can anyone see a reason why this would not work? or why the uLink2 jumps away to undefined memory space? I assume people have debugged out of external RAM before
Thanks :-)
FUNC void SetupPC_SP (void) { // Stack pointer and vector table offset // registers are set to 0x00000000 // Setup Stack Pointer SP = _RDWORD(0xA0000000); // Setup Stack Pointer // Program Counter is set to (0x00000004 -1) PC = _RDWORD(0xA0000004-1); // Setup Program Counter // change vector table address to 0 _WDWORD(0xE000ED08, 0x0); // 0xE000ED08 SCB->VTOR = readonly_region_base; }
I am new to ARM but it seemed to me to be a read command and I thought this was incorrect as well BUT I have seen it in many examples so for whatever reason I believed it was correct to use. I have no idea why it was posted this way in 3 or 4 different websites.
What you wrote seems correct to me and I will try it this morning.
Ok, I have tried this several different ways in the script file and nothing is written to either the PC or SP and the vct does not look right either
Is there some potential for the core to execute some other code? What's initially mapped at zero? I have a bootloader that is in nvm that copies my main code from a external flash into DDR memory starting at 0xA0000000. The map shows that all of the code is there and if I load it with the uLink2 I can see it in the memory window and it looks correct. vector table followed by my main.
What I am trying to do now is to debug the code in DDR using the uLink2. Kiel support told me to create a startup.ini script that is run before the debugger does anything that will initialize the DDR so it can be access, and set the PC/SP/vector base address to the start of DDR (0xA000000).
The init of DDR is working but I cannot get any of the registers to change. Also if I force the PC and SP registers to what I want them to be, the disassembly window shows me main with the proper instructions. If I do one assembly step it jumps to some address in nvm instead of the next address.
I don't understand why the uLink2/ARM is doing this if the PC is set correctly.
I need to debug my code in DDR because it is too big to fit into nvm.
FUNC void SetupPC_SP (void) { // Stack pointer and vector table offset // registers are set to 0x00000000 // Setup Stack Pointer WDWORD (SP, 0x20003000); // Setup Stack Pointer // Program Counter is set WDWORD (PC, 0xA0000004); // Setup Program Counter // change vector table address _WDWORD(0xE000ED08, 0xA0000000); // 0xE000ED08 SCB->VTOR = readonly_region_base; } OR FUNC void SetupPC_SP (void) { // Stack pointer and vector table offset // registers are set to 0x00000000 // Setup Stack Pointer SP = 0x20003000; // Setup Stack Pointer // Program Counter is set PC = 0xA0000004; // Setup Program Counter // change vector table address to 0 _WDWORD(0xE000ED08, 0xA0000000); // 0xE000ED08 SCB->VTOR = readonly_region_base; }
I finally believe I got DDR initialization, DDR remapping and the PC/SP working. I had to remove the FUNC's because the calls to them were getting errors that they did not exist. So I just have the entire startup.ini script execute from top to bottom and it works.
My DDR now shows up at address 0 which is pretty cool and the PC is getting the value I want.
But I am not out of the woods on this yet.
The last thing the script does is a LOAD command. The debugger does spend quite some time loading something big (big blue bar at bottom) but ends with a memory mismatch and exits.
What I found out is that the mismatch was between the value being loaded and what was in the DDR before remapping. The memory windows shows the proper value at address 0 but the LOAD command somehow match against the value address 0xA0000000 instead. That value should have been overwritten because it was remapped to address 0.
Its like the uLink2 was confused by the remap and wrote the code somewhere else but not at address 0.
Any ideas as to what might be going on with the LOAD command?
Hi
I have finally gotten to the point in my bootloader where I have loaded my code into the DDR from external Flash and need to remap and jump to the start address. Pre the Microsemi cortex M3 ARM app notes this is the order that the registers are to be set BUT once the remapping happens the two instructions to load the SP and PC registers cannot be executed because the memory is no longer there to fetch them. Its DDR.
Has anyone does this successfully somehow?
__asm void BootJump(INT32U firmwareStartAddress) { MOVS R1, #0x0 STR R1, [R0,#0x0] ; SYSREG->ESRAM_CR = 0u STR R1, [R0,#0x10] ; SYSREG->ENVM_REMAP_BASE_CR = 0u MOVS R2, #0x01 ; SYSREG->DDR_CR = 1u STR R2, [R0,#0x08] ; Remap DDR to address 0 LDR SP, [R1] ;Load new stack pointer address LDR PC, [R1, #4] ;Load new program counter address } void Jump_to_executable(void) { SCB->VTOR = 0x00000000; BootJump((INT32U)0x40038000); }
So is the boot loader in SRAM? Is it accessible from a shadow/normal address (0x20000000 ?) and is that currently mapped at ZERO? If it's at zero then you'd probably want to transfer control to the higher address code before you remap the memory underneath yourself.
// Program Counter is set PC = 0xA0000004; // Setup Program Counter
You're NOT trying to set the PC to 0xA0000004, you're trying to read the address stored in the vector here. The address stored there is for the Reset Handler, probably something like 0xA0000199, and ODD address because the code will be 16-bit THUMB code.
the bootloader is running in nvm and it copies from a external SPI flash into DDR memory my main code that is has been linked to run starting at address 0.
Microsemi's implementation of the cortex M3 ARM will only allow code to be executed from lower memory 0-0x20000000. So the DDR must be remapped on top of my bootloader in nvm.
That is why the last lines are to change the sp and pc to the reset handler of the code in the DDR memory that has been remapped to 0 (it no longer is at address 0xA0000000).
The problem I am finding is that the last two instructions that load the sp and pc registers cannot be fetched from program memory because the memory has changed due to remapping to the DDR memory so those do not get executed. Its not possible.
I just don't see how this could ever work. It is how the flowchart and description mircosemi's app note on remapping says to do it.
I suppose as you suggested if I copied my bootloader into esram and it executed from there then it would stay out of the way of the reampped memory and be able to execute without getting clobbered.
Yes I do know that I am want to read the address for the new reset handler (from code in the ddr memory) from address 0x4 (the address will be in lower memory) and jumping to that. Same for the initial stack pointer.
So from what you said which is a good idea and NOT mentioned in the app note at all is to have the bootloader run out of esram instead of nvm. I will need to read up on how to do that. I have always run out of nvm starting at address 0.
I assume the sequence is that the memory initially mapped to 0x6000000 (nvm) and the startup code runs and then the scatter loader (?) copies/decompresses my bootloader code into esram.
Is there another way for the bootloader to get into the esram?
I cannot use remapping because that creates the same problem I have now.
I'm not familiar with Microsemi's implementation, but every other Cortex-M3 I've used boots from Zero by mapping one of it's other memories there. ROM 0x1FFF0000, RAM 0x20000000, or FLASH 0x08000000. You build your code for the native address of each memory, it loads the SP/PC from vectors +0x00 and +0x04 and goes directly to the code at 0x08000189 or whatever. Subsequent code, with relative branches gets to SystemInit() and that sets SCB->VTOR to the native memory address, ie NOT ZERO
The memories are typically shadowed/mirrored at ZERO and the NATIVE address space. The mapping is done by vendor logic outside the core, basically taking the address bus, a mapping register, and decoding appropriate chip select (CS) signals.
The goal here would be to get you boot loader to run at it's native address, by setting IROM1 to that address, and as it's then executing safely there you can pull the mapping at zero rug from under your feet because the code is not being fetched from that instance of memory.
Thanks for getting back to me so quickly. I understand what you are saying and I too have worked with many different processors and done what you mentioned. The problem with the Microsemi implementation (believe it or not) is that they restrict execution to ONLY 0-0x20000000 address space. It will not allow execution from any other memory. Believe me I have tried for the past to months to make that happen and only found this fact out a few days ago!! Wasted a ton of time. So they basically are forcing you to use remapping to be able to run code from any other memory except esram.
So my only option is to run just those few assembly instructions from esram. Since ram is blank at power up and the scatterloader will initialize variables in that ram before main() runs, I can put those assembly instructions into a initialized data structure in the ram at a fixed location (say 0x20000000) and my bootloader when the time comes to do the final act of remapping can jump/call to that address and those instructions will be executing out of ram.
After your post, I was able to successfully remap and boot the new image if I use the ulink to load the small bit of code I linked to ram for me. I just need a way to get that code into ram without using the ulink to do it for me. So that proves that it works. Which is great.
Do you think that data structure idea would work or another way?
Well if they don't permit execution elsewhere you can replicate the shadowing by copying your boot loader into the root of the SDRAM, so your executing code at zero is mirrored at 0xA0000000, then when you change the mapping the processor will see the same code as before, but is pulling it from physically different memory. You'd need to advance the placement of your application code from 0xA0000000 to say 0xA0004000 which would give you 16 KB to play with.
memcpy(0xA0000000,0x00000000,0x4000); remap_sdram_to_zero(); // execution magically forks from primary boot loader image to one now in SDRAM
I just wanted to follow up on getting the bootloader to work out of esram. Because of your great help, I was able to implement the critical part of the code out of the line of fire in nvm by putting it into esram.
I did it somewhat differently so that I could pass the base address of the registers by a simple call to esram. The assembly code in esram is in a table which I located at the start of esram. I had to NOP the return assembly code for the function in esram so it would not immediately jump back (return) so the processor would instead execute through it into the table and execute the remapping and pc/sp loading code. It work find although its not a elegant solution. Straightforward though.
thanks again for you help and Microsemi if your out there you need to fix your app notes to make it clear that you cannot be executing out of nvm when you remap on top of yourself!
// code located in nvm memory before remapping ddr on top of it void Jump_to_executable(void) { SCB->VTOR = 0x00000000; // set the vector table base address to 0 int * test = (int*)BootJump1; // get the address of the code table in esram test[-1] = 0xBF00BF00; // back up 1 assembly instruction to NOP the // return address of BootJump // we do not want it to return. BootJump((INT32U)0x40038000); //////////////////////////////////////////////////////////// // code located at address 0x20000000 (esram) // BootJump() MUST be located just before the table in the scatter // file so that execution will fall thought to the table of code void BootJump(INT32U firmwareStartAddress) { return; // BX LR changed to NOp NOP } char BootJump1[18] = { 00, 0x21, //MOVS r1,#0x00 ; R0 contains base address 0x40038000 01, 0x60, //STR r1,[r0,#0x00] ; SYSREG->ESRAM_CR = 0u 01, 0x61, //STR r1,[r0,#0x10] ; SYSREG->ENVM_REMAP_BASE_CR = 0u 01, 0x22, //MOVS r2,#0x01 ; SYSREG->DDR_CR = 1u 0x82, 0x60, //STR r2,[r0,#0x08] ; Remap to address 0 0xD1,0xF8, //LDR sp,[r1,#0x00] ; Load new stack pointer address 00, 0xD0, 0xD1,0xF8, //LDR pc,[r1,#0x04] ; Load new program counter address 04, 0xF0 };