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; }
Well the initial PC is definitely not at 0xA0000003, and you don't need to subtract or add one to the value stored there. It should be ODD because it points to 16-bit THUMB code.
You might also want the SP to point to the top of internal SRAM
I saw that in an example. I questioned that as well. I have tried it both ways and the PC is still not being set to the value I tell to to be.
I wanted the stack to be in the ddr memory because the ddr memory will be remapped to address 0 in the nondebug version. The plan is to have a bootloader copy the code from external flash to ddr memory. That code will be linked at address 0 because the bootloader as its last action will remap the ddr memory to address 0.
For debugging though I am not doing remapping and loading the code with the uLink2 and not a bootloader. So the code is linked at address 0xA00000000 (ddr). The heap and the stack will follow the code.
I problem I am having is why does the script (I posted) unable to modify the PC and SP registers?
Is the function being called? What you posted seems incomplete.
You'd really want the stack in the lowest latency memory available, this would be memory significantly more tightly coupled than the DDR
Another working M3 script (complete)
/******************************************************************************/ /* RAM.INI: RAM Initialization File */ /******************************************************************************/ /* This file is part of the uVision/ARM development tools. */ /* Copyright (c) 2005-2010 Keil Software. All rights reserved. */ /* This software may only be used under the terms of a valid, current, */ /* end user licence from KEIL for a compatible version of KEIL software */ /* development tools. Nothing else gives you the right to use this software. */ /******************************************************************************/ FUNC void Setup (void) { SP = _RDWORD(0x20000000); // Setup Stack Pointer PC = _RDWORD(0x20000004); // Setup Program Counter _WDWORD(0xE000ED08, 0x20000000); // Setup Vector Table Offset Register } LOAD %L INCREMENTAL // load the application Setup(); // Setup for Running g, main
Be sure to also uncheck "Load Application at Startup" and "Update Target before Debugging"
Make sure you don't have any code in SystemInit() that will trash the memory configuration you created in the .INI file.
I am sorry for getting back to your post so late. I had to support the hardware group in getting their board up and running.
I did implement your suggestion but it does not have any affect on the new Microsemi cortex M3 ARM. I just don't understand why not.
I have asked Microsemi to figure out why remapping the DDR to address 0 does not work. They are very slow to respond and most times do not give me answer to I need to proceed with plan B which is to get the Kiel uVision compiler to link my code at address 0xA0000000 and for the uLink emulator to load and execute the code in the DDR.
I can created an image that in entirely in DDR and it will load without error when I start the debugger. The MSP and SP registers are correct at 0x20003000 but the PC register is not set to the entry point in the code (0xA0000188) but to 0x0000076A in the nvm. When I force the PC register to the correct value, the disassembled code appears (main) and is corrected. If I do a disassembly step from there instead of going to instruction 0xA0000190 (scatterload) it jumps to nvm at address 0x0000006D0.
I don’t know if there is something other than the PC that needs to be set or not. Everything I have read says No. The PC, SP and vector table base are the only things that need to be changed as you noted in your reply.
What needs to be done to make the uLink2 emulator it work?
Steve
I cannot post the entire script because it is too long. The initialization of the DD From the startup.ini script (the DDR initialization seems to be working)
FUNC void RemapDDR (void) { //-------------------------------------------------------------------------- // Remap MDDR to address 0x00000000. // _SYSREG_BASE = 0x40038000; _WDWORD(0x40038000, 0x0); // SYSREG->ESRAM_CR = 0u; // 0x40038000u + 0 _WDWORD(0x40038010, 0x0); // SYSREG->ENVM_REMAP_BASE_CR = 0u; // 0x40038000u + 0x10 _WDWORD(0x40038008, 0x1); // SYSREG->DDR_CR = 1u; // 0x40038000u + 8 // DDR_CR register is set for the enabling the // DDR memory address remap to Cortex M3 bottom address space } FUNC void SetupPC_SP (void) { SP = _RDWORD(0x20003000); // Setup Stack Pointer PC = _RDWORD(0xA0000004); // Setup Program Counter (+4) _WDWORD(0xE000ED08, 0xA0000000); // 0xE000ED08 SCB->VTOR = readonly_region_base; } InitDDR(); // initialize DDR so it can be accessed //RemapDDR(); // not working LOAD %L INCREMENTAL // Download SetupPC_SP(); // set PC/SP/vector table base address //g, main /*------------------------------------------------------------------- ** Execute upon software RESET **-----------------------------------------------------------------*/ FUNC void OnResetExec(void) { InitDDR(); // RemapDDR(); SetupPC_SP(); }
You understand what _RDWORD() does, right?
SP = _RDWORD(0x20003000); // Setup Stack Pointer
If you want to set the SP to 0x20003000
SP = 0x20003000;
Setting the code to built at 0xA0000000 should be a matter of configuring the GUI or scatter file. You might want to cross check that with the .MAP file, and disassembling the generated image.
Is there some potential for the core to execute some other code? What's initially mapped at zero?
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