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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: FPGA进阶(3):SDRAM读写控制器的设计与验证(二)

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

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
目录
相关文章
|
2月前
|
编解码 算法 异构计算
基于FPGA的NC图像质量评估verilog实现,包含testbench和MATLAB辅助验证程序
在Vivado 2019.2和Matlab 2022a中测试的图像质量评估算法展示了效果。该算法基于NC指标,衡量图像与原始图像的相似度,关注分辨率、色彩深度和失真。提供的Verilog代码段用于读取并比较两个BMP文件,计算NC值。
|
16天前
|
算法 计算机视觉 异构计算
基于FPGA的图像一维FFT变换IFFT逆变换verilog实现,包含tb测试文件和MATLAB辅助验证
```markdown ## FPGA 仿真与 MATLAB 显示 - 图像处理的 FFT/IFFT FPGA 实现在 Vivado 2019.2 中仿真,结果通过 MATLAB 2022a 展示 - 核心代码片段:`Ddddddddddddddd` - 理论:FPGA 实现的一维 FFT/IFFT,加速数字信号处理,适用于高计算需求的图像应用,如压缩、滤波和识别 ```
|
13天前
|
异构计算 内存技术
FPGA进阶(3):SDRAM读写控制器的设计与验证(一)
FPGA进阶(3):SDRAM读写控制器的设计与验证
18 0
|
1月前
|
算法 计算机视觉 异构计算
基于FPGA的图像直方图均衡化处理verilog实现,包含tb测试文件和MATLAB辅助验证
摘要: 在FPGA上实现了图像直方图均衡化算法,通过MATLAB2022a与Vivado2019.2进行仿真和验证。核心程序涉及灰度直方图计算、累积分布及映射变换。算法旨在提升图像全局对比度,尤其适合低对比度图像。FPGA利用可编程增益器和查表技术加速硬件处理,实现像素灰度的均匀重分布,提升视觉效果。![image preview](https://ucc.alicdn.com/pic/developer-ecology/3tnl7rfrqv6tw_a075525027db4afbb9c0529921fd0152.png)
|
2月前
|
算法 计算机视觉 异构计算
基于肤色模型的人脸识别FPGA实现,包含tb测试文件和MATLAB辅助验证
这是一个关于肤色检测算法的摘要:使用MATLAB 2022a和Vivado 2019.2进行测试和仿真,涉及图像预处理、RGB到YCbCr转换、肤色模型(基于阈值或概率)以及人脸检测。核心程序展示了如何读取图像数据并输入到FPGA处理,通过`tops`模块进行中值滤波、颜色空间转换及人脸检测,最终结果输出到&quot;face.txt&quot;。
|
2月前
|
机器学习/深度学习 算法 异构计算
m基于FPGA的多通道FIR滤波器verilog实现,包含testbench测试文件
本文介绍了使用VIVADO 2019.2仿真的多通道FIR滤波器设计。展示了系统RTL结构图,并简述了FIR滤波器的基本理论,包括单通道和多通道的概念、常见结构及设计方法,如窗函数法、频率采样法、优化算法和机器学习方法。此外,还提供了Verilog核心程序代码,用于实现4通道滤波器模块,包含时钟、复位信号及输入输出接口的定义。
85 7
|
2月前
|
算法 异构计算
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
基于FPGA的ECG信号滤波与心率计算verilog实现,包含testbench
|
2月前
|
算法 异构计算
m基于FPGA的电子钟verilog实现,可设置闹钟,包含testbench测试文件
该文介绍了基于FPGA的电子钟设计,利用Vivado2019.2平台进行开发并展示测试结果。电子钟设计采用Verilog硬件描述语言,核心包括振荡器、分频器和计数器。时间显示为2个十进制格式,闹钟功能通过存储器和比较器实现,当当前时间等于设定时间时触发。文中给出了Verilog核心程序示例,展示了时钟信号、设置信号及输出的交互。
130 2
|
2月前
|
算法 异构计算
m基于FPGA的MPPT最大功率跟踪算法verilog实现,包含testbench
该内容包括三部分:1) 展示了Vivado 2019.2和Matlab中关于某种算法的仿真结果图像,可能与太阳能光伏系统的最大功率点跟踪(MPPT)相关。2) 简述了MPPT中的爬山法原理,通过调整光伏电池工作点以找到最大功率输出。3) 提供了一个Verilog程序模块`MPPT_test_tops`,用于测试MPPT算法,其中包含`UI_test`和`MPPT_module_U`两个子模块,处理光伏电流和电压信号。
33 1
|
6天前
|
C语言 芯片 异构计算
FPGA新起点V1开发板(六-语法篇)——verilog简介+基础语法
FPGA新起点V1开发板(六-语法篇)——verilog简介+基础语法