野火FPGA进阶(3):SDRAM读写控制器的设计与验证

news2024/11/22 15:28:08

文章目录

    • 第50讲:SDRAM读写控制器的设计与验证
      • 理论部分
      • 设计与实现
      • 1. sdram_ctrl
        • sdram_init
        • sdram_a_ref
        • sdram_write
        • sdram_read
        • sdram_arbit
        • sdram_ctrl
      • 2. sdram_top
        • fifo_ctrl
        • sdram_top
      • 3. uart_sdram
        • uart_rx
        • uart_tx
        • fifo_read
        • uart_sdram

第50讲:SDRAM读写控制器的设计与验证

理论部分

SDRAM全称“Synchronous Dynamic Random Access Memory”,译为“同步动态随机存取内存”或“同步动态随机存储器”,是动态随机存储器(Dynamic Random Access Memory,DRAM)家族的一份子。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


设计与实现

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1. sdram_ctrl

sdram_init

`timescale  1ns/1ns

module  sdram_init
(
    input   wire            sys_clk     ,   //系统时钟,频率100MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效

    output  reg     [3:0]   init_cmd    ,   //初始化阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
    output  reg     [1:0]   init_ba     ,   //初始化阶段Bank地址
    output  reg     [12:0]  init_addr   ,   //初始化阶段地址数据,辅助预充电操作
                                            //和配置模式寄存器操作,A12-A0,共13位
    output  wire            init_end        //初始化结束信号
);

// parameter    define
parameter   T_POWER     =   15'd20_000  ;   //上电后等待时钟数(200us)
//SDRAM初始化用到的控制信号命令
parameter   P_CHARGE    =   4'b0010     ,   //预充电指令
            AUTO_REF    =   4'b0001     ,   //自动刷新指令
            NOP         =   4'b0111     ,   //空操作指令
            M_REG_SET   =   4'b0000     ;   //模式寄存器设置指令
//SDRAM初始化过程各个状态
parameter   INIT_IDLE   =   3'b000      ,   //初始状态
            INIT_PRE    =   3'b001      ,   //预充电状态
            INIT_TRP    =   3'b011      ,   //预充电等待          tRP
            INIT_AR     =   3'b010      ,   //自动刷新
            INIT_TRF    =   3'b100      ,   //自动刷新等待        tRC
            INIT_MRS    =   3'b101      ,   //模式寄存器设置
            INIT_TMRD   =   3'b111      ,   //模式寄存器设置等待  tMRD
            INIT_END    =   3'b110      ;   //初始化完成
parameter   TRP_CLK     =   3'd2        ,   //预充电等待周期,20ns
            TRC_CLK     =   3'd7        ,   //自动刷新等待,70ns
            TMRD_CLK    =   3'd3        ;   //模式寄存器设置等待周期,30ns

// wire define
wire            wait_end        ;   //上电后200us等待结束标志
wire            trp_end         ;   //预充电等待结束标志
wire            trc_end         ;   //自动刷新等待结束标志
wire            tmrd_end        ;   //模式寄存器设置等待结束标志

// reg  define
reg     [14:0]  cnt_200us       ;   //SDRAM上电后200us稳定期计数器
reg     [2:0]   init_state      ;   //SDRAM初始化状态
reg     [2:0]   cnt_clk         ;   //时钟周期计数,记录初始化各状态等待周期数
reg             cnt_clk_rst     ;   //时钟周期计数复位标志
reg     [3:0]   cnt_init_aref   ;   //初始化过程自动刷新次数计数器

//cnt_200us:SDRAM上电后200us稳定期计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_200us   <=  15'd0;
    else    if(cnt_200us == T_POWER)
        cnt_200us   <=  T_POWER;
    else
        cnt_200us   <=  cnt_200us + 1'b1;

//wait_end:上电后200us等待结束标志
assign  wait_end = (cnt_200us == (T_POWER - 1'b1)) ? 1'b1 : 1'b0;

//init_end:SDRAM初始化完毕信号
assign  init_end = (init_state == INIT_END) ? 1'b1 : 1'b0;

//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  3'd0;
    else    if(cnt_clk_rst == 1'b1)
        cnt_clk <=  3'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

//cnt_init_aref:初始化过程自动刷新次数计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_init_aref   <=  4'd0;
    else    if(init_state == INIT_IDLE)
        cnt_init_aref   <=  4'd0;
    else    if(init_state == INIT_AR)
        cnt_init_aref   <=  cnt_init_aref + 1'b1;
    else
        cnt_init_aref   <=  cnt_init_aref;

//trp_end,trc_end,tmrd_end:等待结束标志
assign  trp_end     =   ((init_state == INIT_TRP ) && (cnt_clk == TRP_CLK )) ? 1'b1 : 1'b0;
assign  trc_end     =   ((init_state == INIT_TRF ) && (cnt_clk == TRC_CLK )) ? 1'b1 : 1'b0;
assign  tmrd_end    =   ((init_state == INIT_TMRD) && (cnt_clk == TMRD_CLK)) ? 1'b1 : 1'b0;

//SDRAM的初始化状态机
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        init_state  <=  INIT_IDLE;
    else
        case(init_state)
            INIT_IDLE:  //系统上电后,在初始状态等待200us跳转到预充电状态
                if(wait_end == 1'b1)
                    init_state  <=  INIT_PRE;
                else
                    init_state  <=  init_state;
            INIT_PRE:   //预充电状态,直接跳转到预充电等待状态
                init_state  <=  INIT_TRP;
            INIT_TRP:   //预充电等待状态,等待结束,跳转到自动刷新状态
                if(trp_end == 1'b1)
                    init_state  <=  INIT_AR;
                else
                    init_state  <=  init_state;
            INIT_AR :   //自动刷新状态,直接跳转到自动刷新等待状态
                init_state  <=  INIT_TRF;
            INIT_TRF:   //自动刷新等待状态,等待结束,自动跳转到模式寄存器设置状态
                if(trc_end == 1'b1)
                    if(cnt_init_aref == 4'd8)
                        init_state  <=  INIT_MRS;
                    else
                        init_state  <=  INIT_AR;
                else
                    init_state  <=  init_state;
            INIT_MRS:   //模式寄存器设置状态,直接跳转到模式寄存器设置等待状态
                init_state  <=  INIT_TMRD;
            INIT_TMRD:  //模式寄存器设置等待状态,等待结束,跳到初始化完成状态
                if(tmrd_end == 1'b1)
                    init_state  <=  INIT_END;
                else
                    init_state  <=  init_state;
            INIT_END:   //初始化完成状态,保持此状态
                init_state  <=  INIT_END;
            default:    init_state  <=  INIT_IDLE;
        endcase

//cnt_clk_rst:时钟周期计数复位标志
always@(*)
    begin
        case (init_state)
            INIT_IDLE:  cnt_clk_rst <=  1'b1;   //时钟周期计数复位信号,高有效,时钟周期计数清零
            INIT_TRP:   cnt_clk_rst <= (trp_end == 1'b1) ? 1'b1 : 1'b0;
                                                //等待结束标志有效,计数器清零
            INIT_TRF:   cnt_clk_rst <=  (trc_end == 1'b1) ? 1'b1 : 1'b0; 
                                                //等待结束标志有效,计数器清零
            INIT_TMRD:  cnt_clk_rst <=  (tmrd_end == 1'b1) ? 1'b1 : 1'b0;
                                                //等待结束标志有效,计数器清零
            INIT_END:   cnt_clk_rst <=  1'b1;   //初始化完成,计数器清零
            default:    cnt_clk_rst <=  1'b0;
        endcase
    end

//SDRAM操作指令控制
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            init_cmd    <=  NOP;
            init_ba     <=  2'b11;
            init_addr   <=  13'h1fff;
        end
    else
        case(init_state)
            INIT_IDLE,INIT_TRP,INIT_TRF,INIT_TMRD:  //执行空操作指令
                begin
                    init_cmd    <=  NOP;
                    init_ba     <=  2'b11;
                    init_addr   <=  13'h1fff;
                end
            INIT_PRE:   //预充电指令
                begin
                    init_cmd    <=  P_CHARGE;
                    init_ba     <=  2'b11;
                    init_addr   <=  13'h1fff;
                end 
            INIT_AR:    //自动刷新指令
                begin
                    init_cmd    <=  AUTO_REF;
                    init_ba     <=  2'b11;
                    init_addr   <=  13'h1fff;
                end
            INIT_MRS:   //模式寄存器设置指令
                begin
                    init_cmd    <=  M_REG_SET;
                    init_ba     <=  2'b00;
                    init_addr   <=
                    {    //地址辅助配置模式寄存器,参数不同,配置的模式不同
                        3'b000,     //A12-A10:预留
                        1'b0,       //A9=0:读写方式,0:突发读&突发写,1:突发读&单写
                        2'b00,      //{A8,A7}=00:标准模式,默认
                        3'b011,     //{A6,A5,A4}=011:CAS潜伏期,010:2,011:3,其他:保留
                        1'b0,       //A3=0:突发传输方式,0:顺序,1:隔行
                        3'b111      //{A2,A1,A0}=111:突发长度,000:单字节,001:2字节
                                    //010:4字节,011:8字节,111:整页,其他:保留
                    };
                end 
            INIT_END:   //SDRAM初始化完成
                begin
                    init_cmd    <=  NOP;
                    init_ba     <=  2'b11;
                    init_addr   <=  13'h1fff;
                end
            default:
                begin
                    init_cmd    <=  NOP;
                    init_ba     <=  2'b11;
                    init_addr   <=  13'h1fff;
                end    
        endcase

endmodule

sdram_a_ref

`timescale  1ns/1ns

module  sdram_a_ref
(
    input   wire            sys_clk     ,   //系统时钟,频率100MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            init_end    ,   //初始化结束信号
    input   wire            aref_en     ,   //自动刷新使能

    output  reg             aref_req    ,   //自动刷新请求
    output  reg     [3:0]   aref_cmd    ,   //自动刷新阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
    output  reg     [1:0]   aref_ba     ,   //自动刷新阶段Bank地址
    output  reg     [12:0]  aref_addr   ,   //地址数据,辅助预充电操作,A12-A0,13位地址
    output  wire            aref_end        //自动刷新结束标志
);

