【FPGA】FPGA sdram读写实现


想看我之前关于sdram的看我之前的博客

【FPGA】sdram接口实现

一、状态机设计

控制模块的状态机也就idle,read,write,done这几个基本的状态
我发现现在写这些模块,没有状态机不舒服
在这里插入图片描述

二、代码部分

1.sdram_interface.v

【FPGA】sdram接口实现

2.sdram_control

module sdram_control(
    input               clk,
    input               clk_100m,
    input               rst_n,
    //sdram接口和control
    input   [15:0]      din,// 从sdram来的数据
    input               din_vld,
    output  [15:0]      dout,// 给sdram的数据
    output  [23:0]      brc_address,// bank row col 地址 2+13+9
    output  reg         rd_req,// 读请求
    output  reg         wr_req,// 写请求
    input               busy,// 接口busy
    input               ack,// 接口回应
    //sdram control和用户接口 串口
    input               rd_en,
    input   [7:0]       rx_byte,
    input               rx_byte_vld,
    output  [7:0]       tx_byte,
    output              tx_byte_vld,
    input               tx_busy
);

parameter   BURST_MAX_LENGTH = 24'h1fffff,
            BURST_LENGTH = 8;

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

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

wire                idle2read ;
wire                idle2write;
wire                read2done ;
wire                write2done;
wire                done2idle ;
 
// 读地址计数器
reg     [23:0]      cnt_rdadd;
wire                add_cnt_rdadd;
wire                end_cnt_rdadd;

// 写地址计数器
reg     [23:0]      cnt_wradd;
wire                add_cnt_wradd;
wire                end_cnt_wradd;

// 读写标志
// 0代表写,1代表读
reg                 rw_flag;

// 寄存接口响应ack 打一拍
reg                 ack_r;

// 检测ack的下降沿
wire                ack_nedge;

// 写fifo
wire                wrfifo_rdreq  ;  
wire                wrfifo_wrreq  ;  
wire    [15:0]      wrfifo_qout   ;  
wire                wrfifo_rdempty;  
wire                wrfifo_rdfull ;  
wire    [11:0]      wrfifo_rdusedw;  
wire                wrfifo_wrempty;  
wire                wrfifo_wrfull ;  
wire    [12:0]      wrfifo_wrusedw;  

// 读fifo
wire                rdfifo_rdreq  ;  
wire                rdfifo_wrreq  ;
wire    [7:0]       rdfifo_qout   ;
wire                rdfifo_rdempty;
wire                rdfifo_rdfull ;
wire    [13:0]      rdfifo_rdusedw;
wire                rdfifo_wrempty;
wire                rdfifo_wrfull ;
wire    [12:0]      rdfifo_wrusedw;

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

