Detailed transplantation summary of U-BOOT based on ARM processor

Keywords: Linux IoT ARM uboot

         In the development stage of an embedded product, the bootloader needs to be continuously downloaded to the memory. If the memory uses nand flash, but there is nothing in it for the first time, it can only be started in other ways according to the startup mode of the processor, such as SD card or nor memory, Then, on the basis of SD card or nor memory startup, use USB or network interface to download u-boot.bin into memory first, and then write the contents of memory into nand. However, when writing the first four pages, only the first 2KB data of each page can be written (for OK6410 development board, the processor uses S3C6410 processor, and nand uses 4KB memory per page. When starting from nand, the processor will automatically copy the first 2KB of each page of the first four pages of nand into 8KB SRAM in the chip, which is determined by the processor hardware, so only the first 2KB of each page can be stored here). All pages after the first four pages are full write.

         Since u-boot needs to be debugged continuously during u-boot development, and u-boot already exists in nand, you can start it from nand memory, and then download u-boot to nand again according to the menu options under the development download mode.

         When the u-boot is developed and started from nand, the processor hardware will automatically copy the first 2KB of each page in the first four pages of nand flash to the on-chip SRAM for operation as soon as it is powered on. The code running in SRAM copies the code from 0 to 240KB (this size can be changed) in nand to memory, and then jumps to the memory for operation. When it runs in memory, it will apply for more space (in addition to the memory space occupied by itself, it will include 12 bytes for abort exceptions, stack space, malloc memory pool space, environment parameter space and some global variable space), a total of 2MB. See (iii.2.1) u-boot memory distribution diagram in detail.

         When an embedded product leaves the factory, it already has u-boot, kernel and file system in nand memory and can realize the corresponding functions (if nand memory is used, the compiled image is stored in nand and runs in memory. For S3C6410, it runs in 8KB on-chip SRAM first, and then jumps to memory DDR) The u-boot runtime copies the kernel image in nand to memory, and then runs the kernel.

1, U-Boot-1.1.6 top level Makefile file analysis

(the contents of the top-level makefile file are black, the contents of other files are blue, and the modified code during migration is red)

       According to the instructions of the Readme file in the root directory of uboot, you can know that if you want to use u-boot on the development board, you should configure it first, that is, execute the make orlinx_nand_ram256_config command to configure it (add options such as forlinx_nand_ram256_config target in the top-level directory Makefile) , and then execute make all to generate the following three files: U-Boot.bin, U-Boot ELF format file, and U-Boot.srec.

  1. Configuration process of U-Boot

         (1) Version Description

VERSION_FILE = $(obj)include/version_autogenerated.h

(2) Define host system architecture

HOSTARCH := $(shell uname -m | \
	sed -e s/i.86/i386/ \
	    -e s/sun4u/sparc64/ \
	    -e s/arm.*/arm/ \
	    -e s/sa110/arm/ \
	    -e s/powerpc/ppc/ \
	    -e s/macppc/ppc/)

         “sed   – "e" means a string of command scripts followed, and the expression "s/abc/def /" means to find the one with "abc" from the standard input, and then replace it with "def". The "abc" expression can use "." as a wildcard. The command "uname – m" will output the architecture type of the host CPU. If the computer uses Intel Core2 series CPUs, then “uname   – M "will output" i686 ". I686 can match the command" sed "   - e "i.86" in S / i.86 / i386 / ", so if Makefile is executed on the machine, HOSTARCH will be set to" i386 ".

(3) Define host operating system type

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')
# Deal with colliding definitions from tcsh etc.

         “uname   – S "outputs the host kernel name, and the development host uses the Linux distribution fedora-12, so" uname "   – The result of "s" is "Linux". The function of "tr '[: Upper:]' [: lower:] '" is to convert all uppercase letters in standard input into lowercase letters of response. Therefore, the execution result is to set HOSTOS to "Linux".

(4) Define the shell that executes the shell script (this part is not available in the source code)

# Set shell to bash if possible, otherwise fall back to sh
SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi; fi)

         The function of "$$BASH" is essentially to generate the string "$BASH" (the function of the previous $sign is to indicate that the second $is an ordinary character). If the "$BASH" environment variable is defined in the shell executing the current Makefile and the file "$BASH" is an executable file, the shell value is "$BASH". Otherwise, if "/ bin/bash" is an executable file, the shell value is "/ bin/bash" . if neither of the above is true, assign "sh" to the shell variable. If the machine is installed with BASH   Shell, and "$BASH" is defined in the shell default environment variable, so shell is set to $BASH.

(5) Set compilation output directory

ifdef O 
ifeq ("$(origin O)", "command line")

         The output result of the function $(origin, variable) is a string. The output result is determined by the way the variable is defined. If the variable is defined on the command line, the return value of the origin function is "command line". If the command "export BUILD_DIR=/tmp/build" is executed on the command line, the value of "$(origin O)" is "command line" And BUILD_DIR is set to "/ tmp/build".

         The following indicates that if the directory represented by ${BUILD_DIR} is not defined, the directory will be created:

ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

         The following indicates that if $(BUILD_DIR) is empty, it will be assigned as the current directory path (source code directory). Check whether the $(BUILD_DIR) directory exists:

# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)

         In the following content, the CURDIR variable indicates the current working directory of Make. Since Make executes Makefile in the top-level directory of U-Boot, CURDIR is the top-level directory of U-Boot at this time. After executing the following code, SRCTREE and src variables are the top-level directory of U-Boot code, while OBJTREE and obj variables are the output directory. If BUILD_DIR environment variable is not defined, src The tree and src variables, OBJTREE and obj variables are all U-Boot source code directories, while mkconfig represents the mkconfig script under the U-Boot root directory.


MKCONFIG	:= $(SRCTREE)/mkconfig

ifneq ($(OBJTREE),$(SRCTREE))

# $(obj) and (src) are defined in but here in main Makefile
# we also need them before is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
obj :=
src :=
export obj src

         In the above, mkconfig       := $ (SRCTREE)/mkconfig is the mkconfig file in the root directory.

(6) Execute make   forlinx_nand_ram256_config procedure

         Analyzing this process helps to understand which files need to be modified during U-Boot migration. The premise for executing this command is to add the following contents to the Makefile in the root directory during U-Boot migration:

forlinx_nand_ram256_config :  unconfig
	@$(MKCONFIG)  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256
	Dependency among them“ unconfig"As defined below(Makefile Document 330-350 Line left and right): 
	@rm -f $(obj)include/config.h $(obj)include/ \
		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

         The function of "@" is not displayed in the shell when executing the command. The "obj" variable is the directory of compilation output, so the function of "unconfig" is to clear the last make*_ Configuration files generated by the config command (such as include/config.h, include/, etc.).

        $ (MKCONFIG) is specified as "$(SRCTREE)/mkconfig" in (5) above, that is, the mkconfig file of the root directory. If there is a $(@: _config =), $(@: _config =) is one of all parameters to be passed in_ Config is replaced with null (where "@" refers to the target file name of the rule, here is "forlinx_nand_ram256_config". $(text:patternA=patternB). This syntax means to replace the text of patternA at the end of each element of the text variable with patternB, and then output it). Therefore, $(@: _config =) is used to set forlinx_ nand_ ram256_ In config_ Config is removed and forlinx is obtained_ nand_ Ram256, but there is no $(@: _config =) item in the U-BOOT transplanted by OK6410.

    According to the above analysis, make forlinx is executed_ nand_ ram256_ Config, actually execute the following command:

./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256

         So execute make forlinx_nand_ram256_config "smdk6410"   arm   s3c64xx   smdk6410   samsung   s3c6410   NAND   "Ram256" is passed as a parameter to the mkconfig script in the current directory for execution. The practical significance of these parameters is as follows:

smdk6410: Target (Target board model)

arm: Architecture (CPU architecture of target board)

s3c64xx: CPU (specific CPU model)

smdk6410: Board

Sampling: VENDOR (name of manufacturer)

s3c6410: SOC



In the mkconfig file, you will do the following:

  1. Determine the name of the development board board BOARD_NAME
APPEND=no  		# no means to create a new configuration file, and yes means to append to the configuration file
BOARD_NAME=""  	# Name to print in make output
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
-t) shift ; TARGETS="`echo $1 | sed 's:_: :g'` ${TARGETS}" ; shift ;;
*) break ;;
[ "${BOARD_NAME}" ] || BOARD_NAME ="$1"

         For the command. / mkconfig   smdk6410   arm   s3c64xx   smdk6410   samsung   s3c6410   NAND   Ram256, in which there is no "--" "- a" "- n" "- t" "*" symbol, so nothing is done in the while loop. The two values in the first two lines still maintain the original values, but after the last line is executed, board_ The value of name is equal to the first parameter, smdk6410 (the parameters passed in are represented by $x in the mkconfig file, $0= mkconfig, $1= smdk6410, $2= arm, $3= s3c64xx, $4= smdk6410, $5= samsung, $6= s3c6410, $7= NAND, $8= ram256).

