|
本帖最后由 正点原子 于 2021-1-23 15:35 编辑
1)实验平台:正点原子达芬奇FPGA开发板
2)购买链接:https://detail.tmall.com/item.htm?id=624335496505
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:905624739 点击加入:
第四十二章HDMI输入输出环回实验
在HDMI彩条显示实验中,我们成功地在HDMI显示器上显示出了彩条的图像。本章我们将使用FPGA开发板实现对HDMI的输入源解码,再通过编码输出到HDMI显示器上实时显示,实现HDMI输入输出环回的功能。
本章包括以下几个部分:
4242.1简介
42.2实验任务
42.3硬件设计
42.4程序设计
42.5下载验证
42.1
简介
HDMI包含三种独立的通信通道::TMDS、DDC和可选的CEC通道。
TMDS通道用于传送所有的音频和视频数据,还有辅助数据,包括用来描述有效的音频和视频数据流的AVI和音频信息帧。
HDMI源端通过DDC通道用来读取E-EDID数据架构从而获得HDMI接收端的接收能力和接收特性。这样HDMI源端只会发送那些接收端支持的视频格式。除此之外,HDMI接收端还通过DDC通道探测到信息帧,进而正确的接收音视频信息。
CEC是一个可选的功能,用来定义一些高级的用户功能例如单键播放,单键刻录及刻录时间控制等。
由于本次实验只是使用HDMI接口来显示图像,不需要传输音频,因此我们只需要实现DVI接口和DDC通道的驱动逻辑即可,不需要CEC功能。不过在此之前我们还需要简单地了解一下TMDS视频传输协议。在前文的HDMI彩条实验已经介绍过HDMI的编码原理,下面介绍下解码原理。
图 42.1.1 DVI解码器示意图
DVI解码器需要在一系列的串行数据中确定数据边界的位置,一旦数据通道中的边界字符被确定,解码器将依此同步串行数据流,并且将TMDS通道传输的数据流解码。TMDS数据流提供一个周期性的尾接提示给同步解码器。当在视频数据的数据周期和数据岛周期处理TMDS字时,则包含5次或更少的转换,当在控制周期处理TMDS字时,则包含7次或更多的转换。这些字符跳转次数的不同将会被用于接收设备的同步边界。当边界被确定后,将串并转换器转换后的并行数据进行8B/10B的解码,得到像素数据和相关控制信号。
EDID数据结构:
EDID(Extended Display Identification Data,扩展显示标识数据)是VESA的一个简单规范。它定义了一种数据格式,其中包括有关监视器及其性能的参数,包括生产厂家信息、显示器的名称和序列号、支持的所有分辨率清单、颜色设置、厂商预设值等信息。
DDC就是一个有两根符合I2C协议的数据线(SCL,SDA)组成的数据通道。HDMI源端通过这个DDC通道访问HDMI接收端的EEPROM,从而获得EDID信息,也就是获得了一些关于接收端的信息,然后HDMI发送端再根据这些信息决定发送的视频格式,从而达到最好的显示效果。
EDID的扩展数据由256 bytes组成,其中前面128 bytes是基本的数据结构,它的数据如下图所示:
表格 42.1.1 EDID的基本数据结构
在地址7Eh处,当数据为00h时,只有128byte的基础数据结构,如果为01h时,那么基础数据结构就为256byte。当使用VGA和DVI时,将数值设为00h,使用HDMI时,数值设为01h。
表格 42.1.2 EDID扩展数据结构
地址空间(+80H) 值 数据内容描述
HDMI协议与DVI协议在很多方面都是相同的,包括物理连接(TMDS)、有效视频编码算法以及控制字符的定义等。相比于DVI,HDMI在视频的消隐期会传输更多的数据,包括音频数据和附加数据。4-bit音频和附加数据将通过TERC4编码机制转换成10-bit TERC4字符,然后在绿色和红色通道上传输。
42.2实验任务
本节实验任务是使用达芬奇开发板将HDMI_A输入进来的数据流进行解码再编码后,通过HDMI_B的输出接口输出,用以驱动HDMI显示器。
42.3硬件设计
图 42.3.1 HDMI接口A原理图1
图 42.3.1是达芬奇HDMI接口A原理图的一部分,其中HDMI的三个数据通道HDMI_D[2:0]和一个时钟通道HDMI_CLK通过热插拔保护芯片与TMDS差分引脚相连。HDMI接口B的原理图与HDMI接口A是一样的,都是可以作为输入或输出的接口。
HDMI_CEC指的是用户电气控制(Consumer Electronics Control),它用于HDMI连接线上的设备之间进行信息交换。当一个设备的状态发生变化时,CEC可以使用远程控制或自动改变设置来命令连接的关联设备的状态发生相应的变化。例如,如果用户放置一张碟片到蓝光播放器并开始播放,那么高清电视机将会自动打开电源,设置正确的视频输入格式和打开环绕声设备等等,这种关联通信提供了一个更好的客户体验。
HDMI_HPD指的是热拔插检测(Hot Plug Detect),当视频设备与接收设备通过HDMI连接时,接收设备将HPD置为高电平,通知发送设备。当发送设备检测到HPD为低电平时,表明断开连接。
图 42.3.2 HDMI接口A原理图2
在图 42.3.2中,HDMI_OUT_EN信号用于设置HDMI接口的输入输出模式,当其为高电平时作为输出端,此时由达芬奇开发板输出HDMI接口的5V电源。同时图 42.3.1中HDMI_HPD将作为输入信号使用;反之,当HDMI_OUT_EN为低电平时,开发板上的HDMI接口作为输入端,接口上的5V电源将由外部连接设备提供。同时HDMI_HPD将输出高电平,用于指示HDMI连接状态。
图 42.3.3 HDMI接口A原理图3
在图 42.3.3中,HDMI_SCL_LS和HDMI_SDA_LS是HDMI接口的显示数据通道(DDC,Display Data Channel),用于HDMI 发送端和接收端之间交换一些配置信息,通过I2C协议通信。发送端通过 DDC通道,读取接收端保存在 EEPROM中的EDID数据,获取接收端的信息,确认接收端终端显示的设置和功能,决定跟接收端之间以什么格式传输音/视频数据。
本次实验使用以下信号,各信号的管脚分配如下表所示:
表 42.3.1 HDMI输入输出环回实验管脚分配
信号名 方向 管脚 端口说明 电平标准
HDMI输入输出环回实验XDC约束文件如下:
############## NET - IOSTANDARD #################################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN PULLUP [current_design]
#############SPI Configurate Setting##############################
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
############## clock and reset define##############################
create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN R4 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN U2 [get_ports rst_n]
#####################HDMI_IN_A#######################################
create_clock -period 6.000 -name clk_hdmi_in_p -waveform {0.000 3.000}
set_property PACKAGE_PIN D22 [get_ports hdmi_ddc_scl_io]
set_property PACKAGE_PIN D21 [get_ports hdmi_ddc_sda_io]
set_property PACKAGE_PIN E22 [get_ports hdmi_in_hpd]
set_property PACKAGE_PIN C18 [get_ports clk_hdmi_in_p]
set_property PACKAGE_PIN C22 [get_ports {data_hdmi_in_p[0]}]
set_property PACKAGE_PIN B21 [get_ports {data_hdmi_in_p[1]}]
set_property PACKAGE_PIN B20 [get_ports {data_hdmi_in_p[2]}]
set_property PACKAGE_PIN E21 [get_ports hdmi_in_oen]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_ddc_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_ddc_sda_io]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_in_n]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_in_p]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_n[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_n[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_n[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_p[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_in_oen]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_in_hpd]
######################HDMI_OUT_B#######################################
set_property PACKAGE_PIN B17 [get_ports clk_hdmi_out_p]
set_property PACKAGE_PIN A18 [get_ports {data_hdmi_out_p[0]}]
set_property PACKAGE_PIN A15 [get_ports {data_hdmi_out_p[1]}]
set_property PACKAGE_PIN A13 [get_ports {data_hdmi_out_p[2]}]
set_property PACKAGE_PIN C20 [get_ports hdmi_out_oen]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_out_n]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_out_p]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_n[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_n[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_n[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_p[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_out_oen]
42.4程序设计
由于本次实验只需要HDMI接口的视频数据部分,因此将其当成DVI接口进行驱动。我们只需要实现图像的接受和发送功能,由此得出本次实验的系统框图如下所示:
图 42.4.1 顶层系统框图
由上图可知,时钟模块(mmcm)为HDMI解码模块和EDID读取模块提供驱动时钟。HDMI解码模块负责将HDMI输入源输入的串行差分信号转化为串行单端信号,再进行串并转换,最后再进行8B/10B解码,将解码后的时钟、控制信号和数据传给HDMI编码模块。HDMI编码模块与解码模块是相反的操作,先进行8B/10B编码,再进行并串转换,最后再将串行单端信号转为串行差分信号。EDID读取模块负责与输入源进行通信,告诉输入源监视器所需要的性能的参数,包括生产厂家信息、显示器的名称和序列号、支持的所有分辨率清单、颜色设置、厂商预设值等信息。
顶层模块的原理图如下图所示:
图 42.4.2 顶层模块原理图
FPGA顶层模块(hdmi_loop)例化了以下四个模块:时钟模块(mmcm)、HDMI解码模块(hdmi_rx)、HDMI编码模块(dvi_transmitter_top)和EDID读取模块(i2c_edid)。
时钟模块(mmcm):时钟模块通过调用MMCM IP核实现,共输出2个时钟,频率为10Mhz和200Mhz。 10Mhz的时钟负责对输入进来的iic信号和热插拔信号(hdmi_in_hpd)进行采样,200Mhz是HDMI解码模块的参考时钟。
HDMI解码封装层模块(hdmi_rx):HDMI解码模块负责对输入源的输入数据进行解析,将解析后的时钟、数据和控制信号传给HDMI编码模块。
HDMI编码模块(dvi_transmitter_top):HDMI编码模块负责将HDMI解码模块传出的时钟、数据和控制信号重新编码,并输出串行差分数据用以驱动HDMI显示器。有关HDMI编码模块的详细介绍请大家参考“HDMI彩条显示实验”章节。
EDID读取模块(i2c_edid):负责与输入源进行通信,将监视器的性能参数传给输入源。
顶层模块的代码如下:
- 1 module hdmi_loop(
- 2 input sys_clk, //50M系统时钟
- 3 input rst_n, //系统复位,低有效
- 4 //hdmi in
- 5 input hdmi_ddc_scl_io, //IIC时钟
- 6 inout hdmi_ddc_sda_io, //IIC数据
- 7 output hdmi_in_hpd, //热插拔信号
- 8 output [0:0] hdmi_in_oen, //输入输出切换信号
- 9 input clk_hdmi_in_n, //输入差分时钟
- 10 input clk_hdmi_in_p, //输入差分时钟
- 11 input [2:0] data_hdmi_in_n, //输入差分数据
- 12 input [2:0] data_hdmi_in_p, //输入差分数据
- 13 //hdmi out
- 14 output [0:0] hdmi_out_oen, //输入输出切换信号
- 15 output clk_hdmi_out_n, //输出差分时钟
- 16 output clk_hdmi_out_p, //输出差分时钟
- 17 output [2:0] data_hdmi_out_n, //输出差分数据
- 18 output [2:0] data_hdmi_out_p //输出差分数据
- 19 );
- 20
- 21 //wire define
- 22 wire clk_10m; //10m时钟
- 23 wire clk_200m; //200m时钟
- 24 wire rx_rst; //复位信号,高有效
- 25 wire pixel_clk; //像素时钟
- 26 wire pixel_clk_5x; //5倍像素时钟
- 27 wire video_hs; //行信号
- 28 wire video_vs; //场信号
- 29 wire video_de; //数据有效使能
- 30 wire hdmi_in_oen; //输入输出切换信号
- 31 wire hdmi_in_hpd; //热插拔信号
- 32 wire hdmi_out_oen; //输入输出切换信号
- 33 wire [23:0] video_rgb; //像素数据
- 34
- 35 //*****************************************************
- 36 //** main code
- 37 //*****************************************************
- 38 //时钟模块
- 39 mmcm u_mmcm(
- 40 .clk_out1 (clk_10m), // output clk_out1
- 41 .clk_out2 (clk_200m), // output clk_out1
- 42 .locked ( ), // output locked
- 43 .clk_in1 (sys_clk) // input clk_in1
- 44 );
- 45
- 46 //读edid模块
- 47 i2c_edid u_i2c_edid (
- 48 .clk(clk_10m),
- 49 .rst(~rst_n),
- 50 .scl(hdmi_ddc_scl_io),
- 51 .sda(hdmi_ddc_sda_io)
- 52 );
- 53
- 54 //hdmi解码模块
- 55 hdmi_rx u_hdmi_rx(
- 56 .clk_10m (clk_10m),
- 57 .clk_200m (clk_200m),
- 58 //input
- 59 .tmdsclk_p (clk_hdmi_in_p),
- 60 .tmdsclk_n (clk_hdmi_in_n),
- 61 .blue_p (data_hdmi_in_p[0]),
- 62 .green_p (data_hdmi_in_p[1]),
- 63 .red_p (data_hdmi_in_p[2]),
- 64 .blue_n (data_hdmi_in_n[0]),
- 65 .green_n (data_hdmi_in_n[1]),
- 66 .red_n (data_hdmi_in_n[2]),
- 67 .rst_n (rst_n),
- 68 //output
- 69 .reset (rx_rst),
- 70 .pclk (pixel_clk),
- 71 .pclkx5 (pixel_clk_5x),
- 72 .hsync (video_hs),
- 73 .vsync (video_vs),
- 74 .de (video_de),
- 75 .rgb_data (video_rgb),
- 76 .hdmi_in_en (hdmi_in_oen),
- 77 .hdmi_in_hpd (hdmi_in_hpd)
- 78 );
- 79
- 80 //hdmi编码模块
- 81 dvi_transmitter_top u_rgb2dvi_0(
- 82 .pclk (pixel_clk),
- 83 .pclk_x5 (pixel_clk_5x),
- 84 .reset_n (~rx_rst),
- 85
- 86 .video_din (video_rgb),
- 87 .video_hsync (video_hs),
- 88 .video_vsync (video_vs),
- 89 .video_de (video_de),
- 90
- 91 .tmds_clk_p (clk_hdmi_out_p),
- 92 .tmds_clk_n (clk_hdmi_out_n),
- 93 .tmds_data_p (data_hdmi_out_p),
- 94 .tmds_data_n (data_hdmi_out_n),
- 95 .tmds_oen (hdmi_out_oen)
- 96 );
- 97 endmodule
复制代码
在代码的39行至44行,添加了一个MMCM的IP,产生了一个10M时钟和200M时钟,10M时钟用以驱动EDID读取模块和hdmi解码模块,200M时钟作为hdmi解码模块的参考时钟。
在代码的47行至53行,例化了一个EDID的读取模块。本次实验中的IIC是作为从机来使用的,所以用10M时钟来做iic时钟和数据的采样时钟,不需要再对10M时钟进行分频了。
在代码的55行至78行,例化了一个HDMI解码封装层模块。在代码的70和71行,定义了2个时钟信号,一个是像素时钟,一个是5倍的像素的时钟。这里用5倍像素时钟而不用10倍像素时钟的原因是在编码模块里的OSERDESE2原语采用的是DDR模式,而不是SDR模式。在代码的76行,这个信号的作用是告诉外设这个接口是输入还是输出。在代码的77行,这个信号的作用是告诉外设这个接口是否连接上了。
在代码的81行至96行,例化了一个hdmi编码模块。
EDID读取模块的代码如下:
- 1 module i2c_edid
- 2 (
- 3 input wire clk,
- 4 input wire rst,
- 5 input wire scl,
- 6 inout wire sda
- 7 );
- 8
- 9 //parameter define
- 10 parameter EDID_IDLE = 3'b000;
- 11 parameter EDID_ADDR = 3'b001;
- 12 parameter EDID_ADDR_ACK = 3'b010;
- 13 parameter EDID_ADDR_ACK2= 3'b011;
- 14 parameter EDID_DATA = 3'b100;
- 15 parameter EDID_DATA_ACK = 3'b101;
- 16 parameter EDID_DATA_ACK2= 3'b110;
- 17
- 18 //reg define
- 19 reg hiz ;
- 20 reg sda_out;
- 21 reg [4:0] count;
- 22 reg [15:0] rdata;
- 23 reg [7:0] addr;
- 24 reg [7:0] data ;
- 25 reg [2:0] edid_state;
- 26 reg [3:0] scl_data ;
- 27 reg [3:0] sda_data ;
- 28
- 29 //wire define
- 30 wire [7:0] dout;
- 31 wire scl_high;
- 32 wire scl_negedge;
- 33 wire scl_posedge;
- 34
- 35 //*****************************************************
- 36 //** main code
- 37 //*****************************************************
- 38
- 39 //取IIC时钟的上升沿
- 40 assign scl_posedge = (scl_data == 4'b0111) ? 1'b1 : 1'b0;
- 41 //取IIC时钟的下降沿
- 42 assign scl_negedge = (scl_data == 4'b1000) ? 1'b1 : 1'b0;
- 43 //IIC时钟的全高电平
- 44 assign scl_high = (scl_data == 4'b1111) ? 1'b1 : 1'b0;
- 45 //IIC数据的三态输出
- 46 assign sda = hiz ? 1'hz : sda_out;
- 47
- 48 //存储edid信息
- 49 edid_rom edid_rom_0 (
- 50 .addra(addr[7:0]),
- 51 .clka(clk),
- 52 .douta(dout)
- 53 );
- 54
- 55 always @(posedge clk) begin
- 56 if (rst) begin
- 57 hiz <= 1'b1;
- 58 sda_out <= 1'b0;
- 59 count <= 5'd0;
- 60 rdata <= 24'h0;
- 61 addr <= 8'h0;
- 62 data <= 8'h0;
- 63 scl_data <= 4'h00;
- 64 sda_data <= 4'h00;
- 65 end
- 66 else begin
- 67 scl_data <= {scl_data[2:0], scl};
- 68 sda_data <= {sda_data[2:0], sda};
- 69
- 70 if (sda_data == 4'b1000 && scl_high) begin // iic开始的标志
- 71 count <= 5'd0;
- 72 hiz <= 1'b1;
- 73 sda_out <= 1'b0;
- 74 edid_state <= EDID_ADDR;
- 75 end
- 76 else if (sda_data == 4'b0111 && scl_high) begin // iic结束的标志
- 77 edid_state <= EDID_IDLE;
- 78 end
- 79 else
- 80 case (edid_state)
- 81 EDID_IDLE: begin
- 82 hiz <= 1'b1;
- 83 sda_out <= 1'b0;
- 84 end
- 85 EDID_ADDR: begin
- 86 if (scl_posedge) begin
- 87 count <= count + 5'd1;
- 88 rdata <= {rdata[14:0], sda};
- 89 if (count[2:0] == 3'd7) begin //器件地址写完
- 90 edid_state <= EDID_ADDR_ACK;
- 91 if (count == 5'd15) //字地址写完
- 92 addr <= {rdata[6:0],sda}; //将字地址赋给ROM地址
- 93 else
- 94 addr <= addr;
- 95 end
- 96 end
- 97 end
- 98 EDID_ADDR_ACK: begin
- 99 if (scl_negedge) begin
- 100 hiz <= 1'b0;
- 101 sda_out <= 1'b0;
- 102 if (count == 5'd8 && rdata [0] == 1'b1) begin //判断是否是读操作
- 103 data <= dout;
- 104 edid_state <= EDID_DATA;
- 105 end
- 106 else begin
- 107 edid_state <= EDID_ADDR_ACK2;
- 108 end
- 109 end
- 110 end
- 111 EDID_ADDR_ACK2: begin
- 112 if (scl_negedge) begin
- 113 hiz <= 1'b1; //释放总线
- 114 edid_state <= EDID_ADDR;
- 115 end
- 116 end
- 117 EDID_DATA: begin
- 118 if (scl_negedge) begin
- 119 count <= count + 5'd1;
- 120 hiz <= 1'b0;
- 121 sda_out <= data[7];
- 122 data <= {data[6:0], 1'b0}; //数据移位
- 123 if (count[2:0] == 3'd7) begin //一个数据读完
- 124 addr <= addr + 8'h1; //rom地址加1
- 125 edid_state <= EDID_DATA_ACK;
- 126 end
- 127 end
- 128 end
- 129 EDID_DATA_ACK: begin
- 130 if (scl_negedge) begin
- 131 data <= dout;
- 132 hiz <= 1'b1; //释放总线
- 133 sda_out <= 1'b0;
- 134 edid_state <= EDID_DATA_ACK2;
- 135 end
- 136 end
- 137 EDID_DATA_ACK2: begin
- 138 if (scl_posedge) begin
- 139 if (sda) //数据为1,读操作结束,为0,未结束
- 140 edid_state <= EDID_IDLE;
- 141 else
- 142 edid_state <= EDID_DATA;
- 143 end
- 144 end
- 145 endcase
- 146 end
- 147 end
- 148
- 149 endmodule
复制代码
代码的40行至44行,是对IIC时钟变化的定义。
代码的46行,是对双向引脚SDA进行一个三态处理,hiz为1时,释放总线,否则就输出数据。
代码的49行至53行,是对存放EDID信息的ROM的一个例化,其中初始化所用的coe文件是从网上搜索得到的。。
代码的69行至143行,是对IIC时序的一个状态跳转代码,其流程图如下所示:
图 42.4.3 IIC时序的状态跳转图
代码的69行至73行,是IIC时序的开始信号到来时对信号的初始化操作。
代码的74行至75行,是IIC时序的结束信号到来时,将状态跳转到空闲状态。
代码的78行至80行,当状态处于空闲状态时,释放数据总线,使总线处于高阻状态。
代码的82行至94行,是主机向从机写入器件地址和寄存器地址。本次实验中FPGA作为从机,并且与主机是一对一的关系,所以可以不需要识别器件地址和寄存器地址。当信号count在0~7时,主机写入的是器件地址,信号count在8~f时,写入的是寄存器地址。如下图所示:
图 42.4.4 IIC时序图1
代码的95行至106行,判断器件地址的第8位是否为高,为高则表明是读操作,为低则表示是写操作。因为本次的IIC时序采用的是随机读时序,即在时序中有个虚拟写操作,如下图所示。当第8个时钟下降沿到来时,拉低数据总线,以此响应主机。
图 42.4.5 IIC时序图2
代码的107行至112行,当SCL的下降沿到来时,表示从机应答完成,所以要释放总线。
代码的113行至124行,是主机从从机读取数据的操作。当一个字节读完后,主机需要给从机一个应答信号。
代码的125行至132行,此时需要主机回复一个应答信号,所以要释放数据总线。
代码的133行至140行,是用来判别主机的应答信号是高电平还是低电平,如果是高电平,则表示此次读操作已经结束,为低电平则表示读操作还未结束。
HDMI解码封装层模块的代码如下:
- 1 module hdmi_rx(
- 2 input wire clk_10m, // 10M时钟
- 3 input wire clk_200m, // 200M参考时钟
- 4 input wire tmdsclk_p, // HDMI输入差分时钟
- 5 input wire tmdsclk_n, // HDMI输入差分时钟
- 6 input wire blue_p, // HDMI输入蓝色差分数据
- 7 input wire green_p, // HDMI输入绿色差分数据
- 8 input wire red_p, // HDMI输入红色差分数据
- 9 input wire blue_n, // HDMI输入蓝色差分数据
- 10 input wire green_n, // HDMI输入绿色差分数据
- 11 input wire red_n, // HDMI输入红色差分数据
- 12 input wire rst_n, // 复位信号,低有效
- 13
- 14 output wire reset, // RX端复位信号
- 15 output wire pclk, // 像素点采样时钟
- 16 output wire pclkx5, // 像素点5倍采样时钟
- 17 output wire hsync, // 行信号
- 18 output wire vsync, // 场信号
- 19 output wire de, // 数据使能
- 20 output wire [23:0] rgb_data, // 像素数据
- 21 output wire hdmi_in_en, // 输入输出使能信号,0代表输入,1代表输出
- 22 output reg hdmi_in_hpd // 热插拔信号
- 23 );
- 24
- 25
- 26 //wire define
- 27 wire [7:0] red; //红色像素数据
- 28 wire [7:0] green; //绿色像素数据
- 29 wire [7:0] blue; //蓝色像素数据
- 30
- 31 //*****************************************************
- 32 //** main code
- 33 //*****************************************************
- 34
- 35 assign rgb_data = {red,green,blue};
- 36 assign hdmi_in_en = 1'b0;
- 37
- 38 //热插拔信号
- 39 always@(posedge clk_10m or negedge rst_n)begin
- 40 if( rst_n == 1'b0)
- 41 hdmi_in_hpd <= 1'b0;
- 42 else
- 43 hdmi_in_hpd <= 1'b1;
- 44 end
- 45
- 46 //hdmi解码模块
- 47 dvi_decoder u_dvi_decoder(
- 48 //input
- 49 .clk_200m (clk_200m),
- 50 .tmdsclk_p (tmdsclk_p), // tmds clock
- 51 .tmdsclk_n (tmdsclk_n), // tmds clock
- 52 .blue_p (blue_p), // Blue data in
- 53 .green_p (green_p), // Green data in
- 54 .red_p (red_p ), // Red data in
- 55 .blue_n (blue_n ), // Blue data in
- 56 .green_n (green_n ), // Green data in
- 57 .red_n (red_n ), // Red data in
- 58 .exrst_n (rst_n), // external reset input, e.g. reset button
- 59 //output
- 60 .reset (reset), // rx reset
- 61 .pclk (pclk), // double rate pixel clock
- 62 .pclkx5 (pclkx5), // 10x pixel as IOCLK
- 63 .hsync (hsync), // hsync data
- 64 .vsync (vsync), // vsync data
- 65 .de (de), // data enable
- 66 .red (red), // pixel data out
- 67 .green (green), // pixel data out
- 68 .blue (blue) // pixel data out
- 69
- 70 ); // pixel data out
- 71
- 72 endmodule
复制代码
代码第35行是将8bit的红绿蓝的信号拼接成24bit的数据信号。
代码第36行是将输入输出使能信号赋0,表示这个接口是作为输入口使用。
代码第39行至44行,是对热插拔信号进行赋值,为0表示设备未连接,1表示设备连接上。
代码第47行至70行,是对hdmi解码模块的例化。
HDMI解码模块的代码如下:
- 1 module dvi_decoder (
- 2 input wire clk_200m,
- 3 input wire tmdsclk_p, // tmds clock
- 4 input wire tmdsclk_n, // tmds clock
- 5 input wire blue_p, // Blue data in
- 6 input wire green_p, // Green data in
- 7 input wire red_p, // Red data in
- 8 input wire blue_n, // Blue data in
- 9 input wire green_n, // Green data in
- 10 input wire red_n, // Red data in
- 11 input wire exrst_n, // external reset input, e.g. reset button
- 12
- 13 output reg reset, // rx reset
- 14 output wire pclk, // regenerated pixel clock
- 15 output wire pclkx5, // 5x pixel as IOCLK
- 16 output wire hsync, // hsync data
- 17 output wire vsync, // vsync data
- 18 output wire de, // data enable
- 19 output wire [7:0] red, // pixel data out
- 20 output wire [7:0] green, // pixel data out
- 21 output wire [7:0] blue // pixel data out
- 22
- 23 );
- 24
- 25 //parameter define
- 26 parameter kCtlTknCount = 128; //检测到控制字符的最低持续个数
- 27 parameter kTimeoutMs = 50; //未检测到控制字符的最大时间间隔
- 28
- 29 //wire define
- 30 wire de_b, de_g, de_r;
- 31 wire blue_rdy, green_rdy, red_rdy; //数据准备好信号
- 32 wire blue_vld, green_vld, red_vld; //数据有效信号
- 33
- 34 //*****************************************************
- 35 //** main code
- 36 //*****************************************************
- 37
- 38 assign de = de_b;
- 39
- 40 //RX端的复位信号
- 41 always @(posedge pclk ) begin
- 42 if (!exrst_n )
- 43 reset <= 1'b1;
- 44 else
- 45 reset <= ~pll_lckd;
- 46 end
- 47
- 48 //HDMI时钟模块
- 49 tmds_clock u_tmds_clock(
- 50
- 51 .tmds_clk_p (tmdsclk_p),
- 52 .tmds_clk_n (tmdsclk_n),
- 53
- 54 .pixelclk (pclk),
- 55 .serialclk (pclkx5),
- 56 .alocked (pll_lckd)
- 57 );
- 58
- 59 //HDMI红色数据解码模块
- 60 tmds_decoder #(
- 61 .kCtlTknCount (kCtlTknCount), //检测到控制字符的最低持续个数
- 62 .kTimeoutMs (kTimeoutMs), //未检测到控制字符的最大时间间隔
- 63 .kRefClkFrqMHz (200) //参考时钟频率
- 64 ) u_tmds_decoder_0(
- 65
- 66 .arst (reset),
- 67 .pixelclk (pclk),
- 68 .serialclk (pclkx5),
- 69 .refclk (clk_200m),
- 70 .prst (~exrst_n),
- 71 .sdatain_p (red_p),
- 72 .sdatain_n (red_n),
- 73 .potherchrdy ({blue_rdy,green_rdy}),
- 74 .potherchvld ({blue_vld,green_vld}),
- 75 .palignerr (),
- 76 .pc0 (),
- 77 .pc1 (),
- 78 .pmerdy (red_rdy),
- 79 .pmevld (red_vld),
- 80 .pvde (de_r),
- 81 .pdatain (red),
- 82 .peyesize ()
- 83 );
- 84
- 85 //HDMI蓝色数据解码模块
- 86 tmds_decoder #(
- 87 .kCtlTknCount (kCtlTknCount), //检测到控制字符的最低持续个数
- 88 .kTimeoutMs (kTimeoutMs), //未检测到控制字符的最大时间间隔
- 89 .kRefClkFrqMHz (200) //参考时钟频率
- 90 ) u_tmds_decoder_1(
- 91
- 92 .arst (reset),
- 93 .pixelclk (pclk),
- 94 .serialclk (pclkx5),
- 95 .refclk (clk_200m),
- 96 .prst (~exrst_n),
- 97 .sdatain_p (blue_p),
- 98 .sdatain_n (blue_n),
- 99 .potherchrdy ({red_rdy,green_rdy}),
- 100 .potherchvld ({red_vld,green_vld}),
- 101 .palignerr (),
- 102 .pc0 (hsync),
- 103 .pc1 (vsync),
- 104 .pmerdy (blue_rdy),
- 105 .pmevld (blue_vld),
- 106 .pvde (de_b),
- 107 .pdatain (blue),
- 108 .peyesize ()
- 109 );
- 110
- 111 //HDMI绿色数据解码模块
- 112 tmds_decoder #(
- 113 .kCtlTknCount (kCtlTknCount), //检测到控制字符的最低持续个数
- 114 .kTimeoutMs (kTimeoutMs), //未检测到控制字符的最大时间间隔
- 115 .kRefClkFrqMHz (200) //参考时钟频率
- 116 ) u_tmds_decoder_2(
- 117
- 118 .arst (reset),
- 119 .pixelclk (pclk),
- 120 .serialclk (pclkx5),
- 121 .refclk (clk_200m),
- 122 .prst (~exrst_n),
- 123 .sdatain_p (green_p),
- 124 .sdatain_n (green_n),
- 125 .potherchrdy ({red_rdy,blue_rdy}),
- 126 .potherchvld ({red_vld,blue_vld}),
- 127 .palignerr (),
- 128 .pc0 (),
- 129 .pc1 (),
- 130 .pmerdy (green_rdy),
- 131 .pmevld (green_vld),
- 132 .pvde (de_g),
- 133 .pdatain (green),
- 134 .peyesize ()
- 135 );
- 136
- 137 endmodule
复制代码
代码第26行,定义了一个参数kCtlTknCount,这个参数的用意是用来判断控制字符的最低持续个数,这样做是为了防止误识别。
代码第27行,定义了一个参数kTimeoutMs,这个参数的用意是当在一定时间内未检测到控制字符,则对相应的信号进行复位处理。
代码第41行至46行,是对RX端的复位信号进行赋值。这里将RX端进来的时钟信号进入MMCM后产生的lock信号取反,是为了当时钟信号不稳定或者没有的时候对RX端的信号进行复位。
代码第48行至57行,是对HDMI时钟模块的例化。
代码第60行至135行,是对HDMI数据解码模块的例化,其中只有蓝色差分对解出的行场信号是有效的,其他2个差分对解出的不是行场信号。
HDMI时钟模块的代码如下:
- 1 module tmds_clock(
- 2
- 3 input tmds_clk_p,
- 4 input tmds_clk_n,
- 5
- 6
- 7 output pixelclk,
- 8 output serialclk,
- 9 output alocked
- 10 );
- 11
- 12 //wire define
- 13 wire clk_in_hdmi_clk;
- 14 wire pixelclk;
- 15 wire serialclk;
- 16 wire alocked;
- 17 wire clkout1b_unused, clkout2_unused, clkout2b_unused;
- 18 wire clkout3_unused, clkout3b_unused, clkout4_unused;
- 19 wire clkout5_unused, clkout6_unused,drdy_unused, psdone_unused;
- 20 wire clkfbstopped_unused, clkinstopped_unused, clkfboutb_unused;
- 21 wire clkout0b_unused, clkout1_unused ;
- 22 wire [15:0] do_unused;
- 23 wire clkfbout_hdmi_clk;
- 24 wire clk_out_5x_hdmi_clk;
- 25
- 26 //*****************************************************
- 27 //** main code
- 28 //*****************************************************
- 29
- 30 IBUFDS # (
- 31 .DIFF_TERM ("FALSE"),
- 32 .IBUF_LOW_PWR ("TRUE"),
- 33 .IOSTANDARD ("TMDS_33")
- 34 ) u_IBUFDS(
- 35 .O (clk_in_hdmi_clk),
- 36 .I (tmds_clk_p),
- 37 .IB (tmds_clk_n)
- 38 );
- 39
- 40
- 41 MMCME2_ADV
- 42 #(.BANDWIDTH ("OPTIMIZED"),
- 43 .CLKOUT4_CASCADE ("FALSE"),
- 44 .COMPENSATION ("ZHOLD"),
- 45 .STARTUP_WAIT ("FALSE"),
- 46 .DIVCLK_DIVIDE (1),
- 47 .CLKFBOUT_MULT_F (5.000),
- 48 .CLKFBOUT_PHASE (0.000),
- 49 .CLKFBOUT_USE_FINE_PS ("FALSE"),
- 50 .CLKOUT0_DIVIDE_F (1.000),
- 51 .CLKOUT0_PHASE (0.000),
- 52 .CLKOUT0_DUTY_CYCLE (0.500),
- 53 .CLKOUT0_USE_FINE_PS ("FALSE"),
- 54 .CLKOUT1_DIVIDE (5),
- 55 .CLKOUT1_PHASE (0.000),
- 56 .CLKOUT1_DUTY_CYCLE (0.500),
- 57 .CLKOUT1_USE_FINE_PS ("FALSE"),
- 58 .CLKIN1_PERIOD (6.667))
- 59
- 60 mmcm_adv_inst
- 61 // Output clocks
- 62 (
- 63 .CLKFBOUT (clkfbout_hdmi_clk),
- 64 .CLKFBOUTB (clkfboutb_unused),
- 65 .CLKOUT0 (clk_out_5x_hdmi_clk),
- 66 .CLKOUT0B (clkout0b_unused),
- 67 .CLKOUT1 (clk_out_1x_hdmi_clk),
- 68 .CLKOUT1B (clkout1b_unused),
- 69 .CLKOUT2 (clkout2_unused),
- 70 .CLKOUT2B (clkout2b_unused),
- 71 .CLKOUT3 (clkout3_unused),
- 72 .CLKOUT3B (clkout3b_unused),
- 73 .CLKOUT4 (clkout4_unused),
- 74 .CLKOUT5 (clkout5_unused),
- 75 .CLKOUT6 (clkout6_unused),
- 76 // Input clock control
- 77 .CLKFBIN (clkfbout_hdmi_clk),
- 78 .CLKIN1 (clk_in_hdmi_clk),
- 79 .CLKIN2 (1'b0),
- 80 // Tied to always select the primary input clock
- 81 .CLKINSEL (1'b1),
- 82 // Ports for dynamic reconfiguration
- 83 .DADDR (7'h0),
- 84 .DCLK (1'b0),
- 85 .DEN (1'b0),
- 86 .DI (16'h0),
- 87 .DO (do_unused),
- 88 .DRDY (drdy_unused),
- 89 .DWE (1'b0),
- 90 // Ports for dynamic phase shift
- 91 .PSCLK (1'b0),
- 92 .PSEN (1'b0),
- 93 .PSINCDEC (1'b0),
- 94 .PSDONE (psdone_unused),
- 95 // Other control and status signals
- 96 .LOCKED (alocked),
- 97 .CLKINSTOPPED (clkinstopped_unused),
- 98 .CLKFBSTOPPED (clkfbstopped_unused),
- 99 .PWRDWN (1'b0),
- 100 .RST (0));
- 101
- 102 // 5x fast serial clock
- 103 BUFG u_BUFG(
- 104 .O (serialclk), // 1-bit output: Clock output (connect to I/O clock loads).
- 105 .I (clk_out_5x_hdmi_clk) // 1-bit input: Clock input (connect to an IBUF or BUFMR).
- 106 );
- 107
- 108 BUFG u_BUFG_0(
- 109 .O (pixelclk), // 1-bit output: Clock output (connect to I/O clock loads).
- 110 .I (clk_out_1x_hdmi_clk) // 1-bit input: Clock input (connect to an IBUF or BUFMR).
- 111 );
- 112
- 113
- 114 endmodule
复制代码
代码第30行至38行,是通过IBUFDS将差分时钟转换为单端时钟。
代码第41行至100行,是通过MMCM产生一个和输入时钟同频的和5倍频的时钟,以提供给其他模块使用。代码第58行设置了一个输入时钟的周期,即频率为150MHz,所以本实验最大支持的分辨率为1080P。
代码第103行至111行,是将MMCM产生的时钟加入全局时钟网络,以减小延迟和抖动。
本次实验的HDMI数据解码模块包含以下四个模块,框图如下:
图 42.4.6 HDMI数据解码模块框图
串并转化模块模块:将进来的串行差分数据转化为串行单端数据,然后把串行单端数据转化为10bit的并行数据。
字对齐校准模块:将10bit的并行数据与4个控制字符做对比,如果连续在16个延迟值都能检测到控制字符,则认为已经找到延迟值的范围,此时取中间的延迟值做为最终的值,表示校准已经完成。
数据同步模块:将红绿蓝三组的数据进行同步,防止图像的颜色出现偏差。
8b/10b解码模块:将并行的10bit数据转化为8bit数据,同时产生行场信号和数据有效使能。
由系统框图可知,HDMI数据解码模块主要例化了以下四个模块:串并转化模块模块(selectio_1_10)、字对齐校准模块(phasealign)、数据同步模块(channelbond)和8b/10b解码模块(decoder)。各模块端口及信号连接如下图所示:
图 42.4.7 HDMI数据解码模块原理图
HDMI数据解码模块将输入的串行差分信号转化为并行数据,再通过字对齐校准模块确定延迟值,然后通过数据同步模块同步三组颜色数据,最后经过8b/10b解码模块输出8bit颜色数据和行场信号和数据有效使能。
HDMI数据解码模块的代码如下:
- 1 module tmds_decoder #(
- 2 parameter kCtlTknCount = 128, //检测到控制字符的最低持续个数
- 3 parameter kTimeoutMs = 50, //未检测到控制字符的最大时间间隔
- 4 parameter kRefClkFrqMHz = 200 //参考时钟频率
- 5 )(
- 6 input arst ,
- 7 input pixelclk , //TMDS clock x1 (CLKDIV)
- 8 input serialclk , //TMDS clock x5 (CLK)
- 9 input refclk , //200 MHz reference clock
- 10 input prst , //Synchronous reset to restart lock procedure
- 11 input sdatain_p , //TMDS data channel positive
- 12 input sdatain_n , //TMDS data channel negative
- 13 input [1:0] potherchrdy , //另外2组数据同步完成信号
- 14 input [1:0] potherchvld , //另外2组数据校准完成信号
- 15 output palignerr , //校准错误信号
- 16 output pc0 , //控制信号
- 17 output pc1 , //控制信号
- 18 output pmerdy , //数据同步完成信号
- 19 output pmevld , //数据校准完成信号
- 20 output pvde , //数据有效使能
- 21 output [7:0] pdatain , //解码后8bit颜色数据
- 22 output [4:0] peyesize //连续监测到控制字符的延迟值的次数
- 23
- 24 );
- 25
- 26 //wire define
- 27 wire pc0; //控制信号
- 28 wire pc1; //控制信号
- 29 wire pvde; //数据有效使能
- 30 wire [7:0] pdatain; //解码后的8bit颜色数据
- 31 wire [4:0] peyesize; //连续监测到控制字符的延迟值的次数
- 32 wire [9:0] pdatainbnd; //同步后的10bit并行数据
- 33 wire [9:0] pdatainraw; //转化后的10bit并行数据
- 34 wire pmerdy_int; //数据同步完成信号
- 35 wire paligned; //数据校准完成信号
- 36 wire palignerr_int; //校准错误信号
- 37 wire pidly_ld; //IDELAYE控制信号
- 38 wire pidly_ce; //IDELAYE控制信号
- 39 wire pidly_inc; //IDELAYE控制信号
- 40 wire [4:0] pidly_cnt; //IDELAYE延迟值
- 41 wire pbitslip; //字对齐移动信号
- 42
- 43 //*****************************************************
- 44 //** main code
- 45 //*****************************************************
- 46
- 47 assign palignerr = palignerr_int; //校准错误信号
- 48 assign pmevld = paligned; //数据校准完成信号
- 49 assign pmerdy = pmerdy_int; //数据同步完成信号
- 50
- 51 //8b/10b解码模块
- 52 decoder u_decoder(
- 53 .pixelclk (pixelclk),
- 54 .pdatainbnd (pdatainbnd),
- 55 .potherchrdy (potherchrdy),
- 56 .pmerdy_int (pmerdy_int),
- 57
- 58 .pc0 (pc0),
- 59 .pc1 (pc1),
- 60 .pvde (pvde),
- 61 .pdatain (pdatain)
- 62 );
- 63
- 64 //串并转化模块模块
- 65 selectio_1_10 #(
- 66 .SYS_W (1),
- 67 .DEV_W (10)
- 68 )u_selectio_1_10(
- 69 .clk_div_in (pixelclk),
- 70 .clk_in (serialclk),
- 71 .data_in_from_pins_p (sdatain_p),
- 72 .data_in_from_pins_n (sdatain_n),
- 73 .ref_clock (refclk),
- 74 //Encoded parallel data (raw)
- 75 .data_in_to_device (pdatainraw),
- 76 //Control for phase alignment
- 77 .bitslip (pbitslip),
- 78 .in_delay_ld (pidly_ld),
- 79 .in_delay_data_ce (pidly_ce),
- 80 .in_delay_data_inc (pidly_inc),
- 81 .in_delay_data_cnt (pidly_cnt),
- 82 .io_reset (arst)
- 83
- 84 );
- 85
- 86 //字对齐校准模块
- 87 phasealign #(
- 88 .kTimeoutMs (kTimeoutMs),
- 89 .kRefClkFrqMHz (kRefClkFrqMHz),
- 90 .kCtlTknCount (kCtlTknCount)
- 91 )u_phasealign(
- 92 .prst (prst),
- 93 .arst (arst),
- 94 .pixelclk (pixelclk),
- 95 .refclk (refclk),
- 96 .pdata (pdatainraw),
- 97 .pidly_ld (pidly_ld),
- 98 .pidly_ce (pidly_ce),
- 99 .pidly_inc (pidly_inc),
- 100 .pidly_cnt (pidly_cnt),
- 101 .pbitslip (pbitslip),
- 102 .paligned (paligned),
- 103 .perror (palignerr_int),
- 104 .peyesize (peyesize)
- 105 );
- 106
- 107 //数据同步模块
- 108 channelbond u_channelbond(
- 109 .clk (pixelclk),
- 110 .rawdata (pdatainraw),
- 111 .iamvld (paligned),
- 112 .other_ch0_vld (potherchvld[0]),
- 113 .other_ch1_vld (potherchvld[1]),
- 114 .other_ch0_rdy (potherchrdy[0]),
- 115 .other_ch1_rdy (potherchrdy[1]),
- 116 .iamrdy (pmerdy_int),
- 117 .sdata (pdatainbnd)
- 118 );
- 119
- 120 endmodule
复制代码
代码第52行至62行,是对8b/10b解码模块的例化。该模块主要是将并行的10bit数据转化为8bit数据,同时产生行场信号和数据有效使能。
代码第65行至84行,是对串并转化模块模块的例化。该模块主要是将进来的串行差分数据转化为串行单端数据,然后把串行单端数据转化为10bit的并行数据。其中参数SYS_W表示差分对个数,这里设为1表示只有1对差分对;参数DEV_W表示并串的比例,这里设为10表示并串比为10 :1。
代码第87行至105行,是对字对齐校准模块的例化。该模块主要是将10bit的并行数据与4个控制字符做对比,如果连续在16个延迟值都能检测到控制字符,则认为已经找到延迟值的范围,此时取中间的延迟值做为最终的值,表示校准已经完成。
代码第108行至118行,是对数据同步模块的例化。该模块主要是将红绿蓝三组的数据进行同步,防止图像的颜色出现偏差。
串并转化模块模块的代码如下:
- 1 module selectio_1_10
- 2 // width of the data for the system
- 3 #(parameter SYS_W = 1,
- 4 // width of the data for the device
- 5 parameter DEV_W = 10)
- 6 (
- 7 // From the system into the device
- 8 input [SYS_W-1:0] data_in_from_pins_p, //差分数据输入
- 9 input [SYS_W-1:0] data_in_from_pins_n,
- 10 output [DEV_W-1:0] data_in_to_device, //10bit并行数据输出
- 11 input in_delay_ld, //加载寄存器的延迟值
- 12 input [SYS_W -1 :0] in_delay_data_ce, //调整延迟值的有效使能
- 13 input [SYS_W -1 :0] in_delay_data_inc, //增减延迟值
- 14
- 15 input ref_clock, //200M参考时钟
- 16 input [SYS_W-1:0] bitslip, //字对齐调整信号
- 17 output [4:0] in_delay_data_cnt, //当前延迟值
- 18
- 19 input clk_in, //5倍像素时钟
- 20 input clk_div_in, //1倍像素时钟
- 21 input io_reset //io的复位
- 22 );
- 23
- 24 localparam num_serial_bits = DEV_W/SYS_W;
- 25
- 26
- 27 wire [SYS_W-1:0] data_in_from_pins_int;
- 28 wire [SYS_W-1:0] data_in_from_pins_delay;
- 29 wire [SYS_W-1:0] delay_data_busy;
- 30 wire [SYS_W-1:0] in_delay_ce;
- 31 wire [SYS_W-1:0] in_delay_inc_dec;
- 32 wire ref_clock_bufg;
- 33 wire [SYS_W-1:0] iserdes_q[0:13];
- 34
- 35 assign in_delay_ce = { in_delay_data_ce[0]};
- 36 assign in_delay_inc_dec = { in_delay_data_inc[0]};
- 37
- 38 // We have multiple bits- step over every bit, instantiating the required elements
- 39 genvar pin_count;
- 40 genvar slice_count;
- 41 generate for (pin_count = 0; pin_count < SYS_W; pin_count = pin_count + 1) begin: pins
- 42 // Instantiate the buffers
- 43 ////------------------------------
- 44 // Instantiate a buffer for every bit of the data bus
- 45 IBUFDS
- 46 #(.DIFF_TERM ("FALSE"), // Differential termination
- 47 .IOSTANDARD ("TMDS_33"))
- 48 ibufds_inst
- 49 (.I (data_in_from_pins_p [pin_count]),
- 50 .IB (data_in_from_pins_n [pin_count]),
- 51 .O (data_in_from_pins_int[pin_count]));
- 52
- 53 // Instantiate the delay primitive
- 54 ////-------------------------------
- 55
- 56 (* IODELAY_GROUP = "selectio_wiz_0_group" *)
- 57 IDELAYE2
- 58 # (
- 59 .CINVCTRL_SEL ("FALSE"), // TRUE, FALSE
- 60 .DELAY_SRC ("IDATAIN"), // IDATAIN, DATAIN
- 61 .HIGH_PERFORMANCE_MODE ("FALSE"), // TRUE, FALSE
- 62 .IDELAY_TYPE ("VARIABLE"), // FIXED, VARIABLE, or VAR_LOADABLE
- 63 .IDELAY_VALUE (0), // 0 to 31
- 64 .REFCLK_FREQUENCY (200.0),
- 65 .PIPE_SEL ("FALSE"),
- 66 .SIGNAL_PATTERN ("DATA")) // CLOCK, DATA
- 67 idelaye2_bus
- 68 (
- 69 .DATAOUT (data_in_from_pins_delay[pin_count]),
- 70 .DATAIN (1'b0),
- 71 .C (clk_div_in),
- 72 .CE (in_delay_ce[pin_count]),
- 73 .INC (in_delay_inc_dec[pin_count]),
- 74 .IDATAIN (data_in_from_pins_int [pin_count]),
- 75 .LD (in_delay_ld),
- 76 .REGRST (io_reset),
- 77 .LDPIPEEN (1'b0),
- 78 .CNTVALUEIN (5'b00000),
- 79 .CNTVALUEOUT (in_delay_data_cnt),
- 80 .CINVCTRL (1'b0)
- 81 );
- 82
- 83 // local wire only for use in this generate loop
- 84 wire cascade_shift;
- 85 wire [SYS_W-1:0] icascade1;
- 86 wire [SYS_W-1:0] icascade2;
- 87 wire clk_in_int_inv;
- 88
- 89 assign clk_in_int_inv = ~ clk_in;
- 90
- 91 // declare the iserdes
- 92 ISERDESE2
- 93 # (
- 94 .DATA_RATE ("DDR"),
- 95 .DATA_WIDTH (10),
- 96 .INTERFACE_TYPE ("NETWORKING"),
- 97 .DYN_CLKDIV_INV_EN ("FALSE"),
- 98 .DYN_CLK_INV_EN ("FALSE"),
- 99 .NUM_CE (2),
- 100 .OFB_USED ("FALSE"),
- 101 .IOBDELAY ("IFD"),
- 102 .SERDES_MODE ("MASTER"))
- 103 iserdese2_master (
- 104 .Q1 (iserdes_q[0][pin_count]),
- 105 .Q2 (iserdes_q[1][pin_count]),
- 106 .Q3 (iserdes_q[2][pin_count]),
- 107 .Q4 (iserdes_q[3][pin_count]),
- 108 .Q5 (iserdes_q[4][pin_count]),
- 109 .Q6 (iserdes_q[5][pin_count]),
- 110 .Q7 (iserdes_q[6][pin_count]),
- 111 .Q8 (iserdes_q[7][pin_count]),
- 112 .SHIFTOUT1 (icascade1[pin_count]),
- 113 .SHIFTOUT2 (icascade2[pin_count]),
- 114 .BITSLIP (bitslip[pin_count]),
- 115
- 116 .CE1 (1'b1),
- 117 .CE2 (1'b1),
- 118 .CLK (clk_in),
- 119 .CLKB (clk_in_int_inv),
- 120 .CLKDIV (clk_div_in),
- 121 .CLKDIVP (1'b0),
- 122 .D (1'b0),
- 123 .DDLY (data_in_from_pins_delay[pin_count]),
- 124 .RST (io_reset),
- 125 .SHIFTIN1 (1'b0),
- 126 .SHIFTIN2 (1'b0),
- 127 // unused connections
- 128 .DYNCLKDIVSEL (1'b0),
- 129 .DYNCLKSEL (1'b0),
- 130 .OFB (1'b0),
- 131 .OCLK (1'b0),
- 132 .OCLKB (1'b0),
- 133 .O ());
- 134
- 135 ISERDESE2
- 136 # (
- 137 .DATA_RATE ("DDR"),
- 138 .DATA_WIDTH (10),
- 139 .INTERFACE_TYPE ("NETWORKING"),
- 140 .DYN_CLKDIV_INV_EN ("FALSE"),
- 141 .DYN_CLK_INV_EN ("FALSE"),
- 142 .NUM_CE (2),
- 143 .OFB_USED ("FALSE"),
- 144 .IOBDELAY ("IFD"),
- 145 .SERDES_MODE ("SLAVE"))
- 146 iserdese2_slave (
- 147 .Q1 (),
- 148 .Q2 (),
- 149 .Q3 (iserdes_q[8][pin_count]),
- 150 .Q4 (iserdes_q[9][pin_count]),
- 151 .Q5 (iserdes_q[10][pin_count]),
- 152 .Q6 (iserdes_q[11][pin_count]),
- 153 .Q7 (iserdes_q[12][pin_count]),
- 154 .Q8 (iserdes_q[13][pin_count]),
- 155 .SHIFTOUT1 (),
- 156 .SHIFTOUT2 (),
- 157 .SHIFTIN1 (icascade1[pin_count]),
- 158 .SHIFTIN2 (icascade2[pin_count]),
- 159 .BITSLIP (bitslip[pin_count]),
- 160
- 161 .CE1 (1'b1),
- 162 .CE2 (1'b1),
- 163 .CLK (clk_in),
- 164 .CLKB (clk_in_int_inv),
- 165 .CLKDIV (clk_div_in),
- 166 .CLKDIVP (1'b0),
- 167 .D (1'b0),
- 168 .DDLY (1'b0),
- 169 .RST (io_reset),
- 170 // unused connections
- 171 .DYNCLKDIVSEL (1'b0),
- 172 .DYNCLKSEL (1'b0),
- 173 .OFB (1'b0),
- 174 .OCLK (1'b0),
- 175 .OCLKB (1'b0),
- 176 .O ());
- 177 ////---------------------------------------------------------
- 178 for (slice_count = 0; slice_count < num_serial_bits;
- 179 slice_count = slice_count + 1) begin: in_slices
- 180 assign data_in_to_device[slice_count] =
- 181 iserdes_q[num_serial_bits-slice_count-1];
- 182 end
- 183 end
- 184 endgenerate
- 185
- 186 // IDELAYCTRL is needed for calibration
- 187 (* IODELAY_GROUP = "selectio_wiz_0_group" *)
- 188 IDELAYCTRL
- 189 delayctrl (
- 190 .RDY (delay_locked),
- 191 .REFCLK (ref_clock),
- 192 .RST (io_reset));
- 193
- 194 endmodule
复制代码
串并转化模块模块主要是将进来的串行差分数据转化为并行数据,其运用了xilinx提供的底层原语ISERDESE、IDELAYE、IDELAYCTRL和IBUFDS。
IBUFDS原语的作用是将进来的差分信号转化为单端信号。
IDELAYCTRL原语与IDELAYE是绑定的,使用了IDELAYE原语就必须要用IDELAYCTRL原语。IDELAYE原语的作用是将进来的信号进行延时,然后输出给其他原语使用。
下面来讲解IDELAYE原语中几个参数的作用。
(1)CINVCTRL_SEL:切换时钟C的极性,TRUE表示切换,FALSE表示不切换;
(2)DELAY_SRC:选择串行数据的输入方式,选择IDATAIN就从IDATAIN接口输入,选择DATAIN就从DATAIN接口输入;
(3)HIGH_PERFORMANCE_MODE:选择性能模式,TRUE表示减小输出抖动,FALSE表示不减小输出抖动;
(4)IDELAY_TYPE:选择延迟模式,FIXED表示设定一个固定的延迟值,VARIABLE是可以实时调整延迟值,VAR_LOADABLE即可以实时调整延迟值,还可以将CNTVALUEIN接口的值作为延迟值,VAR_LOAD_PIPE即可以实时调整延迟值,也可以存储CNTVALUEIN的值;
(5)IDELAY_VALUE:固定延迟值,只在FIXED模式有效;
(6)REFCLK_FREQUENCY:参考时钟频率,本次实验设为200MHz;
(7)PIPE_SEL:选择pipeline模式,只有在VAR_LOAD_PIPE中可以选择TRUE,其他模式都选FALSE;
(8)SIGNAL_PATTERN:输出信号的类型,CLOCK表示需要延迟的是时钟信号,DATA表示需要延迟的是数据信号。
本次实验中选择的是VARIABLE模式,即实时调整延迟值,而延迟值的变化与下面几个参数有关,如下图所示:
图 42.4.8 延时值变化表
ISERDESE原语的作用是将串行数据转化为并行数据,其示意图如下所示:
图 42.4.9 ISERDESE示意图
通过示意图可以发现一个ISERDESE最多能转化8bit的并行数据,而本次实验需要转化10bit的并行数据,所以需要2个ISERDESE级联,其级联的示意图如下所示:
图 42.4.10 ISERDESE级联示意图
这里有一个地方大家需要注意,xilinx的ISERDESE最多只能级联2个,转化的最大并行数据的位宽为14位。
看到这里大家可能有个疑惑,那就是ISERDESE是怎么知道数据的高低位从而生成并行数据。说到这里就要说下字对齐调整信号(bitslip),这个信号就是调整数据的高低位顺序的,调整的示意图如下所示:
图 42.4.11 字对齐调整示意图
从上图可以看出当bitslip每拉高一次,输出的并行数据的顺序就变化一次,因此在这所有组合的并行数据中必然有大家所需要的并行数据。
字对齐校准模块的代码如下:
- 1 module phasealign #(
- 2 parameter kTimeoutMs = 50,
- 3 parameter kRefClkFrqMHz = 200,
- 4 parameter kCtlTknCount = 128
- 5 )(
- 6
- 7 input prst ,
- 8 input arst ,
- 9 input refclk ,
- 10 input pixelclk ,
- 11 output pbitslip ,
- 12 input [9:0] pdata ,
- 13 output pidly_ld ,
- 14 output pidly_ce ,
- 15 output pidly_inc ,
- 16 input [4:0] pidly_cnt ,
- 17 output paligned ,
- 18 output perror ,
- 19 output [4:0] peyesize
- 20 );
- 21
- 22 //parameter define
- 此处省略一段代码。
- 300 always@(*)begin
- 301 case(pstate)
- 302 ResetSt: begin
- 303 pStateNxt <= IdleSt;
- 304 end
- 305 IdleSt: begin
- 306 if( pblank_begin)
- 307 pStateNxt <= TokenSt;
- 308 else if(ptimeoutovf)
- 309 pStateNxt <= JtrZoneSt;
- 310 else
- 311 pStateNxt <= IdleSt;
- 312 end
- 313 TokenSt: begin
- 314 if( !ptkn_flagq)
- 315 pStateNxt <= IdleSt;
- 316 else if(pctltkn_ovf)
- 317 pStateNxt <= EyeOpenSt;
- 318 else
- 319 pStateNxt <= TokenSt;
- 320 end
- 321 JtrZoneSt: begin
- 322 if(pfoundeye_flag)
- 323 pStateNxt <= DlyDecSt;
- 324 else
- 325 pStateNxt <= DlyIncSt;
- 326 end
- 327 EyeOpenSt: begin
- 328 if(peyeopen_cnt == kEyeOpenCntEnough)
- 329 pStateNxt <= JtrZoneSt;
- 330 else
- 331 pStateNxt <= DlyIncSt;
- 332 end
- 333 DlyIncSt: begin
- 334 pStateNxt <= DlyTstOvfSt;
- 335 end
- 336 DlyTstOvfSt:begin
- 337 if(pdelaywait_ovf)
- 338 if(pdelay_ovf)
- 339 pStateNxt <= AlignErrorSt;
- 340 else
- 341 pStateNxt <= IdleSt;
- 342 else
- 343 pStateNxt <= DlyTstOvfSt;
- 344 end
- 345 DlyDecSt: begin
- 346 pStateNxt <= DlyTstCenterSt;
- 347 end
- 348 DlyTstCenterSt:begin
- 349 if(pdelaywait_ovf)
- 350 if(pdelay_center)
- 351 pStateNxt <= AlignedSt;
- 352 else
- 353 pStateNxt <= DlyDecSt;
- 354 else
- 355 pStateNxt <= DlyTstCenterSt;
- 356 end
- 357 AlignedSt: begin
- 358 pStateNxt <= AlignedSt;
- 359 end
- 360 AlignErrorSt: begin
- 361 pStateNxt <= AlignErrorSt;
- 362 end
- 363 default:begin
- 364 pStateNxt <= IdleSt;
- 365
- 366 end
- 367 endcase
- 368 end
- 369
- 370 syncbase #(
- 371 .kResetTo (0)
- 372 )u_syncbaseovf(
- 373 .areset (arst),
- 374 .inclk (refclk),
- 375 .iin (rtimeoutovf),
- 376 .outclk (pixelclk),
- 377 .oout (ptimeoutovf)
- 378 );
- 379
- 380 syncbase #(
- 381 .kResetTo (1)
- 382 )u_syncbaserst(
- 383 .areset (arst),
- 384 .inclk (pixelclk),
- 385 .iin (ptimeoutrst),
- 386 .outclk (refclk),
- 387 .oout (rtimeoutrst)
- 388 );
- 389
- 390 endmodule
复制代码
字对齐校准模块的主要作用就是确定IDELAYE延迟值,确保ISERDESE解出正确的10bit数据。下面是字对齐校准模块的状态跳转流程图:
图 42.4.12 状态跳转流程图
(1)ResetSt:对所有的寄存器复位,使其处于初值的状态;
(2)IdleSt:信号rtimeout_cnt开始计数,如果在信号rtimeout_cnt未达到最大的时间间隔(kTimeoutEnd)就检测到控制字符,就将状态跳转到TokenSt,否则就跳转到状态JtrZoneSt;
(3)TokenSt:如果在128个时钟周期内检测到信号ptkn_flagq为低,表示在128个时钟周期内没有检测到128个控制字符,则认为此次得延时值是不稳定的,就将状态跳转到IdleSt,否则就认为此次得延时值是稳定的,状态跳转到EyeOpenSt,同时对计数器pctltkn_cnt和rtimeout_cnt进行清零;
(4)JtrZoneSt:若找到了延时区间,则下一步将状态跳转到DlyDecSt,用以确定延时区间的中间延时值,否则就认为还没有找到延时区间,将状态跳转到DlyIncSt,用以重新查找延时区间;
(5)EyeOpenSt:如果连续16次都能连续监测到128个控制字符,则说明已经确定了延时区间的范围,将状态跳转到JtrZoneSt,否则将状态跳转到DlyIncSt;
(6)DlyIncSt:之前的延时值不能完全正确的解出控制字符,所以在pidly_inc为高时拉高信号pidly_ce,使得延时值加一,直接跳转到状态DlyTstOvfSt;
(7)DlyTstOvfSt:如果信号pidly_cnt的值不为0,说明这一次的32次延时值还没有测完,将状态跳转到IdleSt,如果测试完了,将状态跳转到AlignErrorSt,表明此次的字对齐是不对的;
(8)DlyDecSt:说明延时区间已经找到了,接下来就是找到延时区间的中间延时值,之前的延时值是区间的最大值,所以将此延时值减8就是区间的中间延时值,所以在此状态当pidly_inc为低时拉高信号pidly_ce,使得延时值减一,然后将状态跳转到DlyTstCenterSt;
(9)DlyTstCenterSt:当延时值为中间延时值时,将状态跳转到AlignedSt,表明延时值已经确定,状态不需要跳转了,否则将状态跳转到DlyDecSt,使得延时值继续减一,循坏往返8次就可以得到中间延时值;
(10)AlignedSt:表明延时值已经确定,状态不需要跳转了;
(11)AlignErrorSt:表明此次的字对齐的顺序是错误的,等到信号pbitslip拉高后,将状态复位到ResetSt。
代码370行至388行的作用是起到信号跨时钟域同步的作用。波形如下图所示:
图 42.4.13 信号跨时钟域同步
数据同步模块的代码如下:
- 1 module channelbond (
- 2 input wire clk,
- 3 input wire [9:0] rawdata,
- 4 input wire iamvld,
- 5 input wire other_ch0_vld,
- 6 input wire other_ch1_vld,
- 7 input wire other_ch0_rdy,
- 8 input wire other_ch1_rdy,
- 9 output reg iamrdy,
- 10 output reg [9:0] sdata
- 11 );
- 12
- 13 //parameter define
- 14 parameter CTRLTOKEN0 = 10'b1101010100;
- 15 parameter CTRLTOKEN1 = 10'b0010101011;
- 16 parameter CTRLTOKEN2 = 10'b0101010100;
- 17 parameter CTRLTOKEN3 = 10'b1010101011;
- 18
- 19 //reg define
- 20 reg [3:0] wa; //写ram地址
- 21 reg [3:0] ra; //读ram地址
- 22 reg we; //写ram使能
- 23 reg rcvd_ctkn; //控制字符使能
- 24 reg rcvd_ctkn_d0;
- 25 reg rcvd_ctkn_pos; //控制字符上升沿
- 26 reg skip_line; //跳过当前行使能
- 27 reg rawdata_vld_d0;
- 28 reg rawdata_vld_pos; //数据全部校准完成使能上升沿
- 29 reg ra_en; //读使能
- 30
- 31 //wire define
- 32 wire rawdata_vld; //数据全部校准完成使能
- 33 wire [9:0]dpfo_dout; //ram输出数据
- 34 wire next_rcvd_ctkn_pos; //同步信号准备好信号
- 35
- 36 //*****************************************************
- 37 //** main code
- 38 //*****************************************************
- 39
- 40 assign rawdata_vld = other_ch0_vld & other_ch1_vld & iamvld;
- 41 assign next_rcvd_ctkn_pos = skip_line & rcvd_ctkn_pos;
- 42
- 43
- 44 ////////////////////////////////////////////////////////
- 45 // FIFO Write Control Logic
- 46 ////////////////////////////////////////////////////////
- 47 always @ (posedge clk) begin
- 48 we <=#1 rawdata_vld;
- 49 end
- 50
- 51 always @ (posedge clk) begin
- 52 if(rawdata_vld)
- 53 wa <=#1 wa + 1'b1;
- 54 else
- 55 wa <=#1 4'h0;
- 56 end
- 57
- 58 DRAM16XN #(.data_width(10))
- 59 cbfifo_i (
- 60 .DATA_IN(rawdata),
- 61 .ADDRESS(wa),
- 62 .ADDRESS_DP(ra),
- 63 .WRITE_EN(we),
- 64 .CLK(clk),
- 65 .O_DATA_OUT(),
- 66 .O_DATA_OUT_DP(dpfo_dout));
- 67
- 68 always @ (posedge clk) begin
- 69 sdata <=#1 dpfo_dout;
- 70 end
- 71
- 72 ////////////////////////////////////////////////////////
- 73 // FIFO read Control Logic
- 74 ////////////////////////////////////////////////////////
- 75
- 76 ////////////////////////////////
- 77 // Use blank period beginning
- 78 // as a speical marker to
- 79 // align all channel together
- 80 ////////////////////////////////
- 81
- 82 always @ (posedge clk) begin
- 83 rcvd_ctkn <=#1 ((sdata == CTRLTOKEN0) || (sdata == CTRLTOKEN1)
- 84 || (sdata == CTRLTOKEN2) || (sdata == CTRLTOKEN3));
- 85 rcvd_ctkn_d0 <=#1 rcvd_ctkn;
- 86 rcvd_ctkn_pos <=#1 !rcvd_ctkn_d0 & rcvd_ctkn;
- 87 end
- 88
- 89 /////////////////////////////
- 90 //skip the current line
- 91 /////////////////////////////
- 92
- 93 always @ (posedge clk) begin
- 94 if(!rawdata_vld)
- 95 skip_line <=#1 1'b0;
- 96 else if(rcvd_ctkn_pos)
- 97 skip_line <=#1 1'b1;
- 98 end
- 99
- 100 //////////////////////////////
- 101 //Declare my own readiness
- 102 //////////////////////////////
- 103 always @ (posedge clk) begin
- 104 if(!rawdata_vld)
- 105 iamrdy <=#1 1'b0;
- 106 else if(next_rcvd_ctkn_pos)
- 107 iamrdy <=#1 1'b1;
- 108 end
- 109
- 110 always @ (posedge clk) begin
- 111 rawdata_vld_d0 <=#1 rawdata_vld;
- 112 rawdata_vld_pos <=#1 rawdata_vld & !rawdata_vld_d0;
- 113 end
- 114
- 115 //////////////////////////////////////////////////////////////////////////////////////
- 116 // 1. FIFO flow through first when all channels are found valid(phase aligned)
- 117 // 2. When the speical marker on my channel is found, the fifo read is hold
- 118 // 3. Until the same markers are found across all three channels, the fifo read resumes
- 119 ///////////////////////////////////////////////////////////////////////////////////////
- 120
- 121 always @ (posedge clk) begin
- 122 if(rawdata_vld_pos || (other_ch0_rdy & other_ch1_rdy & iamrdy))
- 123 ra_en <=#1 1'b1;
- 124 else if(next_rcvd_ctkn_pos && !(other_ch0_rdy & other_ch1_rdy & iamrdy))
- 125 ra_en <=#1 1'b0;
- 126 end
- 127
- 128 /////////////////////////////////////////
- 129 //FIFO Read Address Counter
- 130 /////////////////////////////////////////
- 131 always @ (posedge clk) begin
- 132 if(!rawdata_vld)
- 133 ra <=#1 4'h0;
- 134 else if(ra_en)
- 135 ra <=#1 ra + 1'b1;
- 136 end
- 137
- 138 endmodule
复制代码
代码第40行信号rawdata_vld表示RGB三组数据已经全部校准完成。
代码第41行信号next_rcvd_ctkn_pos表示本组数据已经同步完成好了。
代码第47行至56行,是对ram的写使能和写地址进行操作。
代码第58行至66行,是对ram模块的例化。
代码第68行至70行,是对ram输出的数据进行打拍处理,以减小信号的扇出。
代码第82行至87行,当数据为控制字符时,拉高使能并同时获取使能的上升沿。
代码第93行至98行,当校准完成后,检测到控制字符的上升沿就拉高信号skip_line。
代码第103行至108行,当校准完成后,下一次的控制字符的上升沿就拉高信号iamrdy,表明本组数据已经同步完成。
代码第110行至113行,对信号rawdata_vld进行打拍并同时获取信号rawdata_vld的上升沿。
代码第121行至126行,当校准完成使能的上升沿到来或者3组数据全部同步完成则拉高ram的读使能,当3组数据没有全部同步完成则在控制字符的上升沿拉低ram的读使能。
代码第131行至136行,当校准未完成时,读地址一直为0,否则当读使能为高时,读地址依次累加。
8b/10b解码模块的代码如下:
- 1 module decoder(
- 2 input pixelclk,
- 3 input [9:0] pdatainbnd,
- 4 input [1:0] potherchrdy,
- 5 input pmerdy_int,
- 6
- 7 output pc0,
- 8 output pc1,
- 9 output pvde,
- 10 output [7:0] pdatain
- 11 );
- 12
- 13 //parameter define
- 14 parameter CTRLTOKEN0 = 10'b1101010100;
- 15 parameter CTRLTOKEN1 = 10'b0010101011;
- 16 parameter CTRLTOKEN2 = 10'b0101010100;
- 17 parameter CTRLTOKEN3 = 10'b1010101011;
- 18
- 19 //wire define
- 20 wire [7:0] pdatain8b;
- 21
- 22 //reg define
- 23 reg [7:0] pdatain =0;
- 24 reg pc0 =0;
- 25 reg pc1 =0;
- 26 reg pvde=0;
- 27
- 28 //*****************************************************
- 29 //** main code
- 30 //*****************************************************
- 31
- 32 assign pdatain8b = (pdatainbnd[9]) ? ~pdatainbnd[7:0] : pdatainbnd[7:0];
- 33
- 34 always @ (posedge pixelclk) begin
- 35 if(pmerdy_int && (potherchrdy == 2'b11)) begin
- 36 case (pdatainbnd)
- 37 CTRLTOKEN0: begin
- 38 pc0 <=#1 1'b0;
- 39 pc1 <=#1 1'b0;
- 40 pvde <=#1 1'b0;
- 41 end
- 42
- 43 CTRLTOKEN1: begin
- 44 pc0 <=#1 1'b1;
- 45 pc1 <=#1 1'b0;
- 46 pvde <=#1 1'b0;
- 47 end
- 48
- 49 CTRLTOKEN2: begin
- 50 pc0 <=#1 1'b0;
- 51 pc1 <=#1 1'b1;
- 52 pvde <=#1 1'b0;
- 53 end
- 54
- 55 CTRLTOKEN3: begin
- 56 pc0 <=#1 1'b1;
- 57 pc1 <=#1 1'b1;
- 58 pvde <=#1 1'b0;
- 59 end
- 60
- 61 default: begin
- 62 pdatain[0] <=#1 pdatain8b[0];
- 63 pdatain[1] <=#1 (pdatainbnd[8]) ? (pdatain8b[1] ^ pdatain8b[0])
- 64 : (pdatain8b[1] ~^ pdatain8b[0]);
- 65 pdatain[2] <=#1 (pdatainbnd[8]) ? (pdatain8b[2] ^ pdatain8b[1])
- 66 : (pdatain8b[2] ~^ pdatain8b[1]);
- 67 pdatain[3] <=#1 (pdatainbnd[8]) ? (pdatain8b[3] ^ pdatain8b[2])
- 68 : (pdatain8b[3] ~^ pdatain8b[2]);
- 69 pdatain[4] <=#1 (pdatainbnd[8]) ? (pdatain8b[4] ^ pdatain8b[3])
- 70 : (pdatain8b[4] ~^ pdatain8b[3]);
- 71 pdatain[5] <=#1 (pdatainbnd[8]) ? (pdatain8b[5] ^ pdatain8b[4])
- 72 : (pdatain8b[5] ~^ pdatain8b[4]);
- 73 pdatain[6] <=#1 (pdatainbnd[8]) ? (pdatain8b[6] ^ pdatain8b[5])
- 74 : (pdatain8b[6] ~^ pdatain8b[5]);
- 75 pdatain[7] <=#1 (pdatainbnd[8]) ? (pdatain8b[7] ^ pdatain8b[6])
- 76 : (pdatain8b[7] ~^ pdatain8b[6]);
- 77
- 78 pvde <=#1 1'b1;
- 79 end
- 80 endcase
- 81 end
- 82 else begin
- 83 pc0 <= 1'b0;
- 84 pc1 <= 1'b0;
- 85 pvde <= 1'b0;
- 86 pdatain <= 8'h0;
- 87 end
- 88 end
- 89
- 90 endmodule
复制代码
TMDS解码的原理流程图如下图所示:
图 42.4.14 解码流程图
当3组颜色数据同步完成后,将输入的10bit数据先与4个控制字符做比较。如果与控制信号相同则产生pc0和pc1这2个控制信号,否则就进行8b/10b的解码过程,具体的解码过程如上图所示。
42.5下载验证
编译完工程之后我们就可以开始下载程序了。将HDMI电缆一端连接到开发板的HDMI_A插座一端连接到电脑主机,并将HDMI电缆一端连接到开发板上的HDMI_B插座另一端连接到显示器。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。
图 42.5.1 达芬奇开发板硬件连接
接下来我们下载程序,验证HDMI输入输出环回功能。下载完成后观察HDMI显示器显示的图案如下图所示,说明HDMI输入输出环回程序下载验证成功。
图 42.5.2 HDMI实时显示图像
|
阿莫论坛20周年了!感谢大家的支持与爱护!!
如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。
如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》
|