This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

remapping of ddr

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;

}

Parents
  •        // 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.

Reply
  •        // 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.

Children
  • 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.

  • 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

  • Hi

    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
     };