FPGA实战操作(1) -- SDRAM(Verilog实现)
对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实现)的更多相关文章
- FPGA实战操作(1) -- SDRAM(操作说明)
SDRAM是做嵌入式系统中,常用是的缓存数据的器件.基本概念如下(注意区分几个主要常见存储器之间的差异): SDRAM(Synchronous Dynamic Random Access Memory ...
- FPGA实战操作(2) -- PCIe总线(例程设计分析)
1.框架总览 平台:vivado 2016.4 FPGA:A7 在实际应用中,我们几乎不可能自己去编写接口协议,所以在IP核的例程上进行修改来适用于项目是个不错的选择. 通过vivado 中有关PCI ...
- FPGA按键去抖verilog代码
按键去抖的原因及其分类就不罗嗦了. 在这里解释一段代码,代码是网上找的,看了半天没懂,无奈查了半天想了半天,终于明白了... module sw_debounce(clk,rst_n,sw1,sw2, ...
- FPGA实战操作(2) -- PCIe总线(协议简述)
目录 1. PCIe基础知识 2. 事务层协议 2.1 数据包结构 2.2 帧头含义详述 3. 报文举例 3.1 寄存器读报文 3.2 完成报文 4. 机制简述 4.1 Non-Posted和Post ...
- FPGA之驱动sdram控制兼容性移植实验
cb早在2012年就推出了VIP 视频开发板 V1.4 这套开发板是ep2的,摄像头是ov7670,虽然不如当前的vip20强大,但也算是其雏形. 在vip20后期,cb对sdram以及其他模块进行 ...
- FPGA 状态机-序列检测器verilog
实现功能:检测出串行输入数据4位Data二进制序列0101,当检测到该序列的时候,out=1,否则out=0 (1)给出状态编码,画出状态图 (2)门电路实现 (3)verilog实现 首先规定Q3Q ...
- 【FPGA篇章三】FPGA常用语句:Verilog基本语法要素
欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 Verilog中总共有十九种数据类型,我们先介绍四个最基本的数据类型,他们是: reg型.wire型.integer型.parameter型 ...
- 基于fpga的256m的SDRAM控制器
2018/7/26 受教于邓堪文老师,开始真真学习控制sdram 由于自己买的sdram模块是256的,原来老师的是128,所以边学边改,不知道最后好不好使,但是我有信心 一.sdram的初始化 sd ...
- 【推荐图书】+ 基于Nios II的嵌入式SoPC系统设计与Verilog开发实例+C#入门经典等
[推荐图书]+ 基于Nios II的嵌入式SoPC系统设计与Verilog开发实例+C#入门经典等 3赞 发表于 2016/7/4 21:14:12 阅读(1921) 评论(3) 初次接触FPGA,到 ...
随机推荐
- java 多线程系列---JUC原子类(一)之框架
根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicIn ...
- javascript——对象的基础知识
一.javascript作为脚本语言可以完成以下任务: 操纵浏览器对象,如窗口的打开与关闭: 操纵Dom树: 通过XMLHttpRequest对象与服务器端进行异步通信: XML编程,借助于Activ ...
- eclipse安卓模拟器Failed to install on device 'emulator-5554': timeout处理方案
我们在用模拟器调试的时候,经常会出现Failed to install on device 'emulator-5554': timeout这个错误.其实就是有些虚拟器在部署的时候时间过于长.系统就认 ...
- hadoop再次集群搭建(5)-CDH Install
登录 http://node1.com:7180/.用户名和密码都是admin.启动服务命令是 service cloudera-scm-server start 最开始两个页面直接conti ...
- volatile关键字在多线程中的作用
- Ajax笔记(二)
JSON基本概念: JSON:javaScript对象表示法(JavaScript Object Notation) JSON是存储和交换文本信息的语法,类似XML.它采用键值对的方式来组织,易于人们 ...
- java虚拟机垃圾回收机制详解
首先,看一下java虚拟机运行的时候内存分配图: jvm虚拟机栈:一个是线程独有的,每次启动一个线程,就创建一个jvm虚拟机栈,线程退出的时候就销毁.这里面主要保存线程本地变量名和局部变量值. 本地方 ...
- [tensorflow]异或门的实现
一段小程序:待理解 import tensorflow as tf import numpy as np #输入训练数据,这里是python的list, 也可以定义为numpy的ndarray x_d ...
- 【转】pecl,pear的不同
PEAR是PHP扩展与应用库(the PHP Extension and Application Repository)的缩写.它是一个PHP扩展及应用的一个代码仓库,基于php代码的,安装目录在/u ...
- Mat的迭代器使用
如果你熟悉 C++的 STL 库,那一定了解迭代器(iterator)的使用.迭代器可以方便地遍历所有元素.Mat 也增加了迭代器的支持,以便于矩阵元素的遍历.下面的例程功能跟上一节的例程类似,但是由 ...