Add 4 I/O wait states; fix HuC6270 BUSY_N and CPU_CE use

HuC6270 signals are now more closely aligned with how huc6280.vhd uses
them.

This fixes a freeze on entering the backup memory screen.
This commit is contained in:
David Hunter
2026-01-14 00:39:38 -08:00
parent 01cae25670
commit fa0e45c92a
5 changed files with 107 additions and 22 deletions

View File

@@ -47,6 +47,7 @@ module fx_ga
// Device control
output WRn,
output RDn,
output VDC_CPU_CE,
input VDC0_BUSYn,
input VDC1_BUSYn,
input MMC_BUSYn,
@@ -77,7 +78,7 @@ assign READYn = unk_cen & ROM_READYn & RAM_READYn & io_readyn;
assign SZRQn = ~unk_cen | (ROM_CEn & IO_CEn);
//////////////////////////////////////////////////////////////////////
// Address decoder / device control
// Address decoder
// Assert A[1] for upper-halfword or -byte access to 16-bit memory /
// IO, i.e., if one or both of BEn[3:2] are asserted.
@@ -101,16 +102,63 @@ assign VDC0_CSn = ~(~IO_CEn & (A[27:8] == 20'h00004)); // HuC6270 #0
assign VDC1_CSn = ~(~IO_CEn & (A[27:8] == 20'h00005)); // HuC6270 #1
assign MMC_CSn = ~(~IO_CEn & (A[27:8] == 20'h00006)); // HuC6272
assign WRn = IO_CEn | DAn | RW;
assign RDn = IO_CEn | DAn | ~RW;
//////////////////////////////////////////////////////////////////////
// I/O device control
logic io_wait;
logic [2:0] io_wait_cnt;
logic vdc_busy_end;
assign WRn = IO_CEn | vdc_busy_end | DAn | RW;
assign RDn = IO_CEn | vdc_busy_end | DAn | ~RW;
// FXGABOAD says: "Write access to normal I/O requires six cycles," or
// 4 wait states. Assume that read access does, too.
//
// TODO: Implement Huc* write buffer.
always @(posedge CLK) if (CE) begin
if (~RESn) begin
io_wait_cnt <= '0;
end
else begin
if (io_wait) // I/O cycle in progress
io_wait_cnt <= io_wait_cnt - 1'd1;
else if (~IO_CEn & ~BCYSTn) // New I/O cycle start
io_wait_cnt <= 3'd4;
end
end
assign io_wait = |io_wait_cnt;
always @* begin
if (~VDC0_CSn) io_readyn = ~VDC0_BUSYn;
else if (~VDC1_CSn) io_readyn = ~VDC1_BUSYn;
else if (~MMC_CSn) io_readyn = ~MMC_BUSYn;
else io_readyn = IO_CEn;
io_readyn |= io_wait;
end
// huc6270.vhd inputs a signal that never existed in actual hardware:
// the internal clock enable of the CPU. It uses this to determine
// when the I/O bus cycle completes. (Actual HW would instead use the
// de-assertion of RDn / WRn for this purpose.)
assign VDC_CPU_CE = CE & ~io_readyn;
// huc6270.vhd assumes that, when it de-asserts BUSYn, RDn / WRn will
// de-assert in the next clock cycle, regardless of CE. But, CPU will
// only act on BUSYn and then change RDn / WRn on CE. This workaround
// de-asserts RDn / WRn early, then resets when DAn de-asserts (CPU
// ends bus cycle).
wire vdc_busy = ~io_wait & (~VDC0_BUSYn | ~VDC1_BUSYn);
logic vdc_busy_d;
always @(posedge CLK) begin
vdc_busy_d <= vdc_busy;
vdc_busy_end <= (vdc_busy_end | (vdc_busy_d & ~vdc_busy)) & ~(~RESn | DAn);
end
//////////////////////////////////////////////////////////////////////
// Register interface

View File

@@ -112,6 +112,7 @@ wire vram1_we;
logic ga_wrn, ga_rdn;
logic ga_csn;
logic [15:0] ga_do;
logic vdc_cpu_ce;
logic pce, pce_negedge;
logic hs_posedge, hs_negedge;
@@ -208,6 +209,7 @@ fx_ga ga
.WRn(ga_wrn),
.RDn(ga_rdn),
.VDC_CPU_CE(vdc_cpu_ce),
.VDC0_BUSYn(vdc0_busyn),
.VDC1_BUSYn(vdc1_busyn),
.MMC_BUSYn(mmc_busyn),
@@ -262,7 +264,7 @@ huc6270 vdc0
.CLK(CLK),
.RST_N(RESn),
.CLR_MEM('0),
.CPU_CE(CE),
.CPU_CE(vdc_cpu_ce),
.BYTEWORD('0),
.A({mem16_a[2], 1'b0}),
@@ -316,7 +318,7 @@ huc6270 vdc1
.CLK(CLK),
.RST_N(RESn),
.CLR_MEM('0),
.CPU_CE(CE),
.CPU_CE(vdc_cpu_ce),
.BYTEWORD('0),
.A({mem16_a[2], 1'b0}),
@@ -527,12 +529,18 @@ assign VID_PCE = pce;
//////////////////////////////////////////////////////////////////////
always @(posedge CLK) if (1 && CE) begin
always @(posedge CLK) if (0 && CE) begin
if (~io_cen & ~cpu_dan)
$display("%t: %x %s %x", $realtime,A, (cpu_rw ? "R" : "w"),
(cpu_rw ? cpu_d_i[15:0] : cpu_d_o[15:0]));
end
always @(posedge CLK) if (1 && CE) begin
if (~cpu_bcystn & ~cpu_mrqn &
((cpu_a == 32'hFFFFFF90) | (cpu_a == 32'hFFFFFFD0)))
$finish(1);
end
always @(sram_cen)
$display("!! %t: sram_cen=%x", $realtime, sram_cen);

View File

@@ -24,7 +24,7 @@ initial begin
$dumpvars();
`else
$dumpfile("pcfx_top_tb.verilator.fst");
#(0e3) $dumpvars();
#(4800e3) $dumpvars();
`endif
end
@@ -271,8 +271,8 @@ initial #0 begin
end
initial begin
#(418e3) ;
//repeat (15) #(1000e3) ;
repeat (5) #(1000e3) ;
#(100e3) ;
//$writememh("vram0.hex", pcfx_top.mach.vram0.mem);
//$writememh("vram1.hex", pcfx_top.mach.vram1.mem);
//$writememh("vce_cp.hex", pcfx_top.mach.vce.cpram.mem);
@@ -285,18 +285,18 @@ initial if (1) begin
$display("Pressing JP1.Left...");
hmi.jp1.l = '1;
#(2600e3) hmi.jp1.l = '0;
#(1300e3) hmi.jp1.l = '0;
#(40e3);
$display("Pressing JP1.Up...");
hmi.jp1.u = '1;
#(40e3) hmi.jp1.u = '0;
#(80e3);
#(40e3);
$display("Pressing JP1.Run...");
hmi.jp1.run = '1;
#(40e3) hmi.jp1.run = '0;
#(80e3);
#(40e3);
end
endmodule

View File

@@ -1,15 +1,15 @@
[*]
[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI
[*] Sun Jan 4 05:13:07 2026
[*] Mon Jan 12 04:51:09 2026
[*]
[dumpfile] "/Users/dhunter/src/mister/PCFX_MiSTer/rtl/tb/pcfx_top_tb.verilator.fst"
[dumpfile_mtime] "Sun Jan 4 05:08:23 2026"
[dumpfile_size] 26093463
[dumpfile_mtime] "Mon Jan 12 01:43:43 2026"
[dumpfile_size] 27041171
[savefile] "/Users/dhunter/src/mister/PCFX_MiSTer/rtl/tb/pcfx_top_tb.verilator.gtkw"
[timestart] 0
[timestart] 4825000000
[size] 1334 601
[pos] -1 -1
*-26.078840 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 0
*-27.020149 5046407940 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
@@ -51,14 +51,13 @@ pcfx_top_tb.pcfx_top.mach.cpu_d_i[31:0]
pcfx_top_tb.pcfx_top.mach.cpu_d_o[31:0]
@28
pcfx_top_tb.pcfx_top.mach.cpu_rw
pcfx_top_tb.pcfx_top.mach.cpu_readyn
@22
pcfx_top_tb.pcfx_top.mach.mem16_a[31:0]
@28
pcfx_top_tb.pcfx_top.mach.ram_cen
pcfx_top_tb.pcfx_top.mach.rom_cen
@29
pcfx_top_tb.pcfx_top.mach.sram_cen
@28
pcfx_top_tb.pcfx_top.mach.io_cen
pcfx_top_tb.pcfx_top.mach.ga_rdn
pcfx_top_tb.pcfx_top.mach.ga_wrn
@@ -69,18 +68,32 @@ pcfx_top_tb.pcfx_top.mach.cpu.INT
@22
pcfx_top_tb.pcfx_top.mach.cpu.eu.pc[31:0]
pcfx_top_tb.pcfx_top.mach.cpu.eu.idex_pc[31:0]
pcfx_top_tb.pcfx_top.mach.cpu.eu.idex_ir[31:0]
@28
pcfx_top_tb.pcfx_top.mach.cpu.SZRQn
@22
pcfx_top_tb.pcfx_top.mach.cpu.eu.DA[31:0]
@100000028
pcfx_top_tb.pcfx_top.mach.cpu.mem.ebst[2:0]
@200
-ga
@28
pcfx_top_tb.pcfx_top.mach.ga.MRQn
@22
pcfx_top_tb.pcfx_top.mach.ga.isr[6:0]
@c00022
pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
@800200
@28
(0)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
(1)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
(2)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
(3)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
(4)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
(5)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
(6)pcfx_top_tb.pcfx_top.mach.ga.imr[6:0]
@1401200
-group_end
@c00200
-kpc0
@28
pcfx_top_tb.pcfx_top.mach.ga.kpc0.KP_LATCH
@@ -96,7 +109,7 @@ pcfx_top_tb.pcfx_top.mach.ga.kpc0.DO[15:0]
pcfx_top_tb.pcfx_top.mach.ga.kpc0.ios
@22
pcfx_top_tb.pcfx_top.mach.ga.kpc0.data_sr[31:0]
@1000200
@1401200
-kpc0
@200
-mmc
@@ -161,15 +174,31 @@ pcfx_top_tb.pcfx_top.mach.scsi_cd.CD_DATA_END
@28
pcfx_top_tb.pcfx_top.mach.vdc0.IRQ_N
pcfx_top_tb.pcfx_top.mach.vdc0.CS_N
pcfx_top_tb.pcfx_top.mach.vdc0.BUSY_N
@22
pcfx_top_tb.pcfx_top.mach.vdc0.AR[4:0]
pcfx_top_tb.pcfx_top.mach.vdc0.VD[8:0]
@200
-vdc1
@28
pcfx_top_tb.pcfx_top.mach.vdc1.CPU_CE
@29
pcfx_top_tb.pcfx_top.mach.vdc1.CS_N
@28
pcfx_top_tb.pcfx_top.mach.vdc1.A[1:0]
pcfx_top_tb.pcfx_top.mach.vdc1.BUSY_N
@22
pcfx_top_tb.pcfx_top.mach.vdc1.AR[4:0]
@28
pcfx_top_tb.pcfx_top.mach.vdc1.RD_N
pcfx_top_tb.pcfx_top.mach.vdc1.WR_N
@22
pcfx_top_tb.pcfx_top.mach.vdc1.VD[8:0]
@28
pcfx_top_tb.pcfx_top.mach.vdc1.CPU_BUSY
pcfx_top_tb.pcfx_top.mach.vdc1.CPURD_PEND
pcfx_top_tb.pcfx_top.mach.vdc1.CPURD_PEND2
pcfx_top_tb.pcfx_top.mach.vdc1.CPURD_EXEC
@200
-vce
@28