rtthread移植到jz2440之BootLoader

从2016年第一次接触rtthread,感觉很容易上手,记得一个项目是小飞行器上的IPC,趁着空闲,手里有一块jz2440的板子,准备在这块板子上跑起来rtthread,查了很多资料,最后决定自己写一个简单的BootLoader启动板子,启动rtthread系统。下面是简单的BootLoader源代码。

init.c

主要是内存控制器初始化、串口初始化及与串口相关函数实现、nandflash初始化及读写功能。

/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000C))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned char *)0x4E000020))

/* GPIO */
#define GPHCON              (*(volatile unsigned long *)0x56000070)
#define GPHUP               (*(volatile unsigned long *)0x56000078)

/* UART registers*/
#define ULCON0              (*(volatile unsigned long *)0x50000000)
#define UCON0               (*(volatile unsigned long *)0x50000004)
#define UFCON0              (*(volatile unsigned long *)0x50000008)
#define UMCON0              (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define UTXH0               (*(volatile unsigned char *)0x50000020)
#define URXH0               (*(volatile unsigned char *)0x50000024)
#define UBRDIV0             (*(volatile unsigned long *)0x50000028)

unsigned int *pGPFCON = (unsigned int *)0x56000050;
unsigned int *pGPFDAT = (unsigned int *)0x56000054;

void led_on(void)
{
    /* 配置GPF4为输出引脚 */
    *pGPFCON = 0x1500;
    /* 设置GPF4输出0 */
    *pGPFDAT = 0;    
}

void led_off(void)
{
    /* 配置GPF4为输出引脚 */
    *pGPFCON = 0x1500;
    /* 设置GPF4输出0 */
    *pGPFDAT = 0xff;    
}
#define TXD0READY   (1<<2)


void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);

extern void puthex(unsigned int val);
extern void puts(char *str);
int isBootFromNorFlash(void)
{
    volatile int *p = (volatile int *)0;
    int val;

    val = *p;
    *p = 0x12345678;
    if (*p == 0x12345678)
    {
        /* 写成功, 是nand启动 */
        *p = val;
        return 0;
    }
    else
    {
        /* NOR不能像内存一样写 */
        return 1;
    }
}

void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{    
    int i = 0;
    
    /* 如果是NOR启动 */
    if (isBootFromNorFlash())
    {
        while (i < len)
        {
            dest[i] = src[i];
            i++;
        }
    }
    else
    {
        //nand_init();
        nand_read((unsigned int)src, dest, len);
    }
}

void clear_bss(void)
{
    extern int __bss_start, __bss_end;
    int *p = &__bss_start;
    
    for (; p < &__bss_end; p++)
        *p = 0;
}

void nand_init(void)
{
#define TACLS   0
#define TWRPH0  1
#define TWRPH1  0
    /* 设置时序 */
    NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
    /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
    NFCONT = (1<<4)|(1<<1)|(1<<0);    
}

void nand_select(void)
{
    NFCONT &= ~(1<<1);    
}

void nand_deselect(void)
{
    NFCONT |= (1<<1);    
}

void nand_cmd(unsigned char cmd)
{
    volatile int i;
    NFCMMD = cmd;
    for (i = 0; i < 10; i++);
}

void nand_addr(unsigned int addr)
{
    unsigned int col  = addr % 2048;
    unsigned int page = addr / 2048;
    volatile int i;

    NFADDR = col & 0xff;
    for (i = 0; i < 10; i++);
    NFADDR = (col >> 8) & 0xff;
    for (i = 0; i < 10; i++);
    
    NFADDR  = page & 0xff;
    for (i = 0; i < 10; i++);
    NFADDR  = (page >> 8) & 0xff;
    for (i = 0; i < 10; i++);
    NFADDR  = (page >> 16) & 0xff;
    for (i = 0; i < 10; i++);    
}


void nand_wait_ready(void)
{
    while (!(NFSTAT & 1));
}

