对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)beginif(rst_n == 1'b0)begincurrent_status <= IDLE;endelse if(init_ing == 1'b0)begincurrent_status <= next_status;endelsebegincurrent_status <= IDLE;endendalways @ (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)beginnext_status = 5'dx;case(current_status)IDLE:beginif(ref_req_auto == 1'b1)                        //收到自动刷新请求beginnext_status = AUTO_REF;endelse if(ctrl_valid == 1'b1 && sdram_wrreq == 1'b1)//在读写控制有效区内收到写请求beginnext_status = WRITE;endelse if(ctrl_valid == 1'b1 && sdram_rdreq == 1'b1)       //在读写控制有效区内收到读请求beginnext_status = READ;endelsebeginnext_status = IDLE;endendWRITE:beginif(wr_done == 1'b1)beginnext_status = AUTO_REF;endelsebeginnext_status = WRITE;end endREAD:beginif(rd_done == 1'b1)beginnext_status = AUTO_REF;endelsebeginnext_status = READ;end endAUTO_REF:beginif(ref_done == 1'b1)beginnext_status = IDLE;endelsebeginnext_status = AUTO_REF;endenddefault:beginnext_status = IDLE;end endcaseend//各个状态下的使能信号,以控制相应的模块执行相应的操作    always @ (posedge clk or negedge rst_n)beginif(rst_n == 1'b0)beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b0;endelsebegincase(next_status)IDLE:beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b0;endWRITE:beginwr_start <= 1'b1;rd_start <= 1'b0;ref_start <= 1'b0;endREAD:beginwr_start <= 1'b0;rd_start <= 1'b1;ref_start <= 1'b0;endAUTO_REF:beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b1;enddefault:beginwr_start <= 1'b0;rd_start <= 1'b0;ref_start <= 1'b0;endendcaseendend

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

        always @(posedge clk or negedge rst_n)beginif(rst_n == 1'b0)begincke_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;endelse if(wr_start == 1'b1)begincase(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;      end4'd1:begincke_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;end4'd2:                                   //4'd2和4'd3是为了延时T_RCD,即两个时钟begincke_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;end4'd3:                                           begincke_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;end4'd4:begincke_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;end4'd5:begincke_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;end4'd6:beginif(sdram_wr_done == 1'b1)       //用于增加NOP持续周期begincke_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;endelsebegincke_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;endend4'd7:begincke_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;enddefault:begincke_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;endendcaseendelsebegincke_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;endend

参考文献

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

转载于:https://www.cnblogs.com/rouwawa/p/7339102.html

FPGA实战操作(1) -- SDRAM(Verilog实现)相关推荐

  1. (111)FPGA面试题-介绍Verilog 块语句fork-join执行过程

    1.1 FPGA面试题-介绍Verilog 块语句fork-join执行过程 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-介绍Verilog 块语句f ...

  2. (110)FPGA面试题-介绍Verilog块语句begin-end执行过程

    1.1 FPGA面试题-介绍Verilog块语句begin-end执行过程 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-介绍Verilog块语句beg ...

  3. (90)FPGA面试题-使用Verilog实现可预置初值的7进制循环计数器

    1.1 FPGA面试题-使用Verilog实现可预置初值的7进制循环计数器 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-使用Verilog实现可预置初 ...

  4. (88)FPGA面试题-使用Verilog HDL编写二进制转格雷码

    1.1 FPGA面试题-使用Verilog HDL编写二进制转格雷码 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-使用Verilog HDL编写二进制 ...

  5. (64)FPGA面试题-在Verilog中,$display语句可以使用哪些不同的选项?

    1.1 FPGA面试题-在Verilog中,$display语句可以使用哪些不同的选项? 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-在Verilog ...

  6. (63)FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(二)

    1.1 FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(二) 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-用verilo ...

  7. (62)FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(一)

    1.1 FPGA面试题-用verilog写一段代码,实现消除一个glitch(毛刺)(一) 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-用verilo ...

  8. (61)FPGA面试题-使用Verilog语言编写异步复位同步释放代码

    1.1 FPGA面试题-使用Verilog语言编写异步复位同步释放代码 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-使用Verilog语言编写异步复位 ...

  9. (67)FPGA模块调用(Verilog调用system Verilog)

    (67)FPGA模块调用(Verilog调用system Verilog) 1.1 目录 1)目录 2)FPGA简介 3)Verilog HDL简介 4)FPGA模块调用(Verilog调用syste ...

最新文章

  1. Redis Info详解
  2. Jupyter notebook与Spyder,以及Jupyter notebook与Spyder集成插件
  3. anaconda换成制定清华的源:一键解决外网下载慢的烦恼
  4. .net访问PostgreSQL数据库发生“找不到函数名”的问题追踪
  5. IOS控件的应用UIPageController与UIScrollView
  6. Python生成(x,y,z)三维坐标序列
  7. 涤纶针织物用分散染料染色时,为什么小样与大样不符?
  8. Aoite 系列(01) - 比 Dapper 更好用的 ORM
  9. Service Mesh-Linkerd安装与使用
  10. bug管理工具之禅道的测试模块的使用
  11. 苹果手机解压缩软件_「 神器 」用得贼舒服的压缩/解压缩工具
  12. Oracle 10g 手工创建一个最简单的数据库
  13. esxi虚拟化服务器端口聚合,配置链路聚合组处理分布式端口组的流量
  14. HDUOJ 4565 So Easy!
  15. visual studio 提高速度的选项
  16. 应聘时漂亮的回答,一生受益的语言
  17. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-堆和优先队列(二)
  18. 考取PMP认证有用吗?
  19. 支付回答——如何理解借记和贷记
  20. 场景实践:使用函数计算打包下载OSS文件

热门文章

  1. spring代码异常捕获到logback logging.config=logback-spring.xml文件中不能输出异常e.printStackTrace...
  2. Android 第三方库RxLifecycle使用
  3. 关于function和Object的认识
  4. (转)Cairngorm初学者入门教程 第三节--从使用Model Locator开始学起Cairngorm
  5. 用zlib库读取单个压缩文档[转]
  6. linux 串口特别是接收
  7. Linux系统下对flash的测试方法
  8. gj10 python socket编程
  9. java 手势识别,AndroidStudio:手势识别
  10. axis2 java.net.url_axis,axis2调用.net的webservice