I have been reading Vivi recently. By the way, I want to streamline the useful parts of VIVI to make up my own code.
After completing the Assembly, head. s finally jumped into main. C. In this process, I add functions one by one. Each time a function is added, the next function is added until the test is successful. I think this accumulation is also beneficial. I may have a detailed understanding of each function module and every step, and I can quickly locate the problem. Of course, the most important thing is to go deep into the details, where you can learn a lot and learn a lot. ("Learning" is different from "Mastering)
I jumped from head. s to main. C and made a mistake in this process. In fact, I have some knowledge about the running domain and the loading domain, but I am stuck here. Here we will analyze:
First of all, it must be clear that the B/BL command cannot be used for this jump. Because 2410 of SDRAM is generally stored in bank6 (0x30000000), we need to jump from bank0 to bank6, b/BL only has a range of +/-32 m (which has been analyzed in another article ). Therefore, this jump can only be achieved by writing values directly to the PC.
This jump process in vivi adopts the "Spring bed" technique. Let's take a look at how it is implemented in Vivi:
@ 10 jump to ram LDR R1, = on_the_ram // Save the on_the_ram address to r1 Add PC, R1, #0 // load the address to PC and jump into on_the_ram NOP NOP 1: B.
// After the link, the code after on_the_ram will be linked to a location in the SDRAM On_the_ram: MoV R1, # gpio_ctl_base Add R1, R1, # ogpio_f MoV R2, # 0xff STR R2, [R1, # ogpio_dat]
@ Print prompt information LDR R1, serbase LDR r0, str_stack BL printword LDR r0, dw_stack_start BL printhexword MoV r0, PC BL printhexword
LDR sp, dw_stack_start MoV FP, #0 MoV A2, #0 // The previous sentence is used for testing. Turn off the light and print the stack address and the current PC address to the serial port.
BL main // here, a streaming light is written in Main. C to help you see the effect.
MoV PC, # flash_base
|
As you can see above, after the sentence "add PC, R1, #0", the program has started to run in SDRAM.
Second, pay attention to the following details: "location-independent code" and "location-related code ".
Location-independent code, that is, code runs correctly wherever it is placed after the link. The program runs in sequence, but the location issue arises during the jump. How is the jump Destination Address calculated? In command B/BL, the target address is calculated by the current PC, and the target address is a relative volume (relative to the current PC ).
Location-related code. After the link, the Code must be placed in the specified place to run correctly. When linking, it mainly links the symbol in the program and gives it an address. In the LDR R1, = on_the_ram command, the address value of on_the_ram is determined after the link. That is to say, the CPU will jump to the specified address after it, and we must put our code in the specified address so that the CPU can run the correct command. Otherwise, an error occurs.
Let's take a look at the compilation process of these programs:
vivi:head.S nand_read.c arm-linux-gcc -c head.S -I ../../include/ -o head.o arm-linux-gcc -c nand_read.c -I ../../include/ -o nand_read.o arm-linux-gcc -c main.c -o main.o arm-linux-ld -Ttext 0x33f00000 head.o nand_read.o main.o -o tmp.o arm-linux-objcopy -O binary -S tmp.o vivi arm-linux-objdump -D -b binary -m arm vivi > aaa
clean: rm *~ -f rm *.o -f rm vivi -f
|
Makefile is a beginner. The link address of the program is 0x33f00000. That is, after the link, the on_the_ram address value must be after 0x33f00000 (in bank6), instead of the address in bank0.
You can also directly write a link script to make it more intuitive:
Sections {
First 0x33f00000: {head. O nand_read.o main. O}
}
It is also very simple here, and it is directly linked to 0x33f00000. All previous operations in LDR R1, = on_the_ram are location-independent code. Copying nand_read.c of NAND is naturally a location-independent code, these operations can be correctly executed whether starting from 0 or 0x33f00000.
Again, why is the link 0x33f00000? In fact, it is very simple. In head. S, I copied the entire code to the beginning of 0x33f00000. If the link reaches 0x30000000, the CPU is allocated to on_the_ram from the corresponding position after 0x30000000, which is naturally not found. I made a mistake and linked it to 0x30000000.
Finally, let's look at the disassembly code.
DC: e59f13e8 LDR R1, [PC, #1000]; 0x4cc // This sentence is LDR R1, = on_the_ram, which assigns the value at 0x4cc to R1, that is, R1 = 0x 33f000f0 e0: e281f000 add pc, r1, #0 ; 0x0 e4: e1a00000 nop (mov r0,r0) e8: e1a00000 nop (mov r0,r0) ec: eafffffe b 0xec f0: e3a01456 mov r1, #1442840576 ; 0x56000000 //
This is now On_the_ram, all of which are in the SDRAM F4: e2811050 add R1, R1, #80; 0x50 F8: e3a020ff mov R2, #255; 0xff FC: e582134 STR R2, [R1, #4] // turn off the light 100: e59f138c LDR R1, [PC, #908]; 0x494 104: e59f03b2 LDR r0, [PC, #946]; 0x4be 108: eb1_c0 BL 0x410 // call the subroutine for printing information 10c: e59f038c LDR r0, [PC, #908]; 0x4a0 110: eb1_a3 BL 0x3a4 // call the subroutine for printing information 114: e1a0000f mov r0, PC 118: eb1_a1 BL 0x3a4 // call the subroutine for printing information 11c: e59fd37c LDR sp, [PC, #892]; 0x4a0 120: e3a0b000 mov FP, #0; 0x0 124: e3a01000 mov R1, #0; 0x0 128: eb000174 BL 0x700 // 0x700 is followed by the content in main. 12c: e3a0f000 mov PC, #0; 0x0
4cc: 33f000f0 mvnccs r0, #240; 0xf0 // the machine code of this command is 33f000f0, which actually stores such a value 4d0: 127f830 andeq PC, R0, R0, LSR R8 // This is also the same principle. The 0x0000f830 value is stored. It looks familiar. It is used to configure NAND Flash.
|
Finally, paste the printed information:
@ 000000d4 // print the Pc value for the first time after the UART is initialized MTST-OK // memory Detection OK // NAND copy NAND-OK // detected after Replication St
33 defffc // Stack pointer 33f0011c // current PC
|