Arm 2440 - Detailed explanation of Nand flash boot mode (taking LED program as an example)

Publisher:心有所属Latest update time:2015-08-19 Source: eefocusKeywords:Arm  Nand  flash Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
I have been studying arm on and off for 2 months. Now I feel that I understand the boot process of arm in Nand flash mode. Now I come here to record it to express my extremely happy mood. Without further ado, while I still remember the feelings during the learning process, I will go straight to the point.

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

  1. .extern main  
  2. .text  
  3. .global _start  
  4. _start:  
  5.     b reset  
  6.   
  7. reset:  
  8.     ldr sp,=4096  
  9.     bl disable_watch_dog  
  10.     bl clock_init  
  11.     bl memsetup  
  12.     bl copy_steppingstone_to_sdram  
  13.     ldr pc,=on_sdram  
  14.   
  15. on_sdram:  
  16.     msr cpsr_c,#0xdf  
  17.     ldr sp,=0x34000000  
  18.     ldr lr,=halt_loop  
  19.     ldr pc,=Main  
  20.   
  21. halt_loop:  
  22.     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
  1. #include "2440addr.h"  
  2.   
  3. void disable_watch_dog(void);  
  4. void clock_init(void);  
  5. void memsetup(void);  
  6. void copy_steppingstone_to_sdram(void);  
  7. void initart(void);  
  8.   
  9. void disable_watch_dog(void)  
  10. {  
  11.     rWTCON = 0;  
  12. }  
  13.   
  14. void clock_init(void)  
  15. {  
  16.     rCLKDIVN = 0x03;  
  17.   
  18.     /*  
  19.      * If HDIVN is non-zero, the CPU's bus mode should be changed from  
  20.      * "fast bus mode" becomes "asynchronous   
  21.      *bus mode”  
  22.      */  
  23.     __asm__(  
  24.             "mrc p15, 0, r1, c1, c0, 0 "  
  25.             "orr r1, r1, #0xc0000000 "  
  26.             "mcr p15, 0, r1, c1, c0, 0 "  
  27.            );  
  28.   
  29.     rMPLLCON = (92<<12)|(1<<4)|(2);  
  30.     //rMPLLCON = ((0x5c<<12)|(0x01<<4)|(0x02));  
  31. }  
  32.   
  33. void memsetup(void)  
  34. {  
  35.     volatile unsigned long *p = (volatile unsigned long *)0x48000000;  
  36.   
  37.     /* This function is assigned in this way, instead of assigning the configuration value like the previous experiment (such as the mmu experiment)  
  38.      * It is written in an array because it is necessary to generate "position-independent code" so that this function can be copied to  
  39.      * SDRAM can run in steppingstone before  
  40.      */  
  41.     /* Storage controller 13 register values ​​*/  
  42.     p[0] = 0x22011110; //BWSCON  
  43.     p[1] = 0x00000700; //BANKCON0  
  44.     p[2] = 0x00000700; //BANKCON1  
  45.     p[3] = 0x00000700; //BANKCON2  
  46.     p[4] = 0x00000700; //BANKCON3    
  47.     p[5] = 0x00000700; //BANKCON4  
  48.     p[6] = 0x00000700; //BANKCON5  
  49.     p[7] = 0x00018005; //BANKCON6  
  50.     p[8] = 0x00018005; //BANKCON7  
  51.   
  52.     /* REFRESH,  
  53.      * HCLK=12MHz: 0x008C07A3,  
  54.      * HCLK=100MHz: 0x008C04F4  
  55.      */   
  56.     p[9] = 0x008C04F4;  
  57.     p[10] = 0x000000B1; //BANKSIZE  
  58.     p[11] = 0x00000030; //MRSRB6  
  59.     p[12] = 0x00000030; //MRSRB7  
  60. }  
  61.   
  62. void copy_steppingstone_to_sdram(void)  
  63. {  
  64.     unsigned int *pdwSrc = (unsigned int *)0;  
  65.     unsigned int *pdwDest = (unsigned int *)0x30000000;  
  66.   
  67.     while (pdwSrc < (unsigned int *)4096)  
  68.     {  
  69.         *pdwDest = *pdwSrc;  
  70.         pdwDest++;  
  71.         pdwSrc++;  
  72.     }  
  73. }  
Note: Since our code is relatively small, much smaller than 4KB, the 4KB code automatically copied to SRAM when ARM starts up contains all our code. Therefore, in the copy operation, I copied the 4KB code in stepingstone to SDRAM. In actual applications, most of the code is more than 4KB, so the copy function should copy all the code in Nand flash to SDRAM, so that ARM can run successfully.

 

Code Main.c:

  1. #include "2440addr.h"  
  2.   
  3. void Delay(int i)  
  4. {  
  5.     int m,n,p;  
  6.     for(m = 0; m != i; ++ m)  
  7.     {  
  8.         for(n = 0; n != 255; ++ n)  
  9.         {  
  10.             for(p = 0; p != 255; ++ p)  
  11.                 ;  
  12.         }  
  13.     }  
  14. }  
  15.   
  16. void Main()  
  17. {  
  18.     int count;  
  19.     int leds[4] = {0x1c0, 0x1a0, 0x160, 0xe0};  
  20.   
  21.     rGPBCON=0x00155555;  
  22.     rGPBUP=rGPBUP&0xFF00;  
  23.   
  24.     while(1)  
  25.     {  
  26.         for(count = 0; count != 4; ++ count)  
  27.         {  
  28.             rGPBDAT=leds[count];  
  29.             if(count%2)  
  30.                 rGPBDAT+=1;  
  31.             Delay(2);  
  32.         }  
  33.     }  
  34. }  

The link file led.lds is as follows:
  1. SECTIONS  
  2. {  
  3.     . = 0x30000000;  
  4.     .text : {*(.text)}  
  5.     .rodata ALIGN(4) : {*(.rodata)}  
  6.     .data ALIGN(4) : {*(.data)}  
  7.     .bss ALIGN(4) : {*(.bss) *(COMMON)}  
  8. }  

The makefile is as follows:
  1. objects:=Head.o Init.o Main.o  
  2.   
  3. led.bin : $(objects)  
  4.     arm-linux-ld -Tled.lds -nostdlib -o led_elf $^  
  5.     arm-linux-objcopy -O binary -S led_elf $@  
  6.     arm-linux-objdump -D -m arm led_elf > led.dis  
  7.   
  8. %.o:%.c  
  9.     arm-linux-gcc -Wall -O2 -c -o $@ $<  
  10. %.o:%.s  
  11.     arm-linux-gcc -Wall -O2 -c -o $@ $<;  
  12.   
  13. .PYTHON:clean  
  14. clean:  
  15.     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.

Keywords:Arm  Nand  flash Reference address:Arm 2440 - Detailed explanation of Nand flash boot mode (taking LED program as an example)

Previous article:ucos-ii study notes-the principle and use of semaphores
Next article:ARM bare metal development environment construction and examples under Linux

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号