前言
结合串口接收模块和 tft 显示屏控制模块,设计一个基于 DDR3 的串口传图帧缓存系统。
提示:以下是本篇文章正文内容,下面案例可供参考
一、接口转换模块设计
fifo_mig_native_fifo模块是系统中相对比较重要的模块,涉及到与 DDR 控制器接口对接。该模块的主要是实现接口的转换,将普通的 FIFO 接口转换成native接口,用于将 FIFO 里的数据读出然后存储在 DDR 存储器以及将 DDR 存储器读出的数据存放到 FIFO 缓存。
上电初始状态为 IDLE 状态,当 DDR 完成初始化和校准(即 init_calib_complete 变为高电平)后进入读状态;代码如下。
IDLE_transform_WRITE = ((curr_state == S_IDLE ) && init_calib_complete && (wr_ddr3_req == 1'b1) ) ,
if(init_calib_complete)
begin
case(curr_state)
S_IDLE:begin
if(IDLE_transform_WRITE)
begin
curr_state <= S_WRITE;
app_addr <= 0;
end
else
curr_state <= curr_state;
end
当进入读请求后,根据条件判断是否完成读数据,若完成则跳转等待状态,否则进行数据与地址操作;
WRITE_transform_WAIT = ((curr_state == S_WRITE ) && app_rdy && app_wdf_rdy && (app_addr == wr_rd_cnt-8)),
WRITE_transform_COUNT = ((curr_state == S_WRITE) && app_rdy && app_wdf_rdy) && wrfifo_rd_cnt_en,
S_WRITE:begin
if(WRITE_transform_WAIT)
curr_state <= S_WAIT;
else if(WRITE_transform_COUNT)
begin
app_addr <= app_addr + 8;
end
else begin
curr_state <= curr_state;
app_addr <= app_addr;
end
end
进入等待状态后,根据设定条件判断是否进入读状态,此时由于读写共用一个地址因此将地址清0:
WAIT_transform_READ = ((curr_state == S_WAIT ) && (rd_ddr3_req == 1'b1)) ,
S_WAIT:begin
if(WAIT_transform_READ)
begin
curr_state <= S_READ;
if (app_addr == wr_rd_cnt-8)
app_addr <= 0;
else if(rdfifo_wr_cnt == 6)
app_addr <= app_addr + 8 ;
else
app_addr <= app_addr;
end
else
curr_state <= curr_state;
end
在满足设定条件后进入读状态,此时由设定条件判断是否连续读取,还是进入等待:
READ_transform_WAIT = ((curr_state == S_READ ) && app_rdy && (rdfifo_wr_cnt >= rdfifo_depth_max - 30) ),
READ_transform_COUNT = ((curr_state == S_READ ) && app_rdy ) ;
S_READ:begin
if(READ_transform_WAIT)
curr_state <= S_WAIT;
else if(READ_transform_COUNT)
begin
if (app_addr == wr_rd_cnt-8)
app_addr <= 0;
else
app_addr <= app_addr + 8;
end
else begin
curr_state <= curr_state;
app_addr <= app_addr;
end
end
完成代码展示:
`timescale 1ns / 1ps
module fifo_ddr3_native_fifo
#(
parameter wr_req_cnt_thresh = 12'd6 ,
rd_req_cnt_thresh = 12'd6 ,
wr_rd_cnt = 20'd384000 ,
rdfifo_depth_max = 12'd4095
//此部分数据可根据实际需要改变 (wr_rd_cnt)64*16位 = 128位*8(wr_ddr_cnt )
)
(
//wr_ddr3_fifo ports
input wrfifo_rst ,
input loc_clk50M ,
input [15:0]wrfifo_din ,
input wrfifo_wren ,
//rd_ddr3_fifo ports
input rdfifo_rst ,
input loc_clk33M ,
input rdfifo_rden ,
output [15:0]rdfifo_dout ,
//tft的EN信号
output reg rdfifo_WR_EN ,
//DDR3 Interface
//input
input loc_clk200M ,
input xx_sys_rst , //用于连接 pll_locked
//output
output ui_clk ,
output ui_clk_sync_rst ,
output init_calib_complete ,
//DDR3 Interface
// Inouts
inout [15:0] ddr3_dq ,
inout [1:0] ddr3_dqs_n ,
inout [1:0] ddr3_dqs_p ,
// Outputs
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 [0:0] ddr3_cs_n ,
output [1:0] ddr3_dm ,
output [0:0] ddr3_odt
);
//------------------------------------------
//状态机参数
localparam S_IDLE = 4'b0001 ,
S_WRITE = 4'b0010 ,
S_WAIT = 4'b0100 ,
S_READ = 4'b1000 ;
//------------------------------------------
//**********************************
//信号定义说明
//**********************************
//wrfifo ports
wire[127:0]wrfifo_dout ;
wire wrfifo_rden ;
wire wrfifo_wr_rst_busy ;
wire wrfifo_rd_rst_busy ;
wire [11:0]wrfifo_rd_cnt ;
//rdfifo ports
wire[127:0]rdfifo_din ;
wire rdfifo_wr_rst_busy ;
wire rdfifo_rd_rst_busy ;
wire [11:0]rdfifo_wr_cnt ;
//cmd ports
reg [27:0]app_addr ;
wire [2:0]app_cmd ;
wire app_en ;
wire app_rdy ;
//write data ports
wire[127:0]app_wdf_data ;
wire app_wdf_end ;
wire app_wdf_wren ;
wire app_wdf_rdy ;
//read data ports
wire[127:0]app_rd_data ;
wire app_rd_data_end ;
wire app_rd_data_valid ;
//ddr3 相关信号
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack ;
//状态机控制相关信号
reg wrfifo_rd_cnt_en;
reg [3:0]wrfifo_rd_cnt_count;
reg [23:0]rdfifo_dout_cnt;
reg [23:0]app_rd_data_valid_cnt;
//wr_ddr3_req ports
wire wr_fifo_rst_busy ;
wire wr_ddr3_req ;
//rd_ddr3_req ports
wire rd_fifo_rst_busy ;
wire rd_ddr3_req ;
//state transform
reg [3:0] curr_state ;
//wrfifo / rdfifo
assign wrfifo_rden = app_wdf_wren && app_wdf_rdy;
assign rdfifo_din = app_rd_data ;
assign rdfifo_wren = app_rd_data_valid ;
//--------------------------------------------------------------------------------
//------------------------------------
//根据wrfifo写入的是16位数据读出为128,
//由16->128此过程需要转换时间因此当ddr
//过早请求数据(128位),而此时wrfifo
//的16位还未转化为128,将造成数据的丢失。
//设计将 wrfifo_rd_cnt == 0后ddr不再写入
//数据,这样可以避免数据丢失
//------------------------------------
always @ ( posedge ui_clk or negedge wrfifo_rst)
begin
if(wrfifo_rst)
wrfifo_rd_cnt_count <= 0;
else if(wrfifo_rd_cnt == 1)
wrfifo_rd_cnt_count <= wrfifo_rd_cnt_count + 1'b1;
else
wrfifo_rd_cnt_count <= 0;
end
always @ (posedge ui_clk or negedge wrfifo_rst )
begin
if(wrfifo_rst)
wrfifo_rd_cnt_en <= 1;
else if(wrfifo_rd_cnt_count == 2)
wrfifo_rd_cnt_en <= 1;
else if( wrfifo_rd_cnt == 0)
wrfifo_rd_cnt_en <= 0;
end
//判断读写的条件
assign wr_fifo_rst_busy = (wrfifo_wr_rst_busy | wrfifo_rd_rst_busy),
wr_ddr3_req = ((wr_fifo_rst_busy == 1'b0) && (wrfifo_rd_cnt >= wr_req_cnt_thresh)) ? 1'b1:1'b0,
rd_fifo_rst_busy = (rdfifo_wr_rst_busy | rdfifo_rd_rst_busy),
rd_ddr3_req = ((rd_fifo_rst_busy == 1'b0) && (rdfifo_wr_cnt <= rd_req_cnt_thresh)) ? 1'b1:1'b0 ;
//**********************************
//state transform
//**********************************
assign IDLE_transform_WRITE = ((curr_state == S_IDLE ) && init_calib_complete && (wr_ddr3_req == 1'b1) ) ,
WRITE_transform_WAIT = ((curr_state == S_WRITE ) && app_rdy && app_wdf_rdy && (app_addr == wr_rd_cnt-8)),
WRITE_transform_COUNT = ((curr_state == S_WRITE) && app_rdy && app_wdf_rdy) && wrfifo_rd_cnt_en,
WAIT_transform_READ = ((curr_state == S_WAIT ) && (rd_ddr3_req == 1'b1)) ,
READ_transform_WAIT = ((curr_state == S_READ ) && app_rdy && (rdfifo_wr_cnt >= rdfifo_depth_max - 30) ),
READ_transform_COUNT = ((curr_state == S_READ ) && app_rdy ) ;
//-----------------------------------------------------------------------------------------------
//**********************************
//状态转移实现
//**********************************
//在写状态或读状态,拉高使能信号,
assign app_en = WRITE_transform_COUNT || READ_transform_COUNT ;
//在写状态,命令接收和数据接收都准备好,此时拉高写使能
assign app_wdf_wren = WRITE_transform_COUNT;
//由于 DDR3 芯片时钟和用户时钟的分频选择 4:1,突发长度为 8,故两个信号相同
assign app_wdf_end = app_wdf_wren;
//ddr读为 1,其他值为 0
assign app_cmd = (curr_state == S_READ) ? 3'd1 :3'd0;
assign app_wdf_data = wrfifo_dout;
//**********************************
//state machine
//**********************************
always @ (posedge ui_clk or negedge ui_clk_sync_rst)
if(ui_clk_sync_rst)
begin
curr_state <= S_IDLE;
app_addr <= 0;
end
else if(init_calib_complete)
begin
case(curr_state)
S_IDLE:begin
if(IDLE_transform_WRITE)
begin
curr_state <= S_WRITE;
app_addr <= 0;
end
else
curr_state <= curr_state;
end
S_WRITE:begin
if(WRITE_transform_WAIT)
curr_state <= S_WAIT;
else if(WRITE_transform_COUNT)
begin
app_addr <= app_addr + 8;
end
else begin
curr_state <= curr_state;
app_addr <= app_addr;
end
end
S_WAIT:begin
if(WAIT_transform_READ)
begin
curr_state <= S_READ;
if (app_addr == wr_rd_cnt-8)
app_addr <= 0;
else if(rdfifo_wr_cnt == 6)
app_addr <= app_addr + 8 ;
else
app_addr <= app_addr;
end
else
curr_state <= curr_state;
end
S_READ:begin
if(READ_transform_WAIT)
curr_state <= S_WAIT;
else if(READ_transform_COUNT)
begin
if (app_addr == wr_rd_cnt-8)
app_addr <= 0;
else
app_addr <= app_addr + 8;
end
else begin
curr_state <= curr_state;
app_addr <= app_addr;
end
end
default:begin
curr_state <= S_IDLE;
app_addr <= 0;
end
endcase
end
//-----------------------------------------------------------------------------------------------
//在rdfifo写数据开始时对TFT相关内容操作
//防止tft请求数据,而rdfifo还未写入数据,造成数据丢失。
always @(posedge ui_clk or negedge rdfifo_rst)
begin
if(rdfifo_rst)
rdfifo_WR_EN <= 0;
else if( rdfifo_wren)
rdfifo_WR_EN <= 1;
else
rdfifo_WR_EN <=rdfifo_WR_EN ;
end
//-----------------------------------------------------------------------------------------------------------
always @(posedge loc_clk33M or negedge rdfifo_rst)
begin
if(rdfifo_rst)
rdfifo_dout_cnt <= 0;
else if(rdfifo_rden)
rdfifo_dout_cnt <= rdfifo_dout_cnt + 1;
else
rdfifo_dout_cnt <= rdfifo_dout_cnt;
end
always @(posedge ui_clk or negedge ui_clk_sync_rst)
if(ui_clk_sync_rst)
app_rd_data_valid_cnt <= 0;
else if(app_rd_data_valid)
app_rd_data_valid_cnt <= app_rd_data_valid_cnt + 1;
else
app_rd_data_valid_cnt <= app_rd_data_valid_cnt;
//------------------------------------------------------------------------------------------------------------
//**********************************
//例化 wr_ddr3_fifo、rd_ddr3_fifo、mig_7series_0
//**********************************
wr_ddr3_fifo wr_ddr3_fifo (
.rst (wrfifo_rst), // input wire rst
.wr_clk (loc_clk50M), // input wire wr_clk
.rd_clk (ui_clk ), // input wire rd_clk
.din (wrfifo_din), // input wire [15 : 0] din
.wr_en (wrfifo_wren), // input wire wr_en
.rd_en (wrfifo_rden), // input wire rd_en
.dout (wrfifo_dout), // output wire [127 : 0] dout
.full ( ), // output wire full
.empty ( ), // output wire empty
.rd_data_count(wrfifo_rd_cnt), // output wire [11 : 0] rd_data_count
.wr_data_count( ), // output wire [14 : 0] wr_data_count
.wr_rst_busy (wrfifo_wr_rst_busy), // output wire wr_rst_busy
.rd_rst_busy (wrfifo_rd_rst_busy) // output wire rd_rst_busy
);
rd_ddr3_fifo rd_ddr3_fifo (
.rst (rdfifo_rst ), // input wire rst
.wr_clk (ui_clk ), // input wire wr_clk
.rd_clk (loc_clk33M ), // input wire rd_clk
.din (rdfifo_din ), // input wire [127 : 0] din
.wr_en (rdfifo_wren), // input wire wr_en
.rd_en (rdfifo_rden), // input wire rd_en
.dout (rdfifo_dout), // output wire [15 : 0] dout
.full ( ), // output wire full
.empty ( ), // output wire empty
.rd_data_count( ), // output wire [14 : 0] rd_data_count
.wr_data_count(rdfifo_wr_cnt), // output wire [11 : 0] wr_data_count
.wr_rst_busy (rdfifo_wr_rst_busy), // output wire wr_rst_busy
.rd_rst_busy (rdfifo_rd_rst_busy) // output wire rd_rst_busy
);
mig_7series_native u_mig_7series_native (
// 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_cs_n (ddr3_cs_n ), // output [0:0] ddr3_cs_n
.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 (1'b0 ), // input app_sr_req
.app_ref_req (1'b0 ), // input app_ref_req
.app_zq_req (1'b0 ), // 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'h0000 ), // input [15:0] app_wdf_mask
// System Clock Ports
.sys_clk_i (loc_clk200M ),
.sys_rst (xx_sys_rst ) // input sys_rst
);
endmodule
二、仿真模块设计
设计写入4096个数据,然后读出两次数据,观察数据是否正确。
`timescale 1ns / 1ns
module fifo_ddr3_native_fifo_tb();
reg wrfifo_rst;
reg loc_clk50M;
reg [15:0]wrfifo_din;
reg wrfifo_wren;
reg rdfifo_rst;
reg loc_clk33M;
reg rdfifo_rden;
wire[15:0]rdfifo_dout;
wire rdfifo_WR_EN;
reg loc_clk200M;
reg xx_sys_rst;
wire ui_clk;
wire ui_clk_sync_rst;
wire init_calib_complete;
wire[13:0]ddr3_addr ;
wire[2:0] ddr3_ba ;
wire ddr3_cas_n ;
wire[0:0] ddr3_ck_n ;
wire[0:0] ddr3_ck_p ;
wire[0:0] ddr3_cke ;
wire ddr3_ras_n ;
wire ddr3_reset_n;
wire ddr3_we_n ;
wire[15:0]ddr3_dq ;
wire[1:0] ddr3_dqs_n ;
wire[1:0] ddr3_dqs_p ;
wire[0:0] ddr3_cs_n ;
wire[1:0] ddr3_dm ;
wire[0:0] ddr3_odt ;
initial loc_clk200M = 1'b1;
always #2.5 loc_clk200M = ~loc_clk200M;
initial loc_clk50M = 1'b1;
always #10 loc_clk50M = ~loc_clk50M;
initial loc_clk33M = 1'b1;
always #15 loc_clk33M = ~loc_clk33M;
initial begin
xx_sys_rst = 1'b0;
wrfifo_rst = 1'b1;
wrfifo_wren= 1'b0;
wrfifo_din = 16'd0;
rdfifo_rst = 1'b1;
rdfifo_rden= 1'b0;
#201;
xx_sys_rst = 1'b1;
#200;
wrfifo_rst = 1'b0;
rdfifo_rst = 1'b0;
@(posedge init_calib_complete)
#200;
wr_data(16'd100,16'd4096);
#2000;
rd_data(16'd4096);
#5000;
rd_data(16'd4096);
#5000;
$stop;
end
task wr_data;
input [15:0]data_begin;
input [15:0]wr_data_cnt;
begin
wrfifo_wren = 1'b0;
wrfifo_din = data_begin;
@(posedge loc_clk50M);
#1 wrfifo_wren = 1'b1;
repeat(wr_data_cnt)
begin
@(posedge loc_clk50M);
wrfifo_din = wrfifo_din + 1'b1;
end
#1 wrfifo_wren = 1'b0;
end
endtask
task rd_data;
input [15:0]rd_data_cnt;
begin
rdfifo_rden = 1'b0;
@(posedge loc_clk33M);
#1 rdfifo_rden = 1'b1;
repeat(rd_data_cnt)
begin
@(posedge loc_clk33M);
end
#1 rdfifo_rden = 1'b0;
end
endtask
fifo_ddr3_native_fifo
#(
. wr_req_cnt_thresh (24'd6 ) ,
. rd_req_cnt_thresh (24'd6 ) ,
. wr_rd_cnt (28'd4096 ) ,
. rdfifo_depth_max (24'd4095 )
)fifo_ddr3_native_fifo
(
//wr_ddr3_fifo ports
.wrfifo_rst (wrfifo_rst) ,
.loc_clk50M (loc_clk50M ) ,
.wrfifo_din (wrfifo_din) ,
.wrfifo_wren (wrfifo_wren) ,
//rd_ddr3_fifo ports
.rdfifo_rst (rdfifo_rst) ,
.loc_clk33M (loc_clk33M) ,
.rdfifo_rden (rdfifo_rden) ,
.rdfifo_dout (rdfifo_dout) ,
.rdfifo_WR_EN (rdfifo_WR_EN) ,
//DDR3 Interface
//input
. loc_clk200M (loc_clk200M) ,
. xx_sys_rst (xx_sys_rst) , //用于连接 pll_locked
//output
. ui_clk (ui_clk) ,
. ui_clk_sync_rst (ui_clk_sync_rst) ,
. init_calib_complete (init_calib_complete) ,
//DDR3 Interface
// Inouts
. ddr3_dq (ddr3_dq ) ,
. ddr3_dqs_n (ddr3_dqs_n ) ,
. ddr3_dqs_p (ddr3_dqs_p ) ,
// Outputs
. ddr3_addr (ddr3_addr ) ,
. ddr3_ba (ddr3_ba ) ,
. ddr3_ras_n (ddr3_ras_n ) ,
. ddr3_cas_n (ddr3_cas_n ) ,
. ddr3_we_n (ddr3_we_n ) ,
. ddr3_reset_n (ddr3_reset_n) ,
. ddr3_ck_p (ddr3_ck_p ) ,
. ddr3_ck_n (ddr3_ck_n ) ,
. ddr3_cke (ddr3_cke ) ,
. ddr3_cs_n (ddr3_cs_n ) ,
. ddr3_dm (ddr3_dm ) ,
. ddr3_odt (ddr3_odt )
);
ddr3_model ddr3_model
(
.rst_n (ddr3_reset_n ),
.ck (ddr3_ck_p ),
.ck_n (ddr3_ck_n ),
.cke (ddr3_cke ),
.cs_n (ddr3_cs_n ),
.ras_n (ddr3_ras_n ),
.cas_n (ddr3_cas_n ),
.we_n (ddr3_we_n ),
.dm_tdqs(ddr3_dm ),
.ba (ddr3_ba ),
.addr (ddr3_addr ),
.dq (ddr3_dq ),
.dqs (ddr3_dqs_p ),
.dqs_n (ddr3_dqs_n ),
.tdqs_n ( ),
.odt (ddr3_odt )
);
endmodule
三、仿真分析
通过仿真分析,写入数据与读出数据吻合(黄色标记处)。因此,验证成功。