//parameter     define
parameter   CNT_REF_MAX =   10'd749     ;   //自动刷新等待时钟数(7.5us)
parameter   TRP_CLK     =   3'd2        ,   //预充电等待周期
            TRC_CLK     =   3'd7        ;   //自动刷新等待周期
parameter   P_CHARGE    =   4'b0010     ,   //预充电指令
            A_REF       =   4'b0001     ,   //自动刷新指令
            NOP         =   4'b0111     ;   //空操作指令
parameter   AREF_IDLE   =   3'b000      ,   //初始状态,等待自动刷新使能
            AREF_PCHA   =   3'b001      ,   //预充电状态
            AREF_TRP    =   3'b011      ,   //预充电等待          tRP
            AUTO_REF    =   3'b010      ,   //自动刷新状态
            AREF_TRF    =   3'b100      ,   //自动刷新等待        tRC
            AREF_END    =   3'b101      ;   //自动刷新结束

//wire  define
wire            trp_end     ;   //预充电等待结束标志
wire            trc_end     ;   //自动刷新等待结束标志
wire            aref_ack    ;   //自动刷新应答信号

//reg   define
reg     [9:0]   cnt_aref        ;   //自动刷新计数器
reg     [2:0]   aref_state      ;   //SDRAM自动刷新状态
reg     [2:0]   cnt_clk         ;   //时钟周期计数,记录自刷新阶段各状态等待时间
reg             cnt_clk_rst     ;   //时钟周期计数复位标志
reg     [1:0]   cnt_aref_aref   ;   //自动刷新次数计数器

