Skip to content

Host Bus Timing

visrealm edited this page Aug 25, 2026 · 4 revisions

This page documents what the PICO9918 actually does on the TMS9918A host bus, cycle by cycle. Every figure here is derived from the PIO programs in src/tms9918.pio, measured against the CPU-VDP interface timing in the TMS9918A datasheet (Figures 5-3 and 5-4). Read the note below before relying on a number.

Important

The cycle-by-cycle figures on this page do not match any released firmware. They were derived from a development state that predates the v1.2.0 release. Every public release from v1.2.0 (the newest one) onward differs in three ways:

  • The PIO clock is not pinned. This page assumes a fixed 252 MHz PIO rate on every clock preset. v1.2.0 sets clkdiv = 1.0 on both bus state machines (src/main.c:741,760; the same call is at src/tms_bus.c:223,237 on the development branch), so the PIO runs at the full system clock and every nanosecond figure below scales with the selected preset.
  • The read program samples MODE before driving the bus, not after. In src/tms9918.pio the nop [7] settling delay and in pins, 1 both come ahead of out pindirs, 8, so the real order is settling check -> MODE -> pindirs -> data byte. The Read cycle table below has the opposite order, and the paragraph above it explains at length why the opposite order was necessary.
  • The write program delays its MODE sample by 8 PIO cycles (wait 0 gpio CSW_PIN [7]), where this page assumes MODE is captured immediately on the strobe edge.

Nothing structural is wrong. The signal path, the settle-then-commit glitch rejection, the read-ahead FIFO word and the reason MODE and write data are sampled at opposite ends of a write all still hold. But the numbers want re-deriving, and pico9918-read-cycle.svg encodes the superseded instruction order.

It is a reference for two situations: diagnosing a host that misbehaves, and understanding why the interface is built the way it is. If you are looking for the firmware's overall structure instead, see Firmware Architecture.

The PIO clock follows the system clock

Both host interface state machines run on pio1 with clkdiv = 1.0 - that is, at the full system clock, whatever preset is selected. There is one timing profile per preset, not one overall:

Preset System clock PIO cycle
VGA 0 (default) 252 MHz 3.968 ns
VGA 1 302.4 MHz 3.307 ns
VGA 2 352 MHz 2.841 ns
SCART 0 (default) 270 MHz 3.704 ns
SCART 1 and 2 324 MHz 3.086 ns

Every [n] delay in the PIO programs is a cycle count, so the settling windows and guard bands below shrink by 28% between the slowest and the fastest preset. Read the tables that follow as cycle counts first and nanoseconds second, and scale the nanosecond column by the row for your preset.

Note

The nanosecond figures throughout the rest of this page are the 252 MHz column, and they also assume the pre-v1.2.0 instruction order described in the banner above. Treat them as the shape of the timing rather than as measurements.

Signal path

MODE, /CSW and /CSR all reach the MCU through U6, but not by the same route.

Signal path from the host socket through the buffers to the MCU

/CSR is deliberately jumpered back through four of U6's channels instead of one. The read cycle specifies tsu(A-RL) = 0 ns, meaning MODE is only guaranteed valid at the falling edge with no setup margin at all. The extra passes manufacture that margin in hardware: by the time the MCU sees /CSR_L fall, MODE_L has been stable at the pin for 11-20 ns.

MODE and /CSW take a single pass each, so their buffer delay is common mode and cancels between them. That is what allows the write path to sample MODE right at the /CSW edge.

The same delayed /CSR_L also drives U5's DIR pin, which has a consequence for the read cycle below.

Read cycle

Read cycle timing, from the host CSR edge to valid data on the MCU pins

U5's direction pin is wired straight to /CSR_L, so the transceiver turns toward the host on the strobe edge whether or not the MCU is ready. The PIO therefore drives CD0-7 as soon as it recognises the strobe, before the settling check rather than after it. Driving later would leave U5 forwarding an MCU-side input that nothing was driving.

