gsim 1.1.4

High speed digital logic simulation
Documentation
use crate::*;
use std::num::NonZeroU8;

mod component;
#[cfg(feature = "dot-export")]
mod dot_export;
mod import;

macro_rules! logic_state {
    ($state:ident) => {
        LogicState::$state
    };
    ({% $($bit:tt),*}) => {
        $crate::bits!($($bit),*)
    };
    ({$value:expr}) => {
        LogicState::from_int($value)
    };
    ([$($value:expr),+ $(,)?]) => {
        LogicState::from_big_int([$($value),+].as_slice()).unwrap()
    };
    ($value:expr) => {
        LogicState::from_int($value)
    };
}

use logic_state;

struct BinaryGateTestData {
    input_a: LogicState,
    input_b: LogicState,
    output: LogicState,
}

fn test_binary_gate<F>(
    add_gate: F,
    width: NonZeroU8,
    test_data: &[BinaryGateTestData],
    max_steps: u64,
) where
    F: FnOnce(&mut SimulatorBuilder, WireId, WireId, WireId) -> AddComponentResult,
{
    let mut builder = SimulatorBuilder::default();

    let input_a = builder.add_wire(width).unwrap();
    let input_b = builder.add_wire(width).unwrap();
    let output = builder.add_wire(width).unwrap();
    let _gate = add_gate(&mut builder, input_a, input_b, output).unwrap();

    let mut sim = builder.build();

    for (i, test_data) in test_data.iter().enumerate() {
        sim.set_wire_drive(input_a, &test_data.input_a).unwrap();
        sim.set_wire_drive(input_b, &test_data.input_b).unwrap();

        match sim.run_sim(max_steps) {
            SimulationRunResult::Ok => {}
            SimulationRunResult::MaxStepsReached => panic!("[TEST {i}] exceeded max steps"),
            SimulationRunResult::Err(err) => panic!("[TEST {i}] {err:?}"),
        }

        let output_state = sim.get_wire_state(output).unwrap();

        assert!(
            output_state.eq(&test_data.output, width),
            "[TEST {i}]  expected: {}  actual: {}",
            test_data.output.display_string(width),
            output_state.display_string(width),
        );
    }
}

fn test_shifter<F>(add_gate: F, width: NonZeroU8, test_data: &[BinaryGateTestData], max_steps: u64)
where
    F: FnOnce(&mut SimulatorBuilder, WireId, WireId, WireId) -> AddComponentResult,
{
    let mut builder = SimulatorBuilder::default();

    let shamnt_width = NonZeroU8::new(width.clog2()).unwrap();
    let input_a = builder.add_wire(width).unwrap();
    let input_b = builder.add_wire(shamnt_width).unwrap();
    let output = builder.add_wire(width).unwrap();
    let _gate = add_gate(&mut builder, input_a, input_b, output).unwrap();

    let mut sim = builder.build();

    for (i, test_data) in test_data.iter().enumerate() {
        sim.set_wire_drive(input_a, &test_data.input_a).unwrap();
        sim.set_wire_drive(input_b, &test_data.input_b).unwrap();

        match sim.run_sim(max_steps) {
            SimulationRunResult::Ok => {}
            SimulationRunResult::MaxStepsReached => panic!("[TEST {i}] exceeded max steps"),
            SimulationRunResult::Err(err) => panic!("[TEST {i}] {err:?}"),
        }

        let output_state = sim.get_wire_state(output).unwrap();

        assert!(
            output_state.eq(&test_data.output, width),
            "[TEST {i}]  expected: {}  actual: {}",
            test_data.output.display_string(width),
            output_state.display_string(width),
        );
    }
}

fn test_binary_module(
    sim: &mut Simulator,
    input_a: WireId,
    input_b: WireId,
    output: WireId,
    width: NonZeroU8,
    test_data: &[BinaryGateTestData],
    max_steps: u64,
) {
    for (i, test_data) in test_data.iter().enumerate() {
        sim.set_wire_drive(input_a, &test_data.input_a).unwrap();
        sim.set_wire_drive(input_b, &test_data.input_b).unwrap();

        match sim.run_sim(max_steps) {
            SimulationRunResult::Ok => {}
            SimulationRunResult::MaxStepsReached => panic!("[TEST {i}] exceeded max steps"),
            SimulationRunResult::Err(err) => panic!("[TEST {i}] {err:?}"),
        }

        let output_state = sim.get_wire_state(output).unwrap();

        assert!(
            output_state.eq(&test_data.output, width),
            "[TEST {i}]  expected: {}  actual: {}",
            test_data.output.display_string(width),
            output_state.display_string(width),
        );
    }
}

