编写SDR SDRAM页突发模式控制器的注意点

网上有很多的SDR SDRAM控制器的代码,但都是基于burst1/2/4/8模式下的,这种模式下传输高速的相机数据还是有点拮据的,所以花了几天把这些模式改造成了页突发模式。我的这个控制器模型是这样的:

                                                    

                                                                                                            图一

这里的有两个缓冲Wrfifo和Rdfifo,它们都是dcfifo(混合宽度异步时钟FIFO)。上面的图我画的很丑,但是有些细节要注意,Wrfifo进来是8bit出去是16bit。不管是手机那种摄像头还是工业相机camera_clk都不会很高,在30hz的640x480输出情况下,8进16出,还是比SDRAM要慢,不会把Wrfifo塞满。工业应用上,往往很强调实时性,所以相机的帧率会达到上百帧,这样保证处理的数据尽量是最新的一帧。

好了,进入正题了,我们在已有的SDRAM burst1/2/4/8的控制器代码下怎么修改成full-page burst 模式,需要注意点什么细节。

先来了解一下器件,我用的是美光的一款SDRAM,MT48LC64M4A2,资源如下图椭圆框标示:

                                                               

                                                                                                                图二

4Meg x 16 x 4banks ,行地址[12:0] ,列地址[8:0]。

模式寄存器配置图:

                                             

图三

从上面得到一个信息:full-page模式下必须把register[2:0]配置成111。

突发定义表格:

                                                 

图四

从上图,我们可以得到两个信息:

  1. full page burst不支持Interleaved,只支持顺序sequential模式,这可以在Mode register设置。
  2. 从注释的第一条和第五条可知道,在16位数据线模式下,页模式可访问的长度是512个16bit数据,也就是一行的长度column[8:0]。

要把burst1/2/4/8改成full page,我们知道最大的不同就是要改读写时序,先来看一下页突发模式的写时序图:

图五

这张图说起来啰嗦,我用verilog表述一下:

     reg[23:0] Moni_Addr;//[23:22] bank;[21:9] row;[8:0] clmn

     reg [14:0]Address;//地址线

     reg[15:0] rData;//寄存器获取SDRAM数据总线数据

