Verilog: SDRAM简单读写系统——SDRAM IP核实现

Avalon 总线实现SDRAM读写功能系统设计

目录

一. 需求分析

二. SDRAM手册阅读

1. SDRAM概念

2. SDRAM手册关键处标注及说明

① 基本特征

② 信号列表

③ 关键时间参数

a. 周期参数

b. 延时或工作时长参数

④ 模式寄存器设置

⑤ 命令真值表

⑥ 手册状态图

⑦ 读时序

⑧ 写时序

⑨ 写到预充电

⑩ 初始化刷新

? 上电

三. AVALON总线协议手册阅读

1.Avalon接口规范

2. Avalon存储器映射的接口——Avalon Memory-Mapped ( Avalon -MM)

① 接口简介

② 接口信号

③ 典型的读传输和写传输

④ 使用 waitrequestAllowance 属性进行传输

四. SDRAM接口IP核手册阅读

?编辑五. 系统模块划分(框图)

六. 各模块实现

1. pll(锁相环)模块

2. fifo模块

① wrfifo

 ?编辑

② rdfifo

3. sdram_interface模块

4. sdram_control模块

①信号列表

② 状态机

③ 代码实现

④ 仿真

a. 代码

b. 仿真波形

 5. 顶层模块

6. 上板验证


一. 需求分析

Avalon 总线实现SDRAM读写功能系统设计,可将读功能和写功能分别单独控制。

读功能启动可以使用按键控制,读出数据通过UART串口显示到PC上;

写操作可由串口发送需要写入的数据,再由wrfifo(写数据缓存fifo)控制何时写入。

二. SDRAM手册阅读

1. SDRAM概念

SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存储器。

同步(Synchronous ):内存工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准;

动态(Dynamic ):存储阵列需要不断的刷新来保证数据不丢失;

随机(Random ):数据不是线性依次存储,而是自由指定地址进行数据读写。

    SDRAM具有空间存储量大、读写速度快、价格相对便宜等优点。然而由于SDRAM内部利用电容来存储数据,为保证数据不丢失,需要持续对各存储电容进行刷新操作;同时在读写过程中 需要考虑行列管理、各种操作延时等,由此导致了其控制逻辑复杂的特点。

2. SDRAM手册关键处标注及说明

① 基本特征

a. 总存储32MByte  256Mbit;

b. 数据位宽16bit;

c. 4个bank  4194304个数据/bank;

a. 8192行/bank刷新周期64ms,64ms/8192 = 7.8us/行

b. 4194304/8192=512列/bank;

c. 命令发出后延迟执行周期(CAS)=2或3周期;

d. 可编程突发有顺序和插入突发两种类型,顺序突发长度1,2,4,8或全页,插入突发长度1,2,4或8.

② 信号列表

信号标志

信号类型

位宽

描述

clk

input

1

系统时钟,所有输入在时钟上升沿被锁存

cke

input

1

时钟使能信号

cs_n

input

1

片选

band_addr

input

2

band地址(地址总位宽:2+13+9=24)

row_addr

input

13

行地址13位,列地址9位,自动预充电标志位A10

ras_n

input

1

行命令

cas_n

input

1

列命令

we_n

input

1

写使能命令

dqm

inout

2

掩码标志

dq

inout

16

数据输入输出

③ 关键时间参数

a. 周期参数

如表所示选择时钟周期时间10ns(100MHz),可兼容所有情况

CL = 3  tAC = 5.4最大

b. 延时或工作时长参数

关键参数列表(时钟周期100MHz  10ns)

名称

时间

描述

tRRC

>63ns(6个周期) 取7个周期

自动刷新需要等待的时间

tRCD

>20ns(2个周期) 取3个周期

预充电需要等待的时间

tRP

>20ns(2个周期) 取3个周期

行激活需要等待的时间

TMRD

>2个周期      取3个周期

模式寄存器设置需要等待的时间

tDPL

>2个周期      取2个周期

数据写入到预充电等待延时的时间

④ 模式寄存器设置

模式寄存器格式:2bit bank地址 + 13bit 行地址。

⑤ 命令真值表

关键命令列表

各个指令

{cs_n,ras_n,cas_n,we_n}

