之前的文章简单介绍了HDMI接口、TMDS编码以及ADV611工作原理和寄存器配置,本篇博客将给出具体的代码以及板级验证结果,代码参考自米联客的教程。
一.ADV7611配置
1.i2c驱动模块
`timescale 1ns / 1ps
module 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; //停止信号1
parameter STOP2 = 4'd7; //停止信号2
parameter 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 timing
wire sda_i; //i2c数据总线输入
//利用计数器产生i2c驱动时钟
always@(posedge clk_i)begin
if(clkdiv < CLK_DIV)
clkdiv <= clkdiv + 1'b1;
else begin
clkdiv <= 16'd0;
scl_clk <= !scl_clk;
end
end
//三态门处理
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 @(*) begin
if(IIC_S == IDLE || IIC_S == STOP1 || IIC_S == STOP2)
scl_r <= 1'b1;
else
scl_r <= ~scl_clk;
end
//sda output
always @(*) begin
if(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) begin
if(IIC_S == W_ACK || IIC_S == START)begin
sda_r <= wr_data[(wcnt*8) +: 8];
if( rd_en ) sda_r <= {wr_data[7:1],1'b1};
end
else if(IIC_S == W_WAIT)
sda_r <= {sda_r[6:0],1'b1};
else
sda_r <= sda_r;
end
//sda输入转换
always @(posedge scl_clk) begin
if(IIC_S == R_WAIT ||IIC_S == W_ACK ) begin
sda_i_r <= {sda_i_r[6:0],sda_i};
end
else if(IIC_S == R_ACK)
rd_data[((rcnt-1'b1)*8) +: 8] <= sda_i_r[7:0];
else if(IIC_S == IDLE)begin
sda_i_r <= 8'd0;
end
end
always @(posedge scl_clk)
iic_sda_r <= sda_i;
always @(posedge clk_i)
sda_dg <= sda_i;
//iic状态机
always @(negedge scl_clk)begin
case(IIC_S) //sda = 1 scl =1
IDLE://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;
end
else begin
iic_busy <= 1'b0;
wcnt <= 8'd0;
rcnt <= 8'd0;
rd_en <= 1'b0;
end
START:begin //sda = 0 then scl_clk =0 scl =0 generate start
bcnt <= 3'd7;
IIC_S <= W_WAIT;
end
W_WAIT://write data
begin
if(bcnt > 3'd0)
bcnt <= bcnt - 1'b1;
else begin
wcnt <= wcnt + 1'b1;
IIC_S <= W_ACK;
end
end
W_ACK://write data ack
begin
if(wcnt < wr_cnt)begin
bcnt <= 3'd7;
IIC_S <= W_WAIT;
end
else if(rd_cnt > 3'd0)begin// read data
if(rd_en == 1'b0 && iic_mode == 1'b1)begin
rd_en <= 1'b1;
IIC_S <= IDLE;
end
else
IIC_S <= R_WAIT;
bcnt <= 3'd7;
end
else
IIC_S <= STOP1;
if(iic_sda_r == 1'b1)
IIC_S <= STOP1;
end
R_WAIT://read data
begin
rd_en <= 1'b0;
bcnt <= bcnt - 1'b1;
if(bcnt == 3'd0)begin
rcnt <= (rcnt < rd_cnt) ? (rcnt + 1'b1) : rcnt;
IIC_S <= R_ACK;
end
end
R_ACK://read date ack
begin
bcnt <= 3'd7;
IIC_S <= (rcnt < rd_cnt) ? R_WAIT : STOP1;
end
STOP1://sda = 0 scl = 1
IIC_S <= STOP2;
STOP2://sda = 1 scl = 1
IIC_S <= IDLE;
default:
IIC_S <= IDLE;
endcase
end
endmodule
2.adv7611寄存器数据
`timescale 1ns / 1ps
module 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 Index
0 : 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 process
4 : 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'h80
10 : REG_DATA = {8'h98,8'h03, 8'h40}; //VGA : 8'hA0; QVGA : 8'hF0
11 : REG_DATA = {8'h98,8'h04, 8'h42}; //VGA : 8'hF0; QVGA : 8'h78
12 : 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;QVGA
15 : 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 setting
24 : REG_DATA = {8'h68,8'hc1, 8'h01}; //ADI recommanded setting
25 : REG_DATA = {8'h68,8'hc2, 8'h01}; //ADI recommanded setting
26 : REG_DATA = {8'h68,8'hc3, 8'h01}; //ADI recommanded setting
27 : REG_DATA = {8'h68,8'hc4, 8'h01}; //ADI recommanded setting
28 : REG_DATA = {8'h68,8'hc5, 8'h01}; //ADI recommanded setting
29 : REG_DATA = {8'h68,8'hc6, 8'h01}; //ADI recommanded setting
30 : REG_DATA = {8'h68,8'hc7, 8'h01}; //ADI recommanded setting
31 : REG_DATA = {8'h68,8'hc8, 8'h01}; //ADI recommanded setting
32 : REG_DATA = {8'h68,8'hc9, 8'h01}; //ADI recommanded settin g
33 : REG_DATA = {8'h68,8'hca, 8'h01}; //ADI recommanded setting
34 : REG_DATA = {8'h68,8'hcb, 8'h01}; //ADI recommanded setting
35 : REG_DATA = {8'h68,8'hcc, 8'h01}; //ADI recommanded setting
36 : REG_DATA = {8'h68,8'h00, 8'h00}; //Set HDMI input Port A
37 : REG_DATA = {8'h68,8'h83, 8'hfe}; //terminator for Port A
38 : REG_DATA = {8'h68,8'h6f, 8'h08}; //ADI recommended setting
39 : REG_DATA = {8'h68,8'h85, 8'h1f}; //ADI recommended setting
40 : REG_DATA = {8'h68,8'h87, 8'h70}; //ADI recommended setting
41 : REG_DATA = {8'h68,8'h8d, 8'h04}; //LFG
42 : REG_DATA = {8'h68,8'h8e, 8'h1e}; //HFG
43 : REG_DATA = {8'h68,8'h1a, 8'h8a}; //unmute audio
44 : REG_DATA = {8'h68,8'h57, 8'hda}; // ADI recommended setting
45 : 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 HPA
48 : REG_DATA = {8'h98,8'h20 ,8'h70};//HPD low
49 : 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 par
50 : 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 EDID
179 : REG_DATA = {8'h98,8'h20 , 8'hf0};// HPD high
180 : REG_DATA = {8'h68,8'h6c , 8'ha2};// disable manual HPA
181 : REG_DATA = {8'h98,8'hf4 , 8'h00};
default:REG_DATA =0;
endcase
endmodule
3.配置模块顶层
`timescale 1ns / 1ps
module 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) begin
if(!rst_n)
rst_cnt <= 9'd0;
else if(!rst_cnt[8])
rst_cnt <= rst_cnt + 1'b1;
end
//状态机工作状态转换以及输出
always@(posedge clk_i) begin
if(!rst_cnt[8])begin
REG_INDEX<= 9'd0;
iic_en <= 1'b0;
wr_data <= 32'd0;
cfg_done<= 1'b0;
TS_S <= 2'd0;
end
else begin
case(TS_S)
0:if(cfg_done == 1'b0)
TS_S <= 2'd1;
1:if(!iic_busy)begin//write data
iic_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];
end
else
TS_S <= 2'd2;
2:begin
iic_en <= 1'b0;
if(!iic_busy)begin
REG_INDEX<= REG_INDEX + 1'b1;
TS_S <= 2'd3;
end
end
3:begin//read rtc register
if(REG_INDEX == REG_SIZE)begin
cfg_done <= 1'b1;
end
TS_S <= 2'd0;
end
endcase
end
end
uii2c#(
.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)
);
//7611reg
ui7611reg 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 ,//控制信号C0
input c1 ,//控制信号c1
input 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)begin
if(rst)begin//初始值为0;
n1d <= 4'd0;
end
else if(de)begin//当DE为高电平,统计输入数据中1的个数。
n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];
end
else begin//当DE为低电平时,对控制信号编码,此时不需要统计输入信号中1的个数,故清零。
n1d <= 4'd0;
end
end
//移位寄存器将输入数据暂存,与后续信号对齐。
always@(posedge clk)begin
din_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)begin
if(rst)begin//初始值为0;
n1q_m <= 4'd0;
n0q_m <= 4'd0;
end
else 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]);
end
else begin//输入数据无效时清零。
n1q_m <= 4'd0;
n0q_m <= 4'd0;
end
end
//判断条件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)begin
if(rst)begin//初始值为0;
cnt <= 6'd0;
q_out <= 10'd0;
end
else if(de_r[1])begin
q_out[8] <= q_m_r[8];//第8位为编码方式位,直接输出即可。
if(condition2)begin
q_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);
end
else if(condition3)begin
q_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;
end
else begin
q_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;
end
end
else begin
cnt <= 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;
endcase
end
end
endmodule
2.并串转换以及差分输出
`timescale 1ns / 1ps
module 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 / 1ps
module 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