always@(posedge CLK_100Mor negedge RSTn)

    ……//忽略

       else if( SDRAM_READ )

            case( i )

                0: // Send Active command with Bank and Row address

                begin Command <= _ACT; Address <= Moni_Addr[23:9]; DQM <= 2'b11; i <= i + 1'b1; end

               

                1: // Send 1 nop Clk for tRCD-20ns

                begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1; end

           

                /***************************************/

               

                2: // Send Read command and column address, bank address ,pull down DQM clock               

       begin Command <= _RD; Address <= { Moni_Addr[23:22], 4'b0000, Moni_Addr[8:0]};

        DQM <= 2'b00; i <= i + 1'b1; end

               

                3:

                begin Command <= _NOP; DQM <= 2'b00; i <= i + 1'b1; end

               

                4: // Send 2 nop Clk for CAS Latency

                begin Command <= _NOP; DQM <= 2'b00; i <= i + 1'b1; end

                                  

                /******************************************/

                

                5: // Read Data

                if(C1 == 511) begin C1 <= 10'd0; i <= i + 1'b1; end

                else begin C1 <= C1 + 1'b1; rData <= SDRAM_DATA; isRead_Ack <= 1'b1;

      DQM <= 2'b00;  Command <= _NOP;

                end

               

                /******************************************/

                6:// Send BurstTerm

                begin  Command <= _BURSTTERM; DQM <= 2'b00; isRead_Ack <= 1'b0; i <= i + 1'b1; end

                7:

                begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1; end

               

                8: // return i to 0

                begin i <= 4'd0; end

               

                /*******************************************/

上面的表述,有几点下面说明一下:

1.full-page模式不支持Auto-precharge.这点手册里面原文表述如下:A precharge of the bank/row that is addressed with the READ or WRITE command is automatically performed upon completion of the READ or WRITE burst, except

in the continuous page burst mode where auto precharge does not apply.

所以在i=2时刻,发送的地址里对A10置1是无效的。

2.full-page模式需要突发中断命令来终止它,为什么?我们来看手册原文表述:A continuous page burst continues until terminated;at the end of the page, it wraps to column 0 and continues.

它的意思是说,一个page burst操作必须在 burstterm下才停止,不然它会重新在地址(column 0)开始获取数据。通俗点说它就会循环获取数据!

3.在i=5的操作很有看点,也是重点:if(C1 == 511) begin C1 <= 10'd0; i <= i + 1'b1; end else begin C1 <= C1 + 1'b1; rData <= SDRAM_DATA; isRead_Ack <= 1'b1 ; DQM <= 2'b00; Command <= _NOP; end

  我循环操作511次去获取数据,即rData <= SDRAM_DATA。循环操作511次去拉高isRead_Ack,即isRead_Ack <= 1'b1;。这个isRead_Ack就是给Rdfifo的rdreq信号,就是为了拉高写进阀门511次,把512个数据都写进Rdfifo去。为什么拉高511次操作写进了512个数据??呵呵,因为verilog语言是有后续效果的。Fifo的特点我这里也不多说了,用用就知道了。

再来看看写时序图吧:

图六

这张写图感觉和读时序图差不多啊,呵呵,也用verilog表述一下:

always@(posedge CLK_100Mor negedge RSTn)

    ……//忽略

        else if( SDRAM_WRITE )

            case( i )

               

                  

                0: // Send Active Command with Bank and Row address

                begin Command <= _ACT; Address <= Moni_Addr[23:9]; DQM <= 2'b11; i <= i + 1'b1; end

               

                1: // Send 1 nop Clk for tRCD-20ns

                begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1;end               

               

                /*********************************************/

               

                2: // Send Write command with row address, bank addr,pull down DQM 1 clk

                begin Command <= _WR; Address <= { Moni_Addr[23:22], 4'b0000, Moni_Addr[8:0] };

       DQM <= 2'b00; isWrite_Ack <= 1'b1; i <= i + 1'b1;end

               

                3:

                if(C1 == 510) begin C1 <= 10'd0; i <= i + 1'b1;j <= j + 1'b1;end

                else begin Command <= _NOP; DQM <= 2'b00; isWrite_Ack <= 1'b1; C1 <= C1 + 1'b1; end

               

                4: // Send BurstTerm

                begin  Command <= _BURSTTERM; DQM <= 2'b00; isWrite_Ack <= 1'b0; i <= i + 1'b1; end           

               

                /**********************************************/

                5:

                begin Command <= _NOP; DQM <= 2'b11; i <= i + 1'b1; end

           

                6: // return i to 0

                begin i <= 4'd0; end

               

                /*********************************************/               

这里也说明一下:

1.和发送SDRAM_READ命令一样,发送SDRAM_WRITE命令时和之后的空命令NOP,DQM都是需要拉低处理的,而在之前的发送ACT命令是DON’T CARE(无所谓)。

2.isWrite_Ack是给Wrififo的wrreq信号,就是控制阀门打开,要把数据放出去,和读的isRead_Ack性质一样。

3.在i=2的时候,发送_WR的时候也拉高了一个时钟的isWrite_Ack,再加上i=3时产生效果的511个高效果isWrite_Ack,这样就一共拉高512个时钟节拍,就是把512个16bit的数据给放出去给SDRAM。

4.页突发写结束时,照样也要发送_BURSTTERM来终止写操作!

好了,总结一下,把burst1/2/4/8修改成full-page burst,我们需要做的工作是:

  1. 修改Mode register的burst length和保证BT为sequential。
  2. 修改控制器里面的读和写时序,很重要这点,而且老实说要很细心,因为有些代码的计时控制,比如我们需要满足tRCD,CAS latency等等这些时间,一般是用计时器来控制的,这些计时器在状态机里面循环,希望不要绕晕你。我上面对读和写只是做了一个顺序操作的流程,让你知道一个读写操作下来需要的小操作,知道这些之后就好添加修改了。
  3. 我这里是有Wrfifo和Rdfifo两个dcfifo,数据在刚开始是放在fifo里面的,流程是先网Wrfifo里面写进512个以上的16bit数据,因为是8进16出,所以其实我们要网Wrfifo的写端至少写1024个8bit的数据!然后对控制器发送写请求,写完之后,再发出读请求,然后512个数据哗啦啦的被装进了Rdfifo。

说了上面三点还有最需要注意的一点,定时刷新,很重要!手册里面原文:

Regardless of device width,the 256Mb SDRAM requires 8192 AUTO REFRESH cycles every 64ms (commercial and

industrial) or 16ms (automotive). Providing a distributed AUTO REFRESH command every 7.813μs (commercial and industrial) or 1.953μs (automotive) will meet the refresh requirement and ensure that each row is refreshed.

这款芯片,必须在64ms内完成8192次刷新。也就是说每隔7.813us就必须发一个Auto-Refresh命令。在100M的时钟下7.813us就是781.3个CLK,my god!?这么迫切,这是什么概念,从上面的读写时序表述中知道:我们仅仅数据耗费了512个CLK!!!准确来说,上面读操作耗费520个时钟,写操作耗费516个时钟,还不算上真正操作时传递信号的消费!这说明了什么,这说明我们在一个读或写操作之后必须进行刷新操作,不然时间来不及,等不了两个读或写操作。

所以第四点,我们必须懂得控制好自动刷新电容的操作!

好了,修改了这么多,但是修改之后怎么测试呢,外围的我们可以加个串口模块来打印SDRAM读取的数据,内部信号的测试用SignalTapII,Altera自带的逻辑分析仪,这个工具很好用。来一张测试的截图:

图七

这张图CMD命令,16是_BUSTTERM,17是_NOP,11是_AUTOREFLESH.这里有个很明显的错误,黑线框画出来了,11,17,16,17,说明自动刷新电容命令之后是突发终止命令,这是很奇怪的,违背了我们的本意,刷新电容之后必然是等待读写命令,不可能出现终止命令。这只是个例子,来说明怎么观察信号来修改代码。

下面看一下,正确的信号观察:

椭圆区1,是往Wrfifo写600个16bit数据,Wrfifo_Req是写请求信号,Wrfifo_Data_In是数据

椭圆区2,isWrite拉高向控制器发出写SDRAM请求,SDRAM_Write_Ack是控制器回馈的应答信号,对应给Wrfifo的rdreq,拉高了512个周期,打开闸门放512个16bit数据。

椭圆区3,向控制器发出读请求,isRead拉高,控制器SDRAM_Read_Ack反馈应答给Rdfifo的wrreq,拉高512个周期,放入512个数据给Rdfifo。

图八

这个SingnalTapII文件最好采样深度设2K,把全部信号都能观察到。外部直观点的,最好在Rdfifo的读出端连个串口模块,直接观察打印…

图九

结束语:在刚开始做视觉项目的时候就想弄个页突发模式的控制器,虽然Altera的Sdram_Control_4Port还能凑合着用,但是我表示很难适应它这代码的风格,很难改。后面在网上得了个burst8模式的控制器,这几天试着改成了页模式,把这些细节记录下来。

(欢迎转载,请注明出处 ---愤怒de狂奔)

原文地址:https://www.cnblogs.com/lueguo/p/3434158.html