手机扫码
链接直达
https://item.taobao.com/item.htm?ft=t&id=776516984361
这个实验与上一个实验相比是加入了SDRAM,加入SDRAM的原因是为了给下一步的摄像头图像处理作铺垫,因为摄像头出的数据并不是像上一个实验一样刚好符合VESA的时序,需要用SDRAM做缓存。
工程层次分析
工程模块简介
- system_ctrl_pll 系统时钟和复位管理模块
- video_tgp_24b 生成视频测试pattern数据模块
- SDRAM_512Kx4x32bit EG4S内部的SDRAM
- Sdram_Control_2Port SDRAM控制器
- lcd_driver 产生视频数据的时序
- hdmi_tx HDMI发送模块
源码分析
system_ctrl_pll 系统时钟和复位管理模块
该模块主要是例化了一个PLL,同时进行了复位的处理。这个模块里的system_init_delay是对输入的参考时钟进行计数,用于等待时钟稳定。sys_pll就是例化的PLL。
这段代码是对板上的复位按键进行异步复位,同步释放。
//----------------------------------------------
//rst_n sync, only controlled by the main clk
reg rst_nr1, rst_nr2;
always @(posedge clk_ref)
begin
if(!rst_n)
begin
rst_nr1 <= 1'b0;
rst_nr2 <= 1'b0;
end
else
begin
rst_nr1 <= 1'b1;
rst_nr2 <= rst_nr1;
end
end
最后这行代码是将内部产生的同步释放的复位与PLL的锁定信号locked进行与操作生成系统复位。
assign sys_rst_n = rst_nr2 & locked; //active low
video_tgp_24b 生成视频测试pattern数据模块
这一块的代码主要完成视频测试数据的生成,由于这个工程中有SDRAM缓存,因此我们并不需要按照VESA的标准来生成测试数据,我们可以按我们的需求来将数据写入到SDRAM。
代码首先对行列进行计数,
if(lcd_xpos < H_TOTAL - 1'b1)
lcd_xpos <= lcd_xpos + 1'b1;
else
lcd_xpos <= 11'd0;
if(lcd_xpos == H_TOTAL - 1'b1)
begin
if(lcd_ypos < V_TOTAL - 1'b1)
lcd_ypos <= lcd_ypos + 1'b1;
else
lcd_ypos <= 11'd0;
end
else
lcd_ypos <= lcd_ypos;
在行列计数都有效的的情况下进行测试视频的生成
if(lcd_xpos <= H_DISP - 1'b1 && lcd_ypos <= V_DISP - 1'b1)
case(img_cnt)
'd0: data <= lcd_xpos * lcd_ypos;
'd1: data <= ((lcd_ypos[4]==1'b1) ^ (lcd_xpos[4]==1'b1)) ? {24{1'b0}} : {24{1'b1}};
'd2: data <= {lcd_ypos[7:0], 8'h00, lcd_xpos[7:0]};
'd3: data <= (lcd_ypos[9]) ? {lcd_ypos[7:0], lcd_ypos[7:0], lcd_ypos[7:0]}:
{lcd_xpos[7:0], lcd_xpos[7:0], lcd_xpos[7:0]};
'd4: begin
case (lcd_xpos[9:7])
'b111: data <= ROYAL ;
'b110: data <= CYAN ;
'b101: data <= YELLOW;
'b100: data <= BLACK ;
'b011: data <= WHITE ;
'b010: data <= BLUE ;
'b001: data <= GREEN ;
'b000: data <= RED;
endcase
end
'd5: begin
case (lcd_ypos[9:7])
'b000: data <= RED;
'b001: data <= GREEN ;
'b010: data <= BLUE ;
'b011: data <= WHITE ;
'b100: data <= BLACK ;
'b101: data <= YELLOW;
'b110: data <= CYAN ;
'b111: data <= ROYAL;
endcase
end
'd6: data <= {16'h0, lcd_xpos[7:0]};
'd7: data <= {lcd_ypos[7:0], 16'h0};
endcase
else
...
SDRAM_512Kx4x32bit EG4S内部的SDRAM
module EG_PHY_SDRAM_2M_32 (
clk,
ras_n,
cas_n,
we_n,
addr,
ba,
dq,
cs_n,
dm0,
dm1,
dm2,
dm3,
cke
);
这个就是EG4S内部合封的SDRAM,跟独立的SDRAM信号引脚一样,不作赘述。
Sdram_Control_2Port SDRAM控制器
module Sdram_Control_2Port(
// HOST Side
REF_CLK, //sdram control clock
OUT_CLK, //sdram output clock
RESET_N, //global clock reset
// FIFO Write Side 1
WR_DATA,
WR,
WR_MIN_ADDR,
WR_MAX_ADDR,
WR_LENGTH,
WR_LOAD,
WR_CLK,
// FIFO Read Side 1
RD_DATA,
RD,
RD_MIN_ADDR,
RD_MAX_ADDR,
RD_LENGTH,
RD_LOAD,
RD_CLK,
// SDRAM Side
SA,
BA,
CS_N,
CKE,
RAS_N,
CAS_N,
WE_N,
DQ,
DQM,
SDR_CLK,
//User interface
Sdram_Init_Done, //SDRAM Init done signal
Sdram_Read_Valid, //SDRAM Read valid : output
Sdram_PingPong_EN //SDRAM PING-PONG operation enable
);
这个模块是上一个模块是绍的SDRAM的控制器,主要完成SDRAM的初始化以及读写操作,这个模块本身相对复杂,后面会专门出一篇教程就先不在这里介绍了,这个模块原本是Northwest Logic 公司提供的,经过网友们魔改之后把它两弄成了一个双FIFO接口。关于每个信号的作用在代码中有详细的注释。
可以看到主要分成以下几部分:
- HOST端,主要提供SDRAM控制器的参考时钟输入,SDRAM的时钟输出,以及SDRAM控制器的复位输入。
- SDRAM端,就直接连接到了EG4S内部的SDRAM脚。
- FIFO 写端, FIFO读端,可以从模块内部看到实际就是连到了内部的两个异步FIFO,用于同步写入时钟和读出时钟,对于写端由用户进行写,由SDRAM进行读。对于读端由SDRAM进行写,由用户进行读。
Sdram_Control_2Port u_Sdram_Control_2Port
(
// HOST Side
.REF_CLK (clk_sdr_ref), //sdram module clock
.OUT_CLK (clk_sdr_out), //sdram clock input
.RESET_N (sys_rst_n), //sdram module reset
// SDRAM Side
.SDR_CLK (sdram_clk), //sdram clock
.CKE (sdram_cke), //sdram clock enable
.CS_N (sdram_cs_n), //sdram chip select
.WE_N (sdram_we_n), //sdram write enable
.CAS_N (sdram_cas_n), //sdram column address strobe
.RAS_N (sdram_ras_n), //sdram row address strobe
.DQM (sdram_dqm), //sdram data output enable
.BA (sdram_ba), //sdram bank address
.SA (sdram_addr), //sdram address
.DQ (sdram_data), //sdram data
// FIFO Write Side
.WR_CLK (sdram_clk_wr), //write fifo clock
.WR_LOAD (1'b0), //write register load & fifo clear
.WR_DATA (sdram_din), //write data input
.WR (sdram_wr), //write data request
.WR_MIN_ADDR (21'd0), //write start address
.WR_MAX_ADDR (21'd1280 * 21'd720),//write max address
.WR_LENGTH (9'd256), //write burst length
// FIFO Read Side
.RD_CLK (clk_read), //read fifo clock
.RD_LOAD (1'b0), //read register load & fifo clear
.RD_DATA (sys_data_out), //read data output
.RD (sys_rd_out), //read request
.RD_MIN_ADDR (21'd0), //read start address
.RD_MAX_ADDR (21'd1280 * 21'd720),//read max address
.RD_LENGTH (9'd256), //read length
//User interface
.Sdram_Init_Done (sdram_init_done), //SDRAM init done signal
.Sdram_Read_Valid (1'b1), //Enable to readl
.Sdram_PingPong_EN (1'b1) //SDRAM PING-PONG operation enable
);
lcd_driver 产生视频数据的时序
该模块跟我们前一个教中的vga_tc模块相似,通过行场计数器产生VESA所要求的720P的行场同步信号。将其中的DE信号作为SDRAM控制器的读请求从SDRAM中读出数据发送到HDMI_TX。同时该模块产生的行场同步还发送给HDMI TX,HDMI_TX将SDRMA控制器输出的数据串行发送出去。
hdmi_tx HDMI发送模块
前面一个教程描述过了,该模块负责将行场视频的并行数据转化为TMDS串行发送出去。
物理约束
set_pin_assignment { clk_10m } { LOCATION = P11; IOSTANDARD = LVCMOS33; PULLTYPE = NONE; }
set_pin_assignment { i_rst_n } { LOCATION = P83; IOSTANDARD = LVCMOS33; PULLTYPE = PULLUP; }
#HDMI TX
set_pin_assignment { HDMI_CLK_P } { LOCATION = P47; IOSTANDARD = LVDS33; }
set_pin_assignment { HDMI_D0_P } { LOCATION = P55; IOSTANDARD = LVDS33; }
set_pin_assignment { HDMI_D1_P } { LOCATION = P61; IOSTANDARD = LVDS33; }
set_pin_assignment { HDMI_D2_P } { LOCATION = P64; IOSTANDARD = LVDS33; }
时序约束
#Created Clock
create_clock -name clk_10m -period 100 [get_ports {clk_10m}]
# Automatically constrain PLL and other generated clocks
derive_pll_clocks
rename_clock -name {clk_sdr_ref} -source [get_ports {clk_10m}] -master_clock {clk_10m} [get_pins {u_system_ctrl_pll/u_sys_pll/pll_inst.clkc[0]}]
rename_clock -name {clk_sdr_out} -source [get_ports {clk_10m}] -master_clock {clk_10m} [get_pins {u_system_ctrl_pll/u_sys_pll/pll_inst.clkc[1]}]
rename_clock -name {clk_pixel} -source [get_ports {clk_10m}] -master_clock {clk_10m} [get_pins {u_system_ctrl_pll/u_sys_pll/pll_inst.clkc[2]}]
rename_clock -name {clk_pixel_5x} -source [get_ports {clk_10m}] -master_clock {clk_10m} [get_pins {u_system_ctrl_pll/u_sys_pll/pll_inst.clkc[3]}]
set_clock_groups -exclusive -group [get_clocks {clk_10m}] -group [get_clocks {clk_sdr_ref}] -group [get_clocks {clk_sdr_out}] -group [get_clocks {clk_pixel clk_pixel_5x}]
上板实验
实验现像跟上一个教程的现象差不多,只是图案有变化。
没有回复内容