手中有OK6410开发板,一直想试试通过Linux来做做裸机开发,在网络论坛上也搜过一些资料作参考,整理了一下并做了解释或改动,希望这些东西可以大家分享下。

裸机程序的构成

基本的裸机程序由启动代码和C函数文件构成。而启动代码包括:硬件设备初始化、调用C函数。

本次分析中代码文件有:

start.S 汇编写的启动代码

commom.h 一些通用的函数

irq.c 中断初始化,中断处理等

regs.h

s3c6410的寄存器,用到的寄存器在本文件里声明/定义

sdram.c 关于sdram内存初始化等操作

time.c 对于系统时钟的设置,如锁相环等

main.c 这个就是主函数了,主程序就在这编写,这里可以写一个流水灯程序

main.lds 该文件为链接脚本,描述了各个输入文件的各个section怎样映射到输出文件的各个section中,还控制输出文件中section和符号等的内存布局。

Makefile makefile文件

1、 学习启动代码有助于我们以后开发uboot,uboot的启动代码跟裸机的相近。

下面把start.S代码贴出来,其中代码中也有注释。

@**************************************

@ File: start.S

@ Function: cpu initial and jump to c program

@**************************************

.extern main

.text

.global _start

_start:

b reset @

when reset, cpu jump to 0 address

b halt @ldr pc,

_undefined_instruction

b halt @ldr pc,

_software_interrupt

b halt @ldr pc,

_prefetch_abort

b halt @ldr pc,

_data_abort

b halt @ldr pc,

_not_used

ldr pc, _irq

b halt @ldr pc,

_fiq

_irq:

.word vector_irq

vector_irq:

ldr sp, =

0x54000000 @ save location

sub lr, lr, #4

stmdb sp!, {r0-r12, lr}

bl do_irq @

deal with exception

@ backing out

ldmia sp!, {r0-r12, pc}^

reset:

ldr r0, =

0x70000000 @ Peripheral port base address

orr r0, r0, #0x13

mcr p15,0,r0,c15,c2,4 @

256M

ldr r0, =

0x7e004000 @ watchdog register address

mov r1, #0x0

str r1,

[r0] @ write 0, disable

watchdog

ldr sp,

=1024*8 @ set stack, notice:

can't larger than 8K

bl clock_init ;初始化系统时钟

bl sdram_init ;初始化SDRAM

adr r0,

_start @ get _start's current

address: 0

ldr r1, =

_start @ _start's link

address

ldr r2, =

bss_start @ bss section's begining link

address

cmp r0, r1

beq clean_bss ;重定位

copy_loop:

ldr r3, [r0], #4

str r3, [r1], #4

cmp r1, r2

bne copy_loop

clean_bss:

ldr r0, = bss_start

ldr r1, = bss_end

mov r3, #0

cmp r0, r1

ldreq pc, =

on_ddr ;清BSS段

clean_loop:

str r3, [r0], #4

cmp r0, r1

bne clean_loop

ldr pc, = on_ddr

on_ddr:

bl irq_init @

initial IRQ

@mrs r0, cpsr

bic r0, r0, #0x9f

orr r0, r0, #0x10

msr cpsr,

r0 @

enter user mode

ldr sp, = 0x57000000

@bl main @

call c program's main function

ldr pc, =

main ;调用C函数

halt:

b halt

启动代码的一般流程如下:

. 硬件相关设置:把外设基地址告诉CPU(ARM11特用)

(分析:ldr r0, = 0x70000000

其中0x70000000是外设的基地址,从6410的datasheet的第二章存储器映射一章可以找到

orr r0, r0, #0x13

指r0中的值是256,代表256M,这是ARM11规定的,具体在ARM11datasheet中)

. 关看门狗

(分析:ldr r0, = 0x7e004000 加载地址0x7e004000上的数据放入r0中

mov r1, #0x0

str r1, [r0] 将r1中的数据存储到r0指向的存储单元中——把看门狗寄存器写0)

.设置堆栈(后面要调用c函数,调用函数就要先设置栈,片内8K内存)

.初始化时钟

.初始化SDRAM

.重定位

.清BSS段

.调用C函数

2、 commom.h共用的头文件

里面编写了一些方便的函数,都是对寄存器的某位或多位进行操作的函数,缩短我们写代码的时间,下面贴出来,可以自己分析下。

#ifndef __COMMON_H

#define __COMMON_H

#define vi *( volatile unsigned int *

)

#define set_zero( addr, bit ) ( (vi

addr) &= ( ~ ( 1 <<

(bit) ) ) )

