zoukankan      html  css  js  c++  java
  • nandflash裸机程序分析

    原文:

    nandflash裸机程序分析 

    它包含7个文件:

    head.S
    init.c
    main.c
    Makefile
    nand.c
    nand.lds
     
    我们之前的程序都是在nandflash的前4k放代码,上电后自动拷贝到SRAM中,之后将SRAM中的代码拷贝到SDRAM中。可是当我们的程序太大超过4k的时候就不行了,因为无法将nandflash的代码完全拷贝到SRAM中去,这时就需要从nandflash中拷贝代码了。
    本程序里面我们要实现的就是:将一部分代码放在nandflash的4096之后,上电后,前面一部分代码自动拷贝到SRAM中,在这段代码里面将nandflash的4096之后的程序拷贝到SDRAM中。
     
    我们从入口函数开始分析:
    @******************************************************************************
    @ File:head.s
    @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
    @******************************************************************************       
    .text
    .global _start
    _start:
                                                              @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
                ldr     sp, =4096                     @设置堆栈 
                bl      disable_watch_dog      @关WATCH DOG
                bl      memsetup                    @初始化SDRAM,主要是设置控制SDRAM的13个寄存器
                bl      nand_init                       @初始化NAND Flas,见注释1
     
                                                              @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
                                                              @nand_read_ll函数需要3个参数:
                ldr     r0,     =0x30000000      @1. 目标地址=0x30000000,这是SDRAM的起始地址
                mov     r1,     #4096               @2.  源地址   = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处
                mov     r2,     #2048               @3.  复制长度= 2048(bytes),对于本实验的main.c,这是足够了
                bl      nand_read                    @调用C函数nand_read,见注释2
     
                ldr     sp, =0x34000000         @设置栈
                ldr     lr, =halt_loop                 @设置返回地址
                ldr     pc, =main                     @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
    halt_loop:
                b       halt_loop
     
    注释1:
    void nand_init(void)
    {
    #define TACLS   0
    #define TWRPH0  3
    #define TWRPH1  0
     
        /* 判断是S3C2410还是S3C2440 */
        if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))//这里表示是2410
        {
            nand_chip.nand_reset         = s3c2410_nand_reset;
            nand_chip.wait_idle          = s3c2410_wait_idle;
            nand_chip.nand_select_chip   = s3c2410_nand_select_chip;
            nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip;
            nand_chip.write_cmd          = s3c2410_write_cmd;
            nand_chip.write_addr         = s3c2410_write_addr;
            nand_chip.read_data          = s3c2410_read_data;
     
    /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */
            s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
        }
        else
        {   /* 定义了各种操作函数 */
            nand_chip.nand_reset         = s3c2440_nand_reset; //见注释1-1
            nand_chip.wait_idle          = s3c2440_wait_idle;         //见注释1-2
            nand_chip.nand_select_chip   = s3c2440_nand_select_chip; //见注释1-3
            nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip; //见注释1-4
            nand_chip.write_cmd          = s3c2440_write_cmd; //见注释1-5
    #ifdef LARGER_NAND_PAGE
            nand_chip.write_addr         = s3c2440_write_addr_lp;//大页写地址,见注释1-6
    #else
    nand_chip.write_addr = s3c2440_write_addr;//小页写地址,见注释1-7
    #endif
            nand_chip.read_data          = s3c2440_read_data;  //读数据,见注释1-8
     
    /* 设置时序 */
            s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);//这个东东在linux编程里面说过了,不再重复
            /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
            s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
        }
        
        /* 复位NAND Flash */
        nand_reset();//见注释1-9
    }
     
    注释1-1:
    /* 复位 */
    static void s3c2440_nand_reset(void)
    {
        s3c2440_nand_select_chip();        //选中芯片
        s3c2440_write_cmd(0xff);             // ffh是复位命令,将这个命令写入命令寄存器即可
        s3c2440_wait_idle();                     //等待就绪
        s3c2440_nand_deselect_chip();  //不选中芯片
    }
     
    注释1-2:
    /* 等待NAND Flash就绪 */
    static void s3c2440_wait_idle(void)
    {
        int i;
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
        while(!(*p & BUSY))//根据NFSTAT第0位判断是否就绪,1表示就绪
            for(i=0; i<10; i++);
    }
     
    注释1-3:
    /* 发出片选信号 */
    static void s3c2440_nand_select_chip(void)
    {
        int i;
        s3c2440nand->NFCONT &= ~(1<<1);//NFCONT寄存器的第1位用于选中芯片,0表示选中
        for(i=0; i<10; i++);   
    }
     
    见注释1-4:
    /* 取消片选信号 */
    static void s3c2440_nand_deselect_chip(void)
    {
        s3c2440nand->NFCONT |= (1<<1);//参考注释1-3
    }
     
    注释1-5
    /* 发出命令 */
    static void s3c2440_write_cmd(int cmd)
    {
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
        *p = cmd;//就是将命令写到命令寄存器里面,很简单
    }
     
    注释1-6:
    /* 大页写地址,分5个周期写入 */
    static void s3c2440_write_addr_lp(unsigned int addr)
    {
    int i;
    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
    int col, page;
             //#define NAND_SECTOR_SIZE_LP    2048
             //#define NAND_BLOCK_MASK_LP     (NAND_SECTOR_SIZE_LP - 1)
    col = addr & NAND_BLOCK_MASK_LP;  //收下地址前11位,屏蔽高位
    page = addr / NAND_SECTOR_SIZE_LP;//屏蔽地址前11位,收下高位
     
    /* 分5个周期将地址写到地址寄存器里面 */
    *p = col & 0xff; /* Column Address A0~A7 */
    for(i=0; i<10; i++);
    *p = (col >> 8) & 0x0f; /* Column Address A8~A11 */
    for(i=0; i<10; i++);
    *p = page & 0xff;/* Row Address A12~A19 */
    for(i=0; i<10; i++);
    *p = (page >> 8) & 0xff;/* Row Address A20~A27 */
    for(i=0; i<10; i++);
    *p = (page >> 16) & 0x03;/* Row Address A28~A29 */
    for(i=0; i<10; i++);
    }
     
    注释1-7:
    /*小页写地址,分4个周期写入*/
    static void s3c2440_write_addr(unsigned int addr)
    {
        int i;
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
        
        *p = addr & 0xff;
        for(i=0; i<10; i++);
        *p = (addr >> 9) & 0xff;
        for(i=0; i<10; i++);
        *p = (addr >> 17) & 0xff;
        for(i=0; i<10; i++);
        *p = (addr >> 25) & 0xff;
        for(i=0; i<10; i++);
    }
     
    注释1-8:
    /* 读取数据 */
    static unsigned char s3c2440_read_data(void)
    {
        /* 很简单,就是将数据从数据寄存器里面读出来 */
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
        return *p;
    }
     
    注释1-9:
    /* 在第一次使用NAND Flash前,复位一下NAND Flash */
    static void nand_reset(void)
    {
        nand_chip.nand_reset();//不解释
    }
     
    注释2:
    首先要知道的是,r0、r1、r2保存着nand_read函数的三个参数,依次对应
    目标、源、大小
    /* 读函数 */
    void nand_read(unsigned char *buf, unsigned long start_addr, int size)
    {
        int i, j;
     
    #ifdef LARGER_NAND_PAGE
        if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {
            return ;    /* 地址或长度不对齐 */
        }
    #else
        if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
            return ;    /* 地址或长度不对齐 */
        }
    #endif
     
        /* 选中芯片 */
        nand_select_chip();
        for(i=start_addr; i < (start_addr + size);) {
          /* 发出READ0命令 */
          write_cmd(0);
     
          /* Write Address */
          write_addr(i);
    #ifdef LARGER_NAND_PAGE
          write_cmd(0x30);
    #endif
          wait_idle();
     
    #ifdef LARGER_NAND_PAGE
          for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {
    #else
      for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
    #endif
              *buf = read_data();//读走一页数据到buf中
              buf++;
          }
        }
     
        /* 取消片选信号 */
        nand_deselect_chip();
        
        return ;
    }
     
    总结一下:
    (1)选中芯片
    (2)发送00h
    (3)发出地址
    (4)发30h
    (5)等待就绪
    (6)读一页数据
    完全是按着时序图来的哦!
     
    接着我们跳到主函数里面:
    发现还是那个点灯的程序,好吧!掠过!
     
    我们最后来看一看连接文件和Makefile文件:
    SECTIONS { 
      firtst  0x00000000 : { head.o init.o nand.o}
      second 0x30000000 : AT(4096) { main.o }
    我们看到这个程序被分成两段,第一段从0地址开始,第二段加载地址是4096处开始的,也就是从4k之后开始的,它不会被自动加载到SRAM中。
     
    objs := head.o init.o nand.o main.o  @这里是一个定义,下面会使用的
     
    nand.bin : $(objs)
    arm-linux-ld -Tnand.lds-o nand_elf $^  @根据连接文件进行连接,因为没有源文件,所以会到下面去查找依赖
    arm-linux-objcopy -O binary -S nand_elf $@ 生成二进制文件
    arm-linux-objdump -D -m arm  nand_elf > nand.dis@反汇编
     
    %.o:%.c
    arm-linux-gcc -Wall -c -O2 -o $@ $<  @所有的.c文件只编译不连接
     
    %.o:%.S
    arm-linux-gcc -Wall -c -O2 -o $@ $<  @所有的.S文件只编译不连接
     
    clean:
    rm -f  nand.dis nand.bin nand_elf *.o
  • 相关阅读:
    centos crash debug
    go get Unknown SSL protocol error in connection to gopkg.in
    Tensorflow serving with Kubernetes
    Spring 集成 Swagger UI
    Docker Registry V2 Garbage Collection
    Docker Registry V2 with Nginx
    Zabbix磁盘性能监控
    Zabbix CPU utilization监控参数
    Windows挂载Gluster复制卷
    Redis持久化存储(三)
  • 原文地址:https://www.cnblogs.com/ganrui/p/3737849.html
Copyright © 2011-2022 走看看