{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "ASc0nYpw6D40" }, "source": [ "# PyCircHDL - Python Logic Circuit
Hardware Description Language Package
\n", "\n", "\n", "\n", "* \n", "Link to GITHUB repository\n", "\n", "* \n", "Link to the the Google Colaboratory notebook\n", "\n", "\n", " * The Google Colaboratory notebook has the advantage\n", " that you can run PyCircHDL from it without having to\n", " install PyCircHDL or even Python on your local device.\n", " * You first need to copy it to your google drive (or github repository)\n", " * You can also download it to your local device and open it as\n", " a Jupyter notebook (if you have it installed with your Python)." ] }, { "cell_type": "markdown", "metadata": { "id": "re0JUU526D47" }, "source": [ "## Installing the PyCircHDL package\n", "* The **PyCircHDL** package can be installed on your local system by running the following command from the command line\n", "```\n", "pip install pycirchdl\n", "```\n", "* Or you may try running one of the following commands from this notebook.\n", "* If you are running it from a Jupyter notebook on your local system, then it will be installed on your device. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "H8Trabf46D47", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "452e076e-6db7-4dc8-d5d6-2e1ab1d16495" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n", "Collecting pycirchdl\n", " Downloading pycirchdl-1.1.tar.gz (10 kB)\n", "Requirement already satisfied: networkx in /usr/local/lib/python3.7/dist-packages (from pycirchdl) (2.6.3)\n", "Building wheels for collected packages: pycirchdl\n", " Building wheel for pycirchdl (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Created wheel for pycirchdl: filename=pycirchdl-1.1-py3-none-any.whl size=10719 sha256=e60de54a495c6c50568ffdea2d99d824ae48efd995e3c9c2b48dfe9149ed7a5e\n", " Stored in directory: /root/.cache/pip/wheels/8f/7b/92/12e0c75c8ac5545cf88a6507a42ed4bf8bc25adcf25ff0d2e8\n", "Successfully built pycirchdl\n", "Installing collected packages: pycirchdl\n", "Successfully installed pycirchdl-1.1\n" ] } ], "source": [ "# To install from this notebook, run this cell.\n", "%pip install pycirchdl\n", "# This should also work:\n", "# !pip install --upgrade pycirchdl\n", "\n", "# After installation, you have to restart this notebook.\n", "# Make sure to comment the %pip or !pip lines above to avoid reinstall each time you run this notebook.\n", "\n", "# To uninstall the package use:\n", "# %pip uninstall pycirchdl\n", "# or\n", "# !pip uninstall pycirchdl" ] }, { "cell_type": "markdown", "metadata": { "id": "5hIXG9SE6D48" }, "source": [ "* **After installation, you may need to restart this notebook.**" ] }, { "cell_type": "markdown", "metadata": { "id": "rlDvlU9I6D48" }, "source": [ "## Loading the PyCircHDL package\n", "* After installing the **PyCircHDL** package, you need to import\n", " it. \n", "* The following command imports **PyCircHDL** to your Python interpreter." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZaIr21bT6D49", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "43c706ec-3bb5-4645-c5d5-20db10060c18" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Loading builtin box cells..\n" ] } ], "source": [ "from pycirchdl import *" ] }, { "cell_type": "markdown", "metadata": { "id": "kD35YCtJ6D4-" }, "source": [ "## Introduction\n", "\n", "* **PyCircHDL** is a new Python package for Logic Circuit\n", " Hardware Description Language (HDL).\n", " It was specifically designed for educational use in introductory\n", " computation and digital logic college courses.\n", "* As such, it was primarily tuned for simplicity, readability\n", " convenience, and fast learning curve.\n", " * Less for speed or industrial production.\n", "* It is a lightweight package especially designed for\n", " small to medium scale circuits, such as those that are studied\n", " in introductory academic courses on the theory of computation\n", " and electronic digital design.\n", "* Its main characteristic is that a digital circuit can be\n", " easily defined by a series of simple Python commands,\n", " rather than an external static language.\n", "* So, the only requirement is a basic knowledge of the\n", " Python programming language, with a little programming skill.\n", "* Experienced Python programmers can probably benefit a lot\n", " more from this package.\n", "* It can be a useful companion for theoretical courses on\n", " computation models and languages who wish also to engage\n", " the students with some programming experience and skills.\n", " * It is planned to be used in such a course by the author\n", " (Hebrew book at http://samyzaf.com/afl.pdf).\n", " * It enables students to easily model and experiment with\n", " * Typical logic circuit hardware design\n", " * Logic Circuit Modeling and Validation\n", " * Logic Circuit Testing\n", " * Logic problem solving\n", "* It does provide an opportunity for students to develop\n", " and practice some programming skills while covering\n", " the theoretical computation course.\n", "* In this tutorial, we will cover:\n", " 1. Basic usage of PyCircHDL for modeling and manipulating Logic Circuits.\n", " 2. A short survey of the commands and tools of the **PyCircHDL** package.\n", " 3. Examples for Logic Circuits and their manipulation.\n", " 4. Advanced usage of PyCircHDL for experienced Python programmers (TODO)." ] }, { "cell_type": "markdown", "metadata": { "id": "qSfarxQ-6D4_" }, "source": [ "## Example 1: The Circuit FOO\n", "* We start with a very simple logic circuit which we call **\"FOO\"**\n", " * Inputs gates: $x_1$, $x_2$, $x_3$\n", " * Output gates: $y_1$, $y_2$\n", " * Logic action: $(y_1, y_2) = (x_1 \\land x_2 , \\ \\neg x_3)$\n", " * This circuit can be represented by the following **PyCirc Diagram** \n", "\n", " \n", "\n", "* A **PyCirc diagram** is a simplified form of circuit diagram in\n", " which gates are represented by text blocks\n", " rather than special shape symbols.\n", " * Many students find the usual gate symbols intimidating and hard\n", " to memorize.\n", " * Plain text on a circle or a rectangle seems to be more\n", " convenient, especially for computation theory courses in\n", " which most of the participants do not have any electronics\n", " background (or plan to go in this direction).\n", "* **Input and output gates** are represented by circles with an\n", " **INP**/**OUT** labels.\n", "* The other **logic gates** are represented by rectangular blocks with input/output **pins** near the block edges.\n", " * Input gate names are **blue colored**.\n", " * Output gate names are **green colored**.\n", " * Logic gate names are **brown colored**.\n", "* **Connections (\"Wires\")** are represented by arrowed lines from\n", " source pin to target pin.\n", "* A **pin** is either an input/output gate or a named entry/exit\n", " point to a logic gate.\n", "* In the above diagram we have 5 wires and 10 pins:\n", " * 3 Input gates: x1, x2, x3\n", " * 2 output gates: y1, y2\n", " * 5 Block pins: g1/x1, g1/x2, g1/y, g2/x, g2/y\n", "* Note the special notation **g/p** for the block pins.\n", " * **g** stands for the gate name and **p** is the input/ouput\n", " name of the cell type of the gate.\n", "* Here is the **PyCirc code** for modeling this circuit:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TxVm9Pf-6D5B", "outputId": "44565cb2-da74-41f0-d2cc-92179cc66f81" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = foo: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 3 } ], "source": [ "Define(\"foo\")\n", "GATE (name=\"x1\", type=\"inp\")\n", "GATE (name=\"x2\", type=\"inp\")\n", "GATE (name=\"x3\", type=\"inp\")\n", "GATE (name=\"y1\", type=\"out\")\n", "GATE (name=\"y2\", type=\"out\")\n", "GATE (name=\"g1\", type=\"and2\")\n", "GATE (name=\"g2\", type=\"not\")\n", "\n", "WIRE (\"x1\", \"g1/x1\")\n", "WIRE (\"x2\", \"g1/x2\")\n", "WIRE (\"x3\", \"g2/x\")\n", "WIRE (\"g1/y\", \"y1\")\n", "WIRE (\"g2/y\", \"y2\")\n", "EndDef()" ] }, { "cell_type": "markdown", "metadata": { "id": "kU-kvZpn6D5B" }, "source": [ "* Notic that this is a **pure Python code**!\n", " * So you need to **run** its cell in order to execute it.\n", "\n", "* A circuit definition starts with the `Define()` function\n", " call which accepts the circuit name as its first argument.\n", "* The definition ends with the `EndDef()` function call.\n", "* A circuit definition consists of GATE\n", " and WIRE commands.\n", " * The GATE function accepts the name and\n", " the logic type of the gate.\n", " * The WIRE function connects a source pin\n", " to a target pin.\n", "* A pin notation g/p consists of a gate name\n", " g and a pin name p.\n", " * The pin p is either an input or an output pin\n", " of the gate.\n", "* For example the expression \"g1/x2\" designates\n", " the input pin \"x2\" of the gate \"g1\".\n", "* The flag \"name=\" is optional.\n", " It is enough to specify the name only:\n", " ```Python\n", " GATE(\"x1\", type=\"inp\")\n", " ```\n", "* The 'type' flag indicates the logic cell name to which the\n", " gate is an instance of.\n", "* **Input gates** are the circuit elements in which we feed\n", " the input (boolean values).\n", "* **Output gate** are the circuit elements from which we read the\n", " output after its computation is done.\n", "* All remaining gates are called **logical gates**.\n", "* They usually have incoming wires and outgoing wires and they\n", " perform some logic action.\n", "* We repeat: the above definition of the cell **FOO** is also\n", " a **pure Python code**!\n", "* This means that you can include it inside longer Python\n", " programs/scripts and manipulate cells dynamically.\n", " * For example, it is possible to add or remove gates or wires\n", " in a circuit dynamically and run it for optimization goals." ] }, { "cell_type": "markdown", "metadata": { "id": "KPAdr0VY6D5C" }, "source": [ "## Circuit libraries\n", "* After successful design of a circuit such as **FOO**,\n", " it can be placed as a single file 'foo.py' in\n", " a **circuit library** and loaded by the **load** command\n", " ```Python\n", "load(\"foo\")\n", "```\n", "* A **circuit library** is simply a directory that\n", " contains circuit files.\n", "* You may have several directories, including directories\n", " residing on far Internet locations.\n", "* You specify the library with a list such as\n", "```Python\n", "set_path([\"c:/eda/pycirc/lib\", \"d:/cmfl/code/logcirc/lib\", \"https://samyzaf.com/pycirc/lib\"])\n", "```\n", "* We will use this library path in this notebook, but you can\n", " download all circuit files from the following url,\n", " place them on your local computer,\n", " and change the path accordingly.\n", "\n", "* In most cases it is better to use the **need** command\n", " ```Python\n", "need(\"foo\")\n", "```\n", " which loads a circuit only if it was not already loaded." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "16yGrAis6D5C" }, "outputs": [], "source": [ "# You need to edit this list.\n", "# Replace the first two library paths to ones in your local system.\n", "# Or just leave the last one to load cells from pycirc web site.\n", "\n", "set_path([\"c:/eda/pycirc/lib\", \"d:/cmfl/code/logcirc/lib1\", \"https://samyzaf.com/pycirc/lib\"])" ] }, { "cell_type": "markdown", "metadata": { "id": "oHjloVC56D5C" }, "source": [ "* You may want to copy the circuit library that we use in this\n", " notebook to your local pc.\n", "* Here is a link to a zip file that contains all the cells we use here:\n", " * [Click to download lib.zip](https://samyzaf.com/pycirc/lib.zip)\n", "* You can also browse this library and pick individual files from it\n", " * [Click to browse the library](https://samyzaf.com/pycirc/lib)\n", "* After copying it to your local space, you can add more cells to it,\n", " or create more libraries like it.\n", "* If you do, don't forget to update the new list with the\n", " **set_path** command:\n", "```Python\n", "set_path([ dir1, dir2, url1, url2, ...])\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "-CFPg_Dd6D5D" }, "source": [ "## Gates as Cell Instances\n", "* The term **cell** means a packaged circuit or any other function\n", " which accepts input and provide output.\n", " After defining a circuit for example, it is \"packaged\" as a\n", " cell which hides its content and only exposes its input and output.\n", "* Once we have defined a cell such as **FOO**,\n", " we can use it as a building\n", " block element inside the definitions of new cells.\n", "* Every logic gate in a logic circuit must be an instance of some cell.\n", "* Typically, there can be several instances of the same cell\n", " in a logic circuit definition.\n", "* This is how the **FOO** cell is represented as a single block element\n", " in a logic circuit\n", "* It appears as a named black box with only entry and exit points (pins)\n", "\n", "
\n", "" ] }, { "cell_type": "markdown", "metadata": { "id": "80ERuNF06D5D" }, "source": [ "## The Circuit FRED\n", "* Here is an example of cell called **FRED** which uses an instance of the cell **FOO** as one of its building blocks." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "CO5O0_up6D5D", "outputId": "478c5038-cba2-416f-ab13-cc219b898ae0", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = fred: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 5 } ], "source": [ "# CELL: FRED\n", "# This cell is using a gate of type FOO which we defined earlier\n", "# It will be automatically loaded.\n", "\n", "Define(\"fred\")\n", "\n", "GATE(\"x1\", type=\"inp\") # No need to use: name=\"x1\"\n", "GATE(\"x2\", type=\"inp\")\n", "GATE(\"y1\", type=\"out\")\n", "GATE(\"y2\", type=\"out\")\n", "GATE(\"g1\", type=\"foo\") # Here we define a gate g1 of type FOO ! <<<<<<<\n", "GATE(\"g0\", type=\"zero\")\n", "\n", "WIRE(\"x1\", \"g1/x1\")\n", "WIRE(\"x2\", \"g1/x2\")\n", "WIRE(\"g0\", \"g1/x3\")\n", "WIRE(\"g1/y1\", \"y1\")\n", "WIRE(\"g1/y2\", \"y2\")\n", "\n", "EndDef()" ] }, { "cell_type": "markdown", "metadata": { "id": "B5eHBew46D5E" }, "source": [ "* This is the standard for defining a new circuit from a regular\n", " Python code.\n", " * Within a librarry tree, the Define/EndDef lines must\n", " be omitted since the name of the circuit, beginning and end, are\n", " easily inferred from the circuit file.\n", "* Usually every circuit is defined in a single file **\"name.py\"**,\n", " and kept within a library directory,\n", " but several circuit definitions can also reside in a single file\n", " and imported to a Python code via the Python import mechanism.\n", "* You create a library by\n", " - allocating a folder or a url for it.\n", " - putting all your circuit definitions in this folder\n", " (one circuit per file).\n", " - adding this directory (or url) to the **set_path** list (see above.\n", " - Note that the Define/EndDef lines are omitted in a circuit file!\n", "* Note the line GATE(\"g1\", type=\"foo\") in which the\n", " gate of type **FOO** is declared! It automatically triggers the\n", " loading of **FOO** (if it wasn't already loaded).\n", "* It is easy to draw a **PyCirc Diagram** from the above definition\n", "\n", " \n", "\n", "* Note that we used the **ZERO** cell which has no inputs, and only one constant 0 output.\n", "* It is the hardware equivalent of a **boolean constant**.\n", " * We use it to fix the input x3 of gate g1 to zero.\n", "* The **ZERO** cell is a fundamental **PyCirc** cell which is automatically loaded when PyCirc starts." ] }, { "cell_type": "markdown", "metadata": { "id": "Ko9IcHhT6D5E" }, "source": [ "## The Circuit HAM\n", "* The follwing cell **HAM** contains two gates of type **FOO** and two gates of type **XOR3**.\n", "* The **XOR3** cell is a typical **xor** cell with 3 input gates (built in PyCirc).\n", "* It also contains one gate of type **NOT**.\n", "* The **XOR3** and **NOT** cells are built in types of **PyCirc** and are loaded automatically when **PyCirc** starts.\n", "\n", " " ] }, { "cell_type": "markdown", "source": [ "* Here is a **PyCirc code** for modeling this cell.\n", "* Note the **compressed notation** style for writing shorter code." ], "metadata": { "id": "TmMuARGzI9VV" } }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "gFwgNmlj6D5F", "outputId": "2d2d160f-202c-4c01-8e24-42c99ab3b27a", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = ham: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 6 } ], "source": [ "# CELL: HAM\n", "# This cell is using the FOO cell which we defined earlier\n", "# \"xor3\" is a basic cell in PyCirc - it is built in and loaded at start time.\n", "\n", "Define(\"ham\")\n", "\n", "GATE(\"x<1:4>\", type=\"inp\") # Input gates: x1, x2, x3, x4 \n", "GATE(\"y<1:3>\", type=\"out\") # Output gates: y1, y2, y3\n", "GATE(\"g1; g2\", type=\"foo\") # Here we define two gates g1 and g2 of type FOO !\n", "GATE(\"g4\", type=\"not\")\n", "GATE(\"g3; g5\", type=\"xor3\") # Here we define two gates g3 and g5 of type XOR3 !\n", "\n", "WIRE(\"x1\", \"g1/x1\")\n", "WIRE(\"x2\", \"g1/x3\")\n", "WIRE(\"x3\", \"g1/x2; g2/x1\") # Two wires defined in one line: \"x3\" -> \"g1/x2\" and \"x3\" -> \"g2/x1\"\n", "WIRE(\"x4\", \"g2/x2; g2/x3\")\n", "WIRE(\"g1/y1\", \"g3/x2\")\n", "WIRE(\"g1/y2\", \"g3/x1; g4/x; g5/x1\") # Three wires defined in one line!\n", "WIRE(\"g2/y1\", \"g5/x2\")\n", "WIRE(\"g2/y2\", \"g3/x3; g5/x3\")\n", "WIRE(\"g3/y\", \"y1\")\n", "WIRE(\"g4/y\", \"y2\")\n", "WIRE(\"g5/y\", \"y3\")\n", "\n", "EndDef()" ] }, { "cell_type": "markdown", "metadata": { "id": "NZysMP816D5F" }, "source": [ "* Note that `\"x<1:4>\"` stands for `x1, x2, x3, x4`\n", "* Instead of writing 4 lines of code\n", "```Python\n", "GATE(\"x1\", type=\"inp\") \n", "GATE(\"x2\", type=\"inp\") \n", "GATE(\"x3\", type=\"inp\") \n", "GATE(\"x4\", type=\"inp\") \n", "```\n", "We can write one equivalent line!\n", "```Python\n", "GATE(\"x<1:4>\", type=\"inp\") \n", "```\n", "* Instead of writing\n", "```Python\n", "WIRE(\"g1/y2\", \"g3/x1\")\n", "WIRE(\"g1/y2\", \"g4/x\")\n", "WIRE(\"g1/y2\", \"g5/x1\")\n", "```\n", "We can simply write\n", "```Python\n", "WIRE(\"g1/y2\", \"g3/x1; g4/x; g5/x1\")\n", "```\n", "* Arguments can also be lists or tuples of compressed names!\n", "```Python\n", "WIRE(\"g1/y2\", [\"g%s/x%s\" % (i,) for i in range(20)])\n", "```\n", "* More on **compressed notation** in the examples below.\n", "* The only supported modes:\n", " * one to one\n", " * one to many\n", " * many to one\n", " * n to n" ] }, { "cell_type": "markdown", "metadata": { "id": "NtS-Q0wQ6D5F" }, "source": [ "## Cell Query and Manipulation\n", "* After defining the cell **HAM** we may start performing all kind of queries on it.\n", "* First we need to get a reference to it." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "h8-F_-Dl6D5G" }, "outputs": [], "source": [ "# Get a reference to HAM\n", "\n", "ham = PyCirc[\"ham\"]" ] }, { "cell_type": "markdown", "metadata": { "id": "SKkc5fdo6D5G" }, "source": [ "* This gives us a PyCirc reference to our **ham**\n", " circuit object. which we can use to manipulate the cell.\n", "* `PyCirc` is the name of the Python class which creates logic circuit objects.\n", "* The class `PyCirc` keeps a map between the name and the reference\n", " of every logic circuit created by it.\n", "* So if you have the name of an existing logic circuit,\n", " you can get a handle for it by\n", "using\n", "```python\n", "ref = PyCirc[name]\n", "```\n", "anywhere in your code.\n", "* You may use ref as any other Python\n", "object handle for querying and manipulating the cell object." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "0elFxfGj6D5G", "outputId": "f4a56369-9639-44ce-dece-9c55d7917519", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=13: name=x1, type=inp, value=(None), depth=0\n", "gate id=14: name=x2, type=inp, value=(None), depth=0\n", "gate id=15: name=x3, type=inp, value=(None), depth=0\n", "gate id=16: name=x4, type=inp, value=(None), depth=0\n", "gate id=17: name=y1, type=out, value=(None), depth=3\n", "gate id=18: name=y2, type=out, value=(None), depth=3\n", "gate id=19: name=y3, type=out, value=(None), depth=3\n", "gate id=20: name=g1, type=foo, value=(y1=None, y2=None), depth=1\n", "gate id=21: name=g2, type=foo, value=(y1=None, y2=None), depth=1\n", "gate id=22: name=g4, type=not, value=(y=None), depth=2\n", "gate id=23: name=g3, type=xor3, value=(y=None), depth=2\n", "gate id=24: name=g5, type=xor3, value=(y=None), depth=2\n" ] } ], "source": [ "# Print the gates in the circuit HAM\n", "\n", "for g in ham.gates:\n", " print(g)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PpYHLKDg6D5G", "outputId": "1f706f96-4e53-4058-b9a5-2cd09b439373", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "12\n" ] } ], "source": [ "# Count the number of gates in the circuit HAM\n", "\n", "print(len(ham.gates))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "42DTPY_m6D5H", "outputId": "3f061a25-0c37-461b-96e3-0816ea772614", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1\n", "x2\n", "x3\n", "x4\n" ] } ], "source": [ "# Get names of input gates\n", "\n", "for x in ham.input:\n", " print(x.name)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JybQ6Ptb6D5H", "outputId": "7d88c985-629e-4e37-e576-6b66d1201881", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "y1\n", "y2\n", "y3\n" ] } ], "source": [ "# Get names of output gates\n", "\n", "for x in ham.output:\n", " print(x.name)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "V9wEDvMZ6D5H", "outputId": "5960e129-8370-4240-b3b4-77e8e01dff14", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "16\n" ] } ], "source": [ "# Count the number of wires in the circuit HAM\n", "print(len(ham.wires))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "H7-JwkgF6D5I", "outputId": "060892e7-2180-4999-bcd0-93c0b93f228e", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=23: name=g3, type=xor3, value=(y=None), depth=2\n", "gate id=24: name=g5, type=xor3, value=(y=None), depth=2\n" ] } ], "source": [ "# List all XOR3 gates\n", "\n", "for g in ham.gates:\n", " if g.type == \"xor3\":\n", " print(g)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rVr7jScT6D5I", "outputId": "30447f8b-6a05-43d8-be30-b5f1bf8ba8cd", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=22: name=g4, type=not, value=(y=None), depth=2\n", "gate id=23: name=g3, type=xor3, value=(y=None), depth=2\n", "gate id=24: name=g5, type=xor3, value=(y=None), depth=2\n" ] } ], "source": [ "# Get a reference to gate \"g1\"\n", "g1 = ham[\"g1\"]\n", "\n", "# Print all outcoming gates from g1\n", "for g in ham.out_gates(g1):\n", " print(g)" ] }, { "cell_type": "markdown", "metadata": { "id": "AOzscWqo6D5I" }, "source": [ "* This gives us only gates and ignores pins.\n", "* To get pin connections we need to dig a bit deeper." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "qAKgRpoL6D5J", "outputId": "d4df1676-173a-4818-d1e5-8c94179c8020", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "wire id=16:\n", " source=gate id=20: name=g1, type=foo, value=(y1=None, y2=None), depth=1\n", " target=gate id=23: name=g3, type=xor3, value=(y=None), depth=2\n", " source=y1\n", " target=x2\n", "wire id=17:\n", " source=gate id=20: name=g1, type=foo, value=(y1=None, y2=None), depth=1\n", " target=gate id=23: name=g3, type=xor3, value=(y=None), depth=2\n", " source=y2\n", " target=x1\n", "wire id=18:\n", " source=gate id=20: name=g1, type=foo, value=(y1=None, y2=None), depth=1\n", " target=gate id=22: name=g4, type=not, value=(y=None), depth=2\n", " source=y2\n", " target=x\n", "wire id=19:\n", " source=gate id=20: name=g1, type=foo, value=(y1=None, y2=None), depth=1\n", " target=gate id=24: name=g5, type=xor3, value=(y=None), depth=2\n", " source=y2\n", " target=x1\n" ] } ], "source": [ "# Print all outgoing wires from g1\n", "for w in ham.out_wires(g1):\n", " print(w)" ] }, { "cell_type": "markdown", "metadata": { "id": "RO1yADMH6D5J" }, "source": [ "* This is too verbose.\n", "* We want to get a cleaner list of pin connections:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "C3tp1AKZ6D5J", "outputId": "f710103c-6fdc-4f66-9b4c-b59852425a14", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "g1/y1 => g3/x2\n", "g1/y2 => g3/x1\n", "g1/y2 => g4/x\n", "g1/y2 => g5/x1\n" ] } ], "source": [ "for w in ham.out_wires(g1):\n", " print(\"%s => %s\" % (w.source, w.target))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rYn5ownD6D5J", "outputId": "11f5c6f4-2bc2-4fd4-b198-3eed5ea7922f", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "g1/y1 ==> g3/x2\n", "g1/y2 ==> g3/x1\n", "g2/y2 ==> g3/x3\n" ] } ], "source": [ "# Get a reference to gate \"g3\"\n", "g3 = ham[\"g3\"]\n", "\n", "# Print all incoming wires to gate g3\n", "for w in ham.in_wires(g3):\n", " print(\"%s ==> %s\" % (w.source, w.target))" ] }, { "cell_type": "markdown", "metadata": { "id": "8_itGYa06D5J" }, "source": [ "* Note the `ham[\"g3\"]` expression in the first line.\n", "* The PyCirc object **`ham`** has been overloaded as a dictionary which maps gate and wire names to their reference.\n", "* The following diagram displays examples of typical circuit \n", " representations in the PyCirc package.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": { "id": "kNi7FIqG6D5K" }, "source": [ "## 4x1 Multiplexer Design\n", "* Multiplxers are important circuit elements in electronic design.\n", "* Here is a simple **PyCirc Design Diagram and code** for\n", " a 4x1 Multiplexer circuit (aka MUX2)\n", " \n", " " ] }, { "cell_type": "markdown", "source": [ "```python\n", "# File for MUX2\n", "# input: x3, x2, x1, x0, s2, s1\n", "# output: y\n", "\n", "GATE(\"x0\", type=\"inp\")\n", "GATE(\"x1\", type=\"inp\")\n", "GATE(\"x2\", type=\"inp\")\n", "GATE(\"x3\", type=\"inp\")\n", "GATE(\"s1\", type=\"inp\")\n", "GATE(\"s2\", type=\"inp\")\n", "GATE(\"y\", type=\"out\")\n", "GATE(\"g1\", type=\"not\")\n", "GATE(\"g2\", type=\"not\")\n", "GATE(\"g3\", type=\"and3\")\n", "GATE(\"g4\", type=\"and3\")\n", "GATE(\"g5\", type=\"and3\")\n", "GATE(\"g6\", type=\"and3\")\n", "GATE(\"g7\", type=\"or4\")\n", "\n", "WIRE(\"s1\", \"g1/x\")\n", "WIRE(\"s1\", \"g5/x1\")\n", "WIRE(\"s1\", \"g6/x1\")\n", "WIRE(\"s2\", \"g2/x\")\n", "WIRE(\"s2\", \"g4/x3\")\n", "WIRE(\"s2\", \"g6/x3\")\n", "WIRE(\"x0\", \"g3/x2\")\n", "WIRE(\"x1\", \"g4/x2\")\n", "WIRE(\"x2\", \"g5/x2\")\n", "WIRE(\"x3\", \"g6/x2\")\n", "WIRE(\"g1/y\", \"g3/x1\")\n", "WIRE(\"g1/y\", \"g4/x1\")\n", "WIRE(\"g2/y\", \"g3/x3\")\n", "WIRE(\"g2/y\", \"g5/x3\")\n", "WIRE(\"g3/y\", \"g7/x1\")\n", "WIRE(\"g4/y\", \"g7/x2\")\n", "WIRE(\"g5/y\", \"g7/x3\")\n", "WIRE(\"g6/y\", \"g7/x4\")\n", "WIRE(\"g7/y\", \"y\")\n", "```" ], "metadata": { "id": "Tcdpe9QJz2hE" } }, { "cell_type": "markdown", "metadata": { "id": "lH0of2Ny6D5K" }, "source": [ "* This is long. Took 35 lines of code to define this circuit.\n", "* With **compressed notation** it takes only 15 lines!\n", "* In addition, **compressed notation** can help us understand\n", " better the circuit structure, as it is displayed in one paragraph.\n", "* Remember that this is a clean Python code, so you can use\n", " Python comments, and other Python commands." ] }, { "cell_type": "markdown", "source": [ "```python\n", "# File: mux2.py (compressed version!)\n", "# MUX2 (4x1 Multiplexer)\n", "# input: x3, x2, x1, x0, s2, s1\n", "# output: y\n", "\n", "GATE(\"x<0:3>\", type=\"inp\") # 4 bits\n", "GATE(\"s1;s2\", type=\"inp\") # 2 selectors\n", "GATE(\"y\", type=\"out\")\n", "GATE(\"g1; g2\", type=\"not\")\n", "GATE(\"g<3:6>\", type=\"and3\") # 4 gates of basic type \"and3\"\n", "GATE(\"g7\", type=\"or4\")\n", "\n", "WIRE(\"s1\", \"g1/x; g5/x1; g6/x1\")\n", "WIRE(\"s2\", \"g2/x; g4/x3; g6/x3\") # 3 wires!\n", "WIRE(\"x<0:3>\", \"g<3:6>/x2\")\n", "WIRE(\"g1/y\", \"g3/x1; g4/x1\")\n", "WIRE(\"g2/y\", \"g3/x3; g5/x3\")\n", "WIRE(\"g<3:6>/y\", \"g7/x<1:4>\") # 4 wires\n", "WIRE(\"g7/y\", \"y\")\n", "```" ], "metadata": { "id": "EPNLMqWU1V9_" } }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "YPOUdoQr6D5L", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "7d2d5d58-a365-41fd-8017-aa6f8736e5cd" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "path = ['c:/eda/pycirc/lib', 'd:/cmfl/code/logcirc/lib1', 'https://samyzaf.com/pycirc/lib']\n", "Cell = mux2: Validity check: OK.\n", "Loaded circuit mux2 from: https://samyzaf.com/pycirc/lib/mux2.py\n" ] } ], "source": [ "# get a grip on our MUX2 object \n", "mux2 = load(\"mux2\")" ] }, { "cell_type": "markdown", "metadata": { "id": "j5RcZxl36D5L" }, "source": [ "* Lets print the list of gates in our circuit mux2:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZD54bHk06D5L", "outputId": "4c8f4eb0-ea18-4c8e-fa93-861af4a95110", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(None), depth=0\n", "gate id=26: name=x1, type=inp, value=(None), depth=0\n", "gate id=27: name=x2, type=inp, value=(None), depth=0\n", "gate id=28: name=x3, type=inp, value=(None), depth=0\n", "gate id=29: name=s1, type=inp, value=(None), depth=0\n", "gate id=30: name=s2, type=inp, value=(None), depth=0\n", "gate id=31: name=y, type=out, value=(None), depth=4\n", "gate id=32: name=g1, type=not, value=(y=None), depth=1\n", "gate id=33: name=g2, type=not, value=(y=None), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=None), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=None), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=None), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=None), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=None), depth=3\n" ] } ], "source": [ "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "markdown", "metadata": { "id": "bHBrZrnU6D5L" }, "source": [ "* This is too verbose, but useful for debugging purposes.\n", "* To get more specific info, you may use code like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "LPyMO_8M6D5L", "outputId": "8454e72d-1fb7-4b53-9c8b-61891fd9f328", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Gates list\n", "name = x0, type = inp\n", "name = x1, type = inp\n", "name = x2, type = inp\n", "name = x3, type = inp\n", "name = s1, type = inp\n", "name = s2, type = inp\n", "name = y, type = out\n", "name = g1, type = not\n", "name = g2, type = not\n", "name = g3, type = and3\n", "name = g4, type = and3\n", "name = g5, type = and3\n", "name = g6, type = and3\n", "name = g7, type = or4\n" ] } ], "source": [ "print(\"Gates list\")\n", "for g in mux2.gates:\n", " print(\"name = %s, type = %s\" % (g.name, g.type))" ] }, { "cell_type": "markdown", "metadata": { "id": "zzcIKf6D6D5L" }, "source": [ "## Truth Assignments in PyCirc\n", "* Truth assignment for a boolean formula or a boolean circuit is\n", " a mapping between its boolean variables and the two boolean values $\\{0,1\\}$.\n", "* In PyCirc, trurth assignment is modeled by a Python class called\n", " **Assign**, which is a subclass of the standard Python\n", " dictionary class **dict**.\n", "* An **Assign** object maps a list of gate names to one of three values:\n", " {0, 1, None}.\n", "* The value None indicates uninitialized state.\n", "* Here is a simple example for defining an assignment on four input variables." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ilm2EHV26D5M" }, "outputs": [], "source": [ "a = Assign([\"x1\", \"x2\", \"x3\", \"x4\"], [0,0,1,1])" ] }, { "cell_type": "markdown", "metadata": { "id": "lwURz71k6D5M" }, "source": [ "Here are some code examples for manipulating an Assign object" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UtEvATCz6D5M", "outputId": "42574a6a-3332-4e73-e4d9-401cab1daac6", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1=0, x2=0, x3=1, x4=1\n" ] } ], "source": [ "print(a)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HZNX8hPM6D5M", "outputId": "8b64b202-39a0-4340-b01e-69af811e02a4", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1\n", "x2\n", "x3\n", "x4\n" ] } ], "source": [ "for x in a.names:\n", " print(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "bePneX-p6D5M", "outputId": "5a2f65ff-525d-49c1-bb94-f6d1f2d2a1aa", "colab": { "base_uri": "https://localhost:8080/", "height": 35 } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'0011'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 24 } ], "source": [ "a.bits()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "outputId": "43f3b01c-bcf9-41ba-849b-3e862c749770", "colab": { "base_uri": "https://localhost:8080/", "height": 35 }, "id": "FTofyI6CwCto" }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'001'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 25 } ], "source": [ "a.bits(\"x<1:3>\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "outputId": "6012b5b3-b738-4b6c-b0ad-94a718c715c8", "colab": { "base_uri": "https://localhost:8080/", "height": 35 }, "id": "OFZOfuCUwMGX" }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "'01'" ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" } }, "metadata": {}, "execution_count": 26 } ], "source": [ "a.bits([\"x2\", \"x4\"])" ] }, { "cell_type": "markdown", "source": [ "* An assignment object `a` is callable (i.e., can be called as a function)\n", "* The result of the call `a()` is the list of its bit values." ], "metadata": { "id": "5HSGSYKayk-N" } }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "pbCm4FMd6D5N", "outputId": "3d976a0f-3bc5-46a0-fe31-a333a3af2442", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[0, 0, 1, 1]" ] }, "metadata": {}, "execution_count": 27 } ], "source": [ "a()" ] }, { "cell_type": "markdown", "source": [ "* You can pass bit names in expanded or compressed form:" ], "metadata": { "id": "mli4R50XY_eg" } }, { "cell_type": "code", "execution_count": null, "metadata": { "outputId": "cfe71a3f-dbdc-4d27-c3f3-52f5ea193fc0", "colab": { "base_uri": "https://localhost:8080/" }, "id": "gb5F7bEJwUIK" }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[0, 1]" ] }, "metadata": {}, "execution_count": 28 } ], "source": [ "a([\"x2\",\"x3\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "outputId": "078de5cf-449e-4ce2-b636-80d5afd6a79e", "colab": { "base_uri": "https://localhost:8080/" }, "id": "ARGWIp7mwdr2" }, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[0, 1, 1]" ] }, "metadata": {}, "execution_count": 29 } ], "source": [ "a(\"x<2:4>\")" ] }, { "cell_type": "markdown", "metadata": { "id": "-HmsMRnu6D5N" }, "source": [ "* It is also possible to create an **Assign** object from\n", " keyword/value pairs, just like in a Python dictionary." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "bPhiV7sy6D5N", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "acf8617b-b592-4d54-b564-bf1db5fbd73f" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1=0, x2=0, x3=1, x4=1\n" ] } ], "source": [ "a = Assign.fromKeys(x1=0, x2=0, x3=1, x4=1)\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "id": "ff7cZJdr6D5N" }, "source": [ "* The Assign class also accepts names in compressed form.\n", "* The default of the second argument is `None` for all bits." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "3OsyKTyb6D5N", "outputId": "53a613de-315c-41d1-dae5-703446adad4c", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1=None, x2=None, x3=None, x4=None\n" ] } ], "source": [ "a = Assign(\"x<1:4>\")\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "id": "5hU44FHT6D5O" }, "source": [ "* The None value indicates that the input is uninitialized.\n", "* Use the following to initialize all values to zero (or one)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Bu6mZZUT6D5O", "outputId": "1693d557-b539-4ab6-ff5c-1a7c0e822d00", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1=0, x2=0, x3=0, x4=0\n" ] } ], "source": [ "a = Assign(\"x<1:4>\", 0)\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": { "id": "jgjSbMZk6D5O" }, "source": [ "* The **Assign** class also has a static method **Assign.iter** for creating an iterator for looping over all truth assignments!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "aAr5AYun6D5O", "outputId": "5eabcba3-0366-4d4c-c7dd-ffe79faad476", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1=0, x2=0, s1=0, s2=0\n", "x1=0, x2=0, s1=0, s2=1\n", "x1=0, x2=0, s1=1, s2=0\n", "x1=0, x2=0, s1=1, s2=1\n", "x1=0, x2=1, s1=0, s2=0\n", "x1=0, x2=1, s1=0, s2=1\n", "x1=0, x2=1, s1=1, s2=0\n", "x1=0, x2=1, s1=1, s2=1\n", "x1=1, x2=0, s1=0, s2=0\n", "x1=1, x2=0, s1=0, s2=1\n", "x1=1, x2=0, s1=1, s2=0\n", "x1=1, x2=0, s1=1, s2=1\n", "x1=1, x2=1, s1=0, s2=0\n", "x1=1, x2=1, s1=0, s2=1\n", "x1=1, x2=1, s1=1, s2=0\n", "x1=1, x2=1, s1=1, s2=1\n" ] } ], "source": [ "gates = ['x1', 'x2', 's1', 's2']\n", "for a in Assign.iter(gates):\n", " print(a)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "iW77r7aN6D5O", "outputId": "6d724e38-a2ab-42b9-96e6-39c049a7242e", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "0000\n", "0001\n", "0010\n", "0011\n", "0100\n", "0101\n", "0110\n", "0111\n", "1000\n", "1001\n", "1010\n", "1011\n", "1100\n", "1101\n", "1110\n", "1111\n" ] } ], "source": [ "gates = ['x1', 'x2', 's1', 's2']\n", "for a in Assign.iter(gates):\n", " print(a.bits())" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "VC9mOXBM6D5O", "outputId": "65553eea-8606-406e-f5c9-78288630bc96", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[0, 0, 0, 0]\n", "[0, 0, 0, 1]\n", "[0, 0, 1, 0]\n", "[0, 0, 1, 1]\n", "[0, 1, 0, 0]\n", "[0, 1, 0, 1]\n", "[0, 1, 1, 0]\n", "[0, 1, 1, 1]\n", "[1, 0, 0, 0]\n", "[1, 0, 0, 1]\n", "[1, 0, 1, 0]\n", "[1, 0, 1, 1]\n", "[1, 1, 0, 0]\n", "[1, 1, 0, 1]\n", "[1, 1, 1, 0]\n", "[1, 1, 1, 1]\n" ] } ], "source": [ "gates = ['x1', 'x2', 's1', 's2']\n", "for a in Assign.iter(gates):\n", " bitlist = a()\n", " print(bitlist)" ] }, { "cell_type": "markdown", "metadata": { "id": "8wde80Ym6D5P" }, "source": [ "## How to initialize a PyCirc object?\n", "* When you create a new Pycirc circuit, none of the input gates are initialized.\n", "* All gate values equal None." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false, "id": "jk1fdKRI6D5P", "outputId": "0dd7d94b-cc28-4a26-ed2e-e9aa31326fbf", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(None), depth=0\n", "gate id=26: name=x1, type=inp, value=(None), depth=0\n", "gate id=27: name=x2, type=inp, value=(None), depth=0\n", "gate id=28: name=x3, type=inp, value=(None), depth=0\n", "gate id=29: name=s1, type=inp, value=(None), depth=0\n", "gate id=30: name=s2, type=inp, value=(None), depth=0\n", "gate id=31: name=y, type=out, value=(None), depth=4\n", "gate id=32: name=g1, type=not, value=(y=None), depth=1\n", "gate id=33: name=g2, type=not, value=(y=None), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=None), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=None), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=None), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=None), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=None), depth=3\n" ] } ], "source": [ "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "markdown", "metadata": { "id": "4zJ686CF6D5P" }, "source": [ "* In order to run a circuit, we first need to initialize its input gates.\n", "* Lets assign values to mux2 input gates, and check again the values of its gates.\n", "* The **PyCirc `set`** method is used" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false, "id": "vlCTrjP86D5P", "outputId": "094390b8-a515-4605-807a-1c24c95b0891", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(0), depth=0\n", "gate id=26: name=x1, type=inp, value=(1), depth=0\n", "gate id=27: name=x2, type=inp, value=(0), depth=0\n", "gate id=28: name=x3, type=inp, value=(1), depth=0\n", "gate id=29: name=s1, type=inp, value=(1), depth=0\n", "gate id=30: name=s2, type=inp, value=(0), depth=0\n", "gate id=31: name=y, type=out, value=(None), depth=4\n", "gate id=32: name=g1, type=not, value=(y=None), depth=1\n", "gate id=33: name=g2, type=not, value=(y=None), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=None), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=None), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=None), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=None), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=None), depth=3\n" ] } ], "source": [ "a = Assign.fromKeys(x3=1, x2=0, x1=1, x0=0, s2=0, s1=1) \n", "mux2.set(a) # Initilizing the cel MUX2 with assignment a\n", "\n", "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "markdown", "metadata": { "id": "qKfHF0aC6D5P" }, "source": [ "* We see that all input gates are initialized with boolean values.\n", "* All other gates value are still None.\n", "* **PyCirc** has a step method which triggers\n", " the logic function of every logic gate whose inputs are\n", " initialized to boolean values." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "i8Fz813t6D5P", "outputId": "a98a39ef-456e-44d7-adf3-a86907479dcc", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(0), depth=0\n", "gate id=26: name=x1, type=inp, value=(1), depth=0\n", "gate id=27: name=x2, type=inp, value=(0), depth=0\n", "gate id=28: name=x3, type=inp, value=(1), depth=0\n", "gate id=29: name=s1, type=inp, value=(1), depth=0\n", "gate id=30: name=s2, type=inp, value=(0), depth=0\n", "gate id=31: name=y, type=out, value=(None), depth=4\n", "gate id=32: name=g1, type=not, value=(y=0), depth=1\n", "gate id=33: name=g2, type=not, value=(y=1), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=None), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=None), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=None), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=0), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=None), depth=3\n" ] } ], "source": [ "mux2.step()\n", "\n", "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "markdown", "metadata": { "id": "c78Ncwus6D5Q" }, "source": [ "* We now see that also gates g1 and g2 are initialized to boolean values.\n", "* From the diagram of MUX2 we see that $g_1 = \\neg s_1$.\n", "* Since $s_1=1$, we get $g_1=0$.\n", "* From the diagram we see that $g_2 = \\neg s_2= \\neg 0 = 1$.\n", "* All other gates are still in `None` state.\n", "* To reach the output gate, we need to apply the `step` method\n", " three more times.\n", "* The number of times depends on the **depth** of the circuit." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "etzLW1h56D5Q", "outputId": "af24857a-9911-4c95-9e6d-86a5b89dfc89", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(0), depth=0\n", "gate id=26: name=x1, type=inp, value=(1), depth=0\n", "gate id=27: name=x2, type=inp, value=(0), depth=0\n", "gate id=28: name=x3, type=inp, value=(1), depth=0\n", "gate id=29: name=s1, type=inp, value=(1), depth=0\n", "gate id=30: name=s2, type=inp, value=(0), depth=0\n", "gate id=31: name=y, type=out, value=(None), depth=4\n", "gate id=32: name=g1, type=not, value=(y=0), depth=1\n", "gate id=33: name=g2, type=not, value=(y=1), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=0), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=0), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=0), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=0), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=None), depth=3\n" ] } ], "source": [ "mux2.step()\n", "\n", "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "O96BZJXN6D5Q", "outputId": "5fc18289-bb30-4ef2-d77b-4b487744a359", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(0), depth=0\n", "gate id=26: name=x1, type=inp, value=(1), depth=0\n", "gate id=27: name=x2, type=inp, value=(0), depth=0\n", "gate id=28: name=x3, type=inp, value=(1), depth=0\n", "gate id=29: name=s1, type=inp, value=(1), depth=0\n", "gate id=30: name=s2, type=inp, value=(0), depth=0\n", "gate id=31: name=y, type=out, value=(None), depth=4\n", "gate id=32: name=g1, type=not, value=(y=0), depth=1\n", "gate id=33: name=g2, type=not, value=(y=1), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=0), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=0), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=0), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=0), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=0), depth=3\n" ] } ], "source": [ "mux2.step()\n", "\n", "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Zmz7Iqoe6D5Q", "outputId": "89c88bad-f419-471f-8c96-f566085481ce", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=25: name=x0, type=inp, value=(0), depth=0\n", "gate id=26: name=x1, type=inp, value=(1), depth=0\n", "gate id=27: name=x2, type=inp, value=(0), depth=0\n", "gate id=28: name=x3, type=inp, value=(1), depth=0\n", "gate id=29: name=s1, type=inp, value=(1), depth=0\n", "gate id=30: name=s2, type=inp, value=(0), depth=0\n", "gate id=31: name=y, type=out, value=(0), depth=4\n", "gate id=32: name=g1, type=not, value=(y=0), depth=1\n", "gate id=33: name=g2, type=not, value=(y=1), depth=1\n", "gate id=34: name=g3, type=and3, value=(y=0), depth=2\n", "gate id=35: name=g4, type=and3, value=(y=0), depth=2\n", "gate id=36: name=g5, type=and3, value=(y=0), depth=2\n", "gate id=37: name=g6, type=and3, value=(y=0), depth=1\n", "gate id=38: name=g7, type=or4, value=(y=0), depth=3\n" ] } ], "source": [ "mux2.step()\n", "\n", "for g in mux2.gates:\n", " print(g)" ] }, { "cell_type": "markdown", "metadata": { "id": "JPd3UuKZ6D5R" }, "source": [ "## Getting the circuit output\n", "* After initializing the circuit, we can run it, get the output, and print it.\n", "* Now all gates are initialize and we can extract the circuit outcome by the **PyCirc get method**." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "1mzRkgQ86D5R", "outputId": "da6a77e2-3777-4694-9849-7f9ea6d76a59", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "y=0\n" ] } ], "source": [ "o = mux2.get()\n", "print(o)" ] }, { "cell_type": "markdown", "metadata": { "id": "ilaO1Xz86D5R" }, "source": [ "* This is of course not the normal way to use the circuit for computation.\n", "* It is intended for exhibiting the way a logical circuit operates and for debugging.\n", "* The straightforward way to compute the output of a circuit is quite simple" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "gNxq5SGb6D5R", "outputId": "a80c3734-3e5e-4896-ddbe-8b8cf0a13a7f", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Input: x3=1, x2=0, x1=1, x0=0, s2=0, s1=1\n", "Output: y=0\n" ] } ], "source": [ "o = mux2(a)\n", "print(\"Input:\", a)\n", "print(\"Output:\", o)" ] }, { "cell_type": "markdown", "metadata": { "id": "JkWXD5kA6D5R" }, "source": [ "* In Python it is very easy to turn an object to also serve as a \n", " function (\"callable object\").\n", "* The circuit handle mux2 serves as a function too!\n", "* So in one line o = mux2(a) we perform all the\n", " follwoing things\n", " 1. Initialize the circuit input gates with the assignment `a`.\n", " 2. Perform the `step()` method repeatedly\n", " in order to reach the output gates .\n", " 3. Return the circuit output via the `get` method." ] }, { "cell_type": "markdown", "metadata": { "id": "RH9oo6Rp6D5R" }, "source": [ "* In some circumstances, we may have to initialize the input gates\n", " one by one.\n", "* In such cases we can use the `run()` method for performing\n", " the computation, and the use the `get()` method for obtaining\n", " the circuit output" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZgPKjhF26D5S", "outputId": "8c070c7c-b0b8-43ad-8e73-f7c32ebe236a", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "The output is:\n", "y=0\n" ] } ], "source": [ "# Obtain handles to MUX2 input gates\n", "x0 = mux2[\"x0\"] # mux2[\"x0\"] is a handle to gate \"x0\"\n", "x1 = mux2[\"x1\"]\n", "x2 = mux2[\"x2\"]\n", "x3 = mux2[\"x3\"]\n", "s1 = mux2[\"s1\"]\n", "s2 = mux2[\"s2\"]\n", "\n", "# Now we have handles to all MUX2 input gates, we can do\n", "# all kind of things with them:\n", "\n", "x0.set(0)\n", "x1.set(0)\n", "x2.set(1)\n", "x3.set(1)\n", "s1.set(1)\n", "s2.set(0)\n", "\n", "mux2.run() # Running the circuit\n", "\n", "o = mux2.get() # Obtaining the output\n", "print(\"The output is:\")\n", "print(o)" ] }, { "cell_type": "markdown", "metadata": { "id": "ZY7nLHwq6D5S" }, "source": [ "* A shorter way to do the same thing is:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nSQ4Hx636D5S", "outputId": "5ba3abe2-5d06-4fcf-9585-c20aa2b2f1e6", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "The output is:\n", "y=0\n" ] } ], "source": [ "mux2[\"x0\"].set(0) # mux2[\"x0\"] is a handle to gate \"x0\"\n", "mux2[\"x1\"].set(0)\n", "mux2[\"x2\"].set(1)\n", "mux2[\"x3\"].set(1)\n", "mux2[\"s1\"].set(1)\n", "mux2[\"s2\"].set(0)\n", "\n", "mux2.run() # Running the circuit\n", "\n", "o = mux2.get() # Obtaining the output\n", "print(\"The output is:\")\n", "print(o)" ] }, { "cell_type": "markdown", "metadata": { "id": "csx0nbpC6D5S" }, "source": [ "* The output is also a Python dictionary which maps the circuit\n", " output names to their repective values.\n", "* In our example, the circuit has only on output 'y'.\n", "* Thus, we get a one key assignment y=0.\n", "* The circuit object can also be used as a function for performing\n", " the circuit action.\n", "* So instead of running three different commands\n", "```python\n", " mux2.set(a)\n", " mux2.run()\n", " o = mux2.get()\n", "```\n", "We only need one command\n", "```python\n", " o = mux2(a)\n", "```\n", "* However, in some cases we need to go step by step,\n", " so we need to go a little bit slower.\n", "* For example, in order to observe intermediate values of the\n", " logic gates, we may need to initialize the circuit with the\n", " set method and proceed to the output step by step." ] }, { "cell_type": "markdown", "metadata": { "id": "zA7Oa8Dw6D5T" }, "source": [ "## Assignment Iterator\n", "* The **Assign.iter** iterator is very useful for producing all possible outcomes of a given circuit.\n", "* Here is a code for producing all the outcomes of the circuit **HAM**" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PSgsML276D5T", "outputId": "7a753732-5e2a-44b9-c437-aa5fe08c347b", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Input: ['x1', 'x2', 'x3', 'x4']\n", "Output: ['y1', 'y2', 'y3']\n", "ham: 0000 => 000\n", "ham: 0001 => 101\n", "ham: 0010 => 000\n", "ham: 0011 => 100\n", "ham: 0100 => 111\n", "ham: 0101 => 010\n", "ham: 0110 => 111\n", "ham: 0111 => 011\n", "ham: 1000 => 000\n", "ham: 1001 => 101\n", "ham: 1010 => 000\n", "ham: 1011 => 000\n", "ham: 1100 => 111\n", "ham: 1101 => 010\n", "ham: 1110 => 011\n", "ham: 1111 => 111\n" ] } ], "source": [ "Input = [x.name for x in ham.input] # we need the names of the inputs\n", "Output = [y.name for y in ham.output] # we also want to print the output names\n", "print(\"Input:\", Input)\n", "print(\"Output:\", Output)\n", "\n", "for a in Assign.iter(Input):\n", " o = ham(a)\n", " print(\"ham: %s => %s\" % (a.bits(), o.bits()))" ] }, { "cell_type": "markdown", "metadata": { "id": "3vlNOIsD6D5T" }, "source": [ "* Here is a code snippet for generating the truth table for the cell **HAM**." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Tuj7pqJw6D5T", "outputId": "e310cab8-5e26-46e1-fd2c-4ab51961133e", "colab": { "base_uri": "https://localhost:8080/" } }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x1 x2 x3 x4 y1 y2 y3\n", "0 0 0 0 0 0 0 \n", "0 0 0 1 1 0 1 \n", "0 0 1 0 0 0 0 \n", "0 0 1 1 1 0 0 \n", "0 1 0 0 1 1 1 \n", "0 1 0 1 0 1 0 \n", "0 1 1 0 1 1 1 \n", "0 1 1 1 0 1 1 \n", "1 0 0 0 0 0 0 \n", "1 0 0 1 1 0 1 \n", "1 0 1 0 0 0 0 \n", "1 0 1 1 0 0 0 \n", "1 1 0 0 1 1 1 \n", "1 1 0 1 0 1 0 \n", "1 1 1 0 0 1 1 \n", "1 1 1 1 1 1 1 \n" ] } ], "source": [ "Input = [x.name for x in ham.input]\n", "Output = [y.name for y in ham.output]\n", "names = Input + Output\n", "head = \" \".join(names)\n", "print(head)\n", "\n", "for a in Assign.iter(Input):\n", " o = ham(a)\n", " bits = tuple(a() + o())\n", " line = len(bits) * \"%d \" % bits\n", " print(line)" ] }, { "cell_type": "markdown", "metadata": { "id": "hJbqYg3q6D5T" }, "source": [ "* Two circuits are called **equivalent** if they have the same boolean function.\n", "* Here is a **PyCirc** code for implementing an algorithm that checks\n", " if two circuits are equivalent." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "InbPvYIo6D5U" }, "outputs": [], "source": [ "# Check if two logic circuites are equivalent\n", "# that is: have identical truth tables.\n", "\n", "def is_equiv(circ1, circ2):\n", " Inp1 = [g.name for g in circ1.input]\n", " Inp2 = [g.name for g in circ2.input]\n", " if not len(Inp1) == len(Inp2):\n", " return False\n", " for a in Assign.iter(Inp1):\n", " bits1 = circ1(a).bits()\n", " bits2 = circ2(a).bits()\n", " if not bits1 == bits2:\n", " print(\"a=%s, out1=%s, out2=%s\" % (a, bits1, bits2))\n", " return False\n", "\n", " return True" ] }, { "cell_type": "markdown", "source": [ "* In our cell library we have two definitions of the cell **MUX2**\n", " * A long definition: [\"mux2.py\"](http://samyzaf.com/pycirc/lib/mux2.py)\n", " * A compressed definition: [\"mux2b.py\"](http://samyzaf.com/pycirc/lib/mux2b.py)\n", "* Having two definitions for the same cell is not a good idea, but in some circumstances is required for testing and research.\n", "* In such cases it is a must that we have a tool for checking that indeed these definitions are equivalent! Or else we're in deep trouble ...\n", "* This is where we use our **is_equiv** utility:" ], "metadata": { "id": "Zhk3_JhZ2pl6" } }, { "cell_type": "code", "source": [ "mux2 = load(\"mux2\")\n", "mux2b = load(\"mux2b\")\n", "\n", "print(\"Checking equivalence:\")\n", "is_equiv(mux2, mux2b)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "59C0BVTN6TJ6", "outputId": "1a7f63d7-572d-4556-cda6-ae609a5ddb80" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "path = ['c:/eda/pycirc/lib', 'd:/cmfl/code/logcirc/lib1', 'https://samyzaf.com/pycirc/lib']\n", "Cell = mux2: Validity check: OK.\n", "Loaded circuit mux2 from: https://samyzaf.com/pycirc/lib/mux2.py\n", "path = ['c:/eda/pycirc/lib', 'd:/cmfl/code/logcirc/lib1', 'https://samyzaf.com/pycirc/lib']\n", "Cell = mux2b: Validity check: OK.\n", "Loaded circuit mux2b from: https://samyzaf.com/pycirc/lib/mux2b.py\n", "Checking equivalence:\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "True" ] }, "metadata": {}, "execution_count": 49 } ] }, { "cell_type": "markdown", "metadata": { "id": "luXJPXBy6D5U" }, "source": [ "## 8x1 Multiplexer Design\n", "* Now we build an 8x1 Multiplexer circuit (aka **MUX3**) by using\n", " our **MUX2** circuit as one of its building blocks\n", " (two instance of MUX2 are needed).\n", "* We also need one instance of 2x1 Multiplexer (aka **MUX1**),\n", " which we leave to the student as an easy exercise.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": { "id": "IMN7UrHa6D5U" }, "source": [ "* As you can see from the diagram we now have 8 inputs bits:\n", " x0, x1, x2, x3, x4, x5, x6, x7, \n", " and one output bit: y.\n", "* We only need 3 logic gates: g1, g2, and g3.\n", " * g1 and g2 are two instances of MUX2,\n", " * g3 is an instance of MUX1.\n", "* Here is the code for creating a PyCirc MUX3 object.\n", " * Notice that this time we are using compressed\n", " notation technique for creating the 11 input gates in one line!\n", " * The compressed notation can be used everywhere in PyCirc and\n", " saves a lot of typing!\n", " * Note that this is the python code. Within a circuit file\n", " you must remove the openning Define and closing EndDef commands!" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "KkIIjyn36D5U", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "21afe1b3-a00d-4e3f-cd16-028aad445cdd" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = mux3: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 50 } ], "source": [ "need(\"mux2\")\n", "\n", "Define(\"mux3\")\n", "GATE(\"x<0:7>; s<1:3>\", type=\"inp\")\n", "GATE(\"y\", type=\"out\")\n", "\n", "GATE(\"g1\", type=\"mux2\")\n", "GATE(\"g2\", type=\"mux2\")\n", "GATE(\"g3\", type=\"mux1\")\n", "\n", "WIRE(\"x0\", \"g1/x0\")\n", "WIRE(\"x1\", \"g1/x1\")\n", "WIRE(\"x2\", \"g1/x2\")\n", "WIRE(\"x3\", \"g1/x3\")\n", "WIRE(\"x4\", \"g2/x0\")\n", "WIRE(\"x5\", \"g2/x1\")\n", "WIRE(\"x6\", \"g2/x2\")\n", "WIRE(\"x7\", \"g2/x3\")\n", "WIRE(\"s2\", \"g1/s1\")\n", "WIRE(\"s2\", \"g2/s1\")\n", "WIRE(\"s3\", \"g1/s2\")\n", "WIRE(\"s3\", \"g2/s2\")\n", "WIRE(\"s1\", \"g3/s1\")\n", "WIRE(\"g1/y\", \"g3/x0\")\n", "WIRE(\"g2/y\", \"g3/x1\")\n", "WIRE(\"g3/y\", \"y\")\n", "EndDef()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "7-v4biNt6D5U", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "f2eece60-ce55-49db-df81-24e16e5c0624" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "gate id=67: name=x0, type=inp, value=(None), depth=0\n", "gate id=68: name=x1, type=inp, value=(None), depth=0\n", "gate id=69: name=x2, type=inp, value=(None), depth=0\n", "gate id=70: name=x3, type=inp, value=(None), depth=0\n", "gate id=71: name=x4, type=inp, value=(None), depth=0\n", "gate id=72: name=x5, type=inp, value=(None), depth=0\n", "gate id=73: name=x6, type=inp, value=(None), depth=0\n", "gate id=74: name=x7, type=inp, value=(None), depth=0\n", "gate id=75: name=s1, type=inp, value=(None), depth=0\n", "gate id=76: name=s2, type=inp, value=(None), depth=0\n", "gate id=77: name=s3, type=inp, value=(None), depth=0\n", "gate id=78: name=y, type=out, value=(None), depth=3\n", "gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", "gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", "gate id=81: name=g3, type=mux1, value=(y=None), depth=2\n" ] } ], "source": [ "mux3 = PyCirc[\"mux3\"]\n", "\n", "for g in mux3.gates:\n", " print(g)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "bfIHfruc6D5V", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "3c79a382-9d67-4552-9ecd-c30cf5de88ba" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x0 = None\n", "x1 = None\n", "x2 = None\n", "x3 = None\n", "x4 = None\n", "x5 = None\n", "x6 = None\n", "x7 = None\n", "s1 = None\n", "s2 = None\n", "s3 = None\n", "y = None\n", "g1 = y=None\n", "g2 = y=None\n", "g3 = y=None\n" ] } ], "source": [ "mux3 = PyCirc[\"mux3\"]\n", "\n", "for g in mux3.gates:\n", " print(\"%s = %s\" % (g.name, g.get()))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "id": "X-RQfMHU6D5V", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "2644837f-c693-4e2f-c665-855b9d9fb9db" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "wire id=83:\n", " source=gate id=67: name=x0, type=inp, value=(None), depth=0\n", " target=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x0\n", "wire id=84:\n", " source=gate id=68: name=x1, type=inp, value=(None), depth=0\n", " target=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x1\n", "wire id=85:\n", " source=gate id=69: name=x2, type=inp, value=(None), depth=0\n", " target=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x2\n", "wire id=86:\n", " source=gate id=70: name=x3, type=inp, value=(None), depth=0\n", " target=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x3\n", "wire id=87:\n", " source=gate id=71: name=x4, type=inp, value=(None), depth=0\n", " target=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x0\n", "wire id=88:\n", " source=gate id=72: name=x5, type=inp, value=(None), depth=0\n", " target=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x1\n", "wire id=89:\n", " source=gate id=73: name=x6, type=inp, value=(None), depth=0\n", " target=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x2\n", "wire id=90:\n", " source=gate id=74: name=x7, type=inp, value=(None), depth=0\n", " target=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=x3\n", "wire id=91:\n", " source=gate id=76: name=s2, type=inp, value=(None), depth=0\n", " target=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=s1\n", "wire id=92:\n", " source=gate id=76: name=s2, type=inp, value=(None), depth=0\n", " target=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=s1\n", "wire id=93:\n", " source=gate id=77: name=s3, type=inp, value=(None), depth=0\n", " target=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=s2\n", "wire id=94:\n", " source=gate id=77: name=s3, type=inp, value=(None), depth=0\n", " target=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " source=None\n", " target=s2\n", "wire id=95:\n", " source=gate id=75: name=s1, type=inp, value=(None), depth=0\n", " target=gate id=81: name=g3, type=mux1, value=(y=None), depth=2\n", " source=None\n", " target=s1\n", "wire id=96:\n", " source=gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", " target=gate id=81: name=g3, type=mux1, value=(y=None), depth=2\n", " source=y\n", " target=x0\n", "wire id=97:\n", " source=gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", " target=gate id=81: name=g3, type=mux1, value=(y=None), depth=2\n", " source=y\n", " target=x1\n", "wire id=98:\n", " source=gate id=81: name=g3, type=mux1, value=(y=None), depth=2\n", " target=gate id=78: name=y, type=out, value=(None), depth=3\n", " source=y\n", " target=None\n" ] } ], "source": [ "for w in mux3.wires:\n", " print(w)" ] }, { "cell_type": "markdown", "metadata": { "id": "HdGaY1RJ6D5V" }, "source": [ "* This is quite verbose and not too helpful except for debugging.\n", "* We can extract a more focused output with code like this." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZWorzsBi6D5V", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "8da69aec-c216-4417-8bff-99ede296c83a" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x0 => g1/x0\n", "x1 => g1/x1\n", "x2 => g1/x2\n", "x3 => g1/x3\n", "x4 => g2/x0\n", "x5 => g2/x1\n", "x6 => g2/x2\n", "x7 => g2/x3\n", "s2 => g1/s1\n", "s2 => g2/s1\n", "s3 => g1/s2\n", "s3 => g2/s2\n", "s1 => g3/s1\n", "g1/y => g3/x0\n", "g2/y => g3/x1\n", "g3/y => y\n" ] } ], "source": [ "for w in mux3.wires:\n", " print(\"%s => %s\" % (w.source, w.target))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "id": "-MSpcpMj6D5V", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "a73f00a9-f260-4bda-92f7-98a9e4db93d6" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "x0 = 0\n", "x1 = 0\n", "x2 = 0\n", "x3 = 0\n", "x4 = 1\n", "x5 = 0\n", "x6 = 0\n", "x7 = 0\n", "s1 = 0\n", "s2 = 0\n", "s3 = 0\n", "gate id=75: name=s1, type=inp, value=(0), depth=0\n", "gate id=79: name=g1, type=mux2, value=(y=None), depth=1\n", "gate id=80: name=g2, type=mux2, value=(y=None), depth=1\n", "Depth = 3\n" ] } ], "source": [ "a = Assign(\"x<0:7> ; s<1:3>\", \"00001000\" + \"000\")\n", "mux3.set(a)\n", "for x in mux3.input:\n", " print(\"%s = %d\" % (x.name, x.value))\n", " \n", "g3 = mux3[\"g3\"]\n", "\n", "for g in mux3.in_gates(g3):\n", " print(g)\n", "print(\"Depth =\", mux3.depth)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "tjlLvQh66D5V", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "8f5c5f37-2da5-4b40-a068-1e7b1d630f01" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Input:\n", "x0=0, x1=0, x2=0, x3=0, x4=0, x5=0, x6=1, x7=1, s1=1, s2=1, s3=1\n", "Output:\n", "y=1\n" ] } ], "source": [ "a = Assign(\"x<0:7> ; s<1:3>\", \"00000011\" + \"111\")\n", "print(\"Input:\")\n", "print(a)\n", "o = mux3(a)\n", "print(\"Output:\")\n", "print(o)" ] }, { "cell_type": "markdown", "metadata": { "id": "ehGxPdiO6D5W" }, "source": [ "## 3-bits Adder Design\n", "* Here is a simple design for 3 bits adder with carry in (cin) and\n", " carry out (cout) bits\n", " \n", " \n", " " ] }, { "cell_type": "markdown", "source": [ "* This circuit acceps three types of input\n", " * two binary numbers: $(a_2,a_1,a_0)$, $(b_2,b_1,b_0)$\n", " * a carry in bit: `cin`.\n", "* Its output $(y_2,y_1,y_0)$ is the binary sum of the two numbers\n", " (with the carry added).\n", "* In case of addition overflow,\n", " we need a carry out (cout) output bit as well.\n", "* The following **PyCirc** code is the **PyCirc** model for the **ADDER3** cell." ], "metadata": { "id": "6BuYU8rW6We2" } }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UHbu5JV86D5W", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "c87def63-b75d-41f2-88b5-97a1a7dbc0ee" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = adder3: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 57 } ], "source": [ "# ADDER3\n", "# Input: a2, a1, a0, b2, b1, b0, cin\n", "# Output: y2, y1, y0, cout\n", "\n", "Define(\"adder3\")\n", "GATE(\"a2\", type=\"inp\")\n", "GATE(\"a1\", type=\"inp\")\n", "GATE(\"a0\", type=\"inp\")\n", "\n", "GATE(\"b2\", type=\"inp\")\n", "GATE(\"b1\", type=\"inp\")\n", "GATE(\"b0\", type=\"inp\")\n", "\n", "GATE(\"cin\", type=\"inp\")\n", "\n", "GATE(\"y2\", type=\"out\")\n", "GATE(\"y1\", type=\"out\")\n", "GATE(\"y0\", type=\"out\")\n", "\n", "GATE(\"cout\", type=\"out\")\n", "\n", "GATE(\"g1\", type=\"xor2\")\n", "GATE(\"g2\", type=\"xor2\")\n", "GATE(\"g3\", type=\"xor2\")\n", "GATE(\"g4\", type=\"mux1\")\n", "GATE(\"g5\", type=\"mux1\")\n", "GATE(\"g6\", type=\"mux1\")\n", "GATE(\"g7\", type=\"xor2\")\n", "GATE(\"g8\", type=\"xor2\")\n", "GATE(\"g9\", type=\"xor2\")\n", "\n", "WIRE(\"a2\", \"g3/x2\")\n", "WIRE(\"a2\", \"g6/x0\")\n", "WIRE(\"a1\", \"g2/x2\")\n", "WIRE(\"a0\", \"g1/x2\")\n", "WIRE(\"a0\", \"g4/x0\")\n", "WIRE(\"b2\", \"g3/x1\")\n", "WIRE(\"b1\", \"g2/x1\")\n", "WIRE(\"b1\", \"g5/x0\")\n", "WIRE(\"b0\", \"g4/x1\")\n", "WIRE(\"b0\", \"g9/x2\")\n", "WIRE(\"cin\", \"g1/x1\")\n", "WIRE(\"g1/y\", \"g4/s1\")\n", "WIRE(\"g1/y\", \"g9/x1\")\n", "WIRE(\"g2/y\", \"g5/s1\")\n", "WIRE(\"g2/y\", \"g8/x2\")\n", "WIRE(\"g3/y\", \"g6/s1\")\n", "WIRE(\"g3/y\", \"g7/x2\")\n", "WIRE(\"g4/y\", \"g5/x1\")\n", "WIRE(\"g4/y\", \"g8/x1\")\n", "WIRE(\"g5/y\", \"g6/x1\")\n", "WIRE(\"g5/y\", \"g7/x1\")\n", "WIRE(\"g6/y\", \"cout\")\n", "WIRE(\"g7/y\", \"y2\")\n", "WIRE(\"g8/y\", \"y1\")\n", "WIRE(\"g9/y\", \"y0\")\n", "EndDef()" ] }, { "cell_type": "markdown", "metadata": { "id": "52spLRpQ6D5W" }, "source": [ "* This is the compressed version of this code.\n", "* The number of lines was reduced by half! (from 46 to 23)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "N8BPSOfj6D5W", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "8032c713-a9d0-4995-b5e4-561f3a13dcf6" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = adder3: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 58 } ], "source": [ "# ADDER3, compressed version\n", "# Input: a2, a1, a0, b2, b1, b0, cin\n", "# Output: y2, y1, y0, cout\n", "\n", "Define(\"adder3\")\n", "GATE(\"a<2:0>\", type=\"inp\")\n", "GATE(\"b<2:0>\", type=\"inp\")\n", "GATE(\"cin\", type=\"inp\")\n", "GATE(\"y<2:0>\", type=\"out\")\n", "GATE(\"cout\", type=\"out\")\n", "\n", "GATE(\"g<1:3,7:9>\", type=\"xor2\")\n", "GATE(\"g<4:6>\", type=\"mux1\")\n", "\n", "WIRE(\"a2\", \"g3/x2; g6/x0\")\n", "WIRE(\"a1\", \"g2/x2\")\n", "WIRE(\"a0\", \"g1/x2; g4/x0\")\n", "WIRE(\"b2\", \"g3/x1\")\n", "WIRE(\"b1\", \"g2/x1; g5/x0\")\n", "WIRE(\"b0\", \"g4/x1; g9/x2\")\n", "WIRE(\"cin\", \"g1/x1\")\n", "WIRE(\"g1/y\", \"g4/s1; g9/x1\")\n", "WIRE(\"g2/y\", \"g5/s1; g8/x2\")\n", "WIRE(\"g3/y\", \"g6/s1; g7/x2\")\n", "WIRE(\"g4/y\", \"g5/x1; g8/x1\")\n", "WIRE(\"g5/y\", \"g6/x1; g7/x1\")\n", "WIRE(\"g6/y\", \"cout\")\n", "WIRE(\"g<7:9>/y\", \"y<2:0>\")\n", "EndDef()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JhzPL6CN6D5X" }, "outputs": [], "source": [ "adder3 = PyCirc[\"adder3\"]" ] }, { "cell_type": "markdown", "metadata": { "id": "HMY9I8pY6D5X" }, "source": [ "* Lets test our adder by verifying that\n", "```\n", "011+011=110\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "yQ3RQYD16D5X", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "82ab4162-af67-4e0e-b2c4-868fcedf2ff3" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "011 + 011 = 110 : cin=0 cout=0\n" ] } ], "source": [ "bits = \"011\" + \"011\" + \"0\"\n", "a = Assign(\"a<2:0>; b<2:0>; cin\", bits)\n", "o = adder3(a)\n", "cin = a[\"cin\"]\n", "cout = o[\"cout\"]\n", "A = a.bits(\"a<2:0>\")\n", "B = a.bits(\"b<2:0>\")\n", "Y = o.bits(\"y<2:0>\")\n", "print(\"%s + %s = %s : cin=%s cout=%s\" % (A,B,Y,cin,cout))" ] }, { "cell_type": "markdown", "metadata": { "id": "K78dzlbc6D5X" }, "source": [ "## ADDER9 - 9-bits Adder Design\n", "* Here is a simple **PyCirc Design Diagram** for the standard 9-bits adder with a carry in (cin) and carry out (cout) bits.\n", "* It uses 3 gates g1, g2, g3, of type **ADDER3** which are chained by their cout/cin pins.\n", "* **Input:** `a<8:0> + b<8:0> + cin`\n", "* **Output:** `y<8:0> + cout`\n", " \n", " " ] }, { "cell_type": "markdown", "metadata": { "id": "kSwpeV6Q6D5Y" }, "source": [ "* Here is a compressed **PyCirc** code for **ADDER9**:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "CjizQoMi6D5Y", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "cfebc286-984d-4b69-ded1-134e71b87704" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = adder9: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 61 } ], "source": [ "# ADDER9\n", "# Input: a8, a7, a6, a5, a4, a3, a2, a1, a0, b8, b7, b6, b5, b4, b3, b2, b1, b0, cin\n", "# Output: y8, y7, y6, y5, y4, y3, y2, y1, y0, cout\n", "\n", "need(\"adder3\")\n", "\n", "Define(\"adder9\")\n", "GATE(\"a<8:0>;b<8:0>\", type=\"inp\")\n", "GATE(\"cin\", type=\"inp\")\n", "GATE(\"y<8:0>\", type=\"out\")\n", "GATE(\"cout\", type=\"out\")\n", "GATE(\"g1\", type=\"adder3\") # First ADDER3 gate\n", "GATE(\"g2\", type=\"adder3\") # Second ADDER3 gate\n", "GATE(\"g3\", type=\"adder3\") # Third ADDER3 gate\n", "\n", "WIRE(\"a<0:2>\", \"g1/a<0:2>\"),\n", "WIRE(\"a<3:5>\", \"g2/a<0:2>\"),\n", "WIRE(\"a<6:8>\", \"g3/a<0:2>\"),\n", "WIRE(\"b<0:2>\", \"g1/b<0:2>\"),\n", "WIRE(\"b<3:5>\", \"g2/b<0:2>\"),\n", "WIRE(\"b<6:8>\", \"g3/b<0:2>\"),\n", "WIRE(\"cin\", \"g1/cin\"),\n", "WIRE(\"g1/cout\", \"g2/cin\"),\n", "WIRE(\"g2/cout\", \"g3/cin\"),\n", "WIRE(\"g3/cout\", \"cout\"),\n", "WIRE(\"g1/y<0:2>\", \"y<0:2>\"),\n", "WIRE(\"g2/y<0:2>\", \"y<3:5>\"),\n", "WIRE(\"g3/y<0:2>\", \"y<6:8>\"),\n", "EndDef()\n" ] }, { "cell_type": "markdown", "metadata": { "id": "CIJBQJwE6D5Y" }, "source": [ "* The `full_run` utility can be used for traversing all input/output pairs.\n", "* Interactively.\n", "* To stop: press \"q\"." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "OeDsrurC6D5Y", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "ed359698-8a1d-413a-c434-e57f398755af" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Input:\n", "a8=0, a7=0, a6=0, a5=0, a4=0, a3=0, a2=0, a1=0, a0=0, b8=0, b7=0, b6=0, b5=0, b4=0, b3=0, b2=0, b1=0, b0=0, cin=0\n", "Output:\n", "y8=0, y7=0, y6=0, y5=0, y4=0, y3=0, y2=0, y1=0, y0=0, cout=0\n", "Press to continue or 'q' to quit\n", "Next? \n", "Input:\n", "a8=0, a7=0, a6=0, a5=0, a4=0, a3=0, a2=0, a1=0, a0=0, b8=0, b7=0, b6=0, b5=0, b4=0, b3=0, b2=0, b1=0, b0=0, cin=1\n", "Output:\n", "y8=0, y7=0, y6=0, y5=0, y4=0, y3=0, y2=0, y1=0, y0=1, cout=0\n", "Press to continue or 'q' to quit\n", "Next? \n", "Input:\n", "a8=0, a7=0, a6=0, a5=0, a4=0, a3=0, a2=0, a1=0, a0=0, b8=0, b7=0, b6=0, b5=0, b4=0, b3=0, b2=0, b1=0, b0=1, cin=0\n", "Output:\n", "y8=0, y7=0, y6=0, y5=0, y4=0, y3=0, y2=0, y1=0, y0=1, cout=0\n", "Press to continue or 'q' to quit\n", "Next? q\n" ] } ], "source": [ "adder9 = PyCirc[\"adder9\"]\n", "\n", "full_run(adder9)" ] }, { "cell_type": "markdown", "metadata": { "id": "7iYCv68p6D5Y" }, "source": [ "* However this is not readable and takes forever!\n", "* Number of inputs is 17 bits, so the loop has $2^{17} = 131072$ cycles!\n", "* We will better off running only random assignments, and verify them manually.\n", "* The **pycirchdl** package has the following utility for creating a random assignment.\n", "```python\n", "def random_assignment(names):\n", " names = expand(names)\n", " a = Assign(names)\n", " for x in names:\n", " a[x] = randint(0,1)\n", " return a\n", "```\n", "* Here are 5 random samples on our **ADDER3** input" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "WAEitdUu6D5Z", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "a2200348-43e4-41cf-a7a9-8301d62c4a21" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "011100101 001100101 0\n", "000001010 001110011 1\n", "101011001 100100100 1\n", "100001010 010110111 0\n", "100111110 110110000 1\n" ] } ], "source": [ "names = \"a<8:0>; b<8:0>; cin\"\n", "for i in range(5):\n", " a = random_assignment(names)\n", " A = a.bits(\"a<8:0>\")\n", " B = a.bits(\"b<8:0>\")\n", " print(A, B, a[\"cin\"])" ] }, { "cell_type": "markdown", "metadata": { "id": "K8LVp5kO6D5Z" }, "source": [ "* Now we can generate random inputs for **ADDER9**, get the output, and verify its correctness." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "IgBi6JWd6D5Z", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "e3ddeecc-d192-45f6-c492-3769f5204e3e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--------------------------------------------------------------------------------\n", "Input: 100100000 + 101100101 + 0\n", "Output: 010000101 + 1\n", "Verify sum: 0b1010000101\n", "Press to continue or q to stop: \n", "--------------------------------------------------------------------------------\n", "Input: 001110100 + 100001101 + 1\n", "Output: 110000010 + 0\n", "Verify sum: 0b110000001\n", "Press to continue or q to stop: \n", "--------------------------------------------------------------------------------\n", "Input: 111010001 + 111101101 + 1\n", "Output: 110111111 + 1\n", "Verify sum: 0b1110111110\n", "Press to continue or q to stop: q\n" ] } ], "source": [ "names = \"a<8:0>; b<8:0>; cin\"\n", "while True:\n", " a = random_assignment(names)\n", " A = a.bits(\"a<8:0>\")\n", " B = a.bits(\"b<8:0>\")\n", " cin = a[\"cin\"]\n", " print(80*\"-\")\n", " print(\"Input: %s + %s + %s\" % (A, B, cin))\n", " o = adder9(a)\n", " Y = o.bits(\"y<8:0>\")\n", " cout = o[\"cout\"]\n", " print(\"Output: %s + %s\" % (Y, cout))\n", " print(\"Verify sum: %s\" % (bin(int(A,2) + int(B,2)),))\n", " inpstr = input(\"Press to continue or q to stop: \")\n", " if \"q\" == inpstr:\n", " break" ] }, { "cell_type": "markdown", "metadata": { "id": "uDASOOgx6D5Z" }, "source": [ "## Half Adder\n", "* A half adder is a the same thing except that it does\n", " not have carry bits.\n", "* It simply adds its two input bits and outputs the sum.\n", "* In general, an **n-bits half adder** has $2n$ inputs\n", " bits \"a<0:n-1>\" + \"\" and $n+1$ output bits \"y<0:n>\".\n", "* We will use our **ADDER3** cell to build a half\n", " **2-bits adder**\n", " * by injecting a zero constant to its **cin** input,\n", " * and discarding its **cout** output bit.\n", "* We will also use this opportunity to present a different style for creating a PyCirc cell." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rKPdCmYc6D5Z", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "a115febd-9f02-402b-cf2b-a1db8118d41c" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "ATTENTION: some outputs are dangling! ad3 (adder3) : {'cout'}\n", "Cell = adder2: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 65 } ], "source": [ "# CELL: ADDER2 - HALF ADDER 2 \n", "\n", "# We need adder3\n", "need(\"adder3\")\n", "\n", "# Here we present a different style for defining a logic circuit\n", "\n", "gates = (\n", " GATE(\"a1\", type=\"inp\")\n", "+ GATE(\"a0\", type=\"inp\")\n", "+ GATE(\"b1\", type=\"inp\")\n", "+ GATE(\"b0\", type=\"inp\") \n", "+ GATE(\"ad3\", type=\"adder3\")\n", "+ GATE(\"y2\", type=\"out\")\n", "+ GATE(\"y1\", type=\"out\")\n", "+ GATE(\"y0\", type=\"out\")\n", "+ GATE(\"g0\", type=\"zero\")\n", ")\n", "\n", "wires = [\n", " Wire(\"a0\", \"ad3/a0\"),\n", " Wire(\"a1\", \"ad3/a1\"),\n", " Wire(\"b0\", \"ad3/b0\"),\n", " Wire(\"b1\", \"ad3/b1\"),\n", " Wire(\"g0\", \"ad3/a2\"),\n", " Wire(\"g0\", \"ad3/b2\"),\n", " Wire(\"g0\", \"ad3/cin\"),\n", " Wire(\"ad3/y0\", \"y0\"),\n", " Wire(\"ad3/y1\", \"y1\"),\n", " Wire(\"ad3/y2\", \"y2\"),\n", "]\n", "\n", "ha2 = PyCirc(\"adder2\", gates, wires)\n", "pycircLib.add_circ(ha2)" ] }, { "cell_type": "markdown", "metadata": { "id": "ykD6iyEv6D5a" }, "source": [ "* Notice that in this case, we do not have \n", " a **Define(), EndDef()** calls!\n", "* We simply define a list of gates and a list of wires,\n", " and later use the class **PyCirc** to create the cell.\n", "* The wires are created by the low level `Wire` class instead\n", " of the higher level `WIRE` function.\n", " * The `Wire` class is suited for generating a single wire\n", " object while `WIERE` generates multiple wires and supports\n", " compressed names. \n", "* An output pin which is not connected to any other pin is\n", " called a **dangling pin**.\n", "* Notice that our 2-bits half adder **ADDER2** has a\n", " dangling pin.\n", "* The carry out bit (**cout**) of the gate **ad3** is dangling.\n", "* Also notice the **constant gate** `g0` which fixes the carry\n", " in (**cin**) to a constant 0 value.\n", "* A \"dangling output\" allert means that an output is not\n", " connected to anything.\n", " * This is OK if you intended it as we did in this example.\n", " * To achieve a half adder we had to discard the `cout`\n", " pin of ADDER3.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": { "id": "isWRgyVZ6D5a" }, "source": [ "## Boolean Operators\n", "* The **PyCircHDL** package contains a **logops** module which\n", " defines a small set of boolean functions.\n", "* These functions are also called **boolean operators** since\n", " * they act on a variable number of boolean values.\n", " * return a boolean value.\n", " * They are denoted with a first capital letter.\n", "* These operators are needed for designing **black box cells**\n", " that play an important role in the **VLSI** design process.\n", "* Here are a few examples of operators we have\n", " in the **pycirchdl** package:\n", " * **And**, **Or**, **Nor**, **Xor**, **Not**, **Mux**.\n", " * They all accept an **Assign** object as an argument,\n", " * and return an **Assign** object.\n", " \n", "```Python\n", "def And(a, output=\"y\"):\n", " o = Assign(output)\n", " for x in a:\n", " if a[x] == 0:\n", " for y in o: o[y] = 0\n", " return o\n", " for y in o: o[y] = 1\n", " return o\n", "\n", "def Or(a, output=\"y\"):\n", " o = Assign(output)\n", " for x in a:\n", " if a[x] == 1:\n", " for y in o: o[y] = 1\n", " return o\n", " for y in o: o[y] = 0\n", " return o\n", "\n", "def Nor(a, output=\"y\"):\n", " o = Assign(output)\n", " o1 = OR(a, Assign(\"y\"))\n", " b = Assign(\"x\", o1[\"y\"])\n", " return NOT(b, o)\n", "\n", "def Xor(a, output=\"y\"):\n", " o = Assign(output)\n", " if sum(a[x] for x in a) == 1:\n", " for y in o: o[y] = 1\n", " return o\n", " else:\n", " for y in o: o[y] = 0\n", " return o\n", "```" ] }, { "cell_type": "markdown", "metadata": { "id": "bkRsBuZy6D5a" }, "source": [ "* Users can easily add more operators in client code.\n", "* Here are two examples that we used in the development of\n", " **pycirchdl** for testing the various **1-bit counter** cells\n", " and the **magnitude comparator** cells.\n", "* The first operator is simply a Python code for counting how many\n", " 1-bits a given assigmnet object `a` has?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "FwYu12Wu6D5a" }, "outputs": [], "source": [ "# Count the number of 1-bits in the assignment a\n", "def COUNT_ONES(a):\n", " s = sum(a())\n", " k = len(a).bit_length()\n", " bits = bin(s)[2:].zfill(k)\n", " names = \"y<%d:1>\" % (k,)\n", " bits = [int(y) for y in bits]\n", " o = Assign(names, bits)\n", " return o\n", "\n", "# Magnitude Comparator Operator\n", "# input: \"a<0:n>;b<0:n>\"\n", "# output: \"y<1:3>\"\n", "# If ab returns 001\n", "def COMPARE(a):\n", " A = []\n", " B = []\n", " for name in a:\n", " if name[0] == \"a\":\n", " A.append(name)\n", " else:\n", " B.append(name)\n", " A = int(a.bits(A), 2)\n", " B = int(a.bits(B), 2)\n", " o = Assign(\"y<1:3>\")\n", " if A\", \"01011\")\n", "o = COUNT_ONES(a)\n", "print(o)" ] }, { "cell_type": "markdown", "metadata": { "id": "GIKhA3b06D5b" }, "source": [ "* Note that the result is in numerical binary form: 011.\n", "* The input \"01011\" has 3 occurrences of the bit 1,\n", " which in numerical binary form is 11." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "3saExiz06D5b", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "d99d7aee-c262-40aa-f106-62919fef523e" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Input: 11101011\n", "Output (binary): 0110\n", "Output (decimal): 6\n" ] } ], "source": [ "a = Assign(\"x<1:8>\", \"11101011\")\n", "print(\"Input:\", a.bits())\n", "o = COUNT_ONES(a)\n", "print(\"Output (binary):\", o.bits())\n", "print(\"Output (decimal):\", int(o.bits(), 2))" ] }, { "cell_type": "markdown", "metadata": { "id": "Fw3CKvgV6D5b" }, "source": [ "* Here, the input \"11101011\" contains 6 bits of 1.\n", "* As expected, the output is 110 which is a numerical binary \n", " representation of decimal 6.\n", "* The following operators do not accept any input but produces\n", " a **constant** output.\n", "* These are the **constant operators**.\n", "* The **Zero** operator produces 0.\n", "* The **One** operator produces 1.\n", "* Here are the definitions of the **ZERO** and **ONE** operators\n", "\n", "```Python\n", "def Zero(a=None, output=\"y\"):\n", " o = Assign(output)\n", " for y in o: o[y] = 0\n", " return o\n", "\n", "def One(a=None, output=\"y\"):\n", " o = Assign(output)\n", " for y in o: o[y] = 1\n", " return o\n", "```" ] }, { "cell_type": "markdown", "source": [ "* The **Magnitude Comparator** operator acts on even length\n", " assignments only.\n", "* It splits the input to two equal length binary numbers and\n", " compares their magnitude." ], "metadata": { "id": "kYy5YiwyDs5r" } }, { "cell_type": "code", "source": [ "a = Assign(\"a<0:7>; b<0:7>\", \"00101101\" + \"00110101\")\n", "o = COMPARE(a)\n", "print(o)" ], "metadata": { "id": "OifNYnWKC4YC", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "4b662f0a-0792-4916-ab71-7338c99f44bd" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "y1=1, y2=0, y3=0\n" ] } ] }, { "cell_type": "markdown", "metadata": { "id": "jo1ybisJ6D5b" }, "source": [ "## Creating Cells with the `Cell` Class\n", "* VLSI design usually means designing very large logic circuits\n", " consisting of millions and even billions of elements.\n", "* This is achieved by dividing the main circuit to a dozen or so\n", " sub-circuits, usually called **sections**.\n", "* Each **section** is divided to a set of smaller cells,\n", " usually called **blocks**.\n", "* Each **block** is partitioned to smaller cells,\n", " usually called **functional unit blocks** (or **fubs** for short).\n", "* And finally, each functional unit is partitioned to smaller\n", " cells taken from established cell libraries.\n", "* This design method is usually called **Hierarchical Design**.\n", "* For example, here is a high level hierarchical view of\n", " Intel's **Skylake** cpu\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": { "id": "Z2ef0tkm6D5c" }, "source": [ "* It consists of roughly 5 billion transistors\n", " and 20 billion wires.\n", "* In the early design phase, only a small number of the cell\n", " designs are available.\n", "* Many other cells are replaced by \"black boxes\" that simulate cells,\n", " but their full circuit design is postponed to a later stage after \n", " passing many fire tests that verify the feasibility of\n", " the overall design.\n", "* These simulation tests determine many of the desired properties\n", " and parameters for these cells, and thus provide a lot of information\n", " and clues needed to design them efficiently.\n", "* In such scenarios, a Logic Cell is viewed as a \"black box\" with\n", " entry and exit points that can be defined more conveniently\n", " by means of simple numerical software algorithms.\n", "* **PyCircHDL** provides a high level **Cell** class for defining\n", " such cells.\n", "* So we have two ways to define a new cell: \n", " 1. By defining a **PyCirc** circuit as in the examples above\n", " 2. By defining a **boolean operator** numerically.\n", " * A cell defined by an operator is named **black box**\n", " or **box** for short.\n", " * It is a place holder for a real circuit which is not\n", " yet available, but the overall design need to be tested\n", " before it is decided if its design is feasible.\n", "* Here is an example of a 4x3 cell that is defined by\n", " the `Cell` class." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "jpn1n12q6D5c" }, "outputs": [], "source": [ "count3 = Cell(\"count3\", operator=COUNT_ONES, input=\"x<1:3>\", output=\"y<1:2>\", depth=3)\n", "# currently all box type Cells are stored in the default PyCirc library called pycircLib\n", "pycircLib.add(count3)" ] }, { "cell_type": "markdown", "metadata": { "id": "o4Oy_ysb6D5c" }, "source": [ "* It accepts a 4-bits assignment and outputs a 3-bits assignment.\n", "* It counts the number of 1-bits in the input.\n", "* The operator **COUNT_ONES** was defined in the section above.\n", "* We can now use **count3** as a cell type and use it to define more complex cells.\n", "* As an example, let's define a **COUNT6** circuit by using two gates of type **COUNT3**.\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": { "id": "pruNUy0P6D5d" }, "source": [ "* The plan is straightforward:\n", " * **COUNT6** accepts 6 input bits: x1, x2, x3, x4, x5, x6.\n", " * The first 3 bits x1, x2, x3, are assigned to the gate **c1**\n", " whose type is **COUNT3**.\n", " * The last 3 bits x4, x5, x6, are assigned to the gate **c2**\n", " whose type is also **COUNT3**.\n", " * The outputs of **c1** and **c2** are sent to gate **a**\n", " which is an **ADDER2** cell.\n", " * The gate **a** is adding up the two numbers and sends its\n", " output to y1, y2, y3.\n", "* Here is the **PyCircHDL Program** for the **COUNT6** circuit." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PrneGKdH6D5d", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "531bb664-054b-4128-c444-7e73d26498af" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Cell = count6: Validity check: OK.\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": {}, "execution_count": 71 } ], "source": [ "Define(\"count6\")\n", "GATE(\"x<1:6>\", type=\"inp\")\n", "GATE(\"y<1:3>\", type=\"out\")\n", "GATE(\"c1\", type=\"count3\") # Gate \"c1\" of type count3\n", "GATE(\"c2\", type=\"count3\") # Gate \"c2\" of type count3\n", "GATE(\"a\", type=\"adder2\")\n", "\n", "WIRE(\"x<1:3>\", \"c1/x<1:3>\")\n", "WIRE(\"x<4:6>\", \"c2/x<1:3>\")\n", "WIRE(\"c1/y1\", \"a/a1\")\n", "WIRE(\"c1/y2\", \"a/a0\")\n", "WIRE(\"c2/y1\", \"a/b1\")\n", "WIRE(\"c2/y2\", \"a/b0\")\n", "WIRE(\"a/y<2:0>\", \"y<1:3>\")\n", "EndDef()" ] }, { "cell_type": "markdown", "metadata": { "id": "y1BPWSO06D5d" }, "source": [ "* Here is the Python code for adding some of the box cells in **PyCircHDL**.\n", "* See the **factory** module in the **PyCircHDL** package for more examples.\n", "\n", " ```python\n", "pycircLib.add_box(name=\"and2\", operator=And, input=\"x<1:2>\", output=[\"y\"])\n", "pycircLib.add_box(name=\"and3\", operator=And, input=\"x<1:3>\", output=[\"y\"])\n", "pycircLib.add_box(name=\"and4\", operator=And, input=\"x<1:4>\", output=[\"y\"])\n", "pycircLib.add_box(name=\"and5\", operator=And, input=\"x<1:5>\", output=[\"y\"])\n", "pycircLib.add_box(name=\"and6\", operator=And, input=\"x<1:6>\", output=[\"y\"])\n", "pycircLib.add_box(name=\"and7\", operator=And, input=\"x<1:7>\", output=[\"y\"])\n", "pycircLib.add_box(name=\"and8\", operator=And, input=\"x<1:8>\", output=[\"y\"])\n", "```\n", "\n", "* We can also use Python loops to add box cells to our cell library.\n", "* The following Python loop, adds 32 box cellls to\n", " the **PyCirc cell library**\n", " * 8 **OR** box cells: **OR2, OR3, ..., OR8**\n", " * 8 **XOR** box cells: **XOR2, XOR3, ..., XOR8**\n", " * 8 **NOR** box cells: **NOR2, NOR3, ..., NOR8**\n", "\n", " ```\n", "for k in range(2,9):\n", " inp = \"x<1:%s>\" % (k,)\n", " name = \"or\" + str(k)\n", " pycircLib.add_box(name, operator=Or, input=inp, output=[\"y\"])\n", " name = \"xor\" + str(k)\n", " pycircLib.add_box(name, operator=Xor, input=inp, output=[\"y\"])\n", " name = \"nor\" + str(k)\n", " pycircLib.add_box(name, operator=Nor, input=inp, output=[\"y\"])\n", " name = \"nand\" + str(k)\n", " pycircLib.add_box(name, operator=Nand, input=inp, output=[\"y\"])\n", " ```" ] }, { "cell_type": "markdown", "metadata": { "id": "0QI7OGTB6D5d" }, "source": [ "## Advanced Topics -- To be Continued ...\n", "* For the more experienced students we present here some more\n", " advanced usage of the PyCirc package will be added within the\n", " near future. To be contniued ..." ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.11" }, "colab": { "provenance": [], "collapsed_sections": [] } }, "nbformat": 4, "nbformat_minor": 0 }