macro_rules! binary_gate_test_data {
    ($(($a:tt, $b:tt) -> $o:tt),* $(,)?) => {
        &[
            $(
                BinaryGateTestData {
                    input_a: logic_state!($a),
                    input_b: logic_state!($b),
                    output: logic_state!($o),
                },
            )*
        ]
    };
}

use binary_gate_test_data;

struct UnaryGateTestData {
    input: LogicState,
    output: LogicState,
}

fn test_unary_gate<F>(
    add_gate: F,
    width: NonZeroU8,
    test_data: &[UnaryGateTestData],
    max_steps: u64,
) where
    F: FnOnce(&mut SimulatorBuilder, WireId, WireId) -> AddComponentResult,
{
    let mut builder = SimulatorBuilder::default();

    let input = builder.add_wire(width).unwrap();
    let output = builder.add_wire(width).unwrap();
    let _gate = add_gate(&mut builder, input, output).unwrap();

    let mut sim = builder.build();

    for (i, test_data) in test_data.iter().enumerate() {
        sim.set_wire_drive(input, &test_data.input).unwrap();

        match sim.run_sim(max_steps) {
            SimulationRunResult::Ok => {}
            SimulationRunResult::MaxStepsReached => panic!("[TEST {i}] exceeded max steps"),
            SimulationRunResult::Err(err) => panic!("[TEST {i}] {err:?}"),
        }

        let output_state = sim.get_wire_state(output).unwrap();

        assert!(
            output_state.eq(&test_data.output, width),
            "[TEST {i}]  expected: {}  actual: {}",
            test_data.output.display_string(width),
            output_state.display_string(width),
        );
    }
}

fn test_horizontal_gate<F>(
    add_gate: F,
    width: NonZeroU8,
    test_data: &[UnaryGateTestData],
    max_steps: u64,
) where
    F: FnOnce(&mut SimulatorBuilder, WireId, WireId) -> AddComponentResult,
{
    let mut builder = SimulatorBuilder::default();

    let input = builder.add_wire(width).unwrap();
    let output = builder.add_wire(NonZeroU8::MIN).unwrap();
    let _gate = add_gate(&mut builder, input, output).unwrap();

    let mut sim = builder.build();

    for (i, test_data) in test_data.iter().enumerate() {
        sim.set_wire_drive(input, &test_data.input).unwrap();

        match sim.run_sim(max_steps) {
            SimulationRunResult::Ok => {}
            SimulationRunResult::MaxStepsReached => panic!("[TEST {i}] exceeded max steps"),
            SimulationRunResult::Err(err) => panic!("[TEST {i}] {err:?}"),
        }

        let output_state = sim.get_wire_state(output).unwrap();

        assert!(
            output_state.eq(&test_data.output, NonZeroU8::MIN),
            "[TEST {i}]  expected: {}  actual: {}",
            test_data.output.display_string(NonZeroU8::MIN),
            output_state.display_string(NonZeroU8::MIN),
        );
    }
}

macro_rules! unary_gate_test_data {
    ($($i:tt -> $o:tt),* $(,)?) => {
        &[
            $(
                UnaryGateTestData {
                    input: logic_state!($i),
                    output: logic_state!($o),
                },
            )*
        ]
    };
}

use unary_gate_test_data;

struct WideGateTestData {
    inputs: &'static [LogicState],
    output: LogicState,
}

