From 3067b3afe378620e73e27cfc4b9d9631a86cc88a Mon Sep 17 00:00:00 2001 From: sorgelig Date: Tue, 10 Jan 2017 08:36:44 +0800 Subject: [PATCH] [NES] Add support for YPbPr video. --- cores/nes/mist/NES_mist.v | 3 ++ cores/nes/mist/user_io.v | 2 + cores/nes/nes.qsf | 1 + cores/nes/src/video.v | 48 ++++++++++++++---- cores/nes/src/video_mixer.sv | 98 ++++++++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 cores/nes/src/video_mixer.sv diff --git a/cores/nes/mist/NES_mist.v b/cores/nes/mist/NES_mist.v index 3a69137..0dc6d68 100644 --- a/cores/nes/mist/NES_mist.v +++ b/cores/nes/mist/NES_mist.v @@ -175,6 +175,7 @@ wire palette2_osd = status[5]; wire reset_osd = status[6]; wire scandoubler_disable; +wire ypbpr; wire ps2_kbd_clk, ps2_kbd_data; @@ -190,6 +191,7 @@ user_io #(.STRLEN(CONF_STR_LEN)) user_io( .switches(switches), .buttons(buttons), .scandoubler_disable(scandoubler_disable), + .ypbpr(ypbpr), .joystick_0(joyA), .joystick_1(joyB), @@ -407,6 +409,7 @@ video video ( .count_v(scanline), .count_h(cycle), .mode(scandoubler_disable), + .ypbpr(ypbpr), .smoothing(!smoothing_osd), .scanlines(scanlines_osd), .overscan(overscan_osd), diff --git a/cores/nes/mist/user_io.v b/cores/nes/mist/user_io.v index ee5baa5..17dd624 100644 --- a/cores/nes/mist/user_io.v +++ b/cores/nes/mist/user_io.v @@ -37,6 +37,7 @@ module user_io #(parameter STRLEN=0) ( output [1:0] buttons, output [1:0] switches, output scandoubler_disable, + output ypbpr, output reg [7:0] status, @@ -79,6 +80,7 @@ assign sd_mounted = mount_strobe; assign buttons = but_sw[1:0]; assign switches = but_sw[3:2]; assign scandoubler_disable = but_sw[4]; +assign ypbpr = but_sw[5]; wire [7:0] dout = { sbuf, SPI_DI}; diff --git a/cores/nes/nes.qsf b/cores/nes/nes.qsf index 9e09951..fc38b3b 100644 --- a/cores/nes/nes.qsf +++ b/cores/nes/nes.qsf @@ -296,6 +296,7 @@ set_global_assignment -name VERILOG_FILE src/compat.v set_global_assignment -name VERILOG_FILE src/ppu.v set_global_assignment -name VERILOG_FILE src/mmu.v set_global_assignment -name VERILOG_FILE src/cpu.v +set_global_assignment -name SYSTEMVERILOG_FILE src/video_mixer.sv set_global_assignment -name VERILOG_FILE src/video.v set_global_assignment -name VERILOG_FILE src/nes.v set_global_assignment -name VERILOG_FILE src/MicroCode.v diff --git a/cores/nes/src/video.v b/cores/nes/src/video.v index c93e299..1e711ad 100644 --- a/cores/nes/src/video.v +++ b/cores/nes/src/video.v @@ -7,6 +7,7 @@ module video( input [8:0] count_h, input [8:0] count_v, input mode, + input ypbpr, input smoothing, input scanlines, input overscan, @@ -18,8 +19,8 @@ module video( output VGA_HS, output VGA_VS, - output [5:0] VGA_R, - output [5:0] VGA_G, + output [5:0] VGA_R, + output [5:0] VGA_G, output [5:0] VGA_B, output osd_visible @@ -29,6 +30,8 @@ reg clk2 = 1'b0; always @(posedge clk) clk2 <= ~clk2; wire clkv = mode ? clk2 : clk; +wire [5:0] R_out, G_out, B_out; + osd #(10'd0, 10'd0, 3'd4) osd ( .pclk(clkv), @@ -36,16 +39,16 @@ osd #(10'd0, 10'd0, 3'd4) osd ( .sdi(sdi), .ss(ss), - .red_in ({vga_r, 1'b0}), - .green_in({vga_g, 1'b0}), - .blue_in ({vga_b, 1'b0}), + .red_in ({vga_r, vga_r[4]}), + .green_in({vga_g, vga_g[4]}), + .blue_in ({vga_b, vga_b[4]}), .hs_in(sync_h), .vs_in(sync_v), - .red_out(VGA_R), - .green_out(VGA_G), - .blue_out(VGA_B), - + .red_out(R_out), + .green_out(G_out), + .blue_out(B_out), + .osd_enable(osd_visible) ); @@ -103,7 +106,30 @@ wire [4:0] vga_g = ol ? {4'b0, pixel_v[9:9]} : (darker ? {1'b0, pixel_v[9:6 wire [4:0] vga_b = ol ? {4'b0, pixel_v[14:14]} : (darker ? {1'b0, pixel_v[14:11]} : pixel_v[14:10]); wire sync_h = ((h >= (512 + 23 + (mode ? 18 : 35))) && (h < (512 + 23 + (mode ? 18 : 35) + 82))); wire sync_v = ((v >= (mode ? 240 + 5 : 480 + 10)) && (v < (mode ? 240 + 14 : 480 + 12))); -assign VGA_HS = mode ? ~(sync_h ^ sync_v) : ~sync_h; -assign VGA_VS = mode ? 1'b1 : ~sync_v; + +video_mixer video_mixer +( + .scandoubler_disable(mode), + .ypbpr(ypbpr), + .ypbpr_full(1), + + .r_i({R_out, R_out[5:4]}), + .g_i({G_out, G_out[5:4]}), + .b_i({B_out, B_out[5:4]}), + .hsync_i(sync_h), + .vsync_i(sync_v), + + .r_p({R_out, R_out[5:4]}), + .g_p({G_out, G_out[5:4]}), + .b_p({B_out, B_out[5:4]}), + .hsync_p(sync_h), + .vsync_p(sync_v), + + .VGA_HS(VGA_HS), + .VGA_VS(VGA_VS), + .VGA_R(VGA_R), + .VGA_G(VGA_G), + .VGA_B(VGA_B) +); endmodule diff --git a/cores/nes/src/video_mixer.sv b/cores/nes/src/video_mixer.sv new file mode 100644 index 0000000..cb1f517 --- /dev/null +++ b/cores/nes/src/video_mixer.sv @@ -0,0 +1,98 @@ +`timescale 1ns / 1ps + +// Core should provide as much color resolution as possible with normalized 0-255 range +// this module will reduce color resolution to 6 bits only at final stage. + +module video_mixer +( + // 0 = HVSync 31KHz, 1 = CSync 15KHz + input scandoubler_disable, + + // YPbPr always uses composite sync + input ypbpr, + + // 0 = 16-240 range. 1 = 0-255 range. (only for YPbPr color space) + input ypbpr_full, + + // interlace (15khz) color + input [7:0] r_i, + input [7:0] g_i, + input [7:0] b_i, + + // interlace sync. Positive pulses. + input hsync_i, + input vsync_i, + + // progressive (31khz) color + input [7:0] r_p, + input [7:0] g_p, + input [7:0] b_p, + + // progressive sync. Positive pulses. + input hsync_p, + input vsync_p, + + // MiST video output signals + output [5:0] VGA_R, + output [5:0] VGA_G, + output [5:0] VGA_B, + output VGA_VS, + output VGA_HS +); + +wire [7:0] red = scandoubler_disable ? r_i : r_p; +wire [7:0] green = scandoubler_disable ? g_i : g_p; +wire [7:0] blue = scandoubler_disable ? b_i : b_p; + +wire [5:0] yuv_full[225] = '{ + 6'd0, 6'd0, 6'd0, 6'd0, 6'd1, 6'd1, 6'd1, 6'd1, + 6'd2, 6'd2, 6'd2, 6'd3, 6'd3, 6'd3, 6'd3, 6'd4, + 6'd4, 6'd4, 6'd5, 6'd5, 6'd5, 6'd5, 6'd6, 6'd6, + 6'd6, 6'd7, 6'd7, 6'd7, 6'd7, 6'd8, 6'd8, 6'd8, + 6'd9, 6'd9, 6'd9, 6'd9, 6'd10, 6'd10, 6'd10, 6'd11, + 6'd11, 6'd11, 6'd11, 6'd12, 6'd12, 6'd12, 6'd13, 6'd13, + 6'd13, 6'd13, 6'd14, 6'd14, 6'd14, 6'd15, 6'd15, 6'd15, + 6'd15, 6'd16, 6'd16, 6'd16, 6'd17, 6'd17, 6'd17, 6'd17, + 6'd18, 6'd18, 6'd18, 6'd19, 6'd19, 6'd19, 6'd19, 6'd20, + 6'd20, 6'd20, 6'd21, 6'd21, 6'd21, 6'd21, 6'd22, 6'd22, + 6'd22, 6'd23, 6'd23, 6'd23, 6'd23, 6'd24, 6'd24, 6'd24, + 6'd25, 6'd25, 6'd25, 6'd25, 6'd26, 6'd26, 6'd26, 6'd27, + 6'd27, 6'd27, 6'd27, 6'd28, 6'd28, 6'd28, 6'd29, 6'd29, + 6'd29, 6'd29, 6'd30, 6'd30, 6'd30, 6'd31, 6'd31, 6'd31, + 6'd31, 6'd32, 6'd32, 6'd32, 6'd33, 6'd33, 6'd33, 6'd33, + 6'd34, 6'd34, 6'd34, 6'd35, 6'd35, 6'd35, 6'd35, 6'd36, + 6'd36, 6'd36, 6'd36, 6'd37, 6'd37, 6'd37, 6'd38, 6'd38, + 6'd38, 6'd38, 6'd39, 6'd39, 6'd39, 6'd40, 6'd40, 6'd40, + 6'd40, 6'd41, 6'd41, 6'd41, 6'd42, 6'd42, 6'd42, 6'd42, + 6'd43, 6'd43, 6'd43, 6'd44, 6'd44, 6'd44, 6'd44, 6'd45, + 6'd45, 6'd45, 6'd46, 6'd46, 6'd46, 6'd46, 6'd47, 6'd47, + 6'd47, 6'd48, 6'd48, 6'd48, 6'd48, 6'd49, 6'd49, 6'd49, + 6'd50, 6'd50, 6'd50, 6'd50, 6'd51, 6'd51, 6'd51, 6'd52, + 6'd52, 6'd52, 6'd52, 6'd53, 6'd53, 6'd53, 6'd54, 6'd54, + 6'd54, 6'd54, 6'd55, 6'd55, 6'd55, 6'd56, 6'd56, 6'd56, + 6'd56, 6'd57, 6'd57, 6'd57, 6'd58, 6'd58, 6'd58, 6'd58, + 6'd59, 6'd59, 6'd59, 6'd60, 6'd60, 6'd60, 6'd60, 6'd61, + 6'd61, 6'd61, 6'd62, 6'd62, 6'd62, 6'd62, 6'd63, 6'd63, + 6'd63 +}; + +// http://marsee101.blog19.fc2.com/blog-entry-2311.html +// Y = 16 + 0.257*R + 0.504*G + 0.098*B (Y = 0.299*R + 0.587*G + 0.114*B) +// Pb = 128 - 0.148*R - 0.291*G + 0.439*B (Pb = -0.169*R - 0.331*G + 0.500*B) +// Pr = 128 + 0.439*R - 0.368*G - 0.071*B (Pr = 0.500*R - 0.419*G - 0.081*B) + +wire [18:0] y_8 = 19'd04096 + ({red, 6'd0} + {red, 1'd0}) + ({green, 7'd0} + {green}) + ({blue, 4'd0} + {blue, 3'd0} + {blue}); +wire [18:0] pb_8 = 19'd32768 - ({red, 5'd0} + {red, 2'd0} + {red, 1'd0}) - ({green, 6'd0} + {green, 3'd0} + {green, 1'd0}) + ({blue, 6'd0} + {blue, 5'd0} + {blue, 4'd0}); +wire [18:0] pr_8 = 19'd32768 + ({red, 6'd0} + {red, 5'd0} + {red, 4'd0}) - ({green, 6'd0} + {green, 4'd0} + {green, 3'd0} + {green, 2'd0} + {green, 1'd0}) - ({blue, 4'd0} + {blue , 1'd0}); + +wire [7:0] y = ( y_8[17:8] < 16) ? 8'd16 : ( y_8[17:8] > 235) ? 8'd235 : y_8[15:8]; +wire [7:0] pb = (pb_8[17:8] < 16) ? 8'd16 : (pb_8[17:8] > 240) ? 8'd240 : pb_8[15:8]; +wire [7:0] pr = (pr_8[17:8] < 16) ? 8'd16 : (pr_8[17:8] > 240) ? 8'd240 : pr_8[15:8]; + +assign VGA_R = ypbpr ? (ypbpr_full ? yuv_full[pr-8'd16] : pr[7:2]) : red[7:2]; +assign VGA_G = ypbpr ? (ypbpr_full ? yuv_full[y -8'd16] : y[7:2]) : green[7:2]; +assign VGA_B = ypbpr ? (ypbpr_full ? yuv_full[pb-8'd16] : pb[7:2]) : blue[7:2]; +assign VGA_VS = (scandoubler_disable | ypbpr) ? 1'b1 : ~vsync_p; +assign VGA_HS = scandoubler_disable ? ~(hsync_i ^ vsync_i) : ypbpr ? ~(hsync_p ^ vsync_p) : ~hsync_p; + +endmodule