1+ //
2+ // Copyright (C) 2024 Markus Hiienkari <mhiienka@niksula.hut.fi>
3+ //
4+ // This file is part of Open Source Scan Converter project.
5+ //
6+ // This program is free software: you can redistribute it and/or modify
7+ // it under the terms of the GNU General Public License as published by
8+ // the Free Software Foundation, either version 3 of the License, or
9+ // (at your option) any later version.
10+ //
11+ // This program is distributed in the hope that it will be useful,
12+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ // GNU General Public License for more details.
15+ //
16+ // You should have received a copy of the GNU General Public License
17+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ //
19+
20+ module output_csc (
21+ input PCLK_i,
22+ input reset_n,
23+ input enable,
24+ input [7 :0 ] R_i,
25+ input [7 :0 ] G_i,
26+ input [7 :0 ] B_i,
27+ input HSYNC_i,
28+ input VSYNC_i,
29+ input DE_i,
30+ output reg [7 :0 ] R_o,
31+ output reg [7 :0 ] G_o,
32+ output reg [7 :0 ] B_o,
33+ output reg HSYNC_o,
34+ output reg VSYNC_o,
35+ output reg DE_o
36+ );
37+
38+ localparam PP_PL_START = 1 ;
39+ localparam PP_CSC_START = 0 ;
40+ localparam PP_CSC_LENGTH = 5 ;
41+ localparam PP_CSC_END = PP_CSC_START + PP_CSC_LENGTH;
42+ localparam PP_BIASMUX_START = PP_CSC_END;
43+ localparam PP_BIASMUX_LENGTH = 1 ;
44+ localparam PP_BIASMUX_END = PP_BIASMUX_START + PP_BIASMUX_LENGTH;
45+ localparam PP_PL_END = PP_BIASMUX_END;
46+
47+ reg HSYNC_pp[PP_PL_START:PP_CSC_END] /* synthesis ramstyle = "logic" */ ;
48+ reg VSYNC_pp[PP_PL_START:PP_CSC_END] /* synthesis ramstyle = "logic" */ ;
49+ reg DE_pp[PP_PL_START:PP_CSC_END] /* synthesis ramstyle = "logic" */ ;
50+
51+ // RGB->YPbPr709 decimal values * 2^15
52+ wire signed [17 :0 ] Y_R_coeff = 6966 ;
53+ wire signed [17 :0 ] Y_G_coeff = 23435 ;
54+ wire signed [17 :0 ] Y_B_coeff = 2365 ;
55+ wire signed [17 :0 ] Pb_R_coeff = - 3754 ;
56+ wire signed [17 :0 ] Pb_G_coeff = - 12630 ;
57+ wire signed [17 :0 ] Pb_B_coeff = 16384 ;
58+ wire signed [17 :0 ] Pr_R_coeff = 16384 ;
59+ wire signed [17 :0 ] Pr_G_coeff = - 14882 ;
60+ wire signed [17 :0 ] Pr_B_coeff = - 1502 ;
61+
62+ wire signed [35 :0 ] Y_R_pre, Y_G_pre, Y_B_pre;
63+ wire signed [35 :0 ] Pb_R_pre, Pb_G_pre, Pb_B_pre;
64+ wire signed [35 :0 ] Pr_R_pre, Pr_G_pre, Pr_B_pre;
65+
66+ reg signed [9 :0 ] Y_R, Y_G, Y_B;
67+ reg signed [9 :0 ] Pb_R, Pb_G, Pb_B;
68+ reg signed [9 :0 ] Pr_R, Pr_G, Pr_B;
69+
70+ reg signed [9 :0 ] Y_csc_sum, Pb_csc_sum, Pr_csc_sum;
71+
72+ reg [7 :0 ] Y_csc, Pb_csc, Pr_csc;
73+
74+ always @(posedge PCLK_i) begin
75+ if (enable) begin
76+ // Cycle 1
77+ Y_R <= Y_R_pre[24 :15 ];
78+ Y_G <= Y_G_pre[24 :15 ];
79+ Y_B <= Y_B_pre[24 :15 ];
80+ Pb_R <= Pb_R_pre[24 :15 ];
81+ Pb_G <= Pb_G_pre[24 :15 ];
82+ Pb_B <= Pb_B_pre[24 :15 ];
83+ Pr_R <= Pr_R_pre[24 :15 ];
84+ Pr_G <= Pr_G_pre[24 :15 ];
85+ Pr_B <= Pr_B_pre[24 :15 ];
86+
87+ // Cycle 2
88+ Y_csc_sum <= Y_R + Y_G + Y_B;
89+ Pb_csc_sum <= Pb_R + Pb_G + Pb_B + 10'd128 ;
90+ Pr_csc_sum <= Pr_R + Pr_G + Pr_B + 10'd128 ;
91+
92+ // Cycle 3
93+ if (Y_csc_sum[9 ] == 1'b1 )
94+ Y_csc <= 8'h00 ;
95+ else if (Y_csc_sum[8 ] == 1'b1 )
96+ Y_csc <= 8'hFF ;
97+ else
98+ Y_csc <= Y_csc_sum[7 :0 ];
99+
100+ if (Pb_csc_sum[9 ] == 1'b1 )
101+ Pb_csc <= 8'h00 ;
102+ else if (Pb_csc_sum[8 ] == 1'b1 )
103+ Pb_csc <= 8'hFF ;
104+ else
105+ Pb_csc <= Pb_csc_sum[7 :0 ];
106+
107+ if (Pr_csc_sum[9 ] == 1'b1 )
108+ Pr_csc <= 8'h00 ;
109+ else if (Pr_csc_sum[8 ] == 1'b1 )
110+ Pr_csc <= 8'hFF ;
111+ else
112+ Pr_csc <= Pr_csc_sum[7 :0 ];
113+ end
114+ end
115+
116+ // Pipeline stages 1-
117+ integer pp_idx;
118+ always @(posedge PCLK_i) begin
119+ HSYNC_pp[1 ] <= HSYNC_i;
120+ VSYNC_pp[1 ] <= VSYNC_i;
121+ DE_pp[1 ] <= DE_i;
122+
123+ for (pp_idx = PP_PL_START+ 1 ; pp_idx <= PP_CSC_END; pp_idx = pp_idx+ 1 ) begin
124+ HSYNC_pp[pp_idx] <= HSYNC_pp[pp_idx- 1 ];
125+ VSYNC_pp[pp_idx] <= VSYNC_pp[pp_idx- 1 ];
126+ DE_pp[pp_idx] <= DE_pp[pp_idx- 1 ];
127+ end
128+
129+ R_o <= enable ? (DE_pp[PP_CSC_END] ? Pr_csc : 8'h80 ) : R_i;
130+ G_o <= enable ? (DE_pp[PP_CSC_END] ? Y_csc : 8'h00 ) : G_i;
131+ B_o <= enable ? (DE_pp[PP_CSC_END] ? Pb_csc : 8'h80 ) : B_i;
132+ HSYNC_o <= enable ? HSYNC_pp[PP_CSC_END] : HSYNC_i;
133+ VSYNC_o <= enable ? VSYNC_pp[PP_CSC_END] : VSYNC_i;
134+ DE_o <= enable ? DE_pp[PP_CSC_END] : DE_i;
135+ end
136+
137+ lpm_mult csc_mult_component_0 (
138+ // Inputs
139+ .dataa ({10'h0 , R_i}),
140+ .datab (Y_R_coeff),
141+ .aclr (1'b0 ),
142+ .clken (enable),
143+ .clock (PCLK_i),
144+
145+ // Outputs
146+ .result (Y_R_pre),
147+ .sum (1'b0 )
148+ );
149+ defparam
150+ csc_mult_component_0.lpm_widtha = 18 ,
151+ csc_mult_component_0.lpm_widthb = 18 ,
152+ csc_mult_component_0.lpm_widthp = 36 ,
153+ csc_mult_component_0.lpm_widths = 1 ,
154+ csc_mult_component_0.lpm_type = "LPM_MULT" ,
155+ csc_mult_component_0.lpm_representation = "SIGNED" ,
156+ csc_mult_component_0.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
157+
158+ lpm_mult csc_mult_component_1 (
159+ // Inputs
160+ .dataa ({10'h0 , G_i}),
161+ .datab (Y_G_coeff),
162+ .aclr (1'b0 ),
163+ .clken (enable),
164+ .clock (PCLK_i),
165+
166+ // Outputs
167+ .result (Y_G_pre),
168+ .sum (1'b0 )
169+ );
170+ defparam
171+ csc_mult_component_1.lpm_widtha = 18 ,
172+ csc_mult_component_1.lpm_widthb = 18 ,
173+ csc_mult_component_1.lpm_widthp = 36 ,
174+ csc_mult_component_1.lpm_widths = 1 ,
175+ csc_mult_component_1.lpm_type = "LPM_MULT" ,
176+ csc_mult_component_1.lpm_representation = "SIGNED" ,
177+ csc_mult_component_1.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
178+
179+ lpm_mult csc_mult_component_2 (
180+ // Inputs
181+ .dataa ({10'h0 , B_i}),
182+ .datab (Y_B_coeff),
183+ .aclr (1'b0 ),
184+ .clken (enable),
185+ .clock (PCLK_i),
186+
187+ // Outputs
188+ .result (Y_B_pre),
189+ .sum (1'b0 )
190+ );
191+ defparam
192+ csc_mult_component_2.lpm_widtha = 18 ,
193+ csc_mult_component_2.lpm_widthb = 18 ,
194+ csc_mult_component_2.lpm_widthp = 36 ,
195+ csc_mult_component_2.lpm_widths = 1 ,
196+ csc_mult_component_2.lpm_type = "LPM_MULT" ,
197+ csc_mult_component_2.lpm_representation = "SIGNED" ,
198+ csc_mult_component_2.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
199+
200+ lpm_mult csc_mult_component_3 (
201+ // Inputs
202+ .dataa ({10'h0 , R_i}),
203+ .datab (Pb_R_coeff),
204+ .aclr (1'b0 ),
205+ .clken (enable),
206+ .clock (PCLK_i),
207+
208+ // Outputs
209+ .result (Pb_R_pre),
210+ .sum (1'b0 )
211+ );
212+ defparam
213+ csc_mult_component_3.lpm_widtha = 18 ,
214+ csc_mult_component_3.lpm_widthb = 18 ,
215+ csc_mult_component_3.lpm_widthp = 36 ,
216+ csc_mult_component_3.lpm_widths = 1 ,
217+ csc_mult_component_3.lpm_type = "LPM_MULT" ,
218+ csc_mult_component_3.lpm_representation = "SIGNED" ,
219+ csc_mult_component_3.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
220+
221+ lpm_mult csc_mult_component_4 (
222+ // Inputs
223+ .dataa ({10'h0 , G_i}),
224+ .datab (Pb_G_coeff),
225+ .aclr (1'b0 ),
226+ .clken (enable),
227+ .clock (PCLK_i),
228+
229+ // Outputs
230+ .result (Pb_G_pre),
231+ .sum (1'b0 )
232+ );
233+ defparam
234+ csc_mult_component_4.lpm_widtha = 18 ,
235+ csc_mult_component_4.lpm_widthb = 18 ,
236+ csc_mult_component_4.lpm_widthp = 36 ,
237+ csc_mult_component_4.lpm_widths = 1 ,
238+ csc_mult_component_4.lpm_type = "LPM_MULT" ,
239+ csc_mult_component_4.lpm_representation = "SIGNED" ,
240+ csc_mult_component_4.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
241+
242+ lpm_mult csc_mult_component_5 (
243+ // Inputs
244+ .dataa ({10'h0 , B_i}),
245+ .datab (Pb_B_coeff),
246+ .aclr (1'b0 ),
247+ .clken (enable),
248+ .clock (PCLK_i),
249+
250+ // Outputs
251+ .result (Pb_B_pre),
252+ .sum (1'b0 )
253+ );
254+ defparam
255+ csc_mult_component_5.lpm_widtha = 18 ,
256+ csc_mult_component_5.lpm_widthb = 18 ,
257+ csc_mult_component_5.lpm_widthp = 36 ,
258+ csc_mult_component_5.lpm_widths = 1 ,
259+ csc_mult_component_5.lpm_type = "LPM_MULT" ,
260+ csc_mult_component_5.lpm_representation = "SIGNED" ,
261+ csc_mult_component_5.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
262+
263+ lpm_mult csc_mult_component_6 (
264+ // Inputs
265+ .dataa ({10'h0 , R_i}),
266+ .datab (Pr_R_coeff),
267+ .aclr (1'b0 ),
268+ .clken (enable),
269+ .clock (PCLK_i),
270+
271+ // Outputs
272+ .result (Pr_R_pre),
273+ .sum (1'b0 )
274+ );
275+ defparam
276+ csc_mult_component_6.lpm_widtha = 18 ,
277+ csc_mult_component_6.lpm_widthb = 18 ,
278+ csc_mult_component_6.lpm_widthp = 36 ,
279+ csc_mult_component_6.lpm_widths = 1 ,
280+ csc_mult_component_6.lpm_type = "LPM_MULT" ,
281+ csc_mult_component_6.lpm_representation = "SIGNED" ,
282+ csc_mult_component_6.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
283+
284+ lpm_mult csc_mult_component_7 (
285+ // Inputs
286+ .dataa ({10'h0 , G_i}),
287+ .datab (Pr_G_coeff),
288+ .aclr (1'b0 ),
289+ .clken (enable),
290+ .clock (PCLK_i),
291+
292+ // Outputs
293+ .result (Pr_G_pre),
294+ .sum (1'b0 )
295+ );
296+ defparam
297+ csc_mult_component_7.lpm_widtha = 18 ,
298+ csc_mult_component_7.lpm_widthb = 18 ,
299+ csc_mult_component_7.lpm_widthp = 36 ,
300+ csc_mult_component_7.lpm_widths = 1 ,
301+ csc_mult_component_7.lpm_type = "LPM_MULT" ,
302+ csc_mult_component_7.lpm_representation = "SIGNED" ,
303+ csc_mult_component_7.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
304+
305+ lpm_mult csc_mult_component_8 (
306+ // Inputs
307+ .dataa ({10'h0 , B_i}),
308+ .datab (Pr_B_coeff),
309+ .aclr (1'b0 ),
310+ .clken (enable),
311+ .clock (PCLK_i),
312+
313+ // Outputs
314+ .result (Pr_B_pre),
315+ .sum (1'b0 )
316+ );
317+ defparam
318+ csc_mult_component_8.lpm_widtha = 18 ,
319+ csc_mult_component_8.lpm_widthb = 18 ,
320+ csc_mult_component_8.lpm_widthp = 36 ,
321+ csc_mult_component_8.lpm_widths = 1 ,
322+ csc_mult_component_8.lpm_type = "LPM_MULT" ,
323+ csc_mult_component_8.lpm_representation = "SIGNED" ,
324+ csc_mult_component_8.lpm_hint = "LPM_PIPELINE=2,MAXIMIZE_SPEED=5" ;
325+
326+ endmodule
0 commit comments