历史上的今天
返回首页

历史上的今天

今天是:2024年11月05日(星期二)

正在发生

2021年11月05日 | 基于s3c2440的简易bootloader实现

2021-11-05 来源:eefocus

一、目的

编写一个能够加载并启动OS内核的bootloader。

 

二、思路

第一阶段:

(1)arm920t的异常向量表有两种存放方式,一种是低端存放(从0x00000000处开始存放),另一种是高端存放(从0xfff000000处开始存放)。选择低端存放,建立异常向量表。

(2)s3c2440的看门狗在上电启动时默认是开启的,所有要先把看门狗关了先。免得代码运行还没完成就强制复位。

(3)屏蔽掉所有中断。

(4)初始化时钟。

(5)初始化内存。

(6)清零bss段。

(7)设置好各个模式下的栈空间。

(8)重定位代码,使得代码的运行地址与链接地址相对应,之后就可以直接使用绝对地址。

(9)使用绝对跳转指令跳转到第二阶段,用c语言来实现。

第二阶段:

(1)初始化串口0,一方面方便我们的调试,一方面也为内核启动时打印信息做好初始化。

(2)初始化nand flash,需要把nand flash里的内核镜像拷贝到内存。

(3)把内核镜像拷贝到内存指定位置。

(4)设置好传递给内核的参数,并放置在约定的位置。

(5)跳转到内核起始地址处开始启动内核。

完毕。

 

三、流程图设计

 

四、代码树结构

 

(1)drivers里的src目录放置与外围设备相关配置的编程文件,inc目录放置相关头文件。对应的makefile放在drivers目录里。

①drivers/Makefile

 

#把src目录下的所有.c文件的字符串替换成.o字符串
TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) )
TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )

all:$(TARGET)
 echo "$(TARGET) 编译完成"

GPATH=$(PWD)
vpath %.c ./src
vpath %.S ./src

%.o:%.c
 $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

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

clean:
 rm -f *.o

 

②drivers/inc

 

nand.h

 

#ifndef _NAND_H
#define _NAND_H

/**
*提取出页内列号,块内页号,块号
*/
#define dnand_addr2ColAddr(addr) (addr & 0x7FF)
#define dnand_addr2RowAddr(addr) (( addr & 0xFC00 ) >> 11)
#define dnand_addr2BlockAddr(addr) (addr >> 17)

//初始化NAND Flash , 时钟频率改变了这个一定要记得改
#define dnand_init()
   do{
    NFCONT = 0x73;
    NFCONF = (3<<12) | (1<<8) | (1<<4);
   }while(0)
//复位
void fnand_reset(void);
//等待NAND Flash就绪 
#define dnand_waitReady() while(!(NFSTAT & 0x1))
//发出片选信号 
#define dnand_enable() (NFCONT &= ~(1<<1))
//取消片选信号
#define dnand_disable() (NFCONT |= (1<<1))
//发出命令 
#define dnand_writeCmd(cmd) (NFCMMD = cmd)
//读数据
#define dnand_readData() (*(volatile unsigned char *)&NFDATA)
//写数据
#define dnand_writeData(data) *(volatile unsigned char *)&NFDATA = data
//写地址
#define dnand_writeAddr(addr) 
                        do{ NFADDR = addr & 0xff;
     NFADDR = (addr >> 8) & 0x0f;
     NFADDR = (addr >> 11) & 0xff;
     NFADDR = (addr >> 19) & 0xff;
    }while(0)
    
//擦除块
unsigned char fnand_eraseBlocks(unsigned int startBlockNum , unsigned int blockCount);
//从nand闪存里的sourAddr地址处读取size大小的数据到内存destAddr处。    
void fnand_readOfSize(unsigned char *destAddr , unsigned int sourAddr , unsigned int size);
//。从内存sourAddr处读取size大小的数据到nand闪存里的destAddr地址处 
unsigned char fnand_writeOfSize(unsigned char *sourAddr , unsigned int destAddr , unsigned int size);   
#endif

 

uart0.h

 

#ifndef UART0_H
#define UART0_H


#define TXD0_READY  (0x01<<1)
#define UBRDIV0_VAL     (101250000UL/(115200*16)-1)


