-
Notifications
You must be signed in to change notification settings - Fork 0
/
video_timing.sv
166 lines (143 loc) · 6.45 KB
/
video_timing.sv
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
// video_timing.sv
//
// vim: set et ts=4 sw=4
//
// See top-level LICENSE file for license information. (Hint: MIT-0)
//
// Thanks to the following inspirational and education projects:
//
// Dan "drr" Rodrigues for the amazing icestation-32 project:
// https://github.com/dan-rodrigues/icestation-32
// Sylvain "tnt" Munaut for many amazing iCE40 projects and streams (e.g., 1920x1080 HDMI):
// https://github.com/smunaut/ice40-playground
// Will "Flux" Green and his excellent FPGA video and overall educational site
// https://projectf.io/
//
// Learning from both of these projects (and others) helped me significantly improve this design
`default_nettype none // mandatory for Verilog sanity
`timescale 1ns/1ps // mandatory to shut up Icarus Verilog
`include "video_package.svh"
module video_timing (
// video registers and control
output hres_t h_count_o, // horizontal video counter
output logic v_visible_o, // visible vertical line
output logic visible_o, // pixel is visible
output logic end_of_line_o, // strobe for end of line (h_count resets)
output logic end_of_frame_o, // strobe for end of frame (v_count resets)
output logic vsync_o, // vertical sync output (polarity depends on video mode)
output logic hsync_o, // horizontal sync output (polarity depends on video mode)
output logic dv_de_o, // display enable, true for visible pixel (needed for DV-I)
input wire logic reset_i, // system reset
input wire logic clk // clock (video pixel clock)
);
// NOTE: Both H & V states so both can start at 0
typedef enum logic [1:0] {
H_STATE_PRE_SYNC = 2'b00, // aka front porch
H_STATE_SYNC = 2'b01,
H_STATE_POST_SYNC = 2'b10, // aka back porch
H_STATE_VISIBLE = 2'b11
} horizontal_st;
typedef enum logic [1:0] {
V_STATE_VISIBLE = 2'b00,
V_STATE_PRE_SYNC = 2'b01, // aka front porch
V_STATE_SYNC = 2'b10,
V_STATE_POST_SYNC = 2'b11 // aka back porch
} vertical_st;
// sync generation signals (and combinatorial logic "next" versions)
logic [1:0] h_state, h_state_next;
hres_t h_count, h_count_next;
hres_t h_count_match_value;
logic [1:0] v_state, v_state_next;
vres_t v_count, v_count_next;
vres_t v_count_match_value;
logic hsync, hsync_next;
logic vsync, vsync_next;
logic dv_de, dv_de_next;
logic end_of_line, end_of_line_next;
logic end_of_frame, end_of_frame_next;
// outputs
always_comb h_count_o = h_count;
always_comb v_visible_o = (v_state == V_STATE_VISIBLE);
always_comb visible_o = dv_de;
always_comb end_of_line_o = end_of_line;
always_comb end_of_frame_o = end_of_frame;
always_comb hsync_o = hsync;
always_comb vsync_o = vsync;
always_comb dv_de_o = dv_de;
// video sync generation via state machine (Thanks tnt & drr - a much more efficient method!)
always_comb end_of_line_next = (h_state == H_STATE_VISIBLE) && (h_state_next == H_STATE_PRE_SYNC);
always_comb end_of_frame_next= (v_state == V_STATE_POST_SYNC) && (v_state_next == V_STATE_VISIBLE);
always_comb hsync_next = (h_state_next == H_STATE_SYNC) ? v::H_SYNC_POLARITY : ~v::H_SYNC_POLARITY;
always_comb vsync_next = (v_state_next == V_STATE_SYNC) ? v::V_SYNC_POLARITY : ~v::V_SYNC_POLARITY;
always_comb dv_de_next = (v_state_next == V_STATE_VISIBLE) && (h_state_next == H_STATE_VISIBLE);
// combinational block for video counters
always_comb begin
h_count_next = h_count + 1'b1;
v_count_next = v_count;
if (end_of_line_next) begin
h_count_next = '0;
if (end_of_frame_next) begin
v_count_next = '0;
end else begin
v_count_next = v_count + 1'b1;
end
end
end
// combinational block for horizontal video state
always_comb h_state_next = (h_count == h_count_match_value) ? h_state + 1'b1 : h_state;
always_comb begin
// scanning horizontally left to right, offscreen pixels are on left before visible pixels
case (h_state)
H_STATE_PRE_SYNC:
h_count_match_value = v::H_FRONT_PORCH - 1;
H_STATE_SYNC:
h_count_match_value = v::H_FRONT_PORCH + v::H_SYNC_PULSE - 1;
H_STATE_POST_SYNC:
h_count_match_value = v::H_FRONT_PORCH + v::H_SYNC_PULSE + v::H_BACK_PORCH - 1;
H_STATE_VISIBLE:
h_count_match_value = v::TOTAL_WIDTH - 1;
endcase
end
// combinational block for vertical video state
always_comb v_state_next = end_of_line_next && (v_count == v_count_match_value) ? v_state + 1'b1 : v_state;
always_comb begin
// scanning vertically top to bottom, offscreen lines are on bottom after visible lines
case (v_state)
V_STATE_VISIBLE:
v_count_match_value = v::VISIBLE_HEIGHT - 1;
V_STATE_PRE_SYNC:
v_count_match_value = v::VISIBLE_HEIGHT + v::V_FRONT_PORCH - 1;
V_STATE_SYNC:
v_count_match_value = v::VISIBLE_HEIGHT + v::V_FRONT_PORCH + v::V_SYNC_PULSE - 1;
V_STATE_POST_SYNC:
v_count_match_value = v::TOTAL_HEIGHT - 1;
endcase
end
// video pixel generation
always_ff @(posedge clk) begin
if (reset_i) begin
end_of_line <= 1'b0;
end_of_frame <= 1'b0;
h_state <= H_STATE_PRE_SYNC;
v_state <= V_STATE_VISIBLE;
h_count <= '0;
v_count <= '0;
hsync <= ~v::H_SYNC_POLARITY;
vsync <= ~v::V_SYNC_POLARITY;
dv_de <= 1'b0;
end else begin
// update registered signals from combinatorial "next" versions
end_of_line <= end_of_line_next;
end_of_frame <= end_of_frame_next;
h_state <= h_state_next;
v_state <= v_state_next;
h_count <= h_count_next;
v_count <= v_count_next;
// set other video output signals
hsync <= hsync_next;
vsync <= vsync_next;
dv_de <= dv_de_next;
end
end
endmodule
`default_nettype wire // restore default