be careful:

         In the U-Boot1.1.6-for-OK6410 source code, board_ The name = "" line is followed by SETMMU="no", which means that mmu is used for nand startup, but the value is set to no here.

  1. Check the validity of parameters

[ $# -lt 4 ] && exit 1

[ $# -gt 9 ] && exit 1

echo "Configuring for ${BOARD_NAME} board which boot from $7 $8 $9..."

    The function of the above code is to check whether the number of parameters and parameters are correct. If the number of parameters is less than 4 or more than 9, it is considered to be wrong. The environment variable $# indicates the number of parameters passed to the script. The command here has 9 parameters, so $# is 9. Configuring for ${BOARD_NAME} board which boot from ... That is, execute make forlinx_ nand_ ram256_ The information output by the serial port terminal after the config command.

  1. Create symbolic connections to platform / development board related header files
# Create link to architecture specific headers
if [ "$SRCTREE" != "$OBJTREE" ] ; then
	mkdir -p ${OBJTREE}/include
	mkdir -p ${OBJTREE}/include2
	cd ${OBJTREE}/include2
	rm -f asm
	ln -s ${SRCTREE}/include/asm-$2 asm
	cd ../include
	rm -rf asm-$2
	rm -f asm
	mkdir asm-$2
	ln -s asm-$2 asm
	cd ./include
	rm -f asm
	ln -s asm-$2 asm  # Symbolic connection, i.e. soft link

         The first line of code judges whether the source code directory and the target file directory are the same. You can choose to compile U-BOOT in other directories, which can keep the source code clean and compile with different configurations at the same time. The source code transplanted by the U-BOOT of OK6410 is compiled in the source code directory, so the source code directory is equal to the target file directory, so if the conditions are not met, the else branch code will be executed.

         In the else branch code, first enter the include directory, delete the asm file (which is the link file established during the last configuration), then create the asm file again and link it to the asm- directory, that is, the asm arm directory. The function of this method is to call header files in the source code, such as #include < asm arm / type. H >, modify "asm XXX architecture" for different architectures, first establish a symbolic link from asm to asm arm, and then directly include header files when they are included later

1: rm -f asm-$2/arch
2: if [ -z "$6" -o "$6" = "NULL" ] ; then
3: 		ln -s ${LNPREFIX}arch-$3 asm-$2/arch
4: else
5: 		ln -s ${LNPREFIX}arch-$6 asm-$2/arch
6: fi
7: if [ "$2" = "arm" ] ; then
8: 		rm -f asm-$2/proc
9: 		ln -s ${LNPREFIX}proc-armv asm-$2/proc
10: fi

         Line 1 deletes the include / ASM arm / arch directory for the command. / mkconfig   smdk6410   arm   s3c64xx   smdk6410   samsung   s3c6410   NAND   ram256, $6 is S3C6410, which is neither empty nor NULL. Therefore, if the conditions in line 2 are not met, execute the else branch. In line 5, LNPREFIX is empty (not defined in the top-level makefile), so ln is executed in line 5  – s   arch-s3c6410   ASM arm / arch (in the source code of U-Boot1.1.6-for-OK6410, include / ASM arm / arch is linked to include / ASM arm / arch-s3c64xx).

       Lines 8-9 indicate that if the target board is an arm architecture, the above code will establish a symbolic connection include / ASM arm / proc to link it to the directory include / ASM arm / proc armv. The advantages of establishing the above links: when compiling U-Boot, you can directly enter the directory pointed to by the linked file for compilation without selecting different directories according to different development boards.

be careful:

    In the source code of U-Boot1.1.6-for-OK6410, the following codes are added between lines 6 and 7:

# create link for s3c24xx SoC

if [ "$3" = "s3c24xx" ] ; then

       rm -f regs.h

       ln -s $6.h regs.h

       rm -f asm-$2/arch

       ln -s arch-$3 asm-$2/arch


# create link for s3c64xx SoC

if [ "$3" = "s3c64xx" ] ; then

       rm -f regs.h

       ln -s $6.h regs.h

       rm -f asm-$2/arch

       ln -s arch-$3 asm-$2/arch


That is, if "$3" = "s3c64xx", include/regs.h will be deleted, and regs.h will be linked to include / S3C6410. The register definition for writing S3C6410 is made in this header file. Then delete the include / ASM arm / arch directory, re-establish and link the include / ASM arm / arch directory to the include / ASM arm / arch-s3c64xx directory.

    Between lines 9 and 10, add the following code:


# create link for s3c64xx-mp SoC

if [ "$3" = "s3c64xx-mp" ] ; then

       rm -f regs.h

       ln -s $6.h regs.h

       rm -f asm-$2/arch

       ln -s arch-$3 asm-$2/arch

       Since $3=s3c64xx, the condition in the above code does not hold, that is, the above code does nothing.

         Create the file include/ contained in the top-level Makfile

# Create include file for Make
echo "ARCH = $2" >   
echo "CPU = $3" >>
echo "BOARD = $4" >>
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >>
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >>

         When executing ". / mkconfig   smdk6410   arm   s3c64xx   smdk6410   samsung   s3c6410   NAND   After the "ram256" command, the contents of the include/ file created by the above lines of code are as follows:

ARCH = arm
CPU = s3c64xx
BOARD = smdk6410
VENDOR = samsung
SOC = s3c6410
  1. Specify the directory where the development board code is located

(optional. If there are already those directories in the U-Boot source code to be migrated, the following code is not required, such as the U-Boot source code officially provided by Samsung.)

# Assign board directory to BOARDIR variable
if [ -z "$5" -o "$5" = "NULL" ] ; then

         The above code specifies a directory under the BOARD directory as the directory of the proprietary code of the current development BOARD. If $5 (VENDOR) is empty, the BOARDDIR is set to $4 (BOARD), otherwise it is set to $5 / $4 (VENDOR/BOARD, i.e. sampling / smdk6410). Here, since $5 is not empty, that is, BOARDDIR is set to sampling / smdk6410.

  1. Create the development board related header file include/config.h
if [ "$APPEND" = "yes" ]			# Append to existing config file
2: then
3:  	echo >> config.h
4: else
5: 		> config.h				# Create new config file
6: fi
7: echo "/* Automatically generated - do not edit */" >>config.h
8: echo "#include <configs/$1.h>" >>config.h
9: exit 0
	">" And“>>"by linux Orders,> config.h Indicates re establishment config.h Documents, echo "#include <configs/$1.h>" >>config.h Express handle#Include < configs / $1. H > is added to the config.h file.
	APPEND "Maintain original value" no",therefore config.h Was re established and the following was added:
/*  Automatically  generated  -  do  not  edit  */
#include <configs/smdk6410.h>
	Come here, include/config.h The above contents are contained in the document.
be careful:
	stay U-Boot1.1.6-for-OK6410 In the source code, the following code is added between lines 7 and 8 for OK6410: 
	case $7 in
	echo "#define FORLINX_BOOT_SD"   >> config.h
	echo "#define FORLINX_BOOT_NAND" >> config.h
case $8 in
	echo "#define FORLINX_BOOT_RAM128" >> config.h
         > ../board/samsung/smdk6410/    # clear file context
        echo "ifndef TEXT_BASE"  >> ../board/samsung/smdk6410/
        if [ ${SETMMU} = "yes" ]
         echo "TEXT_BASE = 0xC7E00000" >> ../board/samsung/smdk6410/
         echo "TEXT_BASE = 0x57E00000" >> ../board/samsung/smdk6410/
       echo "endif" >> ../board/samsung/smdk6410/
	echo "#define FORLINX_BOOT_RAM256" >> config.h
         > ../board/samsung/smdk6410/ # clear file context
        echo "ifndef TEXT_BASE"  >> ../board/samsung/smdk6410/
        if [ ${SETMMU} = "yes" ]
         echo "TEXT_BASE = 0xCFE00000" >> ../board/samsung/smdk6410/
         echo "TEXT_BASE = 0x5FE00000" >> ../board/samsung/smdk6410/
        echo "endif" >> ../board/samsung/smdk6410/
if [ "$9" = "hdmi" ] ; then
	echo "#define FORLINX_LCDOUT_HDMI" >> config.h
	about OK6410,$7=NAND,So I will#define FORLINX_ BOOT_ Add nand to the include/config.h file and set the SETMMU value to Yes (set the SETMMU value to no in the code for determining the board name BOARD_NAME above. If you need mmu to start from nand, it is set to yes here).
	about OK6410,$8=ram256,So I will#define FORLINX_BOOT_RAM256 Add to include/config.h In the document, and will include Upper level directory of/board/samsung/smdk6410/ file(Directory of development board code)empty(Execute“> ../board/samsung/smdk6410/ # The line "clear file context" is cleared), and then add ifndef text to it after clearing_ Base, since the value of SETMMU is yes, text is added to it_ Base = 0xcfe00000 (this address is the mapped address. After using mmu, this memory address is mapped. If it is 128 memory, this value is 0XC7E00000, as long as it does not exceed the virtual address corresponding to the maximum range physical address 0x6FFFFFFF of DMC1). Finally, add endif.
	about OK6410,$9 It was not passed in, so it was not put#define FORLINX_ LCDOUT_ Add HDMI to the / include/ config.h file.

         In general, execute make forlinx_ nand_ ram256_ After the config command, all the actions in point (6) above will be performed in the mkconfig script. From these actions, you can see that you need to create a new development board < board in the board directory_ Name > directory and create a < board > directory in the include / configure directory_ Name >. H, which stores the development board < board_ Name > configuration information.

       U-Boot does not have the same visual configuration interface as Linux (such as make menuconfig). You need to manually modify the configuration header file / include / configurations / smdk6410. H to crop and set U-Boot. There are two types of macros in this configuration header file:

  • One is options, prefixed with "CONFIG_", which are used to select CPU, SOC, development board type, set system clock, select device driver, etc. For example:

#define CONFIG_S3C6410          1      /* in a SAMSUNG S3C6410 SoC      */

#define CONFIG_S3C64XX       1      /* in a SAMSUNG S3C64XX Family   */

#define CONFIG_SMDK6410      1      /* on a SAMSUNG SMDK6410 Board  */

  • The other is the parameter (Setting), prefixed with "CFG_", which is used to set the size of malloc buffer pool, the prompt of U-BOOT, the default loading address when U-BOOT downloads files, the starting address of Flash, etc. For example:

#define CFG_MALLOC_LEN  (CFG_ENV_SIZE+128*1024)

#define CFG_PROMPT  

#define CFG_LOAD_ADDR  0x50000000

From the following compilation and linking process, U-Boot Almost every file in is compiled and linked, but whether such a file contains valid code is set by the macro switch. For example, for network card drivers drivers/dm9000x.c,Its format is:
#include <common.h>
#include <command.h>
#include <net.h>

#include "dm9000x.h"

/*Actual code*/

#endif				/* CONFIG_DRIVER_DM9000 */

         If macro config is defined in / include / config / smdk6410. H_ DRIVER_ Dm9000, the file contains a valid code; Otherwise, the file is commented empty.

    It can be said that in addition to setting some parameters, "CONFIG_" is mainly used to set the function of U-Boot and select which part of the file to use; CFG is used to set more detailed parameters.

2. Compilation and linking process of u-boot (followed by Makefile analysis in the top-level directory)

(1) Residual code analysis of top-level file and top-level Makefile file

After configuration, execute "make"   "All" to compile. If you directly execute the "make all" command without executing the "make forlinx_nand_ram256_config" command, an error message "System not configured - see README" will appear,

Then stop compiling.

       After executing the "make forlinx_nand_ram256_config" command, a file will be created in the include directory. U-Boot determines whether there is this file and whether the user has executed the "make forlinx_nand_ram256_config" command. Note that exit 1 returns. The relevant code is as follows:

114:  ifeq ($(OBJTREE)/include/,$(wildcard $(OBJTREE)/include/
241:  all: 
249:  $(obj)u-boot.bin:	$(obj)u-boot
250:       $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
322:  else
327: 	 @echo "System not configured - see README" >&2
328: 	 @ exit 1
329:  endif
	On the top floor Makefile Continue to analyze the following codes:
# load ARCH, BOARD, and CPU configuration
117:  include $(OBJTREE)/include/
127:  ifeq ($(ARCH),arm)
128:  CROSS_COMPILE = arm-linux-
129:  endif
162:  export	CROSS_COMPILE
164:  # load other configuration
165:  include $(TOPDIR)/

Lines 127-129 in the above code indicate that the architecture of ARCH is the same as that of the target machine, so the arm Linux compiler (ARCH=arm) is used.

be careful:

In the U-Boot1.1.6-for-OK6410 source code, the following code is added in front of 162 lines:

CROSS_COMPILE = /usr/local/arm/4.3.2/bin/arm-linux - that is, regardless of the above judgment, the cross compiler always uses the arm Linux compiler in the / usr/local/arm/4.3.2/bin directory.

       Lines 117 and 165 contain the file, and line 117 makes forlinx_ nand_ ram256_ The include/ generated by the config command is included, that is, the include/ file produced during the configuration process, in which the values of ARCH/CPU/BOARD/VENDOR/SOC are defined as arm, s3c64xx, smdk6410, samsung and s3c6410 respectively. Line 165 contains the file under the top-level directory of U-Boot, which contains some settings for compilation. It determines the compiler and compilation options according to the value of ARCH/CPU/BOARD/VENDOR/SOC variables.

Top level file analysis:      

	set up obj And src
ifneq ($(OBJTREE),$(SRCTREE))
ifeq ($(CURDIR),$(SRCTREE))
dir :=
dir := $(subst $(SRCTREE)/,,$(CURDIR))

obj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/)
src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/)

$(shell mkdir -p $(obj))
obj :=
src :=
	Since the target is output to the source code directory, after executing the above code, src and obj It's all empty.
	Set compilation options
PLATFORM_CPPFLAGS =     #Compile options
PLATFORM_	 =     			#Connection options
 These three variables are used to represent the compilation options of the cross compiler, which will be described later Make The compilation options supported by the cross compiler are checked and the appropriate options are added to these three variables.
	Contains configuration files related to the development board
(Skip lines 54 to 74, which are in NetBSD Definitions required when using the cross compiler on)
ifdef	 ARCH
sinclude $(TOPDIR)/$(ARCH)	# include architecture dependend rules
$(ARCH)The value of is“ arm",Therefore, the“ arm _"Included, while the top-level directory arm _ Basically nothing is done in the file, only one line of code is used to set PLATFORM_CPPFLAGS Compile options(And arm Processor related). 
ifdef	CPU
sinclude $(TOPDIR)/cpu/$(CPU)/		# include  CPU	specific rules
$(CPU)The value of is“ s3c64xx",So will“ cpu/ s3c64xx /"Included. This script is mainly
 Set with s3c64xx Processor related compilation options(And arm dependent PLATFORM_CPPFLAGS Compile options). 
ifdef	SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/	# include  SoC	specific rules
$(SOC)The value of is s3c6410,therefore Make The program attempts to cpu/s3c64xx/s3c6410/ Included, and this file does not exist, but because it uses“ sinclude"Command, so there will be no error.
ifdef	 VENDOR
$(BOARD)The value of is smdk6410,VENDOR The value of is samsung,therefore BOARDDIR The value of is samsung/ smdk6410. BOARDDIR The variable represents the directory where the code specific to the development board is located.
ifdef  BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/	# include board specific rules
 Under the top-level directory File will“ board/samsung/smdk6410/"Included. The script is as follows:
ifndef  TEXT_BASE
TEXT_BASE = 0xCFE00000
U-Boot Will be used at compile time TEXT_BASE As the starting address of the code segment connection(This address is via MMU Mapped). 
	Other code 1 (line 95)---115 (row)
CONFIG_SHELL	:= $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
		    else if [ -x /bin/bash ]; then echo /bin/bash; \
		    else echo sh; fi ; fi)

ifeq ($(HOSTOS)-$(HOSTARCH),darwin-ppc)
HOSTCC		= cc
HOSTCC		= gcc
HOSTCFLAGS	= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer

cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
		> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
In the last two lines of code above, the variable CC and CFLAGS In the following code, it is defined as a delay variable, in which CC Namely arm-linux-gcc. function cc-option Used to check the compiler CC Whether an option is supported. Pass 2 options as parameters to cc-option Function, which calls CC The compiler checks whether parameter 1 is supported. If yes, the function returns parameter 1; otherwise, it returns parameter 2 (so CC The compiler must support parameter 1 or parameter 2. If both are not supported, there will be a compilation error). It can be called as follows cc-option Function and add supported options to the FLAGS Medium:
FLAGS +=$(call cc-option,option1,option2)
	Specifies the cross compilation tool
CPP	= $(CC) -E

         For arm architecture processors, CROSS_COMPILE is defined in the top-level makefile file:

CROSS_COMPILE = /usr/local/arm/4.3.2/bin/arm-linux - (about 161 lines)

Therefore, the above code specifies the compilation tools prefixed with "arm linux -" namely arm linux GCC, arm linux LD, etc.

  • Other code 2   (130 lines -- 217 lines)

         In this part of the code, 143 lines (LDSCRIPT: = $(TopDir) / board / $(boarddir) / give the value of LDSCRIPT, which is / board/Samsung/smdk6410/ = means to replace the previous value) in the top-level directory, and 189 lines of code are as follows:


     Therefore, make the LDFLAGS contain "- Bstatic" according to line 189   - T /board/Samsung/smdk6410/ "and" - Ttext   The words "0xCFE00000" (= indicates addition).

  • Specify implicit Compilation Rules

         In the last part of the code (lines 218-246), specify the implicit Compilation Rules. For example, according to the above definition, the target file ending in ". S" will be generated from the source file with the same name but suffix ". S" according to the first rule. If there is no file with the same name ending in ". S", it will be generated from the ". c" file with the same name according to the last rule.

The above is the content of the file in the top-level directory, and the following is the rest of the Makefile in the top-level directory.

166: #########################################################################
167: # U-Boot objects....order is important (i.e. start must be first)
169: OBJS  = cpu/$(CPU)/start.o
170: ifeq ($(CPU),i386)
171: OBJS += cpu/$(CPU)/start16.o
172: OBJS += cpu/$(CPU)/reset.o
173: endif
174: ifeq ($(CPU),ppc4xx)
175: OBJS += cpu/$(CPU)/resetvec.o
176: endif
177: ifeq ($(CPU),mpc83xx)
178: OBJS += cpu/$(CPU)/resetvec.o
179: endif
180: ifeq ($(CPU),mpc85xx)
181: OBJS += cpu/$(CPU)/resetvec.o
182: endif
183: ifeq ($(CPU),mpc86xx)
184: OBJS += cpu/$(CPU)/resetvec.o
185: endif
186: ifeq ($(CPU),bf533)
187: OBJS += cpu/$(CPU)/start1.o	cpu/$(CPU)/interrupt.o	cpu/$(CPU)/cache.o
188: OBJS += cpu/$(CPU)/cplbhdlr.o	cpu/$(CPU)/cplbmgr.o	cpu/$(CPU)/flush.o
189: endif
191: OBJS := $(addprefix $(obj),$(OBJS))
193: LIBS  = lib_generic/libgeneric.a
194: LIBS += board/$(BOARDDIR)/lib$(BOARD).a
195: LIBS += cpu/$(CPU)/lib$(CPU).a
196: ifdef SOC
197: LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
198: endif
199: LIBS += lib_$(ARCH)/lib$(ARCH).a
200: LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
201: 	fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
202: LIBS += net/libnet.a
203: LIBS += disk/libdisk.a
204: LIBS += rtc/librtc.a
205: LIBS += dtt/libdtt.a
206: LIBS += drivers/libdrivers.a
207: LIBS += drivers/nand/libnand.a
208: LIBS += drivers/nand_legacy/libnand_legacy.a
209: LIBS += drivers/sk98lin/libsk98lin.a
210: LIBS += post/libpost.a post/cpu/libcpu.a
211: LIBS += common/libcommon.a
214: LIBS := $(addprefix $(obj),$(LIBS))
215: .PHONY : $(LIBS)
217: # Add GCC lib
218: PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
220: # The "tools" are needed early, so put this first
221: # Don't include stuff already done in $(LIBS)
222: SUBDIRS	= tools \
223: 	  examples \
224: 	  post \
225: 	  post/cpu
226: .PHONY : $(SUBDIRS)
228: ifeq ($(CONFIG_NAND_U_BOOT),y)
229: NAND_SPL = nand_spl
230: U_BOOT_NAND = $(obj)u-boot-nand.bin
231: endif
233: __OBJS := $(subst $(obj),,$(OBJS))
234: __LIBS := $(subst $(obj),,$(LIBS))
236: #########################################################################
237: #########################################################################
239: ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj) $(U_BOOT_NAND)
241: all:		$(ALL)
243: $(obj)u-boot.hex:	$(obj)u-boot
244: 		$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
246: $(obj)u-boot.srec:	$(obj)u-boot
247: 		$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
249: $(obj)u-boot.bin:	$(obj)u-boot
250: 		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
252: $(obj)u-boot.img:	$(obj)u-boot.bin
253: 		./tools/mkimage -A $(ARCH) -T firmware -C none \
254: 		-a $(TEXT_BASE) -e 0 \
255: 		-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
256: 			sed -e 's/"[	 ]*$$/ for $(BOARD) board"/') \
257: 		-d $< $@
259: $(obj)u-boot.dis:	$(obj)u-boot
260: 		$(OBJDUMP) -d $< > $@
262: $(obj)u-boot:		depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
263: 	UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
264: 		cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
265: 			--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
266: 			-Map -o u-boot
268: $(OBJS):
269: 		$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
271: $(LIBS):
272: 		$(MAKE) -C $(dir $(subst $(obj),,$@))
274: $(SUBDIRS):
275: 		$(MAKE) -C $@ all
277: $(NAND_SPL):	version
278: 		$(MAKE) -C nand_spl/board/$(BOARDDIR) all
280: $(U_BOOT_NAND):	$(NAND_SPL) $(obj)u-boot.bin
281: 		cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
283: version:
284: 		@echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
285: 		echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
286: 		echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
287: 			 $(TOPDIR)) >> $(VERSION_FILE); \
288: 		echo "\"" >> $(VERSION_FILE)
290: gdbtools:
291: 		$(MAKE) -C tools/gdb all || exit 1
293: updater:
294: 		$(MAKE) -C tools/updater all || exit 1
296: env:
297: 		$(MAKE) -C tools/env all || exit 1
299: depend dep:
300: 		for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
302: tags ctags:
303: 		ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \
304: 				lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
305: 				fs/cramfs fs/fat fs/fdos fs/jffs2 \
306: 				net disk rtc dtt drivers drivers/sk98lin common \
307: 			\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
309: etags:
310: 		etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \
311: 				lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
312: 				fs/cramfs fs/fat fs/fdos fs/jffs2 \
313: 				net disk rtc dtt drivers drivers/sk98lin common \
314: 			\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
316: $(obj)	$(obj)u-boot
317: 		@$(NM) $< | \
318: 		grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
319: 		sort > $(obj)
321: #########################################################################
322: else
323: all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
324: $(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
325: $(SUBDIRS) version gdbtools updater env depend \
326: dep tags ctags etags $(obj)
327: 	@echo "System not configured - see README" >&2
328: 	@ exit 1
329: endif
333: 	git log --no-merges U-Boot-1_1_5.. | \
334: 	unexpand -a | sed -e 's/\s\s*$$//' > $@
336: #########################################################################
338: unconfig:
339: 	@rm -f $(obj)include/config.h $(obj)include/ \
340: 		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

         In line 169 of the above code, the first value of OBJS is "cpu/$(CPU)/start.o", i.e. "cpu/s3c64xx/start.o". According to the comments in the previous line, OBJS (target) in u-boot is very important, and start.o must be placed first, i.e. start.s compiled first. 170-191 in the above code is the target of other architecture CPUs, which is of little use here.

be careful:

       The 175-177 lines of code are not in the U-Boot1.1.6-for-OK6410 source code. Is there any impact in theory.

         In lines 193 to 218 of the code, the LIBS variable indicates the library files required for U-Boot, including the directories related to the platform / development board and the corresponding libraries under the general directory, which are compiled through the corresponding subdirectories. In line 215, the pseudo target (. Phony: $(LIBS)) is defined, which means that if there is a value corresponding to the LIBS variable (i.e. the library file required for U-Boot,. A file) in the current directory, After making, "*. A is up to date" will not be displayed, and the corresponding. A file will be recompiled.

Note: between lines 208-209, the following code is added to the source code of U-Boot1.1.6-for-OK6410:

# add to support onenand. by scsuh
LIBS += drivers/onenand/libonenand.a
ifeq ($(CPU),mpc83xx)
LIBS += drivers/qe/qe.a
	Increase pair onenand flash Support.

         Lines 228 to 231 are platform independent code, because for some development boards (including OK6410), U-Boot supports starting in nand flash, and the configuration file of these development boards may have macro defined CONFIG_NAND_U_BOOT, which depends on u in line 239_ BOOT_ NAND will not be empty. 239 lines of code, that is, after executing make all, u-boot.srec, u-boot.bin and will be generated. u-boot.srec is the Motorola S-Record format file, is the symbol table of U-Boot, and u-boot.bin is the binary executable file finally written to the development board. If U_BOOT_NAND is not empty, and the u-boot-nand.bin file will also be generated.

         The. o,. A files represented by OBJS and LIBS are the composition of U-boot. They are compiled from the corresponding source files (or files in the corresponding subdirectories) through 268-272 line commands. The rules in lines 268-269 indicate that for each member of OBJS, they will be compiled in the cpu/$(CPU) directory (i.e. cpu/s3c64xx). For smdk6410 development board, OBJS is cpu/s3c64xx/start.o, which will be compiled by cpu/s3c64xx/start.S. The rule in lines 271-272 indicates that for each member in LIBS, it will enter the corresponding subdirectory and execute the "make" command. The makefiles of these subdirectories are similar in structure. They compile and link the files specified in the Makefile into a library file.

         After all. o and. a files represented by OBJS and LIBS are generated, connect them finally, which corresponds to lines 243 to 267 in the above code. First, use the rule link of lines 262-266 to obtain the u-boot in ELF format, and finally convert them to u-boot.bin in binary format, U-Boot.srec in S-Record format, etc. (i.e. lines 243-261). The dependency of ELF format u-boot is described below:

  • Dependent target depend ent

The behavior depends on the rule of target dependent. For $(SUBDIRS) in line 300, enter the directory and execute "make _depend ent", generate. Dependent files of each subdirectory, and. Dependent lists the dependent files of each target file.

  • Dependent on SUBDIRS

The version dependency, that is, the version of U-Boot, is not described in detail here. The 274-275 behavior depends on the rules of subdir. The values 222-226 of subdir are defined, so the makefile in the tools, examples, post and post/cpu directories will be executed.

  • Rely on OBJS and LIBS

These two dependencies have been described above.

  • Dependent LDSCRIPT

This dependency is defined in the top-level file. The value of LDSCRIPT is / board/Samsung/smdk6410/ in the top-level directory.

        In the u-boot rule command, lines 264-266 represent the real connection, and LDFLAGS determines the connection mode. In LDFLAGS, "board/Samsung/smdk6410/" and "- Ttext   0xcfe00000 "(according to the top-level file), these words specify the layout and address of the program (but the starting address of the connection is the offset address 0 + 0xcfe00000 in the connection script). Therefore, $(LDFLAGS) uses the link script and - Ttext   0xCFE00000. Executing the connection command is actually to connect the library files generated by start.o and makefile s in each subdirectory together according to LDFLAGS to generate ELF file u-boot and connection memory allocation diagram file

Note: between 250 and 251 lines in the above code, a line of code is added to the source code of U-Boot1.1.6-for-OK6410: $(objdump) - D $< > $<. Dis. The purpose is to generate the disassembly file of U-Boot.

         The code after 341 lines in the top-level makefile code is related to adding CPU architecture_ Config file (the rest of the makefile is the definition of * _config: target of various development boards). The development board OK6410 adds the following (of course, other processors of Samsung s3c24 and 64 Series):

forlinx_nand_ram256_config :  unconfig

    @$(MKCONFIG) smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256

Note: at the end of the makefile file in the top-level directory is the rule after making clean. Two lines of code are added to the source code of U-Boot1.1.6-for-OK6410: after 2261 lines respectively:         

-o -name '*~' -o -name '.depend*' \

Added after line 2263:

 rm -f u-boot*

(2) Link script analysis (/ board/Samsung/smdk6410/

24: OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
25: /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
26: OUTPUT_ARCH(arm)   /* Specify the output platform as arm */
27: ENTRY(_start)
29: {
30: 	. = 0x00000000; 
32: 	. = ALIGN(4);  /* The code is aligned with 4 bytes */
33: 	.text      :    /* Specifies the code segment, and the base address of. Text is specified by - Ttext $(TEXT_BASE) in LDFLAGS*/
34: 	{
35: 	  cpu/s3c64xx/start.o	(.text)  /* The first part of the code snippet */
36: 	  cpu/s3c64xx/s3c6410/cpu_init.o	(.text)
37: 	  cpu/s3c64xx/onenand_cp.o	(.text)
38: 	  cpu/s3c64xx/nand_cp.o	(.text)
39: 	  cpu/s3c64xx/movi.o (.text) 
40: 	  *(.text)  				/*Other code parts*/
41: 	  lib_arm/div0.o
42: 	}
44: 	. = ALIGN(4);  
45: 	.rodata : { *(.rodata) }   /*Specifies a read-only data segment*/
47: 	. = ALIGN(4);
48: 	.data : { *(.data) }     /*Specifies whether to read or write data segments*/
50: 	. = ALIGN(4);
51: 	.got : { *(.got) }
53: 	__u_boot_cmd_start = .;
54: 	.u_boot_cmd : { *(.u_boot_cmd) }
55: 	__u_boot_cmd_end = .;
57: 	. = ALIGN(4);
58: 	.mmudata : { *(.mmudata) }
60: 	. = ALIGN(4);
61: 	__bss_start = .;     /*Handle__ u_boot_start is assigned to the current position, that is, the start position of the bss segment*/
62: 	.bss : { *(.bss) }    /*Specify bss segment*/
63: 	_end = .;          /*Handle_ End is assigned to the current position, that is, the end position of the bss segment*/
64: }

Complete description of. lds file form:

SECTIONS {defines the segments contained in the domain


secname start BLOCK(align) (NOLOAD) : AT( ldadr )

{ contents } >region :phdr =fill



secname and contents are required, and others are optional. Here are some common ones:

  1. secname: segment name, such as text segment, data segment, read-only data segment and BSS segment
  2. Contents: determine which contents are placed in this section, which can be the whole target file or a section (code section, data section, etc.) in the target file
  1. Start: the connection (operation) address of this segment. If AT (ldadr) is not used, the address stored in this segment is also start. GNU website says that start can be described by any symbol describing the address.

         The link script of U-Boot reflects how many raw materials form U-Boot, who puts it in the front, who puts it in the back, etc. code 30 above indicates that the starting address is 0, but the content text_base in the file in the same directory of the link script should be added (the value is 0xCFE00000, which has been mapped by MMU) Therefore, the link address of U-Boot is 0xCFE00000, that is, it runs at 0xCFE00000. That is, the system starts from the offset address 0, where 0 is only a code address offset value, and the real connection start address is specified by LDFLAGS when compiling the connection.

       Line 24 indicates that the specified output executable file is elf format, 31 bit ARM instruction, small end storage,

Line 35 indicates that CPU/S3C64xx/start.S is placed at the front of the code segment to ensure that start.o is executed first, so the entry point of U-Boot is also in CPU/S3C64xx/start.S. lines 36-39 must be placed in the first 8KB of U-Boot, so they are also placed at the front, including memory initialization. Line 38 includes the code that U-Boot carries itself from NAND Flash.

       Lines 50-51 represent the got segment customized by U-Boot, lines 53 / 55 represent specifying __boot_cmd_start as the current address, and lines 54 represent storing the cmd_tbl_t structure corresponding to all U-Boot commands.

3. Analysis and summary of u-boot top-level Makefile file

  • Determine U-Boot version information
  • Determine the development host architecture and operating system type
  • Determine the source code directory and output directory, that is, the values of src, SRCTREE, obj and OBJTREE
  • MKCONFIG     := $ (SRCTREE)/mkconfig executes make forlinx_nand_ram256_config

  Set "smdk6410"   arm   s3c64xx   smdk6410   samsung   s3c6410   NAND   "ram256" is passed as a parameter to the mkconfig script in the current directory for execution, so skip to the mkconfig script in the current directory for execution. The basic work in this script is as follows:

  (1) Establish symbolic links

  (2) Create the / include/ file contained in the top-level Makefile (this file does not need to be created manually during migration)

  (3) Specify the relevant directory of the development board, i.e. / board / sampling / smdk6410 directory. (during migration, this directory and its contents need to be created or modified by yourself, such as, lowlevel_init.S, smdk6410.c,

  (4) Create a header file related to the development board, include / config. H (this file does not need to be created manually during migration). This header file contains the / include/configs/smdk6410.h configuration header file (so this configuration header file needs to be created manually during migration)

  • The /include/ file that is created in the mkconfig script (as long as the "make forlinx_nand_ram256_config" command is executed, invokes the mkconfig script in the Makefile of the top-level directory, and creates the file in it).
  • It contains the file in the top-level directory, and the file in the top-level directory also contains the / board / sampling / smdk6410 / file related to the development board.

         Finally, compile, connect and generate the target. That is, the actual work of the top-level Makefile is as follows:

  (1) First compile / CPU/S3C64xx/start.S.

 (2) then, for each directory and general directory related to the platform / development board, use their respective Makefile to generate the corresponding library.

 (3) connect the. o,. a files generated in steps 1 and 2 according to the code segment starting address and connection script specified in / board/Samsung/smdk6410/ file.

 (4) convert the U-Boot of ELF format obtained in step 3 into binary format and S-Record format.

    Of course, during migration, the files in the cpu/s3c64xx and cpu/s3c64xx/s3c6410 directories also need to be modified or created.

2, U-Boot-1.1.6 directory structure, startup mode and startup process

1. Directory structure

Board: Store some files related to existing development boards. Each development board appears in the current directory as a subdirectory,
The configuration files related to the development board are stored in the subdirectory board\samsung\smdk6410 There are:
smdk6410.c Board related code(with smdk6410 take as an example) 
flash.c Flash Operation code
smdk6410_val.h Corresponding connection file
common: General, implementation uboot Commands supported on the command line. Each command corresponds to a file. For example bootm The command corresponds to cmd_bootm.c. In addition, write more important c Documents, such as main.c,hush.c Wait.
cpu: With specific CPU Architecture related directories, each Uboot Supported under CPU Corresponding to a subdirectory under this directory,
For example, there are subdirectories s3c64xx Wait cpu\s3c64xx The main documents are as follows:(Not all): 
cpu.c Processor related code
interrupts.c Interrupt handling code
serial.c Serial port initialization code
start.S Global start code
 stay cpu\s3c64xx\s3c6410 The following documents are available:
lib_xxxx: Architecture related library files, such as ARM Relevant libraries are placed in lib_arm Yes.
lib_microblaze: Storage pair ARM Architecture common file, mainly used for implementation ARM Platform general functions.
lib_generic: General, the implementation of all architecture general library functions, such as vsprintf,string Equal function implementation.
Include: Uboot The header files used, as well as the assembly files supported by various hardware platforms, system configuration files and files supported by the file system configs The directory has configuration header files related to the development board, such as smdk6410.h. Under this directory asm Directory has and CPU Architecture related header files, asm Established symbolic link to asm-arm. 
drivers: General, Uboot The supported device drivers are placed in this directory, such as various network cards and support CFI of Flash,Serial port and USB Wait.
disk: General, disk support, hard disk interface driver
dtt: General, sensor driver
fs: General, storing file system related programs, Uboot Now support cramfs,fat,fdos,jffs2 and registerfs.  
nand_spl: General, Nand Flash boot Procedure
net: General purpose, storing network related programs, i.e. code related to network protocol stack, BOOTP Agreement TFTP Agreement RARP Agreement and NFS Implementation of file system.
Post: General, store the power on self-test program
Rtc: Universal, real-time clock(RTC)Driver for
Examples: Application routines, some examples of independently running applications, such as helloworld
Tools: Tools, storage and production S-Record perhaps U-boot Tools such as image format, such as mkimage
Doc: Documents, development and use documents

         The important directories to be concerned about during migration are as follows:

Include,include/configs,board/Samsung/smdk6410,cpu/s3c64xx,cpu/s3c64xx/s3c6410,common,lib_arm,drivers,lib_generic,nand_spl et al.

2. Introduction to startup mode

         Most boot loaders contain two different operating modes: start load mode and download mode, which is meaningful only for developers. However, from the perspective of end users, the function of Boot Loader is to load the operating system, and there is no difference between the so-called startup loading mode and download working mode.

         Boot loading mode: this mode is also called "Autonomous" mode. That is, the Boot Loader loads the operating system into RAM from a solid-state storage device on the target machine without user intervention. This mode is the normal working mode of BootLoader, so it is obvious that Boot Loader must work in this mode when embedded products are released.

         Downloading mode: in this mode, the Boot Loader on the target machine will download files from the Host through communication means such as serial port connection or network connection, such as downloading kernel image and root file system image. The files downloaded from the Host are usually saved to the RAM of the target machine by BootLoader, and then written to the FLASH solid-state storage device on the target machine by BootLoader. This mode of BootLoader is usually used when installing the kernel and root file system for the first time; In addition, future system updates will also use this working mode of BootLoader. Boot Loader working in this mode usually provides a simple command line interface to its end users.

         A powerful Boot Loader such as uboot supports both working modes and allows users to switch between the two working modes. Most bootloader s are divided into two parts: Stage 1 and stage 2, and uboot is no exception. Code that depends on CPU architecture (such as CPU initialization code) is usually placed in phase 1 and implemented in assembly language, while phase 2 is usually implemented in C language, which can realize complex functions and have better readability and portability.

3. Start the process

         The stage1 code of u-boot is usually placed in start.s and the file lowlevel - init.S, which is written in assembly language. For the second stage, the C language code part, lib_ Start in arm / board. C_ Armboot is the starting function of C language, the main function of C language in the whole startup code, and the main function of the whole u-boot(armboot).

3, Source code analysis of U-Boot startup process

1.U-Boot phase I code analysis

U-Boot It belongs to two stages Bootloader,The first stage documents are/CPU/S3C64xx/start.S and board/Samsung/smdk6410/lowlevel_init.S. The former is platform related and the latter is development board related.
|-->lowlevel_init: |
|-->cup_init_crit: |  	         |-->cpu_init_crit:|
_start: -->reset|  					                   |-->reset: -->relocate: -->_start_armboot:-->Start_armboot() -->main_loop() ---|
↑        | 
u-boot-1.1.6/cpu/xxx/Start.S   _start: 
u-boot-1.1.6/cpu/xxx/Start.S   reset: 
u-boot-1.1.6/cpu/xxx/Start.S   cup_init_crit: 
u-boot-1.1.6/board/Samsung/yyy/lowlevel_init.S lowlevel_init: 
u-boot-1.1.6/cpu/xxx/Start.S   relocate: 
u-boot-1.1.6/cpu/xxx/Start.S   _start_armboot: 
u-boot-1.1.6/lib_arm/board.c   start_armboot() 
u-boot-1.1.6/common/main.c     main_loop() 
explain: xxx(Specific on the board cpu Model catalog, e.g arm920t,S3C64xx) 
yyy(Model catalog of development board, such as smdk2410,smdk6410)

(1) Hardware device initialization (in / CPU/S3C64xx/start.S file)

  • Set global entry
  • Jump to reset, that is, the second instruction of U-boot
  • Put CPU into SVC management mode
  • Initialize CPU key registers, such as clearing instruction cache I Cache and data cache D Cache, prohibiting MMU, etc.

be careful:

    In the U-Boot1.1.6-for-OK6410 source code, since nand is used for startup, the following code starts MMU.

       For the detailed part of the start.S file, check the annotated start.S file in the directory of this document, and cooperate with the start.S in the previous naked running program, the file in this directory, and the "instruction level detailed analysis. mht of the start_S source code in Uboot".

(2) Further initialization

    Execute bl in the CPU/S3C64xx/start.S file   lowlevel_init command, enter board / Samsung / smdk6410 / lowlevel_ Perform further initialization in the init. S file. Note that the memory initialization is in cpu\s3c64xx\s3c6410\cpu_init.S file.

  • First save lr in r12, and then set some GPIO and led
  • Set each write of MEM1DRVCON register to 0, i.e. 5mA
  • Shut the watchdog
  • Change the interrupt mode to irq and clean up the interrupt.
  • Initialize system clock
  • Initialize serial port
  • Initialize nand flash   (simple initialization, setting time parameters, enabling nand controller, etc.)
  • MMU Table for SMDK6410
  • Initialize DDR

See board / Samsung / smdk6410 / lowlevel for details_ Init. S file, which has detailed notes. For the initialization of system clock, serial port, nand and DDR, you can refer to the driver of naked running.

(3) Code handling, i.e. relocation

         First, judge whether the PC pointer at this time points to the memory, that is, judge whether it has been running in the memory. Since it is started from nand (the first 8KB is copied to the on-chip SRAM), it is impossible to be in the DDR at this time, so code relocation is required.

         From lowlevel_ After returning from init. S file, jump to copy_ from_ At the nand label, it is still in the Start.S file until it jumps to the C function interface copy_uboot_to_ram, the code of this interface is in cpu\s3c64xx\nand_cp.c. in this file, for the nand flash of OK6410, when copying the U-Boot to the memory, copy it from the 0 address of nand to 0x5FE00000 of DDR, and the copy size is 0x3c000.

         After relocation, the MMU is opened.

(4) Set stack and clean BSS segment

        In the / CPU/S3C64xx/start.S file, since the MMU is enabled, the stack pointer is already at 0xC7FFFFF4.

         Before jumping to the second stage entry, you need to clean up the BSS segment. Global variables and static variables with an initial value of 0 and no initial value are placed in the BSS segment.

2.U-Boot phase II code analysis

          In the / CPU/S3C64xx/start.S file, enable the MMU, and then reset the stack. After cleaning up the BSS section, the running environment of the C function has been fully prepared, and then through ldr    pc, _ start_ The armboot instruction jumps to lib_ In the arm / board. C file. The program after this instruction is executed in the memory DDR (because the position related instruction ldr is used when jumping, and it will jump to the connection address at this time).

             The first phase of the u-boot startup (assembly part) was introduced, where some simple initialization, and the environment (stack) for the execution of the C language, the first function (start_armboot ()) executed from the assembly to C, was initialized, and then the main_ was called. Loop(), select whether to directly load the Linux kernel or enter the command waiting mode according to the configuration.

(1) U-Boot memory distribution

         After u-boot configuration, compilation and connection, the file is generated, that is, the memory allocation diagram file of u-boot. This file is mainly used for debug ging and error checking. For example, a function is used in the u-boot source code, but this function is defined in multiple files. Which function defined in the file is really used and useful. At this time, find this function with the help of the file, and you will get the function path and other information (except the macro definition).

         After u-boot configuration, compilation and connection, the file, i.e. u-boot symbol table, is generated. It contains the address information of global variables and functions of u-boot. That is, view the output information of u-boot with arm linux nm command and output it to after filtering and sorting. represents a mapping relationship between the address label and the address represented by the label. The format of each line of is "addr type name", addr is the address value corresponding to the label, name is the label name, and type represents the label type. The compilation and operation of u-boot does not necessarily generate This file is mainly provided for users or external program debugging. The following figure refers to the and files.




(2) Several important data structures in U-Boot (see struct_and_other. C in this directory for other nand structures)

1)gd_t structure

         This data structure stores the configuration information required by u-boot (include / ASM arm / global_data. H)

         Just use gd_t needs to be declared with a macro definition: DECLARE_GLOBAL_DATA_PTR specifies to occupy register R8, so it does not occupy memory.


typedef    struct    global_data {     bd_t            *bd;   //For board related structures, see include / ASM arm / u-boot. H unsigned long flags// Indication flags, such as the flag that the device has been initialized, are displayed in the console_ init_ Assigned GD - > flags | = Gd in R function_ FLG_ DEVINIT.    
unsigned long    baudrate;   //Serial baud rate    
unsigned long    have_console;    /* serial_init() was called ,That is, the serial port initialization flag is displayed in the console_ init_ The F function is set to 1*/       unsigned long    reloc_off;    /* Redefine the offset, and the position of the actual orientation is specified when compiling the connection
 The position difference of is generally 0 */       unsigned long    env_addr;     /* Environment parameter address, i.e. default_environment array
 First address of & default_ Environment [0], in Env_ The init function is set in the env_ In the relocate function, it is assigned to & (env_ptr - > data) to start env at this time_ PTR - > data also points to & default_ environment[0] */
unsigned long    env_valid;     /* Environmental parameter CRC check valid flag, in Env_ Set to 1 in init function*/       unsigned long    fb_base;	  /* base address of frame buffer */
#ifdef  CONFIG_VFD / / we generally do not configure this, fb_base is the first address of frame buffer unsigned char 	 vfd_type; 	/*  display type */
void		**jt;		/* jump table  The jump table is used to register the function call address in U-Boot1.1.6 and in the start of board.c file_ The jumptable_ is called in the armboot function. Init() function, in which you set*/
} gd_t;

2) bd_t structure

         Save board related configuration parameters (include / ASM arm / u-boot. H)

typedef struct bd_info {
    int			bi_baudrate;	/* serial console baudrate */
    unsigned long	bi_ip_addr;	/* IP Address   Start in board.c_ Assigned value in armboot function*/
    unsigned char	bi_enetaddr[6]; /* Ethernet adress  MAC Address, in board.c's start_ Assigned value in armboot function*/
    struct environment_s	       *bi_env;  /*Environment variable structure*/
    ulong	        bi_arch_number;	/* unique id for this board   Unique id identifying the board*/
    ulong	        bi_boot_params;	/* where this board expects params startup parameter */

/*Each DRAM   Starting address and size of bank, CONFIG_NR_DRAM_BANKS=1, that is, DRAM has two banks*/

		ulong start;  //In DRAM_ The init function is set to PHYS_SDRAM_1, i.e. 0x50000000
ulong size;  //In DRAM_ The init function is set to 256MB

    /* second onboard ethernet port */
    unsigned char   bi_enet1addr[6];
} bd_t;

         When U-Boot starts the kernel, it needs to pass parameters to the kernel. At this time, Gd is used_ t,bd_t structure to set the tag list.

3) Global Data Flags

#define    GD_FLG_RELOC      0x00001        /* Code was relocated to RAM        */
#define    GD_FLG_DEVINIT    0x00002        /* Devices have been initialized    */
#define    GD_FLG_SILENT     0x00004        /* Silent mode                */

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t  *gd  asm ("r8")   

Define gd as gd_t-type pointer, stored in register r8. Register indicates that the variable is very important for execution speed, so it should be placed in the register of the processor (the register is independent of memory, usually on the processor chip). volatile is used to specify that the value of the variable can be asynchronously modified by external processes, such as interrupt routines.

(3)start_armboot function analysis (\ uboot1.1.6\lib_arm\board.c)

         Understand this function in combination with the board.c file in the directory of "self summary materials \ U-Boot learning" and other documents. start_ There are many macro switches in the armboot () function code for users to configure according to their own development board. Basically, what is called clock and serial initialization function in this function is not done, because it has been initialized (including DDR).

    1. Library of U-Boot
On the top floor Makefile In line 190, there are the following rules:
LIBS  = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a					 //Such as NAND in board.c_ Library functions required by init function
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += post/libpost.a post/cpu/libcpu.a
LIBS += common/libcommon.a                      //Such as the library function required by the print function printf function
("+="stay makefile Is the value after the equal sign)

         LIBS variable indicates the library files required by U-Boot, including the directories related to the platform / development board and the corresponding libraries under the general directory, which are compiled through the corresponding subdirectories. In the second stage code of U-Boot, there are many function calls that are not tracked by source insight. The final call relationship should be found in combination with the file.

         2) MTD device framework in U-boot (such as Nand_init function)

