Update sys. HV-Integer scale. Vertical crop.

This commit is contained in:
sorgelig
2021-03-02 20:11:28 +08:00
parent 17fc907348
commit 2c2b476975
11 changed files with 559 additions and 180 deletions

View File

@ -39,8 +39,9 @@ module emu
output CE_PIXEL, output CE_PIXEL,
//Video aspect ratio for HDMI. Most retro systems have ratio 4:3. //Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
output [11:0] VIDEO_ARX, //if VIDEO_ARX[12] or VIDEO_ARY[12] is set then [11:0] contains scaled size instead of aspect ratio.
output [11:0] VIDEO_ARY, output [12:0] VIDEO_ARX,
output [12:0] VIDEO_ARY,
output [7:0] VGA_R, output [7:0] VGA_R,
output [7:0] VGA_G, output [7:0] VGA_G,
@ -52,6 +53,9 @@ module emu
output [1:0] VGA_SL, output [1:0] VGA_SL,
output VGA_SCALER, // Force VGA scaler output VGA_SCALER, // Force VGA scaler
input [11:0] HDMI_WIDTH,
input [11:0] HDMI_HEIGHT,
`ifdef USE_FB `ifdef USE_FB
// Use framebuffer in DDRAM (USE_FB=1 in qsf) // Use framebuffer in DDRAM (USE_FB=1 in qsf)
// FB_FORMAT: // FB_FORMAT:
@ -181,9 +185,23 @@ assign BUTTONS = 0;
assign VGA_SCALER= 0; assign VGA_SCALER= 0;
wire [1:0] ar = status[33:32]; wire [1:0] ar = status[33:32];
wire vcrop_en = status[34];
reg en270p;
always @(posedge CLK_VIDEO) begin
en270p <= ((HDMI_WIDTH == 1920) && (HDMI_HEIGHT == 1080) && !forced_scandoubler && !scale);
end
assign VIDEO_ARX = (!ar) ? 12'd4 : (ar - 1'd1); wire vga_de;
assign VIDEO_ARY = (!ar) ? 12'd3 : 12'd0; video_freak video_freak
(
.*,
.VGA_DE_IN(vga_de),
.ARX((!ar) ? 12'd4 : (ar - 1'd1)),
.ARY((!ar) ? 12'd3 : 12'd0),
.CROP_SIZE((en270p & vcrop_en) ? 10'd270 : 10'd0),
.CROP_OFF(0),
.SCALE(status[36:35])
);
`include "build_id.v" `include "build_id.v"
localparam CONF_STR = { localparam CONF_STR = {
@ -193,6 +211,9 @@ localparam CONF_STR = {
"o01,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];", "o01,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
"O12,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;", "O12,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;",
"-;", "-;",
"d0o2,Vertical Crop,Disabled,270p(5x);",
"o34,Scale,Normal,V-Integer,Narrower HV-Integer,Wider HV-Integer;",
"-;",
"O34,Stereo mix,None,25%,50%,100%;", "O34,Stereo mix,None,25%,50%,100%;",
"OST,General Sound,512KB,1MB,2MB;", "OST,General Sound,512KB,1MB,2MB;",
"-;", "-;",
@ -231,13 +252,12 @@ assign CMOSCfg[27:25]= status[27:25] + 1'd1;
//////////////////// CLOCKS /////////////////// //////////////////// CLOCKS ///////////////////
wire clk_sys; wire clk_sys;
wire clk_vid;
pll pll pll pll
( (
.refclk(CLK_50M), .refclk(CLK_50M),
.outclk_0(clk_sys), .outclk_0(clk_sys),
.outclk_1(clk_vid) .outclk_1(CLK_VIDEO)
); );
reg ce_28m; reg ce_28m;
@ -297,6 +317,7 @@ hps_io #(.STRLEN($size(CONF_STR)>>3)) hps_io
.buttons(buttons), .buttons(buttons),
.status(status), .status(status),
.status_menumask({en270p}),
.forced_scandoubler(forced_scandoubler), .forced_scandoubler(forced_scandoubler),
.gamma_bus(gamma_bus), .gamma_bus(gamma_bus),
@ -425,15 +446,13 @@ assign AUDIO_S = 1;
assign AUDIO_MIX = status[4:3]; assign AUDIO_MIX = status[4:3];
reg ce_pix; reg ce_pix;
always @(posedge clk_vid) begin always @(posedge CLK_VIDEO) begin
reg old_ce; reg old_ce;
old_ce <= ce_vid; old_ce <= ce_vid;
ce_pix <= ~old_ce & ce_vid; ce_pix <= ~old_ce & ce_vid;
end end
assign CLK_VIDEO = clk_vid;
reg VSync, HSync; reg VSync, HSync;
always @(posedge CLK_VIDEO) begin always @(posedge CLK_VIDEO) begin
HSync <= HS; HSync <= HS;
@ -447,12 +466,9 @@ assign VGA_SL = {scale == 3, scale == 2};
video_mixer #(.GAMMA(1)) video_mixer video_mixer #(.GAMMA(1)) video_mixer
( (
.*, .*,
.ce_pix_out(CE_PIXEL),
.scanlines(0),
.scandoubler(scale || forced_scandoubler), .scandoubler(scale || forced_scandoubler),
.hq2x(scale==1), .hq2x(scale==1),
.mono(0) .VGA_DE(vga_de)
); );

View File

@ -112,9 +112,9 @@ wire scandoubler = fx || forced_scandoubler;
video_mixer #(.LINE_LENGTH(WIDTH+4), .HALF_DEPTH(DW!=24), .GAMMA(GAMMA)) video_mixer video_mixer #(.LINE_LENGTH(WIDTH+4), .HALF_DEPTH(DW!=24), .GAMMA(GAMMA)) video_mixer
( (
.clk_vid(CLK_VIDEO), .CLK_VIDEO(CLK_VIDEO),
.ce_pix(CE), .ce_pix(CE),
.ce_pix_out(CE_PIXEL), .CE_PIXEL(CE_PIXEL),
.scandoubler(scandoubler), .scandoubler(scandoubler),
.hq2x(fx==1), .hq2x(fx==1),

View File

@ -429,7 +429,7 @@ ARCHITECTURE rtl OF ascal IS
SIGNAL o_readdataack,o_readdataack_sync,o_readdataack_sync2 : std_logic; SIGNAL o_readdataack,o_readdataack_sync,o_readdataack_sync2 : std_logic;
SIGNAL o_copyv : unsigned(0 TO 8); SIGNAL o_copyv : unsigned(0 TO 8);
SIGNAL o_adrs : unsigned(31 DOWNTO 0); -- Avalon address SIGNAL o_adrs : unsigned(31 DOWNTO 0); -- Avalon address
SIGNAL o_adrs_pre : natural RANGE 0 TO 2**23-1; SIGNAL o_adrs_pre : natural RANGE 0 TO 2**24-1;
SIGNAL o_stride : unsigned(13 DOWNTO 0); SIGNAL o_stride : unsigned(13 DOWNTO 0);
SIGNAL o_adrsa,o_adrsb,o_rline : std_logic; SIGNAL o_adrsa,o_adrsb,o_rline : std_logic;
SIGNAL o_ad,o_ad1,o_ad2,o_ad3 : natural RANGE 0 TO 2*BLEN-1; SIGNAL o_ad,o_ad1,o_ad2,o_ad3 : natural RANGE 0 TO 2*BLEN-1;
@ -1519,7 +1519,7 @@ BEGIN
avl_read_sync<=o_read; -- <ASYNC> avl_read_sync<=o_read; -- <ASYNC>
avl_read_sync2<=avl_read_sync; avl_read_sync2<=avl_read_sync;
avl_read_pulse<=avl_read_sync XOR avl_read_sync2; avl_read_pulse<=avl_read_sync XOR avl_read_sync2;
avl_radrs <=o_adrs AND (RAMSIZE - 1); -- <ASYNC> avl_radrs <=o_adrs; -- <ASYNC>
avl_rline <=o_rline; -- <ASYNC> avl_rline <=o_rline; -- <ASYNC>
-------------------------------------------- --------------------------------------------

109
sys/math.sv Normal file
View File

@ -0,0 +1,109 @@
// result = num/div
module sys_udiv
#(
parameter NB_NUM,
parameter NB_DIV
)
(
input clk,
input start,
output busy,
input [NB_NUM-1:0] num,
input [NB_DIV-1:0] div,
output reg [NB_NUM-1:0] result,
output reg [NB_DIV-1:0] remainder
);
reg run;
assign busy = run;
always @(posedge clk) begin
reg [5:0] cpt;
reg [NB_NUM+NB_DIV+1:0] rem;
if (start) begin
cpt <= 0;
run <= 1;
rem <= num;
end
else if (run) begin
cpt <= cpt + 1'd1;
run <= (cpt != NB_NUM + 1'd1);
remainder <= rem[NB_NUM+NB_DIV:NB_NUM+1];
if (!rem[NB_DIV + NB_NUM + 1'd1])
rem <= {rem[NB_DIV+NB_NUM:0] - (div << NB_NUM),1'b0};
else
rem <= {rem[NB_DIV+NB_NUM:0] + (div << NB_NUM),1'b0};
result <= {result[NB_NUM-2:0], !rem[NB_DIV + NB_NUM + 1'd1]};
end
end
endmodule
// result = mul1*mul2
module sys_umul
#(
parameter NB_MUL1,
parameter NB_MUL2
)
(
input clk,
input start,
output busy,
input [NB_MUL1-1:0] mul1,
input [NB_MUL2-1:0] mul2,
output reg [NB_MUL1+NB_MUL2-1:0] result
);
reg run;
assign busy = run;
always @(posedge clk) begin
reg [NB_MUL1+NB_MUL2-1:0] add;
reg [NB_MUL2-1:0] map;
if (start) begin
run <= 1;
result <= 0;
add <= mul1;
map <= mul2;
end
else if (run) begin
if(!map) run <= 0;
if(map[0]) result <= result + add;
add <= add << 1;
map <= map >> 1;
end
end
endmodule
// result = (mul1*mul2)/div
module sys_umuldiv
#(
parameter NB_MUL1,
parameter NB_MUL2,
parameter NB_DIV
)
(
input clk,
input start,
output busy,
input [NB_MUL1-1:0] mul1,
input [NB_MUL2-1:0] mul2,
input [NB_DIV-1:0] div,
output [NB_MUL1+NB_MUL2-1:0] result,
output [NB_DIV-1:0] remainder
);
wire mul_run;
wire [NB_MUL1+NB_MUL2-1:0] mul_res;
sys_umul #(NB_MUL1,NB_MUL2) umul(clk,start,mul_run,mul1,mul2,mul_res);
sys_udiv #(NB_MUL1+NB_MUL2,NB_DIV) udiv(clk,start|mul_run,busy,mul_res,div,result,remainder);
endmodule

View File

@ -2,7 +2,7 @@
// scandoubler.v // scandoubler.v
// //
// Copyright (c) 2015 Till Harbaum <till@harbaum.org> // Copyright (c) 2015 Till Harbaum <till@harbaum.org>
// Copyright (c) 2017-2019 Sorgelig // Copyright (c) 2017-2021 Alexey Melnikov
// //
// This source file is free software: you can redistribute it and/or modify // This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published // it under the terms of the GNU General Public License as published
@ -23,23 +23,20 @@ module scandoubler #(parameter LENGTH, parameter HALF_DEPTH)
( (
// system interface // system interface
input clk_vid, input clk_vid,
input ce_pix,
output ce_pix_out,
input hq2x, input hq2x,
// shifter video interface // shifter video interface
input ce_pix,
input hs_in, input hs_in,
input vs_in, input vs_in,
input hb_in, input hb_in,
input vb_in, input vb_in,
input [DWIDTH:0] r_in, input [DWIDTH:0] r_in,
input [DWIDTH:0] g_in, input [DWIDTH:0] g_in,
input [DWIDTH:0] b_in, input [DWIDTH:0] b_in,
input mono,
// output interface // output interface
output ce_pix_out,
output reg hs_out, output reg hs_out,
output vs_out, output vs_out,
output hb_out, output hb_out,
@ -109,7 +106,6 @@ Hq2x #(.LENGTH(LENGTH), .HALF_DEPTH(HALF_DEPTH)) Hq2x
.ce_in(ce_x4i), .ce_in(ce_x4i),
.inputpixel({b_d,g_d,r_d}), .inputpixel({b_d,g_d,r_d}),
.mono(mono),
.disable_hq2x(~hq2x), .disable_hq2x(~hq2x),
.reset_frame(vb_in), .reset_frame(vb_in),
.reset_line(req_line_reset), .reset_line(req_line_reset),

View File

@ -67,7 +67,7 @@ reg spdif_out_q;
reg [5:0] parity_count_q; reg [5:0] parity_count_q;
reg channel_status_bit; reg channel_status_bit_q;
//----------------------------------------------------------------- //-----------------------------------------------------------------
// Subframe Counter // Subframe Counter
@ -144,19 +144,20 @@ assign subframe_w[28] = 1'b0; // Valid
assign subframe_w[29] = 1'b0; assign subframe_w[29] = 1'b0;
// Timeslots 30 = Channel status bit // Timeslots 30 = Channel status bit
assign subframe_w[30] = channel_status_bit ; //was constant 1'b0 enabling copy-bit; assign subframe_w[30] = channel_status_bit_q ; //was constant 1'b0 enabling copy-bit;
// Timeslots 31 = Even Parity bit (31:4) // Timeslots 31 = Even Parity bit (31:4)
assign subframe_w[31] = 1'b0; assign subframe_w[31] = 1'b0;
//----------------------------------------------------------------- //-----------------------------------------------------------------
// Preamble // Preamble and Channel status bit
//----------------------------------------------------------------- //-----------------------------------------------------------------
localparam PREAMBLE_Z = 8'b00010111; // "B" channel A data at start of block localparam PREAMBLE_Z = 8'b00010111; // "B" channel A data at start of block
localparam PREAMBLE_Y = 8'b00100111; // "W" channel B data localparam PREAMBLE_Y = 8'b00100111; // "W" channel B data
localparam PREAMBLE_X = 8'b01000111; // "M" channel A data not at start of block localparam PREAMBLE_X = 8'b01000111; // "M" channel A data not at start of block
reg [7:0] preamble_r; reg [7:0] preamble_r;
reg channel_status_bit_r;
always @ * always @ *
begin begin
@ -172,20 +173,28 @@ begin
preamble_r = PREAMBLE_X; // X(M) preamble_r = PREAMBLE_X; // X(M)
if (subframe_count_q[8:1] == 8'd2) // frame 2 => subframes 4 and 5 => 0 = copy inhibited, 1 = copy permitted if (subframe_count_q[8:1] == 8'd2) // frame 2 => subframes 4 and 5 => 0 = copy inhibited, 1 = copy permitted
channel_status_bit = 1'b1; channel_status_bit_r = 1'b1;
else if (subframe_count_q[8:1] == 8'd15) // frame 15 => 0 = no indication, 1 = original media else if (subframe_count_q[8:1] == 8'd15) // frame 15 => 0 = no indication, 1 = original media
channel_status_bit = 1'b1; channel_status_bit_r = 1'b1;
else if (subframe_count_q[8:1] == 8'd25) // frame 24 to 27 => sample frequency, 0100 = 48kHz, 0000 = 44kHz (l2r) else if (subframe_count_q[8:1] == 8'd25) // frame 24 to 27 => sample frequency, 0100 = 48kHz, 0000 = 44kHz (l2r)
channel_status_bit = 1'b1; channel_status_bit_r = 1'b1;
else else
channel_status_bit = 1'b0; // everything else defaults to 0 channel_status_bit_r = 1'b0; // everything else defaults to 0
end end
always @ (posedge rst_i or posedge clk_i ) always @ (posedge rst_i or posedge clk_i )
if (rst_i == 1'b1) begin
if (rst_i == 1'b1)
begin
preamble_q <= 8'h00; preamble_q <= 8'h00;
else if (load_subframe_q) channel_status_bit_q <= 1'b0;
end
else if (load_subframe_q)
begin
preamble_q <= preamble_r; preamble_q <= preamble_r;
channel_status_bit_q <= channel_status_bit_r;
end
end
//----------------------------------------------------------------- //-----------------------------------------------------------------
// Parity Counter // Parity Counter

View File

@ -3,12 +3,14 @@ set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) s
set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) sys_top.sdc ] set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) sys_top.sdc ]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) ascal.vhd ] set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) ascal.vhd ]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) pll_hdmi_adj.vhd ] set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) pll_hdmi_adj.vhd ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) math.sv ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) hq2x.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) hq2x.sv ]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v ]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scanlines.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scanlines.v ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_cleaner.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_cleaner.sv ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) gamma_corr.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) gamma_corr.sv ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_mixer.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_mixer.sv ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_freak.sv ]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) arcade_video.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) arcade_video.v ]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) vga_out.sv ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) vga_out.sv ]

