-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathhdmi_tx_top.v
More file actions
346 lines (279 loc) · 13.4 KB
/
Copy pathhdmi_tx_top.v
File metadata and controls
346 lines (279 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
//--------------------------------------------------------------------------------------------------------
// Module : hdmi_tx_top
// Type : synthesizable, IP's top
// Standard: Verilog 2001 (IEEE1364-2001)
// Function: A HDMI TX (display) controller.
// It can fetch video pixels from user-specified clock domain, cross them to the HDMI clock domain.
// Then, display the video to HDMI (following DVI TMDS timing specification).
//--------------------------------------------------------------------------------------------------------
module hdmi_tx_top #(
// the parameter of request to response latency ---------------------------------------
parameter RESP_LATENCY = 1, // 1, 2, or 3
// paramter of video sizes ------------------------------------------------------------
parameter [13:0] H_TOTAL = 14'd800,
parameter [13:0] H_DRAW_START = 14'd0,
parameter [13:0] H_DRAW_WIDTH = 14'd640,
parameter [13:0] H_SYNC_START = 14'd656,
parameter [13:0] H_SYNC_WIDTH = 14'd96,
parameter [13:0] V_TOTAL = 14'd525,
parameter [13:0] V_DRAW_START = 14'd0,
parameter [13:0] V_DRAW_HEIGHT= 14'd480,
parameter [13:0] V_SYNC_START = 14'd490,
parameter [13:0] V_SYNC_HEIGHT= 14'd2
) (
// user's clock and reset -------------------------------------------------------------
input wire rstn,
input wire clk, // can be asynchronous with pclk_x5. Its frequency must be slightly higher than f(pclk_x5) / 5
// user's pixel request interface (these signals synchronize with clk) ----------------
output reg req_en, // request for a pixel
output reg req_sof, // the requested pixel is at start of frame
output reg req_eof, // the requested pixel is at end of frame
output reg req_sol, // the requested pixel is at start of line
output reg req_eol, // the requested pixel is at end of line
// user's pixel response interface (these signals synchronize with clk) ---------------
input wire [7:0] resp_red,
input wire [7:0] resp_green,
input wire [7:0] resp_blue,
// HDMI driving clock, whose frequency must be 5 * pclk (pclk is the pixel clock) -----
input wire pclk_x5,
// HDMI TX out ------------------------------------------------------------------------
output wire hdmi_clk_p,
output wire hdmi_clk_n,
output wire hdmi_tx0_p,
output wire hdmi_tx0_n,
output wire hdmi_tx1_p,
output wire hdmi_tx1_n,
output wire hdmi_tx2_p,
output wire hdmi_tx2_n
);
reg [13:0] hcnt = 14'd0;
reg [13:0] vcnt = 14'd0;
reg a_en = 1'b0;
reg a_hsync = 1'b0;
reg a_vsync = 1'b0;
reg b_en = 1'b0;
reg b_hsync = 1'b0;
reg b_vsync = 1'b0;
reg b_vde = 1'b0;
reg c_en = 1'b0;
reg c_hsync = 1'b0;
reg c_vsync = 1'b0;
reg c_vde = 1'b0;
reg d_en = 1'b0;
reg d_hsync = 1'b0;
reg d_vsync = 1'b0;
reg d_vde = 1'b0;
reg [ 7:0] d_red, d_green, d_blue;
//wire h_rdy;
wire h_en;
wire [ 9:0] h_tmds0_bits;
wire [ 9:0] h_tmds1_bits;
wire [ 9:0] h_tmds2_bits;
wire j_en;
wire [ 9:0] j_tmds0_bits;
wire [ 9:0] j_tmds1_bits;
wire [ 9:0] j_tmds2_bits;
reg [ 2:0] j_cnt5 = 3'd0; // HDMI control counter, period=5 (j_cnt5 = 0 -> 1 -> 2 -> 3 -> 4 -> 0)
reg j_fetch_start = 1'b0;
reg j_fetch = 1'b0;
wire half_empty;
reg [ 9:0] k_tmds0_bits = 10'd0;
reg [ 9:0] k_tmds1_bits = 10'd0;
reg [ 9:0] k_tmds2_bits = 10'd0;
reg [ 9:0] k_tmdsc_bits = 10'd0;
/////////////////////////////////////////////////////////////////////////////////////////////
// reset synchronize
/////////////////////////////////////////////////////////////////////////////////////////////
reg [3:0] rstn_x5_shift = 4'd0;
wire rstn_x5 = rstn_x5_shift[3];
always @ (posedge pclk_x5 or negedge rstn)
if (~rstn) rstn_x5_shift <= 4'd0;
else rstn_x5_shift <= {rstn_x5_shift[2:0], 1'b1};
reg [3:0] rstn_main_shift = 4'd0;
wire rstn_main = rstn_main_shift[3];
always @ (posedge clk or negedge rstn_x5)
if (~rstn_x5) rstn_main_shift <= 4'd0;
else rstn_main_shift <= {rstn_main_shift[2:0], 1'b1};
/////////////////////////////////////////////////////////////////////////////////////////////
// pixel coordinate counter
/////////////////////////////////////////////////////////////////////////////////////////////
always @ (posedge clk or negedge rstn_main)
if (~rstn_main) begin
hcnt <= 14'd0;
vcnt <= 14'd0;
end else begin
if (half_empty) begin
if (hcnt < (H_TOTAL - 14'd1)) begin
hcnt <= hcnt + 14'd1;
end else begin
hcnt <= 14'd0;
vcnt <= (vcnt < (V_TOTAL - 14'd1)) ? (vcnt + 14'd1) : 14'd0;
end
end
end
wire hsync = (hcnt >= H_SYNC_START) && (hcnt < (H_SYNC_START+H_SYNC_WIDTH) );
wire vsync = (vcnt >= V_SYNC_START) && (vcnt < (V_SYNC_START+V_SYNC_HEIGHT));
wire draw = (hcnt >= H_DRAW_START) && (hcnt < (H_DRAW_START+H_DRAW_WIDTH) ) && (vcnt >= V_DRAW_START) && (vcnt < (V_DRAW_START+V_DRAW_HEIGHT));
wire hfirst = (hcnt == H_DRAW_START);
wire hlast = (hcnt == (H_DRAW_START+H_DRAW_WIDTH-14'd1));
wire vfirst = (vcnt == V_DRAW_START);
wire vlast = (vcnt == (V_DRAW_START+V_DRAW_HEIGHT-14'd1));
/////////////////////////////////////////////////////////////////////////////////////////////
// stage A : generate HSYNC, VSYNC, and request signals
/////////////////////////////////////////////////////////////////////////////////////////////
initial {req_en, req_sol, req_eol, req_sof, req_eof} = 5'b0;
always @ (posedge clk or negedge rstn_main)
if (~rstn_main) begin
{a_en, a_hsync, a_vsync} <= 3'b0;
{req_en, req_sol, req_eol, req_sof, req_eof} <= 5'b0;
end else begin
a_en <= half_empty;
a_hsync <= hsync;
a_vsync <= vsync;
req_en <= half_empty & draw;
req_sol <= half_empty & draw & hfirst;
req_eol <= half_empty & draw & hlast;
req_sof <= half_empty & draw & hfirst & vfirst;
req_eof <= half_empty & draw & hlast & vlast;
end
/////////////////////////////////////////////////////////////////////////////////////////////
// stage B - D : get user response
/////////////////////////////////////////////////////////////////////////////////////////////
always @ (posedge clk or negedge rstn_main)
if (~rstn_main) begin
{b_en, b_hsync, b_vsync, b_vde} <= 4'b0;
{c_en, c_hsync, c_vsync, c_vde} <= 4'b0;
{d_en, d_hsync, d_vsync, d_vde} <= 4'b0;
end else begin
{b_en, b_hsync, b_vsync, b_vde} <= {a_en, a_hsync, a_vsync, req_en};
{c_en, c_hsync, c_vsync, c_vde} <= {b_en, b_hsync, b_vsync, b_vde};
{d_en, d_hsync, d_vsync, d_vde} <= {c_en, c_hsync, c_vsync, c_vde};
end
generate
if (RESP_LATENCY >= 3) begin
always @ (*)
{d_red, d_green, d_blue} = {resp_red, resp_green, resp_blue};
end else if (RESP_LATENCY == 2) begin
always @ (posedge clk)
{d_red, d_green, d_blue} <= {resp_red, resp_green, resp_blue};
end else begin
reg [ 7:0] c_red, c_green, c_blue;
always @ (posedge clk) begin
{c_red, c_green, c_blue} <= {resp_red, resp_green, resp_blue};
{d_red, d_green, d_blue} <= {c_red, c_green, c_blue};
end
end
endgenerate
/////////////////////////////////////////////////////////////////////////////////////////////
// stage E - H : TMDS encoding
/////////////////////////////////////////////////////////////////////////////////////////////
hdmi_tmds_encode u_tmds_encode_red (
.rstn ( rstn_main ),
.clk ( clk ),
.i_en ( d_en ),
.i_vde ( d_vde ),
.i_vd ( d_red ),
.i_cd ( 2'b00 ),
.o_en ( ),
.o_tmds_bits ( h_tmds2_bits )
);
hdmi_tmds_encode u_tmds_encode_green (
.rstn ( rstn_main ),
.clk ( clk ),
.i_en ( d_en ),
.i_vde ( d_vde ),
.i_vd ( d_green ),
.i_cd ( 2'b00 ),
.o_en ( ),
.o_tmds_bits ( h_tmds1_bits )
);
hdmi_tmds_encode u_tmds_encode_blue (
.rstn ( rstn_main ),
.clk ( clk ),
.i_en ( d_en ),
.i_vde ( d_vde ),
.i_vd ( d_blue ),
.i_cd ( {d_vsync, d_hsync} ),
.o_en ( h_en ),
.o_tmds_bits ( h_tmds0_bits )
);
/////////////////////////////////////////////////////////////////////////////////////////////
// stage J : cross to clock domain pclk_x5
/////////////////////////////////////////////////////////////////////////////////////////////
hdmi_async_fifo #(
.DW ( 30 ),
.EA ( 5 )
) u_hdmi_async_fifo (
.i_rstn ( rstn_main ),
.i_clk ( clk ),
.i_tready ( /*h_rdy*/ ),
.i_tvalid ( h_en ),
.i_tdata ( {h_tmds0_bits, h_tmds1_bits, h_tmds2_bits} ),
.o_rstn ( rstn_x5 ),
.o_clk ( pclk_x5 ),
.o_tready ( j_fetch ),
.o_tvalid ( j_en ),
.o_tdata ( {j_tmds0_bits, j_tmds1_bits, j_tmds2_bits} ),
.w_half_empty ( half_empty )
);
/////////////////////////////////////////////////////////////////////////////////////////////
// stage H : fetch control
/////////////////////////////////////////////////////////////////////////////////////////////
always @ (posedge pclk_x5 or negedge rstn_x5)
if (~rstn_x5) begin
j_cnt5 <= 3'd0;
j_fetch_start <= 1'b0;
j_fetch <= 1'b0;
end else begin
j_cnt5 <= j_cnt5[2] ? 3'd0 : (j_cnt5 + 3'd1);
if (j_en & j_cnt5[2]) j_fetch_start <= 1'b1;
j_fetch <= j_cnt5[2] & j_fetch_start;
end
/////////////////////////////////////////////////////////////////////////////////////////////
// stage J : assert that u_hdmi_async_fifo never empty (only for simulation)
/////////////////////////////////////////////////////////////////////////////////////////////
/*
always @ (posedge pclk_x5 or negedge rstn_x5)
if (~rstn_x5) begin
end else begin
if (j_fetch & ~j_en) begin
$display("error : u_hdmi_async_fifo empty");
$finish;
end
end*/
/////////////////////////////////////////////////////////////////////////////////////////////
// stage H : assert that u_hdmi_async_fifo never full (only for simulation)
/////////////////////////////////////////////////////////////////////////////////////////////
/*
always @ (posedge clk or negedge rstn_main)
if (~rstn_main) begin
end else begin
if (~h_rdy & h_en) begin
$display("error : u_hdmi_async_fifo full");
$finish;
end
end*/
/////////////////////////////////////////////////////////////////////////////////////////////
// stage K : serialize TMDS signals
/////////////////////////////////////////////////////////////////////////////////////////////
always @ (posedge pclk_x5)
if (j_fetch) begin
k_tmds0_bits <= j_tmds0_bits;
k_tmds1_bits <= j_tmds1_bits;
k_tmds2_bits <= j_tmds2_bits;
k_tmdsc_bits <= 10'b0000011111;
end else begin
k_tmds0_bits <= k_tmds0_bits >> 2;
k_tmds1_bits <= k_tmds1_bits >> 2;
k_tmds2_bits <= k_tmds2_bits >> 2;
k_tmdsc_bits <= k_tmdsc_bits >> 2;
end
hdmi_tddr u0p_ddr (pclk_x5, k_tmds0_bits[1:0], hdmi_tx0_p);
hdmi_tddr u0n_ddr (pclk_x5, ~k_tmds0_bits[1:0], hdmi_tx0_n);
hdmi_tddr u1p_ddr (pclk_x5, k_tmds1_bits[1:0], hdmi_tx1_p);
hdmi_tddr u1n_ddr (pclk_x5, ~k_tmds1_bits[1:0], hdmi_tx1_n);
hdmi_tddr u2p_ddr (pclk_x5, k_tmds2_bits[1:0], hdmi_tx2_p);
hdmi_tddr u2n_ddr (pclk_x5, ~k_tmds2_bits[1:0], hdmi_tx2_n);
hdmi_tddr u3p_ddr (pclk_x5, k_tmdsc_bits[1:0], hdmi_clk_p);
hdmi_tddr u3n_ddr (pclk_x5, ~k_tmdsc_bits[1:0], hdmi_clk_n);
endmodule