日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

發布時間:2024/3/24 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ok6410linux开发环境搭建,飞凌嵌入式知识汇021期:OK6410裸机程序之开始模板(Linux环境)... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

手中有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环境)...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。