#chip #circuit #libttl

libttl

A library for simulating TTL logic chips

2 releases

Uses new Rust 2024

new 0.1.1 Apr 28, 2025
0.1.0 Apr 28, 2025

#5 in #circuit

Download history 74/week @ 2025-04-23

74 downloads per month

MIT license

53KB
750 lines

libttl - TTL Logic Simulation Library

libttl is a Rust library designed for simulating basic TTL (Transistor-Transistor Logic) chips and circuits. It provides foundational components like logic levels and gates, implementations of several common 74xx series chips, and a circuit simulator to connect and run them.

This library is primarily intended for educational purposes and hobbyist digital logic simulation.

Features

  • LogicLevel: Represents High and Low states with boolean conversions and basic logic operations.
  • Gate Trait: A common interface for logic gates, supporting potential statefulness (though basic gates are stateless).
    • Implementations: NotGate, AndGate, OrGate, NandGate.
  • Chip Trait: An interface for simulating TTL chips, managing pin states, types, and internal logic updates.
    • Implementations: Chip7400 (Quad NAND), Chip7404 (Hex Inverter), Chip7408 (Quad AND), Chip7432 (Quad OR).
  • Circuit Simulator: Allows you to:
    • Add multiple Chip instances to a circuit.
    • Define connections (wires) between chip pins.
    • Set external input levels.
    • Simulate circuit behavior step-by-step (tick()) or run for multiple steps (run()).
    • Probe the logic levels of output pins.
  • Basic Clocking: The Circuit::tick() method provides a discrete time step simulation, propagating signals through connected components.
  • Comprehensive Tests: Includes unit tests for gates and integration tests for each implemented chip.

Library Structure

The library is organized into several modules:

  • logic_level: Defines the LogicLevel enum and related helpers.
  • gates: Defines the Gate trait and basic gate implementations.
  • chips: Defines the Chip trait, common pin types (PinType), and specific chip implementations (e.g., chips::Chip7400).
  • circuit: Defines the Circuit struct for building and simulating interconnected chips.

Installation

You can add libttl as a dependency in your Cargo.toml:

[dependencies]
libttl = "0.1.0"

Basic Usage

Using a Single Chip

use libttl::chips::{Chip, Chip7404}; // Example: Use the 7404 Hex Inverter
use libttl::logic_level::LogicLevel::{High, Low};

fn main() {
    // Create a new chip instance
    let mut inverter_chip = Chip7404::new();

    // Set an input pin (Pin 1 on 7404 is Input 1A)
    inverter_chip.set_input(1, High);

    // Update the chip's internal state based on inputs
    inverter_chip.update();

    // Get the corresponding output pin state (Pin 2 on 7404 is Output 1Y)
    let output_level = inverter_chip.get_output(2);

    println!("Input Pin 1: High");
    println!("Output Pin 2: {:?}", output_level); // Expected: Low

    // Change the input
    inverter_chip.set_input(1, Low);
    inverter_chip.update();
    let output_level = inverter_chip.get_output(2);
    println!("Input Pin 1: Low");
    println!("Output Pin 2: {:?}", output_level); // Expected: High
}

Using a Circuit

use libttl::circuit::Circuit;
use libttl::chips::{Chip7404, Chip7408};
use libttl::logic_level::LogicLevel::{High, Low};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut circuit = Circuit::new();

    // Add chips to the circuit
    let inv_chip_idx = circuit.add_chip(Box::new(Chip7404::new())); // Chip 0
    let and_chip_idx = circuit.add_chip(Box::new(Chip7408::new())); // Chip 1

    // Wire inverter output (Pin 2) to AND gate input A (Pin 1)
    circuit.add_wire(inv_chip_idx, 2, and_chip_idx, 1)?;

    // Set external inputs
    // Set inverter input (Pin 1 on chip 0)
    circuit.set_external_input(inv_chip_idx, 1, High)?;
    // Set AND gate input B (Pin 2 on chip 1)
    circuit.set_external_input(and_chip_idx, 2, High)?;

    // Simulate one clock tick for signals to propagate
    println!("Before tick:");
    // Note: Output of AND gate might be Low initially before propagation
    println!("  Inverter Out (Pin 2): {:?}", circuit.get_output_level(inv_chip_idx, 2)); // Expect Low after update
    println!("  AND Out (Pin 3):      {:?}", circuit.get_output_level(and_chip_idx, 3)); // Expect Low

    circuit.tick(); // Evaluate connections, commit inputs, update chips

    println!("After tick:");
    let inv_out = circuit.get_output_level(inv_chip_idx, 2)?;
    let and_out = circuit.get_output_level(and_chip_idx, 3)?;

    // Inverter input was High -> Inverter output (Pin 2) is Low
    // AND inputs are now Low (from inverter) and High (external) -> AND output (Pin 3) is Low
    println!("  Inverter Out (Pin 2): {:?}", inv_out); // Expected: Low
    println!("  AND Out (Pin 3):      {:?}", and_out); // Expected: Low

    assert_eq!(inv_out, Low);
    assert_eq!(and_out, Low);

    Ok(())
}

Running Tests

The library has unit tests for gates and chips located in the tests/ directory. To run all tests:

cargo test

Running Examples

The library includes examples in the examples directory. Run them with:

  • sr_latch: Implements a standard active-low SR NAND latch using a 7400 chip.
  • multi_gate_unit: Implements a simple 2-input logic unit with selectable AND/OR functions, using one of each implemented chip (7400, 7404, 7408, 7432).

To run an example, run:

# Run the SR Latch example
cargo run --example sr_latch

# Run the Multi-Gate Logic Unit example
cargo run --example multi_gate_unit

Contributing

Contributions are welcome! Whether it's adding more chips, implementing more complex gates, improving the circuit simulator, adding documentation, or fixing bugs, your help is appreciated.

Possible Areas for Contribution:

  • Implement more 74xx series chips (e.g., 7474 D-FlipFlop, 7486 XOR, multiplexers, decoders).
  • Implement more complex/stateful gates (e.g., Flip-Flops as basic Gate implementations).
  • Add support for tristate logic or floating pins (LogicLevel::HighZ).
  • Model propagation delays more accurately in the Circuit simulator.
  • Improve error handling and reporting.
  • Add more examples and documentation.

Contribution Workflow:

  1. Fork the repository.
  2. Create a new branch for your changes.
  3. Make your changes. Please follow Rust style guidelines (https://rust-lang.github.io/rustfmt/).
  4. Add tests for your changes and ensure they pass.
  5. Format your code: cargo fmt.
  6. Commit your changes with a clear commit message: git commit -m "Your commit message"
  7. Push your changes to your forked repository.
  8. Create a pull request to merge your changes into the main repository.

If you plan to make significant changes, it's a good idea to open an issue first to discuss the approach.

License

This project is licensed under the MIT License.

Copyright (c) 2025 Drew Walton

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

No runtime deps