Event After /CSR_L Cycles
GPIO synchroniser 8-12 ns 2-3
Poll loop detects the strobe +0-12 ns 0-3
pindirs set to outputs 16-32 ns 4-8
Data byte on the bus 20-36 ns 5-9
Settling check 56-71 ns 14-18
MODE sampled 60-75 ns 15-19
Status byte on the bus 71-87 ns 18-22

End to end, a data read takes 44-86 ns from the host's /CSR edge to a valid byte back at the socket, adding U6 (11-20 ns) and U5 (13-30 ns). On v1.3 boards add U4, a further 4-7 ns. A Z80 at 4 MHz allows roughly 375 ns, so the margin is comfortable.

Status reads show the data byte first

The read program cannot know which byte the host wants until it has sampled MODE, and it drives before that. So on a status read the data byte appears at 20-36 ns and is replaced by the status byte at 71-87 ns.

Against any real host's sampling point this is invisible, but it is worth knowing when probing the bus with an analyser: a status read genuinely does show two different bytes during the cycle.

Write cycle

Write cycle timing, showing MODE sampled at the falling edge and data captured before the rising edge

The write cycle is the mirror image of the read, and the two halves are sampled at opposite ends on purpose:

  • MODE is only guaranteed for th(WL-A) = 30 ns after /CSW falls. It is sampled about 4 ns in, leaving 26 ns of margin. The GPIO synchroniser delay cancels between the wait and the read, because both observe the same synchronised signal.
  • Write data is only guaranteed for tsu(D-WH) = 100 ns before /CSW rises. It is captured 4-16 ns before the rise, from the poll loop's last sample.

Sampling either one at the other's moment would be a spec violation, which is why the program captures MODE once at the start and then polls the bus continuously until the strobe releases.

Event Timing Budget
MODE sampled ~4 ns after the fall th(WL-A) = 30 ns
Entry settling check 24 ns after the fall tw(WL) = 200 ns
Data captured 4-16 ns before the rise tsu(D-WH) = 100 ns
Exit settling check 32 ns after the rise th(WH-D) = 30 ns
Word reaches the FIFO ~48 ns after the rise tw(CS-H2) = 2 us

The exit check sits past the 30 ns data hold time, which looks wrong at first glance. It is safe because the value pushed was latched before the strobe rose; nothing is read from the bus after it.

Glitch rejection

Both strobes are checked for settling before the access is allowed to proceed.

A short glitch on CSW is discarded while a real cycle proceeds

On the write path this matters most. Without it, a few nanoseconds of ringing on /CSW is enough to start a cycle, and the result is a phantom write: a VRAM byte, a register write, or a desynchronised address flip-flop, depending on what MODE and the data bus happened to hold.

The check is placed after MODE and the data are sampled, not before. Sampling first costs nothing against th(WL-A), and the cycle can still be discarded afterwards if the strobe turns out not to have settled.

The window is 24 ns, about 12% of the 200 ns a real write cycle is specified to hold /CSW low. A genuine cycle from any supported host clears it by an order of magnitude.

On the read path the equivalent check happens after the bus has already been driven, so a short pulse still gets valid data. What the check decides there is whether to continue to the MODE sample and complete the transaction.

Notes

  • All nanosecond figures are the 252 MHz column. They do move with the configured system clock - multiply the cycle counts by that preset's PIO cycle from the table above.
  • Buffer delays are taken from the 74LVC245A and 74HC245 datasheets and vary with temperature and supply voltage; the ranges given are typical, not worst case.
  • tw(CS-H1) = 8 us is the datasheet's minimum interval between accesses. Fast hosts violate it routinely, a 4 MHz Z80 running OTIR manages about 5.25 us, and the original part drops or corrupts accesses under that condition. The PICO9918 does not.
  • The board revisions differ in the data path only. v1.3 has two buffer hops (U5 and U4), v2.0 has one (U5). MODE and strobe timing is identical on both.

Related

Clone this wiki locally