目录
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 引脚功能相同。
引脚功能描述
引脚 | 位宽 | 类型 | 功能描述 |
---|---|---|---|
CLK | 1Bit | Input | 系统时钟:SDRAM 由系统时钟驱动,所有 SDRAM 输入信号都在时钟上升沿采样, |
CKE | 1Bit | Input | 时钟使能;高电平有效 |
CS#(CS_N) | 1Bit | Input | 片选信号;低电平有效。为高电平时,屏蔽所有命令,但已经突发的读/写操作不受影响。 |
CAS#(CAS_N) | 1Bit | Input | 列选通信号;低电平有效。为低电平时,A[8:0]输入的为列地址。 |
RAS#(RAS_N) | 1Bit | Input | 行选通信号;低电平有效。为低电平时,A[12:0]输入的为行地址 |
WE#(WE_N) | 1Bit | Input | 写使能信号;低电平有效。为低电平时,使能写操作和预充电。 |
DQM[1:0] | 1Bit | Input | 数据掩码;DQML(H),低(高)字节掩码,若信号为高电平,在下一个时钟周期的时钟上升沿,数据总线的低(高)字节为高阻态。 |
BA[1:0] | 2Bit | Input | L-Bank 地址;选择不同的逻辑 Bank 进行相关数据操作 |
A[12:0] | 13Bit | Input | 地址总线; |
DQ[15:0] | 16Bit | Input | 数据总线; |
{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_top | SDRAM控制器,实现 SDRAM数据读写 |
fifo_read | SDRAM读出数据缓存 |
uart_tx | 串口数据发送模块,发送读出 SDRAM的数据 |
模块名称 | 功能描述 |
---|---|
sdram_top | SDRAM控制器顶层模块 |
fifo_ctrl | FIFO 控制模块,实现 SDRAM读写数据的跨时钟域处理 |
sdram_ctrl | 实现 SDRAM的初始化、自动刷新和数据读写操作 |
模块名称 | 功能描述 |
---|---|
sdram_ctrl | SDRAM控制部分顶层模块 |
sdram_init | SDRAM初始化模块,实现 SDRAM 的初始化 |
sdram_aref | SDRAM自动刷新模块,实现 SDRAM的自动刷新操作 |
sdram_write | SDRAM数据写模块,实现 SDRAM 的数据写入 |
sdram_read | SDRAM数据读模块,实现 SDRAM 的数据读出 |
sdram_arbit | SDRAM仲裁模块,实现各操作优先级的仲裁 |
个人喜欢从外层往里层写代码,而野火相反。在做仿真的时候,我是喜欢从外往里面仿真,一点一点进行验证。读者可以自己挑选喜欢的方式。
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_clk | 1Bit | Input | 系统时钟,频率 100MHz |
sys_rst_n | 1Bit | Input | 复位信号,低有效 |
init_cmd | 4Bit | Output | 输出初始化阶段指令信号 |
init_ba | 2Bit | Output | 初始化阶段 L-Bank 地址 |
init_addr | 13Bit | Output | 初始化阶段地址总线,辅助预充电操作和模式寄存器的配置 |
init_end | 1Bit | Output | 初始化结束标志,标志着初始化完成,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_clk | 1Bit | Input | 系统时钟,频率 100MHz |
sys_rst_n | 1Bit | Input | 复位信号,低有效 |
init_end | 1Bit | Input | 初始化完成标志信号 |
aref_en | 1Bit | Input | 自动刷新使能信号 |
aref_req | 1Bit | Output | 自动刷新请求信号 |
aref_cmd | 4Bit | Output | 自动刷新阶段指令信号 |
aref_ba | 2Bit | Output | 自动刷新阶段 L-Bank地址 |
aref_addr | 13Bit | Output | 自动刷新阶段地址总线 |
aref_end | 1Bit | Output | 自动刷新结束标志 |
代码实现:
`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_clk | 1Bit | Input | 系统时钟,频率 100MHz |
sys_rst_n | 1Bit | Input | 复位信号,低有效 |
init_end | 1Bit | Input | 初始化完成信号 |
rd_en | 1Bit | Input | 数据读使能信号 |
rd_addr | 24Bit | Input | 数据读阶段地址输入 |
rd_data | 16Bit | Input | 数据读阶段数据输入 |
rd_burst_len | 10Bit | Input | 读突发长度 |
rd_ack | 1Bit | Output | 数据读操作响应 |
rd_end | 1Bit | Output | 一次突发读结束 |
read_cmd | 4Bit | Output | 数据读阶段指令 |
read_ba | 2Bit | Output | 数据读阶段 L-Bank 地址 |
read_addr | 13Bit | Output | 数据读阶段地址输出 |
rd_sdram_data | 16Bit | Output | 数据读阶段数据输出 |
代码实现:
`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-BA1、A0-A12 分别写入 L-Bank 地址、行地址,激活特定 L-Bank的特定行;
(2) 激活指令写入后,等待 tRCD 时间,此过程中操作命令保持为空操作命令;
(3) tRCD 等待时间结束后,写入写数据指令,同时 A0-A8 写入待数据的列首地址;
(4) 读数据指令写入同时,由 DQ 开始写入数据,在最后一个数据写入后的下一个时钟写入突发终止指令;
(5) 突发终止指令写入后, SDRAM的页突发写操作完成。
信号 | 位宽 | 类型 | 功能描述 |
---|---|---|---|
sys_clk | 1Bit | Input | 系统时钟,频率 100MHz |
sys_rst_n | 1Bit | Input | 复位信号,低有效 |
init_end | 1Bit | Input | 初始化完成信号 |
wr_en | 1Bit | Input | 数据写使能信号 |
wr_addr | 24Bit | Input | 数据写阶段地址输入 |
wr_data | 16Bit | Input | 数据写阶段数据输入 |
wr_burst_len | 10Bit | Input | 写突发长度 |
wr_ack | 1Bit | Output | 数据写操作响应 |
wr_end | 1Bit | Output | 一次突发写结束 |
write_cmd | 4Bit | Output | 数据写阶段指令 |
write_ba | 2Bit | Output | 数据写阶段逻辑 Bank地址 |
write_addr | 13Bit | Output | 数据写阶段地址输出 |
wr_sdram_en | 1Bit | Output | 数据写阶段数据输出使能 |
wr_sdram_data | 16Bit | Output | 数据写阶段数据输出 |
代码实现:
`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_clk | 1Bit | Input | 系统时钟,频率 100MHz |
sys_rst_n | 1Bit | Input | 复位信号,低有效 |
init_cmd | 4Bit | Input | 初始化阶段指令 |
init_ba | 2Bit | Input | 初始化阶段逻辑 Bank地址 |
init_addr | 13Bit | Input | 初始化阶段地址总线 |
init_end | 1Bit | Input | 初始化完成信号 |
aref_req | 1Bit | Input | 自动刷新请求信号 |
aref_cmd | 4Bit | Input | 自动刷新阶段指令 |
aref_ba | 2Bit | Input | 自动刷新阶段逻辑 Bank 地址 |
aref_addr | 13Bit | Input | 自动刷新阶段地址总线 |
aref_end | 1Bit | Input | 自动刷新结束信号 |
wr_req | 1Bit | Input | 数据写请求信号 |
wr_cmd | 4Bit | Input | 数据写阶段指令 |
wr_ba | 2Bit | Input | 数据写阶段逻辑 Bank地址 |
wr_addr | 13Bit | Input | 数据写阶段地址总线 |
wr_sdram_en | 1Bit | Input | SDRAM数据写入有效信号 |
wr_data | 16Bit | Input | SDRAM待写入数据 |
wr_end | 1Bit | Input | 数据写结束信号 |
rd_req | 1Bit | Input | 数据读请求信号 |
rd_cmd | 4Bit | Input | 数据读阶段指令 |
rd_ba | 2Bit | Input | 数据读阶段逻辑 Bank地址 |
rd_addr | 13Bit | Input | 数据读阶段地址总线 |
rd_end | 1Bit | Input | 数据读结束信号 |
aref_en | 1Bit | Output | 自动刷新使能 |
wr_en | 1Bit | Output | 数据写使能 |
rd_en | 1Bit | Output | 数据读使能 |
sdram_cke | 1Bit | Output | SDRAM时钟使能信号 |
sdram_cs_n | 1Bit | Output | SDRAM片选信号 |
sdram_cas_n | 1Bit | Output | SDRAM列选通信号 |
sdram_ras_n | 1Bit | Output | SDRAM行选通信号 |
sdram_we_n | 1Bit | Output | SDRAM写使能信号 |
sdram_ba | 2Bit | Output | SDRAM逻辑 Bank地址 |
sdram_addr | 13Bit | Output | SDRAM地址总线 |
sdram_dq | 16Bit | Output | SDRAM数据总线 |
代码实现:
`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