对SDRAM基本概念的介绍以及芯片手册说明,请参考上一篇文章SDRAM操作说明

1. 说明

如图所示为状态机的简化图示,过程大概可以描述为:SDRAM(IS42S16320D)上电初始化完成后,进入“空闲”状态,此时一直监控外部控制模块给予的控制信号。初始化完成后,外部定时器开始定时,定时周期为SDRAM刷新周期(7.7us),一旦计数到刷新周期后,向状态机发送auto_ref_req(自动刷新请求),此时状态机进入“刷新”状态,这样就确保在无任何操作时,SDRAM能正常完成刷新。刷新完成后回到“空闲”状态。

当处于空闲状态时,接收到写命令(wr_en),进入“写”状态(有效接收读写命令的时刻有特殊要求,后面再详细说明),在full_page下连续写600个数据(100MHz,恰好耗时6us多一点,这样方便不用考虑定时刷新),写完之后,发送wr_done命令,进入“刷新”状态,相对于每次连续写完成后,提前刷新一次。此时,定时刷新的计数器清零,重新开始计数。

读多过程跟写过程类似,读完600个数据之后,手动完成刷新。

现在就来说一说,“空闲”状态接收读写命令的特殊要求。理论上充电周期为7.8125us,为保证600次读写在充电周期内完成,并且前后预留一些其他命令的时间,所以推荐在0~1us这个时间内接受读写命令,这样读写的时候专注读写就可以了。当然这是我的设计方式,如有更好的设计方式,那更好,欢迎分享。

2. 代码实现

状态机的代码如下所示,清晰的描述了各状态之间的跳变及其跳变条件。其中信号ctrl_valid即为上图中命令有效期的时间区间。在各状态描述的时序逻辑模块中,只是产生了读、写或刷新执行模块的使能信号,即在“写”状态的时候,使能写模块,完成相信的写操作。

    always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
