mirror of
https://github.com/MiSTer-devel/Arcade-RallyX_MiSTer.git
synced 2026-04-26 03:02:43 +00:00
Merge pull request #10 from JimmyStones/hiscore-autosave
Add hiscore autosave, update sys
This commit is contained in:
479
Arcade-RallyX.sv
479
Arcade-RallyX.sv
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
BIN
releases/Arcade-RallyX_20210922.rbf
Normal file
BIN
releases/Arcade-RallyX_20210922.rbf
Normal file
Binary file not shown.
471
rtl/hiscore.v
471
rtl/hiscore.v
@@ -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
|
||||
|
||||
15
rtl/pause.v
15
rtl/pause.v
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
623
sys/sd_card.sv
623
sys/sd_card.sv
@@ -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
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
143
sys/video_freezer.sv
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user