assign idle2read    = state_c == IDLE && (~busy) && (rd_en);
assign idle2write   = state_c == IDLE && (~busy) && (wrfifo_rdusedw >= BURST_LENGTH);
assign read2done    = state_c == READ && (ack_nedge);
assign write2done   = state_c == WRITE && (ack_nedge);
assign done2idle    = state_c == DONE && (1'b1);

// rw_flag读写标志
// 写一次读一次
// 0代表写,1代表读
// always @(posedge clk or negedge rst_n)begin 
//     if(!rst_n)begin
//         rw_flag <= 1'b0;
//     end 
//     else if(idle2write)begin
//         rw_flag <= 1'b0;
//     end
//     else if(write2done)begin 
//         rw_flag <= 1'b1;
//     end 
//     else if(idle2read)begin
//         rw_flag <= 1'b1;
//     end
//     else if(read2done)begin 
//         rw_flag <= 1'b0;
//     end 
// end

// 接口响应ack 打一拍
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        ack_r <= 0;
    end 
    else begin 
        ack_r <= ack;
    end 
end

// 检测ack的下降沿
assign ack_nedge = ~ack & ack_r;

// 读地址计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_rdadd <= 0;
    end 
    else if(add_cnt_rdadd)begin 
            if(end_cnt_rdadd)begin 
                cnt_rdadd <= 0;
            end
            else begin 
                cnt_rdadd <= cnt_rdadd + BURST_LENGTH;
            end 
    end
   else  begin
       cnt_rdadd <= cnt_rdadd;
    end
end 

assign add_cnt_rdadd = read2done;
assign end_cnt_rdadd = add_cnt_rdadd && cnt_rdadd == BURST_MAX_LENGTH - BURST_LENGTH;

// 写地址计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_wradd <= 0;
    end 
    else if(add_cnt_wradd)begin 
            if(end_cnt_wradd)begin 
                cnt_wradd <= 0;
            end
            else begin 
                cnt_wradd <= cnt_wradd + BURST_LENGTH;
            end 
    end
   else  begin
       cnt_wradd <= cnt_wradd;
    end
end 

assign add_cnt_wradd = write2done;
assign end_cnt_wradd = add_cnt_wradd && cnt_wradd == BURST_MAX_LENGTH - BURST_LENGTH;

// 读写请求
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        rd_req <= 0;
        wr_req <= 0;
    end 
    else if(idle2write)begin 
        wr_req <= 1'b1;
    end 
    else if(idle2read)begin 
        rd_req <= 1'b1;
    end 
    else begin
        wr_req <= 1'b0;
        rd_req <= 1'b0;
    end
end

// 写fifo
// 从串口接收数据发给sdram
wrfifo	wrfifo_inst (
	.data   ( rx_byte       ),
	.rdclk  ( clk_100m      ),
	.rdreq  ( wrfifo_rdreq  ),
	.wrclk  ( clk           ),
	.wrreq  ( wrfifo_wrreq  ),
	.q      ( wrfifo_qout   ),
	.rdempty( wrfifo_rdempty ),
	.rdfull ( wrfifo_rdfull ),
	.rdusedw( wrfifo_rdusedw ),
	.wrempty( wrfifo_wrempty ),
	.wrfull ( wrfifo_wrfull ),
	.wrusedw( wrfifo_wrusedw )
	);

assign wrfifo_rdreq = (~wrfifo_rdempty) && ack && (state_c == WRITE);
assign wrfifo_wrreq = (~wrfifo_wrfull) && rx_byte_vld;
assign dout  = wrfifo_qout;

// 读fifo
// 从sdram接收数据发给串口
rdfifo	rdfifo_inst (
	.data   ( din ),
	.rdclk  ( clk ),
	.rdreq  ( rdfifo_rdreq ),
	.wrclk  ( clk_100m ),
	.wrreq  ( rdfifo_wrreq ),
	.q      ( rdfifo_qout ),
	.rdempty( rdfifo_rdempty ),
	.rdfull ( rdfifo_rdfull ),
	.rdusedw( rdfifo_rdusedw ),
	.wrempty( rdfifo_wrempty ),
	.wrfull ( rdfifo_wrfull ),
	.wrusedw( rdfifo_wrusedw )
	);

assign rdfifo_rdreq = (~rdfifo_rdempty) && (~tx_busy);
assign rdfifo_wrreq = (~rdfifo_wrfull) && (state_c == READ) && din_vld;
assign tx_byte = rdfifo_qout;

// bank row col 地址
assign brc_address = (state_c == WRITE)?cnt_wradd:((state_c == READ)?cnt_rdadd:0);
// assign brc_address = 24'b0;

// 读请求
// assign rd_req = (state_c == READ) ? 1 : 0;

// 写请求
// assign wr_req = (state_c == WRITE) ? 1 :0;

// 串口数据有效
assign tx_byte_vld = rdfifo_rdreq;

endmodule

3.top.v

module top(
    input               clk,
    input               rst_n,
    input   [0:0]       key_in,
    // 串口uart
    input               uart_rx,
    output              uart_tx,
    // sdram
    output              sdram_clk    ,
    output              sdram_cke    ,
    output              sdram_cs_n   ,
    output  [1:0]       sdram_bank   ,
    output  [12:0]      sdram_address,
    output              sdram_ras_n  ,
    output              sdram_cas_n  ,
    output              sdram_we_n   ,
    output  [1:0]       sdram_dqm    ,
    inout   [15:0]      sdram_dq      
);

// 中间信号
wire                clk_100m;
wire                clk_100m_75deg;
wire                key_out;
wire    [15:0]      rx_byte;
wire                rx_byte_vld;
wire    [15:0]      tx_byte;
wire                tx_byte_vld;
wire                tx_busy;
wire    [15:0]      sdram_din;
wire                sdram_din_vld;
wire    [15:0]      sdram_dout;     
wire    [23:0]      brc_address;
wire                rd_req;
wire                wr_req;
wire                busy;
wire                ack;
wire    [15:0]      sdram_dq_in    ;
wire    [15:0]      sdram_dq_out   ;
wire                sdram_dq_out_en;

assign sdram_dq_in = sdram_dq;
assign sdram_dq = sdram_dq_out_en?sdram_dq_out:16'hzzzz;

// pll锁相环
pll	pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk   ),
	.c0     ( clk_100m ),
    .c1     ( clk_100m_75deg ),
	.locked ( locked_sig )
	);

