libttl 0.1.1

A library for simulating TTL logic chips
Documentation
// examples/multi_gate_unit.rs

use libttl::chips::{Chip7400, Chip7404, Chip7408, Chip7432};
use libttl::circuit::Circuit;
use libttl::logic_level::{and, or, nand};
use libttl::LogicLevel::{High, Low}; // Bring enum variants into scope
use std::ops::Not;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Building Multi-Gate Logic Unit Circuit...");
    let mut circuit = Circuit::new();

    // --- Add Chips ---
    println!("Adding Chips:");
    let chip_and_idx = circuit.add_chip(Box::new(Chip7408::new())); // Chip 0: AND
    println!("  Idx {}: 7408 (AND)", chip_and_idx);
    let chip_or_idx = circuit.add_chip(Box::new(Chip7432::new())); // Chip 1: OR
    println!("  Idx {}: 7432 (OR)", chip_or_idx);
    let chip_nand_idx = circuit.add_chip(Box::new(Chip7400::new())); // Chip 2: NAND
    println!("  Idx {}: 7400 (NAND)", chip_nand_idx);
    let chip_not_idx = circuit.add_chip(Box::new(Chip7404::new())); // Chip 3: NOT
    println!("  Idx {}: 7404 (NOT)", chip_not_idx);

    // --- Define Pin Aliases for Clarity ---
    // Inputs (Assume connected externally)
    const INPUT_A_PIN_AND: usize = 1;
    const INPUT_B_PIN_AND: usize = 2;
    const INPUT_A_PIN_OR: usize = 1;
    const INPUT_B_PIN_OR: usize = 2;
    const INPUT_A_PIN_NAND: usize = 1;
    const INPUT_B_PIN_NAND: usize = 2;
    const INPUT_A_PIN_NOT: usize = 1; // For NOT A
    const INPUT_SEL_PIN_NOT: usize = 3; // For NOT SEL

    // Intermediate Outputs
    const AND_RESULT_PIN: usize = 3; // 7408 Gate 1 Output
    const OR_RESULT_PIN: usize = 3; // 7432 Gate 1 Output
    const NAND_RESULT_PIN: usize = 3; // 7400 Gate 1 Output (Unused in final MUX)
    const NOT_A_RESULT_PIN: usize = 2; // 7404 Gate 1 Output (Unused in final MUX)
    const SEL_BAR_RESULT_PIN: usize = 4; // 7404 Gate 2 Output

    // MUX Input Pins (using more gates on existing chips)
    // Need AND Gate 2 (Pins 4, 5 -> 6) on 7408
    const MUX_AND_TERM_IN1: usize = 4; // Connects to AND_RESULT_PIN
    const MUX_AND_TERM_IN2: usize = 5; // Connects to SEL_BAR_RESULT_PIN
    const MUX_AND_TERM_OUT: usize = 6; // Output of AND Gate 2

    // Need AND Gate 3 (Pins 10, 9 -> 8) on 7408 (Note reversed pin order 10,9 -> 8)
    const MUX_OR_TERM_IN1: usize = 10; // Connects to OR_RESULT_PIN
    const MUX_OR_TERM_IN2: usize = 9; // Connects to External SEL input
    const MUX_OR_TERM_OUT: usize = 8; // Output of AND Gate 3

    // Need OR Gate 2 (Pins 4, 5 -> 6) on 7432
    const MUX_FINAL_OR_IN1: usize = 4; // Connects to MUX_AND_TERM_OUT
    const MUX_FINAL_OR_IN2: usize = 5; // Connects to MUX_OR_TERM_OUT
    const FINAL_OUTPUT_PIN: usize = 6; // Output of OR Gate 2

    // --- Wiring ---
    println!("\nWiring Circuit...");

    // 1. Connect Gate 1 outputs to MUX inputs
    // AND Result -> MUX AND Term Input 1
    circuit.add_wire(chip_and_idx, AND_RESULT_PIN, chip_and_idx, MUX_AND_TERM_IN1)?;
    // OR Result -> MUX OR Term Input 1
    circuit.add_wire(chip_or_idx, OR_RESULT_PIN, chip_and_idx, MUX_OR_TERM_IN1)?; // To AND chip Gate 3

    // 2. Connect SEL inverter output to MUX input
    // SEL_BAR -> MUX AND Term Input 2
    circuit.add_wire(chip_not_idx, SEL_BAR_RESULT_PIN, chip_and_idx, MUX_AND_TERM_IN2)?;

    // 3. Connect MUX stage outputs to final OR gate inputs
    // MUX AND Term Out -> Final OR Input 1
    circuit.add_wire(chip_and_idx, MUX_AND_TERM_OUT, chip_or_idx, MUX_FINAL_OR_IN1)?;
    // MUX OR Term Out -> Final OR Input 2
    circuit.add_wire(chip_and_idx, MUX_OR_TERM_OUT, chip_or_idx, MUX_FINAL_OR_IN2)?;

    println!("Wiring Complete.");

    // --- Simulation ---
    let inputs = [
        // A,  B,   SEL
        (Low, Low, Low),  // Expect AND -> Low
        (Low, High, Low), // Expect AND -> Low
        (High, Low, Low), // Expect AND -> Low
        (High, High, Low), // Expect AND -> High
        (Low, Low, High), // Expect OR  -> Low
        (Low, High, High), // Expect OR  -> High
        (High, Low, High), // Expect OR  -> High
        (High, High, High), // Expect OR  -> High
    ];

    println!("\n--- Running Simulation ---");
    println!(" A | B | SEL || Expected | Actual Output | (AND) | (OR)  | (NAND)| (!A)  | (!SEL)");

    for (a, b, sel) in inputs {
        // Set external inputs A, B, SEL
        // A Inputs
        circuit.set_external_input(chip_and_idx, INPUT_A_PIN_AND, a)?;
        circuit.set_external_input(chip_or_idx, INPUT_A_PIN_OR, a)?;
        circuit.set_external_input(chip_nand_idx, INPUT_A_PIN_NAND, a)?;
        circuit.set_external_input(chip_not_idx, INPUT_A_PIN_NOT, a)?;
        // B Inputs
        circuit.set_external_input(chip_and_idx, INPUT_B_PIN_AND, b)?;
        circuit.set_external_input(chip_or_idx, INPUT_B_PIN_OR, b)?;
        circuit.set_external_input(chip_nand_idx, INPUT_B_PIN_NAND, b)?;
        // SEL Inputs
        circuit.set_external_input(chip_not_idx, INPUT_SEL_PIN_NOT, sel)?; // To inverter
        circuit.set_external_input(chip_and_idx, MUX_OR_TERM_IN2, sel)?; // To MUX OR term AND gate

        // Tick the circuit multiple times to ensure signals propagate
        circuit.run(5);

        // Get intermediate and final results
        let and_res = circuit.get_output_level(chip_and_idx, AND_RESULT_PIN)?;
        let or_res = circuit.get_output_level(chip_or_idx, OR_RESULT_PIN)?;
        let nand_res = circuit.get_output_level(chip_nand_idx, NAND_RESULT_PIN)?;
        let not_a_res = circuit.get_output_level(chip_not_idx, NOT_A_RESULT_PIN)?;
        let sel_bar_res = circuit.get_output_level(chip_not_idx, SEL_BAR_RESULT_PIN)?;
        let final_output = circuit.get_output_level(chip_or_idx, FINAL_OUTPUT_PIN)?;

        // Calculate expected output
        let expected_output = match sel {
            Low => and(a, b),  // If SEL=Low, expect AND result
            High => or(a, b), // If SEL=High, expect OR result
        };

        // Print results
        println!(
            " {} | {} | {} ||   {}    |      {}      |   {}   |  {}   |  {}   |  {}   |   {}",
            if a == High { '1' } else { '0' },
            if b == High { '1' } else { '0' },
            if sel == High { '1' } else { '0' },
            if expected_output == High { '1' } else { '0' },
            if final_output == High { '1' } else { '0' },
            if and_res == High { '1' } else { '0' },
            if or_res == High { '1' } else { '0' },
            if nand_res == High { '1' } else { '0' },
            if not_a_res == High { '1' } else { '0' },
            if sel_bar_res == High { '1' } else { '0' }
        );

        // Assert correctness
        assert_eq!(final_output, expected_output, "Mismatch for A={:?}, B={:?}, SEL={:?}", a, b, sel);
        // Also check intermediate results for sanity
        assert_eq!(and_res, and(a,b), "AND calculation failed");
        assert_eq!(or_res, or(a,b), "OR calculation failed");
        assert_eq!(nand_res, nand(a,b), "NAND calculation failed");
        assert_eq!(not_a_res, a.not(), "NOT A calculation failed");
        assert_eq!(sel_bar_res, sel.not(), "NOT SEL calculation failed");
    }

    println!("\nMulti-Gate Logic Unit simulation complete and verified.");
    Ok(())
}