(involving structures: mtd_info, nand_chip, nand_flash_dev, nand_flash_ids, nand_eclayout, nand_ecc_ctrl, etc.)

According to the above description, nand_ is called in this document. The init function actually calls the library, because drivers/nand/nand.c is built into the library libnand.a. according to the file, NAND_ The final call path of init function is drivers/nand/libnand.a(nand.o).

  1. The initialization of nand memory is divided into three stages:

         The first stage is the low level in the first stage code of u-boot_ Init. S has been simply initialized. First, set NFCONF register (0x70200000), set bit4-6, bit8-10 and bit12-14 to 1, that is, set TACLS, TWRPH0 and TWRPH1 to 7 (time parameter); Set NAND memory controller register (0x70200004), set bit0 to 1, and set Bit1 to 1, that is, force nGCS[2] to high (disable chip selection), but it is only effective when bit0 is 1.

         The second stage is NAND when relocating the code_ The NAND is reset in cp.c, and the functions of page reading and block reading are realized. It is only necessary to enable chip selection before each read or reset operation, and then prohibit chip selection after the operation is completed (set Bit1 of NAND memory controller register (0x70200004)).

         The third stage is in drives/nand/nand.c (mainly to do detailed initialization + use advanced code to make it easier to operate NAND later. The advanced code here is actually the MTD framework in U-BOOT), NAND_ The init function will not only print the size of nand flash used by OK6410 development board, but also initialize the structure nand_info and struct nand_chip. nand_info[i] is a structure describing nand_chip[i] is a device structure representing "NAND". The following is their specific introduction:

