FPGA学习笔记——SDR SDRAM的读写(调用IP核版)

发布于:2025-09-07 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

一、任务

二、需求分析

三、Visio图

四、具体分析

1.需要注意的问题

(1)器件SDRAM需要的时钟

(2)跨时钟域(异步FIFO)

2.模块分析和调用

(1)SDR SDRAM IP核调用

(2)串口收发模块

(3)调用PLL IP核

(4)调用异步FIFO IP核

读FIFO

写FIFO

(5)读写控制模块

五、代码

(1)top.v

(2)uart_tx.v

(3)uart_rx.v

(4)key_filter.v

(5)sdram_ctrl.v

(6)rw_ctrl.v

六、实验现象


一、任务

调用SDR SDRAM IP核对SDR SDRAM进行读写操作,按键按下从SDRAM中读出10个数据,并在串口助手显示,用串口助手每发送10个字节数据,就将数据写入SDRAM中,与SDRAM通信的时钟为100MHz。


二、需求分析

分析任务,需要调用SDR SDRAM IP核来完成任务,然后还需要按键模块来进行读操作,还需要串口收发模块来发送和接收数据,每接收到10个数据就进行写操作,说明需要FIFO来进行缓存,读操作也是一样的,而且这里要注意,SDRAM通信的时钟是100MHz,就需要PLL IP核来产生时钟,最后就是需要读写操作模块。(具体看具体分析)


三、Visio图


四、具体分析

1.需要注意的问题

(1)器件SDRAM需要的时钟

        器件SDRAM需要的100MHz时钟需要进行相位偏移,Slave需要的100MHz时钟不需要相位偏移,为什么器件SDRAM需要偏移时钟,就是让SDRAM在数据的中间采集的数据是稳定的。

(2)跨时钟域(异步FIFO)

跨时钟域常用的方法就是:异步FIFO

        这里涉及50MHz和100MHz,从串口接收模块是50MHz时钟,发送给Slave是需要100MHz时钟,所以必须要使用异步FIFO

2.模块分析和调用

(1)SDR SDRAM IP核调用

调用SDR SDRAM IP核

(2)串口收发模块

串口收发模块

(3)调用PLL IP核

(4)调用异步FIFO IP核

这里异步FIFO IP要调用两个一个读FIFO和一个写FIFO:

读FIFO

写FIFO

(5)读写控制模块

调用的SDR SDRAM IP核的突发长度固定是1,所以这里的连续读或写10个数据,就发10个读指令或写指令,是一个伪突发。(指令都是由SDR SDRAM IP核内部发送的)

当串口助手发送10个字节数据,状态由IDLE状态跳到WRITE状态,由于这里的写FIFO是16bit宽度,所以让这个数据{2{data}},在读出来的时候截取低8位,当10个数据传输完成,进入DONE状态,再进入IDLE状态,等待下一次触发。

当按键按下,状态由IDLE状态跳到READ状态,每接收一个数据就发送,当发送10个数据,状态由READ状态进入DONE状态,再进入IDLE状态,等待下一次触发。(更具体的可以看rw_ctrl.v)


五、代码

(1)top.v

module top( 
    input               clk      ,
    input               rst_n    ,
    //key
    input               key_in   ,
    //uart_tx
    input               rx       ,
    //uart_rx
    output              tx       ,
    //sdram_ctrl
    output              clk_100s   ,
    output      [12:0]  sdram_addr ,//行列地址 
    output      [1:0]   sdram_bank ,//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 
);
//key_filter
wire                key_down;
//uart_rx
wire                uart_rxd   ;
wire        [7:0]   rx_data    ;
wire                rx_data_vld;
//uart_tx
wire        [7:0]   tx_data    ;
wire                tx_data_vld;
wire                uart_txd   ;
wire                ready      ;
//pll
wire    clk_in ;
wire    clk_out;
wire    locked ;


key_filter u_key_filter(
    .clk      (clk      ),
    .rst_n    (rst_n    ),
    .key_in   (key_in   ),
    .key_down (key_down )
);

