本例将接着上一例实现的sdram控制器进行封装。上例中只是实现了一个基本的控制器,在实际使用中,通常读写时钟是两个不同频率的,所以并不能满足要求。
在本例中,将对读写接口进行封装,将读写接口封装成FIFO接口这样封装主要是为了让,读写的时候,满足设计要求(存储OV5640传输过来的图像,并且显示在VGA显示器上)。
最后将设计好的接口,配置之前写好的串口以及VGA驱动,实现一个小例子。通过串口发送图片,sdram存储,vga显示。本例完成后,就可以使用此sdram模块做很多事情啦!
欢迎关注微信公众号 FPGA之旅 回复 FPGA之旅设计99例之第二十一例 获取完整代码,以及相关软件。
本例写的比较匆忙,望见谅。
在封装的时候,使用到了FIFO IP,主要用来,外部传输图像数据到SDRAM中间的一个缓存区域,接口信号设计如下,外部给定帧地址,以及同步信号后(将写入sdram的地址复位为帧的起始地址),后面来的时候,就会依次往sdram中存储,这样外部就不需要管地址了,只需要知道存储到第几个帧的位置就可以了。帧大小以及写的突发长度,通过宏定义在sdram_defines.v文件中,方便修改。整个代码实现如下,非常简单。

//写正常 中转,外部写入数据暂存于此,然后写入sdram
`include "sdram_defines.v"
module sdram_hpfifo_write
(
input rst_n,
input write_clk, //写时钟
input write_en, //写使能
input[1:0] write_frame_addr, //帧地址选择
input write_frame_sync, //帧同步
input[15:0] write_data,
//sdram
input read_clk,
output write_req,
input write_ack,
output[23:0] write_addr,
output[9:0] write_burst_length,
output[15:0] write_sdram_data,
input write_data_en
);
wire[9:0] rdusedw;
reg [23:0] read_addr_reg;
assign write_burst_length = `burst_length;
assign write_addr = read_addr_reg;
assign write_req = ( rdusedw >= `burst_length ) ? 1'b1 : 1'b0;
always@(posedge read_clk or negedge rst_n)
begin
if( rst_n == 1'b0)
read_addr_reg <= 'd0;
else if( write_ack == 1'b1 )
read_addr_reg <= read_addr_reg + `burst_length;
else
read_addr_reg <= read_addr_reg;
end
fifo fifo_w
(
.aclr ( ~rst_n ),
.data ( write_data ),
.rdclk ( read_clk ),
.rdreq ( write_data_en ),
.wrclk ( write_clk ),
.wrreq ( write_en ),
.q ( write_sdram_data),
.rdusedw ( rdusedw ),
.wrusedw ()
);
endmodule
读接口主要用于外部从sdram读取数据,内部也调用了一个FIFO IP。信号接口,以及实现过程和写接口封装一模一样。就不做信息的说明了。

封装完成后,整体的框图如下,也是非常的简介。

封装的实现还是比较容易的,大家可以试试不调用FIFO,自己实现一个FIFO实现。
为了检测封装的是否成功,通过这个小例子,就可以检测出来。如果图片显示无误,那么说明封装正确了。
本例所使用到的模块,均是前几例中实现了的模块,所以这个例子也就是将其组合在一起。
module main(
input sys_clk,
input rst_n,
//????
input uartrx, /*uart rx???*/
//vga
output[15:0] vga_rgb,
output vga_hsync,
output vga_vsync,
input key,
//sdram
output sdram_clk, //sdram clock
output sdram_cke, //sdram clock enable
output sdram_cs_n, //sdram chip select
output sdram_we_n, //sdram write enable
output sdram_cas_n, //sdram column address strobe
output sdram_ras_n, //sdram row address strobe
output[1:0] sdram_dqm, //sdram data enable
output[1:0] sdram_ba, //sdram bank address
output[12:0] sdram_addr, //sdram address
inout[15:0] sdram_dq //sdram data
);
wire uart_rxs_done;
wire[15:0] uart_data;
wire display_data_en;
wire[15:0] display_data;
wire vga_clk_25M;
wire sdram_clk_75M;
wire sdram_clk_75M_Shift;
reg[23:0] cnt;
reg[1:0] read_frame_addr;
always@(posedge vga_clk_25M or negedge rst_n)
begin
if( rst_n == 1'b0)
read_frame_addr <= 2'b00;
else if( key == 1'b0)
read_frame_addr <= read_frame_addr + 1'b1;
else
read_frame_addr <= read_frame_addr;
end
pll pll_PL(
.inclk0 (sys_clk),
.c0 (vga_clk_25M),
.c1 (sdram_clk_75M),
.c2 (sdram_clk_75M_Shift));
UART_MulRX UART_MulRX_HP(
.sys_clk ( sys_clk ), /*???? 50M*/
.rst_n ( rst_n ), /*????*/
.uart_rxs_done ( uart_rxs_done ), /*??????*/
.odats ( uart_data ), /*????*/
.uartrx ( uartrx )/*uart rx???*/
);
vga_driver vga_driver_HP(
.vga_clk ( vga_clk_25M ),
.rst_n ( rst_n ),
.display_data_en ( display_data_en ),
.curr_x ( ),
.curr_y ( ),
.display_data ( display_data ),
.vga_rgb ( vga_rgb ),
.vga_hsync ( vga_hsync ),
.vga_vsync ( vga_vsync )
);
sdram_hpfifo sdram_hpfifoHP(
.sys_clk ( sdram_clk_75M ), //sdram_??
.sdram_clk_100M_F ( sdram_clk_75M_Shift ),
.rst_n ( rst_n ),
//?
.read_clk ( vga_clk_25M ), //???
.read_en ( display_data_en ), //???
.read_frame_addr ( read_frame_addr ), //?????
.read_fram_sync ( vga_vsync ), //???
.read_data ( display_data ),
//?
.write_clk ( sys_clk ), //???
.write_en ( uart_rxs_done ), //???
.write_frame_addr ( 2'b00 ), //?????
.write_frame_sync ( 1'b0 ), //???
.write_data ( uart_data ),
//sdram??
.sdram_clk ( sdram_clk ), //sdram clock
.sdram_cke ( sdram_cke ), //sdram clock enable
.sdram_cs_n ( sdram_cs_n ), //sdram chip select
.sdram_we_n ( sdram_we_n ), //sdram write enable
.sdram_cas_n ( sdram_cas_n ), //sdram column address strobe
.sdram_ras_n ( sdram_ras_n ), //sdram row address strobe
.sdram_dqm ( sdram_dqm ), //sdram data enable
.sdram_ba ( sdram_ba ), //sdram bank address
.sdram_addr ( sdram_addr ), //sdram address
.sdram_dq ( sdram_dq ) //sdram data
);
endmodule
通过串口发送图片的时候,并不需要额外去提取图像数据,像很多例程中说提到的通过其他编程语言来实现,只需要用来下面的软件即可生成。这里一定要选择生成二进制文件。

然后将生成好的bin文件,放入com软件中,发送即可。

最后的显示效果如图所示。最后上面的软件会放到本例的下载链接中。

ps: 测试时,本人使用的开发板跑不到100M,调试了好久,才发现这个问题,最终将时钟频率降低到了75M。