/**********初始化uart0中断的配置***********/
void uart0_init(void);

/*******把string消息通过uart0发送出去******/
void uart0_sent_msg(char *string);
void uart0_sent_byte(char byte);
/*打印hex数据*/
void uart0_sent_hex_word(unsigned int val);

#endif

 

 

③drivers/src

 

nand.c

#include "nand.h"
#include "s3c2440.h"

 

/**
* 复位 
*/
void fnand_reset(void)
{
 dnand_enable();
 dnand_writeCmd(0xff);  // 复位命令
 dnand_waitReady();
 dnand_disable();
}

/**
*块擦除函数
*/
unsigned char fnand_eraseBlocks(unsigned int startBlockNum , unsigned int blockCount)
{
 unsigned int i;
 
 dnand_enable(); 
 
 for( i = 0 ; i < blockCount ; i++ ){
  //发送擦除命令
  dnand_writeCmd( 0x60 );
  dnand_writeAddr( startBlockNum << 6 ); 
  dnand_writeCmd(0xD0); 
  
  dnand_waitReady(); 
  
  //读取状态
  dnand_writeCmd( 0x70 );
  if ( ( dnand_readData() & 0x01 ) ){
   
   return 1;  
  }
  
  startBlockNum += 1;  
 }
 
 dnand_disable();
 return 0; 
}

void fnand_readOfSize(unsigned char *destAddr , unsigned int sourAddr , unsigned int size)
{
 unsigned int j , col;

 
 col =  sourAddr & 0x7FF ;       //该地址可能不是从页的0地址开始读 ,所以要先取出列地址

 dnand_enable();
 
 for( j = 0 ; j < size ; ){

  //发出read命令
  dnand_writeCmd( 0x00 );
  dnand_writeAddr( sourAddr );
  dnand_writeCmd( 0x30 );
  dnand_waitReady( );

  //开始读一页数据到destAddr里 ,
  for( ; (col < 2048)&&(j   *destAddr++ = dnand_readData();
   j++;
   sourAddr++;
  }
  col = 0;  
 }
 
 dnand_disable(); 
}
/**
*从内存的sourAddr处写入pageCount页数据到nand flash的destAddr地址处
*/
unsigned char fnand_writeOfSize(unsigned char *sourAddr , unsigned int destAddr , unsigned int size)
{
 unsigned int col, j;
 unsigned int startBlockNum , blockCount ,pageCount;
 col =  destAddr & 0x7FF ;       
        pageCount = size/2048 + ( (col)? 1 : 0 ) ;
 
 startBlockNum = dnand_addr2BlockAddr( destAddr ) ;
 blockCount = ( dnand_addr2RowAddr( destAddr ) + pageCount ) >> 6 ;
 if ( ( dnand_addr2RowAddr( destAddr ) + pageCount ) & 0x3F ){
  blockCount++; 
 }

 if ( fnand_eraseBlocks( startBlockNum , blockCount ) ){
  return 1; 
 } 
 dnand_enable(); 
 for( j = 0 ; j < size ; ){
  //发出read命令
  dnand_writeCmd( 0x80 );
  dnand_writeAddr( destAddr );
 
  //开始写一页数据到destAddr里
  for( ; (col < 2048)&&(j < size) ; col++ ){   
   dnand_writeData( *sourAddr++ );
   destAddr++;
   j++;
  }
  col = 0;
    
  dnand_writeCmd( 0x10 ); 
  dnand_waitReady(); 
  //发送读取状态命令
  dnand_writeCmd( 0x70 ); 
  if ( ( dnand_readData() & 0x01 ) ){
   return 1;  
  }
 }
 dnand_disable();
 return 0;      
}

 

uart0.c

 

#include "Uart0.h"
#include "s3c2440.h"

/*
****************************************
初始化uart0中断的配置
****************************************
*/
void uart0_init(void)
{
        GPHCON |= 0xa0;
        GPHUP  |= 0x0f;
        ULCON0 = 0x03;  //普通模式,禁止奇偶校验,1个结束位,8-bit字长
        UBRDIV0 = UBRDIV0_VAL; //波特率选择115200,所以UBRDIV0=54
        UCON0 = 0x005;  //时钟源=PCLK
                        //Rx,Tx水平触发,禁止接收超时中断,禁止接收错误状态中断,
                        //不使用回路模式,不发送break信号
                        //中断方式发送接收数据到缓冲寄存器       
}

/*
*****************************************************
*uart0发送消息
***************************************************
*/

void uart0_sent_msg(char *string)

 do {    
         while( !(UTRSTAT0&TXD0_READY) ); 
         UTXH0 = *string++;
         
 }while(*string != '');  
}


