S3C2440 开发板实战(2):start.S初认识 + SDRAM配置 + 重定位

2440执行程序的第一件事请就是执行start.S文件,所以这里来学习下最基本的启动文件。

顺便说下,在U-boot里有专门对应各种芯片的start.S文件,在开发手册中的ARM章节可以看到S3X2440的ARM型号为ARM920,所以本文章讲解部分比较重要的启动程序(能够保证能够正常启动),在以后的博客中我会详细对他进行分析。

一个最简单的start.S文件因该包括看门狗部分、时钟初始化部分以及程序代码定位部分

目录

1、看门狗部分

2、时钟部分

3、代码重定位

3.1. SDRAM

3.2. 代码重定位


1、看门狗部分

废话不多说,直接开始配置,首先就是要关闭暂时不使用的看门狗,找到看门狗的寄存器:WTCON,将其第0位置0,即禁用看门狗,即:

# define pWTCON    0x53000000    //WTCON地址

ldr    r0,    =pWTCON
mov    r1,    #0x0
str    r1,    [r0]    //关闭看门狗

2、时钟部分

在S3C2440中有三种时钟频率,分别为FCLK, HCLK, PCLK,分别控制不同种类的外设,在时钟树中可以进行查找,这里不做过多赘述,在以后的博客中有应用。

从芯片手册中查找FCLK, HCLK, PCLK 的最高频率,在范围内我们选择设置FCLK = 400MHZ, HCLK = 100MHZ, PCLK = 50MHZ (TF : TH : TP = 1 : 4 : 8)为例进行设置初始化时钟,看芯片手册的时钟树如下图所示:

OSC可以理解为我们的晶振频率,从开发板的原理图上可以看出为12MHZ。

时钟信号通过OM[3 : 2]( 外接引脚 GND )进行时钟源选择,即设置OM[3, 2] = [0, 0],此时刚上电时FCLK频率等于晶振频率,然后进入lock time,此时,CPU暂停工作,在lock time (该段时间是为稳定输出新的FCLK)过后CPU继续工作,CPU时钟的主频率为设置的FCLK。

为保证CPU能够在启动后一定能发送设置的FCLK信号,所以将FCLK设置为最大值:

# define LOCKTIME    0x4C000000 

ldr    r0,    =LOCKTIME
ldr    r1,    =0xffffffff
str    r1,    [r0]

先不看控制USB部分(UPLL)直接看MPLL部分。时钟树中时间信号通过锁相环(PLL)生成400MHZ的FCLK。其中关于MPLL寄存器设置方法如下:

然后通过查找数值表(也可以手算,在手册中有公式),由Input Frequency = 12MHZ,Output Frequency  = 400MHZ得到MDIV, PDIV, SDIV的参数值,并写入寄存器中:

# define MPLLCON    0x4C000004    //MPLLCON地址

ldr    r0,    =MPLLCON
ldr    r1,    = ((92 << 12) | (1 << 4) | (1 << 0))    // MDIV = 92  PDIV =1  SDIV = 1
str    r1,    [r0]                //写入寄存器

时钟树通过参数HDIVN, PDIVN对FCLK进行分频操作得到可操作进行外设操作的时钟HCLK, PCLK,设置分频系数的寄存器为CLKDIVN: 

# define CLKDIVN    0x4C000014 
ldr    r0,    =CLKDIVN    
ldr    r1,    =((10 << 1) | (1 << 0))    // HCLK = FCLK / 4
                                         // PCLK = HCLK / 2

最后一步由于设置的HDIVN寄存器不等于0,所以需要设置CPU处于异步状态,这里涉及到协处理器的命令(我也不懂哈哈哈),就按照手册中的来编(chao)写就行了。

3、代码重定位

这里先要讲一讲,由于nor flash在运行程序时,是可读不可写的,所以如果程序中有变量储存在 .bin文件中时,该变量不可被改变,即使在程序中对其进行修改。所以我们需要对代码进行重定位到SDRAM中,然后才能对其全局变量进行修改。所以首先应该讲下关于SDRAM的初始化配置:

3.1. SDRAM

由于在start,S程序中只要对SDRAM进行初始化配置,这样就能够对代码进行重定位,所以本章节博客只对SDRANM的初始化程序进行编写以及一些基本操作。

S3C2440中可以支持很多的存储单元,是通过地址对其进行区分的:

内存控制器通过识别不同的地址发出不同的片选信号(0信号低电平有效)。现针对SDRAM分析,2440外设置的是64M的SDRAM,需要对其进行设置寄存器,其SDRAM可以理解为3维的存储空间,bank * row * col,所以大体过程是由CUP发出地址,经过内存控制器转换成row, col, bank信号(为减少pin)

------------------------------------------------------------------------------------------------------------------------------------------------------

既然使用SDRAM就要和CPU说清楚这是个啥类型SDRAM,毕竟人家也是按需办事,所以配置寄存器BWSCON!

ST6:首先来看看芯片手册这么一句话:“nBE[3:0] is the 'AND' signal nWBE[3:0] and nOE”,即nBE是nWBE和nOE的“与”信号,所以nBE是字节选通信号(读+写),nWBE是读字节选通信号。

