第5课,内存控制器

时间:2022-03-09 20:43:24

注:以下内容学习于韦东山老师arm裸机第一期视频教程

    一.内存接口介绍

        1.1 内存控制器的引入

            2440是一个SOC,其中外设分为多种类型

                1. 门电路类:GPIO

               2. 协议类接口: UART I2C SPI


            但是对于CPU来说只需要将值写给寄存器,CPU通过地址来区分不同的寄存器,因此就会引入另一个控制单元->内存控制器

            

        1.2 内存控制器的作用

            1.2.1 根据CPU发出的地址来选择不同的模块,把数据发送给模块,或者取出数据并且返回给CPU


        1.3 内存接口

            1.3.1 CPU发出的信号并不会直接输出到外部电路去,但是凡事都有例外,例如内存接口

            1.3.2 对于内存接口,CPU发出的地址可以直接传给内存类设备(NOR,网卡,内存)

            1.3.3 在内存芯片上有地址总线,数据总线,可以直接接收CPU发出的地址,数据

            1.3.4 对于内存类设备会共用地址总线,因此引入片选引脚cs

                  只有当某个片选引脚输出低电平是对应的设备才被选中,可以向CPU提供数据

                  

            1.3.5 谁来控制片选引脚?(CPU只发出地址信号)

                    内存控制器根据CPU发出的地址来决定发出不同的片选信号.

                  地址与片选信号关系如下图:

第5课,内存控制器

            

            1.3.6 如何确定数据传输方向

                    对于SDRAM、DM9000、NOR芯片上都有读写信号来表示数据传输方向

                    

            1.3.7 关于32位CPU的疑惑

                  其中每块地址范围为128M=2^27,因此至少需要A0,A1-A26,共27条地址线

                但是cpu是32位的,却只用了27条地址线?

                  

                  如下:

                    ldr r0, =addr
                    ldr r1, =val
                    str r1, [r0]           

                    会让CPU发出32位的addr给内存控制器,内存控制器再来发出片选信号和addr0-addr26的地址信号

                  CPU确实发出了32位的地址,但是内存控制器只用到了0-26

                        

            1.3.8 特殊的NAND

            对于GPIO、UART、I2C、SDRAM、DM9000、NOR都是CPU统一编制的,但是对于NAND原理图如下:

                   第5课,内存控制器

            并没有地址总线,不属于CPU的统一编址,

          但是Nand的数据信号仍然接到总线上,因此肯定会有片选信号,NAND的片选信号来自于NAND FLASH控制器发出

        
                        

    二.不同位宽设备的连接

        2.1 SDRAM,NOR,DM9000网卡地址总线的连接如下图

            SDRAM:

第5课,内存控制器

            A0接ADDR2

            A1接ADDR3...

            

            NOR:

第5课,内存控制器

            A0接ADDR1

            A1接ADDR2...

            

            DM9000:

第5课,内存控制器

            只接了ADDR2

            

            在2440芯片手册中提供了如下示例:

第5课,内存控制器

                

            当使用一个8位的芯片时,CPU的A0接芯片的A0

            当使用两个8位的芯片拼接(DATA0-7接一个芯片,DATA8-15接一个芯片,也就是组成了一个16位的芯片)的时候,CPU的A1接芯片的A0

            当使用四个8位的芯片拼接(也就是组成了一个32位的芯片)的时候,CPU的A2接芯片的A0

            

        2.2 不同位宽设备不同接法的详解

            对于8位的ROM,里面的数据以8位为最小数据保存

           对于16位的ROM,里面的数据以16位为最小数据保存,可以拆分为2个字节

           对于32位的ROM,里面的数据以32位为最小数据保存,可以拆分为4个字节

            

            2.2.1 假设CPU执行

                    mov R0, #3

                  ldrb R1, [R0] /* 读地址3的一个字节 */

                 CPU会将地址3发送出去,使得A0 = 1, A1 = 1, A2 = 0....

                    

                    对于8bit的ROM,由于A0-A0,A1-A1,因此收到了0x3地址,就会去第三个最小单元读取,成功的读到了第三个byte

                    

                    对于16位的ROM,由于A1-A0,A2-A1,因此收到了0x1地址,就会去第一个最小单元读取,读取到了16bit的数据,里面包含了我们想要的数据(芯片收到的地址 = CPU发出的地址 >> 1,即除以2, 16 / 2 = 8)

                    对于32位的ROM,由于A2-A0,A3-A1,因此收到了0x0地址,就回去第0个最小单元读取,读到了32bit的数据,里面包含了我们想要的数据(芯片收到的地址 = CPU发出的地址 >> 2,即除以4,32 / 4 = 8)

                    

                    然后将数据发送给内存控制器,由内存控制器帮助跳出来需要得到的数据(根据未接的数据线来挑选).

                                                对于16位,根据A0=1,从得到的数据中挑出第一个字节

                                                对于32位,根据A0=1,A1=1,从得到的数据中挑出第三个字节