void uart0_sent_byte(char byte)

   while(!(UTRSTAT0&TXD0_READY));
         UTXH0 = byte;      
}

void uart0_sent_hex_word(unsigned int val)
{
 char i ,j;
 uart0_sent_msg("0x");

 for ( i = 0 ;  i < 8 ; i++) {


  j = (char)((val >> ((7-i)*4) ) & 0x0f);
  if( (j >= 0) && ( j <= 9)) {
   uart0_sent_byte('0'+j);
  }else{
   uart0_sent_byte('A'+j-0xa);
  }


 }
 uart0_sent_msg("  ");
}

 

(2)cpu里的src目录里放置与arm920t内核相关的编程文件,inc目录放置相关头文件。

cpu/Makefile


#把src目录下的所有.c文件的字符串替换成.o字符串
TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) )
TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )

all:$(TARGET)
 echo "$(TARGET) 编译完成"

GPATH=$(PWD)
vpath %.c ./src
vpath %.S ./src

%.o:%.c
 $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

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

clean:
 rm -f *.o

 

cpu/src

 

ClkConflg.S


#把src目录下的所有.c文件的字符串替换成.o字符串
TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) )
TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )

all:$(TARGET)
 echo "$(TARGET) 编译完成"

GPATH=$(PWD)
vpath %.c ./src
vpath %.S ./src

%.o:%.c
 $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

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

clean:
 rm -f *.o

 

MemCofig.S


@与内存相关
.equ BWSCON   , 0x48000000
.equ BANKCON6 ,  0x4800001C
.equ REFRESH  , 0x48000024
.equ BANKSIZE , 0x48000028
.equ MRSRB6  , 0x4800002C

 

.global MemConfigure

MemConfigure:

/*******内存初始化子程序*********/

 @BWSCON[27:24] = 0 0 10B
 ldr r0 , =BWSCON
 ldr r1 , [r0]
 ldr r2 , =(0x0F<<24)
 bic r1 , r1 , r2
 ldr r2 , =(0x02<<24)
 orr r1 , r1 , r2
 str r1 , [r0] 

推荐阅读

史海拾趣

Easy Braid公司的发展小趣事

品质一直是Easy Braid公司的生命线。在竞争激烈的市场环境中,Easy Braid始终坚持使用高品质的材料和严格的生产工艺,确保每一件产品都达到最高标准。正是这种对品质的执着追求,让Easy Braid赢得了客户的信任和口碑。随着客户群的扩大,Easy Braid的品牌影响力也逐渐增强。

EMCORE公司的发展小趣事

在技术创新和品质保证的基础上,Easy Braid开始积极拓展市场。公司不仅在国内市场取得了良好的业绩,还逐步将目光投向了国际市场。通过与国外知名企业的合作,Easy Braid成功地将自己的产品打入国际市场,实现了品牌的全球化布局。这一过程中,Easy Braid展现出了强大的市场竞争力和适应能力。

e2v technologies公司的发展小趣事

为了进一步提升整体实力,e2v在发展过程中进行了多次收购与整合。这些收购不仅增强了公司的技术实力和市场竞争力,也为其带来了更多的客户资源。例如,e2v曾收购了一家专注于医疗成像技术的公司,这一举措使其在医疗成像领域取得了显著的进展。

骏晔科技(DreamLNK)公司的发展小趣事

物联网设备的互操作性一直是行业内的挑战。骏晔科技在面对这一挑战时,积极寻求解决方案。公司通过与多家物联网系统提供商合作,共同推动物联网互操作性标准的制定和实施。同时,公司还针对不同类型的物联网设备,提供定制化的RF模块和解决方案,帮助客户实现设备的互联互通。