nand_info[i] : nand_info_t  nand_info[CFG_MAX_NAND_DEVICE];

                    typedef  struct  mtd_info  nand_info_t;

The above definition relationships are defined in nand.c, nand.h and mtd.h respectively, and the final mtd_info structure is the unity of mtd layer's device abstraction and block device interface.

nand_chip[i] : static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE];

                    NAND in nand.h_ Chip structure;

    The above defined relationships are defined in nand.c and nand.h respectively, and the final NAND_ The chip structure is the entity of the device, through which all read-write control operations on the device are finally completed.

(the detailed code is in the SI project of uboot1.1.6-for-OK6410) the general framework is as follows (based on 2440 and different versions of u-boot, mainly the framework):

	lib_arm/board.c in start_armboot()Called in function nand_init().   
	nand_init()Function defined in drivers/nand/Nand.c In the file, its ultimate purpose is to print out on the serial port terminal NAND Capacity. It calls the nand_init_chip()Function whose three parameters correspond to&nand_info[i],&nand_chip[i],base_address[i],If there is only one piece on the development board nand,that i=0 Represents the first block MTD Equipment.
	nand_init_chip()Function initialized IO_ADDR_R and IO_ADDR_W,Call back and forth board_nand_init()and nand_scan()Function, board_nand_init()Function in cpu/s3c64xx/nand.c In, nand_scan()Function in drivers/nand/nand_base.c Yes.
	board_nand_init()Function main initialization nand_chip Structure, for example, assign values to some function pointers in this structure, and let them point to themselves as nand Some functions written by driver.  
	nand_scan()function→nand_scan_ident function→nand_set_defaults and nand_get_flash_type Function. among nand_set_defaults Function settings mtd The interface function of the device layer and continue to nand_chip Function pointer assignment of structure; nand_get_flash_type The main function of this function is to get it again nand Chip manufacturer information and ID,And judge whether it is supported. If it is supported, it is nand Equipment and mtd The structure is filled with some function interface functions.
	nand_scan()function→nand_scan_tail Function, which performs ECC Settings and the rest MTD The initialization of the driver function returns 0.    
	nand_select_device()Function not executed, last returned nand_init(),nand End of initialization of.

         For NAND chip, the basic operation flow is as follows (the functions involved below are functions in U-boot source code):

         On the premise of initializing nand chip, nand's read, write, erase and other operations are to send commands first, and then send addresses. Since the nand flash address line, data line and command line used by OK6410 development board are multiplexed, there are 8 lines in total. Whether the 8 lines send the address, data or command is controlled through cle pin and ALE pin. If the command latch enable signal CLE is high level, the command is transmitted. When ALE is high level, the address is transmitted, and when ALE is low level, the data is transmitted, See the truth table in the chip manual P17 for details. Here is the asynchronous mode. The synchronous mode is that both ALE and cle are high, which is the input and output data.

         In u-boot 1.1.6, the function of sending command and address is NAND_ A function pointer in the chip structure, such as:

