本文主要是介绍FPGA实现HDMI传输(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
之前的文章简单介绍了HDMI接口、TMDS编码以及ADV611工作原理和寄存器配置,本篇博客将给出具体的代码以及板级验证结果,代码参考自米联客的教程。
一.ADV7611配置
1.i2c驱动模块
`timescale 1ns / 1psmodule uii2c#
(parameter WMEN_LEN = 8'd0, //写入字节长度parameter RMEN_LEN = 8'd0, //读出字节长度parameter CLK_DIV = 16'd499 //i2c时钟分频系数
)
(input clk_i, //系统时钟input [7:0] rd_cnt, //读数据长度,包含器件地址input iic_en, //i2c使能信号input iic_mode, //i2c工作模式input [WMEN_LEN*8-1'b1:0] wr_data, //写入数据input [7:0] wr_cnt, //写数据长度,包含器件地址output reg [RMEN_LEN*8-1'b1:0] rd_data = 0, //读取数据output reg iic_scl = 0 , //i2c时钟总线output reg iic_busy = 0, //i2c忙碌,拉高时不进行操作inout wire iic_sda, //i2c数据总线output reg sda_dg = 1'b1 //用于调试
);parameter IDLE = 4'd0; //空闲状态parameter START = 4'd1; //开始parameter W_WAIT = 4'd2; //写入数据parameter W_ACK = 4'd3; //写响应parameter R_WAIT = 4'd4; //读取数据parameter R_ACK = 4'd5; //读响应parameter STOP1 = 4'd6; //停止信号1parameter STOP2 = 4'd7; //停止信号2parameter OFFSET = CLK_DIV - CLK_DIV/4; //产生i2c时钟reg [2:0] IIC_S = 4'd0; //i2c工作状态reg [15:0] clkdiv = 16'd0; //时钟分频计数器reg scl_clk = 1'b0; //i2c工作时钟reg scl_r = 1'b1; //i2c时钟寄存器reg sda_o = 1'b0; //sda输出reg [7:0] sda_r = 8'd0; //i2c数据寄存器reg [7:0] sda_i_r = 8'd0; //i2c输入数据寄存器reg [7:0] wcnt = 8'd0; //写数据计数reg [7:0] rcnt = 8'd0; //读数据计数reg [2:0] bcnt = 3'd0; //读写字节计数reg rd_en = 1'b0; //读使能reg iic_sda_r = 1'b1; //数据总线缓存wire scl_offset = (clkdiv == OFFSET); //scl delay output to fit timingwire sda_i; //i2c数据总线输入//利用计数器产生i2c驱动时钟always@(posedge clk_i)beginif(clkdiv < CLK_DIV) clkdiv <= clkdiv + 1'b1;else beginclkdiv <= 16'd0; scl_clk <= !scl_clk;endend//三态门处理IOBUF #(.DRIVE(12), // Specify the output drive strength.IBUF_LOW_PWR("TRUE"), // Low Power - "TRUE", High Performance = "FALSE" .IOSTANDARD("DEFAULT"), // Specify the I/O standard.SLEW("SLOW") // Specify the output slew rate) IOBUF_inst (.O(sda_i), // Buffer output.IO(iic_sda), // Buffer inout port (connect directly to top-level port).I(sda_o), // Buffer input.T(sda_o) // 3-state enable input, high=input, low=output);//时钟总线输出 always @(posedge clk_i) iic_scl <= scl_offset ? scl_r : iic_scl; PULLUP PULLUP_inst (.O(iic_sda));always @(*) beginif(IIC_S == IDLE || IIC_S == STOP1 || IIC_S == STOP2)scl_r <= 1'b1;else scl_r <= ~scl_clk;end //sda output always @(*) beginif(IIC_S == START || IIC_S == STOP1 || (IIC_S == R_ACK && (rcnt != rd_cnt)))sda_o <= 1'b0;else if(IIC_S == W_WAIT)sda_o <= sda_r[7]; else sda_o <= 1'b1;end //sda输出转换always @(negedge scl_clk) beginif(IIC_S == W_ACK || IIC_S == START)beginsda_r <= wr_data[(wcnt*8) +: 8];if( rd_en ) sda_r <= {wr_data[7:1],1'b1};endelse if(IIC_S == W_WAIT)sda_r <= {sda_r[6:0],1'b1};else sda_r <= sda_r;end//sda输入转换 always @(posedge scl_clk) beginif(IIC_S == R_WAIT ||IIC_S == W_ACK ) beginsda_i_r <= {sda_i_r[6:0],sda_i};endelse if(IIC_S == R_ACK)rd_data[((rcnt-1'b1)*8) +: 8] <= sda_i_r[7:0];else if(IIC_S == IDLE)beginsda_i_r <= 8'd0;end endalways @(posedge scl_clk) iic_sda_r <= sda_i;always @(posedge clk_i) sda_dg <= sda_i;//iic状态机always @(negedge scl_clk)begincase(IIC_S) //sda = 1 scl =1IDLE://idle wait iic_en == 1'b1 start trasmit rd_en == 1'b1 restart if(iic_en == 1'b1 || rd_en == 1'b1)begin iic_busy <= 1'b1; IIC_S <= START;endelse beginiic_busy <= 1'b0;wcnt <= 8'd0;rcnt <= 8'd0;rd_en <= 1'b0;endSTART:begin //sda = 0 then scl_clk =0 scl =0 generate startbcnt <= 3'd7; IIC_S <= W_WAIT;end W_WAIT://write data beginif(bcnt > 3'd0)bcnt <= bcnt - 1'b1; else beginwcnt <= wcnt + 1'b1; IIC_S <= W_ACK;endend W_ACK://write data ackbegin if(wcnt < wr_cnt)begin bcnt <= 3'd7;IIC_S <= W_WAIT;endelse if(rd_cnt > 3'd0)begin// read dataif(rd_en == 1'b0 && iic_mode == 1'b1)begin rd_en <= 1'b1;IIC_S <= IDLE; endelse IIC_S <= R_WAIT;bcnt <= 3'd7;endelseIIC_S <= STOP1; if(iic_sda_r == 1'b1)IIC_S <= STOP1;end R_WAIT://read databeginrd_en <= 1'b0;bcnt <= bcnt - 1'b1; if(bcnt == 3'd0)beginrcnt <= (rcnt < rd_cnt) ? (rcnt + 1'b1) : rcnt;IIC_S <= R_ACK;endendR_ACK://read date ackbeginbcnt <= 3'd7;IIC_S <= (rcnt < rd_cnt) ? R_WAIT : STOP1; end STOP1://sda = 0 scl = 1IIC_S <= STOP2;STOP2://sda = 1 scl = 1IIC_S <= IDLE; default:IIC_S <= IDLE;endcase
endendmodule
2.adv7611寄存器数据
`timescale 1ns / 1psmodule ui7611reg(input [8 :0] REG_INDEX, //对已配置寄存器计数output reg [31:0] REG_DATA, //寄存器地址+写入数据output [8 :0] REG_SIZE //需配置寄存器个数
);assign REG_SIZE = 9'd182;//-----------------------------------------------------------------
/ Config Data REG //
always@(*)
case(REG_INDEX)
//write Data Index0 : REG_DATA = {8'h98,8'hF4, 8'h80}; //Manufacturer ID Byte - High (Read only)1 : REG_DATA = {8'h98,8'hF5, 8'h7c}; //Manufacturer ID Byte - Low (Read only)2 : REG_DATA = {8'h98,8'hF8, 8'h4c}; // BIT[7]-Reset all the Reg 3 : REG_DATA = {8'h98,8'hF9, 8'h64}; //DC offset for analog process4 : REG_DATA = {8'h98,8'hFA, 8'h6c}; //COM10 : href/vsync/pclk/data reverse(Vsync H valid)5 : REG_DATA = {8'h98,8'hFB, 8'h68}; //VGA : 8'h22; QVGA : 8'h3f;6 : REG_DATA = {8'h98,8'hFD, 8'h44}; //VGA : 8'ha4; QVGA : 8'h50;7 : REG_DATA = {8'h98,8'h01, 8'h05}; //VGA : 8'h07; QVGA : 8'h03;8 : REG_DATA = {8'h98,8'h00, 8'h1F}; //VGA : 8'hf0; QVGA : 8'h78;9 : REG_DATA = {8'h98,8'h02, 8'hF7}; //HREF / 8'h8010 : REG_DATA = {8'h98,8'h03, 8'h40}; //VGA : 8'hA0; QVGA : 8'hF011 : REG_DATA = {8'h98,8'h04, 8'h42}; //VGA : 8'hF0; QVGA : 8'h7812 : REG_DATA = {8'h98,8'h05, 8'h28}; //13 : REG_DATA = {8'h98,8'h06, 8'ha7}; //14 : REG_DATA = {8'h98,8'h0b, 8'h44}; //BIT[6] : 0 :VGA; 1;QVGA15 : REG_DATA = {8'h98,8'h0C, 8'h42}; //16 : REG_DATA = {8'h98,8'h15, 8'h80}; //17 : REG_DATA = {8'h98,8'h19, 8'h8a}; //18 : REG_DATA = {8'h98,8'h33, 8'h40}; //19 : REG_DATA = {8'h98,8'h14, 8'h3f}; //20 : REG_DATA = {8'h44,8'hba, 8'h01}; //21 : REG_DATA = {8'h44,8'h7c, 8'h01}; // 22 : REG_DATA = {8'h64,8'h40, 8'h81}; //DSP_Ctrl4 :00/01 : YUV or RGB; 10 : RAW8; 11 : RAW10 23 : REG_DATA = {8'h68,8'h9b, 8'h03}; //ADI recommanded setting24 : REG_DATA = {8'h68,8'hc1, 8'h01}; //ADI recommanded setting25 : REG_DATA = {8'h68,8'hc2, 8'h01}; //ADI recommanded setting26 : REG_DATA = {8'h68,8'hc3, 8'h01}; //ADI recommanded setting27 : REG_DATA = {8'h68,8'hc4, 8'h01}; //ADI recommanded setting28 : REG_DATA = {8'h68,8'hc5, 8'h01}; //ADI recommanded setting29 : REG_DATA = {8'h68,8'hc6, 8'h01}; //ADI recommanded setting30 : REG_DATA = {8'h68,8'hc7, 8'h01}; //ADI recommanded setting31 : REG_DATA = {8'h68,8'hc8, 8'h01}; //ADI recommanded setting32 : REG_DATA = {8'h68,8'hc9, 8'h01}; //ADI recommanded settin g33 : REG_DATA = {8'h68,8'hca, 8'h01}; //ADI recommanded setting34 : REG_DATA = {8'h68,8'hcb, 8'h01}; //ADI recommanded setting35 : REG_DATA = {8'h68,8'hcc, 8'h01}; //ADI recommanded setting36 : REG_DATA = {8'h68,8'h00, 8'h00}; //Set HDMI input Port A37 : REG_DATA = {8'h68,8'h83, 8'hfe}; //terminator for Port A38 : REG_DATA = {8'h68,8'h6f, 8'h08}; //ADI recommended setting39 : REG_DATA = {8'h68,8'h85, 8'h1f}; //ADI recommended setting40 : REG_DATA = {8'h68,8'h87, 8'h70}; //ADI recommended setting41 : REG_DATA = {8'h68,8'h8d, 8'h04}; //LFG42 : REG_DATA = {8'h68,8'h8e, 8'h1e}; //HFG43 : REG_DATA = {8'h68,8'h1a, 8'h8a}; //unmute audio44 : REG_DATA = {8'h68,8'h57, 8'hda}; // ADI recommended setting45 : REG_DATA = {8'h68,8'h58, 8'h01};46 : REG_DATA = {8'h68,8'h75, 8'h10}; 47 : REG_DATA = {8'h68,8'h6c ,8'ha3};//enable manual HPA48 : REG_DATA = {8'h98,8'h20 ,8'h70};//HPD low49 : REG_DATA = {8'h64,8'h74 ,8'h00};//disable internal EDID
//edid
//0: REG_DATA = {8'h68,8'h6c ,8'ha3}; enable manual HPA
//1: REG_DATA = {8'h98,8'h20 ,8'h70};//HPD low
//2: REG_DATA = {8'h64,8'h74 ,8'h00};//disable internal EDID
//edid par50 : REG_DATA = {8'h6c,8'd0 , 8'h00};51 : REG_DATA = {8'h6c,8'd1 , 8'hFF};52 : REG_DATA = {8'h6c,8'd2 , 8'hFF};53 : REG_DATA = {8'h6c,8'd3 , 8'hFF};54 : REG_DATA = {8'h6c,8'd4 , 8'hFF};55 : REG_DATA = {8'h6c,8'd5 , 8'hFF};56 : REG_DATA = {8'h6c,8'd6 , 8'hFF};57 : REG_DATA = {8'h6c,8'd7 , 8'h00};58 : REG_DATA = {8'h6c,8'd8 , 8'h20};59 : REG_DATA = {8'h6c,8'd9 , 8'hA3};60 : REG_DATA = {8'h6c,8'd10 , 8'h29};61 : REG_DATA = {8'h6c,8'd11 , 8'h00};62 : REG_DATA = {8'h6c,8'd12 , 8'h01};63 : REG_DATA = {8'h6c,8'd13 , 8'h00};64 : REG_DATA = {8'h6c,8'd14 , 8'h00};65 : REG_DATA = {8'h6c,8'd15 , 8'h00};66 : REG_DATA = {8'h6c,8'd16 , 8'h23};67 : REG_DATA = {8'h6c,8'd17 , 8'h12};68 : REG_DATA = {8'h6c,8'd18 , 8'h01};69 : REG_DATA = {8'h6c,8'd19 , 8'h03};70 : REG_DATA = {8'h6c,8'd20 , 8'h80};71 : REG_DATA = {8'h6c,8'd21 , 8'h73};72 : REG_DATA = {8'h6c,8'd22 , 8'h41};73 : REG_DATA = {8'h6c,8'd23 , 8'h78};74 : REG_DATA = {8'h6c,8'd24 , 8'h0A};75 : REG_DATA = {8'h6c,8'd25 , 8'hF3};76 : REG_DATA = {8'h6c,8'd26 , 8'h30};77 : REG_DATA = {8'h6c,8'd27 , 8'hA7};78 : REG_DATA = {8'h6c,8'd28 , 8'h54};79 : REG_DATA = {8'h6c,8'd29 , 8'h42};80 : REG_DATA = {8'h6c,8'd30 , 8'hAA};81 : REG_DATA = {8'h6c,8'd31 , 8'h26};82 : REG_DATA = {8'h6c,8'd32 , 8'h0F};83 : REG_DATA = {8'h6c,8'd33 , 8'h50};84 : REG_DATA = {8'h6c,8'd34 , 8'h54};85 : REG_DATA = {8'h6c,8'd35 , 8'h25};86 : REG_DATA = {8'h6c,8'd36 , 8'hC8};87 : REG_DATA = {8'h6c,8'd37 , 8'h00};88 : REG_DATA = {8'h6c,8'd38 , 8'h61};89 : REG_DATA = {8'h6c,8'd39 , 8'h4F};90 : REG_DATA = {8'h6c,8'd40 , 8'h01};91 : REG_DATA = {8'h6c,8'd41 , 8'h01};92 : REG_DATA = {8'h6c,8'd42 , 8'h01};93 : REG_DATA = {8'h6c,8'd43 , 8'h01};94 : REG_DATA = {8'h6c,8'd44 , 8'h01};95 : REG_DATA = {8'h6c,8'd45 , 8'h01};96 : REG_DATA = {8'h6c,8'd46 , 8'h01};97 : REG_DATA = {8'h6c,8'd47 , 8'h01};98 : REG_DATA = {8'h6c,8'd48 , 8'h01};99 : REG_DATA = {8'h6c,8'd49 , 8'h01};100 : REG_DATA = {8'h6c,8'd50 , 8'h01};101 : REG_DATA = {8'h6c,8'd51 , 8'h01};102 : REG_DATA = {8'h6c,8'd52 , 8'h01};103 : REG_DATA = {8'h6c,8'd53 , 8'h01};104 : REG_DATA = {8'h6c,8'd54 , 8'h02};105 : REG_DATA = {8'h6c,8'd55 , 8'h3A};106 : REG_DATA = {8'h6c,8'd56 , 8'h80};107 : REG_DATA = {8'h6c,8'd57 , 8'h18};108 : REG_DATA = {8'h6c,8'd58 , 8'h71};109 : REG_DATA = {8'h6c,8'd59 , 8'h38};110 : REG_DATA = {8'h6c,8'd60 , 8'h2D};111 : REG_DATA = {8'h6c,8'd61 , 8'h40};112 : REG_DATA = {8'h6c,8'd62 , 8'h58};113 : REG_DATA = {8'h6c,8'd63 , 8'h2C};114 : REG_DATA = {8'h6c,8'd64 , 8'h45};115 : REG_DATA = {8'h6c,8'd65 , 8'h00};116 : REG_DATA = {8'h6c,8'd66 , 8'h80};117 : REG_DATA = {8'h6c,8'd67 , 8'h88};118 : REG_DATA = {8'h6c,8'd68 , 8'h42};119 : REG_DATA = {8'h6c,8'd69 , 8'h00};120 : REG_DATA = {8'h6c,8'd70 , 8'h00};121 : REG_DATA = {8'h6c,8'd71 , 8'h1E};122 : REG_DATA = {8'h6c,8'd72 , 8'h8C};123 : REG_DATA = {8'h6c,8'd73 , 8'h0A};124 : REG_DATA = {8'h6c,8'd74 , 8'hD0};125 : REG_DATA = {8'h6c,8'd75 , 8'h8A};126 : REG_DATA = {8'h6c,8'd76 , 8'h20};127 : REG_DATA = {8'h6c,8'd77 , 8'hE0};128 : REG_DATA = {8'h6c,8'd78 , 8'h2D};129 : REG_DATA = {8'h6c,8'd79 , 8'h10};130 : REG_DATA = {8'h6c,8'd80 , 8'h10};131 : REG_DATA = {8'h6c,8'd81 , 8'h3E};132 : REG_DATA = {8'h6c,8'd82 , 8'h96};133 : REG_DATA = {8'h6c,8'd83 , 8'h00};134 : REG_DATA = {8'h6c,8'd84 , 8'h80};135 : REG_DATA = {8'h6c,8'd85 , 8'h88};136 : REG_DATA = {8'h6c,8'd86 , 8'h42};137 : REG_DATA = {8'h6c,8'd87 , 8'h00};138 : REG_DATA = {8'h6c,8'd88 , 8'h00};139 : REG_DATA = {8'h6c,8'd89 , 8'h18};140 : REG_DATA = {8'h6c,8'd90 , 8'h00};141 : REG_DATA = {8'h6c,8'd91 , 8'h00};142 : REG_DATA = {8'h6c,8'd92 , 8'h00};143 : REG_DATA = {8'h6c,8'd93 , 8'hFC};144 : REG_DATA = {8'h6c,8'd94 , 8'h00};145 : REG_DATA = {8'h6c,8'd95 , 8'h48};146 : REG_DATA = {8'h6c,8'd96 , 8'h44};147 : REG_DATA = {8'h6c,8'd97 , 8'h4D};148 : REG_DATA = {8'h6c,8'd98 , 8'h49};149 : REG_DATA = {8'h6c,8'd99 , 8'h20};150 : REG_DATA = {8'h6c,8'd100 , 8'h20};151 : REG_DATA = {8'h6c,8'd101 , 8'h20};152 : REG_DATA = {8'h6c,8'd102 , 8'h20};153 : REG_DATA = {8'h6c,8'd103 , 8'h0A};154 : REG_DATA = {8'h6c,8'd104 , 8'h20};155 : REG_DATA = {8'h6c,8'd105 , 8'h20};156 : REG_DATA = {8'h6c,8'd106 , 8'h20};157 : REG_DATA = {8'h6c,8'd107 , 8'h20};158 : REG_DATA = {8'h6c,8'd108 , 8'h00};159 : REG_DATA = {8'h6c,8'd109 , 8'h00};160 : REG_DATA = {8'h6c,8'd110 , 8'h00};161 : REG_DATA = {8'h6c,8'd111 , 8'hFD};162 : REG_DATA = {8'h6c,8'd112 , 8'h00};163 : REG_DATA = {8'h6c,8'd113 , 8'h32};164 : REG_DATA = {8'h6c,8'd114 , 8'h55};165 : REG_DATA = {8'h6c,8'd115 , 8'h1F};166 : REG_DATA = {8'h6c,8'd116 , 8'h45};167 : REG_DATA = {8'h6c,8'd117 , 8'h0F};168 : REG_DATA = {8'h6c,8'd118 , 8'h00};169 : REG_DATA = {8'h6c,8'd119 , 8'h0A};170 : REG_DATA = {8'h6c,8'd120 , 8'h20};171 : REG_DATA = {8'h6c,8'd121 , 8'h20};172 : REG_DATA = {8'h6c,8'd122 , 8'h20};173 : REG_DATA = {8'h6c,8'd123 , 8'h20};174 : REG_DATA = {8'h6c,8'd124 , 8'h20};175 : REG_DATA = {8'h6c,8'd125 , 8'h20};176 : REG_DATA = {8'h6c,8'd126 , 8'h01};177 : REG_DATA = {8'h6c,8'd127 , 8'h24};178 : REG_DATA = {8'h64,8'h74 , 8'h01};// enable internal EDID179 : REG_DATA = {8'h98,8'h20 , 8'hf0};// HPD high180 : REG_DATA = {8'h68,8'h6c , 8'ha2};// disable manual HPA 181 : REG_DATA = {8'h98,8'hf4 , 8'h00};default:REG_DATA =0;
endcaseendmodule
3.配置模块顶层
`timescale 1ns / 1psmodule uicfg7611#(parameter CLK_DIV = 16'd999
)
(input clk_i, //系统时钟input rst_n, //系统复位output adv_scl, //i2c时钟总线inout adv_sda, //i2c数据总线output reg cfg_done //寄存器配置完成信号
); reg [8 :0] rst_cnt = 9'd0; //延迟复位计数器reg iic_en; //i2c使能信号reg [31:0] wr_data; //寄存器地址+写入数据reg [1 :0] TS_S = 2'd0; //状态机工作状态reg [8 :0] byte_cnt = 9'd0; //读写字节计数器reg [8 :0] REG_INDEX; //寄存器配置wire iic_busy; //i2c忙碌wire [23:0] REG_DATA;wire [8 :0] REG_SIZE;always@(posedge clk_i) beginif(!rst_n)rst_cnt <= 9'd0;else if(!rst_cnt[8]) rst_cnt <= rst_cnt + 1'b1;end//状态机工作状态转换以及输出always@(posedge clk_i) beginif(!rst_cnt[8])beginREG_INDEX<= 9'd0;iic_en <= 1'b0;wr_data <= 32'd0;cfg_done<= 1'b0;TS_S <= 2'd0; endelse begincase(TS_S)0:if(cfg_done == 1'b0)TS_S <= 2'd1;1:if(!iic_busy)begin//write dataiic_en <= 1'b1; wr_data[7 :0] <= REG_DATA[23:16]; wr_data[15 :8] <= REG_DATA[15: 8]; wr_data[23:16] <= REG_DATA[7 : 0];endelse TS_S <= 2'd2;2:beginiic_en <= 1'b0; if(!iic_busy)begin REG_INDEX<= REG_INDEX + 1'b1;TS_S <= 2'd3;endend3:begin//read rtc registerif(REG_INDEX == REG_SIZE)begincfg_done <= 1'b1;endTS_S <= 2'd0;end endcaseend
enduii2c#(.WMEN_LEN(4),.RMEN_LEN(1),.CLK_DIV(CLK_DIV)//499 for 50M 999 for 100M)uii2c_inst(.clk_i(clk_i),.iic_scl(adv_scl),.iic_sda(adv_sda),.wr_data(wr_data),.wr_cnt(8'd3),//write data max len = 4BYTES.rd_data(), //read not used.rd_cnt(8'd0),//read not used.iic_mode(1'b0),.iic_en(iic_en),.iic_busy(iic_busy));//7611regui7611reg ui7611reg_inst(.REG_SIZE(REG_SIZE),.REG_INDEX(REG_INDEX),.REG_DATA(REG_DATA)); endmodule
二.HDMI输出
1.TMDS编码
module TMDSEncoder(input clk ,//系统时钟信号;input rst ,//系统复位信号,高电平有效;input [7 : 0] din ,//输入待编码数据input c0 ,//控制信号C0input c1 ,//控制信号c1input de ,//输入数据有效指示信号;output reg [9 : 0] q_out //编码输出数据
);localparam CTRLTOKEN0 = 10'b1101010100 ;localparam CTRLTOKEN1 = 10'b0010101011 ;localparam CTRLTOKEN2 = 10'b0101010100 ;localparam CTRLTOKEN3 = 10'b1010101011 ;reg [7 : 0] din_r ;//reg [1 : 0] de_r,c0_r,c1_r ;reg [3 : 0] n1d,n1q_m,n0q_m ;reg [5 : 0] cnt ;reg [8 : 0] q_m_r ;wire [8 : 0] q_m ;//wire condition1 ;wire condition2 ;wire condition3 ;//统计待编码输入数据中1的个数,最多8个1,所以位宽为4。always@(posedge clk)beginif(rst)begin//初始值为0;n1d <= 4'd0;endelse if(de)begin//当DE为高电平,统计输入数据中1的个数。n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];endelse begin//当DE为低电平时,对控制信号编码,此时不需要统计输入信号中1的个数,故清零。n1d <= 4'd0;endend//移位寄存器将输入数据暂存,与后续信号对齐。always@(posedge clk)begindin_r <= din;de_r <= {de_r[0],de};c0_r <= {c0_r[0],c0};c1_r <= {c1_r[0],c1};q_m_r <= q_m;end//判断条件1,输入数据1的个数多余4或者1的个数等于4并且最低位为0时拉高,其余时间拉低。assign condition1 = ((n1d > 4'd4) || ((n1d == 4'd4) && (~din_r[0])));//对输入的信号进行异或运算。assign q_m[0] = din_r[0];assign q_m[1] = condition1 ? ~((q_m[0] ^ din_r[1])) : (q_m[0] ^ din_r[1]);assign q_m[2] = condition1 ? ~((q_m[1] ^ din_r[2])) : (q_m[1] ^ din_r[2]);assign q_m[3] = condition1 ? ~((q_m[2] ^ din_r[3])) : (q_m[2] ^ din_r[3]);assign q_m[4] = condition1 ? ~((q_m[3] ^ din_r[4])) : (q_m[3] ^ din_r[4]);assign q_m[5] = condition1 ? ~((q_m[4] ^ din_r[5])) : (q_m[4] ^ din_r[5]);assign q_m[6] = condition1 ? ~((q_m[5] ^ din_r[6])) : (q_m[5] ^ din_r[6]);assign q_m[7] = condition1 ? ~((q_m[6] ^ din_r[7])) : (q_m[6] ^ din_r[7]);assign q_m[8] = ~condition1;always@(posedge clk)beginif(rst)begin//初始值为0;n1q_m <= 4'd0;n0q_m <= 4'd0;endelse if(de_r[0])begin//对输入有效数据时,q_m中1和0的个数进行统计;n1q_m <= 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 <= 4'd8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);endelse begin//输入数据无效时清零。n1q_m <= 4'd0;n0q_m <= 4'd0;endend//判断条件2,一行已编码数据中1的个数等于0的个数或者本次编码数据中1的个数等于0的个数。assign condition2 = ((cnt == 6'd0) || (n1q_m == n0q_m));//判断条件3,已编码数据中1的多余0并且本次编码中间数据1的个数也多与0的个数或者已编码数据中0的个数较多并且此次编码中0的个数也比较多时拉高,其余时间拉低。assign condition3 = (((~cnt[5]) && (n1q_m > n0q_m)) || (cnt[5] && (n1q_m < n0q_m)));always@(posedge clk)beginif(rst)begin//初始值为0;cnt <= 6'd0;q_out <= 10'd0;endelse if(de_r[1])beginq_out[8] <= q_m_r[8];//第8位为编码方式位,直接输出即可。if(condition2)beginq_out[9] <= ~q_m_r[8];q_out[7:0] <= q_m_r[8] ? q_m_r[7:0] : ~q_m_r[7:0];//进行cnt的计算;cnt <= q_m_r[8] ? (cnt + n1q_m - n0q_m) : (cnt + n0q_m - n1q_m);endelse if(condition3)beginq_out[9] <= 1'b1;q_out[7:0] <= ~q_m_r[7:0];//进行cnt的计算;cnt <= cnt + {q_m_r[8],1'b0} + n0q_m - n1q_m;endelse beginq_out[9] <= 1'b0;q_out[7:0] <= q_m_r[7:0];//进行cnt的计算;cnt <= cnt - {~q_m_r[8],1'b0} + n1q_m - n0q_m;endendelse begincnt <= 6'd0;//对控制信号进行编码时,将计数器清零。case ({c1_r[1],c0_r[1]})2'b00 : q_out <= CTRLTOKEN0;2'b01 : q_out <= CTRLTOKEN1;2'b10 : q_out <= CTRLTOKEN2;2'b11 : q_out <= CTRLTOKEN3;endcaseendendendmodule
2.并串转换以及差分输出
`timescale 1ns / 1psmodule oserdese2_10to1(input [9:0] txdata, //输入并行10位数据input pclk, //并行数据时钟input clkdiv2, //并串转换时钟input txrst, //复位信号output tx_p, //差分输出output tx_n
); wire [13:0] tx_data;wire cascade_do, cascade_di, cascade_to, cascade_ti;reg int_rst;wire dai;assign tx_data = {4'd0,txdata[9:0]}; //最高支持14位并串转换always @(*)if(txrst == 1'b1)int_rst <= 1'b1;else if(pclk)int_rst <= 1'b0;else int_rst <= int_rst;OBUFDS #(.IOSTANDARD("DEFAULT"), // Specify the output I/O standard.SLEW("SLOW") // Specify the output slew rate) OBUFDS_inst (.O(tx_p), // Diff_p output (connect directly to top-level port).OB(tx_n), // Diff_n output (connect directly to top-level port).I(dai) // Buffer input);//----------------------------------------------------------------------------------
//-- Cascaded OSERDES for 10:1 ratio (DDR)
//----------------------------------------------------------------------------------
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.TRISTATE_WIDTH(1) // 3-state converter width (1,4))oserdese2_master (// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each).D1(tx_data[0]),.D2(tx_data[1]),.D3(tx_data[2]),.D4(tx_data[3]),.D5(tx_data[4]),.D6(tx_data[5]),.D7(tx_data[6]),.D8(tx_data[7]), // T1 - T4: 1-bit (each) input: Parallel 3-state inputs.T1(1'b0),.T2(1'b0),.T3(1'b0),.T4(1'b0), // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each).SHIFTIN1(cascade_di),.SHIFTIN2(cascade_ti),// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each).SHIFTOUT1(),.SHIFTOUT2(), .OCE(1'b1), // 1-bit input: Output data clock enable .CLK(clkdiv2), // 1-bit input: High speed clock.CLKDIV(pclk), // 1-bit input: Divided clock .OQ(dai), // 1-bit output: Data path output.TQ(), // 1-bit output: 3-state control.OFB(), // 1-bit output: Feedback path for data.TBYTEIN(1'b0), // 1-bit input: Byte group tristate .TBYTEOUT(), // 1-bit output: Byte group tristate.TFB(), // 1-bit output: 3-state control.TCE(1'b0), // 1-bit input: 3-state clock enable .RST(int_rst) // 1-bit input: Reset);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.TRISTATE_WIDTH(1) // 3-state converter width (1,4))oserdese2_slave (// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each).D1(1'b0),.D2(1'b0),.D3(tx_data[8]),.D4(tx_data[9]),.D5(tx_data[10]),.D6(tx_data[11]),.D7(tx_data[12]),.D8(tx_data[13]), // T1 - T4: 1-bit (each) input: Parallel 3-state inputs.T1(1'b0),.T2(1'b0),.T3(1'b0),.T4(1'b0), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each).SHIFTOUT1(cascade_di),.SHIFTOUT2(cascade_ti), // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each).SHIFTIN1(1'b0),.SHIFTIN2(1'b0),.OCE(1'b1), // 1-bit input: Output data clock enable .CLK(clkdiv2), // 1-bit input: High speed clock.CLKDIV(pclk), // 1-bit input: Divided clock .OQ(), // 1-bit output: Data path output.TQ(), // 1-bit output: 3-state control.OFB(), // 1-bit output: Feedback path for data.TFB(), // 1-bit output: 3-state control.TBYTEIN(1'b0), // 1-bit input: Byte group tristate .TBYTEOUT(), // 1-bit output: Byte group tristate.TCE(1'b0), // 1-bit input: 3-state clock enable .RST(int_rst) // 1-bit input: Reset); endmodule
三.顶层模块
`timescale 1ns / 1psmodule top(input clk_i, //系统时钟input hs_i, //行同步信号input vs_i, //场同步信号input de_i, //数据有效信号input pclk_i, //像素时钟input [23:0] rgb_i, //输入图像数据output adv_rst, //adv7611复位信号output adv_scl, //时钟总线inout adv_sda, //数据总线output HDMI_CLK_P, //差分时钟输出output HDMI_CLK_N,output [2:0] HDMI_TX_P, //差分数据输出output [2:0] HDMI_TX_N
);assign adv_rst = 1'b1; wire cfg_done; //寄存器配置完成信号wire locked ; //锁相环工作完成信号wire pclkx1,pclkx5; //锁相环输出时钟reg hs_r_0,hs_r_1,hs_r; reg vs_r_0,vs_r_1,vs_r;reg de_r_0,de_r_1,de_r;reg [23:0] rgb_r_0;reg [23:0] rgb_r_1;reg [23:0] rgb_r;wire rst_o1,rst_o2;wire vid_rst,vid_clk,vid_vs,vid_hs,vid_de;reg tpg_vs_r = 1'b0;reg tpg_hs_r = 1'b0;clk_wiz_0 clk_wiz_inst(.clk_out1(pclkx1),.clk_out2(pclkx5),.locked(locked), .clk_in1(pclk_i));uihdmitx uihdmitx_inst
(
.RSTn_i(cfg_done&&locked),
.HS_i(hs_i),
.VS_i(vs_i),
.VDE_i(de_i),
.RGB_i({rgb_i[7:0],rgb_i[15:8],rgb_i[23:16]}),
.PCLKX1_i(pclkx1),
.PCLKX2_5_i(1'b0),
.PCLKX5_i(pclkx5),
.TMDS_TX_CLK_P(HDMI_CLK_P),
.TMDS_TX_CLK_N(HDMI_CLK_N),
.TMDS_TX_P(HDMI_TX_P),
.TMDS_TX_N(HDMI_TX_N)
);uicfg7611 uicfg7611_inst
(
.clk_i(clk_i),
.rst_n(1'b1),
.adv_scl(adv_scl),
.adv_sda(adv_sda),
.cfg_done(cfg_done)
);endmodule
四.板级验证
这篇关于FPGA实现HDMI传输(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!