diff --git a/pkg/gb/Cores/budude2.GB/core.json b/pkg/gb/Cores/budude2.GB/core.json index 10174d1..b07b1db 100644 --- a/pkg/gb/Cores/budude2.GB/core.json +++ b/pkg/gb/Cores/budude2.GB/core.json @@ -7,13 +7,13 @@ "description": "Game Boy", "author": "budude2", "url": "https://github.com/budude2/openfpga-GBC", - "version": "1.2.3", - "date_release": "2024-08-13" + "version": "1.3.0", + "date_release": "2024-08-20" }, "framework": { "target_product": "Analogue Pocket", "version_required": "1.1", - "sleep_supported": false, + "sleep_supported": true, "dock": { "supported": true, "analog_output": false diff --git a/pkg/gb/Cores/budude2.GB/gb.rbf_r b/pkg/gb/Cores/budude2.GB/gb.rbf_r index 7126aff..f6371ae 100644 Binary files a/pkg/gb/Cores/budude2.GB/gb.rbf_r and b/pkg/gb/Cores/budude2.GB/gb.rbf_r differ diff --git a/pkg/gbc/Cores/budude2.GBC/core.json b/pkg/gbc/Cores/budude2.GBC/core.json index 1cf44ce..21bb977 100644 --- a/pkg/gbc/Cores/budude2.GBC/core.json +++ b/pkg/gbc/Cores/budude2.GBC/core.json @@ -7,13 +7,13 @@ "description": "Game Boy Color", "author": "budude2", "url": "https://github.com/budude2/openfpga-GBC", - "version": "1.2.3", - "date_release": "2024-08-13" + "version": "1.3.0", + "date_release": "2024-08-20" }, "framework": { "target_product": "Analogue Pocket", "version_required": "1.1", - "sleep_supported": false, + "sleep_supported": true, "dock": { "supported": true, "analog_output": false diff --git a/pkg/gbc/Cores/budude2.GBC/gbc.rbf_r b/pkg/gbc/Cores/budude2.GBC/gbc.rbf_r index b9f070d..4ae9ba4 100644 Binary files a/pkg/gbc/Cores/budude2.GBC/gbc.rbf_r and b/pkg/gbc/Cores/budude2.GBC/gbc.rbf_r differ diff --git a/src/ap_core.qsf b/src/ap_core.qsf index 94fe1c0..3072c4b 100644 --- a/src/ap_core.qsf +++ b/src/ap_core.qsf @@ -312,7 +312,7 @@ set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC ON set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_DUPLICATION ON set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_RETIMING ON set_global_assignment -name PHYSICAL_SYNTHESIS_ASYNCHRONOUS_SIGNAL_PIPELINING ON -set_global_assignment -name FITTER_EFFORT "AUTO FIT" +set_global_assignment -name FITTER_EFFORT "FAST FIT" set_global_assignment -name ROUTER_LCELL_INSERTION_AND_LOGIC_DUPLICATION ON # Assembler Assignments @@ -324,7 +324,6 @@ set_global_assignment -name GENERATE_RBF_FILE ON # Signal Tap Assignments # ====================== set_global_assignment -name ENABLE_SIGNALTAP OFF -set_global_assignment -name USE_SIGNALTAP_FILE stp1.stp # Power Estimation Assignments # ============================ @@ -741,6 +740,9 @@ set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to bridge_1wire set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to bridge_spiclk set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to bridge_spimiso set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to bridge_spimosi +set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL +set_global_assignment -name SYSTEMVERILOG_FILE gb/sram_128k_x1_x16.sv +set_global_assignment -name SYSTEMVERILOG_FILE gb/save_state_controller.sv set_global_assignment -name SYSTEMVERILOG_FILE gb/rtc_loader.sv set_global_assignment -name SYSTEMVERILOG_FILE gb/rumbler.sv set_global_assignment -name SYSTEMVERILOG_FILE gb/save_handler.sv @@ -809,11 +811,10 @@ set_global_assignment -name QIP_FILE apf/apf.qip set_global_assignment -name QIP_FILE apf/mf_ddio_bidir_12.qip set_global_assignment -name VERILOG_FILE core/core_bridge_cmd.v set_global_assignment -name SDC_FILE core/core_constraints.sdc -set_global_assignment -name SIGNALTAP_FILE core/stp1.stp set_global_assignment -name QIP_FILE core/mf_pllbase.qip set_global_assignment -name SIP_FILE core/mf_pllbase.sip set_global_assignment -name SOURCE_FILE db/ap_core.cmp.rdb -set_global_assignment -name SIGNALTAP_FILE stp1.stp set_global_assignment -name QIP_FILE gb/rtc_ram/rtc_ram.qip -set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL -set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file +set_global_assignment -name ALM_REGISTER_PACKING_EFFORT MEDIUM +set_global_assignment -name FINAL_PLACEMENT_OPTIMIZATION NEVER +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top diff --git a/src/apf/build_id.mif b/src/apf/build_id.mif index 411cde4..0adbdaa 100644 --- a/src/apf/build_id.mif +++ b/src/apf/build_id.mif @@ -9,8 +9,8 @@ DATA_RADIX = HEX; CONTENT BEGIN - 0E0 : 20240813; - 0E1 : 00212410; - 0E2 : ea0c04f8; + 0E0 : 20240725; + 0E1 : 00214947; + 0E2 : 55079a6c; END; diff --git a/src/core/core_top.sv b/src/core/core_top.sv index a7d67b5..9256ff1 100644 --- a/src/core/core_top.sv +++ b/src/core/core_top.sv @@ -320,10 +320,11 @@ wire [31:0] rtc_date_bcd; wire [31:0] rtc_time_bcd; wire rtc_valid; -wire savestate_supported; -wire [31:0] savestate_addr; -wire [31:0] savestate_size; -wire [31:0] savestate_maxloadsize; +wire savestate_supported = 1; +wire [31:0] savestate_addr = 32'h40000000; +//wire [31:0] savestate_size = 32'h2C330; // 32768 + 16384 + 160 + 128 + 131072 + 528 +wire [31:0] savestate_size = 49968 + cart_ram_size_bytes; +wire [31:0] savestate_maxloadsize = savestate_size; wire savestate_start; wire savestate_start_ack; @@ -480,6 +481,7 @@ end always_comb begin casex(bridge_addr) 32'h2xxxxxxx: begin bridge_rd_data = save_rd_data; end + 32'h4xxxxxxx: begin bridge_rd_data = save_state_bridge_read_data; end 32'hF8xxxxxx: begin bridge_rd_data = cmd_bridge_rd_data; end 32'hF1000000: begin bridge_rd_data = int_bridge_read_data; end 32'hF2000000: begin bridge_rd_data = int_bridge_read_data; end @@ -573,7 +575,7 @@ data_loader #( .write_data ( ioctl_dout ) ); -logic bk_wr, bk_rtc_wr, loading_done; +logic bk_wr, bk_rd, bk_rtc_wr, loading_done; logic [16:0] bk_addr; logic [15:0] bk_data, bk_q; logic [31:0] save_rd_data, loaded_save_size; @@ -615,6 +617,74 @@ save_handler save_handler .loading_done ( loading_done ) ); + logic ss_save, ss_load; + logic [63:0] SaveStateExt_Din, SaveStateExt_Dout; + logic [9:0] SaveStateExt_Adr; + logic SaveStateExt_wren, SaveStateExt_rst, SaveStateExt_load; + + logic [19:0] Savestate_CRAMAddr; + logic Savestate_CRAMRWrEn; + logic [7:0] Savestate_CRAMWriteData, Savestate_CRAMReadData; + + logic [63:0] ss_din, ss_dout; + logic [25:0] ss_addr; + logic ss_rnw, ss_req, ss_ack; + logic [7:0] ss_be; + + logic [31:0] save_state_bridge_read_data; + + save_state_controller save_state_controller ( + .clk_74a ( clk_74a ), + .clk_sys ( clk_sys ), + + // APF + .bridge_wr ( bridge_wr ), + .bridge_rd ( bridge_rd ), + .bridge_endian_little ( bridge_endian_little ), + .bridge_addr ( bridge_addr ), + .bridge_wr_data ( bridge_wr_data ), + .save_state_bridge_read_data ( save_state_bridge_read_data ), + + // APF Save States + .savestate_load ( savestate_load ), + .savestate_load_ack_s ( savestate_load_ack ), + .savestate_load_busy_s ( savestate_load_busy ), + .savestate_load_ok_s ( savestate_load_ok ), + .savestate_load_err_s ( savestate_load_err ), + + .savestate_start ( savestate_start ), + .savestate_start_ack_s ( savestate_start_ack ), + .savestate_start_busy_s ( savestate_start_busy ), + .savestate_start_ok_s ( savestate_start_ok ), + .savestate_start_err_s ( savestate_start_err ), + + // Save States Manager + .ss_save ( ss_save ), + .ss_load ( ss_load ), + + .ss_din ( ss_din ), + .ss_dout ( ss_dout ), + .ss_addr ( ss_addr ), + .ss_rnw ( ss_rnw ), + .ss_req ( ss_req ), + .ss_be ( ss_be ), + .ss_ack ( ss_ack ), + + .ss_busy ( sleep_savestate ) + ); + + wire [31:0] cart_ram_size_bytes; + + always_comb begin + case (cart_ram_size) + 0: begin cart_ram_size_bytes = 512; end // for MBC2 + 1: begin cart_ram_size_bytes = 2048; end // 2 KByte + 2: begin cart_ram_size_bytes = 8192; end // 8 KByte + 3: begin cart_ram_size_bytes = 32768; end // 32 KByte + default: begin cart_ram_size_bytes = 131072; end // 128 KByte + endcase + end + //////// Start GB/GBC Stuff //////// reg ioctl_download = 0; @@ -800,18 +870,18 @@ cart_top cart .RTC_savedtimeOut ( RTC_savedtimeOut ), .RTC_inuse ( RTC_inuse ), - .SaveStateExt_Din ( 0 ), - .SaveStateExt_Adr ( 0 ), - .SaveStateExt_wren ( 0 ), - .SaveStateExt_rst ( 0 ), - .SaveStateExt_Dout ( ), - .savestate_load ( 0 ), + .SaveStateExt_Din ( SaveStateExt_Din ), + .SaveStateExt_Adr ( SaveStateExt_Adr ), + .SaveStateExt_wren ( SaveStateExt_wren ), + .SaveStateExt_rst ( SaveStateExt_rst ), + .SaveStateExt_Dout ( SaveStateExt_Dout ), + .savestate_load ( SaveStateExt_load ), .sleep_savestate ( sleep_savestate ), - .Savestate_CRAMAddr ( 0 ), - .Savestate_CRAMRWrEn ( 0 ), - .Savestate_CRAMWriteData ( 0 ), - .Savestate_CRAMReadData ( ), + .Savestate_CRAMAddr ( Savestate_CRAMAddr), + .Savestate_CRAMRWrEn ( Savestate_CRAMRWrEn), + .Savestate_CRAMWriteData ( Savestate_CRAMWriteData), + .Savestate_CRAMReadData ( Savestate_CRAMReadData), .rumbling ( rumbling ) ); @@ -857,100 +927,100 @@ end // the gameboy itself gb gb ( - .reset ( reset | ~loading_done ), + .reset ( reset | ~loading_done ), - .clk_sys ( clk_sys ), - .ce ( ce_cpu ), // the whole gameboy runs on 4mhnz - .ce_2x ( ce_cpu2x ), // ~8MHz in dualspeed mode (GBC) + .clk_sys ( clk_sys ), + .ce ( ce_cpu ), // the whole gameboy runs on 4mhnz + .ce_2x ( ce_cpu2x ), // ~8MHz in dualspeed mode (GBC) - .isGBC ( isGBC ), - .real_cgb_boot ( 1 ), - .isSGB ( sgb_en & ~isGBC ), - .megaduck ( 0 ), + .isGBC ( isGBC ), + .real_cgb_boot ( 1 ), + .isSGB ( sgb_en & ~isGBC ), + .megaduck ( 0 ), - .joy_p54 ( joy_p54 ), - .joy_din ( joy_do_sgb ), + .joy_p54 ( joy_p54 ), + .joy_din ( joy_do_sgb ), // interface to the "external" game cartridge - .ext_bus_addr ( cart_addr ), - .ext_bus_a15 ( cart_a15 ), - .cart_rd ( cart_rd ), - .cart_wr ( cart_wr ), - .cart_do ( cart_do ), - .cart_di ( cart_di ), - .cart_oe ( cart_oe ), - - .nCS ( nCS ), - - .boot_gba_en ( gba_en ), - .fast_boot_en ( 0 ), - - .cgb_boot_download ( cgb_boot_download ), - .dmg_boot_download ( dmg_boot_download ), - .sgb_boot_download ( sgb_boot_download ), - .ioctl_wr ( ioctl_wr ), - .ioctl_addr ( ioctl_addr ), - .ioctl_dout ( ioctl_dout ), + .ext_bus_addr ( cart_addr ), + .ext_bus_a15 ( cart_a15 ), + .cart_rd ( cart_rd ), + .cart_wr ( cart_wr ), + .cart_do ( cart_do ), + .cart_di ( cart_di ), + .cart_oe ( cart_oe ), + + .nCS ( nCS ), + + .boot_gba_en ( gba_en ), + .fast_boot_en ( 0 ), + + .cgb_boot_download ( cgb_boot_download ), + .dmg_boot_download ( dmg_boot_download ), + .sgb_boot_download ( sgb_boot_download ), + .ioctl_wr ( ioctl_wr ), + .ioctl_addr ( ioctl_addr ), + .ioctl_dout ( ioctl_dout ), // audio - .audio_l ( GB_AUDIO_L ), - .audio_r ( GB_AUDIO_R ), + .audio_l ( GB_AUDIO_L ), + .audio_r ( GB_AUDIO_R ), // interface to the lcd - .lcd_clkena ( lcd_clkena ), - .lcd_data ( lcd_data ), - .lcd_data_gb ( lcd_data_gb ), - .lcd_mode ( lcd_mode ), - .lcd_on ( lcd_on ), - .lcd_vsync ( lcd_vsync ), + .lcd_clkena ( lcd_clkena ), + .lcd_data ( lcd_data ), + .lcd_data_gb ( lcd_data_gb ), + .lcd_mode ( lcd_mode ), + .lcd_on ( lcd_on ), + .lcd_vsync ( lcd_vsync ), - .speed ( speed ), - .DMA_on ( DMA_on ), + .speed ( speed ), + .DMA_on ( DMA_on ), // serial port - .sc_int_clock2 ( sc_int_clock_out ), - .serial_clk_in ( ser_clk_in ), - .serial_data_in ( port_tran_si ), - .serial_clk_out ( ser_clk_out ), - .serial_data_out ( port_tran_so ), + .sc_int_clock2 ( sc_int_clock_out ), + .serial_clk_in ( ser_clk_in ), + .serial_data_in ( port_tran_si ), + .serial_clk_out ( ser_clk_out ), + .serial_data_out ( port_tran_so ), // Palette download will disable cheats option (HPS doesn't distinguish downloads), // so clear the cheats and disable second option (chheats enable/disable) - .gg_reset ( 0 ), - .gg_en ( 0 ), - .gg_code ( 0 ), - .gg_available ( ), + .gg_reset ( 0 ), + .gg_en ( 0 ), + .gg_code ( 0 ), + .gg_available ( ), // savestates - .increaseSSHeaderCount ( 0 ), - .cart_ram_size ( 0 ), - .save_state ( 0 ), - .load_state ( 0 ), - .savestate_number ( 0 ), - .sleep_savestate ( sleep_savestate ), - - .SaveStateExt_Din ( ), - .SaveStateExt_Adr ( ), - .SaveStateExt_wren ( ), - .SaveStateExt_rst ( ), - .SaveStateExt_Dout ( 0 ), - .SaveStateExt_load ( ), + .increaseSSHeaderCount ( 0 ), + .cart_ram_size ( cart_ram_size ), + .save_state ( ss_save ), + .load_state ( ss_load ), + .savestate_number ( 0 ), + .sleep_savestate ( sleep_savestate ), + + .SaveStateExt_Din ( SaveStateExt_Din ), + .SaveStateExt_Adr ( SaveStateExt_Adr ), + .SaveStateExt_wren ( SaveStateExt_wren ), + .SaveStateExt_rst ( SaveStateExt_rst ), + .SaveStateExt_Dout ( SaveStateExt_Dout ), + .SaveStateExt_load ( SaveStateExt_load ), - .Savestate_CRAMAddr ( ), - .Savestate_CRAMRWrEn ( ), - .Savestate_CRAMWriteData( ), - .Savestate_CRAMReadData ( 0 ), + .Savestate_CRAMAddr ( Savestate_CRAMAddr ), + .Savestate_CRAMRWrEn ( Savestate_CRAMRWrEn ), + .Savestate_CRAMWriteData( Savestate_CRAMWriteData ), + .Savestate_CRAMReadData ( Savestate_CRAMReadData ), - .SAVE_out_Din ( ), // data read from savestate - .SAVE_out_Dout ( 0 ), // data written to savestate - .SAVE_out_Adr ( ), // all addresses are DWORD addresses! - .SAVE_out_rnw ( ), // read = 1, write = 0 - .SAVE_out_ena ( ), // one cycle high for each action - .SAVE_out_be ( ), - .SAVE_out_done ( 0 ), // should be one cycle high when write is done or read value is valid + .SAVE_out_Din ( ss_din ), // data read from savestate + .SAVE_out_Dout ( ss_dout ), // data written to savestate + .SAVE_out_Adr ( ss_addr ), // all addresses are DWORD addresses! + .SAVE_out_rnw ( ss_rnw ), // read = 1, write = 0 + .SAVE_out_ena ( ss_req ), // one cycle high for each action + .SAVE_out_be ( ss_be ), + .SAVE_out_done ( ss_ack ), // should be one cycle high when write is done or read value is valid - .rewind_on ( 0 ), - .rewind_active ( 0 ) + .rewind_on ( 0 ), + .rewind_active ( 0 ) ); // Sound diff --git a/src/gb/cart.v b/src/gb/cart.v index 4d61b47..f35ebdd 100644 --- a/src/gb/cart.v +++ b/src/gb/cart.v @@ -36,6 +36,7 @@ module cart_top ( output isSGB_game, input ioctl_download, + input ioctl_upload, input ioctl_wr, input [24:0] ioctl_addr, input [15:0] ioctl_dout, @@ -408,7 +409,7 @@ assign cram_rd = cart_rd & is_cram_addr; assign cram_wr = sleep_savestate ? Savestate_CRAMRWrEn : mbc_cram_wr || (cart_wr & is_cram_addr & mbc_ram_enable); wire [16:0] cram_addr = sleep_savestate ? Savestate_CRAMAddr[16:0] : mbc_cram_addr; -wire [7:0] cram_di = sleep_savestate ? Savestate_CRAMWriteData : mbc_cram_wr ? mbc_cram_wr_do : cart_di; +wire [7:0] cram_di = sleep_savestate ? Savestate_CRAMWriteData : mbc_cram_wr ? mbc_cram_wr_do : cart_di; // RAM size assign ram_mask_file = // 0 - no ram diff --git a/src/gb/gb_savestates.vhd b/src/gb/gb_savestates.vhd index de12931..6ef6f91 100644 --- a/src/gb/gb_savestates.vhd +++ b/src/gb/gb_savestates.vhd @@ -80,6 +80,9 @@ architecture arch of gb_savestates is SAVE_WAITSETTLE, SAVEINTERNALS_WAIT, SAVEINTERNALS_WRITE, + DELAY, + SAVEMEMORY_HDR, + --SAVEMEMORY_HDR_WAIT, SAVEMEMORY_NEXT, SAVEMEMORY_FIRST, SAVEMEMORY_READ, @@ -145,6 +148,7 @@ begin reset_out <= '1'; BUS_rst <= '1'; elsif (save = '1') then + --state <= SAVE_WAITVSYNC; state <= SAVE_WAITVSYNC; header_amount <= header_amount + 1; elsif (load = '1') then @@ -156,8 +160,10 @@ begin -- ################# -- SAVE -- ################# - + when SAVE_WAITVSYNC => + bus_out_ena <= '0'; + if (lcd_vsync = '1') then state <= SAVE_WAITSETTLE; settle <= 0; @@ -170,13 +176,26 @@ begin elsif (settle < SETTLECOUNT) then settle <= settle + 1; else - state <= SAVEINTERNALS_WAIT; + state <= SAVEMEMORY_HDR; bus_out_Adr <= std_logic_vector(to_unsigned(savestate_address + HEADERCOUNT, 26)); bus_out_rnw <= '0'; BUS_adr <= (others => '0'); count <= 1; saving_savestate <= '1'; - end if; + end if; + + when SAVEMEMORY_HDR => + bus_out_Din <= std_logic_vector(to_unsigned(STATESIZE, 32)) & std_logic_vector(header_amount); + bus_out_ena <= '1'; + state <= DELAY; + + -- I need to make this delay bigger + + when DELAY => + bus_out_ena <= '0'; + if (bus_out_done = '1') then + state <= SAVEINTERNALS_WAIT; + end if; when SAVEINTERNALS_WAIT => bus_out_Din <= BUS_Dout; @@ -192,9 +211,22 @@ begin BUS_adr <= std_logic_vector(unsigned(BUS_adr) + 1); else state <= SAVEMEMORY_NEXT; + --state <= SAVEMEMORY_HDR; count <= 8; end if; end if; + + --when SAVEMEMORY_HDR_WAIT => + -- bus_out_Adr <= std_logic_vector(to_unsigned(savestate_address, 26)); + -- bus_out_Din <= std_logic_vector(to_unsigned(STATESIZE, 32)) & std_logic_vector(header_amount); + -- bus_out_ena <= '0'; + -- if (count < INTERNALSCOUNT) then + -- state <= SAVEMEMORY_HDR_WAIT; + -- count <= count + 1; + -- else + -- state <= SAVEMEMORY_NEXT; + -- count <= 8; + -- end if; when SAVEMEMORY_NEXT => if (savetype_counter < SAVETYPESCOUNT) then @@ -205,9 +237,6 @@ begin Save_RAMAddr <= (others => '0'); else state <= SAVESIZEAMOUNT; - bus_out_Adr <= std_logic_vector(to_unsigned(savestate_address, 26)); - bus_out_Din <= std_logic_vector(to_unsigned(STATESIZE, 32)) & std_logic_vector(header_amount); - bus_out_ena <= '1'; if (increaseSSHeaderCount = '0') then bus_out_be <= x"F0"; end if; @@ -241,11 +270,11 @@ begin end if; when SAVESIZEAMOUNT => - if (bus_out_done = '1') then + --if (bus_out_done = '1') then state <= IDLE; saving_savestate <= '0'; sleep_savestate <= '0'; - end if; + --end if; -- ################# diff --git a/src/gb/save_state_controller.sv b/src/gb/save_state_controller.sv new file mode 100644 index 0000000..c4fba0b --- /dev/null +++ b/src/gb/save_state_controller.sv @@ -0,0 +1,371 @@ +module save_state_controller ( + input wire clk_74a, + input wire clk_sys, + + // APF + input wire bridge_wr, + input wire bridge_rd, + input wire bridge_endian_little, + input wire [31:0] bridge_addr, + input wire [31:0] bridge_wr_data, + output wire [31:0] save_state_bridge_read_data, + + // APF Save States + input wire savestate_load, + output wire savestate_load_ack_s, + output wire savestate_load_busy_s, + output wire savestate_load_ok_s, + output wire savestate_load_err_s, + + input wire savestate_start, + output wire savestate_start_ack_s, + output wire savestate_start_busy_s, + output wire savestate_start_ok_s, + output wire savestate_start_err_s, + + // Save States + output reg ss_save, + output reg ss_load, + + input wire [63:0] ss_din, + output wire [63:0] ss_dout, + input wire [25:0] ss_addr, + input wire ss_rnw, + input wire ss_req, + input wire [7:0] ss_be, + output reg ss_ack, + + input wire ss_busy +); + wire savestate_load_s; + wire savestate_start_s; + + // Syncing + synch_3 #( + .WIDTH(2) + ) savestate_in ( + {savestate_load, savestate_start}, + {savestate_load_s, savestate_start_s}, + clk_sys + ); + + reg savestate_load_ack; + reg savestate_load_busy; + reg savestate_load_ok; + reg savestate_load_err; + + reg savestate_start_ack; + reg savestate_start_busy; + reg savestate_start_ok; + reg savestate_start_err; + + synch_3 #( + .WIDTH(8) + ) savestate_out ( + { + savestate_load_ack, + savestate_load_busy, + savestate_load_ok, + savestate_load_err, + savestate_start_ack, + savestate_start_busy, + savestate_start_ok, + savestate_start_err + }, + { + savestate_load_ack_s, + savestate_load_busy_s, + savestate_load_ok_s, + savestate_load_err_s, + savestate_start_ack_s, + savestate_start_busy_s, + savestate_start_ok_s, + savestate_start_err_s + }, + clk_74a + ); + + wire save_state_loader_write; + wire [22:0] save_state_loader_addr; + wire [15:0] save_state_loader_data; + + wire save_state_unloader_read; + wire [22:0] save_state_unloader_addr; + + wire fifo_load_empty; + reg fifo_load_read_req = 0; + wire [63:0] fifo_load_dout; + reg fifo_load_clr = 0; + + // TODO: Add endianness + // assign ss_dout = {fifo_load_dout[47:32], fifo_load_dout[63:48], fifo_load_dout[15:0], fifo_load_dout[31:16]}; + assign ss_dout = { + fifo_load_dout[39:32], + fifo_load_dout[47:40], + fifo_load_dout[55:48], + fifo_load_dout[63:56], + fifo_load_dout[7:0], + fifo_load_dout[15:8], + fifo_load_dout[23:16], + fifo_load_dout[31:24] + }; + + dcfifo_mixed_widths fifo_load ( + .data(bridge_wr_data), + .rdclk(clk_sys), + .rdreq(fifo_load_read_req), + .wrclk(clk_74a), + .wrreq(bridge_wr && bridge_addr[31:28] == 4'h4), + .q(fifo_load_dout), + .rdempty(fifo_load_empty), + .aclr(fifo_load_clr) + // .eccstatus(), + // .rdfull(), + // .rdusedw(), + // .wrempty(), + // .wrfull(), + // .wrusedw() + ); + defparam fifo_load.intended_device_family = "Cyclone V", fifo_load.lpm_numwords = 4096, + fifo_load.lpm_showahead = "OFF", fifo_load.lpm_type = "dcfifo_mixed_widths", + fifo_load.lpm_width = 32, fifo_load.lpm_widthu = 12, + fifo_load.lpm_widthu_r = 11, fifo_load.lpm_width_r = 64, fifo_load.overflow_checking = "OFF", + fifo_load.rdsync_delaypipe = 5, fifo_load.underflow_checking = "ON", fifo_load.use_eab = "ON", + fifo_load.wrsync_delaypipe = 5, fifo_load.write_aclr_synch = "ON"; + + reg fifo_save_write_req; + reg fifo_save_read_req; + + wire fifo_save_rd_empty; + wire fifo_save_wr_empty; + + dcfifo_mixed_widths fifo_save ( + .data(ss_din), + .rdclk(clk_74a), + .rdreq(fifo_save_read_req), + .wrclk(clk_sys), + .wrreq(fifo_save_write_req), + .q({ + save_state_bridge_read_data[7:0], + save_state_bridge_read_data[15:8], + save_state_bridge_read_data[23:16], + save_state_bridge_read_data[31:24] + }), + .rdempty(fifo_save_rd_empty), + .wrempty(fifo_save_wr_empty), + .aclr(1'b0) + // .eccstatus(), + // .rdfull(), + // .rdusedw(), + // .wrfull(), + // .wrusedw() + ); + defparam fifo_save.intended_device_family = "Cyclone V", fifo_save.lpm_numwords = 4, + fifo_save.lpm_showahead = "OFF", fifo_save.lpm_type = "dcfifo_mixed_widths", + fifo_save.lpm_width = 64, fifo_save.lpm_widthu = 2, fifo_save.lpm_widthu_r = 3, + fifo_save.lpm_width_r = 32, fifo_save.overflow_checking = "ON", + fifo_save.rdsync_delaypipe = 5, fifo_save.underflow_checking = "ON", fifo_save.use_eab = "ON", + fifo_save.wrsync_delaypipe = 5; + + + reg prev_bridge_rd; + reg [1:0] save_read_state = NONE; + + wire [27:0] bridge_save_addr = bridge_addr[27:0]; + wire bridge_save_rd = bridge_rd && bridge_addr[31:28] == 4'h4; + + localparam SAVE_READ_REQ = 1; + + always @(posedge clk_74a) begin + prev_bridge_rd <= bridge_rd; + + if (bridge_rd && ~prev_bridge_rd && bridge_addr[31:28] == 4'h4) begin + if (~fifo_save_rd_empty && bridge_save_addr[22:2] != last_unloader_addr) begin + save_read_state <= SAVE_READ_REQ; + + fifo_save_read_req <= 1; + + last_unloader_addr <= bridge_save_addr[22:2]; + end + end + + case (save_read_state) + SAVE_READ_REQ: begin + save_read_state <= NONE; + + fifo_save_read_req <= 0; + end + endcase + end + + reg [20:0] last_unloader_addr = 21'hFFFF; + wire [15:0] save_state_unloader_data; + + localparam NONE = 0; + + localparam SAVE_BUSY = 1; + localparam SAVE_WAIT_REQ = SAVE_BUSY + 1; + localparam SAVE_WAIT_REQ_DELAY = SAVE_WAIT_REQ + 1; + localparam SAVE_WAIT_ACK = SAVE_WAIT_REQ_DELAY + 1; + localparam SAVE_SHIFT = SAVE_WAIT_ACK + 1; + + localparam LOAD_WAIT_REQ = 20; + localparam LOAD_READ_REQ = LOAD_WAIT_REQ + 1; + localparam LOAD_WAIT_APF_START = LOAD_READ_REQ + 1; + + localparam LOAD_APF_COMPLETE = LOAD_WAIT_APF_START + 1; + + reg [7:0] state = NONE; + + reg save_state_saving_req = 0; + reg save_state_loading = 0; + reg [63:0] ss_buffer = 0; + // Used for duplicate reads + reg [63:0] last_ss_buffer = 0; + reg [1:0] save_shift_count = 0; + + reg did_req = 0; + reg is_dup_read = 0; + + reg prev_savestate_start = 0; + reg prev_savestate_load = 0; + reg prev_ss_busy = 0; + reg prev_bridge_save_rd = 0; + + always @(posedge clk_sys) begin + prev_ss_busy <= ss_busy; + prev_savestate_start <= savestate_start_s; + prev_savestate_load <= savestate_load_s; + prev_bridge_save_rd <= bridge_save_rd; + + ss_load <= 0; + ss_save <= 0; + ss_ack <= 0; + fifo_save_write_req <= 0; + + if (~fifo_load_empty && ~save_state_loading) begin + // Begin save stating + state <= LOAD_WAIT_REQ; + + save_state_loading <= 1; + + ss_load <= 1; + end + + if (savestate_start_s && ~prev_savestate_start) begin + // Begin saving state + state <= SAVE_BUSY; + + savestate_start_ack <= 1; + savestate_start_ok <= 0; + savestate_start_err <= 0; + + savestate_load_ok <= 0; + savestate_load_err <= 0; + + ss_save <= 1; + end else if (savestate_load_s && ~prev_savestate_load) begin + // Begin APF savestate load (data is already copied into ss manager) + save_state_saving_req <= 1; + + savestate_load_ack <= 1; + savestate_load_ok <= 0; + savestate_load_err <= 0; + + savestate_start_ok <= 0; + savestate_start_err <= 0; + end + + case (state) + // Saving + SAVE_BUSY: begin + savestate_start_ack <= 0; + savestate_start_busy <= 1; + + if (ss_req) begin + // First req, end busy, start loading out + // Duplicate of SAVE_WAIT_REQ + state <= SAVE_WAIT_REQ_DELAY; + + // Latch data + fifo_save_write_req <= 1; + + savestate_start_busy <= 0; + savestate_start_ok <= 1; + end + end + SAVE_WAIT_REQ: begin + // Wait for SS manager to send us more data + if (ss_req) begin + state <= SAVE_WAIT_REQ_DELAY; + + // Latch data + fifo_save_write_req <= 1; + end else if (prev_ss_busy && ~ss_busy) begin + // Left busy, SS manager is done + state <= NONE; + end + end + SAVE_WAIT_REQ_DELAY: begin + // Delay for empty to go low + state <= SAVE_WAIT_ACK; + end + SAVE_WAIT_ACK: begin + // Wait for bridge to read word + if (fifo_save_wr_empty) begin + state <= SAVE_WAIT_REQ; + + ss_ack <= 1; + end + end + + // Loading + LOAD_WAIT_REQ: begin + if (prev_ss_busy && ~ss_busy) begin + // Finished load. Wait for APF ack + state <= LOAD_WAIT_APF_START; + end else if (~fifo_load_empty) begin + if (did_req || ss_req) begin + // Request and FIFO has data + state <= LOAD_READ_REQ; + fifo_load_read_req <= 1; + did_req <= 0; + end + end else if (ss_req) begin + // Request. Wait for data to arrive + did_req <= 1; + end + end + LOAD_READ_REQ: begin + // Data should be available from FIFO read + state <= LOAD_WAIT_REQ; + + fifo_load_read_req <= 0; + ss_ack <= 1; + end + LOAD_WAIT_APF_START: begin + if (save_state_saving_req) begin + // Begin APF savestate load (data is already copied into ss manager) + state <= LOAD_APF_COMPLETE; + + fifo_load_clr <= 1; + + savestate_load_ack <= 0; + savestate_load_busy <= 1; + + save_state_saving_req <= 0; + end + end + LOAD_APF_COMPLETE: begin + state <= NONE; + + fifo_load_clr <= 0; + + savestate_load_busy <= 0; + savestate_load_ok <= 1; + + save_state_loading <= 0; + end + endcase + end +endmodule diff --git a/src/gb/sram_128k_x1_x16.sv b/src/gb/sram_128k_x1_x16.sv new file mode 100644 index 0000000..5c19f6a --- /dev/null +++ b/src/gb/sram_128k_x1_x16.sv @@ -0,0 +1,121 @@ +//------------------------------------------------------------------------------ +// SPDX-License-Identifier: MIT +// SPDX-FileType: SOURCE +// SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors +//------------------------------------------------------------------------------ +// +// SRAM Controller +// +// Copyright (c) 2023, Marcus Andrade +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//------------------------------------------------------------------------------ + +`default_nettype none + +module sram + ( + // Clock and Reset + input wire clk, //! Input Clock + input wire reset, //! Reset + output wire sram_wipe_done, + + // Single Port Internal Bus Interface + input wire we, //! Write Enable + input wire ub, + input wire lb, + input wire [16:0] addr, //! Address In + input wire [15:0] d, //! Data In + output reg [15:0] q, //! Data Out + + // SRAM External Interface + output reg [16:0] sram_addr, //! Address Out + inout reg [15:0] sram_dq, //! Data In/Out + output reg sram_oe_n, //! Output Enable + output reg sram_we_n, //! Write Enable + output reg sram_ub_n, //! Upper Byte Mask + output reg sram_lb_n //! Lower Byte Mask + ); + + typedef enum logic [1:0] { + RESET_MEMORY, + NORMAL_OPERATION + } state_t; + + state_t state, next_state; + reg [16:0] reset_counter; + + // State machine transitions + always_ff @(posedge clk or posedge reset) begin + if(reset) begin + state <= RESET_MEMORY; + reset_counter <= 0; + end + else begin + state <= next_state; + if (state == RESET_MEMORY) begin + reset_counter <= reset_counter + 1; + end + end + end + + // State machine logic + always_ff @(posedge clk) begin : sramFSM + case (state) + RESET_MEMORY: begin + sram_wipe_done <= 0; + {sram_lb_n, sram_ub_n} <= 2'b00; // Unmask Low/High Byte + sram_addr <= reset_counter; // Set Address + sram_dq <= 16'h0000; // Write Zeros + {sram_oe_n, sram_we_n} <= 2'b10; // Output Disabled/Write Enabled + + if (reset_counter == 17'h1FFFF) begin + next_state <= NORMAL_OPERATION; + end + else begin + next_state <= RESET_MEMORY; + end + end + NORMAL_OPERATION: begin + sram_wipe_done <= 1; + sram_addr <= {17{1'bX}}; // Set Address as "Don't Care" + sram_dq <= {16{1'bZ}}; // Set Data Bus as High Impedance (Tristate) + if(we) begin + {sram_lb_n, sram_ub_n} <= {~lb, ~ub}; + {sram_oe_n, sram_we_n} <= 2'b10; // Output Disabled/Write Enabled + sram_addr <= addr; // Set Address + sram_dq <= d; // Write Data + end + else begin + {sram_lb_n, sram_ub_n} <= 2'b00; // Mask Low/High Byte + {sram_oe_n, sram_we_n} <= 2'b01; // Write Disabled/Output Enabled + sram_addr <= addr; // Set Address + q <= sram_dq; // Read Data + end + next_state <= NORMAL_OPERATION; + end + + default: begin + next_state <= RESET_MEMORY; + end + endcase + end + +endmodule