void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);

       This function pointer points to s3c_nand_hwcontrol function. The third parameter of this function determines whether to send a command or an address. If the value or macro NAND is added_ Cle, i.e. command is issued, or NAND is loaded_ Ale instant address. The second parameter is the corresponding command or address. Before issuing a command or address, this function also controls the NFCONT register to enable chip selection. (the register corresponding to the write command is NFCMMD, and the register corresponding to the write address is NFADDR)

After sending commands and addresses, data can be obtained through the NFDATA register.

(note that both the command and address are in chip - > cmdfunc, and the function pointer points to nand_command_lp)

  1. Erase operation of NAND storage device

After sending 0x60 Command + three address cycles + 0xd0 command, it will be erased. The erasure size is in blocks, that is, after sending each block address, a total of 128 pages in this block will be automatically erased (the total capacity is 1GB).

  1. NAND read / write operation

Look at the 4th dot of the fifth dot.

  1. ECC principle of NAND Flash read / write operation

         When writing data to NAND Flash, a check code will be generated every 256 or 512 bytes and written in the OOB area of each page. When reading data from NAND Flash, an ECC check code will also be generated every 256 or 512 bytes. Compare this check code with the check code stored in the OOB area to see if it is consistent, so as to know whether the read data is correct

         Generally speaking, kernel and cramfs are written to NAND Flash through U-BOOT. This process writes data to NAND Flash and generates ECC codes. ECC codes are generated by U-BOOT ECC mechanism (whether generated by hardware or software), and then these ECC codes are stored in the OOB area of NAND Flash.
