I'm confused by the difference between initialization routines for an external SDRAM in mbed (EALib) vs. LPCOpen examples. I'm using the Embedded Artists LPC4088 QuickStart Board in MCUXpresso IDE v10.0.2 [Build 411] [2017-07-11]. The board has an external 32 MB SDRAM chip.
One the one hand, you have the mbed-based EALib, in which you're ostensibly supposed to call sdram_init() before using the external SDRAM. (See sdram.cpp.) sdram_init() appears to be setting values in quite a few control registers.
On the other hand, you have the example LPCOpen Software for LPC40xx (direct download link), which runs on the same board, but does almost none of that. Looking at the periph_memtest example in there, we see that only SystemCoreClockUpdate() and Board_Init() are called before accessing the memory. The only commonality between these and EALib's sdram_init() is that there is one control register whose value they both modify (LPC_SYSCTL->PCONP).
Why the discrepancy? How is LPCOpen getting by doing so little? Or why is EALib/mbed doing so much unnecessary stuff?
已解决! 转到解答。
Very informative, thanks. Now I can put together the answer to my original question (which I didn't know how to ask).
I asked how the LPCOpen code was getting by without initializing the external SDRAM. But it only seemed to me like that because main() is not the entry point in this case, and the memory is initialized before main().
You described in your latest comment the location in memory that specifies the location of the reset handler function. (The reset handler function is what I would say is the "entry point" at the lowest level (or close to it).) In this case, the entry point is the ResetISR() function, located in cr_startup_lpc40xx.c.
So the portion of the call tree leading to the memory initialization (before main()) is as shown below (where each function calls the next).
Function | source file |
---|---|
ResetISR() | cr_startup_lpc40xx.c |
SystemInit() | sysinit.c |
Board_SystemInit() | board_sysinit.c |
Board_SetupExtMemory() | board_sysinit.c |
[EDIT: At risk of poor form, I'm marking this (my own) comment as "correct", but your comments as "helpful", since they all helped me put it together.]
void Board_SetupExtMemory(void)
{
/* Setup EMC Delays */
/* Move all clock delays together */
LPC_SYSCTL->EMCDLYCTL = (CLK0_DELAY) | (CLK0_DELAY << 8) | (CLK0_DELAY << 16 | (CLK0_DELAY << 24));
/* Setup EMC Clock Divider for divide by 2 */
/* Setup EMC clock for a divider of 2 from CPU clock. Enable EMC clock for
external memory setup of DRAM. */
Chip_Clock_SetEMCClockDiv(SYSCTL_EMC_DIV2);
Chip_SYSCTL_PeriphReset(SYSCTL_RESET_EMC);
/* Init EMC Controller -Enable-LE mode- clock ratio 1:1 */
Chip_EMC_Init(1, 0, 0);
/* Init EMC Dynamic Controller */
Chip_EMC_Dynamic_Init((IP_EMC_DYN_CONFIG_T *) &IS42S32800D_config);
/* Init EMC Static Controller CS0 */
Chip_EMC_Static_Init((IP_EMC_STATIC_CONFIG_T *) &SST39VF320_config);
/* Init EMC Static Controller CS1 */
Chip_EMC_Static_Init((IP_EMC_STATIC_CONFIG_T *) &K9F1G_config);
/* EMC Shift Control */
LPC_SYSCTL->SCS |= 1;
}
/* Set up and initialize hardware prior to call to main */
void Board_SystemInit(void)
{
Board_SetupMuxing();
Board_SetupClocking();
Board_SetupExtMemory();
}
Hope this is clear.
Have a great day,
TIC
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Hi Brendan,
wherever in the code the SDRAM is initialized, the instructions are finally the same. But there are of course different ways to program the instructions. Simple sequential write instructions to all required registers, working with structs and loops, etc. But to make the SDRAM working, you need to have them all written correctly and in the right order.
The position in the code where this initialization is done is also debatable. You only need to take care that it is done before you access it. You could think of 2 extreme scenarios:
In LPCOpen we selected something between 1) and 2). The startup.s and the sys_init stuff (sysinit and board_sysinit) runs from the boot memory and prepares all the rest, for example also the SDRAM. At the end of the sys_init stuff the compiler may place code for relocation of objects which were specified in the linker file (scatter file) to be somewhere else, for example in the SDRAM.
Another meaningful example is the placement of code into the SDRAM with a debugger. Here it it is very clear that you need to set up the PLLs and the SDRAM memory interface with an ini script before the debugger can write the code image to the SDRAM.
Whenever and wherever you perform the SDRAM initialization, you only need to be done with it before it's getting accessed.
Regards,
Bernhard.
Hi Bernhard,
Apologies for my delay in responding. Priorities shift...
Thanks for the info. I'm still not quite clear on the answers to my questions, though.
I asked,
What is the entry point, and how/where is that defined?
You said, in part,
Bernhard Fink wrote:
In LPCOpen ... The startup.s and the sys_init stuff (sysinit and board_sysinit) runs from the boot memory and prepares all the rest, for example also the SDRAM.
When the microcontroller boots, what runs first, and how is it configured to be the first thing to run? For instance, is SystemInit() the first thing to run? What if I wanted something else to execute before the first thing - how would I change that?
Also, startup.s is not in this sample code.
Hi Brendon,
you're right, if you use the GCC setup, then the startup files are realized in C code in the framework.
IAR and Keil tools normally use an assembler file, just another thing which is just a philosophy. Technically both options are there, one tool simply uses it this way, the other one the other way.
The entry point is always the reset handler. If you able to localize it, then you have the piece of code which gets located at the address the linker puts it to.
The beginning of an image for the ARM Cortex-M is always the same:
- 4 bytes for address vector to stack pointer start
- 4 bytes for address vector to reset handler start
- 14 words with ARM exception vector addresses
- platform dependent number of IRQ vector addresses
So when you look into the final binary, the second word tells you where the linker placed the reset handler. Normally the linker gets all the freedom to place it wherever it wants to, except if you hard-code it.
Anyway, in the GCC world the startup file is normally called cr_startup_xxxx.c, in there you might find "ResetISR" and that's the reset handler (in principle just another naming convention, could be different).
The way it is set up there, decides how it could work with the SDRAM.
Maybe this helps a little bit.
Regards,
Bernhard.
Very informative, thanks. Now I can put together the answer to my original question (which I didn't know how to ask).
I asked how the LPCOpen code was getting by without initializing the external SDRAM. But it only seemed to me like that because main() is not the entry point in this case, and the memory is initialized before main().
You described in your latest comment the location in memory that specifies the location of the reset handler function. (The reset handler function is what I would say is the "entry point" at the lowest level (or close to it).) In this case, the entry point is the ResetISR() function, located in cr_startup_lpc40xx.c.
So the portion of the call tree leading to the memory initialization (before main()) is as shown below (where each function calls the next).
Function | source file |
---|---|
ResetISR() | cr_startup_lpc40xx.c |
SystemInit() | sysinit.c |
Board_SystemInit() | board_sysinit.c |
Board_SetupExtMemory() | board_sysinit.c |
[EDIT: At risk of poor form, I'm marking this (my own) comment as "correct", but your comments as "helpful", since they all helped me put it together.]