由于在进行SDRAM操作的时候只需要在写入的时候需要选择比如说32位中的前4位,输出时候把全部的输出(这是韦老师讲的,这块我也不了解,以后填坑把),所以置0.

WS6:wait的使用时因为有些质量比较差的储存芯片的反映速度没那么快,你叫他半天不理你和痴呆似的,需要等待他就秒,就是这个道理。开发板上的SDRAM比较好所以不等待。置0

SW6:由JZ2440是由两片SDRAM组成起来的32位SDRAM,所以应该选择10

一般会把CS7设置成CS6(也是SDRAM)但是没有使用它。

所以设置BWSCON = 0x22000000;

------------------------------------------------------------------------------------------------------------------------------------------------------

接着设置寄存器BANKCONn,和BWSCON一样,同时设置CS6和CS7。

可以看出[4 : 14]位都不需要我们设置!而且MT很清楚就知道应该置“11”,接着就是[0 : 3]位的配置。

Trcd的值是芯片的参数,根据HY57V561620F(L)T(P) 芯片手册:

为了和时钟频率对应,取20ns。即2 CLOCKs,将[3 : 2]置"00"

继续查看芯片手册:

明显Column address number  = 9,所以设置[1 : 0]为“01”。

所以设置寄存器

BANKCON6 = 0X18001;
BANKCON7 = 0X18001;

------------------------------------------------------------------------------------------------------------------------------------------------------

在2440芯片手册往下拉继续配置寄存器(我也觉得麻烦,没办法继续走)。

接着就是REFRESH寄存器,这个寄存器作用就是使SDRAM自动刷新,这是一个内部的自动操作。由于SDRAM要不断进行刷新(Refresh)才能保留住数据(储存体中电容的有效保存期有限),因此它是SDRAM最重要的操作。这么重要的参数,咋们肯定去SDRAM芯片手册中进行查找啦(保有),首先看看寄存器是怎么配置的:

REFEN:使能没得跑置“1”。

TREFMD:选择自刷新(SR)和自动刷新(AR)。对于AR,SDRAM内部有一个行地址生成器(也称刷新计数器)用来自动的依次生成行地址。由于刷新是针对一行中的所有存储体进行,所以无需列寻址。对于SR,在发出AR命令时,将CKE置于无效状态,就进入了SR模式。此时不再依靠系统时钟工作,而是根据内部的时钟进行刷新操作。在SR期间除了CKE之外的所有外部信号都是无效的(无需外部提供刷新指令),只有重新使CKE有效才能退出自刷新模式并进入正常操作状态。

所以我们选择AR模式对其进行数据的刷新。置“0”默认值。

Trp: 废话不多说直接查芯片手册

老道理,取整 2倍CLOCK。[21 : 20]为“00”

Tsrc:这个就比较特殊了。芯片手册没有嘿嘿。再看看描述中的式子。Trc = Tsrc + Trp.那这么说找到Trc就是我们的目标!那又回到最初的起点,开查芯片手册!就在Trp的上边。取整取70ns,大一点没坏处!Tsrc = 70 - 20 = 5*CLOCK ---> [19 : 18] 置“01”。

Refresh Counter :这个就是最重要的刷新频率了。这参数就放在SDRAM芯片手册的前面几面。

果然找到Refresh period = 64 / 8192 ms,带入公式中得到

                                                       \frac{64}{8192} * 10^{-3}=\frac{\left ( 2^{11}-RC+1 \right )}{10^{8}}

由于时钟周期为7.8us,且时钟周期为HCLK = 1000MHZ,Refresh = 1269;

综上所述,REFRESH = 0x8404f5。

------------------------------------------------------------------------------------------------------------------------------------------------------

第四个寄存器BANKSIZE,上图!(半夜两点的我已经不知疲倦)

BK76MAP:这个简单,开发板配套的SDRAM容量为256Mb,注意这里是小b,所以容量大小为256/8 = 32MB,由于外设搭载两片SDRAM,所以容量为64MB。置[2 : 0] ="001"

SCKE_EN:断电模式启用 (以后填坑,先开先)

SCLK_EN:推荐值(以后填坑,先开先)

BURST_EN :  启用突发操作。(以后填坑,先开先)

所以BANKSIZE = 0x000000b1

-----------------------------------------------------------------------------------------------------------------------------------------------------------

最后一个寄存器,MRSRBn 

这个寄存器特点:简单!有fixed值就选fixed值!

唯一需要选择的是CL值,一看诶。芯片手册上边查就行了,这个值的意思是由于在读SDRAM时需要发出:bank row col地址,所以需要等一会儿才有数据发回来。所以在芯片手册中可以查到CL=2或者3,这个值设置后回发送至SDRAM中的MR寄存器,以后在2或者3clock时刻返回数据。所以我们设置为2clock 即  =》“010”

所以MRSRB6 = 0x20; MRSRB7 = 0x20; 

-----------------------------------------------------------------------------------------------------------------------------------------------------------

综上所述,对于SDRAM的初始化函数程序如下所示

#include "s3c2440_soc.h"