When you start the Kernel, data will be read from NAND Flash, and ECC code will also be generated at this time. The ECC mechanism of U-BOOT is still used, because the read NAND operation is still in the running state of U-BOOT. Then, the ECC code will be compared with the ECC code generated when writing the Kernel (stored in the OOB area) to see if there is an error. Since both writing and reading the Kernel use the ECC generation mechanism of U-BOOT, there is generally no error.

         But when you read Cramfs, it is already in Kernel state. Therefore, when reading Cramfs, the ECC generation mechanism of the Kernel is used, and the U-Boot mechanism used by Cramfs is written earlier. If the two mechanisms are inconsistent (including one is hardware generation, the other is software generation, or the software generation algorithm is different, or the OOB area planning is different), an error will be generated.

3) Environment variable initialization

         For U-BOOT1.1.6, the storage range of environment variables in flash is 0x100000-0x180000, with a total of 512KB. When the system is started for the first time, environment variables are generally not stored in nand flash, so errors will occur during the verification in the C initialization code in the second stage. At that time, Gd is processed_ Env of T structure_ The addr member points to Env_ PTR - > data, and env_ PTR - > data also points to the string array default_environment, in default_ There are 9 default environment variables in the environment array, which involves the important U-BOOT structure gd_t,bd_ Gd in t_ T structure (see point 2 above).

         gd_ Env of T structure_ Addr member in Env_ Set to & default in init function_ Environment [0], the default. In Lib_ Calling env_ in arm/board.c After the relocate function, it is assigned to & (env_ptr - > data). In fact, env at this time_ PTR - > data also points to & default_ environment[0].

         In Env_ In the relocate function, first allocate a 512KB space env in memory_ PTR is used to store environment variables (temporary), and then from nand flash, the environment variables from CFG_ ENV_ Read out at offset (at 1MB), and the readout size is CFG_ENV_SIZE(512KB), read env_ Go to the place indicated by PTR, read out the data, and then call crc32 to Env_ PTR - > data is verified and saved in Env_ Compare the check codes of PTR - > CRC to see if the data is wrong (if it is wrong, the default will be used, that is, copy default_environment_size bytes from the starting position of the memory address indicated by default_environment to the starting position of the memory address indicated by the target env_ptr - > data).

         It can also be seen from here that when the system is started for the first time, if there are no environment variables stored in Nand Flash, the crc check will certainly make an error (the information of Warning - bad CRC or NAND, using default environment is output in the serial port terminal). After we save the environment variables, then start the board, and the u-boot will not report an error of crc 32. (continue to study the storage and calling of environment variables in u-boot)