View File

@ -32,7 +32,7 @@ set_false_path -to {cfg[*]}
set_false_path -from {cfg[*]} set_false_path -from {cfg[*]}
set_false_path -from {VSET[*]} set_false_path -from {VSET[*]}
set_false_path -to {wcalc[*] hcalc[*]} set_false_path -to {wcalc[*] hcalc[*]}
set_false_path -to {width[*] height[*]} set_false_path -to {hdmi_width[*] hdmi_height[*]}
set_multicycle_path -to {*_osd|osd_vcnt*} -setup 2 set_multicycle_path -to {*_osd|osd_vcnt*} -setup 2
set_multicycle_path -to {*_osd|osd_vcnt*} -hold 1 set_multicycle_path -to {*_osd|osd_vcnt*} -hold 1
@ -62,3 +62,8 @@ set_false_path -from {ascal|o_ivsize*}
set_false_path -from {ascal|o_format*} set_false_path -from {ascal|o_format*}
set_false_path -from {ascal|o_hdown} set_false_path -from {ascal|o_hdown}
set_false_path -from {ascal|o_vdown} set_false_path -from {ascal|o_vdown}
set_false_path -from {ascal|o_hmin* ascal|o_hmax* ascal|o_vmin* ascal|o_vmax*}
set_false_path -from {ascal|o_hdisp* ascal|o_vdisp*}
set_false_path -from {ascal|o_htotal* ascal|o_vtotal*}
set_false_path -from {ascal|o_hsstart* ascal|o_vsstart* ascal|o_hsend* ascal|o_vsend*}
set_false_path -from {ascal|o_hsize* ascal|o_vsize*}

