Some claudage produced me this spec for the current FPGA address glue firmware. It can be handy for this, as well as "Can you add all the comments I was too lazy to add to this assembler?"
What amuses me.... with async 74HCT combination logic you can assert DTACK ... shit I could tie it to ground and it would work. The only reason it got more complicated is because of IO and future bus experiments require I have wait states at all.
So it got to "a few too many gates" to not make a mistake and I added an FPGA. The FPGA took one look at my async combinational DTACK logic... worked perfecty in simulation. Worked most of the time on a protoboard test and oscillated insanely on a PCB...
I added double clk synchronisers to the signals.... and guess what. Now the timing on the ROM is marginal.
Did the FPGA just make things "slower"? Thats not meant to be what it does, right? Bizarre, bizarre. Slower, but far more deterministic, literal and precise about the correct order of things. Which should do me well later.
Eventually I'll put the git repos on the public gitlab where you migtht be able to see this with 2020s era auto formating UI.
address_glue — Firmware Datasheet
68000 async-bus glue logic: decodes CPU address/control lines into ROM/RAM chip selects and OE/WE, and generates correctly-delayed DTACK.
Target: ICE5LP4K-SG48ITR (iCE40 LP4K, SG48), custom PCB. Clock: internal SB_HFOSC, undivided → 48MHz ±10%. All internal logic is synchronous to this clock; the 68000 bus is fully asynchronous to it and is double-flop synchronized on entry (2 clk latency) before decode. CPU bus: 68000, 8MHz.
Pinout (address_glue.pcf)
Signal Pin Dir Active Notes
in_ctl[0] nUDS 44 in low upper data strobe
in_ctl[1] nLDS 42 in low lower data strobe
in_ctl[2] RnW 45 in high=read
in_ctl[3] nAS 43 in low address strobe
in_addr[3:0] 46,47,48,2 in — top 4 address bits (region select)
out_bus[0] ROM_L_nCE 34 out low
out_bus[1] ROM_H_nCE 37 out low
out_bus[2] RAM_L_nCE 35 out low
out_bus[3] RAM_H_nCE 38 out low
out_bus[4] MEM_nOE 32 out low shared ROM/RAM output enable
out_bus[5] MEM_nWE 36 out low shared ROM/RAM write enable — ROM side wired but must never be driven; ROM is read-only by design, not by decode
nDTACK 12 out low to CPU
IO_DTACK 18 in low=trigger external DTACK request, ORed in raw — bypasses the wait counters below entirely; anything driving it must already be correctly timed
led_red/green/blue 39/41/40 out low=on status RGB, see below
out_bus/in_ctl must stay declared [5:0]/[3:0] (not [0:5]) in every module along the chain — a width-matched but bit-order mismatch silently cross-wires every pin (see git history: this hit real hardware).
Address decode
ROM selected: in_addr[3:0] == 4'h0 && nAS==0
RAM selected: in_addr[3:0] == 4'h1 && nAS==0
Either qualified further by nUDS==0 || nLDS==0 (a strobe must be present) before chip-select/OE/WE or the DTACK counters below start.
DTACK timing
Each region has its own saturating wait counter (ROM_DTACK_DELAY, RAM_DTACK_DELAY), started when that region's chip-select/OE go active and sized against real device timing (ROM ~70ns, tRC-decoupled/not trustworthy to the ns; SRAM 55ns) plus the 68000's fixed DTACK sample point (S4/S5 edge, 1.5 CPU clocks after AS):
Region *_DTACK_DELAY CE/OE→DTACK Reads cost Writes/RMW (incl. TAS) cost
RAM 2 ~72ns 0 wait states 1 wait state
ROM 6 ~156ns 1 wait state 2 wait states
Writes/RMW cost one extra wait state versus reads on the same region: the 68000 delays UDS/LDS by a full CPU clock (125ns) on those cycles, and since our counters key off the strobes (not AS), that CPU-side delay and the internal margin shift together in lockstep — reads/writes on a region always differ by exactly one wait-state tier, with matching margin. TAS's write phase is the known-fragile case on real 68000 silicon — retest specifically after any change here.
DTACK is never asserted early/optimistically: it only fires once the counter reaches threshold, never on the same edge chip-select goes active.
Status LED (status[2:0] → RGB, negative-logic drive)
Bit LED Meaning
status[0] blue MEM_DTACK asserted
status[1] green AS asserted (cycle in progress)
status[2] red write cycle (~RnW)
Build/test
make test — iverilog/vvp testbench (address_glue_tb.v)
make all — yosys/nextpnr-ice40/icepack → address_glue.bin
make prog — flash via DIY STM32 SPI programmer (no onboard FTDI/JTAG)