zoukankan      html  css  js  c++  java
  • 编写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终止命令已经释放了控制的资源,不会让下一次操作失效。

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

  • 相关阅读:
    JavaScript 中的undefined and null 学习
    html5 file upload and form data by ajax
    openresty + lua-resty-weedfs + weedfs + graphicsmagick动态生成缩略图(类似淘宝方案)
    ubuntu10.04 安装oracle server 版 笔记
    windows xp + mysql5.5 + phpmyadmin insert 中文繁體
    (原创)ubuntu 10.04+ruby1.9.2+rails3 安装记录
    ruby簡單的代碼行統計工具
    Ruby中如何复制对象 (deep clone)(转载)
    vi 常用命令使用說明
    一个小公司老板的日常管理日记,希望能让创业的朋友学到东西(转载)
  • 原文地址:https://www.cnblogs.com/lueguo/p/3434162.html
Copyright © 2011-2022 走看看