//cnt_ref:刷新计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_aref    <=  10'd0;
    else    if(cnt_aref >= CNT_REF_MAX)
        cnt_aref    <=  10'd0;
    else    if(init_end == 1'b1)
        cnt_aref    <=  cnt_aref + 1'b1;
    else 
        cnt_aref    <=  cnt_aref;
        
//aref_req:自动刷新请求
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        aref_req    <=  1'b0;
    else    if(cnt_aref == CNT_REF_MAX - 1'b1)
        aref_req    <=  1'b1;
    else    if(aref_ack == 1'b1)
        aref_req    <=  1'b0;

//aref_ack:自动刷新应答信号
assign  aref_ack = (aref_state == AREF_PCHA ) ? 1'b1 : 1'b0;
//aref_end:自动刷新结束标志
assign  aref_end = (aref_state == AREF_END  ) ? 1'b1 : 1'b0;

//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  3'd0;
    else    if(cnt_clk_rst == 1'b1)
        cnt_clk <=  3'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

//trp_end,trc_end,tmrd_end:等待结束标志
assign  trp_end = ((aref_state == AREF_TRP) && (cnt_clk == TRP_CLK )) ? 1'b1 : 1'b0;
assign  trc_end = ((aref_state == AREF_TRF) && (cnt_clk == TRC_CLK )) ? 1'b1 : 1'b0;

//cnt_aref_aref:初始化过程自动刷新次数计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_aref_aref   <=  2'd0;
    else    if(aref_state == AREF_IDLE)
        cnt_aref_aref   <=  2'd0;
    else    if(aref_state == AUTO_REF)
        cnt_aref_aref   <=  cnt_aref_aref + 1'b1;
    else
        cnt_aref_aref   <=  cnt_aref_aref;

//SDRAM自动刷新状态机
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        aref_state  <=  AREF_IDLE;
    else
        case(aref_state)
            AREF_IDLE:
                if((aref_en == 1'b1) && (init_end == 1'b1))
                    aref_state  <=  AREF_PCHA;
                else
                    aref_state  <=  aref_state;
            AREF_PCHA:
                aref_state  <=  AREF_TRP;
            AREF_TRP:
                if(trp_end == 1'b1)
                    aref_state  <=  AUTO_REF;
                else
                    aref_state  <=  aref_state;
            AUTO_REF:
                aref_state  <=  AREF_TRF;
            AREF_TRF:
                if(trc_end == 1'b1)
                    if(cnt_aref_aref == 2'd2)
                        aref_state  <=  AREF_END;
                    else
                        aref_state  <=  AUTO_REF;
                else
                    aref_state  <=  aref_state;
            AREF_END:
                aref_state  <=  AREF_IDLE;
            default:
                aref_state  <=  AREF_IDLE;
        endcase

//cnt_clk_rst:时钟周期计数复位标志
always@(*)
    begin
        case (aref_state)
            AREF_IDLE:  cnt_clk_rst <=  1'b1;   //时钟周期计数器清零
            AREF_TRP:   cnt_clk_rst <=  (trp_end == 1'b1) ? 1'b1 : 1'b0;
                                                //等待结束标志有效,计数器清零
            AREF_TRF:   cnt_clk_rst <=  (trc_end == 1'b1) ? 1'b1 : 1'b0;
                                                //等待结束标志有效,计数器清零
            AREF_END:   cnt_clk_rst <=  1'b1;
            default:    cnt_clk_rst <=  1'b0;
        endcase
    end

//SDRAM操作指令控制
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            aref_cmd    <=  NOP;
            aref_ba     <=  2'b11;
            aref_addr   <=  13'h1fff;
        end
    else
        case(aref_state)
            AREF_IDLE,AREF_TRP,AREF_TRF:    //执行空操作指令
                begin
                    aref_cmd    <=  NOP;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end
            AREF_PCHA:  //预充电指令
                begin
                    aref_cmd    <=  P_CHARGE;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end 
            AUTO_REF:   //自动刷新指令
                begin
                    aref_cmd    <=  A_REF;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end
            AREF_END:   //一次自动刷新完成
                begin
                    aref_cmd    <=  NOP;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end    
            default:
                begin
                    aref_cmd    <=  NOP;
                    aref_ba     <=  2'b11;
                    aref_addr   <=  13'h1fff;
                end    
        endcase

endmodule

sdram_write

`timescale  1ns/1ns

module  sdram_write
(
    input   wire            sys_clk         ,   //系统时钟,频率100MHz
    input   wire            sys_rst_n       ,   //复位信号,低电平有效
    input   wire            init_end        ,   //初始化结束信号
    input   wire            wr_en           ,   //写使能
    input   wire    [23:0]  wr_addr         ,   //写SDRAM地址
    input   wire    [15:0]  wr_data         ,   //待写入SDRAM的数据(写FIFO传入)
    input   wire    [9:0]   wr_burst_len    ,   //写突发SDRAM字节数

    output  wire            wr_ack          ,   //写SDRAM响应信号
    output  wire            wr_end          ,   //一次突发写结束
    output  reg     [3:0]   write_cmd       ,   //写数据阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
    output  reg     [1:0]   write_ba        ,   //写数据阶段Bank地址
    output  reg     [12:0]  write_addr      ,   //地址数据,辅助预充电操作,行、列地址,A12-A0,13位地址
    output  reg             wr_sdram_en     ,   //数据总线输出使能
    output  wire    [15:0]  wr_sdram_data       //写入SDRAM的数据
);

//parameter     define
parameter   TRCD_CLK    =   10'd2   ,   //激活周期
            TRP_CLK     =   10'd2   ;   //预充电周期
parameter   WR_IDLE     =   4'b0000 ,   //初始状态
            WR_ACTIVE   =   4'b0001 ,   //激活
            WR_TRCD     =   4'b0011 ,   //激活等待
            WR_WRITE    =   4'b0010 ,   //写操作
            WR_DATA     =   4'b0100 ,   //写数据
            WR_PRE      =   4'b0101 ,   //预充电
            WR_TRP      =   4'b0111 ,   //预充电等待
            WR_END      =   4'b0110 ;   //一次突发写结束
parameter   NOP         =   4'b0111 ,   //空操作指令
            ACTIVE      =   4'b0011 ,   //激活指令
            WRITE       =   4'b0100 ,   //数据写指令
            B_STOP      =   4'b0110 ,   //突发停止指令
            P_CHARGE    =   4'b0010 ;   //预充电指令

//wire  define
wire            trcd_end    ;   //激活等待周期结束
wire            twrite_end  ;   //突发写结束
wire            trp_end     ;   //预充电有效周期结束

//reg   define
reg     [3:0]   write_state ;   //SDRAM写状态
reg     [9:0]   cnt_clk     ;   //时钟周期计数,记录写数据阶段各状态等待时间
reg             cnt_clk_rst ;   //时钟周期计数复位标志

//wr_end:一次突发写结束
assign  wr_end = (write_state == WR_END) ? 1'b1 : 1'b0;

//wr_ack:写SDRAM响应信号
assign  wr_ack = ( write_state == WR_WRITE) || ((write_state == WR_DATA) && (cnt_clk <= wr_burst_len - 2'd2));

//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  10'd0;
    else    if(cnt_clk_rst == 1'b1)
        cnt_clk <=  10'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

//trcd_end,twrite_end,trp_end:等待结束标志
assign  trcd_end    =   ((write_state == WR_TRCD)
                        &&(cnt_clk == TRCD_CLK        )) ? 1'b1 : 1'b0;    //激活周期结束
assign  twrite_end  =   ((write_state == WR_DATA)
                        &&(cnt_clk == wr_burst_len - 1)) ? 1'b1 : 1'b0;    //突发写结束
assign  trp_end     =   ((write_state == WR_TRP )
                        &&(cnt_clk == TRP_CLK         )) ? 1'b1 : 1'b0;    //预充电等待周期结束

//write_state:SDRAM的工作状态机
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
            write_state <=  WR_IDLE;
    else
        case(write_state)
            WR_IDLE:
                if((wr_en ==1'b1) && (init_end == 1'b1))
                        write_state <=  WR_ACTIVE;
                else
                        write_state <=  write_state;
            WR_ACTIVE:
                write_state <=  WR_TRCD;
            WR_TRCD:
                if(trcd_end == 1'b1)
                    write_state <=  WR_WRITE;
                else
                    write_state <=  write_state;
            WR_WRITE:
                write_state <=  WR_DATA;
            WR_DATA:
                if(twrite_end == 1'b1)
                    write_state <=  WR_PRE;
                else
                    write_state <=  write_state;
            WR_PRE:
                write_state <=  WR_TRP;
            WR_TRP:
                if(trp_end == 1'b1)
                    write_state <=  WR_END;
                else
                    write_state <=  write_state;

            WR_END:
                write_state <=  WR_IDLE;
            default:
                write_state <=  WR_IDLE;
        endcase

//计数器控制逻辑
always@(*)
    begin
        case(write_state)
            WR_IDLE:    cnt_clk_rst   <=  1'b1;
            WR_TRCD:    cnt_clk_rst   <=  (trcd_end == 1'b1) ? 1'b1 : 1'b0;
            WR_WRITE:   cnt_clk_rst   <=  1'b1;
            WR_DATA:    cnt_clk_rst   <=  (twrite_end == 1'b1) ? 1'b1 : 1'b0;
            WR_TRP:     cnt_clk_rst   <=  (trp_end == 1'b1) ? 1'b1 : 1'b0;
            WR_END:     cnt_clk_rst   <=  1'b1;
            default:    cnt_clk_rst   <=  1'b0;
        endcase
    end

//SDRAM操作指令控制
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            write_cmd   <=  NOP;
            write_ba    <=  2'b11;
            write_addr  <=  13'h1fff;
        end
    else
        case(write_state)
            WR_IDLE,WR_TRCD,WR_TRP:
                begin
                    write_cmd   <=  NOP;
                    write_ba    <=  2'b11;
                    write_addr  <=  13'h1fff;
                end
            WR_ACTIVE:  //激活指令
                begin
                    write_cmd   <=  ACTIVE;
                    write_ba    <=  wr_addr[23:22];
                    write_addr  <=  wr_addr[21:9];
                end
            WR_WRITE:   //写操作指令
                begin
                    write_cmd   <=  WRITE;
                    write_ba    <=  wr_addr[23:22];
                    write_addr  <=  {4'b0000,wr_addr[8:0]};
                end     
            WR_DATA:    //突发传输终止指令
                begin
                    if(twrite_end == 1'b1)
                        write_cmd <=  B_STOP;
                    else
                        begin
                            write_cmd   <=  NOP;
                            write_ba    <=  2'b11;
                            write_addr  <=  13'h1fff;
                        end
                end
            WR_PRE:     //预充电指令
                begin
                    write_cmd   <= P_CHARGE;
                    write_ba    <= wr_addr[23:22];
                    write_addr  <= 13'h0400;
                end
            WR_END:
                begin
                    write_cmd   <=  NOP;
                    write_ba    <=  2'b11;
                    write_addr  <=  13'h1fff;
                end
            default:
                begin
                    write_cmd   <=  NOP;
                    write_ba    <=  2'b11;
                    write_addr  <=  13'h1fff;
                end
        endcase

//wr_sdram_en:数据总线输出使能
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_sdram_en <=  1'b0;
    else
        wr_sdram_en <=  wr_ack;

//wr_sdram_data:写入SDRAM的数据
assign  wr_sdram_data = (wr_sdram_en == 1'b1) ? wr_data : 16'd0;

endmodule

sdram_read

`timescale  1ns/1ns

module  sdram_read
(
    input   wire            sys_clk         ,   //系统时钟,频率100MHz
    input   wire            sys_rst_n       ,   //复位信号,低电平有效
    input   wire            init_end        ,   //初始化结束信号
    input   wire            rd_en           ,   //读使能
    input   wire    [23:0]  rd_addr         ,   //读SDRAM地址
    input   wire    [15:0]  rd_data         ,   //自SDRAM中读出的数据
    input   wire    [9:0]   rd_burst_len    ,   //读突发SDRAM字节数

    output  wire            rd_ack          ,   //读SDRAM响应信号
    output  wire            rd_end          ,   //一次突发读结束
    output  reg     [3:0]   read_cmd        ,   //读数据阶段写入sdram的指令,{cs_n,ras_n,cas_n,we_n}
    output  reg     [1:0]   read_ba         ,   //读数据阶段Bank地址
    output  reg     [12:0]  read_addr       ,   //地址数据,辅助预充电操作,行、列地址,A12-A0,13位地址
    output  wire    [15:0]  rd_sdram_data       //SDRAM读出的数据
);

//parameter     define
parameter   TRCD_CLK    =   10'd2   ,   //激活等待周期
            TCL_CLK     =   10'd3   ,   //潜伏期
            TRP_CLK     =   10'd2   ;   //预充电等待周期
parameter   RD_IDLE     =   4'b0000 ,   //空闲
            RD_ACTIVE   =   4'b0001 ,   //激活
            RD_TRCD     =   4'b0011 ,   //激活等待
            RD_READ     =   4'b0010 ,   //读操作
            RD_CL       =   4'b0100 ,   //潜伏期
            RD_DATA     =   4'b0101 ,   //读数据
            RD_PRE      =   4'b0111 ,   //预充电
            RD_TRP      =   4'b0110 ,   //预充电等待
            RD_END      =   4'b1100 ;   //一次突发读结束
parameter   NOP         =   4'b0111 ,   //空操作指令
            ACTIVE      =   4'b0011 ,   //激活指令
            READ        =   4'b0101 ,   //数据读指令
            B_STOP      =   4'b0110 ,   //突发停止指令
            P_CHARGE    =   4'b0010 ;   //预充电指令

//wire  define
wire            trcd_end    ;   //激活等待周期结束
wire            trp_end     ;   //预充电等待周期结束
wire            tcl_end     ;   //潜伏期结束标志
wire            tread_end   ;   //突发读结束
wire            rdburst_end ;   //读突发终止

//reg   define
reg     [3:0]   read_state  ;   //SDRAM写状态
reg     [9:0]   cnt_clk     ;   //时钟周期计数,记录初始化各状态等待时间
reg             cnt_clk_rst ;   //时钟周期计数复位标志
reg     [15:0]  rd_data_reg ;

//rd_data_reg
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_data_reg <=  16'd0;
    else
        rd_data_reg <=  rd_data;

//rd_end:一次突发读结束
assign  rd_end = (read_state == RD_END) ? 1'b1 : 1'b0;
//rd_ack:读SDRAM响应信号
assign  rd_ack = (read_state == RD_DATA) && (cnt_clk >= 10'd1) && (cnt_clk < (rd_burst_len + 2'd1));

//cnt_clk:时钟周期计数,记录初始化各状态等待时间
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  10'd0;
    else    if(cnt_clk_rst == 1'b1)
        cnt_clk <=  10'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

//trcd_end,trp_end,tcl_end,tread_end,rdburst_end:等待结束标志
assign  trcd_end    =   ((read_state == RD_TRCD) && (cnt_clk == TRCD_CLK))         ? 1'b1 : 1'b0;    //行选通周期结束
assign  trp_end     =   ((read_state == RD_TRP ) && (cnt_clk == TRP_CLK))          ? 1'b1 : 1'b0;    //预充电有效周期结束
assign  tcl_end     =   ((read_state == RD_CL  ) && (cnt_clk == TCL_CLK - 1))      ? 1'b1 : 1'b0;    //潜伏期结束
assign  tread_end   =   ((read_state == RD_DATA) && (cnt_clk == rd_burst_len + 2)) ? 1'b1 : 1'b0;    //突发读结束
assign  rdburst_end =   ((read_state == RD_DATA) && (cnt_clk == rd_burst_len - 4)) ? 1'b1 : 1'b0;    //读突发终止

//read_state:SDRAM的工作状态机
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
            read_state  <=  RD_IDLE;
    else
        case(read_state)
            RD_IDLE:
                if((rd_en ==1'b1) && (init_end == 1'b1))
                        read_state <=  RD_ACTIVE;
                else
                        read_state <=  RD_IDLE;
            RD_ACTIVE:
                read_state <=  RD_TRCD;
            RD_TRCD:
                if(trcd_end == 1'b1)
                    read_state <=  RD_READ;
                else
                    read_state <=  RD_TRCD;
            RD_READ:
                read_state <=  RD_CL;
            RD_CL:
                read_state <=  (tcl_end == 1'b1) ? RD_DATA : RD_CL;
            RD_DATA:
                read_state <=  (tread_end == 1'b1) ? RD_PRE : RD_DATA;
            RD_PRE:
                read_state  <=  RD_TRP;
            RD_TRP:
                read_state  <=  (trp_end == 1'b1) ? RD_END : RD_TRP;
            RD_END:
                read_state  <=  RD_IDLE;
            default:
                read_state  <=  RD_IDLE;
        endcase

//计数器控制逻辑
always@(*)
    begin
        case(read_state)
            RD_IDLE:    cnt_clk_rst   <=  1'b1;
            RD_TRCD:    cnt_clk_rst   <=  (trcd_end == 1'b1) ? 1'b1 : 1'b0;
            RD_READ:    cnt_clk_rst   <=  1'b1;
            RD_CL:      cnt_clk_rst   <=  (tcl_end == 1'b1) ? 1'b1 : 1'b0;
            RD_DATA:    cnt_clk_rst   <=  (tread_end == 1'b1) ? 1'b1 : 1'b0;
            RD_TRP:     cnt_clk_rst   <=  (trp_end == 1'b1) ? 1'b1 : 1'b0;
            RD_END:     cnt_clk_rst   <=  1'b1;
            default:    cnt_clk_rst   <=  1'b0;
        endcase
    end

//SDRAM操作指令控制
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            read_cmd    <=  NOP;
            read_ba     <=  2'b11;
            read_addr   <=  13'h1fff;
        end
    else
        case(read_state)
            RD_IDLE,RD_TRCD,RD_TRP:
                begin
                    read_cmd    <=  NOP;
                    read_ba     <=  2'b11;
                    read_addr   <=  13'h1fff;
                end
            RD_ACTIVE:  //激活指令
                begin
                    read_cmd    <=  ACTIVE;
                    read_ba     <=  rd_addr[23:22];
                    read_addr   <=  rd_addr[21:9];
                end
            RD_READ:    //读操作指令
                begin
                    read_cmd    <=  READ;
                    read_ba     <=  rd_addr[23:22];
                    read_addr   <=  {4'b0000,rd_addr[8:0]};
                end
            RD_DATA:    //突发传输终止指令
                begin
                    if(rdburst_end == 1'b1)
                        read_cmd <=  B_STOP;
                    else
                        begin
                            read_cmd    <=  NOP;
                            read_ba     <=  2'b11;
                            read_addr   <=  13'h1fff;
                        end
                end
            RD_PRE:     //预充电指令
                begin
                    read_cmd    <= P_CHARGE;
                    read_ba     <= rd_addr[23:22];
                    read_addr   <= 13'h0400;
                end
            RD_END:
                begin
                    read_cmd    <=  NOP;
                    read_ba     <=  2'b11;
                    read_addr   <=  13'h1fff;
                end
            default:
                begin
                    read_cmd    <=  NOP;
                    read_ba     <=  2'b11;
                    read_addr   <=  13'h1fff;
                end
        endcase

//rd_sdram_data:SDRAM读出的数据
assign  rd_sdram_data = (rd_ack == 1'b1) ? rd_data_reg : 16'b0;

endmodule

sdram_arbit

`timescale  1ns/1ns

module  sdram_arbit
(
    input   wire            sys_clk     ,   //系统时钟
    input   wire            sys_rst_n   ,   //复位信号
//sdram_init
    input   wire    [3:0]   init_cmd    ,   //初始化阶段命令
    input   wire            init_end    ,   //初始化结束标志
    input   wire    [1:0]   init_ba     ,   //初始化阶段Bank地址
    input   wire    [12:0]  init_addr   ,   //初始化阶段数据地址
//sdram_auto_ref
    input   wire            aref_req    ,   //自刷新请求
    input   wire            aref_end    ,   //自刷新结束
    input   wire    [3:0]   aref_cmd    ,   //自刷新阶段命令
    input   wire    [1:0]   aref_ba     ,   //自动刷新阶段Bank地址
    input   wire    [12:0]  aref_addr   ,   //自刷新阶段数据地址
//sdram_write
    input   wire            wr_req      ,   //写数据请求
    input   wire    [1:0]   wr_ba       ,   //写阶段Bank地址
    input   wire    [15:0]  wr_data     ,   //写入SDRAM的数据
    input   wire            wr_end      ,   //一次写结束信号
    input   wire    [3:0]   wr_cmd      ,   //写阶段命令
    input   wire    [12:0]  wr_addr     ,   //写阶段数据地址
    input   wire            wr_sdram_en ,
//sdram_read
    input   wire            rd_req      ,   //读数据请求
    input   wire            rd_end      ,   //一次读结束
    input   wire    [3:0]   rd_cmd      ,   //读阶段命令
    input   wire    [12:0]  rd_addr     ,   //读阶段数据地址
    input   wire    [1:0]   rd_ba       ,   //读阶段Bank地址

    output  reg             aref_en     ,   //自刷新使能
    output  reg             wr_en       ,   //写数据使能
    output  reg             rd_en       ,   //读数据使能

    output  wire            sdram_cke   ,   //SDRAM时钟使能
    output  wire            sdram_cs_n  ,   //SDRAM片选信号
    output  wire            sdram_ras_n ,   //SDRAM行地址选通
    output  wire            sdram_cas_n ,   //SDRAM列地址选通
    output  wire            sdram_we_n  ,   //SDRAM写使能
    output  reg     [1:0]   sdram_ba    ,   //SDRAM Bank地址
    output  reg     [12:0]  sdram_addr  ,   //SDRAM地址总线
    inout   wire    [15:0]  sdram_dq        //SDRAM数据总线
);

//parameter define
parameter   IDLE    =   5'b0_0001   ,   //初始状态
            ARBIT   =   5'b0_0010   ,   //仲裁状态
            AREF    =   5'b0_0100   ,   //自动刷新状态
            WRITE   =   5'b0_1000   ,   //写状态
            READ    =   5'b1_0000   ;   //读状态
parameter   CMD_NOP =   4'b0111     ;   //空操作指令

//reg   define
reg     [3:0]   sdram_cmd   ;   //写入SDRAM命令
reg     [4:0]   state       ;   //状态机状态

//state:状态机状态
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else    case(state)
        IDLE:   if(init_end == 1'b1)
                    state   <=  ARBIT;
                else
                    state   <=  IDLE;
        ARBIT:  if(aref_req == 1'b1)
                    state   <=  AREF;
                else    if(wr_req == 1'b1)
                    state   <=  WRITE;
                else    if(rd_req == 1'b1)
                    state   <=  READ;
                else
                    state   <=  ARBIT;
        AREF:   if(aref_end == 1'b1)
                    state   <=  ARBIT;
                else
                    state   <=  AREF; 
        WRITE:  if(wr_end == 1'b1)
                    state   <=  ARBIT;
                else
                    state   <=  WRITE;
        READ:   if(rd_end == 1'b1)
                    state   <=  ARBIT;
                else
                    state   <=  READ;
        default:state   <=  IDLE;
    endcase

//aref_en:自动刷新使能
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        aref_en  <=  1'b0;
    else    if((state == ARBIT) && (aref_req == 1'b1))
        aref_en  <=  1'b1;
    else    if(aref_end == 1'b1)
        aref_en  <=  1'b0;

//wr_en:写数据使能
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_en   <=  1'b0;
    else    if((state == ARBIT) && (aref_req == 1'b0) && (wr_req == 1'b1))
        wr_en   <=  1'b1;
    else    if(wr_end == 1'b1)
        wr_en   <=  1'b0;

//rd_en:读数据使能
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en   <=  1'b0;
    else    if((state == ARBIT) && (aref_req == 1'b0)  && (rd_req == 1'b1))
        rd_en   <=  1'b1;
    else    if(rd_end == 1'b1)
        rd_en   <=  1'b0;

//sdram_cmd:写入SDRAM命令;sdram_ba:SDRAM Bank地址;sdram_addr:SDRAM地址总线
always@(*)
    case(state) 
        IDLE: begin
            sdram_cmd   <=  init_cmd;
            sdram_ba    <=  init_ba;
            sdram_addr  <=  init_addr;
        end
        AREF: begin
            sdram_cmd   <=  aref_cmd;
            sdram_ba    <=  aref_ba;
            sdram_addr  <=  aref_addr;
        end
        WRITE: begin
            sdram_cmd   <=  wr_cmd;
            sdram_ba    <=  wr_ba;
            sdram_addr  <=  wr_addr;
        end
        READ: begin
            sdram_cmd   <=  rd_cmd;
            sdram_ba    <=  rd_ba;
            sdram_addr  <=  rd_addr;
        end
        default: begin
            sdram_cmd   <=  CMD_NOP;
            sdram_ba    <=  2'b11;
            sdram_addr  <=  13'h1fff;
        end
    endcase

//SDRAM时钟使能
assign  sdram_cke = 1'b1;
//SDRAM数据总线
assign  sdram_dq = (wr_sdram_en == 1'b1) ? wr_data : 16'bz;
//片选信号,行地址选通信号,列地址选通信号,写使能信号
assign  {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = sdram_cmd;

endmodule

sdram_ctrl

`timescale  1ns/1ns

module  sdram_ctrl
(
    input   wire            sys_clk         ,   //系统时钟
    input   wire            sys_rst_n       ,   //复位信号,低电平有效
//SDRAM写端口
    input   wire            sdram_wr_req    ,   //写SDRAM请求信号
    input   wire    [23:0]  sdram_wr_addr   ,   //SDRAM写操作的地址
    input   wire    [9:0]   wr_burst_len    ,   //写sdram时数据突发长度
    input   wire    [15:0]  sdram_data_in   ,   //写入SDRAM的数据
    output  wire            sdram_wr_ack    ,   //写SDRAM响应信号
//SDRAM读端口
    input   wire            sdram_rd_req    ,   //读SDRAM请求信号
    input   wire    [23:0]  sdram_rd_addr   ,   //SDRAM读操作的地址
    input   wire    [9:0]   rd_burst_len    ,   //读sdram时数据突发长度
    output  wire    [15:0]  sdram_data_out  ,   //从SDRAM读出的数据
    output  wire            init_end        ,   //SDRAM 初始化完成标志
    output  wire            sdram_rd_ack    ,   //读SDRAM响应信号
//FPGA与SDRAM硬件接口
    output  wire            sdram_cke       ,   // SDRAM 时钟有效信号
    output  wire            sdram_cs_n      ,   // SDRAM 片选信号
    output  wire            sdram_ras_n     ,   // SDRAM 行地址选通
    output  wire            sdram_cas_n     ,   // SDRAM 列地址选通
    output  wire            sdram_we_n      ,   // SDRAM 写使能
    output  wire    [1:0]   sdram_ba        ,   // SDRAM Bank地址
    output  wire    [12:0]  sdram_addr      ,   // SDRAM 地址总线
    inout   wire    [15:0]  sdram_dq            // SDRAM 数据总线
);

//wire  define
//sdram_init
wire    [3:0]   init_cmd    ;   //初始化阶段写入sdram的指令
wire    [1:0]   init_ba     ;   //初始化阶段Bank地址
wire    [12:0]  init_addr   ;   //初始化阶段地址数据,辅助预充电操作
//sdram_a_ref
wire            aref_req    ;   //自动刷新请求
wire            aref_end    ;   //自动刷新结束标志
wire    [3:0]   aref_cmd    ;   //自动刷新阶段写入sdram的指令
wire    [1:0]   aref_ba     ;   //自动刷新阶段Bank地址
wire    [12:0]  aref_addr   ;   //地址数据,辅助预充电操作
wire            aref_en     ;   //自动刷新使能
//sdram_write
wire            wr_en       ;   //写使能
wire            wr_end      ;   //一次写结束信号
wire    [3:0]   write_cmd   ;   //写阶段命令
wire    [1:0]   write_ba    ;   //写数据阶段Bank地址
wire    [12:0]  write_addr  ;   //写阶段数据地址
wire            wr_sdram_en ;   //SDRAM写使能
wire    [15:0]  wr_sdram_data;  //写入SDRAM的数据
//sdram_read
wire            rd_en       ;   //读使能
wire            rd_end      ;   //一次突发读结束
wire    [3:0]   read_cmd    ;   //读数据阶段写入sdram的指令
wire    [1:0]   read_ba     ;   //读阶段Bank地址
wire    [12:0]  read_addr   ;   //读阶段数据地址

//------------- sdram_init_inst -------------
sdram_init  sdram_init_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率100MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效

    .init_cmd   (init_cmd   ),  //初始化阶段写入sdram的指令
    .init_ba    (init_ba    ),  //初始化阶段Bank地址
    .init_addr  (init_addr  ),  //初始化阶段地址数据,辅助预充电操作
    .init_end   (init_end   )   //初始化结束信号
);

//------------- sdram_arbit_inst -------------
sdram_arbit sdram_arbit_inst
(
    .sys_clk    (sys_clk        ),  //系统时钟
    .sys_rst_n  (sys_rst_n      ),  //复位信号
//sdram_init
    .init_cmd   (init_cmd       ),  //初始化阶段命令
    .init_end   (init_end       ),  //初始化结束标志
    .init_ba    (init_ba        ),  //初始化阶段Bank地址
    .init_addr  (init_addr      ),  //初始化阶段数据地址
//sdram_auto_ref
    .aref_req   (aref_req       ),  //自刷新请求
    .aref_end   (aref_end       ),  //自刷新结束
    .aref_cmd   (aref_cmd       ),  //自刷新阶段命令
    .aref_ba    (aref_ba        ),  //自动刷新阶段Bank地址
    .aref_addr  (aref_addr      ),  //自刷新阶段数据地址
//sdram_write
    .wr_req     (sdram_wr_req   ),  //写数据请求
    .wr_end     (wr_end         ),  //一次写结束信号
    .wr_cmd     (write_cmd      ),  //写阶段命令
    .wr_ba      (write_ba       ),  //写阶段Bank地址
    .wr_addr    (write_addr     ),  //写阶段数据地址
    .wr_sdram_en(wr_sdram_en    ),  //SDRAM写使能
    .wr_data    (wr_sdram_data  ),  //写入SDRAM的数据
//sdram_read
    .rd_req     (sdram_rd_req   ),  //读数据请求
    .rd_end     (rd_end         ),  //一次读结束
    .rd_cmd     (read_cmd       ),  //读阶段命令
    .rd_addr    (read_addr      ),  //读阶段数据地址
    .rd_ba      (read_ba        ),  //读阶段Bank地址

    .aref_en    (aref_en        ),  //自刷新使能
    .wr_en      (wr_en          ),  //写数据使能
    .rd_en      (rd_en          ),  //读数据使能

    .sdram_cke  (sdram_cke      ),  //SDRAM时钟使能
    .sdram_cs_n (sdram_cs_n     ),  //SDRAM片选信号
    .sdram_ras_n(sdram_ras_n    ),  //SDRAM行地址选通
    .sdram_cas_n(sdram_cas_n    ),  //SDRAM列地址选通
    .sdram_we_n (sdram_we_n     ),  //SDRAM写使能
    .sdram_ba   (sdram_ba       ),  //SDRAM Bank地址
    .sdram_addr (sdram_addr     ),  //SDRAM地址总线
    .sdram_dq   (sdram_dq       )   //SDRAM数据总线
);

//------------- sdram_a_ref_inst -------------
sdram_a_ref sdram_a_ref_inst
(
    .sys_clk     (sys_clk   ),  //系统时钟,频率100MHz
    .sys_rst_n   (sys_rst_n ),  //复位信号,低电平有效
    .init_end    (init_end  ),  //初始化结束信号
    .aref_en     (aref_en   ),  //自动刷新使能

    .aref_req    (aref_req  ),  //自动刷新请求
    .aref_cmd    (aref_cmd  ),  //自动刷新阶段写入sdram的指令
    .aref_ba     (aref_ba   ),  //自动刷新阶段Bank地址
    .aref_addr   (aref_addr ),  //地址数据,辅助预充电操作
    .aref_end    (aref_end  )   //自动刷新结束标志
);

//------------- sdram_write_inst -------------
sdram_write sdram_write_inst
(
    .sys_clk        (sys_clk        ),  //系统时钟,频率100MHz
    .sys_rst_n      (sys_rst_n      ),  //复位信号,低电平有效
    .init_end       (init_end       ),  //初始化结束信号
    .wr_en          (wr_en          ),  //写使能

    .wr_addr        (sdram_wr_addr  ),  //写SDRAM地址
    .wr_data        (sdram_data_in  ),  //待写入SDRAM的数据(写FIFO传入)
    .wr_burst_len   (wr_burst_len   ),  //写突发SDRAM字节数

    .wr_ack         (sdram_wr_ack   ),  //写SDRAM响应信号
    .wr_end         (wr_end         ),  //一次突发写结束
    .write_cmd      (write_cmd      ),  //写数据阶段写入sdram的指令
    .write_ba       (write_ba       ),  //写数据阶段Bank地址
    .write_addr     (write_addr     ),  //地址数据,辅助预充电操作
    .wr_sdram_en    (wr_sdram_en    ),  //数据总线输出使能
    .wr_sdram_data  (wr_sdram_data  )   //写入SDRAM的数据
);

//------------- sdram_read_inst -------------
sdram_read  sdram_read_inst
(
    .sys_clk        (sys_clk        ),  //系统时钟,频率100MHz
    .sys_rst_n      (sys_rst_n      ),  //复位信号,低电平有效
    .init_end       (init_end       ),  //初始化结束信号
    .rd_en          (rd_en          ),  //读使能

    .rd_addr        (sdram_rd_addr  ),  //读SDRAM地址
    .rd_data        (sdram_dq       ),  //自SDRAM中读出的数据
    .rd_burst_len   (rd_burst_len   ),  //读突发SDRAM字节数

    .rd_ack         (sdram_rd_ack   ),  //读SDRAM响应信号
    .rd_end         (rd_end         ),  //一次突发读结束
    .read_cmd       (read_cmd       ),  //读数据阶段写入sdram的指令
    .read_ba        (read_ba        ),  //读数据阶段Bank地址
    .read_addr      (read_addr      ),  //地址数据,辅助预充电操作
    .rd_sdram_data  (sdram_data_out )   //SDRAM读出的数据
);

endmodule

2. sdram_top

fifo_ctrl

`timescale  1ns/1ns

module  fifo_ctrl
(
    input   wire            sys_clk         ,   //系统时钟
    input   wire            sys_rst_n       ,   //复位信号
//写fifo信号
    input   wire            wr_fifo_wr_clk  ,   //写FIFO写时钟
    input   wire            wr_fifo_wr_req  ,   //写FIFO写请求
    input   wire    [15:0]  wr_fifo_wr_data ,   //写FIFO写数据
    input   wire    [23:0]  sdram_wr_b_addr ,   //写SDRAM首地址
    input   wire    [23:0]  sdram_wr_e_addr ,   //写SDRAM末地址
    input   wire    [9:0]   wr_burst_len    ,   //写SDRAM数据突发长度
    input   wire            wr_rst          ,   //写复位信号
//读fifo信号
    input   wire            rd_fifo_rd_clk  ,   //读FIFO读时钟
    input   wire            rd_fifo_rd_req  ,   //读FIFO读请求
    input   wire    [23:0]  sdram_rd_b_addr ,   //读SDRAM首地址
    input   wire    [23:0]  sdram_rd_e_addr ,   //读SDRAM末地址
    input   wire    [9:0]   rd_burst_len    ,   //读SDRAM数据突发长度
    input   wire            rd_rst          ,   //读复位信号
    output  wire    [15:0]  rd_fifo_rd_data ,   //读FIFO读数据
    output  wire    [9:0]   rd_fifo_num     ,   //读fifo中的数据量

    input   wire            read_valid      ,   //SDRAM读使能
    input   wire            init_end        ,   //SDRAM初始化完成标志
//SDRAM写信号
    input   wire            sdram_wr_ack    ,   //SDRAM写响应
    output  reg             sdram_wr_req    ,   //SDRAM写请求
    output  reg     [23:0]  sdram_wr_addr   ,   //SDRAM写地址
    output  wire    [15:0]  sdram_data_in   ,   //写入SDRAM的数据
//SDRAM读信号
    input   wire            sdram_rd_ack    ,   //SDRAM读相应
    input   wire    [15:0]  sdram_data_out  ,   //读出SDRAM数据
    output  reg             sdram_rd_req    ,   //SDRAM读请求
    output  reg     [23:0]  sdram_rd_addr       //SDRAM读地址
);

//wire define
wire            wr_ack_fall ;   //写响应信号下降沿
wire            rd_ack_fall ;   //读相应信号下降沿
wire    [9:0]   wr_fifo_num ;   //写fifo中的数据量

//reg define
reg        wr_ack_dly       ;   //写响应打拍
reg        rd_ack_dly       ;   //读响应打拍

//wr_ack_dly:写响应信号打拍
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_ack_dly  <=  1'b0;
    else
        wr_ack_dly  <=  sdram_wr_ack;

//rd_ack_dly:读响应信号打拍
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_ack_dly  <=  1'b0;
    else
        rd_ack_dly <=  sdram_rd_ack;

//wr_ack_fall,rd_ack_fall:检测读写响应信号下降沿
assign  wr_ack_fall = (wr_ack_dly & ~sdram_wr_ack);
assign  rd_ack_fall = (rd_ack_dly & ~sdram_rd_ack);

//sdram_wr_addr:sdram写地址
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sdram_wr_addr   <=  24'd0;
    else    if(wr_rst == 1'b1)
        sdram_wr_addr   <=  sdram_wr_b_addr;
    else    if(wr_ack_fall == 1'b1) //一次突发写结束,更改写地址
        begin
            if(sdram_wr_addr < (sdram_wr_e_addr - wr_burst_len))
                        //不使用乒乓操作,一次突发写结束,更改写地址,未达到末地址,写地址累加
                sdram_wr_addr   <=  sdram_wr_addr + wr_burst_len;
            else        //不使用乒乓操作,到达末地址,回到写起始地址
                sdram_wr_addr   <=  sdram_wr_b_addr;
        end

//sdram_rd_addr:sdram读地址
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sdram_rd_addr   <=  24'd0;
    else    if(rd_rst == 1'b1)
        sdram_rd_addr   <=  sdram_rd_b_addr;
    else    if(rd_ack_fall == 1'b1) //一次突发读结束,更改读地址
        begin
            if(sdram_rd_addr < (sdram_rd_e_addr - rd_burst_len))
                    //读地址未达到末地址,读地址累加
                sdram_rd_addr   <=  sdram_rd_addr + rd_burst_len;
            else    //到达末地址,回到首地址
                sdram_rd_addr   <=  sdram_rd_b_addr;
        end

//sdram_wr_req,sdram_rd_req:读写请求信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            sdram_wr_req    <=  1'b0;
            sdram_rd_req    <=  1'b0;
        end
    else    if(init_end == 1'b1)   //初始化完成后响应读写请求
        begin   //优先执行写操作,防止写入SDRAM中的数据丢失
            if(wr_fifo_num >= wr_burst_len)
                begin   //写FIFO中的数据量达到写突发长度
                    sdram_wr_req    <=  1'b1;   //写请求有效
                    sdram_rd_req    <=  1'b0;
                end
            else    if((rd_fifo_num < rd_burst_len) && (read_valid == 1'b1))
                begin //读FIFO中的数据量小于读突发长度,且读使能信号有效
                    sdram_wr_req    <=  1'b0;
                    sdram_rd_req    <=  1'b1;   //读请求有效
                end
            else
                begin
                    sdram_wr_req    <=  1'b0;
                    sdram_rd_req    <=  1'b0;
                end
        end
    else
        begin
            sdram_wr_req    <=  1'b0;
            sdram_rd_req    <=  1'b0;
        end

//------------- wr_fifo_data -------------
fifo_data   wr_fifo_data(
    //用户接口
    .wrclk      (wr_fifo_wr_clk ),  //写时钟
    .wrreq      (wr_fifo_wr_req ),  //写请求
    .data       (wr_fifo_wr_data),  //写数据
    //SDRAM接口
    .rdclk      (sys_clk        ),  //读时钟
    .rdreq      (sdram_wr_ack   ),  //读请求
    .q          (sdram_data_in  ),  //读数据

    .rdusedw    (wr_fifo_num    ),  //FIFO中的数据量
    .wrusedw    (               ),
    .aclr       (~sys_rst_n || wr_rst)  //清零信号
    );

//------------- rd_fifo_data -------------
fifo_data   rd_fifo_data(
    //sdram接口
    .wrclk      (sys_clk        ),  //写时钟
    .wrreq      (sdram_rd_ack   ),  //写请求
    .data       (sdram_data_out ),  //写数据
    //用户接口
    .rdclk      (rd_fifo_rd_clk ),  //读时钟
    .rdreq      (rd_fifo_rd_req ),  //读请求
    .q          (rd_fifo_rd_data),  //读数据

    .rdusedw    (               ),
    .wrusedw    (rd_fifo_num    ),  //FIFO中的数据量
    .aclr       (~sys_rst_n || rd_rst)  //清零信号
    );

endmodule

sdram_top

`timescale  1ns/1ns

module  sdram_top
(
    input   wire            sys_clk         ,   //系统时钟
    input   wire            clk_out         ,   //相位偏移时钟
    input   wire            sys_rst_n       ,   //复位信号,低有效
//写FIFO信号
    input   wire            wr_fifo_wr_clk  ,   //写FIFO写时钟
    input   wire            wr_fifo_wr_req  ,   //写FIFO写请求
    input   wire    [15:0]  wr_fifo_wr_data ,   //写FIFO写数据
    input   wire    [23:0]  sdram_wr_b_addr ,   //写SDRAM首地址
    input   wire    [23:0]  sdram_wr_e_addr ,   //写SDRAM末地址
    input   wire    [9:0]   wr_burst_len    ,   //写SDRAM数据突发长度
    input   wire            wr_rst          ,   //写复位信号
//读FIFO信号
    input   wire            rd_fifo_rd_clk  ,   //读FIFO读时钟
    input   wire            rd_fifo_rd_req  ,   //读FIFO读请求
    input   wire    [23:0]  sdram_rd_b_addr ,   //读SDRAM首地址
    input   wire    [23:0]  sdram_rd_e_addr ,   //读SDRAM末地址
    input   wire    [9:0]   rd_burst_len    ,   //读SDRAM数据突发长度
    input   wire            rd_rst          ,   //读复位信号
    output  wire    [15:0]  rd_fifo_rd_data ,   //读FIFO读数据
    output  wire    [9:0]   rd_fifo_num     ,   //读fifo中的数据量

    input   wire            read_valid      ,   //SDRAM读使能
    output  wire            init_end        ,   //SDRAM初始化完成标志
//SDRAM接口信号
    output  wire            sdram_clk       ,   //SDRAM芯片时钟
    output  wire            sdram_cke       ,   //SDRAM时钟有效信号
    output  wire            sdram_cs_n      ,   //SDRAM片选信号
    output  wire            sdram_ras_n     ,   //SDRAM行地址选通脉冲
    output  wire            sdram_cas_n     ,   //SDRAM列地址选通脉冲
    output  wire            sdram_we_n      ,   //SDRAM写允许位
    output  wire    [1:0]   sdram_ba        ,   //SDRAM的L-Bank地址线
    output  wire    [12:0]  sdram_addr      ,   //SDRAM地址总线
    output  wire    [1:0]   sdram_dqm       ,   //SDRAM数据掩码
    inout   wire    [15:0]  sdram_dq            //SDRAM数据总线
);

//wire  define
wire            sdram_wr_req    ;   //sdram 写请求
wire            sdram_wr_ack    ;   //sdram 写响应
wire    [23:0]  sdram_wr_addr   ;   //sdram 写地址
wire    [15:0]  sdram_data_in   ;   //写入sdram中的数据

wire            sdram_rd_req    ;   //sdram 读请求
wire            sdram_rd_ack    ;   //sdram 读响应
wire    [23:0]  sdram_rd_addr   ;   //sdram 读地址
wire    [15:0]  sdram_data_out  ;   //从sdram中读出的数据

//sdram_clk:SDRAM芯片时钟
assign  sdram_clk = clk_out;
//sdram_dqm:SDRAM数据掩码
assign  sdram_dqm = 2'b00;

//------------- fifo_ctrl_inst -------------
fifo_ctrl   fifo_ctrl_inst(

//system    signal
    .sys_clk        (sys_clk        ),  //SDRAM控制时钟
    .sys_rst_n      (sys_rst_n      ),  //复位信号
//write fifo signal
    .wr_fifo_wr_clk (wr_fifo_wr_clk ),  //写FIFO写时钟
    .wr_fifo_wr_req (wr_fifo_wr_req ),  //写FIFO写请求
    .wr_fifo_wr_data(wr_fifo_wr_data),  //写FIFO写数据
    .sdram_wr_b_addr(sdram_wr_b_addr),  //写SDRAM首地址
    .sdram_wr_e_addr(sdram_wr_e_addr),  //写SDRAM末地址
    .wr_burst_len   (wr_burst_len   ),  //写SDRAM数据突发长度
    .wr_rst         (wr_rst         ),  //写清零信号
//read fifo signal
    .rd_fifo_rd_clk (rd_fifo_rd_clk ),  //读FIFO读时钟
    .rd_fifo_rd_req (rd_fifo_rd_req ),  //读FIFO读请求
    .rd_fifo_rd_data(rd_fifo_rd_data),  //读FIFO读数据
    .rd_fifo_num    (rd_fifo_num    ),  //读FIFO中的数据量
    .sdram_rd_b_addr(sdram_rd_b_addr),  //读SDRAM首地址
    .sdram_rd_e_addr(sdram_rd_e_addr),  //读SDRAM末地址
    .rd_burst_len   (rd_burst_len   ),  //读SDRAM数据突发长度
    .rd_rst         (rd_rst         ),  //读清零信号
//USER ctrl signal
    .read_valid     (read_valid     ),  //SDRAM读使能
    .init_end       (init_end       ),  //SDRAM初始化完成标志
//SDRAM ctrl of write
    .sdram_wr_ack   (sdram_wr_ack   ),  //SDRAM写响应
    .sdram_wr_req   (sdram_wr_req   ),  //SDRAM写请求
    .sdram_wr_addr  (sdram_wr_addr  ),  //SDRAM写地址
    .sdram_data_in  (sdram_data_in  ),  //写入SDRAM的数据
//SDRAM ctrl of read
    .sdram_rd_ack   (sdram_rd_ack   ),  //SDRAM读请求
    .sdram_data_out (sdram_data_out ),  //SDRAM读响应
    .sdram_rd_req   (sdram_rd_req   ),  //SDRAM读地址
    .sdram_rd_addr  (sdram_rd_addr  )  //读出SDRAM数据
);

//------------- sdram_ctrl_inst -------------
sdram_ctrl  sdram_ctrl_inst(

    .sys_clk        (sys_clk        ),   //系统时钟
    .sys_rst_n      (sys_rst_n      ),   //复位信号,低电平有效
//SDRAM 控制器写端口
    .sdram_wr_req   (sdram_wr_req   ),   //写SDRAM请求信号
    .sdram_wr_addr  (sdram_wr_addr  ),   //SDRAM写操作的地址
    .wr_burst_len   (wr_burst_len   ),   //写sdram时数据突发长度
    .sdram_data_in  (sdram_data_in  ),   //写入SDRAM的数据
    .sdram_wr_ack   (sdram_wr_ack   ),   //写SDRAM响应信号
//SDRAM 控制器读端口
    .sdram_rd_req   (sdram_rd_req   ),  //读SDRAM请求信号
    .sdram_rd_addr  (sdram_rd_addr  ),  //SDRAM写操作的地址
    .rd_burst_len   (rd_burst_len   ),  //读sdram时数据突发长度
    .sdram_data_out (sdram_data_out ),  //从SDRAM读出的数据
    .init_end       (init_end       ),  //SDRAM 初始化完成标志
    .sdram_rd_ack   (sdram_rd_ack   ),  //读SDRAM响应信号
//FPGA与SDRAM硬件接口
    .sdram_cke      (sdram_cke      ),  // SDRAM 时钟有效信号
    .sdram_cs_n     (sdram_cs_n     ),  // SDRAM 片选信号
    .sdram_ras_n    (sdram_ras_n    ),  // SDRAM 行地址选通脉冲
    .sdram_cas_n    (sdram_cas_n    ),  // SDRAM 列地址选通脉冲
    .sdram_we_n     (sdram_we_n     ),  // SDRAM 写允许位
    .sdram_ba       (sdram_ba       ),  // SDRAM L-Bank地址线
    .sdram_addr     (sdram_addr     ),  // SDRAM 地址总线
    .sdram_dq       (sdram_dq       )   // SDRAM 数据总线
);

endmodule

3. uart_sdram

uart_rx

`timescale  1ns/1ns

module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

uart_tx

`timescale  1ns/1ns

module  uart_tx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
     input   wire            sys_clk     ,   //系统时钟50MHz
     input   wire            sys_rst_n   ,   //全局复位
     input   wire    [7:0]   pi_data     ,   //模块输入的8bit数据
     input   wire            pi_flag     ,   //并行数据有效标志信号
 
     output  reg             tx              //串转并后的1bit数据
);

//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg [12:0]  baud_cnt;
reg         bit_flag;
reg [3:0]   bit_cnt ;
reg         work_en ;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            work_en <= 1'b0;
        else    if(pi_flag == 1'b1)
            work_en <= 1'b1;
        else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            baud_cnt <= 13'b0;
        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
            baud_cnt <= 13'b0;
        else    if(work_en == 1'b1)
            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            bit_flag <= 1'b0;
        else    if(baud_cnt == 13'd1)
            bit_flag <= 1'b1;
        else
            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
        bit_cnt <= 4'b0;
    else    if((bit_flag == 1'b1) && (work_en == 1'b1))
        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出
always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            tx <= 1'b1; //空闲状态时为高电平
        else    if(bit_flag == 1'b1)
            case(bit_cnt)
                0       : tx <= 1'b0;
                1       : tx <= pi_data[0];
                2       : tx <= pi_data[1];
                3       : tx <= pi_data[2];
                4       : tx <= pi_data[3];
                5       : tx <= pi_data[4];
                6       : tx <= pi_data[5];
                7       : tx <= pi_data[6];
                8       : tx <= pi_data[7];
                9       : tx <= 1'b1;
                default : tx <= 1'b1;
            endcase

endmodule

fifo_read

`timescale  1ns/1ns

module  fifo_read
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire    [9:0]   rd_fifo_num ,   //SDRAM中读fifo中数据个数
    input   wire    [7:0]   pi_data     ,   //读出数据
    input   wire    [9:0]   burst_num   ,   //一次突发数据个数

    output  reg             read_en     ,   //SDRAM中读fifo的读使能
    output  wire    [7:0]   tx_data     ,   //输出数据
    output  reg             tx_flag         //输出数据标志信号
);

//parameter define
parameter   BAUD_CNT_END        =   13'd5207        ,
            BAUD_CNT_END_HALF   =   13'd2603        ;
parameter   CNT_WAIT_MAX        =   24'd4_999_999   ;

//wire  define
wire    [9:0]   data_num    ;   //fifo中数据个数

//reg   define
reg         read_en_dly     ;
reg [12:0]  baud_cnt        ;
reg         rd_en           ;
reg         rd_flag         ;
reg [9:0]   cnt_read        ;
reg [3:0]   bit_cnt         ;
reg         bit_flag        ;

//read_en:SDRAM中读fifo的读使能
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        read_en   <=  1'b0;
    else    if(rd_fifo_num == burst_num)
        read_en   <=  1'b1;
    else    if(data_num == burst_num - 2)
        read_en   <=  1'b0;

//read_en_dly:SDRAM中读fifo的读使能打拍
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        read_en_dly    <=  1'b0;
    else
        read_en_dly    <=  read_en;

//rd_flag:向tx模块发送数据使能
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_flag <=  1'b0;
    else    if(cnt_read == burst_num)
        rd_flag <=  1'b0;
    else    if(data_num == burst_num)
        rd_flag <=  1'b1;

//baud_cnt:波特率计数器计数从0计数到BAUD_CNT_END
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt    <=  13'd0;
    else    if(baud_cnt == BAUD_CNT_END)
        baud_cnt    <=  13'd0;
    else    if(rd_flag == 1'b1)
        baud_cnt    <=  baud_cnt + 1'b1;

//bit_flag:bit计数器计数使能
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag    <=  1'b0;
    else    if(baud_cnt == BAUD_CNT_END_HALF)
        bit_flag    <=  1'b1;
    else
        bit_flag    <=  1'b0;

//bit_cnt:bit计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <=  4'b0;
    else    if((bit_cnt == 4'd9) && (bit_flag == 1'b1))
        bit_cnt <=  4'b0;
    else    if(bit_flag ==  1'b1)
        bit_cnt <=  bit_cnt +   1'b1;

//rd_en:读fifo的读使能
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en   <=  1'b0;
    else    if(bit_cnt == 4'd9 && bit_flag == 1'b1)
        rd_en   <=  1'b1;
    else
        rd_en   <=  1'b0;

//cnt_read:读出数据计数
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_read    <=  10'd0;
    else    if(cnt_read == burst_num)
        cnt_read    <=  10'b0;
    else    if(rd_en == 1'b1)
        cnt_read    <=  cnt_read + 1'b1;
    else
        cnt_read    <=  cnt_read;

//tx_flag:读出数据标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        tx_flag <=  1'b0;
    else
        tx_flag <=  rd_en;

//-------------fifo_read_inst--------------
read_fifo   read_fifo_inst(
    .clock  (sys_clk        ),  //input clk
    .data   (pi_data        ),  //input [7 : 0] din
    .wrreq  (read_en_dly    ),  //input wr_en
    .rdreq  (rd_en          ),  //input rd_en

    .q      (tx_data        ),  //output [7 : 0] dout
    .usedw  (data_num       )
);

endmodule

uart_sdram

`timescale  1ns/1ns

module  uart_sdram
(
    input   wire            sys_clk     ,   //时钟信号
    input   wire            sys_rst_n   ,   //复位信号
    input   wire            rx          ,   //串口接收数据

    output  wire            tx          ,   //串口发送数据

    output  wire            sdram_clk   ,   //SDRAM 芯片时钟
    output  wire            sdram_cke   ,   //SDRAM 时钟有效
    output  wire            sdram_cs_n  ,   //SDRAM 片选
    output  wire            sdram_cas_n ,   //SDRAM 行有效
    output  wire            sdram_ras_n ,   //SDRAM 列有效
    output  wire            sdram_we_n  ,   //SDRAM 写有效
    output  wire    [1:0]   sdram_ba    ,   //SDRAM Bank地址
    output  wire    [12:0]  sdram_addr  ,   //SDRAM 行/列地址
    output  wire    [1:0]   sdram_dqm   ,   //SDRAM 数据掩码
    inout   wire    [15:0]  sdram_dq        //SDRAM 数据
);

//parameter define
parameter   DATA_NUM    =   24'd10          ;   //写入SDRAM数据个数
parameter   WAIT_MAX    =   16'd750         ;   //等待计数最大值
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率

// wire define
//uart_rx
wire    [ 7:0]  rx_data         ;   //串口接收模块拼接后的8位数据
wire            rx_flag         ;   //数据标志信号

//fifo_read
wire    [ 7:0]  rfifo_wr_data   ;   //读fifo发热写入数据
wire            rfifo_wr_en     ;   //读fifo的写使能
wire    [ 7:0]  rfifo_rd_data   ;   //读fifo的读数据
wire            rfifo_rd_en     ;   //读fifo的读使能
wire    [9:0]   rd_fifo_num     ;   //读fifo中的数据量

//clk_gen
wire            clk_50m         ;
wire            clk_100m        ;
wire            clk_100m_shift  ;   //pll产生时钟
wire            locked          ;   //pll锁定信号
wire            rst_n           ;   //复位信号

//sdram_top_inst
reg     [23:0]  data_num        ;   //写入SDRAM数据个数计数
reg             read_valid      ;   //数据读使能
reg     [15:0]  cnt_wait        ;   //等待计数器

//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;

//data_num:写入SDRAM数据个数计数
always@(posedge clk_50m or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_num    <=  24'd0;
    else    if(read_valid == 1'b1)
        data_num    <=  24'd0;
    else    if(rx_flag == 1'b1)
        data_num    <=  data_num + 1'b1;
    else
        data_num    <=  data_num;

//cnt_wait:等待计数器
always@(posedge clk_50m or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_wait    <=  16'd0;
    else    if(cnt_wait == WAIT_MAX)
        cnt_wait    <=  16'd0;
    else    if(data_num == DATA_NUM)
        cnt_wait    <=  cnt_wait + 1'b1;

//read_valid:数据读使能
always@(posedge clk_50m or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        read_valid  <=  1'b0;
    else    if(cnt_wait == WAIT_MAX)
        read_valid  <=  1'b1;
    else    if(rd_fifo_num == DATA_NUM)
        read_valid  <=  1'b0;
        
//------------- clk_gen_inst -------------
clk_gen clk_gen_inst (
    .inclk0     (sys_clk        ),
    .areset     (~sys_rst_n     ),
    .c0         (clk_50m        ),
    .c1         (clk_100m       ),
    .c2         (clk_100m_shift ),

    .locked     (locked         )
);

//-------------uart_rx_inst-------------
uart_rx
#(
    .UART_BPS    (UART_BPS ),   //串口波特率
    .CLK_FREQ    (CLK_FREQ )    //时钟频率
)
uart_rx_inst
(
    .sys_clk     (clk_50m  ),   //input             sys_clk
    .sys_rst_n   (rst_n    ),   //input             sys_rst_n
    .rx          (rx       ),   //input             rx

    .po_data     (rx_data  ),   //output    [7:0]   rx_data
    .po_flag     (rx_flag  )    //output            rx_flag
);

//------------- sdram_top_inst -------------
sdram_top   sdram_top_inst
(
    .sys_clk            (clk_100m       ),  //sdram 控制器参考时钟
    .clk_out            (clk_100m_shift ),  //用于输出的相位偏移时钟
    .sys_rst_n          (rst_n          ),  //系统复位
//用户写端口
    .wr_fifo_wr_clk     (clk_50m        ),  //写端口FIFO: 写时钟
    .wr_fifo_wr_req     (rx_flag        ),  //写端口FIFO: 写使能
    .wr_fifo_wr_data    ({8'b0,rx_data} ),  //写端口FIFO: 写数据
    .sdram_wr_b_addr    (24'd0          ),  //写SDRAM的起始地址
    .sdram_wr_e_addr    (DATA_NUM       ),  //写SDRAM的结束地址
    .wr_burst_len       (DATA_NUM       ),  //写SDRAM时的数据突发长度
    .wr_rst             (               ),  //写复位
//用户读端口
    .rd_fifo_rd_clk     (clk_50m        ),  //读端口FIFO: 读时钟
    .rd_fifo_rd_req     (rfifo_wr_en    ),  //读端口FIFO: 读使能
    .rd_fifo_rd_data    (rfifo_wr_data  ),  //读端口FIFO: 读数据
    .sdram_rd_b_addr    (24'd0          ),  //读SDRAM的起始地址
    .sdram_rd_e_addr    (DATA_NUM       ),  //读SDRAM的结束地址
    .rd_burst_len       (DATA_NUM       ),  //从SDRAM中读数据时的突发长度
    .rd_rst             (               ),  //读复位
    .rd_fifo_num        (rd_fifo_num    ),   //读fifo中的数据量
//用户控制端口
    .read_valid         (read_valid     ),  //SDRAM 读使能
    .init_end           (               ),  //SDRAM 初始化完成标志
//SDRAM 芯片接口
    .sdram_clk          (sdram_clk      ),  //SDRAM 芯片时钟
    .sdram_cke          (sdram_cke      ),  //SDRAM 时钟有效
    .sdram_cs_n         (sdram_cs_n     ),  //SDRAM 片选
    .sdram_ras_n        (sdram_ras_n    ),  //SDRAM 行有效
    .sdram_cas_n        (sdram_cas_n    ),  //SDRAM 列有效
    .sdram_we_n         (sdram_we_n     ),  //SDRAM 写有效
    .sdram_ba           (sdram_ba       ),  //SDRAM Bank地址
    .sdram_addr         (sdram_addr     ),  //SDRAM 行/列地址
    .sdram_dq           (sdram_dq       ),  //SDRAM 数据
    .sdram_dqm          (sdram_dqm      )   //SDRAM 数据掩码
);

//------------- fifo_read_inst --------------
fifo_read   fifo_read_inst
(
    .sys_clk     (clk_50m       ),   //input             sys_clk
    .sys_rst_n   (sys_rst_n     ),   //input             sys_rst_n
    .rd_fifo_num (rd_fifo_num   ),
    .pi_data     (rfifo_wr_data ),   //input     [7:0]   pi_data
    .burst_num   (DATA_NUM      ),
    
    .read_en     (rfifo_wr_en   ),   //input             pi_flag
    .tx_data     (rfifo_rd_data ),   //output    [7:0]   tx_data
    .tx_flag     (rfifo_rd_en   )    //output            tx_flag
);

//-------------uart_tx_inst-------------
uart_tx
#(
    .UART_BPS    (UART_BPS      ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ      )   //时钟频率
)
uart_tx_inst
(
    .sys_clk     (sys_clk       ),   //input         sys_clk
    .sys_rst_n   (sys_rst_n     ),   //input         sys_rst_n
    .pi_data     (rfifo_rd_data ),   //input [7:0]   pi_data
    .pi_flag     (rfifo_rd_en   ),   //input         pi_flag

    .tx          (tx            )    //output        tx
);

endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51470.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]JAVA毕业设计工程车辆动力电池管理系统(系统+LW)

[附源码]JAVA毕业设计工程车辆动力电池管理系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 …

怎么在Windows下使用Makefile文件

前言&#xff1a;如果熟悉在Linux开发的话&#xff0c;肯定知道Makefile文件的用处&#xff0c;它给我们带来很多的便利。在Linux系统下并不会像Windows那么多开发工具&#xff0c;在Windows下&#xff0c;只要在开发工具上点击一个按钮&#xff0c;就能将工程的所有源码进行编…

365天深度学习训练营-第P2周:彩色图片识别

目录 一、前言 二、我的环境 三、代码实现 1、数据下载以及可视化 2、CNN模型 3、训练结果可视化 4、随机图像预测 四、模型优化 1、CNN模型 2、VGG-16模型 3、Alexnet模型 4、Resnet模型 一、前言 >- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营]…

2022年钒电池行业研究报告

第一章 行业概况 钒电池&#xff08;Vanadium Redox Battery&#xff0c;缩写为VRB&#xff09;&#xff0c;全称为全钒氧化还原液流电池&#xff0c;是一种活性物质呈循环流动液态的氧化还原电池。钒电池可以作为大容量储能电站的电池&#xff0c;其工作原理如下&#xff1a;…

Unity 2021 请求 Android 12 读取本地文件权限

目标 工具&#xff1a; Unity 2021.2.14c1f1Android 12 系统手机 目标&#xff1a;实现Unity打出来的Apk包能请求读写android手机本地文件权限 原理 在Android系统中&#xff0c;操作手机中不安全的数据时&#xff0c;需要配置相应的权限&#xff0c;只有经过用户许可才能…

[附源码]JAVA毕业设计个人信息管理系统(系统+LW)

[附源码]JAVA毕业设计个人信息管理系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

深度学习-第三章概率与信息论

前言 概率论学科定义概率与信息论在人工智能领域的应用 3.1&#xff0c;为什么要使用概率论3.2&#xff0c;随机变量3.3&#xff0c;概率分布 3.3.1&#xff0c;离散型变量和概率质量函数3.3.2&#xff0c;连续型变量和概率密度分布函数 3.4&#xff0c;边缘概率3.5&#xff0c…

量子计算新突破!来源于150年前的思想实验

澳大利亚新南威尔士大学的研究表明&#xff0c;使用现代版本的“麦克斯韦妖”&#xff0c;可将量子比特重置为“0”态的错误减少了20倍。 Andrea Morello教授解释了麦克斯韦妖思想实验如何与他的团队通过仅选择冷电子进行量子计算的成就相类比。&#xff08;图片来源&#xff1…

Go-Windows环境的快速搭建

下载 Downloads - The Go Programming Language 或者直接到指定版本下载可以根据个人喜好&#xff0c;下载zip或者执行版 下载后文件夹 查看版本 必须查看版本&#xff0c;通过go version命令进行查看最新版本1.19.3版本 配置的GoPath 已经自动配置进去 需要重新进入一个新的…

HBuilder X 导入git项目以及拉取和推送

1. 首先在 HB中 > 工具 > 插件安装 > Git插件 2. 安装好 Git 插件之后还要安装一个 tortoisegit (小乌龟) tortoisegit : 这里根据电脑下载对应的位数,需要转换成中文的可以下载中文包: 安装 tortoisegit : 1. 双击刚刚下载的msi文件进入安装 2. 连续两次next之后…

[附源码]计算机毕业设计JAVA校园环境保护监督系统

[附源码]计算机毕业设计JAVA校园环境保护监督系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM my…

虚拟机NAL模式连接linux系统

windows虚拟机连外网&#xff0c;相当于设置双网卡&#xff0c;虚拟机编辑网关如下&#xff1a; 在window系统查看设置vmnet8 在linux系统配置ip静态网址 cd /etc/sysconfig/network-scripts/ vim ifcfg-ens33在linux上重启网卡。 在window上添加路由&#xff0c;先查询路由&a…

C语言经典题目之字符串逆序

目录 一、字符串逆序&#xff08;基础题&#xff09; 1.一个经典的错误&#xff0c;标准的零分 2.采用gets函数来修补漏洞 ​编辑 3.非要使用scanf怎么办&#xff1f; 4.使用指针来实现逆序函数 5.将函数修改为&#xff0c;只要传入两个地址&#xff0c;就能逆序这两个地址…

最新 | VDA-ISA5.0.4最新版本发布,汽车企业如何增强信息安全?

汽车行业拥有广泛而复杂的供应链&#xff0c;包括汽车整车制造商、不同层级的零部件厂商、供应商、服务商等众多企业。在这个链条上&#xff0c;其中任何一家企业的网络安全问题不论是数据泄密还是内外部攻击都有可能对整个供应链造成巨大影响。 比如2021年6月&#xff0c;某德…

Apifox很难不爱

一、背景 项目开发我们都知道在一个项目团队中是由很多角色组成&#xff0c;最常见团队的就是前端开发工程师、客户端开发工程师、服务端开发工程师组成一个团队&#xff0c;团队之间进行合作&#xff0c;一般我们都离不开API接口管理和测试&#xff0c;API接口管理可以理解为前…

推荐,文本转图像,图像转图像运营再也不用担心配图了

由 CompVis 领导的 Stable Diffusion V1 改变了开源人工智能模型的性质&#xff0c;并在全球范围内催生了数百个其他模型和创新。Stable Diffusion 如今也是所有软件中最快攀升至 Github 10K Stars 的软件之一&#xff0c;在不到两个月的时间里&#xff0c;它的 Stars 飙升至 3…

【内网安全】——windows信息收集

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

Nginx安装

目录 1. 安装必要环境 1.1 需要安装gcc环境 1.2 PERE 1.3 zlib 1.4 openssl 2. 安装nginx 2.1 下载和解压 2.2 编译 2.2.1 设定配置 2.2.2 编译 2.2.3 安装 3. 启动nginx 4. 配置环境变量 5. 加入system管理 1. 下载Nginx 1. 安装必要环境 1.1 需要安装gcc环境 y…

基于PHP+MySQL学院信息发布系统的设计与实现

再添加完最新动态后可以点击最新动态管理,对已经添加过的最新动态进行编辑和删除,绑定的主要信息包括用文章标题,发布人,发布时间,文章类型,内容等信息 信息技术学院信息发布系统,是一个为学校提供信息的平台,是完全的,高速的,开放的,其核心思想是提供一个以自然语言为主的用户…

算法竞赛入门【码蹄集进阶塔335题】(MT2276-2280)

算法竞赛入门【码蹄集进阶塔335题】(MT2276-2280&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2276-2280&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2276 数的自我2. MT2277 分数个数3. MT2278 欧拉函数…