uart_rx #( .CLOCK_FRQ(50_000_000),
           .BAUD(115200),
           .DATA_LENTH(8)     ,
           .CHECKBIT_SELECT(0),
           .CHECK_TYPE(0)   //偶校验:0  奇校验:1
)uart_rx_inst( 
    /*input               */.clk      (clk        ),
    /*input               */.rst_n    (rst_n      ),
    /*input               */.uart_rxd (rx         ),
    /*output  reg [7:0]   */.dout     (rx_data    ),
    /*output              */.dout_vld (rx_data_vld)
);

uart_tx #(  .CLOCK_FRQ(50_000_000),
                .BAUD(115200),
                .DATA_LENTH(8)     ,
                .CHECKBIT_SELECT(0),
                .CHECK_TYPE(0)   //偶校验:0  奇校验:1
    )uart_tx_inst( 
    /*input                           */.clk      (clk        ),
    /*input                           */.rst_n    (rst_n      ),
    /*input       [DATA_LENTH-1:0]    */.tx_din   (tx_data    ),
    /*input                           */.tx_enable(tx_data_vld),
    /*output  reg                     */.uart_txd (tx         ),
    /*output  reg                     */.ready    (ready      ) //忙闲指示信号
);

sdram_ctrl u_sdram_ctrl(
    .clk         (clk_in      ),
    .clk_in      (clk         ),
    .clk_out     (clk         ),
    .rst_n       (rst_n       ),
    .rx_data     (rx_data     ),
    .rx_data_vld (rx_data_vld ),
    .ready       (ready       ),
    .tx_data     (tx_data     ),
    .tx_data_vld (tx_data_vld ),
    .key_down    (key_down    ),
    .sdram_addr  (sdram_addr  ),
    .sdram_bank  (sdram_bank  ),
    .sdram_cas_n (sdram_cas_n ),
    .sdram_cke   (sdram_cke   ),
    .sdram_cs_n  (sdram_cs_n  ),
    .sdram_dq    (sdram_dq    ),
    .sdram_dqm   (sdram_dqm   ),
    .sdram_ras_n (sdram_ras_n ),
    .sdram_we_n  (sdram_we_n  )
);

pll_0 u_pll_0(
    .areset (~rst_n ),
    .inclk0 (clk    ),
    .c0     (clk_in ),
    .c1     (clk_out),
    .locked (locked )
);

assign  clk_100s = clk_out;

endmodule

(2)uart_tx.v

module uart_tx #(parameter CLOCK_FRQ  = 50_000_000,
                           BAUD       = 9600      ,
                           DATA_LENTH = 8         ,
                           CHECKBIT_SELECT = 0    ,
                           CHECK_TYPE = 0           //偶校验:0  奇校验:1
)( 
    input                           clk      ,
    input                           rst_n    ,
    input       [DATA_LENTH-1:0]    tx_din   ,
    input                           tx_enable,
    output  reg                     uart_txd ,
    output  reg                     ready     //忙闲指示信号
);
//---------<参数定义>------------------------------------------------
    //状态机参数定义
    localparam  IDLE  = 5'b00001,
                START = 5'b00010,
                DATA  = 5'b00100,
                CHECK = 5'b01000,
                STOP  = 5'b10000;
    localparam  BUAD_MAX = CLOCK_FRQ/BAUD;

//---------<内部信号定义>--------------------------------------------
    reg     [4:0]   state_c     ;
    reg     [4:0]   state_n     ;

    reg		[15:0]	cnt_baud	;//波特率计数器
    wire			add_cnt_baud;
    wire			end_cnt_baud;
    reg		[2:0]	cnt_bit	    ;//数据传输的比特计数器
    wire			add_cnt_bit ;
    wire			end_cnt_bit ;

    reg     [7:0]   tx_din_r    ;

    //状态转移条件定义
    wire            idle2start  ;
    wire            start2data  ;
    wire            data2check  ;
    wire            data2stop   ;
    wire            check2stop  ;
    wire            stop2idle   ;

//*******************************************************************
//--tx_din_r
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_din_r <= 'd0;
        end 
        else if(tx_enable)begin 
            tx_din_r <= tx_din;
        end 
    end

