SDRAM 学习笔记

目录

1. 理论学习

1.1 基本概念

1.2 SDRAM数据存取原理

1.3 SDRAM引脚说明

引脚功能描述

SDRAM器件功能框图​编辑

1.4 SDRAM的速率等级与存储容量

存储容量

速率等级

1.5 操作命令

1.5.1 禁止命令(Command Inhibit)

1.5.2 无操作命令

1.5.3 配置模式寄存器命令

突发长度(Burst Length)

突发类型

列选通潜伏期

运行模式

写模式

1.5.4 预充电命令

1.5.5 刷新命令

1.5.6 激活命令

1.5.7 写命令

1.5.8 读命令

1.5.9 突发终止命令

2.项目实战

2.1 uart_sdram

2.1.1 uart_sdram顶层模块

2.1.2 uart_tx 模块

2.1.3 uart_rx模块

2.1.4 fifo_read 模块

2.2 sdram_top

2.2.1 sdram_top 顶层模块

2.2.2 fifo_ctrl 模块

2.2.3 sdram_ctrl 模块

2.3 sdram_ctrl

2.3.1 sdram_ctrl 顶层模块

2.3.2 初始化模块

代码实现:

2.3.3 自刷新模块

代码实现:

2.3.4 数据读模块

代码实现:

2.3.5 数据写模块

代码实现:

2.3.6 仲裁模块

代码实现:


1. 理论学习

1.1 基本概念

SDRAM英文全称“Synchronous Dynamic Random Access Memory”,

1.2 SDRAM数据存取原理

简单来说,SDRAM 内部可以理解为一个存储阵列,这是 SDRAM 区别于管道式存储,实现随机地址存取的结构特点。将 SDRAM 内部存储阵列类比于一张表格,表格中的每一个单元格可以类比为存储阵列的单个存储单元。若想要实现存储阵列中的某一存储单元的数据读写操作,我们要通过行地址(Row Address)和列地址(Column Address)(先行后列)精确定位到这一存储单元,进而进行数据的读写操作,这就是所谓的随机地址存取。

对于 SDRAM,我们将类比于单元格的存储空间称之为存储单元,N(行列个数乘积)个存储单元构成一个存储阵列,这个存储阵列我们称之为一个逻辑 Bank(Logical Bank,简称L-Bank、Bank)。

SDRAM 内部并不是一个全容量的 L-Bank,而是分割为若干个 L-Bank,目前大多为 4个。若干 L-Bank 的分割,原因有二,一是技术、成本等诸多因素;二是由于SDRAM 的工作原理限制,单一 L-Bank 可能会造成非常严重的寻址冲突,大幅度降低内存效率。

在对 SDRAM 进行数据存取时,要先确定 L-Bank 地址,定位到指定逻辑Bank,再依次确定行地址和列地址,选中存储单元,进而进行数据的存取操作,而且一次只能对一个 L-Bank 的一个存储单元进行操作

SDRAM 的基本存储单位是存储单元,而一个存储单元的容量为若干个 Bit,对于SDRAM 而言就是芯片的位宽,每个 Bit 存放于一个单独的存储体中,存储体是利用电容能够保持电荷以及可充放电的特性制成,主要由行选通三极管、列选通三极管、存储电容以及刷新放大器构成。电容所存储的电荷会随时间慢慢释放,这就需要不断刷新为电容充电,以保证存储数据可靠性。

将每个存储单元简化为单 Bit 的存储体,再将若干存储体排列为矩阵,同一行将行地址线相连同一列将列地址线相连,就构成了一个存储阵列的简化模型。

1.3 SDRAM引脚说明

x4、x8、x16 分别表示位宽 4bit、8bit、16bit;#符号表示信号为低电平有效;短 划线 - 表示 x8和 x4 引脚功能与 x16 引脚功能相同。

引脚功能描述
引脚位宽类型功能描述
CLK1BitInput系统时钟:SDRAM 由系统时钟驱动,所有 SDRAM 输入信号都在时钟上升沿采样,
CKE1BitInput时钟使能;高电平有效
CS#(CS_N)1BitInput片选信号;低电平有效。为高电平时,屏蔽所有命令,但已经突发的读/写操作不受影响。
CAS#(CAS_N)1BitInput列选通信号;低电平有效。为低电平时,A[8:0]输入的为列地址。
RAS#(RAS_N)1BitInput行选通信号;低电平有效。为低电平时,A[12:0]输入的为行地址
WE#(WE_N)1BitInput写使能信号;低电平有效。为低电平时,使能写操作和预充电。
DQM[1:0]1BitInput数据掩码;DQML(H),低(高)字节掩码,若信号为高电平,在下一个时钟周期的时钟上升沿,数据总线的低(高)字节为高阻态。
BA[1:0]2BitInputL-Bank 地址;选择不同的逻辑 Bank 进行相关数据操作
A[12:0]13BitInput地址总线;
DQ[15:0]16BitInput数据总线;

