zoukankan      html  css  js  c++  java
  • 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
    
    ");
        puthex(buf[0]);    
        puts("
    
    ");
        puts("device id
    
    ");
        puthex(buf[1]);    
        puts("
    
    ");
        puts("3rd byte
    
    ");
        puthex(buf[2]);    
        puts("
    
    ");
        puts("4th byte
    
    ");
        puthex(buf[3]);        
        puts("
    
    ");        
        puts("page  size
    
    ");
        puthex(1  <<  (buf[3] & 0x03));    
        puts("
    
    ");
        puts("block size
    
    ");
        puthex(64 << ((buf[3] >> 4) & 0x03));
        puts("
    
    ");    
        puts("5th byte
    
    ");
        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
    
    ");
            return 0;
        }
        
        if (len & (0x1FFFF))
        {
            puts("nand_erase err, len is not block align
    
    ");
            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++) != '');
    }
    
    int main(void)
    {
        void (*theKernel)( unsigned int params);
        unsigned int *p = 0x30000000;
        /* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */
        uart0_init();
        
        /* 1. 从NAND FLASH里把内核读入内存 */
        puts("Copy kernel from nand
    
    ");
        nand_read(0x0, (unsigned char *)0x30000000, 0x100000);
        puts("
    
    ");
        led_off();
        
        /* 3. 跳转执行 */
        puts("Boot rtthread
    
    ");
        theKernel = (void (*)(unsigned int))0x30000000;
        theKernel(0x30000000);  
        puts("Error!
    
    ");
        /* 如果一切正常, 不会执行到这里 */
    
        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
  • 相关阅读:
    前端总结挺全面的
    cmd与bat脚本的使用
    Spring控制反转(IoC)的理解
    C# 中迭代器
    URL 分页并排序
    结合Flash上传文件时显示进度条
    C# 语言特性
    where T:new()
    图片缩放特效
    C# 隐藏窗体 ALT+TAb不可见
  • 原文地址:https://www.cnblogs.com/Ye-Jason/p/10245038.html
Copyright © 2011-2022 走看看