自己写bootloader

发布者:HarmoniousDream最新更新时间:2016-04-01 来源: eefocus关键字:bootloader  汇编文件 手机看文章 扫描二维码
随时随地手机看文章
启动汇编文件:
 
#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]
 
// 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        // 写入控制寄存器 //
 
    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、初始化SDARM //
    ldr r0, =MEM_CTL_BASE
    adr r1, sdarm_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        //让SP指向最高的内存,栈是往下增长的
    
    bl nand_init                     //即使是nor启动也要初始化nand flash,因为内核是存在nand flash上面的,还                                                //要去nand flash上面把内核读出来。
 
    mov r0, #0
    ldr r1, =_start //链接地址在链接脚本中注明,即程序运行时应该在的地方,0x33f80000
    ldr r2, =__bss_start
    sub r2, r2, r1
 
bl copy_code_to_sdram
bl clear_bss
 
// 5、执行main函数 //
ldr lr, =halt
ldr pc, =main  //跳到main函数中运行,不用bl指令是因为该指令会跳到sdarm中执行
 
halt:
    halt
 
sdarm_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 00x008C04F4    //REFRESH
    .long 00x000000B1    //BANKSIZE
    .long 00x00000030    //MRSRB6
    .long 00x00000030    //MRSRB7
==============================================================
nand flash初始化文件:
 
// 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))
 
void nand_init(void)
{
    #define TACLS   0
    #define TWRPH0  3
    #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 del_select(void)
{
    NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
    volatile int i=0;
 
    NFCMMD = cmd;
    for(i=0; i<10; i++);
}
void nand_wait_ready(void)
{
    while (!(NFSTAT & 1));
}
 
unsigned char nand_data(void)
{
    return NFDATA;
}
void nand_addr(unsigned int addr)
{
    unsigned int col  = addr % 2048;
    unsigned int page = addr / 2048;
    unsigned int i=0;
 
    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++);
}
//addr--->0,下面的nand_read函数从nand flash的0地址读数据放在buf地方
//buf---->_start 即链接地址在链接脚本中注明,程序运行时应该在的地方,0x33f80000,把程序拷贝过去
//len----->__bss_start-_start 
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
    int col = addr 48;
    int i = 0;
 
    // 选中片选 //
    nand_select();
    
    while(i
    {
        // 发读命令 //
        nand_cmd(0x00);
        
        // 发地址 //
        nand_addr(addr);
        
        // 发读命令 //
        nand_cmd(0x30);
        
        // 判断状态 //
        nand_wait_ready();
        
        // 读数据 //
        for(; (col<2048) && (i
        {
            buf[i] = nand_data();
            i++;
            addr++;
        }
        col = 0;
    }
 
    // 取消片选 //
    del_select();
}
 
//
       为什么该函数能够判断成功,因为程序运行到此时如果是nand启动,则上电后nand flash的前4K会被复制到片内SARM中运行,往地址0写入数据相当于往片内内存0地址写数据可以成功;但是如果是nor启动,上电后直接在nor的0地址运行,程序依然在nor flash中,nor flash可以像内存一样读,但是无法像内存一样写,故直接向0地址赋值会失败。
//
int isBootFromNorFlash(void)
{
    volatile int *p =(volatile int *)0;
    int val=0;
 
    val = *p;
    *p = 0x1234ab;
    if(*p == 0x1234ab)
    {
        //写成功,表示是nand flash启动
        *p=val;
        return 0;
    }
    else
    {
        //写失败,说明是nor flash启动
        return 1;
    }
}
 
void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{
    unsigned int i=0;
    
    if(isBootFromNorFlash())//nor启动
    {
        while(i
        {
            dest[i] = src[i];
            i++;
        }
    }
    else                    //nand启动
    {
        nand_read(src,dest,len);
    }
}
 
void clear_bss(void)
{
    extern int __bss_start, __bss_end;
    int *p = &__bss_start;
 
    for(; p < &__bss_end; p++)
        *p = 0;
}
 
#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);
}
}
 
=================================================================
boot.c
 
#include "setup.h"
 
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);
 
static struct tag *params;
 
void setup_start_tag(void)
{
   params = (struct tag *)0x30000100;
 
   params->hdr.tag = ATAG_CORE;
   params->hdr.size = tag_size (tag_core);
 
   params->u.core.flags = 0;
   params->u.core.pagesize = 0;
   params->u.core.rootdev = 0;
 
   params = tag_next (params);
}
 
void setup_memory_tags(void)
{
   params->hdr.tag = ATAG_MEM;
   params->hdr.size = tag_size (tag_mem32);
   
   params->u.mem.start = 0x30000000;
   params->u.mem.size  = 64*1024*1024;
   
   params = tag_next (params);
}
 
int strlen(char *str)
{
   int i = 0;
   while (str[i])
   {
      i++;
   }
   return i;
}
 
