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'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.
Hi
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 };