void sdram_init(void)
{   
   BWSCON = 0x22000000;
   BANKCON6 = 0x18001;
   BANKCON7 = 0x18001;
   REFRESH  = 0x8404f5;
   BANKSIZE = 0xb1;
   MRSRB6   = 0x20;
   MRSRB7   = 0x20;
}

3.2. 判断设置是nor flash启动还是nand flash启动

这一段代码主要是基于NOR flash 可读不可写,然而NAND flash是可读可写的特性,所以可以通过对某一地址进行写数据对其进行判断,但如果是NAND falsh启动的话则会破坏内存,所以还需要保护数据

mov    r1,    #0   
ldr    r0,    [r1]    // 保存数据
str    r1,    [r1]    // 写入数据
ldr    r2,    [r1]    // 读出数据
cmp    r1,    r2      // 如果相等 Z = 1
ldr    sp,    =0x40000000 + 4096   //NOR启动
moceq  sp,    #4096   // NAND启动
streq    r0,    [r1]    //恢复数据

但是对于NOR flash启动的u-boot,他就没有管那么多了。反正他要使用NOR启动,在Start.S文件中使用以下代码,区别就是破坏了NAND flash部分代码

ldr sp, =4092
ldr r0, =0x12345678
str r0, [sp]
ldr r1, [sp]
cmp r0, r1
ldrne sp, =0x40000000+4096
bl clock_init

3.3. 代码重定位

首先用XXH查看dis文件(返回编码文件)。(该文件的程序里面包括了初始化的全局变量和未初始化的全局变量。

用VIM打开dis文件,/搜索关键词:Disassembly of section 查找到程序数据类型包括:

                                                       .text、.rodata、.data、,bss、.comment等等

其中看源文件的文件大小可以知道 .bss段是不在bin文件的内容中的这个段包括了未初始化的全局变量,.data段是在bin文件范围中包括了已经初始化的全局变量。这里有两种转移代码的方式:①把需要改变的部分移至SDRAM,②把全部程序移至SDRAM。我们选择第二种进行移动(链接脚本比较简洁)。所以应该把.data的部分进行拷贝。

所以这个时候就要使用链接脚本 *.lds文件,通过查阅Using ld The GNU linker有一下源代码格式:

SECTIONS {
...
secname start BLOCK(align)(NOLOAD) : AT (ldadr)
{ contents } >region:phdr =fill
...
}

start: 起始地址

runtime addr:运行时的地址

relocate addr:重定位的地址

AT(ldadr) Load Addr:加载地址  可以省略不写

LoadAddr = runtime addr 如果没有加AT,它的的加载地址就等于链接时的起始地址

以实例来进行说明:

sdram.lds

SECTIONS
{
    . = 0x30000000;              //设置当前地址为SDRAM首地址

    . = ALIGN(4);                //向四取整
    .text      :
    {
      *(.text)                   //Load Addr = Routime Addr
    }
    . = ALIGN(4);                // 紧接着.text文件排放              
    .rodata : { *(.rodata) }
    . = ALIGN(4);
    .data : { *(.data) }
    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) *(.COMMON) }
    _end = .;
}
/* 重定位text, rodata, data段整个程序 */
    mov r1, #0
    ldr r2, =_start         /* 第1条指令运行时的地址 */
    ldr r3, =__bss_start    /* bss段的起始地址 */

cpy:
    ldr r4, [r1]             //r1 --> r2
    str r4, [r2]
    add r1, r1, #4           //循环4次 (16byte/4byte)
    add r2, r2, #4
    cmp r2, r3
    ble cpy


    /* 清除BSS段 */
    ldr r1, =__bss_start
    ldr r2, =_end
    mov r3, #0
clean:
    str r3, [r1]
    add r1, r1, #4
    cmp r1, r2
    ble clean

注:

* ldr: 这里我们可以进行ldrb(1byte)或者使用ldr (4byte),由于sdram是16byte的,所以使用ldr函数能够极大的减少访问SDRA的次数:、

①原来:读ldrb执行16次指令,并且访问16次SDRAM;写strb执行16次,并访问16次SDRAM

②现在:读ldr执行4次指令,并且访问8次(我也很迷惑),写str执行4次,并访问4次SDRAM(每次读出四字节)

*写程序:在写程序中会发出地址加上DPM,把感兴趣的对应写入,对应的是SDRAM配置中的写使能

*.bss段:由于bss段数据都是未初始化,都是0,但是转移后的位置可能存的不是0数据,所以应该对其进行初始化置“0”

* 向四取整:由于ldr的操作是四个字节的赋值,但是我们的地址并不是四字节对齐,所以会进行向四取整

                                      实例:

                                                    命令存放地址     3 0 0 0 0 0 0 2 ( 2 < 4)

                                                    真实存放地址     3 0 0 0 0 0 0 0 

所以应对方法也是对当前地址进行向四取整即:

. = ALIGN(4);

这样命令存放的地址就变为:

                                           实例:

                                                    命令存放地址     3 0 0 0 0 0 0 4 ( 4 >= 4)

                                                    真实存放地址     3 0 0 0 0 0 0 4

这就是start文件初认识了!

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值