编写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终止命令已经释放了控制的资源,不会让下一次操作失效。
结束语:本来是没想写这么多的,累人,不过有些东西还是说透彻点好,说透彻了证明自己才懂了。这些图弄的粗糙,大家凑合着看吧
编写SDR SDRAM页突发模式控制器的注意点-下篇的更多相关文章
- 编写SDR SDRAM页突发模式控制器的注意点
网上有很多的SDR SDRAM控制器的代码,但都是基于burst1/2/4/8模式下的,这种模式下传输高速的相机数据还是有点拮据的,所以花了几天把这些模式改造成了页突发模式.我的这个控制器模型是这样的 ...
- Linux 桌面玩家指南:08. 使用 GCC 和 GNU Binutils 编写能在 x86 实模式运行的 16 位代码
特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...
- centos LNMP第一部分环境搭建 LAMP LNMP安装先后顺序 php安装 安装nginx 编写nginx启动脚本 懒汉模式 mv /usr/php/{p.conf.default,p.conf} php运行方式SAPI介绍 第二十三节课
centos LNMP第一部分环境搭建 LAMP安装先后顺序 LNMP安装先后顺序 php安装 安装nginx 编写nginx启动脚本 懒汉模式 mv /usr/local/php/{ ...
- 痞子衡嵌入式:串行NOR Flash的页编程模式对于量产时间的影响
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是串行NOR Flash的页编程模式对于量产时间的影响. 任何嵌入式产品最终都绕不开量产效率话题,尤其是对于主控是非内置 Flash 型 ...
- 使用 GCC 和 GNU Binutils 编写能在 x86 实模式运行的 16 位代码
不可否认,这次的标题有点长.之所以把标题写得这么详细,主要是为了搜索引擎能够准确地把确实需要了解 GCC 生成 16 位实模式代码方法的朋友带到我的博客.先说一下背景,编写能在 x86 实模式下运行的 ...
- CoffeeScript编写简单新闻页(仅UI)
CoffeeScript编写简单新闻页(仅UI) 1. 配置(在公司搭建好的环境下配置) omnisocials-backend/src/backend/modules/member/config/m ...
- 2016/5/6 thinkphp ①框架 ② 框架项目部署 ③MVC模式 ④控制器访问及路由解析 ⑤开发和生产模式 ⑥控制器和对应方法创建 ⑦视图模板文件创建 ⑧url地址大小写设置 ⑨空操作空控制器 ⑩项目分组
真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格不一样) 项目稳 ...
- loadrunner之WebServices协议脚本编写(三种请求模式)
以天气预报网站为例:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl 一.web_service_call模式 步骤如下 ...
- 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个对立的元素存于vector中
#include<iostream> #include<string> #include<vector> #include<fstream> using ...
随机推荐
- jquery mobile常用的data-role类型介绍
转自原文 jquery mobile常用的data-role类型介绍 data-role参数表: page 页面容器,其内部的mobile元素将会继承这个容器上所设置的属性 header ...
- HDU 2475 Box 树型转线型 + 伸展树
树型转线型.第一次听说这个概念. . . , 可是曾经已经接触过了,如LCA的预处理部分和树链剖分等.可是没想到还能这么用,三者虽说有不同可是大体思想还是非常相近的,学习了. 推荐博客http://b ...
- 博客迁移到reetsee.com
正如上一篇博客所言.眼下CSDN的博客已经基本完毕它的使命了.感谢CSDN带给我的全部美好回顾. 如今我想尝试一下自己维护一个博客,所以博客的全部内容都迁移到了reetsee.com. 以后博客更新会 ...
- perl getopt 用法
我们在linux经常常使用到一个程序须要增加參数,如今了解一下perl中的有关控制參数的函数.getopt.在linux有的參数有二种形式.一种是--help,还有一种是-h.也就是-和--的分别.- ...
- js中console强大之处体现在哪
js中console强大之处体现在哪 一.总结 一句话总结:在我用过的浏览器当中,我是最喜欢Chrome的,因为它对于调试脚本及前端设计调试都有它比其它浏览器有过之而无不及的地方.可能大家对conso ...
- SpringMVC+Spring+Hibernate框架整合原理,作用及使用方法
转自:https://blog.csdn.net/bieleyang/article/details/77862042 SSM框架是spring MVC ,spring和mybatis框架的整合,是标 ...
- String转换成int型
private boolean judge(String str){ int year = 0; try{ year = Integer.valueOf(str).intValue(); }catch ...
- DISM
C:\WINDOWS\system32>DISM /Online /Cleanup-image /RestoreHealth 部署映像服务和管理工具版本: 10.0.16193.1001 映像版 ...
- jQuery学习(六)——使用JQ完成省市二级联动
1.JQ的遍历操作 方式一: 1 $(function(){ //全选/全不选 $("#checkallbox").click(function(){ var isChecked= ...
- Javascript平稳退化、渐进增强
平稳退化 : javascript平稳退化就是如果一个浏览器完全不支持js或者禁用js的时候,它的基本功能不会受到任何影响.比方说一个网站使用了大量javascript来优化页面,我们现在把浏览器的j ...