void strcpy(char *dest, char *src)
{
   while ((*dest++ = *src++) != '\0');
}
 
void setup_commandline_tag(char *cmdline)
{
   int len = strlen(cmdline) + 1;
   
   params->hdr.tag  = ATAG_CMDLINE;
   params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;
 
   strcpy (params->u.cmdline.cmdline, cmdline);
 
   params = tag_next (params);
}
 
void setup_end_tag(void)
{
   params->hdr.tag = ATAG_NONE;
   params->hdr.size = 0;
}
 
int main(void)
{
   void (*theKernel)(int zero, int arch, unsigned int params);
   
   // 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 //
   uart0_init();
    
   // 1. 从NAND FLASH里把内核读入内存 //
   nand_read(0x60000+64,(unsigned char *)0x30008000,0x200000);
   //在u-boot中用mtd命令可以看到u-boot的分区,uImage文件放在NAND FLASH的0x60000地址,但是    //uImage文件包括文件头+内核,文件头长64,故实际内核放在0x60000+64的地方,所以要从                      //0x60000+64的地方读取内核到0x30008000,在SDRAM中内核存在哪里可以看内核的启动信息,一般        //0x30008000。
 
   // 2. 设置参数 //
   setup_start_tag();
   setup_memory_tags();
   setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
   setup_end_tag();
   
   // 3. 跳转执行 //
   puts("Boot kernel\n\r");
   theKernel = (void (*)(int, int, unsigned int))0x30008000;
   theKernel(0, 362, 0x30000100);   //第一个参数是0;第二个参数是机器ID,在内核里面用号码表示单板属于                                                            //哪一类,内核里面会保存若干单板的ID;第三个参数表示参数的位置                                                                 //0x30000100,把参数的位置作为参数传给内核setup_start_tag函                                                                  //数中已经写死。
注意:第二个参数机器ID可以在u-boot函数:boot_jump_linux中unsigned long machid = gd->bd->bi_arch_number;中搜索“bi_arch_number”找到:board/samsung/smdk2440/smdk2410.c 中定义:
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;根据MACH_TYPE_SMDK2410的定义找到:
#define MACH_TYPE_S3C2440              362
 
   // 
    *  mov r0, #0
    *  ldr r1, =362
    *  ldr r2, =0x30000100
    *  mov pc, #0x30008000 
    //
 
   // 如果一切正常, 不会执行到这里 //
   return -1;
}
===============================================================
链接文件:
SECTIONS {
    . = 0x33f80000;
    .text : { *(.text) }
    
    . = ALIGN(4);
    .rodata : {*(.rodata*)} 
    
    . = ALIGN(4);
    .data : { *(.data) }
    
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss)  *(COMMON) }
    __bss_end = .;
}
===============================================================
注:
最简单的bootloader的编写步骤:
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 如果bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置"要传给内核的参数"
5. 跳转执行内核
 
改进:
1. 提高CPU频率, 200MHZ ==> 400MHZ
2. 启动ICACHE
 
存储地址解析:
       u-boot文件存在nand flash/nor flash的0地址处,但正式在SDRAM中运行时的链接地址在链接脚本中已经注明,程序运行时应该在的地方:0x33f80000,因此从地址0x33f80000~0x34000000共512K用于存放u-boot。
       在外部flash中,u-boot(bootloader)、params、kernel、root等存储地址可以在u-boot启动后用mtd命令可以看到u-boot的分区,uImage文件放在NAND FLASH的0x60000地址,但是uImage文件包括文件头+内核,文件头长64,故实际内核放在0x60000+64的地方,所以要从0x60000+64的地方读取内核到0x30008000,在SDRAM中内核存在哪里可以看内核的启动信息,一般是0x30008000。
 
        栈是往下增长的,即从高地址向低地址增长。
 
注意:nor flash启动的u-boot既可以烧写nor flash本身也可以烧写nand flash,但是如果是nand flash启动的话就没办法操作nor flash,因为nand flash启动的时候0地址对应片内内存,无法访问nor flash。
 
 

 
 

 
关键字:bootloader  汇编文件 引用地址:自己写bootloader

上一篇:伪指令ADR和LDR的区别
下一篇:移植最新uboot的总结

推荐阅读最新更新时间:2024-03-16 14:48