ADDR[12:0] + A10 +BA[1:0]

Mode Register Set

4’b0000

BA(2’b00) + 3’b000,OP Code,2’b00,CAS Latency,BT,Burst Length

Bank Active

4’b0011

BA(2’b00) + Row Address(addr[21:9])

Read

4’b0101

BA(2’b00) + A10(1’b0) + Column Address(addr[8:0])(其余补齐0)

Write

4’b0100

BA(2’b00) + A10(1’b0) + Column Address(addr[8:0])(其余补齐0)

Precharge All Banks

4’b0010

A10(11’b100_0000_0000)

Auto Refresh

4’b0001

⑥ 手册状态图

本次仅使用红色框中状态即:

POWER_ON

PRECHARGE_ALL_BANK

MODE_REGISTER_SET

IDLE

AUTO_REFRESH

PRECHARGE_ALL

ROW_ACTIVE

WRITE

READ

⑦ 读时序

⑧ 写时序

⑨ 写到预充电

tDPL = 2 clk

⑩ 初始化刷新

刷新8次间隔8tREF。

? 上电

上电等待200us

三. AVALON总线协议手册阅读

1.Avalon接口规范

规范定义了以下七个接口:

? Avalon Streaming Interface ( Avalon -ST) — 支持单向数据流的接口,包括多路复用流,数据包和 DSP 数据。

? Avalon Memory Mapped Interface ( Avalon -MM) — 一种基于地址的读/写接口,是主–从连接的典型接口。

? Avalon Conduit Interface — 一种接口类型,适用于那些不适合任何其他 Avalon 类型的单个信号或信号组。您可以在一个 Platform Designer 系统内部连接管道接口(conduit

interface)。或者,您可以将它们导出以连接到设计中的其他模块或者连接到 FPGA 管脚。

? Avalon Tri-State Conduit Interface ( Avalon -TC) — 支持与片外(off-chip)连接的接口。

多个外设可以通过信号多路复用(signal multiplexing)来共享管脚,从而减少 FPGA 的管脚数和 PCB 上的走线数量。

? Avalon Interrupt Interface—允许组件向其他组件发送事件信号的接口。

? Avalon Clock Interface—驱动或接收时钟的接口。

? Avalon Reset Interface—提供复位连接的接口。

一个组件可以包括任意数量的这些接口,并且还可以包括相同接口类型的多个实例。

2. Avalon存储器映射的接口——Avalon Memory-Mapped ( Avalon -MM)

① 接口简介

您可以使用 Avalon Memory-Mapped ( Avalon -MM)接口实现主从组件的读写接口。以下是通常

包含存储器映射接口的组件示例:

? 微处理器

? 储存器

? UART

? DMA

? 计时器(Timer)

Avalon -MM 接口有简单的也有有复杂的。例如,SRAM 接口有固定周期的读写传输,具有简单的

Avalon -MM 接口。能够进行突发传输的流水线接口(pipelined interface)很复杂。

② 接口信号

③ 典型的读传输和写传输

此时序图中的数字标记以下转换:

1. address,byteenable 和 read 在 clk 的上升沿后置位。slave 置位 waitrequest,

暂停传输。

2. waitrequest 被采样。由于 waitrequest 置位,因此周期变成一个等待状态(wait

state)。address,read,write 和 byteenable 保持不变。

3. slave 在 clk 的上升沿之后置低 waitrequest。slave 置位 readdata 和 response。

4. master 对 readdata,response 和置低的 waitrequest(完成传输)的进行采样。

5. address,writedata,byteenable 和 write 信号在 clk 的上升沿之后置位。slave

置位 waitrequest,暂停传输。

6. slave 在 clk 的上升沿之后置低 waitrequest。

7. slave 采集结束传输的写数据。

④ 使用 waitrequestAllowance 属性进行传输

waitrequestAllowance =1

此图中的编号标识了以下事件:

1. Avalon -MM master 驱动 write 和 data。

2. Avalon -MM slave 置位 waitrequest。由于 waitrequestAllowance 为 1,因此

master 能够写操作。

3. master 置低 write,因为 slave 置位 waitrequest 第二个周期。

4. Avalon -MM master 驱动 write 和 data。slave 没有置位 waitrequest。写操作完