current_status <= IDLE;
end
else if(init_ing == 1'b0)
begin
current_status <= next_status;
end
else
begin
current_status <= IDLE;
end
end always @ (rst_n or current_status or sdram_wrreq or sdram_rdreq or ref_req_auto or wr_done or rd_done or ref_done or ctrl_valid)
begin
next_status = 5'dx;
case(current_status)
IDLE:
begin
if(ref_req_auto == 1'b1) //收到自动刷新请求
begin
next_status = AUTO_REF;
end
else if(ctrl_valid == 1'b1 && sdram_wrreq == 1'b1)//在读写控制有效区内收到写请求
begin
next_status = WRITE;
end
else if(ctrl_valid == 1'b1 && sdram_rdreq == 1'b1) //在读写控制有效区内收到读请求
begin
next_status = READ;
end
else
begin
next_status = IDLE;
end
end
WRITE:
begin
if(wr_done == 1'b1)
begin
next_status = AUTO_REF;
end
else
begin
next_status = WRITE;
end
end
READ:
begin
if(rd_done == 1'b1)
begin
next_status = AUTO_REF;
end
else
begin
next_status = READ;
end
end
AUTO_REF:
begin
if(ref_done == 1'b1)
begin
next_status = IDLE;
end
else
begin
next_status = AUTO_REF;
end
end
default:
begin
next_status = IDLE;
end
endcase
end
//各个状态下的使能信号,以控制相应的模块执行相应的操作
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
else
begin
case(next_status)
IDLE:
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
WRITE:
begin
wr_start <= 1'b1;
rd_start <= 1'b0;
ref_start <= 1'b0;
end
READ:
begin
wr_start <= 1'b0;
rd_start <= 1'b1;
ref_start <= 1'b0;
end
AUTO_REF:
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b1;
end
default:
begin
wr_start <= 1'b0;
rd_start <= 1'b0;
ref_start <= 1'b0;
end endcase
end end

以下给出写操作模块的部分代码,读操作和刷新同理。中间有些信号是我工程需要,参考一下思路即可。

        always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
cke_wr <= 1'b0;
cmd_wr <= NOP;
dqm_wr <= DQM_DIS;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR;
wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
else if(wr_start == 1'b1)
begin
case(status_wr)
4'd0:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd1:
begin
cke_wr <= 1'b1;
cmd_wr <= ACT;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= row_addr; //行地址 wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd2: //4'd2和4'd3是为了延时T_RCD,即两个时钟
begin cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1; end
4'd3:
begin cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1; end
4'd4:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b1; //用于写入第一个数据的时序标记
status_wr <= status_wr + 4'd1;
end
4'd5:
begin
cke_wr <= 1'b1;
cmd_wr <= WR;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= column_addr; //{A12A11,A10,column_address} wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
4'd6:
begin
if(sdram_wr_done == 1'b1) //用于增加NOP持续周期
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_DIS;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b1;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr + 4'd1;
end
else
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= status_wr;
end
end
4'd7:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_DIS;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
default:
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
endcase
end
else
begin
cke_wr <= 1'b1;
cmd_wr <= NOP;
dqm_wr <= DQM_EN;
bank_addr_wr <= BANK0;
addr_wr <= DONT_CARE_ADDR; wr_done <= 1'b0;
wr_first_flag_r <= 1'b0;
status_wr <= 4'd0;
end
end

参考文献

SDRAM驱动篇之简易SDRAM控制器的verilog代码实现

FPGA实战操作(1) -- SDRAM(Verilog实现)的更多相关文章

  1. FPGA实战操作(1) -- SDRAM(操作说明)

    SDRAM是做嵌入式系统中,常用是的缓存数据的器件.基本概念如下(注意区分几个主要常见存储器之间的差异): SDRAM(Synchronous Dynamic Random Access Memory ...

  2. FPGA实战操作(2) -- PCIe总线(例程设计分析)

    1.框架总览 平台:vivado 2016.4 FPGA:A7 在实际应用中,我们几乎不可能自己去编写接口协议,所以在IP核的例程上进行修改来适用于项目是个不错的选择. 通过vivado 中有关PCI ...

  3. FPGA按键去抖verilog代码

    按键去抖的原因及其分类就不罗嗦了. 在这里解释一段代码,代码是网上找的,看了半天没懂,无奈查了半天想了半天,终于明白了... module sw_debounce(clk,rst_n,sw1,sw2, ...

  4. FPGA实战操作(2) -- PCIe总线(协议简述)

    目录 1. PCIe基础知识 2. 事务层协议 2.1 数据包结构 2.2 帧头含义详述 3. 报文举例 3.1 寄存器读报文 3.2 完成报文 4. 机制简述 4.1 Non-Posted和Post ...

  5. FPGA之驱动sdram控制兼容性移植实验

    cb早在2012年就推出了VIP 视频开发板 V1.4  这套开发板是ep2的,摄像头是ov7670,虽然不如当前的vip20强大,但也算是其雏形. 在vip20后期,cb对sdram以及其他模块进行 ...

  6. FPGA 状态机-序列检测器verilog

    实现功能:检测出串行输入数据4位Data二进制序列0101,当检测到该序列的时候,out=1,否则out=0 (1)给出状态编码,画出状态图 (2)门电路实现 (3)verilog实现 首先规定Q3Q ...

  7. 【FPGA篇章三】FPGA常用语句:Verilog基本语法要素

    欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 Verilog中总共有十九种数据类型,我们先介绍四个最基本的数据类型,他们是: reg型.wire型.integer型.parameter型 ...

  8. 基于fpga的256m的SDRAM控制器

    2018/7/26 受教于邓堪文老师,开始真真学习控制sdram 由于自己买的sdram模块是256的,原来老师的是128,所以边学边改,不知道最后好不好使,但是我有信心 一.sdram的初始化 sd ...

  9. 【推荐图书】+ 基于Nios II的嵌入式SoPC系统设计与Verilog开发实例+C#入门经典等

    [推荐图书]+ 基于Nios II的嵌入式SoPC系统设计与Verilog开发实例+C#入门经典等 3赞 发表于 2016/7/4 21:14:12 阅读(1921) 评论(3) 初次接触FPGA,到 ...

随机推荐

  1. ping 127.0.0.1请求超时的解决办法?

    转自:http://blog.51cto.com/dengyong/1429699 打开网络连接,你很有可能启用了虚拟wifi.若有无线网卡就把无线网卡关掉,然后本地连接那里(就是有线网卡的那个连接) ...

  2. java常用八大排序法

    最近查资料发现java排序挺有意思的,其中包含常见八种具有代表性的排序法:笔者觉得排序的功能重要,但更重要的是排序的思想:所以简单叙述一下常见排序方法名称,并用代码举例. A.插入排序(直接插入排序. ...

  3. latex如何进行多行注释

    单行注释:当LATEX 处理一个源文件时,如果遇到一个百分号%,LATEX 将忽略% 后的该行内容,换11行符以及下一行前的空白字符.多行注释:\begin{comment}rather stupid ...

  4. springMVC 返回json乱码问题

    多次遇见过这个问题,springMVC下返回给前端的json字符串,中文总是乱码,每次都要去翻一下之前的代码来看解决办法,有必要做个笔记记一下这个问题了. 解决方法: 在方法注解中加入如下: @Req ...

  5. 一个典型的PHP分页实例代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. DR客户端一直连接服务器....(6)

    DR客户端一直连接服务器....(非未选择网卡)解决方法: 控制面板-->网络和 Internet-->网络连接,打开本地连接(以太网)属性,将IPV4协议前面的对勾去掉,重启DR 这样D ...

  7. ARC100D Equal Cut

    传送门 分析 首先我们想到的肯定是n^3暴力枚举,但这显然不行.然后我们想到的就是二分了,但这题没有什么单调性,所以二分也不行.这时候我就想到了先枚举找出p2的位置再在它的左右两边找到p1和p3,但是 ...

  8. 无法认识patch请求

    Content-Type: application/vnd.api+jsonbase64:账号密码的设置总结:该及时消化的知识,就应该当时消化.不能拖.注意细节,一个小细节的疏忽,导致自己几乎一天的时 ...

  9. 操作系统--UNIX代码段和数据段分开

    (1)代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像.代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作----它是不可写的. (2) ...

  10. Java 分析模板方法设计模型

    http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html //父类 abstract class Operate{ prot ...