FPGA project : sdram

这篇具有很好参考价值的文章主要介绍了FPGA project : sdram。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

sdram读写控制器

实验目标:

设计并实现一个 SDRAM 数据读写控制器,使用 PC 机通过串口向 SDRAM 写入 10 字
节数据,并将写入的 10 字节数据读出,通过串口回传至 PC 机,在串口助手上位机上打印
显示回传数据。

框图设计:

第一部分:

sdram基本操作实的实现sdram_ctrl

要实现数据的读写,还要有初始化和刷新操作。所以该模块要有分别产生这四条指令的模块。

由于时序冲突问题,刷新,和读写指令存在优先级的问题。所以还应有仲裁模块。

如下:

指令产生模块应该有指令端口,bank地址端口,行列地址端口,结束标志信号。

刷新模块的请求信号传向仲裁模块,反馈给刷新模块一个使能信号。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 写指令模块的写数据,写地址,写突发长度。必不可少。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

需要注意的是有几个输入端口是外界给的,有几个输出端口直接输出出去。

整体:

 FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 第二部分:

sdram控制器sdram_top

fifo控制模块,实现sdram读写数据的跨时钟域处理。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 第三部分:

uart_sdram。uart与sdram的数据交互与最顶层模块设计。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 指令:

1,取消设备选择:1XXX;

2,无操作指令:0111;

3,配置模式指令:0000;+ addr A12~A0

4,预充电指令:0010;+ addr

5,自动刷新与自刷新指令:0001;

6,激活指令:0011;+addr/bank

7,写指令:0100;+bank/col;

8,读指令:0101;+bank/col;

明天完成初始化模块,加油!!!

这个sdram读写控制器工程确实挺大的。

代码设计:

sdram_ctrl:

sdram_init:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节设计思路:

1 预充电指令,在写入0010指令+A10 == 1 之前,先等待200us,加载稳定时钟,并传递空操作指令。

2 写入预充电之后要等待TRP = 2  ,并传递空操作指令0111。

3 TRP时间结束后,写入自动刷新指令0001,并等待TRC = 7 ,并传递空操作指令。

4 TRC时间结束后,再次刷新指令。

5 TRC时间结束后,模式配置寄存器指令0000。+A11~A0;并等待TMRD = 时间,并传递空操作。

6 等待TMRD结束后,完成初始化。

