{
"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
}