Display Engineering Services公司的发展小趣事

物联网设备的互操作性一直是行业内的挑战。骏晔科技在面对这一挑战时,积极寻求解决方案。公司通过与多家物联网系统提供商合作,共同推动物联网互操作性标准的制定和实施。同时,公司还针对不同类型的物联网设备,提供定制化的RF模块和解决方案,帮助客户实现设备的互联互通。

Brainboxes公司的发展小趣事

多年来,Brainboxes一直致力于技术创新和研发投入。公司拥有一支高素质的软件和硬件工程师团队,他们在产品设计和制造方面具有丰富的经验。凭借先进的设计和制造技术,Brainboxes在业界赢得了多项荣誉,如英国制造联合会颁布的“2007年度最佳企业”奖和欧洲电子工业奖的“2005年度制造商”。这些荣誉不仅证明了公司的技术实力,也提升了其在全球电子行业中的影响力。

问答坊 | AI 解惑

自恢复保险使用心得

1,现在市场上有除了瑞侃之外的JK系列,可以完全互换; 2,我接触的主要有两种电压规格60V和250V,交直流均可以用,指的是击穿电压; 3,额定电流是在一定的条件下给出的,如果要求工作在较宽的温度范围,应该留有一定的裕量,一般可以取1.5-2 ...…

查看全部问答>

uclinux jedce_probe探测ID,变成flash的内容

Flash:两片SST39VF3201 ,一片挂CS0:地址0x80000000 一片挂CS1:地址0x81000000 使用jecdec探测 static struct map_info lpc24xx_map[2] = { {         .name =                \ ...…

查看全部问答>

运行CETK时出现的问题 0x00000000”指令指引的“0x00000000”内存,该内存不能为“read”!

如题!用CETK调试驱动时,cetest.exe出现上述问题 。看了网上对这类问题的说法五花八门的,也不知道问题出在哪里,设备端clientSide.exe运行是正常的!…

查看全部问答>

猎头职位:多媒体开发~上海多个职位

汽车音响高级软件工程师 职位描述: 汽车音响嵌入式软件开发 职位要求: -电子工程或相关专业本科以上学历; -2-3年以上汽车电子相关工作经验; -熟悉嵌入式,单片机开发,有独立的开发能力; -熟悉汇编语言及C语言; -熟悉汽车总线控制 ...…

查看全部问答>

无法加载流驱动

各位高手: 本人新手,所用设备是pocket pc 2003,在PB5.0下写完了一个测试驱动flashtest.dll,编辑注册表,注册表中Dll,Prefix,Order和Index字段都设置好了,DLL文件放在\\windows目录下,重启后,pocket pc 2003中注册表字段都在,但device.exe ...…

查看全部问答>

如何开发内核流驱动程序?

最近一直在看DDK中的MSVAD,想开发虚拟声卡驱动。都说看DDK,但是看的不知所云,一些概念不清楚,如mixer,MixerMute,Adapter,Topology,Miniport,还有端口等等一些概念,请问这些东西是关于什么的?哪里有相关的资料看? 小弟跪求帮助!!!! ...…

查看全部问答>

关于ADC0809与AT89S51接口程序中的一条指令的疑问,请大家帮帮忙~

   ……       MOV DPTR  #0FEF8H   ;指向ADC0809首地址          MOVX @DPTR , A      ;启动A/D转换         ...…

查看全部问答>

请问专家,每个厂家的MAC地址是怎么规定的?

最近研究的一个课题涉及到MAC地址。世界上的每个网卡都有一个独有的MAC地址,我想知道是不是有什么组织向生产网卡的这些厂家分配网卡地址?他们分配的原则是什么?有没有像IP地址分类一样,MAC地址也有一些分类呢?…

查看全部问答>

在用TI的OMAP35xx系列产品,性能不够怎么办

之前遇到很多朋友在用TI的OMAP3530及OMAP3515等Sitara相关产品。他们开发了很多优秀的产品,如:KTV点播产品,大型游戏机等。最近有朋友问我,“David,我们使用OMAP3530开发的产品,觉得非常不错,可是我们现在有一个担心,要是我们将来想在产品上 ...…

查看全部问答>