成。

5. slave 置位 waitrequest。由于 waitrequestAllowance 为 1 个周期,因此写操作完

成。

6. Avalon -MM master 驱动 write 和 data。slave 没有置位 waitrequest。写操作完

成。

7. Avalon -MM slave 置位 waitrequest。由于 waitrequestAllowance 为 1,因此

master 能够完成 1 个额外的数据传输。

8. Avalon master 驱动 write 和 data。slave 没有置位 waitrequest。写操作完成。

注意:当waitrequestAllowance=0时,一旦waitrequest拉高,数据传输立即停止。

四. SDRAM接口IP核手册阅读

三百多页的手册就看了这张图,有时间可以慢慢研究。

IP核突发长度为1

五. 系统模块划分(框图)

六. 各模块实现

其中按键消抖模块(key_filter)和UART串口模块直接调用以往编写好的模块,不再赘述。

1. pll(锁相环)模块

时钟1:产生100MHz的时钟驱动sdram_control和sdram_interface模块;

时钟2:产生一个100MHz,相位相比于上一个时钟偏移75deg的时钟驱动SDRAM;

调用步骤如下:

 

 

 

 

 

 

 

 

2. fifo模块

① wrfifo

目的实现8bit写入数据输入时钟50MHz到输出16bit数据输出时钟100MHz的跨时钟域数据的缓存传输。

实现方法1:手写异步fifo,可参考ACFIFO实现章节

实现方法2:调用FIFO IP。

本次使用FIFO IP实现wrfifo,调用步骤如下:

 

 

 

 

② rdfifo

目的实现16bit写入数据输入时钟100MHz到输出8bit数据输出时钟50MHz的跨时钟域数据的缓存传输。

调用步骤:

其余步骤与wrfifo相同。

3. sdram_interface模块

SDRAM IP核实现

与FIFO和PLL等IP调用方法不同,tool-->Platform Designer

调用步骤:

搜索sdram,双击蓝色部分。

 

 

 

 

 

 

4. sdram_control模块

信号列表

名称

类型

描述

clk_100m

input

pll生成时钟100MHz

clk_50m_in

input

系统时钟50MHz

clk_50m_out

input

系统时钟50MHz

rst_n

input

系统复位

din

input

用户输入数据

din_vld

input

用户输入数据有效信号

dout

output

输出给用户数据

dout_vld

output

输出给用户数据有效信号

busy

input

uart_tx忙信号

rd_en

input

读请求信号

avm_addr

output

访问sdram的地址

avm_wr_n

output

访问sdram的写使能信号

avm_wr_data

output

访问sdram的写数据

avm_rd_n

output

访问sdram的读使能信号

avm_rd_data

input

访问sdram的读出数据

avm_rd_data_vld

input

访问sdram的读出数据有效信号

avm_waitrequest

input

sdram等待请求信号

② 状态机

控制模块状态机比较简单就四个状态:

IDLE:空闲状态

READ:读数据状态

WRITE:写数据状态

DONE:读或写完成状态

③ 代码实现