//*******************************************************************
//--状态机
//*******************************************************************
    //第一段:同步时序描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end
        
    //第二段:组合逻辑判断状态转移条件,描述状态转移规律
    always @(*) begin
        case(state_c)
            IDLE  : begin
                if(idle2start)
                    state_n = START; 
                else 
                    state_n = state_c;
            end
            START : begin
                if(start2data)
                    state_n = DATA;
                else 
                    state_n = state_c;
            end
            DATA  : begin
                if(data2check)
                    state_n = CHECK;
                else if(data2stop)
                    state_n = STOP;
                else 
                    state_n = state_c;
            end
            CHECK : begin
                if(check2stop)
                    state_n = STOP;
                else 
                    state_n = state_c;
            end
            STOP  : begin
                if(stop2idle)
                    state_n = IDLE; 
                else 
                    state_n = state_c;
            end
            default : state_n = IDLE;
        endcase
    end
    
    assign  idle2start = (state_c == IDLE ) && tx_enable; 
    assign  start2data = (state_c == START) && end_cnt_baud;
    assign  data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;
    assign  data2stop  = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;
    assign  check2stop = (state_c == CHECK) && end_cnt_baud;
    assign  stop2idle  = (state_c == STOP ) && end_cnt_baud;
    
//*******************************************************************
//--cnt_baud
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_baud <= 'd0;
        end 
        else if(add_cnt_baud)begin 
            if(end_cnt_baud)begin 
                cnt_baud <= 'd0;
            end
            else begin 
                cnt_baud <= cnt_baud + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_baud = state_c != IDLE;
    assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));

//*******************************************************************
//--cnt_bit
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = state_c == DATA && end_cnt_baud;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;
    
//*******************************************************************
//--uart_txd
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            uart_txd <= 1'b1;
        end 
        else begin 
            case (state_c)
                IDLE  : uart_txd <= 1'b1;
                START : uart_txd <= 1'b0;
                DATA  : uart_txd <= tx_din_r[cnt_bit];//并转串,LSB
                // CHECK : uart_txd <= ^tx_din_r + CHECK_TYPE;
                CHECK : uart_txd <= CHECK_TYPE ? (^tx_din_r + 1'b1) : (^tx_din_r); 
                STOP  : uart_txd <= 1'b1;
                default: uart_txd <= 1'b1;
            endcase
        end 
    end

//*******************************************************************
//--ready
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            ready <= 'd0;
        end 
        else begin 
            ready <= state_n == IDLE;
        end 
    end
    


endmodule

(3)uart_rx.v

module uart_rx #(parameter CLOCK_FRQ  = 50_000_000,
                           BAUD       = 9600      ,
                           DATA_LENTH = 8         ,
                           CHECKBIT_SELECT = 0    ,
                           CHECK_TYPE = 0           //偶校验:0  奇校验:1
)( 
    input               clk      ,
    input               rst_n    ,
    input               uart_rxd ,
    output  reg [7:0]   dout     ,
    output              dout_vld 
);
//---------<参数定义>------------------------------------------------
    //状态机参数定义
    localparam  IDLE  = 5'b00001,
                START = 5'b00010,
                DATA  = 5'b00100,
                CHECK = 5'b01000,
                STOP  = 5'b10000;
    localparam  BUAD_MAX = CLOCK_FRQ/BAUD;
    
//---------<内部信号定义>--------------------------------------------
    reg     [4:0]   state_c     ;
    reg     [4:0]   state_n     ;
    reg		[15:0]	cnt_baud	;//波特率计数器
    wire			add_cnt_baud;
    wire			end_cnt_baud;
    reg		[2:0]	cnt_bit	   ;//接收bit数量的计数器
    wire			add_cnt_bit;
    wire			end_cnt_bit;

    reg     [2:0]   uart_rxd_r  ;
    wire            rxd_n_edge  ;

    //状态转移条件定义
    wire            idle2start  ;
    wire            start2data  ;
    wire            data2check  ;
    wire            data2stop   ;
    wire            check2stop  ;
    wire            stop2idle   ;

