From 6f6d075d8a5a07dce10d13f6b56fcd760b8dc032 Mon Sep 17 00:00:00 2001 From: David Hunter Date: Sat, 24 Jan 2026 18:44:06 -0800 Subject: [PATCH] Refine FX-BMP implementation - Add battery status readout - Narrow address range to E800_0000 .. EBFF_FFFF - Enable BMP only if it's mounted - Configure BMP size according to mounted image size --- files.qip | 1 + rtl/fx_bmp.sv | 67 +++++++++++++++++++++++++++++++ rtl/fx_ga.sv | 12 +++--- rtl/mach.sv | 40 +++++++++--------- rtl/pcfx_top.sv | 65 ++++++++++++++++++++++++++---- rtl/tb/pcfx_top.files | 1 + rtl/tb/pcfx_top_tb.sv | 10 ++++- rtl/tb/pcfx_top_tb.verilator.gtkw | 35 ++++++++++++---- 8 files changed, 189 insertions(+), 42 deletions(-) create mode 100644 rtl/fx_bmp.sv diff --git a/files.qip b/files.qip index abca34e..0a88676 100644 --- a/files.qip +++ b/files.qip @@ -14,6 +14,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE rtl/v810/v810.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/mach.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/ram.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/sdram.v +set_global_assignment -name SYSTEMVERILOG_FILE rtl/fx_bmp.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/dpram.sv # set_global_assignment -name VHDL_FILE rtl/dpram.vhd set_global_assignment -name SYSTEMVERILOG_FILE rtl/memif_sdram.sv diff --git a/rtl/fx_bmp.sv b/rtl/fx_bmp.sv new file mode 100644 index 0000000..6a305c2 --- /dev/null +++ b/rtl/fx_bmp.sv @@ -0,0 +1,67 @@ +// A FX-BMP with battery-backed SRAM, connected to the Memory Card Port +// +// Copyright (c) 2026 David Hunter +// +// This program is GPL licensed. See COPYING for the full license. + +module fx_bmp + ( + // Emulation configuration + input CFG_EN, + input [2:0] CFG_SIZE, // 0=128KB, 1=256KB, 2=512KB, .. 6=8MB + + // Memory Card port interface + input [26:1] MCP_A, // A24 is omitted + input [7:0] MCP_DI, + output [7:0] MCP_DO, + input MCP_CSn, // aka /CartSel + input MCP_RDn, + input MCP_WRn, + output MCP_READYn, + + // Memory interface + output [22:0] RAM_A, + output [7:0] RAM_DI, + input [7:0] RAM_DO, + output RAM_CEn, + output RAM_WEn, + input RAM_READYn + ); + +// Control size by masking (zeroing) RAM address bits above 128KB. +wire [22:0] ram_a_mask; + +always @* begin + ram_a_mask[16:0] = '1; + case (CFG_SIZE) + 3'd0: ram_a_mask[22:17] = 'b000_000; // 128KB + 3'd1: ram_a_mask[22:17] = 'b000_001; // 256KB + 3'd2: ram_a_mask[22:17] = 'b000_011; // 512KB + 3'd3: ram_a_mask[22:17] = 'b000_111; // 1MB + 3'd4: ram_a_mask[22:17] = 'b001_111; // 2MB + 3'd5: ram_a_mask[22:17] = 'b011_111; // 4MB + default:ram_a_mask[22:17] = 'b111_111; // 8MB + endcase +end + +// BMP address range is E800_0000 .. EBFF_FFFF -- half the port's addressable range. +wire bmp_sel = ~(~CFG_EN | MCP_CSn | MCP_A[26]); + +// BMP memory is split down the middle: +// E800_0000 + (0000_0000 .. 01FF_FFFE) is SRAM +// E800_0000 + (0200_0000 .. 03FF_FFFE) is battery status +wire sram_sel = bmp_sel & ~MCP_A[25]; +wire bat_sel = bmp_sel & MCP_A[25]; + +// The battery is always "good". +wire bat_good = '1; + +assign RAM_A = MCP_A[23:1] & ram_a_mask; +assign RAM_DI = MCP_DI; +assign RAM_CEn = ~sram_sel; +assign RAM_WEn = RAM_CEn | MCP_WRn; + +assign MCP_DO = {8{~bmp_sel}} | (bat_sel ? {7'h7F, bat_good} : RAM_DO); +assign MCP_READYn = MCP_CSn | (sram_sel & RAM_READYn); + +endmodule diff --git a/rtl/fx_ga.sv b/rtl/fx_ga.sv index 45c2da3..d3e32a5 100644 --- a/rtl/fx_ga.sv +++ b/rtl/fx_ga.sv @@ -30,7 +30,7 @@ module fx_ga output ROM_CEn, output RAM_CEn, output SRAM_CEn, - output BMP_CEn, + output MCP_CSn, output IO_CEn, output FX_GA_CSn, @@ -45,7 +45,7 @@ module fx_ga input ROM_READYn, input RAM_READYn, input SRAM_READYn, - input BMP_READYn, + input MCP_READYn, // Device control output WRn, @@ -77,8 +77,8 @@ module fx_ga logic unk_cen; logic io_readyn; -assign READYn = unk_cen & ROM_READYn & RAM_READYn & SRAM_READYn & BMP_READYn & io_readyn; -assign SZRQn = ~unk_cen | (ROM_CEn & IO_CEn & SRAM_CEn & BMP_CEn); +assign READYn = unk_cen & ROM_READYn & RAM_READYn & SRAM_READYn & MCP_READYn & io_readyn; +assign SZRQn = ~unk_cen | (ROM_CEn & IO_CEn & SRAM_CEn & MCP_CSn); ////////////////////////////////////////////////////////////////////// // Address decoder @@ -90,10 +90,10 @@ assign A1_16 = A[1] | (~&BEn[3:2] & &BEn[1:0]); assign ROM_CEn = ~(~MRQn & (A[31:28] == 4'hF)); // F000_0000 .. FFFF_FFFF assign RAM_CEn = ~(~MRQn & (A[31:24] == 8'h00)); // 0000_0000 .. 00FF_FFFF assign SRAM_CEn = ~(~MRQn & (A[31:27] == 5'b1110_0)); // E000_0000 .. E7FF_FFFF -assign BMP_CEn = ~(~MRQn & (A[31:27] == 5'b1110_1)); // E800_0000 .. EFFF_FFFF +assign MCP_CSn = ~(~MRQn & (A[31:27] == 5'b1110_1)); // E800_0000 .. EFFF_FFFF assign IO_CEn = ~((MRQn | (A[31:28] == 4'h8)) & (~BCYSTn | ~DAn) & (ST == 2'b10)); -assign unk_cen = ~(RAM_CEn & ROM_CEn & SRAM_CEn & BMP_CEn & IO_CEn); +assign unk_cen = ~(RAM_CEn & ROM_CEn & SRAM_CEn & MCP_CSn & IO_CEn); assign FX_GA_CSn = ~(~IO_CEn & (A[30:12] == 19'h00000)) | ~(PSG_CSn & VPU_CSn & VCE_CSn & VDC0_CSn & diff --git a/rtl/mach.sv b/rtl/mach.sv index d151573..33c21e6 100644 --- a/rtl/mach.sv +++ b/rtl/mach.sv @@ -36,12 +36,13 @@ module mach output SRAM_WEn, input SRAM_READYn, - output [22:0] BMP_A, - output [7:0] BMP_DI, - input [7:0] BMP_DO, - output BMP_CEn, - output BMP_WEn, - input BMP_READYn, + output [26:1] MCP_A, + output [7:0] MCP_DI, + input [7:0] MCP_DO, + output MCP_CSn, + output MCP_RDn, + output MCP_WRn, + input MCP_READYn, input hmi_t HMI, @@ -85,9 +86,9 @@ logic sram_cen; wire [7:0] sram_do; logic sram_readyn; -logic bmp_cen; -wire [7:0] bmp_do; -logic bmp_readyn; +logic mcp_csn; +wire [7:0] mcp_do; +logic mcp_readyn; logic a1_16; logic [31:0] mem16_a; @@ -206,7 +207,7 @@ fx_ga ga .ROM_CEn(rom_cen), .RAM_CEn(ram_cen), .SRAM_CEn(sram_cen), - .BMP_CEn(bmp_cen), + .MCP_CSn(mcp_csn), .IO_CEn(io_cen), .FX_GA_CSn(ga_csn), @@ -220,7 +221,7 @@ fx_ga ga .ROM_READYn(rom_readyn), .RAM_READYn(ram_readyn), .SRAM_READYn(sram_readyn), - .BMP_READYn(bmp_readyn), + .MCP_READYn(mcp_readyn), .WRn(ga_wrn), .RDn(ga_rdn), @@ -451,8 +452,8 @@ always @* begin cpu_d_i = ram_do; else if (~sram_cen) cpu_d_i = {24'b0, sram_do}; - else if (~bmp_cen) - cpu_d_i = {24'b0, bmp_do}; + else if (~mcp_csn) + cpu_d_i = {24'b0, mcp_do}; else if (~io_cen) cpu_d_i = {16'b0, io_do}; else @@ -483,8 +484,8 @@ assign ram_readyn = ram_cen | RAM_READYn; assign sram_do = SRAM_DO; assign sram_readyn = sram_cen | SRAM_READYn; -assign bmp_do = BMP_DO; -assign bmp_readyn = bmp_cen | BMP_READYn; +assign mcp_do = MCP_DO; +assign mcp_readyn = mcp_csn | MCP_READYn; assign mem16_a = {cpu_a[31:2], a1_16, 1'b0}; @@ -507,10 +508,11 @@ assign SRAM_A = mem16_a[15:1]; assign SRAM_DI = cpu_d_o[7:0]; assign SRAM_WEn = sram_cen | cpu_rw; -assign BMP_CEn = bmp_cen; -assign BMP_A = mem16_a[23:1]; -assign BMP_DI = cpu_d_o[7:0]; -assign BMP_WEn = bmp_cen | cpu_rw; +assign MCP_CSn = mcp_csn; +assign MCP_A = mem16_a[26:1]; +assign MCP_DI = cpu_d_o[7:0]; +assign MCP_RDn = mcp_csn | ~cpu_rw; +assign MCP_WRn = mcp_csn | cpu_rw; ////////////////////////////////////////////////////////////////////// // SCSI interface diff --git a/rtl/pcfx_top.sv b/rtl/pcfx_top.sv index 94d498e..e7836ee 100644 --- a/rtl/pcfx_top.sv +++ b/rtl/pcfx_top.sv @@ -159,6 +159,15 @@ wire sram_cen; wire sram_wen; wire sram_readyn; +wire [26:1] mcp_a; +wire [7:0] mcp_di, mcp_do; +wire mcp_csn; +wire mcp_rdn; +wire mcp_wrn; +wire mcp_readyn; + +wire bmp_cfg_en; +logic [2:0] bmp_cfg_size; wire [22:0] bmp_a; wire [7:0] bmp_di, bmp_do; wire bmp_cen; @@ -207,12 +216,13 @@ mach mach .SRAM_WEn(sram_wen), .SRAM_READYn(sram_readyn), - .BMP_A(bmp_a), - .BMP_DI(bmp_di), - .BMP_DO(bmp_do), - .BMP_CEn(bmp_cen), - .BMP_WEn(bmp_wen), - .BMP_READYn(bmp_readyn), + .MCP_A(mcp_a), + .MCP_DI(mcp_di), + .MCP_DO(mcp_do), + .MCP_CSn(mcp_csn), + .MCP_RDn(mcp_rdn), + .MCP_WRn(mcp_wrn), + .MCP_READYn(mcp_readyn), .HMI(HMI), @@ -276,6 +286,27 @@ memif_sdram memif_sdram .SDRAM_DOUT(sdram_dout) ); +fx_bmp bmp + ( + .CFG_EN(bmp_cfg_en), + .CFG_SIZE(bmp_cfg_size), + + .MCP_A(mcp_a), + .MCP_DI(mcp_di), + .MCP_DO(mcp_do), + .MCP_CSn(mcp_csn), + .MCP_RDn(mcp_rdn), + .MCP_WRn(mcp_wrn), + .MCP_READYn(mcp_readyn), + + .RAM_A(bmp_a), + .RAM_DI(bmp_di), + .RAM_DO(bmp_do), + .RAM_CEn(bmp_cen), + .RAM_WEn(bmp_wen), + .RAM_READYn(bmp_readyn) + ); + ////////////////////////////////////////////////////////////////////// // ROM loader @@ -318,6 +349,26 @@ always @(posedge clk_sys) begin end end +////////////////////////////////////////////////////////////////////// +// FX-BMP -> Memory Cord Port + +wire [31:0] bmp_sd_blk_cnt = bk_sd_blk_cnt[1]; + +assign bmp_cfg_en = bk_mounted[1]; + +// Configure the BMP size to match the mounted image size. +always @* begin + casez ({|bmp_sd_blk_cnt[31:14], bmp_sd_blk_cnt[13:8]}) + 7'b1??_????: bmp_cfg_size = 3'd6; // 8MB + 7'b01?_????: bmp_cfg_size = 3'd5; // 4MB + 7'b001_????: bmp_cfg_size = 3'd4; // 2MB + 7'b000_1???: bmp_cfg_size = 3'd3; // 1MB + 7'b000_01??: bmp_cfg_size = 3'd2; // 512KB + 7'b000_001?: bmp_cfg_size = 3'd1; // 256KB + default: bmp_cfg_size = 3'd0; // 128KB + endcase +end + ////////////////////////////////////////////////////////////////////// // Backup RAM transfer @@ -347,7 +398,7 @@ logic sd_vd; // volume select logic sd_ack_d; always @(posedge clk_sys) begin - if (img_mounted) begin + if (img_mounted != 0) begin bk_mounted[img_mounted[1]] <= |img_size; bk_sd_blk_cnt[img_mounted[1]] <= img_size[9+:32]; end diff --git a/rtl/tb/pcfx_top.files b/rtl/tb/pcfx_top.files index a2fd00f..1236cf1 100644 --- a/rtl/tb/pcfx_top.files +++ b/rtl/tb/pcfx_top.files @@ -8,6 +8,7 @@ dpram.sv ../pcfx_top.sv ../memif_sdram.sv ../sdram.v +../fx_bmp.sv ../mach.sv ../huc6261.sv diff --git a/rtl/tb/pcfx_top_tb.sv b/rtl/tb/pcfx_top_tb.sv index 368c080..79ad81e 100644 --- a/rtl/tb/pcfx_top_tb.sv +++ b/rtl/tb/pcfx_top_tb.sv @@ -346,7 +346,7 @@ integer code; sd_size[vd] = $ftell(fin); sd_vd = vd; -> mount_sd; - repeat (2) @(posedge clk_sys) ; // wait for mount completion + repeat (3) @(posedge clk_sys) ; // wait for mount completion end endtask @@ -397,7 +397,7 @@ always @(negedge vs) begin $display("%t: Frame %03d A=%x", $time, frame, pcfx_top.mach.cpu_a); $sformat(fname, "frames/render-%03d", frame); pice = 0; - if (frame >= 237) begin + if (frame >= 220) begin fpic = $fopen({fname, ".hex"}, "w"); end frame = frame + 1; @@ -424,6 +424,8 @@ end ////////////////////////////////////////////////////////////////////// +event running; + initial #0 begin #10 ; // wait for sdram init. @@ -443,9 +445,12 @@ initial #0 begin $display("RAMs loaded."); end `endif + + -> running; end initial begin + @(running) ; repeat (4) #(1000e3) ; //#(500e3) ; @@ -465,6 +470,7 @@ initial begin end initial if (1) begin + @(running) ; #(216e3); repeat (4) begin diff --git a/rtl/tb/pcfx_top_tb.verilator.gtkw b/rtl/tb/pcfx_top_tb.verilator.gtkw index 2a5fb1e..035234e 100644 --- a/rtl/tb/pcfx_top_tb.verilator.gtkw +++ b/rtl/tb/pcfx_top_tb.verilator.gtkw @@ -1,15 +1,15 @@ [*] [*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI -[*] Mon Jan 19 23:30:13 2026 +[*] Sun Jan 25 01:51:43 2026 [*] [dumpfile] "/Users/dhunter/src/mister/PCFX_MiSTer/rtl/tb/pcfx_top_tb.verilator.fst" -[dumpfile_mtime] "Mon Jan 19 23:29:06 2026" -[dumpfile_size] 9107446 +[dumpfile_mtime] "Sun Jan 25 01:48:02 2026" +[dumpfile_size] 18901994 [savefile] "/Users/dhunter/src/mister/PCFX_MiSTer/rtl/tb/pcfx_top_tb.verilator.gtkw" -[timestart] 1230000 +[timestart] 0 [size] 1334 601 [pos] -1 -1 -*-24.251604 50041100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +*-25.045605 50097180 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [markername] AA [markername] BB [markername] CC @@ -37,7 +37,6 @@ [markername] YY [markername] ZZ [treeopen] pcfx_top_tb. -[treeopen] pcfx_top_tb.pcfx_top. [treeopen] pcfx_top_tb.pcfx_top.mach.cpu. [treeopen] pcfx_top_tb.pcfx_top.mach.vdc1.SAT. [sst_width] 255 @@ -48,6 +47,7 @@ -pcfx_top @28 pcfx_top_tb.pcfx_top.img_mounted[1:0] +pcfx_top_tb.pcfx_top.bk_mounted[1:0] pcfx_top_tb.pcfx_top.bk_load pcfx_top_tb.pcfx_top.bk_save pcfx_top_tb.pcfx_top.sd_vd @@ -62,7 +62,6 @@ pcfx_top_tb.pcfx_top.sd_buff_addr[7:0] pcfx_top_tb.pcfx_top.sd_buff_dout[15:0] @28 pcfx_top_tb.pcfx_top.bk_loading -@29 pcfx_top_tb.pcfx_top.bk_saving @100000028 pcfx_top_tb.pcfx_top.bk_state[3:0] @@ -107,6 +106,26 @@ pcfx_top_tb.pcfx_top.memif_sdram.wact pcfx_top_tb.pcfx_top.memif_sdram.mem_rdy @1000200 -memif_sdram +@800200 +-fx_bmp +@28 +pcfx_top_tb.pcfx_top.bmp.CFG_EN +@24 +pcfx_top_tb.pcfx_top.bmp.CFG_SIZE[2:0] +@29 +pcfx_top_tb.pcfx_top.bmp.MCP_CSn +@22 +pcfx_top_tb.pcfx_top.bmp.MCP_A[26:1] +@28 +pcfx_top_tb.pcfx_top.bmp.MCP_READYn +@22 +pcfx_top_tb.pcfx_top.bmp.MCP_DO[7:0] +@28 +pcfx_top_tb.pcfx_top.bmp.bat_sel +pcfx_top_tb.pcfx_top.bmp.RAM_CEn +pcfx_top_tb.pcfx_top.bmp.RAM_READYn +@1000200 +-fx_bmp @28 pcfx_top_tb.pcfx_top.mach.ERROR @22 @@ -123,7 +142,7 @@ pcfx_top_tb.pcfx_top.mach.mem16_a[31:0] pcfx_top_tb.pcfx_top.mach.ram_cen pcfx_top_tb.pcfx_top.mach.rom_cen pcfx_top_tb.pcfx_top.mach.sram_cen -pcfx_top_tb.pcfx_top.mach.bmp_cen +pcfx_top_tb.pcfx_top.mach.mcp_csn pcfx_top_tb.pcfx_top.mach.io_cen pcfx_top_tb.pcfx_top.mach.ga_rdn pcfx_top_tb.pcfx_top.mach.ga_wrn