A recent project needs to port Vivi to our Samsung 2440 board at head. after the crystal oscillator frequency clock frequency is set in S, Vivi can run, but after entering main, the program crashes when the MTD device is initialized.
Connect to the JTAG and use the axd debugger of ads for assembly debugging. It is found that the program is running an endless loop, according to Vivi. the address information provided in the map shows that the current command belongs to the undefentrypoint exception interrupt processing program, that is, the program encountered an undefined instruction exception.
Because you cannot know where the command has an exception, you can only use the debugger to assemble the code in one step for inspection. In this process, I am not familiar with the axd debugger, which wastes a lot of time.
First, let's talk about the nand flash Boot mechanism. Unlike nor flash, NAND Flash only provides data access interfaces. Therefore, commands stored on NAND flash must be copied to SDRAM for execution. Considering that the cost of large-capacity nor flash is much higher than that of NAND Flash, the general practice is to set a small-capacity nor flash and a large-capacity NAND flash on the board, and write the guiding code in nor flash, the Boot Code then copies the content in the NAND flash to the SDRAM for execution. Compared with the use of nor flash, the cost is much lower.
Samsung's S3C2440 adopts another method to save nor flash. The principle is that the CPU has a built-in 4 k ram. When the CPU is set to the NAND Boot Mode, the 4 k ram address will be mapped to 0x00000000. When the CPU is initialized, the first thing is to read the first 4 K content of NAND flash into the built-in 4 k ram, and then execute the commands in this ram. This requires that the content of the boot loader must be controlled within 4 K. This is too high. Modern Boot Loader usually has a large number of debugging functions, which is basically impossible to be so small, therefore, the boot loader is divided into two parts: the simple functions of the NAND boot loader and the general sense of the boot loader, for example, WinCE is divided into nboot and eboot. The NAND boot loader is only responsible for moving the boot loader to the memory. Even if another jump occurs.
However, the two boot loaders are still too troublesome. Vivi uses another method. After the loader is started, it first carries itself from the NAND flash to the SDRAM, and then precisely jumps to the next instruction, in this way, you can save the NAND boot loader.
This is the handling process, which creates a lot of trouble for me. After the operation, Vivi checks whether the code is correctly copied, that is, the 4 K content starting with 0x00000000 is compared with the first 4 K content of the target address. If the content is consistent, it is deemed that the operation has been properly carried. Of course, when I set a breakpoint to skip this large loop during debugging, I found that the data verification failed .... it's really strange, because I personally checked the two memories and they are exactly the same.
I can't find the reason from the code. I can only observe it in one step. The result is unexpected: the memory at the breakpoint location is protected, and the data read is incorrect.
- Heap initialization error
After the verification is passed, Vivi jumps to the SDRAM region address 0x33f00000 and continues to execute. It enters the main function and then hangs out in heap_init. After adding the debugging code, you will find that the reason is that the initial value of gheapbase is incorrect and heap_init fails, but the definition in the code is "static blockhead * gheapbase = NULL ;"! No way, you can only debug and trace in one step. It is hard to explain that gheapbase is inexplicably changed to 0x000000ff.
Later, I checked Vivi. during map, it is found that the gheapbase address is 0x33f0d63c (0x33f0 is the starting address of VIVI in Ram), while 0xd63c is converted to a 10th hexadecimal value of 54844, which is exactly the length of the compiled Vivi file! The idea is clear, that is to say, the initial value of gheapbase is not stored in the VIVI file. It should have been initialized to 0 in the memory, but because our VIVI is copied from NAND Flash to SDRAM, it must be due to a problem with the content of NAND Flash, resulting in gheapbase being assigned an unexpected value, check the content of NAND Flash immediately. After the file is completed, it will be followed by "ff 00 00 00". This is a big picture. When VIVI is burned to NAND Flash, install the software (I burned it with sjf2440) although the correct data is written to Vivi, but the content that should be filled with 00 is not processed, Vivi itself does not record the file length, so a brain copies the data to the SDRAM.
There are two solutions: one is to modify the burning program once and for all, and the more rapid way is to directly modify the VIVI image file in the hexadecimal Editor, fill 00 until the block alignment is re-installed.
After the heap initialization, The mtd_dev_init fails again and the debugging code is added. It is known that an exception occurred when calling arch_udelay (). It is strange to trigger an exception when calculating the CPU frequency. The Code is as follows:
/* <Br/> * CPU clock = (mdiv + 8) * fin)/(pdiv + 2) * (1 <sdiv ))) <br/> * fin = input frequency (to cpu) <br/> */<br/> static inline unsigned long <br/> get_cpu_clk (void) <br/>{< br/> unsigned long Val = mpllcon; <br/> return (get_mdiv (VAL) + 8) * fin * 2) /(get_pdiv (VAL) + 2) * (1 <get_sdiv (VAL); <br/>}
Can you see what is wrong with the code? I can't see it. Simply calculate the value. Even if the Division by 0 error is not, I only know that it is okay to directly return a value. skip this step first.
This is a serious problem. printk is an important item for debugging. Even it has a problem and we have no choice but to do it. Check it step by step.
An exception occurred while calling vsnprintf.
An error occurred while calling the number. It turns out that the numeric value conversion is incorrect. Continue with the process.
Do_div exception, WTF? This is a macro...
/* We're not 64-bit,... */<br/> # define do_div (n, base)/<br/> ({/<br/> int _ res; /<br/> _ res = (unsigned long) n) % (unsigned INT) base;/<br/> N = (unsigned long) N) /(unsigned INT) base;/<br/> _ res;/<br/> })
To put it simply, there is another problem with the division operation. It is still not the error of division by 0!
What is the problem? Such a simple code, the most basic division, is not even a floating point. What's the problem?
I found out that there was no division command for arm! All arm Division operations are calculated by the software algorithms provided by the compiler.
When compiling a program, arm-Linux-GCC calls the subroutine _ aeabi_uidiv to implement division and uses objdump (ARM-Linux-objdump-D vivi-elf> test. out), view its assembly code:
33f09e58 <__ aeabi_uidiv >:< br/> 33f09e58: e251_1 subs R2, R1, #1; 0x1 <br/> 33f09e5c: 012fff1e bxeq LR <br/> 33f09e60: 3a000074 BCC 33f0a038 <__aeabi_uidiv + 0x1e0> <br/> 33f09e64: e1500001 CMP r0, R1 <br/> 33f09e68: 9a00006b 201733f0a01c <__aeabi_uidiv + 0x1c4> <br/> 33f09e6c: e1110002 TST R1, R2 <br/> 33f09e70: 0a00006c beq 33f0a028 <__aeabi_uidiv + 0x1d0> <br/> 33f09e74: e16f3f10 clz R3, R0 <br/> 33f09e78: e16f2f11 clz R2, R1 <br/> labels: e0423003 sub R3, R2, R3
An undefined instruction exception occurred when a single-step execution was tracked to 0x33f09e74!
Clz what is this command? On the official arm website, we will introduce:
4.4.5. clz
Calculates the number of leading zeros.
Syntax clz {cond} Rd, RM
Where:
Cond
Is an optional conditional code (see conditional execution ).
Rd
Is the target register. Rd cannot be R15.
Rm
Is the operand register. Rm cannot be R15.
Usage:
The clz command can calculate the number of leading zeros of values in RM and store the results to RD. If the bit in the source register is not set, the result is 32. If the bit 31 is set, the result is zero.
Condition mark
This command does not change the flag.
Architecture:
This arm command can be used for armv5 and later versions.
The 32-bit thumb command can be used for armv6t2 and armv7.
This command has no 16-bit thumb version.
Check the CPU information again:
Arm1 V1
Arm2 v2
Arm2as, arm3 v2a
Arm6, arm600, arm610 v3
Arm710, arm700, and arm710 v3
ARM7TDMI, arm710t, arm720t, arm740t v4t
Strong Arm, arm8, arm810 v4
Arm9tdmi, ARM920T, arm940t v4t
ARM9E-S v5te
Arm10tdmi, arm1020e v5te
Arm11, ARM1156T2-S, ARM1156T2F-S, ARM1176JZ-S, ARM11JZF-S v6
Originally, the S3C2440 kernel is ARM920T and only supports v4t commands. clz commands are not supported.
At this point, we already know that it is a compiler configuration problem. Arm-Linux-GCC generates unsupported commands.
Arm-Linux-GCC can generate the command and optimization code of the compilation target through the-M parameter. There are three methods:-mcpu,-mtune, and-March:
-Mcpu = Name
This specifies the name of the target ARM processor. GCC uses this name
Determine what kind of instructions it can emit when generating assembly code.
Permissible names are: arm2, arm250, arm3, arm6, arm60, arm600, arm610, arm620,
Arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700, arm700i, arm710,
Arm710c, arm7100, arm7500, arm7500fe, ARM7TDMI, arm7tdmi-s, arm8, Strongarm,
Strongarm110, strongarm1100, arm8, arm810, arm9e, arm920, ARM920T,
Arm922t, arm946e-s, arm966e-s, arm968e-s, arm926ej-s, arm940t, arm9tdmi,
Arm10tdmi, arm1020t, arm1026ej-s, arm10e, arm1020e, arm1022e, arm1136j-s,
Arm1136jf-s, mpcore, mpcorenovfp, arm1176jz-s, arm1176jzf-s, XScale, iwmmxt,
Ep9312.
-Mtune = Name
This option is very similar to the-mcpu = option, doesn't that instead of speci-
Fying the actual target processor type, and hence restricting which restart UC-
Tions can be used, it specifies that GCC shocould tune the performance of
Code as if the target were of the type specified in this option, but still
Choosing the instructions that it will generate Based on the CPU specified by
-Mcpu = option. For some arm implementations better performance can be obtained
By using this option.
-March = Name
This specifies the name of the target ARM architecture. GCC uses this name
Determine what kind of instructions it can emit when generating assembly code.
This option can be used in conjunction with or instead of the-mcpu = option.
Permissible names are: armv2, armv2a, armv3, armv3m, armv4, armv4t, armv5,
Armv5t, armv5te, armv6, armv6j, iwmmxt, ep9312.
It seems that we only need to add-mcpu = ARM920T during compilation to solve this problem, but the results are still unsatisfactory. As a result, exceptions still occur. We can use objdump to check whether the clz code is generated. After careful consideration, I understand that the Division implementation function _ aeabi_uidiv is linked to Vivi, and the GCC Library link is incorrect!
Check out makefile. The default directory of the Linked Library is/usr/local/ARM/4.3.2/lib/GCC/ARM-None-Linux-gnueabi/4.3.2 /, however, the directory contains a subdirectory armv4t/. Modify the link directory to/usr/local/ARM/4.3.2/lib/GCC/ARM-None-Linux-gnueabi/4.3.2/armv4t /, compile the code again and use objdump to view the code again:
33f09e58 <__ aeabi_uidiv >:< br/> 33f09e58: e251_1 subs R2, R1, #1; 0x1 <br/> 33f09e5c: 012fff1e bxeq LR <br/> 33f09e60: 3a000036 BCC 33f09f40 <__ aeabi_uidiv + 0xe8> <br/> 33f09e64: e1500001 CMP r0, R1 <br/> 33f09e68: 9a000022 201733f09ef8 <__aeabi_uidiv + 0xa0> <br/> 33f09e6c: e1110002 TST R1, R2 <br/> 33f09e70: 0a000023 beq 33f09f04 <__ 0000+ 0xac> <br/> 33f09e74: e311020e TST R1, #-536870912; 0xe0000000 <br/> 33f09e78: 01a01181 lsleq R1, R1, #3 <br/> 33f09e7c: 03a03008 moveq R3, #8; 0x8 <br/> 33f09e80: 13a03001 movne R3, #1; 0x1 <br/> 33f09e84: e3510201 CMP R1, #268435456; 0x10000000 <br/> 33f09e88: 31510000 cmpcc R1, R0
The damn clz command finally completely disappears, and Vivi finally ran smoothly.
A convenient macro for debugging. It is used in the same way as printk. It only outputs the file line number and function name before displaying the debugging content.
# Define debug (...) (printk ("% s @ % s, line % u:" ,__ func __, _ file __,__ line __), printk ("_ va_args __))