This project simulates a Digital Circuit System that allows users to create, simulate, and analyze the behavior of basic combinational logic ICs using classes representing different types of Integrated Circuits (ICs) like AND, OR, NOT, NOR, NAND, XOR, XNOR gates, and a system to connect and interact with these ICs.
The project demonstrates Object-Oriented Programming concepts in C++ (like Encapsulation, Abstraction, Operator Overloading, Inheritance, Polymorphism, Function Templates, Class Templates and Exception handling) and provides an interactive menu-driven interface for designing circuits dynamically by connecting ICs and wires, setting pin values, and observing results.
- Project Structure
- Features
- Getting Started
- Usage
- Classes and Their Functions
- Code Structure
- Example
- Future Implementation
- License
The project consists of the following files:
IC.hpp
andIC.cpp
: Base classIC
for all integrated circuits.Wire.hpp
andWire.cpp
: ClassWire
to connect Pins of ICs.CustomExceptions.hpp
: Classes for Custom Error Messages for better understanding.ANDGateIC.hpp
andANDGateIC.cpp
: ClassANDGateIC
to simulate an AND gate IC.ORGateIC.hpp
andORGateIC.cpp
: ClassORGateIC
to simulate an OR gate IC.NOTGateIC.hpp
andNOTGateIC.cpp
: ClassNOTGateIC
to simulate a NOT gate IC.NORGateIC.hpp
andNORGateIC.cpp
: ClassNORGateIC
to simulate a NOR gate IC.NANDGateIC.hpp
andNANDGateIC.cpp
: ClassNANDGateIC
to simulate a NAND gate IC.XORateIC.hpp
andXORGateIC.cpp
: ClassXORGateIC
to simulate a XOR gate IC.NORGateIC.hpp
andXNORGateIC.cpp
: ClassXNORGateIC
to simulate a XNOR gate IC.
-
Interactive User Input:
The program uses a menu-driven interface to guide users through creating and managing circuits.
Supported ICs:- AND Gate IC
- OR Gate IC
- NOT Gate IC
- XOR Gate IC
- NAND Gate IC
- NOR Gate IC
- XNOR Gate IC
-
Dynamic Circuit Design:
Users can:- Create new ICs.
- Set pin values for ICs.
- Connect power to the ICs.
- Connect ICs with wires.
- Simulate the circuit and observe the output.
-
IC Manipulation:
Set and retrieve pin values, connect ICs to each other, and use logic gates. -
Operator Overloading:
Use operators for pin manipulation, IC comparison, and power connections. -
Error Handling:
The program handles invalid inputs and operations gracefully. -
Virtual Functions:
Define asimulate()
function for IC-specific behavior and execute digital logic. Simulate INDIVIDUAL ICs and view pin states after simulation.
To compile and run this project, you need:
- C++ compiler supporting C++11 or later (e.g., GCC/G++)
- CMake (optional for build automation)
- Basic understanding of Digital Circuits
Note: Now the Project can be compiled and run using just 2 commands and clear all the cache files in 1 command using MakeFile concept.
Tests will be implemented soon.
Make sure you have cloned the GitHub repo into your local system if you have not already:
git clone https://github.com/Prometheus052404/CIRCUIT.git
Use the below commands in Git Bash
at your Project's Root Directory:
git fetch
git pull
As you have made sure that you're up-to-date, you are now ready to continue ahead!
-
Navigate to the project directory:
cd DigitalLogicSimulator
-
Compile the project:
g++ src/*.cpp main.cpp -o simulator
-
Run the executable:
./simulator
-
Create a 'build' directory:
mkdir build && cd build
-
Generate Makefiles from CMake:
cmake ..
-
Build using Make:
make
-
Run the program:
./bin/DigitalLogicSimulator
Note: Skip this section if make --version
gives desired output in your git bash
If you are using Windows and have wsl installed, but not make, then follow the below steps:
- Go to ezwinports, i.e. https://sourceforge.net/projects/ezwinports/files/
- Download
make-4.1-2-without-guile-w32-bin.zip
(get the version without guile) - Extract zip
- Copy the contents to
C:\Program Files\Git\mingw64\
merging the folders, but do NOT overwrite/replace any exisiting files.
If you are using Ubuntu/Debian,
-
Open WSL and update the package list:
sudo apt update
-
Install make:
sudo apt install make
Navigate to your project directory within WSL and run make
. OR
sudo apt install build-essential
build-essential
includes make
and other essential development tools like gcc
and g++
.
For MacOS,
- If you have Homebrew installed, you can install
make
with:brew install make
- Open a new Git Bash window to refresh your PATH.
- Run:
make --version
This should display the version of make
if it’s installed correctly.
Create the Project using Makefile:
make
Execute the code:
./DCSimulator
Remove all the build / cache files:
make clean
Note: Run the above commands in git bash, at the project's root directory.
- Create IC objects: Instantiate various IC objects, e.g.,
ANDGateIC
,ORGateIC
. - Connect Power: Connect VCC and GND to the ICs to simulate power supply.
- Simulate: Call the
simulate()
function on each IC to execute its digital logic.
The template-based Base class for all simulating all ICs.
- Constructor: Initializes the IC with pins, VCC, GND, and a name.
- getTotalPins(): Returns the total number of pins.
- connectVCC(): Connects the IC to the power (VCC) rail.
- connectGround(): Connects the IC to the ground (GND) rail.
- setPin(int pin, T value): Sets a specific pin's value.
- getPin(int pin): Retrieves a specific pin's value.
- operator[](int pinNumber): Accesses pin values using the
[]
operator (0-indexed). - operator()(int pinOut, IC& otherIC, int pinIn): Connects an output pin to another IC's input pin.
- operator+=(const string& connection): Connects the IC to VCC or GND.
- operator==(const IC& other): Compares two ICs for equality.
- operator!(): Resets all pin values to
0
.
The Wire
class connects IC pins to simulate signal flow between them.
- Constructor: Initializes the wire with the source IC and pin, as well as the destination IC and pin.
- Destructor: Safely disconnects the wire, resetting the connection to prevent dangling pointers.
- connect(): Connects the source IC's pin to the destination IC's pin and transfers the signal.
- disconnect(): Disconnects the wire, resets the destination IC's pin value, and prevents dangling pointers.
- getSourceIC(): Returns a pointer to the source IC.
- getDestinationIC(): Returns a pointer to the destination IC.
- getSourcePin(): Returns the source pin number.
Each gate IC (ANDGateIC
, ORGateIC
, NOTGateIC
, NORGateIC
, NANDGateIC
, XORGateIC
, XNNORGateIC
) inherits from IC
and overrides the simulate()
method to perform specific logic operations.
- View ICs.
- Create ICs.
- Set pin values.
- Connect ICs with wires using the Wire class.
- Simulate the circuit.
- Connect Power & Ground to the ICs
- View IC pin states.
- Step 1: Create an AND Gate IC.
- Step 2: Set input pin values to
1
and0
. - Step 3: Simulate the IC to view the output.
- Step 4: Connect the output of the AND Gate to another IC's input using a wire.
To exit the simulator, choose the Exit
option from the menu.
DigitalLogicSimulator/
├── include/
│ ├── IC.hpp
│ ├── Wire.hpp
│ ├── CustomExceptions.hpp
│ ├── ANDGateIC.hpp
│ ├── ORGateIC.hpp
│ ├── NOTGateIC.hpp
│ ├── XORGateIC.hpp
│ ├── NANDGateIC.hpp
│ ├── NORGateIC.hpp
│ └── XNORGateIC.hpp
├── src/
│ ├── IC.cpp
│ ├── Wire.cpp
│ ├── ANDGateIC.cpp
│ ├── ORGateIC.cpp
│ ├── NOTGateIC.cpp
│ ├── XORGateIC.cpp
│ ├── NANDGateIC.cpp
│ ├── NORGateIC.cpp
│ └── XNORGateIC.cpp
├── tests/
│ ├── test_input.txt
│ ├── expected_output.txt
│ ├── ICWireTest.cpp
│ ├── ANDGateICTest.cpp
│ ├── ORGateICTest.cpp
│ ├── NOTGateICTest.cpp
│ ├── XORGateICTest.cpp
│ ├── NANDGateICTest.cpp
│ ├── NORateICTest.cpp
│ ├── XNORGateICTest.cpp
│ └── test_main.cpp
├── .github/workflows
│ └── build.yml
├── main.cpp
├── CMakeLists.txt
├── Makefile
└── README.md
Below is a sample usage example:
#include "./include/ANDGateIC.hpp"
#include "./include/ORGateIC.hpp"
#include "./include/NOTGateIC.hpp"
#include "./include/XORGateIC.hpp"
#include "./include/NANDGateIC.hpp"
#include "./include/NORGateIC.hpp"
#include "./include/XNORGateIC.hpp"
#include "./include/Wire.hpp"
#include <string>
#include <vector>
int main() {
vector<IC*> icList;
vector<Wire*> wireList;
while (true) {
//making a box for the menu
cout << "\n-----------------------------------\n";
cout << "\n--- Circuit Simulator Menu ---\n";
cout << "1. View ICs\n";
cout << "2. Create a new IC\n";
cout << "3. Set pin values\n";
cout << "4. Connect ICs with a wire\n";
cout << "5. Simulate IC\n";
cout << "6. Connect Power and Ground\n";
cout << "7. View IC pin states\n";
cout << "8. Exit\n";
cout << "-----------------------------------\n";
//taking the input from the user
cout << "Enter your choice: ";
int choice;
cin >> choice;
if (choice == 1) {
if (icList.empty()) {
cout << "No ICs available.\n";
continue;
}
cout << "ICs available: \n";
for (size_t i = 0; i < icList.size(); ++i) {
cout << i + 1 << ". " << icList[i]->getName() << endl;
}
}
else if (choice == 2) {
cout << "Select IC type:\n";
cout << "1. AND Gate\n2. OR Gate\n3. NOT Gate\n4. XOR Gate\n5. NAND Gate\n6. NOR Gate\n7. XNOR Gate\n";
int icType;
cin >> icType;
IC* newIC = nullptr;
switch (icType) {
case 1: newIC = new ANDGateIC(); break;
case 2: newIC = new ORGateIC(); break;
case 3: newIC = new NOTGateIC(); break;
case 4: newIC = new XORGateIC(); break;
case 5: newIC = new NANDGateIC(); break;
case 6: newIC = new NORGateIC(); break;
case 7: newIC = new XNORGateIC(); break;
default: cout << "Invalid choice.\n"; continue;
}
string powerChoice;
cout << "Connect power to this IC? (y/n): ";
cin >> powerChoice;
if (powerChoice == "y" || powerChoice == "Y") {
newIC->connectVCC();
newIC->connectGround();
cout << "Power connected to IC.\n";
}
icList.push_back(newIC);
cout << "IC created and added to the circuit.\n";
}
else if (choice == 3) {
if (icList.empty()) {
cout << "No ICs available. Create an IC first.\n";
continue;
}
size_t icIndex;
int pin, value;
cout<< "ICs available: \n";
for (size_t i = 0; i < icList.size(); ++i) {
cout << i + 1 << ". " << icList[i]->getName() << endl;
}
cout<< "-----------------------------------\n";
cout << "Select IC index (1-" << icList.size() << "): ";
cin >> icIndex;
if (icIndex < 1 || icIndex > icList.size()) {
cout << "Invalid IC index.\n";
continue;
}
IC* selectedIC = icList[icIndex - 1];
cout << "Pin values of IC " << icIndex << ":\n";
for (int pin = 1; pin <= selectedIC->getTotalPins(); ++pin) {
cout << "Pin " << pin << ": " << selectedIC->getPin(pin) << " ";
}
cout << "\n-----------------------------------\n";
cout << "Enter pin number to set (1-" << selectedIC->getTotalPins() << "): ";
cin >> pin;
if (pin < 1 || pin > selectedIC->getTotalPins()) {
cout << "Invalid pin number.\n";
continue;
}
else if(pin == selectedIC->vccPin || pin == selectedIC->groundPin) {
cout << "Cannot set VCC or GND pin value.\n";
continue;
}
cout << "Enter value for pin " << pin << " (0/1): ";
cin >> value;
try {
selectedIC->setPin(pin, value);
//changing values for pins connected by wire without accessing the private members
for (Wire* wire : wireList) {
if (wire->getSourceIC() == selectedIC && wire->getSourcePin() == pin) {
wire->connect();
}
}
cout << "Pin value set successfully.\n";
} catch (const exception& e) {
cout << "Error: " << e.what() << endl;
}
}
else if (choice == 4) {
if (icList.size() < 2) {
cout << "At least two ICs are required for connection.\n";
continue;
}
size_t srcIC, destIC;
int srcPin, destPin;
cout << "ICs available: \n";
for (size_t i = 0; i < icList.size(); ++i) {
cout << i + 1 << ". " << icList[i]->getName() << endl;
}
cout << "-----------------------------------\n";
cout << "Select source IC index (1-" << icList.size() << "): ";
cin >> srcIC;
cout << "Pin values of IC " << srcIC << ":\n";
for (int pin = 1; pin <= icList[srcIC - 1]->getTotalPins(); ++pin) {
cout << "Pin " << pin << ": " << icList[srcIC - 1]->getPin(pin) << " ";
}
cout << "\n-----------------------------------\n";
cout << "Enter source pin: ";
cin >> srcPin;
cout << "Select destination IC index (1-" << icList.size() << "): ";
cin >> destIC;
cout << "Pin values of IC " << destIC << ":\n";
for (int pin = 1; pin <= icList[destIC - 1]->getTotalPins(); ++pin) {
cout << "Pin " << pin << ": " << icList[destIC - 1]->getPin(pin) << " ";
}
cout << "\n-----------------------------------\n";
cout << "Enter destination pin: ";
cin >> destPin;
if (srcIC < 1 || srcIC > icList.size() || destIC < 1 || destIC > icList.size()) {
cout << "Invalid IC index.\n";
continue;
}
try {
Wire* newWire = new Wire(icList[srcIC - 1], srcPin, icList[destIC - 1], destPin);
newWire->connect();
wireList.push_back(newWire);
cout << "Wire connected successfully.\n";
} catch (const exception& e) {
cout << "Error: " << e.what() << endl;
}
}
else if (choice == 5) {
if (icList.empty()) {
cout << "No ICs available to simulate.\n";
continue;
}
size_t icIndex;
cout << "ICs available: \n";
for (size_t i = 0; i < icList.size(); ++i) {
cout << i + 1 << ". " << icList[i]->getName() << endl;
}
cout << "-----------------------------------\n";
cout << "Select IC index to simulate (1-" << icList.size() << "): ";
cin >> icIndex;
if (icIndex < 1 || icIndex > icList.size()) {
cout << "Invalid IC index.\n";
continue;
}
try {
icList[icIndex - 1]->simulate();
cout << "IC simulation completed successfully.\n";
} catch (const exception& e) {
cout << "Error: " << e.what() << endl;
}
}
else if (choice == 6) {
if (icList.empty()) {
cout << "No ICs available to connect power and ground.\n";
continue;
}
size_t icIndex;
cout << "ICs available: \n";
for (size_t i = 0; i < icList.size(); ++i) {
cout << i + 1 << ". " << icList[i]->getName() << endl;
}
cout << "-----------------------------------\n";
cout << "Select IC index to connect power and ground (1-" << icList.size() << "): ";
cin >> icIndex;
if (icIndex < 1 || icIndex > icList.size()) {
cout << "Invalid IC index.\n";
continue;
}
try {
icList[icIndex - 1]->connectVCC();
icList[icIndex - 1]->connectGround();
cout << "Power and ground connected to IC.\n";
} catch (const exception& e) {
cout << "Error: " << e.what() << endl;
}
}
else if (choice == 7) {
if (icList.empty()) {
cout << "No ICs available to view.\n";
continue;
}
for (size_t i = 0; i < icList.size(); ++i) {
cout << "IC " << i + 1 << " (" << icList[i]->getName() << "): ";
for (int pin = 1; pin <= icList[i]->getTotalPins(); ++pin) {
try {
cout << "Pin " << pin << ": " << icList[i]->getPin(pin) << " ";
} catch (const exception&) {
cout << "N/A ";
}
}
cout << endl;
}
}
else if (choice == 8) {
cout << "Exiting program...\n";
break;
}
else {
cout << "Invalid choice. Try again.\n";
}
}
// Clean up dynamically allocated memory
for (IC* ic : icList) delete ic;
for (Wire* wire : wireList) delete wire;
return 0;
}
- Breadboard Integration
- Implement a
Breadboard
class to simulate multiple wires and IC connections. - Allow dynamic connections to pins on the breadboard and ICs.
- Testing Framework
- Create detailed error logs for invalid connections or simulations.
- Extensibility
- Add additional ICs like multiplexers, demultiplexers, flip-flops, etc.
- Support for larger IC pin configurations (e.g., 16-pin, 20-pin ICs).
- Add support for sequential logic circuits (e.g., Flip-Flops, Counters).
- Advanced Signal Propagation
- Simulate signal propagation delays in wires.
- Introduce realistic electrical behavior (e.g., resistance, capacitance).
- Circuit Visualization
- Create a GUI/CLI-based circuit visualization tool for better interaction.
- Use frameworks like Qt for GUI or text-based interfaces.
This project, Digital Circuit Simulator, was created by OOPS Team - 64, Harith Yerragolam and Parth Pandey.
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, 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.