//*******************************************************************
//--打三拍(前两拍同步,后一拍获得边沿)
//*******************************************************************
    //uart_rxd_r
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            uart_rxd_r <= 3'b111;
        end 
        else begin 
            uart_rxd_r <= {uart_rxd_r[1:0],uart_rxd};
        end 
    end
    
    //rxd_n_edge
    assign  rxd_n_edge = ~uart_rxd_r[1] & uart_rxd_r[2];

//*******************************************************************
//--状态机
//*******************************************************************
    //第一段:同步时序描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            state_c <= IDLE;
        end 
        else begin 
            state_c <= state_n;
        end 
    end
        
    //第二段:组合逻辑判断状态转移条件,描述状态转移规律
    always @(*) begin
        case(state_c)
            IDLE  : begin
                if(idle2start)
                    state_n = START; 
                else 
                    state_n = state_c;
            end
            START : begin
                if(start2data)
                    state_n = DATA;
                else 
                    state_n = state_c;
            end
            DATA  : begin
                if(data2check)
                    state_n = CHECK;
                else if(data2stop)
                    state_n = STOP;
                else 
                    state_n = state_c;
            end
            CHECK : begin
                if(check2stop)
                    state_n = STOP;
                else 
                    state_n = state_c;
            end
            STOP  : begin
                if(stop2idle)
                    state_n = IDLE; 
                else 
                    state_n = state_c;
            end
            default : state_n = IDLE;
        endcase
    end
    
    assign  idle2start = (state_c == IDLE ) && rxd_n_edge;
    assign  start2data = (state_c == START) && end_cnt_baud;
    assign  data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;
    assign  data2stop  = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;
    assign  check2stop = (state_c == CHECK) && end_cnt_baud;
    assign  stop2idle  = (state_c == STOP ) && end_cnt_baud;
    
//*******************************************************************
//--cnt_baud
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_baud <= 'd0;
        end 
        else if(add_cnt_baud)begin 
            if(end_cnt_baud)begin 
                cnt_baud <= 'd0;
            end
            else begin 
                cnt_baud <= cnt_baud + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_baud = state_c != IDLE;
    assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));

//*******************************************************************
//--cnt_bit
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = state_c == DATA && end_cnt_baud;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;
    
//*******************************************************************
//--dout
//*******************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout <= 'd0;
        end 
        else if(state_c == DATA && cnt_baud == (BUAD_MAX>>1) - 1)begin 
            dout[cnt_bit] <= uart_rxd_r[2];
            // dout <= {uart_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)begin
            state_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;
                end
                else begin 
                    // state_n = IDLE;
                    state_n = state_c;
                end
            end
            FILTER_DOWN : begin 
                if(filter_down2idle)begin 
                    state_n = IDLE;
                end
                else if(filter_down2hold)begin 
                    state_n = HOLD;
                end
                else begin 
                    state_n = state_c;
                end
            end
            HOLD        : begin 
                if(hold2filter_up)begin 
                    state_n = FILTER_UP;
                end
                else begin 
                    state_n = state_c;
                end
            end
            FILTER_UP   : begin 
                if(filter_up2hold)begin 
                    state_n = HOLD;
                end
                else if(filter_up2idle)begin 
                    state_n = IDLE;
                end
                else begin 
                    state_n = state_c;
                end
            end
            default: state_n = IDLE;
        endcase
    end

    assign 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)begin
            key_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 
    end
    
    assign 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)begin
            cnt_20ms <= 'd0;
        end 
        else if(add_cnt_20ms)begin 
            if(end_cnt_20ms || filter_down2idle || filter_up2hold)begin 
                cnt_20ms <= 'd0;
            end
            else begin 
                cnt_20ms <= cnt_20ms + 1'b1;
            end 
        end
    end 
    
    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)begin
            key_down <= 'd0;
        end 
        else begin 
            key_down <= filter_down2hold ? ~key_r2 : 1'b0;
        end
    end

endmodule

(5)sdram_ctrl.v