// 串口接收模块
uart_rx u_uart_rx(
    /* input            */.clk      (clk     ),
    /* input            */.rst_n    (rst_n   ),
    /* input            */.baud_sel (1),// 波特率的选择
    /* input            */.din      (uart_rx     ),// 串口接收模块接收到主机来的1bit的数据
    /* output  [7:0]    */.dout     (rx_byte    ),// 串口接收模块串并转换的数据发送
    /* output           */.dout_vld (rx_byte_vld)
);

uart_tx u_uart_tx(
    /* input            */.clk      (clk     ),
    /* input            */.rst_n    (rst_n   ),
    /* input            */.baud_sel (1),// 波特率的选择
    /* input   [7:0]    */.din      (tx_byte     ),// 串并转换的数据
    /* input            */.din_vld  (tx_byte_vld ),// 串并转换的数据有效
    /* output           */.dout     (uart_tx    ),// 发送模块发送的1bit数据
    /* output           */.busy     (tx_busy    ) // 发送模块忙标志
);

key_filter u_key_filter(
    /* input                        */.clk      (clk    ),
    /* input                        */.rst_n    (rst_n  ),
    /* input         [KEY_W-1:0]    */.key_in   (key_in ), 
    /* output  reg   [KEY_W-1:0]    */.key_out  (key_out)
);

sdram_control u_sdram_control(
    /* input                */.clk          (clk        ),
    /* input                */.clk_100m     (clk_100m   ),
    /* input                */.rst_n        (rst_n      ),
    /* input   [15:0]       */.din          (sdram_din  ),// 从sdram来的数据
    /* input                */.din_vld      (sdram_din_vld),
    /* output  [15:0]       */.dout         (sdram_dout ),// 给sdram的数据
    /* output  [23:0]       */.brc_address  (brc_address),// bank row col 地址 2+13+9
    /* output               */.rd_req       (rd_req     ),// 读请求
    /* output               */.wr_req       (wr_req     ),// 写请求
    /* input                */.busy         (busy       ),// 接口busy
    /* input                */.ack          (ack        ),// 接口回应
    /* input                */.rd_en        (key_out    ),
    /* input   [7:0]        */.rx_byte      (rx_byte    ),
    /* input                */.rx_byte_vld  (rx_byte_vld),
    /* output  [7:0]        */.tx_byte      (tx_byte    ),
    /* output               */.tx_byte_vld  (tx_byte_vld),
    /* input                */.tx_busy      (tx_busy    )
);

sdram_interface u_sdram_interface(
    /* input                */.clk              (clk_100m       ),
    /* input                */.sclk             (clk_100m_75deg ),
    /* input                */.rst_n            (rst_n          ),
    /* input   [15:0]       */.din              (sdram_dout     ),// 从wrfifo来的数据
    /* output  [15:0]       */.dout             (sdram_din      ),// 给rdfifo的数据
    /* output               */.dout_vld         (sdram_din_vld  ),
    /* input   [23:0]       */.brc_address      (brc_address    ),// bank row col 地址 2+13+9
    /* input                */.rd_req           (rd_req         ),// 读请求
    /* input                */.wr_req           (wr_req         ),// 写请求
    /* output               */.busy             (busy           ),// 接口busy
    /* output               */.ack              (ack            ),// 接口回应
    /* output               */.sdram_clk        (sdram_clk      ),
    /* output               */.sdram_cke        (sdram_cke      ),
    /* output               */.sdram_cs_n       (sdram_cs_n     ),
    /* output  [1:0]        */.sdram_bank       (sdram_bank     ),
    /* output  [12:0]       */.sdram_address    (sdram_address  ),
    /* output               */.sdram_ras_n      (sdram_ras_n    ),
    /* output               */.sdram_cas_n      (sdram_cas_n    ),
    /* output               */.sdram_we_n       (sdram_we_n     ),
    /* output  [1:0]        */.sdram_dqm        (sdram_dqm      ),
    /* input   [15:0]       */.sdram_dq_in      (sdram_dq_in    ),
    /* output  [15:0]       */.sdram_dq_out     (sdram_dq_out   ),
    /* output               */.sdram_dq_out_en  (sdram_dq_out_en)
);


endmodule

3.其他模块

串口发送模块
串口接收模块
按键消抖模块

三、仿真验证

这里我直接用signal tap没啥仿真

四、上板验证

我的突发长度是8
因为串口是8位数据,sdram是16位数据
在这里插入图片描述

五、总结

这个sdram我又是写了一周,一直看手册,一直漏条件,一直修改,本来想在sdram控制模块加个读写仲裁的,发现不行,嫌麻烦的我就直接上手按键了,我的fpga学习周期也快结束了,期望能在结束前写完全部的项目。

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值