FPGA驱动SDRAM

一.SDRAM简介(手册分析)

  • SDRAM(同步动态随机存取存储器)。

1.1存储空间

  • 本实验中使用的SDRAM存储空间被分为4个bank(如下图就是一个bank),每个bank选定行地址13位,列地址9位可以确定一个存储单元,每个存储单元16bit。那么它的存储空间就是(4 * (2^13) * (2^9) * 16) = 256M(bit)
    在这里插入图片描述

1.2特征

  • 7.8us刷新一行
    在这里插入图片描述

  • CAS延迟为2或3

  • 突发长度和突发延迟控制
    在这里插入图片描述

(这里先有印象,具体CAS latency,突发长度和突发类型后续会提到)

1.3引脚

在这里插入图片描述

1.4内部结构

在这里插入图片描述

1.5需要关注的一些时间

在这里插入图片描述

1.6模式寄存器

在这里插入图片描述

  • 那么突发长度是什么意思?所谓翻译为突发不如翻译成连续。那所谓的“突发”就是当我们对一个地址寻址并操作完成后,不用再去寻址,直接进行操作,那么就可以节省下来很多时间
  • 突发长度1、2、4、8、全页都是啥意思呢?那经过前面解释突发,就是连续操作多少个数据;至于全页,是每次写入某个bank512个数据(同一个bank,同一个行地址)

1.7命令真值表

在这里插入图片描述

二.时序分析(手册分析)

2.1Avalon时序

在这里插入图片描述

在这里插入图片描述

2.2行激活时序

  • 初始化操作完成后,无论读写都要进行行激活(选bank和行地址)
    在这里插入图片描述

  • CS片选信号:低有效;

  • BA0,BA1:bank寻址;

  • Address中A0-A12:行地址寻址;

  • RAS行选通低,CAS列选通高:进行行选址;

2.3列读写时序

  • 行激活操作后,进行列寻址。因为行列地址是复用的,列寻址的地址线仍然是行地址的A0-A12。上文有提到,通过RAS和CAS高低电平来控制Address是行地址还是列地址。
    在这里插入图片描述

  • CS片选信号:低有效;

  • Adress中A0-A8:列地址寻址;

  • Adress中A10:是否进行预充电;

  • RAS行选通高,CAS列选通低:进行列选址;

  • WE位低:写命令;WE为高读命令;

2.4读数据

在这里插入图片描述

  • CAS发完后,经过一段时间才有数据输出,我们之前提到过CAS潜伏周期,根据模式寄存器配置,这里延时对应的周期数

2.5写数据

在这里插入图片描述

  • 注意:写数据是不需要CAS潜伏周期的

三.初步设计

3.1状态转移图

在这里插入图片描述

  • 手册上的状态图如上,感觉有些乱,个人简化整理了一下
    在这里插入图片描述

3.2模块设计图

在这里插入图片描述

四.操作步骤

  • Tools -> Platform Designer
    在这里插入图片描述

  • 搜索SDRAM并选择
    在这里插入图片描述

  • 配置参数如下
    在这里插入图片描述

  • 相关延时如下
    在这里插入图片描述

  • 时钟设置为100mHz
    在这里插入图片描述

  • 连线和端口命名
    在这里插入图片描述

  • Generate
    在这里插入图片描述

五.代码

  • sdram_control模块
