330 lines
8.1 KiB
Systemverilog
330 lines
8.1 KiB
Systemverilog
//
|
|
//
|
|
// Video crop
|
|
// Copyright (c) 2020 Grabulosaure, (c) 2021 Alexey Melnikov
|
|
//
|
|
// Integer scaling
|
|
// Copyright (c) 2021 Alexey Melnikov
|
|
//
|
|
// This program is GPL Licensed. See COPYING for the full license.
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
`timescale 1ns / 1ps
|
|
|
|
module video_freak
|
|
(
|
|
input CLK_VIDEO,
|
|
input CE_PIXEL,
|
|
input VGA_VS,
|
|
input [11:0] HDMI_WIDTH,
|
|
input [11:0] HDMI_HEIGHT,
|
|
output VGA_DE,
|
|
output reg [12:0] VIDEO_ARX,
|
|
output reg [12:0] VIDEO_ARY,
|
|
|
|
input VGA_DE_IN,
|
|
input [11:0] ARX,
|
|
input [11:0] ARY,
|
|
input [11:0] CROP_SIZE,
|
|
input [4:0] CROP_OFF, // -16...+15
|
|
input [2:0] SCALE //0 - normal, 1 - V-integer, 2 - HV-Integer-, 3 - HV-Integer+, 4 - HV-Integer
|
|
);
|
|
|
|
reg mul_start;
|
|
wire mul_run;
|
|
reg [11:0] mul_arg1, mul_arg2;
|
|
wire [23:0] mul_res;
|
|
sys_umul #(12,12) mul(CLK_VIDEO,mul_start,mul_run, mul_arg1,mul_arg2,mul_res);
|
|
|
|
reg vde;
|
|
reg [11:0] arxo,aryo;
|
|
reg [11:0] vsize;
|
|
reg [11:0] hsize;
|
|
|
|
always @(posedge CLK_VIDEO) begin
|
|
reg old_de, old_vs,ovde;
|
|
reg [11:0] vtot,vcpt,vcrop,voff;
|
|
reg [11:0] hcpt;
|
|
reg [11:0] vadj;
|
|
reg [23:0] ARXG,ARYG;
|
|
reg [11:0] arx,ary;
|
|
reg [1:0] vcalc;
|
|
|
|
if (CE_PIXEL) begin
|
|
old_de <= VGA_DE_IN;
|
|
old_vs <= VGA_VS;
|
|
if (VGA_VS & ~old_vs) begin
|
|
vcpt <= 0;
|
|
vtot <= vcpt;
|
|
vcalc <= 1;
|
|
vcrop <= (CROP_SIZE >= vcpt) ? 12'd0 : CROP_SIZE;
|
|
end
|
|
|
|
if (VGA_DE_IN) hcpt <= hcpt + 1'd1;
|
|
if (~VGA_DE_IN & old_de) begin
|
|
vcpt <= vcpt + 1'd1;
|
|
if(!vcpt) hsize <= hcpt;
|
|
hcpt <= 0;
|
|
end
|
|
end
|
|
|
|
arx <= ARX;
|
|
ary <= ARY;
|
|
|
|
vsize <= vcrop ? vcrop : vtot;
|
|
|
|
mul_start <= 0;
|
|
|
|
if(!vcrop || !ary || !arx) begin
|
|
arxo <= arx;
|
|
aryo <= ary;
|
|
end
|
|
else if (vcalc) begin
|
|
if(~mul_start & ~mul_run) begin
|
|
vcalc <= vcalc + 1'd1;
|
|
case(vcalc)
|
|
1: begin
|
|
mul_arg1 <= arx;
|
|
mul_arg2 <= vtot;
|
|
mul_start <= 1;
|
|
end
|
|
|
|
2: begin
|
|
ARXG <= mul_res;
|
|
mul_arg1 <= ary;
|
|
mul_arg2 <= vcrop;
|
|
mul_start <= 1;
|
|
end
|
|
|
|
3: begin
|
|
ARYG <= mul_res;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
else if (ARXG[23] | ARYG[23]) begin
|
|
arxo <= ARXG[23:12];
|
|
aryo <= ARYG[23:12];
|
|
end
|
|
else begin
|
|
ARXG <= ARXG << 1;
|
|
ARYG <= ARYG << 1;
|
|
end
|
|
|
|
vadj <= (vtot-vcrop) + {{6{CROP_OFF[4]}},CROP_OFF,1'b0};
|
|
voff <= vadj[11] ? 12'd0 : ((vadj[11:1] + vcrop) > vtot) ? vtot-vcrop : vadj[11:1];
|
|
ovde <= ((vcpt >= voff) && (vcpt < (vcrop + voff))) || !vcrop;
|
|
vde <= ovde;
|
|
end
|
|
|
|
assign VGA_DE = vde & VGA_DE_IN;
|
|
|
|
video_scale_int scale
|
|
(
|
|
.CLK_VIDEO(CLK_VIDEO),
|
|
.HDMI_WIDTH(HDMI_WIDTH),
|
|
.HDMI_HEIGHT(HDMI_HEIGHT),
|
|
.SCALE(SCALE),
|
|
.hsize(hsize),
|
|
.vsize(vsize),
|
|
.arx_i(arxo),
|
|
.ary_i(aryo),
|
|
.arx_o(VIDEO_ARX),
|
|
.ary_o(VIDEO_ARY)
|
|
);
|
|
|
|
endmodule
|
|
|
|
|
|
module video_scale_int
|
|
(
|
|
input CLK_VIDEO,
|
|
|
|
input [11:0] HDMI_WIDTH,
|
|
input [11:0] HDMI_HEIGHT,
|
|
|
|
input [2:0] SCALE,
|
|
|
|
input [11:0] hsize,
|
|
input [11:0] vsize,
|
|
|
|
input [11:0] arx_i,
|
|
input [11:0] ary_i,
|
|
|
|
output reg [12:0] arx_o,
|
|
output reg [12:0] ary_o
|
|
);
|
|
|
|
reg div_start;
|
|
wire div_run;
|
|
reg [23:0] div_num;
|
|
reg [11:0] div_den;
|
|
wire [23:0] div_res;
|
|
sys_udiv #(24,12) div(CLK_VIDEO,div_start,div_run, div_num,div_den,div_res);
|
|
|
|
reg mul_start;
|
|
wire mul_run;
|
|
reg [11:0] mul_arg1, mul_arg2;
|
|
wire [23:0] mul_res;
|
|
sys_umul #(12,12) mul(CLK_VIDEO,mul_start,mul_run, mul_arg1,mul_arg2,mul_res);
|
|
|
|
always @(posedge CLK_VIDEO) begin
|
|
reg [11:0] oheight,htarget,wres,hinteger,wideres;
|
|
reg [12:0] arxf,aryf;
|
|
reg [3:0] cnt;
|
|
reg narrow;
|
|
|
|
div_start <= 0;
|
|
mul_start <= 0;
|
|
|
|
if (!SCALE || (!ary_i && arx_i)) begin
|
|
arxf <= arx_i;
|
|
aryf <= ary_i;
|
|
end
|
|
else if(~div_start & ~div_run & ~mul_start & ~mul_run) begin
|
|
cnt <= cnt + 1'd1;
|
|
case(cnt)
|
|
// example ideal and non-ideal cases:
|
|
// [1] 720x400 4:3 VGA 80x25 text-mode (non-square pixels)
|
|
// [2] 640x480 4:3 VGA graphics mode (square pixels)
|
|
// [3] 512x512 4:3 X68000 graphics mode (non-square pixels)
|
|
0: begin
|
|
div_num <= HDMI_HEIGHT;
|
|
div_den <= vsize;
|
|
div_start <= 1;
|
|
end
|
|
// [1] 1080 / 400 -> 2
|
|
// [2] 1080 / 480 -> 2
|
|
// [3] 1080 / 512 -> 2
|
|
|
|
1: if(!div_res[11:0]) begin
|
|
// screen resolution is lower than video resolution.
|
|
// Integer scaling is impossible.
|
|
arxf <= arx_i;
|
|
aryf <= ary_i;
|
|
cnt <= 0;
|
|
end
|
|
else begin
|
|
mul_arg1 <= vsize;
|
|
mul_arg2 <= div_res[11:0];
|
|
mul_start <= 1;
|
|
end
|
|
// [1] 1080 / 400 * 400 -> 800
|
|
// [2] 1080 / 480 * 480 -> 960
|
|
// [3] 1080 / 512 * 512 -> 1024
|
|
|
|
2: begin
|
|
oheight <= mul_res[11:0];
|
|
if(!ary_i) begin
|
|
cnt <= 8;
|
|
end
|
|
end
|
|
|
|
3: begin
|
|
mul_arg1 <= mul_res[11:0];
|
|
mul_arg2 <= arx_i;
|
|
mul_start <= 1;
|
|
end
|
|
// [1] 1080 / 400 * 400 * 4 -> 3200
|
|
// [2] 1080 / 480 * 480 * 4 -> 3840
|
|
// [3] 1080 / 512 * 512 * 4 -> 4096
|
|
|
|
4: begin
|
|
div_num <= mul_res;
|
|
div_den <= ary_i;
|
|
div_start <= 1;
|
|
end
|
|
// [1] 1080 / 480 * 480 * 4 / 3 -> 1066
|
|
// [2] 1080 / 480 * 480 * 4 / 3 -> 1280
|
|
// [3] 1080 / 512 * 512 * 4 / 3 -> 1365
|
|
// saved as htarget
|
|
|
|
5: begin
|
|
htarget <= div_res[11:0];
|
|
div_num <= div_res;
|
|
div_den <= hsize;
|
|
div_start <= 1;
|
|
end
|
|
// computes wide scaling factor as a ceiling division
|
|
// [1] 1080 / 400 * 400 * 4 / 3 / 720 -> 1
|
|
// [2] 1080 / 480 * 480 * 4 / 3 / 640 -> 2
|
|
// [3] 1080 / 512 * 512 * 4 / 3 / 512 -> 2
|
|
|
|
6: begin
|
|
mul_arg1 <= hsize;
|
|
mul_arg2 <= div_res[11:0] ? div_res[11:0] : 12'd1;
|
|
mul_start <= 1;
|
|
end
|
|
// [1] 1080 / 400 * 400 * 4 / 3 / 720 * 720 -> 720
|
|
// [2] 1080 / 480 * 480 * 4 / 3 / 640 * 640 -> 1280
|
|
// [3] 1080 / 512 * 512 * 4 / 3 / 512 * 512 -> 1024
|
|
|
|
7: if(mul_res <= HDMI_WIDTH) begin
|
|
hinteger = mul_res[11:0];
|
|
cnt <= 12;
|
|
end
|
|
|
|
8: begin
|
|
div_num <= HDMI_WIDTH;
|
|
div_den <= hsize;
|
|
div_start <= 1;
|
|
end
|
|
// [1] 1920 / 720 -> 2
|
|
// [2] 1920 / 640 -> 3
|
|
// [3] 1920 / 512 -> 3
|
|
|
|
9: begin
|
|
mul_arg1 <= hsize;
|
|
mul_arg2 <= div_res[11:0] ? div_res[11:0] : 12'd1;
|
|
mul_start <= 1;
|
|
end
|
|
// [1] 1920 / 720 * 720 -> 1440
|
|
// [2] 1920 / 640 * 640 -> 1920
|
|
// [3] 1920 / 512 * 512 -> 1536
|
|
|
|
10: begin
|
|
hinteger <= mul_res[11:0];
|
|
mul_arg1 <= vsize;
|
|
mul_arg2 <= div_res[11:0] ? div_res[11:0] : 12'd1;
|
|
mul_start <= 1;
|
|
end
|
|
|
|
11: begin
|
|
oheight <= mul_res[11:0];
|
|
end
|
|
|
|
12: begin
|
|
wideres <= hinteger + hsize;
|
|
narrow <= ((htarget - hinteger) <= (wideres - htarget)) || (wideres > HDMI_WIDTH);
|
|
wres <= hinteger == htarget ? hinteger : wideres;
|
|
end
|
|
// [1] 1066 - 720 = 346 <= 1440 - 1066 = 374 || 1440 > 1920 -> true
|
|
// [2] 1280 - 1280 = 0 <= 1920 - 1280 = 640 || 1920 > 1920 -> true
|
|
// [3] 1365 - 1024 = 341 <= 1536 - 1365 = 171 || 1536 > 1920 -> false
|
|
// 1. narrow flag is true when mul_res[11:0] narrow width is closer to
|
|
// htarget aspect ratio target width or when wideres wider width
|
|
// does not fit to the screen.
|
|
// 2. wres becomes wideres only when mul_res[11:0] narrow width not equal
|
|
// to target width, meaning it is not optimal for source aspect ratio.
|
|
// otherwise it is set to narrow width that is optimal.
|
|
|
|
13: begin
|
|
case(SCALE)
|
|
2: arxf <= {1'b1, hinteger};
|
|
3: arxf <= {1'b1, (wres > HDMI_WIDTH) ? hinteger : wres};
|
|
4: arxf <= {1'b1, narrow ? hinteger : wres};
|
|
default: arxf <= {1'b1, div_num[11:0]};
|
|
endcase
|
|
aryf <= {1'b1, oheight};
|
|
end
|
|
endcase
|
|
end
|
|
|
|
arx_o <= arxf;
|
|
ary_o <= aryf;
|
|
end
|
|
|
|
endmodule
|