时序图:FPGA project : sdram,野火征途pro,fpga开发,1024程序员节
代码: 
// 初始化模块,产生控指令时序,预充电时序,刷新时序,模式配置时序。sys_clk == 100mHz
module sdram_init(
    input       wire            sys_clk     , 
    input       wire            sys_rst_n   ,

    output      reg     [3:0]   init_cmd    ,
    output      reg     [1:0]   init_ba     ,
    output      reg     [12:0]  init_addr   ,
    output      reg             init_end
);

    // parameter
    parameter   T_POWER   = 200_00       , // 等待时钟稳定。200us。
                T_RP      = 2            , // 2个时钟周期的预充电时间。
                T_RFC     = 7            , // 7个时钟周期的刷新时间。
                T_MRD     = 3            , // 3个时钟周期的模式配置时间。
                MAX_CNT_AR= 8            ; // 刷新次数。
    localparam  INIT_NOP  = 4'b0111      , // 空指令
                INIT_PREC = 4'b0010      , // 预充电
                INIT_REF  = 4'b0001      , // 自动刷新
                INIT_MOD  = 4'b0000      ; // 模式配置
    localparam  INIT_IDLE = 8'b0000_0001 ,
                INIT_PRE  = 8'b0000_0010 , // 预充电
                INIT_TRP  = 8'b0000_0100 ,
                INIT_AR   = 8'b0000_1000 , // 刷新
                INIT_TFR  = 8'b0001_0000 ,
                INIT_MRS  = 8'b0010_0000 , // 模式配置
                INIT_TMRD = 8'b0100_0000 ,
                INIT_ENDS = 8'b1000_0000 ; // 完成
    // reg signal define
    reg     [7:0]       state_c  ;
    reg     [7:0]       state_n  ;
    reg     [15:0]      cnt_init ;
    reg     [3:0]       cnt_ar   ; // 记录刷新次数,以产生状态跳转条件。
    // wire signal define
    wire                INIT_IDLEtoINIT_PRE ;
    wire                INIT_TRPtoINIT_AR   ;  
    wire                INIT_TFRtoINIT_MRS  ; 
    wire                INIT_TFRtoINIT_AR   ;
    wire                INIT_TMRDtoINIT_ENDS;
    /************************************************************************/
    // reg     [7:0]       state_c  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= INIT_IDLE ;
        else
            state_c <= state_n ;
    end
    // reg     [7:0]       state_n  ;
    always @(*) begin
        case(state_c)
        INIT_IDLE : if(INIT_IDLEtoINIT_PRE)
                        state_n = INIT_PRE ;
                    else 
                        state_n = INIT_IDLE ;
        INIT_PRE  : state_n = INIT_TRP ;
        INIT_TRP  : if(INIT_TRPtoINIT_AR)
                        state_n = INIT_AR ;
                    else 
                        state_n = INIT_TRP ;
        INIT_AR   : state_n = INIT_TFR ;
        INIT_TFR  : if(INIT_TFRtoINIT_MRS)
                        state_n = INIT_MRS ;
                    else if(INIT_TFRtoINIT_AR) // 没有达到规定的刷新次数,回到刷新指令。
                        state_n = INIT_AR ;
                    else 
                        state_n = INIT_TFR ;
        INIT_MRS  : state_n = INIT_TMRD ;
        INIT_TMRD : if(INIT_TMRDtoINIT_ENDS)
                        state_n = INIT_ENDS ;
                    else 
                        state_n = INIT_TMRD ;
        INIT_ENDS : state_n = INIT_ENDS ; // 保持在此状态。
        default   : state_n = INIT_IDLE ;
        endcase
    end
    assign INIT_IDLEtoINIT_PRE  = (state_c == INIT_IDLE) && (cnt_init == T_POWER - 1) ; 
    assign INIT_TRPtoINIT_AR    = (state_c == INIT_TRP ) && (cnt_init == T_RP    - 1) ;
    assign INIT_TFRtoINIT_MRS   = (state_c == INIT_TFR ) && ((cnt_ar == MAX_CNT_AR - 1) && (cnt_init == T_RFC - 1)) ;
    assign INIT_TFRtoINIT_AR    = (state_c == INIT_TFR ) && ((cnt_ar != MAX_CNT_AR - 1) && (cnt_init == T_RFC - 1)) ;
    assign INIT_TMRDtoINIT_ENDS = (state_c == INIT_TMRD) && (cnt_init == T_MRD    - 1) ;
    // reg     [15:0]       cnt_init ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_init <= 16'd0 ;
        else 
        case (state_c)
        INIT_IDLE : if(cnt_init == T_POWER - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_PRE  : cnt_init <= 16'd0 ;
        INIT_TRP  : if(cnt_init == T_RP    - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_AR   : cnt_init <= 16'd0 ;
        INIT_TFR  : if(cnt_init == T_RFC - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_MRS  : cnt_init <= 16'd0 ;
        INIT_TMRD : if(cnt_init == T_MRD    - 1)
                        cnt_init <= 16'd0 ;
                    else 
                        cnt_init <= cnt_init + 1'b1 ;
        INIT_ENDS : cnt_init <= 16'd0 ;
        default   : cnt_init <= 16'd0 ;
        endcase
    end
    // reg     [3:0]       cnt_ar   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_ar <= 4'd0 ;
        else if((state_c == INIT_TFR) && (cnt_init == T_RFC - 1) && (cnt_ar == MAX_CNT_AR - 1))
            cnt_ar <= 4'd0 ;
        else if((state_c == INIT_TFR) && (cnt_init == T_RFC - 1))
            cnt_ar <= cnt_ar + 1'b1 ;
        else
            cnt_ar <= cnt_ar ;  
    end
    // reg     [3:0]   init_cmd    ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            init_cmd <= INIT_NOP ;
        else 
        case (state_c)
        INIT_IDLE : init_cmd <= INIT_NOP ;
        INIT_PRE  : init_cmd <= INIT_PREC;
        INIT_TRP  : init_cmd <= INIT_NOP ;
        INIT_AR   : init_cmd <= INIT_REF ;
        INIT_TFR  : init_cmd <= INIT_NOP ;
        INIT_MRS  : init_cmd <= INIT_MOD ;
        INIT_TMRD : init_cmd <= INIT_NOP ;
        INIT_ENDS : init_cmd <= INIT_NOP ;
        default   : init_cmd <= INIT_NOP ;
        endcase
    end
    // reg     [1:0]   init_ba     ,
    // reg     [12:0]  init_addr   ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            init_ba <= 2'b11 ;
            init_addr <= 13'h1FFF ;
        end
        else if(state_c == INIT_MRS) begin
            init_ba <= 2'b00 ;
            init_addr <= 13'b000_0_00_011_0_111 ;
        end
        else begin
            init_ba <= 2'b11 ;
            init_addr <= 13'h1FFF ;
        end
    end
    // reg             init_end
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            init_end <= 1'b0 ;
        else if(state_c == INIT_ENDS)
            init_end <= 1'b1 ;
        else 
            init_end <= init_end ;
    end
endmodule
仿真: 
`timescale 1ns/1ns
module test_sdram_init ();
    reg              sys_clk     ; // 100mHz
    reg              sys_rst_n   ;

    wire     [3:0]   init_cmd    ;
    wire     [1:0]   init_ba     ;
    wire     [12:0]  init_addr   ;
    wire             init_end    ;

sdram_init sdram_init_inst(
    .sys_clk    ( sys_clk  ) , 
    .sys_rst_n  ( sys_rst_n) ,

    .init_cmd   ( init_cmd ) ,
    .init_ba    ( init_ba  ) ,
    .init_addr  ( init_addr) ,
    .init_end   ( init_end )
);

    parameter CYCLE = 10 ; // 10ns
    initial begin
        sys_clk = 1'b1 ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE) ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
        $stop ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

sdram_aref:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

设计思路: 

分析时序图可知,刷新模块要做的是先传预充电时序,然后传刷新时序>=2次。简化版的初始化。

自动刷新周期为7.5us。设参数MAX_CNT_REF = 750.

在初始化结束信号拉高后,cnt_750us开始计数,计满后aref_req拉高,等待仲裁模块给出aref_en拉高,开始进行刷新状态机跳转。当跳转到预充电命令时,说明已经开始刷新了,aref_req拉低。

根据时序,绘制状态跳转。

当进入结束状态时,发出一个周期的aref_end==1信号,仲裁模块把aref_en拉低。完成一次刷新。

cnt_750us计数器是在init_end==1后循环计数的。

cnt_ar记录刷新次数,要刷新两次才可以。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

时序图:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 代码:
// 模块说明:刷新模块,在初始化完成后,以750us为周期进行刷新时序的输出(要受仲裁模块控制),但刷新周期不会大于750us。
module sdram_aref(
    input       wire                sys_clk     ,
    input       wire                sys_rst_n   ,
    input       wire                init_end    ,
    input       wire                aref_en     ,

    output      reg                 aref_req    ,
    output      reg     [3:0]       aref_cmd    ,
    output      wire    [1:0]       aref_ba     ,
    output      wire    [12:0]      aref_addr   ,
    output      wire                aref_end    
);
    // parameter    
    parameter   MAX_CNT_750US   = 750        ;
    // localparam
    localparam  T_RP            = 2          , // 2个时钟周期的预充电时间。
                T_RFC           = 7          , // 7个时钟周期的刷新时间。
                MAX_CNT_AR      = 2          , // 刷新次数。
                AREF_NOP        = 4'b0111    , // 空指令
                AREF_PREC       = 4'b0010    , // 预充电
                AREF_REFC       = 4'b0001    ; // 自动刷新,指令与状态机命名冲突了,后面加个C。

    localparam  AREF_IDLE       = 6'b00_0001 ,
                AREF_PCHA       = 6'b00_0010 ,
                AREF_TRP        = 6'b00_0100 ,
                AREF_REF        = 6'b00_1000 ,
                AREF_TRF        = 6'b01_0000 ,
                AREF_END        = 6'b10_0000 ;
    // reg signal define
    reg     [5:0]       state_c   ;
    reg     [5:0]       state_n   ;
    reg     [9:0]       cnt_750us ;
    reg     [3:0]       cnt_aref  ;
    reg                 cnt_ar    ;
    // wire signal define 
    wire                AREF_IDLEtoAREF_PCHA;    
    wire                AREF_TRPtoAREF_REF  ;      
    wire                AREF_TRFtoAREF_REF  ;      
    wire                AREF_TRFtoAREF_END  ;      
    /*************************************************************/
    // reg     [5:0]       state_c   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= AREF_IDLE ;
        else 
            state_c <= state_n ;
    end
    // reg     [5:0]       state_n   ;
    always @(*) begin
        case(state_c)
        AREF_IDLE : if(AREF_IDLEtoAREF_PCHA)
                        state_n = AREF_PCHA ;
                    else 
                        state_n = AREF_IDLE ;
        AREF_PCHA : state_n = AREF_TRP ;
        AREF_TRP  : if(AREF_TRPtoAREF_REF)
                        state_n = AREF_REF ;
                    else 
                        state_n = AREF_TRP ;
        AREF_REF  : state_n = AREF_TRF ;
        AREF_TRF  : if(AREF_TRFtoAREF_END)
                        state_n = AREF_END ;
                    else if(AREF_TRFtoAREF_REF)
                        state_n = AREF_REF ;
                    else 
                        state_n = AREF_TRF ;
        AREF_END  : state_n = AREF_IDLE ;
        default   : state_n = AREF_IDLE ;
        endcase
    end
    assign  AREF_IDLEtoAREF_PCHA    = (state_c == AREF_IDLE) && ( aref_en  ) ; 
    assign  AREF_TRPtoAREF_REF      = (state_c == AREF_TRP ) && ( cnt_aref ) ; 
    assign  AREF_TRFtoAREF_REF      = (state_c == AREF_TRF ) && ( cnt_aref == 6 && ~cnt_ar ) ; 
    assign  AREF_TRFtoAREF_END      = (state_c == AREF_TRF ) && ( cnt_aref == 6 &&  cnt_ar ) ; 
    // reg     [9:0]       cnt_750us ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_750us <= 10'd0 ;
        else if(init_end) 
            begin 
                if(cnt_750us == MAX_CNT_750US - 1)
                    cnt_750us <= 10'd0 ;
                else 
                    cnt_750us <= cnt_750us + 1'b1 ;
            end
            else
                cnt_750us <= 10'd0 ;
    end
    // reg     [3:0]       cnt_aref  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_aref <= 4'd0 ;
        else
        case (state_c)
        AREF_IDLE : cnt_aref <= 4'd0 ;
        AREF_PCHA : cnt_aref <= 4'd0 ;
        AREF_TRP  : if(cnt_aref == T_RP -1)
                        cnt_aref <= 4'd0 ;
                    else    
                        cnt_aref <= cnt_aref + 1'b1 ;
        AREF_REF  : cnt_aref <= 4'd0 ;
        AREF_TRF  : if(cnt_aref == T_RFC - 1)
                        cnt_aref <= 4'd0 ;
                    else    
                        cnt_aref <= cnt_aref + 1'b1 ;
        AREF_END  : cnt_aref <= 4'd0 ;
        default   : cnt_aref <= 4'd0 ;
        endcase
    end
    // reg                 cnt_ar    ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_ar <= 1'b0 ;
        else if(state_c == AREF_TRF && cnt_aref == T_RFC - 1) // 由于是一位宽的,就不用写归零情况了。
            cnt_ar <= cnt_ar + 1'b1 ;
        else 
            cnt_ar <= cnt_ar ;
    end
    // output signal describe
    // reg                 aref_req  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            aref_req <= 1'b0 ;
        else if(cnt_750us == MAX_CNT_750US - 1)
            aref_req <= 1'b1 ;
        else if(state_c == AREF_PCHA)
            aref_req <= 1'b0 ;
        else 
            aref_req <= aref_req ;
    end
    // reg     [3:0]       aref_cmd  ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            aref_cmd <= 4'd0 ;
        else
        case (state_c)
        AREF_IDLE : aref_cmd <= AREF_NOP ;
        AREF_PCHA : aref_cmd <= AREF_PREC;
        AREF_TRP  : aref_cmd <= AREF_NOP ;
        AREF_REF  : aref_cmd <= AREF_REFC;
        AREF_TRF  : aref_cmd <= AREF_NOP ;
        AREF_END  : aref_cmd <= AREF_NOP ;
        default   : aref_cmd <= AREF_NOP ;
        endcase
    end
    // wire     [1:0]       aref_ba   ;
    assign  aref_ba = 2'b11 ;
    // reg     [12:0]      aref_addr ;
    assign  aref_addr = 13'h1FFF ;
    // wire                aref_end  ;
    assign  aref_end = (state_c == AREF_END) ? 1'b1 : 1'b0 ;
endmodule
仿真:

在仿真代码中模拟了一小部分仲裁。

`timescale 1ns/1ns
module test_sdram_aref ();
    reg             sys_clk     ; // 50mHz
    reg             sys_rst_n   ;

    reg             aref_en     ; // 自刷新模块的输入,应该是仲裁模块传出。

    wire    [3:0]   init_cmd    ; // 初始化模块的输出。
    wire    [1:0]   init_ba     ;
    wire    [12:0]  init_addr   ;
    wire            init_end    ;

    wire            aref_req    ; // 自刷新模块输出
    wire    [3:0]   aref_cmd    ; 
    wire    [1:0]   aref_ba     ;  
    wire    [12:0]  aref_addr   ;
    wire            aref_end    ; 

    wire    [3:0]   sdram_cmd   ; 
    wire    [1:0]   sdram_ba    ;  
    wire    [12:0]  sdram_addr  ;
    assign          sdram_cmd   = (init_end) ? aref_cmd  : init_cmd  ; // 经过仲裁后的命令与地址。
    assign          sdram_ba    = (init_end) ? aref_ba   : init_ba   ;  
    assign          sdram_addr  = (init_end) ? aref_addr : init_addr ;

    wire            c0_sig      ; // pll的输出。
    wire            c1_sig      ;
    wire            c2_sig      ;
    wire            locked_sig  ;
    wire            rst_n       ; // 全局复位。
    assign          rst_n = sys_rst_n && (locked_sig) ;
sdram_aref sdram_aref_inst(
    .sys_clk                            ( c1_sig     ) ,
    .sys_rst_n                          ( rst_n      ) ,
    .init_end                           ( init_end   ) ,
    .aref_en                            ( aref_en    ) ,
  
    .aref_req                           ( aref_req   ) ,
    .aref_cmd                           ( aref_cmd   ) ,
    .aref_ba                            ( aref_ba    ) ,
    .aref_addr                          ( aref_addr  ) ,
    .aref_end                           ( aref_end   ) 
);

clk_gen	clk_gen_inst (
	.areset                             (~sys_rst_n  ) ,
	.inclk0                             ( sys_clk    ) ,

	.c0                                 ( c0_sig     ) , // 50
	.c1                                 ( c1_sig     ) , // 100
	.c2                                 ( c2_sig     ) , // 100+shift30
	.locked                             ( locked_sig )
);

sdram_model_plus sdram_model_plus_inst(
    .Dq                                 (  ) , 
    .Addr                               ( sdram_addr  ) , 
    .Ba                                 ( sdram_ba    ) , 
    .Clk                                ( c2_sig      ) , 
    .Cke                                ( 1'b1        ) , 
    .Cs_n                               ( sdram_cmd[3]) , 
    .Ras_n                              ( sdram_cmd[2]) , 
    .Cas_n                              ( sdram_cmd[1]) , 
    .We_n                               ( sdram_cmd[0]) , 
    .Dqm                                ( 2'b00       ) ,
    .Debug                              ( 1'b1        )  
);

sdram_init sdram_init_inst(
    .sys_clk                            ( c1_sig   ) , 
    .sys_rst_n                          ( rst_n    ) ,

    .init_cmd                           ( init_cmd ) ,
    .init_ba                            ( init_ba  ) ,
    .init_addr                          ( init_addr) ,
    .init_end                           ( init_end )
);

    parameter   CYCLE = 20 ; // 10ns
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    // aref_en 仲裁
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)
            aref_en <= 1'b0 ;
        else if(init_end && aref_req)
            aref_en <= 1'b1 ;
        else if(aref_end)
            aref_en <= 1'b0 ;
        else 
            aref_en <= aref_en ;
    end

    initial begin
        sys_clk = 1'b1    ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE)          ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 sdram_write:

设计思路:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 指令:激活指令0011,空指令0111,写指令0100,突发停止指令0110(也就是中止页写的指令)。

由于使用的模式是突发一页的读写模式,给出列首地址后,会自动生成下一个地址。

1,传送激活指令+bank地址+列地址,激活特定行。0011

2,等待tRCD,激活时间。此过程中,传送空指令。0111

3.tRCD时间结束后,传送写指令,同时A8~A0开始传送列地址(首地址),DQ开始传送要写入的数据。

4,在最后一个数据写入后的下一个时钟周期写入突发中止指令。

5,突发中止指令写入后,页写突发操作完成。0110

但是在设计的时候,在页写突发指令完成后加了预充电指令。

设计状态机时,把突发中止指令放进了写数据状态的最后。

一个细节:在写指令传送时,DQ已经开始传递第一个数据了。

sdram_en用作三态门的写数据使能,输出使能。

第一部分:状态机。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 第二部分:数据写响应设计。wr_ack。

第三部分:各输出信号的设计与实现。

时序图:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

代码:
// 模块说明:写数据模块,在初始化完成后,fifo_ctrl模块产生wr_req信号传给仲裁模块,仲裁模块传回写使能信号,开始写数据是时序传输。
// input       wire    [23:0]      write_addr  , // 高两位是bank地址,中间13位为数据写入位置的行地址,后9位为列首地址。
// input       wire    [9:0]       write_burst_len , // 写突发长度由fifo_ctrl模块给出。
// output      wire                wr_sdram_en , // 用作三态输出使能信号。
// wr_ack 传给fifo_ctrl模块,用来做读数据使能,告知fifo_ctrl模块,已经开始读时序了,请求信号可以拉低了。
// wr_burst_len    , // 写突发长度,控制WR_DATA状态下传输数据的个数。每一行有512个存储单元。
module sdram_write(
    input       wire                sys_clk         ,
    input       wire                sys_rst_n       ,
    input       wire                init_end        ,
    input       wire                wr_en           ,
    input       wire    [23:0]      wr_addr         ,
    input       wire    [15:0]      wr_data         ,
    input       wire    [9:0]       wr_burst_len    , // 写突发长度,控制WR_DATA状态下传输数据的个数。

    output      reg     [3:0]       write_cmd       ,
    output      reg     [1:0]       write_ba        ,
    output      reg     [12:0]      write_addr      ,
    output      wire                wr_end          ,
    output      wire                wr_sdram_en     ,
    output      wire    [15:0]      wr_sdram_data   ,
    output      wire                wr_ack          
);
    // parameter
    parameter   TRCD        = 2       ,
                TRP         = 2       ,
                NOP         = 4'b0111 ,
                ACTIVE      = 4'b0011 ,
                WRITE       = 4'b0100 ,
                B_STOP      = 4'b0110 ,
                P_CHARGE    = 4'b0010 ;
    // localparam
    localparam  WR_IDLE     = 3'b000 ,
                WR_ACTIVE   = 3'b001 ,
                WR_TRCD     = 3'b011 ,
                WR_WRITE    = 3'b010 ,
                WR_DATA     = 3'b110 ,
                WR_PRE      = 3'b111 ,
                WR_TRP      = 3'b101 ,
                WR_END      = 3'b100 ;

    // reg signal define
    reg     [2:0]       state_c      ;
    reg     [2:0]       state_n      ;
    reg     [9:0]       cnt_wr       ; // 位宽注意。
    reg                 cnt_wr_rst   ; // 用组合逻辑赋值。
    // wire dignal define
    wire                trcd_end     ;
    wire                twrite_end   ;
    wire                trp_end      ;
    wire                WR_IDLEtoWR_ACTIVE ;
    wire                WR_TRCDtoWR_WRITE  ;
    wire                WR_DATAtoWR_PRE    ;
    wire                WR_TRPtoWR_END     ;
    /********************************************************************/
    // reg     [2:0]       state_c     ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= WR_IDLE ;
        else
            state_c <= state_n ;
    end
    // reg     [2:0]       state_n     ;
    always @(*) begin
        case(state_c)
        WR_IDLE   : if(WR_IDLEtoWR_ACTIVE)
                        state_n = WR_ACTIVE ;
                    else 
                        state_n = WR_IDLE ;
        WR_ACTIVE : state_n = WR_TRCD ;
        WR_TRCD   : if(WR_TRCDtoWR_WRITE)
                        state_n = WR_WRITE ;
                    else 
                        state_n = WR_TRCD ;
        WR_WRITE  : state_n = WR_DATA ;
        WR_DATA   : if(WR_DATAtoWR_PRE)
                        state_n = WR_PRE ;
                    else 
                        state_n = WR_DATA ;
        WR_PRE    : state_n = WR_TRP ;
        WR_TRP    : if(WR_TRPtoWR_END)
                        state_n = WR_END ;
                    else 
                        state_n = WR_TRP ;
        WR_END    : state_n = WR_IDLE ;
        default   : state_n = WR_IDLE ;
        endcase
    end
    assign  WR_IDLEtoWR_ACTIVE  = (state_c == WR_IDLE) && (wr_en     ) && (init_end);
    assign  WR_TRCDtoWR_WRITE   = (state_c == WR_TRCD) && (trcd_end  ) && (init_end);
    assign  WR_DATAtoWR_PRE     = (state_c == WR_DATA) && (twrite_end) && (init_end);
    assign  WR_TRPtoWR_END      = (state_c == WR_TRP ) && (trp_end   ) && (init_end);
    // reg     [9:0]       cnt_wr      ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_wr <= 10'd0 ;
        else if(cnt_wr_rst)
            cnt_wr <= 10'd0 ;
        else 
            cnt_wr <= cnt_wr + 1'b1 ;
    end
    // reg                 cnt_wr_rst  ;
    always @(*) begin
        case(state_c)
        WR_IDLE   : cnt_wr_rst = 1'b1 ;
        WR_ACTIVE : cnt_wr_rst = trcd_end ;
        WR_TRCD   : cnt_wr_rst = trcd_end ;
        WR_WRITE  : cnt_wr_rst = 1'b1 ;
        WR_DATA   : cnt_wr_rst = twrite_end ;
        WR_PRE    : cnt_wr_rst = trp_end ;
        WR_TRP    : cnt_wr_rst = trp_end ;
        WR_END    : cnt_wr_rst = 1'b1 ;
        default   : cnt_wr_rst = 1'b1 ;
        endcase
    end
    // wire                trcd_end    ;
    assign  trcd_end = (state_c == WR_TRCD && cnt_wr == TRCD) ? 1'b1 : 1'b0 ;
    // wire                twrite_end  ;
    assign  twrite_end = (state_c == WR_DATA && cnt_wr == (wr_burst_len - 1)) ? 1'b1 : 1'b0 ;
    // wire                trp_end     ;
    assign  trp_end = (state_c == WR_TRP && cnt_wr == TRP) ? 1'b1 : 1'b0 ;
    // output signal describe
    // wire                wr_end      ,
    assign  wr_end = (state_c == WR_END) ? 1'b1 : 1'b0 ;
    // reg     [3:0]       write_cmd   ,
    // reg     [1:0]       write_ba    ,
    // reg     [12:0]      write_addr  ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            write_cmd   <= NOP      ;
            write_ba    <= 2'b11    ;
            write_addr  <= 13'h1FFF ;
        end else begin
            case (state_c)
            WR_IDLE  : 
                begin
                    write_cmd   <= NOP      ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;   
                end
            WR_ACTIVE: 
                begin
                    write_cmd   <= ACTIVE   ;
                    write_ba    <= wr_addr[23:22] ;
                    write_addr  <= wr_addr[21:9 ] ; //wr_addr[23:0]
                end
            WR_TRCD  : 
                begin
                    write_cmd   <= NOP      ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;   
                end
            WR_WRITE : 
                begin
                    write_cmd   <= WRITE    ;
                    write_ba    <= wr_addr[23:22] ;
                    write_addr  <= {4'b0000,wr_addr[ 8:0 ]} ; 
                end
            WR_DATA  : 
                if(!twrite_end)
                begin
                    write_cmd   <= NOP      ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;
                end
                else 
                begin
                    write_cmd   <= B_STOP   ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;
                end
            WR_PRE   : 
                begin
                    write_cmd   <= P_CHARGE ; // 预充电指令,一开始忘记赋值了。
                    write_ba    <= wr_addr[23:22] ;
                    write_addr  <= 13'h0400 ;
                end
            WR_TRP   : 
                begin
                    write_cmd   <= NOP      ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;
                end
            WR_END   : 
                begin
                    write_cmd   <= NOP      ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;
                end
            default  : 
                begin
                    write_cmd   <= NOP      ;
                    write_ba    <= 2'b11    ;
                    write_addr  <= 13'h1FFF ;
                end
            endcase
        end
    end
    // wire                wr_sdram_en ,
    assign  wr_sdram_en = (state_c == WR_DATA) ? 1'b1 : 1'b0 ;
    // reg     [15:0]      wr_sdram_data ,
    assign  wr_sdram_data = (wr_sdram_en) ? wr_data : 16'd0 ;
    // wire                wr_ack   
    assign  wr_ack = (((state_c == WR_WRITE) || (state_c == WR_DATA)) && (cnt_wr <= wr_burst_len - 2)) ? 1'b1 : 1'b0 ;   
endmodule
`timescale 1ns/1ns
module test_sdram_write ();
    reg             sys_clk     ; // 50mHz
    reg             sys_rst_n   ;

    wire    [3:0]   init_cmd    ;
    wire    [1:0]   init_ba     ;
    wire    [12:0]  init_addr   ;
    wire            init_end    ;

    wire    [15:0]  sdram_data  ;
    wire    [3:0]   sdram_cmd   ; 
    wire    [1:0]   sdram_ba    ;  
    wire    [12:0]  sdram_addr  ;

    wire            c0_sig      ;
    wire            c1_sig      ;
    wire            c2_sig      ;
    wire            locked_sig  ;
    wire            rst_n       ;
    assign          rst_n = sys_rst_n && (locked_sig) ;

    reg             wr_en           ;
    reg     [15:0]  wr_data         ;
    wire    [3:0]   write_cmd       ;
    wire    [1:0]   write_ba        ;
    wire    [12:0]  write_addr      ;
    wire            wr_end          ;
    wire            wr_sdram_en     ;
    wire    [15:0]  wr_sdram_data   ;
    wire            wr_ack          ; 
    reg             wr_en_flag      ;
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)  
            wr_en_flag <= 1'b0 ;
        else if(wr_end)
            wr_en_flag <= ~wr_en_flag ;
    end
    // always @(posedge c1_sig or negedge sys_rst_n) begin
    //     if(~sys_rst_n) 
    //         wr_en <= 1'b0 ;
    //     else if(wr_end)
    //         wr_en <= 1'b0 ;
    //     else if(init_end && !wr_en_flag)
    //         wr_en <= 1'b1 ;
    //     else 
    //         wr_en <= wr_en ;
    // end
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            wr_en <= 1'b0 ;
        else if(wr_end)
            wr_en <= 1'b0 ;
        else if(init_end)
            wr_en <= 1'b1 ;
        else 
            wr_en <= wr_en ;
    end
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)
            wr_data <= 16'd0 ;
        else if(wr_ack == 0)
            wr_data <= 16'd0 ;
        else
            wr_data <= wr_data + 1'b1 ;
    end
    assign          sdram_cmd   = (init_end) ? write_cmd  : init_cmd  ; 
    assign          sdram_ba    = (init_end) ? write_ba   : init_ba   ; 
    assign          sdram_addr  = (init_end) ? write_addr : init_addr ;
    assign          sdram_data  = (wr_sdram_en) ? wr_data : 16'hzzzz     ;
sdram_write sdram_write_inst(
    .sys_clk                ( c1_sig        ) ,
    .sys_rst_n              ( rst_n         ) ,
    .init_end               ( init_end      ) ,
    .wr_en                  ( wr_en         ) ,
    .wr_addr                ( 24'd0         ) ,
    .wr_data                ( wr_data       ) ,
    .wr_burst_len           ( 10            ) , // 写突发长度,控制WR_DATA状态下传输数据的个数。

    .write_cmd              ( write_cmd     ) ,
    .write_ba               ( write_ba      ) ,
    .write_addr             ( write_addr    ) ,
    .wr_end                 ( wr_end        ) ,
    .wr_sdram_en            ( wr_sdram_en   ) ,
    .wr_sdram_data          ( wr_sdram_data ) ,
    .wr_ack                 ( wr_ack        )  
);


clk_gen	clk_gen_inst (
	.areset             (~sys_rst_n     ) ,
	.inclk0             ( sys_clk       ) ,

	.c0                 ( c0_sig        ) , // 50
	.c1                 ( c1_sig        ) , // 100
	.c2                 ( c2_sig        ) , // 100+shift30
	.locked             ( locked_sig    )
);
sdram_model_plus sdram_model_plus_inst(
    .Dq                                 ( sdram_data  ) , 
    .Addr                               ( sdram_addr  ) , 
    .Ba                                 ( sdram_ba    ) , 
    .Clk                                ( c2_sig      ) , 
    .Cke                                ( 1'b1        ) , 
    .Cs_n                               ( sdram_cmd[3]) , 
    .Ras_n                              ( sdram_cmd[2]) , 
    .Cas_n                              ( sdram_cmd[1]) , 
    .We_n                               ( sdram_cmd[0]) , 
    .Dqm                                ( 2'b00       ) ,
    .Debug                              ( 1'b1        )  
);

sdram_init sdram_init_inst(
    .sys_clk                            ( c1_sig   ) , 
    .sys_rst_n                          ( rst_n    ) ,

    .init_cmd                           ( init_cmd ) ,
    .init_ba                            ( init_ba  ) ,
    .init_addr                          ( init_addr) ,
    .init_end                           ( init_end )
);

    parameter   CYCLE = 20 ; // 10ns
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    initial begin
        sys_clk = 1'b1 ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE) ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
        $stop ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
仿真:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

sdram_read:

设计思路:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 1,传送激活指令(0011),同时传递bank地址与行地址,激活特定行。

2,然后进入激活等待状态,等待时间tRCD,此过程中传送空指令。

3, 然后传动读指令,同时A8~A0写入列地址。

4,然后跳转到潜伏状态,3个clk,结束后,开始读数据。

5,进入读数据状态。设读取的数据为N。

6,从读指令周期开始计数,N个周期后,写入突发中止指令。

7,突发中止指令写入后,DQ继续传送2个周期数据。数据传输完成后,sdram页突发读操作完成。

8,然后要发预充电命令,进入预充电等待状态,结束后进入读结束状态。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 第一部分状态机;

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

第二部分读突发中止指令应该何时写入?

 assign rd_bust_flag = (state_c == RD_DATA && cnt_clk == rd_bust_len - CL - 1).

提前CL个时钟周期传送中止指令,又因为从零开始计数。

第三部分输出信号。

注意,写计数器时,先写归零条件,把它放在最高优先级。

本模块偏移时钟向前移动和向后移动会造成很大的不同。

由于我一开始设置的向后移动30,导致潜伏期状态,读数据状态,ack响应信号,与官方教程里的代码很不一样。

由于时序图是仿照官方画的,和代码有很多不相符合的地方,但是对于代码而言只需改动两个地方。

tcl_end与rd_bust_flag!!!!!!!!!!!

时序图:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

代码:
// 读时序产生模块。先传送激活指令,然后传送读指令,然后读突发中止指令,然后预充电指令。
// 注意rd_data的跨时钟域处理。
module sdram_read(
    input       wire                sys_clk         ,
    input       wire                sys_rst_n       ,
    input       wire                init_end        ,
    input       wire    [23:0]      rd_addr         ,
    input       wire    [15:0]      rd_data         ,
    input       wire    [9:0]       rd_burst_len    ,
    input       wire                rd_en           ,

    output      wire                rd_end          ,
    output      reg     [3:0]       read_cmd        ,
    output      reg     [1:0]       read_ba         ,
    output      reg     [12:0]      read_addr       ,
    output      wire    [15:0]      rd_sdram_data   ,
    output      wire                rd_ack          
);
    // parameter 
    parameter   NOP       = 4'b0111 , // 空指令
                P_CHARGE  = 4'b0010 , // 预充电指令
                ACTIVE    = 4'b0011 , // 激活指令
                READ      = 4'b0101 , // 读指令
                B_STOP    = 4'b0110 ; // 读中止指令
    parameter   T_RCD     = 2       , // 激活周等待期
                T_CL      = 3       , // 列潜伏
                T_RP      = 2       ; // 预充电
    localparam  RD_IDLE   = 9'b0_0000_0001 , // 空闲状态
                RD_ACTIVE = 9'b0_0000_0010 , // 激活状态
                RD_TRCD   = 9'b0_0000_0100 , // 激活等待状态
                RD_READ   = 9'b0_0000_1000 , // 传送读指令状态
                RD_CL     = 9'b0_0001_0000 , // 潜伏状态
                RD_DATA   = 9'b0_0010_0000 , // 读数据状态
                RD_PRE    = 9'b0_0100_0000 , // 预充电指令传送状态
                RD_TRP    = 9'b0_1000_0000 , // 预充电等待状态
                RD_END    = 9'b1_0000_0000 ; // 读结束
    // reg signal define
    // reg     [15:0]      rd_data_reg ; 
    reg     [9:0]       state_c     ;
    reg     [9:0]       state_n     ; // 用的组合逻辑
    reg     [9:0]       cnt_read    ;
    reg                 cnt_rd_rst  ; // 用的组合逻辑
    // wire signal define
    wire                trcd_end    ;
    wire                tcl_end     ;
    wire                tread_end   ;
    wire                trp_end     ;
    wire                rd_bust_flag;
    wire                RD_IDLEtoRD_ACTIVE  ;
    wire                RD_TRCDtoRD_READ    ;
    wire                RD_CLtoRD_DATA      ;
    wire                RD_DATAto_RD_PRE    ;
    wire                RD_TRPtoRD_END      ;
    /****************************************************************************/
    // // reg     [15:0]      rd_data_reg ; 
    // always @(posedge sys_clk or negedge sys_rst_n) begin
    //     if(~sys_rst_n) 
    //         rd_data_reg <= 16'd0 ; 
    //     else 
    //         rd_data_reg <= rd_data ; 
    // end
    // reg     [9:0]       state_c     ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= RD_IDLE ;
        else
            state_c <= state_n ;
    end
    // reg     [9:0]       state_n     ;
    always @(*) begin
        case (state_c)
        RD_IDLE   : if(RD_IDLEtoRD_ACTIVE)
                        state_n = RD_ACTIVE ;
                    else 
                        state_n = RD_IDLE ;
        RD_ACTIVE : state_n = RD_TRCD ;
        RD_TRCD   : if(RD_TRCDtoRD_READ) // 可以写成三元运算符:state_n <= (RD_TRCDtoRD_READ) ? RD_READ : RD_TRCD ;
                        state_n = RD_READ ;
                    else 
                        state_n = RD_TRCD ;
        RD_READ   : state_n = RD_CL ;
        RD_CL     : if(RD_CLtoRD_DATA)
                        state_n = RD_DATA ;
                    else 
                        state_n = RD_CL ;
        RD_DATA   : if(RD_DATAto_RD_PRE)
                        state_n = RD_PRE ;
                    else 
                        state_n = RD_DATA ;
        RD_PRE    : state_n = RD_TRP ;
        RD_TRP    : if(RD_TRPtoRD_END)
                        state_n = RD_END ;
                    else 
                        state_n = RD_TRP ;
        RD_END    : state_n = RD_IDLE ;
        default   : state_n = RD_IDLE ;
        endcase
    end
    assign  RD_IDLEtoRD_ACTIVE  = ( state_c == RD_IDLE ) && ( rd_en    ) && ( init_end ) ;
    assign  RD_TRCDtoRD_READ    = ( state_c == RD_TRCD ) && ( trcd_end ) && ( init_end ) ;
    assign  RD_CLtoRD_DATA      = ( state_c == RD_CL   ) && ( tcl_end  ) && ( init_end ) ;
    assign  RD_DATAto_RD_PRE    = ( state_c == RD_DATA ) && ( tread_end) && ( init_end ) ;
    assign  RD_TRPtoRD_END      = ( state_c == RD_TRP  ) && ( trp_end  ) && ( init_end ) ;
    // reg     [9:0]       cnt_read    ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            cnt_read <= 10'd0 ;
        else if(cnt_rd_rst)
            cnt_read <= 10'd0 ;
        else    
            cnt_read <= cnt_read + 1'b1 ;
    end
    // wire                cnt_rd_rst  ;
    always @(*) begin
        case(state_c)
        RD_IDLE   : cnt_rd_rst = 1'b1 ;
        RD_ACTIVE : cnt_rd_rst = trcd_end ;
        RD_TRCD   : cnt_rd_rst = trcd_end ;
        RD_READ   : cnt_rd_rst = tcl_end ;
        RD_CL     : cnt_rd_rst = tcl_end ;
        RD_DATA   : cnt_rd_rst = tread_end ;
        RD_PRE    : cnt_rd_rst = trp_end ;
        RD_TRP    : cnt_rd_rst = trp_end ;
        RD_END    : cnt_rd_rst = 1'b1 ;
        default   : cnt_rd_rst = 1'b1 ; 
        endcase
    end
    // wire                trcd_end    ;T_RP
    assign  trcd_end = (state_c == RD_TRCD && cnt_read == T_RCD) ? 1'b1 : 1'b0 ;
    // wire                tcl_end     ;
    assign  tcl_end  = (state_c == RD_CL   && cnt_read == T_CL - 1) ? 1'b1 : 1'b0 ; // 注意这里时序图多画了一个时钟周期。要计数到2,而不是3。
    // wire                tread_end   ;
    assign  tread_end= (state_c == RD_DATA && cnt_read == rd_burst_len) ? 1'b1 : 1'b0 ;
    // wire                trp_end     ;
    assign  trp_end  = (state_c == RD_TRP  && cnt_read == T_RP) ? 1'b1 : 1'b0 ;
    // wire                rd_bust_flag;
    assign  rd_bust_flag = (state_c == RD_DATA && cnt_read == (rd_burst_len - T_CL)) ? 1'b1 : 1'b0 ;
    // // output 
    // wire                rd_end       ;
    assign  rd_end  = (state_c == RD_END) ? 1'b1 : 1'b0 ;
    // reg     [3:0]       read_cmd     ;
    // reg     [1:0]       read_ba      ;
    // reg     [12:0]      read_addr    ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
        begin
            read_cmd  <= NOP ;
            read_ba   <= 2'b11 ;
            read_addr <= 13'h1FFF ;
        end
        else 
        case (state_c)
        RD_IDLE   : 
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        RD_ACTIVE : 
                    begin
                        read_cmd  <= ACTIVE ;
                        read_ba   <= rd_addr[23:22] ;
                        read_addr <= rd_addr[21:9] ;
                    end
        RD_TRCD   : 
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        RD_READ   : 
                    begin
                        read_cmd  <= READ ;
                        read_ba   <= rd_addr[23:22] ;
                        read_addr <= rd_addr[8:0] ;
                    end
        RD_CL     : 
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        RD_DATA   : 
                    if(!rd_bust_flag)
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
                    else
                    begin
                        read_cmd  <= B_STOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        RD_PRE    : 
                    begin
                        read_cmd  <= P_CHARGE ;
                        read_ba   <= rd_addr[23:22] ;
                        read_addr <= 13'h0400 ; // 辅助预充电,选中所有bank 
                    end
        RD_TRP    : 
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        RD_END    : 
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        default   : 
                    begin
                        read_cmd  <= NOP ;
                        read_ba   <= 2'b11 ;
                        read_addr <= 13'h1FFF ;
                    end
        endcase
    end
    // wire    [15:0]      rd_sdram_data;
    assign  rd_sdram_data = (rd_ack) ? rd_data : 16'd0 ; // rd_data_reg
    // wire                rd_ack       ;
    assign  rd_ack = (state_c == RD_DATA && (cnt_read >= 1 && cnt_read <= rd_burst_len)) ? 1'b1 : 1'b0 ;
endmodule
`timescale 1ns/1ns
module test_sdram_read ();
//

reg                 rd_en           ;

wire                rd_end          ;
wire    [3:0]       read_cmd        ;
wire    [1:0]       read_ba         ;
wire    [12:0]      read_addr       ;
wire    [15:0]      rd_sdram_data   ;
wire                rd_ack          ;
//
    reg             sys_clk     ; // 50mHz
    reg             sys_rst_n   ;

    wire    [3:0]   init_cmd    ;
    wire    [1:0]   init_ba     ;
    wire    [12:0]  init_addr   ;
    wire            init_end    ;

    wire    [15:0]  sdram_data  ;
    wire    [3:0]   sdram_cmd   ; 
    wire    [1:0]   sdram_ba    ;  
    wire    [12:0]  sdram_addr  ;

    wire            c0_sig      ;
    wire            c1_sig      ;
    wire            c2_sig      ;
    wire            locked_sig  ;
    wire            rst_n       ;
    assign          rst_n = sys_rst_n && (locked_sig) ;

    reg             wr_en           ;
    reg     [15:0]  wr_data         ;
    wire    [3:0]   write_cmd       ;
    wire    [1:0]   write_ba        ;
    wire    [12:0]  write_addr      ;
    wire            wr_end          ;
    wire            wr_sdram_en     ;
    wire    [15:0]  wr_sdram_data   ;
    wire            wr_ack          ; 
    reg             wr_en_flag      ;
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)  
            wr_en_flag <= 1'b0 ;
        else if(wr_end)
            wr_en_flag <= ~wr_en_flag ;
    end
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            wr_en <= 1'b0 ;
        else if(wr_end)
            wr_en <= 1'b0 ;
        else if(init_end && !wr_en_flag)
            wr_en <= 1'b1 ;
        else 
            wr_en <= wr_en ;
    end
    // always @(posedge c1_sig or negedge sys_rst_n) begin
    //     if(~sys_rst_n) 
    //         wr_en <= 1'b0 ;
    //     else if(wr_end)
    //         wr_en <= 1'b0 ;
    //     else if(init_end)
    //         wr_en <= 1'b1 ;
    //     else 
    //         wr_en <= wr_en ;
    // end
    reg     rd_en_flag ;
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)  
            rd_en_flag <= 1'b0 ;
        else if(rd_end)
            rd_en_flag <= ~rd_en_flag ;
    end
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            rd_en <= 1'b0 ;
        else if(rd_end || rd_en_flag)
            rd_en <= 1'b0 ;
        else if(init_end == 1 && wr_en == 0 && wr_en_flag == 1 && ~rd_en_flag)
            rd_en <= 1'b1 ;
        else 
            rd_en <= rd_en ;
    end 
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)
            wr_data <= 16'd0 ;
        else if(wr_ack == 0)
            wr_data <= 16'd0 ;
        else
            wr_data <= wr_data + 1'b1 ;
    end
    assign          sdram_cmd   = (init_end) ? (( wr_en ) ?  write_cmd  : read_cmd ) : init_cmd  ; 
    assign          sdram_ba    = (init_end) ? (( wr_en ) ?  write_ba   : read_ba  ) : init_ba   ; 
    assign          sdram_addr  = (init_end) ? (( wr_en ) ?  write_addr : read_addr) : init_addr ;
    assign          sdram_data  = (wr_sdram_en) ? wr_data : 16'hzzzz     ;
sdram_read sdram_read_inst(
    .sys_clk                ( c1_sig       ) ,
    .sys_rst_n              ( rst_n        ) ,
    .init_end               ( init_end     ) ,
    .rd_addr                ( 24'd0        ) ,
    .rd_data                ( sdram_data   ) ,
    .rd_burst_len           ( 10'd20       ) ,
    .rd_en                  ( rd_en        ) ,

    .rd_end                 ( rd_end       ) ,
    .read_cmd               ( read_cmd     ) ,
    .read_ba                ( read_ba      ) ,
    .read_addr              ( read_addr    ) ,
    .rd_sdram_data          ( rd_sdram_data) ,
    .rd_ack                 ( rd_ack       )  
);
sdram_write sdram_write_inst(
    .sys_clk                ( c1_sig        ) ,
    .sys_rst_n              ( rst_n         ) ,
    .init_end               ( init_end      ) ,
    .wr_en                  ( wr_en         ) ,
    .wr_addr                ( 24'd0         ) ,
    .wr_data                ( wr_data       ) ,
    .wr_burst_len           ( 10            ) , // 写突发长度,控制WR_DATA状态下传输数据的个数。

    .write_cmd              ( write_cmd     ) ,
    .write_ba               ( write_ba      ) ,
    .write_addr             ( write_addr    ) ,
    .wr_end                 ( wr_end        ) ,
    .wr_sdram_en            ( wr_sdram_en   ) ,
    .wr_sdram_data          ( wr_sdram_data ) ,
    .wr_ack                 ( wr_ack        )  
);


clk_gen	clk_gen_inst (
	.areset             (~sys_rst_n     ) ,
	.inclk0             ( sys_clk       ) ,

	.c0                 ( c0_sig        ) , // 50
	.c1                 ( c1_sig        ) , // 100
	.c2                 ( c2_sig        ) , // 100+shift30
	.locked             ( locked_sig    )
);
sdram_model_plus sdram_model_plus_inst(
    .Dq                                 ( sdram_data  ) , 
    .Addr                               ( sdram_addr  ) , 
    .Ba                                 ( sdram_ba    ) , 
    .Clk                                ( c2_sig      ) , 
    .Cke                                ( 1'b1        ) , 
    .Cs_n                               ( sdram_cmd[3]) , 
    .Ras_n                              ( sdram_cmd[2]) , 
    .Cas_n                              ( sdram_cmd[1]) , 
    .We_n                               ( sdram_cmd[0]) , 
    .Dqm                                ( 2'b00       ) ,
    .Debug                              ( 1'b1        )  
);

sdram_init sdram_init_inst(
    .sys_clk                            ( c1_sig   ) , 
    .sys_rst_n                          ( rst_n    ) ,

    .init_cmd                           ( init_cmd ) ,
    .init_ba                            ( init_ba  ) ,
    .init_addr                          ( init_addr) ,
    .init_end                           ( init_end )
);

    parameter   CYCLE = 20 ; // 10ns
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    initial begin
        sys_clk = 1'b1 ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE) ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
        $stop ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;
endmodule
仿真:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 sdram_arbit:

设计思路:

仲裁模块通过状态机协调各个模块的正常进行,每项操作的实现都需要对sdram写入对应操作指令,且各项操作指令不同,为了方便赋值与准确传输指令,内部声明sdram_cmd信号,用来保存不同状态下对应的指令。

如状态机所示:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

we_n:写使能有效信号,低电平有效,使能写操作和预充电。

 FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

时序图:

看着状态机与之前画的时序图就能写出来代码。

代码:
module  sdram_arbit(
    input       wire            sys_clk   ,
    input       wire            sys_rst_n ,

    input       wire    [3:0]   init_cmd  ,
    input       wire    [1:0]   init_ba   ,
    input       wire    [12:0]  init_addr ,
    input       wire            init_end  ,

    input       wire            aref_req  ,
    input       wire    [3:0]   aref_cmd  ,
    input       wire    [1:0]   aref_ba   ,
    input       wire    [12:0]  aref_addr ,
    input       wire            aref_end  ,

    input       wire            read_req  ,
    input       wire    [3:0]   read_cmd  ,
    input       wire    [1:0]   read_ba   ,
    input       wire    [12:0]  read_addr ,
    input       wire            read_end  ,

    input       wire            write_req ,
    input       wire    [3:0]   write_cmd ,
    input       wire    [1:0]   write_ba  ,
    input       wire    [12:0]  write_addr,
    input       wire            wr_end    ,
    input       wire            wr_sdram_en   ,
    input       wire    [15:0]  wr_sdram_data ,

    output      wire            sdram_cke , // 时钟使能
    output      wire            sdram_csn ,
    output      wire            sdram_ras ,
    output      wire            sdram_cas ,
    output      wire            sdram_wen , // 写使能。
    output      reg     [1:0]   sdram_bank,
    output      reg     [12:0]  sdram_addr,

    inout       wire    [15:0]  sdram_dq  ,

    output      wire            aref_en   ,
    output      wire            write_en  ,
    output      wire            read_en   
);
    // localparam
    localparam  NOP   = 4'b0111   ;
    localparam  INIT  = 5'b0_0001 ,
                ARBIT = 5'b0_0010 , // 仲裁状态
                WRITE = 5'b0_0100 ,
                READ  = 5'b0_1000 ,
                AREF  = 5'b1_0000 ;

    // reg signal define
    reg     [4:0]   state_c   ;
    reg     [4:0]   state_n   ;
    reg     [3:0]   sdram_cmd ; // 组合逻辑
    // wire signal define
    wire            INITtoARBIT ; 
    wire            ARBITtoAREF ; 
    wire            ARBITtoWRITE;
    wire            ARBITtoREAD ; 
    wire            WRITEtoARBIT;
    wire            READtoARBIT ; 
    wire            AREFtoARBIT ; 
    /****************************************************************/
    // reg     [4:0]   state_c   ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            state_c <= INIT ;
        else 
            state_c <= state_n ;
    end
    // reg     [4:0]   state_n   ;
    always @(*) begin
        case(state_c)
        INIT  : 
                if(INITtoARBIT)
                    state_n = ARBIT ;
                else 
                    state_n = INIT ;
        ARBIT : 
                if(ARBITtoAREF)
                    state_n = AREF ;
                else if(ARBITtoWRITE)
                    state_n = WRITE ;
                else if(ARBITtoREAD)
                    state_n = READ ;
                else 
                    state_n = ARBIT ;
        WRITE : 
                if(WRITEtoARBIT)
                    state_n = ARBIT ;
                else 
                    state_n = WRITE ;
        READ  : 
                if(READtoARBIT)
                    state_n = ARBIT ;
                else 
                    state_n = READ ;
        AREF  : if(AREFtoARBIT)
                    state_n = ARBIT ;
                else 
                    state_n = AREF ;
        default:    state_n = ARBIT ;
        endcase
    end
    assign  INITtoARBIT     = ( state_c == INIT  ) && ( init_end    ) ;
    assign  ARBITtoAREF     = ( state_c == ARBIT ) && ( aref_req    ) ;
    assign  ARBITtoWRITE    = ( state_c == ARBIT ) && ( write_req   ) ;
    assign  ARBITtoREAD     = ( state_c == ARBIT ) && ( read_req    ) ;
    assign  WRITEtoARBIT    = ( state_c == WRITE ) && ( wr_end      ) ;
    assign  READtoARBIT     = ( state_c == READ  ) && ( read_end    ) ;
    assign  AREFtoARBIT     = ( state_c == AREF  ) && ( aref_end    ) ;
    // output 
    // wire            sdram_cke , 
    assign  sdram_cke = 1'b1 ;
    // wire            sdram_csn ,
    // wire            sdram_ras ,
    // wire            sdram_cas ,
    // wire            sdram_wen , 
    assign  sdram_csn = sdram_cmd[3] ;
    assign  sdram_ras = sdram_cmd[2] ;
    assign  sdram_cas = sdram_cmd[1] ;
    assign  sdram_wen = sdram_cmd[0] ;
    // assign {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = sdram_cmd;
    // reg     [3:0]   sdram_cmd ;
    // reg     [1:0]   sdram_bank,
    // reg     [12:0]  sdram_addr,
    always @(*) begin
        case (state_c)
        INIT  : 
                begin
                    sdram_cmd  = init_cmd  ;
                    sdram_bank = init_ba   ;
                    sdram_addr = init_addr ;
                end
        ARBIT : 
                begin
                    sdram_cmd  = NOP      ;
                    sdram_bank = 2'b11    ;
                    sdram_addr = 13'h1FFF ;
                end
        WRITE : 
                begin
                    sdram_cmd  = write_cmd  ;
                    sdram_bank = write_ba   ;
                    sdram_addr = write_addr ;
                end
        READ  : 
                begin
                    sdram_cmd  = read_cmd  ;
                    sdram_bank = read_ba   ;
                    sdram_addr = read_addr ;
                end
        AREF  : 
                begin
                    sdram_cmd  = aref_cmd  ;
                    sdram_bank = aref_ba   ;
                    sdram_addr = aref_addr ;
                end
        default:    
                begin
                    sdram_cmd  = NOP      ;
                    sdram_bank = 2'b11    ;
                    sdram_addr = 13'h1FFF ;
                end
        endcase
    end
    // wire    [15:0]  sdram_dq  ,
    assign  sdram_dq = (wr_sdram_en) ? wr_sdram_data : 16'hzzzz ;
    // wire            aref_en   ,
    assign  aref_en  = (state_c == AREF) ? 1'b1 : 1'b0 ; // 灵活运用三段式状态机。
    // wire            write_en  ,
    assign  write_en  = (state_c == WRITE) ? 1'b1 : 1'b0 ;
    // wire            read_en    
    assign  read_en  = (state_c == READ) ? 1'b1 : 1'b0 ;

endmodule
`timescale 1ns/1ns
module test_sdram_ctrl();
    reg             sys_clk     ; // 50mHz
    reg             sys_rst_n   ;


    wire            c0_sig      ;
    wire            c1_sig      ;
    wire            c2_sig      ;
    wire            locked_sig  ;
    wire            rst_n       ;
    assign          rst_n = sys_rst_n && (locked_sig) ;

    wire            wr_end ; // 需要从sdram_write模块引出。
    assign  wr_end = sdram_ctrl_inst.sdram_write_inst.wr_end ;
    // sdram_ctrl模块输入端口信号。
    reg             sdram_wr_req  ;
    reg     [23:0]  sdram_wr_addr ;
    reg     [15:0]  sdram_wr_data ;
    reg     [9:0]   sdram_wr_bust ;

    reg             sdram_rd_req  ;
    reg     [23:0]  sdram_rd_addr ;
    reg     [9:0]   sdram_rd_bust ;
    // sdram_ctrl模块的输出信号。
    wire            sdram_sck     ;
    wire            sdram_cke     ; 
    wire            sdram_csn     ;
    wire            sdram_ras     ;
    wire            sdram_cas     ;
    wire            sdram_wen     ; 
    wire    [1:0]   sdram_bank    ;
    wire    [12:0]  sdram_addr    ;
    wire    [15:0]  sdram_dq      ;
    wire            sdram_wr_ack  ;
    wire            sdram_rd_ack  ;
    wire            sdram_init_end;
    wire    [15:0]  sdram_data_out;

clk_gen	clk_gen_inst (
	.areset                     (~sys_rst_n      ) ,
	.inclk0                     ( sys_clk        ) ,

	.c0                         ( c0_sig         ) , // 50
	.c1                         ( c1_sig         ) , // 100
	.c2                         ( c2_sig         ) , // 100+shift30
	.locked                     ( locked_sig     )
);
sdram_ctrl sdram_ctrl_inst(
    .sys_clk                    ( c1_sig         ) ,
    .sys_rst_n                  ( rst_n          ) ,
    .clk_shift                  ( c2_sig         ) ,

    .sdram_wr_req               ( sdram_wr_req   ) ,
    .sdram_wr_addr              ( sdram_wr_addr  ) ,
    .sdram_wr_data              ( sdram_wr_data  ) ,
    .sdram_wr_bust              ( sdram_wr_bust  ) ,

    .sdram_rd_req               ( sdram_rd_req   ) ,
    .sdram_rd_addr              ( sdram_rd_addr  ) ,
    .sdram_rd_bust              ( sdram_rd_bust  ) ,

    .sdram_sck                  ( sdram_sck      ) ,        
    .sdram_cke                  ( sdram_cke      ) ,         // 时钟使能
    .sdram_csn                  ( sdram_csn      ) ,        
    .sdram_ras                  ( sdram_ras      ) ,        
    .sdram_cas                  ( sdram_cas      ) ,        
    .sdram_wen                  ( sdram_wen      ) ,         // 写使能。
    .sdram_bank                 ( sdram_bank     ) ,        
    .sdram_addr                 ( sdram_addr     ) ,        

    .sdram_dq                   ( sdram_dq       ) ,        

    .sdram_wr_ack               ( sdram_wr_ack   ) ,        
    .sdram_rd_ack               ( sdram_rd_ack   ) ,        
    .sdram_init_end             ( sdram_init_end ) ,        
    .sdram_data_out             ( sdram_data_out ) 
);      
sdram_model_plus sdram_model_plus_inst(
    .Dq                         ( sdram_dq       ) , 
    .Addr                       ( sdram_addr     ) , 
    .Ba                         ( sdram_bank     ) , 
    .Clk                        ( sdram_sck      ) , 
    .Cke                        ( sdram_cke      ) , 
    .Cs_n                       ( sdram_csn      ) , 
    .Ras_n                      ( sdram_ras      ) , 
    .Cas_n                      ( sdram_cas      ) , 
    .We_n                       ( sdram_wen      ) , 
    .Dqm                        ( 2'b00          ) ,
    .Debug                      ( 1'b1           )  
);

    parameter   CYCLE = 20 ; // 10ns
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    defparam    sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 75 ;
    /**********************************************************************/
    reg     init_end_reg1 ;
    reg     init_end_reg2 ;
    wire    wr_flag ;
    always @(posedge c1_sig or negedge rst_n) begin
        if(~rst_n) begin
            init_end_reg1 <= 1'b0 ;
            init_end_reg2 <= 1'b0 ;
        end 
        else begin
            init_end_reg1 <= sdram_init_end ;
            init_end_reg2 <= init_end_reg1 ;
        end
    end
    assign  wr_flag = init_end_reg1 && ~init_end_reg2 ;
    // reg             sdram_wr_req  ;
    always @(posedge c1_sig or negedge rst_n) begin
        if(~rst_n) 
            sdram_wr_req <= 1'b0 ;
        else if(wr_flag) // 初始化完成之后第一个始终周期就拉高写亲求。
            sdram_wr_req <= 1'b1 ;
        else if(sdram_wr_ack) // sdram_ctrl回应给FIFO控制模块一个写响应信号后,写请求就可以拉低了。
            sdram_wr_req <= 1'b0 ;
        else    
            sdram_wr_req <= sdram_wr_req ;
    end
    // reg     [23:0]  sdram_wr_addr ;
    // reg     [15:0]  sdram_wr_data ;
    // reg     [9:0]   sdram_wr_bust ;
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~sys_rst_n)
            sdram_wr_data <= 16'd0 ;
        else if(sdram_wr_ack == 0)
            sdram_wr_data <= 16'd0 ;
        else
            sdram_wr_data <= sdram_wr_data + 1'b1 ;
    end
    initial begin
        sdram_wr_addr <= 24'd0 ; // bank00,行13'd0,列9'd0;
        sdram_wr_bust <= 10'd20 ; // 写入10个数据。
    end 
    // reg             sdram_rd_req  ;
    // always @(posedge c1_sig or negedge sys_rst_n) begin
    //     if(~rst_n)
    //         sdram_rd_req <= 1'b0;
    //     else if(wr_end) // 写操作结束后拉高读请求信号。
    //         sdram_rd_req >= 1'b1 ;
    //     else if(sdram_rd_ack) // 读响应信号拉高后,读请求信号拉低。
    //         sdram_rd_req <= 1'b0;
    //     else
    //         sdram_rd_req <= sdram_rd_req ;
    // end
    always @(posedge c1_sig or negedge sys_rst_n) begin
        if(~rst_n)
            sdram_rd_req <= 1'b0 ;
        else if(wr_end) 
            sdram_rd_req <= 1'b1 ;
        else 
            sdram_rd_req <= sdram_rd_req ;
    end
    // reg     [23:0]  sdram_rd_addr ;
    // reg     [9:0]   sdram_rd_bust ;
    initial begin
        sdram_rd_addr <= 24'd0 ; 
        sdram_rd_bust <= 10'd13 ;
    end 
    initial begin
        sys_clk = 1'b1 ;
        sys_rst_n <= 1'b0 ;
        #(CYCLE) ;
        sys_rst_n <= 1'b1 ;
        #(CYCLE * 250_00) ;
        $stop ;
    end
    always #(CYCLE / 2) sys_clk = ~sys_clk ;

endmodule
仿真:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

fifo_ctrl: 

设计思路:

这个模块要想写好,需要先明确输入端口具体信号是什么样子的。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

我在代码部分有详细说明。 

第一部分:

输入端口的设计

写数据控制部分:fifo_ctrl模块先把uart传输接收到的数据,传输进(write_fifo)中。然后用wr_ack做(write_fifo)的读使能,并把数据传输给sdram_data_in[15:0]。

读数据控制部分:fifo_ctrl模块先从sdram_ctrl中把数据读出来,并存进(read_fifo)中。然后根据rd_fifo_rd_req把(read_fifo)中的数据读出来。

1,wr_fifo_wr_clk:写FIFO时钟。

2,wr_fifo_wr_req:写fifo,写请求。应该连接uart_rx,的po_falg端口。

3,wr_fifo_wr_data:写进fifo中的数据,应该连接uart_rx,的po_data端口。

4,sdram_wr_b_addr[23:0]:先把从uart_rx模块接收到的数据存进写fifo做跨时钟域处理,然后写进sdram中。这个sdram_wr_b_addr是写数据进sdram的首地址。

5,sdram_wr_e_addr[23:0]:是末地址。

6,wr_rst:地址复位信号,高电平有效。

7,wr_burst_len[9:0]:写突发长度,由顶层自己设置一个数据写入。

读数据部分:先从sdram_ctrl模块中读出数据,然后写进FIFO控制模块中的读fifo存储器。

8,rd_fifo_rd_clk:读fifo的读时钟。

9,rd_fifo_rd_req:读FIFO的读请求信号。串口数据发送模块产生,读取fifo控制模块中“读FIFO”中的数据。

10,sdram_rd_b_addr[23:0],需要先读取SDRAM、中的数据,然后写进读fifo。这个信号时读sdram的首地址。

11,sdram_rd_e_addr[23:0],末地址。

12,rd_rst,读地址复位信号。

13,rd_valid:由顶层模块传入,高电平有效,表示可进行sdram读操作。

14,rd_burst_len[9:0]:读数据突发长度。

15:init_end:初始化结束信号。

16:sdram_rd_ack与sdram_wr_ack,分别为sdram对应操作的响应信号。

17:sdram_data_out[15:0]:从sdram中读出的数据,传递给fifo控制模块中的读fifo模块。为了通过uart_tx模块发送数据,做跨时钟域同步处理。

第二部分:

内部变量的设计

满足数据通路,时序对齐,怎么设计都行。明天完成这个fifo_ctrl模块。

时序图:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

代码:

编译时遇到一个时序逻辑中产生锁存器的情况:

时序逻辑,复位某变量时,如果给该变量赋值某变量(非常量),会产生锁存器。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 改成常量就好了。

// 该模块的作用是辅助sdram_ctrl模块进行数据的读写。
// 将要写进sdram模块的数据进行跨时钟域同步处理。
// 通过uart接收模块(50Mhz)接收(写)数据,缓存进write_fifo中。然后大于突发长度时,产生写请求信号,把写数据,写进sdram。
// 通过uart发送模块(50Mhz)发送(读)数据。读数据是通过saram读模块(100Mhz)读取到的缓存进read_fifo的(读)数据。
// 这个读数据的读请求信号是当,初始化完成,写操作完成,read_fifo中数据量小于读突发长度,那么读请求拉高。从sdram中读数据,并写进read_fifo中。
// uart发送模块产生读请求信号,读取read_fifo中的数据,并传输给上位机。
// 该模块部分代码设计与野火官方有很大不同,与模块框图有不一致的地方。但是谁规定一定要完全仿照别人的东西了?
module fifo_ctrl(
    input       wire            clk_50Mhz       , // write_fifo的输入端时钟,read_fifo的输出端时钟。
    input       wire            clk_100Mhz      , // 大多数信号的时钟,write_fifo的输出端时钟,read_fifo的输入端时钟。
    input       wire            sys_rst_n       , // 系统复位
    input       wire            init_end        , // 一切请求在初始化完成之后产生?其实没有必要,因为你即使产生了,也不会被响应。
    // 写数据进写write_fifo
    input       wire            wr_fifo_wr_req  , // 与uart_rx模块的po_flag相连。
    input       wire    [15:0]  wr_fifo_wr_data , // 与uart_rx模块的po_data相连。
    // 把数据从write_fifo中读出来并写进sdram
    output      reg             sdram_wr_req    , // 当write_fifo中的二数据量大于一次写突发长度时,即可拉高。不大于时拉低。也就是说如果write_fifo中数据比写突发长度大很多倍,写请求会一直拉高。直到write_fifo中数据小于写突发长度时拉低。
    output      reg     [23:0]  sdram_wr_addr   , // 输出一次突发写sdram的首地址。
    output      wire    [15:0]  sdram_data_in   , // 输出突发写,数据。连接到write_fifo的读出数据端口。
    input       wire    [23:0]  sdram_wr_b_addr , // 写进sdram的首末地址。2bit,bank地址,9bit列地址,中间13bit的行地址。
    input       wire    [23:0]  sdram_wr_e_addr , // 写进sdram的首末地址。
    input       wire            sdram_wr_ack    , // 用来复位写进sdram地址的(sdram_wr_addr),它的下降沿表示一次写突发数据完成,可以用此下降沿作数据更新依据。因为采用突发方式写数据(写突发长度是任意的)。写完后需要更新地址。除非你想一直在相同地址范围内写数据,那就不用更新写地址,就不用复位。
    input       wire            wr_rst          , 
    input       wire    [9:0]   wr_bust_len     , // 写数据突发长度。
    // 从sdram中读数据
    output      reg             sdram_rd_req    , // 申请读sdram中数据的读请求。
    output      reg     [23:0]  sdram_rd_addr   , // 一次读突发的,读首地址。
    input       wire    [23:0]  rd_fifo_b_addr  , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
    input       wire    [23:0]  rd_fifo_e_addr  , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
    input       wire            sdram_rd_ack    , // 用来复位读突发地址。用来做read_fifo的写入数据使能。
    input       wire            rd_rst          , // 与wr_rst一样,是外界传输的
    input       wire    [9:0]   rd_bust_len     , // 读突发长度。
    input       wire    [15:0]  sdram_data_out  , // 从sdram中读数据。
    // 从read_fifo中读出数据。传给下一个模块。
    output      wire    [15:0]  rd_fifo_rd_data , // 从read_fifo中读出的数据。连接到read_fifo的数据输出端口。
    output      wire    [9:0]   rd_fifo_num     , // read_fifo中“存储”的数据个数,传给外界的一个读模块,用来产生read_fifo的读请求模块。
    input       wire            rd_valid        , // 读数据有效信号,sdram_top凭空产生的。个人认为没什么用。类似于使能信号?但是下面都有请求信号了。
    input       wire            rd_fifo_rd_req    // 下一个模块产生的,根据read_fifo中存储的数据个数。
);                                                // 仿真时记得把rd_valid信号拉高;rd_rst ;wr_rst 信号相对于sys_rst_n 取反。
    // 例化间连线。
    wire    [9:0]   wrusedw_write_num1  ; // write_fifo中存储的数据个数。
    wire    [9:0]   wrusedw_write_num2  ; // write_fifo中存储的数据个数。
    wire    [9:0]   wrusedw_read_num1   ; // read_fifo中存储的数据个数。
    wire    [9:0]   wrusedw_read_num2   ; // read_fifo中存储的数据个数。
    wire            wr_ack_dly1_nege    ;
    wire            rd_ack_dly1_nege    ;
    // reg signal define
    reg             wr_ack_dly1 ;
    reg             rd_ack_dly1 ;
    // wr_ack_dly1  rd_ack_dly1
    always @(posedge clk_100Mhz or negedge sys_rst_n) begin
        if(~sys_rst_n) 
        begin
            wr_ack_dly1 <= 1'b0 ;
            rd_ack_dly1 <= 1'b0 ;
        end
        else 
        begin
            wr_ack_dly1 <= sdram_wr_ack ;
            rd_ack_dly1 <= sdram_rd_ack ;
        end
    end
    // wr_ack_dly1_nege  rd_ack_dly1_nege
    assign  wr_ack_dly1_nege = (~sdram_wr_ack) && (wr_ack_dly1);
    assign  rd_ack_dly1_nege = (~sdram_rd_ack) && (rd_ack_dly1);
    // reg             sdram_wr_req
    always @(posedge clk_100Mhz or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            sdram_wr_req <= 1'b0 ;
        else if(init_end && wrusedw_write_num1 >= wr_bust_len) 
            sdram_wr_req <= 1'b1 ;
        else 
            sdram_wr_req <= 1'b0 ;
    end
    // reg     [23:0]  sdram_wr_addr   , // 输出一次突发写sdram的首地址
    // always @(posedge clk_100Mhz or negedge sys_rst_n) begin
    //     if(~sys_rst_n) 
    //         sdram_wr_addr <= sdram_wr_b_addr ;
    //     else if((sdram_wr_addr < (sdram_wr_e_addr - sdram_wr_b_addr - wr_bust_len)) && wr_ack_dly1_nege)
    //         sdram_wr_addr <= sdram_wr_addr + wr_bust_len;
    //     else if(wr_ack_dly1_nege)
    //         sdram_wr_addr <= sdram_wr_b_addr ;
    //     else 
    //         sdram_wr_addr <= sdram_wr_addr ;
    // end
    always @(posedge clk_100Mhz or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            sdram_wr_addr <= 24'd0 ;
        else if(wr_rst == 1)
            sdram_wr_addr <= sdram_wr_b_addr ;
        else if(wr_ack_dly1_nege) 
        begin
            if(sdram_wr_addr < (sdram_wr_e_addr - sdram_wr_b_addr - wr_bust_len))
                sdram_wr_addr <= sdram_wr_addr + wr_bust_len;
            else
                sdram_wr_addr <= sdram_wr_b_addr ;
        end
        else
            sdram_wr_addr <= sdram_wr_addr ; 
    end
    // reg            sdram_rd_req    , // 申请读sdram中数据的读请求。每完成一次写操作,或者读操作后,若read_fifo中的数据小于rd_bust_len,则需拉高读请求。
    always @(posedge clk_100Mhz or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            sdram_rd_req <= 1'b0 ;
        else 
        if(rd_valid == 1) 
        begin
            if(wrusedw_read_num1 < rd_bust_len && (wr_ack_dly1_nege || rd_ack_dly1_nege)) 
                sdram_rd_req <= 1'b1 ;
            else if(wrusedw_read_num1 >= rd_bust_len && (wr_ack_dly1_nege || rd_ack_dly1_nege))
                sdram_rd_req <= 1'b0 ;
            else 
                sdram_rd_req <= sdram_rd_req ;
        end
        else 
            sdram_rd_req <= 1'b0 ;
    end
    // reg    [23:0]  sdram_rd_addr   , // 一次读突发的,读首地址。
    always @(posedge clk_100Mhz or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            sdram_rd_addr <= 24'd0 ;
        else if(rd_rst == 1)
            sdram_rd_addr <= rd_fifo_b_addr ;
        else if(sdram_rd_addr < (rd_fifo_e_addr - rd_fifo_b_addr - rd_bust_len) && rd_ack_dly1_nege)
            sdram_rd_addr <= sdram_rd_addr + rd_bust_len ;
        else if(sdram_rd_addr >= (rd_fifo_e_addr - rd_fifo_b_addr - rd_bust_len) && rd_ack_dly1_nege)
            sdram_rd_addr <= rd_fifo_b_addr ;
        else 
            sdram_rd_addr <= sdram_rd_addr ;
    end
    // wire    [9:0]   rd_fifo_num     , // read_fifo中“存储”的数据个数,传给外界的一个读模块,用来产生read_fifo的读请求模块。
    assign  rd_fifo_num = wrusedw_read_num1 ;
    // 例化write_fifo read_fifo
fifo_data_16x1024 write_fifo_data_16x1024(
	.data                   ( wr_fifo_wr_data    ) ,
	.wrclk                  ( clk_50Mhz          ) ,
	.wrreq                  ( wr_fifo_wr_req     ) ,
	.wrusedw                ( wrusedw_write_num1 ) ,  // ???
	.rdclk                  ( clk_100Mhz         ) ,
	.rdreq                  ( sdram_wr_ack       ) ,
	.q                      ( sdram_data_in      ) ,
	.rdusedw                ( wrusedw_write_num2 )  // ???
);

fifo_data_16x1024 read_fifo_data_16x1024(
	.data                   ( sdram_data_out     ) ,
	.wrclk                  ( clk_100Mhz         ) ,
	.wrreq                  ( sdram_rd_ack       ) ,
	.wrusedw                ( wrusedw_read_num1  ) ,  // ???
	.rdclk                  ( clk_50Mhz          ) ,
	.rdreq                  ( rd_fifo_rd_req     ) ,
	.q                      ( rd_fifo_rd_data    ) ,
	.rdusedw                ( wrusedw_read_num2  )  // ???
);

endmodule
仿真:

没有单独仿真。

sdram_top:

设计思路

该模块例化了fifo_ctrl模块与sdram_ctrl模块。

例化完后对sdram_top进行仿真验证:

通过uart_rx模块,先传输10个数据进write_fifo

等待sdram完成初始化,然后拉高写请求,把write_fifo中的10个数据写进sdram中。

写完后仲裁模块响应读请求,然后读取10个数据进read_fifo

然后拉高rd_fifo_rd_req读读fifo请求信号,把read_fifo中存的10个数据读出来。

但是要保持read_fifo存储器中存有10个数据,会再次拉高读请求,从sdram中读出10个数据进read_fifo。

之后不会再次拉高rd_fifo_rd_req,除非有其他控制信号干预,想要再次读取数据时会拉高。

代码: 

module sdram_top(
    input       wire            clk_50Mhz       ,
    input       wire            clk_100Mhz      ,
    input       wire            clk_shift       ,
    input       wire            sys_rst_n       ,
    input       wire            wr_fifo_wr_req  ,
    input       wire    [15:0]  wr_fifo_wr_data ,
    input       wire    [23:0]  sdram_wr_b_addr ,
    input       wire    [23:0]  sdram_wr_e_addr ,
    input       wire            wr_rst          ,
    input       wire    [9:0]   wr_bust_len     ,
    input       wire    [23:0]  rd_fifo_b_addr  ,
    input       wire    [23:0]  rd_fifo_e_addr  ,
    input       wire            rd_rst          ,
    input       wire    [9:0]   rd_bust_len     ,
    input       wire            rd_valid        , 
    input       wire            rd_fifo_rd_req  ,

    output      wire    [15:0]  rd_fifo_rd_data ,
    output      wire    [9:0]   rd_fifo_num     ,
    output      wire            sdram_sck       , // 偏移+30度的时钟。
    output      wire            sdram_cke       , // 时钟使能
    output      wire            sdram_csn       ,
    output      wire            sdram_ras       ,
    output      wire            sdram_cas       ,
    output      wire            sdram_wen       , // 写使能。
    output      wire    [1:0]   sdram_bank      ,
    output      wire    [12:0]  sdram_addr      ,

    inout       wire    [15:0]  sdram_dq        ,

    output      wire    [1:0]   sdram_dqm        // 数据掩码。
);
    assign          sdram_dqm = 2'b00 ;
    // 例化间连线
    wire            sdram_init_end  ;
    wire            sdram_wr_req    ;
    wire    [23:0]  sdram_wr_addr   ;
    wire    [15:0]  sdram_data_in   ;
    wire            sdram_wr_ack    ;
    wire            sdram_rd_ack    ;
    wire            sdram_rd_req    ;
    wire    [23:0]  sdram_rd_addr   ;  
    wire    [15:0]  sdram_data_out  ; 
fifo_ctrl fifo_ctrl_inst(
    .clk_50Mhz                  ( clk_50Mhz         ) , // write_fifo的输入端时钟,read_fifo的输出端时钟。
    .clk_100Mhz                 ( clk_100Mhz        ) , // 大多数信号的时钟,write_fifo的输出端时钟,read_fifo的输入端时钟。
    .sys_rst_n                  ( sys_rst_n         ) , // 系统复位
    .init_end                   ( sdram_init_end    ) , // 一切请求在初始化完成之后产生?其实没有必要,因为你即使产生了,也不会被响应。
    // 写数据进写write_fifo
    .wr_fifo_wr_req             ( wr_fifo_wr_req    ) , // 与uart_rx模块的po_flag相连。
    .wr_fifo_wr_data            ( wr_fifo_wr_data   ) , // 与uart_rx模块的po_data相连。
    // 把数据从write_fifo中读出来并写进sdram
    .sdram_wr_req               ( sdram_wr_req      ) , // 当write_fifo中的二数据量大于一次写突发长度时,即可拉高。不大于时拉低。也就是说如果write_fifo中数据比写突发长度大很多倍,写请求会一直拉高。直到write_fifo中数据小于写突发长度时拉低。
    .sdram_wr_addr              ( sdram_wr_addr     ) , // 输出一次突发写sdram的首地址。
    .sdram_data_in              ( sdram_data_in     ) , // 输出突发写,数据。连接到write_fifo的读出数据端口。
    .sdram_wr_b_addr            ( sdram_wr_b_addr   ) , // 写进sdram的首末地址。2bit,bank地址,9bit列地址,中间13bit的行地址。
    .sdram_wr_e_addr            ( sdram_wr_e_addr   ) , // 写进sdram的首末地址。
    .sdram_wr_ack               ( sdram_wr_ack      ) , // 用来复位写进sdram地址的(sdram_wr_addr),它的下降沿表示一次写突发数据完成,可以用此下降沿作数据更新依据。因为采用突发方式写数据(写突发长度是任意的)。写完后需要更新地址。除非你想一直在相同地址范围内写数据,那就不用更新写地址,就不用复位。
    .wr_rst                     ( wr_rst            ) , 
    .wr_bust_len                ( wr_bust_len       ) , // 写数据突发长度。
    // 从sdram中读数据
    .sdram_rd_req               ( sdram_rd_req      ) , // 申请读sdram中数据的读请求。
    .sdram_rd_addr              ( sdram_rd_addr     ) , // 一次读突发的,读首地址。
    .rd_fifo_b_addr             ( rd_fifo_b_addr    ) , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
    .rd_fifo_e_addr             ( rd_fifo_e_addr    ) , // 外界传入的读突发的首末地址。作为sdram_rd_addr的赋值参考依据。
    .sdram_rd_ack               ( sdram_rd_ack      ) , // 用来复位读突发地址。用来做read_fifo的写入数据使能。
    .rd_rst                     ( rd_rst            ) , // 与wr_rst一样,是外界传输的
    .rd_bust_len                ( rd_bust_len       ) , // 读突发长度。
    .sdram_data_out             ( sdram_data_out    ) , // 从sdram中读数据。
    // 从read_fifo中读出数据。传给下一个模块。
    .rd_fifo_rd_data            ( rd_fifo_rd_data   ) , // 从read_fifo中读出的数据。连接到read_fifo的数据输出端口。
    .rd_fifo_num                ( rd_fifo_num       ) , // read_fifo中“存储”的数据个数,传给外界的一个读模块,用来产生read_fifo的读请求模块。
    .rd_valid                   ( rd_valid          ) , // 读数据有效信号,sdram_top凭空产生的。个人认为没什么用。类似于使能信号?但是下面都有请求信号了。
    .rd_fifo_rd_req             ( rd_fifo_rd_req    )   // 下一个模块产生的,根据read_fifo中存储的数据个数。
);            

sdram_ctrl sdram_ctrl_inst(
    .sys_clk                    ( clk_100Mhz        ) ,
    .sys_rst_n                  ( sys_rst_n         ) ,
    .clk_shift                  ( clk_shift         ) ,

    .sdram_wr_req               ( sdram_wr_req      ) ,
    .sdram_wr_addr              ( sdram_wr_addr     ) ,
    .sdram_wr_data              ( sdram_data_in     ) ,
    .sdram_wr_bust              ( wr_bust_len       ) ,

    .sdram_rd_req               ( sdram_rd_req      ) ,
    .sdram_rd_addr              ( sdram_rd_addr     ) ,
    .sdram_rd_bust              ( rd_bust_len       ) ,

    .sdram_sck                  ( sdram_sck         ) , // 偏移+30度的时钟。
    .sdram_cke                  ( sdram_cke         ) , // 时钟使能
    .sdram_csn                  ( sdram_csn         ) ,
    .sdram_ras                  ( sdram_ras         ) ,
    .sdram_cas                  ( sdram_cas         ) ,
    .sdram_wen                  ( sdram_wen         ) , // 写使能。
    .sdram_bank                 ( sdram_bank        ) ,
    .sdram_addr                 ( sdram_addr        ) ,

    .sdram_dq                   ( sdram_dq          ) ,

    .sdram_wr_ack               ( sdram_wr_ack      ) ,
    .sdram_rd_ack               ( sdram_rd_ack      ) ,
    .sdram_init_end             ( sdram_init_end    ) ,
    .sdram_data_out             ( sdram_data_out    ) 
);
endmodule

仿真: 

`timescale 1ns/1ns
module test_sdram_top ();
    reg             sys_clk_50Mhz   ;
    reg             sys_rst_n       ;
    wire            c0_sig          ;
    wire            c1_sig          ;
    wire            c2_sig          ;
    wire            locked_sig      ;
    wire            rst_n           ;
    reg             rx              ;
    wire            po_flag         ;
    wire    [7:0]   po_data         ;
    reg             rd_fifo_rd_req  ;
    wire    [15:0]  rd_fifo_rd_data ;
    wire    [9:0]   rd_fifo_num     ;
    wire            sdram_sck       ;
    wire            sdram_cke       ;
    wire            sdram_csn       ;
    wire            sdram_ras       ;
    wire            sdram_cas       ;
    wire            sdram_wen       ;
    wire    [1:0]   sdram_bank      ;
    wire    [12:0]  sdram_addr      ;
    wire    [15:0]  sdram_dq        ;
    wire    [1:0]   sdram_dqm       ;
    wire    [9:0]   rd_bust_len     ;
    wire    [9:0]   wr_bust_len     ;
    reg             rd_flag         ;
    //  例化
clk_gen	clk_gen_inst (
	.areset             ( ~sys_rst_n    ) ,
	.inclk0             ( sys_clk_50Mhz ) ,
	.c0                 ( c0_sig        ) , // 50m
	.c1                 ( c1_sig        ) , // 100m
	.c2                 ( c2_sig        ) , // 100m shift
	.locked             ( locked_sig    )
);
uart_rx uart_rx_inst(
    .sys_clk            ( c0_sig        ) ,
    .sys_rst_n          ( rst_n         ) ,
    .rx                 ( rx            ) ,

    .po_flag            ( po_flag       ) ,
    .po_data            ( po_data       )  
);
sdram_top sdram_top_inst(
    .clk_50Mhz          ( c0_sig        ) ,
    .clk_100Mhz         ( c1_sig        ) ,
    .clk_shift          ( c2_sig        ) ,
    .sys_rst_n          ( rst_n         ) ,
    .wr_fifo_wr_req     ( po_flag       ) ,
    .wr_fifo_wr_data    ( {8'h00,po_data}       ) ,
    .sdram_wr_b_addr    ( 24'd0         ) ,
    .sdram_wr_e_addr    ( 24'd10        ) ,
    .wr_rst             ( ~rst_n        ) ,
    .wr_bust_len        ( wr_bust_len   ) ,
    .rd_fifo_b_addr     ( 24'd0         ) ,
    .rd_fifo_e_addr     ( 24'd10        ) ,
    .rd_rst             ( ~rst_n        ) ,
    .rd_bust_len        ( rd_bust_len   ) ,
    .rd_valid           ( 1'b1          ) , 
    .rd_fifo_rd_req     ( rd_fifo_rd_req) ,

    .rd_fifo_rd_data    (rd_fifo_rd_data) ,
    .rd_fifo_num        ( rd_fifo_num   ) ,
    .sdram_sck          ( sdram_sck     ) , // 偏移+30度的时钟。
    .sdram_cke          ( sdram_cke     ) , // 时钟使能
    .sdram_csn          ( sdram_csn     ) ,
    .sdram_ras          ( sdram_ras     ) ,
    .sdram_cas          ( sdram_cas     ) ,
    .sdram_wen          ( sdram_wen     ) , // 写使能。
    .sdram_bank         ( sdram_bank    ) ,
    .sdram_addr         ( sdram_addr    ) ,

    .sdram_dq           ( sdram_dq      ) ,

    .sdram_dqm          ( sdram_dqm     ) // 数据掩码。
);
sdram_model_plus sdram_model_plus_inst(
    .Dq                 ( sdram_dq      ) , 
    .Addr               ( sdram_addr    ) , 
    .Ba                 ( sdram_bank    ) , 
    .Clk                ( sdram_sck     ) , 
    .Cke                ( sdram_cke     ) , 
    .Cs_n               ( sdram_csn     ) , 
    .Ras_n              ( sdram_ras     ) , 
    .Cas_n              ( sdram_cas     ) , 
    .We_n               ( sdram_wen     ) , 
    .Dqm                ( {2'b00,sdram_dqm}     ) ,
    .Debug              ( 1'b1          )  
);
/****************************************************************/
    parameter   CYCLE = 20 ;
    defparam    uart_rx_inst.CLK_UART = 50_000_0 ;
    defparam    sdram_top_inst.sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 200 ;
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    // rst_n rd_bust_len wr_bust_len
    assign  rst_n = sys_rst_n && locked_sig ;
    assign  rd_bust_len = 10'd10 ;
    assign  wr_bust_len = 10'd10 ;
    // rx
    task rx_bit ;
        input   [7:0]   data ;
        integer i ;
        for (i = 0;i <= 9 ;i = i  + 1 ) begin
            case (i)
                0: rx <= 1'b0 ;
                1: rx <= data[i - 1];
                2: rx <= data[i - 1];
                3: rx <= data[i - 1];
                4: rx <= data[i - 1];
                5: rx <= data[i - 1];
                6: rx <= data[i - 1];
                7: rx <= data[i - 1];
                8: rx <= data[i - 1];
                9: rx <= 1'b1 ;
                default: rx <= 1'b1 ;
            endcase
            #(CYCLE * 52) ;
        end
    endtask
    // rd_fifo_rd_req
    // always @(posedge c0_sig or negedge rst_n) begin
    //     if(~rst_n)
    //         rd_fifo_rd_req <= 1'b0 ;
    //     else if(rd_fifo_num == rd_bust_len)
    //         rd_fifo_rd_req <= 1'b1 ;
    //     else if(rd_fifo_num ==  0)
    //         rd_fifo_rd_req <= 1'b0 ;
    //     else 
    //         rd_fifo_rd_req <= rd_fifo_rd_req ;
    // end
    always @(posedge c0_sig or negedge rst_n) begin
        if(~rst_n) 
            rd_flag <= 1'b1 ;
        else if(rd_fifo_num == rd_bust_len && rd_flag == 1'b1)
            rd_flag <= 1'b0 ;
        else 
            rd_flag <= rd_flag ;
    end
    always @(posedge c0_sig or negedge rst_n) begin
        if(~rst_n)
            rd_fifo_rd_req <= 1'b0 ;
        else if(rd_fifo_num == rd_bust_len && rd_flag)
            rd_fifo_rd_req <= 1'b1 ;
        else if(rd_fifo_num ==  0)
            rd_fifo_rd_req <= 1'b0 ;
        else 
            rd_fifo_rd_req <= rd_fifo_rd_req ;
    end
    initial begin
        sys_clk_50Mhz = 1'b1    ;
        sys_rst_n    <= 1'b0    ;
        rx           <= 1'b1    ;
        #(CYCLE)                ;
        sys_rst_n     <= 1'b1   ;
        #( CYCLE * 100  )       ;
        rx_bit(8'b1)            ;
        rx_bit(8'd2)            ;
        rx_bit(8'd3)            ;
        rx_bit(8'd4)            ;
        rx_bit(8'd5)            ;
        rx_bit(8'd6)            ;
        rx_bit(8'd7)            ;
        rx_bit(8'd8)            ;
        rx_bit(8'd9)            ;
        rx_bit(8'd9)            ;
    end
    always #(CYCLE / 2) sys_clk_50Mhz = ~sys_clk_50Mhz ;

endmodule

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

uart_sdram:

read_fifo:

设计思路:

从read_fifo中读出的10个数据时50Mhz时钟下的,需要先存进fifo,然后由波特率产生标志信号,作为fifo的读标志。把标志打拍与读出的数据作为po_flag_tx与po_flag_tx。传给uart_tx模块。

修改uart_tx模块,产生一个data_over信号,传递完一帧数据的最后一位拉高一个时钟周期,作为read_fifo的读信号。

时序图:

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

代码:
module fifo_read(
    input       wire            sys_clk     , // clk_50M
    input       wire            sys_rst_n   ,
    input       wire    [7:0]   rd_fifo_rd_data ,
    input       wire    [9:0]   rd_fifo_num ,
    input       wire    [9:0]   bust_num    , // 作为顶层模块设置并输入,为固定参数,表示本模块一次读取读 FIFO 中数据个数,参数数值与数据读模块中读突发长度一致,防止数据读空。
    input       wire            data_over   ,

    output      reg             read_en     , // read_en的设计可以参考仿真模块里的设计。
    output      wire    [7:0]   tx_data     ,
    output      reg             tx_flag     
);
    parameter       BPS = 9600  ,
                    BIT = 10    ;

    reg             fifo_256_wren ;  // 经过观察read_en与rd_fifo_rd_data的关系,打一拍,会与rd_fifo_rd_data对齐,做fifo的写使能。
    wire    [7:0]   wrusedw       ;  // 写进本模块fifo中的数据。当wrusedw == bust_len - 2时,read_en拉低。
    reg             rd_fifo256_en ;  // fifo256开始进行读的使能信号。
    reg             rd_fifo256_en_r ;  // fifo256开始进行读的使能信号。打拍
    reg             rd_fifo256    ;  // fifo256读请求信号。
    wire            empty         ;

    // fifo_256_wren
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            fifo_256_wren <= 1'b0 ;
        else 
            fifo_256_wren <= read_en ;
    end
    // rd_fifo256_en
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            rd_fifo256_en <= 1'b0 ;
        else if(empty == 1)
            rd_fifo256_en <= 1'b0 ;
        else if(!empty && !read_en && fifo_256_wren)
            rd_fifo256_en <= 1'b1 ;
    end
    // rd_fifo256_en_r
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            rd_fifo256_en_r <= 1'b0 ;
        else 
            rd_fifo256_en_r <= rd_fifo256_en ;
    end
    // rd_fifo256
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            rd_fifo256 <= 1'b0 ;
        else if(rd_fifo256_en) begin
                if((~rd_fifo256_en_r) || (!empty && data_over))
                    rd_fifo256 <= 1'b1 ;
                else
                    rd_fifo256 <= 1'b0 ;
            end
        else 
            rd_fifo256 <= 1'b0 ;
    end
    // output signal describe
    // read_en
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            read_en <= 1'b0 ;
        else if(rd_fifo_num == bust_num)
            read_en <= 1'b1 ;
        else if(wrusedw == bust_num - 3) // 提前拉低。
            read_en <= 1'b0 ;
        else 
            read_en <= read_en ;
    end
    // wire    [7:0]   tx_data     ,
    // reg             tx_flag     
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) 
            tx_flag <= 1'b0 ;
        else
            tx_flag <= rd_fifo256 ;
    end

    // 例化
fifo_data_8x256 fifo_data_8x256_inst(
	.wrclk              ( sys_clk         ) , // 写时钟与读时钟貌似是相同的。
	.wrreq              ( fifo_256_wren   ) , // 写请求。
	.data               ( rd_fifo_rd_data ) ,
	.rdclk              ( sys_clk         ) ,
	.rdreq              ( rd_fifo256      ) ,
    .rdempty            ( empty           ),
	.q                  ( tx_data         ) ,
	.rdusedw            (  ) ,
	.wrusedw            ( wrusedw         ) 
);

endmodule
仿真:

仍然对顶层模块仿真,测试文件稍作修改。

把read_fifo模块与uart_tx加进去。

`timescale 1ns/1ns
module test_sdram_top ();
    reg             sys_clk_50Mhz   ;
    reg             sys_rst_n       ;
    wire            c0_sig          ;
    wire            c1_sig          ;
    wire            c2_sig          ;
    wire            locked_sig      ;
    wire            rst_n           ;
    reg             rx              ;
    wire            po_flag         ;
    wire    [7:0]   po_data         ;
    reg             rd_fifo_rd_req  ;
    wire    [15:0]  rd_fifo_rd_data ;
    wire    [9:0]   rd_fifo_num     ;
    wire            sdram_sck       ;
    wire            sdram_cke       ;
    wire            sdram_csn       ;
    wire            sdram_ras       ;
    wire            sdram_cas       ;
    wire            sdram_wen       ;
    wire    [1:0]   sdram_bank      ;
    wire    [12:0]  sdram_addr      ;
    wire    [15:0]  sdram_dq        ;
    wire    [1:0]   sdram_dqm       ;
    wire    [9:0]   rd_bust_len     ;
    wire    [9:0]   wr_bust_len     ;
    reg             rd_flag         ;
    wire            data_over       ;
    wire            read_en         ;
    wire    [7:0]   tx_data         ;
    wire            tx_flag         ;
    wire            tx              ;
    //  例化
clk_gen	clk_gen_inst (
	.areset             ( ~sys_rst_n    ) ,
	.inclk0             ( sys_clk_50Mhz ) ,
	.c0                 ( c0_sig        ) , // 50m
	.c1                 ( c1_sig        ) , // 100m
	.c2                 ( c2_sig        ) , // 100m shift
	.locked             ( locked_sig    )
);
uart_rx uart_rx_inst(
    .sys_clk            ( c0_sig        ) ,
    .sys_rst_n          ( rst_n         ) ,
    .rx                 ( rx            ) ,

    .po_flag            ( po_flag       ) ,
    .po_data            ( po_data       )  
);
sdram_top sdram_top_inst(
    .clk_50Mhz          ( c0_sig        ) ,
    .clk_100Mhz         ( c1_sig        ) ,
    .clk_shift          ( c2_sig        ) ,
    .sys_rst_n          ( rst_n         ) ,
    .wr_fifo_wr_req     ( po_flag       ) ,
    .wr_fifo_wr_data    ( {8'h00,po_data}       ) ,
    .sdram_wr_b_addr    ( 24'd0         ) ,
    .sdram_wr_e_addr    ( 24'd10        ) ,
    .wr_rst             ( ~rst_n        ) ,
    .wr_bust_len        ( wr_bust_len   ) ,
    .rd_fifo_b_addr     ( 24'd0         ) ,
    .rd_fifo_e_addr     ( 24'd10        ) ,
    .rd_rst             ( ~rst_n        ) ,
    .rd_bust_len        ( rd_bust_len   ) ,
    .rd_valid           ( 1'b1          ) , 
    .rd_fifo_rd_req     ( read_en       ) , // rd_fifo_rd_req

    .rd_fifo_rd_data    (rd_fifo_rd_data) ,
    .rd_fifo_num        ( rd_fifo_num   ) ,
    .sdram_sck          ( sdram_sck     ) , // 偏移+30度的时钟。
    .sdram_cke          ( sdram_cke     ) , // 时钟使能
    .sdram_csn          ( sdram_csn     ) ,
    .sdram_ras          ( sdram_ras     ) ,
    .sdram_cas          ( sdram_cas     ) ,
    .sdram_wen          ( sdram_wen     ) , // 写使能。
    .sdram_bank         ( sdram_bank    ) ,
    .sdram_addr         ( sdram_addr    ) ,

    .sdram_dq           ( sdram_dq      ) ,

    .sdram_dqm          ( sdram_dqm     ) // 数据掩码。
);
sdram_model_plus sdram_model_plus_inst(
    .Dq                 ( sdram_dq      ) , 
    .Addr               ( sdram_addr    ) , 
    .Ba                 ( sdram_bank    ) , 
    .Clk                ( sdram_sck     ) , 
    .Cke                ( sdram_cke     ) , 
    .Cs_n               ( sdram_csn     ) , 
    .Ras_n              ( sdram_ras     ) , 
    .Cas_n              ( sdram_cas     ) , 
    .We_n               ( sdram_wen     ) , 
    .Dqm                ( {2'b00,sdram_dqm}     ) ,
    .Debug              ( 1'b1          )  
);
fifo_read fifo_read_inst(
    .sys_clk            ( c0_sig ) , // clk_50M
    .sys_rst_n          ( rst_n  ) ,
    .rd_fifo_rd_data    ( rd_fifo_rd_data[7:0] ) ,
    .rd_fifo_num        ( rd_fifo_num          ) ,
    .bust_num           ( rd_bust_len          ) ,
    .data_over          ( data_over            ) ,

    .read_en            ( read_en              ) , // read_en的设计可以参考仿真模块里的设计。
    .tx_data            ( tx_data              ) ,
    .tx_flag            ( tx_flag              ) 
);
uart_tx uart_tx_inst(
    .sys_clk            ( c0_sig    ) ,
    .sys_rst_n          ( rst_n     ) ,
    .pi_data            ( tx_data   ) ,
    .pi_flag            ( tx_flag   ) ,

    .data_over          ( data_over ) ,
    .tx                 ( tx        )        
);
/****************************************************************/
    parameter   CYCLE = 20 ;
    defparam    uart_tx_inst.CLK_UART = 50_000_0 ;
    defparam    uart_rx_inst.CLK_UART = 50_000_0 ;
    defparam    sdram_top_inst.sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 200 ;
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    // rst_n rd_bust_len wr_bust_len
    assign  rst_n = sys_rst_n && locked_sig ;
    assign  rd_bust_len = 10'd10 ;
    assign  wr_bust_len = 10'd10 ;
    // rx
    task rx_bit ;
        input   [7:0]   data ;
        integer i ;
        for (i = 0;i <= 9 ;i = i  + 1 ) begin
            case (i)
                0: rx <= 1'b0 ;
                1: rx <= data[i - 1];
                2: rx <= data[i - 1];
                3: rx <= data[i - 1];
                4: rx <= data[i - 1];
                5: rx <= data[i - 1];
                6: rx <= data[i - 1];
                7: rx <= data[i - 1];
                8: rx <= data[i - 1];
                9: rx <= 1'b1 ;
                default: rx <= 1'b1 ;
            endcase
            #(CYCLE * 52) ;
        end
    endtask
    // rd_fifo_rd_req
    // always @(posedge c0_sig or negedge rst_n) begin
    //     if(~rst_n)
    //         rd_fifo_rd_req <= 1'b0 ;
    //     else if(rd_fifo_num == rd_bust_len)
    //         rd_fifo_rd_req <= 1'b1 ;
    //     else if(rd_fifo_num ==  0)
    //         rd_fifo_rd_req <= 1'b0 ;
    //     else 
    //         rd_fifo_rd_req <= rd_fifo_rd_req ;
    // end
    always @(posedge c0_sig or negedge rst_n) begin
        if(~rst_n) 
            rd_flag <= 1'b1 ;
        else if(rd_fifo_num == rd_bust_len && rd_flag == 1'b1)
            rd_flag <= 1'b0 ;
        else 
            rd_flag <= rd_flag ;
    end
    always @(posedge c0_sig or negedge rst_n) begin
        if(~rst_n)
            rd_fifo_rd_req <= 1'b0 ;
        else if(rd_fifo_num == rd_bust_len && rd_flag)
            rd_fifo_rd_req <= 1'b1 ;
        else if(rd_fifo_num ==  2)
            rd_fifo_rd_req <= 1'b0 ;
        else 
            rd_fifo_rd_req <= rd_fifo_rd_req ;
    end
    initial begin
        sys_clk_50Mhz = 1'b1    ;
        sys_rst_n    <= 1'b0    ;
        rx           <= 1'b1    ;
        #(CYCLE)                ;
        sys_rst_n     <= 1'b1   ;
        #( CYCLE * 100  )       ;
        rx_bit(8'b1)            ;
        rx_bit(8'd2)            ;
        rx_bit(8'd3)            ;
        rx_bit(8'd4)            ;
        rx_bit(8'd5)            ;
        rx_bit(8'd6)            ;
        rx_bit(8'd7)            ;
        rx_bit(8'd8)            ;
        rx_bit(8'd9)            ;
        rx_bit(8'd10)           ;
    end
    always #(CYCLE / 2) sys_clk_50Mhz = ~sys_clk_50Mhz ;

endmodule

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

top: 

对下列模块进行例化。

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节

 代码:

module top(
    input       wire            sys_clk         ,
    input       wire            sys_rst_n       ,
    input       wire            rx              ,

    output      wire            tx              ,
    output      wire            sdram_cke       ,
    output      wire            sdram_sck       ,
    output      wire            sdram_csn       ,
    output      wire            sdram_ras       ,
    output      wire            sdram_cas       ,
    output      wire            sdram_wen       ,
    output      wire    [1:0]   sdram_bank      ,
    output      wire    [12:0]  sdram_addr      ,

    inout       wire    [15:0]  sdram_dq        ,

    output      wire    [1:0]   sdram_dqm        
);

    // 例化间连线
    wire            clk_50Mhz       ;        
    wire            clk_100Mhz      ;       
    wire            clk_100Mhz_shift; 
    wire            rst_n           ;
    wire            po_flag         ;
    wire    [7:0]   po_data         ;
    wire            read_en         ; 
    wire    [7:0]   tx_data         ;
    wire            tx_flag         ;
    wire    [15:0]  rd_fifo_rd_data ;
    wire    [9:0]   rd_fifo_num     ;
    wire            data_over       ;
    //  例化
clk_gen clk_gen_inst(
	.areset             ( ~sys_rst_n            ) ,
	.inclk0             ( sys_clk               ) ,
	.c0                 ( clk_50Mhz             ) ,
	.c1                 ( clk_100Mhz            ) ,
	.c2                 ( clk_100Mhz_shift      ) ,
	.locked             ( rst_n                 ) 
);
uart_rx uart_rx_inst(
    .sys_clk            ( clk_50Mhz             ) ,
    .sys_rst_n          ( rst_n                 ) ,
    .rx                 ( rx                    ) ,

    .po_flag            ( po_flag               ) ,
    .po_data            ( po_data               )  
);

sdram_top sdram_top_inst(
    .clk_50Mhz          ( clk_50Mhz             ) ,
    .clk_100Mhz         ( clk_100Mhz            ) ,
    .clk_shift          ( clk_100Mhz_shift      ) ,
    .sys_rst_n          ( rst_n                 ) ,
    .wr_fifo_wr_req     ( po_flag               ) ,
    .wr_fifo_wr_data    ( {8'd0,po_data}        ) ,
    .sdram_wr_b_addr    ( 24'd0                 ) ,
    .sdram_wr_e_addr    ( 24'd10                ) ,
    .wr_rst             ( ~rst_n                ) ,
    .wr_bust_len        ( 10'd10                ) ,
    .rd_fifo_b_addr     ( 24'd0                 ) ,
    .rd_fifo_e_addr     ( 24'd10                ) ,
    .rd_rst             ( ~rst_n                ) ,
    .rd_bust_len        ( 10'd10                ) ,
    .rd_valid           ( 1'b1                  ) , 
    .rd_fifo_rd_req     ( read_en               ) ,

    .rd_fifo_rd_data    ( rd_fifo_rd_data       ) ,
    .rd_fifo_num        ( rd_fifo_num           ) ,
    .sdram_cke          ( sdram_cke             ) , 
    .sdram_sck          ( sdram_sck             ) , 
    .sdram_csn          ( sdram_csn             ) ,
    .sdram_ras          ( sdram_ras             ) ,
    .sdram_cas          ( sdram_cas             ) ,
    .sdram_wen          ( sdram_wen             ) , 
    .sdram_bank         ( sdram_bank            ) ,
    .sdram_addr         ( sdram_addr            ) ,

    .sdram_dq           ( sdram_dq              ) ,

    .sdram_dqm          ( sdram_dqm             )
);
fifo_read fifo_read_inst(
    .sys_clk            ( clk_50Mhz             ) , 
    .sys_rst_n          ( rst_n                 ) ,
    .rd_fifo_rd_data    ( rd_fifo_rd_data[7:0]  ) ,
    .rd_fifo_num        ( rd_fifo_num           ) ,
    .bust_num           ( 10'd10                ) , 
    .data_over          ( data_over             ) ,

    .read_en            ( read_en               ) , 
    .tx_data            ( tx_data               ) ,
    .tx_flag            ( tx_flag               ) 
);
uart_tx uart_tx_inst(
    .sys_clk            ( clk_50Mhz             ) ,
    .sys_rst_n          ( rst_n                 ) ,
    .pi_data            ( tx_data               ) ,
    .pi_flag            ( tx_flag               ) ,

    .data_over          ( data_over             ) ,
    .tx                 ( tx                    )          
);
endmodule

仿真:

`timescale 1ns/1ns
module test_top();
    reg             sys_clk_50Mhz   ;
    reg             sys_rst_n       ;
    reg             rx              ;

    wire            tx              ;
    wire            sdram_cke       ;
    wire            sdram_sck       ;
    wire            sdram_csn       ;
    wire            sdram_ras       ;
    wire            sdram_cas       ;
    wire            sdram_wen       ;
    wire    [1:0]   sdram_bank      ;
    wire    [12:0]  sdram_addr      ;
    wire    [15:0]  sdram_dq        ;
    wire    [1:0]   sdram_dqm       ;

top top_inst(
    .sys_clk         ( sys_clk_50Mhz    ) ,
    .sys_rst_n       ( sys_rst_n        ) ,
    .rx              ( rx               ) ,

    .tx              ( tx               ) ,
    .sdram_cke       ( sdram_cke        ) ,
    .sdram_sck       ( sdram_sck        ) ,
    .sdram_csn       ( sdram_csn        ) ,
    .sdram_ras       ( sdram_ras        ) ,
    .sdram_cas       ( sdram_cas        ) ,
    .sdram_wen       ( sdram_wen        ) ,
    .sdram_bank      ( sdram_bank       ) ,
    .sdram_addr      ( sdram_addr       ) ,

    .sdram_dq        ( sdram_dq         ) ,

    .sdram_dqm       ( sdram_dqm        )  
);
sdram_model_plus sdram_model_plus_inst(
    .Dq                 ( sdram_dq      ) , 
    .Addr               ( sdram_addr    ) , 
    .Ba                 ( sdram_bank    ) , 
    .Clk                ( sdram_sck     ) , 
    .Cke                ( sdram_cke     ) , 
    .Cs_n               ( sdram_csn     ) , 
    .Ras_n              ( sdram_ras     ) , 
    .Cas_n              ( sdram_cas     ) , 
    .We_n               ( sdram_wen     ) , 
    .Dqm                ( {2'b00,sdram_dqm}     ) ,
    .Debug              ( 1'b1          )  
);
    /******************************************************/
    parameter   CYCLE = 20 ;
    defparam    top_inst.uart_rx_inst.CLK_UART = 50_000_0 ;
    defparam    top_inst.uart_tx_inst.CLK_UART = 50_000_0 ;
    defparam    top_inst.uart_tx_inst.SUB_1K   = 100      ;
    defparam    top_inst.sdram_top_inst.sdram_ctrl_inst.sdram_aref_inst.MAX_CNT_750US = 200 ;
    defparam    sdram_model_plus_inst.addr_bits = 13 ;
    defparam    sdram_model_plus_inst.data_bits = 16 ;
    defparam    sdram_model_plus_inst.col_bits  = 9  ;
    defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024  ;
    // rx
    task rx_bit ;
        input   [7:0]   data ;
        integer i ;
        for (i = 0;i <= 9 ;i = i  + 1 ) begin
            case (i)
                0: rx <= 1'b0 ;
                1: rx <= data[i - 1];
                2: rx <= data[i - 1];
                3: rx <= data[i - 1];
                4: rx <= data[i - 1];
                5: rx <= data[i - 1];
                6: rx <= data[i - 1];
                7: rx <= data[i - 1];
                8: rx <= data[i - 1];
                9: rx <= 1'b1 ;
                default: rx <= 1'b1 ;
            endcase
            #(CYCLE * 52) ;
        end
    endtask
    initial begin
        sys_clk_50Mhz = 1'b1    ;
        sys_rst_n    <= 1'b0    ;
        rx           <= 1'b1    ;
        #(CYCLE)                ;
        sys_rst_n     <= 1'b1   ;
        #( CYCLE * 100  )       ;
        rx_bit(8'b1)            ;
        rx_bit(8'd2)            ;
        rx_bit(8'd3)            ;
        rx_bit(8'd4)            ;
        rx_bit(8'd5)            ;
        rx_bit(8'd6)            ;
        rx_bit(8'd7)            ;
        rx_bit(8'd8)            ;
        rx_bit(8'd9)            ;
        rx_bit(8'd10)           ;
    end
    always #(CYCLE / 2) sys_clk_50Mhz = ~sys_clk_50Mhz ;

endmodule

上板验证: 

FPGA project : sdram,野火征途pro,fpga开发,1024程序员节文章来源地址https://www.toymoban.com/news/detail-794817.html

到了这里,关于FPGA project : sdram的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【FPGA】Verilog编程实现SDRAM读写(一) ----- 初识SDRAM

    SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存储器。同步、动态、随机是其性能特点的外在说明: 同步(Synchronous )是指内存工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准 动态(Dynamic )是指存储阵列 需要不断的刷新来保证数据不丢失 随机(

    2023年04月08日
    浏览(31)
  • 基于FPGA 的SDRAM控制器

    4X16X4=256(Mbit),注意不是MByte sdram包含两个部分:sdram_ctrl、fifo_ctrl。 sdram_ctrl:其顶层为SDRAM的控制模块内部实例化了5个模块,有初始化、自刷新、写和读模块,还有一个仲裁模块对这四个不同操作进行分配; fifo_ctrl:其顶层为SDRAM的数据输入输出,内部实例化了两个用于连

    2024年02月08日
    浏览(49)
  • 【FPGA】基于Avalon_MM接口的SDRAM读写

    C4开发板上的SDRAM芯片是海力士生产,有256Mbits容量。 SDRAM是同步动态随机存储器(存储阵列不断刷新)。 SDRAM寻址基本原理:行列寻址 SDRAM的总存储容量=L-BANK的数量×行数×列数×存储单元的容量(数据线的位宽)。 clk 系统时钟 CKE 时钟使能 1:clk有效 cs_n 片选 0:clk、CKE、

    2024年02月16日
    浏览(25)
  • Low Cost and High Performance FPGA with ARM and SDRAM inside

    AG10KSDE176 AGM AG10KSDE176 是由 AGM FPGA AG10K 与 SDRAM 叠封集成的芯片,具有 AG10K FPGA 的可编程功能,提供更多可编程 IO,同时内部连接大容量 SDRAM。  FPGA 外部管脚输出 EQFP176 封装底部 Pad 为 GND,管脚说明请见下表:  SDRAM 说明 内部 SDRAM 为 64Mbit(512K words × 4 banks × 32 bits)容量

    2024年02月04日
    浏览(36)
  • FPGA project : IIC_wr_eeprom

    简单双向二线制,同步串行总线。 scl:串行时钟线,用于同步通讯数据。 sda:双向串行数据线。 1,支持挂载多设备。 2,二线制。 3,每个设备有其单独的地址。 4,空闲时,sda会被上拉电阻拉高。 5,存在多个主机时,通过仲裁逻辑决定那个主机控制总线。 6,三个速度模

    2024年02月08日
    浏览(29)
  • FPGA project : flash_secter_erase

    flash的指定扇区擦除实验。 先发写指令,再进入写锁存周期等待500ns,进入写扇区擦除指令,然后写扇区地址,页地址,字节地址。即可完成扇区擦除。  仿真图:忘记截屏了。 需要用到仿真模型。 上版验证通过。

    2024年02月03日
    浏览(27)
  • FPGA project : dht11 温湿度传感器

    没有硬件,过几天上板测试。        其他模块都是之前的,就不发了。    

    2024年02月08日
    浏览(36)
  • 纠错[Synth 8-327] inferring latch for variable ‘next_state_reg‘ [“E:/FPGA/FPGA_Project/Practice/Key_ D

    错误的:因为在组合逻辑中用了非阻塞赋值。  纠正后:  

    2024年02月16日
    浏览(29)
  • Fpga开发笔记(二):高云FPGA发开发软件Gowin和高云fpga基本开发过程

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/135620590 红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中… 上一篇:《Fpga开发笔记(一):

    2024年01月16日
    浏览(55)
  • 【FPGA】高云FPGA之科学的FPGA开发流程

    项目需要做什么,了解系统的功能,如果是大型的项目还会划分模块 通过使用verilog、VHDL、成熟的IP核或者通过画原理图的方式进行逻辑输入设计 当逻辑输入设计完成后需要对其进行验证,该部分由软件部分进行验证,如果逻辑输入有问题需要检查语法错误或则重新设计设计

    2024年02月04日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包