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

本来是没打算写这些的,但是后面逐渐发现点问题,所以决定再写一个下篇来补充说明一下。

图一

细心的网友会发现上篇末尾的打印是有点问题的,因为我的数据产生器产生的是1-200,1-200,1-200,1-200,1-200,1-200,共六组1200个8bit数据,全部写进Wrfifo,写进去之后发写请求,同时给Moni_Addr[23:0]为0,也就是0地址开始写。既然第一个数据是0102,为什么打印0000呢,这里只有一个可能,就是提前了一个节拍吧SDRAM_Read_Ack(isRead_Ack)拉高了,导致Rdfifo装进去0000。返回去查看SDRAM_Read_Ack的拉高轨迹,发现在周期的543-1055处于拉高状态。如下图起始:

图二

结束位置:

图三

这个现象很诡异,SDRAM_Read_Ack(isRead_Ack)的确是拉高了512个周期,但是呢,第一个数据对应的却是0000.我们得重新认识这个dcfifo,它很可能在里面预存了一个0000,所以我们0102就把它0000先排了出来。反过头来看Wrfifo的情况,下图:

图四

果然是在SDRAM_Write_Ack(isWrite_Ack)拉高的第一个周期对应的是0000!这dcfifo不够老实哈,有办法对付它这特性,我们拉高513个周期,提前拉高一个周期把0000拉出来,但是呢我们此时还没不发出_WRITE,这样就让SDRAM错过0000.

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 。Add isWrite_Ack

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

               //在i=1时添加一个时钟的isWrite_Ack为高。提前拉高是为了把0000先排出去

……………………………..

                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

               

串口打印修改后的数据:

图五

不过虽然写入的问题解决了,但是读的问题还在,有没有发现最后打印了两次1516,最后的一个数据应该是1718才对,因为1-200个8bit数循环,512个16bit的数打印,最后肯定是十进制的2324,即十六进制的1718。为什么1718没打印出来,却把1516打印了两遍?和下图所示一样:

图六

是不是1718没有传给SDRAM呢,看写SDRAM时的信号:

图七

最后一个数据1718已经给了SDRAM数据总线。那到底为什么没读出来呢?

走到这里的时候,我犯了个错误,怀疑逻辑了,怀疑是不是Wrfifo的最后一个1718是不是没有出来,需要再加一个数据把它挤出来?哈哈,你看,明明逻辑分析仪已经显示1718已经出来给了Wrfifo_To_SDRAM,我还怀疑它,那就试试吧,我又添加了一个时钟的SDRAM_Write_Ack(isWrite_Ack)高电平。修改如下:

………………….

                3: //这里C1改成到511,再添加一个时钟,用来”期望”把1718挤出来,虚幻~

                if(C1 == 511) 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

好了,来看打印结果:

图八

第一个数据竟让是191A!而且后面连接着0304,0102去哪了?呵呵,来看SignalTapII里面的信号吧:

图九

SignalTapII分析出了一切,我写的是第一个页0-511个地址,但是却给了513个数据,到一页的结尾时,我们没有准时发送突发中断命令,导致第513个数据从头开始写了,于是191A写进了0地址!

虽然不相信逻辑做的无用功测试,但是这个打印却能深刻说明,为什么需要中断突发命令来中断页突发操作!

好了,无用功让我们能体会到点东西,但是我们我们的问题还未解决。不过我已经有点眉目了,两次发出1516说明最后的一个读取没有更新。呵呵,来看看先前的读操作吧:

。。。

    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

。。。

上面的代码让SDRAM_Write_Ack(isWrite_Ack)拉高512周期,没错啊,但是为什么两次1516,天,我拉高isRead_Ack <= 1'b1;510次操作,511次没有操作它,所以呢,它保持了512个高电平。问题来了,那rData <= SDRAM_DATA;呢,是不是C1==511的时候,也会”保持”,当然会保持,不过是rData这个寄存器保持着上次的1516,而不是保持着又操作了一次rData <= SDRAM_DATA。这真是个很蛋疼的错误,可以说是一个低级的语法理解误区。说明一下:SDRAM_To_Rdfifo <= rData;所以呢因为第511次我们没有去操作rData <= SDRAM_DATA;所以导致rData保持了上一次的1516,而没有更新成1718。不信看看下图:

图十

果真是要非常小心的去理解表达技巧上效果啊!

好,让我们改过来吧:

。。。//红色就是添加的那句

    5: // Read Data

                if(C1 == 511) begin C1 <= 10'd0; rData <= SDRAM_DATA;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

。。。

编译烧写,看串口:

图十一

哈哈,perfect!经过此番细细挖掘一番,我们获得了两个进步:第一,认识到了页突发的读和写操作结束后的确是非常需要突发中断命令(BURST TERM)的,不然它真的会循环从头开始读或者写,所以我们要准时发送_ BURSTTERM拉住这头驴。第二,我真的觉得学一种语言要建立自己的一套表述风格,并且深刻理清它,不然它会把你的逻辑搞混乱。

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

我还有一些疑问:

1.上面的操作都是单次操作,那么连续操作会不会有什么问题呢?

2.我们已经知道页突发模式是不支持Auto-precharge,那么它要不要手动precharge?或者突发中断命令BURSTTERM已经包括了完成释放资源的操作?

第一个问题,其实就是回答连续操作和单次操作有什么不同。我是遇到了点问题,就是Auto-Refresh的时间控制,这个真的非常重要,在上一篇中,我们已经知道了在100M的情况下,781个CLK就必须刷新电容了。而我们一次性的读或写操作已经耗费了500多个CLK。这个概念就是我们惊醒一次读或者写就马上得进行Auto-Refresh操作,这个不是开完笑的,不信你试试,超时之后数据完全丢失!哈哈,我第一次试连续操作,七改八改的,没有满足这个时间就造成第二次读取完全错误的数据。有的网友开始问了,假设我530个CLK完成一个读或者写操作,那还剩下281个CLK岂不是干等,浪费了,哎呀,如果你一定要用这200多个时钟,那就弄个组合吧,burst1/2/4/8 + full-page burst,打个比方就是写burst8+页操作读,很多论文都这样写的哦,哈哈,鄙视太多的论文,根本说不清楚。好了,别担心,因为我们一次读或写操作之后就马上进行了Auto-Refresh,然后又可以进行下一次读或写操作了,这个效率算是很高的!

我的多次读写是这样测试的,先把Wrfifo和Rdfifo都扩大,至少扩大到可以装1024个16bit吧,这样就能先对0地址写512个数据,然后再给512这个虚拟地址写后面512个16bit数据。写完之后,就可以同理连续读两次了,照样,串口打印吧。

这第二个问题一直存在我心中,只不过在上一篇里面没做过测试,不敢下妄言。现在在连续读写完成的情况下,我做了个实验,先对0地址写一组数据,再重复对0地址,然后先去读512这个首地址的数据,再去读0地址的数据。

图十二

第一个黑框写第一组数据到0地址,第二个黑框写第二组数据到0地址,第三个黑框去读512地址的数据,第四黑框就是去读0开始地址的整页数据。如果第二个黑框操作失败,那么第四个黑框操作后,打印的会是01020304…..。看下面不是0102开始,而是1B1C,这已经是第二组数据了。

好了,看打印结果吧:

图十三

呵呵,证明了,连续突发的页操作是不需要precharge操作的,或者可以认为BURSTTERM终止命令已经释放了控制的资源,不会让下一次操作失效。

结束语:本来是没想写这么多的,累人,不过有些东西还是说透彻点好,说透彻了证明自己才懂了。这些图弄的粗糙,大家凑合着看吧

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