diff --git a/SF2000-FW.peri.xml b/SF2000-FW.peri.xml index 9ceee0b..d10b2c5 100644 --- a/SF2000-FW.peri.xml +++ b/SF2000-FW.peri.xml @@ -1,5 +1,5 @@ - + @@ -26,7 +26,7 @@ - + @@ -118,7 +118,7 @@ - + @@ -138,89 +138,91 @@ - + - - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -249,8 +251,8 @@ - - + + @@ -341,8 +343,8 @@ - - + + diff --git a/SF2000-FW.sdc b/SF2000-FW.sdc index c603f73..44f23fb 100644 --- a/SF2000-FW.sdc +++ b/SF2000-FW.sdc @@ -1,6 +1,25 @@ # PLL Constraints ################# -#create_clock -period 70.00 C14M create_clock -period 140.00 C7M_n +# turbo_clk (40 MHz): PLL 80 MHz / 2, drives CLKCPU in turbo mode. +# Using create_clock because Efinity cannot trace create_generated_clock +# through the global clock buffer from the PLL output to this FF. +create_clock -period 25.00 -name turbo_clk [get_pins {turbo_clk~FF|Q}] + +# CDC: Three asynchronous clock domains. +# All cross-domain paths use 2-stage synchronizers — no timing analysis needed. +set_clock_groups -asynchronous \ + -group {C7M_n} \ + -group {turbo_clk} \ + -group {pll_inst1_CLKOUT1} + +# False paths: Quasi-static autoconfig registers (C7M domain) +# These only change during boot autoconfig and are completely static +# during normal operation. Eliminates false hold violations on +# base_ram/ram_configured_n → fastram OE/WE paths that waste PnR effort. +# Using get_cells -from (Efinity rejects get_pins |Q as a startpoint). +set_false_path -from [get_cells {base_ram[*]~FF}] +set_false_path -from [get_cells {ram_configured_n~FF}] +set_false_path -from [get_cells {sd_configured_n~FF}] diff --git a/SF2000-FW.xml b/SF2000-FW.xml index 9ffb786..c729449 100644 --- a/SF2000-FW.xml +++ b/SF2000-FW.xml @@ -1,5 +1,5 @@ - + @@ -25,9 +25,9 @@ - + @@ -64,17 +64,21 @@ + + + + - - + + diff --git a/rtl/autoconfig_zii.v b/rtl/autoconfig_zii.v index 1485a86..04414ce 100644 --- a/rtl/autoconfig_zii.v +++ b/rtl/autoconfig_zii.v @@ -32,9 +32,9 @@ localparam [7:0] RAM_PROD_ID = 8'd10; // 5194/10 - SF2000, Memory Master (4M localparam [7:0] SD_PROD_ID = 8'd11; // 5194/11 - SF2000, SD card controller I/O device (64K) localparam [15:0] SERIAL = 16'd0; -reg [1:0] configured_n = 2'b11; +reg [1:0] configured_n = 2'b11; // Both unconfigured reg [1:0] shutup_n = 2'b11; -reg [1:0] config_out_n = 2'b11; +reg [1:0] config_out_n = 2'b11; // Start with RAM wire autoconfig_access = !CFGIN_n && CFGOUT_n && (A_HIGH == 8'hE8) && !AS_CPU_n; @@ -44,15 +44,15 @@ assign SD_CONFIGURED_n = configured_n[SD_CARD]; assign CFGOUT_n = |config_out_n; assign DATA_OE = autoconfig_access && RW_n && !DS_n; -always @(negedge RESET_n or posedge C7M) begin +always @(negedge RESET_n or posedge AS_CPU_n) begin if (!RESET_n) begin - config_out_n <= 2'b11; + config_out_n <= 2'b11; // Start with RAM end else begin - if (AS_CPU_n) config_out_n <= configured_n & shutup_n; + config_out_n <= configured_n & shutup_n; end end @@ -61,7 +61,7 @@ always @(negedge RESET_n or posedge C7M) begin if (!RESET_n) begin - configured_n <= 2'b11; + configured_n <= 2'b11; // Both unconfigured shutup_n <= 2'b11; end else begin @@ -159,5 +159,4 @@ always @(negedge RESET_n or posedge C7M) begin end end - endmodule diff --git a/rtl/bus_arbiter.v b/rtl/bus_arbiter.v index 1a4204c..8f03a9a 100644 --- a/rtl/bus_arbiter.v +++ b/rtl/bus_arbiter.v @@ -34,18 +34,11 @@ module bus_arbiter( ); /* -Hybrid Bus Arbiter - -Takes KEY working elements from user's proven firmware: -1. BR_68SEC000_n = 0 at reset (hold 68SEC000!) -2. Fast bootstrap check (not 100 clocks) -3. Condition: (BG_n_IN != 0 || JP2 != 0) -4. BOSS_n_IN HIGH = B2000 - -Keeps BETTER structure from previous attempt: -1. Synchronizers for metastability protection -2. Cleaner state machine -3. Clear mode separation +Bus Arbiter + +Detects machine type (A2000 vs B2000) and CPU presence at boot. +Manages 3-way bus arbitration between 68SEC000, internal 68000, and +external DMA masters. All external signals use 2-stage synchronizers. */ //============================================================================= @@ -197,10 +190,10 @@ always @(posedge C7M) begin if (dma_en) begin // Mode 1 or Mode 3: DMA enabled BR_n_OE <= 1'b0; // Don't drive BR to internal CPU - BR_68SEC000_n <= BR_n_IN & BGACK_n; // 3-to-2 mapping (your elegant formula!) - + BR_68SEC000_n <= br_n_in_sync[1] & bgack_sync[1]; // 3-to-2 mapping (synced) + BG_n_OE <= 1'b1; // Drive BG to external DMA - BG_n_OUT <= BG_68SEC000_n; // Pass through 68SEC000's BG + BG_n_OUT <= bg_68sec_sync[1]; // Pass through 68SEC000's BG (synced — CDC from 25MHz) end else begin // Mode 2: A500 with CPU, no DMA diff --git a/rtl/fastram.v b/rtl/fastram.v index 5594335..36fcd4c 100644 --- a/rtl/fastram.v +++ b/rtl/fastram.v @@ -3,6 +3,7 @@ module fastram( input wire CLKCPU, + input wire BGACK_n, input wire CPU_SPEED_SWITCH, input wire [23:21] A, input wire JP4, @@ -27,15 +28,8 @@ module fastram( /* Fast RAM Controller - Conditional Registered OE/WE -KEY INSIGHT: -- At 7 MHz: Combinatorial OE/WE works fine (always worked!) -- At turbo: Need registered OE/WE for longer write pulses (stable writes) - -Solution: Use registered OE/WE only at turbo speeds! - -This way: -- 7 MHz: Fast, combinatorial (DMA/HCII+8 works) ✅ -- Turbo: Registered negedge WE (stable, reliable writes) ✅ +7 MHz: Combinatorial OE/WE (fast, DMA-compatible). +Turbo: Registered OE/WE on negedge for longer write pulses. */ /* @@ -53,6 +47,22 @@ wire second_4MB_access = !AS_n && !RAM_CONFIGURED_n && JP4 && ( (A == (BASE_RAM assign RAM_ACCESS = JP4 ? (first_4MB_access || second_4MB_access) : first_4MB_access; +// Filter address glitches during external DMA. +// Address transitions can transiently match the SRAM range, causing +// OE/WE to glitch. Requires 1-clock address stability before enabling. +reg dma_ram_valid; +always @(posedge CLKCPU) begin + if (AS_n) + dma_ram_valid <= 1'b0; + else + dma_ram_valid <= RAM_ACCESS; +end + +// Gate for combinatorial OE/WE: +// If DMA (!BGACK_n), requires 1-clock stability to prevent address glitches. +// If CPU (BGACK_n), bypasses filter for 0-wait-state performance. +wire safe_to_enable = BGACK_n ? 1'b1 : dma_ram_valid; + //============================================================================= // Registered OE/WE Signals (for turbo mode) //============================================================================= @@ -65,7 +75,7 @@ reg WE_BANK0_EVEN_n_reg = 1'b1; reg WE_BANK1_EVEN_n_reg = 1'b1; // OE registered on posedge -always @(posedge CLKCPU or posedge AS_n) begin +always @(posedge CLKCPU) begin if (AS_n) begin OE_BANK0_n_reg <= 1'b1; OE_BANK1_n_reg <= 1'b1; @@ -76,7 +86,7 @@ always @(posedge CLKCPU or posedge AS_n) begin end // WE registered on negedge (for maximum write time) -always @(negedge CLKCPU or posedge AS_n) begin +always @(negedge CLKCPU) begin if (AS_n) begin WE_BANK0_ODD_n_reg <= 1'b1; WE_BANK1_ODD_n_reg <= 1'b1; @@ -94,14 +104,14 @@ end // Combinatorial OE/WE Signals (for 7 MHz mode) //============================================================================= -wire OE_BANK0_n_comb = first_4MB_access && RW_n && !DS_n ? 1'b0 : 1'b1; -wire OE_BANK1_n_comb = second_4MB_access && RW_n && !DS_n ? 1'b0 : 1'b1; +wire OE_BANK0_n_comb = first_4MB_access && safe_to_enable && RW_n && !DS_n ? 1'b0 : 1'b1; +wire OE_BANK1_n_comb = second_4MB_access && safe_to_enable && RW_n && !DS_n ? 1'b0 : 1'b1; -wire WE_BANK0_ODD_n_comb = first_4MB_access && !RW_n && !LDS_n ? 1'b0 : 1'b1; -wire WE_BANK1_ODD_n_comb = second_4MB_access && !RW_n && !LDS_n ? 1'b0 : 1'b1; +wire WE_BANK0_ODD_n_comb = first_4MB_access && safe_to_enable && !RW_n && !LDS_n ? 1'b0 : 1'b1; +wire WE_BANK1_ODD_n_comb = second_4MB_access && safe_to_enable && !RW_n && !LDS_n ? 1'b0 : 1'b1; -wire WE_BANK0_EVEN_n_comb = first_4MB_access && !RW_n && !UDS_n ? 1'b0 : 1'b1; -wire WE_BANK1_EVEN_n_comb = second_4MB_access && !RW_n && !UDS_n ? 1'b0 : 1'b1; +wire WE_BANK0_EVEN_n_comb = first_4MB_access && safe_to_enable && !RW_n && !UDS_n ? 1'b0 : 1'b1; +wire WE_BANK1_EVEN_n_comb = second_4MB_access && safe_to_enable && !RW_n && !UDS_n ? 1'b0 : 1'b1; //============================================================================= // Output Mux: Registered at turbo, Combinatorial at 7 MHz @@ -120,20 +130,21 @@ assign WE_BANK1_EVEN_n = CPU_SPEED_SWITCH ? WE_BANK1_EVEN_n_reg : WE_BANK1_EVEN_ // DTACK Generation //============================================================================= -reg [2:0] wait_counter; -wire [2:0] wait_states = CPU_SPEED_SWITCH ? 3'd0 : 3'd0; +reg [3:0] wait_counter; +// DMA: 6 wait states for expansion card stability. CPU: 0 wait states. +wire [3:0] wait_states = (!BGACK_n) ? 4'd6 : 4'd0; -always @(posedge CLKCPU or posedge AS_CPU_n) begin +always @(posedge CLKCPU) begin - if (AS_CPU_n) begin + if (AS_n) begin DTACK_n <= 1'b1; - wait_counter <= 3'd0; + wait_counter <= 4'd0; end else begin if (RAM_ACCESS) begin if (wait_counter < wait_states) begin DTACK_n <= 1'b1; - wait_counter <= wait_counter + 3'd1; + wait_counter <= wait_counter + 4'd1; end else begin DTACK_n <= 1'b0; end diff --git a/rtl/flash.v b/rtl/flash.v index f5ba5a7..d6d81b5 100644 --- a/rtl/flash.v +++ b/rtl/flash.v @@ -27,7 +27,7 @@ assign FLASH_ACCESS = A[23:20] == 4'hA && !maprom_enabled || / A[23:19] == 5'b11111 && maprom_enabled || // $F80000-FFFFFF A[23:19] == 5'b11100 && maprom_enabled; // $E00000-E7FFFF -always @(posedge CLKCPU or posedge AS_CPU_n) begin +always @(posedge CLKCPU) begin if (AS_CPU_n) begin DTACK_n <= 1'b1; diff --git a/rtl/main_top.v b/rtl/main_top.v index a1a0790..38b9db7 100644 --- a/rtl/main_top.v +++ b/rtl/main_top.v @@ -6,14 +6,16 @@ module main_top( input wire JP2, input wire JP3, // Flash ROM Kickstart overlay enable input wire JP4, - input wire pll_inst1_CLKOUT0, // 80 MHz from PLL (for turbo clock) - input wire pll_inst1_CLKOUT1, // 100 MHz from PLL + input wire pll_inst1_CLKOUT0, // 80 MHz from PLL (turbo clock source, /2 → 40 MHz) + input wire pll_inst1_CLKOUT1, // 100 MHz from PLL (C100M) input wire C7M_n, input wire RESET_n, input wire AS_CPU_n, input wire VPA_n, input wire [2:0] FC, - input wire DTACK_MB_n, + input wire DTACK_MB_n_IN, + output wire DTACK_MB_n_OUT, + output wire DTACK_MB_n_OE, input wire E_IN, input wire AS_MB_n_IN, @@ -67,7 +69,7 @@ module main_top( output wire FLASH_A19, output wire FLASH_WE_n, output wire FLASH_OE_n, - output wire INT2_n, + input wire INT2_n, output wire E_OE ); @@ -101,29 +103,55 @@ wire ram_access; wire flash_access; // SD card access detection -wire sdcard_access = !sd_configured_n && (A[23:16] == base_sd) && !AS_CPU_n; +wire sdcard_access = !sd_configured_n && (A[23:16] == base_sd) && !AS_CPU_n && bgack_sync2; + +// BGACK_n synchronization — raw BGACK_n glitches can corrupt the as_n mux +// and downstream DTACK logic. 2-stage C100M sync for all consumers. +reg bgack_sync1, bgack_sync2; +always @(posedge C100M) begin + bgack_sync1 <= BGACK_n; + bgack_sync2 <= bgack_sync1; +end + +// 2-stage CLKCPU re-sync of bgack_sync2 (C100M -> CLKCPU CDC). +reg bgack_cpu_s1, bgack_cpu; +always @(posedge CLKCPU) begin + bgack_cpu_s1 <= bgack_sync2; + bgack_cpu <= bgack_cpu_s1; +end -// Combined signals -wire as_n = BG_68SEC000_n ? AS_CPU_n : AS_MB_n_IN; +// Select AS source by bus ownership (BGACK), not bus grant (BG). +// BG can deassert while the DMA master still holds BGACK. +wire as_n = bgack_sync2 ? AS_CPU_n : AS_MB_n_IN; wire ds_n = LDS_n & UDS_n; // AS control - don't drive AS to motherboard when accessing local RAM, SD card, or Flash wire as_mobo_n = AS_CPU_n | ram_access | sdcard_access | flash_access; +//============================================================================= +// Turbo Mode - Tunable Timing Parameters +//============================================================================= +// These parameters control expansion bus timing in turbo mode. +// Adjust these values for hardware tuning without changing logic. + +localparam [3:0] PRECHARGE_TICKS = 4'd11; // Minimum gap between bus cycles (40MHz ticks, 275ns) +localparam [2:0] SETUP_TICKS = 3'd5; // Minimum address setup time (40MHz ticks, 125ns) +localparam [3:0] EXPANSION_DTACK_TICKS = 4'd12; // Consecutive C100M samples of DTACK LOW before asserting (120ns) + //============================================================================= // Turbo Mode - Clock Generation (40 MHz from 80 MHz PLL) //============================================================================= reg turbo_clk; always @ (posedge pll_inst1_CLKOUT0) begin - turbo_clk <= ~turbo_clk; // Divide by 2: 80 MHz → 40 MHz + turbo_clk <= ~turbo_clk; // 80 MHz → 40 MHz end //============================================================================= // CPU Speed Switch with Debouncing //============================================================================= -localparam BOOT_7M_LIMIT = 30'd300000000; // 3 seconds at 100 MHz +localparam BOOT_7M_LIMIT = 30'd500000000; // 5 seconds at 100 MHz reg [29:0] b_count; // boot on 7 MHz counter localparam DEBOUNCE_LIMIT = 21'd2000000; // 20 ms at 100 MHz @@ -174,32 +202,60 @@ always @(negedge RESET_n or posedge C100M) begin end //============================================================================= -// Motherboard Synchronization (1-Stage C7M + Async AS_CPU_n Reset) +// Motherboard Synchronization (C7M domain) //============================================================================= reg mobo_as_n = 1'b1; reg mobo_dtack_n = 1'b1; -always @(negedge RESET_n or posedge C7M or posedge AS_CPU_n) begin +// Safety counters (CLKCPU domain) +// p_cnt: precharge — enforces minimum gap between bus cycles +// s_cnt: setup — enforces minimum address valid time +reg [3:0] p_cnt; +reg [2:0] s_cnt; +reg last_as_cpu; - if (!RESET_n) begin +always @(posedge CLKCPU) begin + last_as_cpu <= AS_CPU_n; + + // Precharge Counter (Reset when AS High) + if (AS_CPU_n && !last_as_cpu) begin + p_cnt <= PRECHARGE_TICKS; + end else if (p_cnt != 0) begin + p_cnt <= p_cnt - 4'd1; + end + + // Address Setup Counter (Starts counting when AS_CPU_n goes Low) + // Address is valid when AS_CPU_n is Low. + if (AS_CPU_n) begin + s_cnt <= 3'd0; + end else begin + if (s_cnt <= SETUP_TICKS) begin + s_cnt <= s_cnt + 3'd1; + end + end +end + +wire safety_ok = (p_cnt == 0) && (s_cnt >= SETUP_TICKS); +// C7M-synchronous gate. Instant AS termination via combinatorial OR at AS_MB_n_OUT. +// mobo_dtack_n provides C7M-filtered DTACK for 7MHz bypass and turbo legacy access. +always @(posedge C7M or negedge RESET_n) begin + if (!RESET_n) begin + mobo_as_n <= 1'b1; + mobo_dtack_n <= 1'b1; + end else if (AS_CPU_n) begin mobo_as_n <= 1'b1; mobo_dtack_n <= 1'b1; - end else begin - - if (AS_CPU_n) begin - + if (cpu_speed_switch && !safety_ok) mobo_as_n <= 1'b1; - mobo_dtack_n <= 1'b1; - - end else begin - + else mobo_as_n <= as_mobo_n; - mobo_dtack_n <= DTACK_MB_n; - - end + if (!mobo_as_n) + mobo_dtack_n <= DTACK_MB_n_IN; + else + mobo_dtack_n <= 1'b1; end end @@ -210,13 +266,134 @@ end // Clock selection (hot-switchable) assign CLKCPU = cpu_speed_switch ? turbo_clk : C7M; -// DTACK from motherboard (synchronized when in turbo) -wire dtack_mobo_n = cpu_speed_switch ? mobo_dtack_n : DTACK_MB_n; -assign DTACK_CPU_n = dtack_mobo_n & m6800_dtack_n & ram_dtack_n & sdcard_dtack_n & flash_dtack_n; +// ---------------------------------------------------------------------------- +// Selective DTACK Timing +// ---------------------------------------------------------------------------- + +// Address decoding: legacy devices use C7M DTACK path + armed gate, +// expansion devices use C100M oversampled DTACK counter. +wire is_chip_ram = (A[23:21] == 3'b000); // 000000-1FFFFF +wire is_cia = (A[23:16] == 8'hBF); +wire is_custom = (A[23:16] == 8'hDF); +wire is_kickstart = (A[23:16] >= 8'hF0); // F00000-FFFFFF +wire is_autoconfig = (A[23:16] == 8'hE8); // Autoconfig Space (0xE8xxxx) + +wire is_legacy_access = is_chip_ram | is_cia | is_custom | is_kickstart | is_autoconfig; + +// turbo_as_gate: blocks AS/DTACK until address setup + precharge are met +wire turbo_as_gate = mobo_as_n | AS_CPU_n | (cpu_speed_switch & !safety_ok); + +// ---------------------------------------------------------------------------- +// Expansion DTACK — C100M Pause-on-Noise Counter +// ---------------------------------------------------------------------------- +// Requires DTACK_MB_n_IN to be sampled LOW for EXPANSION_DTACK_TICKS consecutive +// C100M cycles (120ns) before asserting mobo_dtack_n_fine. Noise glitches HIGH +// PAUSE the counter but do NOT reset it — genuine DTACK eventually accumulates. +// This gives far better noise immunity than a single C7M sample while keeping +// total latency bounded (~120ns counter + CLKCPU retime). +// +// Reset via turbo_as_gate synced to C100M. The counter only runs after AS is +// on the bus (safety_ok met, mobo_as_n LOW) — so stale DTACK from prior cycles +// or safety-countdown noise can't accumulate ticks prematurely. +reg turbo_gate_c100m_s1, turbo_gate_c100m_s2; +always @(posedge C100M) begin + turbo_gate_c100m_s1 <= turbo_as_gate; + turbo_gate_c100m_s2 <= turbo_gate_c100m_s1; +end + +reg [3:0] dtack_cnt; +reg mobo_dtack_n_fine; +reg dtack_s1, dtack_s2; + +always @(posedge C100M) begin + if (turbo_gate_c100m_s2) begin + dtack_cnt <= 4'd0; + mobo_dtack_n_fine <= 1'b1; + dtack_s1 <= 1'b1; + dtack_s2 <= 1'b1; + end else begin + dtack_s1 <= DTACK_MB_n_IN; + dtack_s2 <= dtack_s1; + // Counter: only increments when DTACK is actively LOW. + // When dtack_s2 goes HIGH (noise/glitch), counter PAUSES but + // does NOT reset. Prevents noise from restarting the count + // while still requiring genuine DTACK assertion. + if (!dtack_s2) begin + if (dtack_cnt < EXPANSION_DTACK_TICKS) begin + dtack_cnt <= dtack_cnt + 1'b1; + mobo_dtack_n_fine <= 1'b1; + end else begin + mobo_dtack_n_fine <= 1'b0; + end + end + end +end + +// CLKCPU CDC sync — mobo_dtack_n_fine transitions on C100M edges, CLKCPU is +// async to C100M. 2-stage sync for metastability protection. AS_CPU_n gate +// prevents stale values from leaking across cycle boundaries. +reg exp_dtack_sync1, exp_dtack_synced; +always @(posedge CLKCPU) begin + if (AS_CPU_n) begin + exp_dtack_sync1 <= 1'b1; + exp_dtack_synced <= 1'b1; + end else begin + exp_dtack_sync1 <= mobo_dtack_n_fine; + exp_dtack_synced <= exp_dtack_sync1; + end +end -// AS to motherboard (synchronized when in turbo) -assign AS_MB_n_OUT = cpu_speed_switch ? mobo_as_n : as_mobo_n; -assign AS_MB_n_OE = BG_68SEC000_n | !AS_CPU_n; // Keep driving until cycle completes +// CLKCPU-domain synchronizer: detect when physical DTACK has been released HIGH +// Only used for edge detection (noise-tolerant), not for the DTACK value itself. +reg legacy_dtack_sync1, legacy_dtack_sync2; +always @(posedge CLKCPU) begin + legacy_dtack_sync1 <= DTACK_MB_n_IN; + legacy_dtack_sync2 <= legacy_dtack_sync1; +end + +// Armed gate: latches HIGH once DTACK has been confirmed HIGH for 6 consecutive +// CLKCPU samples (after 2-stage sync). Requires ~320ns of genuine HIGH from +// physical pin — rejects noise/ringing that briefly crosses VIH. +// Resets when AS_CPU_n goes HIGH (between cycles). +reg [2:0] dtack_high_cnt; +reg legacy_dtack_armed; +always @(posedge CLKCPU) begin + if (AS_CPU_n) begin + dtack_high_cnt <= 3'd0; + legacy_dtack_armed <= 1'b0; + end else if (!legacy_dtack_armed) begin + if (legacy_dtack_sync2) begin + if (dtack_high_cnt == 3'd5) + legacy_dtack_armed <= 1'b1; + else + dtack_high_cnt <= dtack_high_cnt + 1'b1; + end else begin + dtack_high_cnt <= 3'd0; // reset on any LOW — must be consecutive + end + end +end + +// Turbo DTACK path selection: +// Legacy: mobo_dtack_n + m6800_dtack_n + turbo_as_gate + armed gate. +// Expansion: exp_dtack_synced + turbo_as_gate (C100M counter provides noise immunity). +// Armed gate is legacy-only — expansion devices respond before it can arm. +wire turbo_dtack_src = is_legacy_access ? + ((mobo_dtack_n & m6800_dtack_n) | turbo_as_gate | !legacy_dtack_armed) : + (exp_dtack_synced | turbo_as_gate); + +wire dtack_mobo_n = cpu_speed_switch ? turbo_dtack_src : DTACK_MB_n_IN; + +// In turbo mode, m6800_dtack_n is already gated inside turbo_dtack_src. +// In 7MHz mode, it must still be AND'd directly. +wire dtack_combined_n = dtack_mobo_n & (cpu_speed_switch ? 1'b1 : m6800_dtack_n) & ram_dtack_n & sdcard_dtack_n & flash_dtack_n; + +assign DTACK_CPU_n = dtack_combined_n; + +// AS to motherboard: turbo_as_gate in turbo mode (safety-gated), raw in 7MHz. +assign AS_MB_n_OUT = cpu_speed_switch ? turbo_as_gate : as_mobo_n; +// Tri-state AS during DMA (BGACK LOW). Uses bgack_sync2 — raw BGACK_n +// glitches can tri-state AS mid-cycle, hanging expansion cards. +assign AS_MB_n_OE = (BG_68SEC000_n | !AS_CPU_n) & bgack_sync2; // Data bus - autoconfig or SD card assign D_OUT = ac_data_oe ? {ac_data_out, 12'd0} : sd_data_out; @@ -315,7 +492,8 @@ autoconfig_zii autoconfig( fastram ramcontrol( .CLKCPU(CLKCPU), - .CPU_SPEED_SWITCH(cpu_speed_switch), + .BGACK_n(bgack_sync2), + .CPU_SPEED_SWITCH(cpu_speed_switch && bgack_sync2), // Force 7MHz mode during DMA (BGACK_n=0) .A(A[23:21]), .JP4(JP4), .RW_n(RW_n), @@ -356,7 +534,7 @@ sdcard sdcontrol( .MISO(SD_MISO), .CD_n(SD_CD_n), .DATA_OE(sd_data_oe), - .INT2_n(INT2_n), + .INT2_n(), // Directly active on bus — must not drive .SS_n(SD_SS_n), .SCLK(SD_SCLK), .MOSI(SD_MOSI), @@ -384,4 +562,17 @@ flash romoverlay( .DTACK_n(flash_dtack_n) ); +// ---------------------------------------------------------------------------- +// Bidirectional DTACK Drive (DMA to FastRAM) +// ---------------------------------------------------------------------------- +// Drive DTACK_MB_n LOW when a DMA master accesses FPGA fast RAM. +// Synchronous OE register + combinatorial AS gate for instant release. +reg dtack_oe_reg; +always @(posedge CLKCPU) begin + dtack_oe_reg <= (!bgack_cpu && ram_access && !ram_dtack_n); +end + +assign DTACK_MB_n_OUT = 1'b0; +assign DTACK_MB_n_OE = dtack_oe_reg & !as_n; + endmodule diff --git a/rtl/sdcard.v b/rtl/sdcard.v index e02ff91..0febd16 100644 --- a/rtl/sdcard.v +++ b/rtl/sdcard.v @@ -122,6 +122,7 @@ wire [15:0] int_act = int_req & int_ena; wire any_int_act = |int_act; +// INT2_n driver (Internal use only, not driving pin) assign INT2_n = !(any_int_act); always @(posedge C100M) begin