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. addressbyteenable read clk 的上升沿后置位。slave 置位 waitrequest

暂停传输。

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

state)addressreadwrite byteenable 保持不变。

3. slave clk 的上升沿之后置低 waitrequestslave 置位 readdata response

4. master readdataresponse 和置低的 waitrequest(完成传输)的进行采样。

5. addresswritedatabyteenable 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 dataslave 没有置位 waitrequest。写操作完

成。

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

成。

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

成。

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

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

8. Avalon master 驱动 write dataslave 没有置位 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)

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SDRAM控制器是一种很常见的控制器,可以用Verilog实现。下面是一个简单SDRAM控制器的Verilog代码示例: ``` module sdram_controller ( input clk, input rst, input [11:0] addr, input [1:0] cs_n, input [1:0] ras_n, input [1:0] cas_n, input we_n, input [15:0] data_in, output [15:0] data_out ); // 定义状态机的状态 typedef enum logic [2:0] { IDLE, READ, WRITE } state_t; // 定义状态机的变量 state_t state, next_state; // 定义读写时序 parameter t_RC = 20; // 行命令周期 parameter t_RCD = 10; // 行到列命令延迟 parameter t_RAS = 42; // 行选通时间 parameter t_CAS = 12; // 列选通时间 parameter t_WR = 15; // 写入时间 parameter t_RRD = 10; // 行间隔时间 // 定义状态机的寄存器 reg [11:0] row_addr_reg, col_addr_reg; reg [15:0] data_reg; reg [3:0] t_rc_count, t_rcd_count, t_ras_count, t_cas_count, t_wr_count, t_rrd_count; // 状态机的组合逻辑 always_comb begin case (state) IDLE: if (cs_n == 2'b00 && ras_n == 2'b10 && cas_n == 2'b10 && we_n == 1'b0) begin next_state = READ; row_addr_reg = addr[11:0]; col_addr_reg = addr[9:0]; end else if (cs_n == 2'b00 && ras_n == 2'b10 && cas_n == 2'b10 && we_n == 1'b1) begin next_state = WRITE; row_addr_reg = addr[11:0]; col_addr_reg = addr[9:0]; data_reg = data_in; end else begin next_state = IDLE; end READ: if (t_rc_count == t_RC) begin next_state = READ; t_rc_count = 0; end else if (t_ras_count == t_RAS && t_cas_count == t_CAS) begin next_state = IDLE; t_ras_count = 0; t_cas_count = 0; end else begin next_state = READ; end WRITE: if (t_rc_count == t_RC) begin next_state = WRITE; t_rc_count = 0; end else if (t_ras_count == t_RAS && t_cas_count == t_CAS && t_wr_count == t_WR) begin next_state = IDLE; t_ras_count = 0; t_cas_count = 0; t_wr_count = 0; end else begin next_state = WRITE; end endcase end // 状态机的时序逻辑 always @(posedge clk) begin if (rst) begin state <= IDLE; t_rc_count <= 0; t_rcd_count <= 0; t_ras_count <= 0; t_cas_count <= 0; t_wr_count <= 0; t_rrd_count <= 0; end else begin state <= next_state; t_rc_count <= t_rc_count + 1; t_rcd_count <= t_rcd_count + 1; t_ras_count <= t_ras_count + 1; t_cas_count <= t_cas_count + 1; t_wr_count <= t_wr_count + 1; t_rrd_count <= t_rrd_count + 1; end end // 读写数据的时序逻辑 always @(posedge clk) begin if (rst) begin data_out <= 16'd0; end else begin case (state) READ: if (t_rcd_count == t_RCD) begin data_out <= data_reg; end WRITE: if (t_rcd_count == t_RCD && t_wr_count == t_WR) begin data_out <= data_in; end endcase end end endmodule ``` 这个SDRAM控制器的代码实现读写操作的时序控制,包括行命令周期、行到列命令延迟、行选通时间、列选通时间、写入时间和行间隔时间等时序参数。在具体使用时,需要根据SDRAM的具体型号和规格,调整这些时序参数的值。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值