自己写bootloader------编写第1阶段

2024-08-30 22:48

本文主要是介绍自己写bootloader------编写第1阶段,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.bootloader的作用

2.编写代码

2.1 start.S

2.2 boot.lds 链接脚本

2.3 init.c

2.3.1 nand_init

2.3.2 nand_read


1.bootloader的作用

bootloader的目标:启动内核

2.编写代码

2.1 start.S


#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))
#define MEM_CTL_BASE    0x48000000.text
.global _start
_start:/* 1. 关看门狗 */ldr r0, =0x53000000mov r1, #0str r1, [r0]/* 2. 设置时钟 */ldr r0, =0x4c000014mov r1, #0x03;            // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1str 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_200MHZ */ldr r0, =0x4c000004ldr r1, =S3C2440_MPLL_200MHZstr r1, [r0]/* 3. 初始化SDRAM */ldr r0, =MEM_CTL_BASEadr r1, sdram_config     /* sdram_config的当前地址 */add r3, r0, #(13*4)
1:ldr r2, [r1], #4str r2, [r0], #4cmp r0, r3bne 1b/* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */ldr sp, =0x34000000bl nand_initmov r0, #0ldr r1, =_startldr r2, =__bss_startsub r2, r2, r1bl copy_code_to_sdrambl clear_bss/* 5. 执行main */ldr lr, =haltldr pc, =main
halt:b haltsdram_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

2.2 boot.lds 链接脚本

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

2.3 init.c

我们在进行重定位的时候要判断是norflash启动还是nandflash启动,我们用下面方法进行判断。

  • 如果是norflash启动,那么0地址就是再norflash上面,norflash可以像内存一样读但是不能像内存一样写。
  • 如果是nandflash启动,那么0地址是片内4k的sram上面,sram是可以读写的。

另外init.c里面还做了清除bss段的操作,

然后就是nandflash的初始化和读操作。

2.3.1 nand_init

 nandflash初始化的时序设置主要有三个值,

  • TACLS:发出CLE/ALE之后,要等待多长时间,写信号才能变为低电平。
  • TWRPH0:nWE这个低电平要维持多长时间。
  • TWRPH1:当nWE变为高电平之后,这个CLE/ALE还要保持多长时间。

2.3.2 nand_read

 我们通过2440的nandflash控制器,发地址之前要先把ALE设置为高电平,发命令之前要把CLE设置为高电平。

1页有2Kbyte和64字节的OBB(out of bank)。为什么引入OOB,是因为nandflash有一个位反转的缺陷,比如我们读某一页的时候,大多数是对的,但是某一位可能会发生反转,我们怎么避免位反转,所以我们在写页数据的时候,出了写数据之外,还要生成ECC校验码,ECC写入OOB,读的时候,要把OOB里面的ECC读出来,并且读出来的数据重新生成校验码,然后进行对比,如果不相等,会以某种算法找出来是哪一位反转了。

注意oob是不参与nandflash的地址编址的,那我们怎么说访问oob呢,我们可以说oob里的第0字节,或者这一页里面的第2049个。

我们读的时候,我们发出读命令,发出地址,然后nandflash会把这一整页的数据弄到page register里面,然后我们在发出读操作,就可以从page register里面把数据读出来了。

我们发送地址的时候,一共包含五个地址周期,其中前面两个表示这一页中的哪一个,后面三个是表示哪一页。 

从国上面的读操作时序图我们可以看到,首先我们要发出00命令,然后发出5个地址,然后发出30命令,然后等待数据就绪,然后读数据即可。

  1. 选中片选
  2. 发出读命令00h
  3. 发出地址(分5步发出)
  4. 发出读命令30h
  5. 判断状态
  6. 读数据
//init.c/* 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)#define TXD0READY   (1<<2)void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);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_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();
}#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,RXD0GPHUP   = 0x0c;     // GPH2,GPH3内部上拉ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)UCON0   = 0x05;     // 查询方式,UART时钟源为PCLKUFCON0  = 0x00;     // 不使用FIFOUMCON0  = 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);elseputc('A' + j - 0xa);}}