unsigned char nand_data(void)
{
    return NFDATA;
}
void nand_chip_id(void)
{ 
    unsigned char buf[5]={0};
    
    nand_select(); 
    nand_cmd(0x90);
    nand_addr(0x00);

    buf[0] = nand_data();
    buf[1] = nand_data();    
    buf[2] = nand_data();
    buf[3] = nand_data();
    buf[4] = nand_data();    
    nand_deselect();     

    puts("maker id\n\r");
    puthex(buf[0]);    
    puts("\n\r");
    puts("device id\n\r");
    puthex(buf[1]);    
    puts("\n\r");
    puts("3rd byte\n\r");
    puthex(buf[2]);    
    puts("\n\r");
    puts("4th byte\n\r");
    puthex(buf[3]);        
    puts("\n\r");        
    puts("page  size\n\r");
    puthex(1  <<  (buf[3] & 0x03));    
    puts("\n\r");
    puts("block size\n\r");
    puthex(64 << ((buf[3] >> 4) & 0x03));
    puts("\n\r");    
    puts("5th byte\n\r");
    puthex(buf[4]);            
}

void nand_w_data(unsigned char val)
{
    NFDATA = val;
}

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
    int col = addr % 2048;
    int i = 0;
        
    /* 1. 选中 */
    nand_select();

    while (i < len)
    {
        /* 2. 发出读命令00h */
        nand_cmd(0x00);

        /* 3. 发出地址(分5步发出) */
        nand_addr(addr);

        /* 4. 发出读命令30h */
        nand_cmd(0x30);

        /* 5. 判断状态 */
        nand_wait_ready();

        /* 6. 读数据 */
        for (; (col < 2048) && (i < len); col++)
        {
            buf[i] = nand_data();
            i++;
            addr++;
        }
        
        col = 0;
    }

    /* 7. 取消选中 */        
    nand_deselect();
}

void nand_addr_byte(unsigned char addr)
{
    volatile int i;
    NFADDR = addr;
    for(i=0; i<10; i++);
}


int nand_erase(unsigned int addr, unsigned int len)
{
    int page = addr / 2048;

    if (addr & (0x1FFFF))
    {
        puts("nand_erase err, addr is not block align\n\r");
        return 0;
    }
    
    if (len & (0x1FFFF))
    {
        puts("nand_erase err, len is not block align\n\r");
        return 0;
    }
    
    nand_select(); 

    while (1)
    {
        page = addr / 2048;
        
        nand_cmd(0x60);
        /* row/page addr */
        nand_addr_byte(page & 0xff);
        nand_addr_byte((page>>8) & 0xff);
        nand_addr_byte((page>>16) & 0xff);
        nand_cmd(0xD0);
        nand_wait_ready();

        len -= (128*1024);
        if (len == 0)
            break;
        addr += (128*1024);
    }
    
    nand_deselect();     
    return 1;
}

void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
    int page = addr / 2048;
    int col  = addr & (2048 - 1);
    int i = 0;

    nand_select(); 

    while (1)
    {
        nand_cmd(0x80);

        nand_addr(addr);

        /* 发出数据 */
        for (; (col < 2048) && (i < len); )
        {
            nand_w_data(buf[i++]);
        }
        nand_cmd(0x10);
        nand_wait_ready();

        if (i == len)
            break;
        else
        {
            /* 开始下一个循环page */
            col = 0;
            page++;
        }
        
    }
    
    nand_deselect();     
}

#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

/*
 * 初始化UART0
 * 115200,8N1,无流控
 */
void uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉

    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率为115200
}

/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));
    
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}

void puts(char *str)
{
    int i = 0;
    while (str[i])
    {
        putc(str[i]);
        i++;
    }
}

void puthex(unsigned int val)
{
    /* 0x1234abcd */
    int i;
    int j;
    
    puts("0x");

    for (i = 0; i < 8; i++)
    {
        j = (val >> ((7-i)*4)) & 0xf;
        if ((j >= 0) && (j <= 9))
            putc('0' + j);
        else
            putc('A' + j - 0xa);
        
    }
    
}
View Code

start.S

    a、 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
    b、如果bootloader比较大,要把它重定位到SDRAM
    c、把内核从NAND FLASH读到SDRAM
   e、设置"要传给内核的参数"
 
#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))
#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))
#define MEM_CTL_BASE    0x48000000

.text
.global _start
_start:

/* 1. 关看门狗 */
    ldr r0, =0x53000000
    mov r1, #0
    str r1, [r0]
    
/* 配置GPF4为输出引脚
 * 把0x100写到地址0x56000050
 */
    ldr r1, =0x56000050
    ldr r0, =0x1500    /* mov r0, #0x100 */
    str r0, [r1]
/* 设置GPF4输出高电平 
 * 把0写到地址0x56000054
 */
    ldr r1, =0x56000054
    ldr r0, =0    /* mov r0, #0 */
    str r0, [r1]