STM8做IAP(Bootloader)时在RAM中执行Flash块擦写函数问题
1、STM8的外设库驱动提供了很多代码,要求不高的话直接用库驱动即可 2、Flash块擦写速度快,但是必须要把函数放到RAM中执行(因为MCU的是NorFlash,普通的函数都是直接在Flash上执行的) 官方库如下 /** * @brief Erases a block in the program or data memory. * @note This function should be executed from RAM. * @param FLASH_MemType : The type of memory to erase * @param BlockNum : Indicates the bl
[单片机]
ARM的嵌入式Bootloader实现自动升级
0 引言 作为一种32位的高性能、低成本、低功耗的嵌入式RISC(Reduced Instruction Set Computer)微处理器,ARM(Advanced RISC Machines)微处理器目前已经成为应用最广泛的嵌入式微处理器。和基于简单RTOS甚至没有使用任何操作系统的嵌入式程序设计相比,基于ARM- uClinux嵌入式系统的开发采用了成熟、高效、可靠、模块化、易于配置的操作系统,使程序具有良好的可移植性,博得众多嵌入式开发者的青睐 。嵌入式系统由硬件和软件两部分组成,软件部分主要包括Bootloader、内核和文件系统。但由于Bootloader与处理器的体系结构和具体嵌入式板级设备的配置密切相关,至今没
[单片机]
ARM的嵌入式<font color='red'>Bootloader</font>实现自动升级
FL2440无法烧写bootloader的解决方法
sjf2410 烧写三得flash的工具 2、sjf2410对2410板子上falsh的烧写注意事项 如果运行sjf2410.exe提示找不到并口 ,则可能是并口的模式或者地址不正确,前者可以进入bios中设置为epp或者ecp模式,后者在设备管理器中进行更改。 烧写过程中应该注意的问题 在上面打开sjf2410的目录之后,我们要输入 sjf2410 /f:2410loader.bin 在这里需要注意的是 /f: 并不是盘符!而是文件 File 的意思。要记住:不要理解为盘符了! 最好把bin文件放在与sjf2410相同的目录下!如果不放在同一个目录下,会比较麻烦的哦!据个例子把: 比如说把2410loader.bin放在C
[单片机]
使用STM8S自带BootLoader_2
仔细读ST支持文档 UM0560,按步骤操作。 程序准备工作: 1.开串口接收中断,发送。 /* ******************************************** UART2 configured as follow: - BaudRate = 115200 baud - Word Length = 8 Bits - One Stop Bit - No parity - Receive and transmit enabled - Receive interrupt - UART2 Clock disabled ***********************************
[单片机]
ARM bootloader程序的理解
作为一个嵌入式工程师,如果不能写bootloader程序,那就不能成为一个真正的嵌入式工程师.以前做linux driver,常对bootloader程序是一知半解,其实要写好bootloader程序还得好好去研读一下处理器 architecture. ARM 处理器是一种很热门的嵌入式处理器,现在绝大部分的嵌入式产品都用到了ARM处理器,其低廉的价格和丰富的性能都受到的消费者的青睐.下面针对ARM处理器来具体谈一下ARM bootloader程序. 首先简单的了解一下ARM architecture.ARM主要由ARM内核单元,vector interface ,coprocessor,两种cache(ins
[单片机]
解析单片机上应用的一款Boot Loader
Boot Loader的概念及功能 在以ARM为代表的嵌入式系统中,操作系统内核运行前的硬件初始化、建立内核镜像等都是由Boot Loader来完成的。在PC机上,最先启动的是主板上的BIOS, BIOS负责对硬件初始化,给操作系统提供硬件的接口函数等等,但在嵌入式操作系统中并没有BIOS,因此整个嵌入式操作系统的加载启动任务就完全由 Boot Loader来完成。 图1 Boot Loader流程图 Boot Loader 通常存放于目标平台的非易失存储介质中,主要用于完成由硬件启动到操作系统启动的过渡,能够在上电后对SDRAM、CACHE、FLASH等硬件部分进行检测,建立内存空间的映射图和内核镜像,建立通讯通道和调
[单片机]
解析单片机上应用的一款Boot Loader
STM32通过USB实现Bootloader/IAP功能
前沿: 最近在做STM32的USB Bootlader/IAP功能,也就是通过USB实现固件升级,本文介绍下实现的基本思路,希望对实现IAP的同学一个参考,改方法已经在产品中得到实际应用并验证是比较合理,稳定可靠的。 程序空间划分: 在单片机的程序Flash中分两个区,分别存储Bootloader代码和App代码,Bootloader放到代码起始地址,也就是0x08000000,App放到0x8020000地址,中间预留了很多的地址空间,主要是为了用来存储一些需要掉电保存的数据,比如我在0x0800C000地址就存放了App程序运行后写入该地址的标志数据。 启动流程: 上电后自然是运行Bootloader程序,Bootloader
[单片机]
STM32通过USB实现<font color='red'>Bootloader</font>/IAP功能
AVR-atmega16 BOOTLoader 程序
本程序参照马潮mega128的编写。可支持485或232接口。变异软件ICCAVR,上位机软件应用超级终端或avrubd等,Xmodem,9600,8,1,n #include iom16v.h #include macros.h #include eeprom.h #define SPM_PAGESIZE 128 //M16的一个Flash页为128字节(64字),共128页 #define BAUD 9600 //波特率采用9600bps #define CRYSTAL 14745600 //系统时钟 ?? M Hz //计算和定义M16的波特率设置参数 #define BAUD_SETTING (
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved