update plan after kernal module reseach

This commit is contained in:
Christian Werner 2026-06-18 10:32:31 +02:00
parent d7a9b94e0a
commit e5e51754b2

View File

@ -62,11 +62,14 @@ From the research, a pure listener has a radically reduced surface:
|-------|-------------------------|--------------|
| ATN | C64 drives; we react | **input** |
| CLK | Talker (C64) drives during data; we sample | **input** |
| DATA | Listener drives (ack ATN, ready, byte-ack, EOI-ack) | **output** (open-collector) |
| DATA | Listener drives the handshake (ack ATN, ready, byte-ack, EOI-ack); talker drives the bits we sample | **bidirectional** (open-drain) |
| RESET | C64 drives; we react | **input** |
| SRQ | unused on stock C64 | not connected |
So we drive **exactly one** bus line (DATA) and read three. No turnaround, no
So we **actively drive exactly one** bus line (DATA, open-drain) and sense the
rest — but DATA doubles as an *input* during bit sampling (we release it to Hi-Z,
then read the talker's bits), which is why it is a single bidirectional pin (§3).
No turnaround, no
EOI generation, no 60 µs talker timing. The only timing the device generates is
the 1 ms ATN ack and the per-byte ack — both forgiving. The tight constraint is
*sampling* the C64's ~20 µs bit clock while receiving (see §6).
@ -75,34 +78,50 @@ the 1 ms ATN ack and the per-byte ack — both forgiving. The tight constraint i
## 3. Hardware
### 3.1 Interface circuit (per the research, simplified for one output)
### 3.1 Interface circuit (single bidirectional DATA pin, sd2iec-style)
- **DATA (drive):** open-collector driver. Two equivalent options:
- **7406/74LS06** inverting OC buffer: Pi GPIO → 7406 input → bus. Pi HIGH ⇒ bus
pulled low (asserted). *(Software must invert.)*
- **NPN transistor** (2N3904/BC547): Pi GPIO → 1 kΩ → base; emitter → GND;
collector → DATA line. Pi HIGH ⇒ transistor on ⇒ bus low. Same inversion.
- Use whichever is on hand; the 7406 also gives spare drivers for later (CLK,
talker mode). Recommend the **7406** to match the eventual full device.
- **ATN, CLK, RESET (sense):** resistor divider 5 V→~3.1 V into each Pi input
(e.g. 3.3 kΩ top / 2.2 kΩ to GND). **No inversion** in sensing: bus low (0 V,
asserted) ⇒ Pi reads LOW.
- 100 nF bypass cap across the 7406 VCCGND. Common GND between C64 IEC pin 2 and Pi.
Design decision: DATA is a **single bidirectional pin** doing **open-drain
emulation**, mirroring the sd2iec one-line scheme rather than a separate
drive-pin + sense-pin pair. Because the Pi is **not 5 V-tolerant**, the line goes
through a **bidirectional MOSFET level shifter** (BSS138 + pull-ups — the NXP
AN10441 / common "logic level converter" topology). sd2iec needs no shifter only
because its AVR runs at 5 V; the 3.3 V Pi does.
- **DATA (single pin, bidirectional):** GPIO 18 ↔ level shifter ↔ IEC DATA. The Pi
pin is never a push-pull driver of the bus; it emulates open-drain:
- **Assert** (pull bus low): GPIO 18 = **output LOW** → shifter pulls bus to 0 V.
- **Release / read**: GPIO 18 = **input (Hi-Z)** → the bus pull-ups define the
level and GPIO 18 reads the bus. **Non-inverting:** bus low (asserted) ⇒ Pi
reads LOW. (Releasing to Hi-Z is what lets us sample the talker's bits in §6.)
- Direction is flipped in the hot path via the BCM `GPFSEL` register — a single
register write, ~tens of ns, negligible against the 20 µs bit window.
- **ATN, CLK, RESET (sense, input only):** route through the **same kind of
shifter** (remaining channels of a 4-channel BSS138 board). Pi side always input.
**Non-inverting:** bus low (asserted) ⇒ Pi reads LOW. *(A resistor divider also
works for these input-only lines, but reusing the shifter gives a uniform, fully
in-spec 3.3 V swing and sidesteps the marginal divider voltages flagged in the
kernel research.)*
- **Recommended part:** one 4-channel bidirectional level-shifter board carries all
four lines (DATA bidirectional; ATN/CLK/RESET input-only). No 7406, no transistor,
no hand-matched dividers — low side to Pi 3.3 V, high side to bus 5 V.
- 100 nF bypass cap on the shifter VCC pins. Common GND between C64 IEC pin 2 and Pi.
- **Do not connect bus 5 V to any Pi pin directly — Pi GPIO is not 5 V tolerant.**
### 3.2 Pin map (Pi Zero 2 W, 40-pin header)
| IEC signal | Pi GPIO (BCM) | Header pin | Direction | Logic note |
|------------|---------------|------------|-----------|------------|
| ATN | GPIO 2 | 3 | input (divider) | bus low = Pi low = asserted |
| CLK | GPIO 17 | 11 | input (divider) | bus low = Pi low = asserted |
| DATA (in) | GPIO 18 | 12 | output → 7406 → bus | Pi HIGH = bus asserted (inverted) |
| RESET | GPIO 3 | 5 | input (divider) | bus low = Pi low = asserted |
| GND | — | 6 (or any) | — | tie to IEC pin 2 |
| ATN | GPIO 2 | 3 | input (via shifter) | bus low = Pi low = asserted |
| CLK | GPIO 17 | 11 | input (via shifter) | bus low = Pi low = asserted |
| DATA | GPIO 18 | 12 | **bidirectional (via shifter)** | open-drain: assert = output LOW, release/read = input. bus low = Pi low = asserted |
| RESET | GPIO 3 | 5 | input (via shifter) | bus low = Pi low = asserted |
| GND | — | 6 (or any) | — | tie to IEC pin 2 and shifter GND |
> Convention used in code: a helper layer converts electrical reads/writes into
> **logical** `asserted=True / released=False` so the rest of the code reasons in
> the protocol's true/false sense and never juggles the inversions inline.
> Convention used in code: a helper layer (`iec_lines.h`) converts electrical
> reads/writes into **logical** `asserted=True / released=False`. With the
> non-inverting level shifter the mapping is direct — assert ⇒ DATA GPIO output
> LOW, release ⇒ DATA GPIO input (Hi-Z), and a Pi LOW read on any line means
> asserted. No 7406 inversion to juggle.
---
@ -205,9 +224,11 @@ never juggles inversions inline (mirrors the original userspace plan, now in C):
- `atn_asserted()` — true when ATN GPIO reads low
- `clk_asserted()` — true when CLK GPIO reads low
- `data_in()` — read DATA line (the talker drives it during bit transfer)
- `data_assert()` / `data_release()` — drive DATA true / float it; encapsulates
the 7406 inversion (assert ⇒ Pi GPIO HIGH)
- `data_in()` — read DATA line (the talker drives it during bit transfer; only
valid after `data_release()` has set the pin to Hi-Z input)
- `data_assert()` / `data_release()` — assert DATA (set GPIO 18 **output LOW**) /
release it (set GPIO 18 **input / Hi-Z**, letting the bus float high). Open-drain
emulation through the level shifter; **non-inverting** (no 7406 to invert)
- `reset_asserted()` — true when RESET GPIO reads low
### 5.2 Userspace record stream (`device.py`)
@ -416,7 +437,7 @@ Requirements:
| Even in-kernel, busy-poll mid-byte gets preempted/IRQ'd and garbles a bit | Medium | Disable local IRQs/preemption for the duration of a byte; if still marginal, isolcpus + steer other IRQs away (raspbiec pattern); last resort `PREEMPT_RT`. Quantify in Phase 0.5. |
| Keeping IRQs off too long for a byte harms system stability | Medium | Bound the off-window to one byte (~few hundred µs); re-enable between bytes. A design constraint to validate in Phase 0.5. |
| Kernel module build/ABI churn against Raspberry Pi OS kernel | Medium | Build against installed kernel headers; pin kernel version; document in `kernel-notes.md`. |
| 7406 inversion / divider miswire | Medium | `iec_lines.h` logical layer isolates it; module `selftest` ioctl toggles DATA and reads ATN/CLK with the C64 off. |
| Level-shifter wiring / DATA direction-flip bug | Medium | `iec_lines.h` logical layer isolates assert/release/read; module `selftest` ioctl drives DATA (output LOW), releases to Hi-Z and reads it back, and reads ATN/CLK with the C64 off. Confirm Hi-Z release truly floats so the talker's bits read through. |
| C64 has JiffyDOS | Low (printer rarely uses it) | Out of scope; document. Standard protocol still works for a printer device. |
| ATN response missed | Low | Hardware IRQ pulls DATA in the ISR; 1 ms budget is ample for kernel interrupt latency. |