第5课,内存控制器

                                                

            2.2.2 假设CPU执行

                   mov R0, #4

                 ldr R1, [R0]    /* 读地址4的4个字节 */

                CPU会将地址4发送出去,使得A0 = 0,A1 = 0,A2 = 1,A3 = 0....

                

                对于8bit的ROM,发现需要读取4个字节,因此会发送4个地址给ROM,收到了0x4,0x5,0x6,0x7地址,将第4,5,6,7单元的数据发送给内存控制器,内存控制器进行组装发送给CPU

                

                对于16bit的ROM,发现需要读取4个字节,因此会发送2个地址给ROM,收到了0x2,0x3地址,将第2,3单元的数据(也对应第4,5,6,7字节)发送给内存控制器,内存控制器进行组装发送给CPU

                

                对于32bit的ROM,发现需要读取4个字节,因此会发送1个地址给ROM,收到了0x1地址,将第1单元的数据(也对应第4,5,6,7字节)发送给内存控制器,内存控制器直接发送给CPU

                                                                                        

                注意:在发送多个地址的时候是内存控制器来发送多个地址,CPU只会发出一个地址0001

                第5课,内存控制器

        2.3 怎么确定芯片的访问地址?    

            

            2.3.1 根据片选信号来确定基地址

            2.3.2 根据连接的地址线来确定范围

            
            

            NOR使用片选0,因此基地址等于0,会使用A0-A20,因此范围为 0-2^21 = 2M

            网卡使用片选4,因此基地址等于0x20000000,只接了ADDR2,会用A0来分辨数据是低8位还是高8位(16位芯片),使用了A0 A2,因此范围 0x20000000 - 0x20000005    

            SDRAM使用片选6,因此基地址等于0x400000001,接了A2-A14,A24,A25,A0,A1只有17跟,大小为128K

            但是SDRAM比较特殊,在访问的时候发出的地址信号分为列地址和行地址,也就是在访问的时候先发出行地址/列地址,在发出列地址/行地址(具体以后再讲)

                
                    

    三.时序图分析示例

        3.1 以NOR为例,分析各种信号时如何工作的,NOR的原理图如下:

         第5课,内存控制器

            有地址信号,数据信号,片选信号,读信号,写信号,这些信号如何协同工作?

            

        3.2 2440读时序图如下:

            第5课,内存控制器
            

            3.2.1 首先发出地址信号,经过Tacs时间发出片选信号,再经过Tcos时间发出读信号,过一会数据有效,然后释放掉读信号,片选信号

            

            3.2.2 Tacs,Tcos,Tacc,Tacp等等的时间需要根据不同的芯片来进行不同的设置

            

            

        3.3 NOR读操作时序图如下

         第5课,内存控制器
        

        3.4 NOR时序要求图

             第5课,内存控制器

            3.4.1 Taa->发出地址信号后经过多长时间能够访问数据,最大设置为70ns

                        (不同型号可能经过不同的时间才能够访问数据,但是经过70ns之后都可以访问,这就是"最大"的含义)

            3.4.2 Tce->片选信号后经过多长时间数据有效,最大设置为70ns

            3.4.3 Toe->数据在读信号之后多长时间数据有效,最大设置为30ns

            3.4.4 Toh->数据保持时间,最小设置为0

            3.4.5 Tdf->在这段时间,不能访问其他芯片,如果访问NOR会影响你的数据,最大设置为30ns

            3.4.6 Trc->读周期时间(NOR每读一次消耗的时间),最小设置为70ns,对应2440手册上的Tacc

                

        3.5 结合2440的读时序图与NOR读操作时序图对参数进行设置        


            为简单,设置地址信号,片选信号,读信号同时发出,设置Tacc >= 70ns

            

        3.6 NOR使用片选0,因此我们要设置BANKCON0寄存器

            

            3.6.1 设置BANKCON0

第5课,内存控制器

            注意上电时Tacc的默认值是111,代表14个clocks,刚上电使用12M晶振,因此Tacc = 14 * 12000000 = 116ns,可以兼容各种类型的NOR

            在设置完时钟之后,HCLK=100MHZ = 10ns,Tacc >= 70ns = 7个clock,因此可以设置为8个clock即101

            

            3.6.2 设置BWSCON,如下

             第5课,内存控制器

            NOR接在BANK0上面,只需要设置bit[2:1],bit[2:1]是只读的表示外接的NOR是多少位的,因此不需要设置.

            

    四.设置NOR的Tacc来提高性能

        /* main.c */
        
        #include "my_printf.h"
        #include "uart.h"
        #include "SetTacc.h"

        int main()
        {
            char c;
            
            Uart0Init();
            puts("Enter the value of tacc:");

            while (1)
            {
                c = getchar();    
                if(c == '\n')
                    putchar('\r');
                if(c == '\r')
                    putchar('\n');
                putchar(c);  /* 回显输入语句 */

                if (c >= '0' && c <= '7')
                    SetTacc(c - '0');    /* 根据输入设置Tacc的值,当输入0-4时Tacc<8clock,程序无法工作 */
                else
                    printf("Error val,tacc should betwen 0-7");
            }
            
            return 0;
        }
        
        /* SetTacc.c */
        
        #include "s3c2440_soc.h"

        void SetTacc(int val)
        {
            BANKCON0 = val << 8;    
        }