一、初衷
HDMI是之前项目的一部分,本来不打算上传博客的,最近我们专业课设开始,恰好可以选择完成HDMI显示,也重新整理一下之前的项目,对于课设,这只是实现了基本功能,附加条件等过几天再更;希望这篇博客能帮助更多的初学者,结尾附代码;
二、开发平台
这个项目开发板我使用的是Xilinx的zynq-7030,读者可进行更改管脚进行更换;调用了vivado的pll以及ROM的IP;读者需要进行配置,在此,我就不做介绍,请自行百度;
三、项目介绍
先将图片转为coe文件存入ROM中,从ROM中读取24位真彩数据通过HDMI进行显示,显示的大小设置为1280*1024,如下所示:
图片大小为500*500;读者可根据需要自行更改;
由于我正在一家公司实习,开发板无法使用,故没有展示环节,代码曾经下过开发板了,可以正常使用;故只提供仿真波形,如果有问题可以留言,我看到就会回复;
四、HDMI接口
英文全称:H-High,D-Definition,M-Multimedia,I-Interface;高清晰度多媒体接口。
HDMI的数据传输有TMDS0,TMDS1,TMDS2三个通道,每个通道的传输流程都是一样的:
如上图所示,发送端将8bit的数据进入TMDS编码器,得到抗干扰性强的10bit TMDS信号,然后再进行串行化输出,在接收端收到串行的HDMI信号后,进行信号复原,得到10bit的TMDS信号,最后用TMDS解码器解码得到原来的8bit数据。
本项目使用的是1.3版本的HDMI接口,它支持的最大速率为10.2Gbps,而1.3版本的一个HDMI包括3个TMDS数据通道和1个TMDS时钟通道。什么是TMDS呢?
——TMDS(Transition Minimized Differential signal),最小化传输差分信号。
下图能很好的解释差分信号的优势:
它能很好的屏蔽噪声的影响;
实际上,我们只需要完成发送端这部分,即:
第一步:将8位并行RED数据发送到TMDS Tx。
第二步:进行最小化传输处理,加上第9位,即编码过程。第9位数据称为编码位。
第三步:并/串转换。
第四步:串行转差分
对第二步进行补充:每个数据通道都通过编码算法(异或、异或非等),将8位数据转换成10位数据,前8位数据由原始信号经运算后获得,第9位指示运算的方式,第10位用来对应直流平衡。通过这种算法,会使得数据的传输和恢复更加可靠。
对传输速率及时钟的一些补充:
PCLK:像素时钟
以1920x1080p/60hz为例:1920*1080*60=124.4MHz
以1280x720p/60hz为例:1280*720*60=55.3MHz
带宽:1s内传输的数据量(bit)
4K频率需要的带宽:选最常用的3840x2160分辨率,色深的话常用的是8位,RGB三色就是24bit,我们的目标是60Hz刷新率(60fps)→→→ 3840*2160*24bit*60fps=11.94Gbps
HDMI1.3/1.4版本像素时钟高达340MHz,即最大带宽是:
→→→ 340MHz*10bit(10bit编码)*3(3个数据通道)=10.2Gbps
但是由于HDMI采用的是8bit/10bit编码方式,实际效率是理论值的80%,所以10.2Gbps能传输的最大视频带宽是10.2*0.8=8.1Gbps
借鉴:(38条消息) HDMI接口简介---分辨率 时钟频率 lane速率计算_打怪升级ing的博客-CSDN博客_hdmi带宽计算https://blog.csdn.net/weixin_42229404/article/details/80461700
则本项目
- 频率:1280*1024*60 = 78.6Mhz,为什么不一样?我觉得可能是图像无效区也应该是进去;
- 实际频率:1688*1066*60 = 107.9Mhz,与定义的相同;
- 速率:108*10*3 = 3.24Gpbs;
五、代码讲解
(1)顶层:
由于我使用的开发板输入的是差分时钟,所以输入时钟是两对;
注意观察可以发现,有个540M的时钟;由于将并行的10bit数据转换为串行的数据,时钟频率需要快10倍;但是是差分输出,所以只需要5倍时钟,即540M;
//**************************************************************************
// *** 名称 : hdmixs_top
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : top
//**************************************************************************`timescale 1ns / 1ps
module hdmixs_top(input sys_clk_n ,//差分时钟输入input sys_clk_p ,input sys_rst_n ,output tmds_clk_n,//TMDS时钟通道output tmds_clk_p,output [2:0] tmds_data_n,//TMDS数据通道output [2:0] tmds_data_p);wire rst_n ;
wire clk_out1;
wire clk_out2;
wire locked;
wire hdmi_hs;
wire hdmi_vs;
wire [10:0] w_x_pixel;
wire [10:0] w_y_pixel;
// wire [15:0] pixel_data;
wire hdmi_de;
wire [23:0] rgb_data;
wire tmds_oen;//TMDS输出使能
wire [23:0] pixel_data;assign rst_n=sys_rst_n&locked; //在保证时钟稳定的情况下,才保证正常工作
assign clk_out1 = sys_clk_n;
assign clk_out2 = sys_clk_p;clk_wiz_0 clk_div(.clk_out1 (clk_108M ), //输出108MHZ的频率.clk_out2 (clk_540M ), .reset (1'b0 ), .locked (locked ),.clk_in1_p (sys_clk_p ),.clk_in1_n (sys_clk_n )
);hdmixs_driver dri(.clk_out1 (clk_108M ),.rst_n (rst_n ),.pixel_data (pixel_data ),.hdmi_hs (hdmi_hs ),.hdmi_vs (hdmi_vs ),.hdmi_de (hdmi_de ),.vedio_rgb (rgb_data ),.x_pixel (w_x_pixel ),.y_pixel (w_y_pixel )
);hdmixs_display dis(.clk_out1 (clk_108M ),.rst_n (rst_n ),.x_pixel (w_x_pixel ),.y_pixel (w_y_pixel ),.pixel_data (pixel_data )
);rgb_to_hdmi urtg(.clk_out1 (clk_108M ), //输出108MHZ的频率.clk_out2 (clk_540M ), .rst_n (rst_n ),.hdmi_de (hdmi_de ),.pixel_data (rgb_data ),.hdmi_hs (hdmi_hs ),.hdmi_vs (hdmi_vs ),.tmds_clk_n (tmds_clk_n ),.tmds_clk_p (tmds_clk_p ),.tmds_oen (tmds_oen ),.tmds_data_n (tmds_data_n ),.tmds_data_p (tmds_data_p )
);endmodule
(2) 显示驱动设置:
此模块可以定义显示的屏幕设置,以及输出当前行列计数值和图像数据;
//**************************************************************************
// *** 名称 : hdmixs_driver
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 显示大小设置
//**************************************************************************`timescale 1ns / 1ps
module hdmixs_driver(input clk_out1 ,input rst_n ,input [23:0] pixel_data,output reg hdmi_de ,output hdmi_hs , output hdmi_vs ,output [23:0] vedio_rgb ,output [ 10:0] y_pixel , //y像素坐标output [ 10:0] x_pixel //x像素坐标);//1280*1024@60Hz 108Mhz
parameter Hor_Total_Time = 11'd1688; //
parameter Hor_Sync = 10'd112; //
parameter Hor_Back_Porch = 10'd248; //
parameter Hor_Addr_Time = 11'd1280; //
parameter Hor_Front_Porch = 10'd48; //parameter Ver_Total_Time = 11'd1066; //
parameter Ver_Sync = 10'd3; //
parameter Ver_Back_Porch = 10'd38; //
parameter Ver_Addr_Time = 11'd1024; //
parameter Ver_Front_Porch = 10'd1; // //行列计数器
reg [10:0] x_cnt;
reg [10:0] y_cnt;always@(posedge clk_out1 or negedge rst_n)beginif(!rst_n)begin x_cnt<= 11'd0;y_cnt<= 11'd0;end else if(x_cnt==Hor_Total_Time)begin x_cnt<= 11'd0;if(y_cnt==Ver_Total_Time)y_cnt<=11'd0;elsey_cnt<= y_cnt+1'b1;endelse x_cnt<= x_cnt + 1'b1;endassign hdmi_hs=(x_cnt < Hor_Sync)?1'b0:1'b1;
assign hdmi_vs=(y_cnt < Ver_Sync)?1'b0:1'b1;always@(*) //使能、以及得到像素坐标beginhdmi_de<=(x_cnt>(Hor_Sync + Hor_Back_Porch)&&x_cnt<=(Hor_Sync+Hor_Back_Porch+Hor_Addr_Time)&&y_cnt>(Ver_Sync+Ver_Back_Porch)&&y_cnt<=(Ver_Sync+Ver_Back_Porch+Ver_Addr_Time));endassign vedio_rgb=(hdmi_de)?pixel_data:24'd0;
assign x_pixel=(hdmi_de)?(x_cnt-(Hor_Sync + Hor_Back_Porch)):10'd0; //行像素坐标
assign y_pixel=(hdmi_de)?(y_cnt-(Ver_Sync + Ver_Back_Porch)):10'd0; //列像素坐标endmodule
(3)图像显示模块
此模块可以设置需要显示的图片大小及位置,并输出图像;
//**************************************************************************
// *** 名称 : hdmixs_display
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 显示图像区域设置
//**************************************************************************
`timescale 1ns / 1ps
module hdmixs_display(input wire clk_out1 ,input wire rst_n ,input wire [10:0] x_pixel ,input wire [10:0] y_pixel ,output reg [23:0] pixel_data
);parameter height = 10'd500, width = 10'd500;
parameter pos_x0 = 320, pos_y0 = 272;// picture wire rom_rd_en0; //读ROM使能信号
wire [32:0] total;
wire [23:0] rom_data0;reg [23:0] rom_addr0; //读ROM有效信号 assign total = height*width;assign rom_rd_en0 =(x_pixel > pos_x0)&&(x_pixel<=pos_x0+width)&&(y_pixel>pos_y0)&&(y_pixel<=pos_y0+height)?1'b1:1'b0; //first picture locationalways @(posedge clk_out1 or negedge rst_n)begin if(!rst_n)beginpixel_data <= 24'b0;endelse if(rom_rd_en0)pixel_data <= rom_data0;elsepixel_data <= 24'h000_000;endalways@(posedge clk_out1 or negedge rst_n)beginif(!rst_n)rom_addr0<=24'd0;else if(rom_rd_en0)beginif(rom_addr0<total-1'b1)rom_addr0<=rom_addr0+1'b1;else rom_addr0<=24'd0;endelse rom_addr0<=rom_addr0;endblk_mem_gen_0 rom1(.addra(rom_addr0),.ena(1),.clka(clk_out1),.douta(rom_data0)); endmodule
(4)rgb2hdmi模块
这个模块例化了其他两个子模块;实现了8bit/10bit,以及并行转串行;并使用原语OBUFDS进行串行转差分;
//**************************************************************************
// *** 名称 : rgb_to_hdmi
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 差分输出
//**************************************************************************`timescale 1ns / 1ps
module rgb_to_hdmi(input clk_out1 ,input clk_out2 ,input rst_n ,input hdmi_de ,input [24:0] pixel_data ,input hdmi_hs ,input hdmi_vs ,output tmds_clk_n ,output tmds_clk_p , output tmds_oen , output [2:0] tmds_data_n ,output [2:0] tmds_data_p );wire [9:0] clk_10bit;wire syn_rst;wire [9:0] encode_red;wire [9:0] encode_green;wire [9:0] encode_blue;wire [2:0] serial_data;wire serial_out_clk;assign clk_10bit=10'b11111_00000;
assign tmds_oen=1'b1;asyn_to_syn syn(
.clk(clk_out1),
.rst_n(rst_n),
.syn_rst(syn_rst)
);encoder r(.clkin(clk_out1), .rstin(syn_rst), .din(pixel_data[23:16]), .c0(1'b0), .c1(1'b0), .de(hdmi_de), .dout(encode_red)
);encoder g(.clkin(clk_out1), .rstin(syn_rst), .din(pixel_data[15:8]), .c0(1'b0), .c1(1'b0), .de(hdmi_de), .dout(encode_green)
);encoder b(.clkin(clk_out1), .rstin(syn_rst), .din(pixel_data[7:0]), .c0(hdmi_hs), .c1(hdmi_vs), .de(hdmi_de), .dout(encode_blue)
);serializer red(.paralell_data(encode_red),.paralell_clk(clk_out1),.reset(syn_rst),.serial_clk(clk_out2),.serial_data_out(serial_data[2])
);serializer green(.paralell_data(encode_green),.paralell_clk(clk_out1),.reset(syn_rst),.serial_clk(clk_out2),.serial_data_out(serial_data[1])
);serializer blue(.paralell_data(encode_blue),.paralell_clk(clk_out1),.reset(syn_rst),.serial_clk(clk_out2),.serial_data_out(serial_data[0])
);serializer clk(.paralell_data(clk_10bit),.paralell_clk(clk_out1),.reset(syn_rst),.serial_clk(clk_out2),.serial_data_out(serial_out_clk)
);OBUFDS #(.IOSTANDARD("TMDS_33"), // Specify the output I/O standard.SLEW("SLOW") // Specify the output slew rate
) TMDS0 (.O(tmds_data_p[0]), // Diff_p output (connect directly to top-level port).OB(tmds_data_n[0]), // Diff_n output (connect directly to top-level port).I(serial_data[0]) // Buffer input
);OBUFDS #(.IOSTANDARD("TMDS_33"), // Specify the output I/O standard.SLEW("SLOW") // Specify the output slew rate
) TMDS1 (.O(tmds_data_p[1]), // Diff_p output (connect directly to top-level port).OB(tmds_data_n[1]), // Diff_n output (connect directly to top-level port).I(serial_data[1]) // Buffer input
);OBUFDS #(.IOSTANDARD("TMDS_33"), // Specify the output I/O standard.SLEW("SLOW") // Specify the output slew rate
) TMDS2 (.O(tmds_data_p[2]), // Diff_p output (connect directly to top-level port).OB(tmds_data_n[2]), // Diff_n output (connect directly to top-level port).I(serial_data[2]) // Buffer input
);OBUFDS #(.IOSTANDARD("TMDS_33"), // Specify the output I/O standard.SLEW("SLOW") // Specify the output slew rate
) TMDS_clk (.O(tmds_clk_p), // Diff_p output (connect directly to top-level port).OB(tmds_clk_n), // Diff_n output (connect directly to top-level port).I(serial_out_clk) // Buffer input
);endmodule
(5)编码模块:
实现了8bit/10bit
//**************************************************************************
// *** 名称 : encoder
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 编码模块
//**************************************************************************
`timescale 1ns / 1ps
module encoder(input clkin, // pixel clock inputinput rstin, // async. reset input (active high)input [7:0] din, // data inputs: expect registeredinput c0, // c0 inputinput c1, // c1 inputinput de, // de inputoutput reg [9:0] dout // data outputs
);// Counting number of 1s and 0s for each incoming pixel// component. Pipe line the result.// Register Data Input so it matches the pipe lined adder// outputreg [3:0] n1d; //number of 1s in dinreg [7:0] din_q;always @ (posedge clkin) beginn1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];din_q <=#1 din;end///// Stage 1: 8 bit -> 9 bit// Refer to DVI 1.0 Specification, page 29, Figure 3-5///wire decision1;assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));
/*reg [8:0] q_m;always @ (posedge clkin) beginq_m[0] <=#1 din_q[0];q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1;end
*/wire [8:0] q_m;assign q_m[0] = din_q[0];assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);assign q_m[8] = (decision1) ? 1'b0 : 1'b1;/// Stage 2: 9 bit -> 10 bit// Refer to DVI 1.0 Specification, page 29, Figure 3-5/reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_malways @ (posedge clkin) beginn1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);endparameter CTRLTOKEN0 = 10'b1101010100;parameter CTRLTOKEN1 = 10'b0010101011;parameter CTRLTOKEN2 = 10'b0101010100;parameter CTRLTOKEN3 = 10'b1010101011;reg [4:0] cnt; //disparity counter, MSB is the sign bitwire decision2, decision3;assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);/// [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]/assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));// pipe line alignmentreg de_q, de_reg;reg c0_q, c1_q;reg c0_reg, c1_reg;reg [8:0] q_m_reg;always @ (posedge clkin) beginde_q <=#1 de;de_reg <=#1 de_q;c0_q <=#1 c0;c0_reg <=#1 c0_q;c1_q <=#1 c1;c1_reg <=#1 c1_q;q_m_reg <=#1 q_m;end///// 10-bit out// disparity counter///always @ (posedge clkin or posedge rstin) beginif(rstin) begindout <= 10'h0;cnt <= 5'h0;end else beginif (de_reg) beginif(decision2) begindout[9] <=#1 ~q_m_reg[8]; dout[8] <=#1 q_m_reg[8]; dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);end else beginif(decision3) begindout[9] <=#1 1'b1;dout[8] <=#1 q_m_reg[8];dout[7:0] <=#1 ~q_m_reg;cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);end else begindout[9] <=#1 1'b0;dout[8] <=#1 q_m_reg[8];dout[7:0] <=#1 q_m_reg[7:0];cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);endendend else begincase ({c1_reg, c0_reg})2'b00: dout <=#1 CTRLTOKEN0;2'b01: dout <=#1 CTRLTOKEN1;2'b10: dout <=#1 CTRLTOKEN2;default: dout <=#1 CTRLTOKEN3;endcasecnt <=#1 5'h0;endendendendmodule
(6)并行数据转串行
使用原语OSERDESE2进行串行转差分操作;
//**************************************************************************
// *** 名称 : serializer
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 并行数据转串行
//**************************************************************************`timescale 1ns / 1ps
module serializer(input reset ,input paralell_clk, //并行input [9:0] paralell_data,input serial_clk, //串行,比并行数据高output serial_data_out);wire cascade1;wire cascade2;OSERDESE2 #(.DATA_RATE_OQ("DDR"), // DDR, SDR.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR.DATA_WIDTH(10), // Parallel data width (2-8,10,14).SERDES_MODE("MASTER"), // MASTER, SLAVE.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE).TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE).TRISTATE_WIDTH(1) // 3-state converter width (1,4))OSERDESE2_MAS (.CLK(serial_clk), // 1-bit input: High speed clock.CLKDIV(paralell_clk), // 1-bit input: Divided clock.RST(reset), // 1-bit input: Reset.OCE(1'b1), // 1-bit input: Output data clock enable.OQ(serial_data_out), // 1-bit output: Data path output// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each).D1(paralell_data[0]),.D2(paralell_data[1]),.D3(paralell_data[2]),.D4(paralell_data[3]),.D5(paralell_data[4]),.D6(paralell_data[5]),.D7(paralell_data[6]),.D8(paralell_data[7]),.SHIFTOUT1(),.SHIFTOUT2(),.SHIFTIN1(cascade1), .SHIFTIN2(cascade2),.OFB(), // 1-bit output: Feedback path for data// T1 - T4: 1-bit (each) input: Parallel 3-state inputs.T1(1'b0),.T2(1'b0),.T3(1'b0),.T4(1'b0),.TBYTEIN(1'b0), // 1-bit input: Byte group tristate.TCE(1'b0), // 1-bit input: 3-state clock enable.TBYTEOUT(), // 1-bit output: Byte group tristate.TFB(), // 1-bit output: 3-state control.TQ() // 1-bit output: 3-state control);OSERDESE2 #(.DATA_RATE_OQ("DDR"), // DDR, SDR.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR.DATA_WIDTH(10), // Parallel data width (2-8,10,14).SERDES_MODE("SLAVE"), // MASTER, SLAVE.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE).TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE).TRISTATE_WIDTH(1) // 3-state converter width (1,4))OSERDESE2_SLA (.CLK(serial_clk), // 1-bit input: High speed clock.CLKDIV(paralell_clk), // 1-bit input: Divided clock.RST(reset), // 1-bit input: Reset.OCE(1'b1), // 1-bit input: Output data clock enable.OQ(), // 1-bit output: Data path output// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each).D1(1'b0),.D2(1'b0),.D3(paralell_data[8]),.D4(paralell_data[9]),.D5(1'b0),.D6(1'b0),.D7(1'b0),.D8(1'b0),.SHIFTOUT1(cascade1),.SHIFTOUT2(cascade2),.SHIFTIN1(), .SHIFTIN2(),.OFB(), // 1-bit output: Feedback path for data// T1 - T4: 1-bit (each) input: Parallel 3-state inputs.T1(1'b0),.T2(1'b0),.T3(1'b0),.T4(1'b0),.TBYTEIN(1'b0), // 1-bit input: Byte group tristate.TCE(1'b0), // 1-bit input: 3-state clock enable.TBYTEOUT(), // 1-bit output: Byte group tristate.TFB(), // 1-bit output: 3-state control.TQ() // 1-bit output: 3-state control);endmodule
(7)同步模块
对复位进行同步处理(异步复位,同步释放);
//**************************************************************************
// *** 名称 : asyn_to_syn
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 同步处理模块
//**************************************************************************
`timescale 1ns / 1ps
module asyn_to_syn(input clk,input rst_n,output syn_rst);reg rst_1;
reg rst_2;assign syn_rst=rst_2;
always @(posedge clk or negedge rst_n)beginif(!rst_n)begin rst_1<=1'b1;rst_2<=1'b1;endelsebegin rst_1<=1'b0;rst_2<=rst_1;endendendmodule
六、Testbench编写
由于使用的是差分时钟,使用需要产生一个差分时钟:
//**************************************************************************
// *** 名称 : tb_hdmixs_top
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 测试模块
//**************************************************************************
·timescale 1ns/1ps
module tb_hdmixs_top();reg sys_clk_n ;
reg sys_clk_p ;
reg sys_rst_n ;wire tmds_clk_n ;//TMDS时钟通道
wire tmds_clk_p ;
wire [2:0] tmds_data_n ;//TMDS数据通道
wire [2:0] tmds_data_p ;initialbeginsys_clk_p = 1'b0;sys_clk_n = 1'b1;sys_rst_n <= 1'b0;#20sys_rst_n <= 1'b1;endalways #2.5 sys_clk_p = ~sys_clk_p;
always #2.5 sys_clk_n = ~sys_clk_n;hdmixs_top hdmixs_top_inst(.sys_clk_n (sys_clk_n ),.sys_clk_p (sys_clk_p ),.sys_rst_n (sys_rst_n ),.tmds_clk_n (tmds_clk_n ),//TMDS时钟通道.tmds_clk_p (tmds_clk_p ),.tmds_data_n (tmds_data_n),//TMDS数据通道.tmds_data_p (tmds_data_p));endmodule
七、Vivado仿真结果与COE对比
读取的数据与COE文件一致。