From 2c2b4769752e3c9ae7fece0b0214b009d0f37469 Mon Sep 17 00:00:00 2001 From: sorgelig Date: Tue, 2 Mar 2021 20:11:28 +0800 Subject: [PATCH] Update sys. HV-Integer scale. Vertical crop. --- TSConf.sv | 42 ++++++--- sys/arcade_video.v | 4 +- sys/ascal.vhd | 4 +- sys/math.sv | 109 +++++++++++++++++++++ sys/scandoubler.v | 10 +- sys/spdif.v | 31 +++--- sys/sys.qip | 2 + sys/sys_top.sdc | 7 +- sys/sys_top.v | 147 ++++++++++++++++++++--------- sys/video_freak.sv | 231 +++++++++++++++++++++++++++++++++++++++++++++ sys/video_mixer.sv | 152 +++++++++++------------------ 11 files changed, 559 insertions(+), 180 deletions(-) create mode 100644 sys/math.sv create mode 100644 sys/video_freak.sv diff --git a/TSConf.sv b/TSConf.sv index 170a5a4..03f8970 100644 --- a/TSConf.sv +++ b/TSConf.sv @@ -39,8 +39,9 @@ module emu output CE_PIXEL, //Video aspect ratio for HDMI. Most retro systems have ratio 4:3. - output [11:0] VIDEO_ARX, - output [11:0] VIDEO_ARY, + //if VIDEO_ARX[12] or VIDEO_ARY[12] is set then [11:0] contains scaled size instead of aspect ratio. + output [12:0] VIDEO_ARX, + output [12:0] VIDEO_ARY, output [7:0] VGA_R, output [7:0] VGA_G, @@ -51,6 +52,9 @@ module emu output VGA_F1, output [1:0] VGA_SL, output VGA_SCALER, // Force VGA scaler + + input [11:0] HDMI_WIDTH, + input [11:0] HDMI_HEIGHT, `ifdef USE_FB // Use framebuffer in DDRAM (USE_FB=1 in qsf) @@ -181,9 +185,23 @@ assign BUTTONS = 0; assign VGA_SCALER= 0; 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); -assign VIDEO_ARY = (!ar) ? 12'd3 : 12'd0; +wire vga_de; +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" localparam CONF_STR = { @@ -193,6 +211,9 @@ localparam CONF_STR = { "o01,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];", "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%;", "OST,General Sound,512KB,1MB,2MB;", "-;", @@ -231,13 +252,12 @@ assign CMOSCfg[27:25]= status[27:25] + 1'd1; //////////////////// CLOCKS /////////////////// wire clk_sys; -wire clk_vid; pll pll ( .refclk(CLK_50M), .outclk_0(clk_sys), - .outclk_1(clk_vid) + .outclk_1(CLK_VIDEO) ); reg ce_28m; @@ -297,6 +317,7 @@ hps_io #(.STRLEN($size(CONF_STR)>>3)) hps_io .buttons(buttons), .status(status), + .status_menumask({en270p}), .forced_scandoubler(forced_scandoubler), .gamma_bus(gamma_bus), @@ -425,15 +446,13 @@ assign AUDIO_S = 1; assign AUDIO_MIX = status[4:3]; reg ce_pix; -always @(posedge clk_vid) begin +always @(posedge CLK_VIDEO) begin reg old_ce; old_ce <= ce_vid; ce_pix <= ~old_ce & ce_vid; end -assign CLK_VIDEO = clk_vid; - reg VSync, HSync; always @(posedge CLK_VIDEO) begin HSync <= HS; @@ -447,12 +466,9 @@ assign VGA_SL = {scale == 3, scale == 2}; video_mixer #(.GAMMA(1)) video_mixer ( .*, - .ce_pix_out(CE_PIXEL), - - .scanlines(0), .scandoubler(scale || forced_scandoubler), .hq2x(scale==1), - .mono(0) + .VGA_DE(vga_de) ); diff --git a/sys/arcade_video.v b/sys/arcade_video.v index a1683f5..ff554a5 100644 --- a/sys/arcade_video.v +++ b/sys/arcade_video.v @@ -112,9 +112,9 @@ wire scandoubler = fx || forced_scandoubler; 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_out(CE_PIXEL), + .CE_PIXEL(CE_PIXEL), .scandoubler(scandoubler), .hq2x(fx==1), diff --git a/sys/ascal.vhd b/sys/ascal.vhd index 6d687ac..a65ccc1 100644 --- a/sys/ascal.vhd +++ b/sys/ascal.vhd @@ -429,7 +429,7 @@ ARCHITECTURE rtl OF ascal IS SIGNAL o_readdataack,o_readdataack_sync,o_readdataack_sync2 : std_logic; SIGNAL o_copyv : unsigned(0 TO 8); 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_adrsa,o_adrsb,o_rline : std_logic; 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; -- avl_read_sync2<=avl_read_sync; avl_read_pulse<=avl_read_sync XOR avl_read_sync2; - avl_radrs <=o_adrs AND (RAMSIZE - 1); -- + avl_radrs <=o_adrs; -- avl_rline <=o_rline; -- -------------------------------------------- diff --git a/sys/math.sv b/sys/math.sv new file mode 100644 index 0000000..e7c0144 --- /dev/null +++ b/sys/math.sv @@ -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 diff --git a/sys/scandoubler.v b/sys/scandoubler.v index a1d5a44..81e7c3f 100644 --- a/sys/scandoubler.v +++ b/sys/scandoubler.v @@ -2,7 +2,7 @@ // scandoubler.v // // Copyright (c) 2015 Till Harbaum -// Copyright (c) 2017-2019 Sorgelig +// Copyright (c) 2017-2021 Alexey Melnikov // // 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 @@ -23,23 +23,20 @@ module scandoubler #(parameter LENGTH, parameter HALF_DEPTH) ( // system interface input clk_vid, - input ce_pix, - output ce_pix_out, - input hq2x, // shifter video interface + input ce_pix, input hs_in, input vs_in, input hb_in, input vb_in, - input [DWIDTH:0] r_in, input [DWIDTH:0] g_in, input [DWIDTH:0] b_in, - input mono, // output interface + output ce_pix_out, output reg hs_out, output vs_out, output hb_out, @@ -109,7 +106,6 @@ Hq2x #(.LENGTH(LENGTH), .HALF_DEPTH(HALF_DEPTH)) Hq2x .ce_in(ce_x4i), .inputpixel({b_d,g_d,r_d}), - .mono(mono), .disable_hq2x(~hq2x), .reset_frame(vb_in), .reset_line(req_line_reset), diff --git a/sys/spdif.v b/sys/spdif.v index db5027c..eee2b08 100644 --- a/sys/spdif.v +++ b/sys/spdif.v @@ -67,7 +67,7 @@ reg spdif_out_q; reg [5:0] parity_count_q; -reg channel_status_bit; +reg channel_status_bit_q; //----------------------------------------------------------------- // Subframe Counter @@ -144,19 +144,20 @@ assign subframe_w[28] = 1'b0; // Valid assign subframe_w[29] = 1'b0; // 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) 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_Y = 8'b00100111; // "W" channel B data localparam PREAMBLE_X = 8'b01000111; // "M" channel A data not at start of block reg [7:0] preamble_r; +reg channel_status_bit_r; always @ * begin @@ -172,20 +173,28 @@ begin 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 - 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 - 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) - channel_status_bit = 1'b1; + channel_status_bit_r = 1'b1; else - channel_status_bit = 1'b0; // everything else defaults to 0 + channel_status_bit_r = 1'b0; // everything else defaults to 0 end always @ (posedge rst_i or posedge clk_i ) -if (rst_i == 1'b1) - preamble_q <= 8'h00; -else if (load_subframe_q) - preamble_q <= preamble_r; +begin + if (rst_i == 1'b1) + begin + preamble_q <= 8'h00; + channel_status_bit_q <= 1'b0; + end + else if (load_subframe_q) + begin + preamble_q <= preamble_r; + channel_status_bit_q <= channel_status_bit_r; + end +end //----------------------------------------------------------------- // Parity Counter diff --git a/sys/sys.qip b/sys/sys.qip index 242e396..eeae907 100644 --- a/sys/sys.qip +++ b/sys/sys.qip @@ -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 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 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 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 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) 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) osd.v ] set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) vga_out.sv ] diff --git a/sys/sys_top.sdc b/sys/sys_top.sdc index 24e713b..8187969 100644 --- a/sys/sys_top.sdc +++ b/sys/sys_top.sdc @@ -32,7 +32,7 @@ set_false_path -to {cfg[*]} set_false_path -from {cfg[*]} set_false_path -from {VSET[*]} 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*} -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_hdown} 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*} diff --git a/sys/sys_top.v b/sys/sys_top.v index 034d235..8f0043c 100644 --- a/sys/sys_top.v +++ b/sys/sys_top.v @@ -316,7 +316,7 @@ reg [6:0] coef_addr; reg [8:0] coef_data; reg coef_wr = 0; -wire[11:0] ARX, ARY; +wire[12:0] ARX, ARY; reg [11:0] VSET = 0, HSET = 0; reg FREESCALE = 0; reg [2:0] scaler_flt; @@ -337,10 +337,10 @@ reg [23:0] acy0 = -24'd6216759; reg [23:0] acy1 = 24'd6143386; reg [23:0] acy2 = -24'd2023767; reg areset = 0; -reg [11:0] arc1x = 0; -reg [11:0] arc1y = 0; -reg [11:0] arc2x = 0; -reg [11:0] arc2y = 0; +reg [12:0] arc1x = 0; +reg [12:0] arc1y = 0; +reg [12:0] arc2x = 0; +reg [12:0] arc2y = 0; always@(posedge clk_sys) begin reg [7:0] cmd; @@ -430,6 +430,7 @@ always@(posedge clk_sys) begin 6: LFB_HMAX <= io_din[11:0]; 7: LFB_VMIN <= io_din[11:0]; 8: LFB_VMAX <= io_din[11:0]; + 9: LFB_STRIDE <= io_din[13:0]; endcase end if(cmd == 'h25) {led_overtake, led_state} <= io_din; @@ -462,10 +463,10 @@ always@(posedge clk_sys) begin if(cmd == 'h3A) begin cnt <= cnt + 1'd1; case(cnt[3:0]) - 0: arc1x <= io_din[11:0]; - 1: arc1y <= io_din[11:0]; - 2: arc2x <= io_din[11:0]; - 3: arc2y <= io_din[11:0]; + 0: arc1x <= io_din[12:0]; + 1: arc1y <= io_din[12:0]; + 2: arc2x <= io_din[12:0]; + 3: arc2y <= io_din[12:0]; endcase end end @@ -747,6 +748,7 @@ reg [11:0] LFB_HMAX = 0; reg [11:0] LFB_VMIN = 0; reg [11:0] LFB_VMAX = 0; reg [31:0] LFB_BASE = 0; +reg [13:0] LFB_STRIDE = 0; reg FB_EN = 0; reg [5:0] FB_FMT = 0; @@ -762,7 +764,7 @@ always @(posedge clk_sys) begin FB_WIDTH <= LFB_WIDTH; FB_HEIGHT <= LFB_HEIGHT; FB_BASE <= LFB_BASE; - FB_STRIDE <= 0; + FB_STRIDE <= LFB_STRIDE; end else begin FB_FMT <= fb_fmt; @@ -778,74 +780,123 @@ reg fb_vbl; always @(posedge clk_vid) fb_vbl <= hdmi_vbl; `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] hmax; reg [11:0] vmin; reg [11:0] vmax; +reg [11:0] hdmi_height; +reg [11:0] hdmi_width; always @(posedge clk_vid) begin - reg [31:0] wcalc; - reg [31:0] hcalc; + reg [11:0] hmini,hmaxi,vmini,vmaxi; + reg [11:0] wcalc,videow,arx; + reg [11:0] hcalc,videoh,ary; reg [2:0] state; - reg [11:0] videow; - reg [11:0] videoh; - reg [11:0] height; - reg [11:0] width; - reg [11:0] arx; - reg [11:0] ary; + reg xy; + + hdmi_height <= (VSET && (VSET < HEIGHT)) ? VSET : HEIGHT; + hdmi_width <= (HSET && (HSET < WIDTH)) ? HSET : WIDTH; - height <= (VSET && (VSET < HEIGHT)) ? VSET : HEIGHT; - width <= (HSET && (HSET < WIDTH)) ? HSET : WIDTH; - if(!ARY) begin if(ARX == 1) begin - arx <= arc1x; - ary <= arc1y; + arx <= arc1x[11:0]; + ary <= arc1y[11:0]; + xy <= arc1x[12] | arc1y[12]; end else if(ARX == 2) begin - arx <= arc2x; - ary <= arc2y; + arx <= arc2x[11:0]; + ary <= arc2y[11:0]; + xy <= arc2x[12] | arc2y[12]; end else begin arx <= 0; ary <= 0; + xy <= 0; end end else begin - arx <= ARX; - ary <= ARY; + arx <= ARX[11:0]; + ary <= ARY[11:0]; + xy <= ARX[12] | ARY[12]; end - + + ar_md_start <= 0; state <= state + 1'd1; case(state) 0: if(LFB_EN) begin - hmin <= LFB_HMIN; - vmin <= LFB_VMIN; - hmax <= LFB_HMAX; - vmax <= LFB_VMAX; - state<= 0; + hmini <= LFB_HMIN; + vmini <= LFB_VMIN; + hmaxi <= LFB_HMAX; + vmaxi <= LFB_VMAX; + state <= 0; end else if(FREESCALE || !arx || !ary) begin - wcalc <= width; - hcalc <= height; + wcalc <= hdmi_width; + hcalc <= hdmi_height; + state <= 6; end - else begin - wcalc <= (height*arx)/ary; - hcalc <= (width*ary)/arx; + else if(xy) begin + wcalc <= 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 6: begin - videow <= (wcalc > width) ? width : wcalc[11:0]; - videoh <= (hcalc > height) ? height : hcalc[11:0]; + videow <= (wcalc > hdmi_width) ? hdmi_width : wcalc[11:0]; + videoh <= (hcalc > hdmi_height) ? hdmi_height : hcalc[11:0]; end 7: begin - hmin <= ((WIDTH - videow)>>1); - hmax <= ((WIDTH - videow)>>1) + videow - 1'd1; - vmin <= ((HEIGHT - videoh)>>1); - vmax <= ((HEIGHT - videoh)>>1) + videoh - 1'd1; + hmini <= ((WIDTH - videow)>>1); + hmaxi <= ((WIDTH - videow)>>1) + videow - 1'd1; + vmini <= ((HEIGHT - videoh)>>1); + vmaxi <= ((HEIGHT - videoh)>>1) + videoh - 1'd1; end endcase + + hmin <= hmini; + hmax <= hmaxi; + vmin <= vmini; + vmax <= vmaxi; end `ifndef DEBUG_NOHDMI @@ -1483,7 +1534,10 @@ emu emu ( .CLK_50M(FPGA_CLK2_50), .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_G(g_out), @@ -1494,6 +1548,9 @@ emu emu .VGA_F1(f1), .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), .CE_PIXEL(ce_pix), .VGA_SL(scanlines), diff --git a/sys/video_freak.sv b/sys/video_freak.sv new file mode 100644 index 0000000..155289a --- /dev/null +++ b/sys/video_freak.sv @@ -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 diff --git a/sys/video_mixer.sv b/sys/video_mixer.sv index 8f204ee..93b106e 100644 --- a/sys/video_mixer.sv +++ b/sys/video_mixer.sv @@ -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. // @@ -15,8 +15,6 @@ // May be less if line_start is used. // // 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 12161 @@ -28,32 +26,21 @@ module video_mixer parameter GAMMA = 0 ) ( - // video clock - // it should be multiple by (ce_pix*4). - input clk_vid, + input CLK_VIDEO, // should be multiple by (ce_pix*4) + output reg CE_PIXEL, // output pixel clock enable - // Pixel clock or clock_enable (both are accepted). - input ce_pix, - output ce_pix_out, + input ce_pix, // input pixel clock or clock_enable input scandoubler, + input hq2x, // high quality 2x scaling - // scanlines (00-none 01-25% 10-50% 11-75%) - input [1:0] scanlines, - - // High quality 2x scaling - input hq2x, + inout [21:0] gamma_bus, // color input [DWIDTH:0] R, input [DWIDTH:0] G, input [DWIDTH:0] B, - // Monochrome mode (for HALF_DEPTH only) - input mono, - - inout [21:0] gamma_bus, - // Positive pulses. input HSync, input VSync, @@ -75,9 +62,9 @@ localparam HALF_DEPTH_SD = GAMMA ? 0 : HALF_DEPTH; generate if(GAMMA && HALF_DEPTH) begin - wire [7:0] R_in = mono ? {G,R} : {R,R}; - wire [7:0] G_in = mono ? {G,R} : {G,G}; - wire [7:0] B_in = mono ? {G,R} : {B,B}; + wire [7:0] R_in = {R,R}; + wire [7:0] G_in = {G,G}; + wire [7:0] B_in = {B,B}; end else begin wire [DWIDTH:0] R_in = R; wire [DWIDTH:0] G_in = G; @@ -95,7 +82,7 @@ generate assign gamma_bus[21] = 1; gamma_corr gamma( .clk_sys(gamma_bus[20]), - .clk_vid(clk_vid), + .clk_vid(CLK_VIDEO), .ce_pix(ce_pix), .gamma_en(gamma_bus[19]), @@ -122,7 +109,6 @@ generate end endgenerate - wire [DWIDTH_SD:0] R_sd; wire [DWIDTH_SD:0] G_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 ( - .*, + .clk_vid(CLK_VIDEO), + .hq2x(hq2x), + + .ce_pix(ce_pix), .hs_in(hs_g), .vs_in(vs_g), .hb_in(hb_g), @@ -149,90 +138,55 @@ scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH_SD)) sd .b_out(B_sd) ); -wire [DWIDTH_SD:0] rt = (scandoubler ? R_sd : R_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] rt = (scandoubler ? R_sd : R_gamma); +wire [DWIDTH_SD:0] gt = (scandoubler ? G_sd : G_gamma); +wire [DWIDTH_SD:0] bt = (scandoubler ? B_sd : B_gamma); -generate - if(!GAMMA && HALF_DEPTH) begin - wire [7:0] r = mono ? {gt,rt} : {rt,rt}; - 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 +always @(posedge CLK_VIDEO) begin + reg [7:0] r,g,b; + reg hde,vde,hs,vs, old_vs; reg old_hde; + reg old_ce; + reg ce_osc, fs_osc; + + old_ce <= ce_pix; + ce_osc <= ce_osc | (old_ce ^ ce_pix); - if(ce_pix_out) begin - case(scanlines & {scanline, scanline}) - 1: begin // reduce 25% = 1/2 + 1/4 - v_r <= {1'b0, r[7:1]} + {2'b00, r[7:2]}; - v_g <= {1'b0, g[7:1]} + {2'b00, g[7:2]}; - v_b <= {1'b0, b[7:1]} + {2'b00, b[7:2]}; - end + old_vs <= vs; + if(~old_vs & vs) begin + fs_osc <= ce_osc; + ce_osc <= 0; + end - 2: begin // reduce 50% = 1/2 - v_r <= {1'b0, r[7:1]}; - v_g <= {1'b0, g[7:1]}; - v_b <= {1'b0, b[7:1]}; - end + CE_PIXEL <= scandoubler ? ce_pix_sd : fs_osc ? (~old_ce & ce_pix) : ce_pix; - 3: begin // reduce 75% = 1/4 - v_r <= {2'b00, r[7:2]}; - v_g <= {2'b00, g[7:2]}; - v_b <= {2'b00, b[7:2]}; - end + if(!GAMMA && HALF_DEPTH) begin + r <= {rt,rt}; + g <= {gt,gt}; + b <= {bt,bt}; + end + else begin + r <= rt; + g <= gt; + b <= bt; + end - default: begin - v_r <= r; - v_g <= g; - v_b <= b; - end - endcase + hde <= scandoubler ? ~hb_sd : ~hb_g; + vde <= scandoubler ? ~vb_sd : ~vb_g; + vs <= scandoubler ? vs_sd : vs_g; + hs <= scandoubler ? hs_sd : hs_g; - v_vs <= vs; - v_hs <= hs; + if(CE_PIXEL) begin + VGA_R <= r; + VGA_G <= g; + VGA_B <= b; + + VGA_VS <= vs; + VGA_HS <= hs; old_hde <= hde; - if(~old_hde && hde) v_de <= vde; - if(old_hde && ~hde) v_de <= 0; + if(old_hde ^ hde) VGA_DE <= vde & hde; 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