4) network initialization

         Network initialization is divided into IP address initialization and MAC address initialization. Note that the two for loops in the environment parameter table are not carefully looked up. The network card used by OK6410 has not been initialized.

5) Equipment management framework in U-BOOT

  1. View the documents in this directory: < equipment management framework in u-boot >
  2. devices_ The init() function can also refer to the following links:

  1. Because in devices_ In the init() function, the serial port device is registered, the number of devices in the device list, and the first device "serial" is registered as 0. Console in the back_ init_ Set a console in the r() function, and then output the following information in the serial port terminal:

In:      serial

Out:     serial

Err:     serial

6) Open interrupt

         Open the interrupt exception vector table in cpu/s3c64xx/libs3c64xx.a(interrupts.o), because there is no macro definition config in smdk6410.h_ USE_ IRQ, so the following function does nothing.

void enable_interrupts(void)





(this function jumps from the start_armboot function in board.c in common/main.c)

  1. Boot delay operation

         The Boot delay enters main_ After loop, it will wait for a period of time (the value of the environment variable "bootdelay") to judge whether any key is pressed on the keyboard. If it is pressed, it will enter the U-BOOT download mode. Otherwise, it will directly Boot the operating system.

         Due to CONFIG_BOOTDELAY macro is defined, so in main_ In loop, the boot delay operation is initialized first. First, the address of the "bootdelay" environment variable value is obtained through the getenv function, and then through simple_ The value of the environment variable obtained by the strtol function is assigned to bootdelay.

         Execute the abortboot function in the if judgment, and first output Hit any key to stop autoboot:   1. First, judge whether a key is pressed. If yes, set Hit any key to stop autoboot:   Change 1 of 1 to 0, and exit this function to return 1. If it is judged that no key is pressed at this time, first set the bootdelay variable to 0, and then wait for one second. During this second, it will constantly judge whether a key is pressed. If no key is pressed, terminate the function and return to 0. If a key is pressed, the terminate function returns to 1.

  1. U-BOOT download mode (NAND_ARMMenu function)

When no key is pressed, NAND will be executed downward_ Armmenu function, in which the following information will be printed first:

###################### User Menu for OK6410#####################

[1] Format the nand flash

[2] Burn image from USB

[3] configure the lcd size

[4] Boot the system

[5] Reboot the u-boot

[6] Exit to command line


Enter your Selection:

    Note that in u-boot, functions such as getc() and tstc () are implemented by recursive calls, that is, if there is no input in the serial port terminal, they will not return.

When there is input, the getc() function returns the input character, then printf the input sequence number, and enters the corresponding function according to the sequence number.

NAND_ The armmenu function is a while(1) loop. It will always execute the contents and will not exit.

Posted by neox_blueline on Thu, 07 Oct 2021 15:51:02 -0700