#define set_one( addr, bit ) ( (vi addr) |= ( 1

<< ( bit ) ) )

#define set_bit( addr, bit, val ) ( (vi

addr) = (( vi

addr)&=(~(1<

) | ( (val)<

#define set_2bit( addr, bit, val ) (

(vi addr) = (( vi

addr)&(~(3<

| ( (val)<

#define set_nbit( addr, bit,

len, val ) \

( (vi addr) = ((( vi addr)&(~((

((1<

)<

(val)<

#define get_bit( addr, bit ) ( (( vi

addr ) & ( 1 << (bit)

)) > 0 )

#define get_val( addr, val ) ( (val) =

vi addr )

#define read_val( addr ) ( vi ( addr ) )

#define set_val( addr, val ) ( (vi addr) = (val) )

#define or_val( addr, val ) ( (vi addr) |= (val) )

///

typedef unsigned char u8;

typedef unsigned short u16;

typedef unsigned int u32;

// function declare

int delay( int );

#endif

3、 irq.c

这是一个中断初始化和中断处理函数文件。

下面对ARM异常进行介绍:

中断也是异常的一种,ARM处理器用7中工作模式:

(1)用户模式(user) usr 正常的执行模式

(2)快速中断模式(FIQ) fiq 高优先级中断产生进入的模式(高速数据传输等情况使用)

(3)外部中断模式(IRQ) irq 低优先级中断产生进入的模式(一般的外部中断)

(4)特权模式(Superviser) svc 复位或软中断,供操作系统使用的保护模式

(5)数据访问中止模式(Abort) abt 存取数据异常,用于虚拟存储或存储保护

(6)未定义指令模式(undefine)und 当执行未定义指令时进入的模式

(7)系统模式(system) sys 运行特权级操作系统任务

中断过程:

(1)系统上电,CPU处于svc模式

(2)如果发生中断,那么CPU进入IRQ模式;R13、R14切换到自己的R13、R14;跳到相应的中断向量地址

如何进行中断编程?

(1)中断初始化

a. 设置中断源(也就是配置引脚模式)

b. 设置中断控制器(参照6410datasheet中断控制器一章)

c. 打开总中断开关(设置CPSR)

4、 regs.h

这是一个6410中声明和定义寄存器的,需要哪个寄存器就在该文件当中定义,在后面的文件当中直接调用即可。

5、 sdram.c

下面我们来看一下6410核心板DDR的原理图

从图中我们可以看到,该核心板有两个DDR级联而得,每个有16位,两个一共32位。每个DDR有15根地址线,而2^15=32K,内存芯片K4X1G163PC的datasheet可以看出为每片64M*16

= 1G bit,15根地址线必然不够,所以地址应该是分两次发出来的。

BA0和BA1可以访问4块bank,而它提供13条行地址和10条列地址。而6410提供了DDR控制器,只要控制DDR控制器即可。关于初始化DDR:

(1)地址线设置

(2)告诉位宽

(3)设置时序

可参照s3c6410的datasheet:设置DDR控制器

初始化DDR芯片,

参照代码,初始化DDR的顺序

#include "common.h"

#define

MEMCCMD 0x7e001004

#define P1REFRESH 0x7e001010

#define P1CASLAT 0x7e001014

#define MEM_SYS_CFG 0x7e00f120

#define P1MEMCFG 0x7e00100c

#define P1T_DQSS 0x7e001018

#define P1T_MRD 0x7e00101c

#define P1T_RAS 0x7e001020

#define P1T_RC 0x7e001024

#define P1T_RCD 0x7e001028

#define P1T_RFC 0x7e00102c

#define P1T_RP 0x7e001030

#define P1T_RRD 0x7e001034

#define P1T_WR 0x7e001038

#define P1T_WTR 0x7e00103c

#define P1T_XP 0x7e001040

#define P1T_XSR 0x7e001044

#define P1T_ESR 0x7e001048

#define P1MEMCFG2 0X7e00104c

#define P1_chip_0_cfg 0x7e001200

#define

P1MEMSTAT 0x7e001000

#define P1MEMCCMD 0x7e001004

#define P1DIRECTCMD 0x7e001008

#define HCLK 133000000

#define

nstoclk(ns) (ns/( 1000000000/HCLK)+1)

void sdram_init( void )

{

// tell dramc to configure

set_val(MEMCCMD, 0x4 );

// set refresh

period

set_val( P1REFRESH, nstoclk(7800) );

// set timing

para

set_val( P1CASLAT, ( 3

<< 1 ) ); set_val( P1T_DQSS, 0x1 ); //

0.75 - 1.25

set_val( P1T_MRD, 0x2 );

set_val( P1T_RAS, nstoclk(45) );

set_val( P1T_RC, nstoclk(68) );

u32 trcd = nstoclk(

23 );

set_val( P1T_RCD, trcd | (( trcd - 3 )

<< 3 ) );

u32 trfc = nstoclk( 80 );

set_val( P1T_RFC, trfc | ( ( trfc-3 )

<< 5 )

); u32 trp = nstoclk( 23 );

set_val( P1T_RP, trp | ( ( trp - 3 )

<< 3 ) );

set_val( P1T_RRD, nstoclk(15) );

set_val( P1T_WR, nstoclk(15) );

set_val( P1T_WTR, 0x7 );

set_val( P1T_XP, 0x2 );

set_val( P1T_XSR, nstoclk(120) );

set_val( P1T_ESR, nstoclk(120) );

// set mem cfg

set_nbit( P1MEMCFG, 0, 3, 0x2

);

set_nbit( P1MEMCFG, 3, 3, 0x2

); set_zero( P1MEMCFG, 6

); set_nbit( P1MEMCFG, 15, 3, 0x2 );

set_nbit( P1MEMCFG2,

0, 4, 0x5 );

set_2bit( P1MEMCFG2, 6, 0x1

); set_nbit( P1MEMCFG2, 8, 3, 0x3 );

set_2bit( P1MEMCFG2, 11, 0x1 );

set_one(

P1_chip_0_cfg, 16 );

// memory 初始化

set_val( P1DIRECTCMD, 0xc0000 ); // NOP

set_val( P1DIRECTCMD, 0x000 ); // 预充电

set_val( P1DIRECTCMD, 0x40000 );// 自刷新

set_val( P1DIRECTCMD, 0x40000 );// 自刷新

set_val( P1DIRECTCMD, 0xa0000 ); // EMRS

set_val( P1DIRECTCMD, 0x80032 ); // MRS

set_val( MEM_SYS_CFG,

0x0 );

//

设置内存控制器为"运行"状态 set_val( P1MEMCCMD, 0x000 );

// wait ready

while( !(( read_val( P1MEMSTAT )

& 0x3 ) == 0x1));

}

6、 time.c

对系统时钟进行初始化设置,而对于6410的晶振是12M,需要通过一系列的变频,分频来产生500~600M的时钟。对时钟t进行设置需要参照6410的datasheet中的系统控制器一章的时钟体系。

初始化设置系统时钟,无非就是对相应的寄存器进行设置,设置分频等,下面说几个知识点:

图中ARMCLK是ARM11的CPU时钟,一般设置为532MHz

HCLK为133MHz,一般为NandFlash和DDR提供时钟,PCLK为67MHz

SCLK为某些特殊设备提供时钟

当系统上电,晶振开始起振,不可能一下子从12M就变为532M,需要一段缓冲的时间,这段时间称为LOCKTIME,如下图所示:

#define APLL_LOCK (*((volatile unsigned long *)0x7E00F000))

#define MPLL_LOCK (*((volatile unsigned long *)0x7E00F004))

#define EPLL_LOCK (*((volatile unsigned long *)0x7E00F008))

#define

OTHERS (*((volatile unsigned long *)0x7e00f900))

#define CLK_DIV0 (*((volatile unsigned long *)0x7E00F020))

#define

ARM_RATIO 0 #define HCLKX2_RATIO 4 #define HCLK_RATIO 0 #define PCLK_RATIO 1 #define MPLL_RATIO 0

#define APLL_CON (*((volatile unsigned long

*)0x7E00F00C))

#define APLL_CON_VAL ((1<<31) | (266

<< 16) | (3

<< 8) | (1))

#define MPLL_CON (*((volatile unsigned long *)0x7E00F010))

#define MPLL_CON_VAL ((1<<31) | (266

<< 16) | (3

<< 8) | (1))

#define CLK_SRC (*((volatile unsigned long *)0x7E00F01C))

void clock_init(void)

{

APLL_LOCK = 0xffff;

MPLL_LOCK = 0xffff;

EPLL_LOCK = 0xffff;

OTHERS &= ~(0xc0);

while((OTHERS & 0xf00) != 0);

CLK_DIV0 =

(ARM_RATIO) | (MPLL_RATIO << 4)

| (HCLK_RATIO

<< 8) | (HCLKX2_RATIO

<< 9)

| (PCLK_RATIO

<< 12);

APLL_CON = APLL_CON_VAL; MPLL_CON = MPLL_CON_VAL;

CLK_SRC = 0x03;

}

下面对时钟设置步骤进行说明:

(1)设置LockTime,包括APLL_LOCK,MPLL_LOCK,EPLL_LOCK,一般设置为默认值即可,也可以不用设置,因为它复位后即为默认值。

(2)设置为异步模式,当CPU时钟和内存时钟不相等的时候,需要设置为异步模式,主要是设置寄存器OTHERS,然后在查询相对应位是否为0,一直等待设置完毕。

(3)然后沿着上图的时钟体系设置PLL寄存器的值

7、 main.c

主函数文件可以自己去写,实现何种功能由C来完成

8、 main.lds

该裸机程序的链接脚本

SECTIONS {

. =

0x50000000; //当前地址

. = ALIGN(4);

.text :

{ //段名称,放置所有文件的代码段

start.o (.text)

time.o (.text)

irq.o (.text)

led.o (.text)

}

. =

ALIGN(4); //4位对齐

.rodata : {

* (.rodata)

}

. = ALIGN(4);

.data : {

* (.data)

}

. = ALIGN(4);

bss_start =

.; //bss段开始处

.bss :

{ //放置所用bss段

* (.bss)

}

bss_end =

.; //bss段结束处

}

以前写简单的程序,可以不用DDR,只将程序放在6410的8K片内ram运行即可,但是程序很大时,那就难以在片内ram中运行程序了。就需要用到SDRAM,这就得涉及到链接地址。

一个程序可分为下面几个部分:

(1)代码段(text):就是我们写的代码,指令

(2)数据段(data):有初始值的全局变量或静态变量

(3)Bss段(Bss):未初始化或初始值为0的全局变量或静态变量

分析反汇编文件我们得出:访问全局变量使用的是链接地址来访问的。在系统上电后,系统会自动的把NandFlash中的前8K程序拷贝到片内8K内存当中去,而一个程序要执行,应该位于链接地址。当程序的链接地址不等于当前地址时,就需要重定位,将程序拷贝到相应的链接地址中去执行。

位置无关码:相对跳转指令,不访问全局变量。下面看一下重定位代码:

adr r0,

_start ;获得_start的当前地址:

0

ldr r1, =

_start ; _start的链接地址

ldr r2, =

bss_start ; bss 段的气质链接地址

cmp r0,

r1 ; compare isnot equal

beq clean_bss

copy_loop:

ldr r3, [r0], #4

str r3, [r1], #4

cmp r1, r2

bne copy_loop

clean_bss:

ldr r0, = bss_start

ldr r1, = bss_end

mov r3, #0

cmp r0, r1

ldreq pc, = on_ddr

clean_loop:

str r3, [r0], #4

cmp r0, r1

bne clean_loop

ldr pc, = on_ddr

在分析过程中,我们可参照反汇编文件来分析

9、 Makefile

在Linux下开发,了解Makefile也是很有必要,下面是Makefile代码:

CC = arm-linux-gcc

LD = arm-linux-ld

AR = arm-linux-ar

OBJCOPY = arm-linux-objcopy

OBJDUMP = arm-linux-objdump

CFLAGS = -Wall -Os -fno-builtin-printf

export CC LD AR OBJCOPY OBJDUMP

CFLAGS

objs := start.o time.o sdram.o irq.o

main.o

led.bin : $(objs)

$(LD) –Tmain.lds -o main_elf $^

$(OBJCOPY) -O binary -S main_elf $@

$(OBJDUMP) -D -m arm main_elf >

main.dis

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

$<
%.o : %.S

$(CC) $(CFLAGS) -c -o $@

$<

clean:

rm -f *.dis *.bin *_elf *.o

对于Makefile的理解学习可以在网上搜索一下《跟我一起写 Makefile》这个文档,看着写的不错。

欢迎关注飞凌嵌入式官方微信:

ok6410linux开发环境搭建,飞凌嵌入式知识汇021期:OK6410裸机程序之开始模板(Linux环境)...相关推荐

  1. 嵌入式linux头文件,飞凌嵌入式知识汇092期:C工程的组织方式(头文件的功能)以及Linux下常用的头文件的作用...

    1.1每个C++/C程序通常分为两个文件.一个文件用于保存程序的声明(declaration),称为头文件.另一个文件用于保存程序的实现(implementation),称为定义(definition ...

  2. LINUX内核模块上下文,飞凌嵌入式知识分享-Linux内核模块编译

    本文主要讲解什么是Linux内核,以及通过多张图片展示Linux内核的作用.功能及基本编程方法,以便于读者能快速理解什么是Linux内核,能看懂Linux内核. 拥有超过1300万行的代码,Linux ...

  3. 首发!飞凌嵌入式i.MX9系列核心板重磅发布

    来源:飞凌嵌入式官网 www.forlinx.com 为了让更多设备实现高能效.高安全性和智能化升级,NXP推出了全新的i.MX 93x系列处理器.作为NXP的重要合作伙伴,飞凌嵌入式在i.MX 93 ...

  4. qt工程在linux系统里颜色显示错误_【飞凌嵌入式RK3399开发板试用体验】+QT开发环境搭建测试(二)...

    作者:飞扬的青春 在拿到开发板之后,已经体验了Android操作系统,接下来就是体验Linux下的开发,本次以QT的一个小案例来测试下. 首先是自己先搭建了一个Ubuntu18.04的虚拟机,使用真机 ...

  5. 使用飞凌嵌入式IMX6UL-C1板子——qt+opencv环境搭建

    使用飞凌嵌入式IMX6UL-C1板子--qt+opencv环境搭建 准备工作: Ubuntu18.04虚拟机环境 飞凌官方提供的qt-tslib库,gcc交叉编译器,opencv源码库,qt源码,具体 ...

  6. 飞凌嵌入式NXP i.MX8MMini 开发板试用体验

    收到飞凌OKMX8MM-C开发板进行测评.这款属于工控板,采用核心板和地板设计思路. CPU芯片等部分制成核心板,外设芯片接口部分制成底板,把核心板和底板组装在一起成为一块功能完整的开发板.软件也可简 ...

  7. 飞凌嵌入式研发干货 | LS1028A开发板网络应用测试

    本文硬件平台以飞凌嵌入式OK1028A-C开发板为基础进行讲解,其它LS1028产品,由于各个厂家设置不同会有所差异,请参考使用.本文档主要介绍 NXP LS1028A开发板网络中的应用测试.包括 D ...

  8. lighttp支持PHP移植到imx6,[Qt开发指南]飞凌嵌入式iMX6开发板QT移植

    虚拟机环境: ubuntu12.04 32bit /64bit 目标平台: i.MX6 开发板 交叉编译工具链: gcc gcc-4.6.2-glibc-2.13-linaro-multilib-20 ...

  9. linux 开发板模拟u盘,S5P4418开发板Linux下实现模拟U盘教程飞凌嵌入式

    飞凌嵌入式ARMCortex-A9 S5P4418开发板上能否满足U盘的功能?其实可通过修改内核配置和文件系统相关内容,在OK4418开发板上实现模拟U盘功能,模拟U盘可以帮助我们实现开发板与Wind ...

最新文章

  1. docker 2375 vulnerability and self-signatuer certifications
  2. PostGIS_导入shp格式的数据
  3. 【职场】你做程序员,真的是因为热爱吗?
  4. MySQL Administrator
  5. 做靠谱的程序员--《程序员修炼之道》读书报告
  6. Java程序设计----Java编程基础
  7. java全jit编译_JVM即时编译(JIT)(转载)
  8. [Java基础]字节,字符打印流
  9. 2018年11月27日 分类与集合
  10. 给WP7初学者:《WP7 Dev Quick Start系列视频》
  11. Java课堂作业-------参数求和
  12. Laravel 学习笔记之文件上传
  13. resin服务器搭建
  14. python实现pdf转ppt_wps中pdf转成word文档 Python转换PPT为PDF
  15. 资产类别某一类折旧查询
  16. 如何用人工智能自动玩游戏
  17. 15. Python3 处理JSON格式数据(制作交易收盘价走势图)
  18. 计算机管理 合并分区,硬盘分区进行合并 硬盘分区后怎么合并
  19. Spark Streaming简介 (三十四)
  20. 老派程序员浅谈Python的异常处理

热门文章

  1. 微软ERP dynamics Axapta中的保留字
  2. linux_配置_cron定时任务配置
  3. 中国象棋程序的设计与实现(零)--原始版源码
  4. 计算机审计 报告哦,计算机审计实训报告.doc
  5. 行云管家荣膺《中国网络安全行业全景图(第九版)》收录
  6. Ripro9.0全解密去扩展版修正升级版+两款WP美化包+下载信息美化等插件,新增易支付接口
  7. RESTful风格(使用Ajax+Spring MVC框架实现)
  8. 发票账单很多?python助你批量完成数据提取
  9. Python基础知识、基本概念、下载python和pycharm、列表、元组、字典等等
  10. 大疆推教育机器人已到第二代!机器人编程玩具的前世今生是怎样的?