module sdram_ctrl( 
    input               clk        ,//100Mhz
    input               clk_in     ,//50Mhz
    input               clk_out    ,//50Mhz
    input               rst_n      ,
    //uart_rx
    input       [7:0]   rx_data    ,
    input               rx_data_vld,
    //uart_tx
    input               ready      ,
    output      [7:0]   tx_data    ,
    output              tx_data_vld,
    //key_filter
    input               key_down   ,
    //sdram memory
    output      [12:0]  sdram_addr ,//行列地址 
    output      [1:0]   sdram_bank ,//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    [23:0]  avm_address    ;
wire    [15:0]  avm_wrdata     ;
wire            avm_read_n     ;
wire            avm_write_n    ;
wire    [15:0]  avm_rddata     ;
wire            avm_rddata_vld ;
wire            avm_waitrequest;

rw_sdram u_rw_sdram(
    .clk             (clk             ),
    .clk_in          (clk_in          ),
    .clk_out         (clk_out         ),
    .rst_n           (rst_n           ),
    .rx_data         (rx_data         ),
    .rx_data_vld     (rx_data_vld     ),
    .ready           (ready           ),
    .tx_data         (tx_data         ),
    .tx_data_vld     (tx_data_vld     ),
    .key_down        (key_down        ),
    .avm_address     (avm_address     ),
    .avm_wrdata      (avm_wrdata      ),
    .avm_read_n      (avm_read_n      ),
    .avm_write_n     (avm_write_n     ),
    .avm_rddata      (avm_rddata      ),
    .avm_rddata_vld  (avm_rddata_vld  ),
    .avm_waitrequest (avm_waitrequest )
);
    
sdram_intf u_sdram_intf(
    .avs_address       (avm_address    ),
    .avs_byteenable_n  (2'b00          ),
    .avs_chipselect    (1'b1           ),
    .avs_writedata     (avm_wrdata     ),
    .avs_read_n        (avm_read_n     ),
    .avs_write_n       (avm_write_n    ),
    .avs_readdata      (avm_rddata     ),
    .avs_readdatavalid (avm_rddata_vld ),
    .avs_waitrequest   (avm_waitrequest),
    .clk_clk           (clk            ),
    .reset_reset_n     (rst_n          ),
    .sdram_addr        (sdram_addr     ),
    .sdram_ba          (sdram_bank     ),
    .sdram_cas_n       (sdram_cas_n    ),
    .sdram_cke         (sdram_cke      ),
    .sdram_cs_n        (sdram_cs_n     ),
    .sdram_dq          (sdram_dq       ),
    .sdram_dqm         (sdram_dqm      ),
    .sdram_ras_n       (sdram_ras_n    ),
    .sdram_we_n        (sdram_we_n     )
);

    
    
endmodule

(6)rw_ctrl.v

module rw_sdram#(parameter BURST_LENTH = 10)( 
    input               clk            ,//100Mhz
    input               clk_in         ,//50Mhz
    input               clk_out        ,//50Mhz
    input               rst_n          ,
    //uart_rx
    input       [7:0]   rx_data        ,
    input               rx_data_vld    ,
    //uart_tx
    input               ready          ,
    output      [7:0]   tx_data        ,
    output              tx_data_vld    ,
    //key_filter
    input               key_down       ,
    //sdram ip: Avalon-MM Master Interface 
    output      [23:0]  avm_address    ,//{bank[1],row[12:0],bank[0],colum[8:0]}   
    output      [15:0]  avm_wrdata     ,   
    output              avm_read_n     ,      
    output              avm_write_n    ,      
    input       [15:0]  avm_rddata     ,     
    input               avm_rddata_vld ,
    input               avm_waitrequest  
);

localparam IDLE  = 4'b0001,
           WRITE = 4'b0010,
           READ  = 4'b0100,
           DONE  = 4'b1000;

reg     [3:0]   state_c ;
reg     [3:0]   state_n ;    

reg		[3:0]	cnt_burst	 ;//突发读/写长度计数器
wire			add_cnt_burst;
wire			end_cnt_burst;
reg     [23:0]  wr_addr    ;//{bank[1:0],rwo[12:0],colum[8:0]}
wire			add_wr_addr;
wire			end_wr_addr;
reg		[23:0]	rd_addr	   ;//{bank[1:0],rwo[12:0],colum[8:0]}
wire			add_rd_addr;
wire			end_rd_addr;

wire            idle2write;
wire            idle2read ;
wire            write2done;
wire            read2done ;

wire            wrfifo_wrreq;
wire            wrfifo_rdempty;
wire    [3:0]   wrfifo_rdusedw;
wire            wrfifo_wrfull ;        
wire            wrfifo_rdreq  ;
wire    [15:0]  wrfifo_q      ;

wire            rdfifo_wrreq  ;
wire            rdfifo_rdempty;
wire            rdfifo_wrfull ;
wire            rdfifo_rdreq  ;
wire    [15:0]  rdfifo_q      ;

//---------<FIFO例化>------------------------------------------------- 
wr_fifo u_wr_fifo(
    .aclr    (~rst_n        ),
    .data    ({2{rx_data} } ),
    .rdclk   (clk           ),
    .rdreq   (wrfifo_rdreq  ),
    .wrclk   (clk_in        ),
    .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 && ~avm_waitrequest;

rd_fifo u_rd_fifo(
    .aclr    (~rst_n        ),
    .data    (avm_rddata    ),
    .rdclk   (clk_out       ),
    .rdreq   (rdfifo_rdreq  ),
    .wrclk   (clk           ),
    .wrreq   (rdfifo_wrreq  ),
    .q       (rdfifo_q      ),
    .rdempty (rdfifo_rdempty),
    .wrfull  (rdfifo_wrfull )
);

assign rdfifo_wrreq = ~rdfifo_wrfull && avm_rddata_vld;
assign rdfifo_rdreq = ~rdfifo_rdempty && ready;

//---------<State Machine>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state_c <= IDLE;
    end 
    else begin 
        state_c <= state_n;
    end 
end
    
always @(*) begin
    case(state_c)
        IDLE  : begin
            if(idle2write)
                state_n = WRITE;
            else if(idle2read)
                state_n = READ;
            else
                state_n = state_c;
        end
        WRITE : begin
            if(write2done)
                state_n = DONE;
            else
                state_n = state_c;
        end
        READ  : begin
            if(read2done)
                state_n = DONE;
            else
                state_n = state_c;
        end
        DONE  : state_n = IDLE;
        default : ;
    endcase
end

assign 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;

//---------<cnt_burst>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_burst <= 'd0;
    end 
    else if(add_cnt_burst)begin 
        if(end_cnt_burst)begin 
            cnt_burst <= 'd0;
        end
        else begin 
            cnt_burst <= cnt_burst + 1'b1;
        end 
    end
end 

assign add_cnt_burst = (state_c == WRITE | state_c == READ) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == BURST_LENTH - 1;

//---------<写地址>------------------------------------------------- 
// always @(posedge clk or negedge rst_n)begin 
//     if(!rst_n)
//         wr_addr <= 24'd0;
//     else if(state_c == WRITE && ~avm_waitrequest)
//         wr_addr <= wr_addr + 1'b1;
// end

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        wr_addr <= 'd0;
    end 
    else if(add_wr_addr)begin 
        if(end_wr_addr)begin 
            wr_addr <= 'd0;
        end
        else begin 
            wr_addr <= wr_addr + 1'b1;
        end 
    end
end 

assign add_wr_addr = state_c == WRITE && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff;

//---------<rd_addr>------------------------------------------------- 
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        rd_addr <= 'd0;
    end 
    else if(add_rd_addr)begin 
        if(end_rd_addr)begin 
            rd_addr <= 'd0;
        end
        else begin 
            rd_addr <= rd_addr + 1'b1;
        end 
    end
end 

assign add_rd_addr = state_c == READ && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff;

//---------<输出>------------------------------------------------- 
assign avm_address = (state_c == WRITE) ? {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} : 
                                            {rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;
assign avm_wrdata = wrfifo_q; 
assign avm_read_n = ~(state_c == READ) ;
assign avm_write_n = ~(state_c ==WRITE);

assign tx_data = rdfifo_q[7:0];   
assign tx_data_vld = rdfifo_rdreq;

endmodule

六、实验现象


以上就是SDR SDRAM的读写。(如果有错误的地方,还请大家指出来,谢谢!)


网站公告

今日签到

点亮在社区的每一天
去签到