FPGA学习笔记——SDR SDRAM的读写(不调用IP核版)
目录
一、任务
二、需求分析
三、Visio图
四、具体分析
1.模块分析
2.FIFO IP核
五、代码
1.top.v
2.uart_tx.v
3.uart_rx.v
4.key_filter.v
5.sdram_controller.v
6.rw_ctrl.v
7.sdram_intf.v
六、实验现象
这篇文章,可以参考SDR SDRAM(调用IP 核版),两篇最大的不同点在于,不调用IP核版的文章相当于需要自己写SDR SDRAM IP核,这里我只重点介绍怎么实现接口。
一、任务
对SDR SDRAM进行读写操作,按键按下从SDRAM中读出10个数据,并在串口助手显示,用串口助手每发送10个字节数据,就将数据写入SDRAM中,与SDRAM通信的时钟为100MHz。
二、需求分析
分析任务,需要手写SDR SDRAM接口,接口模块需要收发相应的指令和数据,就需要FIFO来缓存数据,然后还需要按键模块来进行读操作,还需要串口收发模块来发送和接收数据,每接收到10个数据就进行写操作,说明需要FIFO来进行缓存,读操作也是一样的,而且这里要注意,SDRAM通信的时钟是100MHz,就需要PLL IP核来产生时钟,最后就是需要读写操作模块。(具体看具体分析)
三、Visio图
四、具体分析
1.模块分析
这里的模块只介绍Slave模块,其他模块可以看看SDR SDRAM(调用IP 核版)这个文章的介绍。
要实现接口模块,必须知道流程,根据流程来复刻SDRAM的读写。下面是读写的流程图:
WAIT到PRECH:延时200us
PRECH到ATREF:初始流程,预充电操作完成(tRP,至少20ns)PRECH到IDLE:非初始流程,预充电操作完成(tRP,至少20ns)
ATREF到MRS:初始化流程,自动刷新1次(tRC,至少63ns)
ATREF到IDLE:空闲状态间隔一定时间进入自动刷新操作完成tRC,至少63ns)
MRS到IDLE:初始化流程,模式寄存器设置成功(至少2个clk)
IDLE到ATREF:空闲状态,间隔7.8us进行一次刷新操作
IDLE到ACTIVE:读/写操作开始
ACTIVE到WRITE:写请求,且延时70ns(行激活时间42ns+行选通与列选通的间隔时间)
ACTIVE到READ: 读请求,且延时70ns(行激活时间+行选通与列选通的间隔时间)
READ到PRECH:突发读完成
WRITE到RECOV:突发写完成
RECOV到RECH:写恢复延时结束(tDPL,至少2个个时钟周期)
可以看到图中有绿色和红色两种颜色的箭头,其中绿色箭头是上电以后必须做的流程,状态在IDLE状态,就等待读写操作的触发或者每计时到7.8us的时候就进行刷新操作,这里的刷新操作时分布式的刷新操作,还有一种是集中式的刷新操作,就是在64ms的时间内的某一段时间只进行刷新操作,其他时间就可以做其他的操作。
每当在IDLE状态,当有读或者写使能时,则跳到ACTIVE状态,进行Bank和行地址的选通,再跳到READ或WRITE状态,进行列地址选通,当突发读或写完成时,最后跳到IDLE状态。
注意:如果在读或写操作的时候,计数到7.8us,又要进入到刷新操作,这时候需要一个flag信号,来表示需要进行刷新操作,读或写操作结束时,到IDLE状态,立马进行刷新操作。SDR SDRAM的64ms要刷新所有的行,这个64ms只是一个比较保守的值。
2.FIFO IP核
这里的位宽={s_write,s_read,s_byteenable,s_wrdata,s_addr}:写(1)+ 读(1)+ DQM(2)+ DQ(16)+ {Bank,row(行),colum(列)}(24)
五、代码
1.top.v
module top( input clk ,input rst_n ,input key_in ,input uart_rxd ,output uart_txd ,output [12:0] sdram_addr ,output [1:0] sdram_bank ,output sdram_cas_n ,output sdram_cke ,output sdram_cs_n ,inout [15:0] sdram_dq ,output [1:0] sdram_dqm ,output sdram_ras_n ,output sdram_we_n ,output sdram_clk
);
//---------<参数定义>------------------------------------------------//---------<内部信号定义>--------------------------------------------wire key_down ;wire [7:0] rx_data ;wire rx_data_vld ;wire tx_rdy ;wire [7:0] tx_data ;wire tx_data_vld ;wire clk_100m ;wire clk_100m_s ;wire reset_n ;wire locked ;assign reset_n = locked && rst_n;assign sdram_clk = clk_100m_s;//*******************************************************************
//--模块例化
//*******************************************************************ip_pll ip_pll_inst (.areset ( ~rst_n ),.inclk0 ( clk ),.c0 ( clk_100m ),.c1 ( clk_100m_s ),.locked ( locked ));key_filter u_key_filter( /*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input [WIDTH-1:0] */.key_in (key_in ),/*output reg [WIDTH-1:0] */.key_down (key_down )
); uart_rx uart_rx_inst( /*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input */.uart_rxd (uart_rxd ),/*input [2:0] */.buad_sel (3'd4 ),//0->BPS_9600;1->BPS_19200;2->BPS_38400;3->BPS_57600;4->BPS115200/*output reg [7:0] */.dout (rx_data ),/*output */.dout_vld (rx_data_vld )
);sdram_controller u_sdram_ctrl( /*input */.clk_50m (clk ),/*input */.clk_100m (clk_100m ),/*input */.rst_n (reset_n ),/*input */.key_down (key_down ),/*input [7:0] */.rx_data (rx_data ),/*input */.rx_data_vld (rx_data_vld),/*input */.tx_rdy (tx_rdy ),/*output [7:0] */.tx_data (tx_data ),/*output */.tx_data_vld (tx_data_vld),/*output [12:0] */.sdram_addr (sdram_addr ),/*output [1:0] */.sdram_bank (sdram_bank ),/*output */.sdram_cas_n (sdram_cas_n),/*output */.sdram_cke (sdram_cke ),/*output */.sdram_cs_n (sdram_cs_n ),/*inout [15:0] */.sdram_dq (sdram_dq ),/*output [1:0] */.sdram_dqm (sdram_dqm ),/*output */.sdram_ras_n (sdram_ras_n),/*output */.sdram_we_n (sdram_we_n )
);uart_tx uart_tx_isnt( /*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input [7:0] */.din (tx_data ),/*input */.send_en (tx_data_vld ),/*input [2:0] */.buad_sel (3'd4 ),//0->BPS_9600;1->BPS_19200;2->BPS_38400;3->BPS_57600;4->BPS115200/*output */.tx_rdy (tx_rdy ),/*output reg */.uart_txd (uart_txd )
);endmodule
2.uart_tx.v
module uart_tx( input clk ,input rst_n ,input [7:0] din ,input send_en ,input [2:0] buad_sel ,output tx_rdy ,output reg uart_txd
);
//---------<参数定义>------------------------------------------------//状态机参数定义parameter IDLE = 4'b0001 ,//独热码START = 4'b0010 ,DATA = 4'b0100 ,STOP = 4'b1000 ; //波特率参数定义parameter BAUD_9600 = 13'd5208 ,BAUD_19200 = 13'd2604 ,BAUD_38400 = 13'd1302 ,BAUD_57600 = 13'd868 ,BAUD_115200 = 13'd434 ;//---------<内部信号定义>--------------------------------------------reg [3:0] state_c ;//现态reg [3:0] state_n ;//次态reg [12:0] cnt_baud ;//波特率计数器wire add_cnt_baud ;wire end_cnt_baud ;reg [2:0] cnt_num ;//比特数量计数器wire add_cnt_num ;wire end_cnt_num ;reg [12:0] max_baud ;reg [7:0] din_r ;//状态转移条件wire idle2start ;wire start2data ;wire data2stop ;wire stop2idle ;//*******************************************************************
//--波特率设置
//*******************************************************************always @(*)begin case (buad_sel)0 : max_baud = BAUD_9600 ;1 : max_baud = BAUD_19200 ;2 : max_baud = BAUD_38400 ;3 : max_baud = BAUD_57600 ;4 : max_baud = BAUD_115200;default: max_baud = BAUD_9600;endcaseend//*******************************************************************
//--输入寄存
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begindin_r <= 'd0;end else if(send_en && tx_rdy)begin din_r <= din;end end//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE : beginif(idle2start)state_n = START;else state_n = state_c;endSTART: beginif(start2data)state_n = DATA ; else state_n = state_c;endDATA : beginif(data2stop)state_n = STOP; else state_n = state_c;endSTOP : beginif(stop2idle)state_n = IDLE;else state_n = state_c;enddefault : ;endcaseendassign idle2start = state_c == IDLE && send_en;assign start2data = state_c == START && end_cnt_baud;assign data2stop = state_c == DATA && end_cnt_num;assign stop2idle = state_c == STOP && end_cnt_baud;//*******************************************************************
//--cnt_baud
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end endend assign add_cnt_baud = state_c != IDLE;assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((max_baud>>2) + (max_baud>>1)) : (max_baud - 1));//*******************************************************************
//--cnt_num
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_num <= 'd0;end else if(add_cnt_num)begin if(end_cnt_num)begin cnt_num <= 'd0;endelse begin cnt_num <= cnt_num + 1'b1;end endend assign add_cnt_num = state_c == DATA && end_cnt_baud;assign end_cnt_num = add_cnt_num && cnt_num == 8-1;//*******************************************************************
//--uart_txd
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginuart_txd <= 1'b1;end else begin case (state_c)IDLE : uart_txd <= 1'b1;START : uart_txd <= 1'b0;DATA : uart_txd <= din_r[cnt_num];STOP : uart_txd <= 1'b1;default: ;endcaseend end//*******************************************************************
//--tx_rdy
//*******************************************************************assign tx_rdy = state_c == IDLE;endmodule
3.uart_rx.v
module uart_rx( input clk ,input rst_n ,input uart_rxd ,input [2:0] buad_sel ,output reg [7:0] dout ,output dout_vld
);
//---------<参数定义>------------------------------------------------//状态机参数定义parameter IDLE = 4'b0001 ,//独热码START = 4'b0010 ,DATA = 4'b0100 ,STOP = 4'b1000 ; //波特率参数定义parameter BAUD_9600 = 13'd5208 ,BAUD_19200 = 13'd2604 ,BAUD_38400 = 13'd1302 ,BAUD_57600 = 13'd868 ,BAUD_115200 = 13'd434 ;//---------<内部信号定义>--------------------------------------------reg [3:0] state_c ;//现态reg [3:0] state_n ;//次态reg [12:0] cnt_baud ;//波特率计数器wire add_cnt_baud ;wire end_cnt_baud ;reg [2:0] cnt_num ;//比特数量计数器wire add_cnt_num ;wire end_cnt_num ;reg [12:0] max_baud ;reg [2:0] rxd_r ;wire rxd_nedge ;//状态转移条件wire idle2start ;wire start2data ;wire data2stop ;wire stop2idle ;//*******************************************************************
//--波特率设置
//*******************************************************************always @(*)begin case (buad_sel)0 : max_baud = BAUD_9600 ;1 : max_baud = BAUD_19200 ;2 : max_baud = BAUD_38400 ;3 : max_baud = BAUD_57600 ;4 : max_baud = BAUD_115200;default: max_baud = BAUD_9600;endcaseend//*******************************************************************
//--打三拍+下降沿检测
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrxd_r <= 3'b111;end else begin // rxd_r[0] <= uart_rxd;// rxd_r[1] <= rxd_r[0];// rxd_r[2] <= rxd_r[1];rxd_r <= {rxd_r[1:0],uart_rxd};end end//下降沿检测assign rxd_nedge = ~rxd_r[1] & rxd_r[2];//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE : beginif(idle2start)state_n = START;else state_n = state_c;endSTART: beginif(start2data)state_n = DATA ; else state_n = state_c;endDATA : beginif(data2stop)state_n = STOP; else state_n = state_c;endSTOP : beginif(stop2idle)state_n = IDLE;else state_n = state_c;enddefault : ;endcaseendassign idle2start = (state_c == IDLE ) && rxd_nedge;assign start2data = (state_c == START) && end_cnt_baud;assign data2stop = (state_c == DATA ) && end_cnt_num;assign stop2idle = (state_c == STOP ) && end_cnt_baud;//*******************************************************************
//--cnt_baud
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_baud <= 'd0;end else if(add_cnt_baud)begin if(end_cnt_baud)begin cnt_baud <= 'd0;endelse begin cnt_baud <= cnt_baud + 1'b1;end endend assign add_cnt_baud = state_c != IDLE;assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? (max_baud>>2 + max_baud>>1) : (max_baud - 1));//*******************************************************************
//--cnt_num
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_num <= 'd0;end else if(add_cnt_num)begin if(end_cnt_num)begin cnt_num <= 'd0;endelse begin cnt_num <= cnt_num + 1'b1;end endend assign add_cnt_num = state_c == DATA && end_cnt_baud;assign end_cnt_num = add_cnt_num && cnt_num == 8-1;//*******************************************************************
//--dout
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begindout <= 'd0;end else if(state_c == DATA && cnt_baud == (max_baud>>1))begin dout[cnt_num] <= rxd_r[2];// dout <= {rxd_r[2],dout[7:1]};end end//*******************************************************************
//--dout_vld
//*******************************************************************assign dout_vld = stop2idle;endmodule
4.key_filter.v
module key_filter#(parameter WIDTH = 1,TIME_20MS = 1000_000)( input clk ,input rst_n ,input [WIDTH-1:0] key_in ,output reg [WIDTH-1:0] key_down
);
//---------<参数定义>--------------------------------------------------------- //状态机参数 独热码编码localparam IDLE = 4'b0001,//空闲状态FILTER_DOWN = 4'b0010,//按键按下抖动状态HOLD = 4'b0100,//按键稳定按下状态FILTER_UP = 4'b1000;//按键释放抖动状态//---------<内部信号定义>-----------------------------------------------------reg [3:0] state_c ;//现态reg [3:0] state_n ;//次态reg [WIDTH-1:0] key_r0 ;//同步打拍reg [WIDTH-1:0] key_r1 ;reg [WIDTH-1:0] key_r2 ;wire [WIDTH-1:0] n_edge ;//下降沿wire [WIDTH-1:0] p_edge ;//上升沿 reg [19:0] cnt_20ms ;//延时计数器(20ms)wire add_cnt_20ms ;wire end_cnt_20ms ; //状态转移条件信号wire idle2filter_down ;wire filter_down2idle ;wire filter_down2hold ;wire hold2filter_up ;wire filter_up2hold ;wire filter_up2idle ; //****************************************************************
//--状态机
//****************************************************************//第一段:时序逻辑描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑描述状态转移规律和状态转移条件always @(*)begin case (state_c)IDLE : begin if(idle2filter_down)begin state_n = FILTER_DOWN;endelse begin // state_n = IDLE;state_n = state_c;endendFILTER_DOWN : begin if(filter_down2idle)begin state_n = IDLE;endelse if(filter_down2hold)begin state_n = HOLD;endelse begin state_n = state_c;endendHOLD : begin if(hold2filter_up)begin state_n = FILTER_UP;endelse begin state_n = state_c;endendFILTER_UP : begin if(filter_up2hold)begin state_n = HOLD;endelse if(filter_up2idle)begin state_n = IDLE;endelse begin state_n = state_c;endenddefault: state_n = IDLE;endcaseendassign idle2filter_down = (state_c == IDLE) && n_edge;assign filter_down2idle = (state_c == FILTER_DOWN) && p_edge;assign filter_down2hold = (state_c == FILTER_DOWN) && end_cnt_20ms && !p_edge;assign hold2filter_up = (state_c == HOLD) && p_edge;assign filter_up2hold = (state_c == FILTER_UP) && n_edge;assign filter_up2idle = (state_c == FILTER_UP) && end_cnt_20ms;//****************************************************************
//--n_edge、p_edge
//**************************************************************** always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_r0 <= {WIDTH{1'b1}};key_r1 <= {WIDTH{1'b1}};key_r2 <= {WIDTH{1'b1}};end else begin key_r0 <= key_in;key_r1 <= key_r0;key_r2 <= key_r1; end endassign n_edge = ~key_r1 & key_r2;//下降沿assign p_edge = ~key_r2 & key_r1;//上升沿//****************************************************************
//--cnt_20ms
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_20ms <= 'd0;end else if(add_cnt_20ms)begin if(end_cnt_20ms || filter_down2idle || filter_up2hold)begin cnt_20ms <= 'd0;endelse begin cnt_20ms <= cnt_20ms + 1'b1;end endend assign add_cnt_20ms = (state_c == FILTER_DOWN) || (state_c == FILTER_UP);assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;//****************************************************************
//--key_down
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_down <= 'd0;end else begin key_down <= filter_down2hold ? ~key_r2 : 1'b0;endendendmodule
5.sdram_controller.v
module sdram_controller( input clk_50m ,input clk_100m ,input rst_n ,//key_filterinput key_down ,//uartinput [7:0] rx_data ,input rx_data_vld ,input tx_rdy ,output [7:0] tx_data ,output tx_data_vld ,//sdram memoryoutput [12:0] sdram_addr ,output [1:0] sdram_bank ,output sdram_cas_n ,output sdram_cke ,output sdram_cs_n ,inout [15:0] sdram_dq ,output [1:0] sdram_dqm ,output sdram_ras_n ,output sdram_we_n
);
//---------<参数定义>------------------------------------------------//---------<内部信号定义>--------------------------------------------wire burst_begin;wire [8:0] burst_count;wire write ;wire [15:0] wrdata ;wire [23:0] addr ;wire read ;wire [15:0] rddata ;wire rddata_vld ;wire rdy ; //*******************************************************************
//--模块例化
//*******************************************************************rw_ctrl u_rw_ctrl( /*input */.clk_50m (clk_50m ),/*input */.clk_100m (clk_100m ),/*input */.rst_n (rst_n ),/*input */.key_down (key_down ),/*input [7:0] */.rx_data (rx_data ),/*input */.rx_data_vld (rx_data_vld),/*input */.tx_rdy (tx_rdy ),/*output [7:0] */.tx_data (tx_data ),/*output */.tx_data_vld (tx_data_vld),/*output */.burst_begin (burst_begin),/*output [8:0] */.burst_count (burst_count),/*output */.write (write ),/*output [15:0] */.wrdata (wrdata ),/*output [23:0] */.addr (addr ),//bank+row+colum/*output */.read (read ),/*input [15:0] */.rddata (rddata ),/*input */.rddata_vld (rddata_vld ),/*input */.rdy (rdy )
);sdram_intf u_sdram_intf( /*input */.clk (clk_100m ),/*input */.rst_n (rst_n ),/*input */.s_burst_begin (burst_begin ),/*input [1:0] */.s_byteenable (2'b00 ),/*input [8:0] */.s_burst_count (burst_count ),/*input */.s_write (write ),/*input [15:0] */.s_wrdata (wrdata ),/*input [23:0] */.s_addr (addr ),//bank+row+colum/*input */.s_read (read ),/*output [15:0] */.s_rddata (rddata ),/*output */.s_rddata_vld (rddata_vld ),/*output */.s_rdy (rdy ),/*output */.sdram_cke (sdram_cke ),/*output */.sdram_cs_n (sdram_cs_n ),/*output */.sdram_ras_n (sdram_ras_n ),/*output */.sdram_cas_n (sdram_cas_n ),/*output */.sdram_we_n (sdram_we_n ),/*output [1:0] */.sdram_bank (sdram_bank ),/*output [12:0] */.sdram_addr (sdram_addr ),/*inout [15:0] */.sdram_dq (sdram_dq ),/*output [1:0] */.sdram_dqm (sdram_dqm )
);endmodule
6.rw_ctrl.v
module rw_ctrl#(parameter BURST_LENTH = 9'd10
)( input clk_50m ,input clk_100m ,input rst_n ,//key_filterinput key_down ,//uartinput [7:0] rx_data ,input rx_data_vld ,input tx_rdy ,output [7:0] tx_data ,output tx_data_vld ,//sdram_intfoutput burst_begin ,output [8:0] burst_count ,output write ,output [15:0] wrdata ,output [23:0] addr ,//bank+row+columoutput read ,input [15:0] rddata ,input rddata_vld ,input rdy
);
//---------<参数定义>------------------------------------------------//状态机参数定义localparam IDLE = 4'b0001,WRITE = 4'b0010,READ = 4'b0100,DONE = 4'b1000;//---------<内部信号定义>--------------------------------------------reg [3:0] state_c ;reg [3:0] state_n ;reg [$clog2(BURST_LENTH) - 1:0] cnt_burst ;wire add_cnt_burst ;wire end_cnt_burst ;reg [23:0] wr_addr ;//bank+row+columreg [23:0] rd_addr ;//状态转移条件wire idle2write ;wire idle2read ;wire write2done ;wire read2done ;wire done2idle ;wire wrfifo_wrreq ;wire wrfifo_rdreq ;wire [7:0] wrfifo_q ;wire wrfifo_rdempty ;wire [7:0] wrfifo_rdusedw ;wire wrfifo_wrfull ;wire rdfifo_wrreq ;wire rdfifo_rdreq ;wire [7:0] rdfifo_q ;wire rdfifo_rdempty ;wire rdfifo_wrfull ; //*******************************************************************
//--模块例化
//*******************************************************************wr_fifo wr_fifo_inst (.aclr ( ~rst_n ),.data ( rx_data ),.rdclk ( clk_100m ),.rdreq ( wrfifo_rdreq ),.wrclk ( clk_50m ),.wrreq ( wrfifo_wrreq ),.q ( wrfifo_q ),.rdempty ( wrfifo_rdempty ),.rdusedw ( wrfifo_rdusedw ),.wrfull ( wrfifo_wrfull ));assign wrfifo_wrreq = ~wrfifo_wrfull && rx_data_vld;assign wrfifo_rdreq = ~wrfifo_rdempty && state_c == WRITE && rdy;rd_fifo rd_fifo_inst (.aclr ( ~rst_n ),.data ( rddata[7:0] ),.rdclk ( clk_50m ),.rdreq ( rdfifo_rdreq ),.wrclk ( clk_100m ),.wrreq ( rdfifo_wrreq ),.q ( rdfifo_q ),.rdempty ( rdfifo_rdempty ),.wrfull ( rdfifo_wrfull ));assign rdfifo_wrreq = ~rdfifo_wrfull && rddata_vld;assign rdfifo_rdreq = ~rdfifo_rdempty && tx_rdy;//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk_100m or negedge rst_n)begin if(!rst_n)beginstate_c <= IDLE;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)IDLE : beginif(idle2write)state_n = WRITE;else if(idle2read)state_n = READ;else state_n = state_c;endWRITE : beginif(write2done)state_n = DONE;else state_n = state_c;endREAD : beginif(read2done)state_n = DONE;else state_n = state_c;endDONE : beginif(done2idle)state_n = IDLE;else state_n = state_c;enddefault : ;endcaseendassign idle2write = (state_c == IDLE ) && wrfifo_rdusedw >= BURST_LENTH;assign idle2read = (state_c == IDLE ) && key_down;assign write2done = (state_c == WRITE) && end_cnt_burst;assign read2done = (state_c == READ ) && end_cnt_burst;assign done2idle = (state_c == DONE ) && 1'b1;//*******************************************************************
//--cnt_burst
//*******************************************************************always @(posedge clk_100m or negedge rst_n)begin if(!rst_n)begincnt_burst <= 'd0;end else if(add_cnt_burst)begin if(end_cnt_burst)begin cnt_burst <= 'd0;endelse begin cnt_burst <= cnt_burst + 1'b1;end endend assign add_cnt_burst = (state_c == WRITE || state_c == READ) && rdy;assign end_cnt_burst = add_cnt_burst && cnt_burst == BURST_LENTH - 1;//*******************************************************************
//--wr_addr
//*******************************************************************always @(posedge clk_100m or negedge rst_n)begin if(!rst_n)beginwr_addr <= 'd0;end else if(state_c == WRITE && rdy)begin wr_addr <= wr_addr + 1'b1;end end//*******************************************************************
//--rd_addr
//*******************************************************************always @(posedge clk_100m or negedge rst_n)begin if(!rst_n)beginrd_addr <= 'd0;end else if(state_c == READ && rdy)begin rd_addr <= rd_addr + 1'b1;end end//*******************************************************************
//--输出
//*******************************************************************assign tx_data = rdfifo_q;assign tx_data_vld = rdfifo_rdreq;assign burst_begin = idle2read | idle2write;assign burst_count = BURST_LENTH;assign write = state_c == WRITE;assign wrdata = {2{wrfifo_q}};assign addr = (state_c == WRITE) ? wr_addr : rd_addr;assign read = state_c == READ;endmodule
7.sdram_intf.v
module sdram_intf( input clk ,input rst_n ,//sdram masterinput s_burst_begin ,input [1:0] s_byteenable ,input [8:0] s_burst_count ,input s_write ,input [15:0] s_wrdata ,input [23:0] s_addr ,//bank+row+columinput s_read ,output [15:0] s_rddata ,output s_rddata_vld ,output s_rdy ,//sdram memoryoutput sdram_cke ,output sdram_cs_n ,output sdram_ras_n ,output sdram_cas_n ,output sdram_we_n ,output [1:0] sdram_bank ,output [12:0] sdram_addr ,inout [15:0] sdram_dq ,output [1:0] sdram_dqm
);
//---------<参数定义>------------------------------------------------//状态机参数定义parameter WAIT = 9'b000_000_001, PRECH = 9'b000_000_010, ATREF = 9'b000_000_100, MRS = 9'b000_001_000, IDLE = 9'b000_010_000, ACTIVE = 9'b000_100_000, READ = 9'b001_000_000, WRITE = 9'b010_000_000, RECOV = 9'b100_000_000;//时间参数定义parameter TIME_200US = 15'd20_000 ,//上电等待200usTIME_PRE = 15'd3 ,//预充电操作至少20nsTIME_ATREF = 15'd7 ,//自动刷新操作至少63nsTIME_MRS = 15'd2 ,//模式寄存器设置操作至少2个clkTIME_REF = 15'd780 ,//刷新间隔7.8usTIME_ACT = 15'd7 ,//行激活至少42+行选通与列选通的延时 TIME_RECOV = 15'd3 ;//写恢复时间至少2个clk //命令参数定义 localparam CMD_NOP = 4'b0111,//空操作命令CMD_PRECH = 4'b0010,//预充电命令CMD_ATREF = 4'b0001,//自动刷新命令CMD_MRS = 4'b0000,//模式寄存器设置命令CMD_ACT = 4'b0011,//行激活命令CMD_WRITE = 4'b0100,//写命令CMD_READ = 4'b0101;//读命令//---------<内部信号定义>--------------------------------------------wire [15:0] dq_in ;wire dq_out_en ;wire [15:0] dq_out ;reg [8:0] state_c ;reg [8:0] state_n ;reg [14:0] cnt_xx ;wire add_cnt_xx ;wire end_cnt_xx ;reg [9:0] cnt_ref ;//刷新计数器wire add_cnt_ref ;wire end_cnt_ref ;reg [14:0] xx ;reg [8:0] burst_lenth ;//突发长度reg init_flag ;//初始化标志reg ref_flag ;//刷新标志reg [3:0] cmd ;//{sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} reg [12:0] addr ;reg [1:0] dqm ;reg [15:0] rddata ;reg [2:0] rddata_vld ;//状态转移条件wire wait2prech ;wire prech2atref ;wire prech2idle ;wire atref2mrs ;wire atref2idle ;wire mrs2idle ;wire idle2atref ;wire idle2active ;wire active2read ;wire active2write;wire write2recov ;wire read2prech ;wire recov2prech ;wire f_wrreq ;wire [43:0] f_data ;//{s_write,s_read,s_byteenable,s_wrdata,s_addr}wire f_rdreq ;wire [43:0] f_q ;wire f_empty ;wire f_full ;wire [3:0] f_usedw ;//*******************************************************************
//--sdram_dq 双向端口描述
//******************************************************************* assign sdram_dq = dq_out_en ? dq_out : 16'hzzzz;assign dq_in = sdram_dq;//*******************************************************************
//--模块例化
//*******************************************************************ip_fifo ip_fifo_inst (.aclr ( ~rst_n ),.clock ( clk ),.data ( f_data ),.rdreq ( f_rdreq ),.wrreq ( f_wrreq ),.empty ( f_empty ),.full ( f_full ),.q ( f_q ),.usedw ( f_usedw ));assign f_data = {s_write,s_read,s_byteenable,s_wrdata,s_addr};assign f_wrreq = ~f_full && (s_write | s_read);assign f_rdreq = ~f_empty && (state_c == WRITE | state_c == READ);//*******************************************************************
//--状态机
//*******************************************************************//第一段:同步时序描述状态转移always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= WAIT;end else begin state_c <= state_n;end end//第二段:组合逻辑判断状态转移条件,描述状态转移规律always @(*) begincase(state_c)WAIT : beginif(wait2prech)state_n = PRECH; elsestate_n = state_c;endPRECH : beginif(prech2atref)state_n = ATREF;else if(prech2idle)state_n = IDLE;elsestate_n = state_c;endATREF : beginif(atref2mrs)state_n = MRS; else if(atref2idle)state_n = IDLE;elsestate_n = state_c;endMRS : beginif(mrs2idle)state_n = IDLE;elsestate_n = state_c;endIDLE : beginif(idle2atref)state_n = ATREF;else if(idle2active)state_n = ACTIVE;elsestate_n = state_c;endACTIVE : beginif(active2read)state_n = READ;else if(active2write)state_n = WRITE;elsestate_n = state_c;endREAD : beginif(read2prech)state_n = PRECH; elsestate_n = state_c;endWRITE : beginif(write2recov)state_n = RECOV;elsestate_n = state_c;endRECOV : beginif(recov2prech)state_n = PRECH; elsestate_n = state_c;enddefault : state_n = IDLE;endcaseendassign wait2prech = (state_c == WAIT ) && end_cnt_xx;assign prech2atref = (state_c == PRECH ) && end_cnt_xx && init_flag;assign prech2idle = (state_c == PRECH ) && end_cnt_xx && ~init_flag;assign atref2mrs = (state_c == ATREF ) && end_cnt_xx && init_flag;assign atref2idle = (state_c == ATREF ) && end_cnt_xx && ~init_flag;assign mrs2idle = (state_c == MRS ) && end_cnt_xx;assign idle2atref = (state_c == IDLE ) && (end_cnt_ref | ref_flag);assign idle2active = (state_c == IDLE ) && ~f_empty;//不能用(f_q[42]|f_q[43])判断,FIFO前显模式下最后一个数据读出后,q端仍然时最后一个数据assign active2read = (state_c == ACTIVE) && end_cnt_xx && f_q[42];assign active2write = (state_c == ACTIVE) && end_cnt_xx && f_q[43];assign write2recov = (state_c == WRITE ) && end_cnt_xx;assign read2prech = (state_c == READ ) && end_cnt_xx;assign recov2prech = (state_c == RECOV ) && end_cnt_xx;//*******************************************************************
//--cnt_xx
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_xx <= 'd0;end else if(add_cnt_xx)begin if(end_cnt_xx)begin cnt_xx <= 'd0;endelse begin cnt_xx <= cnt_xx + 1'b1;end endend assign add_cnt_xx = state_c != IDLE;assign end_cnt_xx = add_cnt_xx && cnt_xx == xx - 1;always @(*)begin case (state_c)WAIT : xx = TIME_200US;PRECH : xx = TIME_PRE;ATREF : xx = TIME_ATREF;MRS : xx = TIME_MRS;ACTIVE: xx = TIME_ACT;READ,WRITE: xx = burst_lenth;RECOV : xx = TIME_RECOV;default: xx = 0;endcaseend//*******************************************************************
//--burst_lenth
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginburst_lenth <= 'd0;end else if(s_burst_begin)begin burst_lenth <= s_burst_count;end end//*******************************************************************
//--init_flag
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begininit_flag <= 1'b1;end else if(mrs2idle)begin init_flag <= 1'b0;end end//*******************************************************************
//--cnt_ref
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_ref <= 'd0;end else if(add_cnt_ref)begin if(end_cnt_ref)begin cnt_ref <= 'd0;endelse begin cnt_ref <= cnt_ref + 1'b1;end endend assign add_cnt_ref = ~init_flag;assign end_cnt_ref = add_cnt_ref && cnt_ref == TIME_REF - 1;//*******************************************************************
//--ref_flag
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginref_flag <= 'd0;end else if(end_cnt_ref)begin ref_flag <= 1'b1;end else if(atref2idle)beginref_flag <= 1'b0;endend//*******************************************************************
//--cmd
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincmd <= CMD_NOP;end else if(wait2prech | recov2prech | read2prech)begin cmd <= CMD_PRECH;end else if(prech2atref |idle2atref)begincmd <= CMD_ATREF;endelse if(atref2mrs)begincmd <= CMD_MRS;endelse if(idle2active)begincmd <= CMD_ACT;endelse if(state_n == READ)begincmd <= CMD_READ;endelse if(state_n == WRITE)begincmd <= CMD_WRITE;endelse begin cmd <= CMD_NOP;end end//*******************************************************************
//--addr
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginaddr <= 'd0;end else if(wait2prech | recov2prech | read2prech)begin addr <= 1'b1 << 10;//Precharge all bankend else if(atref2mrs)beginaddr <= {3'd0,1'b0,2'd0,3'b011,1'b0,3'b000};//突发长度为1,顺序突发,CL为3endelse if(idle2active)beginaddr <= f_q[21:9];//row addrend// else if(active2write | active2read)begin// addr <= {4'd0,f_q[8:0]};// end// else if(state_c == READ | state_c == WRITE)begin// addr <= f_q[8:0] + 1'b1;//强行拼凑对齐,优化方案把读写状态单独拿出去考虑// endelse begin addr <= 'd0;end end//*******************************************************************
//--dq_out dq_out_en
//*******************************************************************assign dq_out_en = state_c == WRITE;assign dq_out = f_q[24 +:16];//*******************************************************************
//--dqm
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begindqm <= 'd0;end else if(state_n == RECOV)begin dqm <= 2'b11;end else begin dqm <= f_q[41:40];end end//*******************************************************************
//--rddata rddata_vld
//*******************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrddata <= 'd0;rddata_vld <= 'd0;end else begin rddata <= dq_in;rddata_vld <= {rddata_vld[1:0],state_c == READ};end end//*******************************************************************
//--输出
//*******************************************************************assign sdram_cke = 1'b1;assign {sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = cmd;assign sdram_bank = f_q[23:22];// assign sdram_addr = addr; //未优化方案assign sdram_addr = (state_c == READ | state_c == WRITE) ? {4'd0,f_q[8:0]} : addr; //优化方案assign sdram_dqm = dqm;assign s_rddata = rddata;assign s_rddata_vld = rddata_vld[2];assign s_rdy = ~f_full;endmodule
六、实验现象
以上就是SDR SDRAM的读写。(如果有错误的地方,还请大家指出来,谢谢!)