/**************************************功能介绍***********************************
Description:	avalon sdram 读写控制模块 对应SDRAM IP核接口
Change history:  2023年7月21日  
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module sdram_control( 
    input				clk_50m_in	   ,//wrfifo写入端时钟
    input				clk_50m_out	   ,//rdfifo读出段时钟
    input				clk_100m       ,//pll生成的100MHz时钟
    input				rst_n          ,
    //uart       
    input				din_vld	       ,//用户输入数据有效信号
    input		[7:0]   din		       ,//用户输入数据
    input               busy           ,//uart_tx忙信号
    output		    	dout_vld       ,//输出给用户数据有效信号
    output		[7:0]	dout	       ,//输出给用户数据
    //key       
    input               rd_en          ,//读请求信号
    //sdram       
    output      [23:0]  avm_addr       ,//访问sdram的地址
    output              avm_wr_n       ,//访问sdram的写使能信号
    output      [15:0]  avm_wr_data    ,//访问sdram的写数据
    output              avm_rd_n       ,//访问sdram的读使能信号
    input       [15:0]  avm_rd_data    ,//访问sdram的读出数据
    input               avm_rd_data_vld,//访问sdram的读出数据有效信号
    input               avm_waitrequest //sdram等待请求信号
    
);								 
//---------<参数定义>--------------------------------------------------------- 
    //突发长度
    parameter   ROW_LENGTH   = 24'h1fffff,//写(读)满一行
                BURST_LENGTH = 1'd1      ;//SDRAM IP突发长度1
    //状态机参数定义
    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                 rd_en_reg          ;//跨时钟域同步寄存

    reg                 avm_waitrequest_reg;//从机输入信号 同步寄存
    reg         [15:0]  avm_rd_data_reg    ;//从机输入信号 同步寄存
    reg                 avm_rd_data_vld_reg;//从机输入信号 同步寄存

    // 读地址计数器
    reg         [23:0]  cnt_rd_addr        ;
    wire                add_cnt_rd_addr    ;
    wire                end_cnt_rd_addr    ;
    
    // 写地址计数器
    reg         [23:0]  cnt_wr_addr        ;
    wire                add_cnt_wr_addr    ;
    wire                end_cnt_wr_addr    ;

    // wrfifo
    wire                wrfifo_rdreq       ;  
    wire                wrfifo_wrreq       ;  
    wire        [15:0]  wrfifo_q           ;  
    wire                wrfifo_rdempty     ;  
    wire                wrfifo_rdfull      ;  
    wire        [7:0]   wrfifo_rdusedw     ;  
    wire                wrfifo_wrempty     ;  
    wire                wrfifo_wrfull      ;  
    wire        [8:0]   wrfifo_wrusedw     ;  
         
    // rdfifo     
    wire                rdfifo_rdreq       ;  
    wire                rdfifo_wrreq       ;
    wire        [7:0]   rdfifo_q           ;
    wire                rdfifo_rdempty     ;
    wire                rdfifo_rdfull      ;
    wire        [8:0]   rdfifo_rdusedw     ;
    wire                rdfifo_wrempty     ;
    wire                rdfifo_wrfull      ;
    wire        [7:0]   rdfifo_wrusedw     ;

//****************************************************************
//--rd_en_reg 由于按键模块与sdram_control时钟频率不同,所以按键模块发送来的信号需要同步寄存
//****************************************************************
    always @(posedge clk_100m or negedge rst_n)begin 
        if(!rst_n)begin
            rd_en_reg <= 1'b0;
        end 
        else begin
            rd_en_reg <= rd_en;
        end
    end

//****************************************************************
//--avm_waitrequest_reg  avm_rd_data_reg    avm_rd_data_vld_reg  由于主从机时钟相位不同,所以从机发送来的信号需要同步寄存
//****************************************************************
    always @(posedge clk_100m or negedge rst_n)begin 
        if(!rst_n)begin
            avm_waitrequest_reg <= 1'b0 ;
            avm_rd_data_vld_reg <= 1'b0 ;
            avm_rd_data_reg     <= 16'b0;
        end  
        else begin 
            avm_waitrequest_reg <= avm_waitrequest;
            avm_rd_data_vld_reg <= avm_rd_data_vld;
            avm_rd_data_reg     <= avm_rd_data    ;
        end 
    end

//****************************************************************
//--状态机
//**************************************************************** 
    //第一段:时序逻辑描述状态转移
    always @(posedge clk_100m 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 : begin
                    state_n = state_c;
            end
        endcase
    end

    //状态转移条件
    assign  idle2read  = state_c == IDLE  &&  rd_en_reg          ;
    assign  read2done  = state_c == READ  && !avm_waitrequest_reg;
    assign  idle2write = state_c == IDLE  &&  wrfifo_rdusedw     ;
    assign  write2done = state_c == WRITE && !avm_waitrequest_reg;
    assign  done2idle  = state_c == DONE                         ;
                
    //第三段:描述输出,时序逻辑或组合逻辑皆可
                
//****************************************************************
//--读地址计数器
//****************************************************************
    always @(posedge clk_100m or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_rd_addr <= 24'b0;//访问的首地址24'b0
        end 
        else if(add_cnt_rd_addr)begin 
                if(end_cnt_rd_addr)begin 
                    cnt_rd_addr <= 24'b0;
                end
                else begin 
                    cnt_rd_addr <= cnt_rd_addr + BURST_LENGTH;
                end 
        end
       else  begin
           cnt_rd_addr <= cnt_rd_addr;
        end
    end 
    
    assign add_cnt_rd_addr = read2done;//访问完一次地址+1
    assign end_cnt_rd_addr = add_cnt_rd_addr && cnt_rd_addr == ROW_LENGTH - BURST_LENGTH;

//****************************************************************
//--写地址计数器
//****************************************************************
    always @(posedge clk_100m or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_wr_addr <= 24'b0;
        end 
        else if(add_cnt_wr_addr)begin 
                if(end_cnt_wr_addr)begin 
                    cnt_wr_addr <= 24'b0;
                end
                else begin 
                    cnt_wr_addr <= cnt_wr_addr + BURST_LENGTH;
                end 
        end
       else  begin
           cnt_wr_addr <= cnt_wr_addr;
        end
    end 
    
    assign add_cnt_wr_addr = write2done;
    assign end_cnt_wr_addr = add_cnt_wr_addr && cnt_wr_addr == ROW_LENGTH - BURST_LENGTH;

//****************************************************************
//--FIFO IP 核例化调用
//****************************************************************
    wrfifo	wrfifo_inst (
	.data    ( din           ),
	.rdclk   ( clk_100m      ),
	.rdreq   ( wrfifo_rdreq  ),
	.wrclk   ( clk_50m_in    ),
	.wrreq   ( wrfifo_wrreq  ),
	.q       ( wrfifo_q      ),
	.rdempty ( wrfifo_rdempty),
	.rdfull  ( wrfifo_rdfull ),
	.rdusedw ( wrfifo_rdusedw),
	.wrempty ( wrfifo_wrempty),
	.wrfull  ( wrfifo_wrfull ),
	.wrusedw ( wrfifo_wrusedw)
	);

    assign  wrfifo_wrreq = !wrfifo_wrfull && din_vld;
    assign  wrfifo_rdreq = !wrfifo_rdempty && state_c == WRITE && !avm_waitrequest_reg;

    rdfifo	rdfifo_inst (
	.data    ( avm_rd_data_reg),
	.rdclk   ( clk_50m_out    ),
	.rdreq   ( rdfifo_rdreq   ),
	.wrclk   ( clk_100m       ),
	.wrreq   ( rdfifo_wrreq   ),
	.q       ( rdfifo_q       ),
	.rdempty ( rdfifo_rdempty ),
	.rdfull  ( rdfifo_rdfull  ),
	.rdusedw ( rdfifo_rdusedw ),
	.wrempty ( rdfifo_wrempty ),
	.wrfull  ( rdfifo_wrfull  ),
	.wrusedw ( rdfifo_wrusedw )
	);

    assign rdfifo_wrreq= !rdfifo_wrfull && avm_rd_data_vld_reg && !avm_waitrequest_reg;
    assign rdfifo_rdreq= !rdfifo_rdempty && !busy;

//****************************************************************
//--output
//****************************************************************
    //to sdram_interface
    assign  avm_addr    = (state_c == WRITE) ? {cnt_wr_addr[23],cnt_wr_addr[21:9],cnt_wr_addr[22],cnt_wr_addr[8:0]} : 
                                               {cnt_rd_addr[23],cnt_rd_addr[21:9],cnt_rd_addr[22],cnt_rd_addr[8:0]} ;
                                             // bankA1          行地址             bankA0          列地址 
    assign  avm_wr_data = wrfifo_q           ;
    assign  avm_rd_n    = !(state_c == READ );
    assign  avm_wr_n    = !(state_c == WRITE);   
    //to uart_tx
    assign  dout_vld    = rdfifo_rdreq       ;
    assign  dout	    = rdfifo_q           ;



endmodule

④ 仿真

a. 代码

`timescale 1ns/1ns
    
module tb_sdram_control();

//激励信号定义 
    reg				clk_50m_in	   ;
    reg				clk_50m_out	   ;
    wire			clk_100m   	   ;
    wire            clk_100m_deg   ;
    reg             rst_n          ;
    //uart   
    reg             din_vld        ;
    reg     [7:0]   din		       ;
    reg             busy           ;
    wire            dout_vld       ;
    wire    [7:0]   dout           ;
    //key   
    reg             rd_en          ;
    //sdram
    wire    [23:0]  avm_addr       ;
    wire            avm_wr_n       ;
    wire    [15:0]  avm_wr_data    ;
    wire            avm_rd_n       ;
    wire    [15:0]  avm_rd_data    ;
    wire            avm_rd_data_vld;
    wire            avm_waitrequest;

    wire    [15:0]  dq             ;
    wire    [12:0]  addr           ;
    wire    [1:0]   ba             ;
    wire            cke            ;
    wire            cs_n           ;
    wire            ras_n          ;
    wire            cas_n          ;
    wire            we_n           ;
    wire    [1:0]   dqm            ;




//模块例化
    sdram_control u1( 
    /*input				*/.clk_50m_in	  (clk_50m_in     ),//wrfifo写入端时钟
    /*input				*/.clk_50m_out	  (clk_50m_out    ),//rdfifo读出段时钟
    /*input				*/.clk_100m       (clk_100m       ),//pll生成的100MHz时钟
    /*input				*/.rst_n          (rst_n          ),
    /*//uart       */    
    /*input				*/.din_vld	      (din_vld        ),//用户输入数据有效信号
    /*input		[7:0]   */.din		      (din		      ),//用户输入数据
    /*input             */.busy           (busy           ),//uart_tx忙信号
    /*output	    	*/.dout_vld       (dout_vld       ),//输出给用户数据有效信号
    /*output	[7:0]	*/.dout	          (dout	          ),//输出给用户数据
    /*//key       */    
    /*input             */.rd_en          (rd_en          ),//读请求信号
    /*//sdram       */
    /*output    [23:0]  */.avm_addr       (avm_addr       ),//访问sdram的地址
    /*output            */.avm_wr_n       (avm_wr_n       ),//访问sdram的写使能信号
    /*output    [15:0]  */.avm_wr_data    (avm_wr_data    ),//访问sdram的写数据
    /*output            */.avm_rd_n       (avm_rd_n       ),//访问sdram的读使能信号
    /*input     [15:0]  */.avm_rd_data    (avm_rd_data    ),//访问sdram的读出数据
    /*input             */.avm_rd_data_vld(avm_rd_data_vld),//访问sdram的读出数据有效信号
    /*input             */.avm_waitrequest(avm_waitrequest) //sdram等待请求信号
    
);

    sdram_interface u0 (
		.avalon_bus_address       (avm_addr        ),       // avalon_bus.address
		.avalon_bus_byteenable_n  (2'b11           ),  //           .byteenable_n
		.avalon_bus_chipselect    (1'b1            ),    //           .chipselect
		.avalon_bus_writedata     (avm_wr_data     ),     //           .writedata
		.avalon_bus_read_n        (avm_rd_n        ),        //           .read_n
		.avalon_bus_write_n       (avm_wr_n        ),       //           .write_n
		.avalon_bus_readdata      (avm_rd_data     ),      //           .readdata
		.avalon_bus_readdatavalid (avm_rd_data_vld ), //           .readdatavalid
		.avalon_bus_waitrequest   (avm_waitrequest ),   //           .waitrequest
		.clk_clk                  (clk_100m        ),                  //        clk.clk
		.mem_port_addr            (addr            ),            //   mem_port.addr
		.mem_port_ba              (ba              ),              //           .ba
		.mem_port_cas_n           (cas_n           ),           //           .cas_n
		.mem_port_cke             (cke             ),             //           .cke
		.mem_port_cs_n            (cs_n            ),            //           .cs_n
		.mem_port_dq              (dq              ),              //           .dq
		.mem_port_dqm             (dqm             ),             //           .dqm
		.mem_port_ras_n           (ras_n           ),           //           .ras_n
		.mem_port_we_n            (we_n            ),            //           .we_n
		.reset_reset_n            (rst_n           )             //      reset.reset_n
	);

    sdr slave_module(
        .Dq      (dq          ),  
        .Addr    (addr        ), 
        .Ba      (ba          ), 
        .Clk     (clk_100m_deg),
        .Cke     (cke         ),
        .Cs_n    (cs_n        ),
        .Ras_n   (ras_n       ),
        .Cas_n   (cas_n       ),
        .We_n    (we_n        ),
        .Dqm     (Dqm         )
    );

    pll_ip_100m_75deg	pll_ip_100m_75deg_inst (
	.areset ( !rst_n      ),
	.inclk0 ( clk_50m_in  ),
	.c0     ( clk_100m    ),
	.c1     ( clk_100m_deg),
	.locked (             )
	);



//产生时钟
    initial 		clk_50m_in = 1'b0;
    always #10 clk_50m_in = ~clk_50m_in;

    initial 		clk_50m_out = 1'b0;
    always #10 clk_50m_out = ~clk_50m_out;

//产生激励
    initial  begin
        rst_n = 1'b1;
        din = 0;
        din_vld = 0;
        busy = 0;
        rd_en = 0;
        #200;
        rst_n = 1'b0;
        #400;
        rst_n = 1'b1;
        #3;
        #220000 ;
        //模拟写操作
        din = 8'h12;
        din_vld = 1'b1;
        #20;
        din_vld = 1'b0;
        #20;

        din = 8'h13;
        din_vld = 1'b1;
        #20;
        din_vld = 1'b0;
        #20;

        din = 8'h14;
        din_vld = 1'b1;
        #20;
        din_vld = 1'b0;
        #20;

        din = 8'h15;
        din_vld = 1'b1;
        #20;
        din_vld = 1'b0;
        din = 8'h0;
        #20;

        

        //模拟读操作
        rd_en = 1'b1;
        #20;
        rd_en = 1'b0;
        #1000;
        rd_en = 1'b1;
        #20;
        rd_en = 1'b0;

        #2000;
        $stop;

    end

endmodule 

b. 仿真波形

 5. 顶层模块

代码

/**************************************功能介绍***********************************
Description:		avalon sdram 顶层模块 对应SDRAM IP核接口
Change history:    
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk		       ,
    input				rst_n	       ,
    //key       
    input               key_in         ,
    //uart       
    input				rx		       ,
    output		        tx		       ,
    //sdram   
    output		        clk_100m_deg   ,
    inout     [15:0]    dq             ,
    output    [12:0]    addr           ,
    output    [1:0]     ba             ,
    output              cke            ,
    output              cs_n           ,
    output              ras_n          ,
    output              cas_n          ,
    output              we_n           ,
    output    [1:0]     dqm            
);								 
//---------<参数定义>--------------------------------------------------------- 
    
//---------<内部信号定义>-----------------------------------------------------
    wire                key_en         ;
   
    wire      [7:0]     rx_dout        ;
    wire                rx_dout_vld    ;
    wire      [7:0]     tx_din         ;
    wire                tx_req         ;
    wire                busy           ;
   
    wire                clk_100m       ;

    wire      [23:0]    avm_addr       ;//访问sdram的地址
    wire                avm_wr_n       ;//访问sdram的写使能信号
    wire      [15:0]    avm_wr_data    ;//访问sdram的写数据
    wire                avm_rd_n       ;//访问sdram的读使能信号
    wire      [15:0]    avm_rd_data    ;//访问sdram的读出数据
    wire                avm_rd_data_vld;//访问sdram的读出数据有效信号
    wire                avm_waitrequest;//sdram等待请求信号

    
    
    key_filter u_key_filter(
    /*input             */.clk        (clk        ) ,
    /*input             */.rst_n      (rst_n      ) ,
    /*input             */.key_in     (key_in     ) ,
    /*output  reg       */.key_down   (key_en     ) 
);
    
    uart_rx u_uart_rx( 
    /*input				*/.clk		  (clk		  ) ,
    /*input				*/.rst_n	  (rst_n	  ) ,
    /*input             */.rx_din     (rx         ) ,//串口数据接收端
    /*input       [2:0] */.baud_sel   (3'd0       ) ,//波特率选择信号
    /*output  reg [7:0] */.rx_dout    (rx_dout    ) ,//接收模块输出的数据
    /*output  reg       */.rx_dout_vld(rx_dout_vld)  //接收模块输出的数据有效的标志信号,高有效
);

    uart_tx u_uart_tx( 
    /*input				*/.clk		  (clk	      ) ,
    /*input				*/.rst_n	  (rst_n	  ) ,
    /*input       [7:0] */.tx_din     (tx_din     ) ,//输入要发的数据
    /*input             */.tx_req     (tx_req     ) ,//发送请求信号
    /*input       [2:0] */.baud_sel   (3'd0       ) ,
    /*output  reg       */.tx_dout    (tx         ) ,//发送模块输出数据,1bit
    /*output  reg       */.busy       (busy       )  //忙闲指示信号,busy为0时,表示空闲;busy为1时,表示正在发送数据(忙)
);  
    
    sdram_control u_sdram_control( 
    /*input				*/.clk_50m_in	  (clk            ),//wrfifo写入端时钟
    /*input				*/.clk_50m_out	  (clk            ),//rdfifo读出段时钟
    /*input				*/.clk_100m       (clk_100m       ),//pll生成的100MHz时钟
    /*input				*/.rst_n          (rst_n          ),
    /*//uart       */    
    /*input				*/.din_vld	      (rx_dout_vld    ),//用户输入数据有效信号
    /*input		[7:0]   */.din		      (rx_dout		  ),//用户输入数据
    /*input             */.busy           (busy           ),//uart_tx忙信号
    /*output	    	*/.dout_vld       (tx_req         ),//输出给用户数据有效信号
    /*output	[7:0]	*/.dout	          (tx_din	      ),//输出给用户数据
    /*//key       */    
    /*input             */.rd_en          (key_en         ),//读请求信号
    /*//sdram       */
    /*output    [23:0]  */.avm_addr       (avm_addr       ),//访问sdram的地址
    /*output            */.avm_wr_n       (avm_wr_n       ),//访问sdram的写使能信号
    /*output    [15:0]  */.avm_wr_data    (avm_wr_data    ),//访问sdram的写数据
    /*output            */.avm_rd_n       (avm_rd_n       ),//访问sdram的读使能信号
    /*input     [15:0]  */.avm_rd_data    (avm_rd_data    ),//访问sdram的读出数据
    /*input             */.avm_rd_data_vld(avm_rd_data_vld),//访问sdram的读出数据有效信号
    /*input             */.avm_waitrequest(avm_waitrequest) //sdram等待请求信号
    
);

    sdram_interface u0 (
		.avalon_bus_address       (avm_addr        ),       // avalon_bus.address
		.avalon_bus_byteenable_n  (2'b0            ),  //           .byteenable_n
		.avalon_bus_chipselect    (1'b1            ),    //           .chipselect
		.avalon_bus_writedata     (avm_wr_data     ),     //           .writedata
		.avalon_bus_read_n        (avm_rd_n        ),        //           .read_n
		.avalon_bus_write_n       (avm_wr_n        ),       //           .write_n
		.avalon_bus_readdata      (avm_rd_data     ),      //           .readdata
		.avalon_bus_readdatavalid (avm_rd_data_vld ), //           .readdatavalid
		.avalon_bus_waitrequest   (avm_waitrequest ),   //           .waitrequest
		.clk_clk                  (clk_100m        ),                  //        clk.clk
		.mem_port_addr            (addr            ),            //   mem_port.addr
		.mem_port_ba              (ba              ),              //           .ba
		.mem_port_cas_n           (cas_n           ),           //           .cas_n
		.mem_port_cke             (cke             ),             //           .cke
		.mem_port_cs_n            (cs_n            ),            //           .cs_n
		.mem_port_dq              (dq              ),              //           .dq
		.mem_port_dqm             (dqm             ),             //           .dqm
		.mem_port_ras_n           (ras_n           ),           //           .ras_n
		.mem_port_we_n            (we_n            ),            //           .we_n
		.reset_reset_n            (rst_n           )             //      reset.reset_n
	);

    pll_ip_100m_75deg	pll_ip_100m_75deg_inst (
	.areset ( !rst_n      ),
	.inclk0 ( clk         ),
	.c0     ( clk_100m    ),
	.c1     ( clk_100m_deg),
	.locked (             )
	);
    
    
    
endmodule

6. 上板验证

由于SDRAM IP核突发长度只有1,所以只能一个数据一个数据的读写

串口可发送多个写入数据,读功能只能按一下按键读一个数据(16bit)