{CS#、CAS#、RAS#、WE#}构成 SDRAM 操作命令。

SDRAM器件功能框图

①SDRAM 内部包含一个逻辑控制单元,内部包含模式寄存器命令解码器

②外部通过{CS#、CAS#、RAS#、WE#}以及地址总线向逻辑控制单元输入命令。

③命令经过命令解码器进行译码后,将控制参数保存到模式寄存器中,逻辑控制单元进而控制逻辑运行。

1.4 SDRAM的速率等级与存储容量

存储容量

以镁光公司的三款SDRAM芯片举例,

由图可知,“4Meg × 16 × 4Banks”才是表示 SDRAM存储容量的具体参数,

4Meg” 表示单个 L-Bank 包含的存储单元的个数,计算方法为单个存储阵列行地址数列地址数的乘积;以此芯片为例,行地址总线为 A0-A12,行地址位宽为13 位,行地址数为 8192 (2^13)行,列地址位宽为 9 位,列地址数为 512(2^9)列,乘积为4M;

16”表示数据位宽,即每个存储单元存储数据的 bit数;

4BANKS”表示一片 SDRAM 中包含的 L-Bank个数,此 SDRAM 芯片包含 4 个 L-Bank,

总的存储容量就是:256MBit(4Meg × 16 × 4BANKS),

容量计算方法可简化为:存储容量(Bit) = L-Bank 存储单元数 ×数据位宽(Bit) × L-Bank 个数。

速率等级

5个相关参数:

①时钟频率(Clock Frequency,单位:MHz)。SDRAM 正常工作的最高时钟频率,SDRAM 工作时只能等于或低于这一时钟频率。

②tRCD(单位: ns)。表示写入自激活命令开始进行数据读写,中间所需的等待时间,列举的数值表示等待时间的最小值。

③ tRP (单位: ns)表示自预充电指令写入到预充电完成所需的等待时间,列举的数值表示等待时间的最小值。

④CL(CAS(READ)latency ,单位: ns)。列选通潜伏期,表示自数据读指令写入到第一个有效数据输出所需等待时间。

⑤Target tRCD -tRP -CL ,表示最大工作频率下tRCD 、tRP 、CL等待的最小时钟周期数。

1.5 操作命令

CS_N、RAS_N、CAS_N、WE_N 四路控制信号构成 SDRAM 指令集。

CKE、BA[1:0]、A[12:0]等信号,在 SDRAM 的操作中,也起到辅助作用

1.5.1 禁止命令(Command Inhibit)

控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b1XXX

不论 SDRAM 处于何种状态,此命令均可被执行,无需顾及 CKE是否有效,即 CLK是否使能,无需关心 DQM、ADDR、DQ 的信号输入;执行此命令后,SDRAM 芯片不被选择,新的命令无法写入,但已经执行的命令不受影响。

1.5.2 无操作命令

无操作命令(No-operation,简称为 NOP 命令)。

控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0111。

目的是为了防止 SDRAM 处于空闲或等待状态时,其他命令被写入,此命令对正在执行的命令无影响。

1.5.3 配置模式寄存器命令

配置模式寄存器命令(Load Mode Register,也被称为 Mode Reigister Set,简称 LMR命令)。

控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0000

此命令只有所有 L-Bank 均处于空闲状态时才可被写入,否则配置出错。

在执行此命令后,SDRAM 必须等待相应的响应时间tRSC (Register Set Cycle,模式寄存器配置周期 ) 后,才可写入新的命令。

此时需要地址总线来辅助完成命令。A0-A11 赋值不同对应寄存器配置不同模式,未使用的地址总线设置为低电平。

突发长度(Burst Length)

突发(Burst)是指在同一行中相邻的存储单元连续进行数据传输的方式,连续传输所涉及到存储单元(列)的数量就是突发长度。

地址总线的低三位 A0-A2 是突发长度的控制位。

SDRAM 芯片的突发长度可设置为1、2、4、8 和整页,单位为字节,整页表示一次突发传输一整行的数据量。

若在数据读/写操作时不使用突发传输,此时可等效为突发长度为 1 字节,每次读/写数据时,都要对存储单元进行寻址,如果要实现连续的读/写操作,就要不断地发送列地址和读/写命令,这种方法控制资源占用极大,效率较低。

使用突发传输,只要指定起始列地址突发长度,内存就会依次地自动对后面相应数量的存储单元进行读/写操作而不再需要控制器连续地提供列地址,这样,除了第一笔数据传输需要若干个周期外,其后的每个数据只要一个周期即可获得。突发的数据长度一般是 4或 8,如果传输时实际需要的数据长度小于设定的 BL值,则调用“突发停止”命令结束传输。

突发类型

位置为A3,突发类型有两类:顺序和隔行低电平为顺序类型,高电平为隔行类型,一般选择顺序类型。

列选通潜伏期

指从读命令被寄存到数据总线上出现第一个有效数据之间的时钟周期间隔,列选通潜伏期可被设置为 2 个或 3 个时钟周期

设置位为 A6,A5,A4,

{A6,A5,A4}=3’b010 时,潜伏期为 2 个时钟周期;

{A6,A5,A4}=3’b011 时,潜伏期为 3 个时钟周期。

运行模式

运行模式设置位为 A7,A8,SDRAM 存在标准模式测试模式等多种模式,但对于普通用户,只开放了标准模式,在使用 SDRAM 时只需将 A7,A8 设置为低电平进入标准模式。

写模式

设置位为 A9,

A9 为低电平时,突发写& 突发读【突发长度由突发长度寄存器(A0-A2)设定】;

A9 位高电平时,突发读& 普通写【突发长度由突发长度寄存器(A0-A2)设定】,每一个写命令只能写入一个数据;

{不管怎么样,总是突发读模式}

A10-A12 为保留位,对模式寄存器的配置不起作用,赋值为 0 即可

1.5.4 预充电命令

预充电的作用就是关闭指定的 L-Bank 或者全部 L-Bank 中激活的行。

预充电命令执行后,必须等待对应的等待时间 tRP (Precharge command Period,预充电命令周期),相对应的 L-Bank 将可以被重新操作。

预充电包括两类:全部预充电、指定 L-Bank预充电

控制指令均为 {CS_N,RAS_N,CAS_N,WE_N} = 4’b0010;地址总线中的 A10 和 L-Bank 地址线 BA[1:0]辅助控制预充电类型。

A10 为高电平时,执行全部预充电命令,对所有的 L-Bank 进行预充电,无需关心 BA[1:0]信号的输入;

A10 为低电平时,只对由 BA[1:0]选定的 L-Bank 进行预充电

1.5.5 刷新命令

SDRAM 通过刷新操作保证数据的可靠性

SDRAM 的刷新操作是周期性的,在两次刷新的间隔可以进行数据的相关操作。

目前国际公认的标准是,存储体中电容的数据有效保存期上限是 64ms,也就是说每一行刷新的循环周期最大为 64ms,那么刷新速度就是:行数/64ms。

【我们在 SDRAM 的数据手册中经常会看到 4096 Refresh Cycles/64ms 或 8192 Refresh Cycles/64ms 的相关介绍,这里的 4096 与 8192 就代表 SDRAM 芯片中单个 L-Bank 的行数。刷新命令一次对一行有效,发送间隔也是随总行数而变化, 当单个 L-Bank 为 4096 行时,刷新间隔最大为 15.625μs,单个 L-Bank 为 8192 行时,刷新间隔最大为 7.8125μs。】

刷新命令(Refresh)分为两种:自动刷新命令(Auto Refresh)和自刷新(Self Refresh),

控制指令为 {CS_N,RAS_N,CAS_N,WE_N} = 4’b0001

CKE 为高电平时,写入刷新指令,执行自动刷新操作;

CKE 为低电平时,写入刷新指令,执行自刷新操作。

1.5.6 激活命令

控制命令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0011

1.5.7 写命令

控制命令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0100

对已经激活特定的L-bank行,执行突发写操作。BA[1:0]指定需要写入数据的特定 L-Bank,地址总线 A0-A9 指定需要写入存储单元的起始列地址;A10 的电平变化控制突发写操作完成后是否立即执行自动预充电操作

A10 为高电平,突发写操作完成后,立即执行自动预充电操作,关闭当前行;

A10 为低电平,突发写操作完成后,当前行依然处于激活状态,以便对当前行执行新的读/写操作,想要关闭当前激活行,需写入预充电指令

1.5.8 读命令

控制命令为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0101

对已激活特定 L-Bank 的特定行的数据突发读取操作。BA[1:0]指定需要读取数据的特定 L-Bank,地址总线 A0-A9 指定需要读取存储单元的起始列地址;A10 的电平变化控制突发读操作完成后是否立即执行自动预充电操作

A10 为高电平,突发读操作完成后,立即执行自动预充电操作,关闭当前行;

A10 为低电平,突发读操作完成后,当前行依然处于激活状态,以便对当前行执行新的读/写操作,想要关闭当前激活行,需写入预充电指令

1.5.9 突发终止命令

控 制 命 令 为{CS_N,RAS_N,CAS_N,WE_N} = 4’b0110

SDRAM 处于读/写操作过程中可被写入,突发停止操作被用来截断固定长度或者整页长度的突发,执行突发停止命令后,最近执行的数据读/写操作被终止,此命令操作并不会通过预充电关闭当前激活行,需通过预充电操作关闭被激活行。

2.项目实战

设计并实现了一个SDR SDRAM数据读写控制器,可以实现10bit的数据的写入与读出。

SDRAM控制器模块包含五个模块:初始化模块、自动刷新模块、数据读模块、数据写模块和仲裁模块

控制器连接一个异步FIFO,对待写入的数据和读出的数据进行缓存,实现跨时钟域处理,并且为数据读、写模块提供SDRAM的读写地址,并产生读写请求;

再连接分频器、uart_tx、fifo_read、uart_rx模块。

模块名称功能描述
uart_sdram串口读写 SDRAM顶层模块
clk_gen时钟生成模块,为整个工程提供工作时钟
uart_rx串口数据接收模块,接收串口发送过来的数据
sdram_topSDRAM控制器,实现 SDRAM数据读写
fifo_readSDRAM读出数据缓存
uart_tx串口数据发送模块,发送读出 SDRAM的数据

模块名称功能描述
sdram_topSDRAM控制器顶层模块
fifo_ctrlFIFO 控制模块,实现 SDRAM读写数据的跨时钟域处理
sdram_ctrl实现 SDRAM的初始化、自动刷新和数据读写操作
模块名称功能描述
sdram_ctrlSDRAM控制部分顶层模块
sdram_initSDRAM初始化模块,实现 SDRAM 的初始化
sdram_arefSDRAM自动刷新模块,实现 SDRAM的自动刷新操作
sdram_writeSDRAM数据写模块,实现 SDRAM 的数据写入
sdram_readSDRAM数据读模块,实现 SDRAM 的数据读出
sdram_arbitSDRAM仲裁模块,实现各操作优先级的仲裁

个人喜欢从外层往里层写代码,而野火相反。在做仿真的时候,我是喜欢从外往里面仿真,一点一点进行验证。读者可以自己挑选喜欢的方式。

2.1 uart_sdram

2.1.1 uart_sdram顶层模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  uart_sdram
--  Description :  
------------------------------------------------------------------------------*/
  module uart_sdram(
  /*-------------------------- Interface definitation ------------------------*/
  input                    sys_clk,
  input                    sys_rst_n,
  
  input                    rx,
  output                   tx,
  
  output                   sdram_clk,
  output                   sdram_cke,
  output                   sdram_cs_n,
  output                   sdram_cas_n,
  output                   sdram_ras_n,
  output                   sdram_we_n,
  output [1:0]             sdram_ba,
  output [12:0]            sdram_addr,
  output [1:0]             sdram_dqm,
  inout  [15:0]            sdram_dq
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter DATA_NUM = 24'd10;
  parameter WAIT_MAX = 16'd750;
  parameter UART_BPS = 14'd9600;
  parameter CLK_FREQ = 26'd50_000_000;
  
  /*------------- Internal Interface definitation -------------*/
  //uart_rx
  wire [7:0]               rx_data;
  wire                     rx_flag;
  
  //fifo_read
  wire [7:0]               rfifo_wr_data;
  wire                     rfifo_wr_en;
  wire [7:0]               rfifo_rd_data;
  wire                     rfifo_rd_en;
  wire [9:0]               rd_fifo_num;
  
  //clk_gen
  wire                     clk_50m;
  wire                     clk_100m;
  wire                     clk_100m_shift;
  wire                     locked;
  wire                     rst_n;
  
  //Inst_sdram_top
  reg  [23:0]              data_num;
  reg                      read_valid;
  reg  [15:0]              cnt_wait;
  
  /*-----------------------------------------------------------*/
  
  //rst_n
  assign rst_n = sys_rst_n && locked;
  
  //data_num
  always@(posedge clk_50m or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        data_num <= 24'b0;
      end
    else if(read_valid == 1'b1)
      begin
        data_num <= 24'b0;
      end
    else if(rx_flag == 1'b1)
      begin
        data_num <= data_num + 1'b1;
      end
    else
      begin
        data_num <= data_num;
      end
  end
  
  //cnt_wait
  always@(posedge clk_50m or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_wait <= 16'b0;
      end
    else if(cnt_wait == WAIT_MAX)
      begin
        cnt_wait <= 16'b0;
      end
    else if(data_num == DATA_NUM)
      begin
        cnt_wait <= cnt_wait + 1'b1;
      end
  end
  
  //read_valid
  always@(posedge clk_50m or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        read_valid <= 1'b0;
      end
    else if(cnt_wait ==WAIT_MAX)
      begin
        read_valid <= 1'b1;
      end
    else if(rd_fifo_num == DATA_NUM)
      begin
        read_valid <= 1'b0;
      end
  end
  //clk_gen
  clk_gen Inst_clk_gen(.inclk0(sys_clk),
                       .reset(~sys_rst_n),
                       .C0(clk_50m),
                       .C1(clk_100m),
                       .C2(clk_100m_shift),
                       .locked(locked));
  
  //uart_rx
  uart_rx#(.UART_BPS(UART_BPS),
           .CLK_FREQ(CLK_FREQ))
  Inst_uart_rx(.sys_clk(clk_50m),
               .sys_rst_n(rst_n),
               .rx(rx),
               .po_data(rx_data),
               .po_flag(rx_flag));
  
  //uart_tx
  uart_tx#(.UART_BPS(UART_BPS),
           .CLK_FREQ(CLK_FREQ))
  Inst_uart_tx(.sys_clk(sys_clk),
               .sys_rst_n(sys_rst_n),
               .pi_data(rfifo_rd_data),
               .pi_flag(rfifo_rd_en),
               .tx(tx));
  
  //fifo_read
  fifo_read Inst_fifo_read(.sys_clk(clk_50m),
                           .sys_rst_n(sys_rst_n),
                           .rd_fifo_num(rd_fifo_num),
                           .pi_data(rfifo_wr_data),
                           .burst_num(DATA_NUM),
                           .read_en(rfifo_wr_en),
                           .tx_data(rfifo_rd_data),
                           .tx_flag(rfifo_rd_en));
  
  //sdram_top
  sdram_top Inst_sdram_top(.sys_clk(clk_100m),
                           .clk_out(clk_100m_shift),
                           .sys_rst_n(rst_n),
                           
                           .wr_fifo_wr_clk(clk_50m),
                           .wr_fifo_wr_req(rx_flag),
                           .wr_fifo_wr_data({8'b0,rx_data}),
                           .sdram_wr_b_addr(24'b0),
                           .sdram_wr_e_addr(DATA_NUM),
                           .wr_burst_len(DATA_NUM),
                           .wr_rst(),
                           
                           .rd_fifo_rd_clk(clk_50m),
                           .rd_fifo_rd_req(rfifo_wr_en),
                           .rd_fifo_rd_data(rfifo_wr_data),
                           .sdram_rd_b_addr(24'b0),
                           .sdram_rd_e_addr(DATA_NUM),
                           .rd_burst_len(DATA_NUM),
                           .rd_rst(),
                           .rd_fifo_num(rd_fifo_num),
                           
                           .read_valid(read_valid),
                           .init_end(),
                           
                           .sdram_clk(sdram_clk),
                           .sdram_cke(sdram_cke),
                           .sdram_cs_n(sdram_cs_n),
                           .sdram_ras_n(sdram_ras_n),
                           .sdram_cas_n(sdram_cas_n),
                           .sdram_we_n(sdram_we_n),
                           .sdram_ba(sdram_ba),
                           .sdram_addr(sdram_addr),
                           .sdram_dq(sdram_dq),
                           .sdram_dqm(sdram_dqm));
  
  endmodule
2.1.2 uart_tx 模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  uart_tx
--  Description :  
------------------------------------------------------------------------------*/
  module uart_tx(
  /*-------------------------- Interface definitation ------------------------*/
  input                      sys_clk,
  input                      sys_rst_n,
  input [7:0]                pi_data,
  input                      pi_flag,
  
  output reg                 tx
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter      UART_BPS     = 14'd9600;
  parameter      CLK_FREQ     = 26'd50_000_000;
  localparam     BAUD_CNT_MAX = CLK_FREQ/UART_BPS;
  
  /*------------- Internal Interface definitation -------------*/
  reg [12:0]                baud_cnt;
  reg                       bit_flag;
  reg [3:0]                 bit_cnt;
  reg                       work_en;
  
  /*-----------------------------------------------------------*/
  
  //work_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        work_en <= 1'b0;
      end
    else if(pi_flag == 1'b1)
      begin
        work_en <= 1'b1;
      end
    else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
      begin
        work_en <= 1'b0;
      end
  end
  
  //baud_cnt
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        baud_cnt <= 13'b0;
      end
    else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
      begin
        baud_cnt <= 13'b0;
      end
    else if(work_en == 1'b1)
      begin
        baud_cnt <= baud_cnt + 1'b1;
      end
  end
  
  //bit_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        bit_flag <= 1'b0;
      end
    else if(baud_cnt == 13'd1)
      begin
        bit_flag <= 1'b1;
      end
    else
      begin
        bit_flag <= 1'b0;
      end
  end
  
  //bit_cnt
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        bit_cnt <= 4'b0;
      end
    else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
      begin
        bit_cnt <= 4'b0;
      end
    else if((bit_flag == 1'b1) && (work_en == 1'b1))
      begin
        bit_cnt <= bit_cnt +1'b1;
      end
  end
  
  //tx
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        tx <= 1'b1;
      end
    else if(bit_flag == 1'b1)
      begin
        case(bit_cnt)
          0 : tx <= 1'b0;
          1 : tx <= pi_data[0];
          2 : tx <= pi_data[1];
          3 : tx <= pi_data[2];
          4 : tx <= pi_data[3];
          5 : tx <= pi_data[4];
          6 : tx <= pi_data[5];
          7 : tx <= pi_data[6];
          8 : tx <= pi_data[7];
          9 : tx <= 1'b1;
          default : tx <= 1'b1;
        endcase
      end
  end
  endmodule
2.1.3 uart_rx模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  uart_rx
--  Description :  
------------------------------------------------------------------------------*/
  module uart_rx(
  /*-------------------------- Interface definitation ------------------------*/
  input                    sys_clk,
  input                    sys_rst_n,
  input                    rx,
  
  output reg [7:0]         po_data,
  output reg               po_flag
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter UART_BPS = 14'd9600;
  parameter CLK_FREQ = 26'd50_000_000;
  
  localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS;
  
  /*------------- Internal Interface definitation -------------*/
  reg                      rx_reg1;
  reg                      rx_reg2;
  reg                      rx_reg3;
  reg                      start_nedge;
  reg                      work_en;
  reg [12:0]               baud_cnt;
  reg                      bit_flag;
  reg [3:0]                bit_cnt;
  reg [7:0]                rx_data;
  reg                      rx_flag;
  
  /*-----------------------------------------------------------*/
  
  //rx_reg1,rx_reg2,rx_reg3
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        rx_reg1 <= 1'b1;
        rx_reg2 <= 1'b1;
        rx_reg3 <= 1'b1;
      end
    else
      begin
        rx_reg1 <= rx;
        rx_reg2 <= rx_reg1;
        rx_reg3 <= rx_reg2;
      end
  end
  
  //start_nedge
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        start_nedge <= 1'b0;
      end
    else if((~rx_reg2) || (rx_reg3))
      begin
        start_nedge <= 1'b1;
      end
    else
      begin
        start_nedge <= 1'b0;
      end
  end
  
  //work_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        work_en <= 1'b0;
      end
    else if(start_nedge ==1'b1)
      begin
        work_en <= 1'b1;
      end
    else if((bit_cnt == 4'd8) || (bit_flag == 1'b1))
      begin
        work_en <= 1'b0;
      end
  end
  
  //baud_cnt
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        baud_cnt <= 13'b0;
      end
    else if((baud_cnt == BAUD_CNT_MAX -1) || (work_en == 1'b0))
      begin
        baud_cnt <= 13'b0;
      end
    else if(work_en ==1'b1)
      begin
        baud_cnt <= baud_cnt + 1'b1;
      end
  end
  
  //bit_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        bit_flag <= 1'b0;
      end
    else if(baud_cnt == BAUD_CNT_MAX/2-1)
      begin
        bit_flag <= 1'b1;
      end
    else
      begin
        bit_flag <= 1'b0;
      end
  end
  
  //bit_cnt
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        bit_cnt <= 1'b0;
      end
    else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
      begin
        bit_cnt <= 4'b0;
      end
    else if(bit_flag == 1'b1)
      begin
        bit_cnt <= bit_cnt + 1'b1;
      end
  end
  
  //rx_data
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        rx_data <= 8'b0;
      end
    else if((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8) && (bit_flag == 1'b1))
      begin
        rx_data <= {rx_reg3,rx_data[7:1]};
      end
  end
  
  //rx_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        rx_flag <= 1'b0;
      end
    else if((bit_cnt ==4'd8) && (bit_flag == 1'b1))
      begin
        rx_flag <= 1'b1;
      end
    else
      begin
        rx_flag <= 1'b0;
      end
  end
  
  //po_data
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        po_data <= 8'b0;
      end
    else if(rx_flag == 1'b1)
      begin
        po_data <= rx_data;
      end
  end
  
  //po_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        po_flag <= 1'b0;
      end
    else
      begin
        po_flag <= rx_flag;
      end
  end
  
  endmodule
2.1.4 fifo_read 模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  fifo_read
--  Description :  
------------------------------------------------------------------------------*/
  module fifo_read(
  /*-------------------------- Interface definitation ------------------------*/
  input                          sys_clk,
  input                          sys_rst_n,
  input [9:0]                    rd_fifo_num,
  input [7:0]                    pi_data,
  input [9:0]                    burst_num,
  
  output reg                     read_en,
  output     [7:0]               tx_data,
  output reg                     tx_flag
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter BAUD_CNT_END      = 13'd5207;
  parameter BAUD_CNT_END_HALF = 13'd2603;
  parameter CNT_WAIT_MAX      = 24'd4_999_999;
  
  /*------------- Internal Interface definitation -------------*/
  wire [9:0]                     data_num;
  reg                            read_en_dly;
  reg  [12:0]                    baud_cnt;
  reg                            rd_en;
  reg                            rd_flag;
  reg  [9:0]                     cnt_read;
  reg  [3:0]                     bit_cnt;
  reg                            bit_flag;
  
  /*-----------------------------------------------------------*/
  
  //read_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        read_en <= 1'b0;
      end
    else if(rd_fifo_num == burst_num)
      begin
        read_en <= 1'b1;
      end
    else if(data_num == burst_num -2)
      begin
        read_en <= 1'b0;
      end
  end
  
  //read_en_dly
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        read_en_dly <= 1'b0;
      end
    else
      begin
        read_en_dly <= 1'b1;
      end
  end
  
  //rd_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        rd_flag <= 1'b0;
      end
    else if(cnt_read == burst_num)
      begin
        rd_flag <= 1'b0;
      end
    else if(data_num == burst_num)
      begin
        rd_flag <= 1'b1;
      end
  end
  
  //baud_cnt
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        baud_cnt <= 13'b0;
      end
    else if(baud_cnt == BAUD_CNT_END)
      begin
        baud_cnt <= 13'b0;
      end
    else if(rd_flag == 1'b1)
      begin
        baud_cnt <= baud_cnt + 1'b1;
      end
  end
  
  //bit_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        bit_flag <= 1'b0;
      end
    else if(baud_cnt == BAUD_CNT_END_HALF)
      begin
        bit_flag <= 1'b1;
      end
    else
      begin
        bit_flag <= 1'b0;
      end
  end
  
  //bit_cnt
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        bit_cnt <= 4'b0;
      end
    else if((bit_cnt == 4'd9) && (bit_flag == 1'b1))
      begin
        bit_cnt <= 4'b0;
      end
    else
      begin
        bit_cnt <= bit_cnt +1'b1;
      end
  end
  
  //rd_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        rd_en <= 1'b0;
      end
    else if((bit_cnt == 4'd9) && bit_flag == 1'b1)
      begin
        rd_en <= 1'b1;
      end
    else
      begin
        rd_en <= 1'b0;
      end
  end
  
  //cnt_read
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_read <= 10'd0;
      end
    else if(cnt_read == burst_num)
      begin
        cnt_read <= 10'd0;
      end
    else if(rd_en == 1'b1)
      begin
        cnt_read <= cnt_read + 1'b1;
      end
    else
      begin
        cnt_read <= cnt_read;
      end
  end
  
  //tx_flag
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
   if(sys_rst_n == 0)
     begin
       tx_flag <= 1'b0;
     end
   else
     begin
       tx_flag <= rd_en;
     end
  end
  
  //read_fifo
  read_fifo Inst_read_fifo(.clock(sys_clk),
                           .data(pi_data),
                           .wrreq(read_en_dly),
                           .rdreq(rd_en),
                           
                           .q(tx_data),
                           .usedw(data_num));
  
  endmodule

2.2 sdram_top

2.2.1 sdram_top 顶层模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_top
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_top(
  /*-------------------------- Interface definitation ------------------------*/
  input                    sys_clk,
  input                    clk_out,
  input                    sys_rst_n,
  // write fifo
  input                    wr_fifo_wr_clk,
  input                    wr_fifo_wr_req,
  input [15:0]             wr_fifo_wr_data,
  input [24:0]             sdram_wr_b_addr,//write sdram begin addr
  input [23:0]             sdram_wr_e_addr,//write sdram end   addr
  input [9:0]              wr_burst_len,
  input                    wr_rst,
  //read fifo
  input                    rd_fifo_rd_clk,
  input                    rd_fifo_rd_req,
  input [23:0]             sdram_rd_b_addr,
  input [23:0]             sdram_rd_e_addr,
  input [9:0]              rd_burst_len,
  input                    rd_rst,
  
  output [15:0]            rd_fifo_rd_data,
  output [9:0]             rd_fifo_num,
  input                    read_valid,//sdram read valid
  output                   init_end,
  //sdram
  output                   sdram_clk,
  output                   sdram_cke,//时钟有效信号
  output                   sdram_cs_n,//片选信号
  output                   sdram_ras_n,//行地址选通脉冲
  output                   sdram_cas_n,//列地址选通脉冲
  output                   sdram_we_n,//SDRAM写允许位
  output [1:0]             sdram_ba,//SDRAM的L-Bank地址线
  output [12:0]            sdram_addr,//SDRAM地址总线
  output [1:0]             sdram_dqm,//SDRAM数据掩码
  inout [15:0]             sdram_dq//SDRAM数据总线
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
​
  /*------------- Internal Interface definitation -------------*/
  wire                     sdram_wr_req;
  wire                     sdram_wr_ack;
  wire [23:0]              sdram_wr_data;
  wire [15:0]              sdram_data_in;
  
  wire                     sdram_rd_req;
  wire                     sdram_rd_ack;
  wire [23:0]              sdram_rd_addr;
  wire [15:0]              sdram_data_out;
  /*-----------------------------------------------------------*/
  //fifo_ctrl 
  fifo_ctrl Inst_fifo_ctrl(.sys_clk(sys_clk),
                           .sys_rst_n(sys_rst_n),
                           
                           .wr_fifo_wr_clk(wr_fifo_wr_clk),
                           .wr_fifo_req(wr_fifo_req),
                           .wr_fifo_wr_data(wr_fifo_wr_data),
                           .sdram_wr_b_addr(sdram_wr_b_addr),
                           .sdram_wr_e_addr(sdram_wr_e_addr),
                           .wr_burst_len(wr_burst_len),
                           .wr_rst(wr_rst),
                           
                           .rd_fifo_rd_clk(rd_fifo_rd_clk),
                           .rd_fifo_rd_req(rd_fifo_rd_req),
                           .rd_fifo_rd_data(rd_fifo_rd_data),
                           .rd_fifo_num(rd_fifo_num),
                           .sdram_rd_b_addr(sdram_rd_b_addr),
                           .sdram_rd_e_addr(sdram_rd_e_addr),
                           .rd_burst_len(rd_burst_len),
                           .rd_rst(rd_rst),
                           
                           .read_valid(read_valid),
                           .init_end(init_end),
                           
                           .sdram_wr_ack(sdram_wr_ack),
                           .sdram_wr_req(sdram_wr_req),
                           .sdram_wr_addr(sdram_wr_addr),
                           .sdram_data_in(sdram_data_in),
                           
                           .sdram_rd_ack(sdram_rd_ack),
                           .sdram_rd_req(sdram_rd_req),
                           .sdram_data_out(sdram_data_out),
                           .sdram_rd_addr(sdram_rd_addr));
  //sdram_ctrl
  sdram_ctrl Inst_sdram_ctrl(.sys_clk(sys_clk),
                             .sys_rst_n(sys_rst_n),
                             
                             .sdram_wr_req(sdram_wr_req),
                             .sdram_wr_addr(sdram_wr_addr),
                             .wr_burst_len(wr_burst_len),
                             .sdram_data_in(sdram_data_in),
                             .sdram_wr_ack(sdram_wr_ack),
                             
                             .sdram_rd_req(sdram_rd_req),
                             .sdram_rd_addr(sdram_rd_addr),
                             .rd_burst_len(rd_burst_len),
                             .sdram_data_out(sdram_data_out),
                             .init_end(init_end),
                             .sdram_rd_ack(sdram_rd_ack),
                             
                             .sdram_cke(sdram_cke),
                             .sdram_cs_n(sdram_cs_n),
                             .sdram_ras_n(sdram_ras_n),
                             .sdram_cas_n(sdram_cas_n),
                             .sdram_we_n(sdram_we_n),
                             .sdram_ba(sdram_ba),
                             .sdram_addr(sdram_addr),
                             .sdram_dq(sdram_dq));
  
  endmodule
2.2.2 fifo_ctrl 模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  fifo_ctrl
--  Description :  主要使用FIFO对传入的待写入sdram的数据和自SDRAM读出的数据 进行缓存,
                   实现跨时钟域处理;为数据读写模块提供SDRAM读写地址,产生读写请求;
------------------------------------------------------------------------------*/
  module fifo_ctrl(
  /*-------------------------- Interface definitation ------------------------*/
  input                        sys_clk,
  input                        sys_rst_n,
  input                        wr_fifo_wr_clk,
  input                        wr_fifo_wr_req,
  input [15:0]                 wr_fifo_wr_data,
  input [23:0]                 sdram_wr_b_addr,
  input [23:0]                 sdram_wr_e_addr,
  input [9:0]                  wr_burst_len,
  input                        wr_rst,
  
  input                        rd_fifo_rd_clk,
  input                        rd_fifo_rd_req,
  input [23:0]                 sdram_rd_b_addr,
  input [23:0]                 sdram_rd_e_addr,
  input [9:0]                  rd_burst_len,
  input                        rd_rst,
  output [15:0]                rd_fifo_rd_data,
  output [9:0]                 rd_fifo_num,
  input                        read_valid,
  input                        init_end,
  
  input                        sdram_wr_ack,
  output reg                   sdram_wr_req,
  output reg [23:0]            sdram_wr_addr,
  output     [15:0]            sdram_data_in,
  
  input                        sdram_rd_ack,
  input      [15:0]            sdram_data_out,
  output reg                   sdram_rd_req,
  output reg [23:0]            sdram_rd_addr
  
  /*-----------------------------------------------------------*/
  );
  /*------------- Parameter and Internal Signal ---------------*/
  
  /*------------- Internal Interface definitation -------------*/
  wire                         wr_ack_fall;
  wire                         rd_ack_fall;
  wire [9:0]                   wr_fifo_num;
  
  reg                          wr_ack_dly;
  reg                          rd_ack_dly;
  
  //wr_ack_dly ,rd_ack_dly  读写响应信号打拍
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        wr_ack_dly <= 1'b0;
        rd_ack_dly <= 1'b0;
      end
    else
      begin
        wr_ack_dly <= sdram_wr_ack;
        rd_ack_dly <= sdram_rd_ack;
      end
  end
  
  //wr_ack_fall,rd_ack_fall,检测读写响应信号下降沿
  assign wr_ack_fall = (wr_ack_dly & ~sdram_wr_ack);
  assign rd_ack_fall = (rd_ack_dly & ~sdram_rd_ack);
  
  //sdram_wr_addr
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        sdram_wr_addr <= 24'b0;
      end
    else if(wr_rst == 1'b1)
      begin
        sdram_wr_addr <= sdram_wr_b_addr;
      end
    else if(wr_ack_fall == 1'b1)//一次突发写结束,更改写地址
      begin
        if(sdram_wr_addr < (sdram_wr_e_addr - wr_burst_len))//不使用乒乓操作,一次突发写结束,更改写地址,未达到末地址之前,写地址增加
          begin
            sdram_wr_addr <= sdram_wr_addr + wr_burst_len;
          end
        else //不使用乒乓操作,到达末地址,回到写起始地址
          begin
            sdram_wr_addr <= sdram_wr_b_addr;
          end
      end
  end
  
  //sdram_rd_addr
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        sdram_rd_addr <= 1'b0;
      end
    else if(rd_rst ==1'b1)
      begin
        sdram_rd_addr <= sdram_rd_b_addr;
      end
    else if(rd_ack_fall == 1'b1)
      begin
        if(sdram_rd_addr < (sdram_rd_e_addr - rd_burst_len))
          begin
            sdram_rd_addr <= sdram_rd_addr + rd_burst_len;
          end
        else
          begin
            sdram_rd_addr <= sdram_rd_b_addr;
          end
      end
  end
  
  //sdram_wr_req,sdram_rd_req
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        sdram_wr_req <= 1'b0;
        sdram_rd_req <= 1'b0;
      end
    else if(init_end == 1'b1)//初始化完成后响应读写请求
      begin
        if(wr_fifo_num >= wr_burst_len)//优先执行写操作,防止写入SDRAM数据的丢失
          begin
            sdram_wr_req <= 1'b1;//写FIFO中的数据量达到写突发长度,写请求有效
            sdram_rd_req <= 1'b0;
          end
        else if((rd_fifo_num < rd_burst_len) && (read_valid == 1'b1))//读FIFO中的数据量小于读的突发数据量,且读使能信号有效,
          begin
            sdram_wr_req <= 1'b0;
            sdram_rd_req <= 1'b1;//读请求有效
          end
        else
          begin
            sdram_wr_req <= 1'b0;
            sdram_rd_req <= 1'b0;
          end
      end
    else
      begin
        sdram_wr_req <= 1'b0;
        sdram_rd_req <= 1'b0;
      end
  end
  
  fifo_data Inst_wr_fifo_data(.wr_clk(wr_fifo_wr_clk),
                              .wrreq(wr_fifo_wr_req),
                              .data(wr_fifo_wr_data),
                              
                              .rdclk(sys_clk),
                              .rdreq(sdram_wr_ack),
                              .q(sdram_data_in),
                              .rdusedw(wr_fifo_num),
                              .wrusedw(),
                              .aclr(~sys_rst_n || wr_rst));
  
  fifo_data Inst_rd_fifo_data(.wr_clk(sys_clk),
                              .wrreq(sdram_rd_ack),
                              .data(sdram_data_out),
                              
                              .rdclk(rd_fifo_rd_clk),
                              .rdreq(rd_fifo_rd_req),
                              .q(rd_fifo_rd_data),
                              .rdusedw(),
                              .wrusedw(rd_fifo_num),
                              .aclr(~sys_rst_n || rd_rst));
  
  endmodule
2.2.3 sdram_ctrl 模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_ctrl
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_ctrl(
  /*-------------------------- Interface definitation ------------------------*/
  input                        sys_clk,
  input                        sys_rst_n,
  //sdram write
  input                        sdram_wr_req,
  input [23:0]                 sdram_wr_addr,
  input [9:0]                  wr_burst_len,
  input [15:0]                 sdram_data_in,
  output                       sdram_wr_ack,
  //sdram read
  input                        sdram_rd_req,
  input [23:0]                 sdram_rd_addr,
  input [9:0]                  rd_burst_len,
  output [15:0]                sdram_data_out,
  output                       init_end,
  output                       sdram_rd_ack,
  //FPGA TO SDRAM
  output                       sdram_cke,
  output                       sdram_cs_n,
  output                       sdram_ras_n,
  output                       sdram_cas_n,
  output                       sdram_we_n,
  output [1:0]                 sdram_ba,
  output [12:0]                sdram_addr,
  output [15:0]                sdram_dq
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  /*------------- Internal Interface definitation -------------*/
  //sdram_init
  wire [3:0]                   init_cmd;
  wire [1:0]                   init_ba;
  wire [12:0]                  init_addr;
  //sdram_a_ref
  wire                         aref_req;
  wire                         aref_end;
  wire [3:0]                   aref_cmd;
  wire [1:0]                   aref_ba;
  wire [12:0]                  aref_addr;
  wire                         aref_en;
  //sdram_write
  wire                         wr_en;
  wire                         wr_end;
  wire [3:0]                   write_cmd;
  wire [1:0]                   write_ba;
  wire [12:0]                  write_addr;
  wire                         wr_sdram_en;
  wire [15:0]                  wr_sdram_data;
  //sdram_read
  wire                         rd_en;
  wire                         rd_end;
  wire [3:0]                   read_cmd;
  wire [1:0]                   read_ba;
  wire [12:0]                  read_addr;
  /*-----------------------------------------------------------*/
  sdram_init Inst_sdram_init(.sys_clk(sys_clk),
                             .sys_rst_n(sys_rst_n),
                             
                             .init_cmd(init_cmd),
                             .init_ba(init_ba),
                             .init_addr(init_addr),
                             .init_end(init_end));
  
  sdram_arbit Inst_sdram_arbit(.sys_clk(sys_clk),
                               .sys_rst_n(sys_rst_n),
                               //sdram_init
                               .init_cmd(init_cmd),
                               .init_end(init_end),
                               .init_ba(init_ba),
                               .init_addr(init_addr),
                               //sdram_a_ref
                               .aref_req(aref_req),
                               .aref_end(aref_end),
                               .aref_cmd(aref_cmd),
                               .aref_ba(aref_ba),
                               .aref_addr(aref_addr),
                               //sdram_write
                               .wr_req(sdram_wr_req),
                               .wr_end(wr_end),
                               .wr_cmd(writ_ba),
                               .wr_ba(write_ba),
                               .wr_addr(write_addr),
                               .wr_sdram_en(wr_sdram_en),
                               .wr_data(wr_sdram_data),
                               //sdram_read
                               .rd_req(sdram_rd_req),
                               .rd_end(rd_end),
                               .rd_cmd(read_cmd),
                               .rd_addr(read_addr),
                               .rd_ba(read_ba),
                               
                               .aref_en(aref_en),
                               .wr_en(wr_en),
                               .rd_en(rd_en),
                               
                               .sdram_cke(sdram_cke),
                               .sdram_cs_n(sdram_cs_n),
                               .sdram_ras_n(sdram_ras_n),
                               .sdram_cas_n(sdram_cas_n),
                               .sdram_we_n(sdram_we_n),
                               .sdram_ba(sdram_ba),
                               .sdram_addr(sdram_addr),
                               .sdram_dq(sdram_dq));
  
  sdram_a_ref Inst_sdram_a_ref(.sys_clk(sys_clk),
                               .sys_rst_n(sys_rst_n),
                               .init_end(init_end),
                               .aref_en(aref_en),
                               
                               .aref_req(aref_req),
                               .aref_cmd(aref_cmd),
                               .aref_ba(aref_ba),
                               .aref_addr(aref_addr),
                               .aref_end(aref_end));
  //sdram_write
  sdram_write Inst_sdram_write(.sys_clk(sys_clk),
                               .sys_rst_n(sys_rst_n),
                               .init_end(init_end),
                               .wr_en(wr_en),
                               
                               .wr_addr(wr_sdram_addr),
                               .wr_burst_len(wr_burst_len),
                               .wr_data(sdram_data_in),
                               
                               .wr_ack(sdram_wr_ack),
                               .wr_end(wr_end),
                               .write_cmd(write_cmd),
                               .write_ba(write_ba),
                               .write_addr(write_addr),
                               .wr_sdram_en(wr_sdram_en),
                               .wr_sdram_data(wr_sdram_data));
  
  //sdram_read
  sdram_read Inst_sdram_read(.sys_clk(sys_clk),
                             .sys_rst_n(sys_rst_n),
                             .init_end(init_end),
                             .rd_en(rd_en),
                             
                             .rd_addr(sdram_rd_addr),
                             .rd_data(sdram_dq),
                             .rd_burst_len(rd_burst_len),
                             
                             .rd_ack(sdram_rd_ack),
                             .rd_end(rd_end),
                             .read_cmd(read_cmd),
                             .read_ba(read_ba),
                             .read_addr(read_addr),
                             .rd_sdram_data(sdram_data_out));
  endmodule

2.3 sdram_ctrl

网上最多的是这部分,这部分也是我们的核心代码。做仿真时也是必须要做的。

2.3.1 sdram_ctrl 顶层模块
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_ctrl
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_ctrl(
  /*-------------------------- Interface definitation ------------------------*/
  input                        sys_clk,
  input                        sys_rst_n,
  //sdram write
  input                        sdram_wr_req,
  input [23:0]                 sdram_wr_addr,
  input [9:0]                  wr_burst_len,
  input [15:0]                 sdram_data_in,
  output                       sdram_wr_ack,
  //sdram read
  input                        sdram_rd_req,
  input [23:0]                 sdram_rd_addr,
  input [9:0]                  rd_burst_len,
  output [15:0]                sdram_data_out,
  output                       init_end,
  output                       sdram_rd_ack,
  //FPGA TO SDRAM
  output                       sdram_cke,
  output                       sdram_cs_n,
  output                       sdram_ras_n,
  output                       sdram_cas_n,
  output                       sdram_we_n,
  output [1:0]                 sdram_ba,
  output [12:0]                sdram_addr,
  output [15:0]                sdram_dq
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  /*------------- Internal Interface definitation -------------*/
  //sdram_init
  wire [3:0]                   init_cmd;
  wire [1:0]                   init_ba;
  wire [12:0]                  init_addr;
  //sdram_a_ref
  wire                         aref_req;
  wire                         aref_end;
  wire [3:0]                   aref_cmd;
  wire [1:0]                   aref_ba;
  wire [12:0]                  aref_addr;
  wire                         aref_en;
  //sdram_write
  wire                         wr_en;
  wire                         wr_end;
  wire [3:0]                   write_cmd;
  wire [1:0]                   write_ba;
  wire [12:0]                  write_addr;
  wire                         wr_sdram_en;
  wire [15:0]                  wr_sdram_data;
  //sdram_read
  wire                         rd_en;
  wire                         rd_end;
  wire [3:0]                   read_cmd;
  wire [1:0]                   read_ba;
  wire [12:0]                  read_addr;
  /*-----------------------------------------------------------*/
  sdram_init Inst_sdram_init(.sys_clk(sys_clk),
                             .sys_rst_n(sys_rst_n),
                             
                             .init_cmd(init_cmd),
                             .init_ba(init_ba),
                             .init_addr(init_addr),
                             .init_end(init_end));
  
  sdram_arbit Inst_sdram_arbit(.sys_clk(sys_clk),
                               .sys_rst_n(sys_rst_n),
                               //sdram_init
                               .init_cmd(init_cmd),
                               .init_end(init_end),
                               .init_ba(init_ba),
                               .init_addr(init_addr),
                               //sdram_a_ref
                               .aref_req(aref_req),
                               .aref_end(aref_end),
                               .aref_cmd(aref_cmd),
                               .aref_ba(aref_ba),
                               .aref_addr(aref_addr),
                               //sdram_write
                               .wr_req(sdram_wr_req),
                               .wr_end(wr_end),
                               .wr_cmd(writ_ba),
                               .wr_ba(write_ba),
                               .wr_addr(write_addr),
                               .wr_sdram_en(wr_sdram_en),
                               .wr_data(wr_sdram_data),
                               //sdram_read
                               .rd_req(sdram_rd_req),
                               .rd_end(rd_end),
                               .rd_cmd(read_cmd),
                               .rd_addr(read_addr),
                               .rd_ba(read_ba),
                               
                               .aref_en(aref_en),
                               .wr_en(wr_en),
                               .rd_en(rd_en),
                               
                               .sdram_cke(sdram_cke),
                               .sdram_cs_n(sdram_cs_n),
                               .sdram_ras_n(sdram_ras_n),
                               .sdram_cas_n(sdram_cas_n),
                               .sdram_we_n(sdram_we_n),
                               .sdram_ba(sdram_ba),
                               .sdram_addr(sdram_addr),
                               .sdram_dq(sdram_dq));
  
  sdram_a_ref Inst_sdram_a_ref(.sys_clk(sys_clk),
                               .sys_rst_n(sys_rst_n),
                               .init_end(init_end),
                               .aref_en(aref_en),
                               
                               .aref_req(aref_req),
                               .aref_cmd(aref_cmd),
                               .aref_ba(aref_ba),
                               .aref_addr(aref_addr),
                               .aref_end(aref_end));
  //sdram_write
  sdram_write Inst_sdram_write(.sys_clk(sys_clk),
                               .sys_rst_n(sys_rst_n),
                               .init_end(init_end),
                               .wr_en(wr_en),
                               
                               .wr_addr(wr_sdram_addr),
                               .wr_burst_len(wr_burst_len),
                               .wr_data(sdram_data_in),
                               
                               .wr_ack(sdram_wr_ack),
                               .wr_end(wr_end),
                               .write_cmd(write_cmd),
                               .write_ba(write_ba),
                               .write_addr(write_addr),
                               .wr_sdram_en(wr_sdram_en),
                               .wr_sdram_data(wr_sdram_data));
  
  //sdram_read
  sdram_read Inst_sdram_read(.sys_clk(sys_clk),
                             .sys_rst_n(sys_rst_n),
                             .init_end(init_end),
                             .rd_en(rd_en),
                             
                             .rd_addr(sdram_rd_addr),
                             .rd_data(sdram_dq),
                             .rd_burst_len(rd_burst_len),
                             
                             .rd_ack(sdram_rd_ack),
                             .rd_end(rd_end),
                             .read_cmd(read_cmd),
                             .read_ba(read_ba),
                             .read_addr(read_addr),
                             .rd_sdram_data(sdram_data_out));
  endmodule
2.3.2 初始化模块

初始化时芯片上电之后的一项必须的操作,只有进行了初始化操作的SDRAM芯片,才能被正常使用,时序图如下:

主要看command这一行;

(1) 对 SDRAM 上电,加载稳定时钟信号,CKE 设置为高电平; (2) 等待 T=100us 的时间,此过程中操作命令保持为空操作命令; (3) 100us 等待结束后,写入预充电命令,A10 设置为高电平对所b有 L-Bank 进行预充电

(4) 预充电指令写入后,等待 tRP 时间,此过程中操作命令保持为空操作命令;【预充电命令执行后,必须等待对应的等待时间 tRP

(5) tRP 等待时间结束后,写入自动刷新命令

【当 CKE 为高电平时,写入刷新指令,执行自动刷新操作。在执行自动刷新命令前,必须先要执行预充电命令,将所有 L-Bank 关闭】

(6) 自动刷新命令写入后,等待 tRC 时间,此过程中操作命令保持为空操作命令

(7) tRC 等待时间结束后,再次写入自动刷新命令

(8) 自动刷新命令写入后,等待 tRC 时间,此过程中操作命令保持为空操作命令;

(9) tRC 等待时间结束后,写入模式寄存器配置指令,地址总线 A0-A11 参数不同辅助模式寄存器不同模式的设置;

信号位宽类型功能描述
sys_clk1BitInput系统时钟,频率 100MHz
sys_rst_n1BitInput复位信号,低有效
init_cmd4BitOutput输出初始化阶段指令信号
init_ba2BitOutput初始化阶段 L-Bank 地址
init_addr13BitOutput初始化阶段地址总线,辅助预充电操作和模式寄存器的配置
init_end1BitOutput初始化结束标志,标志着初始化完成,SDRAM可进行其他操作
代码实现:
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_init
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_init(
  /*-------------------------- Interface definitation ------------------------*/
  input                        sys_clk,
  input                        sys_rst_n,
  
  output reg [3:0]             init_cmd,//{cs_n,ras_n,cas_n,we_n}
  output reg [1:0]             init_ba,
  output reg [12:0]            init_addr,
  output                       init_end
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter T_POWER   = 15'd20_000;
  //SDRAM初始化用到的控制信号命令
  parameter P_CHARGE  = 4'b0010;//预充电
  parameter AUTO_REF  = 4'b0001;//自动刷新
  parameter NOP       = 4'b0111;//空操作
  parameter M_REG_SET = 4'b0000;//模式寄存器设置
  //SDRAM初始化过程中各个状态
  parameter INIT_IDLE = 3'b000;//初始状态
  parameter INIT_PRE  = 3'b001;//预充电状态
  parameter INIT_TRP  = 3'b011;//预充电等待   tRP
  parameter INIT_AR   = 3'b010;//自动刷新
  parameter INIT_TRF  = 3'b100;//自动刷新等待  tRC
  parameter INIT_MRS  = 3'b101;//模式寄存器设置
  parameter INIT_TMRD = 3'b111;//模式寄存器设置等待 tMRD
  parameter INIT_END  = 3'b110;//初始化完成
  
  parameter TRP_CLK   = 3'd2;//预充电等待周期 20ns
  parameter TRC_CLK   = 3'd7;//自动刷新等待 70ns
  parameter TMRD_CLK  = 3'd3;//模式寄存器设置等待周期 30ns
  
  /*------------- Internal Interface definitation -------------*/
  wire                          wait_end;//上电后200us等待结束标志
  wire                          trp_end;//预充电等待结束标志
  wire                          trc_end;//自动刷新等待结束标志
  wire                          tmrd_end;//模式寄存器设置等待结束标志
  
  reg  [14:0]                   cnt_200us;//SDRAM上电后200us稳定期计数器
  reg  [2:0]                    init_state;//SDRAM初始化状态
  reg  [2:0]                    cnt_clk;//时钟周期计数,记录初始化各状态等待周期
  reg                           cnt_clk_rst;//时钟周期计数复位标志
  reg  [3:0]                    cnt_init_aref;//初始化过程自动刷新次数计数器
  
  //cnt_200us
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_200us <= 15'd0;
      end
    else if(cnt_200us == T_POWER)
      begin
        cnt_200us <= T_POWER;
      end
    else
      begin
        cnt_200us <= cnt_200us + 1'd1;
      end
  end
  
  //wait_end
  assign wait_end = (cnt_200us ==(T_POWER - 1'b1))?1'b1:1'b0;
  
  //init_end
  assign init_end = (init_state == INIT_END)?1'b1:1'b0;
  
  //cnt_clk
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_clk <= 3'd0;
      end
    else if(cnt_clk_rst == 1'b1)
      begin
        cnt_clk <= 3'd0;
      end
    else
      begin
        cnt_clk <= cnt_clk +1'b1;
      end
  end
  
  //cnt_init_aref
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_init_aref <= 4'd0;
      end
    else if(init_state == INIT_IDLE)
      begin
        cnt_init_aref <= 4'd0;
      end
    else if(init_state == INIT_AR)
      begin
        cnt_init_aref <= cnt_init_aref + 1'b1;
      end
    else
      begin
        cnt_init_aref <= cnt_init_aref;
      end
  end
  
  //trp_end,trc_end,tmrd_end
  assign trp_end  = ((init_state == INIT_TRP) && (cnt_clk == TRP_CLK))?1'b1:1'b0;
  assign trc_end  = ((init_state == INIT_TRF) && (cnt_clk == TRC_CLK))?1'b1:1'b0;
  assign tmrd_end = ((init_state == INIT_TMRD) && (cnt_clk == TMRD_CLK))?1'b1:1'b0;
  
  //SDRAM 初始化状态机
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        init_state <= INIT_IDLE;
      end
    else
      case(init_state)
      
        INIT_IDLE:
          if(wait_end == 1)
            init_state <= INIT_PRE;
          else
            init_state <= init_state;
        
        INIT_PRE : init_state <= INIT_TRP;
        
        INIT_TRP :
          if(trp_end == 1)
            init_state <= INIT_AR;
          else
            init_state <= init_state;
        
        INIT_AR  : init_state <= INIT_TRF;
        
        
        INIT_TRF :
          if(trc_end == 1)
            begin
              if(cnt_init_aref == 4'd8)
                init_state <= INIT_MRS;
              else
                init_state <= INIT_AR;
            end
          else 
            begin
              init_state <= init_state;
            end
        
        
        INIT_MRS : init_state <= INIT_TMRD;
        
        INIT_TMRD:
          if(tmrd_end == 1)
            begin
              init_state <= INIT_END;
            end
          else
            begin
              init_state <= init_state;
            end
            
        INIT_END : init_state <= INIT_END;
        
        default  : init_state <= INIT_IDLE;
      endcase
  end
  //cnt_clk_rst
  always@(*)
  begin
    case(init_state)
      INIT_IDLE : cnt_clk_rst <= 1'b1;
      INIT_TRP  : cnt_clk_rst <= (trp_end == 1'b1 )?1'b1:1'b0;
      INIT_TRF  : cnt_clk_rst <= (trc_end == 1'b1 )?1'b1:1'b0;
      INIT_TMRD : cnt_clk_rst <= (tmrd_end == 1'b1)?1'b1:1'b0;
      INIT_END  : cnt_clk_rst <= 1'b1;
      default   : cnt_clk_rst <= 1'b0;
    endcase
  end
  
  //sdram操作指令控制
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        init_cmd  <= NOP;
        init_ba   <= 2'b11;
        init_addr <= 13'h1fff;
      end
    else
      case(init_state)
        INIT_IDLE,INIT_TRP,INIT_TRF,INIT_TMRD:begin//执行空操作
          init_cmd  <= NOP;
          init_ba   <= 2'b11;
          init_addr <= 13'h1fff;
        end
        
        INIT_PRE:begin//预充电
          init_cmd  <= P_CHARGE;
          init_ba   <= 2'b11;
          init_addr <= 13'h1fff;
        end
        
        INIT_AR:begin//自动刷新
          init_cmd  <= AUTO_REF;
          init_ba   <= 2'b11;
          init_addr <= 13'h1fff;
        end
        
        INIT_MRS:begin//模式寄存器设置
          init_cmd  <= M_REG_SET;
          init_ba   <= 2'b00;
          init_addr <={3'b000, //A12-A10,保留
                       1'b0,   //A9 ,读写方式,0:突发读&突发写,1:突发读&单写
                       2'b00,  //A8,A7,00,标准模式,默认
                       3'b011, //A6,A5,A4, = 011:cas潜伏期,010:2,011:3,其他:保留
                       1'b0,   //A3 :突发传输方式,0:顺序,1:隔行
                       3'b111};//A2-A0 突发长度     111:整页,   000:单字节,   001:2字节,    010:4字节,   011:8字节
        end
        
        INIT_END:begin//SDRAM初始化完成
          init_cmd  <= NOP;
          init_ba   <= 2'b11;
          init_addr <= 13'h1fff;
        end
        
        default:begin
          init_cmd  <= NOP;
          init_ba   <= 2'b11;
          init_addr <= 13'h1fff;
        end
      endcase
  end
  endmodule
2.3.3 自刷新模块

SDRAM的刷新方式分为两种,自刷新和自动刷新。本文用到的是自动刷新方式。

自动刷新模式可以使SDRAM在正常操作过程中保证数据不丢失,需要外部时钟的参与,刷新行地址由内部刷新计数器控制,无需外部写入。

操作流程为:

(1) 写入预充电命令,A10 设置为高电平对所有 L-Bank 进行预充电

(2) 预充电指令写入后,等待 tRP 时间,此过程中操作命令保持为空操作命令;

(3) tRP 等待时间结束后,写入自动刷新命令

(4) 自动刷新命令写入后,等待 tRC 时间,此过程中操作命令保持为空操作命令;

(5) tRC 等待时间结束后,再次写入自动刷新命令

(6) 自动刷新命令写入后,等待 tRC 时间,此过程中操作命令保持为空操作命令;

(7) tRC 等待时间结束后,自动刷新操作完成。

信号位宽类型功能描述
sys_clk1BitInput系统时钟,频率 100MHz
sys_rst_n1BitInput复位信号,低有效
init_end1BitInput初始化完成标志信号
aref_en1BitInput自动刷新使能信号
aref_req1BitOutput自动刷新请求信号
aref_cmd4BitOutput自动刷新阶段指令信号
aref_ba2BitOutput自动刷新阶段 L-Bank地址
aref_addr13BitOutput自动刷新阶段地址总线
aref_end1BitOutput自动刷新结束标志
代码实现:
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_a_ref
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_a_ref(
  /*-------------------------- Interface definitation ------------------------*/
  input                        sys_clk,
  input                        sys_rst_n,
  input                        init_end,
  input                        aref_en,
  
  output reg                   aref_req,
  output reg [3:0]             aref_cmd,//{cs_n,ras_n,cas_n,we_n}
  output reg [1:0]             aref_ba,
  output reg [12:0]            aref_addr,
  output                       aref_end
  
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter CNT_REF_MAX = 10'd749;//自动刷新等待时钟数(7.5us)
  
  parameter TRP_CLK     = 3'd2;//预充电等待周期
  parameter TRC_CLK     = 3'd7;//自动刷新等待周期
  
  parameter P_CHARGE   = 4'b0010;//预充电指令
  parameter A_REF       = 4'b0001;//自动刷新指令
  parameter NOP         = 4'b0111;//空操作指令
  
  parameter AREF_IDLE   = 3'b000;//初始状态,等待自动刷新使能
  parameter AREF_PCHA   = 3'b001;//预充电状态
  parameter AREF_TRP    = 3'b011;//预充电等待          tRP
  parameter AUTO_REF    = 3'b010;//自动刷新状态
  parameter AREF_TRF    = 3'b100;//自动刷新等待        tRC
  parameter AREF_END    = 3'b101;//自动刷新结束
  
  /*------------- Internal Interface definitation -------------*/
  wire                        trp_end;
  wire                        trc_end;
  wire                        aref_ack;
  
  reg  [9:0]                  cnt_aref;
  reg  [2:0]                  aref_state;
  reg  [2:0]                  cnt_clk;
  reg                         cnt_clk_rst;
  reg  [1:0]                  cnt_aref_aref;
  
  /*--------------------- Main Code --------------------------*/
  //cnt_ref
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_aref <= 1'b0;
      end
    else if(cnt_aref >= CNT_REF_MAX)
      begin
        cnt_aref <= 10'd0;
      end
    else if(init_end == 1'b1)
      begin
        cnt_aref <= cnt_aref +1'b1;
      end
  end
  
  //aref_req
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        aref_req <= 1'b0;
      end
    else if(cnt_aref == (CNT_REF_MAX - 1'b1))
      begin
        aref_req <= 1'b1;
      end
    else if(aref_ack ==1'b1)
      begin
        aref_req <= 1'b0;
      end
  end
  
  //aref_ack
  assign aref_ack = (aref_state == AREF_PCHA)?1'b1:1'b0;
  
  //aref_end
  assign aref_end = (aref_state == AREF_END )?1'b1:1'b0;
  
  //cnt_clk
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_clk <= 3'd0;
      end
    else if(cnt_clk_rst == 1)
      begin
        cnt_clk <= 3'd0;
      end
    else
      begin
        cnt_clk <= cnt_clk +1'b1;
      end
  end
  
  //trp_end,trc_end,tmrd_end
  assign trp_end  = ((aref_state == AREF_TRP ) && (cnt_clk == TRP_CLK ))?1'b1:1'b0;
  assign trc_end  = ((aref_state == AREF_TRF ) && (cnt_clk == TRC_CLK ))?1'b1:1'b0;
//   assign tmrd_end = ((aref_state == AREF_TMRD) && (cnt_clk == TRMD_CLK))?1'b1:1'b0;
  
  //cnt_aref_aref
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_aref_aref <= 2'd0;
      end
    else if(aref_state == AREF_IDLE)
      begin
        cnt_aref_aref <= 2'd0;
      end
    else if(aref_state == AUTO_REF)
      begin
        cnt_aref_aref <= cnt_aref_aref +1'b1;
      end
    else
      begin
        cnt_aref_aref <= cnt_aref_aref;
      end
  end
  
  //sdram
  always@(posedge sys_clk or sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        aref_state <= AREF_IDLE;
      end
    else
      case(aref_state)
          AREF_IDLE:begin
            if((aref_en == 1'b1) && (init_end == 1'b1))
              begin
                aref_state <= AREF_PCHA;
              end
            else
              begin
                aref_state <= aref_state;
              end
          end
          
          AREF_PCHA:begin
            aref_state <= AREF_TRP;
          end
          
          AREF_TRP:begin
            if(trp_end == 1'b1)
              begin
                aref_state <= AUTO_REF;
              end
            else
              begin
                aref_state <= aref_state;
              end
          end
          
          AUTO_REF:begin
            aref_state <= AREF_TRF;
          end
          
          AREF_TRF:begin
            if(trc_end == 1'b1)
              begin
                if(cnt_aref_aref == 2'd2)
                  begin
                    aref_state <= AREF_END;
                  end
                else
                  begin
                    aref_state <= AUTO_REF;
                  end
              end
            else
              begin
                aref_state <= aref_state;
              end
          end
          
          AREF_END:begin
            aref_state <= AREF_IDLE;
          end
          
          default:begin
            aref_state <= AREF_IDLE;
          end
      endcase
  end
  
  //cnt_clk_rst
  always@(*)
  begin
    case(aref_state)
      AREF_IDLE: cnt_clk_rst <= 1'b1;
      AREF_TRP : cnt_clk_rst <= (trp_end == 1'b1)?1'b1:1'b0;
      AREF_TRF : cnt_clk_rst <= (trc_end == 1'b1)?1'b1:1'b0;
      AREF_END : cnt_clk_rst <= 1'b1;
      default  : cnt_clk_rst <= 1'b0;
    endcase
  end
  
  //sdram
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        aref_cmd  <= NOP;
        aref_ba   <= 2'b11;
        aref_addr <= 13'h1fff;
      end
    else
      case(aref_state)
        AREF_IDLE,AREF_TRP,AREF_TRF:begin
          aref_cmd  <= NOP;
          aref_ba   <= 2'b11;
          aref_addr <= 13'h1fff;
        end
        
        AREF_PCHA:begin
          aref_cmd  <= P_CHARGE;
          aref_ba   <= 2'b11;
          aref_addr <= 13'h1fff;
        end
        
        AUTO_REF:begin
          aref_cmd  <= A_REF;
          aref_ba   <= 2'b11;
          aref_addr <= 13'h1fff;
        end
        
        AREF_END:begin
          aref_cmd  <= NOP;
          aref_ba   <= 2'b11;
          aref_addr <= 13'h1fff;
        end
        
        default:begin
          aref_cmd  <= NOP;
          aref_ba   <= 2'b11;
          aref_addr <= 13'h1fff;
        end
      endcase
  end
  
  endmodule
2.3.4 数据读模块

本文采用的是SDRAM中不带自动预充电的页突发读模式。

操作流程:

(1) 发送激活指令到 SDRAM,同时 BA0-BA1、A0-A12 分别写入 L-Bank 地址、行地址,激活特定 L-Bank的特定行;

(2) 激活指令写入后,等待 tRCD 时间,此过程中操作命令保持为空操作命令;

(3) tRCD 等待时间结束后,写入读数据指令同时 A0-A8 写入数据读取首地址

(4) 读数据指令写入后,随机跳转到潜伏状态,等待潜伏期结束,DQ 开始输出读取数据,设读取数据个数为 N

(5) 自读指令写入周期的下个时钟周期开始计数,N 个时钟周期后写入突发停止指令,终止数据读操作;

(6) 突发停止指令写入后,DQ 数据输出数据输出完成后SDRAM 的页突发读操作完成

信号位宽类型功能描述
sys_clk1BitInput系统时钟,频率 100MHz
sys_rst_n1BitInput复位信号,低有效
init_end1BitInput初始化完成信号
rd_en1BitInput数据读使能信号
rd_addr24BitInput数据读阶段地址输入
rd_data16BitInput数据读阶段数据输入
rd_burst_len10BitInput读突发长度
rd_ack1BitOutput数据读操作响应
rd_end1BitOutput一次突发读结束
read_cmd4BitOutput数据读阶段指令
read_ba2BitOutput数据读阶段 L-Bank 地址
read_addr13BitOutput数据读阶段地址输出
rd_sdram_data16BitOutput数据读阶段数据输出
代码实现:
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_read
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_read(
  /*-------------------------- Interface definitation ------------------------*/
  input                       sys_clk,
  input                       sys_rst_n,
  input                       init_end,
  input                       rd_en,
  input [23:0]                rd_addr,
  input [15:0]                rd_data,
  input [9:0]                 rd_burst_len,
  
  output                      rd_ack,
  output                      rd_end,
  output reg [3:0]            read_cmd,
  output reg [1:0]            read_ba,
  output reg [12:0]           read_addr,
  output [15:0]               rd_sdram_data
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter TRCD_CLK   = 10'd2,
            TCL_CLK    = 10'd3,
            TRP_CLK    = 10'd2;
  parameter RD_IDLE    = 4'b0000,
            RD_ACTIVE  = 4'b0001,
            RD_TRCD    = 4'b0011,
            RD_READ    = 4'b0010,
            RD_CL      = 4'b0100,
            RD_DATA    = 4'b0101,
            RD_PRE     = 4'b0111,
            RD_TRP     = 4'b0110,
            RD_END     = 4'b1100;
  parameter NOP        = 4'b0111,
            ACTIVE     = 4'b0011,
            READ       = 4'b0101,
            B_STOP     = 4'b0110,
            P_CHARGE   = 4'b0010;
  /*------------- Internal Interface definitation -------------*/
  wire                    trcd_end;
  wire                    trp_end;
  wire                    tcl_end;
  wire                    tread_end;
  wire                    rdburst_end;
  
  reg [3:0]               read_state;
  reg [9:0]               cnt_clk;
  reg                     cnt_clk_rst;
  reg [15:0]              rd_data_reg;
  /*--------------------- Main Code --------------------------*/
  //rd_data_reg
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        rd_data_reg <= 16'd0;
      end
    else
      begin
        rd_data_reg <= rd_data;
      end
  end
  
  //rd_ack
  assign rd_ack = (read_state == RD_DATA) && (cnt_clk >= 10'd1) && (cnt_clk < rd_burst_len + 2'd1);
  
  //cnt_clk
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        cnt_clk <= 10'd0;
      end
    else if(cnt_clk_rst == 1'b1)
      begin
        cnt_clk <= 10'd0;
      end
    else
      begin
        cnt_clk <= cnt_clk +1'b1;
      end
  end
  
  //trcd_end,   trp_end,   tcl_end,   tread_end,   rdburst_end
  assign trcd_end    = ((read_state == RD_TRCD) && (cnt_clk == TRCD_CLK       ))?1'b1:1'b0;
  assign trp_end     = ((read_state == RD_TRP ) && (cnt_clk == TRP_CLK        ))?1'b1:1'b0;
  assign tcl_end     = ((read_state == RD_CL  ) && (cnt_clk == TCL_CLK - 1    ))?1'b1:1'b0;
  assign tread_end   = ((read_state == RD_DATA) && (cnt_clk == rd_burst_len + 2))?1'b1:1'b0;
  assign rdburst_end = ((read_state == RD_DATA) && (cnt_clk == rd_burst_len - 4))?1'b1:1'b0;
  
  //read_state
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        read_state <= RD_IDLE;
      end
    else
      begin
        case(read_state)
          RD_IDLE:begin
            if((rd_en == 1'b1) && (init_end == 1'b1))begin
              read_state <= RD_ACTIVE;
            end
            else begin
              read_state <= read_state;
            end
          end
          
          RD_ACTIVE:begin
            read_state <= RD_TRCD;
          end
          
          RD_TRCD:begin
            if(trcd_end == 1'b1)begin
              read_state <= RD_READ;
            end
            else begin
              read_state <= RD_TRCD;
            end
          end
          
          RD_READ:begin
            read_state <= RD_CL;
          end
          
          RD_CL:begin
            read_state <= (tcl_end == 1'b1)?RD_DATA:RD_CL;
          end
          
          RD_DATA:begin
            read_state <= (tread_end == 1'b1)?RD_PRE : RD_DATA;
          end
          
          RD_PRE:begin
            read_state <= RD_TRP;
          end
          
          RD_TRP:begin
            read_state <= (trp_end)?RD_END:RD_TRP;
          end
          
          RD_END:begin
            read_state <= RD_IDLE;
          end
          
          default:begin
            read_state <= RD_IDLE;
          end
        endcase
      end
  end
  
  //
  always@(*)
  begin
    case(read_state)
      RD_IDLE: cnt_clk_rst <= 1'b1;
      RD_TRCD: cnt_clk_rst <= (trcd_end == 1'b1)?1'b1:1'b0;
      RD_READ: cnt_clk_rst <= 1'b1;
      RD_CL  : cnt_clk_rst <= (tcl_end == 1'b1)?1'b1:1'b0;
      RD_DATA: cnt_clk_rst <= (tread_end == 1'b1)?1'b1:1'b0;
      RD_TRP : cnt_clk_rst <= (trp_end == 1'b1)?1'b1:1'b0;
      RD_END : cnt_clk_rst <= 1'b1;
      default: cnt_clk_rst <= 1'b0;
    endcase
  end
  
  //sdram
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        read_cmd  <= NOP;
        read_ba   <= 2'b11;
        read_addr <= 13'h1fff;
      end
    else
      begin
        case(read_state)
        RD_IDLE,RD_TRCD,RD_TRP:begin
          read_cmd  <= NOP;
          read_ba   <= 2'b11;
          read_addr <= 13'h1fff;
        end
        
        RD_ACTIVE:begin
          read_cmd  <= ACTIVE;
          read_ba   <= rd_addr[23:22];
          read_addr <= rd_addr[21:9];
        end
        
        RD_READ:begin
          read_cmd  <= READ;
          read_ba   <= rd_addr[23:22];
          read_addr <= {4'b0000,rd_addr[8:0]};
        end
        
        RD_DATA:begin
          if(rdburst_end == 1'b1)begin
            read_cmd <= B_STOP;
          end
          else begin
            read_cmd  <= NOP;
            read_ba   <= 2'b11;
            read_addr <= 13'h1fff;
          end
        end
        
        RD_PRE:begin
          read_cmd  <= P_CHARGE;
          read_ba   <= rd_addr[23:22];
          read_addr <= 13'h0400;
        end
        
        RD_END:begin
          read_cmd  <= NOP;
          read_ba   <= 2'b11;
          read_addr <= 13'h1fff;
        end
        default:begin
          read_cmd  <= NOP;
          read_ba   <= 2'b11;
          read_addr <= 13'h1fff;
        end
        endcase
      end
  end
  
  //
  assign rd_sdram_data = (rd_ack == 1'b1) ? rd_data_reg :16'b0;
  
  endmodule
2.3.5 数据写模块

本文采用的是SDRAM的不进行自动预充电的页突发写模式,并针对这一模式进行讲解

操作流程:

(1) 发送激活指令到 SDRAM,同时 BA0-BA1A0-A12 分别写入 L-Bank 地址、行地址,激活特定 L-Bank的特定行;

(2) 激活指令写入后,等待 tRCD 时间,此过程中操作命令保持为空操作命令;

(3) tRCD 等待时间结束后,写入写数据指令,同时 A0-A8 写入待数据的列首地址

(4) 读数据指令写入同时,由 DQ 开始写入数据,在最后一个数据写入后的下一个时钟写入突发终止指令;

(5) 突发终止指令写入后, SDRAM的页突发写操作完成

信号位宽类型功能描述
sys_clk1BitInput系统时钟,频率 100MHz
sys_rst_n1BitInput复位信号,低有效
init_end1BitInput初始化完成信号
wr_en1BitInput数据写使能信号
wr_addr24BitInput数据写阶段地址输入
wr_data16BitInput数据写阶段数据输入
wr_burst_len10BitInput写突发长度
wr_ack1BitOutput数据写操作响应
wr_end1BitOutput一次突发写结束
write_cmd4BitOutput数据写阶段指令
write_ba2BitOutput数据写阶段逻辑 Bank地址
write_addr13BitOutput数据写阶段地址输出
wr_sdram_en1BitOutput数据写阶段数据输出使能
wr_sdram_data16BitOutput数据写阶段数据输出
代码实现:
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_write
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_write(
  /*-------------------------- Interface definitation ------------------------*/
  input                        sys_clk,
  input                        sys_rst_n,
  input                        init_end,
  input                        wr_en,
  input [23:0]                 wr_addr,
  input [15:0]                 wr_data,
  input [9:0]                  wr_burst_len,
  
  output                       wr_ack,
  output                       wr_end,
  output reg [3:0]             write_cmd,
  output reg [1:0]             write_ba,
  output reg [12:0]            write_addr,
  output reg                   wr_sdram_en,
  output [15:0]                wr_sdram_data
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter TRCD_CLK   = 10'd2;
  parameter TRP_CLK    = 10'd2;
  
  parameter WR_IDLE    = 4'b0000;
  parameter WR_ACTIVE  = 4'b0001;
  parameter WR_TRCD    = 4'b0011;
  parameter WR_WRITE   = 4'b0010;
  parameter WR_DATA    = 4'b0100;
  parameter WR_PRE     = 4'b0101;
  parameter WR_TRP     = 4'b0111;
  parameter WR_END     = 4'b0110;
  
  parameter NOP        = 4'b0111;
  parameter ACTIVE     = 4'b0011;
  parameter WRITE      = 4'b0100;
  parameter B_STOP     = 4'b0110;
  parameter P_CHARGE   = 4'b0010;
  /*------------- Internal Interface definitation -------------*/
  wire                    trcd_end;
  wire                    twrite_end;
  wire                    trp_end;
  
  reg [3:0]               write_state;
  reg [9:0]               cnt_clk;
  reg                     cnt_clk_rst;
  /*--------------------- Main Code --------------------------*/
  //wr_end,wr_ack
  assign wr_end = (write_state == WR_END)?1'b1:1'b0;
  assign wr_ack = (write_state == WR_WRITE)||((write_state == WR_DATA) && (cnt_clk <= (wr_burst_len - 2'd2)));
  
  //cnt_clk
  always@(posedge sys_clk or sys_rst_n)
  begin
    if(sys_rst_n == 0)
      begin
        cnt_clk <= 10'd0;
      end
    else if(cnt_clk_rst == 1'b1)
      begin
        cnt_clk <= 10'd0;
      end
    else
      begin
        cnt_clk <= cnt_clk + 1'b1;
      end
  end
  
  //trcd_end,twrite_end,trp_end
  assign trcd_end   = ((write_state == WR_TRCD) && (cnt_clk == TRCD_CLK       ))?1'b1:1'b0;
  assign twrite_end = ((write_state == WR_DATA) && (cnt_clk == wr_burst_len -1))?1'b1:1'b0;
  assign trp_end    = ((write_state == WR_TRP ) && (cnt_clk == TRP_CLK        ))?1'b1:1'b0;
  
  //write_state
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        write_state <= WR_IDLE;
      end
    else
      begin
        case(write_state)
          WR_IDLE:begin
            if((wr_en == 1'b1) && (init_end == 1'b1))begin
              write_state <= WR_ACTIVE;
            end
            else begin
              write_state <= write_state;
            end
          end
          
          WR_ACTIVE:begin
            write_state <= WR_TRCD;
          end
          
          WR_TRCD:begin
            if(trcd_end == 1'b1)begin
              write_state <= WR_WRITE;
            end
            else begin
              write_state <= write_state;
            end
          end
          
          WR_WRITE:begin
            write_state <= WR_DATA;
          end
          
          WR_DATA:begin
            if(twrite_end == 1'b1)begin
              write_state <= WR_PRE;
            end
            else begin
              write_state <= write_state;
            end
          end
          
          WR_PRE:begin
            write_state <= WR_TRP;
          end
          
          WR_TRP:begin
            if(trp_end == 1'b1)begin
              write_state <= WR_END;
            end
            else begin
              write_state <= write_state;
            end
          end
          
          WR_END:begin
            write_state <= WR_IDLE;
          end
          
          default:begin
            write_state <= WR_IDLE;
          end
        endcase
      end
  end
  
  //
  always@(*)
  begin
    case(write_state)
      WR_IDLE  : cnt_clk_rst <= 1'b1;
      WR_TRCD  : cnt_clk_rst <= (trcd_end == 1'b1  ) ? 1'b1 : 1'b0;
      WR_WRITE : cnt_clk_rst <= 1'b1;
      WR_DATA  : cnt_clk_rst <= (twrite_end == 1'b1) ? 1'b1 : 1'b0;
      WR_TRP   : cnt_clk_rst <= (trp_end == 1'b1   ) ? 1'b1 : 1'b0;
      WR_END   : cnt_clk_rst <= 1'b1;
      default  : cnt_clk_rst <= 1'b0;
    endcase
  end
  
  //
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        write_cmd  <= NOP;
        write_ba   <= 2'b11;
        write_addr <= 13'h1fff;
      end
    else
      begin
        case(write_state)
          WR_IDLE,WR_TRCD,WR_TRP:begin
            write_cmd  <= NOP;
            write_ba   <= 2'b11;
            write_addr <= 13'h1fff;
          end
          
          WR_ACTIVE:begin
            write_cmd  <= ACTIVE;
            write_ba   <= wr_addr[23:22];
            write_addr <= wr_addr[21:9];
          end
          
          WR_WRITE:begin
            write_cmd  <= WRITE;
            write_ba   <= wr_addr[23:22];
            write_addr <= {4'b0000,wr_addr[8:0]};
          end
          
          WR_DATA:begin
            if(twrite_end == 1'b1)begin
              write_cmd <= B_STOP;
            end
            else begin
              write_cmd  <= NOP;
              write_ba   <= 2'b11;
              write_addr <= 13'h1fff;
            end
          end
          
          WR_PRE:begin
            write_cmd  <= P_CHARGE;
            write_ba   <= wr_addr[23:22];
            write_addr <= 13'h0400;
          end
          
          WR_END:begin
            write_cmd  <= NOP;
            write_ba   <= 2'b11;
            write_addr <= 13'h1fff;
          end
          
          default:begin
            write_cmd  <= NOP;
            write_ba   <= 2'b11;
            write_addr <= 13'h1ffff;
          end
        endcase
      end
  end
  
  //wr_sdram_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        wr_sdram_en <= 1'b0;
      end
    else
      begin
        wr_sdram_en <= wr_ack;
      end
  end
  
  //wr_sdram_data
  assign wr_sdram_data = (wr_sdram_en == 1'b1) ? wr_data :16'd0;
  
  endmodule
2.3.6 仲裁模块

涉及四种操作方式,初始化操作、自动刷新操作、数据读操作、数据写操作,其中初始化操作的优先级最高。

设置初始状态为IDLE状态(初始化模式),初始化完成后,跳转到仲裁状态,设置优先级为:

自动刷新操作>数据写操作>数据读操作

信号位宽类型功能描述
sys_clk1BitInput系统时钟,频率 100MHz
sys_rst_n1BitInput复位信号,低有效
init_cmd4BitInput初始化阶段指令
init_ba2BitInput初始化阶段逻辑 Bank地址
init_addr13BitInput初始化阶段地址总线
init_end1BitInput初始化完成信号
aref_req1BitInput自动刷新请求信号
aref_cmd4BitInput自动刷新阶段指令
aref_ba2BitInput自动刷新阶段逻辑 Bank 地址
aref_addr13BitInput自动刷新阶段地址总线
aref_end1BitInput自动刷新结束信号
wr_req1BitInput数据写请求信号
wr_cmd4BitInput数据写阶段指令
wr_ba2BitInput数据写阶段逻辑 Bank地址
wr_addr13BitInput数据写阶段地址总线
wr_sdram_en1BitInputSDRAM数据写入有效信号
wr_data16BitInputSDRAM待写入数据
wr_end1BitInput数据写结束信号
rd_req1BitInput数据读请求信号
rd_cmd4BitInput数据读阶段指令
rd_ba2BitInput数据读阶段逻辑 Bank地址
rd_addr13BitInput数据读阶段地址总线
rd_end1BitInput数据读结束信号
aref_en1BitOutput自动刷新使能
wr_en1BitOutput数据写使能
rd_en1BitOutput数据读使能
sdram_cke1BitOutputSDRAM时钟使能信号
sdram_cs_n1BitOutputSDRAM片选信号
sdram_cas_n1BitOutputSDRAM列选通信号
sdram_ras_n1BitOutputSDRAM行选通信号
sdram_we_n1BitOutputSDRAM写使能信号
sdram_ba2BitOutputSDRAM逻辑 Bank地址
sdram_addr13BitOutputSDRAM地址总线
sdram_dq16BitOutputSDRAM数据总线

代码实现:
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
--  Module      :  sdram_arbit
--  Description :  
------------------------------------------------------------------------------*/
  module sdram_arbit(
  /*-------------------------- Interface definitation ------------------------*/
  input                       sys_clk,
  input                       sys_rst_n,
  //sdram_init
  input [3:0]                 init_cmd,
  input                       init_end,
  input [1:0]                 init_ba,
  input [12:0]                init_addr,
  //sdram_aref
  input                       aref_req,
  input                       aref_end,
  input [3:0]                 aref_cmd,
  input [1:0]                 aref_ba,
  input [12:0]                aref_addr,
  //sdram_write
  input                       wr_req,
  input [1:0]                 wr_ba,
  input [15:0]                wr_data,
  input                       wr_end,
  input [3:0]                 wr_cmd,
  input [12:0]                wr_addr,
  input                       wr_sdram_en,
  //sdram_read
  input                       rd_req,
  input                       rd_end,
  input [3:0]                 rd_cmd,
  input [12:0]                rd_addr,
  input [1:0]                 rd_ba,
  
  output reg                  aref_en,
  output reg                  wr_en,
  output reg                  rd_en,
  
  output                      sdram_cke,
  output                      sdram_cs_n,
  output                      sdram_ras_n,
  output                      sdram_we_n,
  output reg [1:0]            sdram_ba,
  output reg [12:0]           sdram_addr,
  inout  [15:0]               sdram_dq
  /*-----------------------------------------------------------*/
  );
  
  /*------------- Parameter and Internal Signal ---------------*/
  parameter IDLE    = 5'b0_0001,
            ARBIT   = 5'b0_0010,
            AREF    = 5'b0_0100,
            WRITE   = 5'b0_1000,
            READ    = 5'b1_0000;
  parameter CMD_NOP = 4'b0111;
  /*------------- Internal Interface definitation -------------*/
  reg [3:0]              sdram_cmd;
  reg [4:0]              state;
  /*--------------------- Main Code --------------------------*/
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        state <= IDLE;
      end
    else 
      begin
        case(state)
          IDLE:begin
            if(init_end == 1'b1)begin
              state <= ARBIT;
            end
            else begin
              state <= IDLE;
            end
          end
          
          ARBIT:begin
            if(aref_req == 1'b1)begin
              state <= AREF;
            end
            else if(wr_req == 1'b1)begin
              state <= WRITE;
            end
            else if(rd_req == 1'b1)begin
              state <= READ;
            end
            else begin
              state <= ARBIT;
            end
          end
          
          AREF: begin
            if(aref_end == 1'b1)begin
              state <= ARBIT;
            end
            else begin
              state <= AREF;
            end
          end
          
          WRITE :begin
            if(wr_end == 1'b1)begin
              state <= ARBIT;
            end
            else begin
              state <= WRITE;
            end
          end
          
          READ:begin
            if(rd_end == 1'b1)begin
              state <= ARBIT;
            end
            else begin
              state <= READ;
            end
          end
          
          default:begin
            state <= IDLE;
          end
        endcase
      end
  end
  //aref_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        aref_en <= 1'b0;
      end
    else if((state == ARBIT) && (aref_req == 1'b1))
      begin
        aref_en <= 1'b1;
      end
    else if(aref_end == 1'b1)
      begin
        aref_en <= 1'b0;
      end
  end
  
  //wr_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        wr_en <= 1'b0;
      end
    else if((state == ARBIT) && (aref_req == 1'b0) && (wr_req == 1'b1))
      begin
        wr_en <= 1'b1;
      end
    else if(wr_end == 1'b1)
      begin
        wr_en <= 1'b0;
      end
  end
  
  //rd_en
  always@(posedge sys_clk or negedge sys_rst_n)
  begin
    if(sys_rst_n == 1'b0)
      begin
        rd_en <= 1'b0;
      end
    else if((state == ARBIT) && (aref_req == 1'b0) && (rd_req == 1'b1))
      begin
        rd_en <= 1'b1;
      end
    else if(rd_end == 1'b1)
      begin
        rd_en <= 1'b0;
      end
  end
  
  //sdram_cmd
  always@(*)
  begin
    case(state)
      IDLE: begin
        sdram_cmd  <= init_cmd;
        sdram_ba   <= init_ba;
        sdram_addr <= init_addr;
      end
      
      AREF: begin
        sdram_cmd  <= aref_cmd;
        sdram_ba   <= aref_ba;
        sdram_addr <= aref_addr;
      end
      
      WRITE: begin
        sdram_cmd  <= wr_cmd;
        sdram_ba   <= wr_ba;
        sdram_addr <= wr_addr;
      end
      
      READ: begin
        sdram_cmd  <= rd_cmd;
        sdram_ba   <= rd_ba;
        sdram_addr <= rd_addr;
      end
      
      default: begin
        sdram_cmd  <= CMD_NOP;
        sdram_ba   <= 2'b11;
        sdram_addr <= 13'h1fff;
      end
    endcase
  end
  
  //sdram cke,dq,cmd
  assign sdram_cke = 1'b1;
  assign sdram_dq  = (wr_sdram_en == 1'b1) ? wr_data : 16'bz;
  assign {sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd;
  
  endmodule

  • 41
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炎龙铠甲会合体

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值