use crate::core::sync::Mutex;
use alloc::boxed::Box;
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use core::fmt;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Level {
#[default]
Low,
High,
}
impl Level {
#[inline]
pub const fn from_bool(b: bool) -> Level {
if b { Level::High } else { Level::Low }
}
#[inline]
pub const fn as_bool(self) -> bool {
matches!(self, Level::High)
}
#[inline]
pub const fn is_high(self) -> bool {
matches!(self, Level::High)
}
#[inline]
pub const fn is_low(self) -> bool {
matches!(self, Level::Low)
}
#[inline]
pub const fn inverted(self) -> Level {
match self {
Level::Low => Level::High,
Level::High => Level::Low,
}
}
}
impl From<bool> for Level {
#[inline]
fn from(b: bool) -> Level {
Level::from_bool(b)
}
}
impl From<Level> for bool {
#[inline]
fn from(l: Level) -> bool {
l.as_bool()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Edge {
Rising,
Falling,
}
impl Edge {
#[inline]
pub const fn between(from: Level, to: Level) -> Option<Edge> {
match (from, to) {
(Level::Low, Level::High) => Some(Edge::Rising),
(Level::High, Level::Low) => Some(Edge::Falling),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum EdgeTrigger {
#[default]
Rising,
Falling,
Both,
}
impl EdgeTrigger {
#[inline]
pub const fn matches(self, edge: Edge) -> bool {
matches!(
(self, edge),
(EdgeTrigger::Both, _)
| (EdgeTrigger::Rising, Edge::Rising)
| (EdgeTrigger::Falling, Edge::Falling)
)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum Resolve {
#[default]
Or,
And,
}
impl Resolve {
#[inline]
pub const fn idle(self) -> Level {
match self {
Resolve::Or => Level::Low,
Resolve::And => Level::High,
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WireId(
pub u64,
);
impl WireId {
pub const NONE: WireId = WireId(0);
#[inline]
pub const fn new(raw: u64) -> WireId {
WireId(raw)
}
#[inline]
pub const fn raw(self) -> u64 {
self.0
}
}
#[derive(Debug)]
pub struct WireIdAllocator {
next: AtomicUsize,
}
impl WireIdAllocator {
pub fn new() -> Self {
WireIdAllocator {
next: AtomicUsize::new(1),
}
}
pub fn alloc(&self) -> WireId {
WireId(self.next.fetch_add(1, SeqCst) as u64)
}
}
impl Default for WireIdAllocator {
fn default() -> Self {
WireIdAllocator::new()
}
}
pub trait WireSink: Send + Sync {
fn set_level(&self, src: WireId, line: u32, level: Level);
}
pub trait IntAck: Send + Sync + fmt::Debug {
fn acknowledge(&self, cycle: IntAckCycle) -> IntAckResponse;
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IntAckKind(pub u16);
impl IntAckKind {
pub const VECTOR: IntAckKind = IntAckKind(0);
pub const LEVEL: IntAckKind = IntAckKind(1);
pub const DATA_BUS: IntAckKind = IntAckKind(2);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IntAckCycle {
kind: IntAckKind,
detail: u16,
}
impl IntAckCycle {
#[inline]
pub const fn new(kind: IntAckKind, detail: u16) -> IntAckCycle {
IntAckCycle { kind, detail }
}
#[inline]
pub const fn vector_only() -> IntAckCycle {
IntAckCycle::new(IntAckKind::VECTOR, 0)
}
#[inline]
pub const fn at_level(level: u8) -> IntAckCycle {
IntAckCycle::new(IntAckKind::LEVEL, level as u16)
}
#[inline]
pub const fn data_bus(mode: u8) -> IntAckCycle {
IntAckCycle::new(IntAckKind::DATA_BUS, mode as u16)
}
#[inline]
pub const fn kind(self) -> IntAckKind {
self.kind
}
#[inline]
pub const fn detail(self) -> u16 {
self.detail
}
#[inline]
pub const fn level(self) -> Option<u8> {
match self.kind {
IntAckKind::LEVEL => Some(self.detail as u8),
_ => None,
}
}
#[inline]
pub const fn mode(self) -> Option<u8> {
match self.kind {
IntAckKind::DATA_BUS => Some(self.detail as u8),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IntAckResponse {
Declined,
Autovector,
Vector(u32),
}
impl IntAckResponse {
#[inline]
pub const fn vector(self) -> Option<u32> {
match self {
IntAckResponse::Vector(vector) => Some(vector),
_ => None,
}
}
#[inline]
pub const fn answered(self) -> bool {
!matches!(self, IntAckResponse::Declined)
}
}
#[derive(Debug, Default)]
pub struct IntAckHandlers {
handlers: Mutex<Vec<Weak<dyn IntAck>>>,
}
impl IntAckHandlers {
#[must_use]
pub const fn new() -> IntAckHandlers {
IntAckHandlers {
handlers: Mutex::new(Vec::new()),
}
}
pub fn attach(&self, ack: Weak<dyn IntAck>) {
let mut handlers = self.handlers.lock();
if handlers.iter().any(|existing| Weak::ptr_eq(existing, &ack)) {
return;
}
handlers.push(ack);
}
pub fn is_empty(&self) -> bool {
self.handlers.lock().is_empty()
}
pub fn len(&self) -> usize {
self.handlers.lock().len()
}
pub fn clear(&self) {
self.handlers.lock().clear();
}
pub fn run(&self, cycle: IntAckCycle) -> IntAckResponse {
let mut next = 0;
loop {
let handler = {
let handlers = self.handlers.lock();
match handlers.get(next) {
Some(handler) => handler.clone(),
None => return IntAckResponse::Declined,
}
};
next += 1;
if let Some(ack) = handler.upgrade() {
let response = ack.acknowledge(cycle);
if response.answered() {
return response;
}
}
}
}
}
pub trait LocalController: Send + Sync + fmt::Debug {
fn take_startup(&self) -> Startup;
fn base_register(&self) -> u64 {
0
}
fn set_base_register(&self, _value: u64) {}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Startup {
pub init: bool,
pub held: bool,
pub page: Option<u8>,
}
impl Startup {
pub const NONE: Startup = Startup {
init: false,
held: false,
page: None,
};
#[must_use]
pub const fn is_none(self) -> bool {
!self.init && !self.held && self.page.is_none()
}
}
pub trait DmaPeripheral: Send + Sync + fmt::Debug {
fn dma_read(&self, terminal: bool) -> u8;
fn dma_write(&self, byte: u8, terminal: bool);
fn dma_ready(&self) -> bool {
true
}
}
#[derive(Debug)]
pub struct FanIn {
sources: Box<[WireId]>,
levels: Box<[AtomicBool]>,
}
impl FanIn {
pub fn new(sources: &[WireId]) -> Self {
let mut ids: Vec<WireId> = sources.to_vec();
ids.sort_unstable();
ids.dedup();
let levels: Vec<AtomicBool> = ids.iter().map(|_| AtomicBool::new(false)).collect();
FanIn {
sources: ids.into_boxed_slice(),
levels: levels.into_boxed_slice(),
}
}
#[inline]
pub fn sources(&self) -> &[WireId] {
&self.sources
}
#[inline]
pub fn contains(&self, src: WireId) -> bool {
self.index_of(src).is_some()
}
#[inline]
fn index_of(&self, src: WireId) -> Option<usize> {
self.sources.binary_search(&src).ok()
}
#[inline]
fn level_at(&self, i: usize) -> Level {
Level::from_bool(self.levels[i].load(SeqCst))
}
#[inline]
fn set_at(&self, i: usize, level: Level) -> bool {
self.levels[i].swap(level.as_bool(), SeqCst) != level.as_bool()
}
#[inline]
pub fn set(&self, src: WireId, level: Level) -> bool {
match self.index_of(src) {
Some(i) => self.set_at(i, level),
None => false,
}
}
#[inline]
pub fn level_of(&self, src: WireId) -> Option<Level> {
self.index_of(src).map(|i| self.level_at(i))
}
pub fn any_high(&self) -> bool {
self.levels.iter().any(|l| l.load(SeqCst))
}
pub fn all_high(&self) -> bool {
self.levels.iter().all(|l| l.load(SeqCst))
}
#[inline]
pub fn resolve(&self, mode: Resolve) -> Level {
if self.sources.is_empty() {
return mode.idle();
}
match mode {
Resolve::Or => Level::from_bool(self.any_high()),
Resolve::And => Level::from_bool(self.all_high()),
}
}
pub fn clear(&self) {
for l in self.levels.iter() {
l.store(false, SeqCst);
}
}
pub fn snapshot(&self) -> Vec<(WireId, Level)> {
self.sources
.iter()
.enumerate()
.map(|(i, id)| (*id, self.level_at(i)))
.collect()
}
pub fn restore(&self, state: &[(WireId, Level)]) {
for (id, level) in state {
if let Some(i) = self.index_of(*id) {
self.levels[i].store(level.as_bool(), SeqCst);
}
}
}
}
enum SinkRef {
Strong(Arc<dyn WireSink>),
Weak(Weak<dyn WireSink>),
}
impl SinkRef {
#[inline]
fn with(&self, f: impl FnOnce(&dyn WireSink)) {
match self {
SinkRef::Strong(s) => f(&**s),
SinkRef::Weak(w) => {
if let Some(s) = w.upgrade() {
f(&*s);
}
}
}
}
}
struct SinkPort {
sink: SinkRef,
line: u32,
}
pub struct Wire {
inputs: FanIn,
pending: Box<[AtomicBool]>,
sinks: Box<[SinkPort]>,
delivering: AtomicBool,
unsettled: AtomicUsize,
}
impl Wire {
pub const SETTLE_LIMIT: u32 = 64;
pub fn builder() -> WireBuilder {
WireBuilder::new()
}
pub fn set(&self, src: WireId, level: Level) -> bool {
let Some(i) = self.inputs.index_of(src) else {
return false;
};
if !self.inputs.set_at(i, level) {
return false;
}
self.pending[i].store(true, SeqCst);
self.deliver();
true
}
pub fn refresh(&self) {
for p in self.pending.iter() {
p.store(true, SeqCst);
}
self.deliver();
}
fn deliver(&self) {
if self.delivering.swap(true, SeqCst) {
return;
}
loop {
let mut passes: u32 = 0;
loop {
let mut moved = false;
for (i, src) in self.inputs.sources.iter().enumerate() {
if self.pending[i].swap(false, SeqCst) {
moved = true;
let level = self.inputs.level_at(i);
for port in self.sinks.iter() {
port.sink.with(|s| s.set_level(*src, port.line, level));
}
}
}
if !moved {
break;
}
passes += 1;
if passes >= Self::SETTLE_LIMIT {
self.unsettled.fetch_add(1, SeqCst);
for p in self.pending.iter() {
p.store(false, SeqCst);
}
self.delivering.store(false, SeqCst);
return;
}
}
self.delivering.store(false, SeqCst);
if !self.pending.iter().any(|p| p.load(SeqCst)) {
return;
}
if self.delivering.swap(true, SeqCst) {
return;
}
}
}
#[inline]
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
#[inline]
pub fn sources(&self) -> &[WireId] {
self.inputs.sources()
}
#[inline]
pub fn level_of(&self, src: WireId) -> Option<Level> {
self.inputs.level_of(src)
}
#[inline]
pub fn resolve(&self, mode: Resolve) -> Level {
self.inputs.resolve(mode)
}
#[inline]
pub fn sink_count(&self) -> usize {
self.sinks.len()
}
pub fn unsettled(&self) -> usize {
self.unsettled.load(SeqCst)
}
pub fn snapshot(&self) -> Vec<(WireId, Level)> {
self.inputs.snapshot()
}
pub fn restore(&self, state: &[(WireId, Level)]) {
self.inputs.restore(state);
}
}
impl fmt::Debug for Wire {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Wire")
.field("inputs", &self.inputs)
.field("sinks", &self.sinks.len())
.field("unsettled", &self.unsettled.load(SeqCst))
.finish()
}
}
#[derive(Default)]
pub struct WireBuilder {
sources: Vec<WireId>,
sinks: Vec<SinkPort>,
}
impl WireBuilder {
pub fn new() -> Self {
WireBuilder::default()
}
#[must_use]
pub fn source(mut self, src: WireId) -> Self {
self.sources.push(src);
self
}
#[must_use]
pub fn sources(mut self, srcs: &[WireId]) -> Self {
self.sources.extend_from_slice(srcs);
self
}
#[must_use]
pub fn sink(mut self, sink: Arc<dyn WireSink>, line: u32) -> Self {
self.sinks.push(SinkPort {
sink: SinkRef::Strong(sink),
line,
});
self
}
#[must_use]
pub fn sink_weak(mut self, sink: Weak<dyn WireSink>, line: u32) -> Self {
self.sinks.push(SinkPort {
sink: SinkRef::Weak(sink),
line,
});
self
}
pub fn build(self) -> Wire {
let inputs = FanIn::new(&self.sources);
let pending: Vec<AtomicBool> = inputs
.sources()
.iter()
.map(|_| AtomicBool::new(false))
.collect();
Wire {
inputs,
pending: pending.into_boxed_slice(),
sinks: self.sinks.into_boxed_slice(),
delivering: AtomicBool::new(false),
unsettled: AtomicUsize::new(0),
}
}
pub fn build_shared(self) -> Arc<Wire> {
Arc::new(self.build())
}
}
impl fmt::Debug for WireBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WireBuilder")
.field("sources", &self.sources)
.field("sinks", &self.sinks.len())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct WireSource {
wire: Arc<Wire>,
id: WireId,
}
impl WireSource {
pub fn new(wire: Arc<Wire>, id: WireId) -> Self {
WireSource { wire, id }
}
#[inline]
pub fn id(&self) -> WireId {
self.id
}
#[inline]
pub fn wire(&self) -> &Arc<Wire> {
&self.wire
}
#[inline]
pub fn set(&self, level: Level) -> bool {
self.wire.set(self.id, level)
}
#[inline]
pub fn raise(&self) -> bool {
self.set(Level::High)
}
#[inline]
pub fn lower(&self) -> bool {
self.set(Level::Low)
}
#[inline]
pub fn level(&self) -> Level {
self.wire.level_of(self.id).unwrap_or(Level::Low)
}
pub fn pulse(&self, active: Level) {
self.set(active);
self.set(active.inverted());
}
}
#[derive(Debug)]
pub struct WireSplit {
inputs: FanIn,
mode: Resolve,
outs: Box<[WireSource]>,
}
impl WireSplit {
pub const CLASS: &'static str = "wire.split";
pub fn new(sources: &[WireId], outs: Vec<WireSource>) -> Self {
Self::with_resolve(sources, Resolve::Or, outs)
}
pub fn with_resolve(sources: &[WireId], mode: Resolve, outs: Vec<WireSource>) -> Self {
WireSplit {
inputs: FanIn::new(sources),
mode,
outs: outs.into_boxed_slice(),
}
}
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
pub fn level(&self) -> Level {
self.inputs.resolve(self.mode)
}
pub fn announce(&self) {
let out = self.level();
for o in self.outs.iter() {
o.set(out);
}
}
}
impl WireSink for WireSplit {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
if self.inputs.set(src, level) {
let out = self.inputs.resolve(self.mode);
for o in self.outs.iter() {
o.set(out);
}
}
}
}
#[derive(Debug)]
pub struct WireOr {
inputs: FanIn,
out: WireSource,
}
impl WireOr {
pub const CLASS: &'static str = "wire.or";
pub fn new(sources: &[WireId], out: WireSource) -> Self {
WireOr {
inputs: FanIn::new(sources),
out,
}
}
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
pub fn level(&self) -> Level {
self.inputs.resolve(Resolve::Or)
}
pub fn announce(&self) {
self.out.set(self.level());
}
}
impl WireSink for WireOr {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
if self.inputs.set(src, level) {
self.out.set(self.inputs.resolve(Resolve::Or));
}
}
}
#[derive(Debug)]
pub struct WireAnd {
inputs: FanIn,
out: WireSource,
}
impl WireAnd {
pub const CLASS: &'static str = "wire.and";
pub fn new(sources: &[WireId], out: WireSource) -> Self {
WireAnd {
inputs: FanIn::new(sources),
out,
}
}
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
pub fn level(&self) -> Level {
self.inputs.resolve(Resolve::And)
}
pub fn announce(&self) {
self.out.set(self.level());
}
}
impl WireSink for WireAnd {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
if self.inputs.set(src, level) {
self.out.set(self.inputs.resolve(Resolve::And));
}
}
}
#[derive(Debug)]
pub struct WireNot {
inputs: FanIn,
mode: Resolve,
out: WireSource,
}
impl WireNot {
pub const CLASS: &'static str = "wire.not";
pub fn new(sources: &[WireId], out: WireSource) -> Self {
Self::with_resolve(sources, Resolve::Or, out)
}
pub fn with_resolve(sources: &[WireId], mode: Resolve, out: WireSource) -> Self {
WireNot {
inputs: FanIn::new(sources),
mode,
out,
}
}
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
pub fn level(&self) -> Level {
self.inputs.resolve(self.mode).inverted()
}
pub fn announce(&self) {
self.out.set(self.level());
}
}
impl WireSink for WireNot {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
if self.inputs.set(src, level) {
self.out.set(self.inputs.resolve(self.mode).inverted());
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EdgeState {
pub inputs: Vec<(WireId, Level)>,
pub last: Level,
}
#[derive(Debug)]
pub struct LevelToEdge {
inputs: FanIn,
mode: Resolve,
trigger: EdgeTrigger,
last: AtomicBool,
active: Level,
out: WireSource,
edges: AtomicUsize,
}
impl LevelToEdge {
pub const CLASS: &'static str = "wire.level-to-edge";
pub fn new(sources: &[WireId], trigger: EdgeTrigger, out: WireSource) -> Self {
Self::with_options(sources, Resolve::Or, trigger, Level::High, out)
}
pub fn with_options(
sources: &[WireId],
mode: Resolve,
trigger: EdgeTrigger,
active: Level,
out: WireSource,
) -> Self {
let inputs = FanIn::new(sources);
let last = inputs.resolve(mode);
LevelToEdge {
inputs,
mode,
trigger,
last: AtomicBool::new(last.as_bool()),
active,
out,
edges: AtomicUsize::new(0),
}
}
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
pub fn last_level(&self) -> Level {
Level::from_bool(self.last.load(SeqCst))
}
pub fn edge_count(&self) -> usize {
self.edges.load(SeqCst)
}
pub fn snapshot(&self) -> EdgeState {
EdgeState {
inputs: self.inputs.snapshot(),
last: self.last_level(),
}
}
pub fn restore(&self, state: &EdgeState) {
self.inputs.restore(&state.inputs);
self.last.store(state.last.as_bool(), SeqCst);
}
}
impl WireSink for LevelToEdge {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
if !self.inputs.set(src, level) {
return;
}
let now = self.inputs.resolve(self.mode);
let before = Level::from_bool(self.last.swap(now.as_bool(), SeqCst));
let Some(edge) = Edge::between(before, now) else {
return;
};
if self.trigger.matches(edge) {
self.edges.fetch_add(1, SeqCst);
self.out.pulse(self.active);
}
}
}
#[derive(Debug)]
pub struct EdgeLatch {
active: Level,
pending: AtomicBool,
seen: AtomicUsize,
}
impl EdgeLatch {
pub fn new(active: Level) -> Self {
EdgeLatch {
active,
pending: AtomicBool::new(false),
seen: AtomicUsize::new(0),
}
}
pub fn peek(&self) -> bool {
self.pending.load(SeqCst)
}
pub fn take(&self) -> bool {
self.pending.swap(false, SeqCst)
}
pub fn clear(&self) {
self.pending.store(false, SeqCst);
}
pub fn count(&self) -> usize {
self.seen.load(SeqCst)
}
pub fn snapshot(&self) -> bool {
self.peek()
}
pub fn restore(&self, pending: bool) {
self.pending.store(pending, SeqCst);
}
}
impl WireSink for EdgeLatch {
fn set_level(&self, _src: WireId, _line: u32, level: Level) {
if level == self.active {
self.pending.store(true, SeqCst);
self.seen.fetch_add(1, SeqCst);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
const A: WireId = WireId::new(1);
const B: WireId = WireId::new(2);
const C: WireId = WireId::new(3);
const GATE: WireId = WireId::new(100);
const GATE2: WireId = WireId::new(101);
fn stage_id(n: u64) -> WireId {
WireId::new(200 + n)
}
#[derive(Debug)]
struct Irq {
inputs: FanIn,
asserted: AtomicBool,
changes: AtomicUsize,
calls: AtomicUsize,
last_line: AtomicUsize,
}
impl Irq {
fn new(sources: &[WireId]) -> Arc<Self> {
Arc::new(Irq {
inputs: FanIn::new(sources),
asserted: AtomicBool::new(false),
changes: AtomicUsize::new(0),
calls: AtomicUsize::new(0),
last_line: AtomicUsize::new(0),
})
}
fn level(&self) -> Level {
Level::from_bool(self.asserted.load(SeqCst))
}
fn changes(&self) -> usize {
self.changes.load(SeqCst)
}
}
impl WireSink for Irq {
fn set_level(&self, src: WireId, line: u32, level: Level) {
self.calls.fetch_add(1, SeqCst);
self.last_line.store(line as usize, SeqCst);
if self.inputs.set(src, level) {
let now = self.inputs.resolve(Resolve::Or).as_bool();
if self.asserted.swap(now, SeqCst) != now {
self.changes.fetch_add(1, SeqCst);
}
}
}
}
#[derive(Debug)]
struct Probe {
level: AtomicBool,
calls: AtomicUsize,
}
impl Probe {
fn new() -> Arc<Self> {
Arc::new(Probe {
level: AtomicBool::new(false),
calls: AtomicUsize::new(0),
})
}
fn level(&self) -> Level {
Level::from_bool(self.level.load(SeqCst))
}
fn calls(&self) -> usize {
self.calls.load(SeqCst)
}
}
impl WireSink for Probe {
fn set_level(&self, _src: WireId, _line: u32, level: Level) {
self.level.store(level.as_bool(), SeqCst);
self.calls.fetch_add(1, SeqCst);
}
}
#[derive(Debug)]
struct WeakGate {
inputs: FanIn,
target: Weak<Wire>,
id: WireId,
invert: bool,
}
impl WireSink for WeakGate {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
if !self.inputs.set(src, level) {
return;
}
let out = self.inputs.resolve(Resolve::Or);
let out = if self.invert { out.inverted() } else { out };
if let Some(wire) = self.target.upgrade() {
wire.set(self.id, out);
}
}
}
fn is_send_sync<T: Send + Sync>() {}
#[test]
fn core_types_are_send_and_sync() {
is_send_sync::<Wire>();
is_send_sync::<FanIn>();
is_send_sync::<WireSource>();
is_send_sync::<WireSplit>();
is_send_sync::<WireOr>();
is_send_sync::<WireAnd>();
is_send_sync::<WireNot>();
is_send_sync::<LevelToEdge>();
is_send_sync::<EdgeLatch>();
is_send_sync::<WireIdAllocator>();
}
#[test]
fn wired_or_holds_the_line_when_one_source_deasserts() {
let cpu = Irq::new(&[A, B]);
let wire = Wire::builder()
.sources(&[A, B])
.sink(cpu.clone(), 0)
.build();
assert_eq!(cpu.level(), Level::Low);
wire.set(A, Level::High);
assert_eq!(cpu.level(), Level::High);
wire.set(B, Level::High);
assert_eq!(cpu.level(), Level::High);
wire.set(A, Level::Low);
assert_eq!(
cpu.level(),
Level::High,
"the line must stay high while another source asserts"
);
assert_eq!(wire.resolve(Resolve::Or), Level::High);
wire.set(B, Level::Low);
assert_eq!(cpu.level(), Level::Low);
assert_eq!(cpu.changes(), 2);
}
#[test]
fn wired_or_through_an_explicit_or_device() {
let probe = Probe::new();
let out = Wire::builder()
.source(GATE)
.sink(probe.clone(), 0)
.build_shared();
let gate = Arc::new(WireOr::new(&[A, B], WireSource::new(out, GATE)));
let net = Wire::builder()
.sources(&[A, B])
.sink(gate.clone(), 0)
.build();
net.set(A, Level::High);
net.set(B, Level::High);
assert_eq!(probe.level(), Level::High);
net.set(A, Level::Low);
assert_eq!(probe.level(), Level::High);
assert_eq!(gate.level(), Level::High);
net.set(B, Level::Low);
assert_eq!(probe.level(), Level::Low);
}
#[test]
fn a_source_that_was_never_declared_changes_nothing() {
let cpu = Irq::new(&[A]);
let wire = Wire::builder().source(A).sink(cpu.clone(), 0).build();
wire.set(A, Level::High);
assert!(!wire.set(C, Level::Low));
assert_eq!(cpu.level(), Level::High);
assert_eq!(wire.level_of(C), None);
assert!(!wire.inputs().contains(C));
}
#[test]
fn repeating_a_level_delivers_nothing() {
let probe = Probe::new();
let wire = Wire::builder().source(A).sink(probe.clone(), 0).build();
assert!(wire.set(A, Level::High));
assert!(!wire.set(A, Level::High));
assert!(!wire.set(A, Level::High));
assert_eq!(probe.calls(), 1);
}
#[test]
fn wired_and_needs_every_source() {
let probe = Probe::new();
let out = Wire::builder()
.source(GATE)
.sink(probe.clone(), 0)
.build_shared();
let gate = Arc::new(WireAnd::new(&[A, B], WireSource::new(out, GATE)));
let net = Wire::builder()
.sources(&[A, B])
.sink(gate.clone(), 0)
.build();
net.set(A, Level::High);
assert_eq!(probe.level(), Level::Low);
net.set(B, Level::High);
assert_eq!(probe.level(), Level::High);
net.set(A, Level::Low);
assert_eq!(probe.level(), Level::Low);
assert_eq!(gate.level(), Level::Low);
}
#[test]
fn an_and_with_no_sources_reads_as_a_pull_up() {
let idle = FanIn::new(&[]);
assert_eq!(idle.resolve(Resolve::And), Level::High);
assert_eq!(idle.resolve(Resolve::Or), Level::Low);
}
#[test]
fn one_wire_fans_out_to_several_sinks() {
let a = Probe::new();
let b = Probe::new();
let c = Irq::new(&[A]);
let wire = Wire::builder()
.source(A)
.sink(a.clone(), 0)
.sink(b.clone(), 7)
.sink(c.clone(), 3)
.build();
assert_eq!(wire.sink_count(), 3);
wire.set(A, Level::High);
assert_eq!(a.level(), Level::High);
assert_eq!(b.level(), Level::High);
assert_eq!(c.level(), Level::High);
assert_eq!(b.calls(), 1);
assert_eq!(c.last_line.load(SeqCst), 3, "each sink gets its own line");
wire.set(A, Level::Low);
assert_eq!(a.level(), Level::Low);
assert_eq!(b.level(), Level::Low);
assert_eq!(c.level(), Level::Low);
}
#[test]
fn a_weak_sink_is_skipped_once_it_is_dropped() {
let probe = Probe::new();
let weak: Weak<dyn WireSink> = Arc::downgrade(&(probe.clone() as Arc<dyn WireSink>));
let wire = Wire::builder().source(A).sink_weak(weak, 0).build();
wire.set(A, Level::High);
assert_eq!(probe.level(), Level::High);
drop(probe);
assert!(wire.set(A, Level::Low));
}
#[test]
fn split_forwards_to_every_output() {
let x = Probe::new();
let y = Probe::new();
let out_x = Wire::builder()
.source(GATE)
.sink(x.clone(), 0)
.build_shared();
let out_y = Wire::builder()
.source(GATE)
.sink(y.clone(), 0)
.build_shared();
let split = Arc::new(WireSplit::new(
&[A],
vec![WireSource::new(out_x, GATE), WireSource::new(out_y, GATE)],
));
let net = Wire::builder().source(A).sink(split.clone(), 0).build();
net.set(A, Level::High);
assert_eq!(x.level(), Level::High);
assert_eq!(y.level(), Level::High);
assert_eq!(split.level(), Level::High);
net.set(A, Level::Low);
assert_eq!(x.level(), Level::Low);
assert_eq!(y.level(), Level::Low);
}
#[test]
fn not_inverts_and_nors_multiple_inputs() {
let probe = Probe::new();
let out = Wire::builder()
.source(GATE)
.sink(probe.clone(), 0)
.build_shared();
let inv = Arc::new(WireNot::new(&[A, B], WireSource::new(out, GATE)));
let net = Wire::builder()
.sources(&[A, B])
.sink(inv.clone(), 0)
.build();
assert_eq!(inv.level(), Level::High);
assert_eq!(probe.level(), Level::Low);
inv.announce();
assert_eq!(probe.level(), Level::High);
net.set(A, Level::High);
assert_eq!(probe.level(), Level::Low);
net.set(B, Level::High);
assert_eq!(probe.level(), Level::Low);
net.set(A, Level::Low);
assert_eq!(probe.level(), Level::Low, "NOR: B still asserts");
net.set(B, Level::Low);
assert_eq!(probe.level(), Level::High);
assert_eq!(inv.level(), Level::High);
}
fn edge_rig(trigger: EdgeTrigger) -> (Wire, Arc<LevelToEdge>, Arc<EdgeLatch>) {
let latch = Arc::new(EdgeLatch::new(Level::High));
let out = Wire::builder()
.source(GATE)
.sink(latch.clone(), 0)
.build_shared();
let det = Arc::new(LevelToEdge::new(&[A], trigger, WireSource::new(out, GATE)));
let net = Wire::builder().source(A).sink(det.clone(), 0).build();
(net, det, latch)
}
#[test]
fn rising_edges_only() {
let (net, det, latch) = edge_rig(EdgeTrigger::Rising);
net.set(A, Level::High);
assert!(latch.take(), "a rising edge is latched");
assert!(!latch.take(), "and taking it clears the latch");
assert_eq!(det.edge_count(), 1);
net.set(A, Level::Low);
assert!(
!latch.peek(),
"a falling edge does not fire a rising trigger"
);
assert_eq!(det.edge_count(), 1);
net.set(A, Level::High);
assert!(latch.take());
assert_eq!(det.edge_count(), 2);
assert_eq!(latch.count(), 2);
}
#[test]
fn falling_edges_only() {
let (net, det, latch) = edge_rig(EdgeTrigger::Falling);
net.set(A, Level::High);
assert!(!latch.peek());
assert_eq!(det.last_level(), Level::High);
net.set(A, Level::Low);
assert!(latch.take(), "a falling edge is latched");
assert_eq!(det.edge_count(), 1);
}
#[test]
fn both_edges() {
let (net, det, latch) = edge_rig(EdgeTrigger::Both);
net.set(A, Level::High);
assert!(latch.take());
net.set(A, Level::Low);
assert!(latch.take());
assert_eq!(det.edge_count(), 2);
}
#[test]
fn an_edge_detector_resolves_its_inputs_before_detecting() {
let latch = Arc::new(EdgeLatch::new(Level::High));
let out = Wire::builder()
.source(GATE)
.sink(latch.clone(), 0)
.build_shared();
let det = Arc::new(LevelToEdge::new(
&[A, B],
EdgeTrigger::Both,
WireSource::new(out, GATE),
));
let net = Wire::builder()
.sources(&[A, B])
.sink(det.clone(), 0)
.build();
net.set(A, Level::High);
assert_eq!(det.edge_count(), 1);
net.set(B, Level::High);
assert_eq!(det.edge_count(), 1);
net.set(A, Level::Low);
assert_eq!(det.edge_count(), 1);
net.set(B, Level::Low);
assert_eq!(det.edge_count(), 2);
assert!(latch.take());
}
#[test]
fn a_pulse_leaves_the_line_where_it_started() {
let probe = Probe::new();
let latch = Arc::new(EdgeLatch::new(Level::High));
let out = Wire::builder()
.source(GATE)
.sink(probe.clone(), 0)
.sink(latch.clone(), 0)
.build_shared();
WireSource::new(out, GATE).pulse(Level::High);
assert_eq!(probe.level(), Level::Low);
assert_eq!(probe.calls(), 2, "both transitions are delivered");
assert!(latch.take());
}
#[test]
fn propagation_survives_a_deep_chain() {
const STAGES: u64 = 16;
let probe = Probe::new();
let mut wire = Wire::builder()
.source(stage_id(STAGES))
.sink(probe.clone(), 0)
.build_shared();
let mut stages: Vec<Arc<WireNot>> = Vec::new();
for stage in (0..STAGES).rev() {
let inv = Arc::new(WireNot::new(
&[stage_id(stage)],
WireSource::new(wire, stage_id(stage + 1)),
));
stages.push(inv.clone());
wire = Wire::builder()
.source(stage_id(stage))
.sink(inv, 0)
.build_shared();
}
for inv in stages.iter().rev() {
inv.announce();
}
assert_eq!(probe.level(), Level::Low, "an even chain follows its input");
wire.set(stage_id(0), Level::High);
assert_eq!(probe.level(), Level::High);
wire.set(stage_id(0), Level::Low);
assert_eq!(probe.level(), Level::Low);
assert_eq!(wire.unsettled(), 0);
}
#[derive(Debug)]
struct Mirror {
target: Weak<Wire>,
calls: AtomicUsize,
}
impl WireSink for Mirror {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
self.calls.fetch_add(1, SeqCst);
if src == A
&& let Some(wire) = self.target.upgrade()
{
wire.set(B, level);
}
}
}
#[test]
fn a_sink_may_drive_the_wire_that_is_notifying_it() {
let mut sink: Option<Arc<Mirror>> = None;
let wire = Arc::new_cyclic(|me: &Weak<Wire>| {
let m = Arc::new(Mirror {
target: me.clone(),
calls: AtomicUsize::new(0),
});
sink = Some(m.clone());
Wire::builder().sources(&[A, B]).sink(m, 0).build()
});
let sink = sink.expect("built");
wire.set(A, Level::High);
assert_eq!(wire.level_of(B), Some(Level::High), "B mirrored A");
assert_eq!(
sink.calls.load(SeqCst),
2,
"the re-entrant change is delivered by the outer pass, once"
);
assert_eq!(
wire.unsettled(),
0,
"a settling feedback path is not a loop"
);
wire.set(A, Level::Low);
assert_eq!(wire.level_of(B), Some(Level::Low));
assert_eq!(wire.unsettled(), 0);
}
#[test]
fn a_combinational_loop_is_bounded_rather_than_fatal() {
let wire = Arc::new_cyclic(|me: &Weak<Wire>| {
let gate = Arc::new(WeakGate {
inputs: FanIn::new(&[A, GATE]),
target: me.clone(),
id: GATE,
invert: true,
});
Wire::builder().sources(&[A, GATE]).sink(gate, 0).build()
});
wire.set(A, Level::High);
assert_eq!(wire.unsettled(), 0);
wire.set(A, Level::Low);
assert!(
wire.unsettled() > 0,
"an oscillating loop must be reported, not hang or overflow"
);
assert!(wire.level_of(GATE).is_some());
}
#[test]
fn a_two_wire_loop_is_also_bounded() {
let mut inner: Option<Arc<Wire>> = None;
let w1 = Arc::new_cyclic(|w1: &Weak<Wire>| {
let relay = Arc::new(WeakGate {
inputs: FanIn::new(&[GATE]),
target: w1.clone(),
id: GATE2,
invert: false,
});
let w2 = Wire::builder().source(GATE).sink(relay, 0).build_shared();
inner = Some(w2.clone());
let inv = Arc::new(WireNot::new(&[A, GATE2], WireSource::new(w2, GATE)));
Wire::builder().sources(&[A, GATE2]).sink(inv, 0).build()
});
assert!(inner.is_some());
w1.set(A, Level::High);
w1.set(A, Level::Low);
assert!(w1.unsettled() > 0, "an odd-inversion loop must be bounded");
}
#[test]
fn wire_state_round_trips() {
let cpu = Irq::new(&[A, B]);
let wire = Wire::builder()
.sources(&[A, B])
.sink(cpu.clone(), 0)
.build();
wire.set(A, Level::High);
let saved = wire.snapshot();
assert_eq!(saved, vec![(A, Level::High), (B, Level::Low)]);
wire.set(A, Level::Low);
wire.set(B, Level::High);
assert_ne!(wire.snapshot(), saved);
wire.restore(&saved);
assert_eq!(wire.snapshot(), saved);
assert_eq!(cpu.inputs.level_of(B), Some(Level::High));
wire.refresh();
assert_eq!(cpu.inputs.level_of(A), Some(Level::High));
assert_eq!(cpu.inputs.level_of(B), Some(Level::Low));
assert_eq!(cpu.level(), Level::High);
}
#[test]
fn edge_detector_state_round_trips_without_inventing_an_edge() {
let (net, det, latch) = edge_rig(EdgeTrigger::Rising);
net.set(A, Level::High);
assert!(latch.take());
let saved = det.snapshot();
assert_eq!(saved.last, Level::High);
net.set(A, Level::Low);
net.set(A, Level::High);
assert!(latch.take());
assert_eq!(det.edge_count(), 2);
det.restore(&saved);
latch.clear();
assert_eq!(det.snapshot(), saved);
assert!(!latch.peek(), "restoring state emits nothing");
net.set(A, Level::High);
assert!(!latch.peek(), "already high: no edge");
}
#[test]
fn latch_state_round_trips() {
let latch = EdgeLatch::new(Level::High);
latch.set_level(A, 0, Level::High);
assert!(latch.snapshot());
latch.clear();
latch.restore(true);
assert!(latch.take());
assert!(!latch.peek());
}
#[test]
fn fan_in_tracks_and_resolves() {
let f = FanIn::new(&[B, A, A]);
assert_eq!(f.sources(), &[A, B], "sorted and deduplicated");
assert!(f.set(A, Level::High));
assert!(!f.set(A, Level::High));
assert_eq!(f.level_of(A), Some(Level::High));
assert_eq!(f.resolve(Resolve::Or), Level::High);
assert_eq!(f.resolve(Resolve::And), Level::Low);
assert!(f.set(B, Level::High));
assert!(f.all_high());
assert_eq!(f.resolve(Resolve::And), Level::High);
f.clear();
assert!(!f.any_high());
assert_eq!(f.resolve(Resolve::Or), Level::Low);
assert!(!f.set(C, Level::High), "an untracked source is ignored");
}
#[test]
fn ids_are_allocated_per_machine_and_never_zero() {
let alloc = WireIdAllocator::new();
let first = alloc.alloc();
let second = alloc.alloc();
assert_ne!(first, WireId::NONE);
assert_ne!(first, second);
assert_eq!(first.raw(), 1);
let other = WireIdAllocator::default();
assert_eq!(other.alloc(), first);
}
#[test]
fn level_and_edge_helpers() {
assert_eq!(Level::default(), Level::Low);
assert_eq!(Level::High.inverted(), Level::Low);
assert!(Level::from_bool(true).is_high());
assert!(Level::from_bool(false).is_low());
assert!(bool::from(Level::High));
assert_eq!(Level::from(true), Level::High);
assert_eq!(Edge::between(Level::Low, Level::High), Some(Edge::Rising));
assert_eq!(Edge::between(Level::High, Level::Low), Some(Edge::Falling));
assert_eq!(Edge::between(Level::High, Level::High), None);
assert!(EdgeTrigger::Both.matches(Edge::Falling));
assert!(!EdgeTrigger::Rising.matches(Edge::Falling));
assert_eq!(Resolve::And.idle(), Level::High);
assert_eq!(Resolve::default(), Resolve::Or);
}
#[test]
fn source_port_drives_and_reads_back() {
let probe = Probe::new();
let wire = Wire::builder()
.source(A)
.sink(probe.clone(), 0)
.build_shared();
let port = WireSource::new(wire.clone(), A);
assert_eq!(port.id(), A);
assert_eq!(port.level(), Level::Low);
assert!(port.raise());
assert_eq!(port.level(), Level::High);
assert_eq!(probe.level(), Level::High);
assert!(port.lower());
assert_eq!(port.level(), Level::Low);
assert!(Arc::ptr_eq(port.wire(), &wire));
}
#[derive(Debug)]
struct Claiming {
level: Option<u8>,
answer: IntAckResponse,
asked: AtomicUsize,
}
impl Claiming {
fn new(level: Option<u8>, answer: IntAckResponse) -> Arc<Claiming> {
Arc::new(Claiming {
level,
answer,
asked: AtomicUsize::new(0),
})
}
fn asked(&self) -> usize {
self.asked.load(SeqCst)
}
}
impl IntAck for Claiming {
fn acknowledge(&self, cycle: IntAckCycle) -> IntAckResponse {
self.asked.fetch_add(1, SeqCst);
match self.level {
Some(level) if cycle.level() != Some(level) => IntAckResponse::Declined,
_ => self.answer,
}
}
}
#[test]
fn a_cycle_carries_only_what_its_kind_presents() {
let level = IntAckCycle::at_level(5);
assert_eq!(level.kind(), IntAckKind::LEVEL);
assert_eq!(level.level(), Some(5));
assert_eq!(level.mode(), None);
let plain = IntAckCycle::vector_only();
assert_eq!(plain.kind(), IntAckKind::VECTOR);
assert_eq!(plain.level(), None);
assert_ne!(plain, IntAckCycle::at_level(0));
let z80 = IntAckCycle::data_bus(2);
assert_eq!(z80.mode(), Some(2));
assert_eq!(z80.level(), None);
assert_eq!(z80.detail(), 2);
}
#[test]
fn declining_passes_the_cycle_on_and_answering_ends_it() {
let handlers = IntAckHandlers::new();
assert!(handlers.is_empty());
assert_eq!(
handlers.run(IntAckCycle::at_level(1)),
IntAckResponse::Declined,
"nothing attached declines"
);
let low = Claiming::new(Some(2), IntAckResponse::Vector(80));
let high = Claiming::new(Some(5), IntAckResponse::Vector(96));
handlers.attach(Arc::downgrade(&low) as Weak<dyn IntAck>);
handlers.attach(Arc::downgrade(&high) as Weak<dyn IntAck>);
assert_eq!(handlers.len(), 2);
assert_eq!(
handlers.run(IntAckCycle::at_level(5)),
IntAckResponse::Vector(96)
);
assert_eq!(low.asked(), 1, "asked, and declined");
assert_eq!(high.asked(), 1);
assert_eq!(
handlers.run(IntAckCycle::at_level(2)),
IntAckResponse::Vector(80)
);
assert_eq!(low.asked(), 2);
assert_eq!(high.asked(), 1, "a cycle that was taken is not passed on");
let vpa = Claiming::new(Some(3), IntAckResponse::Autovector);
let behind = Claiming::new(None, IntAckResponse::Vector(112));
let chain = IntAckHandlers::new();
chain.attach(Arc::downgrade(&vpa) as Weak<dyn IntAck>);
chain.attach(Arc::downgrade(&behind) as Weak<dyn IntAck>);
assert_eq!(
chain.run(IntAckCycle::at_level(3)),
IntAckResponse::Autovector
);
assert_eq!(behind.asked(), 0);
assert_eq!(
chain.run(IntAckCycle::at_level(4)),
IntAckResponse::Vector(112)
);
assert_eq!(behind.asked(), 1);
}
#[test]
fn the_same_controller_offered_twice_is_kept_once() {
let handlers = IntAckHandlers::new();
let pic = Claiming::new(None, IntAckResponse::Vector(8));
handlers.attach(Arc::downgrade(&pic) as Weak<dyn IntAck>);
handlers.attach(Arc::downgrade(&pic) as Weak<dyn IntAck>);
assert_eq!(handlers.len(), 1, "one controller, two nets");
assert_eq!(
handlers.run(IntAckCycle::vector_only()),
IntAckResponse::Vector(8)
);
assert_eq!(pic.asked(), 1);
}
#[test]
fn a_controller_the_machine_has_dropped_is_skipped() {
let handlers = IntAckHandlers::new();
let gone = Claiming::new(None, IntAckResponse::Vector(1));
let live = Claiming::new(None, IntAckResponse::Vector(2));
handlers.attach(Arc::downgrade(&gone) as Weak<dyn IntAck>);
handlers.attach(Arc::downgrade(&live) as Weak<dyn IntAck>);
drop(gone);
assert_eq!(
handlers.run(IntAckCycle::vector_only()),
IntAckResponse::Vector(2),
"the weak edge is the point: a dead controller answers nothing"
);
handlers.clear();
assert!(handlers.is_empty());
}
#[test]
fn a_response_reports_what_it_supplied() {
assert_eq!(IntAckResponse::Vector(0x40).vector(), Some(0x40));
assert_eq!(IntAckResponse::Autovector.vector(), None);
assert!(IntAckResponse::Autovector.answered());
assert!(!IntAckResponse::Declined.answered());
}
}