上一节实现了FPGA串口通信,这一节继续跟着特权同学的例程,记录一下基于UART的DDR3数据读写实验。本文全部基于书本《Xilinx FPGA伴你玩转USB3.0与LVDS》,我是新人请多多关照,一起进步!
一、功能概述
上电之后,自动向DDR3写256*32*【128bits】数据,PC通过串口助手发8位数据(地址)给串口接收模块,串口接收模块存储地址数据并且发送给data_source模块,从而确定了从哪个地址读DDR3,然后将读出的512byte数据发给PC端。
二、所有模块及顶层文件(.V)代码及注释
2.1 data_source.v
我自己补充的一些注释,我自己的理解...写的不一定对。
/
//特权同学 精心打造 Xilinx FPGA开发板系列
//工程硬件平台: Xilinx Artex7 FPGA
//开发套件型号: SF-AT7 特权打造
//版 权 申 明: 本例程由《深入浅出玩转FPGA》作者“特权同学”原创,
// 仅供SF-AT7开发套件学习使用,谢谢支持
/
module data_source(
input clk, //在生成DDR3 IP的界面中有选择1/4还是1/2模式,本工程选择的1/4,所以是DDR3时钟(400MHz)的1/4,即100MHz
input rst_n, //复位信号
//----------------DDR3控制器IP的用户接口信号:这部分时序与 命令和地址 相关----------------------------------//
output reg[27:0] app_addr, //DDR3地址总线
output reg[2:0] app_cmd, //DDR3命令总线,3‘b000--写;3'b001--读;
output reg app_en, //DDR3操作请求信号,高电平有效
input app_rdy, //DDR3操作响应信号,表示当前的app_en请求已经获得响应,可以继续操作
//----------------DDR3控制器IP的用户接口信号:这部分时序与 向DDR3写数据 相关----------------------------------//
output reg[127:0] app_wdf_data, //DDR3写入数据总线
output reg app_wdf_wren, //DDR3写入数据使能信号,表示当前写入数据有效
output app_wdf_end, //关于 app_wdf_end 信号,该信号表示:当前突发写的最后一个数据。
//在A7 DDR3 的控制器IP核中,只存在突发长度为 8 地址的形式 ,1 个地址能存放的数据是 16bit
//因此每一次的地址突发带来的数据突发为 8*16=128 bit(对外接口为128bit)。
//本次 DDR3 IP 核调取时,我们选取的 “物理层 - 用户端” 的速率为 4:1,每次发送的有效数据为 128 bit
//因此只需1 次突发写就完成了数据的写入,故app_wdf_end 和 app_wdf_en 时序上同步了。
input app_wdf_rdy, //DDR3可以执行写入数据操作,该信号拉高表示写数据FIFO已经准备好接收数据
//----------------DDR3控制器IP的用户接口信号:这部分时序与 从DDR3读数据 相关----------------------------------//
input[127:0] app_rd_data, //DDR3读取数据总线
input app_rd_data_end, //类比app_wdf_end信号
input app_rd_data_valid, //DDR3读出数据使能信号,表示当前读出数据有效
//用户逻辑读写DDR3接口
input user_ddr3_rden, //串口接收模块输出的rx_rdy接收有效信号连接user_ddr3_rden端口,拉高时代表进入读取DDR3数据状态,并
//同时将数据缓存至FIFO,通过串口发给PC机
input[7:0] user_ddr3_addr, //用户规定的首地址
output user_ddr3_rddata_vld, //DDR3 IP读数据有效信号输出给fifo的wr_en写使能端口,代表从DDR3读出的数据即将进入FIFO缓存
output[127:0] user_ddr3_rddata //DDR3中存储的数据进FIFO
);
/********************************功能描述****************************************/
//上电后,从0地址开始遍历写256*32*【128bits数据】到DDR3的地址0-(256*32-1)中
//外部信号可以指定读某个地址数据,连续读出32*【128bits数据】
/********************************功能描述****************************************/
parameter BURST_WR_128BIT = 9'd32; //burst写数据数量
parameter BURST_RD_128BIT = 9'd32; //burst读数据数量
//2.?us定时计数逻辑 时钟为100MHz(10ns)
reg[7:0] scnt;
reg[8:0] times;
always @(posedge clk or negedge rst_n)
if(!rst_n)
scnt <= 8'd0;
else if(scnt < 269)
scnt<=scnt+1'b1;
else
scnt<=0;
wire timer_wrreq = (scnt == 8'd100); //定时DDR3写数据信号,每过两点多微妙就会产生timer_wrreq有效信号
always @(posedge clk or negedge rst_n)
if(!rst_n) times <= 9'd0;
else if(scnt == 8'd200) begin
if(times < 9'd256) times <= times+1'b1;
else ;
end
//产生读写DDR3操作的状态
parameter SIDLE = 4'd0;
parameter SWRDB = 4'd1;
parameter SRDDB = 4'd2;
parameter SSTOP = 4'd3;
reg[3:0] nstate,cstate;
reg[8:0] num;
reg[8:0] wrnum;
always @(posedge clk or negedge rst_n)
if(!rst_n) cstate <= SIDLE;
else cstate <= nstate;
//数据读写仲裁控制状态机
always @(cstate or timer_wrreq or times or user_ddr3_rden or num or app_rdy or app_wdf_rdy) begin
case(cstate)
SIDLE: begin
if(timer_wrreq && (times < 9'd256)) nstate <= SWRDB;
else if(user_ddr3_rden) nstate <= SRDDB; //PC给串口发送地址之后,状态才会跳到SRDDB
else nstate <= SIDLE;
end
SWRDB: begin //写数据
if((wrnum > BURST_WR_128BIT) && (num > BURST_WR_128BIT)) nstate <= SSTOP;//一个单位数据32*128bits=4096bits=512byte,
//这里就是保证一个timer_wrreq有效之后
//在下一个timer_wrreq有效来之前32个【128bits数据】都写完
else nstate <= SWRDB;
end
SRDDB: begin //读数据
if(num > BURST_RD_128BIT) nstate <= SSTOP; //这里就是保证32个128bits都读完
else nstate <= SRDDB;
end
SSTOP: nstate <= SIDLE;
default: nstate <= SIDLE;
endcase
end
//读或写数据控制信号计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) num <= 9'd0;
else if(cstate == SWRDB) begin
if(app_rdy) num <= num+1'b1;
else ;
end
else if(cstate == SRDDB) begin
if(app_rdy) num <= num+1'b1;
else ;
end
else num <= 9'd0;
//写数据控制信号计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) wrnum <= 9'd0;
else if(cstate == SWRDB) begin
if(app_wdf_rdy) wrnum <= wrnum+1'b1;
else ;
end
else wrnum <= 9'd0;
//读写数据控制时序产生
//reg[27:0] app_addr, //DDR3地址总线
//reg[2:0] app_cmd, //DDR3命令总线,3‘b000--写;3'b001--读;3‘b011--wr_bytes(With ECC enabled, the wr_bytes operation is required for writes with any non-zero app_wdf_mask bits.)
//reg app_en, //DDR3操作请求信号,高电平有效
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
app_cmd <= 3'd0;
app_en <= 1'b0;
app_addr <= 28'd0;
end
else if(cstate == SWRDB) begin
app_cmd <= 3'b000; //状态机现态为写数据时,用户接口app_cmd=3'b000
if(app_rdy) begin
if(num < BURST_WR_128BIT) app_en <= 1'b1;//保证这32个周期内,app_en都置1,让所有32*128bits数据全部写入
else app_en <= 1'b0;
end
else ;
{app_addr[27:11],app_addr[2:0]} <= {4'd0,times[7:0],5'd0,3'd0}; //地址随着时钟更新,第一个time,num更新32次
//第二个time,num再更新32次
//如此,有种256次,每次512byte数据传输的感觉
if(app_rdy) app_addr[10:3] <= num[7:0];//app_addr[10:3]+1'b1;
else ;
end
else if(cstate == SRDDB) begin
app_cmd <= 3'b001; //状态机现态为读数据时,用户接口app_cmd=3'b001
if(app_rdy) begin
if(num < BURST_RD_128BIT) app_en <= 1'b1;//保证这32个周期内,app_en都置1,让所有32*128bits数据全部读入
else app_en <= 1'b0;
end
else ;
{app_addr[27:11],app_addr[2:0]} <= {4'd0,user_ddr3_addr,5'd0,3'd0}; //写入数据有256组【32*128bits】,但读数据只会
//读出user_ddr3_addr规定的这一组
//user_ddr3_addr等于多少,由PC端发送的数据决定
if(app_rdy) app_addr[10:3] <= num[7:0];//app_addr[10:3]+1'b1;
else ;
end
else begin
app_cmd <= 3'd0;
app_en <= 1'b0;
app_addr <= 28'd0;
end
//写数据控制时序产生
//reg[127:0] app_wdf_data, //DDR3写入数据总线
//reg app_wdf_end, //DDR3最后一个字节写入指示信号,与app_wdf_data同步指示当前操作为最后一个数据写入
//reg app_wdf_wren, //DDR3写入数据使能信号,表示当前写入数据有效
//input app_wdf_rdy, //DDR3可以执行写入数据操作,该信号拉高表示写数据FIFO已经准备好接收数据
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
app_wdf_wren <= 1'b0;
app_wdf_data <= 128'd0;
end
else if(cstate == SWRDB) begin
if(app_wdf_rdy) begin
if(wrnum < BURST_WR_128BIT) app_wdf_wren <= 1'b1;
else app_wdf_wren <= 1'b0;
end
else ;
if(app_wdf_rdy) begin
app_wdf_data[7:0] <= times[7:0]+{wrnum[3:0],4'd0}+8'd15;//
app_wdf_data[15:8] <= times[7:0]+{wrnum[3:0],4'd0}+8'd14;//
app_wdf_data[23:16] <= times[7:0]+{wrnum[3:0],4'd0}+8'd13;//
app_wdf_data[31:24] <= times[7:0]+{wrnum[3:0],4'd0}+8'd12;//
app_wdf_data[39:32] <= times[7:0]+{wrnum[3:0],4'd0}+8'd11;//
app_wdf_data[47:40] <= times[7:0]+{wrnum[3:0],4'd0}+8'd10;//
app_wdf_data[55:48] <= times[7:0]+{wrnum[3:0],4'd0}+8'd9; //
app_wdf_data[63:56] <= times[7:0]+{wrnum[3:0],4'd0}+8'd8; //
app_wdf_data[71:64] <= times[7:0]+{wrnum[3:0],4'd0}+8'd7; //
app_wdf_data[79:72] <= times[7:0]+{wrnum[3:0],4'd0}+8'd6; //
app_wdf_data[87:80] <= times[7:0]+{wrnum[3:0],4'd0}+8'd5; //
app_wdf_data[95:88] <= times[7:0]+{wrnum[3:0],4'd0}+8'd4; //
app_wdf_data[103:96] <= times[7:0]+{wrnum[3:0],4'd0}+8'd3; //
app_wdf_data[111:104] <= times[7:0]+{wrnum[3:0],4'd0}+8'd2; //
app_wdf_data[119:112] <= times[7:0]+{wrnum[3:0],4'd0}+8'd1; //
app_wdf_data[127:120] <= times[7:0]+{wrnum[3:0],4'd0}+8'd0; //wrnum会加32次,每次都把128bits给一个地址;32次之后就给了32*128bits
//32次作为一个单元,即为256次512byte
end
else ;
end
else begin
app_wdf_wren <= 1'b0;
app_wdf_data <= 128'd0;
end
assign app_wdf_end = 1'b1;
//读数据控制时序产生
//input[127:0] app_rd_data, //DDR3读取数据总线
//input app_rd_data_end, //DDR3最有一个字节读取指示信号,与app_rd_data同步指示当前操作为最后一个数据读出
//input app_rd_data_valid //DDR3读出数据使能信号,表示当前读出数据有效
/*reg ram_we_en;
reg[7:0] ram_addr;
reg[127:0] ram_data;
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
ram_we_en <= 1'b0;
ram_addr <= 8'd0;
ram_data <= 128'd0;
end
else if(timer_rdreq) begin
ram_we_en <= 1'b0;
ram_addr <= 8'd0;
ram_data <= 128'd0;
end
else if(app_rd_data_valid) begin
ram_we_en <= 1'b1;
ram_addr <= ram_addr+1'b1;
ram_data <= app_rd_data;
end
//RAM例化,256*128bit
dist_mem_gen_0 onchip_ram_ddr3_rddb (
.a(ram_addr), // input wire [7 : 0] a
.d(ram_data), // input wire [127 : 0] d
.clk(clk), // input wire clk
.we(ram_we_en), // input wire we
.qspo() // output wire [127 : 0] qspo
); */
assign user_ddr3_rddata_vld = app_rd_data_valid;
assign user_ddr3_rddata = app_rd_data;
//在线逻辑分析仪例化
ila_0 ila_chipscope_for_dubug (
.clk(clk), // input wire clk
.probe0(app_en), // input wire [0:0] probe0
.probe1(app_cmd), // input wire [0:0] probe1
.probe2(app_addr), // input wire [0:0] probe2
.probe3(app_rdy), // input wire [0:0] probe3
.probe4(app_wdf_end), // input wire [0:0] probe4
.probe5(app_wdf_wren), // input wire [0:0] probe5
.probe6(app_wdf_rdy), // input wire [0:0] probe6
.probe7(app_wdf_data), // input wire [0:0] probe7
.probe8(app_rd_data_end), // input wire [0:0] probe8
.probe9(app_rd_data_valid), // input wire [0:0] probe9
.probe10(app_rd_data), // input wire [0:0] probe10
.probe11(timer_wrreq), // input wire [0:0] probe11
.probe12(user_ddr3_rden),
.probe13(user_ddr3_addr)
);
endmodule
2.2 uart_tx_source.v模块
我自己补充的一些注释,我自己的理解...写的不一定对。
/
//特权同学 精心打造 Xilinx FPGA开发板系列
//工程硬件平台: Xilinx Artex7 FPGA
//开发套件型号: SF-AT7 特权打造
//版 权 申 明: 本例程由《深入浅出玩转FPGA》作者“特权同学”原创,
// 仅供SF-AT7开发套件学习使用,谢谢支持
//官方淘宝店铺: http://myfpga.taobao.com/
//最新资料下载: http://pan.baidu.com/s/1c2iTPra
//公 司: 上海或与电子科技有限公司
/
module at7(
// DDR3接口
inout [15:0] ddr3_dq,
inout [1:0] ddr3_dqs_n,
inout [1:0] ddr3_dqs_p,
output [13:0] ddr3_addr,
output [2:0] ddr3_ba,
output ddr3_ras_n,
output ddr3_cas_n,
output ddr3_we_n,
output ddr3_reset_n,
output [0:0] ddr3_ck_p,
output [0:0] ddr3_ck_n,
output [0:0] ddr3_cke,
output [1:0] ddr3_dm,
output [0:0] ddr3_odt,
//系统时钟、外部复位
input sys_clk_i,
input ext_rst_n,
//UART接口
input uart_rx, // UART接收数据信号
output uart_tx, // UART发送数据信号
//LED指示灯接口
output[0:0] led //用于测试的LED指示灯 PIN_L5
);
//PLL例化
wire clk_200m;
wire sys_rst_n;
clk_wiz_0 uut_clk_wiz_0
(
.clk_in1(sys_clk_i),
.clk_out1(clk_200m),
.reset(!ext_rst_n),
.locked(sys_rst_n));
//DDR3 controller例化
wire ui_clk; //时钟信号,是DDR3时钟(400MHz)的1/4,即100MHz,ui_clk是DDR3IP输出的一个时钟,供后面所有模块使用
wire ui_clk_sync_rst; //复位信号,也是DDR3IP输出的一个信号,供后面所由模块使用
/*地址、命令*/
wire[27:0] app_addr; //DDR3地址总线
wire[2:0] app_cmd; //DDR3命令总线,3‘b000--写;3'b001--读;
wire app_en; //DDR3操作请求信号,高电平有效
wire app_rdy; //DDR3操作响应信号,表示当前的app_en请求已经获得响应,可以继续操作
wire init_calib_complete; //校准初始化完成标志信号,高电平有效
/*向DDR3写数据*/
wire[127:0] app_wdf_data; //DDR3写入数据总线
wire app_wdf_end; //DDR3最后一个字节写入指示信号,与app_wdf_data同步指示当前操作为最后一个数据写入
wire app_wdf_wren; //DDR3写入数据使能信号,表示当前写入数据有效
wire app_wdf_rdy; //DDR3可以执行写入数据操作
/*从DDR3读数据*/
wire[127:0] app_rd_data; //DDR3读取数据总线
wire app_rd_data_end; //DDR3最有一个字节读取指示信号,与app_rd_data同步指示当前操作为最后一个数据读出
wire app_rd_data_valid; //DDR3读出数据使能信号,表示当前读出数据有效,这个信号会使能uart_tx_source模块中的fifo,DDR3读出的数据会进入fifo缓存
/*DDR3控制器IP的用户接口信号:刷新请求与响应*/
wire app_ref_req = 1'b0; //DDR3刷新请求信号,高电平有效,该信号拉高必须保持到app_ref_ack拉高表示刷新请求已经响应
wire app_ref_ack; //DDR3刷新响应信号,高电平有效,该信号拉高表示已经执行DDR3的刷新操作
/*DDR3控制器IP的用户接口信号:ZQ校准请求与响应*/
wire app_zq_req = 1'b0; //DDR3 ZQ校准命令请求信号,高电平有效,该信号拉高必须保持到app_zq_ack拉高表示刷新请求已经响应
wire app_zq_ack; //DDR3 ZQ校准响应信号,高电平有效,该信号拉高表示已经执行DDR3的ZQ校准操作
/*DDR3控制器IP的用户接口信号*/
wire app_sr_req = 1'b0;
wire app_sr_active;
mig_7series_0 u1_mig_7series_0 (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [13:0] ddr3_addr
.ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba
.ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n
.ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p
.ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke
.ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n
.ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n
.ddr3_we_n (ddr3_we_n), // output ddr3_we_n
.ddr3_dq (ddr3_dq), // inout [15:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n), // inout [1:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p), // inout [1:0] ddr3_dqs_p
.init_calib_complete (init_calib_complete), // output init_calib_complete
.ddr3_dm (ddr3_dm), // output [1:0] ddr3_dm
.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
// Application interface ports
.app_addr (app_addr), // input [27:0] app_addr
.app_cmd (app_cmd), // input [2:0] app_cmd
.app_en (app_en), // input app_en
.app_wdf_data (app_wdf_data), // input [127:0] app_wdf_data
.app_wdf_end (app_wdf_end), // input app_wdf_end
.app_wdf_wren (app_wdf_wren), // input app_wdf_wren
.app_rd_data (app_rd_data), // output [127:0] app_rd_data
.app_rd_data_end (app_rd_data_end), // output app_rd_data_end
.app_rd_data_valid (app_rd_data_valid), // output app_rd_data_valid
.app_rdy (app_rdy), // output app_rdy
.app_wdf_rdy (app_wdf_rdy), // output app_wdf_rdy
.app_sr_req (app_sr_req), // input app_sr_req
.app_ref_req (app_ref_req), // input app_ref_req
.app_zq_req (app_zq_req), // input app_zq_req
.app_sr_active (app_sr_active), // output app_sr_active
.app_ref_ack (app_ref_ack), // output app_ref_ack
.app_zq_ack (app_zq_ack), // output app_zq_ack
.ui_clk (ui_clk), // output ui_clk
.ui_clk_sync_rst (ui_clk_sync_rst), // output ui_clk_sync_rst
.app_wdf_mask (16'h00/*app_wdf_mask*/), // input [15:0] app_wdf_mask不确认高低有效
// System Clock Ports
.sys_clk_i (clk_200m), // input sys_clk_i
.sys_rst (sys_rst_n) // input sys_rst
);
//产生数据源,用于测试DDR2的读写
//用户逻辑读写DDR3接口
wire user_ddr3_rden;
wire[7:0] user_ddr3_addr;
wire user_ddr3_rddata_vld;
wire[127:0] user_ddr3_rddata;
data_source u2_data_source(
.clk(ui_clk),
.rst_n(!ui_clk_sync_rst & init_calib_complete),
.app_addr(app_addr),
.app_cmd(app_cmd),
.app_en(app_en),
.app_rdy(app_rdy),
.app_wdf_data(app_wdf_data),
.app_wdf_end(app_wdf_end),
.app_wdf_wren(app_wdf_wren),
.app_wdf_rdy(app_wdf_rdy),
.app_rd_data(app_rd_data),
.app_rd_data_end(app_rd_data_end),
.app_rd_data_valid(app_rd_data_valid),
.user_ddr3_rden(user_ddr3_rden),
.user_ddr3_addr(user_ddr3_addr),//串口调试助手发送的8位地址数据通过u5_my_uart_rx的rx_data输出给user_ddr3_addr端口
//这8位地址数据将成为一个地址起点,从此地址开始往后的所有数据再通过串口返回给PC端
.user_ddr3_rddata_vld(user_ddr3_rddata_vld),
.user_ddr3_rddata(user_ddr3_rddata)
);
//UART需要发送数据缓存
wire ddr3uart_tx_en;
wire[7:0] ddr3uart_tx_db;
uart_tx_source u3_uart_tx_source(
.clk(ui_clk), //波特率选择模块
.rst_n(!ui_clk_sync_rst),
.user_ddr3_rddata_vld(user_ddr3_rddata_vld),
.user_ddr3_rddata(user_ddr3_rddata),
.ddr3uart_tx_db(ddr3uart_tx_db),
.ddr3uart_tx_en(ddr3uart_tx_en)
);
//下面的四个模块中,speed_rx和speed_tx是两个完全独立的硬件模块,可称之为逻辑复制
//(不是资源共享,和软件中的同一个子程序调用不能混为一谈)
wire bps_start1,bps_start2; //接收到数据后,波特率时钟启动信号置位
wire clk_bps1,clk_bps2; // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
//wire[7:0] rx_data; //接收数据寄存器,保存直至下一个数据来到
//wire rx_int; //接收数据中断信号,接收到数据期间始终为高电平
//UART接收信号波特率设置
speed_setting u4_speed_rx(
.clk(ui_clk), //波特率选择模块
.rst_n(!ui_clk_sync_rst),
.bps_start(bps_start1),
.clk_bps(clk_bps1)
);
//UART接收数据处理
my_uart_rx u5_my_uart_rx(
.clk(ui_clk), //接收数据模块
.rst_n(!ui_clk_sync_rst),
.uart_rx(uart_rx),
.rx_data(user_ddr3_addr),
.rx_rdy(user_ddr3_rden),
.clk_bps(clk_bps1),
.bps_start(bps_start1)
);
//-------------------------------------
//UART发送信号波特率设置
speed_setting u6_speed_tx(
.clk(ui_clk), //波特率选择模块
.rst_n(!ui_clk_sync_rst),
.bps_start(bps_start2),
.clk_bps(clk_bps2)
);
//UART发送数据处理
my_uart_tx u7_my_uart_tx(
.clk(ui_clk), //发送数据模块
.rst_n(!ui_clk_sync_rst),
.rx_data(ddr3uart_tx_db),
.rx_int(ddr3uart_tx_en),//uart_tx_source模块的ddr3uart_tx_en连接rx_int端口使能串口发送模块
.uart_tx(uart_tx),
.clk_bps(clk_bps2),
.bps_start(bps_start2)
);
//LED闪烁逻辑产生模块例化
led_controller u8_led_controller(
.clk(ui_clk),
.rst_n(!ui_clk_sync_rst),
.led(led[0])
);
endmodule
2.3 my_uart_rx.V模块、my_uart_tx.V模块和speed_setting.V模块
这一部分是上一节记录过的串口通信。文件层次图如下,这里直接放上工程,可以下载学习,我就不贴代码了。
链接:https://pan.baidu.com/s/1rX317rWFyBW2n2yrr2nHkQ
提取码:0809
2.4 顶层文件
/
//特权同学 精心打造 Xilinx FPGA开发板系列
//工程硬件平台: Xilinx Artex7 FPGA
//开发套件型号: SF-AT7 特权打造
//版 权 申 明: 本例程由《深入浅出玩转FPGA》作者“特权同学”原创,
// 仅供SF-AT7开发套件学习使用,谢谢支持
//官方淘宝店铺: http://myfpga.taobao.com/
//最新资料下载: http://pan.baidu.com/s/1c2iTPra
//公 司: 上海或与电子科技有限公司
/
module at7(
// DDR3接口
inout [15:0] ddr3_dq,
inout [1:0] ddr3_dqs_n,
inout [1:0] ddr3_dqs_p,
output [13:0] ddr3_addr,
output [2:0] ddr3_ba,
output ddr3_ras_n,
output ddr3_cas_n,
output ddr3_we_n,
output ddr3_reset_n,
output [0:0] ddr3_ck_p,
output [0:0] ddr3_ck_n,
output [0:0] ddr3_cke,
output [1:0] ddr3_dm,
output [0:0] ddr3_odt,
//系统时钟、外部复位
input sys_clk_i,
input ext_rst_n,
//UART接口
input uart_rx, // UART接收数据信号
output uart_tx, // UART发送数据信号
//LED指示灯接口
output[0:0] led //用于测试的LED指示灯 PIN_L5
);
//PLL例化
wire clk_200m;
wire sys_rst_n;
clk_wiz_0 uut_clk_wiz_0
(
.clk_in1(sys_clk_i),
.clk_out1(clk_200m),
.reset(!ext_rst_n),
.locked(sys_rst_n));
//DDR3 controller例化
wire ui_clk; //时钟信号,是DDR3时钟(400MHz)的1/4,即100MHz,ui_clk是DDR3IP输出的一个时钟,供后面所有模块使用
wire ui_clk_sync_rst; //复位信号,也是DDR3IP输出的一个信号,供后面所由模块使用
/*地址、命令*/
wire[27:0] app_addr; //DDR3地址总线
wire[2:0] app_cmd; //DDR3命令总线,3‘b000--写;3'b001--读;
wire app_en; //DDR3操作请求信号,高电平有效
wire app_rdy; //DDR3操作响应信号,表示当前的app_en请求已经获得响应,可以继续操作
wire init_calib_complete; //校准初始化完成标志信号,高电平有效
/*向DDR3写数据*/
wire[127:0] app_wdf_data; //DDR3写入数据总线
wire app_wdf_end; //DDR3最后一个字节写入指示信号,与app_wdf_data同步指示当前操作为最后一个数据写入
wire app_wdf_wren; //DDR3写入数据使能信号,表示当前写入数据有效
wire app_wdf_rdy; //DDR3可以执行写入数据操作
/*从DDR3读数据*/
wire[127:0] app_rd_data; //DDR3读取数据总线
wire app_rd_data_end; //DDR3最有一个字节读取指示信号,与app_rd_data同步指示当前操作为最后一个数据读出
wire app_rd_data_valid; //DDR3读出数据使能信号,表示当前读出数据有效,这个信号会使能uart_tx_source模块中的fifo,DDR3读出的数据会进入fifo缓存
/*DDR3控制器IP的用户接口信号:刷新请求与响应*/
wire app_ref_req = 1'b0; //DDR3刷新请求信号,高电平有效,该信号拉高必须保持到app_ref_ack拉高表示刷新请求已经响应
wire app_ref_ack; //DDR3刷新响应信号,高电平有效,该信号拉高表示已经执行DDR3的刷新操作
/*DDR3控制器IP的用户接口信号:ZQ校准请求与响应*/
wire app_zq_req = 1'b0; //DDR3 ZQ校准命令请求信号,高电平有效,该信号拉高必须保持到app_zq_ack拉高表示刷新请求已经响应
wire app_zq_ack; //DDR3 ZQ校准响应信号,高电平有效,该信号拉高表示已经执行DDR3的ZQ校准操作
/*DDR3控制器IP的用户接口信号*/
wire app_sr_req = 1'b0;
wire app_sr_active;
mig_7series_0 u1_mig_7series_0 (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [13:0] ddr3_addr
.ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba
.ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n
.ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p
.ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke
.ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n
.ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n
.ddr3_we_n (ddr3_we_n), // output ddr3_we_n
.ddr3_dq (ddr3_dq), // inout [15:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n), // inout [1:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p), // inout [1:0] ddr3_dqs_p
.init_calib_complete (init_calib_complete), // output init_calib_complete
.ddr3_dm (ddr3_dm), // output [1:0] ddr3_dm
.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
// Application interface ports
.app_addr (app_addr), // input [27:0] app_addr
.app_cmd (app_cmd), // input [2:0] app_cmd
.app_en (app_en), // input app_en
.app_wdf_data (app_wdf_data), // input [127:0] app_wdf_data
.app_wdf_end (app_wdf_end), // input app_wdf_end
.app_wdf_wren (app_wdf_wren), // input app_wdf_wren
.app_rd_data (app_rd_data), // output [127:0] app_rd_data
.app_rd_data_end (app_rd_data_end), // output app_rd_data_end
.app_rd_data_valid (app_rd_data_valid), // output app_rd_data_valid
.app_rdy (app_rdy), // output app_rdy
.app_wdf_rdy (app_wdf_rdy), // output app_wdf_rdy
.app_sr_req (app_sr_req), // input app_sr_req
.app_ref_req (app_ref_req), // input app_ref_req
.app_zq_req (app_zq_req), // input app_zq_req
.app_sr_active (app_sr_active), // output app_sr_active
.app_ref_ack (app_ref_ack), // output app_ref_ack
.app_zq_ack (app_zq_ack), // output app_zq_ack
.ui_clk (ui_clk), // output ui_clk
.ui_clk_sync_rst (ui_clk_sync_rst), // output ui_clk_sync_rst
.app_wdf_mask (16'h00/*app_wdf_mask*/), // input [15:0] app_wdf_mask不确认高低有效
// System Clock Ports
.sys_clk_i (clk_200m), // input sys_clk_i
.sys_rst (sys_rst_n) // input sys_rst
);
//产生数据源,用于测试DDR2的读写
//用户逻辑读写DDR3接口
wire user_ddr3_rden;
wire[7:0] user_ddr3_addr;
wire user_ddr3_rddata_vld;
wire[127:0] user_ddr3_rddata;
data_source u2_data_source(
.clk(ui_clk),
.rst_n(!ui_clk_sync_rst & init_calib_complete),
.app_addr(app_addr),
.app_cmd(app_cmd),
.app_en(app_en),
.app_rdy(app_rdy),
.app_wdf_data(app_wdf_data),
.app_wdf_end(app_wdf_end),
.app_wdf_wren(app_wdf_wren),
.app_wdf_rdy(app_wdf_rdy),
.app_rd_data(app_rd_data),
.app_rd_data_end(app_rd_data_end),
.app_rd_data_valid(app_rd_data_valid),
.user_ddr3_rden(user_ddr3_rden),
.user_ddr3_addr(user_ddr3_addr),//串口调试助手发送的8位地址数据通过u5_my_uart_rx的rx_data输出给user_ddr3_addr端口
//这8位地址数据将成为一个地址起点,从此地址开始往后的所有数据再通过串口返回给PC端
.user_ddr3_rddata_vld(user_ddr3_rddata_vld),
.user_ddr3_rddata(user_ddr3_rddata)
);
//UART需要发送数据缓存
wire ddr3uart_tx_en;
wire[7:0] ddr3uart_tx_db;
uart_tx_source u3_uart_tx_source(
.clk(ui_clk), //波特率选择模块
.rst_n(!ui_clk_sync_rst),
.user_ddr3_rddata_vld(user_ddr3_rddata_vld),
.user_ddr3_rddata(user_ddr3_rddata),
.ddr3uart_tx_db(ddr3uart_tx_db),
.ddr3uart_tx_en(ddr3uart_tx_en)
);
//下面的四个模块中,speed_rx和speed_tx是两个完全独立的硬件模块,可称之为逻辑复制
//(不是资源共享,和软件中的同一个子程序调用不能混为一谈)
wire bps_start1,bps_start2; //接收到数据后,波特率时钟启动信号置位
wire clk_bps1,clk_bps2; // clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
//wire[7:0] rx_data; //接收数据寄存器,保存直至下一个数据来到
//wire rx_int; //接收数据中断信号,接收到数据期间始终为高电平
//UART接收信号波特率设置
speed_setting u4_speed_rx(
.clk(ui_clk), //波特率选择模块
.rst_n(!ui_clk_sync_rst),
.bps_start(bps_start1),
.clk_bps(clk_bps1)
);
//UART接收数据处理
my_uart_rx u5_my_uart_rx(
.clk(ui_clk), //接收数据模块
.rst_n(!ui_clk_sync_rst),
.uart_rx(uart_rx),
.rx_data(user_ddr3_addr),
.rx_rdy(user_ddr3_rden),
.clk_bps(clk_bps1),
.bps_start(bps_start1)
);
//-------------------------------------
//UART发送信号波特率设置
speed_setting u6_speed_tx(
.clk(ui_clk), //波特率选择模块
.rst_n(!ui_clk_sync_rst),
.bps_start(bps_start2),
.clk_bps(clk_bps2)
);
//UART发送数据处理
my_uart_tx u7_my_uart_tx(
.clk(ui_clk), //发送数据模块
.rst_n(!ui_clk_sync_rst),
.rx_data(ddr3uart_tx_db),
.rx_int(ddr3uart_tx_en),
.uart_tx(uart_tx),
.clk_bps(clk_bps2),
.bps_start(bps_start2)
);
//LED闪烁逻辑产生模块例化
led_controller u8_led_controller(
.clk(ui_clk),
.rst_n(!ui_clk_sync_rst),
.led(led[0])
);
endmodule
三、实验现象
打开串口调试助手,发0,代表首地址是0,从首地址开始,读出DDR3 512byte数据
如图,返回了512字节数据!!