/* 2. 设置时钟 */
    ldr r0, =0x4c000014
    //    mov r1, #0x03;              // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
    mov r1, #0x05;              // FCLK:HCLK:PCLK=1:4:8
    str r1, [r0]

    /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
    mrc    p15, 0, r1, c1, c0, 0        /* 读出控制寄存器 */ 
    orr    r1, r1, #0xc0000000            /* 设置为“asynchronous bus mode” */
    mcr    p15, 0, r1, c1, c0, 0        /* 写入控制寄存器 */

    /* MPLLCON = S3C2440_MPLL_400MHZ */
    ldr r0, =0x4c000004
    ldr r1, =S3C2440_MPLL_400MHZ
    str r1, [r0]

    /* 启动ICACHE */
    mrc p15, 0, r0, c1, c0, 0    @ read control reg
    orr r0, r0, #(1<<12)
    mcr    p15, 0, r0, c1, c0, 0   @ write it back


/* 3. 初始化SDRAM */
    ldr r0, =MEM_CTL_BASE
    adr r1, sdram_config     /* sdram_config的当前地址 */
    add r3, r0, #(13*4)
1:
    ldr r2, [r1], #4
    str r2, [r0], #4
    cmp r0, r3
    bne 1b

/* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */
    ldr sp, =0x34000000

    bl nand_init

    mov r0, #0
    ldr r1, =_start
    ldr r2, =__bss_start
    sub r2, r2, r1
    
    bl copy_code_to_sdram
    bl clear_bss
    
/* 5. 执行main */
    ldr lr, =halt
    ldr pc, =main
halt:
    b halt

sdram_config:
    .long 0x22011110     //BWSCON
    .long 0x00000700     //BANKCON0
    .long 0x00000700     //BANKCON1
    .long 0x00000700     //BANKCON2
    .long 0x00000700     //BANKCON3  
    .long 0x00000700     //BANKCON4
    .long 0x00000700     //BANKCON5
    .long 0x00018005     //BANKCON6
    .long 0x00018005     //BANKCON7
    .long 0x008C04F4     // REFRESH
    .long 0x000000B1     //BANKSIZE
    .long 0x00000030     //MRSRB6
    .long 0x00000030     //MRSRB7
View Code

boot.c

实现把rtthread从nandflash读到SDRAM

extern void uart0_init(void);
extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
extern void puts(char *str);
extern void puthex(unsigned int val);
extern void led_on(void);
extern void led_off(void);

int strlen(char *str)
{
    int i = 0;
    while (str[i])
    {
        i++;
    }
    return i;
}

void strcpy(char *dest, char *src)
{
    while ((*dest++ = *src++) != '\0');
}

int main(void)
{
    void (*theKernel)( unsigned int params);
    unsigned int *p = 0x30000000;
    /* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */
    uart0_init();
    
    /* 1. 从NAND FLASH里把内核读入内存 */
    puts("Copy kernel from nand\n\r");
    nand_read(0x0, (unsigned char *)0x30000000, 0x100000);
    puts("\n\r");
    led_off();
    
    /* 3. 跳转执行 */
    puts("Boot rtthread\n\r");
    theKernel = (void (*)(unsigned int))0x30000000;
    theKernel(0x30000000);  
    puts("Error!\n\r");
    /* 如果一切正常, 不会执行到这里 */

    return -1;
}
View Code

boot.lds

链接脚本

SECTIONS {
    . = 0x33f80000;
    .text : { *(.text) }
    
    . = ALIGN(4);
    .rodata : {*(.rodata*)} 
    
    . = ALIGN(4);
    .data : { *(.data) }
    
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss)  *(COMMON) }
    __bss_end = .;
}
View Code

Makefile

CC      = arm-linux-gcc
LD      = arm-linux-ld
AR      = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump

CFLAGS         := -Wall -O2
CPPFLAGS       := -nostdinc -nostdlib -fno-builtin

objs := start.o init.o boot.o

boot.bin: $(objs)
    ${LD} -Tboot.lds -o boot.elf $^
    ${OBJCOPY} -O binary -S boot.elf $@
    ${OBJDUMP} -D -m arm boot.elf > boot.dis
    
%.o:%.c
    ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

%.o:%.S
    ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:
    rm -f *.o *.bin *.elf *.dis
    
View Code

猜你喜欢

转载自www.cnblogs.com/Ye-Jason/p/10245038.html