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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: FPGA进阶(3):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

FPGA进阶(3):SDRAM读写控制器的设计与验证(二)+https://developer.aliyun.com/article/1556623

目录
相关文章
|
5月前
|
编解码 算法 异构计算
基于FPGA的NC图像质量评估verilog实现,包含testbench和MATLAB辅助验证程序
在Vivado 2019.2和Matlab 2022a中测试的图像质量评估算法展示了效果。该算法基于NC指标,衡量图像与原始图像的相似度,关注分辨率、色彩深度和失真。提供的Verilog代码段用于读取并比较两个BMP文件,计算NC值。
|
27天前
|
监控 算法 安全
基于颜色模型和边缘检测的火焰识别FPGA实现,包含testbench和matlab验证程序
本项目展示了基于FPGA的火焰识别算法,可在多种应用场景中实时检测火焰。通过颜色模型与边缘检测技术,结合HSV和YCbCr颜色空间,高效提取火焰特征。使用Vivado 2019.2和Matlab 2022a实现算法,并提供仿真结果与测试样本。FPGA平台充分发挥并行处理优势,实现低延迟高吞吐量的火焰检测。项目包含完整代码及操作视频说明。
|
4月前
|
算法 计算机视觉 异构计算
基于FPGA的图像一维FFT变换IFFT逆变换verilog实现,包含tb测试文件和MATLAB辅助验证
```markdown ## FPGA 仿真与 MATLAB 显示 - 图像处理的 FFT/IFFT FPGA 实现在 Vivado 2019.2 中仿真,结果通过 MATLAB 2022a 展示 - 核心代码片段:`Ddddddddddddddd` - 理论:FPGA 实现的一维 FFT/IFFT,加速数字信号处理,适用于高计算需求的图像应用,如压缩、滤波和识别 ```
|
4月前
|
算法 计算机视觉 异构计算
基于FPGA的图像直方图均衡化处理verilog实现,包含tb测试文件和MATLAB辅助验证
摘要: 在FPGA上实现了图像直方图均衡化算法,通过MATLAB2022a与Vivado2019.2进行仿真和验证。核心程序涉及灰度直方图计算、累积分布及映射变换。算法旨在提升图像全局对比度,尤其适合低对比度图像。FPGA利用可编程增益器和查表技术加速硬件处理,实现像素灰度的均匀重分布,提升视觉效果。![image preview](https://ucc.alicdn.com/pic/developer-ecology/3tnl7rfrqv6tw_a075525027db4afbb9c0529921fd0152.png)
|
3月前
|
异构计算
FPGA进阶(3):SDRAM读写控制器的设计与验证(二)
FPGA进阶(3):SDRAM读写控制器的设计与验证(二)
24 0
|
5月前
|
机器学习/深度学习 算法 异构计算
m基于FPGA的多通道FIR滤波器verilog实现,包含testbench测试文件
本文介绍了使用VIVADO 2019.2仿真的多通道FIR滤波器设计。展示了系统RTL结构图,并简述了FIR滤波器的基本理论,包括单通道和多通道的概念、常见结构及设计方法,如窗函数法、频率采样法、优化算法和机器学习方法。此外,还提供了Verilog核心程序代码,用于实现4通道滤波器模块,包含时钟、复位信号及输入输出接口的定义。
138 7
|
5月前
|
算法 异构计算
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
|
5月前
|
算法 异构计算
m基于FPGA的电子钟verilog实现,可设置闹钟,包含testbench测试文件
该文介绍了基于FPGA的电子钟设计,利用Vivado2019.2平台进行开发并展示测试结果。电子钟设计采用Verilog硬件描述语言,核心包括振荡器、分频器和计数器。时间显示为2个十进制格式,闹钟功能通过存储器和比较器实现,当当前时间等于设定时间时触发。文中给出了Verilog核心程序示例,展示了时钟信号、设置信号及输出的交互。
167 2
|
5月前
|
算法 异构计算
m基于FPGA的MPPT最大功率跟踪算法verilog实现,包含testbench
该内容包括三部分:1) 展示了Vivado 2019.2和Matlab中关于某种算法的仿真结果图像,可能与太阳能光伏系统的最大功率点跟踪(MPPT)相关。2) 简述了MPPT中的爬山法原理,通过调整光伏电池工作点以找到最大功率输出。3) 提供了一个Verilog程序模块`MPPT_test_tops`,用于测试MPPT算法,其中包含`UI_test`和`MPPT_module_U`两个子模块,处理光伏电流和电压信号。
53 1
|
3月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的MSK调制解调系统verilog开发,包含testbench,同步模块,高斯信道模拟模块,误码率统计模块
升级版FPGA MSK调制解调系统集成AWGN信道模型,支持在Vivado 2019.2中设置不同SNR仿真误码率。示例SNR值从0到15,结果展示解调质量随SNR提升。MATLAB仿真验证了MSK性能,图片显示了仿真结果。 ### 理论概要 研究聚焦于软件无线电中的MSK调制解调,利用Verilog实现。MSK是一种相位连续、恒包络的二进制调制技术,优点包括频谱效率高。系统采用无核设计,关键模块包括调制器、解调器和误码检测。复位、输入数据、中频信号等关键信号通过Verilog描述,并通过Chipscope在线观察。
73 6
基于FPGA的MSK调制解调系统verilog开发,包含testbench,同步模块,高斯信道模拟模块,误码率统计模块

热门文章

最新文章