这篇关于自己写bootloader------编写第1阶段的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122153

相关文章

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

Wondows dos下怎么编写bat批处理文件

最近搞php,在运行时,以Nginx+php-cgi.exe方式运行Wordpress项目 打开dos,先cd到php-cgi.exe文件当前目录下执行启动命令:php-cgi.exe -b 127.0.0.1:9001再打开一个dos,再cd到nginx.exe文件当前目录下执行启动命令:start nginx 大概过程要经过这些步骤,觉得很麻烦,就学下怎么编写一个bat文件,以双击运行代替

安卓玩机工具------小米工具箱扩展工具 小米机型功能拓展

小米工具箱扩展版                     小米工具箱扩展版 iO_Box_Mi_Ext是由@晨钟酱开发的一款适用于小米(MIUI)、多亲(2、2Pro)、多看(多看电纸书)的多功能工具箱。该工具所有功能均可以免root实现,使用前,请打开开发者选项中的“USB调试”  功能特点 【小米工具箱】 1:冻结MIUI全家桶,隐藏状态栏图标,修改下拉通知栏图块数量;冻结

用Python编写倒计时程序:详细教程

目录 引言 环境准备 基本概念 代码实现 步骤一:导入必要的库 步骤二:获取用户输入 步骤三:实现倒计时逻辑 步骤四:整合代码 运行程序 高级功能 扩展功能示例:支持分钟和小时输入 扩展功能示例:图形用户界面 (GUI) 总结 引言 倒计时程序是一个非常常见的小工具,广泛用于各种应用场景中,例如考试时间提醒、烹饪计时器、会议倒计时等。Python 作为一种

8阶段项目:五子棋(附带源码)

8阶段项目:五子棋 8.1-技术实现 1.静态变量 静态变量只能定义在类中,不能定义在方法中。静态变量可以在static修饰的方法中使用,也可以在非静态的方法中访问。主要解决在静态方法中不能访问非静态的变量。 2.静态方法 静态方法就相当于一个箱子,只是这个箱子中装的是代码,需要使用这些代码的时候,就把这个箱子放在指定的位置即可。   /*** 静态变量和静态方法*/public cl

【Spring boot】编写代码及测试用例入门之 Hello Spring boot _踩坑记

先贴下目录: 这是我从 start.spring.io 里下载的依赖Web的模板 // DemoApplication.javapackage com.abloume.springboot.blog.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autocon

windows下使用vscode编写运行以及调试C/C++

vscode支持类似于vs的断点调试c/c++,也可以直接编译&运行c/c++ 先是编译运行 c/c++的方法                              微软官方起初设定的科学做法(这也是现在的科学做法)是通过在vscode集成控制台写命令行的方式来实现编译运行程序的,但也可以通过code runner插件来简化步骤,实现一键编译执行 但无论是什么方法,因为vscod

2023 CCPC(秦皇岛)现场(第二届环球杯.第 2 阶段:秦皇岛)部分题解

所有题目链接:Dashboard - The 2023 CCPC (Qinhuangdao) Onsite (The 2nd Universal Cup. Stage 9: Qinhuangdao) - Codeforces 中文题面: contest-37054-zh.pdf (codeforces.com) G. Path 链接: Problem - G - Codeforces

Python编写简单登录系统的完整指南

在现代应用中,用户认证和登录系统是一个非常重要的功能。通过登录系统,应用能够识别用户的身份,并为其提供相应的权限和服务。本文将介绍如何使用Python编写一个简单的登录系统,包括用户注册、登录验证、密码加密等功能。通过这一教程,将学习如何构建一个基本的用户登录系统,并理解其中的关键技术。 这里插播一条粉丝福利,如果你在学习Python或者有计划学习Python,想在未来人工智能领域吃上一口饭的,