As we all know, when ARM starts in Nand flash boot mode, the system will copy the first 4KB code in Nand flash to SRAM (that is, Steppingstone), and SRAM will configure the interrupt vector table and complete the necessary initialization of Nand flash access, and then copy all the program codes in Nand flash to SDRAM, and finally jump from SRAM to SDRAM, and then the program will execute normally. This process looks simple, but it is not easy to really understand this process. Despite this, I still want to tell you that it is relatively easy to understand this process if you understand it carefully. If you are an ADS user, you have saved a lot of trouble, but I am not sure whether the trouble you have saved is worth it. Here is a troublesome way, the LED program under Linux.
Code Head.s
- .extern main
- .text
- .global _start
- _start:
- b reset
- reset:
- ldr sp,=4096
- bl disable_watch_dog
- bl clock_init
- bl memsetup
- bl copy_steppingstone_to_sdram
- ldr pc,=on_sdram
- on_sdram:
- msr cpsr_c,#0xdf
- ldr sp,=0x34000000
- ldr lr,=halt_loop
- ldr pc,=Main
- halt_loop:
- b halt_loop
I think the most important thing to understand is this code. Let me explain it briefly.
(1) Since the pc will be cleared to zero after the arm executes reset, that is, the interrupt entry address of the reset interrupt, the first instruction is b reset, which jumps to the reset interrupt processing function.
(2) Since the hardware configuration here is all done in C language, and our initial code is relatively small and will not exceed 4KB at all, we can use the stack in SRAM, so we set SP to 4096 to provide a C running environment
(3) The next three bls respectively complete the tasks of disabling the watchdog timer, configuring the clock signal and memory configuration. The fourth bl copies the code in the 4KB space of SRAM to SDRAM.
(4) The following ldr statement assigns pc to the address of on_sdram, thus implementing a jump from SRAM to SDRAM (the reason will be explained below).
(5) on_sdram switches to system mode and allocates the system mode stack, sets the link register to halt_loop and then jumps to Main in SDRAM
The above explanation only roughly explains the meaning of the code, but beginners always have a question: why does ldr pc,=on_sdram implement the jump from SRAM to SDRAM? I was troubled by this question for a long time, and I finally figured it out today. The key to the problem is the issue of relative jump and absolute jump. To explain this problem, I will first explain the difference between the bl instruction and the ldr instruction in the execution process.
The B instruction is a relative jump instruction. The B instruction is the simplest jump instruction. Once a B instruction is encountered, the ARM processor will immediately jump to the given target address and continue execution from there. Note that the actual value stored in the jump instruction is an offset relative to the current PC value, not an absolute address. Its value is calculated by the assembler (refer to the relative addressing in the addressing mode). It is a 24-bit signed number, which is extended to 32 bits after being shifted left by two bits. The effective offset represented is 26 bits (32MB of address space before and after). Similarly, BL and BX are relative jumps.
The LDR pseudo-instruction directly assigns the second operation to the first operand. When ldr pc,=Main is executed, the absolute address of Main is assigned to PC.
OK, now that we know the difference between these two instructions, let's see how the code implements the jump from SRAM to SDRAM. First of all, it should be pointed out that the 2440 development board has SRAM and SDRAM. SRAM is a 4KB memory space starting at address 0x00000000, and SDRAM is a 64M space starting at 0x30000000.
Whether compiled with ADS or arm-linux-gcc, the code will be linked to after 0x30000000 (that is, in SDRAM). ADS users can check the ADS project configuration, where one configuration is that the RO starting address is 0x30000000. Linux users need to use -T to specify that the actual address of the code is 0x30000000 when linking.
According to the compilation principle, the address of the function in the program has been determined during the linking phase, that is, the actual address of the function is after 0x30000000, and the address of the program's entry function, which is _start here, is 0x300000000, and other functions will be greater than this number.
However, after the arm is powered on, the system will copy the first 4KB code of the Nand flash to the SRAM, that is, the 4KB instructions starting with the _start function will be copied to the SRAM for execution. According to the above example, the instruction executed at 0x00000000 is "b reset". Since b is a relative jump, it is based on the current pc value plus or minus a certain number to jump to the code to be executed. Therefore, after the pc is added or subtracted by the number, it will reach the position of the reset function. Therefore, the reset function cannot be written to the space outside 4KB, otherwise the arm startup will fail. Similarly, the next few bl are relative jumps, so they are all jumps relative to the current pc. Since the Nand flash has only 64M of space in total, it is impossible for a relative jump to jump to SDRAM, because jumping to SDRAM requires at least a jump of 0x30000000, and this relative displacement is much larger than 64M.
And ldr pc,=Main assigns the actual address of the Main function to pc, and the actual address of Main is after 0x30000000, so it jumps from SRAM to SDRAM.
Since this process involves the hardware structure and compilation principle, it is difficult for ordinary people to understand. In addition, due to my own level, many places can only be understood by heart and not by words. Please forgive me if I mislead you. Of course, if you still don't understand the arm startup process after reading this, you can leave a message to discuss this issue. The following are other related codes. I attach them here. 2440addr.h is not posted because I also use the code in the sample program that comes with arm, and the code has more than 4,000 lines, and most of the addresses are not used. The other codes are as follows
Code Init.s- #include "2440addr.h"
- void disable_watch_dog(void);
- void clock_init(void);
- void memsetup(void);
- void copy_steppingstone_to_sdram(void);
- void initart(void);
- void disable_watch_dog(void)
- {
- rWTCON = 0;
- }
- void clock_init(void)
- {
- rCLKDIVN = 0x03;
- /*
- * If HDIVN is non-zero, the CPU's bus mode should be changed from
- * "fast bus mode" becomes "asynchronous
- *bus mode”
- */
- __asm__(
- "mrc p15, 0, r1, c1, c0, 0 "
- "orr r1, r1, #0xc0000000 "
- "mcr p15, 0, r1, c1, c0, 0 "
- );
- rMPLLCON = (92<<12)|(1<<4)|(2);
- //rMPLLCON = ((0x5c<<12)|(0x01<<4)|(0x02));
- }
- void memsetup(void)
- {
- volatile unsigned long *p = (volatile unsigned long *)0x48000000;
- /* This function is assigned in this way, instead of assigning the configuration value like the previous experiment (such as the mmu experiment)
- * It is written in an array because it is necessary to generate "position-independent code" so that this function can be copied to
- * SDRAM can run in steppingstone before
- */
- /* Storage controller 13 register values */
- p[0] = 0x22011110; //BWSCON
- p[1] = 0x00000700; //BANKCON0
- p[2] = 0x00000700; //BANKCON1
- p[3] = 0x00000700; //BANKCON2
- p[4] = 0x00000700; //BANKCON3
- p[5] = 0x00000700; //BANKCON4
- p[6] = 0x00000700; //BANKCON5
- p[7] = 0x00018005; //BANKCON6
- p[8] = 0x00018005; //BANKCON7
- /* REFRESH,
- * HCLK=12MHz: 0x008C07A3,
- * HCLK=100MHz: 0x008C04F4
- */
- p[9] = 0x008C04F4;
- p[10] = 0x000000B1; //BANKSIZE
- p[11] = 0x00000030; //MRSRB6
- p[12] = 0x00000030; //MRSRB7
- }
- void copy_steppingstone_to_sdram(void)
- {
- unsigned int *pdwSrc = (unsigned int *)0;
- unsigned int *pdwDest = (unsigned int *)0x30000000;
- while (pdwSrc < (unsigned int *)4096)
- {
- *pdwDest = *pdwSrc;
- pdwDest++;
- pdwSrc++;
- }
- }
Code Main.c:
- #include "2440addr.h"
- void Delay(int i)
- {
- int m,n,p;
- for(m = 0; m != i; ++ m)
- {
- for(n = 0; n != 255; ++ n)
- {
- for(p = 0; p != 255; ++ p)
- ;
- }
- }
- }
- void Main()
- {
- int count;
- int leds[4] = {0x1c0, 0x1a0, 0x160, 0xe0};
- rGPBCON=0x00155555;
- rGPBUP=rGPBUP&0xFF00;
- while(1)
- {
- for(count = 0; count != 4; ++ count)
- {
- rGPBDAT=leds[count];
- if(count%2)
- rGPBDAT+=1;
- Delay(2);
- }
- }
- }
The link file led.lds is as follows:
- SECTIONS
- {
- . = 0x30000000;
- .text : {*(.text)}
- .rodata ALIGN(4) : {*(.rodata)}
- .data ALIGN(4) : {*(.data)}
- .bss ALIGN(4) : {*(.bss) *(COMMON)}
- }
The makefile is as follows:
- objects:=Head.o Init.o Main.o
- led.bin : $(objects)
- arm-linux-ld -Tled.lds -nostdlib -o led_elf $^
- arm-linux-objcopy -O binary -S led_elf $@
- arm-linux-objdump -D -m arm led_elf > led.dis
- %.o:%.c
- arm-linux-gcc -Wall -O2 -c -o $@ $<
- %.o:%.s
- arm-linux-gcc -Wall -O2 -c -o $@ $<;
- .PYTHON:clean
- clean:
- rm -f led.bin led_elf led.dis *.o
As above, except for 2440addr.h, all are complete. In addition, it should be pointed out that Option.h is referenced in 2440addr.h. In order to simplify the code, this sentence can be commented out. The functions related to this file are not used in our code at all. Otherwise, you need to modify the makefile file to complete the compilation and linking work related to Option.h.
Okay, I have wasted so much of your time again, so I won't say much here. If you have any questions, please let experts know.
Previous article:ucos-ii study notes-the principle and use of semaphores
Next article:ARM bare metal development environment construction and examples under Linux
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Detailed explanation of intelligent car body perception system
- How to solve the problem that the servo drive is not enabled
- Why does the servo drive not power on?
- What point should I connect to when the servo is turned on?
- How to turn on the internal enable of Panasonic servo drive?
- What is the rigidity setting of Panasonic servo drive?
- How to change the inertia ratio of Panasonic servo drive
- What is the inertia ratio of the servo motor?
- Is it better for the motor to have a large or small moment of inertia?
- What is the difference between low inertia and high inertia of servo motors?
- O-RAN development trends, reference architecture, and interoperability testing
- Qorvo PAC series highly integrated motor control chips and applications
- Five skills required for RF test engineers
- 【GD32L233C-START Review】Display driver for color OLED screen
- Would you choose the popular outdoor power supply?
- Solution to severe static heating of wireless network card
- China's latest classification catalogue of medical devices (No. 104, 2017)
- What is the function of the cross connection of the five tubes in the middle of this comparator?
- What is the difference between pyb.delay and time.sleep?
- Stepper motor current test