View File

@ -316,7 +316,7 @@ reg [6:0] coef_addr;
reg [8:0] coef_data; reg [8:0] coef_data;
reg coef_wr = 0; reg coef_wr = 0;
wire[11:0] ARX, ARY; wire[12:0] ARX, ARY;
reg [11:0] VSET = 0, HSET = 0; reg [11:0] VSET = 0, HSET = 0;
reg FREESCALE = 0; reg FREESCALE = 0;
reg [2:0] scaler_flt; reg [2:0] scaler_flt;
@ -337,10 +337,10 @@ reg [23:0] acy0 = -24'd6216759;
reg [23:0] acy1 = 24'd6143386; reg [23:0] acy1 = 24'd6143386;
reg [23:0] acy2 = -24'd2023767; reg [23:0] acy2 = -24'd2023767;
reg areset = 0; reg areset = 0;
reg [11:0] arc1x = 0; reg [12:0] arc1x = 0;
reg [11:0] arc1y = 0; reg [12:0] arc1y = 0;
reg [11:0] arc2x = 0; reg [12:0] arc2x = 0;
reg [11:0] arc2y = 0; reg [12:0] arc2y = 0;
always@(posedge clk_sys) begin always@(posedge clk_sys) begin
reg [7:0] cmd; reg [7:0] cmd;
@ -430,6 +430,7 @@ always@(posedge clk_sys) begin
6: LFB_HMAX <= io_din[11:0]; 6: LFB_HMAX <= io_din[11:0];
7: LFB_VMIN <= io_din[11:0]; 7: LFB_VMIN <= io_din[11:0];
8: LFB_VMAX <= io_din[11:0]; 8: LFB_VMAX <= io_din[11:0];
9: LFB_STRIDE <= io_din[13:0];
endcase endcase
end end
if(cmd == 'h25) {led_overtake, led_state} <= io_din; if(cmd == 'h25) {led_overtake, led_state} <= io_din;
@ -462,10 +463,10 @@ always@(posedge clk_sys) begin
if(cmd == 'h3A) begin if(cmd == 'h3A) begin
cnt <= cnt + 1'd1; cnt <= cnt + 1'd1;
case(cnt[3:0]) case(cnt[3:0])
0: arc1x <= io_din[11:0]; 0: arc1x <= io_din[12:0];
1: arc1y <= io_din[11:0]; 1: arc1y <= io_din[12:0];
2: arc2x <= io_din[11:0]; 2: arc2x <= io_din[12:0];
3: arc2y <= io_din[11:0]; 3: arc2y <= io_din[12:0];
endcase endcase
end end
end end
@ -747,6 +748,7 @@ reg [11:0] LFB_HMAX = 0;
reg [11:0] LFB_VMIN = 0; reg [11:0] LFB_VMIN = 0;
reg [11:0] LFB_VMAX = 0; reg [11:0] LFB_VMAX = 0;
reg [31:0] LFB_BASE = 0; reg [31:0] LFB_BASE = 0;
reg [13:0] LFB_STRIDE = 0;
reg FB_EN = 0; reg FB_EN = 0;
reg [5:0] FB_FMT = 0; reg [5:0] FB_FMT = 0;
@ -762,7 +764,7 @@ always @(posedge clk_sys) begin
FB_WIDTH <= LFB_WIDTH; FB_WIDTH <= LFB_WIDTH;
FB_HEIGHT <= LFB_HEIGHT; FB_HEIGHT <= LFB_HEIGHT;
FB_BASE <= LFB_BASE; FB_BASE <= LFB_BASE;
FB_STRIDE <= 0; FB_STRIDE <= LFB_STRIDE;
end end
else begin else begin
FB_FMT <= fb_fmt; FB_FMT <= fb_fmt;
@ -778,74 +780,123 @@ reg fb_vbl;
always @(posedge clk_vid) fb_vbl <= hdmi_vbl; always @(posedge clk_vid) fb_vbl <= hdmi_vbl;
`endif `endif
reg ar_md_start;
wire ar_md_busy;
reg [11:0] ar_md_mul1, ar_md_mul2, ar_md_div;
wire [11:0] ar_md_res;
sys_umuldiv #(12,12,12) ar_muldiv
(
.clk(clk_vid),
.start(ar_md_start),
.busy(ar_md_busy),
.mul1(ar_md_mul1),
.mul2(ar_md_mul2),
.div(ar_md_div),
.result(ar_md_res)
);
reg [11:0] hmin; reg [11:0] hmin;
reg [11:0] hmax; reg [11:0] hmax;
reg [11:0] vmin; reg [11:0] vmin;
reg [11:0] vmax; reg [11:0] vmax;
reg [11:0] hdmi_height;
reg [11:0] hdmi_width;
always @(posedge clk_vid) begin always @(posedge clk_vid) begin
reg [31:0] wcalc; reg [11:0] hmini,hmaxi,vmini,vmaxi;
reg [31:0] hcalc; reg [11:0] wcalc,videow,arx;
reg [11:0] hcalc,videoh,ary;
reg [2:0] state; reg [2:0] state;
reg [11:0] videow; reg xy;
reg [11:0] videoh;
reg [11:0] height;
reg [11:0] width;
reg [11:0] arx;
reg [11:0] ary;
height <= (VSET && (VSET < HEIGHT)) ? VSET : HEIGHT; hdmi_height <= (VSET && (VSET < HEIGHT)) ? VSET : HEIGHT;
width <= (HSET && (HSET < WIDTH)) ? HSET : WIDTH; hdmi_width <= (HSET && (HSET < WIDTH)) ? HSET : WIDTH;
if(!ARY) begin if(!ARY) begin
if(ARX == 1) begin if(ARX == 1) begin
arx <= arc1x; arx <= arc1x[11:0];
ary <= arc1y; ary <= arc1y[11:0];
xy <= arc1x[12] | arc1y[12];
end end
else if(ARX == 2) begin else if(ARX == 2) begin
arx <= arc2x; arx <= arc2x[11:0];
ary <= arc2y; ary <= arc2y[11:0];
xy <= arc2x[12] | arc2y[12];
end end
else begin else begin
arx <= 0; arx <= 0;
ary <= 0; ary <= 0;
xy <= 0;
end end
end end
else begin else begin
arx <= ARX; arx <= ARX[11:0];
ary <= ARY; ary <= ARY[11:0];
xy <= ARX[12] | ARY[12];
end end
ar_md_start <= 0;
state <= state + 1'd1; state <= state + 1'd1;
case(state) case(state)
0: if(LFB_EN) begin 0: if(LFB_EN) begin
hmin <= LFB_HMIN; hmini <= LFB_HMIN;
vmin <= LFB_VMIN; vmini <= LFB_VMIN;
hmax <= LFB_HMAX; hmaxi <= LFB_HMAX;
vmax <= LFB_VMAX; vmaxi <= LFB_VMAX;
state<= 0; state <= 0;
end end
else if(FREESCALE || !arx || !ary) begin else if(FREESCALE || !arx || !ary) begin
wcalc <= width; wcalc <= hdmi_width;
hcalc <= height; hcalc <= hdmi_height;
state <= 6;
end end
else begin else if(xy) begin
wcalc <= (height*arx)/ary; wcalc <= arx;
hcalc <= (width*ary)/arx; hcalc <= ary;
state <= 6;
end
1: begin
ar_md_mul1 <= hdmi_height;
ar_md_mul2 <= arx;
ar_md_div <= ary;
ar_md_start<= 1;
end
2: begin
wcalc <= ar_md_res;
if(ar_md_start | ar_md_busy) state <= 2;
end
3: begin
ar_md_mul1 <= hdmi_width;
ar_md_mul2 <= ary;
ar_md_div <= arx;
ar_md_start<= 1;
end
4: begin
hcalc <= ar_md_res;
if(ar_md_start | ar_md_busy) state <= 4;
end end
6: begin 6: begin
videow <= (wcalc > width) ? width : wcalc[11:0]; videow <= (wcalc > hdmi_width) ? hdmi_width : wcalc[11:0];
videoh <= (hcalc > height) ? height : hcalc[11:0]; videoh <= (hcalc > hdmi_height) ? hdmi_height : hcalc[11:0];
end end
7: begin 7: begin
hmin <= ((WIDTH - videow)>>1); hmini <= ((WIDTH - videow)>>1);
hmax <= ((WIDTH - videow)>>1) + videow - 1'd1; hmaxi <= ((WIDTH - videow)>>1) + videow - 1'd1;
vmin <= ((HEIGHT - videoh)>>1); vmini <= ((HEIGHT - videoh)>>1);
vmax <= ((HEIGHT - videoh)>>1) + videoh - 1'd1; vmaxi <= ((HEIGHT - videoh)>>1) + videoh - 1'd1;
end end
endcase endcase
hmin <= hmini;
hmax <= hmaxi;
vmin <= vmini;
vmax <= vmaxi;
end end
`ifndef DEBUG_NOHDMI `ifndef DEBUG_NOHDMI
@ -1483,7 +1534,10 @@ emu emu
( (
.CLK_50M(FPGA_CLK2_50), .CLK_50M(FPGA_CLK2_50),
.RESET(reset), .RESET(reset),
.HPS_BUS({f1, HDMI_TX_VS, clk_100m, clk_ihdmi, ce_hpix, hde_emu, hhs_fix, hvs_fix, io_wait, clk_sys, io_fpga, io_uio, io_strobe, io_wide, io_din, io_dout}), .HPS_BUS({f1, HDMI_TX_VS,
clk_100m, clk_ihdmi,
ce_hpix, hde_emu, hhs_fix, hvs_fix,
io_wait, clk_sys, io_fpga, io_uio, io_strobe, io_wide, io_din, io_dout}),
.VGA_R(r_out), .VGA_R(r_out),
.VGA_G(g_out), .VGA_G(g_out),
@ -1494,6 +1548,9 @@ emu emu
.VGA_F1(f1), .VGA_F1(f1),
.VGA_SCALER(vga_force_scaler), .VGA_SCALER(vga_force_scaler),
.HDMI_WIDTH(direct_video ? 12'd0 : hdmi_width),
.HDMI_HEIGHT(direct_video ? 12'd0 : hdmi_height),
.CLK_VIDEO(clk_vid), .CLK_VIDEO(clk_vid),
.CE_PIXEL(ce_pix), .CE_PIXEL(ce_pix),
.VGA_SL(scanlines), .VGA_SL(scanlines),

231
sys/video_freak.sv Normal file
View File

@ -0,0 +1,231 @@
//
//
// 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,
input [1:0] SCALE
);
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,vcalc,ovde;
reg [11:0] vtot,vcpt,vcrop,voff;
reg [11:0] hcpt;
reg [11:0] vadj;
reg [23:0] ARXG,ARYG,arx,ary;
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) || !CROP_SIZE) ? 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;
if(!vcrop || !ary || !arx) begin
arxo <= arx[11:0];
aryo <= ary[11:0];
vsize <= vtot;
end
else if (vcalc) begin
ARXG <= arx * vtot;
ARYG <= ary * vcrop;
vcalc <= 0;
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),
.hsize(hsize),
.vsize(vsize),
.arx_i(arxo),
.ary_i(aryo),
.scale(SCALE),
.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 [11:0] hsize,
input [11:0] vsize,
input [11:0] arx_i,
input [11:0] ary_i,
input [1:0] scale,
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);
wire [11:0] wideres = mul_res[11:0] + hsize;
reg [12:0] arxf,aryf;
always @(posedge CLK_VIDEO) begin
reg [11:0] oheight;
reg [3:0] cnt;
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)
0: begin
div_num <= HDMI_HEIGHT;
div_den <= vsize;
div_start <= 1;
end
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
2: begin
oheight <= mul_res[11:0];
mul_arg1 <= mul_res[11:0];
mul_arg2 <= arx_i;
mul_start <= 1;
end
3: begin
div_num <= mul_res;
div_den <= ary_i;
div_start <= 1;
end
4: begin
div_num <= div_res;
div_den <= hsize;
div_start <= 1;
end
5: begin
mul_arg1 <= hsize;
mul_arg2 <= div_res[11:0] ? div_res[11:0] : 12'd1;
mul_start <= 1;
end
6: if(mul_res <= HDMI_WIDTH) cnt <= 8;
else begin
div_num <= HDMI_WIDTH;
div_den <= hsize;
div_start <= 1;
end
7: begin
mul_arg1 <= hsize;
mul_arg2 <= div_res[11:0] ? div_res[11:0] : 12'd1;
mul_start <= 1;
end
8: begin
arxf <= {1'b1, ~scale[1] ? div_num[11:0] : (scale[0] && (wideres <= HDMI_WIDTH)) ? wideres : mul_res[11:0]};
aryf <= {1'b1, oheight};
end
endcase
end
arx_o <= arxf;
ary_o <= aryf;
end
endmodule

View File

@ -1,6 +1,6 @@
// //
// //
// Copyright (c) 2017 Sorgelig // Copyright (c) 2017,2021 Alexey Melnikov
// //
// This program is GPL Licensed. See COPYING for the full license. // This program is GPL Licensed. See COPYING for the full license.
// //
@ -15,8 +15,6 @@
// May be less if line_start is used. // May be less if line_start is used.
// //
// HALF_DEPTH: If =1 then color dept is 4 bits per component // HALF_DEPTH: If =1 then color dept is 4 bits per component
// For half depth 8 bits monochrome is available with
// mono signal enabled and color = {G, R}
// //
// altera message_off 10720 // altera message_off 10720
// altera message_off 12161 // altera message_off 12161
@ -28,32 +26,21 @@ module video_mixer
parameter GAMMA = 0 parameter GAMMA = 0
) )
( (
// video clock input CLK_VIDEO, // should be multiple by (ce_pix*4)
// it should be multiple by (ce_pix*4). output reg CE_PIXEL, // output pixel clock enable
input clk_vid,
// Pixel clock or clock_enable (both are accepted). input ce_pix, // input pixel clock or clock_enable
input ce_pix,
output ce_pix_out,
input scandoubler, input scandoubler,
input hq2x, // high quality 2x scaling
// scanlines (00-none 01-25% 10-50% 11-75%) inout [21:0] gamma_bus,
input [1:0] scanlines,
// High quality 2x scaling
input hq2x,
// color // color
input [DWIDTH:0] R, input [DWIDTH:0] R,
input [DWIDTH:0] G, input [DWIDTH:0] G,
input [DWIDTH:0] B, input [DWIDTH:0] B,
// Monochrome mode (for HALF_DEPTH only)
input mono,
inout [21:0] gamma_bus,
// Positive pulses. // Positive pulses.
input HSync, input HSync,
input VSync, input VSync,
@ -75,9 +62,9 @@ localparam HALF_DEPTH_SD = GAMMA ? 0 : HALF_DEPTH;
generate generate
if(GAMMA && HALF_DEPTH) begin if(GAMMA && HALF_DEPTH) begin
wire [7:0] R_in = mono ? {G,R} : {R,R}; wire [7:0] R_in = {R,R};
wire [7:0] G_in = mono ? {G,R} : {G,G}; wire [7:0] G_in = {G,G};
wire [7:0] B_in = mono ? {G,R} : {B,B}; wire [7:0] B_in = {B,B};
end else begin end else begin
wire [DWIDTH:0] R_in = R; wire [DWIDTH:0] R_in = R;
wire [DWIDTH:0] G_in = G; wire [DWIDTH:0] G_in = G;
@ -95,7 +82,7 @@ generate
assign gamma_bus[21] = 1; assign gamma_bus[21] = 1;
gamma_corr gamma( gamma_corr gamma(
.clk_sys(gamma_bus[20]), .clk_sys(gamma_bus[20]),
.clk_vid(clk_vid), .clk_vid(CLK_VIDEO),
.ce_pix(ce_pix), .ce_pix(ce_pix),
.gamma_en(gamma_bus[19]), .gamma_en(gamma_bus[19]),
@ -122,7 +109,6 @@ generate
end end
endgenerate endgenerate
wire [DWIDTH_SD:0] R_sd; wire [DWIDTH_SD:0] R_sd;
wire [DWIDTH_SD:0] G_sd; wire [DWIDTH_SD:0] G_sd;
wire [DWIDTH_SD:0] B_sd; wire [DWIDTH_SD:0] B_sd;
@ -130,7 +116,10 @@ wire hs_sd, vs_sd, hb_sd, vb_sd, ce_pix_sd;
scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH_SD)) sd scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH_SD)) sd
( (
.*, .clk_vid(CLK_VIDEO),
.hq2x(hq2x),
.ce_pix(ce_pix),
.hs_in(hs_g), .hs_in(hs_g),
.vs_in(vs_g), .vs_in(vs_g),
.hb_in(hb_g), .hb_in(hb_g),
@ -153,86 +142,51 @@ wire [DWIDTH_SD:0] rt = (scandoubler ? R_sd : R_gamma);
wire [DWIDTH_SD:0] gt = (scandoubler ? G_sd : G_gamma); wire [DWIDTH_SD:0] gt = (scandoubler ? G_sd : G_gamma);
wire [DWIDTH_SD:0] bt = (scandoubler ? B_sd : B_gamma); wire [DWIDTH_SD:0] bt = (scandoubler ? B_sd : B_gamma);
generate always @(posedge CLK_VIDEO) begin
if(!GAMMA && HALF_DEPTH) begin reg [7:0] r,g,b;
wire [7:0] r = mono ? {gt,rt} : {rt,rt}; reg hde,vde,hs,vs, old_vs;
wire [7:0] g = mono ? {gt,rt} : {gt,gt};
wire [7:0] b = mono ? {gt,rt} : {bt,bt};
end else begin
wire [7:0] r = rt;
wire [7:0] g = gt;
wire [7:0] b = bt;
end
endgenerate
wire hs = (scandoubler ? hs_sd : hs_g);
wire vs = (scandoubler ? vs_sd : vs_g);
assign ce_pix_out = scandoubler ? ce_pix_sd : ce_pix;
reg scanline = 0;
always @(posedge clk_vid) begin
reg old_hs, old_vs;
old_hs <= hs;
old_vs <= vs;
if(old_hs && ~hs) scanline <= ~scanline;
if(old_vs && ~vs) scanline <= 0;
end
wire hde = scandoubler ? ~hb_sd : ~hb_g;
wire vde = scandoubler ? ~vb_sd : ~vb_g;
reg [7:0] v_r,v_g,v_b;
reg v_vs,v_hs,v_de;
always @(posedge clk_vid) begin
reg old_hde; reg old_hde;
reg old_ce;
reg ce_osc, fs_osc;
if(ce_pix_out) begin old_ce <= ce_pix;
case(scanlines & {scanline, scanline}) ce_osc <= ce_osc | (old_ce ^ ce_pix);
1: begin // reduce 25% = 1/2 + 1/4
v_r <= {1'b0, r[7:1]} + {2'b00, r[7:2]}; old_vs <= vs;
v_g <= {1'b0, g[7:1]} + {2'b00, g[7:2]}; if(~old_vs & vs) begin
v_b <= {1'b0, b[7:1]} + {2'b00, b[7:2]}; fs_osc <= ce_osc;
ce_osc <= 0;
end end
2: begin // reduce 50% = 1/2 CE_PIXEL <= scandoubler ? ce_pix_sd : fs_osc ? (~old_ce & ce_pix) : ce_pix;
v_r <= {1'b0, r[7:1]};
v_g <= {1'b0, g[7:1]}; if(!GAMMA && HALF_DEPTH) begin
v_b <= {1'b0, b[7:1]}; r <= {rt,rt};
g <= {gt,gt};
b <= {bt,bt};
end
else begin
r <= rt;
g <= gt;
b <= bt;
end end
3: begin // reduce 75% = 1/4 hde <= scandoubler ? ~hb_sd : ~hb_g;
v_r <= {2'b00, r[7:2]}; vde <= scandoubler ? ~vb_sd : ~vb_g;
v_g <= {2'b00, g[7:2]}; vs <= scandoubler ? vs_sd : vs_g;
v_b <= {2'b00, b[7:2]}; hs <= scandoubler ? hs_sd : hs_g;
end
default: begin if(CE_PIXEL) begin
v_r <= r; VGA_R <= r;
v_g <= g; VGA_G <= g;
v_b <= b; VGA_B <= b;
end
endcase
v_vs <= vs; VGA_VS <= vs;
v_hs <= hs; VGA_HS <= hs;
old_hde <= hde; old_hde <= hde;
if(~old_hde && hde) v_de <= vde; if(old_hde ^ hde) VGA_DE <= vde & hde;
if(old_hde && ~hde) v_de <= 0;
end end
end end
always @(posedge clk_vid) if(ce_pix_out) begin
VGA_R <= v_r;
VGA_G <= v_g;
VGA_B <= v_b;
VGA_HS <= v_hs;
VGA_VS <= v_vs;
VGA_DE <= v_de;
end
endmodule endmodule