fn test_wide_gate<F>(add_gate: F, width: NonZeroU8, test_data: &[WideGateTestData], max_steps: u64)
where
    F: Fn(&mut SimulatorBuilder, &[WireId], WireId) -> AddComponentResult,
{
    for (i, test_data) in test_data.iter().enumerate() {
        let mut builder = SimulatorBuilder::default();

        let inputs: Vec<_> = test_data
            .inputs
            .iter()
            .map(|drive| {
                let wire = builder.add_wire(width).unwrap();
                builder.set_wire_drive(wire, drive).unwrap();
                wire
            })
            .collect();
        let output = builder.add_wire(width).unwrap();
        let _gate = add_gate(&mut builder, &inputs, output).unwrap();

        let mut sim = builder.build();

        match sim.run_sim(max_steps) {
            SimulationRunResult::Ok => {}
            SimulationRunResult::MaxStepsReached => panic!("[TEST {i}] exceeded max steps"),
            SimulationRunResult::Err(err) => panic!("[TEST {i}] {err:?}"),
        }

        let output_state = sim.get_wire_state(output).unwrap();

        assert!(
            output_state.eq(&test_data.output, width),
            "[TEST {i}]  expected: {}  actual: {}",
            test_data.output.display_string(width),
            output_state.display_string(width),
        );
    }
}

macro_rules! wide_gate_test_data {
    ($(($($i:tt),+) -> $o:tt),* $(,)?) => {
        &[
            $(
                WideGateTestData {
                    inputs: &[$(logic_state!($i)),+],
                    output: logic_state!($o),
                },
            )*
        ]
    };
}

use wide_gate_test_data;

fn test_comparator<F>(add_comparator: F, compare_op: impl Fn(u32, u32) -> bool)
where
    F: Fn(&mut SimulatorBuilder, WireId, WireId, WireId) -> AddComponentResult,
{
    const WIDTH: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(4) };

    let mut builder = SimulatorBuilder::default();

    let input_a = builder.add_wire(WIDTH).unwrap();
    let input_b = builder.add_wire(WIDTH).unwrap();
    let output = builder.add_wire(NonZeroU8::MIN).unwrap();
    let _comparator = add_comparator(&mut builder, input_a, input_b, output).unwrap();

    let mut sim = builder.build();

    for a in 0..16 {
        for b in 0..16 {
            sim.set_wire_drive(input_a, &LogicState::from_int(a))
                .unwrap();
            sim.set_wire_drive(input_b, &LogicState::from_int(b))
                .unwrap();

            match sim.run_sim(2) {
                SimulationRunResult::Ok => {}
                SimulationRunResult::MaxStepsReached => {
                    panic!("[TEST ({a}, {b})] exceeded max steps")
                }
                SimulationRunResult::Err(err) => panic!("[TEST ({a}, {b})] {err:?}"),
            }

            let expected = LogicState::from_bool(compare_op(a, b));
            let output_state = sim.get_wire_state(output).unwrap();

            assert!(
                output_state.eq(&expected, NonZeroU8::MIN),
                "[TEST ({a}, {b})]  expected: {}  actual: {}",
                expected.display_string(NonZeroU8::MIN),
                output_state.display_string(NonZeroU8::MIN),
            );
        }
    }
}

fn test_signed_comparator<F>(add_comparator: F, compare_op: impl Fn(i32, i32) -> bool)
where
    F: Fn(&mut SimulatorBuilder, WireId, WireId, WireId) -> AddComponentResult,
{
    const WIDTH: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(4) };

    let mut builder = SimulatorBuilder::default();

    let input_a = builder.add_wire(WIDTH).unwrap();
    let input_b = builder.add_wire(WIDTH).unwrap();
    let output = builder.add_wire(NonZeroU8::MIN).unwrap();
    let _comparator = add_comparator(&mut builder, input_a, input_b, output).unwrap();

    let mut sim = builder.build();

    for a in -8..8 {
        for b in -8..8 {
            sim.set_wire_drive(input_a, &LogicState::from_int(a as u32))
                .unwrap();
            sim.set_wire_drive(input_b, &LogicState::from_int(b as u32))
                .unwrap();

            match sim.run_sim(2) {
                SimulationRunResult::Ok => {}
                SimulationRunResult::MaxStepsReached => {
                    panic!("[TEST ({a}, {b})] exceeded max steps")
                }
                SimulationRunResult::Err(err) => panic!("[TEST ({a}, {b})] {err:?}"),
            }

            let expected = LogicState::from_bool(compare_op(a, b));
            let output_state = sim.get_wire_state(output).unwrap();

            assert!(
                output_state.eq(&expected, NonZeroU8::MIN),
                "[TEST ({a}, {b})]  expected: {}  actual: {}",
                expected.display_string(NonZeroU8::MIN),
                output_state.display_string(NonZeroU8::MIN),
            );
        }
    }
}