第50讲:SDRAM读写控制器的设计与验证
理论部分
SDRAM全称“Synchronous Dynamic Random Access Memory”,译为“同步动态随机存取内存”或“同步动态随机存储器”,是动态随机存储器(Dynamic Random Access Memory,DRAM)家族的一份子。
设计与实现
1. sdram_ctrl
sdram_init
`timescale 1ns/1ns module sdram_init ( input wire sys_clk , //系统时钟,频率100MHz input wire sys_rst_n , //复位信号,低电平有效 output reg [3:0] init_cmd , //初始化阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n} output reg [1:0] init_ba , //初始化阶段Bank地址 output reg [12:0] init_addr , //初始化阶段地址数据,辅助预充电操作 //和配置模式寄存器操作,A12-A0,共13位 output wire init_end //初始化结束信号 ); // parameter define parameter T_POWER = 15'd20_000 ; //上电后等待时钟数(200us) //SDRAM初始化用到的控制信号命令 parameter P_CHARGE = 4'b0010 , //预充电指令 AUTO_REF = 4'b0001 , //自动刷新指令 NOP = 4'b0111 , //空操作指令 M_REG_SET = 4'b0000 ; //模式寄存器设置指令 //SDRAM初始化过程各个状态 parameter INIT_IDLE = 3'b000 , //初始状态 INIT_PRE = 3'b001 , //预充电状态 INIT_TRP = 3'b011 , //预充电等待 tRP INIT_AR = 3'b010 , //自动刷新 INIT_TRF = 3'b100 , //自动刷新等待 tRC INIT_MRS = 3'b101 , //模式寄存器设置 INIT_TMRD = 3'b111 , //模式寄存器设置等待 tMRD INIT_END = 3'b110 ; //初始化完成 parameter TRP_CLK = 3'd2 , //预充电等待周期,20ns TRC_CLK = 3'd7 , //自动刷新等待,70ns TMRD_CLK = 3'd3 ; //模式寄存器设置等待周期,30ns // wire define wire wait_end ; //上电后200us等待结束标志 wire trp_end ; //预充电等待结束标志 wire trc_end ; //自动刷新等待结束标志 wire tmrd_end ; //模式寄存器设置等待结束标志 // reg define 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:SDRAM上电后200us稳定期计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_200us <= 15'd0; else if(cnt_200us == T_POWER) cnt_200us <= T_POWER; else cnt_200us <= cnt_200us + 1'b1; //wait_end:上电后200us等待结束标志 assign wait_end = (cnt_200us == (T_POWER - 1'b1)) ? 1'b1 : 1'b0; //init_end:SDRAM初始化完毕信号 assign init_end = (init_state == INIT_END) ? 1'b1 : 1'b0; //cnt_clk:时钟周期计数,记录初始化各状态等待时间 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_clk <= 3'd0; else if(cnt_clk_rst == 1'b1) cnt_clk <= 3'd0; else cnt_clk <= cnt_clk + 1'b1; //cnt_init_aref:初始化过程自动刷新次数计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_init_aref <= 4'd0; else if(init_state == INIT_IDLE) cnt_init_aref <= 4'd0; else if(init_state == INIT_AR) cnt_init_aref <= cnt_init_aref + 1'b1; else cnt_init_aref <= cnt_init_aref; //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) if(sys_rst_n == 1'b0) init_state <= INIT_IDLE; else case(init_state) INIT_IDLE: //系统上电后,在初始状态等待200us跳转到预充电状态 if(wait_end == 1'b1) init_state <= INIT_PRE; else init_state <= init_state; INIT_PRE: //预充电状态,直接跳转到预充电等待状态 init_state <= INIT_TRP; INIT_TRP: //预充电等待状态,等待结束,跳转到自动刷新状态 if(trp_end == 1'b1) init_state <= INIT_AR; else init_state <= init_state; INIT_AR : //自动刷新状态,直接跳转到自动刷新等待状态 init_state <= INIT_TRF; INIT_TRF: //自动刷新等待状态,等待结束,自动跳转到模式寄存器设置状态 if(trc_end == 1'b1) if(cnt_init_aref == 4'd8) init_state <= INIT_MRS; else init_state <= INIT_AR; else init_state <= init_state; INIT_MRS: //模式寄存器设置状态,直接跳转到模式寄存器设置等待状态 init_state <= INIT_TMRD; INIT_TMRD: //模式寄存器设置等待状态,等待结束,跳到初始化完成状态 if(tmrd_end == 1'b1) init_state <= INIT_END; else init_state <= init_state; INIT_END: //初始化完成状态,保持此状态 init_state <= INIT_END; default: init_state <= INIT_IDLE; endcase //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) if(sys_rst_n == 1'b0) 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:读写方式,0:突发读&突发写,1:突发读&单写 2'b00, //{A8,A7}=00:标准模式,默认 3'b011, //{A6,A5,A4}=011:CAS潜伏期,010:2,011:3,其他:保留 1'b0, //A3=0:突发传输方式,0:顺序,1:隔行 3'b111 //{A2,A1,A0}=111:突发长度,000:单字节,001:2字节 //010:4字节,011:8字节,111:整页,其他:保留 }; end INIT_END: //SDRAM初始化完成 begin 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 endmodule
sdram_a_ref
`timescale 1ns/1ns module sdram_a_ref ( input wire sys_clk , //系统时钟,频率100MHz input wire sys_rst_n , //复位信号,低电平有效 input wire init_end , //初始化结束信号 input wire aref_en , //自动刷新使能 output reg aref_req , //自动刷新请求 output reg [3:0] aref_cmd , //自动刷新阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n} output reg [1:0] aref_ba , //自动刷新阶段Bank地址 output reg [12:0] aref_addr , //地址数据,辅助预充电操作,A12-A0,13位地址 output wire aref_end //自动刷新结束标志 ); //parameter define parameter CNT_REF_MAX = 10'd749 ; //自动刷新等待时钟数(7.5us) parameter TRP_CLK = 3'd2 , //预充电等待周期 TRC_CLK = 3'd7 ; //自动刷新等待周期 parameter P_CHARGE = 4'b0010 , //预充电指令 A_REF = 4'b0001 , //自动刷新指令 NOP = 4'b0111 ; //空操作指令 parameter AREF_IDLE = 3'b000 , //初始状态,等待自动刷新使能 AREF_PCHA = 3'b001 , //预充电状态 AREF_TRP = 3'b011 , //预充电等待 tRP AUTO_REF = 3'b010 , //自动刷新状态 AREF_TRF = 3'b100 , //自动刷新等待 tRC AREF_END = 3'b101 ; //自动刷新结束 //wire define wire trp_end ; //预充电等待结束标志 wire trc_end ; //自动刷新等待结束标志 wire aref_ack ; //自动刷新应答信号 //reg define reg [9:0] cnt_aref ; //自动刷新计数器 reg [2:0] aref_state ; //SDRAM自动刷新状态 reg [2:0] cnt_clk ; //时钟周期计数,记录自刷新阶段各状态等待时间 reg cnt_clk_rst ; //时钟周期计数复位标志 reg [1:0] cnt_aref_aref ; //自动刷新次数计数器 //cnt_ref:刷新计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_aref <= 10'd0; else if(cnt_aref >= CNT_REF_MAX) cnt_aref <= 10'd0; else if(init_end == 1'b1) cnt_aref <= cnt_aref + 1'b1; else cnt_aref <= cnt_aref; //aref_req:自动刷新请求 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) aref_req <= 1'b0; else if(cnt_aref == CNT_REF_MAX - 1'b1) aref_req <= 1'b1; else if(aref_ack == 1'b1) aref_req <= 1'b0; //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) if(sys_rst_n == 1'b0) cnt_clk <= 3'd0; else if(cnt_clk_rst == 1'b1) cnt_clk <= 3'd0; else cnt_clk <= cnt_clk + 1'b1; //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; //cnt_aref_aref:初始化过程自动刷新次数计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_aref_aref <= 2'd0; else if(aref_state == AREF_IDLE) cnt_aref_aref <= 2'd0; else if(aref_state == AUTO_REF) cnt_aref_aref <= cnt_aref_aref + 1'b1; else cnt_aref_aref <= cnt_aref_aref; //SDRAM自动刷新状态机 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) aref_state <= AREF_IDLE; else case(aref_state) AREF_IDLE: if((aref_en == 1'b1) && (init_end == 1'b1)) aref_state <= AREF_PCHA; else aref_state <= aref_state; AREF_PCHA: aref_state <= AREF_TRP; AREF_TRP: if(trp_end == 1'b1) aref_state <= AUTO_REF; else aref_state <= aref_state; AUTO_REF: aref_state <= AREF_TRF; AREF_TRF: if(trc_end == 1'b1) if(cnt_aref_aref == 2'd2) aref_state <= AREF_END; else aref_state <= AUTO_REF; else aref_state <= aref_state; AREF_END: aref_state <= AREF_IDLE; default: aref_state <= AREF_IDLE; endcase //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) if(sys_rst_n == 1'b0) 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 endmodule
sdram_write
`timescale 1ns/1ns module sdram_write ( input wire sys_clk , //系统时钟,频率100MHz input wire sys_rst_n , //复位信号,低电平有效 input wire init_end , //初始化结束信号 input wire wr_en , //写使能 input wire [23:0] wr_addr , //写SDRAM地址 input wire [15:0] wr_data , //待写入SDRAM的数据(写FIFO传入) input wire [9:0] wr_burst_len , //写突发SDRAM字节数 output wire wr_ack , //写SDRAM响应信号 output wire wr_end , //一次突发写结束 output reg [3:0] write_cmd , //写数据阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n} output reg [1:0] write_ba , //写数据阶段Bank地址 output reg [12:0] write_addr , //地址数据,辅助预充电操作,行、列地址,A12-A0,13位地址 output reg wr_sdram_en , //数据总线输出使能 output wire [15:0] wr_sdram_data //写入SDRAM的数据 ); //parameter define parameter TRCD_CLK = 10'd2 , //激活周期 TRP_CLK = 10'd2 ; //预充电周期 parameter WR_IDLE = 4'b0000 , //初始状态 WR_ACTIVE = 4'b0001 , //激活 WR_TRCD = 4'b0011 , //激活等待 WR_WRITE = 4'b0010 , //写操作 WR_DATA = 4'b0100 , //写数据 WR_PRE = 4'b0101 , //预充电 WR_TRP = 4'b0111 , //预充电等待 WR_END = 4'b0110 ; //一次突发写结束 parameter NOP = 4'b0111 , //空操作指令 ACTIVE = 4'b0011 , //激活指令 WRITE = 4'b0100 , //数据写指令 B_STOP = 4'b0110 , //突发停止指令 P_CHARGE = 4'b0010 ; //预充电指令 //wire define wire trcd_end ; //激活等待周期结束 wire twrite_end ; //突发写结束 wire trp_end ; //预充电有效周期结束 //reg define reg [3:0] write_state ; //SDRAM写状态 reg [9:0] cnt_clk ; //时钟周期计数,记录写数据阶段各状态等待时间 reg cnt_clk_rst ; //时钟周期计数复位标志 //wr_end:一次突发写结束 assign wr_end = (write_state == WR_END) ? 1'b1 : 1'b0; //wr_ack:写SDRAM响应信号 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 negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_clk <= 10'd0; else if(cnt_clk_rst == 1'b1) cnt_clk <= 10'd0; else cnt_clk <= cnt_clk + 1'b1; //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:SDRAM的工作状态机 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) write_state <= WR_IDLE; else case(write_state) WR_IDLE: if((wr_en ==1'b1) && (init_end == 1'b1)) write_state <= WR_ACTIVE; else write_state <= write_state; WR_ACTIVE: write_state <= WR_TRCD; WR_TRCD: if(trcd_end == 1'b1) write_state <= WR_WRITE; else write_state <= write_state; WR_WRITE: write_state <= WR_DATA; WR_DATA: if(twrite_end == 1'b1) write_state <= WR_PRE; else write_state <= write_state; WR_PRE: write_state <= WR_TRP; WR_TRP: if(trp_end == 1'b1) write_state <= WR_END; else write_state <= write_state; WR_END: write_state <= WR_IDLE; default: write_state <= WR_IDLE; endcase //计数器控制逻辑 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 //SDRAM操作指令控制 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin write_cmd <= NOP; write_ba <= 2'b11; write_addr <= 13'h1fff; end else 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) write_cmd <= B_STOP; 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'h1fff; end endcase //wr_sdram_en:数据总线输出使能 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) wr_sdram_en <= 1'b0; else wr_sdram_en <= wr_ack; //wr_sdram_data:写入SDRAM的数据 assign wr_sdram_data = (wr_sdram_en == 1'b1) ? wr_data : 16'd0; endmodule
sdram_read
`timescale 1ns/1ns module sdram_read ( input wire sys_clk , //系统时钟,频率100MHz input wire sys_rst_n , //复位信号,低电平有效 input wire init_end , //初始化结束信号 input wire rd_en , //读使能 input wire [23:0] rd_addr , //读SDRAM地址 input wire [15:0] rd_data , //自SDRAM中读出的数据 input wire [9:0] rd_burst_len , //读突发SDRAM字节数 output wire rd_ack , //读SDRAM响应信号 output wire rd_end , //一次突发读结束 output reg [3:0] read_cmd , //读数据阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n} output reg [1:0] read_ba , //读数据阶段Bank地址 output reg [12:0] read_addr , //地址数据,辅助预充电操作,行、列地址,A12-A0,13位地址 output wire [15:0] rd_sdram_data //SDRAM读出的数据 ); //parameter define 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 ; //预充电指令 //wire define wire trcd_end ; //激活等待周期结束 wire trp_end ; //预充电等待周期结束 wire tcl_end ; //潜伏期结束标志 wire tread_end ; //突发读结束 wire rdburst_end ; //读突发终止 //reg define reg [3:0] read_state ; //SDRAM写状态 reg [9:0] cnt_clk ; //时钟周期计数,记录初始化各状态等待时间 reg cnt_clk_rst ; //时钟周期计数复位标志 reg [15:0] rd_data_reg ; //rd_data_reg always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rd_data_reg <= 16'd0; else rd_data_reg <= rd_data; //rd_end:一次突发读结束 assign rd_end = (read_state == RD_END) ? 1'b1 : 1'b0; //rd_ack:读SDRAM响应信号 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) if(sys_rst_n == 1'b0) cnt_clk <= 10'd0; else if(cnt_clk_rst == 1'b1) cnt_clk <= 10'd0; else cnt_clk <= cnt_clk + 1'b1; //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:SDRAM的工作状态机 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) read_state <= RD_IDLE; else case(read_state) RD_IDLE: if((rd_en ==1'b1) && (init_end == 1'b1)) read_state <= RD_ACTIVE; else read_state <= RD_IDLE; RD_ACTIVE: read_state <= RD_TRCD; RD_TRCD: if(trcd_end == 1'b1) read_state <= RD_READ; else read_state <= RD_TRCD; RD_READ: read_state <= RD_CL; RD_CL: read_state <= (tcl_end == 1'b1) ? RD_DATA : RD_CL; RD_DATA: read_state <= (tread_end == 1'b1) ? RD_PRE : RD_DATA; RD_PRE: read_state <= RD_TRP; RD_TRP: read_state <= (trp_end == 1'b1) ? RD_END : RD_TRP; RD_END: read_state <= RD_IDLE; default: read_state <= RD_IDLE; endcase //计数器控制逻辑 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) if(sys_rst_n == 1'b0) begin read_cmd <= NOP; read_ba <= 2'b11; read_addr <= 13'h1fff; end else 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) read_cmd <= B_STOP; 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 //rd_sdram_data:SDRAM读出的数据 assign rd_sdram_data = (rd_ack == 1'b1) ? rd_data_reg : 16'b0; endmodule
sdram_arbit
`timescale 1ns/1ns module sdram_arbit ( input wire sys_clk , //系统时钟 input wire sys_rst_n , //复位信号 //sdram_init input wire [3:0] init_cmd , //初始化阶段命令 input wire init_end , //初始化结束标志 input wire [1:0] init_ba , //初始化阶段Bank地址 input wire [12:0] init_addr , //初始化阶段数据地址 //sdram_auto_ref input wire aref_req , //自刷新请求 input wire aref_end , //自刷新结束 input wire [3:0] aref_cmd , //自刷新阶段命令 input wire [1:0] aref_ba , //自动刷新阶段Bank地址 input wire [12:0] aref_addr , //自刷新阶段数据地址 //sdram_write input wire wr_req , //写数据请求 input wire [1:0] wr_ba , //写阶段Bank地址 input wire [15:0] wr_data , //写入SDRAM的数据 input wire wr_end , //一次写结束信号 input wire [3:0] wr_cmd , //写阶段命令 input wire [12:0] wr_addr , //写阶段数据地址 input wire wr_sdram_en , //sdram_read input wire rd_req , //读数据请求 input wire rd_end , //一次读结束 input wire [3:0] rd_cmd , //读阶段命令 input wire [12:0] rd_addr , //读阶段数据地址 input wire [1:0] rd_ba , //读阶段Bank地址 output reg aref_en , //自刷新使能 output reg wr_en , //写数据使能 output reg rd_en , //读数据使能 output wire sdram_cke , //SDRAM时钟使能 output wire sdram_cs_n , //SDRAM片选信号 output wire sdram_ras_n , //SDRAM行地址选通 output wire sdram_cas_n , //SDRAM列地址选通 output wire sdram_we_n , //SDRAM写使能 output reg [1:0] sdram_ba , //SDRAM Bank地址 output reg [12:0] sdram_addr , //SDRAM地址总线 inout wire [15:0] sdram_dq //SDRAM数据总线 ); //parameter define 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 ; //空操作指令 //reg define reg [3:0] sdram_cmd ; //写入SDRAM命令 reg [4:0] state ; //状态机状态 //state:状态机状态 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(init_end == 1'b1) state <= ARBIT; else state <= IDLE; ARBIT: if(aref_req == 1'b1) state <= AREF; else if(wr_req == 1'b1) state <= WRITE; else if(rd_req == 1'b1) state <= READ; else state <= ARBIT; AREF: if(aref_end == 1'b1) state <= ARBIT; else state <= AREF; WRITE: if(wr_end == 1'b1) state <= ARBIT; else state <= WRITE; READ: if(rd_end == 1'b1) state <= ARBIT; else state <= READ; default:state <= IDLE; endcase //aref_en:自动刷新使能 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) aref_en <= 1'b0; else if((state == ARBIT) && (aref_req == 1'b1)) aref_en <= 1'b1; else if(aref_end == 1'b1) aref_en <= 1'b0; //wr_en:写数据使能 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) wr_en <= 1'b0; else if((state == ARBIT) && (aref_req == 1'b0) && (wr_req == 1'b1)) wr_en <= 1'b1; else if(wr_end == 1'b1) wr_en <= 1'b0; //rd_en:读数据使能 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rd_en <= 1'b0; else if((state == ARBIT) && (aref_req == 1'b0) && (rd_req == 1'b1)) rd_en <= 1'b1; else if(rd_end == 1'b1) rd_en <= 1'b0; //sdram_cmd:写入SDRAM命令;sdram_ba:SDRAM Bank地址;sdram_addr:SDRAM地址总线 always@(*) 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 //SDRAM时钟使能 assign sdram_cke = 1'b1; //SDRAM数据总线 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
sdram_ctrl
`timescale 1ns/1ns module sdram_ctrl ( input wire sys_clk , //系统时钟 input wire sys_rst_n , //复位信号,低电平有效 //SDRAM写端口 input wire sdram_wr_req , //写SDRAM请求信号 input wire [23:0] sdram_wr_addr , //SDRAM写操作的地址 input wire [9:0] wr_burst_len , //写sdram时数据突发长度 input wire [15:0] sdram_data_in , //写入SDRAM的数据 output wire sdram_wr_ack , //写SDRAM响应信号 //SDRAM读端口 input wire sdram_rd_req , //读SDRAM请求信号 input wire [23:0] sdram_rd_addr , //SDRAM读操作的地址 input wire [9:0] rd_burst_len , //读sdram时数据突发长度 output wire [15:0] sdram_data_out , //从SDRAM读出的数据 output wire init_end , //SDRAM 初始化完成标志 output wire sdram_rd_ack , //读SDRAM响应信号 //FPGA与SDRAM硬件接口 output wire sdram_cke , // SDRAM 时钟有效信号 output wire sdram_cs_n , // SDRAM 片选信号 output wire sdram_ras_n , // SDRAM 行地址选通 output wire sdram_cas_n , // SDRAM 列地址选通 output wire sdram_we_n , // SDRAM 写使能 output wire [1:0] sdram_ba , // SDRAM Bank地址 output wire [12:0] sdram_addr , // SDRAM 地址总线 inout wire [15:0] sdram_dq // SDRAM 数据总线 ); //wire define //sdram_init wire [3:0] init_cmd ; //初始化阶段写入sdram的指令 wire [1:0] init_ba ; //初始化阶段Bank地址 wire [12:0] init_addr ; //初始化阶段地址数据,辅助预充电操作 //sdram_a_ref wire aref_req ; //自动刷新请求 wire aref_end ; //自动刷新结束标志 wire [3:0] aref_cmd ; //自动刷新阶段写入sdram的指令 wire [1:0] aref_ba ; //自动刷新阶段Bank地址 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 ; //写数据阶段Bank地址 wire [12:0] write_addr ; //写阶段数据地址 wire wr_sdram_en ; //SDRAM写使能 wire [15:0] wr_sdram_data; //写入SDRAM的数据 //sdram_read wire rd_en ; //读使能 wire rd_end ; //一次突发读结束 wire [3:0] read_cmd ; //读数据阶段写入sdram的指令 wire [1:0] read_ba ; //读阶段Bank地址 wire [12:0] read_addr ; //读阶段数据地址 //------------- sdram_init_inst ------------- sdram_init sdram_init_inst ( .sys_clk (sys_clk ), //系统时钟,频率100MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .init_cmd (init_cmd ), //初始化阶段写入sdram的指令 .init_ba (init_ba ), //初始化阶段Bank地址 .init_addr (init_addr ), //初始化阶段地址数据,辅助预充电操作 .init_end (init_end ) //初始化结束信号 ); //------------- sdram_arbit_inst ------------- sdram_arbit sdram_arbit_inst ( .sys_clk (sys_clk ), //系统时钟 .sys_rst_n (sys_rst_n ), //复位信号 //sdram_init .init_cmd (init_cmd ), //初始化阶段命令 .init_end (init_end ), //初始化结束标志 .init_ba (init_ba ), //初始化阶段Bank地址 .init_addr (init_addr ), //初始化阶段数据地址 //sdram_auto_ref .aref_req (aref_req ), //自刷新请求 .aref_end (aref_end ), //自刷新结束 .aref_cmd (aref_cmd ), //自刷新阶段命令 .aref_ba (aref_ba ), //自动刷新阶段Bank地址 .aref_addr (aref_addr ), //自刷新阶段数据地址 //sdram_write .wr_req (sdram_wr_req ), //写数据请求 .wr_end (wr_end ), //一次写结束信号 .wr_cmd (write_cmd ), //写阶段命令 .wr_ba (write_ba ), //写阶段Bank地址 .wr_addr (write_addr ), //写阶段数据地址 .wr_sdram_en(wr_sdram_en ), //SDRAM写使能 .wr_data (wr_sdram_data ), //写入SDRAM的数据 //sdram_read .rd_req (sdram_rd_req ), //读数据请求 .rd_end (rd_end ), //一次读结束 .rd_cmd (read_cmd ), //读阶段命令 .rd_addr (read_addr ), //读阶段数据地址 .rd_ba (read_ba ), //读阶段Bank地址 .aref_en (aref_en ), //自刷新使能 .wr_en (wr_en ), //写数据使能 .rd_en (rd_en ), //读数据使能 .sdram_cke (sdram_cke ), //SDRAM时钟使能 .sdram_cs_n (sdram_cs_n ), //SDRAM片选信号 .sdram_ras_n(sdram_ras_n ), //SDRAM行地址选通 .sdram_cas_n(sdram_cas_n ), //SDRAM列地址选通 .sdram_we_n (sdram_we_n ), //SDRAM写使能 .sdram_ba (sdram_ba ), //SDRAM Bank地址 .sdram_addr (sdram_addr ), //SDRAM地址总线 .sdram_dq (sdram_dq ) //SDRAM数据总线 ); //------------- sdram_a_ref_inst ------------- sdram_a_ref sdram_a_ref_inst ( .sys_clk (sys_clk ), //系统时钟,频率100MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .init_end (init_end ), //初始化结束信号 .aref_en (aref_en ), //自动刷新使能 .aref_req (aref_req ), //自动刷新请求 .aref_cmd (aref_cmd ), //自动刷新阶段写入sdram的指令 .aref_ba (aref_ba ), //自动刷新阶段Bank地址 .aref_addr (aref_addr ), //地址数据,辅助预充电操作 .aref_end (aref_end ) //自动刷新结束标志 ); //------------- sdram_write_inst ------------- sdram_write sdram_write_inst ( .sys_clk (sys_clk ), //系统时钟,频率100MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .init_end (init_end ), //初始化结束信号 .wr_en (wr_en ), //写使能 .wr_addr (sdram_wr_addr ), //写SDRAM地址 .wr_data (sdram_data_in ), //待写入SDRAM的数据(写FIFO传入) .wr_burst_len (wr_burst_len ), //写突发SDRAM字节数 .wr_ack (sdram_wr_ack ), //写SDRAM响应信号 .wr_end (wr_end ), //一次突发写结束 .write_cmd (write_cmd ), //写数据阶段写入sdram的指令 .write_ba (write_ba ), //写数据阶段Bank地址 .write_addr (write_addr ), //地址数据,辅助预充电操作 .wr_sdram_en (wr_sdram_en ), //数据总线输出使能 .wr_sdram_data (wr_sdram_data ) //写入SDRAM的数据 ); //------------- sdram_read_inst ------------- sdram_read sdram_read_inst ( .sys_clk (sys_clk ), //系统时钟,频率100MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .init_end (init_end ), //初始化结束信号 .rd_en (rd_en ), //读使能 .rd_addr (sdram_rd_addr ), //读SDRAM地址 .rd_data (sdram_dq ), //自SDRAM中读出的数据 .rd_burst_len (rd_burst_len ), //读突发SDRAM字节数 .rd_ack (sdram_rd_ack ), //读SDRAM响应信号 .rd_end (rd_end ), //一次突发读结束 .read_cmd (read_cmd ), //读数据阶段写入sdram的指令 .read_ba (read_ba ), //读数据阶段Bank地址 .read_addr (read_addr ), //地址数据,辅助预充电操作 .rd_sdram_data (sdram_data_out ) //SDRAM读出的数据 ); endmodule
FPGA进阶(3):SDRAM读写控制器的设计与验证(二)+https://developer.aliyun.com/article/1556623