Merge pull request #10 from JimmyStones/hiscore-autosave

Add hiscore autosave, update sys
This commit is contained in:
Alan Steremberg
2021-09-22 11:58:12 -07:00
committed by GitHub
15 changed files with 1200 additions and 3032 deletions

View File

@@ -21,158 +21,157 @@ module emu
//Multiple resolutions are supported using different CE_PIXEL rates.
//Must be based on CLK_VIDEO
output CE_PIXEL,
//Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
//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,
output [7:0] VGA_B,
output VGA_HS,
output VGA_VS,
output VGA_DE, // = ~(VBlank | HBlank)
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 MISTER_FB
// Use framebuffer in DDRAM (USE_FB=1 in qsf)
// FB_FORMAT:
// [2:0] : 011=8bpp(palette) 100=16bpp 101=24bpp 110=32bpp
// [3] : 0=16bits 565 1=16bits 1555
// [4] : 0=RGB 1=BGR (for 16/24/32 modes)
//
// FB_STRIDE either 0 (rounded to 256 bytes) or multiple of pixel size (in bytes)
output FB_EN,
output [4:0] FB_FORMAT,
output [11:0] FB_WIDTH,
output [11:0] FB_HEIGHT,
output [31:0] FB_BASE,
output [13:0] FB_STRIDE,
input FB_VBL,
input FB_LL,
output FB_FORCE_BLANK,
`ifdef MISTER_FB_PALETTE
// Palette control for 8bit modes.
// Ignored for other video modes.
output FB_PAL_CLK,
output [7:0] FB_PAL_ADDR,
output [23:0] FB_PAL_DOUT,
input [23:0] FB_PAL_DIN,
output FB_PAL_WR,
`endif
`endif
output LED_USER, // 1 - ON, 0 - OFF.
// b[1]: 0 - LED status is system status OR'd with b[0]
// 1 - LED status is controled solely by b[0]
// hint: supply 2'b00 to let the system control the LED.
output [1:0] LED_POWER,
output [1:0] LED_DISK,
// I/O board button press simulation (active high)
// b[1]: user button
// b[0]: osd button
output [1:0] BUTTONS,
input CLK_AUDIO, // 24.576 MHz
output [15:0] AUDIO_L,
output [15:0] AUDIO_R,
output AUDIO_S, // 1 - signed audio samples, 0 - unsigned
output [1:0] AUDIO_MIX, // 0 - no mix, 1 - 25%, 2 - 50%, 3 - 100% (mono)
//ADC
inout [3:0] ADC_BUS,
//SD-SPI
output SD_SCK,
output SD_MOSI,
input SD_MISO,
output SD_CS,
input SD_CD,
//High latency DDR3 RAM interface
//Use for non-critical time purposes
output DDRAM_CLK,
input DDRAM_BUSY,
output [7:0] DDRAM_BURSTCNT,
output [28:0] DDRAM_ADDR,
input [63:0] DDRAM_DOUT,
input DDRAM_DOUT_READY,
output DDRAM_RD,
output [63:0] DDRAM_DIN,
output [7:0] DDRAM_BE,
output DDRAM_WE,
//SDRAM interface with lower latency
output SDRAM_CLK,
output SDRAM_CKE,
output [12:0] SDRAM_A,
output [1:0] SDRAM_BA,
inout [15:0] SDRAM_DQ,
output SDRAM_DQML,
output SDRAM_DQMH,
output SDRAM_nCS,
output SDRAM_nCAS,
output SDRAM_nRAS,
output SDRAM_nWE,
`ifdef MISTER_DUAL_SDRAM
//Secondary SDRAM
//Set all output SDRAM_* signals to Z ASAP if SDRAM2_EN is 0
input SDRAM2_EN,
output SDRAM2_CLK,
output [12:0] SDRAM2_A,
output [1:0] SDRAM2_BA,
inout [15:0] SDRAM2_DQ,
output SDRAM2_nCS,
output SDRAM2_nCAS,
output SDRAM2_nRAS,
output SDRAM2_nWE,
`endif
input UART_CTS,
output UART_RTS,
input UART_RXD,
output UART_TXD,
output UART_DTR,
input UART_DSR,
// Open-drain User port.
// 0 - D+/RX
// 1 - D-/TX
// 2..6 - USR2..USR6
// Set USER_OUT to 1 to read from USER_IN.
input [6:0] USER_IN,
output [6:0] USER_OUT,
input OSD_STATUS
);
///////// Default values for ports not used in this core /////////
assign ADC_BUS = 'Z;
assign USER_OUT = '1;
assign {UART_RTS, UART_TXD, UART_DTR} = 0;
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
assign {SDRAM_DQ, SDRAM_A, SDRAM_BA, SDRAM_CLK, SDRAM_CKE, SDRAM_DQML, SDRAM_DQMH, SDRAM_nWE, SDRAM_nCAS, SDRAM_nRAS, SDRAM_nCS} = 'Z;
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = '0;
assign VGA_F1 = 0;
assign VGA_SCALER = 0;
assign LED_USER = ioctl_download;
assign BUTTONS = 0;
assign AUDIO_MIX = 0;
//Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
//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,
output [7:0] VGA_B,
output VGA_HS,
output VGA_VS,
output VGA_DE, // = ~(VBlank | HBlank)
output VGA_F1,
output [1:0] VGA_SL,
output VGA_SCALER, // Force VGA scaler
input [11:0] HDMI_WIDTH,
input [11:0] HDMI_HEIGHT,
output HDMI_FREEZE,
`ifdef MISTER_FB
// Use framebuffer in DDRAM (USE_FB=1 in qsf)
// FB_FORMAT:
// [2:0] : 011=8bpp(palette) 100=16bpp 101=24bpp 110=32bpp
// [3] : 0=16bits 565 1=16bits 1555
// [4] : 0=RGB 1=BGR (for 16/24/32 modes)
//
// FB_STRIDE either 0 (rounded to 256 bytes) or multiple of pixel size (in bytes)
output FB_EN,
output [4:0] FB_FORMAT,
output [11:0] FB_WIDTH,
output [11:0] FB_HEIGHT,
output [31:0] FB_BASE,
output [13:0] FB_STRIDE,
input FB_VBL,
input FB_LL,
output FB_FORCE_BLANK,
`ifdef MISTER_FB_PALETTE
// Palette control for 8bit modes.
// Ignored for other video modes.
output FB_PAL_CLK,
output [7:0] FB_PAL_ADDR,
output [23:0] FB_PAL_DOUT,
input [23:0] FB_PAL_DIN,
output FB_PAL_WR,
`endif
`endif
output LED_USER, // 1 - ON, 0 - OFF.
// b[1]: 0 - LED status is system status OR'd with b[0]
// 1 - LED status is controled solely by b[0]
// hint: supply 2'b00 to let the system control the LED.
output [1:0] LED_POWER,
output [1:0] LED_DISK,
// I/O board button press simulation (active high)
// b[1]: user button
// b[0]: osd button
output [1:0] BUTTONS,
input CLK_AUDIO, // 24.576 MHz
output [15:0] AUDIO_L,
output [15:0] AUDIO_R,
output AUDIO_S, // 1 - signed audio samples, 0 - unsigned
output [1:0] AUDIO_MIX, // 0 - no mix, 1 - 25%, 2 - 50%, 3 - 100% (mono)
//ADC
inout [3:0] ADC_BUS,
//SD-SPI
output SD_SCK,
output SD_MOSI,
input SD_MISO,
output SD_CS,
input SD_CD,
//High latency DDR3 RAM interface
//Use for non-critical time purposes
output DDRAM_CLK,
input DDRAM_BUSY,
output [7:0] DDRAM_BURSTCNT,
output [28:0] DDRAM_ADDR,
input [63:0] DDRAM_DOUT,
input DDRAM_DOUT_READY,
output DDRAM_RD,
output [63:0] DDRAM_DIN,
output [7:0] DDRAM_BE,
output DDRAM_WE,
//SDRAM interface with lower latency
output SDRAM_CLK,
output SDRAM_CKE,
output [12:0] SDRAM_A,
output [1:0] SDRAM_BA,
inout [15:0] SDRAM_DQ,
output SDRAM_DQML,
output SDRAM_DQMH,
output SDRAM_nCS,
output SDRAM_nCAS,
output SDRAM_nRAS,
output SDRAM_nWE,
`ifdef MISTER_DUAL_SDRAM
//Secondary SDRAM
//Set all output SDRAM_* signals to Z ASAP if SDRAM2_EN is 0
input SDRAM2_EN,
output SDRAM2_CLK,
output [12:0] SDRAM2_A,
output [1:0] SDRAM2_BA,
inout [15:0] SDRAM2_DQ,
output SDRAM2_nCS,
output SDRAM2_nCAS,
output SDRAM2_nRAS,
output SDRAM2_nWE,
`endif
input UART_CTS,
output UART_RTS,
input UART_RXD,
output UART_TXD,
output UART_DTR,
input UART_DSR,
// Open-drain User port.
// 0 - D+/RX
// 1 - D-/TX
// 2..6 - USR2..USR6
// Set USER_OUT to 1 to read from USER_IN.
input [6:0] USER_IN,
output [6:0] USER_OUT,
input OSD_STATUS
);
///////// Default values for ports not used in this core /////////
assign {SDRAM_DQ, SDRAM_A, SDRAM_BA, SDRAM_CLK, SDRAM_CKE, SDRAM_DQML, SDRAM_DQMH, SDRAM_nWE, SDRAM_nCAS, SDRAM_nRAS, SDRAM_nCS} = 'Z;
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = '0;
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
assign {UART_RTS, UART_TXD, UART_DTR} = 0;
assign VGA_F1 = 0;
assign VGA_SCALER= 0;
assign USER_OUT = '1;
assign LED_USER = ioctl_download;
assign LED_DISK = 0;
assign LED_POWER = 0;
assign BUTTONS = 0;
assign AUDIO_MIX = 0;
assign HDMI_FREEZE = 0;
wire [1:0] ar = status[17:16];
@@ -189,10 +188,11 @@ localparam CONF_STR = {
//"-;",
"DIP;",
"-;",
"P1,Pause options;",
"P1OP,Pause when OSD is open,On,Off;",
"P1OQ,Dim video after 10s,On,Off;",
"-;",
"H1OR,Autosave Hiscores,Off,On;",
"P1,Pause options;",
"P1OP,Pause when OSD is open,On,Off;",
"P1OQ,Dim video after 10s,On,Off;",
"-;",
"R0,Reset;",
"J1,Smoke,Start 1P,Start 2P,Coin,Pause;",
"V,v",`BUILD_DATE
@@ -224,35 +224,38 @@ wire [31:0] status;
wire [1:0] buttons;
wire forced_scandoubler;
wire [21:0] gamma_bus;
wire ioctl_download;
wire ioctl_upload;
wire ioctl_wr;
wire [7:0] ioctl_index;
wire [24:0] ioctl_addr;
wire [7:0] ioctl_din;
wire direct_video;
wire ioctl_download;
wire ioctl_upload;
wire ioctl_upload_req;
wire ioctl_wr;
wire [7:0] ioctl_index;
wire [24:0] ioctl_addr;
wire [7:0] ioctl_din;
wire [7:0] ioctl_dout;
wire [15:0] joystk1, joystk2;
hps_io #(.STRLEN($size(CONF_STR)>>3)) hps_io
hps_io #(.CONF_STR(CONF_STR)) hps_io
(
.clk_sys(clk_sys),
.HPS_BUS(HPS_BUS),
.conf_str(CONF_STR),
.buttons(buttons),
.status(status),
.forced_scandoubler(forced_scandoubler),
.gamma_bus(gamma_bus),
.ioctl_download(ioctl_download),
.ioctl_upload(ioctl_upload),
.ioctl_wr(ioctl_wr),
.ioctl_addr(ioctl_addr),
.ioctl_dout(ioctl_dout),
.ioctl_din(ioctl_din),
.direct_video(direct_video),
.status_menumask({~hs_configured,direct_video}),
.ioctl_download(ioctl_download),
.ioctl_upload(ioctl_upload),
.ioctl_upload_req(ioctl_upload_req),
.ioctl_wr(ioctl_wr),
.ioctl_addr(ioctl_addr),
.ioctl_dout(ioctl_dout),
.ioctl_din(ioctl_din),
.ioctl_index(ioctl_index),
.joystick_0(joystk1),
@@ -279,25 +282,25 @@ wire m_trig1 = joystk1[4] | (bCabinet ? 1'b0 : m_trig2);
wire m_coin1 = joystk1[7];
wire m_coin2 = joystk2[7];
wire m_pause = joystk1[8] | joystk2[8];
// PAUSE SYSTEM
wire pause_cpu;
wire [11:0] rgb_out;
pause #(4,4,4,24) pause (
.*,
.reset(iRST),
.user_button(m_pause),
.pause_request(hs_pause),
.options(~status[26:25])
);
wire m_pause = joystk1[8] | joystk2[8];
// PAUSE SYSTEM
wire pause_cpu;
wire [11:0] rgb_out;
pause #(4,4,4,24) pause (
.*,
.reset(iRST),
.user_button(m_pause),
.pause_request(hs_pause),
.options(~status[26:25])
);
///////////////////////////////////////////////////
wire hblank, vblank;
wire ce_vid;
wire hs, vs;
wire [3:0] r,g,b;
wire [3:0] r,g,b;
reg ce_pix;
always @(posedge clk_hdmi) begin
@@ -338,9 +341,9 @@ assign AUDIO_R = AUDIO_L;
assign AUDIO_S = 0; // unsigned PCM
///////////////////////////////////////////////////
///////////////////////////////////////////////////
wire rom_download = ioctl_download & !ioctl_index;
wire rom_download = ioctl_download & !ioctl_index;
wire iRST = RESET | status[0] | buttons[1] | rom_download;
// Note that bit 0 on controller 1 in the real hardware is the service indicator,
@@ -352,52 +355,56 @@ wire [7:0] iCTR2 = ~{ m_coin2, m_start2, m_up2, m_down2, m_right2, m_left2, m_t
wire [7:0] oPIX;
wire [7:0] oSND;
fpga_NRX GameCore (
fpga_NRX GameCore (
.RESET(iRST),.CLK24M(clk_24M),
.HP(HPOS),.VP(VPOS),.PCLK(PCLK),
.POUT(oPIX),.SND(oSND),
.DSW(~dips),.CTR1(iCTR1),.CTR2(iCTR2),
.ROMCL(clk_sys),.ROMAD(ioctl_addr),.ROMDT(ioctl_dout),.ROMEN(ioctl_wr & rom_download),
.pause(pause_cpu),
.hs_address(hs_address),
.hs_data_in(hs_data_in),
.hs_data_out(ioctl_din),
.hs_write(hs_write),
.hs_access(hs_access)
);
// HISCORE SYSTEM
wire [15:0]hs_address;
wire [7:0]hs_data_in;
wire hs_write;
wire hs_access;
wire hs_pause;
hiscore #(
.HS_ADDRESSWIDTH(16),
.HS_SCOREWIDTH(5),
.CFG_ADDRESSWIDTH(2),
.CFG_LENGTHWIDTH(2)
) hi (
.clk(clk_sys),
.reset(iRST),
.ioctl_upload(ioctl_upload),
.ioctl_download(ioctl_download),
.ioctl_wr(ioctl_wr),
.ioctl_addr(ioctl_addr),
.ioctl_dout(ioctl_dout),
.ioctl_din(ioctl_din),
.ioctl_index(ioctl_index),
.ram_address(hs_address),
.data_to_ram(hs_data_in),
.ram_write(hs_write),
.ram_access(hs_access),
.pause_cpu(hs_pause)
);
.pause(pause_cpu),
.hs_address(hs_address),
.hs_data_in(hs_data_in),
.hs_data_out(hs_data_out),
.hs_write(hs_write_enable),
.hs_access(hs_access_read|hs_access_write)
);
// HISCORE SYSTEM
wire [15:0]hs_address;
wire [7:0] hs_data_in;
wire [7:0] hs_data_out;
wire hs_write_enable;
wire hs_access_read;
wire hs_access_write;
wire hs_pause;
wire hs_configured;
hiscore #(
.HS_ADDRESSWIDTH(16),
.HS_SCOREWIDTH(6),
.CFG_ADDRESSWIDTH(2),
.CFG_LENGTHWIDTH(2)
) hi (
.*,
.clk(clk_sys),
.reset(iRST),
.paused(pause_cpu),
.autosave(status[27]),
.ram_address(hs_address),
.data_from_ram(hs_data_out),
.data_to_ram(hs_data_in),
.data_from_hps(ioctl_dout),
.data_to_hps(ioctl_din),
.ram_write(hs_write_enable),
.ram_intent_read(hs_access_read),
.ram_intent_write(hs_access_write),
.pause_cpu(hs_pause),
.configured(hs_configured)
);
assign POUT = {oPIX[7:6],2'b00,oPIX[5:3],1'b0,oPIX[2:0],1'b0};
assign AOUT = {oSND,8'h0};
@@ -426,19 +433,19 @@ assign VPOS = vcnt;
always @(posedge PCLK) begin
case (hcnt)
288: begin HBLK <= 1; hcnt <= hcnt+1; end
311: begin HSYN <= 0; hcnt <= hcnt+1; end
342: begin HSYN <= 1; hcnt <= 471; end
511: begin HBLK <= 0; hcnt <= 0;
288: begin HBLK <= 1; hcnt <= hcnt + 9'd1; end
311: begin HSYN <= 0; hcnt <= hcnt + 9'd1; end
342: begin HSYN <= 1; hcnt <= 9'd471; end
511: begin HBLK <= 0; hcnt <= 9'd0;
case (vcnt)
223: begin VBLK <= 1; vcnt <= vcnt+1; end
226: begin VSYN <= 0; vcnt <= vcnt+1; end
233: begin VSYN <= 1; vcnt <= 483; end
511: begin VBLK <= 0; vcnt <= 0; end
default: vcnt <= vcnt+1;
223: begin VBLK <= 1; vcnt <= vcnt + 9'd1; end
226: begin VSYN <= 0; vcnt <= vcnt + 9'd1; end
233: begin VSYN <= 1; vcnt <= 9'd483; end
511: begin VBLK <= 0; vcnt <= 9'd0; end
default: vcnt <= vcnt + 9'd1;
endcase
end
default: hcnt <= hcnt+1;
default: hcnt <= hcnt + 9'd1;
endcase
oRGB <= (HBLK|VBLK) ? 12'h0 : iRGB;
end

View File

@@ -11,6 +11,6 @@ set_global_assignment -name VERILOG_FILE rtl/nrx_sprite.v
set_global_assignment -name VERILOG_FILE rtl/nrx_sound.v
set_global_assignment -name VERILOG_FILE rtl/fpga_nrx.v
set_global_assignment -name VHDL_FILE rtl/dpram.vhd
set_global_assignment -name SYSTEMVERILOG_FILE "rtl/pause.v"
set_global_assignment -name SYSTEMVERILOG_FILE "rtl/hiscore.v"
set_global_assignment -name VERILOG_FILE "rtl/pause.v"
set_global_assignment -name VERILOG_FILE "rtl/hiscore.v"
set_global_assignment -name SYSTEMVERILOG_FILE "Arcade-RallyX.sv"

Binary file not shown.

View File

@@ -30,6 +30,13 @@
0005 - 2021-03-18 - Add configurable score table width, clean up some stupid mistakes
0006 - 2021-03-27 - Move 'tweakable' parameters into MRA data header
0007 - 2021-04-15 - Improve state machine maintainability, add new 'pause padding' states
0008 - 2021-05-12 - Feed back core-level pause to halt startup timer
0009 - 2021-07-31 - Split hiscore extraction from upload (updates hiscore buffer on OSD open)
0010 - 2021-08-03 - Add hiscore buffer and change detection (ready for autosave!)
0011 - 2021-08-07 - Optional auto-save on OSD open
0012 - 2021-08-17 - Add variable length change detection mask
0013 - 2021-09-01 - Output configured signal for autosave option menu masking
0014 - 2021-09-09 - Fix turning on autosave w/o core reload
============================================================================
*/
@@ -44,21 +51,28 @@ module hiscore
)
(
input clk,
input paused, // Signal from core confirming CPU is paused
input reset,
input autosave, // Auto-save enabled (active high)
input ioctl_upload,
output reg ioctl_upload_req,
input ioctl_download,
input ioctl_wr,
input [24:0] ioctl_addr,
input [7:0] ioctl_dout,
input [7:0] ioctl_din,
input [7:0] ioctl_index,
input OSD_STATUS,
output [HS_ADDRESSWIDTH-1:0] ram_address, // Address in game RAM to read/write score data
output [7:0] data_to_ram, // Data to write to game RAM
output reg ram_write, // Write to game RAM (active high)
output ram_access, // RAM read or write required (active high)
output pause_cpu // Pause core CPU to prepare for/relax after RAM access
input [7:0] data_from_hps, // Incoming data from HPS ioctl_dout
input [7:0] data_from_ram, // Incoming data from game RAM
output [HS_ADDRESSWIDTH-1:0] ram_address, // Address in game RAM to read/write score data
output [7:0] data_to_hps, // Data to send to HPS ioctl_din
output [7:0] data_to_ram, // Data to send to game RAM
output reg ram_write, // Write to game RAM (active high)
output ram_intent_read, // RAM read required (active high)
output ram_intent_write, // RAM write required (active high)
output reg pause_cpu, // Pause core CPU to prepare for/relax after RAM access
output configured // Hiscore module has valid configuration (active high)
);
// Parameters read from config header
@@ -69,12 +83,12 @@ reg [15:0] WRITE_HOLD =16'd2; // Hold time for game RAM writes
reg [15:0] WRITE_REPEATCOUNT =16'b1; // Number of times to write score to game RAM
reg [15:0] WRITE_REPEATWAIT =16'b1111; // Delay between subsequent write attempts to game RAM
reg [7:0] ACCESS_PAUSEPAD =8'd4; // Cycles to wait with paused CPU before and after RAM access
reg [7:0] CHANGEMASK =1'b0; // Length of change mask
// State machine constants
localparam SM_STATEWIDTH = 4; // Width of state machine net
localparam SM_STATEWIDTH = 5; // Width of state machine net
localparam SM_INIT = 0;
localparam SM_INIT_RESTORE = 0;
localparam SM_TIMER = 1;
localparam SM_CHECKPREP = 2;
@@ -90,7 +104,19 @@ localparam SM_WRITEDONE = 10;
localparam SM_WRITECOMPLETE = 11;
localparam SM_WRITERETRY = 12;
localparam SM_STOPPED = 13;
localparam SM_COMPAREINIT = 16;
localparam SM_COMPAREBEGIN = 17;
localparam SM_COMPAREREADY = 18;
localparam SM_COMPAREREAD = 19;
localparam SM_COMPAREDONE = 20;
localparam SM_COMPARECOMPLETE = 21;
localparam SM_EXTRACTINIT = 22;
localparam SM_EXTRACT = 23;
localparam SM_EXTRACTSAVE = 24;
localparam SM_EXTRACTCOMPLETE = 25;
localparam SM_STOPPED = 30;
/*
Hiscore config data structure (version 1)
@@ -107,7 +133,8 @@ Hiscore config data structure (version 1)
2 byte WRITE_HOLD
2 byte WRITE_REPEATCOUNT
2 byte WRITE_REPEATWAIT
2 byte (padding/future use)
1 byte ACCESS_PAUSEPAD
1 byte CHANGEMASK
- Entry format (when CFG_LENGTHWIDTH=1)
00 00 43 0b 0f 10 01 00
@@ -132,77 +159,100 @@ Hiscore config data structure (version 1)
*/
localparam HS_VERSION =7; // Version identifier for module
localparam HS_VERSION =14; // Version identifier for module
localparam HS_DUMPFORMAT =1; // Version identifier for dump format
localparam HS_HEADERLENGTH =16; // Size of header chunk (default=16 bytes)
// HS_DUMPFORMAT = 1 --> No header, just the extracted hiscore data
// Hiscore config and dump status
wire downloading_config;
wire parsing_header;
wire downloading_dump;
wire uploading_dump;
reg downloaded_config = 1'b0;
reg downloaded_dump = 1'b0;
reg uploaded_dump = 1'b0;
reg [3:0] initialised;
reg writing_scores = 1'b0;
reg checking_scores = 1'b0;
// Hiscore config tracking
wire downloading_config; // Is hiscore configuration currently being loaded from HPS?
reg downloaded_config = 1'b0; // Has hiscore configuration been loaded successfully
wire parsing_header; // Is hiscore configuration header currently being parsed?
wire parsing_mask; // Is hiscore configuration change mask currently being parsed? (optional 2nd line of config)
// Hiscore data tracking
wire downloading_dump; // Is hiscore data currently being loaded from HPS?
reg downloaded_dump = 1'b0; // Has hiscore data been loaded successfully
wire uploading_dump; // Is hiscore data currently being sent to HPS?
reg extracting_dump = 1'b0; // Is hiscore data currently being extracted from game RAM?
reg restoring_dump = 1'b0; // Is hiscore data currently being (or waiting to) restore to game RAM
reg checking_scores = 1'b0; // Is state machine currently checking game RAM for highscore restore readiness
reg reading_scores = 1'b0; // Is state machine currently reading game RAM for highscore dump
reg writing_scores = 1'b0; // Is state machine currently restoring hiscore data to game RAM
reg [3:0] initialised; // Number of times state machine has been initialised (debug only)
assign configured = downloaded_config;
assign downloading_config = ioctl_download && (ioctl_index==HS_CONFIGINDEX);
assign parsing_header = downloading_config && (ioctl_addr<=HS_HEADERLENGTH);
assign parsing_header = downloading_config && (ioctl_addr<HS_HEADERLENGTH);
assign parsing_mask = downloading_config && !parsing_header && (CHANGEMASK > 8'b0) && (ioctl_addr < HS_HEADERLENGTH + CHANGEMASK);
assign downloading_dump = ioctl_download && (ioctl_index==HS_DUMPINDEX);
assign uploading_dump = ioctl_upload && (ioctl_index==HS_DUMPINDEX);
assign ram_access = uploading_dump | writing_scores | checking_scores;
assign ram_intent_read = reading_scores | checking_scores;
assign ram_intent_write = writing_scores;
assign ram_address = ram_addr[HS_ADDRESSWIDTH-1:0];
reg [(SM_STATEWIDTH-1):0] state = SM_INIT; // Current state machine index
reg [(SM_STATEWIDTH-1):0] next_state = SM_INIT; // Next state machine index to move to after wait timer expires
reg [31:0] wait_timer; // Wait timer for inital/read/write delays
reg [(SM_STATEWIDTH-1):0] state = SM_INIT_RESTORE; // Current state machine index
reg [(SM_STATEWIDTH-1):0] next_state = SM_INIT_RESTORE; // Next state machine index to move to after wait timer expires
reg [31:0] wait_timer; // Wait timer for inital/read/write delays
reg [CFG_ADDRESSWIDTH-1:0] counter = 1'b0; // Index for current config table entry
reg [CFG_ADDRESSWIDTH-1:0] total_entries = 1'b0; // Total count of config table entries
reg reset_last = 1'b0; // Last cycle reset
reg [7:0] write_counter = 1'b0; // Index of current game RAM write attempt
reg [255:0] change_mask; // Bit mask for dump change check
reg [7:0] last_ioctl_index; // Last cycle HPS IO index
reg last_ioctl_download = 0;// Last cycle HPS IO download
reg last_ioctl_upload = 0; // Last cycle HPS IO upload
reg [7:0] last_ioctl_dout; // Last cycle HPS IO data out
reg [7:0] last_ioctl_dout2; // Last cycle +1 HPS IO data out
reg [7:0] last_ioctl_dout3; // Last cycle +2 HPS IO data out
reg [7:0] last_data_from_hps; // Last cycle HPS IO data out
reg [7:0] last_data_from_hps2; // Last cycle +1 HPS IO data out
reg [7:0] last_data_from_hps3; // Last cycle +2 HPS IO data out
reg last_OSD_STATUS; // Last cycle OSD status
reg [24:0] ram_addr; // Target RAM address for hiscore read/write
reg [24:0] old_io_addr;
reg [24:0] base_io_addr;
wire [23:0] addr_base;
wire [23:0] addr_base /* synthesis keep */;
wire [(CFG_LENGTHWIDTH*8)-1:0] length;
wire [24:0] end_addr = (addr_base + length - 1'b1);
reg [HS_SCOREWIDTH-1:0] local_addr;
wire [7:0] start_val;
wire [7:0] end_val;
reg [HS_SCOREWIDTH-1:0] data_addr;
reg [HS_SCOREWIDTH-1:0] buffer_addr;
wire [7:0] start_val /* synthesis keep */;
wire [7:0] end_val /* synthesis keep */;
wire [7:0] hiscore_data_out /* synthesis keep */;
reg dump_write = 1'b0;
wire [7:0] hiscore_buffer_out /* synthesis keep */;
reg buffer_write = 1'b0;
reg [19:0] compare_length = 1'b0;
reg compare_nonzero = 1'b1; // High after extract and compare if any byte returned is non-zero
reg compare_changed = 1'b1; // High after extract and compare if any byte is different to current hiscore data
wire check_mask = change_mask[compare_length]/* synthesis keep */;
reg dump_dirty = 1'b0; // High if dump has changed since last save (or first load if no save has occurred)
wire [23:0] address_data_in;
wire [(CFG_LENGTHWIDTH*8)-1:0] length_data_in;
assign address_data_in = {last_ioctl_dout2, last_ioctl_dout, ioctl_dout};
assign length_data_in = (CFG_LENGTHWIDTH == 1'b1) ? ioctl_dout : {last_ioctl_dout, ioctl_dout};
assign address_data_in = {last_data_from_hps2, last_data_from_hps, data_from_hps};
assign length_data_in = (CFG_LENGTHWIDTH == 1'b1) ? data_from_hps : {last_data_from_hps, data_from_hps};
wire address_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd3);
wire length_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd3 + CFG_LENGTHWIDTH);
wire startdata_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd4 + CFG_LENGTHWIDTH);
wire enddata_we = downloading_config & ~parsing_header & (ioctl_addr[2:0] == 3'd5 + CFG_LENGTHWIDTH);
wire parsing_config = ~(parsing_header | parsing_mask); // Hiscore config lines are being parsed
wire [CFG_ADDRESSWIDTH-1:0] config_upload_addr = ioctl_addr[CFG_ADDRESSWIDTH+2:3] - (9'd2 + CHANGEMASK[7:3]) /* synthesis keep */;
wire address_we = downloading_config & parsing_config & (ioctl_addr[2:0] == 3'd3);
wire length_we = downloading_config & parsing_config & (ioctl_addr[2:0] == 3'd3 + CFG_LENGTHWIDTH);
wire startdata_we = downloading_config & parsing_config & (ioctl_addr[2:0] == 3'd4 + CFG_LENGTHWIDTH);
wire enddata_we = downloading_config & parsing_config & (ioctl_addr[2:0] == 3'd5 + CFG_LENGTHWIDTH);
// RAM chunks used to store configuration data
// - address_table
// - length_table
// - startdata_table
// - enddata_table
// - Address table
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(24))
address_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.addr_a(config_upload_addr),
.we_a(address_we & ioctl_wr),
.d_a(address_data_in),
.addr_b(counter),
@@ -212,45 +262,61 @@ address_table(
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(CFG_LENGTHWIDTH*8))
length_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.addr_a(config_upload_addr),
.we_a(length_we & ioctl_wr),
.d_a(length_data_in),
.addr_b(counter),
.q_b(length)
);
// - Start data table
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(8))
startdata_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.addr_a(config_upload_addr),
.we_a(startdata_we & ioctl_wr),
.d_a(ioctl_dout),
.d_a(data_from_hps),
.addr_b(counter),
.q_b(start_val)
);
// - End data table
dpram_hs #(.aWidth(CFG_ADDRESSWIDTH),.dWidth(8))
enddata_table(
.clk(clk),
.addr_a(ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2),
.addr_a(config_upload_addr),
.we_a(enddata_we & ioctl_wr),
.d_a(ioctl_dout),
.d_a(data_from_hps),
.addr_b(counter),
.q_b(end_val)
);
// RAM chunk used to store hiscore data
// RAM chunk used to store valid hiscore data
dpram_hs #(.aWidth(HS_SCOREWIDTH),.dWidth(8))
hiscoredata (
hiscore_data (
.clk(clk),
.addr_a(ioctl_addr[(HS_SCOREWIDTH-1):0]),
.we_a(downloading_dump),
.d_a(ioctl_dout),
.addr_b(local_addr),
.we_b(ioctl_upload),
.d_b(ioctl_din),
.q_b(data_to_ram)
.d_a(data_from_hps),
.addr_b(data_addr),
.we_b(dump_write),
.d_b(hiscore_buffer_out),
.q_b(hiscore_data_out)
);
// RAM chunk used to store temporary high score data
dpram_hs #(.aWidth(HS_SCOREWIDTH),.dWidth(8))
hiscore_buffer (
.clk(clk),
.addr_a(buffer_addr),
.we_a(buffer_write),
.d_a(data_from_ram),
.q_a(hiscore_buffer_out)
);
assign data_to_ram = hiscore_data_out;
assign data_to_hps = hiscore_data_out;
wire [3:0] header_chunk = ioctl_addr[3:0];
wire [7:0] mask_chunk = ioctl_addr[7:0] - 5'd16;
wire [255:0] mask_load_index = mask_chunk * 8;
always @(posedge clk)
begin
@@ -262,19 +328,25 @@ begin
begin
if(ioctl_wr)
begin
if(header_chunk == 4'd3) START_WAIT <= { last_ioctl_dout3, last_ioctl_dout2, last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd5) CHECK_WAIT <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd7) CHECK_HOLD <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd9) WRITE_HOLD <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd11) WRITE_REPEATCOUNT <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd13) WRITE_REPEATWAIT <= { last_ioctl_dout, ioctl_dout };
if(header_chunk == 4'd14) ACCESS_PAUSEPAD <= ioctl_dout;
if(header_chunk == 4'd3) START_WAIT <= { last_data_from_hps3, last_data_from_hps2, last_data_from_hps, data_from_hps };
if(header_chunk == 4'd5) CHECK_WAIT <= { last_data_from_hps, data_from_hps };
if(header_chunk == 4'd7) CHECK_HOLD <= { last_data_from_hps, data_from_hps };
if(header_chunk == 4'd9) WRITE_HOLD <= { last_data_from_hps, data_from_hps };
if(header_chunk == 4'd11) WRITE_REPEATCOUNT <= { last_data_from_hps, data_from_hps };
if(header_chunk == 4'd13) WRITE_REPEATWAIT <= { last_data_from_hps, data_from_hps };
if(header_chunk == 4'd14) ACCESS_PAUSEPAD <= data_from_hps;
if(header_chunk == 4'd15) CHANGEMASK <= data_from_hps;
end
end
else
if(parsing_mask)
begin
if(ioctl_wr == 1'b1) change_mask[mask_load_index +: 8] <= data_from_hps;
end
else
begin
// Keep track of the largest entry during config download
total_entries <= ioctl_addr[CFG_ADDRESSWIDTH+2:3] - 2'd2;
total_entries <= config_upload_addr;
end
end
@@ -285,71 +357,214 @@ begin
if (last_ioctl_index==HS_DUMPINDEX) downloaded_dump <= 1'b1;
end
// Track completion of dump upload
if ((last_ioctl_upload != ioctl_upload) && (ioctl_upload == 1'b0))
begin
if (last_ioctl_index==HS_DUMPINDEX)
begin
uploaded_dump <= 1'b1;
// Mark uploaded dump as readable in case of reset
downloaded_dump <= 1'b1;
end
end
// Track last ioctl values
// Track last cycle values
last_ioctl_download <= ioctl_download;
last_ioctl_upload <= ioctl_upload;
last_ioctl_index <= ioctl_index;
last_OSD_STATUS <= OSD_STATUS;
// Cascade incoming data bytes from HPS
if(ioctl_download && ioctl_wr)
begin
last_ioctl_dout3 = last_ioctl_dout2;
last_ioctl_dout2 = last_ioctl_dout;
last_ioctl_dout = ioctl_dout;
last_data_from_hps3 = last_data_from_hps2;
last_data_from_hps2 = last_data_from_hps;
last_data_from_hps = data_from_hps;
end
// If we have a valid configuration then enable the hiscore system
if(downloaded_config)
begin
// Check for end of state machine reset to initialise state machine
// Check for end of core reset to initialise state machine for restore
reset_last <= reset;
if (reset_last == 1'b1 && reset == 1'b0)
if (downloaded_dump == 1'b1 && reset_last == 1'b1 && reset == 1'b0)
begin
wait_timer <= START_WAIT;
next_state <= SM_INIT;
next_state <= SM_INIT_RESTORE;
state <= SM_TIMER;
counter <= 1'b0;
initialised <= initialised + 1'b1;
restoring_dump <= 1'b1;
end
else
begin
// Upload scores to HPS
// Upload scores if requested by HPS
// - Data is now sent from the hiscore data buffer rather than game RAM as in previous versions
if (uploading_dump == 1'b1)
begin
// generate addresses to read high score from game memory. Base addresses off ioctl_address
if (ioctl_addr == 25'b0) begin
local_addr <= 0;
base_io_addr <= 25'b0;
counter <= 1'b0000;
end
// Move to next entry when last address is reached
if (old_io_addr!=ioctl_addr && ram_addr==end_addr[24:0])
begin
counter <= counter + 1'b1;
base_io_addr <= ioctl_addr;
end
// Set game ram address for reading back to HPS
ram_addr <= addr_base + (ioctl_addr - base_io_addr);
// Set local addresses to update cached dump in case of reset
local_addr <= ioctl_addr[HS_SCOREWIDTH-1:0];
// Set local address to read from hiscore data based on ioctl_address
data_addr <= ioctl_addr[HS_SCOREWIDTH-1:0];
// Clear dump dirty flag
dump_dirty <= 1'b0;
end
// Trigger hiscore extraction when OSD is opened
if(last_OSD_STATUS==1'b0 && OSD_STATUS==1'b1 && extracting_dump==1'b0 && uploading_dump==1'b0 && restoring_dump==1'b0)
begin
extracting_dump <= 1'b1;
state <= SM_COMPAREINIT;
end
// Extract hiscore data from game RAM and save in hiscore data buffer
if (extracting_dump == 1'b1)
begin
case (state)
// Compare process states
SM_COMPAREINIT: // Initialise state machine for comparison
begin
// Setup addresses and comparison flags
buffer_addr <= 0;
data_addr <= 0;
counter <= 0;
compare_nonzero <= 1'b0;
compare_changed <= 1'b0;
compare_length <= 1'b0;
// Pause cpu and wait for next state
pause_cpu <= 1'b1;
state <= SM_TIMER;
next_state <= SM_COMPAREBEGIN;
wait_timer <= ACCESS_PAUSEPAD;
end
SM_COMPAREBEGIN:
begin
// Get ready to read next line (wait until addr_base is updated)
reading_scores <= 1'b1;
state <= SM_COMPAREREADY;
end
SM_COMPAREREADY:
begin
// Set ram address and wait for it to return correctly
ram_addr <= addr_base;
if(ram_addr == addr_base)
begin
state <= SM_COMPAREREAD;
end
end
SM_COMPAREREAD:
begin
// Setup next address and signal write enable to hiscore buffer
buffer_write <= 1'b1;
state <= SM_COMPAREDONE;
end
SM_COMPAREDONE:
begin
// If RAM data has changed since last dump and there is either no mask or a 1 in the mask for this address
if (data_from_ram != hiscore_data_out && (CHANGEMASK==8'b0 || check_mask==1))
begin
// Hiscore data changed
compare_changed <= 1'b1;
end
if (data_from_ram != 8'b0)
begin
// Hiscore data is not blank
compare_nonzero <= 1'b1;
end
compare_length <= compare_length + 20'b1;
// Move to next entry when last address is reached
if (ram_addr == end_addr)
begin
// If this was the last entry then we are done
if (counter == total_entries)
begin
state <= SM_TIMER;
reading_scores <= 1'b0;
next_state <= SM_COMPARECOMPLETE;
wait_timer <= ACCESS_PAUSEPAD;
end
else
begin
// Next config line
counter <= counter + 1'b1;
state <= SM_COMPAREBEGIN;
end
end
else
begin
// Keep extracting this section
state <= SM_COMPAREREAD;
ram_addr <= ram_addr + 1'b1;
end
// Always stop writing to hiscore dump ram and increment local address
buffer_addr <= buffer_addr + 1'b1;
data_addr <= data_addr + 1'b1;
buffer_write <= 1'b0;
end
SM_COMPARECOMPLETE:
begin
pause_cpu <= 1'b0;
reading_scores <= 1'b0;
if (compare_changed == 1'b1 && compare_nonzero == 1'b1)
begin
// If high scores have changed and are not blank, update the hiscore data from extract buffer
dump_dirty <= 1'b1;
state <= SM_EXTRACTINIT;
end
else
begin
// If no change or scores are invalid leave the existing hiscore data in place
if(dump_dirty == 1'b1 && autosave == 1'b1)
begin
state <= SM_EXTRACTSAVE;
end
else
begin
extracting_dump <= 1'b0;
state <= SM_STOPPED;
end
end
end
SM_EXTRACTINIT:
begin
// Setup address and counter
data_addr <= 0;
buffer_addr <= 0;
state <= SM_EXTRACT;
dump_write <= 1'b1;
end
SM_EXTRACT:
begin
// Keep writing until end of buffer
if (buffer_addr == compare_length)
begin
dump_write <= 1'b0;
state <= SM_EXTRACTSAVE;
end
// Increment buffer address and set data address to one behind
data_addr <= buffer_addr;
buffer_addr <= buffer_addr + 1'b1;
end
SM_EXTRACTSAVE:
begin
if(autosave == 1'b1)
begin
ioctl_upload_req <= 1'b1;
state <= SM_TIMER;
next_state <= SM_EXTRACTCOMPLETE;
wait_timer <= 4'd4;
end
else
begin
extracting_dump <= 1'b0;
state <= SM_STOPPED;
end
end
SM_EXTRACTCOMPLETE:
begin
ioctl_upload_req <= 1'b0;
extracting_dump <= 1'b0;
state <= SM_STOPPED;
end
endcase
end
if (ioctl_upload == 1'b0 && downloaded_dump == 1'b1 && reset == 1'b0)
// If we are not uploading or resetting and valid hiscore data is available then start the state machine to write data to game RAM
if (uploading_dump == 1'b0 && downloaded_dump == 1'b1 && reset == 1'b0)
begin
// State machine to write data to game RAM
case (state)
SM_INIT: // Start state machine
SM_INIT_RESTORE: // Start state machine
begin
// Setup base addresses
local_addr <= 0;
data_addr <= 0;
base_io_addr <= 25'b0;
// Reset entry counter and states
counter <= 0;
@@ -380,7 +595,7 @@ begin
SM_CHECKSTARTVAL: // Start check
begin
// Check for matching start value
if(wait_timer != CHECK_HOLD & ioctl_din == start_val)
if(wait_timer != CHECK_HOLD && data_from_ram == start_val)
begin
// Prepare end check
ram_addr <= end_addr;
@@ -408,7 +623,7 @@ begin
SM_CHECKENDVAL: // End check
begin
// Check for matching end value
if (wait_timer != CHECK_HOLD & ioctl_din == end_val)
if (wait_timer != CHECK_HOLD & data_from_ram == end_val)
begin
if (counter == total_entries)
begin
@@ -448,7 +663,7 @@ begin
SM_CHECKCANCEL: // Cancel start/end check run - disable RAM access and keep CPU paused
begin
pause_cpu <= 1'b0;
next_state <= SM_INIT;
next_state <= SM_INIT_RESTORE;
state <= SM_TIMER;
wait_timer <= CHECK_WAIT;
end
@@ -472,7 +687,7 @@ begin
SM_WRITEREADY: // local ram should be correct, start write to game RAM
begin
ram_addr <= addr_base + (local_addr - base_io_addr);
ram_addr <= addr_base + (data_addr - base_io_addr);
state <= SM_TIMER;
next_state <= SM_WRITEDONE;
wait_timer <= WRITE_HOLD;
@@ -481,7 +696,7 @@ begin
SM_WRITEDONE:
begin
local_addr <= local_addr + 1'b1; // Increment to next byte of entry
data_addr <= data_addr + 1'b1; // Increment to next byte of entry
if (ram_addr == end_addr)
begin
// End of entry reached
@@ -494,7 +709,7 @@ begin
// Move to next entry
counter <= counter + 1'b1;
write_counter <= 1'b0;
base_io_addr <= local_addr + 1'b1;
base_io_addr <= data_addr + 1'b1;
state <= SM_WRITEBEGIN;
end
end
@@ -509,12 +724,13 @@ begin
begin
ram_write <= 1'b0;
writing_scores <= 1'b0;
restoring_dump <= 1'b0;
state <= SM_TIMER;
if(write_counter < WRITE_REPEATCOUNT)
begin
// Schedule next write
next_state <= SM_WRITERETRY;
local_addr <= 0;
data_addr <= 0;
wait_timer <= WRITE_REPEATWAIT;
end
else
@@ -536,23 +752,28 @@ begin
begin
pause_cpu <= 1'b0;
end
SM_TIMER: // timer wait state
begin
if (wait_timer > 1'b0)
wait_timer <= wait_timer - 1'b1;
else
state <= next_state;
end
endcase
end
if(state == SM_TIMER) // timer wait state
begin
// Do not progress timer if CPU is paused by source other than this module
// - Stops initial hiscore load delay being foiled by user pausing/entering OSD
if (paused == 1'b0 || pause_cpu == 1'b1)
begin
if (wait_timer > 1'b0)
wait_timer <= wait_timer - 1'b1;
else
state <= next_state;
end
end
end
end
old_io_addr<=ioctl_addr;
end
endmodule
// Simple dual-port RAM module used by hiscore module
module dpram_hs #(
parameter dWidth=8,
parameter aWidth=8
@@ -592,4 +813,4 @@ always @(posedge clk) begin
end
end
endmodule
endmodule

View File

@@ -27,6 +27,7 @@
Version history:
0001 - 2021-03-15 - First marked release
0002 - 2021-08-28 - Add optional output of dim_video signal (currently used by Galaga)
============================================================================
*/
module pause #(
@@ -49,19 +50,26 @@ module pause #(
input [(BW-1):0] b, // Blue channel
output pause_cpu, // Pause signal to CPU (active-high)
`ifdef PAUSE_OUTPUT_DIM
output dim_video, // Dim video requested (active-high)
`endif
output [(RW+GW+BW-1):0] rgb_out // RGB output to arcade_video module
);
// Option constants
localparam pause_in_osd = 1'b0;
localparam dim_video = 1'b1;
localparam dim_video_timer= 1'b1;
reg pause_toggle = 1'b0; // User paused (active-high)
reg [31:0] pause_timer = 1'b0; // Time since pause
reg [31:0] dim_timeout = (CLKSPD*10000000); // Time until video output dim (10 seconds @ CLKSPD Mhz)
`ifndef PAUSE_OUTPUT_DIM
wire dim_video; // Dim video requested (active-high)
`endif
assign pause_cpu = (pause_request | pause_toggle | (OSD_STATUS & options[pause_in_osd])) & !reset;
assign dim_video = (pause_timer >= dim_timeout);
always @(posedge clk_sys) begin
@@ -73,7 +81,7 @@ always @(posedge clk_sys) begin
// Clear user pause on reset
if(pause_toggle & reset) pause_toggle <= 0;
if(pause_cpu & options[dim_video])
if(pause_cpu & options[dim_video_timer])
begin
// Track pause duration for video dim
if((pause_timer<dim_timeout))
@@ -87,7 +95,6 @@ always @(posedge clk_sys) begin
end
end
// Dim video output if pause timer exceeds threshold
assign rgb_out = (pause_timer >= dim_timeout) ? {r >> 1,g >> 1, b >> 1} : {r,g,b};
assign rgb_out = dim_video ? {r >> 1,g >> 1, b >> 1} : {r,g,b};
endmodule

2205
stp1.stp

File diff suppressed because one or more lines are too long

View File

@@ -382,8 +382,9 @@ ARCHITECTURE rtl OF ascal IS
SIGNAL avl_o_vs_sync,avl_o_vs : std_logic;
SIGNAL avl_fb_ena : std_logic;
FUNCTION buf_next(a,b : natural RANGE 0 TO 2) RETURN natural IS
FUNCTION buf_next(a,b : natural RANGE 0 TO 2; freeze : std_logic := '0') RETURN natural IS
BEGIN
IF (freeze='1') THEN RETURN a; END IF;
IF (a=0 AND b=1) OR (a=1 AND b=0) THEN RETURN 2; END IF;
IF (a=1 AND b=2) OR (a=2 AND b=1) THEN RETURN 0; END IF;
RETURN 1;
@@ -400,6 +401,7 @@ ARCHITECTURE rtl OF ascal IS
----------------------------------------------------------
-- Output
SIGNAL o_run : std_logic;
SIGNAL o_freeze : std_logic;
SIGNAL o_mode,o_hmode,o_vmode : unsigned(4 DOWNTO 0);
SIGNAL o_format : unsigned(5 DOWNTO 0);
SIGNAL o_fb_pal_dr : unsigned(23 DOWNTO 0);
@@ -1730,28 +1732,61 @@ BEGIN
-- Triple buffering.
-- For intelaced video, half frames are updated independently
-- Input : Toggle buffer at end of input frame
o_freeze <= freeze;
o_inter <=i_inter; -- <ASYNC>
o_iendframe0<=i_endframe0; -- <ASYNC>
o_iendframe02<=o_iendframe0;
IF o_iendframe0='1' AND o_iendframe02='0' THEN
o_ibuf0<=buf_next(o_ibuf0,o_obuf0);
o_ibuf0<=buf_next(o_ibuf0,o_obuf0,o_freeze);
o_bufup0<='1';
END IF;
o_iendframe1<=i_endframe1; -- <ASYNC>
o_iendframe12<=o_iendframe1;
IF o_iendframe1='1' AND o_iendframe12='0' THEN
o_ibuf1<=buf_next(o_ibuf1,o_obuf1);
o_ibuf1<=buf_next(o_ibuf1,o_obuf1,o_freeze);
o_bufup1<='1';
END IF;
-- Output : Change framebuffer, and image properties, at VS falling edge
IF o_vsv(1)='1' AND o_vsv(0)='0' AND o_bufup0='1' THEN
o_obuf0<=buf_next(o_obuf0,o_ibuf0,o_freeze);
o_bufup0<='0';
END IF;
IF o_vsv(1)='1' AND o_vsv(0)='0' AND o_bufup1='1' THEN
o_obuf1<=buf_next(o_obuf1,o_ibuf1);
o_obuf1<=buf_next(o_obuf1,o_ibuf1,o_freeze);
o_bufup1<='0';
o_ihsize<=i_hrsize; -- <ASYNC>
o_ivsize<=i_vrsize; -- <ASYNC>
o_hdown<=i_hdown; -- <ASYNC>
o_vdown<=i_vdown; -- <ASYNC>
END IF;
-- Simultaneous change of input and output framebuffers
IF o_vsv(1)='1' AND o_vsv(0)='0' AND
o_iendframe0='1' AND o_iendframe02='0' THEN
o_bufup0<='0';
o_obuf0<=o_ibuf0;
END IF;
IF o_vsv(1)='1' AND o_vsv(0)='0' AND
o_iendframe1='1' AND o_iendframe12='0' THEN
o_bufup1<='0';
o_obuf1<=o_ibuf1;
END IF;
-- Non-interlaced, use same buffer for even and odd lines
IF o_inter='0' THEN
o_ibuf1<=o_ibuf0;
o_obuf1<=o_obuf0;
END IF;
-- Triple buffer disabled
IF o_mode(3)='0' THEN
o_obuf0<=0;
o_obuf1<=0;
o_ibuf0<=0;
o_ibuf1<=0;
END IF;
-- Framebuffer mode.
IF o_fb_ena='1' THEN
o_ihsize<=o_fb_hsize;
@@ -1771,25 +1806,6 @@ BEGIN
o_stride<=to_unsigned(o_ihsize_temp2,14);
o_stride(NB_BURST-1 DOWNTO 0)<=(OTHERS =>'0');
END IF;
IF o_vsv(1)='1' AND o_vsv(0)='0' AND o_bufup0='1' THEN
o_obuf0<=buf_next(o_obuf0,o_ibuf0);
o_bufup0<='0';
END IF;
IF o_inter='0' THEN
o_ibuf1<=o_ibuf0;
o_obuf1<=o_obuf0;
END IF;
-- Triple buffer disabled
IF o_mode(3)='0' THEN
o_obuf0<=0;
o_obuf1<=0;
o_ibuf0<=0;
o_ibuf1<=0;
END IF;
------------------------------------------------------
o_hmode<=o_mode;
IF o_hdown='1' AND DOWNSCALE THEN

View File

@@ -24,15 +24,14 @@
// Use buffer to access SD card. It's time-critical part.
//
// WIDE=1 for 16 bit file I/O
// VDNUM 1-4
module hps_io #(parameter STRLEN=0, PS2DIV=0, WIDE=0, VDNUM=1, PS2WE=0)
// VDNUM 1..4
// BLKSZ 0..7: 0 = 128, 1 = 256, 2 = 512(default), .. 7 = 16384
//
module hps_io #(parameter CONF_STR, CONF_STR_BRAM=1, PS2DIV=0, WIDE=0, VDNUM=1, BLKSZ=2, PS2WE=0)
(
input clk_sys,
inout [45:0] HPS_BUS,
// parameter STRLEN and the actual length of conf_str have to match
input [(8*STRLEN)-1:0] conf_str,
// buttons up to 32
output reg [31:0] joystick_0,
output reg [31:0] joystick_1,
@@ -86,7 +85,8 @@ module hps_io #(parameter STRLEN=0, PS2DIV=0, WIDE=0, VDNUM=1, PS2WE=0)
output reg [63:0] img_size, // size of image in bytes. valid only for active bit in img_mounted
// SD block level access
input [31:0] sd_lba,
input [31:0] sd_lba[VDNUM],
input [5:0] sd_blk_cnt[VDNUM], // number of blocks-1, total size ((sd_blk_cnt+1)*(1<<(BLKSZ+7))) must be <= 16384!
input [VD:0] sd_rd,
input [VD:0] sd_wr,
output reg [VD:0] sd_ack,
@@ -94,9 +94,8 @@ module hps_io #(parameter STRLEN=0, PS2DIV=0, WIDE=0, VDNUM=1, PS2WE=0)
// SD byte level access. Signals for 2-PORT altsyncram.
output reg [AW:0] sd_buff_addr,
output reg [DW:0] sd_buff_dout,
input [DW:0] sd_buff_din,
input [DW:0] sd_buff_din[VDNUM],
output reg sd_buff_wr,
input [15:0] sd_req_type,
// ARM -> FPGA download
output reg ioctl_download = 0, // signal indicating an active download
@@ -105,6 +104,7 @@ module hps_io #(parameter STRLEN=0, PS2DIV=0, WIDE=0, VDNUM=1, PS2WE=0)
output reg [26:0] ioctl_addr, // in WIDE mode address will be incremented by 2
output reg [DW:0] ioctl_dout,
output reg ioctl_upload = 0, // signal indicating an active upload
input ioctl_upload_req,
input [DW:0] ioctl_din,
output reg ioctl_rd,
output reg [31:0] ioctl_file_ext,
@@ -156,10 +156,8 @@ module hps_io #(parameter STRLEN=0, PS2DIV=0, WIDE=0, VDNUM=1, PS2WE=0)
assign EXT_BUS[31:16] = HPS_BUS[31:16];
assign EXT_BUS[35:33] = HPS_BUS[35:33];
localparam MAX_W = $clog2((32 > (STRLEN+2)) ? 32 : (STRLEN+2))-1;
localparam DW = (WIDE) ? 15 : 7;
localparam AW = (WIDE) ? 7 : 8;
localparam AW = (WIDE) ? 12 : 13;
localparam VD = VDNUM-1;
wire io_strobe= HPS_BUS[33];
@@ -182,22 +180,18 @@ assign forced_scandoubler = cfg[4];
//cfg[5] - ypbpr handled in sys_top
assign direct_video = cfg[10];
// command byte read by the io controller
wire [15:0] sd_cmd =
{
2'b00,
(VDNUM>=4) ? sd_wr[3] : 1'b0,
(VDNUM>=3) ? sd_wr[2] : 1'b0,
(VDNUM>=2) ? sd_wr[1] : 1'b0,
reg [3:0] sdn;
reg [3:0] sd_rrb = 0;
always_comb begin
int n, i;
(VDNUM>=4) ? sd_rd[3] : 1'b0,
(VDNUM>=3) ? sd_rd[2] : 1'b0,
(VDNUM>=2) ? sd_rd[1] : 1'b0,
4'h5, 1'b0, 1'b0,
sd_wr[0],
sd_rd[0]
};
sdn = 0;
for(i = VDNUM - 1; i >= 0; i = i - 1) begin
n = i + sd_rrb;
if(n >= VDNUM) n = n - VDNUM;
if(sd_wr[n] | sd_rd[n]) sdn = n[3:0];
end
end
/////////////////////////////////////////////////////////
@@ -221,6 +215,19 @@ video_calc video_calc
/////////////////////////////////////////////////////////
localparam STRLEN = $size(CONF_STR)>>3;
localparam MAX_W = $clog2((32 > (STRLEN+2)) ? 32 : (STRLEN+2))-1;
wire [7:0] conf_byte;
generate
if(CONF_STR_BRAM) begin
confstr_rom #(CONF_STR, STRLEN) confstr_rom(.*, .conf_addr(byte_cnt - 1'd1));
end
else begin
assign conf_byte = CONF_STR[{(STRLEN - byte_cnt),3'b000} +:8];
end
endgenerate
assign gamma_bus[20:0] = {clk_sys, gamma_en, gamma_wr, gamma_wr_addr, gamma_value};
reg gamma_en;
reg gamma_wr;
@@ -232,7 +239,8 @@ wire pressed = (ps2_key_raw[15:8] != 8'hf0);
wire extended = (~pressed ? (ps2_key_raw[23:16] == 8'he0) : (ps2_key_raw[15:8] == 8'he0));
reg [MAX_W:0] byte_cnt;
wire [7:0] disk = 4'd1 << (io_din[10:8]-1'd1);
reg [3:0] sdn_ack;
wire [15:0] disk = 16'd1 << io_din[11:8];
always@(posedge clk_sys) begin : uio_block
reg [15:0] cmd;
@@ -243,16 +251,22 @@ always@(posedge clk_sys) begin : uio_block
reg [3:0] stflg = 0;
reg [63:0] status_req;
reg old_status_set = 0;
reg old_upload_req = 0;
reg upload_req = 0;
reg old_info = 0;
reg [7:0] info_n = 0;
reg [15:0] tmp1;
reg [7:0] tmp2;
reg [3:0] sdn_r;
old_status_set <= status_set;
if(~old_status_set & status_set) begin
stflg <= stflg + 1'd1;
status_req <= status_in;
end
old_upload_req <= ioctl_upload_req;
if(~old_upload_req & ioctl_upload_req) upload_req <= 1;
old_info <= info_req;
if(~old_info & info_req) info_n <= info;
@@ -291,14 +305,16 @@ always@(posedge clk_sys) begin : uio_block
cmd <= io_din;
casex(io_din)
'hX17,
'hX18: sd_ack <= VD ? disk[VD:0] : 1'd1;
'h29: io_dout <= {4'hA, stflg};
'h2B: io_dout <= 1;
'h2F: io_dout <= 1;
'h32: io_dout <= gamma_bus[21];
'h36: begin io_dout <= info_n; info_n <= 0; end
'h39: io_dout <= 1;
'h16: begin io_dout <= {1'b1, sd_blk_cnt[sdn], BLKSZ[2:0], sdn, sd_wr[sdn], sd_rd[sdn]}; sdn_r <= sdn; end
'h0X17,
'h0X18: begin sd_ack <= disk[VD:0]; sdn_ack <= io_din[11:8]; end
'h29: io_dout <= {4'hA, stflg};
'h2B: io_dout <= 1;
'h2F: io_dout <= 1;
'h32: io_dout <= gamma_bus[21];
'h36: begin io_dout <= info_n; info_n <= 0; end
'h39: io_dout <= 1;
'h3C: if(upload_req) begin io_dout <= 1; upload_req <= 0; end
endcase
sd_buff_addr <= 0;
@@ -347,29 +363,28 @@ always@(posedge clk_sys) begin : uio_block
end
// reading config string, returning a byte from string
'h14: if(byte_cnt <= STRLEN) io_dout[7:0] <= conf_str[{(STRLEN - byte_cnt),3'b000} +:8];
'h14: if(byte_cnt <= STRLEN) io_dout[7:0] <= conf_byte;
// reading sd card status
'h16: if(!byte_cnt[MAX_W:3]) begin
case(byte_cnt[2:0])
1: io_dout <= sd_cmd;
2: io_dout <= sd_lba[15:0];
3: io_dout <= sd_lba[31:16];
4: io_dout <= sd_req_type;
'h16: if(!byte_cnt[MAX_W:2]) begin
case(byte_cnt[1:0])
1: sd_rrb <= (sd_rrb == VD) ? 4'd0 : (sd_rrb + 1'd1);
2: io_dout <= sd_lba[sdn_r][15:0];
3: io_dout <= sd_lba[sdn_r][31:16];
endcase
end
// send sector IO -> FPGA
// flag that download begins
'hX17: begin
'h0X17: begin
sd_buff_dout <= io_din[DW:0];
b_wr <= 1;
end
// reading sd card write data
'hX18: begin
'h0X18: begin
if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1;
io_dout <= sd_buff_din;
io_dout <= sd_buff_din[sdn_ack];
end
// joystick analog
@@ -925,3 +940,16 @@ always @(posedge clk_100) begin
end
endmodule
module confstr_rom #(parameter CONF_STR, STRLEN)
(
input clk_sys,
input [$clog2(STRLEN+1)-1:0] conf_addr,
output reg [7:0] conf_byte
);
wire [7:0] rom[STRLEN];
initial for(int i = 0; i < STRLEN; i++) rom[i] = CONF_STR[((STRLEN-i)*8)-1 -:8];
always @ (posedge clk_sys) conf_byte <= rom[conf_addr];
endmodule

View File

@@ -72,7 +72,7 @@ always@(posedge clk_sys) begin
// command 0x40: OSDCMDENABLE, OSDCMDDISABLE
if(io_din[7:4] == 4) begin
if(!io_din[0]) {osd_status,highres} <= 0;
else {osd_status,info} <= {~io_din[2],io_din[2]};
else {osd_status,info} <= {~io_din[2] & ~io_din[3],io_din[2]};
bcnt <= 0;
end
// command 0x20: OSDCMDWRITE

View File

@@ -23,9 +23,9 @@
//
// Made module syncrhronous. Total code refactoring. (Sorgelig)
// clk_spi must be at least 4 x sck for proper work.
// clk_spi must be at least 2 x sck for proper work.
module sd_card #(parameter WIDE = 0)
module sd_card #(parameter WIDE = 0, OCTAL=0)
(
input clk_sys,
input reset,
@@ -49,25 +49,29 @@ module sd_card #(parameter WIDE = 0)
input ss,
input sck,
input mosi,
output reg miso
input [SW:0] mosi,
output reg [SW:0] miso
);
localparam AW = WIDE ? 7 : 8;
localparam DW = WIDE ? 15 : 7;
localparam AW = WIDE ? 7 : 8;
localparam DW = WIDE ? 15 : 7;
localparam SZ = OCTAL ? 8 : 1;
localparam SW = SZ-1;
wire[31:0] OCR = { 1'b1, csd_sdhc, 30'd0 }; // bit30 = 1 -> high capaciry card (sdhc) // bit31 = 0 -> card power up finished
wire [7:0] READ_DATA_TOKEN = 8'hfe;
wire [7:0] DATA_TOKEN_CMD25 = 8'hfc;
wire [7:0] STOP_TRAN = 8'hfd;
wire [7:0] DATA_TOKEN = 8'hfe;
wire [7:0] WRITE_DATA_RESPONSE = 8'h05;
// number of bytes to wait after a command before sending the reply
localparam NCR=3;
localparam NCR = 5+3; // 5 bytes are required (command length)
localparam RD_STATE_IDLE = 0;
localparam RD_STATE_WAIT_IO = 1;
localparam RD_STATE_SEND_TOKEN = 2;
localparam RD_STATE_SEND_DATA = 3;
localparam RD_STATE_WAIT_M = 4;
localparam RD_STATE_START = 1;
localparam RD_STATE_WAIT_IO = 2;
localparam RD_STATE_SEND_TOKEN = 3;
localparam RD_STATE_SEND_DATA = 4;
localparam RD_STATE_WAIT_M = 5;
localparam WR_STATE_IDLE = 0;
localparam WR_STATE_EXP_DTOKEN = 1;
@@ -80,7 +84,7 @@ localparam WR_STATE_BUSY = 6;
localparam PREF_STATE_IDLE = 0;
localparam PREF_STATE_RD = 1;
localparam PREF_STATE_FINISH = 2;
altsyncram sdbuf
(
.clock0 (clk_sys),
@@ -157,402 +161,301 @@ end
wire [127:0] CSD = {1'b0,csd_sdhc,6'h00,8'h0e,8'h00,8'h32,8'h5b,8'h59,6'h00,csd_size,7'h7f,8'h80,8'h0a,8'h40,8'h40,8'hf1};
wire [127:0] CID = {8'hcd,8'hc7,8'h00,8'h93,8'h6f,8'h2f,8'h73,8'h00,8'h00,8'h44,8'h32,8'h38,8'h34,8'h00,8'h00,8'h3e};
reg [31:0] new_lba;
reg [8:0] buffer_ptr;
reg [7:0] buffer_din;
wire [7:0] buffer_dout;
wire [7:0] config_dout;
reg buffer_wr;
reg [1:0] sd_buf, spi_buf;
reg [6:0] sbuf;
reg [2:0] bit_cnt;
wire last_bit = &bit_cnt || OCTAL;
wire [7:0] ibuf = OCTAL ? mosi : {sbuf,mosi[0]};
always @(posedge clk_spi) begin
reg [2:0] read_state;
reg [2:0] write_state;
reg [6:0] sbuf;
reg cmd55;
reg [7:0] cmd;
reg [2:0] bit_cnt;
reg [3:0] byte_cnt;
reg [7:0] reply;
reg [7:0] reply0, reply1, reply2, reply3;
reg [3:0] reply_len;
reg tx_finish;
reg rx_finish;
reg old_sck;
reg synced;
reg [5:0] ack;
reg [4:0] idle_cnt = 0;
reg [2:0] wait_m_cnt;
reg [1:0] pref_state;
reg [2:0] read_state;
reg [2:0] write_state;
reg [1:0] pref_state;
reg [5:0] cmd;
reg cmd55;
reg [39:0] reply;
reg [3:0] byte_cnt;
reg old_sck;
reg [2:0] ack;
reg [2:0] wait_m_cnt;
reg [31:0] arg;
if(buffer_wr & ~&buffer_ptr) buffer_ptr <= buffer_ptr + 1'd1;
buffer_wr <= 0;
ack[1:0] <= {ack[0],sd_ack};
if(ack[1] == ack[0]) ack[2] <= ack[1];
ack <= {ack[4:0], sd_ack};
if(ack[5:4] == 2'b01) {sd_rd,sd_wr} <= 0;
if(ack[5:4] == 2'b10) begin
if(~ack[2] & ack[1]) {sd_rd,sd_wr} <= 0;
if( ack[2] & ~ack[1]) begin
sd_buf <= sd_buf + 1'd1;
sd_lba <= sd_lba + 1;
end
case(pref_state)
PREF_STATE_IDLE:
if(((sd_buf - spi_buf) < 2) && (read_state != RD_STATE_IDLE)) begin
sd_rd <= 1;
pref_state <= PREF_STATE_RD;
end
PREF_STATE_RD:
if(read_state == RD_STATE_IDLE) begin
pref_state <= PREF_STATE_IDLE;
end
else if(ack[5:4] == 2'b10) begin
pref_state <= (cmd == 'h52) ? PREF_STATE_IDLE : PREF_STATE_FINISH;
end
buffer_wr <= 0;
PREF_STATE_FINISH:
if(read_state == RD_STATE_IDLE) begin
pref_state <= PREF_STATE_IDLE;
end
endcase
old_sck <= sck;
if(~ss) idle_cnt <= 31;
else if(~old_sck && sck && idle_cnt) idle_cnt <= idle_cnt - 1'd1;
if(reset || !idle_cnt) begin
if(reset) begin
bit_cnt <= 0;
byte_cnt <= 15;
synced <= 0;
miso <= 1;
sbuf <= 7'b1111111;
tx_finish <= 0;
rx_finish <= 0;
byte_cnt <= '1;
miso <= '1;
cmd <= 0;
sd_wr <= 0;
sd_rd <= 0;
read_state <= RD_STATE_IDLE;
write_state <= WR_STATE_IDLE;
pref_state <= PREF_STATE_IDLE;
end
else begin
if(old_sck & ~sck & ~ss) begin
tx_finish <= 0;
miso <= 1; // default: send 1's (busy/wait)
if(byte_cnt == 5+NCR) begin
miso <= reply[~bit_cnt];
if(bit_cnt == 7) begin
// these three commands all have a reply_len of 0 and will thus
// not send more than a single reply byte
// CMD9: SEND_CSD
// CMD10: SEND_CID
if((cmd == 'h49) | (cmd == 'h4a))
read_state <= RD_STATE_SEND_TOKEN; // jump directly to data transmission
// CMD17/CMD18
if((cmd == 'h51) | (cmd == 'h52)) begin
spi_buf <= 0;
sd_buf <= 0;
sd_lba <= csd_sdhc ? new_lba : {9'd0, new_lba[31:9]};
read_state <= RD_STATE_WAIT_IO; // start waiting for data from io controller
end
if(buffer_wr) begin
if(~&buffer_ptr) buffer_ptr <= buffer_ptr + 1'd1;
else begin
spi_buf <= spi_buf + 1'd1;
sd_wr <= 1;
end
end
else if((reply_len > 0) && (byte_cnt == 5+NCR+1)) miso <= reply0[~bit_cnt];
else if((reply_len > 1) && (byte_cnt == 5+NCR+2)) miso <= reply1[~bit_cnt];
else if((reply_len > 2) && (byte_cnt == 5+NCR+3)) miso <= reply2[~bit_cnt];
else if((reply_len > 3) && (byte_cnt == 5+NCR+4)) miso <= reply3[~bit_cnt];
else begin
if(byte_cnt > 5+NCR && read_state==RD_STATE_IDLE && write_state==WR_STATE_IDLE) tx_finish <= 1;
end
// ---------- read state machine processing -------------
case(read_state)
RD_STATE_IDLE: ; // do nothing
// waiting for io controller to return data
RD_STATE_WAIT_IO: begin
if((sd_buf != spi_buf) & (bit_cnt == 7)) read_state <= RD_STATE_SEND_TOKEN;
end
// send data token
RD_STATE_SEND_TOKEN: begin
miso <= READ_DATA_TOKEN[~bit_cnt];
if(bit_cnt == 7) begin
read_state <= RD_STATE_SEND_DATA; // next: send data
buffer_ptr <= 0;
case(pref_state)
PREF_STATE_IDLE:
if(((sd_buf - spi_buf) <= 1) && (read_state != RD_STATE_IDLE) && (cmd == 17 || cmd == 18)) begin
sd_rd <= 1;
pref_state <= PREF_STATE_RD;
end
end
// send data
RD_STATE_SEND_DATA: begin
miso <= (cmd == 'h49) ? CSD[{buffer_ptr[3:0],~bit_cnt}] : (cmd == 'h4A) ? CID[{buffer_ptr[3:0],~bit_cnt}] : buffer_dout[~bit_cnt];
if(bit_cnt == 7) begin
// sent 512 sector data bytes?
if((cmd == 'h51) & &buffer_ptr) read_state <= RD_STATE_IDLE;
else if((cmd == 'h52) & &buffer_ptr) begin
read_state <= RD_STATE_WAIT_M;
wait_m_cnt <= 0;
end
// sent 16 cid/csd data bytes?
else if(((cmd == 'h49) | (cmd == 'h4a)) & (&buffer_ptr[3:0])) read_state <= RD_STATE_IDLE;
// not done yet -> trigger read of next data byte
else buffer_ptr <= buffer_ptr + 1'd1;
PREF_STATE_RD:
if(read_state == RD_STATE_IDLE) begin
pref_state <= PREF_STATE_IDLE;
end
end
RD_STATE_WAIT_M: begin
if(bit_cnt == 7) begin
wait_m_cnt <= wait_m_cnt + 1'd1;
if(&wait_m_cnt) begin
spi_buf <= spi_buf + 1'd1;
read_state <= RD_STATE_WAIT_IO;
end
else if(ack[2] & ~ack[1]) begin
pref_state <= (cmd == 18) ? PREF_STATE_IDLE : PREF_STATE_FINISH;
end
PREF_STATE_FINISH:
if(read_state == RD_STATE_IDLE) begin
pref_state <= PREF_STATE_IDLE;
end
end
endcase
// ------------------ write support ----------------------
// send write data response
if(write_state == WR_STATE_SEND_DRESP) miso <= WRITE_DATA_RESPONSE[~bit_cnt];
// busy after write until the io controller sends ack
if(write_state == WR_STATE_BUSY) miso <= 0;
end
if(~old_sck & sck & ~ss) begin
if(synced) bit_cnt <= bit_cnt + 1'd1;
// assemble byte
if(bit_cnt != 7) begin
sbuf[6:0] <= { sbuf[5:0], mosi };
// resync while waiting for token
if(write_state==WR_STATE_EXP_DTOKEN) begin
if(cmd == 'h58) begin
if({sbuf,mosi} == 8'hfe) begin
write_state <= WR_STATE_RECV_DATA;
buffer_ptr <= 0;
bit_cnt <= 0;
end
end
else begin
if({sbuf,mosi} == 8'hfc) begin
write_state <= WR_STATE_RECV_DATA;
buffer_ptr <= 0;
bit_cnt <= 0;
end
if({sbuf,mosi} == 8'hfd) begin
write_state <= WR_STATE_IDLE;
rx_finish <= 1;
bit_cnt <= 0;
end
end
end
old_sck <= sck;
if(ss) begin
bit_cnt <= 0;
byte_cnt <= '1;
miso <= '1;
end
else begin
// finished reading one byte
// byte counter runs against 15 byte boundary
if(byte_cnt != 15) byte_cnt <= byte_cnt + 1'd1;
else if(old_sck & ~sck) begin
miso <= '1; // default: send 1's (busy/wait)
if(byte_cnt >= NCR) {miso,reply} <= {reply, {SZ{1'b1}}};
// byte_cnt > 6 -> complete command received
// first byte of valid command is 01xxxxxx
// don't accept new commands once a write or read command has been accepted
if((byte_cnt > 5) & (write_state == WR_STATE_IDLE) & (read_state == RD_STATE_IDLE) && !rx_finish) begin
byte_cnt <= 0;
cmd <= { sbuf, mosi};
// ---------- read state machine processing -------------
case(read_state)
RD_STATE_IDLE: ;
// set cmd55 flag if previous command was 55
cmd55 <= (cmd == 'h77);
end
RD_STATE_START: begin
if(byte_cnt == NCR && last_bit) read_state <= (cmd == 9 || cmd == 10) ? RD_STATE_SEND_TOKEN : RD_STATE_WAIT_IO;
end
if((byte_cnt > 5) & (read_state != RD_STATE_IDLE) && ({sbuf, mosi} == 8'h4c)) begin
byte_cnt <= 0;
rx_finish <= 0;
cmd <= {sbuf, mosi};
read_state <= RD_STATE_IDLE;
end
// waiting for io controller to return data
RD_STATE_WAIT_IO: begin
if(sd_buf != spi_buf && last_bit) read_state <= RD_STATE_SEND_TOKEN;
end
// parse additional command bytes
if(byte_cnt == 0) new_lba[31:24] <= { sbuf, mosi};
if(byte_cnt == 1) new_lba[23:16] <= { sbuf, mosi};
if(byte_cnt == 2) new_lba[15:8] <= { sbuf, mosi};
if(byte_cnt == 3) new_lba[7:0] <= { sbuf, mosi};
// last byte (crc) received, evaluate
if(byte_cnt == 4) begin
// default:
reply <= 4; // illegal command
reply_len <= 0; // no extra reply bytes
rx_finish <= 1;
case(cmd)
// CMD0: GO_IDLE_STATE
'h40: reply <= 1; // ok, busy
// CMD1: SEND_OP_COND
'h41: reply <= 0; // ok, not busy
// CMD8: SEND_IF_COND (V2 only)
'h48: begin
reply <= 1; // ok, busy
reply0 <= 'h00;
reply1 <= 'h00;
reply2 <= 'h01;
reply3 <= 'hAA;
reply_len <= 4;
end
// CMD9: SEND_CSD
'h49: reply <= 0; // ok
// CMD10: SEND_CID
'h4a: reply <= 0; // ok
// CMD12: STOP_TRANSMISSION
'h4c: reply <= 0; // ok
// CMD13: SEND_STATUS
'h4d: begin
reply <= 'h00; // ok
reply0 <='h00;
reply_len <= 1;
end
// CMD16: SET_BLOCKLEN
'h50: begin
// we only support a block size of 512
if(new_lba == 512) reply <= 0; // ok
else reply <= 'h40; // parmeter error
end
// CMD17: READ_SINGLE_BLOCK
'h51: reply <= 0; // ok
// CMD18: READ_MULTIPLE
'h52: reply <= 0; // ok
// ACMD23: SET_WR_BLK_ERASE_COUNT
'h57: reply <= 0; //ok
// CMD24: WRITE_BLOCK
'h58,
// CMD25: WRITE_MULTIPLE
'h59: begin
reply <= 0; // ok
write_state <= WR_STATE_EXP_DTOKEN; // expect data token
rx_finish <=0;
sd_lba <= csd_sdhc ? new_lba : {9'd0, new_lba[31:9]};
spi_buf <= 0;
sd_buf <= 0;
end
// ACMD41: APP_SEND_OP_COND
'h69: if(cmd55) reply <= 0; // ok, not busy
// CMD55: APP_COND
'h77: reply <= 1; // ok, busy
// CMD58: READ_OCR
'h7a: begin
reply <= 0; // ok
reply0 <= OCR[31:24]; // bit 30 = 1 -> high capacity card
reply1 <= OCR[23:16];
reply2 <= OCR[15:8];
reply3 <= OCR[7:0];
reply_len <= 4;
end
// CMD59: CRC_ON_OFF
'h7b: reply <= 0; // ok
endcase
end
// ---------- handle write -----------
case(write_state)
// do nothing in idle state
WR_STATE_IDLE: ;
// waiting for data token
WR_STATE_EXP_DTOKEN: begin
buffer_ptr <= 0;
if(cmd == 'h58) begin
if({sbuf,mosi} == 8'hfe) write_state <= WR_STATE_RECV_DATA;
end
else begin
if({sbuf,mosi} == 8'hfc) write_state <= WR_STATE_RECV_DATA;
if({sbuf,mosi} == 8'hfd) begin
write_state <= WR_STATE_IDLE;
rx_finish <= 1;
end
// send data token
RD_STATE_SEND_TOKEN: begin
miso <= DATA_TOKEN[~bit_cnt -:SZ];
if(last_bit) begin
read_state <= RD_STATE_SEND_DATA; // next: send data
buffer_ptr <= 0;
end
end
// transfer 512 bytes
WR_STATE_RECV_DATA: begin
// push one byte into local buffer
buffer_wr <= 1;
buffer_din <= {sbuf, mosi};
// send data
RD_STATE_SEND_DATA: begin
// all bytes written?
if(&buffer_ptr) write_state <= WR_STATE_RECV_CRC0;
miso <= (cmd == 9) ? CSD[{buffer_ptr[3:0],~bit_cnt} -:SZ] : (cmd == 10) ? CID[{buffer_ptr[3:0],~bit_cnt} -:SZ] : buffer_dout[~bit_cnt -:SZ];
if(last_bit) begin
// sent 512 sector data bytes?
if(cmd == 17 && &buffer_ptr) read_state <= RD_STATE_IDLE;
else if(cmd == 18 && &buffer_ptr) begin
read_state <= RD_STATE_WAIT_M;
wait_m_cnt <= 0;
end
// sent 16 cid/csd data bytes?
else if((cmd == 9 || cmd == 10) && &buffer_ptr[3:0]) read_state <= RD_STATE_IDLE;
// not done yet -> trigger read of next data byte
else buffer_ptr <= buffer_ptr + 1'd1;
end
end
// transfer 1st crc byte
WR_STATE_RECV_CRC0:
write_state <= WR_STATE_RECV_CRC1;
RD_STATE_WAIT_M: begin
if(last_bit) begin
wait_m_cnt <= wait_m_cnt + 1'd1;
if(&wait_m_cnt) begin
spi_buf <= spi_buf + 1'd1;
read_state <= RD_STATE_WAIT_IO;
end
end
end
endcase
// transfer 2nd crc byte
WR_STATE_RECV_CRC1:
write_state <= WR_STATE_SEND_DRESP;
// ------------------ write support ----------------------
// send write data response
if(write_state == WR_STATE_SEND_DRESP) miso <= WRITE_DATA_RESPONSE[~bit_cnt -:SZ];
// send data response
WR_STATE_SEND_DRESP: begin
write_state <= WR_STATE_BUSY;
spi_buf <= spi_buf + 1'd1;
sd_wr <= 1;
// busy after write until the io controller sends ack
if(write_state == WR_STATE_BUSY) miso <= 0;
end
else if(~old_sck & sck) begin
sbuf[6:0] <= {sbuf[5:0],mosi[0]};
bit_cnt <= bit_cnt + SZ[2:0];
if(last_bit) begin
// finished reading one byte
// byte counter runs against 15 byte boundary
if(~&byte_cnt) byte_cnt <= byte_cnt + 1'd1;
// byte_cnt > 6 -> complete command received
// first byte of valid command is 01xxxxxx
// don't accept new commands once a write or read command has been accepted
if(byte_cnt > 5 &&
((write_state == WR_STATE_IDLE && read_state == RD_STATE_IDLE && ibuf[7:6] == 1) ||
(read_state != RD_STATE_IDLE && ibuf == 8'h4c))) begin
byte_cnt <= 0;
cmd <= ibuf[5:0];
cmd55 <= (cmd == 55); // set cmd55 flag if previous command was 55
if(ibuf[5:0] == 12) read_state <= RD_STATE_IDLE;
end
// wait for io controller to accept data
WR_STATE_BUSY:
if(spi_buf == sd_buf) begin
if(cmd == 'h59) begin
write_state <= WR_STATE_EXP_DTOKEN;
sd_lba <= sd_lba + 1;
// parse additional command bytes
if(byte_cnt == 0) arg[31:24] <= ibuf;
if(byte_cnt == 1) arg[23:16] <= ibuf;
if(byte_cnt == 2) arg[15:8] <= ibuf;
if(byte_cnt == 3) arg[7:0] <= ibuf;
// last byte (crc) received, evaluate
if(byte_cnt == 4) begin
// default:
reply <= 40'h04FFFFFFFF; // illegal command
case(cmd)
// CMD0: GO_IDLE_STATE
0: reply[39:32] <= 1; // ok, busy
// CMD1: SEND_OP_COND
1: reply[39:32] <= 0;
// CMD8: SEND_IF_COND (V2 only)
8: reply <= 40'h01000001AA; // ok, busy
// CMD9: SEND_CSD
9,
// CMD10: SEND_CID
10: begin
reply[39:32] <= 0;
read_state <= RD_STATE_START;
end
// CMD12: STOP_TRANSMISSION
12: reply[39:32] <= 0;
// CMD13: SEND_STATUS
13: reply[39:24] <= 16'h0000;
// CMD16: SET_BLOCKLEN
16: reply[39:32] <= (arg == 512) ? 8'h00 : 8'h40; // we only support a block size of 512
// CMD17: READ_SINGLE_BLOCK
17,
// CMD18: READ_MULTIPLE
18: begin
reply[39:32] <= 0;
read_state <= RD_STATE_START;
spi_buf <= 0;
sd_buf <= 0;
sd_lba <= csd_sdhc ? arg : {9'd0, arg[31:9]};
end
// ACMD23: SET_WR_BLK_ERASE_COUNT
23: reply[39:32] <= 0;
// CMD24: WRITE_BLOCK
24,
// CMD25: WRITE_MULTIPLE
25: begin
reply[39:32] <= 0;
write_state <= WR_STATE_EXP_DTOKEN; // expect data token
spi_buf <= 0;
sd_buf <= 0;
sd_lba <= csd_sdhc ? arg : {9'd0, arg[31:9]};
end
// ACMD41: APP_SEND_OP_COND
41: if(cmd55) reply[39:32] <= 0; // ok, not busy
// CMD55: APP_COND
55: reply[39:32] <= 1; // ok, busy
// CMD58: READ_OCR
58: reply <= { 8'h00, 1'b1, csd_sdhc, 30'd0 }; // bit 30 = 1 -> high capacity card
// CMD59: CRC_ON_OFF
59: reply[39:32] <= 0;
endcase
end
// ---------- handle write -----------
case(write_state)
// do nothing in idle state
WR_STATE_IDLE: ;
// waiting for data token
WR_STATE_EXP_DTOKEN: begin
buffer_ptr <= 0;
if(cmd == 24) begin
if(ibuf == DATA_TOKEN) write_state <= WR_STATE_RECV_DATA;
end
else begin
write_state <= WR_STATE_IDLE;
rx_finish <= 1;
if(ibuf == DATA_TOKEN_CMD25) write_state <= WR_STATE_RECV_DATA;
if(ibuf == STOP_TRAN) write_state <= WR_STATE_IDLE;
end
end
endcase
end
// wait for first 0 bit until start counting bits
if(!synced && !mosi) begin
synced <= 1;
bit_cnt <= 1; // byte assembly prepare for next time loop
sbuf <= 7'b1111110; // byte assembly prepare for next time loop
rx_finish<= 0;
end else if (synced && tx_finish && rx_finish ) begin
synced <= 0;
bit_cnt <= 0;
rx_finish<= 0;
// transfer 512 bytes
WR_STATE_RECV_DATA: begin
// push one byte into local buffer
buffer_wr <= 1;
buffer_din <= ibuf;
// all bytes written?
if(&buffer_ptr) write_state <= WR_STATE_RECV_CRC0;
end
// transfer 1st crc byte
WR_STATE_RECV_CRC0:
write_state <= WR_STATE_RECV_CRC1;
// transfer 2nd crc byte
WR_STATE_RECV_CRC1:
write_state <= WR_STATE_SEND_DRESP;
// send data response
WR_STATE_SEND_DRESP:
write_state <= WR_STATE_BUSY;
// wait for io controller to accept data
WR_STATE_BUSY:
if(spi_buf == sd_buf) write_state <= (cmd == 25) ? WR_STATE_EXP_DTOKEN : WR_STATE_IDLE;
endcase
end
end
end
end

View File

@@ -11,6 +11,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) v
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 SYSTEMVERILOG_FILE [file join $::quartus(qip_path) video_freezer.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 ]
@@ -29,4 +30,4 @@ set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) f
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) ddr_svc.sv ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) sysmem.sv ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) sd_card.sv ]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) hps_io.v ]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) hps_io.sv ]

View File

@@ -292,7 +292,8 @@ reg cfg_custom_t = 0;
reg [5:0] cfg_custom_p1;
reg [31:0] cfg_custom_p2;
reg [4:0] vol_att = 0;
reg [4:0] vol_att;
initial vol_att = 5'b11111;
reg [6:0] coef_addr;
reg [8:0] coef_data;
@@ -618,6 +619,7 @@ wire vbuf_write;
wire [23:0] hdmi_data;
wire hdmi_vs, hdmi_hs, hdmi_de, hdmi_vbl;
wire freeze;
`ifndef MISTER_DEBUG_NOHDMI
wire clk_hdmi = hdmi_clk_out;
@@ -639,7 +641,7 @@ ascal
(
.reset_na (~reset_req),
.run (1),
.freeze (0),
.freeze (freeze),
.i_clk (clk_ihdmi),
.i_ce (ce_hpix),
@@ -1522,6 +1524,7 @@ emu emu
.HDMI_WIDTH(direct_video ? 12'd0 : hdmi_width),
.HDMI_HEIGHT(direct_video ? 12'd0 : hdmi_height),
.HDMI_FREEZE(freeze),
.CLK_VIDEO(clk_vid),
.CE_PIXEL(ce_pix),

View File

@@ -25,27 +25,44 @@ wire [5:0] blue = din[7:2];
// 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)
reg [18:0] y_2, pb_2, pr_2;
reg [7:0] y, pb, pr;
reg [23:0] din2, din3;
reg hsync2, vsync2, csync2;
reg [23:0] rgb;
always @(posedge clk) begin
y_2 <= 19'd04096 + ({red, 8'd0} + {red, 3'd0}) + ({green, 9'd0} + {green, 2'd0}) + ({blue, 6'd0} + {blue, 5'd0} + {blue, 2'd0});
pb_2 <= 19'd32768 - ({red, 7'd0} + {red, 4'd0} + {red, 3'd0}) - ({green, 8'd0} + {green, 5'd0} + {green, 3'd0}) + ({blue, 8'd0} + {blue, 7'd0} + {blue, 6'd0});
pr_2 <= 19'd32768 + ({red, 8'd0} + {red, 7'd0} + {red, 6'd0}) - ({green, 8'd0} + {green, 6'd0} + {green, 5'd0} + {green, 4'd0} + {green, 3'd0}) - ({blue, 6'd0} + {blue , 3'd0});
reg [18:0] y_1r, pb_1r, pr_1r;
reg [18:0] y_1g, pb_1g, pr_1g;
reg [18:0] y_1b, pb_1b, pr_1b;
reg [18:0] y_2, pb_2, pr_2;
reg [23:0] din1, din2;
reg hsync2, vsync2, csync2;
reg hsync1, vsync1, csync1;
y_1r <= 19'd04096 + ({red, 8'd0} + {red, 3'd0});
pb_1r <= 19'd32768 - ({red, 7'd0} + {red, 4'd0} + {red, 3'd0});
pr_1r <= 19'd32768 + ({red, 8'd0} + {red, 7'd0} + {red, 6'd0});
y_1g <= {green, 9'd0} + {green, 2'd0};
pb_1g <= {green, 8'd0} + {green, 5'd0} + {green, 3'd0};
pr_1g <= {green, 8'd0} + {green, 6'd0} + {green, 5'd0} + {green, 4'd0} + {green, 3'd0};
y_1b <= {blue, 6'd0} + {blue, 5'd0} + {blue, 2'd0};
pb_1b <= {blue, 8'd0} + {blue, 7'd0} + {blue, 6'd0};
pr_1b <= {blue, 6'd0} + {blue, 3'd0};
y_2 <= y_1r + y_1g + y_1b;
pb_2 <= pb_1r - pb_1g + pb_1b;
pr_2 <= pr_1r - pr_1g - pr_1b;
y <= ( y_2[18] || !y_2[17:12]) ? 8'd16 : (y_2[17:8] > 235) ? 8'd235 : y_2[15:8];
pb <= (pb_2[18] || !pb_2[17:12]) ? 8'd16 : (&pb_2[17:12]) ? 8'd240 : pb_2[15:8];
pr <= (pr_2[18] || !pr_2[17:12]) ? 8'd16 : (&pr_2[17:12]) ? 8'd240 : pr_2[15:8];
hsync_o <= hsync2; hsync2 <= hsync;
vsync_o <= vsync2; vsync2 <= vsync;
csync_o <= csync2; csync2 <= csync;
hsync_o <= hsync2; hsync2 <= hsync1; hsync1 <= hsync;
vsync_o <= vsync2; vsync2 <= vsync1; vsync1 <= vsync;
csync_o <= csync2; csync2 <= csync1; csync1 <= csync;
din2 <= din;
din3 <= din2;
rgb <= din2; din2 <= din1; din1 <= din;
end
assign dout = ypbpr_en ? {pr, y, pb} : din3;
assign dout = ypbpr_en ? {pr, y, pb} : rgb;
endmodule

143
sys/video_freezer.sv Normal file
View File

@@ -0,0 +1,143 @@
//
// video freeze with sync
// (C) Alexey Melnikov
//
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
module video_freezer
(
input clk,
output sync,
input freeze,
input hs_in,
input vs_in,
input hbl_in,
input vbl_in,
output hs_out,
output vs_out,
output hbl_out,
output vbl_out
);
sync_lock #(33) vs_lock
(
.clk(clk),
.sync_in(vs_in),
.sync_out(vs_out),
.de_in(vbl_in),
.de_out(vbl_out),
.freeze(freeze)
);
wire sync_pt;
sync_lock #(21) hs_lock
(
.clk(clk),
.sync_in(hs_in),
.sync_out(hs_out),
.de_in(hbl_in),
.de_out(hbl_out),
.freeze(freeze),
.sync_pt(sync_pt)
);
reg sync_o;
always @(posedge clk) begin
reg old_hs, old_vs;
reg vs_sync;
old_vs <= vs_out;
if(~old_vs & vs_out) vs_sync <= 1;
if(sync_pt & vs_sync) begin
vs_sync <= 0;
sync_o <= ~sync_o;
end
end
assign sync = sync_o;
endmodule
module sync_lock #(parameter WIDTH)
(
input clk,
input sync_in,
input de_in,
output sync_out,
output de_out,
input freeze,
output sync_pt,
output valid
);
reg [WIDTH-1:0] f_len, s_len, de_start, de_end;
reg sync_valid;
reg old_sync;
always @(posedge clk) old_sync <= sync_in;
always @(posedge clk) begin
reg [WIDTH-1:0] cnti;
reg f_valid;
reg old_de;
cnti <= cnti + 1'd1;
if(~old_sync & sync_in) begin
if(sync_valid) f_len <= cnti;
f_valid <= 1;
sync_valid <= f_valid;
cnti <= 0;
end
if(old_sync & ~sync_in & sync_valid) s_len <= cnti;
old_de <= de_in;
if(~old_de & de_in & sync_valid) de_start <= cnti;
if(old_de & ~de_in & sync_valid) de_end <= cnti;
if(freeze) {f_valid, sync_valid} <= 0;
end
reg sync_o, de_o, sync_o_pre;
always @(posedge clk) begin
reg [WIDTH-1:0] cnto;
cnto <= cnto + 1'd1;
if(old_sync & ~sync_in & sync_valid) cnto <= s_len + 2'd2;
if(cnto == f_len) cnto <= 0;
sync_o_pre <= (cnto == (s_len>>1)); // middle in sync
if(cnto == f_len) sync_o <= 1;
if(cnto == s_len) sync_o <= 0;
if(cnto == de_start) de_o <= 1;
if(cnto == de_end) de_o <= 0;
end
assign sync_out = freeze ? sync_o : sync_in;
assign valid = sync_valid;
assign sync_pt = sync_o_pre;
assign de_out = freeze ? de_o : de_in;
endmodule

View File

@@ -10,10 +10,7 @@
`timescale 1ns / 1ps
//
// LINE_LENGTH: Length of display line in pixels
// Usually it's length from HSync to HSync.
// May be less if line_start is used.
//
// LINE_LENGTH: Length of display line in pixels when HBlank = 0;
// HALF_DEPTH: If =1 then color dept is 4 bits per component
//
// altera message_off 10720
@@ -47,6 +44,12 @@ module video_mixer
input HBlank,
input VBlank,
// Freeze engine
// HDMI: displays last frame
// VGA: black screen with HSync and VSync
input HDMI_FREEZE,
output freeze_sync,
// video output signals
output reg [7:0] VGA_R,
output reg [7:0] VGA_G,
@@ -60,19 +63,43 @@ localparam DWIDTH = HALF_DEPTH ? 3 : 7;
localparam DWIDTH_SD = GAMMA ? 7 : DWIDTH;
localparam HALF_DEPTH_SD = GAMMA ? 0 : HALF_DEPTH;
wire frz_hs, frz_vs;
wire frz_hbl, frz_vbl;
video_freezer freezer
(
.clk(CLK_VIDEO),
.freeze(HDMI_FREEZE),
.hs_in(HSync),
.vs_in(VSync),
.hbl_in(HBlank),
.vbl_in(VBlank),
.sync(freeze_sync),
.hs_out(frz_hs),
.vs_out(frz_vs),
.hbl_out(frz_hbl),
.vbl_out(frz_vbl)
);
reg frz;
always @(posedge CLK_VIDEO) begin
reg frz1;
frz1 <= HDMI_FREEZE;
frz <= frz1;
end
generate
if(GAMMA && HALF_DEPTH) begin
wire [7:0] R_in = {R,R};
wire [7:0] G_in = {G,G};
wire [7:0] B_in = {B,B};
wire [7:0] R_in = frz ? 8'd0 : {R,R};
wire [7:0] G_in = frz ? 8'd0 : {G,G};
wire [7:0] B_in = frz ? 8'd0 : {B,B};
end else begin
wire [DWIDTH:0] R_in = R;
wire [DWIDTH:0] G_in = G;
wire [DWIDTH:0] B_in = B;
wire [DWIDTH:0] R_in = frz ? 1'd0 : R;
wire [DWIDTH:0] G_in = frz ? 1'd0 : G;
wire [DWIDTH:0] B_in = frz ? 1'd0 : B;
end
endgenerate
wire hs_g, vs_g;
wire hb_g, vb_g;
wire [DWIDTH_SD:0] R_gamma, G_gamma, B_gamma;
@@ -90,10 +117,10 @@ generate
.gamma_wr_addr(gamma_bus[17:8]),
.gamma_value(gamma_bus[7:0]),
.HSync(HSync),
.VSync(VSync),
.HBlank(HBlank),
.VBlank(VBlank),
.HSync(frz_hs),
.VSync(frz_vs),
.HBlank(frz_hbl),
.VBlank(frz_vbl),
.RGB_in({R_in,G_in,B_in}),
.HSync_out(hs_g),
@@ -105,7 +132,7 @@ generate
end else begin
assign gamma_bus[21] = 0;
assign {R_gamma,G_gamma,B_gamma} = {R_in,G_in,B_in};
assign {hs_g, vs_g, hb_g, vb_g} = {HSync, VSync, HBlank, VBlank};
assign {hs_g, vs_g, hb_g, vb_g} = {frz_hs, frz_vs, frz_hbl, frz_vbl};
end
endgenerate