/**************************************功能介绍***********************************
Date	: 
Author	: Alegg xy.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module sdram_control( 
    input				clk             ,
    input				rst_n		    ,
    //写数据
    // input               wr_req          ,
    input       [7:0]   wr_data         ,
    input               wr_data_vld     ,
    //读数据
    input               rd_req          ,   
    input               rd_ready        ,//串口发送模块就绪
    output      [7:0]   rd_data         ,
    output              rd_data_vld     ,
    //clk
    input               wr_clk          ,
    input               rd_clk          ,
    //avalon_master
    output      [23:0]  am_addr         ,
    output              am_wr_req_n     ,
    output              am_rd_req_n     ,
    output      [15:0]  am_wr_data      ,
    input       [15:0]  am_rd_data      ,
    input               am_rd_data_vld  ,
    output      [1:0]   am_be_n         ,
    output              am_cs           ,
    input               am_waitrequest  
);								 
//---------<参数定义>--------------------------------------------------------- 
    //状态机参数定义
    localparam  IDLE    = 4'b0001,//
                WRITE   = 4'b0010,//
                READ    = 4'b0100,//
                DONE    = 4'b1000;//
    
    reg 	[3:0]	cstate     ;//现态
    reg		[3:0]	nstate     ;//次态
    
    wire            IDLE_WRITE  ;
    wire            IDLE_READ   ;
    wire            WRITE_DONE  ;
    wire            READ_DONE   ;
    wire            DONE_IDLE   ;

    //fifo
    wire            wr_fifo_rd_req  ;
    wire    [15:0]  wr_fifo_rd_data ;
    wire            wr_fifo_empty   ;
    wire            wr_fifo_full    ;
    wire    [5:0]   wr_fifo_usedw   ;

    wire            rd_fifo_wr_req  ;
    wire    [15:0]  rd_fifo_wr_data ;
    wire            rd_fifo_empty   ;
    wire            rd_fifo_full    ;
    wire    [5:0]   rd_fifo_usedw   ;
    //计数器
    reg			[8:0]	cnt_burst	   	;
    wire				add_cnt_burst	;
    wire				end_cnt_burst	;
    
    reg			[23:0]	cnt_wr_addr	   	;
    wire				add_cnt_wr_addr	;
    wire				end_cnt_wr_addr	;

    reg			[23:0]	cnt_rd_addr	   	;
    wire				add_cnt_rd_addr	;
    wire				end_cnt_rd_addr	;

    parameter           ADDR_MAX  = 512,
                        BURST_MAX = 5;
//---------<内部信号定义>-----------------------------------------------------
    //fifo
    wrfifo	wrfifo_inst (
	    .aclr   ( ~rst_n ),
	    .data   ( wr_data ),
	    .rdclk  ( clk ),
	    .rdreq  ( wr_fifo_rd_req ),
	    .wrclk  ( wr_clk ),
	    .wrreq  ( wr_data_vld && ~wr_fifo_full ),
	    .q      ( wr_fifo_rd_data ),
        .rdusedw( wr_fifo_usedw ),
	    .rdempty( wr_fifo_empty ),
	    .wrfull ( wr_fifo_full )
	);

    assign wr_fifo_rd_req = add_cnt_wr_addr && ~wr_fifo_empty;

    rdfifo	rdfifo_inst (
	    .aclr   ( ~rst_n ),
	    .data   ( rd_fifo_wr_data ),
	    .rdclk  ( rd_clk ),
	    .rdreq  ( rd_data_vld ),
	    .wrclk  ( clk ),
	    .wrreq  ( rd_fifo_wr_req && ~rd_fifo_full ),
	    .q      ( rd_data ),
        .rdusedw( rd_fifo_usedw ),
	    .rdempty( rd_fifo_empty ),
	    .wrfull ( rd_fifo_full )
	);

    assign rd_data_vld  = rd_ready && ~rd_fifo_empty  ;
    assign rd_fifo_wr_data = am_rd_data;
    assign rd_fifo_wr_req = am_rd_data_vld;

    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE    :begin
                if (IDLE_WRITE) begin
                    nstate = WRITE;
                end
                else if (IDLE_READ) begin
                    nstate = READ;
                end
                else begin
                    nstate = cstate;
                end
            end
            WRITE   :begin
                if (WRITE_DONE) begin
                    nstate = DONE;
                end
                else begin
                    nstate = cstate;
                end
            end
            READ    :begin
                if (READ_DONE) begin
                    nstate = DONE;
                end
                else begin
                    nstate = cstate;
                end
            end
            DONE    :begin
                if (DONE_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end
            default : nstate = IDLE;
        endcase
    end

    assign IDLE_WRITE  = (cstate == IDLE)   && wr_fifo_usedw >= BURST_MAX;
    assign IDLE_READ   = (cstate == IDLE)   && rd_req;
    assign WRITE_DONE  = (cstate == WRITE)  && end_cnt_burst;
    assign READ_DONE   = (cstate == READ)   && end_cnt_burst;
    assign DONE_IDLE   = (cstate == DONE)   && 1'b1;            
    
    //突发计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_burst <= 'd0;
        end 
        else if(add_cnt_burst)begin 
            if(end_cnt_burst)begin 
                cnt_burst <= 'd0;
            end
            else begin 
                cnt_burst <= cnt_burst + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_burst = (cstate == WRITE || cstate == READ) && ~am_waitrequest;
    assign end_cnt_burst = add_cnt_burst && cnt_burst == BURST_MAX - 1;
    
    //地址计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_wr_addr <= 'd0;
        end 
        else if(add_cnt_wr_addr)begin 
            if(end_cnt_wr_addr)begin 
                cnt_wr_addr <= 'd0;
            end
            else begin 
                cnt_wr_addr <= cnt_wr_addr + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_wr_addr = (cstate == WRITE) && ~am_waitrequest;
    assign end_cnt_wr_addr = add_cnt_wr_addr && cnt_wr_addr == ADDR_MAX - 1;
    
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_rd_addr <= 'd0;
        end 
        else if(add_cnt_rd_addr)begin 
            if(end_cnt_rd_addr)begin 
                cnt_rd_addr <= 'd0;
            end
            else begin 
                cnt_rd_addr <= cnt_rd_addr + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_rd_addr = (cstate == READ) && ~am_waitrequest;
    assign end_cnt_rd_addr = add_cnt_rd_addr && cnt_rd_addr == ADDR_MAX - 1;
    
    //avalon_master时序     
    assign am_wr_req_n = ~(cstate == WRITE);
    assign am_rd_req_n = ~(cstate == READ);
    assign am_addr = (cstate == WRITE) ? cnt_wr_addr : cnt_rd_addr;
    assign am_wr_data = wr_fifo_rd_data;
    assign am_be_n = 2'b00;
    assign am_cs = 1'b1;
    
endmodule
  • 顶层模块(这里有用到PLL,需要100mHz时钟)
/**************************************功能介绍***********************************
Date	: 
Author	: Alegg xy.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk			,
    input				rst_n		,

    input               rx          ,
    output              tx          ,
    input       [3:0]   key_in      ,

    output              sdram_clk   ,
    output      [12:0]  sdram_addr  ,
    output      [1:0]   sdram_ba    ,
    output              sdram_cas_n ,
    output              sdram_cke   ,
    output              sdram_cs_n  ,
    inout       [15:0]  sdram_dq    ,
    output      [1:0]   sdram_dqm   ,
    output              sdram_ras_n ,
    output              sdram_we_n  
);								 
//---------<参数定义>--------------------------------------------------------- 
    //PLL
    wire            clk_50mhz   ;
    wire            clk_100mhz  ;
    wire            clk_offset  ;
    wire            locked      ;
    //key_debounce
    wire    [3:0]   key_out     ;
    //uart
    wire    [7:0]   wr_data     ;
    wire            wr_data_vld ;
    wire    [7:0]   rd_data     ;
    wire            rd_data_vld ;
    wire            rd_ready    ;
    //control
    wire    [23:0]  am_addr     ;
    wire            am_wr_req_n ;
    wire            am_rd_req_n ;
    wire    [15:0]  am_wr_data  ;
    wire    [15:0]  am_rd_data  ;
    wire            am_rd_data_vld;
    wire    [1:0]   am_be_n     ;
    wire            an_cs       ;
    wire            am_waitrequest;
//---------<内部信号定义>-----------------------------------------------------
    pll	pll_inst (
	    .areset ( ~rst_n ),
	    .inclk0 ( clk ),
	    .c0     ( clk_50mhz ),
	    .c1     ( clk_100mhz ),
	    .c2     ( sdram_clk ),
	    .locked ( locked )
	);
   
    key_debounce u_key_debounce(
        .clk		(clk_50mhz),
        .rst_n	    (rst_n),
        .key_in	    (key_in),
        .key_out    (key_out)  
    );
    
    uart_rx u_uart_rx(
        .clk		(clk_50mhz),
        .rst_n	    (rst_n),
        .rx         (rx),
        .rx_data_vld(wr_data_vld),
        .rx_data    (wr_data)        
    );
    
    uart_tx u_uart_tx(
        .clk		(clk_50mhz),
        .rst_n	    (rst_n),
        .tx_data    (rd_data),
        .tx_data_vld(rd_data_vld),
        .ready      (rd_ready),
        .tx         (tx)            
    );

    sdram_control u_sdram_control(
        .clk             (clk_100mhz),
        .rst_n		     (rst_n),   
        .wr_data         (wr_data),
        .wr_data_vld     (wr_data_vld),
        .rd_req          (key_out[0]),
        .rd_ready        (rd_ready),
        .rd_data         (rd_data),
        .rd_data_vld     (rd_data_vld),
        .wr_clk          (clk_50mhz),
        .rd_clk          (clk_50mhz),
        .am_addr         (am_addr),
        .am_wr_req_n     (am_wr_req_n),
        .am_rd_req_n     (am_rd_req_n),
        .am_wr_data      (am_wr_data),
        .am_rd_data      (am_rd_data),
        .am_rd_data_vld  (am_rd_data_vld),
        .am_be_n         (am_be_n),
        .am_cs           (am_cs),
        .am_waitrequest  (am_waitrequest)       
    );

    sdram u0 (
		.avalon_slave_address       (am_addr),       // avalon_slave.address
		.avalon_slave_byteenable_n  (am_be_n),  //             .byteenable_n
		.avalon_slave_chipselect    (am_cs),    //             .chipselect
		.avalon_slave_writedata     (am_wr_data),     //             .writedata
		.avalon_slave_read_n        (am_rd_req_n),        //             .read_n
		.avalon_slave_write_n       (am_wr_req_n),       //             .write_n
		.avalon_slave_readdata      (am_rd_data),      //             .readdata
		.avalon_slave_readdatavalid (am_rd_data_vld), //             .readdatavalid
		.avalon_slave_waitrequest   (am_waitrequest),   //             .waitrequest
		.clk_clk                    (clk_100mhz),                    //          clk.clk
		.reset_reset_n              (rst_n),              //        reset.reset_n
		.sdram_addr                 (sdram_addr),                 //        sdram.addr
		.sdram_ba                   (sdram_ba),                   //             .ba
		.sdram_cas_n                (sdram_cas_n),                //             .cas_n
		.sdram_cke                  (sdram_cke),                  //             .cke
		.sdram_cs_n                 (sdram_cs_n),                 //             .cs_n
		.sdram_dq                   (sdram_dq),                   //             .dq
		.sdram_dqm                  (sdram_dqm),                  //             .dqm
		.sdram_ras_n                (sdram_ras_n),                //             .ras_n
		.sdram_we_n                 (sdram_we_n)                  //             .we_n
	);
endmodule

六.仿真&效果

  • 仿真代码(这里用到了仿真模型)
`timescale 1ns/1ns
    
module tb_top();

//激励信号定义 
    reg				clk  	;
    reg				rst_n	;
    reg             rx      ;
    reg     [3:0]   key_in  ;

//输出信号定义	 
    wire            tx          ;
    wire            sdram_clk   ;
    wire    [12:0]  sdram_addr  ; 
    wire    [1:0]   sdram_ba    ; 
    wire            sdram_cas_n ; 
    wire            sdram_cke   ; 
    wire            sdram_cs_n  ; 
    wire    [15:0]  sdram_dq    ; 
    wire    [1:0]   sdram_dqm   ; 
    wire            sdram_ras_n ; 
    wire            sdram_we_n  ;

    wire            clk_50mhz   ;
    wire            clk_100mhz  ;
    wire            clk_offset  ;
    wire            locked      ;
//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

//模块例化
    top u_top(	
        .clk	     (clk),
        .rst_n		 (rst_n),
        .rx          (rx),
        .tx          (tx),
        .key_in      (key_in),
        .sdram_addr  (sdram_addr ),
        .sdram_ba    (sdram_ba   ),
        .sdram_cas_n (sdram_cas_n),
        .sdram_cke   (sdram_cke  ),
        .sdram_cs_n  (sdram_cs_n ),
        .sdram_dq    (sdram_dq   ),
        .sdram_dqm   (sdram_dqm  ),
        .sdram_ras_n (sdram_ras_n),
        .sdram_we_n  (sdram_we_n )
    );

    assign sdram_clk = clk_offset;

    sdr u_sdr
    (   
        .Dq      (sdram_dq), 
        .Addr    (sdram_addr), 
        .Ba      (sdram_ba), 
        .Clk     (sdram_clk), 
        .Cke     (sdram_cke), 
        .Cs_n    (sdram_cs_n), 
        .Ras_n   (sdram_ras_n), 
        .Cas_n   (sdram_cas_n), 
        .We_n    (sdram_we_n), 
        .Dqm     (sdram_dqm)
    );

    pll	pll_inst (
	    .areset ( ~rst_n ),
	    .inclk0 ( clk ),
	    .c0 ( clk_50mhz ),
	    .c1 ( clk_100mhz ),
	    .c2 ( clk_offset ),
	    .locked ( locked )
	);   
//产生时钟
    initial 		clk = 1'b0;
    always #(CLOCK_CYCLE/2) clk = ~clk;

    integer i;
//产生激励
    initial  begin 
        rst_n = 1'b1;
        rx = 0;
        key_in[0] = 1;
        #(CLOCK_CYCLE*2);
        rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        rst_n = 1'b1;
        #2000;

        //写数据
        repeat(5)begin
            for (i = 0 ; i < 16 ; i = i + 1) begin
                rx = {$random} % 1;
                #20;
            end
        end

        //读数据
        key_in[0] = 0;
        #(CLOCK_CYCLE * 200);
        key_in[0] = 1;

        #10000;
        $stop;
    end

endmodule 

七.参考

在这里插入图片描述

https://blog.csdn.net/qq_52215423/article/details/132897181

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值