{ "cells": [ { "cell_type": "markdown", "id": "89096261-a72a-4aa4-8ee0-10111e612bee", "metadata": {}, "source": [ "# System on Chip design auxiliaries\n", "\n", "For mass generation of bus decoders, a register bit map must be associated with corresponding control, status or data signals.\n", "This SoC concept follows the [*MaSoCist*](https://github.com/hackfin/MaSoCist) register map design rules:\n", "\n", "* Registers are mapped into memory space and are accessed by an address, hence.\n", "* They can be flagged read-only, write-only or volatile:\n", " * READONLY: Writing to the register has no effect\n", " * WRITEONLY: Reading from this register returns an undefined value\n", " * VOLATILE: Write or read access triggers a pulse on the corresponding `select` lines.\n", " This allows to implement `W1C` (write one to clear) behaviour, or optimized data in/out transfers.\n", "* Registers contain bit fields that can be READONLY or WRITEONLY\n", "* Two register definitions (one READONLY, one WRITEONLY) can be mapped to one address. This is used for data I/O." ] }, { "cell_type": "markdown", "id": "f9d9a758-9645-411e-8891-9ee0af0c88bf", "metadata": {}, "source": [ "We import all needed MMR elements as follows:" ] }, { "cell_type": "code", "execution_count": 1, "id": "4d94057a-4cd1-4ae7-8501-272e98db9ea2", "metadata": {}, "outputs": [], "source": [ "from cyrite.library.soc import *" ] }, { "cell_type": "markdown", "id": "f2ac6798-f12e-41af-817a-777c50ec05e5", "metadata": {}, "source": [ "## Register definitions\n", "\n", "Add a few register with bit fields and flags (these are normally taken from an external module or generated from an XML description).\n", "\n", "This MMR scheme follows the register design scheme used by `gensoc` from the [MaSoCist SoC builder](https://github.com/hackfin/MaSoCist). Detailed information on how to interpret the register maps is found in the MaSoCist documentation." ] }, { "cell_type": "code", "execution_count": 2, "id": "8efe074d-fd59-404e-a6c3-39872ce39de7", "metadata": {}, "outputs": [], "source": [ "reg01 = Reg(16,\n", " [\n", " BF(\"im\", 3, 1, flags = BF.READONLY),\n", " BF(\"ex\", 7, 6),\n", " BF(\"inv\", 4, 4, flags = BF.WRITEONLY),\n", " BF(\"mode\", 14, 10, default = 2)\n", " ]\n", ")\n", "\n", "reg02 = Reg(16,\n", " [\n", " BF(\"gna\", 6, 1, default = 8),\n", " BF(\"reset\", 7, 7, default = True)\n", " ],\n", " flags = Reg.VOLATILE | Reg.WRITEONLY\n", ")" ] }, { "cell_type": "markdown", "id": "e75f647a-e070-4f06-8766-7877bc1c96ed", "metadata": {}, "source": [ "**Note** `VOLATILE` flagged registers create a special output pin in the `.select` container of the RegisterSignal which is pulsed when an access was made. See wave trace below." ] }, { "cell_type": "markdown", "id": "d4e8bfc3-d7d3-4953-864f-6ae3570984f7", "metadata": {}, "source": [ "## The MMR factory\n", "\n", "The code generation for the memory mapped registers is done by the `MMRGenerator` class. We derive from it and define a register map." ] }, { "cell_type": "code", "execution_count": 3, "id": "14e66c15-12f0-4b8f-9e6f-bf31e0ea121d", "metadata": {}, "outputs": [], "source": [ "class MMRDesign(MMRGenerator):\n", " # This is a description for an address map\n", " regdesc = {\n", " 0x01: ['stat', reg01],\n", " 0x02: ['ctrl', reg02],\n", " 0x04: ['TXD', Reg(16, [ BF(\"DATA\", 15, 0)], flags = Reg.WRITEONLY | Reg.VOLATILE) ],\n", " 0x05: ['RXD', Reg(16, [ BF(\"DATA\", 15, 0)], flags = Reg.READONLY | Reg.VOLATILE)]\n", " }\n", "\n", " def build(self, p : MMRPort, interface : dict):\n", " \"Creates an instance of a MMR decoder\"\n", " inst = self.mmr_decode(\n", " clk = p.clk,\n", " reset = p.rst,\n", " addr = p.addr,\n", " wr = p.wr,\n", " data_in = p.din,\n", " data_out = p.dout,\n", " REGDESC = self.regdesc,\n", " **interface\n", " )\n", "\n", " return inst" ] }, { "cell_type": "markdown", "id": "9407f4b8-749b-4e86-9125-5a75259aed88", "metadata": {}, "source": [ "## The test bench simulation class\n", "\n", "Now we derive from this class again in order to create a custom simulation class that includes a test bench.\n", "We will be using a co-simulation environment, where the test bench code below is run in native Python while the instanced MMR decoder runs as compiled hardware in the background.\n", "\n", "The test bench makes use of the MMRPort container, which supplies a few simulation macros to mimic write and read sequences. You can derive from this container to define own sequences." ] }, { "cell_type": "code", "execution_count": 4, "id": "781ab894-7f10-4840-abcb-a3823fc61fe2", "metadata": {}, "outputs": [], "source": [ "from cyhdl import *\n", "\n", "class SimMMR(MMRDesign):\n", " @cyrite_factory.testbench(\"ns\")\n", " def testbench(self):\n", " p = MMRPort(self)\n", "\n", " clk = p.clk\n", "\n", " interface = self.generate_interface(self.regdesc)\n", " ctrl = interface['ctrl']\n", " stat = interface['stat']\n", "\n", " # This debug signal is not connected to the\n", " # simulation back end\n", " debug = self.Signal(bool(), name = 'debug')\n", " \n", " inst = self.build(p, interface)\n", "\n", " @self.always(delay(2))\n", " def clkgen():\n", " clk.next = ~clk\n", "\n", " @self.sequence\n", " def main():\n", " p.rst.next = True\n", " stat.read.ex.next = 0\n", "\n", " yield delay(10)\n", "\n", " yield clk.posedge\n", " print(\"START\")\n", "\n", " debug.next = False\n", " p.wr.next = False\n", " p.addr.next = 0x001\n", " \n", " yield from p.reset_sequence()\n", "\n", " print(\"DONE RESET\")\n", "\n", " assert p.rst == False\n", " \n", " yield clk.negedge\n", " print(\"SETTING stat.read\")\n", " stat.read.ex.next = 0\n", " stat.read.mode.next = 4\n", " stat.read.im.next = 2\n", "\n", " yield clk.negedge\n", " yield clk.negedge\n", "\n", " p.assert_read(0x001, 0x1004)\n", " yield clk.negedge\n", " \n", " yield from p.write_sequence(0x002, 0xfa)\n", " debug.next = True\n", " \n", " yield clk.negedge\n", " print(\"VAL\", ctrl.select.sel_w)\n", " # assert ctrl.select.sel_w == True\n", " assert ctrl.write.gna == 0x3d\n", " yield clk.posedge\n", " yield clk.negedge\n", " assert ctrl.select.sel_w == False\n", "\n", " yield from p.write_sequence(0x001, 0x10)\n", " yield clk.negedge # Here, we can see different\n", " # behaviour of icarus vs ghdl. Icarus needs this clk.negedge to\n", " # update its signals, GHDL doesn't\n", " assert stat.write.inv == True\n", "\n", " yield 2 * (clk.posedge, )\n", "\n", " yield delay(10)\n", " \n", " print(\"DONE\")\n", "\n", " raise StopSimulation\n", " \n", " return instances()\n" ] }, { "cell_type": "markdown", "id": "90d4841e-1e7b-4528-b3c3-b705a277ea6b", "metadata": {}, "source": [ "## Defining an architecture\n", "\n", "We define an RTL architecture for the a LatticeSemi ECP5 target, for instance:" ] }, { "cell_type": "code", "execution_count": 5, "id": "fb16eb11-6048-4c41-8b52-7e9ac93d4d92", "metadata": {}, "outputs": [], "source": [ "from myirl.library.architecture import Architecture\n", "from cyrite.library.targets import ECP5\n", "from yosys.simulator import CXXRTL\n", "\n", "class RTLArch(Architecture):\n", "\tdef __init__(self):\n", "\t\tself.sim_class = CXXRTL\n", "\t\tself.target_class = ECP5" ] }, { "cell_type": "markdown", "id": "0c589f00-ed17-430a-b153-045ff4351c0d", "metadata": {}, "source": [ "Then we create a design instance and pass this architecture as target:" ] }, { "cell_type": "code", "execution_count": 6, "id": "2e3326f3-896a-4ec0-94d9-25a8272c5717", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[7;35m Declare obj 'mmr_decode' in context '(SimMMR 'mmr')'() \u001b[0m\n", "DEBUG LIB ALL ELEM (SimMMR 'mmr')\n", "DEBUG MAIN ELAB [Instance mmr_decode I/F: [// ID: mmr_decode_0 ]]\n", " DEBUG components ['mmr_decode_obj_SimMMRu_1u_1u_12u_1u_16u_16_d_1_2_4_5'] (SimMMR 'mmr') \n", "\u001b[32m Adding module with name `mmr_decode` \u001b[0m\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32mUse Cython binding for dict\u001b[0m\n", "\u001b[7;31mDEBUG: not handling type in co-simulation. Your Cosimulation may not run correctly.\u001b[0m\n", "\u001b[7;31mDEBUG: not handling type .dummy.._mixin'> in co-simulation. Your Cosimulation may not run correctly.\u001b[0m\n" ] } ], "source": [ "m = SimMMR(\"mmr\", RTLArch())\n", "tb = m.testbench()\n", "tb.debug()\n", "\n", "# tb.design.display_rtl()" ] }, { "cell_type": "code", "execution_count": 7, "id": "4fe0e208-7c2e-48c0-8c84-fa34ff86212d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " DEBUG components ['mmr_decode_obj_SimMMRu_1u_1u_12u_1u_16u_16_d_1_2_4_5'] (SimMMR 'mmr') \n", "Compiling /tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0.pyx because it changed.\n", "[1/1] Cythonizing /tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0.pyx\n", "running build_ext\n", "building 'runtime.mmr_decode_2cc0' extension\n", "creating build/temp.linux-x86_64-3.10/tmp/myirl_mmr_fhw1y82_\n", "gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DCOSIM_NAMESPACE=mmr_decode_2cc0 -Iruntime -I/tmp/myirl_mmr_fhw1y82_/ -I/usr/share/yosys/include/backends/cxxrtl/runtime -I/usr/local/include/python3.10 -c /tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0.cpp -o build/temp.linux-x86_64-3.10/tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0.o\n", "gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DCOSIM_NAMESPACE=mmr_decode_2cc0 -Iruntime -I/tmp/myirl_mmr_fhw1y82_/ -I/usr/share/yosys/include/backends/cxxrtl/runtime -I/usr/local/include/python3.10 -c /tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0_rtl.cpp -o build/temp.linux-x86_64-3.10/tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0_rtl.o\n", "g++ -pthread -shared -Wl,--strip-all build/temp.linux-x86_64-3.10/tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0.o build/temp.linux-x86_64-3.10/tmp/myirl_mmr_fhw1y82_/mmr_decode_2cc0_rtl.o -L/usr/local/lib -o build/lib.linux-x86_64-3.10/runtime/mmr_decode_2cc0.cpython-310-x86_64-linux-gnu.so\n", "copying build/lib.linux-x86_64-3.10/runtime/mmr_decode_2cc0.cpython-310-x86_64-linux-gnu.so -> runtime\n", "Open for writing: mmr.vcd\n", "\u001b[7;35m CXXRTL context: SKIP INTERFACE ITEM `self` \u001b[0m\n", "\u001b[7;35m CXXRTL context: SKIP INTERFACE ITEM `REGDESC` \u001b[0m\n", "START\n", "DEBUG RESUME PROCESS main\n", "DONE RESET\n", "SETTING stat.read\n", "VAL : False\n", "DEBUG RESUME PROCESS main\n", "DONE\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32mUsing '/tmp/myirl_mmr_fhw1y82_/' for output\u001b[0m\n", "\u001b[32mCosimulation: debug not connected to backend\u001b[0m\n", "\u001b[7;34mSTOP SIMULATION @88\u001b[0m\n" ] }, { "data": { "text/plain": [ "0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Turn 'debug' on for simulation output\n", "tb.run(400, debug = True, wavetrace = 'mmr.vcd', recompile = True)" ] }, { "cell_type": "markdown", "id": "95688510-f54f-4f93-aab7-141ffa28d7df", "metadata": {}, "source": [ "## Waveform display\n", "\n", "The `*.vcd` format hides the `MMRPort` record members from the trace. Therefore we need a few monitoring auxiliary signals.\n", "\n", "If we change signal names, we will also have to change the config below.\n", "In order to retrieve the signal names generated by the VCD trace, set the `cfg` parameter to None. This will then display all traced signals." ] }, { "cell_type": "code", "execution_count": 8, "id": "cef473a2-447d-4986-9437-957779db8937", "metadata": {}, "outputs": [], "source": [ "from cyrite import waveutils" ] }, { "cell_type": "code", "execution_count": 9, "id": "7bffa6d8-2750-4e1d-822f-ad6f4d0f57b3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "new Promise(function(resolve, reject) {\n", "\tvar script = document.createElement(\"script\");\n", "\tscript.onload = resolve;\n", "\tscript.onerror = reject;\n", "\tscript.src = \"https://wavedrom.com/wavedrom.min.js\";\n", "\tdocument.head.appendChild(script);\n", "}).then(() => {\n", "new Promise(function(resolve, reject) {\n", "\tvar script = document.createElement(\"script\");\n", "\tscript.onload = resolve;\n", "\tscript.onerror = reject;\n", "\tscript.src = \"https://wavedrom.com/skins/narrow.js\";\n", "\tdocument.head.appendChild(script);\n", "}).then(() => {\n", "WaveDrom.ProcessAll();\n", "});\n", "});" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "config = {\n", " '.clk' : None,\n", " '.reset' : None,\n", " '.ctrl_select_sel_w' : None,\n", " '.data_in' : None,\n", " '.data_out' : None,\n", " '.addr' : None\n", "}\n", "\n", "waveutils.draw_wavetrace(tb, 'mmr.vcd', 'clk', cfg = config)" ] }, { "cell_type": "markdown", "id": "ed9124e2-8934-4f2d-a3c0-86587d5a78de", "metadata": {}, "source": [ "## HDL translation\n", "\n", "When a MMR decoder instance is created, the interface, i.e. port and parameters (VHDL generics) is generated from the Register description as well. The `ctrl` and `stat` signal containers are normally wired up to the peripheral modules, the bus signals map into a specific MMR address space of the CPU.\n", "\n", "We build the above design again and explicitely output to VHDL." ] }, { "cell_type": "code", "execution_count": 10, "id": "27321e8d-ad64-42a0-9e30-8a88e5f534f1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[7;35m Declare obj 'testbench' in context '(SimMMR 'test')'() \u001b[0m\n", "\u001b[7;35m Declare obj 'mmr_decode' in context '(SimMMR 'test')'() \u001b[0m\n", " Writing 'mmr_decode' to file /tmp/myirl_test_lap0hgam/mmr_decode.vhdl \n", " Writing 'testbench' to file /tmp/myirl_test_lap0hgam/testbench.vhdl \n", " Creating library file module_defs.vhdl \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32mUse Cython binding for dict\u001b[0m\n" ] } ], "source": [ "from cyrite.simulation import icarus\n", "design = SimMMR(\"test\", icarus.ICARUS)\n", "# Create an instance of the top level tb:\n", "tb = design.testbench()\n", "files = design.elab(targets.VHDL)" ] }, { "cell_type": "markdown", "id": "6e5715a7-0595-4d0e-ae7b-6de737af0369", "metadata": {}, "source": [ "Inspect the MMR decoder:" ] }, { "cell_type": "code", "execution_count": 11, "id": "3681f6be-9249-4064-b290-889ea47e06ac", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-- File generated from source:\r\n", "-- \r\n", "-- (c) 2016-2022 section5.ch\r\n", "-- Modifications may be lost, edit the source file instead.\r\n", "\r\n", "library IEEE;\r\n", "use IEEE.std_logic_1164.all;\r\n", "use IEEE.numeric_std.all;\r\n", "\r\n", "library work;\r\n", "\r\n", "use work.module_defs.all;\r\n", "use work.txt_util.all;\r\n", "use work.myirl_conversion.all;\r\n", "\r\n", "entity mmr_decode is\r\n", " port (\r\n", " clk : in std_ulogic;\r\n", " reset : in std_ulogic;\r\n", " addr : in unsigned(11 downto 0);\r\n", " wr : in std_ulogic;\r\n", " data_in : in unsigned(15 downto 0);\r\n", " data_out : out unsigned(15 downto 0);\r\n", " stat_read : in t_stat_read;\r\n", " stat_write : out t_stat_write;\r\n", " ctrl_read : in t_ctrl_read;\r\n", " ctrl_write : out t_ctrl_write;\r\n", " ctrl_select : out t_ctrl_sel;\r\n", " TXD_read : in t_TXD_read;\r\n", " TXD_write : out t_TXD_write;\r\n", " TXD_select : out t_TXD_sel;\r\n", " RXD_read : in t_RXD_read;\r\n", " RXD_write : out t_RXD_write;\r\n", " RXD_select : out t_RXD_sel\r\n", " );\r\n", "end entity mmr_decode;\r\n", "\r\n", "architecture myIRL of mmr_decode is\r\n", " -- Local type declarations\r\n", " -- Signal declarations\r\n", " signal idata : unsigned(15 downto 0);\r\n", "begin\r\n", " \r\n", "drive:\r\n", " process(clk)\r\n", " begin\r\n", " if rising_edge(clk) then\r\n", " data_out <= idata;\r\n", " end if;\r\n", " end process;\r\n", " \r\n", "worker:\r\n", " process(clk, reset)\r\n", " begin\r\n", " if rising_edge(clk) then\r\n", " if reset = '1' then\r\n", " ctrl_select.sel_w <= '0';\r\n", " TXD_select.sel_w <= '0';\r\n", " RXD_select.sel_r <= '0';\r\n", " idata <= x\"0000\";\r\n", " TXD_write.out_DATA <= x\"0000\";\r\n", " ctrl_write.out_gna <= \"001000\";\r\n", " ctrl_write.out_reset <= '1';\r\n", " stat_write.out_ex <= \"00\";\r\n", " stat_write.out_inv <= '0';\r\n", " stat_write.out_mode <= \"00010\";\r\n", " else\r\n", " ctrl_select.sel_w <= '0';\r\n", " TXD_select.sel_w <= '0';\r\n", " RXD_select.sel_r <= '0';\r\n", " if (wr = '1') then\r\n", " case addr is\r\n", " when x\"001\" =>\r\n", " stat_write.out_ex <= data_in(8-1 downto 6);\r\n", " stat_write.out_inv <= data_in(4);\r\n", " stat_write.out_mode <= data_in(15-1 downto 10);\r\n", " when x\"002\" =>\r\n", " ctrl_write.out_gna <= data_in(7-1 downto 1);\r\n", " ctrl_write.out_reset <= data_in(7);\r\n", " ctrl_select.sel_w <= '1';\r\n", " when x\"004\" =>\r\n", " TXD_write.out_DATA <= data_in(16-1 downto 0);\r\n", " TXD_select.sel_w <= '1';\r\n", " when others =>\r\n", " end case;\r\n", " else\r\n", " case addr is\r\n", " when x\"001\" =>\r\n", " idata <= ('0' & stat_read.in_mode & \"00\" & stat_read.in_ex & '0' & '0' & stat_read.in_im & '0');\r\n", " when x\"005\" =>\r\n", " idata <= (RXD_read.in_DATA);\r\n", " RXD_select.sel_r <= '1';\r\n", " when others =>\r\n", " RXD_select.sel_r <= '0';\r\n", " end case;\r\n", " end if;\r\n", " end if;\r\n", " end if;\r\n", " end process;\r\n", "end architecture myIRL;\r\n", "\r\n" ] } ], "source": [ "!cat {files[0]}" ] }, { "cell_type": "markdown", "id": "23ef0293-0cac-44d4-8d9a-d5d62ed6f7c0", "metadata": {}, "source": [ "We note:\n", "\n", "* The interface is dynamically generated, according to the Peripheral map\n", "* The particular VHDL output uses records that are generated from the Register map description" ] }, { "cell_type": "markdown", "id": "72eb0cba-a2e3-4c33-867c-37e595320fb4", "metadata": {}, "source": [ "## Synthesis details\n", "\n", "To check the details of a particular sequence of mapping steps done inside yosys, we derive the `MMRDesign` again and augment it by a synthesis method.\n", "\n", "The `target.map` call actually maps the elaborated design to the target architecture. Then, we call 'stat' on the design via the yosys library:" ] }, { "cell_type": "code", "execution_count": 12, "id": "5fc80757-fd15-496f-87d0-18af0c8a506c", "metadata": {}, "outputs": [], "source": [ "class SynMMR(MMRDesign):\n", " def synthesis_stats(self):\n", " p = MMRPort(self)\n", " interface = self.generate_interface(self.regdesc)\n", " inst = self.build(p, interface)\n", " target = self.target_class(\"mmr\")\n", " d = inst.elab(target)\n", " target.map(capture = False)\n", " design = d[0]\n", " out = design.run(\"stat\", capture = None)\n", " return out\n" ] }, { "cell_type": "markdown", "id": "3ef00f1c-c2d8-41d8-aeee-fd3c6e39bdc9", "metadata": {}, "source": [ "Instance the `SynMMR` class and call its particular synthesis method:" ] }, { "cell_type": "code", "execution_count": 13, "id": "288fb34a-da05-43c4-9290-327081bd8f1b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[7;35m Declare obj 'mmr_decode' in context '(SynMMR 'syn')'() \u001b[0m\n", " DEBUG components ['mmr_decode_obj_SynMMRu_1u_1u_12u_1u_16u_16_d_1_2_4_5'] (SynMMR 'syn') \n", "\u001b[32m Adding module with name `mmr_decode` \u001b[0m\n", " Mapping to ECP5 technology \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[32mUse Cython binding for dict\u001b[0m\n" ] } ], "source": [ "m = SynMMR(\"syn\", RTLArch())\n", "out = m.synthesis_stats()" ] }, { "cell_type": "markdown", "id": "36712943-91df-4531-82b7-7b12f4bf8da3", "metadata": {}, "source": [ "The stat command output finally can be obtained separately through the capture:" ] }, { "cell_type": "code", "execution_count": 14, "id": "8a4260ce-5ba4-441c-b399-fd78e6707ad8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "-- Running command `stat' --\n", "\n", "34. Printing statistics.\n", "\n", "=== mmr_decode ===\n", "\n", " Number of wires: 61\n", " Number of wire bits: 274\n", " Number of public wires: 24\n", " Number of public wire bits: 162\n", " Number of memories: 0\n", " Number of memory bits: 0\n", " Number of processes: 0\n", " Number of cells: 166\n", " $_AND_ 30\n", " $_NOT_ 13\n", " $_OR_ 57\n", " TRELLIS_FF 66\n", "\n", "\n" ] } ], "source": [ "print(out)" ] }, { "cell_type": "markdown", "id": "adf1835c-f1ae-4681-99d7-b0f90420dcf2", "metadata": {}, "source": [ "We can see the number of (virtual) primitives used by this logic past the mapping stage." ] } ], "metadata": { "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.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }