存档

文章标签 ‘Embedded System’

Luminary的Cortex-M3核心ARM处理器基于IAR的中断注册方法

2008年8月22日

建议采用IAR工程模板来编写程序,比较快捷。

注册中断有两种方法:采用xxxRegister()函数,或者手工修改启动文件startup.c的中断向量表

例如中断函数是:
void I2C_ISR(void)

则在starup.c里要修改两个地方:

1. 找到
extern int main(void);
在后面补上中断函数的声明:
extern void I2C_ISR(void);

2. 在中断向量表的数组里,找到注释I2C的项目,将IntDefaultHandler换成I2C_ISR

完成。

Jacob 技术 ,

关于SBC2410板子的串口

2008年6月30日

回家了一个星期,就算是放过暑假了,明天开始去实验室~~~

回家之前一天一直在折腾SBC2410这块板子的串口,以前这个串口程序是调出来的,程序都有以前写好的,但是烧在板子上PC机就是收不到数据,所以一直怀疑是我新买的PALM的USB转串口线有问题。在家的时候买了一台示波器,找了一根串口的对接线用来连接PC和示波器,测试发现我的USB转串口是好的,而且超级稳定,但是很奇怪,用这根线没法让两台电脑进行串口通讯,于是开始怀疑是线的问题,家里找不到万用表,所以一直拖到现在。

回南京之后,用示波器看了下SBC2410板子的串口,单次触发,得到的结果竟然是板子上2号脚是发送,难怪之前不能用。又用万用表测了一下家里用来连接PC和示波器的那个串口母对母的线,竟然是直连线,2号和3号没有交叉,难怪不能两台计算机通讯。

结论:1、SBC2410和示波器的串口都是公口,但都和电脑上的公口定义不一样。
2、串口母对母的线是直连线,不能进行双机通讯(这点目前仅限我手头上的这根,但是我觉得估计都是这样。。。。)
3、PALM的USB转串口线可以用。

附串口各引脚定义:
Pin Name Dir Description
1 CD Carrier Detect
2 RXD Receive Data
3 TXD Transmit Data
4 DTR Data Terminal Ready
5 GND System Ground
6 DSR Data Set Ready
7 RTS Request to Send
8 CTS Clear to Send
9 RI Ring Indicator

over

Jacob 技术

重新编译SJF2410以适应NM9805并口卡(PCMICIA接口)

2008年6月23日

H-Jtag软件自带的H-Flasher只提供了烧写Nor-Flash的功能,并不能烧写Nand等,很庆幸SJF2410提供了C++源代码,立马装了个VC对代码进行重编译以适应我的PCI转出来的并口卡,简单的来说就是将默认的378地址改成我的FFE8。

主要要改的是两个文件

1、ppt.h

源代码如下,可以看到默认的LPT1-3地址都在上面,将2和3删去,将LPT1地址改为0xFFE8
#ifndef __PPT_H__
#define __PPT_H__

int GetValidPpt(void);
int InstallGiveIo(void);
void SetPptCompMode(void);

extern int validPpt;

#define LPT1 0×378  // the search order is LPT1 then 2 then 3
#define LPT2 0×278  // first valid address found is used (re-order if needed for multiple ports)
#define LPT3 0×3bc  // hardware base address for parallel port

#define OutputPpt(value)    _outp((unsigned short)validPpt,value)
#define InputPpt()      _inp((unsigned short)(validPpt+0×1))

#endif //__PPT_H__

如下:

#ifndef __PPT_H__
#define __PPT_H__

int GetValidPpt(void);
int InstallGiveIo(void);
void SetPptCompMode(void);

extern int validPpt;

#define LPT1 0xFFE8
#define OutputPpt(value)    _outp((unsigned short)validPpt,value)
#define InputPpt()      _inp((unsigned short)(validPpt+0×1))

#endif //__PPT_H__

2、PPT.c
按道理来说改完上面的就可以用了,但是不知道为什么,编译后还是提示找不到并口,于是手动将LPT1返回。
涉及到的函数是int GetValidPpt(void)。
源代码:

int GetValidPpt(void)
{
// search for valid parallel port
_outp(LPT1, 0×55);
if((int)_inp(LPT1) == 0×55)
return LPT1;

_outp(LPT2, 0×55);
if((int)_inp(LPT2) == 0×55)
return LPT2;

_outp(LPT3, 0×55);
if((int)_inp(LPT3) == 0×55)
return LPT3;

return LPT1;
}

修改后:

int GetValidPpt(void)
{
// search for valid parallel port
return LPT1;
}

Jacob 技术

关于ro(rw)的问答

2007年3月4日

2007-03-4,23:04:47
请再解释一个关于ADS中ro/rw base的疑惑(已查阅资料)!

首先说一下,我认为我还是看了不少的资料,然后再来问这个问题的,看起来下面我写的很多,其实我只是想把问题描述清楚一点,我认为我已经写的比较详细了,所以应该不会耽误各位大侠多少时间,所以希望能耐心看完,谢谢 !

我的CPU是4510,我在各种资料上都看过相关的解释:
-ro-base address(即设定Image$$RO$$Base--我的理解)
这个选项将包含有RO(Read-Only属性)输出段的加载地址和运行地址设置为address,
该地址必须是字对齐的,如果没有指定这个选项,则默认的RO基地址值为0×8000。
-rw-base address
这个选项设置包含RW(Read/Write属性)输出段的域的运行时地址,该地址必须是字对
齐的。

Image$$RO$$Base is the address of the read-only execution region (usually
contains code and read-only data).--ro运行时地址

Image$$RW$$Base is the address of the read-write execution region (usually
contains data).--rw运行时地址

所以大致意思,我是知道的,不过在还是在有几个问题想不明白:

问题1、就是这个设定的Image$$RO$$Base与我们复位时CPU读取第一条指令的 0×0 地址有什么关系?这个“Image$$RO$$Base”到底又设定了那些源代码的运行起始地址?比如在一个Bootloader中,往往包含同时包含汇编代码和C语言代码两部分,这个“Image$$RO$$Base”只设定了C语言中RO段的运行地址 ?(拷贝RO RW段代码到Image$$RO$$Base,Image$$RW$$Base标识的位置是我的bootloader 汇编代码进入C前做的最后一件事)
如果说也设定了汇编中ro部分代码的运行地址,那问题2又怎么解释 ?

问题2、有人说“请注意,将工程编译为烧入Flash的二进制代码时,需要将链接器重新设置,将调试时程序空间定位地址(RO Base地址)改为0×00000000”,但我明明刚刚编译的Bootloader就将ro base设为了0xf00000,现在我已将其烧写入Flash中测试过,Bootloader运行良好,而且在上电时Flash首先被映射到0地址开始的地方,这与设定的0xf00000差的很远,当然在程序将进入C语言前,会remap SDRAM到0~16M空间,并将RO RW段拷贝到Image$$RO$$Base,Image$$RW$$Base标识的位置,问题是这个拷贝之前我的所有代码(比如SDRAM初始化,remap,堆栈初始化等)都运行的很正常,这又怎么解释呢?
这样一来我到有个奇怪的想法就是,如果我的程序没有C语言代码,岂不是就可以不用管这个ro base的设置?因为从一开机到拷贝ro/rw段前,我所有的汇编代码执行良好。

问题3、我的vector.S中复位入口“ResetEntry”的部分代码如下,用AXD载入ADS编译的这个.bin文件的时候可以看到,这个 “ResetEntry”标号开始的代码被编译到了.bin文件的最前面,因为我在链接设置里面把reset section放在了img的最前面。我有点不明白就是这个“ResetEntry”标号代表着的一个地址与Image$$RO$$Base又有什么关系?与复位时CPU读取第一条指令的 0×0 地址又有什么关系?因为我的Bootloader在拷贝ro段代码时用到了这个标号,同时我的Bootloader代码也并不是真正放在Flash的起始部分(即开机的0地址处),只是在0地址处放了一条跳转到Flash的高端,再执行哪里存放的Bootloader代码。

AREA reset, CODE, READONLY
ENTRY
;复位和上电启动的入口
ResetEntry
b SYS_RST_HANDLER
b UDF_INS_HANDLER
…………………………………..

4、我在AXD调试程序时还发现,调试过程中我可以把ADS生成的.bin文件载入SDRAM中任何位置,然后修改pc值执行之,不论你的 Image$$RO$$Base,Image$$ROW$$Base设定在什么地方,当然我的这个调试的程序肯定没有对ro/rw段做任何的拷贝操作。这让我更对RW段运行时地址和Image$$ROW$$Base的关系感到疑惑,因为rw段中不是保存有程序需要的变量或数据么?Image$$ROW$$Base设定了rw段的运行地址,那岂不是也就设定了运行时变量或数据在内存中的存在位置?那我用AXD加载的这个程序又在哪里获得的程序运行需要的变量和数据呢 ?

上面的问题其实一直就困扰了我好久,不过因为项目方面比较急,所以也就一直放在哪里没有对其深究,现在稍微有点空,才放到网上来,请各位解惑,非常感谢 !
我的表达能力也不怎么好,上面的问题也许有点罗嗦,如果还有什么没有说清楚的也希望能提出来,3KS ag !

********************************************************************************************

四个问题其实就是一个,即链接器到底指定了什么东西。简单地说,linker把对函数的调用转成向某一地址的跳转,把对变量的读写转成对某一地址的访问。

但是,一段正在执行的代码本身,从本质上来说,并不需要知道自己运行在什么地址。只要PC指针有一个初值(比如0×0),然后每次加4之后,能取到下一条指令就行了。与此相关的另一个概念就是PIC(Position Independent Code,与位置无关的代码)。

只有当代码中调用了函数,向某个symbol跳转或访问了变量,才需要知道真实的地址。所以Image$$RO$$Base是否起作用,并不取决于C代码和汇编代码的区别,而在于PIC与PDC的区别。
********************************************************************************************

版主和TALISKER已经回答了楼主的问题。我也说说自己的看法,希望对你有帮助。
1. Image$$RO$$Base is the address of the read-only execution region
你仔细琢磨RO BASE的意思,RO BASE指定的只是RO段的起始地址,注意:并没
有说始第一条指令的地址。所以,如果你的程序包括多个段的时候,有可能别
的段放到了RO BASE指定的地址上,而并不是期望执行的第一条指令。在调试
的时候,这不会造成什么问题,因为DEBUGGER会把PC指向程序的第一条指令。
如果你把程序烧写到FLASH里面后,这可能会造成问题,因为CPU在RESET后只
从地址0×0处开始取指令。所以,如果你写一个程序,期望程序从地址0×0开始
执行,要满足两个条件:1-RO BASE要为0×0,2-告诉linker,把包括你希望
执行的第一天指令SECTION链接到RO段的最前面。

2. 对于你说到的第二个问题,为什么你把RO BASE设置为0xF0000000还能从地址
0×0开始执行?这是因为4510的地址绕卷问题造成的,其实地址0xF0000000就是
地址0×0,不信你可以自己测试一下,从地址0xF00000读出来的指令和地址0×0
的指令一样。所以,你的第二个问题不是问题,这和RO BASE没什么关系。

3. 前面说过了,一个程序包括了RO段,但程序可能包括了很多个SECTION,在编译
的时候,各个section的RO段都是被链接到你指定的RO BASE开始的地方的,这
就涉及到另外一个问题:在RO段里面,你怎么安排各个sedtion在RO段的顺序?
如果你写的是BOOTLOADER的话,你必须要把包括你要执行的第一条指令的段放
在最前面,这样才能保证板子上电后能正常运行。

4. 我不知道你的程序里面是否有RW段?程序分为RO段和RW段,但并不是每个程序
都一定有RW段,如果你的程序里面没有全局变量等,那编译后,肯定是没有RW
段的,在这种情况下,你不进行RW段的拷贝,程序也能正常运行的。OK,在来
看看你的问题,你说在编译生成bin文件后,在bin文件里如何定位RW段?答案
是:单单依赖bin文件是无法定位RW段的。所以 - 如果你的程序有RW段的话,
你必须在自己程序里面处理RW段的拷贝。
********************************************************************************************

首先谢谢各位兄弟、大哥的回复,非常感谢 !

其次针对各位的回复,再综合我所知道的别人给的解释,我想再谈一下我自己现在的想法:
to Talisker:我不知道你想设定ro base在0×04的意思是什么?

to Twentyone:
针对你的回答1:
“在调试的时候,这不会造成什么问题,因为DEBUGGER会把PC指向程序的第一条指令。”---我调试的时候采用的是ADW载入ADS编译的二进制bin文件的方式,所以pc值或bin文件载入地址都是由我指定的,但都与ro base设定的值不同。
针对你的回答2:“对于你说到的第二个问题,为什么你把RO BASE设置为0xF0000000还能从地址0×0开始执行?这是因为4510的地址绕卷问题造成的,其实地址0xF0000000就是地址 0×0”---你说的没错,0xF0000000地址最终会对应到0地址,不过我是将ro base设在了0xf00000,即15M内存的地方,而调试使用的obey xxx.ini命令将0~16M的内存都分配给了SDRAM,所以将ro base 设在0xf00000,还没有到地址卷绕的地方。
针对你的回答4:
我调试的程序首先是比较简单,那是不错,不过变量肯定是有的,不论全局、局部变量,都有 !是一个同时包含串口和IO口简单测试的程序。

另外有人回答说:

解答问题1&2:这说明你的bootloader是基于相对地址的,也就是说与地址无关,而这也是对bootloader的一个基本要求。一般的bootloader都设计成地址无关的。

解答问题4:这说明你的程序比较小,或者函数调用不复杂,堆栈操作不频繁等。也就是说,不按照你的设置的ro和rw基址调入程序,有可能数据空间不够,或者代码和数据重叠,程序执行就可能出错。

------我看这个回答好像是对的,不知道各位对此有什么理解或看法 ?

********************************************************************************************
Twentyone:

文后附的是一个测试程序,这个程序可以验证RW的影响。
测试的之前,你把你的板子的MEMORY给配置好,把SDRAM配置到地址0X0开始的地方。

测试1:程序的RO_BASE = 0×0,RW_BASE不设置,你用仿真器单步运行程序,然后观察
内存0×800的内容变化,变化顺序应该是0×0 -> 0×1 -> 0×2 … -> 0×9

测试2:程序的R0_BASE不变,还是RO_BASE = 0×0,RW_BASE设置为0×400,单步运行程
序,观察内存0×800的内容变化,看看结果和第一次测试的是否一样

我:

我试过了,也基本上清楚了,按照你的实验设定测试2会看到0×800地址的数据不规则的变化,我又看了一下对应的汇编代码(我是指编译后的汇编代码),发现程序在读取array数组值时,是从rw base开始的地方读取数据的,因此如果一开始没有将rw段放到rw base开始的地方,则程序执行后读取的数据肯定是不对的,这也符合rw base本身的意思,现在我应该说是彻底的清楚了,谢谢 !
我以前的问题是:我在程序中使用变量前首先就先改变了变量的值,因此RW段首先就被我程序先修改了,也就是说虽然我没有搬移RW 段的操作,但是我重新修改了rw base开始的数据的值,对应到你的这个测试程序,也就是说本来我没做搬移RW段的操作,则0×400地址开始的数据(在你这个测试程序中存放的是 array数组的值)是不确定的,但后来程序一开始我就修改了array数组的数据,也即修改了0×400开始的数据,因此RW段虽然我没有搬移操作,但其实也就被我重新设定正确了,然后再读取使用,当然执行起来,当然就没有什么问题了,不过这里有一点:就是我的程序本来就比较小,RW段也很小,所以没有什么问题,但如果程序大一点,RW段太大,我不能够一一重新指定,那程序就会出错了,问题解决,非常感谢 !
不过关于RO段的问题:RO base 设定值与bin程序载入值不同,但程序仍能正确执行的问题 。
我想可能只能用我得代码都是“地址无关”的这个来解释了,对吧 ?
这东西有点绞,不知道我上面的表达清楚没? 呵呵,如果没什么问题,我想就把上面这段话转到电子产品世界去了,然后我那个帖子也就可以方封贴了,呵呵,不过真的非常感谢你的帮助,谢谢 !

Twentyone:

不用客气!

你说的很清楚,你的理解是正确的,关于RO_BASE的问题,如果程序完全运行正确的话
,只能用与地址无关来解释。

如果你想做一个RO_BASE的测试的话,你可以写一个测试程序,在程序里面包括一些绝
对寻址的指令,你就会发现程序运行时用不同的RO_BASE的话,运行会有问题的(可能
不影响结果,但中间肯定是有地方和你期望的不一样的)。

ft,忘了贴代码了,Twentyone大侠的测试程序如下:

init.s:

AREA Init, CODE, READONLY
CODE32
ENTRY
ARM
LDR SP, =0xC00
IMPORT CTEST
B CTEST
END

test.c:

unsigned int array[] = {0×1230,1,2,3,4,5,6,7,8,9};

int CTEST(void)
{
int i;
unsigned int base;
i = 0;
base = 0×800;
for(i = 0; i < 10; i++){
*((unsigned int*)(base)) = array[i];
}
return 0;
}

引用自
http://bbs.edw.com.cn/dispbbs.asp?boardid=20&replyid=249879&id=46592&page=1&skin=0&Star=1

Jacob 技术

S3C2410串口的使用

2006年12月27日

2006-12-27,19:01:04
S3C2410共有3个串口,每个串口有16字节的FIFO寄存器,可选红外模式,可编程波特率,一或两位停止位,5-8位数据位可编,和奇偶校验。

我们先做一个最简单的程序,波特率定为115200,不使用FIFO,一位停止位,8位数据位,无奇偶校验。
程序作用是不停向串口0发送字符’a',即ASCII码61

程序清单如下:

#include “2410addr.h”
void Delay(void);
int Main()
{
int i,j;
rGPHCON=0×2faaa;;
rGPHUP=0xfff;
rULCON0=0×3;
rUCON0=0×5;
rUFCON0=0×0;
rUMCON0=0×0;
rUBRDIV0=( (int)(PCLK/16./115200) -1 );

while(1)
{rUTXH0=’a';
Delay();};

}
void Delay(void)
{
int i;
for(i=100000;i!=0;i–);
}

Jacob 技术

S3C2410 定时计数器的使用

2006年12月26日

调试定时计数器成功,根据手册上的方法

1) Write the initial value into TCNTBn and TCMPBn.
2) Set the manual update bit of the corresponding timer. It is recommended that you configure the inverter on/off bit.
(whether use inverter or not).
3) Set start bit of the corresponding timer to start the timer (and clear the manual update bit).

代码如下:

#include “2410addr.h”
void Delay(void);
int Main()
{
rGPBCON=0×2aa;
rTCFG0=0xff;
rTCFG1=0×0;
rTCNTB0=0xa0;
rTCMPB0=0×6e;
rTCON|=0×0a;
rTCON|=0×1;
rTCON&=0xfffffffd;

}

Jacob 技术

ARM9调试问题解决

2006年12月18日

折腾了很多天AXD调试总是不对,调试的时候运行的都是板上的程序,而不是CodeWarrior写的程序,这年头2410的资料真是少。原来只要在 Edit–DebugRel Settings ARM Linker里设置RO = 0, RW = 0×30000000就可以了。自己试出来的,后面转贴一下原理。

Jacob 技术

[转贴]S3C2410软件调试总结

2006年12月18日

这部分介绍下ADS下如何生成可以运行的ROM镜像文件,我们知道当程序下载到flash中运行的时候,对于RW、ZI数据就存在着两个环境,一个 load环境,一个是exec环境,有时候由于速度的需要RO数据也要重新加载,那么对RO数据也是有两个环境。编译器产生ROM镜像文件时候,这三块数据的存放依次为RO、RW、ZI,并且地址空间时连续的。但是到了运行的时候,RW数据必须被拷贝到SDRAM(SRAM)中以支持读写,这就是我们所谓的运行环境。那么就要有一段代码去完成这个任务,在本章中我们介绍如何生成这段代码。

玩过2410的朋友都知道2410初始化代码中有一段搬运RW和ZI初始化的代码,没错,它确实能够在一定程度上完成上面所说的任务,只要我们在生成二进制可执行代码的时候在编译器链接项的地方填写正确的RO&RW地址,(比如RO = 0, RW = 0×30000000), 那么将程序下到 NOR flash的零地址并从nor flash启动,启动代码会将RW&ZI数据弄到0×30000000,程序就能跑起来了。

但是各位有没有想过,怎么把RO代码弄到SDRAM中(有时候这是必须的,比方后面我将提到用nor flash的bootloader烧写nor flash)?如果直接设RO=0×30000000,那么这段代码下载到0地址肯定跑不起来,除非是ROPI,这个要求就高了。这里我们有必要从介绍 ADS中规定的C语言入口开始,ADS中从初始化汇编代码跳到main函数有两种方式,main和__main:

1,在__main入口的模式下,汇编代码的指令为 b __main, 编译器在跳转到main之前还要作一系列的工作,这其中就包括对运行环境的初始化,在中提到: copies nonroot(RO&RW) execution regions from load addr to exec addr, and Zeros ZI region. 借助编译器,我们就可以定义更为复杂的运行环境,这里要用到scatter文件(.scf),比如我们要的目标运行环境是:将启动代码以外的所有代码都拷贝到SDRAM的初始地址中运行,比且把RW段设在0×30800000,那么对应的scf文件如下:

FLASH 0×0 0×200000
{
EXEC1 0×0 0×200000
{
2410init.o(Init, +First)
__main.o(+RO) copy code
* (Region$$Table) RO/RW addresses to copy
* (ZISection$$Table) ZI addresses to zero
}
EXEC2 0×30000000 0×00800000
{
*(+RO)
}
SDRAM 0×30800000 0×00800000
{
*(+RW,+ZI)
}
}
;Sections named Region$$Table and ZISection$$Table which contain the addresses of the code/data to be copied.

当然,在这种模式下,有些入口函数必须自己重定义,比如__user_initial_stackheap,具体参见ADS文档。

2, main入口模式即简单的跳转,这里起始不用“main”这个名字也无所谓。那么编译器不会作任何的初始化,所有运行环境的建立都要* 我们自己,这就是大家看到的那段搬运代码存在的理由。但是它实现一些简单的运行环境是可取的,如果用scf定义的复杂环境,虽然我相信是可以做到的,但是可能会比较麻烦。我还没深究。

另外,这里提一下semihost,因为我们在看ADS的东西的时候经常出现这个词,我也一直受其困扰。这里我简单说一下自己的见解,semihost 仅仅是一种调试手段,它的机理就是利用MULTI_IDE等工具捕捉目标环境运行过程中产生的值为0×123456的SWI中断,然后向上位机的ADS 软件发送对应的调试信息。对于我们最后的应用代码来说,都是nonsemihost类型的。如果我们在调试中使用semihost,那么只要在最后重定义 ADS中的一些使用到的库函数(比如fputc),代码就可以从semihost向nonsemihost的类型转变。不过到目前为止,我还没体会到 semihost的威力。

2410启动代码分析
这一章主要对目前广泛流行的2410启动代码进行分析:S3C2410的初始化代码主要涉及到对系统主要模块的配置、运行环境的建立、系统时钟、MMU等模块的配置,下面按执行顺序依次都各个部分进行分析:

程序入口:(ResetHandler)
在程序一开始,首先进行的一些操作主要保证初始化程序能够顺利的运行, 因此主要包括关闭WDT、中断,配置锁相环等。

配置memory接口
memory接口是确保数据访问正确的基本保障,此处主要配置SFR寄存器中0×48000000开始的memory接口寄存器组, 确保每个bank的位宽、访问类型(waitable)以及时序参数正确。如果没有特别的要求,一般来说时序参数使用默认值即可。

初始化堆栈
ARM有6种运行模式,必须为每一种模式提供独立的堆栈空间,在堆栈设置之前是不能进行C函数的调用的。ARM的堆栈模式 是从高地址递减的,我的所有代码统一将堆栈的首地址设在0×33ff8000处,往低依次为FIQ、IRQ、Abort、Undef、SVC,其中
SVC和User模式不予区分。堆栈大小一般可在头文件或者当前文件中修改。

运行空间的初始化
这段代码主要完成两个功能,一是将RW数据搬运到RW空间(我们生成ROM镜像时,RW数据是跟在RO数据之后的),二是 初始化ZI数据段。当然,这段代码存在的前提是代码的运行环境只是标准的两段式:一段RO空间和一段RW空间;并且在C程序
入口时没有调用编译器的链接库(__main)。后者已经提供相应的功能,并且支持更加复杂的运行环境定义(使用SCF文件),
(关于这一点,我在介绍ADS中C代码的启动模式时已经详细介绍)。

__rt_lib_init
在ADS1.2的环境中,如果在C入口没有调用编译器的链接库(__main),那么在C程序一开始要调用该函数以初始化运行时的函数库,以保证对ADS提供的某些库函数能够正常调用。从这个函数开始,我们已经在C语言环境下了。

MMU初始化
2410的MMU支持1级&2级地址映射,在我们目前大部分应用中均采用1级section模式的地址映射,一个section的大小为1M,也就是说从逻辑地址到物理地址的转变是这样的一个过程:
一个32位的地址,高12位决定了该地址在页表中的index,这个index的内容决定了该逻辑section对应的物理section; 低20位决定了该地址在section中的偏移(index)。
因此从0×0~0xffffffff的地址空间总共可以分成0×1000(4K)个section,页表中每项的大小为32个bit,因此页表的大小为0×4000(16K)。在我的代码中所有程序的页表统一存放在地址0×33ff8000。
每个页表项的内容如下:

bit: 31 20 19 12 11 10 9 8 5 4 3 2 1 0
content: Section对应的物理地址 NULL AP 0 Domain 1 C B 1 0

最低两位(10)是section分页的标识。
AP:Access Permission,区分只读、读写、SVC&其它模式。
Domain:每个section都属于某个Domain,一个有16个Domain,每个Domain的属性由CP15的R3寄存器控制。在我得所有程序中,都只包含两个Domain,一个是SFR地址以下(包括SFR)的空间,可访问; 另一个是SFR以上的空间,不可访问。
C、B:这两位决定了该section的cache&write buffer属性,这与该段的用途(RO or RW)有密切关系。不同的用途要做不同的设置。

C B 具体含义
0 0 无cache,无写缓冲,任何对memory的读写都反映到ASB总线上。

对 memory 的操作过程中CPU需要等待。
0 1 无cache,有写缓冲,读操作直接反映到ASB总线上。写操作CPU将数据写

入 到写缓冲后继续运行,由写缓冲进行ASB操作。
1 0 有cache,写通模式,读操作首先考虑cache hit;写操作时直接将数据写入

写缓冲,如果同时出现cache hit,那么也更新cache。
1 1 有cache,写回模式,读操作首先考虑cache hit;写操作也首先考虑cache,

如果hit,则只修改cache,并将cache对应半行的dirty比特置位;如果miss,

则写入写缓冲,触发ASB总线操作。

在我的程序中内存空间的分配统一采用了文末的MEMORY图。虽然MMU只是使用了逻辑地址到物理地址的linear transfer(值不改变),但是由于MMU能够引入cache&write buffer,因此系统性能有很大的提高!

配置时钟比、重新设置PLL
2410内部有三个时钟:FCLK、HCLK、PCLK,分别供CPU、AHB总线和APB总线使用,为了降低功耗,一般都选择周期比为1:2:4的合理配置。 同时将PLL配置为运行环境时钟,一般都达到最高202M。

IO初始化
将IO口配置为对应的功能选项,同时一般会点亮相应的LED灯。

中断初始化
2410的内存空间没有remap的机制,应该中断入口时钟位于零地址。因此中断服务机制可以描述如下:
首先,不管使用那种启动方式,必须确保一下代码段位于内存的0×0地址:
b ResetHandler
b HandlerUndef handler for Undefined mode
b HandlerSWI handler for SWI interrupt
b HandlerPabort handler for PAbort
b HandlerDabort handler for DAbort
b . reserved
b HandlerIRQ handler for IRQ interrupt
b HandlerFIQ handler for FIQ interrupt
除ResetHandler外,其余各项都是由如下的宏定义的一段代码:
HandlerFIQ HANDLER HandleFIQ
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 decrement sp(to store jump address)
stmfd sp!,{r0} PUSH the work register to stack
ldr r0,=$HandleLabel load the address of HandleXXX to r0
ldr r0,[r0] load the contents
str r0,[sp,#4] store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} POP the work register and pc(jump to ISR)
MEND
这段代码的含义是通过堆栈将中断向量表中的内容赋给PC指针(如HandleFIQ是存放着FIQ服务程序入口地址的地址),自然程序就跳到相应的入口地址。
可见,中断向量表存放的是各个中断服务程序的入口地址,它是用来被加载的,而并不是可执行代码。为了统一,所有示例程序都将中断向量表放在0×33ffff00开始的地址,并根据入口地址依次排列。
需要注意的是如果各种模式的服务程序用C语言定义,那么类型必须用__irq定义,以保证能够正确返回。

初始化串口
串口统一选用UART0,模式采用115200、1bit STOP、No Parity。

最后跳转到我们自己的应用程序!

附:我得程序所使用的地址空间结构以及MMU中C、B的设置:

Blank Area: RW_FAULT 0×5b000000 ~ 0xffffffff

Sram & SFR: NCNB 0×40000000 ~ 0×4affffff

Blank Area: RW_FAULT 0×34000000 ~ 0×3fffffff

Int_Vec, Stack, MTT: CNB 0×33f00000 ~ 0×33ffffff

SDRAM Download: NCNB 0×31000000 ~ 0×33efffff

SDRAM Exec RW: CB 0×30800000 ~ 0×30ffffff

SDRAM Exec R CNB 0×30000000 ~ 0×307fffff

Bank5, FPGA: NCNB 0×28000000 ~ 0×2fffffff

Bank4, FPGA: NCNB 0×20000000 ~ 0×27ffffff

Bank3, Bottom NIC: NCNB 0×18000000 ~ 0×1fffffff

Bank2, Bottom Flash: CNB 0×10000000 ~ 0×17ffffff

Bank1, Bottom Sram: CNB 0×08000000 ~ 0×0fffffff

Bank0, Flash or Sram: CNB 0×00000000 ~ 0×07ffffff

Nor Flash Bootloader
这是我着手写的第一个程序,我的想法是让这个程序同时支持通过串口对Nand 和 Nor FLASH的烧写,如果不进行任何烧写,那么就跳到Nor Flash的第二个section启动应用程序,这样一来,即使脱离JTGA,我也可以使用串口进行盲调。

由于有现成的初始化文件和flash烧写的示例程序,开发起来还比较快。当然也遇到了一些问题,一开始连flash的device ID都读不出来,后来发现我指针没有定义成volatile类型,flash的操作时序被编译器优化了;再者,在对Nor Flash进行操作时,bank0在MMU中的类型一定要设为NCNB,这样比较保险。

遇到最大的问题就是下面的了,一开始我用jtag 把程序下载到0×30000000的地方运行,对Nor Flash的烧写完全正常,但是当把程序下载到Nor Flash中启动运行后,再对Nor Flash的section 2进行烧写时,就出现了问题。所幸没多久我就意识到了问题,将程序放在Nor Flash中运行,同时有对同一片flash进行操作,那么操作时序势必会被CPU的指令读取时序所破坏,因此程序必须搬运到SDRAM中运行。

但是启动地址有必须是零地址,所以我采用了前文提到的scatter文件的方法,将非必要的代码全部搬到sdram中运行,scf文件格式就是前文中的那个。当然采用了__main的入口,调用了ADS的链接库,让它帮忙建立程序的运行环境。

至此,Nor Flash Bootloader可以顺畅无忧的实现其功能了。

Jacob 技术

NAND Flash Addressing Mode

2006年12月16日

NAND Flash的寻址方式和NAND Flash的memory组织方式紧密相关。NAND Flash的数据是以
bit的方式保存在memory cell,一般来说,一个cell中只能存储一个bit。这些cell以8个
或者16个为单位,连成bit line,形成所谓的byte(x8)/word(x16),这就是NAND Device的
位宽。
这些Line会再组成Page,通常是528Byte/page或者264Word/page。然后,每32个page形成
一个Block,Sizeof(block)=16kByte
Block是NAND Flash中最大的操作单元,擦除就是按照block为单位完成的,而
编程/读取是按照page为单位完成的。
所以,按照这样的组织方式可以形成所谓的三类地址:
-Block Address
-Page Address
-Column Address
首先,必须清楚一点,对于NAND Flash来讲,地址和命令只能在I/O[7:0]上传递,数据宽度
可以是8位或者16位,但是,对于x16的NAND Device,I/O[15:8]只用于传递数据。清楚了这
一点,我们就可以开始分析NAND Flash的寻址方式了。
->以528Byte/page 总容量512Mbit+512kbyte的NAND器件为例:
因为1 block=16kbyte,512Mbit=64Mbyte,Numberof(block)=1024
1block=32page, 1page=528byte=512byte(Main Area)+16byte(Spare Area)
用户数据保存在main area中。
512byte需要9bit来表示,对于528byte系列的NAND,这512byte被分成1st half和2nd half,
各自的访问由所谓的pointer operation命令来选择,也就是选择了bit8的高低。因此A8就是
halfpage pointer(这是我给出的一个名字),A[7:0]就是所谓的column address。
32个page需要5bit来表示,占用A[13:9],即该page在块内的相对地址
Block的地址是由A14以上的bit来表示,例如512Mb的NAND,共4096block,因此,需要12个bit
来表示,即A[25:14],如果是1Gbit的528byte/page的NAND Flash,则block address用
A[26:24]表示。而page address就是blcok address|page address in block
NAND Flash的地址表示为:
Block Address|Page Address in block|halfpage pointer|Column Address
地址传送顺序是Column Address,Page Address,Block Address。
由于地址只能在I/O[7:0]上传递,因此,必须采用移位的方式进行。
例如,对于512Mbit x8的NAND flash,地址范围是0~0×3FF_FFFF,只要是这个范围内的数值
表示的地址都是有效的。
以NAND_ADDR为例:
第1步是传递column address,就是NAND_ADDR[7:0],不需移位即可传递到I/O[7:0]上
而halfpage pointer即bit8是由操作指令决定的,即指令决定在哪个halfpage上进行读写,
而真正的bit8的值是don’t care的。
第2步就是将NAND_ADDR右移9位,将NAND_ADDR[16:9]传到I/O[7:0]上
第3步将NAND_ADDR[24:17]放到I/O上
第4步需要将NAND_ADDR[25]放到I/O上
因此,整个地址传递过程需要4步才能完成,即4-step addressing。
如果NAND Flash的容量是256Mbit以下,那么,block adress最高位只到bit24,因此寻址
只需要3步。
下面,就x16的NAND flash器件稍微进行一下说明。
由于一个page的main area的容量为256word,仍相当于512byte。但是,这个时候没有所谓
的1st halfpage和2nd halfpage之分了,所以,bit8就变得没有意义了,也就是这个时候
bit8完全不用管,地址传递仍然和x8器件相同。除了,这一点之外,x16的NAND使用方法和
x8的使用方法完全相同。

Jacob 技术

关于ARM处理器Remap的理解 Comprehension About ARM Remap

2006年12月16日

0.什么是Remap
我的理解是:在ROM从0×0用几句指令引导系统之后,把RAM映射到0×0就是Remap。
1.Remap的作用
当ARM处理器上电或者Reset之后,处理器从0×0取指。因此,必须保证系统上电时,0×0处有指令可以执行。所以,上电的时候,0×0地址处必定是ROM或者Flash(NOR)。
但是,为了加快启动的速度,也方便可以更改异常向量表,加快中断响应速度,往往把异常向量表映射到更快、更宽(32bit/16bit)的RAM中。但是异常向量表的开始地址是由ARM架构决定的,必须位于0×0处,因此,必须把RAM映射到0×0。
2.Remap的配置
Remap的实现和ARM处理器的实现相关。
1)如果处理器有专门的寄存器可以完成Remap。那么Remap是通过Remap寄存器的相应bit置1完成的。如Atmel AT91xx
2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank的起始地址,那么只要把RAM的起始地址编程为0×0,也可以完成remap。如samsung s3c4510
3)如果上面两种机制都没有,那么Remap就不要做了。因为处理器实现决定了SDRAM对应的bank地址是不能改变的。如Samsung S3c2410.
3.Remap配置前后要做的工作
Remap前后,不同之处就是RAM的位置变了。为了达到Remap的目的,就是加快启动的速度和异常处理速度,一定要初始化异常堆栈和建立异常向量表的。
4.如果象2410那样不能Remap的话怎么办?
2410不是不能Remap吗?为了加快启动速度,可以这样做
1)使用它的NAND boot模式。为什么NAND boot会比较快,那是因为2410里面有块小石头——“SteppingStone”,一块4KB SRAM,它是映射在0×0的。启动程序会自动被copy到这个石头里面。自然异常向量的入口放到这个地方,一样可以达到比NOR boot快的启动、异常响应速度。
2)如果你对NOR Boot情有独衷,那么你只好把你的异常向量的入口copy到SDRAM里面,实现所谓的High Vector

Jacob 技术