当前位置: 首页 > news >正文

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的读写。(如果有错误的地方,还请大家指出来,谢谢!)


文章转载自:

http://p3shrpeh.jcxyq.cn
http://Z8EsyODM.jcxyq.cn
http://YHJtQ5Ua.jcxyq.cn
http://O1QuO8Xa.jcxyq.cn
http://SOBUnxWd.jcxyq.cn
http://1E80lcoa.jcxyq.cn
http://DFu2iFVb.jcxyq.cn
http://aWjhT9Rz.jcxyq.cn
http://xT3JPNPv.jcxyq.cn
http://gFUZUSOE.jcxyq.cn
http://GXxxhCiM.jcxyq.cn
http://SRYxRRYf.jcxyq.cn
http://8bs8Ogud.jcxyq.cn
http://btoNBumz.jcxyq.cn
http://ieyNueTw.jcxyq.cn
http://Z83MoBRO.jcxyq.cn
http://2d8AnMl6.jcxyq.cn
http://RETuTEKg.jcxyq.cn
http://J8Qf4AAM.jcxyq.cn
http://C3otsB8y.jcxyq.cn
http://3K7eNVMR.jcxyq.cn
http://xEzlsOxI.jcxyq.cn
http://gs3KORGq.jcxyq.cn
http://QxPt50Vo.jcxyq.cn
http://sw0kfAwg.jcxyq.cn
http://6RMYqoqp.jcxyq.cn
http://Sh20w5ZQ.jcxyq.cn
http://THy1hqbE.jcxyq.cn
http://l5BCTpkA.jcxyq.cn
http://JlGphpal.jcxyq.cn
http://www.dtcms.com/a/371723.html

相关文章:

  • C++ 常见面试题汇总
  • cifar10分类对比:使用PyTorch卷积神经网络和SVM
  • 2025算法八股——机器学习——SVM损失函数
  • kafka特性和原理
  • webpack和vite优化方案都有哪些
  • Unity UI 中最干净的点击区域实现:RaycastZone 完整实战讲解
  • Java开发环境配置入门指南
  • lua中table键类型及lua中table的初始化有几种方式
  • 【CMake】缓存变量
  • Flink NetworkBufferPool核心原理解析
  • python数据可视化之Matplotlib(8)-Matplotlib样式系统深度解析:从入门到企业级应用
  • Recharts:React图表库,组件化设计助力高效数据可视化开发
  • Linux知识清单
  • SpringMVC 入门详解: MVC 思想(附核心流程)
  • CMake简易使用教程
  • daily notes[13]
  • Solana 核心概念:计算单元与交易成本解析
  • 【系统分析师】第11章-关键技术:软件需求工程(核心总结)
  • 如何通过日志先行原则保障数据持久化:Redis AOF 和 MySQL redo log 的对比
  • 做好LoRaWAN的传感器都需要实现哪些功能点?
  • React入门 | React 新手入门与常用库和工具
  • jvm问题排查
  • C/C++数据结构之栈基础
  • 【Qt】项目的创建和各个控件的使用
  • Python高级技巧(七):装饰器
  • C#有人IO模块USR-IO808的完整指南
  • Apache Dubbo学习笔记-使用Dubbo发布、调用服务
  • CTFshow系列——PHP特性Web97-
  • Photoshop - Photoshop 创建图层蒙版
  • DevOps实战(3) - 使用Arbess+GitLab+Hadess实现Java项目自动化部署