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)