博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
编写SDR SDRAM页突发模式控制器的注意点-下篇
阅读量:5273 次
发布时间:2019-06-14

本文共 4580 字,大约阅读时间需要 15 分钟。

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

 

 

图一

细心的网友会发现上篇末尾的打印是有点问题的,因为我的数据产生器产生的是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

你可能感兴趣的文章
自动化运维工具之Ansible
查看>>
Python初识爬虫
查看>>
MySQL线程独享内存参数
查看>>
指甲有竖纹的成因及治疗方法
查看>>
VC控件MSComm编写串口通信程序(转)
查看>>
在Linux下测试SD卡的读写速度
查看>>
闭合电路欧姆定律 证明题1
查看>>
第三次作业-效能分析
查看>>
关于【最长递增子序列(LIS)】
查看>>
HDU 5768:Lucky7(中国剩余定理 + 容斥原理)
查看>>
训练总结
查看>>
HDU 3949:XOR(高斯消元+线性基)
查看>>
next_permutation()—遍历全排列
查看>>
JNA的使用
查看>>
Web设计的十大错误
查看>>
React总结
查看>>
BSP:bootstrap导航栏:navbar,navbar-default,collapse
查看>>
【洛谷2304_LOJ2134】[NOI2015]小园丁与老司机(动态规划_网络流)
查看>>
linux下清除tomcat缓存
查看>>
WPF自定义仪表盘控件
查看>>