u-boot is an open source bootloader, the current version is 1.1.2. u-boot is developed on the basis of ppcboot and armboot, it is quite mature and stable, and has been adopted in many embedded system development processes. Due to its development source code, it supports many development boards.
Why do we need u-boot? Obviously, uClinux can be burned directly into flash, so there is no need for an additional bootloader. However, from the perspective of software upgrades and program patches, automatic software updates are very important. In fact, the use of bootloaders is not only that, but the need for automatic software updates alone shows that our development is necessary. At the same time, the process of u-boot porting is also a process of deepening the understanding of embedded systems, including hardware, software and operating systems.
2. Framework of u-boot porting
Porting u-boot to a new development board only requires modifying the hardware-related parts. In terms of code structure:
1) Create a gold44b directory under the board directory, and create gold44b.c, flash.c, memsetup.S, u-boot.lds, etc. You don't need to start from scratch, you can choose a similar directory, copy it directly, and modify the file name and content. When I was porting u-boot, I chose the Dave/B2 directory. Since u-boot already contains a development board directory based on s3c24b0, you can also copy the corresponding directory as a reference.
2) Create a s3c44b0x directory under the cpu directory, which mainly contains start.S, interrupts.c, cpu.c, and serial.c files. There is also no need to create files from scratch, just copy them from arm720t, and then modify the corresponding content.
3) Add gold44b.h to the include/configs directory, and put global macro definitions here.
4) Find the Makefile in the u-boot root directory and modify it to add
gold44b_config: unconfig
@./mkconfig $(@:_config=) arm s3c44b0 gold44b
5) Run make ev44bii_config. If there is no error, you can start the hardware-related code porting work
. 3. u-boot architecture
1) Overall structure
u-boot is a hierarchical structure. Software personnel doing porting work should provide serial port drivers (UART Driver), Ethernet drivers (Ethernet Driver), Flash drivers (Flash Driver), and USB drivers (USB Driver). At present, it is not very necessary to download programs through the USB port, and there is no USB interface on the development board, so the USB driver has not been ported for the time being. Above the driver layer is the u-boot application, and command provides a human-machine interface through the serial port. We can use some commands to do some common tasks, such as the memory viewing command md. The
Kermit application is mainly used to support the use of the serial port to download applications through the hyperterminal. TFTP is used to download applications through the network, such as the uClinux operating system.
2) Memory distribution
The flash size of gold44b is 2M (8 bits). Now 0-40000, a total of 256k, is used as the storage space for u-boot. Since there are some environment variables in u-boot, such as IP address, boot file name, etc., they can be configured through setenv in the command line and saved in the space of 40000-50000 (a total of 64k) through saveenv. If there are saved environment variables, u-boot will directly use these environment variables. As can be seen from the code analysis, we will move the flash boot code to DRAM for operation. The location of the u-boot code in DRAM is configured as follows in u-boot-1.1.2/board/gold44b/config.mk: TEXT_BASE = 0x0C700000. In this way, the boot code u-boot will be moved from 0x0000 0000 to 0x0C700000. Special attention should be paid to the fact that the interrupt vector program address of gold44b uClinux is at 0x0c000 0000, so the program cannot be downloaded to 0x0c000 0000, but usually downloaded to 0x0c008 0000.
4. start.S code structure
1) Define entry point An executable image must have an entry point and can only have one unique global entry point, which is usually placed at the 0x0 address of Rom (flash). For example , .globl _start _start
in start.S : It is worth noting that you must tell the compiler to know this entry point, and this work mainly involves modifying the connector script file (lds). The u-boot.lds on the development board is as follows: OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { cpu/s3c44b0/start.o (.text) *( . text) } . = ALIGN( 4); .rodata: { * (.rodata) } . .; armboot_end_data = .; . = ALIGN(4); __bss_start = .; .bss : { *(.bss) } _end = .; }
2) Set the exception vector.
The exception vector table is one of the key points for the connection between u-boot and uClinux kernel. Even if the uClinux kernel has obtained the control of the processor, once an interrupt occurs, the processor will automatically jump to a certain table entry (based on the interrupt type) in the first-level exception vector table starting from address 0x0 to read the instruction and run. The
exception vector table must be stored continuously starting from address 0. The following includes reset, undefined processing (undef), software interrupt (SWI), pre-instruction error (Pabort), data error (Dabort), reserved, and IRQ, FIQ, etc. Note that the value here must be consistent with the vector_base of uClinux. That is to say, if vector_base (include/armnommu/proc-armv/system.h) in uClinux is defined as 0x0c00 0000, then HandleUndef should be
0x0c00 0004.
.globl _start
_start: b reset
/*Modfied by zl 2005-2-21 */
/* add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000 000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
*/
ldr pc, =0x0c000004
ldr pc, =0x0c000008
ldr pc, =0x0c00000c
ldr pc, =0x0c000010
ldr pc, =0x0c000014
ldr pc, =0x0c000018
ldr pc, =0x0c00001c
.balignl 16,0xdeadbeefHere
, the first-level exception interrupt vector table at address 0x0 simply contains the jump instruction to the second-level exception interrupt vector table. In this way, the event that occurred can be correctly handed over to the uClinux interrupt handler for processing. This design is because interrupts are not used in this u-boot transplantation. Both 8019 and Timer are polling interrupts. If u-boot wants to use interrupts (such as using USB devices), it is necessary to establish a second-level exception interrupt vector table. 0x000000 add pc, pc, #0x0c000000 add pc, pc, #0x0c000000 add pc
, pc, #0x0c000000 add pc, pc, #0x0c000000 add pc, pc, #0x0c000000 add pc, pc, #0x0c000000 add pc, pc, #0x0c000000 .balignl 16,0xdeadbeef .... At Reset is to copy the interrupt vector table /* now copy to sram the interrupt vector */ adr r0, real_vectors add r2, r0, #1024 ldr r1, =0x0c000000 add r1, r1, #0x08 vector_copy_loop: ldmia r0!, {r3-r10} stmia r1!, {r3-r10} cmp r0, r2 ble vector_copy_loop .... Create a three-level interrupt jump /******************************************************/ /* interrupt vectors */ /*************************************************/ /* real_vectors: b reset b undefined_instruction b software_interrupt b prefetch_abort b data_abort b not_used b irq b fiq */ /***********************************************/ undefined_instruction: mov r6, #3 b reset software_interrupt: mov r6, #4 b reset prefetch_abort: mov r6, #5 b reset data_abort: mov r6, #6 b reset not_used: /* we *should* never reach this */
mov r6, #7
b reset
irq:
mov r6, #8
b reset
fiq:
mov r6, #9
b reset
3) Initialize CPU related pll, clock, interrupt control registers
In order to turn off the watch dog timer, turn off the interrupt, set LockTime, PLL (phase lock loop), and clock.
These values (except LOCKTIME) can be found in the manual of Samsung 44b0.
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*******************************************************************
*/
#define INTCON (0x01c00000+0x200000)
#define INTMSK (0x01c00000+0x20000c)
#define LOCKTIME (0x01c0000 0+0x18000c)
#define PLLCON (0x01c00000+0x180000)
#define CLKCON (0x01c00000+0x180004)
#define WTCON (0x01c00000+0x130000)
cpu_init_crit:
/* disable watch dog */
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
/*
* mask all IRQs by clearing all bits in the INTMRs
*/
ldr r1,=INTMSK
ldr r0, =0x03ffffeff
str r0, [r1]
ldr r1, =INTCON
ldr r0, =0x05
str r0, [r1]
/* Set Clock Control Register */
ldr r1, =LOCKTIME
ldrb r0 , =800
strb r0, [r1]
ldr r1, =PLLCON
#if CONFIG_S3C44B0_CLOCK_SPEED==64
ldr r0, =0x38021 /* smdk4110: Xtal=8MHz Fclk=64MHz */
#elif CONFIG_S3C44B0_CLOCK_SPEED==66
ldr r0, =0x34031 /* 66MHz (Quartz=11MHz) */
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
ldr r0, =0x610c1 /*B2: Xtal=20mhz Fclk=75MHz */
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
str r0, [r1]
ldr r1,=CLKCON
ldr r0, =0x7ff8
str r0, [r1]
mov pc, lr
4) Initialize SDRAM controller
The memory controller (mainly SDRAM controller) is mainly configured by setting 13 registers starting from 1c80000, including bus width, 8 memory banks, bank size, sclk, and two bank modes.
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a memsetup.S in your board directory.
*/
bl memsetup
#endif
The code to initialize the memory controller is stored in u-boot-1.1.2/board/gold44b/memsetup.S
It is consistent with the boot code (memcfg.s and 44binit.s) under ADS or SDT, but the assembly format is slightly different.
5) Copy the program in rom to RAM
First, use the PC to obtain the starting address of the bootloader in flash, and then calculate
the size of the program code by the difference in the label. The compiler will generate the correct distribution value for these labels when linking.
After obtaining the correct information, copy the code to RAM through registers (r3 to r10) as the intermediate medium for copying.
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don 't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop :
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
6) Initialize stack
Enter each mode and set the corresponding mode stack.
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
7) Go to RAM Using
the instruction ldr,pc, the C function address in RAM can be transferred to RAM for execution.
ldr pc, _start_armboot
5. System initialization
1) Serial port part (u-boot-1.1.2/cpu/s3c44b0/serial.c)
The serial port settings mainly include initializing the serial port. It is worth noting that the Baudrate of the serial port has a lot to do with the clock MCLK. The relationship is calculated by: rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1 ). This can be found in the manual. Since u-boot supports variable Baud rate, so use macro definition to set the default baud rate (64Mhz, 115200bps) and other baud rates. The code is as follows:
void serial_setbrg (void)
{
DECLARE_GLOBAL_DATA_PTR;
u32 divisor = 0;
/* get correct divisor */
switch(gd->baudrate) {
case 1200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 3124;
#elif CONFIG_S3C44B0_CLOCK_SPEED==7 5
divisor = 3905;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /default
divisor = 3332;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;
case 9600:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 390;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 487;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /default
divisor = 416;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;
case 19200:
#if CONFIG_S3C44B0_CLOCK _SPEED==66
divisor = 194;
#elif CONFIG_S3C44B0_CLOCK_SPEED== 75
divisor = 243;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /Default
divisor = 207;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;
case 38400:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 97;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 121;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /Default
divisor = 103;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;
case 57600:
#if CONFIG_S3C 44B0_CLOCK_SPEED==66
divisor = 64;
# elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 80;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /default
divisor = 68;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;
case 115200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 32;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64
divisor = 34;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75 /default
divisor = 40;
#else
# error CONFIG_S 3C44B0_CLOCK_SPEED undefined
#endif break;
}
serial_flush_output();
serial_flush_input();
UFCON0 = 0x0;
ULCON0 = 0x03;
UCON0 = 0x05;
UBRDIV0 = divisor;
UFCON1 = 0x0;
ULCON1 = 0x03;
UCON1 = 0x05;
UBRDIV1 = divisor;
for(divisor=0; divisor<100; divisor++) {
/* NOP */
}
}
Other functions include sending and receiving. There is no interrupt at this time, and the loop waits to determine whether the action is completed.
For example, the receiving function :
static int serial_flush_input(void)
{
volatile u32 tmp;
/* keep on reading as long as the receiver is not empty */
while(UTRSTAT0&0x01) {
tmp = REGB(URXH0);
}
return 0;
}
2) Clock part (u -boot-1.1.2/cpu/s3c44b0/interrupt.c)
implements the delay function udelay.
Since get_timer here does not use interrupts, it uses global variables to accumulate.
void udelay (unsigned long usec)
{
ulong tmo;
tmo = usec / 1000;
tmo *= CFG_HZ;
tmo /= 8;
tmo += get_timer (0);
while (get_timer_masked () < tmo)
/*NOP*/;
}
3) Flash part (u-boot-1.1.2/board/gold44b.c)
As part of the memory, flash can be read without any problem. The key is the writing part of flash.
Flash must be erased first and then written.
flash_init The initialization part is completed. The main purpose here is to check whether the flash model is correct.
unsigned long flash_init (void)
{
#ifdef __DEBUG_START_FROM_SRAM__
return CFG_DUMMY_FLASH_SIZE;
#else
unsigned long size_b0;
int i;
/* Init: no FLASHes known */
for (i=0; i
}
/* Static FLASH Bank configuration here - FIXME XXX */
size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]);
if (flash_info[0].flash_id == FLASH_UNKNOWN) {
printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n",
size_b0, size_b0<<20);
}
/* Setup offsets */
flash_get_offsets (0, &flash_info[0]);
/* Monitor protection ON by default */
(void)flash_protect(FLAG_PROTECT_SET,
-CFG_MONITOR_LEN,
0xffffffff,
&flash_info[0]);
flash_info[0].size = size_b0;
return (size_b0);
#endif
}
flash_erase 擦除flash,BlankCheck 则检查该部分内容是否擦除成功。
int flash_erase (flash_info_t *info, int s_first, int s_last)
{
volatile CFG_FLASH_WORD_SIZE *addr = (CFG_FLASH_WORD_SIZE *)(info->start[0]);
volatile CFG_FLASH_WORD_SIZE *addr2;
int flag, prot, sect, l_sect;
ulong start, now, last;
int i;
if ((s_first < 0) || (s_first > s_last)) {
if (info->flash_id == FLASH_UNKNOWN) {
printf ("- missing\n");
} else {
printf ("- no sectors to erase\n");
}
return 1;
}
if (info->flash_id == FLASH_UNKNOWN) {
printf ("Can't erase unknown flash type - aborted\n");
return 1;
}
prot = 0;
for (sect=s_first; sect<=s_last; ++sect) {
if (info->protect[sect]) {
prot++;
}
}
if (prot) {
printf ("- Warning: %d protected sectors will not be erased!\n",
prot);
} else {
printf ("\n");
}
l_sect = -1;
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();
/* Start erase on unprotected sectors */
for (sect = s_first; sect<=s_last; sect++) {
if (info->protect[sect] == 0) { /* not protected */
addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[sect]);
if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) {
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr2[0] = (CFG_FLASH_WORD_SIZE)0x00500050; /* block erase */
for (i=0; i<50; i++)
udelay(1000); /* wait 1 ms */
} else {
if (sect == s_first) {
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
}
addr2[0] = (CFG_FLASH_WORD_SIZE)0x00300030; /* sector erase */
}
l_sect = sect;
}
}
/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();
/* wait at least 80us - let's wait 1 ms */
udelay (1000);
/*
* We wait for the last triggered sector
*/
if (l_sect < 0)
goto DONE;
start = get_timer (0);
last = start;
addr = (CFG_FLASH_WORD_SIZE *)(info->start[l_sect]);
while ((addr[0] & (CFG_FLASH_WORD_SIZE)0x00800080) != (CFG_FLASH_WORD_SIZE)0x00800080) {
if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) {
printf ("Timeout\n");
return 1;
}
/* show that we're waiting */
if ((now - last) > 50000000) { /* every second */
putc ('.');
last = now;
}
}
DONE:
/* reset to read mode */
addr = (CFG_FLASH_WORD_SIZE *)info->start[0];
addr[0] = (CFG_FLASH_WORD_SIZE)0x00F000F0; /* reset bank */
printf (" done\n");
return 0;
}
wirte_word 则想flash 里面写入unsigned long 类型的data,因为flash 一次只能写入16bits,所以这里分两次写入。
/*-----------------------------------------------------------------------
* Write a word to Flash, returns:
* 0 - OK
* 1 - write timeout
* 2 - Flash not erased
*/
static int write_word (flash_info_t *info, ulong dest, ulong data)
{
volatile CFG_FLASH_WORD_SIZE *addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[0]);
volatile CFG_FLASH_WORD_SIZE *dest2 = (CFG_FLASH_WORD_SIZE *)dest;
volatile CFG_FLASH_WORD_SIZE *data2 = (CFG_FLASH_WORD_SIZE *)&data;
ulong start;
int flag;
int i;
/* Check if Flash is (sufficiently) erased */
if ((*((volatile ulong *)dest) & data) != data) {
return (2);
}
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();
for (i=0; i<4/sizeof(CFG_FLASH_WORD_SIZE); i++)
{
addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr2[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00A000A0;
dest2[i] = data2[i];
/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();
/* data polling for D7 */
start = get_timer (0);
while ((dest2[i] & (CFG_FLASH_WORD_SIZE)0x00800080) !=
(data2[i] & (CFG_FLASH_WORD_SIZE)0x00800080)) {
if (get_timer(start) > CFG_FLASH_WRITE_TOUT) {
return (1);
}
}
}
return (0);
}
Previous article:S3C2440 external interrupt key interrupt
Next article:Experience of porting uboot to S3C44B0X development board
Recommended ReadingLatest update time:2024-11-16 22:23
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- EEWORLD University Hall----Live Replay: Introduction to Keysight's New Optical Communication Test Solution
- A complete list of filter circuits, save it for future use!
- How to choose FPGA development board
- Let’s discuss: When you make PPT, do you make the template yourself or download it from the Internet?
- TI points exchanged for E coins, but not received
- EEWORLD University Hall ---- Data Structure and Algorithm Peking University Zhang Ming
- NUCLEO_G431RB Review: Get G431
- Thank you for having you +EE @【chat, laugh, make noise】
- How to write C/C++ code in DSP? + Data types in c6000
- What role does power supply potting glue play in power supplies, and why do power supplies need potting?