rigless 0.2.0

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model and unit of virtual time in Rust, and a CPU core of its own that translates the firmware's instructions to host code
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! What joins a [`Bus`] to the models that give its peripherals behavior.
//!
//! A [`Bus`] knows where an address lands and what that register holds. It owns no models. An MCU
//! owns them as fields of its own type, like `mcu.clocks` and `mcu.serial`, and implements [`Mcu`]
//! to say which field answers for which slot. That one `match` is the whole of the mapping, and it
//! is why nothing after construction reaches a model by name or downcasts to its type.
//!
//! An SoC module writes its fields, its dispatch and what its part does at a reset and at a stop,
//! the five methods of [`Mcu`]. Everything the runtime does with an MCU is [`Operate`]:
//! [`Operate::read`], [`Operate::write`] and [`Operate::advance_to`] are written once against
//! [`Mcu::split`], and every MCU has them, so a part can't change them and a wrapper can't lose them.
//!
//! A board adds a chip to a part with [`Wired`], which puts it in a [`Window`] of the part's
//! addresses. It adds memory with [`Bus::add_region`].

use crate::chip::parallel;
use crate::device::{
    ClockChange, DeviceContext, ErasedDevice, InterruptChange, LineChange, PendingAccess,
    RegisterFile, RequestChange, ResetCause, Told,
};
use crate::emulator::{AccessKind, Error as EmulatorError, Memory};
use crate::snapshot::{Reader, Store, Writer};
use crate::soc::{
    Bus, Cpu, Error, Fault, NO_REGISTERS, RegisterSpec, SAME_INSTANT_LIMIT, SETTLE_LIMIT, Width,
    Window,
};
use crate::time::{EventId, VirtualNanos};

/// A requested access the bus refused.
///
/// A model is answered only when its access lands, so a refusal is otherwise silent: the controller waits
/// for a word that never arrives. A run collects these the way it collects the addresses a firmware reached
/// and nothing answered.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Refused {
    /// The instance that asked.
    pub by: &'static str,
    /// The address it asked for.
    pub address: u64,
    /// Why the bus refused.
    pub fault: Fault,
}

/// One whole microcontroller: a bus, and a model for each of its slots that has one.
///
/// Every method is required, and they're all an SoC module writes by hand. [`Mcu::split`] borrows
/// the bus and one model apart, which is what lets a model be called with its own register file.
/// [`Mcu::reset`] and [`Mcu::poll`] are the part's own: a part that keeps what survives a reset or
/// polls only the models that take outside input says so there, and one that doesn't calls
/// [`Operate::reset_every_model`] and [`Operate::poll_every_model`]. A type that wraps a part
/// forwards all five, since none has a default to fall back on, and [`Wired`] does it for a board
/// that adds a chip.
pub trait Mcu {
    /// The bus, for reading the map and what the registers hold.
    fn bus(&self) -> &Bus;

    /// The bus, mutably. `split` gives it alongside a model, and this gives it alone.
    fn bus_mut(&mut self) -> &mut Bus;

    /// A system reset at `at`: the part as its firmware finds it at the reset vector, every register
    /// at its reset value and nothing in flight, with whatever a board put on its pins still there.
    /// A chip a controller carries through it hears so at `at`, through its seam's `restarted`,
    /// since every event it scheduled went with the reset. `cause` is why,
    /// for the part's own reset flags. A bus with no such flags ignores it. When a second cause joins a
    /// restart, like the core's own request at the stop a model asked at, it's called again with that
    /// cause, so the flags gain both.
    ///
    /// [`Operate::reset_every_model`] resets the bus, every register and nothing scheduled, driven or
    /// waiting for an answer, and tells every model through
    /// [`Device::reset`](crate::device::Device::reset), so each puts back what a reset clears in
    /// it. A part can instead rebuild itself around what's on its pins, like the parts in this crate
    /// do. A part that rebuilds keeps the regions and windows a board added to its bus by calling
    /// [`Bus::keep_added`].
    fn reset(&mut self, at: VirtualNanos, cause: ResetCause);

    /// Gives the models a look outside at `now`, which the session does at every stop of the
    /// target. See [`Device::poll`](crate::device::Device::poll). [`Operate::poll_every_model`]
    /// asks every one.
    fn poll(&mut self, now: VirtualNanos);

    /// The bus, and the model that answers for `slot` if this MCU has one, borrowed apart.
    ///
    /// The implementation is one `match` from the slot to a field. A board can add slots after the
    /// part's own with [`Bus::add_window`], so a slot the part doesn't know answers `None`:
    ///
    /// ```ignore
    /// fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
    ///     let Self { bus, clocks, serial, wiring, .. } = self;
    ///     let model: Option<&mut dyn ErasedDevice> = match wiring.get(slot) {
    ///         Some(Field::Clocks) => Some(clocks),
    ///         Some(Field::Serial) => Some(serial),
    ///         Some(Field::None) | None => None,
    ///     };
    ///     (bus, model)
    /// }
    /// ```
    fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>);
}

/// What the runtime does with an MCU: serve an access, move it on in time, settle what its models
/// asked for, and take what they drove. Every [`Mcu`] has it, written once against
/// [`Mcu::split`], and no part changes it. Bring it into scope with `use rigless::mcu::Operate`.
pub trait Operate: Mcu {
    /// [`Mcu::reset`] as a part with nothing of its own to keep does it: the bus, every register
    /// and nothing scheduled, driven or waiting for an answer, and every model told through
    /// [`Device::reset`](crate::device::Device::reset).
    fn reset_every_model(&mut self) {
        self.bus_mut().reset();
        for slot in 0..self.bus().slots().len() {
            if let (_, Some(model)) = self.split(slot) {
                model.reset();
            }
        }
    }

    /// [`Mcu::poll`] as a part that asks every model does it.
    fn poll_every_model(&mut self, now: VirtualNanos) {
        for slot in 0..self.bus().slots().len() {
            let (bus, model) = self.split(slot);
            let Some(model) = model else {
                continue;
            };
            let (carried, mut context) = bus.call(now, slot);
            model.poll(carried.registers_mut(), &mut context);
        }
    }

    /// That every model is wired to a slot carrying the layout it was written for.
    ///
    /// The one mistake a hand-written dispatch can make is pointing a slot at the wrong field, and it
    /// is a mistake the type system cannot catch, because every model erases to the same trait object.
    /// This asks `split` itself what it hands over and compares that model's own layout to the slot's,
    /// so the answer comes from the dispatch under test rather than from a second table beside it.
    /// It also refuses a description that routes interrupts to an instance with no model, which
    /// would take every interrupt and give the core none.
    fn check(&mut self) -> Result<(), Error> {
        for index in 0..self.bus().slots().len() {
            let (bus, model) = self.split(index);
            let Some(model) = model else {
                continue;
            };
            let carried = bus.slots()[index].peripheral();
            if !std::ptr::eq(model.layout(), carried.registers) {
                return Err(Error::LayoutMismatch {
                    instance: carried.name,
                });
            }
        }
        // A router with no model would take every interrupt and give the core none.
        if let Some(router) = self.bus().router()
            && let (bus, None) = self.split(router)
        {
            return Err(Error::UnheardRouter {
                instance: bus.slots()[router].peripheral().name,
            });
        }
        Ok(())
    }

    /// The processor core this MCU carries.
    fn cpu(&self) -> Cpu {
        self.bus().cpu()
    }

    /// The instant of the earliest pending event, which is when the emulator next has to stop.
    fn next_event(&self) -> Option<VirtualNanos> {
        self.bus().next_event()
    }

    /// A read from the firmware of `width` bits at `address`, which may be part of a wider register.
    ///
    /// The model answering for that slot sees it first. What it doesn't answer, the register file
    /// does. In a [`Window`](crate::soc::Window), the chip in it answers. A window with no chip is
    /// unmapped.
    fn read(&mut self, at: VirtualNanos, address: u64, width: Width) -> Result<u64, Fault> {
        let located = self.bus().locate(address, width, AccessKind::Read)?;
        let (bus, model) = self.split(located.slot);
        let (slot, mut context) = bus.call(at, located.slot);
        if let Some(offset) = located.window {
            let chip = model.ok_or(Fault::Unmapped { address })?;
            return Ok(chip.read_window(offset, width, &mut context) & width.mask());
        }
        let answered =
            model.and_then(|model| model.read(located.row, slot.registers_mut(), &mut context));
        let value = answered.unwrap_or_else(|| slot.registers_mut().bus_read(located.row));
        Ok((value >> located.lane.shift) & width.mask())
    }

    /// What [`Operate::read`] would answer at `at`, with nothing changed and no model told, whatever
    /// the instance's gate says. That's a debugger's read. The model answers through
    /// [`Device::peek`](crate::device::Device::peek), and what it doesn't answer, the register file
    /// does. Advance the part to `at` first, as the runtime does before a debugger looks, since a
    /// peek doesn't catch up on what's due. It takes `&mut` only because [`Mcu::split`] does.
    fn peek(&mut self, at: VirtualNanos, address: u64, width: Width) -> Result<u64, Fault> {
        let located = self
            .bus()
            .locate_ungated(address, width, AccessKind::Read)?;
        let (bus, model) = self.split(located.slot);
        if let Some(offset) = located.window {
            let chip = model.ok_or(Fault::Unmapped { address })?;
            return chip
                .peek_window(offset, width, at)
                .map(|value| value & width.mask())
                .ok_or(Fault::Unmapped { address });
        }
        let registers = bus.slots()[located.slot].registers();
        let value = model
            .and_then(|model| model.peek(located.row, registers, at))
            .unwrap_or_else(|| registers.bus_peek(located.row));
        Ok((value >> located.lane.shift) & width.mask())
    }

    /// A write over the bus at `at`: the register file applies the rules, then the model sees what landed.
    fn write(
        &mut self,
        at: VirtualNanos,
        address: u64,
        width: Width,
        value: u64,
    ) -> Result<(), Fault> {
        let located = self.bus().locate(address, width, AccessKind::Write)?;
        let (bus, model) = self.split(located.slot);
        let (slot, mut context) = bus.call(at, located.slot);
        if let Some(offset) = located.window {
            let chip = model.ok_or(Fault::Unmapped { address })?;
            chip.write_window(offset, width, value & width.mask(), &mut context);
            return Ok(());
        }
        let register = &slot.peripheral().registers[located.row];
        // A write-only register stores nothing, so the lanes a narrow write leaves alone carry no action.
        let neutral = if register.access.is_readable() {
            let registers = slot.registers_mut();
            registers
                .write_rule(located.row)
                .neutral(registers.get_row(located.row))
        } else {
            0
        };
        let untouched = neutral & !(width.mask() << located.lane.shift);
        let written = untouched | ((value & width.mask()) << located.lane.shift);
        slot.registers_mut().bus_write(located.row, written);
        if let Some(model) = model {
            model.write(located.row, written, slot.registers_mut(), &mut context);
        }
        Ok(())
    }

    /// Fires every event due by `now`, earliest first, each to the model that scheduled it. An
    /// event scheduled meanwhile at `now` fires in the same pass.
    ///
    /// What the events asked for beyond their own registers is carried out by the next
    /// [`Operate::settle`], each at the instant of the event that asked. A caller stepping across
    /// several instants at once therefore settles them all after the last of them. A session steps
    /// to its next event, so for it one call is one instant.
    fn advance_to(&mut self, now: VirtualNanos) {
        // Each event runs at its own instant, not at the one being advanced to: a model that schedules a
        // second event from the first would otherwise anchor it to however far the caller happened to step.
        //
        // A model that asks for an instant already gone gets this one instead, and would then be called
        // again in this same pass, forever. That is a model bug rather than a state a run should reach, so
        // it is caught here where it can be named rather than left to hang.
        let mut fired_here = 0_u32;
        let mut last = None;
        while let Some((event, owner, at)) = self.bus_mut().pop_due(now) {
            if last == Some(at) {
                fired_here += 1;
                assert!(
                    fired_here <= SAME_INSTANT_LIMIT,
                    "{} scheduled {fired_here} events at {at:?} without advancing the clock",
                    self.bus().slots()[owner].peripheral().name
                );
            } else {
                last = Some(at);
                fired_here = 0;
            }
            let (bus, model) = self.split(owner);
            let (slot, mut context) = bus.call(at, owner);
            if let Some(model) = model {
                model.on_event(event, slot.registers_mut(), &mut context);
            }
        }
    }

    /// Carries out what the models asked for during an access or an event, until nothing is left
    /// or a model asks for a reset, and what the reset would discard is left undone.
    ///
    /// Five things reach this, and in this order. A clock one instance drives, which the instances
    /// the description runs on that output are told about. A request one instance drives, which
    /// every sink the description named is told about. An interrupt one instance sets, which the
    /// controller the description named to route them is told about, and which waits for
    /// [`Operate::drain_interrupts`] on a part with none. An answer to an access a model asked for,
    /// which goes back to the model that asked. And an access itself, which goes to a peripheral
    /// through [`Operate::read`] and [`Operate::write`] or to `memory` when it lies wholly inside one
    /// region. Each may produce more of any of them, so it repeats. A DMA controller moving a block
    /// between two memories does the whole block here.
    ///
    /// The order is what makes a request line mean what it means. An access a model makes may
    /// change a level, and a model handed its answer first would read the level as it stood before
    /// its own access. The hardware releases a request when whatever it asked reaches it, and that
    /// reader sees the release.
    ///
    /// Each effect happens at the instant of the access or the event that set it off, whatever instant
    /// the run has reached by the time it is served: a settle is the part reaching its next steady state
    /// and not time passing, so an event fired at one instant cannot have its consequences stamped with
    /// another's. An access the bus refuses is reported rather than answered, so a model waiting on one
    /// is told so and decides for itself what it means.
    ///
    /// # Panics
    ///
    /// Panics when the models have not run out of work after a bounded number of steps, which is a model
    /// asking for an access on every answer and never finishing. The bound is twice the largest transfer
    /// a part can legitimately ask for in one settle.
    fn settle(&mut self, memory: &mut dyn Memory) -> Result<Vec<Refused>, EmulatorError> {
        let mut refused = Vec::new();
        for _ in 0..SETTLE_LIMIT {
            if self.bus().reset_requested() {
                return Ok(refused);
            }
            if let Some((at, change)) = self.bus_mut().next_clock_change() {
                let readers: Vec<usize> = self.bus().readers_of(change).collect();
                for reader in readers {
                    let (bus, model) = self.split(reader);
                    let (slot, mut context) = bus.call(at, reader);
                    if let Some(model) = model {
                        model.on_clock(change.hz, slot.registers_mut(), &mut context);
                    }
                }
                continue;
            }
            if let Some((at, change)) = self.bus_mut().next_request_change() {
                for index in 0..self.bus().sinks().len() {
                    let sink = self.bus().sinks()[index];
                    let (bus, model) = self.split(sink);
                    let (slot, mut context) = bus.call(at, sink);
                    if let Some(model) = model {
                        model.on_request(change, slot.registers_mut(), &mut context);
                    }
                }
                continue;
            }
            if let Some(router) = self.bus().router()
                && let Some((at, change)) = self.bus_mut().next_interrupt_change()
            {
                let (bus, model) = self.split(router);
                let (slot, mut context) = bus.call(at, router);
                if let Some(model) = model {
                    model.on_interrupt(change, slot.registers_mut(), &mut context);
                }
                continue;
            }
            if let Some(answer) = self.bus_mut().next_answer() {
                let (bus, model) = self.split(answer.owner);
                let (slot, mut context) = bus.call(answer.at, answer.owner);
                if let Some(model) = model {
                    let registers = slot.registers_mut();
                    if let Some(value) = answer.found {
                        model.on_access(answer.token, value, registers, &mut context);
                    } else {
                        model.on_refused(answer.token, registers, &mut context);
                    }
                }
                continue;
            }
            let Some(access) = self.bus_mut().next_pending() else {
                return Ok(refused);
            };
            match perform(self, access, memory)? {
                Ok(value) => self.bus_mut().answer(&access, Some(value)),
                Err(fault) => {
                    refused.push(Refused {
                        by: self.bus().slots()[access.owner].peripheral().name,
                        address: access.address,
                        fault,
                    });
                    self.bus_mut().answer(&access, None);
                }
            }
        }
        panic!("the models took {SETTLE_LIMIT} steps without settling");
    }

    /// Takes the interrupt levels the models set since the last drain, in the order they set them.
    /// On a part whose description names an instance to route them, [`Operate::settle`] has handed
    /// that instance every one already, and this answers none.
    fn drain_interrupts(&mut self) -> Vec<InterruptChange> {
        self.bus_mut().drain_interrupts().collect()
    }

    /// Takes the levels the part's interrupt controller drove onto the core's lines since the last
    /// drain, in the order it drove them. A part with no controller drives none here, and the run
    /// drives the line of each interrupt's own number from [`Operate::drain_interrupts`].
    fn drain_lines(&mut self) -> Vec<LineChange> {
        self.bus_mut().drain_lines().collect()
    }

    /// Takes the request levels the models set since the last drain, in the order they set them.
    ///
    /// [`Operate::settle`] is what normally takes them, and gives each to every sink the description named. A
    /// test putting one model on a bus of its own has no sink, so this is how it reads what the model
    /// drove.
    fn drain_requests(&mut self) -> Vec<RequestChange> {
        self.bus_mut().drain_requests().collect()
    }

    /// Takes the clock rates the models drove since the last drain, in the order they drove them.
    /// It's what [`Operate::drain_requests`] is for a request, for a test reading what a clock tree
    /// drove.
    fn drain_clocks(&mut self) -> Vec<ClockChange> {
        self.bus_mut().drain_clocks().collect()
    }

    /// Takes the reset a model asked for during the last access or stop, if one did.
    fn take_reset_request(&mut self) -> Option<ResetCause> {
        self.bus_mut().take_reset_request()
    }

    /// Takes what the models told since the last drain, in the order they told it.
    fn drain_told(&mut self) -> Vec<Told> {
        self.bus_mut().drain_told().collect()
    }

    /// What one instance holds, for a test reading a register back.
    fn registers(&self, instance: &str) -> Option<&RegisterFile> {
        self.bus().registers(instance)
    }
}

impl<M: Mcu + ?Sized> Operate for M {}

/// One access a model asked for: memory where the whole access lies inside one region, and the
/// peripheral bus everywhere else. A write answers zero, which is what its model is told.
fn perform<M: Operate + ?Sized>(
    mcu: &mut M,
    access: PendingAccess,
    memory: &mut dyn Memory,
) -> Result<Result<u64, Fault>, EmulatorError> {
    let PendingAccess {
        at,
        kind,
        address,
        width,
        value,
        ..
    } = access;
    if mcu.bus().region_holding(address, width).is_some() {
        return match kind {
            AccessKind::Read => memory.read(address, width).map(Ok),
            AccessKind::Write => memory.write(address, width, value).map(|()| Ok(0)),
        };
    }
    Ok(match kind {
        AccessKind::Read => mcu.read(at, address, width),
        AccessKind::Write => mcu.write(at, address, width, value).map(|()| 0),
    })
}

/// One model on a bus of its own: what a test wants when it puts a model under the microscope.
///
/// It's an [`Mcu`] with exactly one field, so everything the runtime does to a whole part works on
/// it unchanged: serve a read, fire an event, drain an interrupt. The model is `single.model`, a
/// field, with `single.registers()` beside it.
#[derive(Debug)]
pub struct Single<D> {
    /// The model, reachable as a field like any other.
    pub model: D,
    bus: Bus,
    slot: usize,
}

impl<D: crate::device::Device + 'static> Single<D> {
    /// Puts `model` on `instance` of the bus `builder` describes, with every gate the description
    /// declared dropped, so that the one model answers alone. A part of your own builds its bus with
    /// [`crate::soc::Builder::build`] and keeps its gates.
    ///
    /// Fails when the bus has no such instance, or when that instance's layout is not the one the
    /// model was written for.
    pub fn new(
        builder: crate::soc::Builder,
        instance: &'static str,
        model: D,
    ) -> Result<Self, Error> {
        let bus = builder.ungated().build()?;
        let slot = bus
            .slot_of(instance)
            .ok_or(Error::UnknownInstance { instance })?;
        let mut single = Self { model, bus, slot };
        single.check()?;
        Ok(single)
    }

    /// What the instance holds, which is where a model keeps half its state.
    pub fn registers(&mut self) -> &mut RegisterFile {
        self.bus.slot_mut(self.slot).registers_mut()
    }

    /// The model and its registers together, which is what a boundary method takes.
    pub fn pair(&mut self) -> (&mut D, &mut RegisterFile) {
        let Self { model, bus, slot } = self;
        (model, bus.slot_mut(*slot).registers_mut())
    }

    /// Drives an input the bus does not carry at `at`: the model gets its registers and a context as
    /// on a bus access, so it may schedule and raise.
    pub fn drive<T>(
        &mut self,
        at: VirtualNanos,
        input: impl FnOnce(&mut D, &mut RegisterFile, &mut DeviceContext<'_>) -> T,
    ) -> T {
        let Self { model, bus, slot } = self;
        let (registers, mut context) = bus.drive(at, *slot);
        input(model, registers, &mut context)
    }
}

impl<D: crate::device::Device + 'static> Mcu for Single<D> {
    fn bus(&self) -> &Bus {
        &self.bus
    }

    fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
        let Self {
            model,
            bus,
            slot: mine,
        } = self;
        let answering: Option<&mut dyn ErasedDevice> = (slot == *mine).then_some(model);
        (bus, answering)
    }

    fn bus_mut(&mut self) -> &mut Bus {
        &mut self.bus
    }
    fn reset(&mut self, _at: VirtualNanos, _cause: ResetCause) {
        self.reset_every_model();
    }

    fn poll(&mut self, now: VirtualNanos) {
        self.poll_every_model(now);
    }
}

/// A bus with no models at all: every instance answers from its register file alone.
///
/// It is what "register-only" means, made a type. A part is this plus its models, and a test that
/// wants to see what the tables alone do wants this.
#[derive(Debug)]
pub struct Bare(Bus);

impl Bare {
    /// The bus `builder` describes, with nothing on it.
    pub fn new(builder: crate::soc::Builder) -> Result<Self, Error> {
        Ok(Self(builder.ungated().build()?))
    }
}

impl Mcu for Bare {
    fn bus(&self) -> &Bus {
        &self.0
    }

    fn split(&mut self, _slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
        (&mut self.0, None)
    }

    fn bus_mut(&mut self) -> &mut Bus {
        &mut self.0
    }
    fn reset(&mut self, _at: VirtualNanos, _cause: ResetCause) {
        self.reset_every_model();
    }

    fn poll(&mut self, now: VirtualNanos) {
        self.poll_every_model(now);
    }
}

/// A part with a board's chip in one of its [`Window`]s.
///
/// `Wired` implements [`Mcu`], so a board holds it in place of the part. The part is `mcu.part`,
/// and the chip is `mcu.chip()` and `mcu.chip_mut()`, the same way a controller hands back a chip
/// it carries. The window gets a slot on the part's bus after all of the part's own slots, and
/// [`Mcu::split`] hands the chip out for that slot like any other model. That gives the chip the
/// window's time, events and interrupt lines. Accesses a model of the part makes in the window
/// reach it, and the window's gate refuses accesses the part hasn't enabled.
///
/// It forwards [`Mcu::reset`] and [`Mcu::poll`] to the part, and resets and polls the chip as well.
/// Everything [`Operate`] does runs through its own `split`, so the window is reachable. For two
/// chips, wrap one `Wired` in another.
#[derive(Debug)]
pub struct Wired<M, C> {
    /// The part.
    pub part: M,
    chip: Hung<C>,
    slot: usize,
}

impl<M: Mcu, C: parallel::Peer> Wired<M, C> {
    /// Puts `chip` in `window` of `part`.
    ///
    /// Fails like [`Bus::add_window`]: when the window overlaps a region, an instance or another
    /// window, or its gate names a bit the part doesn't have.
    pub fn new(mut part: M, window: Window, chip: C) -> Result<Self, Error> {
        let slot = part.bus_mut().add_window(window)?;
        Ok(Self {
            part,
            chip: Hung(chip),
            slot,
        })
    }

    /// The chip.
    pub fn chip(&self) -> &C {
        &self.chip.0
    }

    /// The chip, mutably, to set its inputs or read its state.
    pub fn chip_mut(&mut self) -> &mut C {
        &mut self.chip.0
    }
}

impl<M: Mcu, C: parallel::Peer> Mcu for Wired<M, C> {
    fn bus(&self) -> &Bus {
        self.part.bus()
    }

    fn bus_mut(&mut self) -> &mut Bus {
        self.part.bus_mut()
    }

    fn split(&mut self, slot: usize) -> (&mut Bus, Option<&mut dyn ErasedDevice>) {
        if slot == self.slot {
            return (self.part.bus_mut(), Some(&mut self.chip));
        }
        self.part.split(slot)
    }

    fn reset(&mut self, at: VirtualNanos, cause: ResetCause) {
        self.part.reset(at, cause);
        self.chip.0.reset();
    }

    fn poll(&mut self, now: VirtualNanos) {
        self.part.poll(now);
        let (_, mut context) = self.part.bus_mut().call(now, self.slot);
        self.chip.0.poll(&mut context);
    }
}

impl<M: Store, C: Store> Store for Wired<M, C> {
    fn store(&self, to: &mut Writer) -> Result<(), crate::snapshot::Error> {
        self.part.store(to)?;
        self.chip.0.store(to)
    }

    fn load(&mut self, from: &mut Reader) -> Result<(), crate::snapshot::Error> {
        self.part.load(from)?;
        self.chip.0.load(from)
    }
}

/// The model [`Mcu::split`] hands out for a window's slot.
#[derive(Debug)]
struct Hung<C>(C);

impl<C: parallel::Peer> ErasedDevice for Hung<C> {
    fn layout(&self) -> &'static [RegisterSpec] {
        &NO_REGISTERS
    }

    fn read(&mut self, _: usize, _: &mut RegisterFile, _: &mut DeviceContext<'_>) -> Option<u64> {
        None
    }

    fn write(&mut self, _: usize, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}

    fn on_event(&mut self, event: EventId, _: &mut RegisterFile, context: &mut DeviceContext<'_>) {
        self.0.on_event(event, context);
    }

    fn on_request(&mut self, _: RequestChange, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}

    fn on_clock(&mut self, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}

    fn poll(&mut self, _: &mut RegisterFile, context: &mut DeviceContext<'_>) {
        self.0.poll(context);
    }

    fn reset(&mut self) {
        self.0.reset();
    }

    fn on_access(&mut self, _: u64, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}

    fn on_refused(&mut self, _: u64, _: &mut RegisterFile, _: &mut DeviceContext<'_>) {}

    fn read_window(&mut self, offset: u64, width: Width, context: &mut DeviceContext<'_>) -> u64 {
        self.0.read(offset, width, context)
    }

    fn write_window(
        &mut self,
        offset: u64,
        width: Width,
        value: u64,
        context: &mut DeviceContext<'_>,
    ) {
        self.0.write(offset, width, value, context);
    }

    fn peek_window(&self, offset: u64, width: Width, now: VirtualNanos) -> Option<u64> {
        self.0.peek(offset, width, now)
    }
}