对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代码实现

SDRAM操作(FPGA实现)的更多相关文章

  1. 用ModelSim仿真SDRAM操作

    之前写了两篇关于Modelsim仿真的blog,其中模块管脚的命名可能让人觉得有些奇怪,其实不然,之前的两篇内容都是为了仿真SDRAM操作做铺垫的. 由于SDRAM的仿真过程相对比较复杂,也比较繁琐. ...

  2. [FPGA] 1、开发板使用和引脚连接

    目录 1.注意事项 2.设备简介 3.引脚分配 注意事项: ① 插拔下载线时必须断电! ② Quartus II 软件和 NIOS 软件的版本必须一致,并安装在同一个目录下面,安装目录不要有中文和空格 ...

  3. 通过HPS控制FPGA端的GPIO

    该笔记主要记录HPS端如何通过AXI Bridge控制FPGA端口的GPIO,主要是如何操作FPGA侧的Led 1.AXI Bridge         AXIB主要包括H2FB.F2HB.LWH2F ...

  4. HPS端如何通过AXI Bridge控制FPGA端口的GPIO

    该笔记主要记录HPS端如何通过AXI Bridge控制FPGA端口的GPIO,主要是如何操作FPGA侧的Led 1.AXI Bridge         AXIB主要包括H2FB.F2HB.LWH2F ...

  5. 转载 基于NicheStack协议栈的TCP/IP实现

    一.摘要 Altera软件NIOS II高版本(7.2版本以上,本例程中使用的是9.0版本)中实现TCP/IP所用的协议栈为NicheStack,常用的例程有2个,web_server和simple_ ...

  6. 【DSP开发】6455EMIF

     外部设备连接接口包括外部存储器连接接口(EMIF).主机接口(HPI)等.外部存储器接口主要用来同并行存储器连接,这些存储器包括SDRAM.SBSRAM.Flash.SRAM存储器等,外部存储器接口 ...

  7. [转]DDR3基础知识介绍

    本文转自:(4条消息) xilinx ddr3 MIG ip核使用详解_admiraion123的博客-CSDN博客 1,DDR3基本内容介绍1.1,DDR3简介DDR3全称double-data-r ...

  8. 【图像处理】【SEED-VPM】6.文件目录结构

    ———————————————————————————————————————————————————————————————————————— seed-vpm6467 \ Hardware Tes ...

  9. cavium octeon 处理器启动总线Bootbus 简介

    cavium octeon 处理器启动总线Bootbus 简介: 韩大卫@吉林师范大学 Boot-bus(启动总线)是cavium octeon处理器的一种用于启动系统的硬件. CPU通过boot b ...

随机推荐

  1. C#直接发送打印机命令到打印机及ZPL常用打印命令 - 条码打印机

    using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServi ...

  2. IP报文分片

    1. 最大传输单元(Maximum Transmission Unit,MTU). 以太网帧中的数据长度规定最小46 字节,最大1500 字节,MTU 指数据帧中有效载荷的最大长度,不包括帧首部的长度 ...

  3. WPF DelegateCommand 出现Specified cast is not valid

    使用 DelegateCommand 出现 Specified cast is not valid 最近写快捷键需要 DelegateCommand ,于是用了 DelegateCommand< ...

  4. Django - - - -视图层之视图函数(views)

    视图层之视图函数(views) 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档 ...

  5. 用 eclipse 创建一个简单的 meaven spring springMvc mybatis 项目

    下面是整体步骤: 1: 先创建一个Maven 项目: 选择跳过骨架: 因为要搭建的是 web 项目  所以这个地方选择 war 包; 点击完成 这样就完成 Maven项目的搭建: 接下俩 先把 Mav ...

  6. JUnit5 安装与使用

    虽然JUnit5 的测试版本早就出来了,但正式版直到几年9月份推出,目前最新版5.0.1.几乎所有的Java 开发人员都会使用JUnit 来做测试,但其实很多自动化测试人员也会使用Junit .目前, ...

  7. 媲美jQuery的JS框架----AngularJS(二)

    前言 对于AngularJS什么,小编在这就不多做介绍了.大家可以看小编的上一篇博客. 言归正传,小编在上一篇博客中介绍了AngularJS中的指令.表达式还有非常实用的三种服务.接下来,带大家看一看 ...

  8. Robot Framework自动化测试框架初探

    Robot Framework是一款python语言编写,通用的功能自动化测试框架.它使用了比较易用的表格数据语法,基于关键字驱动测试,主要用来验收测试和验收测试驱动开发(ATDD). 本文主要介绍R ...

  9. Mybatis Generator生成Mybatis Dao接口层*Mapper.xml以及对应实体类

    [前言] 使用Mybatis-Generator自动生成Dao.Model.Mapping相关文件,Mybatis-Generator的作用就是充当了一个代码生成器的角色,使用代码生成器不仅可以简化我 ...

  10. require.js实现js模块化编程(一)

    1.认识require.js: 官方文档:http://requirejs.org/RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一.最新版本的Requ ...