├── _test ├── __init__.py ├── test5_SRV32 │ ├── sim │ │ ├── irq │ │ │ ├── dmem.bin │ │ │ └── imem.bin │ │ ├── hello │ │ │ ├── dmem.bin │ │ │ └── imem.bin │ │ ├── dhrystone │ │ │ ├── dmem.bin │ │ │ └── imem.bin │ │ └── exception │ │ │ ├── dmem.bin │ │ │ └── imem.bin │ └── __init__.py ├── test4_riscv │ ├── rv32_1stage │ │ └── __init__.py │ └── __init__.py ├── test1_config │ ├── __init__.py │ └── config.pyh ├── test3_simple │ ├── fifo │ │ ├── __init__.py │ │ └── FIFO.pyh │ ├── mem │ │ ├── __init__.py │ │ └── mem.pyh │ ├── reg │ │ ├── __init__.py │ │ └── reg.pyh │ ├── adder │ │ ├── __init__.py │ │ └── adder.pyh │ ├── inout │ │ ├── __init__.py │ │ └── inout.pyh │ ├── divider │ │ ├── __init__.py │ │ └── divider.pyh │ ├── multiplier │ │ ├── __init__.py │ │ └── multiplier.pyh │ ├── state │ │ ├── __init__.py │ │ └── vending_machine.pyh │ └── __init__.py ├── test6_AES │ └── __init__.py ├── test8_basic_simd │ └── __init__.py ├── test7_chisel_examples │ └── __init__.py ├── .gitignore ├── test2_basic │ ├── __init__.py │ ├── sint.pyh │ └── combinational.py ├── test0_grammar │ ├── __init__.py │ ├── dispatcher.py │ ├── array.py │ └── grammar.py └── __main__.py ├── docs ├── .nojekyll ├── _sources │ ├── PyHGL │ │ ├── 1-basic │ │ │ ├── sim.md.txt │ │ │ ├── index.md.txt │ │ │ ├── assignment.md.txt │ │ │ ├── module.md.txt │ │ │ ├── signals.md.txt │ │ │ ├── gates.md.txt │ │ │ └── start.md.txt │ │ └── 2-examples │ │ │ ├── index.md.txt │ │ │ └── multiplier.md.txt │ └── index.md.txt ├── objects.inv ├── _static │ ├── file.png │ ├── minus.png │ ├── plus.png │ ├── css │ │ ├── fonts │ │ │ ├── lato-bold.woff │ │ │ ├── lato-bold.woff2 │ │ │ ├── lato-normal.woff │ │ │ ├── lato-normal.woff2 │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ ├── lato-bold-italic.woff │ │ │ ├── lato-bold-italic.woff2 │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── lato-normal-italic.woff │ │ │ └── lato-normal-italic.woff2 │ │ └── badge_only.css │ ├── documentation_options.js │ ├── js │ │ ├── badge_only.js │ │ ├── html5shiv.min.js │ │ ├── html5shiv-printshiv.min.js │ │ └── theme.js │ ├── pygments.css │ ├── doctools.js │ ├── sphinx_highlight.js │ └── language_data.js ├── .doctrees │ ├── index.doctree │ ├── environment.pickle │ └── PyHGL │ │ ├── 1-basic │ │ ├── sim.doctree │ │ ├── gates.doctree │ │ ├── index.doctree │ │ ├── module.doctree │ │ ├── start.doctree │ │ ├── signals.doctree │ │ └── assignment.doctree │ │ └── 2-examples │ │ ├── AES.doctree │ │ ├── index.doctree │ │ └── multiplier.doctree ├── .buildinfo ├── genindex.html ├── search.html └── PyHGL │ └── 1-basic │ └── sim.html ├── pyhgl ├── analog │ └── __init__.py ├── logic │ ├── compile │ │ ├── __init__.py │ │ └── graph.py │ ├── signal │ │ ├── __init__.py │ │ └── sint.py │ ├── gate │ │ └── __init__.py │ ├── utils │ │ ├── integer.py │ │ ├── unit.py │ │ ├── trace.py │ │ └── __init__.py │ └── __init__.py ├── array │ ├── __init__.py │ ├── _hgl.py │ ├── functions.py │ └── dispatch.py ├── tester │ ├── __init__.py │ └── utils.py ├── __init__.py ├── parser │ ├── __init__.py │ ├── hgl_token.py │ └── tokenizer.py └── hook.py ├── documents ├── PyHGL │ ├── 1-basic │ │ ├── sim.md │ │ ├── index.md │ │ ├── assignment.md │ │ ├── module.md │ │ ├── signals.md │ │ ├── gates.md │ │ └── start.md │ └── 2-examples │ │ ├── index.md │ │ └── multiplier.md ├── index.md └── conf.py ├── setup.py ├── .gitignore ├── pyproject.toml ├── _make ├── __main__.py └── utils.py └── README.md /_test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyhgl/analog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyhgl/logic/compile/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/irq/dmem.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_test/test4_riscv/rv32_1stage/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_test/test5_SRV32/__init__.py: -------------------------------------------------------------------------------- 1 | from . import SRV32 -------------------------------------------------------------------------------- /pyhgl/array/__init__.py: -------------------------------------------------------------------------------- 1 | from .functions import * -------------------------------------------------------------------------------- /pyhgl/tester/__init__.py: -------------------------------------------------------------------------------- 1 | from .runner import * -------------------------------------------------------------------------------- /_test/test1_config/__init__.py: -------------------------------------------------------------------------------- 1 | from . import config -------------------------------------------------------------------------------- /_test/test3_simple/fifo/__init__.py: -------------------------------------------------------------------------------- 1 | from . import FIFO -------------------------------------------------------------------------------- /_test/test3_simple/mem/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mem -------------------------------------------------------------------------------- /_test/test3_simple/reg/__init__.py: -------------------------------------------------------------------------------- 1 | from . import reg -------------------------------------------------------------------------------- /_test/test6_AES/__init__.py: -------------------------------------------------------------------------------- 1 | from . import aes_core -------------------------------------------------------------------------------- /_test/test8_basic_simd/__init__.py: -------------------------------------------------------------------------------- 1 | from . import simd -------------------------------------------------------------------------------- /pyhgl/__init__.py: -------------------------------------------------------------------------------- 1 | import pyhgl.hook as _ 2 | 3 | -------------------------------------------------------------------------------- /_test/test3_simple/adder/__init__.py: -------------------------------------------------------------------------------- 1 | from . import adder -------------------------------------------------------------------------------- /_test/test3_simple/inout/__init__.py: -------------------------------------------------------------------------------- 1 | from . import inout -------------------------------------------------------------------------------- /_test/test7_chisel_examples/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ex -------------------------------------------------------------------------------- /_test/test3_simple/divider/__init__.py: -------------------------------------------------------------------------------- 1 | from . import divider -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/sim.md: -------------------------------------------------------------------------------- 1 | # Assertoins 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /_test/test3_simple/multiplier/__init__.py: -------------------------------------------------------------------------------- 1 | from . import multiplier -------------------------------------------------------------------------------- /_test/test3_simple/state/__init__.py: -------------------------------------------------------------------------------- 1 | from . import vending_machine -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/sim.md.txt: -------------------------------------------------------------------------------- 1 | # Assertoins 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup() 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /_test/.gitignore: -------------------------------------------------------------------------------- 1 | *.svg 2 | *.dot 3 | *.gv 4 | *.vcd 5 | *.v 6 | *.sv 7 | *.vvp -------------------------------------------------------------------------------- /pyhgl/logic/signal/__init__.py: -------------------------------------------------------------------------------- 1 | from .sint import * 2 | from .state import * -------------------------------------------------------------------------------- /_test/test2_basic/__init__.py: -------------------------------------------------------------------------------- 1 | from . import combinational 2 | from . import sint -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/objects.inv -------------------------------------------------------------------------------- /_test/test4_riscv/__init__.py: -------------------------------------------------------------------------------- 1 | from . import riscv_spec 2 | from .rv32_1stage import core -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/index.doctree -------------------------------------------------------------------------------- /_test/test0_grammar/__init__.py: -------------------------------------------------------------------------------- 1 | from . import grammar 2 | from . import dispatcher 3 | from . import array -------------------------------------------------------------------------------- /pyhgl/logic/gate/__init__.py: -------------------------------------------------------------------------------- 1 | from .common import * 2 | from .compare import * 3 | from .sint_gate import * -------------------------------------------------------------------------------- /docs/.doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/environment.pickle -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/irq/imem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/irq/imem.bin -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/hello/dmem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/hello/dmem.bin -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/hello/imem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/hello/imem.bin -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/dhrystone/dmem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/dhrystone/dmem.bin -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/dhrystone/imem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/dhrystone/imem.bin -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/exception/dmem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/exception/dmem.bin -------------------------------------------------------------------------------- /_test/test5_SRV32/sim/exception/imem.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/_test/test5_SRV32/sim/exception/imem.bin -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/sim.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/sim.doctree -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /documents/PyHGL/2-examples/index.md: -------------------------------------------------------------------------------- 1 | # Simple Examples 2 | 3 | ```{toctree} 4 | :maxdepth: 1 5 | :glob: 6 | 7 | * 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/gates.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/gates.doctree -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/index.doctree -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/module.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/module.doctree -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/start.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/start.doctree -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/2-examples/AES.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/2-examples/AES.doctree -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/signals.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/signals.doctree -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/2-examples/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/2-examples/index.doctree -------------------------------------------------------------------------------- /docs/_sources/PyHGL/2-examples/index.md.txt: -------------------------------------------------------------------------------- 1 | # Simple Examples 2 | 3 | ```{toctree} 4 | :maxdepth: 1 5 | :glob: 6 | 7 | * 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/1-basic/assignment.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/1-basic/assignment.doctree -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/_static/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/_static/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *__pycache__ 3 | *.swp 4 | *.egg-info 5 | *.vscode 6 | *.ipynb 7 | *.prof 8 | dist 9 | build 10 | /.* 11 | !.gitignore -------------------------------------------------------------------------------- /docs/.doctrees/PyHGL/2-examples/multiplier.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyHGL/pyhgl/HEAD/docs/.doctrees/PyHGL/2-examples/multiplier.doctree -------------------------------------------------------------------------------- /pyhgl/logic/utils/integer.py: -------------------------------------------------------------------------------- 1 | def parity(x): 2 | k = 0 3 | d = x 4 | while d != 0: 5 | k = k + 1 6 | d = d & (d - 1) 7 | return k % 2 8 | -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/index.md: -------------------------------------------------------------------------------- 1 | # Basic 2 | 3 | 4 | ```{toctree} 5 | :maxdepth: 1 6 | 7 | start.md 8 | signals.md 9 | gates.md 10 | assignment.md 11 | module.md 12 | sim.md 13 | 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/index.md.txt: -------------------------------------------------------------------------------- 1 | # Basic 2 | 3 | 4 | ```{toctree} 5 | :maxdepth: 1 6 | 7 | start.md 8 | signals.md 9 | gates.md 10 | assignment.md 11 | module.md 12 | sim.md 13 | 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /_test/test3_simple/__init__.py: -------------------------------------------------------------------------------- 1 | from . import adder 2 | from . import multiplier 3 | from . import divider 4 | from . import reg 5 | from . import state 6 | from . import fifo 7 | from . import mem 8 | from . import inout -------------------------------------------------------------------------------- /documents/index.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to PyHGL's documentation! 3 | 4 | ```{toctree} 5 | :hidden: 6 | 7 | 8 | PyHGL/1-basic/index 9 | 10 | PyHGL/2-examples/index 11 | 12 | 13 | ``` 14 | 15 | 16 | ```{include} ../README.md 17 | ``` -------------------------------------------------------------------------------- /docs/_sources/index.md.txt: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to PyHGL's documentation! 3 | 4 | ```{toctree} 5 | :hidden: 6 | 7 | 8 | PyHGL/1-basic/index 9 | 10 | PyHGL/2-examples/index 11 | 12 | 13 | ``` 14 | 15 | 16 | ```{include} ../README.md 17 | ``` -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 66fe355132678fd2399e4a2cb9af89a2 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /pyhgl/logic/__init__.py: -------------------------------------------------------------------------------- 1 | import pyhgl.hook as _ 2 | from pyhgl.array import * 3 | from ._config import conf 4 | from ._session import Session 5 | from .hgl_core import * 6 | from .module_hgl import * 7 | from .hgl_assign import * 8 | from .simulator_py import * 9 | from .hgl_assertion import * 10 | 11 | from .gate import * 12 | from .signal import * 13 | -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '', 4 | LANGUAGE: 'en', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false, 12 | SHOW_SEARCH_SUMMARY: true, 13 | ENABLE_SEARCH_SHORTCUTS: true, 14 | }; -------------------------------------------------------------------------------- /_test/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pyhgl 3 | from pyhgl.tester import tester 4 | 5 | import sys, traceback 6 | 7 | 8 | filter = '|'.join(sys.argv[1:]) 9 | if not filter: filter = '.*' 10 | tester.filter(filter) 11 | 12 | from _test import test0_grammar 13 | from _test import test1_config 14 | from _test import test2_basic 15 | from _test import test3_simple 16 | from _test import test5_SRV32 17 | from _test import test6_AES 18 | from _test import test7_chisel_examples 19 | from _test import test8_basic_simd 20 | 21 | 22 | 23 | tester.summary() 24 | 25 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "pyhgl" 7 | version = "0.2.0" 8 | description = "a Python-based Hardware Generation Language" 9 | requires-python = ">=3.8" 10 | dependencies = ["gmpy2", "graphviz", "numpy", "pegen", "pyvcd"] 11 | authors = [{name="Jintao Sun", email="jintaos2@outlook.com"}] 12 | readme = "README.md" 13 | classifiers = [ 14 | "Programming Language :: Python :: 3", 15 | "License :: OSI Approved :: GNU General Public License (GPL)", 16 | "Operating System :: OS Independent", 17 | ] 18 | 19 | [tool.setuptools] 20 | packages = ["pyhgl"] 21 | -------------------------------------------------------------------------------- /_test/test1_config/config.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import * 3 | 4 | 5 | @conf Global(w): 6 | conf.timing = Bundle( 7 | Wire = {'delay': 2}, 8 | And = {'delay': 2}, 9 | ) 10 | clk1 = 'clk1' 11 | clk2 = 'clk2' 12 | @conf('.*') RippleCarry: 13 | w = 6 14 | @conf FA: 15 | x = conf.up.w * 4 16 | @conf Sub: 17 | y = 'a' 18 | 19 | _tester = None 20 | 21 | @module FASub: 22 | _tester.Assert(conf.p.y == 'a') 23 | 24 | @module FA: 25 | _tester.Assert(conf.p.x == 12) 26 | _tester.Assert(conf.p.w == 6 ) 27 | _tester.Assert(conf.p.clk1 == 'clk1') 28 | _tester.Assert(conf.p.clk2 == 'clk2') 29 | fasub = FASub['Sub']() 30 | 31 | @tester test_config(self): 32 | global _tester 33 | _tester = self 34 | with Session(Global(3)) as sess: 35 | fa = FA() 36 | 37 | 38 | -------------------------------------------------------------------------------- /_test/test0_grammar/dispatcher.py: -------------------------------------------------------------------------------- 1 | 2 | from pyhgl.array import * 3 | from pyhgl.tester import * 4 | from typing import Any 5 | 6 | 7 | res = [] 8 | 9 | @tester 10 | def test_dispatch(self): 11 | test = Dispatcher() 12 | test.dispatch('Add', lambda x,y: res.append('Add'), [str, int], Any) 13 | test.call('Add',1, 1.2) 14 | test.dispatch('Add', lambda x,y: res.append('Add2'), [float, str, int], [float]) 15 | test.call('Add',1.1, 1.2) 16 | test.dispatch('make', lambda x: x*2, [str, int]) 17 | test2 = test.copy 18 | test.dispatch('make', lambda x: res.append(x), [str, int]) 19 | test2.dispatch('Add2', lambda x: res.append(x), Any) 20 | test2.call('Add2', 'xxx') 21 | 22 | self.AssertEq(res, ['Add', 'Add2', 'xxx']) 23 | 24 | import time 25 | t = time.time() 26 | for i in range(10000): 27 | test.call('make', 'xxx') 28 | self.AssertEq(len(res), 10003) 29 | -------------------------------------------------------------------------------- /docs/_static/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); -------------------------------------------------------------------------------- /pyhgl/logic/utils/unit.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | import re 3 | 4 | 5 | _number = re.compile(r""" 6 | [-+]? 7 | (?: 8 | (?: \d*\.\d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc 9 | |(?: \d+\.? ) # 1. 12. 123. etc 1 12 123 etc 10 | ) 11 | (?: [Ee] [+-]? \d+ ) ? # exponent part 12 | """, re.VERBOSE) 13 | 14 | 15 | TIME = { 16 | 's': 1.0, 17 | 'ms': 1e-3, 18 | 'us': 1e-6, 19 | 'ns': 1e-9, 20 | 'ps': 1e-12, 21 | 'fs': 1e-15 22 | } 23 | 24 | 25 | def quantity(x: str) -> Tuple[float, Tuple[float, str, dict]]: 26 | """ 27 | ex. '1ns' -> (1e-9, (1e-9, 's',TIME)) 28 | '3.3 ms' -> (3.3e-6, (3.3e-3, 'ms', TIME))' 29 | """ 30 | x = x.strip() 31 | if r := _number.match(x).span(): 32 | value = x[r[0]:r[1]] 33 | unit = x[r[1]:].strip() 34 | value = float(value) 35 | if unit in TIME: 36 | return value * TIME[unit], (value, unit, TIME) 37 | 38 | raise ValueError(f'invalid quantity: {x}') 39 | 40 | 41 | -------------------------------------------------------------------------------- /_make/__main__.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import shutil 4 | 5 | from .utils import * 6 | 7 | 8 | def make_requires(): 9 | """ install setuptools wheel twine pegen sphinx sphinx_rtd_theme sphinx_multiversion myst_parser linkify-it-py""" 10 | python("-m pip install -U setuptools wheel twine pegen") 11 | python("-m pip install -U sphinx sphinx_rtd_theme sphinx_multiversion myst_parser linkify-it-py") 12 | 13 | def make_parser(): 14 | """ generate pyhgl parser """ 15 | gram_file = relative_path('../pyhgl/parser/hgl_parser.gram', check_exist=True) 16 | target = relative_path('../pyhgl/parser/hgl_parser.py', check_exist=True) 17 | print(f"{gram_file} -> {target}") 18 | python(f"-m pegen {gram_file} -q -o {target}" ) 19 | 20 | 21 | def make_install(): 22 | """ install pyhgl package as editable """ 23 | python('-m build --wheel') 24 | python('-m pip install -e .') 25 | 26 | 27 | def make_release(): 28 | """ upload to pypi.org """ 29 | python('-m twine upload dist/*') 30 | 31 | 32 | def make_doc(): 33 | """ generate pyhgl doc """ 34 | try: 35 | import sphinx, myst_parser, linkify_it, sphinx_multiversion 36 | except: 37 | raise Exception('make_requires') 38 | 39 | source = relative_path('../documents', check_exist=True) 40 | target = relative_path('../docs', check_exist=True) 41 | shutil.rmtree(target) 42 | os.mkdir(target) 43 | 44 | print(f"{source} -> {target}") 45 | python(f"-m sphinx -q -b html {source} {target}" ) 46 | 47 | 48 | finish() 49 | 50 | -------------------------------------------------------------------------------- /pyhgl/logic/utils/trace.py: -------------------------------------------------------------------------------- 1 | import io 2 | import inspect 3 | import traceback 4 | import re 5 | import os 6 | 7 | _pyhgl_exec = re.compile(r'PyHGL Traceback:') 8 | 9 | def format_hgl_stack(n_ignored: int = 2, max_n: int = 100) -> str: 10 | """ return part of traced frame 11 | 12 | filename:lineno \n code \n 13 | """ 14 | stack = inspect.stack() 15 | i = 0 16 | for frame in stack: 17 | if (lines:=frame.code_context) is None: 18 | break 19 | else: 20 | line = lines[0] 21 | if _pyhgl_exec.search(line): 22 | break 23 | i += 1 24 | if i >= n_ignored+max_n: 25 | break 26 | 27 | useful_frames = stack[n_ignored:i] 28 | ret = [] 29 | for frame in useful_frames: 30 | if frame.code_context: 31 | code = frame.code_context[0] 32 | else: 33 | code = ' \n' 34 | ret.append(f' {frame.filename}:{frame.lineno}\n {code.lstrip()}') 35 | return ''.join(ret) 36 | 37 | def relative_path(path: str, level: int = 1, check_exist: bool = False) -> str: 38 | """ 39 | path: 40 | path of dir/file, relative to caller's directory/path. ex. ../a, ./b/, c/ 41 | level: 42 | nth caller 43 | check_exist: 44 | whether check the existance of path 45 | 46 | return: 47 | absolute path 48 | """ 49 | if os.path.isabs(path): 50 | ret = path 51 | else: 52 | a = os.path.dirname(inspect.stack()[level].filename) 53 | ret = os.path.abspath(os.path.join(a, path)) 54 | if check_exist: 55 | assert os.path.exists(ret) 56 | return re.sub(r'\\', '/', ret) 57 | -------------------------------------------------------------------------------- /_test/test3_simple/inout/inout.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import * 3 | 4 | 5 | @module sub: 6 | en3 = UInt('0') @ Input 7 | bus = UInt('8:b0') @ InOut 8 | 9 | when en3: 10 | bus <== UInt('8:hff') 11 | 12 | @module top: 13 | bus = UInt('8:b0') @ InOut 14 | data_in = UInt('8:b0') @ Input 15 | en1, en2, en3 = (0,0,0) @ Input 16 | 17 | when en1: 18 | bus <== data_in 19 | when en2: 20 | bus <== '8:hf0' 21 | 22 | m = sub() 23 | m.en3 <== en3 24 | m.bus <=> bus 25 | 26 | 27 | @task task1(self, dut): 28 | yield 50 29 | setv(dut.en1, 1) 30 | setv(dut.data_in, '00110011') 31 | yield 50 32 | self.EQ += getv(dut.bus), '00110011' 33 | setv(dut.en1, 0) 34 | setv(dut.en2, 1) 35 | yield 50 36 | self.EQ += getv(dut.bus), '11110000' 37 | setv(dut.en2, 0) 38 | yield 50 39 | self.EQ += getv(dut.bus), 'xxxxxxxx' 40 | setv(dut.en3, 1) 41 | yield 50 42 | self.EQ += getv(dut.bus), '11111111' 43 | 44 | 45 | 46 | @tester test_inout(self): 47 | sess = Session() 48 | sess.enter() 49 | 50 | dut = top() 51 | sess.track(dut) 52 | # print(dut.bus._data.writer._driver.iports) 53 | sess.join_none(task1(dut)) 54 | sess.step(12000) 55 | 56 | 57 | @blackbox testbench(builder): 58 | builder.append('initial begin\n$dumpfile("inout_iverilog.vcd");$dumpvars(0);') 59 | builder.append('$finish;\nend') 60 | sess.dumpVCD('inout.vcd') 61 | sess.dumpGraph('inout.gv') 62 | sess.dumpVerilog('inout.sv', delay=True, top=True) 63 | sess.test_iverilog() 64 | print(sess) 65 | sess.exit() 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /_make/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess 4 | import inspect 5 | import re 6 | 7 | def python(cmd: str): 8 | python = sys.executable 9 | subprocess.run(f"{python} {cmd}" ) 10 | 11 | def relative_path(path: str, level: int = 1, check_exist: bool = False) -> str: 12 | """ 13 | path: 14 | directory/filename that relative to caller's directory/path. ex. ../a, ./b/, c/ 15 | level: 16 | nth caller 17 | check_exist: 18 | whether check the existance of path 19 | 20 | return: 21 | an absolute path 22 | """ 23 | if os.path.isabs(path): 24 | ret = path 25 | else: 26 | a = os.path.dirname(inspect.stack()[level].filename) 27 | ret = os.path.abspath(os.path.join(a, path)) 28 | if check_exist: 29 | assert os.path.exists(ret), f'path {ret} does not exit.' 30 | return re.sub(r'\\', '/', ret) 31 | 32 | def yellow(x: str) -> str: 33 | return f'\033[0;33m{x}\033[0m' 34 | 35 | def finish(level: int = 1): 36 | """ match task names and then execute 37 | """ 38 | f_locals = sys._getframe(level).f_locals 39 | tasks = [(k, v) for k,v in f_locals.items() if k.startswith('make_') and inspect.isfunction(v)] 40 | del f_locals 41 | argv = '|'.join(sys.argv[1:]) 42 | if not argv: 43 | for f_name, f in tasks: 44 | print(yellow(f"python -m _make {f_name[5:]:<10}"), f.__doc__) 45 | print(yellow(f"python -m _make {'.*':<11}"), 'run all tasks inorder') 46 | sys.exit(0) 47 | filter = f"make_({argv})" 48 | for f_name, f in tasks: 49 | if re.match(filter, f_name): 50 | print(yellow(f_name)) 51 | f() 52 | sys.exit(0) -------------------------------------------------------------------------------- /pyhgl/logic/compile/graph.py: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | # 3 | # PyHGL - A Python-embedded Hardware Generation Language 4 | # Copyright (C) 2022 Jintao Sun 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | #################################################################################################### 20 | 21 | 22 | from __future__ import annotations 23 | from itertools import chain, islice 24 | from typing import Any, Dict, List, Set, Union, Tuple 25 | 26 | 27 | from pyhgl.array import * 28 | import pyhgl.logic.hgl_core as hgl_core 29 | import pyhgl.logic.module_hgl as module_hgl 30 | import pyhgl.logic._session as _session 31 | import pyhgl.logic.utils as utils 32 | 33 | class Value(HGL): 34 | def __init__(self) -> None: 35 | self.v64 = 0 36 | self.x64 = 0 37 | self.len = 64 38 | self.idx = 0 39 | 40 | 41 | class Node(HGL): 42 | 43 | def __init__(self) -> None: 44 | self.iports: List[Value] = [] 45 | self.oports: List[Value] = [] 46 | self.gates: List[hgl_core.Gate] = [] -------------------------------------------------------------------------------- /_test/test3_simple/reg/reg.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import * 3 | 4 | import random 5 | 6 | 7 | 8 | @module DLatch(en, D): 9 | """ en = 1: hold; en = 0: Q <== D 10 | """ 11 | Q = UInt() 12 | up = (~en) | Q 13 | down = en | D 14 | Q <== up & down 15 | 16 | 17 | @module MyReg: 18 | data, clk, set, reset = UInt[1].zeros(4) @ Input 19 | Q, Qn = UInt[1].zeros(2) @ Output 20 | 21 | nand1, nand2, nand3, nand4 = UInt[1].zeros(4) 22 | nand1 <== Nand(set, nand4, nand2) 23 | nand2 <== Nand(nand1, clk, reset) 24 | nand3 <== Nand(nand2, clk, nand4) 25 | nand4 <== Nand(nand3, data, reset) 26 | Q <== Nand(set, nand2, Qn) 27 | Qn <== Nand(Q, nand3, reset) 28 | 29 | 30 | 31 | @module ShiftRegs(n = 1): 32 | data, set_n, reset_n = UInt[1].zeros(3) @ Input 33 | clock = conf.clock[0] 34 | 35 | regs = Array(MyReg() for i in range(n)) 36 | regs[:, 'clk'] <== clock 37 | regs[:, 'set'] <== set_n 38 | regs[:, 'reset'] <== reset_n 39 | 40 | regs[0].data <== data 41 | regs[1:, 'data'] <== regs[:-1, 'Q'] 42 | 43 | out = regs[-1].Q 44 | out_n = regs[-1].Qn 45 | state = Cat(regs[:,'Q']) 46 | 47 | @task data_gen(self, dut): 48 | yield 10 49 | setv(dut.set_n, 1) 50 | yield 10 51 | setv(dut.reset_n, 1) 52 | 53 | for _ in range(100): 54 | yield self.clock_n() 55 | setr(dut.data) 56 | 57 | 58 | 59 | 60 | @tester test_reg(self): 61 | with Session() as sess: 62 | dut = ShiftRegs(10) 63 | 64 | sess.dumpVerilog('reg.sv', delay=True, top=True) 65 | sess.track(dut) 66 | sess.join(data_gen(dut)) 67 | 68 | sess.dumpVCD('reg.vcd') 69 | sess.dumpGraph('reg.gv') 70 | print(sess) 71 | 72 | -------------------------------------------------------------------------------- /_test/test3_simple/mem/mem.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import * 3 | 4 | 5 | @module memory(N): 6 | w_en = UInt('0') @ Input 7 | w_addr = UInt('8:b0') @ Input 8 | w_data1 = UInt('8:b0') @ Input 9 | w_data2 = UInt('8:b0') @ Input 10 | r_addr = UInt('8:b0') @ Input 11 | r_mem = UInt('8:b0') @ Output 12 | r_rom = UInt('8:b0') @ Output 13 | 14 | mem_t = MemArray[N,8] 15 | mem = Reg(mem_t(0), reset=None) 16 | rom = mem_t(i for i in range(N)) 17 | 18 | when w_en: 19 | mem[w_addr] <== w_data1 20 | mem[w_addr] <== w_data2 21 | 22 | r_mem <== mem[r_addr] 23 | r_rom <== rom[r_addr] 24 | 25 | 26 | 27 | @task task1(self, dut): 28 | for i in range(128): 29 | setv(dut.w_en, 1) 30 | setv(dut.w_addr, i) 31 | setv(dut.w_data1, i+1) 32 | setv(dut.w_data2, i+2) 33 | setv(dut.r_addr, i-1) 34 | yield self.clock() 35 | if i == 0 or i > 100: 36 | self.EQ += getv(dut.r_rom), Logic(0,0xff) 37 | self.EQ += getv(dut.r_mem), Logic(0,0xff) 38 | else: 39 | self.EQ += getv(dut.r_rom), i-1 40 | self.EQ += getv(dut.r_mem), i+1 41 | setv(dut.w_en, 0) 42 | 43 | 44 | 45 | @tester test_mem(self): 46 | sess = Session() 47 | sess.enter() 48 | 49 | dut = memory(100) 50 | sess.track(dut) 51 | sess.join_none(task1(dut)) 52 | sess.step(12000) 53 | 54 | 55 | @blackbox testbench(builder): 56 | builder.append('initial begin\n$dumpfile("mem_iverilog.vcd");$dumpvars(0);') 57 | builder.append('$finish;\nend') 58 | sess.dumpVCD('mem.vcd') 59 | sess.dumpGraph('mem.gv') 60 | sess.dumpVerilog('mem.sv', delay=True, top=True) 61 | sess.test_iverilog() 62 | print(sess) 63 | sess.exit() 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/assignment.md: -------------------------------------------------------------------------------- 1 | # Conditional Assignment 2 | 3 | Conditional assignments are core semantics of the **register-transfer abstraction**. There is no `always` block and sensitive list in PyHGL, and all assignments are non-blocking. 4 | 5 | 6 | ## Condition Statements 7 | 8 | - `when` statement is the special case of `switch` statement (same as `switch 1`). It sotres conditions in Python's local frame. These conditions will only influence the operator `<==`. 9 | - `switch` statement is more general, which is similar to the `case` statement in Verilog. 10 | 11 | ## Dynamic Enumerated Type 12 | 13 | The `Enum` type maps states to a specific encoding dynamically. Unlike `UInt`, a `Enum` type has variable bit-length. 14 | 15 | ```py 16 | state_t = Enum['idle', ...] # binary encoded enum type 17 | state_t = Enum['a', 'b', 'c'] # a 2-bit frozen enum type 18 | state_t = EnumOnehot['a','b','c',...] # a dynamic onehot-encoded enum type 19 | ``` 20 | 21 | VendingMachine example comes from [Chisel Tutorials](https://github.com/ucb-bar/chisel-tutorial) 22 | ```py 23 | @module VendingMachine: 24 | nickel = UInt(0) @ Input 25 | dime = UInt(0) @ Input 26 | valid = UInt(0) @ Output 27 | switch s:=Reg(EnumOnehot()): # a onehot-encoded signal 28 | once 'sIdle': # record state 'sIdle' 29 | when nickel: s <== 's5' # record state 's5' 30 | when dime : s <== 's10' # record state 's10' 31 | once 's5': 32 | when nickel: s <== 's10' 33 | when dime : s <== 's15' # record state 's15' 34 | once 's10': 35 | when nickel: s <== 's15' 36 | when dime : s <== 'sOk' # record state 'sOk' 37 | once 's15': 38 | when nickel: s <== 'sOk' 39 | when dime : s <== 'sOk' 40 | once 'sOk': 41 | s, valid <== 'sIdle', 1 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/assignment.md.txt: -------------------------------------------------------------------------------- 1 | # Conditional Assignment 2 | 3 | Conditional assignments are core semantics of the **register-transfer abstraction**. There is no `always` block and sensitive list in PyHGL, and all assignments are non-blocking. 4 | 5 | 6 | ## Condition Statements 7 | 8 | - `when` statement is the special case of `switch` statement (same as `switch 1`). It sotres conditions in Python's local frame. These conditions will only influence the operator `<==`. 9 | - `switch` statement is more general, which is similar to the `case` statement in Verilog. 10 | 11 | ## Dynamic Enumerated Type 12 | 13 | The `Enum` type maps states to a specific encoding dynamically. Unlike `UInt`, a `Enum` type has variable bit-length. 14 | 15 | ```py 16 | state_t = Enum['idle', ...] # binary encoded enum type 17 | state_t = Enum['a', 'b', 'c'] # a 2-bit frozen enum type 18 | state_t = EnumOnehot['a','b','c',...] # a dynamic onehot-encoded enum type 19 | ``` 20 | 21 | VendingMachine example comes from [Chisel Tutorials](https://github.com/ucb-bar/chisel-tutorial) 22 | ```py 23 | @module VendingMachine: 24 | nickel = UInt(0) @ Input 25 | dime = UInt(0) @ Input 26 | valid = UInt(0) @ Output 27 | switch s:=Reg(EnumOnehot()): # a onehot-encoded signal 28 | once 'sIdle': # record state 'sIdle' 29 | when nickel: s <== 's5' # record state 's5' 30 | when dime : s <== 's10' # record state 's10' 31 | once 's5': 32 | when nickel: s <== 's10' 33 | when dime : s <== 's15' # record state 's15' 34 | once 's10': 35 | when nickel: s <== 's15' 36 | when dime : s <== 'sOk' # record state 'sOk' 37 | once 's15': 38 | when nickel: s <== 'sOk' 39 | when dime : s <== 'sOk' 40 | once 'sOk': 41 | s, valid <== 'sIdle', 1 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /pyhgl/logic/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .convert import * 2 | from .unit import * 3 | from .trace import * 4 | from .integer import * 5 | 6 | 7 | def get_dll_close(): 8 | import sys, platform, ctypes 9 | OS = platform.system() 10 | if OS == "Windows": # pragma: Windows 11 | dll_close = ctypes.windll.kernel32.FreeLibrary 12 | elif OS == "Darwin": 13 | try: 14 | try: 15 | # macOS 11 (Big Sur). Possibly also later macOS 10s. 16 | stdlib = ctypes.CDLL("libc.dylib") 17 | except OSError: 18 | stdlib = ctypes.CDLL("libSystem") 19 | except OSError: 20 | # Older macOSs. Not only is the name inconsistent but it's 21 | # not even in PATH. 22 | stdlib = ctypes.CDLL("/usr/lib/system/libsystem_c.dylib") 23 | dll_close = stdlib.dlclose 24 | elif OS == "Linux": 25 | try: 26 | stdlib = ctypes.CDLL("") 27 | except OSError: 28 | # Alpine Linux. 29 | stdlib = ctypes.CDLL("libc.so") 30 | dll_close = stdlib.dlclose 31 | elif sys.platform == "msys": 32 | # msys can also use `ctypes.CDLL("kernel32.dll").FreeLibrary()`. Not sure 33 | # if or what the difference is. 34 | stdlib = ctypes.CDLL("msys-2.0.dll") 35 | dll_close = stdlib.dlclose 36 | elif sys.platform == "cygwin": 37 | stdlib = ctypes.CDLL("cygwin1.dll") 38 | dll_close = stdlib.dlclose 39 | elif OS == "FreeBSD": 40 | # FreeBSD uses `/usr/lib/libc.so.7` where `7` is another version number. 41 | # It is not in PATH but using its name instead of its path is somehow the 42 | # only way to open it. The name must include the .so.7 suffix. 43 | stdlib = ctypes.CDLL("libc.so.7") 44 | dll_close = stdlib.close 45 | else: 46 | raise NotImplementedError("Unknown platform.") 47 | 48 | dll_close.argtypes = [ctypes.c_void_p] 49 | return dll_close -------------------------------------------------------------------------------- /pyhgl/tester/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import shutil 5 | import subprocess 6 | import time 7 | import inspect 8 | 9 | def _red(x: str) -> str: return f'\033[0;31m{x}\033[0m' 10 | def _green(x: str) -> str: return f'\033[0;32m{x}\033[0m' 11 | def _yellow(x: str) -> str: return f'\033[0;33m{x}\033[0m' 12 | def _blue(x: str) -> str: return f'\033[0;34m{x}\033[0m' 13 | def _purple(x: str) -> str: return f'\033[0;35m{x}\033[0m' 14 | 15 | def _fill_terminal(x: str, char: str, pos: str = 'center') -> str: 16 | try: 17 | terminal_width = shutil.get_terminal_size().columns 18 | except: 19 | terminal_width = 80 20 | n = terminal_width - len(x) - 1 21 | if n < 2: 22 | return x 23 | else: 24 | if pos == 'center': 25 | left = n // 2 26 | right = n - left 27 | return f'{left*char}{x}{right*char}' 28 | elif pos == 'left': 29 | return f'{x}{char*n}' 30 | elif pos == 'right': 31 | return f'{char*n}{x}' 32 | else: 33 | return x 34 | 35 | def relative_path(path: str, level: int = 1, check_exist: bool = False) -> str: 36 | """ 37 | path: 38 | path of dir/file, relative to caller's directory/path. ex. ../a, ./b/, c/ 39 | level: 40 | nth caller 41 | check_exist: 42 | whether check the existance of path 43 | 44 | return: 45 | absolute path 46 | """ 47 | if os.path.isabs(path): 48 | ret = path 49 | else: 50 | a = os.path.dirname(inspect.stack()[level].filename) 51 | ret = os.path.abspath(os.path.join(a, path)) 52 | if check_exist: 53 | assert os.path.exists(ret) 54 | return re.sub(r'\\', '/', ret) 55 | 56 | def caller_filename(level: int = 1): 57 | ret = inspect.stack()[level+1].filename 58 | return re.sub(r'\\', '/', ret) 59 | 60 | 61 | def run_python(cmd: str): 62 | python = sys.executable 63 | subprocess.run(f"{python} {cmd}" ) 64 | 65 | 66 | -------------------------------------------------------------------------------- /pyhgl/array/_hgl.py: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | # 3 | # PyHGL - A Python-embedded Hardware Generation Language 4 | # Copyright (C) 2022 Jintao Sun 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | #################################################################################################### 20 | 21 | 22 | from __future__ import annotations 23 | 24 | 25 | 26 | 27 | class _Session: 28 | def __init__(self): self.sess: object = None 29 | def __set__(self, obj, sess): self.sess = sess 30 | def __get__(self, obj, cls=None): return self.sess 31 | 32 | 33 | class HGL: 34 | 35 | __slots__ = () 36 | _sess = _Session() 37 | 38 | @property 39 | def __hgl_version__(self): 40 | return (0,0,1) 41 | 42 | @property 43 | def __hgl_type__(self): 44 | return type(self) 45 | 46 | def __copy__(self): 47 | return self 48 | 49 | def __hash__(self): 50 | return id(self) 51 | 52 | def __bool__(self): 53 | raise NotImplementedError(self.__class__) 54 | 55 | def __eq__(self, x): 56 | raise NotImplementedError(self.__class__) 57 | 58 | def __str__(self): 59 | return object.__repr__(self) 60 | 61 | def __repr__(self): 62 | return str(self) 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /_test/test2_basic/sint.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import * 3 | 4 | 5 | @module Top: 6 | 7 | a = UInt[8](0) @ Input 8 | b = UInt[8](0) @ Input 9 | 10 | 11 | res_mul1 = SInt(a.sext(16)) * SInt(b.sext(16)) 12 | res_mul2 = a.zext(16) * SInt(b.sext(16)) 13 | res_mul3 = SInt(a.sext(16)) * b.zext(16) 14 | 15 | res_div = SInt(a) // SInt(b) 16 | res_rem = SInt(a) % SInt(b) 17 | res_rshift = SInt(a) >> b[:4] 18 | 19 | 20 | vec_t = UInt[8]*3 21 | v = vec_t(0) 22 | 23 | struct_t = Struct( 24 | a = UInt[3] @ 2, 25 | b = UInt[4] @ 8 26 | ) 27 | x = struct_t({ 28 | 'a' : 3, 'b' : '1011' 29 | }) 30 | 31 | x['a'] <== 0 32 | 33 | print(BitPat('b0101??11')) 34 | 35 | print(12*UInt[8]) 36 | print(Struct( 37 | a = UInt[3] @2, 38 | b = (3,4) * UInt[4] @7, 39 | c = UInt[1] @13, 40 | d = Struct( 41 | x = UInt[1], 42 | y = UInt[2] 43 | ) @14 44 | )) 45 | 46 | 47 | 48 | 49 | @task test_top(self, dut, test_data): 50 | for _ in range(20): 51 | a, b = setr(dut.a), setr(dut.b) 52 | test_data.append((a,b)) 53 | yield 20 54 | self.AssertEq(getv(dut.res_mul1).to_int(16), a.to_int(8) * b.to_int(8)) 55 | self.Assert(True) 56 | 57 | 58 | @tester test_sint(self): 59 | with Session() as sess: 60 | dut = Top() 61 | test_data = [] 62 | sess.track(dut) 63 | sess.join(test_top(dut, test_data)) 64 | 65 | sess.dumpVCD('sint.vcd') 66 | sess.dumpGraph('sint.gv') 67 | print(sess) 68 | 69 | @blackbox testbench(builder): 70 | a = builder.get_name(dut.a) 71 | b = builder.get_name(dut.b) 72 | builder.append('initial begin\n$dumpfile("sint_iverilog.vcd");$dumpvars(0);') 73 | for test_a, test_b in test_data: 74 | builder.append(f'{a}={test_a};{b}={test_b};#20;') 75 | builder.append('$finish;\nend') 76 | 77 | sess.dumpVerilog('sint.sv', delay=True, top = True) 78 | sess.test_iverilog() 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /documents/PyHGL/2-examples/multiplier.md: -------------------------------------------------------------------------------- 1 | # WallaceMultiplier 2 | 3 | ```py 4 | from pyhgl.logic import * 5 | 6 | import random 7 | import time 8 | import itertools 9 | 10 | 11 | def compressN(column: list, sums: list, couts: list): 12 | for i in range(0, len(column), 3): 13 | x = column[i:i+3] 14 | if len(x) == 1: 15 | sums.append(x[0]) 16 | elif len(x) == 2: 17 | sums.append(Xor(*x)) 18 | couts.append(And(*x)) 19 | else: 20 | sums.append(Xor(*x)) 21 | couts.append(x[0] & x[1] | x[2] & (x[0] ^ x[1])) 22 | 23 | 24 | @module WallaceMultiplier(w1: int, w2: int): 25 | io = Bundle( 26 | x = Input(UInt(0, w1)), 27 | y = Input(UInt(0, w2)), 28 | out = Output(UInt(0, w1+w2)), 29 | ) 30 | products = [[] for _ in range(w1+w2)] 31 | for (i, x_i), (j, y_j) in itertools.product(enumerate(io.x.split()), enumerate(io.y.split())): 32 | products[i+j].append(x_i & y_j) 33 | 34 | 35 | while max(len(i) for i in products) >= 3: 36 | products_new = [[] for _ in range(len(products)+1)] 37 | for i, column in enumerate(products): 38 | compressN(column, products_new[i], products_new[i+1]) 39 | products = products_new 40 | 41 | while len(products[-1]) == 0: 42 | products.pop() 43 | 44 | a = Cat(*[i[0] for i in products]) 45 | b = Cat(*[i[1] if len(i) == 2 else UInt(0) for i in products]) 46 | io.out <== a + b 47 | 48 | 49 | #--------------------------------- test ---------------------------------------- 50 | 51 | @task mult_test(self, dut, data): 52 | out_mask = Logic( (1 << len(dut.io.out)) - 1, 0) 53 | for x, y in data: 54 | setv(dut.io.x, x) 55 | setv(dut.io.y, y) 56 | yield self.clock_n() 57 | self.AssertEq(getv(dut.io.out), (x * y) & out_mask) 58 | 59 | 60 | with Session() as sess: 61 | W, N = 16, 2000 62 | ratio = 0.0 63 | dut = WallaceMultiplier(W, W) 64 | sess.track(dut.io, dut.a, dut.b) 65 | test_data = [(Logic.rand(W,W, ratio=ratio), Logic.rand(W,W, ratio=ratio)) for _ in range(N)] 66 | 67 | sess.join(mult_test(dut, test_data)) 68 | sess.dumpVCD('Multiplier.vcd') 69 | 70 | sess.dumpVerilog('Multiplier.sv', delay=True, top = True) 71 | print(sess) 72 | ``` -------------------------------------------------------------------------------- /docs/_sources/PyHGL/2-examples/multiplier.md.txt: -------------------------------------------------------------------------------- 1 | # WallaceMultiplier 2 | 3 | ```py 4 | from pyhgl.logic import * 5 | 6 | import random 7 | import time 8 | import itertools 9 | 10 | 11 | def compressN(column: list, sums: list, couts: list): 12 | for i in range(0, len(column), 3): 13 | x = column[i:i+3] 14 | if len(x) == 1: 15 | sums.append(x[0]) 16 | elif len(x) == 2: 17 | sums.append(Xor(*x)) 18 | couts.append(And(*x)) 19 | else: 20 | sums.append(Xor(*x)) 21 | couts.append(x[0] & x[1] | x[2] & (x[0] ^ x[1])) 22 | 23 | 24 | @module WallaceMultiplier(w1: int, w2: int): 25 | io = Bundle( 26 | x = Input(UInt(0, w1)), 27 | y = Input(UInt(0, w2)), 28 | out = Output(UInt(0, w1+w2)), 29 | ) 30 | products = [[] for _ in range(w1+w2)] 31 | for (i, x_i), (j, y_j) in itertools.product(enumerate(io.x.split()), enumerate(io.y.split())): 32 | products[i+j].append(x_i & y_j) 33 | 34 | 35 | while max(len(i) for i in products) >= 3: 36 | products_new = [[] for _ in range(len(products)+1)] 37 | for i, column in enumerate(products): 38 | compressN(column, products_new[i], products_new[i+1]) 39 | products = products_new 40 | 41 | while len(products[-1]) == 0: 42 | products.pop() 43 | 44 | a = Cat(*[i[0] for i in products]) 45 | b = Cat(*[i[1] if len(i) == 2 else UInt(0) for i in products]) 46 | io.out <== a + b 47 | 48 | 49 | #--------------------------------- test ---------------------------------------- 50 | 51 | @task mult_test(self, dut, data): 52 | out_mask = Logic( (1 << len(dut.io.out)) - 1, 0) 53 | for x, y in data: 54 | setv(dut.io.x, x) 55 | setv(dut.io.y, y) 56 | yield self.clock_n() 57 | self.AssertEq(getv(dut.io.out), (x * y) & out_mask) 58 | 59 | 60 | with Session() as sess: 61 | W, N = 16, 2000 62 | ratio = 0.0 63 | dut = WallaceMultiplier(W, W) 64 | sess.track(dut.io, dut.a, dut.b) 65 | test_data = [(Logic.rand(W,W, ratio=ratio), Logic.rand(W,W, ratio=ratio)) for _ in range(N)] 66 | 67 | sess.join(mult_test(dut, test_data)) 68 | sess.dumpVCD('Multiplier.vcd') 69 | 70 | sess.dumpVerilog('Multiplier.sv', delay=True, top = True) 71 | print(sess) 72 | ``` -------------------------------------------------------------------------------- /_test/test3_simple/state/vending_machine.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import tester, utils 3 | 4 | import random 5 | import time 6 | 7 | 8 | 9 | @module VendingMachine: 10 | io = Bundle( 11 | nickel = UInt(0) @ Input, 12 | dime = UInt(0) @ Input, 13 | valid = UInt(0) @ Output, 14 | ) 15 | 16 | 17 | switch s:=Reg(Enum()): 18 | once 'sIdle': 19 | when io.nickel: 20 | s <== 's5' 21 | when io.dime: 22 | s <== 's10' 23 | once 's5': 24 | when io.nickel: 25 | s <== 's10' 26 | when io.dime: 27 | s <== 's15' 28 | once 's10': 29 | when io.nickel: 30 | s <== 's15' 31 | when io.dime: 32 | s <== 'sOk' 33 | once 's15': 34 | when io.nickel: 35 | s <== 'sOk' 36 | when io.dime: 37 | s <== 'sOk' 38 | once 'sOk': 39 | s <== 'sIdle' 40 | io.valid <== 1 41 | 42 | 43 | #--------------------------------- test ---------------------------------------- 44 | 45 | @task task1(self, dut): 46 | setv(dut.io.nickel, 1) 47 | setv(dut.io.dime, 1) 48 | yield self.until(dut.io.valid, 1) 49 | self.Assert(True) 50 | 51 | @tester test_vending_machine(self): 52 | sess = Session() 53 | sess.enter() 54 | 55 | 56 | dut = VendingMachine() 57 | sess.track(dut) 58 | sess.join(task1(dut)) 59 | 60 | 61 | sess.dumpVCD('VendingMachine.vcd') 62 | 63 | @blackbox testbench(builder): 64 | dut_name = builder.get_name(dut) 65 | nickel = builder.get_name(dut.io.nickel) 66 | dime = builder.get_name(dut.io.dime) 67 | reset = builder.get_name(dut.reset[0]) 68 | 69 | builder.append('initial begin') 70 | builder.append(f'$dumpfile("VendingMachine_iverilog.vcd");') 71 | builder.append(f'$dumpvars(0, {dut_name});') 72 | builder.append(f'{reset} = 1; #100; {reset}=0;') 73 | builder.append(f'{nickel} = 1; {dime} = 1; #1000;') 74 | builder.append('$finish;') 75 | builder.append('end') 76 | 77 | sess.dumpVerilog('VendingMachine.sv', delay=True, top = True) 78 | sess.dumpGraph('VendingMachine.gv') 79 | sess.test_iverilog() 80 | print(sess) 81 | sess.exit() 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # PyHGL 3 | 4 | PyHGL is a Python-based Hardware Generation Language. Similar languages are: Verilog, Chisel, PyRTL, etc. PyHGL provides hardware modeling, simulation and verification in pure Python environment. Some features are: 5 | 6 | - Pythonic syntax 7 | - Three-state (0, 1, and X) logic 8 | - Vectorized operation 9 | - Asynchronous event-driven simulaton 10 | - Simulation tasks and concurrent assertions 11 | 12 | --- 13 | 14 | - Documentation: [https://pyhgl.github.io/pyhgl/](https://pyhgl.github.io/pyhgl/) 15 | - Similar Projects: [https://github.com/drom/awesome-hdl](https://github.com/drom/awesome-hdl) 16 | 17 | 18 | # Install 19 | 20 | ``` 21 | python -m pip install pyhgl 22 | ``` 23 | 24 | # Example 25 | 26 | Design and verify a 32-bit Carry Ripple Adder and a 64-bit Kogge Stone Adder. 27 | 28 | ```py 29 | from pyhgl.logic import * 30 | 31 | @conf Config: 32 | @conf RippleCarry: 33 | w = 32 34 | @conf KoggeStone: 35 | w = 64 36 | 37 | AdderIO = lambda w: Bundle( 38 | x = UInt[w ](0) @ Input, 39 | y = UInt[w ](0) @ Input, 40 | out = UInt[w+1](0) @ Output, 41 | ) 42 | 43 | @module FullAdder: 44 | a, b, cin = UInt([0,0,0]) 45 | s = a ^ b ^ cin 46 | cout = a & b | (a ^ b) & cin 47 | 48 | @module RippleCarry: 49 | io = AdderIO(conf.p.w) 50 | adders = Array(FullAdder() for _ in range(conf.p.w)) 51 | adders[:,'a' ] <== io.x.split() 52 | adders[:,'b' ] <== io.y.split() 53 | adders[:,'cin'] <== 0, *adders[:-1,'cout'] 54 | io.out <== Cat(*adders[:,'s'], adders[-1,'cout']) 55 | 56 | @module KoggeStone: 57 | io = AdderIO(conf.p.w) 58 | P_odd = io.x ^ io.y 59 | P = P_odd.split() 60 | G = (io.x & io.y).split() 61 | dist = 1 62 | while dist < conf.p.w: 63 | for i in reversed(range(dist,conf.p.w)): 64 | G[i] = G[i] | (P[i] & G[i-dist]) 65 | if i >= dist * 2: 66 | P[i] = P[i] & P[i-dist] 67 | dist *= 2 68 | io.out <== Cat(0, *G) ^ P_odd 69 | 70 | @task tb(self, dut, N): 71 | for _ in range(N): 72 | x, y = setr(dut.io[['x','y']]) 73 | yield self.clock_n() 74 | self.AssertEq(getv(dut.io.out), x + y) 75 | 76 | with Session(Config()) as sess: 77 | adder1, adder2 = RippleCarry(), KoggeStone() 78 | sess.track(adder1, adder2) 79 | sess.join(tb(adder1, 100), tb(adder2, 200)) 80 | sess.dumpVCD('Adders.vcd') 81 | sess.dumpVerilog('Adders.sv') 82 | ``` 83 | -------------------------------------------------------------------------------- /pyhgl/parser/__init__.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import io 3 | 4 | from .hgl_parser import parse_string as hgl_parse_string 5 | from .hgl_parser import parse_file as hgl_parse_file 6 | from .hgl_tokenize import generate_tokens 7 | 8 | 9 | def hgl_compile(source:str, name:str = ''): 10 | """parse and compile in exec mode 11 | """ 12 | ast_tree = hgl_parse_string(source, 'exec') 13 | return compile(ast_tree, name, 'exec') 14 | 15 | def hgl_tokenizer(code: str): 16 | return generate_tokens(io.StringIO(code).readline) 17 | 18 | # https://bitbucket.org/takluyver/greentreesnakes/src/master/astpp.py 19 | def ast_dump(node, annotate_fields=True, include_attributes=False, indent=' '): 20 | """ 21 | Return a formatted dump of the tree in *node*. This is mainly useful for 22 | debugging purposes. The returned string will show the names and the values 23 | for fields. This makes the code impossible to evaluate, so if evaluation is 24 | wanted *annotate_fields* must be set to False. Attributes such as line 25 | numbers and column offsets are not dumped by default. If this is wanted, 26 | *include_attributes* can be set to True. 27 | """ 28 | if isinstance(node, str): 29 | node = hgl_parse_string(node, 'exec') 30 | 31 | def _format(node, level=0): 32 | if isinstance(node, ast.AST): 33 | fields = [(a, _format(b, level)) for a, b in ast.iter_fields(node)] 34 | if include_attributes and node._attributes: 35 | fields.extend([(a, _format(getattr(node, a), level)) 36 | for a in node._attributes]) 37 | return ''.join([ 38 | node.__class__.__name__, 39 | '(', 40 | ', '.join(('%s=%s' % field for field in fields) 41 | if annotate_fields else 42 | (b for a, b in fields)), 43 | ')']) 44 | elif isinstance(node, list): 45 | lines = ['['] 46 | lines.extend((indent * (level + 2) + _format(x, level + 2) + ',' 47 | for x in node)) 48 | if len(lines) > 1: 49 | lines.append(indent * (level + 1) + ']') 50 | else: 51 | lines[-1] += ']' 52 | return '\n'.join(lines) 53 | return repr(node) 54 | 55 | if not isinstance(node, ast.AST): 56 | raise TypeError('expected AST, got %r' % node.__class__.__name__) 57 | return _format(node) 58 | 59 | 60 | -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/module.md: -------------------------------------------------------------------------------- 1 | # Module 2 | 3 | 4 | - Module ports can be automatically infered for signals cross multiple modules 5 | - Each module instance has its own parameters. All module instances construct a module tree, and all parameters construct a parameter tree. 6 | - configuration function is lazy evaluated and matches module id, which is the declaration name by default . 7 | 8 | 9 | ## Config 10 | 11 | ```py 12 | @conf Global(clock_period): 13 | # set timing 14 | conf.timing = Bundle( 15 | timescale = '1ns', 16 | Clock = {'low':clock_period, 'high':clock_period, 'phase':0}, 17 | Logic = {'delay': 1}, 18 | Gate = {'delay': 1}, 19 | ) 20 | # a configuration that matches "RippleCarry.*" 21 | @conf RippleCarry: 22 | w = 32 23 | @conf KoggeStone: 24 | w = 64 25 | 26 | 27 | AdderIO = lambda w: Bundle( 28 | x = UInt[w ](0) @ Input, 29 | y = UInt[w ](0) @ Input, 30 | out = UInt[w+1](0) @ Output) 31 | 32 | @module FullAdder: 33 | a, b, cin = UInt([0,0,0]) 34 | s = a ^ b ^ cin 35 | cout = a & b | (a ^ b) & cin 36 | 37 | @module RippleCarry: 38 | io = AdderIO(conf.p.w) 39 | adders = Array(FullAdder() for _ in range(conf.p.w)) 40 | adders[:,'a' ] <== io.x.split() 41 | adders[:,'b' ] <== io.y.split() 42 | adders[:,'cin'] <== 0, *adders[:-1,'cout'] 43 | io.out <== Cat(*adders[:,'s'], adders[-1,'cout']) 44 | 45 | @module KoggeStone: 46 | io = AdderIO(conf.p.w) 47 | P_odd = io.x ^ io.y 48 | P = P_odd.split() 49 | G = (io.x & io.y).split() 50 | dist = 1 51 | while dist < conf.p.w: 52 | for i in reversed(range(dist,conf.p.w)): 53 | G[i] = G[i] | (P[i] & G[i-dist]) 54 | if i >= dist * 2: 55 | P[i] = P[i] & P[i-dist] 56 | dist *= 2 57 | io.out <== Cat(0, *G) ^ P_odd 58 | ``` 59 | 60 | 61 | 62 | ## Session 63 | 64 | All signals, gates and modules should be instantiated in a `Session`, which contains the simulator and other necessary settings. 65 | 66 | ```py 67 | @task tb(self, dut, N): 68 | for _ in range(N): 69 | x, y = setr(dut.io[['x','y']]) 70 | yield self.clock_n() 71 | self.AssertEq(getv(dut.io.out), x + y) 72 | 73 | with Session(Config()) as sess: 74 | adder1, adder2 = RippleCarry(), KoggeStone() 75 | sess.track(adder1, adder2) 76 | sess.join(tb(adder1, 100), tb(adder2, 200)) 77 | sess.dumpVCD('Adders.vcd') 78 | sess.dumpVerilog('Adders.sv') 79 | ``` -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/module.md.txt: -------------------------------------------------------------------------------- 1 | # Module 2 | 3 | 4 | - Module ports can be automatically infered for signals cross multiple modules 5 | - Each module instance has its own parameters. All module instances construct a module tree, and all parameters construct a parameter tree. 6 | - configuration function is lazy evaluated and matches module id, which is the declaration name by default . 7 | 8 | 9 | ## Config 10 | 11 | ```py 12 | @conf Global(clock_period): 13 | # set timing 14 | conf.timing = Bundle( 15 | timescale = '1ns', 16 | Clock = {'low':clock_period, 'high':clock_period, 'phase':0}, 17 | Logic = {'delay': 1}, 18 | Gate = {'delay': 1}, 19 | ) 20 | # a configuration that matches "RippleCarry.*" 21 | @conf RippleCarry: 22 | w = 32 23 | @conf KoggeStone: 24 | w = 64 25 | 26 | 27 | AdderIO = lambda w: Bundle( 28 | x = UInt[w ](0) @ Input, 29 | y = UInt[w ](0) @ Input, 30 | out = UInt[w+1](0) @ Output) 31 | 32 | @module FullAdder: 33 | a, b, cin = UInt([0,0,0]) 34 | s = a ^ b ^ cin 35 | cout = a & b | (a ^ b) & cin 36 | 37 | @module RippleCarry: 38 | io = AdderIO(conf.p.w) 39 | adders = Array(FullAdder() for _ in range(conf.p.w)) 40 | adders[:,'a' ] <== io.x.split() 41 | adders[:,'b' ] <== io.y.split() 42 | adders[:,'cin'] <== 0, *adders[:-1,'cout'] 43 | io.out <== Cat(*adders[:,'s'], adders[-1,'cout']) 44 | 45 | @module KoggeStone: 46 | io = AdderIO(conf.p.w) 47 | P_odd = io.x ^ io.y 48 | P = P_odd.split() 49 | G = (io.x & io.y).split() 50 | dist = 1 51 | while dist < conf.p.w: 52 | for i in reversed(range(dist,conf.p.w)): 53 | G[i] = G[i] | (P[i] & G[i-dist]) 54 | if i >= dist * 2: 55 | P[i] = P[i] & P[i-dist] 56 | dist *= 2 57 | io.out <== Cat(0, *G) ^ P_odd 58 | ``` 59 | 60 | 61 | 62 | ## Session 63 | 64 | All signals, gates and modules should be instantiated in a `Session`, which contains the simulator and other necessary settings. 65 | 66 | ```py 67 | @task tb(self, dut, N): 68 | for _ in range(N): 69 | x, y = setr(dut.io[['x','y']]) 70 | yield self.clock_n() 71 | self.AssertEq(getv(dut.io.out), x + y) 72 | 73 | with Session(Config()) as sess: 74 | adder1, adder2 = RippleCarry(), KoggeStone() 75 | sess.track(adder1, adder2) 76 | sess.join(tb(adder1, 100), tb(adder2, 200)) 77 | sess.dumpVCD('Adders.vcd') 78 | sess.dumpVerilog('Adders.sv') 79 | ``` -------------------------------------------------------------------------------- /_test/test3_simple/adder/adder.pyh: -------------------------------------------------------------------------------- 1 | 2 | from pyhgl.logic import * 3 | from pyhgl.tester import tester, utils 4 | 5 | import time 6 | import sys 7 | 8 | @conf Global: 9 | w = 512 10 | @conf RippleCarry: 11 | w = conf.up.w 12 | @conf KoggeStone: 13 | w = conf.up.w 14 | 15 | AdderIO = lambda w: Bundle( 16 | x = UInt[w](0) @ Input, 17 | y = UInt[w](0) @ Input, 18 | out = UInt[w+1](0) @ Output, 19 | ) 20 | 21 | @module FA: 22 | a, b, cin = UInt(0), UInt(0), UInt(0) 23 | s = Xor(a, b, cin) 24 | cout = a & b | (a ^ b) & cin 25 | 26 | @module RippleCarry: 27 | io = AdderIO(conf.p.w) 28 | fa = Array(FA() for _ in range(conf.p.w)) 29 | fa[:, 'a'] <== io.x.split() 30 | fa[:, 'b'] <== io.y.split() 31 | fa[:, 'cin'] <== 0, *fa[:-1,'cout'] 32 | io.out <== Cat(*fa[:,'s'], fa[-1,'cout']) 33 | 34 | @module KoggeStone: 35 | io = AdderIO(conf.p.w) 36 | P_odd = io.x ^ io.y 37 | P = P_odd.split() 38 | G = (io.x & io.y).split() 39 | dist = 1 40 | while dist < conf.p.w: 41 | for i in reversed(range(dist,conf.p.w)): 42 | G[i] = G[i] | (P[i] & G[i-dist]) 43 | if i >= dist * 2: 44 | P[i] = P[i] & P[i-dist] 45 | dist *= 2 46 | io.out <== Cat(0, *G) ^ P_odd 47 | 48 | 49 | @task data_gen(self, dut, test_data): 50 | for x, y in test_data: 51 | setv(dut.io.x, x) 52 | setv(dut.io.y, y) 53 | yield self.clock_n() 54 | self.AssertEq(getv(dut.io.out), x+y) 55 | 56 | @tester test_adder(self): 57 | with Session(conf=Global(), verbose_sim=False) as sess: 58 | 59 | # dut = KoggeStone() 60 | dut = RippleCarry() 61 | # sess.track(dut.io) 62 | 63 | test_data = [(Logic.rand(conf.p.w), Logic.rand(conf.p.w)) for _ in range(1000)] 64 | t = time.time() 65 | sess.join(data_gen(dut, test_data)) 66 | print('pyhgl cost: ', time.time()-t) 67 | sess.dumpVCD('Adder.vcd') 68 | sess.dumpGraph('Adder.gv') 69 | print(sess) 70 | 71 | @blackbox testbench(builder): 72 | x = builder.get_name(dut.io.x) 73 | y = builder.get_name(dut.io.y) 74 | 75 | builder.append('initial begin\n$dumpfile("Adder_iverilog.vcd");$dumpvars(0);') 76 | for test_x, test_y in test_data: 77 | builder.append(f'{x}={test_x};{y}={test_y};#200;') 78 | builder.append('$finish;\nend') 79 | 80 | sess.dumpVerilog('Adder.sv', delay=True, top = True) 81 | sess.test_iverilog() 82 | 83 | 84 | -------------------------------------------------------------------------------- /docs/_static/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /_test/test3_simple/multiplier/multiplier.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import tester, utils 3 | 4 | import random 5 | import time 6 | import itertools 7 | 8 | 9 | def compressN(column: list, sums: list, couts: list): 10 | for i in range(0, len(column), 3): 11 | x = column[i:i+3] 12 | if len(x) == 1: 13 | sums.append(x[0]) 14 | elif len(x) == 2: 15 | sums.append(Xor(*x)) 16 | couts.append(And(*x)) 17 | else: 18 | sums.append(Xor(*x)) 19 | couts.append(x[0] & x[1] | x[2] & (x[0] ^ x[1])) 20 | 21 | 22 | @module WallaceMultiplier(w1: int, w2: int): 23 | io = Bundle( 24 | x = Input(UInt(0, w1)), 25 | y = Input(UInt(0, w2)), 26 | out = Output(UInt(0, w1+w2)), 27 | ) 28 | products = [[] for _ in range(w1+w2)] 29 | for (i, x_i), (j, y_j) in itertools.product(enumerate(io.x.split()), enumerate(io.y.split())): 30 | products[i+j].append(x_i & y_j) 31 | 32 | 33 | while max(len(i) for i in products) >= 3: 34 | products_new = [[] for _ in range(len(products)+1)] 35 | for i, column in enumerate(products): 36 | compressN(column, products_new[i], products_new[i+1]) 37 | products = products_new 38 | 39 | while len(products[-1]) == 0: 40 | products.pop() 41 | 42 | a = Cat(*[i[0] for i in products]) 43 | b = Cat(*[i[1] if len(i) == 2 else UInt(0) for i in products]) 44 | io.out <== a + b 45 | 46 | 47 | #--------------------------------- test ---------------------------------------- 48 | 49 | @task mult_test(self, dut, data): 50 | out_mask = Logic( (1 << len(dut.io.out)) - 1, 0) 51 | for x, y in data: 52 | setv(dut.io.x, x) 53 | setv(dut.io.y, y) 54 | yield self.clock_n() 55 | self.AssertEq(getv(dut.io.out), (x * y) & out_mask) 56 | 57 | 58 | @tester test_multiplier(self): 59 | sess = Session() 60 | sess.enter() 61 | W, N = 16, 2000 62 | ratio = 0.0 63 | dut = WallaceMultiplier(W, W) 64 | # sess.track(dut.io, dut.a, dut.b) 65 | test_data = [(Logic.rand(W,W, ratio=ratio), Logic.rand(W,W, ratio=ratio)) for _ in range(N)] 66 | 67 | t = time.time() 68 | sess.join(mult_test(dut, test_data)) 69 | print('pyhgl cost: ', time.time()-t) 70 | sess.dumpVCD('Multiplier.vcd') 71 | 72 | 73 | @blackbox testbench(builder): 74 | dut_name = builder.get_name(dut) 75 | x = builder.get_name(dut.io.x) 76 | y = builder.get_name(dut.io.y) 77 | out = builder.get_name(dut.io.out) 78 | 79 | builder.append('initial begin') 80 | builder.append(f'$dumpfile("Multiplier_iverilog.vcd");') 81 | builder.append(f'$dumpvars(0, {x}, {y}, {out});') 82 | for x1, y1 in test_data: 83 | builder.append(f'{x} = {x1}; {y} = {y1}; #100;') 84 | builder.append('$finish;') 85 | builder.append('end') 86 | 87 | sess.dumpVerilog('Multiplier.sv', delay=True, top = True) 88 | sess.test_iverilog() 89 | print(sess) 90 | sess.exit() 91 | -------------------------------------------------------------------------------- /docs/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} -------------------------------------------------------------------------------- /_test/test0_grammar/array.py: -------------------------------------------------------------------------------- 1 | from pyhgl.array import * 2 | from pyhgl.tester import tester 3 | 4 | import numpy as np 5 | 6 | 7 | @tester 8 | def test_array(self): 9 | a = Array([ 10 | [], 11 | [{"x": 1.1, "y": [1]}, {"x": 2.2, "y": [1, 2]}, {"x": 3.3, "y": [1, 2, 3]}], 12 | [{"x": 4.4, "y": [1, 2, 3, 4], 'j': 3}, {"x": 5.5, "y": [1, 2, 3, 4, 5]}] 13 | ], recursive=True, atoms=[str]) 14 | 15 | self.EQ += str(a), 'Vec{\n Vec{}\n Vec{\n Bundle{\n x: 1.1\n y: Vec{\n 1\n }\n }\n Bundle{\n x: 2.2\n y: Vec{\n 1\n 2\n }\n }\n Bundle{\n x: 3.3\n y: Vec{\n 1\n 2\n 3\n }\n }\n }\n Vec{\n Bundle{\n x: 4.4\n y: Vec{\n 1\n 2\n 3\n 4\n }\n j: 3\n }\n Bundle{\n x: 5.5\n y: Vec{\n 1\n 2\n 3\n 4\n 5\n }\n }\n }\n}' 16 | 17 | self.EQ += str(a[1:,:,'y',1:]), 'Vec{\n Vec{\n Vec{}\n Vec{\n 2\n }\n Vec{\n 2\n 3\n }\n }\n Vec{\n Vec{\n 2\n 3\n 4\n }\n Vec{\n 2\n 3\n 4\n 5\n }\n }\n}' 18 | 19 | b = Bundle( 20 | x = 1, 21 | y = 2, 22 | z = Bundle( 23 | m = Bundle('a',2.0,'m0'), 24 | n = Bundle(-1, 'n0','n1'), 25 | ), 26 | zz = Bundle( 27 | m = Bundle('a',2.0,'m1'), 28 | n = Bundle(2, 'n2','n3'), 29 | ) 30 | ) 31 | self.EQ += str(b[['z','zz'], :, -1]), 'Bundle{\n z: Bundle{\n m: m0\n n: n1\n }\n zz: Bundle{\n m: m1\n n: n3\n }\n}' 32 | self.EQ += str(b[{'sel1':'z', 'sel2':'zz'}, None, 'm', ::-1]), 'Bundle{\n sel1: Vec{\n Vec{\n m0\n 2.0\n a\n }\n }\n sel2: Vec{\n Vec{\n m1\n 2.0\n a\n }\n }\n}' 33 | 34 | b[['z','zz'], :, -1] = Array([['c','d'], ['e']]) 35 | self.EQ += str(b), "Bundle{\n x: 1\n y: 2\n z: Bundle{\n m: Vec{\n a\n 2.0\n ['c', 'd']\n }\n n: Vec{\n -1\n n0\n ['c', 'd']\n }\n }\n zz: Bundle{\n m: Vec{\n a\n 2.0\n ['e']\n }\n n: Vec{\n 2\n n2\n ['e']\n }\n }\n}" 36 | 37 | 38 | c = Array([ 39 | [1,2,3,4], 40 | [5,6,7,8] 41 | ], recursive=True) 42 | self.EQ += c._shape, (2, 4) 43 | self.EQ += str(c[1:,1:]), 'Vec{\n Vec{\n 6\n 7\n 8\n }\n}' 44 | 45 | self.EQ += str(Map(lambda x,y: x+y, b, b)), "Bundle{\n x: 2\n y: 4\n z: Bundle{\n m: Vec{\n aa\n 4.0\n ['c', 'd', 'c', 'd']\n }\n n: Vec{\n -2\n n0n0\n ['c', 'd', 'c', 'd']\n }\n }\n zz: Bundle{\n m: Vec{\n aa\n 4.0\n ['e', 'e']\n }\n n: Vec{\n 4\n n2n2\n ['e', 'e']\n }\n }\n}" 46 | 47 | # test arraylize 48 | # -------------- 49 | 50 | @vectorize_axis 51 | def mysum(*args, hint) -> int: 52 | if hint == 'hint': 53 | return sum(args) 54 | else: 55 | return -1 56 | 57 | x = Array.ones((2,3,4)) 58 | 59 | self.EQ += mysum(1,2,3, hint='hint'), 6 60 | self.EQ += list(mysum([1,2],3,[4,5], hint='hint')._flat), [8, 10] 61 | self.EQ += mysum(x, hint='hint', axis=None), 24.0 62 | self.EQ += list(mysum(x, axis=-1,hint='hint')._flat), [4.0, 4.0, 4.0, 4.0, 4.0, 4.0] 63 | self.EQ += list(mysum(x, axis=(1,0),hint='hint')._flat), [6.0, 6.0, 6.0, 6.0] 64 | 65 | -------------------------------------------------------------------------------- /pyhgl/parser/hgl_token.py: -------------------------------------------------------------------------------- 1 | _globals_before = None 2 | _globals_before = set(globals().keys()) 3 | 4 | ENDMARKER = None 5 | NAME = None 6 | NUMBER = None 7 | STRING = None 8 | NEWLINE = None 9 | INDENT = None 10 | DEDENT = None 11 | 12 | LPAR = '(' 13 | RPAR = ')' 14 | LSQB = '[' 15 | RSQB = ']' 16 | COLON = ':' 17 | COMMA = ',' 18 | SEMI = ';' 19 | PLUS = '+' 20 | MINUS = '-' 21 | STAR = '*' 22 | SLASH = '/' 23 | VBAR = '|' 24 | AMPER = '&' 25 | LESS = '<' 26 | GREATER = '>' 27 | EQUAL = '=' 28 | DOT = '.' 29 | PERCENT = '%' 30 | LBRACE = '{' 31 | RBRACE = '}' 32 | EQEQUAL = '==' 33 | NOTEQUAL = '!=' 34 | LESSEQUAL = '<=' 35 | GREATEREQUAL = '>=' 36 | TILDE = '~' 37 | CIRCUMFLEX = '^' 38 | LEFTSHIFT = '<<' 39 | RIGHTSHIFT = '>>' 40 | DOUBLESTAR = '**' 41 | PLUSEQUAL = '+=' 42 | MINEQUAL = '-=' 43 | STAREQUAL = '*=' 44 | SLASHEQUAL = '/=' 45 | PERCENTEQUAL = '%=' 46 | AMPEREQUAL = '&=' 47 | VBAREQUAL = '|=' 48 | CIRCUMFLEXEQUAL = '^=' 49 | LEFTSHIFTEQUAL = '<<=' 50 | RIGHTSHIFTEQUAL = '>>=' 51 | DOUBLESTAREQUAL = '**=' 52 | DOUBLESLASH = '//' 53 | DOUBLESLASHEQUAL = '//=' 54 | AT = '@' 55 | ATEQUAL = '@=' 56 | RARROW = '->' 57 | ELLIPSIS = '...' 58 | COLONEQUAL = ':=' 59 | 60 | # pyhgl operators 61 | LOGICNOT = '!' 62 | LOGICAND = '&&' 63 | LOGICOR = '||' 64 | LLEFTSHIFT = '<<<' 65 | RRIGHTSHIFT = '>>>' 66 | LESSEQEQUAL = '<==' 67 | LESSEQGREATER = '<=>' 68 | EQEQEQUAL = '===' 69 | VBARRARROW = '|->' 70 | BACKTICK = '`' 71 | 72 | OP = None 73 | AWAIT = None 74 | ASYNC = None 75 | TYPE_IGNORE = None 76 | TYPE_COMMENT = None 77 | SOFT_KEYWORD = None 78 | ERRORTOKEN = None 79 | COMMENT = None 80 | NL = None 81 | ENCODING = None 82 | 83 | N_TOKENS = None 84 | # Special definitions for cooperation with parser 85 | NT_OFFSET = None 86 | 87 | 88 | _globals_after = globals().copy() 89 | 90 | 91 | 92 | 93 | # list of (token_name:str, token_value:int, _token:str|None) 94 | _tokens = [] 95 | 96 | for _name, _token in _globals_after.items(): 97 | if not _name in _globals_before: 98 | _token_value = 256 if _name == 'NT_OFFSET' else len(_tokens) 99 | _tokens.append((_name, _token_value, _token)) 100 | 101 | # {0:'ENDMARKER', 1:'NAME', ...} 102 | tok_name = {} 103 | # {'(': 7, ')': 8, ...} 104 | EXACT_TOKEN_TYPES = {} 105 | 106 | for _name, _token_value, _token in _tokens: 107 | globals()[_name] = _token_value 108 | tok_name[_token_value] = _name 109 | if _token is not None: 110 | EXACT_TOKEN_TYPES[_token] = _token_value 111 | 112 | 113 | 114 | def ISTERMINAL(x): 115 | return x < NT_OFFSET 116 | 117 | def ISNONTERMINAL(x): 118 | return x >= NT_OFFSET 119 | 120 | def ISEOF(x): 121 | return x == ENDMARKER 122 | 123 | 124 | __all__ = ['tok_name', 'ISTERMINAL', 'ISNONTERMINAL', 'ISEOF'] + list(tok_name.values()) 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /pyhgl/hook.py: -------------------------------------------------------------------------------- 1 | """ 2 | python import hook: https://www.python.org/dev/peps/pep-0302/ 3 | reference: https://github.com/pfalcon/python-imphook 4 | """ 5 | 6 | import sys 7 | import importlib 8 | import traceback 9 | import re 10 | import pyhgl.parser 11 | 12 | 13 | 14 | _drop = re.compile(r'\s*File "', 'exec') 106 | exec(code, shell.user_ns, local_ns) 107 | except: 108 | shell.showtraceback() 109 | return 110 | except: 111 | pass 112 | 113 | -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/signals.md: -------------------------------------------------------------------------------- 1 | # Literals & Signals 2 | 3 | ## Literals 4 | 5 | ### Logic 6 | 7 | `Logic` is three-state (0, 1, and X) and has an arbitrary length. Two bits are used to represent a three-state value: `00` as zero, `10` as one, and both `01` and `11` as the unknown state. 8 | 9 | ```py 10 | Logic(1,2) # 0...x1 11 | Logic(3) # 0...11 12 | Logic(-1) # 1...1 13 | Logic('xx') # 0...xx 14 | Logic('x1x1') | Logic('1x1x') # 0...1111 15 | ``` 16 | 17 | For convenience, `==` and `!=` between `Logic` and other literals return a python `bool`. 18 | 19 | ```py 20 | Logic('11') == 3 # True 21 | Logic('x1') == 3 # False 22 | Logic(0,3) == Logic(3,3) # True 23 | ``` 24 | 25 | Explicit `Logic` literal are usually unnecessary. 26 | 27 | ### BitPat 28 | 29 | `BitPat` is three-state (0, 1, and don't care) and has a fixed length. It is only used in comparation. 30 | 31 | ```py 32 | BitPat('11??') # 4'b11?? 33 | ``` 34 | 35 | ### strings 36 | 37 | Python strings are usually converted into `Logic` automatically. 38 | 39 | ```py 40 | '0011_1100' # 8'b00111100 41 | '4:b111111' # 4'b1111 42 | '8:hab' # 8'hab 43 | '-d3' # 3'b101 44 | '4:-d3' # 4'b1101 45 | '16.x:habc' # 16'hxabc 46 | ``` 47 | 48 | ## Signals 49 | 50 | PyHGL models digital circuits as a direct graph of `Gate` and `SignalData` nodes. `Writer` is the edge from gate to data, while `Reader` is the edge from data to gate. 51 | 52 | - Signals are `Reader`, which contains a `SignalType` and a `SignalData` 53 | - `SignalType` indicates signal type and bit width. 54 | - `SignalData` contains the real three-state singal value that is unique for each signal. 55 | - Type castings are only allowed for same bit length. 56 | 57 | 58 | ### UInt 59 | 60 | `UInt[w]` is the most basic signal type, similar as `logic [w-1:0]` in SystemVerilog. A `SignalType` is callable and return a signal instance. 61 | 62 | ```py 63 | # lines below are the same 64 | UInt[8](1) 65 | UInt[8]('1') 66 | UInt('8:b1') 67 | UInt(1, w=8) 68 | ``` 69 | 70 | ### Other 71 | 72 | ```py 73 | uint8_t = UInt[8] # signal type: 8-bit unsigned integer 74 | sint32_t = SInt[32] # signal type: 32-bit signed integer 75 | vector_t = 4 ** uint8_t # signal type: packed array of 4x8 bits 76 | mem_t = MemArray[1024,8] # signal type: unpacked array of 1024x8 bits 77 | struct_t = Struct( 78 | a = vector_t @ 0, # field 'a' starts from 0 79 | b = sint32_t @ 0, # field 'b' starts from 0 80 | c = UInt[32] @ 32, # field 'c' starts from 32 81 | ) # signal type: 64-bit structure 82 | x = struct_t({ 83 | 'a': [1,2,3,4], 84 | 'c': Logic('xxxx1100'), 85 | }) # signal x has an initial value of 64'hxc04030201 86 | y = UInt[64](x) # type casting 87 | ``` 88 | 89 | 90 | 91 | 92 | 93 | ## Array 94 | 95 | `Array` is not a hardware type, but a tree-like data struct like [awkward-array](https://awkward-array.readthedocs.io/en/latest/). `Array` is used for vectorized operations and module ports. 96 | 97 | 98 | - `Array` can be either a named array like `Dict[str, Any]` or a list `List[Any]` 99 | - supports advanced slicing 100 | - operators on `Array` are vectorized and broadcast 101 | - use function `Bundle` to generate a named array 102 | 103 | 104 | ```py 105 | # 1-d vector 106 | Array([1,2,3,4]) 107 | Bundle(1,2,3,4) 108 | # 2-d vector 109 | Array([[1,2],[3,4]], recursive=True) 110 | # named array 111 | Array({'a':1, 'b':2}) 112 | Bundle(a=1, b=2) 113 | ``` 114 | 115 | 116 | ## Useful Functions 117 | 118 | ```py 119 | Array.zeros(2,3) # Array([[0,0,0],[0,0,0]], recursive=True) 120 | UInt.zeros(2,3) # Array([[UInt(0), UInt(0), UInt(0)],[UInt(0),UInt(0),UInt(0)]], recursive=True) 121 | Logic(-1).to_bin(4) # '1111' 122 | Logic(-1).to_hex(16) # 'ffff' 123 | Logic(-1).to_int(1) # -1 124 | ``` -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/signals.md.txt: -------------------------------------------------------------------------------- 1 | # Literals & Signals 2 | 3 | ## Literals 4 | 5 | ### Logic 6 | 7 | `Logic` is three-state (0, 1, and X) and has an arbitrary length. Two bits are used to represent a three-state value: `00` as zero, `10` as one, and both `01` and `11` as the unknown state. 8 | 9 | ```py 10 | Logic(1,2) # 0...x1 11 | Logic(3) # 0...11 12 | Logic(-1) # 1...1 13 | Logic('xx') # 0...xx 14 | Logic('x1x1') | Logic('1x1x') # 0...1111 15 | ``` 16 | 17 | For convenience, `==` and `!=` between `Logic` and other literals return a python `bool`. 18 | 19 | ```py 20 | Logic('11') == 3 # True 21 | Logic('x1') == 3 # False 22 | Logic(0,3) == Logic(3,3) # True 23 | ``` 24 | 25 | Explicit `Logic` literal are usually unnecessary. 26 | 27 | ### BitPat 28 | 29 | `BitPat` is three-state (0, 1, and don't care) and has a fixed length. It is only used in comparation. 30 | 31 | ```py 32 | BitPat('11??') # 4'b11?? 33 | ``` 34 | 35 | ### strings 36 | 37 | Python strings are usually converted into `Logic` automatically. 38 | 39 | ```py 40 | '0011_1100' # 8'b00111100 41 | '4:b111111' # 4'b1111 42 | '8:hab' # 8'hab 43 | '-d3' # 3'b101 44 | '4:-d3' # 4'b1101 45 | '16.x:habc' # 16'hxabc 46 | ``` 47 | 48 | ## Signals 49 | 50 | PyHGL models digital circuits as a direct graph of `Gate` and `SignalData` nodes. `Writer` is the edge from gate to data, while `Reader` is the edge from data to gate. 51 | 52 | - Signals are `Reader`, which contains a `SignalType` and a `SignalData` 53 | - `SignalType` indicates signal type and bit width. 54 | - `SignalData` contains the real three-state singal value that is unique for each signal. 55 | - Type castings are only allowed for same bit length. 56 | 57 | 58 | ### UInt 59 | 60 | `UInt[w]` is the most basic signal type, similar as `logic [w-1:0]` in SystemVerilog. A `SignalType` is callable and return a signal instance. 61 | 62 | ```py 63 | # lines below are the same 64 | UInt[8](1) 65 | UInt[8]('1') 66 | UInt('8:b1') 67 | UInt(1, w=8) 68 | ``` 69 | 70 | ### Other 71 | 72 | ```py 73 | uint8_t = UInt[8] # signal type: 8-bit unsigned integer 74 | sint32_t = SInt[32] # signal type: 32-bit signed integer 75 | vector_t = 4 ** uint8_t # signal type: packed array of 4x8 bits 76 | mem_t = MemArray[1024,8] # signal type: unpacked array of 1024x8 bits 77 | struct_t = Struct( 78 | a = vector_t @ 0, # field 'a' starts from 0 79 | b = sint32_t @ 0, # field 'b' starts from 0 80 | c = UInt[32] @ 32, # field 'c' starts from 32 81 | ) # signal type: 64-bit structure 82 | x = struct_t({ 83 | 'a': [1,2,3,4], 84 | 'c': Logic('xxxx1100'), 85 | }) # signal x has an initial value of 64'hxc04030201 86 | y = UInt[64](x) # type casting 87 | ``` 88 | 89 | 90 | 91 | 92 | 93 | ## Array 94 | 95 | `Array` is not a hardware type, but a tree-like data struct like [awkward-array](https://awkward-array.readthedocs.io/en/latest/). `Array` is used for vectorized operations and module ports. 96 | 97 | 98 | - `Array` can be either a named array like `Dict[str, Any]` or a list `List[Any]` 99 | - supports advanced slicing 100 | - operators on `Array` are vectorized and broadcast 101 | - use function `Bundle` to generate a named array 102 | 103 | 104 | ```py 105 | # 1-d vector 106 | Array([1,2,3,4]) 107 | Bundle(1,2,3,4) 108 | # 2-d vector 109 | Array([[1,2],[3,4]], recursive=True) 110 | # named array 111 | Array({'a':1, 'b':2}) 112 | Bundle(a=1, b=2) 113 | ``` 114 | 115 | 116 | ## Useful Functions 117 | 118 | ```py 119 | Array.zeros(2,3) # Array([[0,0,0],[0,0,0]], recursive=True) 120 | UInt.zeros(2,3) # Array([[UInt(0), UInt(0), UInt(0)],[UInt(0),UInt(0),UInt(0)]], recursive=True) 121 | Logic(-1).to_bin(4) # '1111' 122 | Logic(-1).to_hex(16) # 'ffff' 123 | Logic(-1).to_int(1) # -1 124 | ``` -------------------------------------------------------------------------------- /_test/test3_simple/divider/divider.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import tester, utils 3 | 4 | import random 5 | import time 6 | import itertools 7 | 8 | 9 | @module Divider(w: int): 10 | io = Bundle( 11 | x = Input(UInt[w](0)), # dividend 12 | y = Input(UInt[w](0)), # divisor 13 | valid = Input(UInt(0)), 14 | 15 | q = Output(UInt[w](0)), # quotient 16 | r = Output(UInt[w](0)), # remainder 17 | ready = Output(UInt(0)), 18 | ) 19 | 20 | # registers 21 | A = Reg(UInt[w*2](0)) # dividend 22 | B = Reg(UInt[w](0)) # divisor 23 | C = Reg(UInt[w](0)) # quotient 24 | io.q <== C 25 | io.r <== A[w:] 26 | 27 | counter = Reg(UInt(w-1)) 28 | io.ready <== (counter == w-1) 29 | 30 | # comb 31 | A_curr = Mux(io.ready, io.x, A) 32 | B_curr = Mux(io.ready, io.y, B) 33 | C_curr = Mux(io.ready, 0, C) 34 | 35 | A_curr_shift = A_curr << 1 36 | B_curr_shift = Cat(UInt[w](0), B_curr) 37 | A_greater_B = A_curr_shift >= B_curr_shift 38 | A_next = Mux(A_greater_B, A_curr_shift - B_curr_shift, A_curr_shift) 39 | C_next = Mux(A_greater_B, Cat('1', C_curr[:-1]), Cat('0', C_curr[:-1])) 40 | 41 | # control 42 | when io.ready: # idle 43 | when io.valid: 44 | A <== A_next 45 | B <== B_curr 46 | C <== C_next 47 | counter <== 0 48 | otherwise: # exec 49 | A <== A_next 50 | C <== C_next 51 | counter <== counter + 1 52 | 53 | clk = conf.clock[0] 54 | # with AssertCtrl(): 55 | # Assert(Rise(clk) >>> clk) 56 | 57 | 58 | 59 | @conf Global(clock_period): 60 | conf.timing = Bundle( 61 | timescale = '1ns', 62 | Clock = {'low':clock_period, 'high':clock_period, 'phase':0}, 63 | Logic = {'delay': 1}, 64 | Gate = {'delay': 1}, 65 | ) 66 | 67 | 68 | #--------------------------------- test ---------------------------------------- 69 | 70 | @task divider_tests(self, dut, test_data): 71 | yield self.reset() 72 | for x,y in test_data: 73 | yield self.clock_n() 74 | setv(dut.io.valid, 1) 75 | setv(dut.io.x,x) 76 | setv(dut.io.y,y) 77 | yield self.clock_n() 78 | setv(dut.io.valid, 0) 79 | yield self.until(dut.io.ready, 1) 80 | self.AssertEq(getv(dut.io.q), x // y) 81 | self.AssertEq(getv(dut.io.r), x % y) 82 | 83 | @tester test_divider(self): 84 | 85 | W, N = 256, 30 86 | clock_period = 20 87 | mask = (1 << W) - 1 88 | 89 | sess = Session(Global(clock_period)) 90 | sess.enter() 91 | 92 | dut = Divider(W) 93 | # sess.track(dut.clock[0], dut) 94 | 95 | test_data = [(Logic.rand(W), Logic.rand(W)) for _ in range(N)] 96 | t = time.time() 97 | sess.join(divider_tests(dut, test_data)) 98 | print('pyhgl simulation cost:', time.time()-t) 99 | sess.dumpVCD('Divider.vcd') 100 | print(sess) 101 | 102 | @blackbox testbench(builder): 103 | dut_name = builder.get_name(dut) 104 | reset = builder.get_name(dut.reset[0]) 105 | x = builder.get_name(dut.io.x) 106 | y = builder.get_name(dut.io.y) 107 | q = builder.get_name(dut.io.q) 108 | r = builder.get_name(dut.io.r) 109 | valid = builder.get_name(dut.io.valid) 110 | 111 | builder.append('initial begin') 112 | builder.append(f'$dumpfile("Divider_iverilog.vcd");') 113 | builder.append(f'$dumpvars(0, {dut_name});') 114 | builder.append(f'{reset} = 1; #{clock_period*2}; {reset} = 0;') 115 | for x1, y1 in test_data: 116 | builder.append(f'{x} = {x1}; {y} = {y1}; {valid} = 1; #{clock_period*2};') 117 | builder.append(f'{valid} = 0; # {clock_period*2*W};') 118 | builder.append('$finish;') 119 | builder.append('end') 120 | 121 | 122 | sess.dumpVerilog('Divider.sv', delay=False, top = True) 123 | # sess.dumpGraph('Divider.gv') 124 | sess.test_iverilog() 125 | sess.exit() 126 | -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/gates.md: -------------------------------------------------------------------------------- 1 | # Gates 2 | 3 | In the **circuit abstraction**, hardware is described as a graph of connected components. 4 | 5 | - Operations on PyHGL signals usually generate a hardware gate. 6 | - Directional assignment `<==` to a signal will automatically insert a `Wire` if the signal is not assignable. 7 | 8 | ## Functions 9 | 10 | - Unary functions: `Not`, `Bool`, `LogicNot`, `AndR`, `OrR`, `XorR` 11 | - Binary functions: `Lshift`, `Rshift`, `Eq`, `Ne`, `Lt`, `Gt`, `Le`, `Ge`, `Floordiv`, `Mod` 12 | - Multi-inputs functions: `And`, `Or`, `Xor`, `Nand`, `Nor`, `Nxor`, `Cat`, `Pow`, `LogicAnd`, `LogicOr`, `Add`, `Sub`, `Mul` 13 | 14 | | Function | Description | Operator | Output Width | 15 | | ------------------ | ---------------- | -------- | ------------------ | 16 | | `Not(x)` | bitwise not | `~` | `len(x)` | 17 | | `Bool(x)` | logic bool | | 1 | 18 | | `LogicNot(x)` | logic not | `!` | 1 | 19 | | `AndR(x)` | and reduce | | 1 | 20 | | `OrR(x)` | or reduce | | 1 | 21 | | `XorR(x)` | xor reduce | | 1 | 22 | | `Lshift(x,y)` | left shift | `<<` | `len(x)` | 23 | | `Rshift(x,y)` | right shift | `>>` | `len(x)` | 24 | | `Eq(x,y)` | comparation | `==` | 1 | 25 | | `Floordiv(x, y)` | divide | `//` | `len(x)` | 26 | | `Mod(x, y)` | mod | `%` | `len(x)` | 27 | | `And(x,...)` | bitwise and | `&` | `max(len(x), ...)` | 28 | | `Nand(x,...)` | bitwise nand | | `max(len(x), ...)` | 29 | | `Cat(x, ...)` | concatenation | | `sum(len(x), ...)` | 30 | | `Pow(x, n)` | bits duplication | `**` | `len(x) * n` | 31 | | `LogicAnd(x, ...)` | logic and | `&&` | 1 | 32 | | `LogicOr(x, ...)` | logic or | `x || y` | 1 | 33 | | `Add(x, ...)` | add | `+` | `max(len(x), ...)` | 34 | | `Add(x, ...)` | add | `+` | `max(len(x), ...)` | 35 | | `Mul(x, ...)` | mul | `*` | `max(len(x), ...)` | 36 | 37 | ## Netlist 38 | 39 | Netlists are gate that is **assignable**. Unlike Verilog, registers and latches should be explicitly declared, while wires are usually implicitly generated. 40 | 41 | 42 | | Function | Description | 43 | | ------------------ | -------------------------------------------------------------------- | 44 | | `Wire(x)` | connect a wire behind x if x is not assignable | 45 | | `WireNext(x)` | connect a wire after x and return new signal | 46 | | `Reg(x)` | a register whose input/output is x, reset/init is current value of x | 47 | | `RegNext(x)` | a register whose input is x, output is new signal | 48 | | `Latch(x, enable)` | latch | 49 | | `Wtri(x)` | tri-state wire | 50 | 51 | ### ClockDomain 52 | 53 | `Reg` will get the default clock and reset signals, which can be set by `ClockDomain`. 54 | 55 | ```py 56 | # negedge clk, asynchronous low-valid rst_n 57 | with ClockDomain(clock=(clk,1), reset=(rst_n,0)): 58 | register = Reg(UInt[8](0)) 59 | ``` 60 | 61 | 62 | ## Examples 63 | 64 | 65 | 66 | ### FullAdder 67 | 68 | 69 | ```py 70 | @module FullAdder: 71 | a, b, cin = UInt.zeros(3) 72 | s = a ^ b ^ cin 73 | cout = a & b | (a ^ b) & cin 74 | ``` 75 | 76 | 77 | ### D flip flop 78 | 79 | ```py 80 | @module Register: 81 | data, clk, set, reset = UInt.zeros(4) 82 | nand1, nand2, nand3, nand4, Q, Qn = UInt.zeros(6) 83 | 84 | nand1 <== Nand(set, nand4, nand2) 85 | nand2 <== Nand(nand1, clk, reset) 86 | nand3 <== Nand(nand2, clk, nand4) 87 | nand4 <== Nand(nand3, data, reset) 88 | Q <== Nand(set, nand2, Qn) 89 | Qn <== Nand(Q, nand3, reset) 90 | ``` 91 | -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/gates.md.txt: -------------------------------------------------------------------------------- 1 | # Gates 2 | 3 | In the **circuit abstraction**, hardware is described as a graph of connected components. 4 | 5 | - Operations on PyHGL signals usually generate a hardware gate. 6 | - Directional assignment `<==` to a signal will automatically insert a `Wire` if the signal is not assignable. 7 | 8 | ## Functions 9 | 10 | - Unary functions: `Not`, `Bool`, `LogicNot`, `AndR`, `OrR`, `XorR` 11 | - Binary functions: `Lshift`, `Rshift`, `Eq`, `Ne`, `Lt`, `Gt`, `Le`, `Ge`, `Floordiv`, `Mod` 12 | - Multi-inputs functions: `And`, `Or`, `Xor`, `Nand`, `Nor`, `Nxor`, `Cat`, `Pow`, `LogicAnd`, `LogicOr`, `Add`, `Sub`, `Mul` 13 | 14 | | Function | Description | Operator | Output Width | 15 | | ------------------ | ---------------- | -------- | ------------------ | 16 | | `Not(x)` | bitwise not | `~` | `len(x)` | 17 | | `Bool(x)` | logic bool | | 1 | 18 | | `LogicNot(x)` | logic not | `!` | 1 | 19 | | `AndR(x)` | and reduce | | 1 | 20 | | `OrR(x)` | or reduce | | 1 | 21 | | `XorR(x)` | xor reduce | | 1 | 22 | | `Lshift(x,y)` | left shift | `<<` | `len(x)` | 23 | | `Rshift(x,y)` | right shift | `>>` | `len(x)` | 24 | | `Eq(x,y)` | comparation | `==` | 1 | 25 | | `Floordiv(x, y)` | divide | `//` | `len(x)` | 26 | | `Mod(x, y)` | mod | `%` | `len(x)` | 27 | | `And(x,...)` | bitwise and | `&` | `max(len(x), ...)` | 28 | | `Nand(x,...)` | bitwise nand | | `max(len(x), ...)` | 29 | | `Cat(x, ...)` | concatenation | | `sum(len(x), ...)` | 30 | | `Pow(x, n)` | bits duplication | `**` | `len(x) * n` | 31 | | `LogicAnd(x, ...)` | logic and | `&&` | 1 | 32 | | `LogicOr(x, ...)` | logic or | `x || y` | 1 | 33 | | `Add(x, ...)` | add | `+` | `max(len(x), ...)` | 34 | | `Add(x, ...)` | add | `+` | `max(len(x), ...)` | 35 | | `Mul(x, ...)` | mul | `*` | `max(len(x), ...)` | 36 | 37 | ## Netlist 38 | 39 | Netlists are gate that is **assignable**. Unlike Verilog, registers and latches should be explicitly declared, while wires are usually implicitly generated. 40 | 41 | 42 | | Function | Description | 43 | | ------------------ | -------------------------------------------------------------------- | 44 | | `Wire(x)` | connect a wire behind x if x is not assignable | 45 | | `WireNext(x)` | connect a wire after x and return new signal | 46 | | `Reg(x)` | a register whose input/output is x, reset/init is current value of x | 47 | | `RegNext(x)` | a register whose input is x, output is new signal | 48 | | `Latch(x, enable)` | latch | 49 | | `Wtri(x)` | tri-state wire | 50 | 51 | ### ClockDomain 52 | 53 | `Reg` will get the default clock and reset signals, which can be set by `ClockDomain`. 54 | 55 | ```py 56 | # negedge clk, asynchronous low-valid rst_n 57 | with ClockDomain(clock=(clk,1), reset=(rst_n,0)): 58 | register = Reg(UInt[8](0)) 59 | ``` 60 | 61 | 62 | ## Examples 63 | 64 | 65 | 66 | ### FullAdder 67 | 68 | 69 | ```py 70 | @module FullAdder: 71 | a, b, cin = UInt.zeros(3) 72 | s = a ^ b ^ cin 73 | cout = a & b | (a ^ b) & cin 74 | ``` 75 | 76 | 77 | ### D flip flop 78 | 79 | ```py 80 | @module Register: 81 | data, clk, set, reset = UInt.zeros(4) 82 | nand1, nand2, nand3, nand4, Q, Qn = UInt.zeros(6) 83 | 84 | nand1 <== Nand(set, nand4, nand2) 85 | nand2 <== Nand(nand1, clk, reset) 86 | nand3 <== Nand(nand2, clk, nand4) 87 | nand4 <== Nand(nand3, data, reset) 88 | Q <== Nand(set, nand2, Qn) 89 | Qn <== Nand(Q, nand3, reset) 90 | ``` 91 | -------------------------------------------------------------------------------- /docs/_static/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); -------------------------------------------------------------------------------- /pyhgl/parser/tokenizer.py: -------------------------------------------------------------------------------- 1 | # origin: https://github.com/we-like-parsers/pegen/blob/main/src/pegen/tokenizer.py 2 | 3 | from . import hgl_token as token # modified 4 | from . import hgl_tokenize as tokenize # modified 5 | from typing import Dict, Iterator, List 6 | 7 | Mark = int # NewType('Mark', int) 8 | 9 | exact_token_types = token.EXACT_TOKEN_TYPES 10 | 11 | 12 | def shorttok(tok: tokenize.TokenInfo) -> str: 13 | return "%-25.25s" % f"{tok.start[0]}.{tok.start[1]}: {token.tok_name[tok.type]}:{tok.string!r}" 14 | 15 | 16 | class Tokenizer: 17 | """Caching wrapper for the tokenize module. 18 | 19 | This is pretty tied to Python's syntax. 20 | """ 21 | 22 | _tokens: List[tokenize.TokenInfo] 23 | 24 | def __init__( 25 | self, tokengen: Iterator[tokenize.TokenInfo], *, path: str = "", verbose: bool = False 26 | ): 27 | self._tokengen = tokengen 28 | self._tokens = [] 29 | self._index = 0 30 | self._verbose = verbose 31 | self._lines: Dict[int, str] = {} 32 | self._path = path 33 | if verbose: 34 | self.report(False, False) 35 | 36 | def getnext(self) -> tokenize.TokenInfo: 37 | """Return the next token and updates the index.""" 38 | cached = not self._index == len(self._tokens) 39 | tok = self.peek() 40 | self._index += 1 41 | if self._verbose: 42 | self.report(cached, False) 43 | return tok 44 | 45 | def peek(self) -> tokenize.TokenInfo: 46 | """Return the next token *without* updating the index.""" 47 | while self._index == len(self._tokens): 48 | tok = next(self._tokengen) 49 | if tok.type in (tokenize.NL, tokenize.COMMENT): 50 | continue 51 | if tok.type == token.ERRORTOKEN and tok.string.isspace(): 52 | continue 53 | if ( 54 | tok.type == token.NEWLINE 55 | and self._tokens 56 | and self._tokens[-1].type == token.NEWLINE 57 | ): 58 | continue 59 | self._tokens.append(tok) 60 | if not self._path: 61 | self._lines[tok.start[0]] = tok.line 62 | return self._tokens[self._index] 63 | 64 | def diagnose(self) -> tokenize.TokenInfo: 65 | if not self._tokens: 66 | self.getnext() 67 | return self._tokens[-1] 68 | 69 | def get_last_non_whitespace_token(self) -> tokenize.TokenInfo: 70 | for tok in reversed(self._tokens[: self._index]): 71 | if tok.type != tokenize.ENDMARKER and ( 72 | tok.type < tokenize.NEWLINE or tok.type > tokenize.DEDENT 73 | ): 74 | break 75 | return tok 76 | 77 | def get_lines(self, line_numbers: List[int]) -> List[str]: 78 | """Retrieve source lines corresponding to line numbers.""" 79 | if self._lines: 80 | lines = self._lines 81 | else: 82 | n = len(line_numbers) 83 | lines = {} 84 | count = 0 85 | seen = 0 86 | with open(self._path) as f: 87 | for line in f: 88 | count += 1 89 | if count in line_numbers: 90 | seen += 1 91 | lines[count] = line 92 | if seen == n: 93 | break 94 | 95 | return [lines[n] for n in line_numbers] 96 | 97 | def mark(self) -> Mark: 98 | return self._index 99 | 100 | def reset(self, index: Mark) -> None: 101 | if index == self._index: 102 | return 103 | assert 0 <= index <= len(self._tokens), (index, len(self._tokens)) 104 | old_index = self._index 105 | self._index = index 106 | if self._verbose: 107 | self.report(True, index < old_index) 108 | 109 | def report(self, cached: bool, back: bool) -> None: 110 | if back: 111 | fill = "-" * self._index + "-" 112 | elif cached: 113 | fill = "-" * self._index + ">" 114 | else: 115 | fill = "-" * self._index + "*" 116 | if self._index == 0: 117 | print(f"{fill} (Bof)") 118 | else: 119 | tok = self._tokens[self._index - 1] 120 | print(f"{fill} {shorttok(tok)}") 121 | -------------------------------------------------------------------------------- /docs/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t. 18 | # 19 | #################################################################################################### 20 | 21 | 22 | from __future__ import annotations 23 | from typing import Optional, Dict, Any, List, Tuple, Generator, Callable, Literal 24 | from pyhgl.logic.hgl_core import * 25 | 26 | 27 | class SIntType(LogicType): 28 | 29 | _cache = {} 30 | 31 | def __init__(self, width: int = 0): 32 | assert width > 0 33 | super().__init__(width) 34 | 35 | def _eval(self, v: Union[int, str, Logic, BitPat]) -> Union[Logic, BitPat]: 36 | """ 37 | - overflow: 38 | BitPat not allowed 39 | others cut off 40 | - underflow: 41 | negative int|Logic is sign-extended 42 | other immds always zero-extend 43 | """ 44 | if isinstance(v, str) and '?' in v: 45 | v = BitPat(v) 46 | 47 | if isinstance(v, BitPat): 48 | assert len(v) <= self._width, f'{v} overflow for SInt[{self._width}]' 49 | return v 50 | elif isinstance(v, str): # zext for '4:-d1' 51 | _v, _x, _w = utils.str2logic(v) 52 | mask = gmpy2.bit_mask(self._width) 53 | return Logic(_v & mask, _x & mask) 54 | elif isinstance(v, Logic): 55 | _v = v.v 56 | _x = v.x 57 | mask = gmpy2.bit_mask(self._width) 58 | return Logic(_v & mask, _x & mask) 59 | else: 60 | _v = v 61 | _x = 0 62 | mask = gmpy2.bit_mask(self._width) 63 | return Logic(_v & mask, _x & mask) 64 | 65 | def __call__( 66 | self, 67 | v: Union[int, str, Reader, Logic]=0, 68 | *, 69 | name: str = '' 70 | ) -> Reader: 71 | name = name or 'sint' 72 | # is type casting 73 | if isinstance(v, Reader): 74 | data = v._data 75 | assert isinstance(data, LogicData), f'signal {v} is not logic signal' 76 | assert v._type._storage == 'packed', f'signal {v} has variable bit length' 77 | assert len(v) == len(self), 'bit length mismatch' 78 | return Reader(data=data, type=self, name=name) 79 | else: 80 | data = self._eval(v) 81 | assert isinstance(data, Logic) 82 | return Reader(data=LogicData(data.v, data.x, len(self)), type=self, name=name) 83 | 84 | def __str__(self, data: LogicData = None): 85 | if data is None: 86 | return f"SInt[{len(self)}]" 87 | else: 88 | return f"s{utils.logic2str(data.v, data.x, width=len(self))}" 89 | 90 | 91 | @singleton 92 | class SInt(HGLFunction): 93 | 94 | def __getitem__(self, key: int): 95 | """ 96 | ex. SInt[2], SInt[8] 97 | """ 98 | assert key > 0 and isinstance(key, int) 99 | cache = SIntType._cache 100 | if key in cache: 101 | return cache[key] 102 | else: 103 | cache[key] = SIntType(key) 104 | return cache[key] 105 | 106 | def __call__( 107 | self, 108 | v: Union[str, int, Reader, Iterable, Logic]='0', 109 | w: int = None, 110 | name: str = '' 111 | ) -> Reader: 112 | 113 | # array 114 | v = ToArray(v) 115 | w = ToArray(w) 116 | if isinstance(v, Array) or isinstance(w, Array): 117 | return Map(self, v, w, name=name) 118 | 119 | # with width, pass 120 | if w is not None: 121 | return SInt[w](v, name=name) 122 | # without width 123 | if isinstance(v, Reader): 124 | _w = len(v) 125 | else: 126 | if isinstance(v, str): 127 | _, _, _w = utils.str2logic(v) 128 | elif isinstance(v, Logic): 129 | _w = max(utils.width_infer(v.v, signed=True), utils.width_infer(v.x, signed=True)) 130 | else: 131 | _w = utils.width_infer(v, signed=True) 132 | return SInt[_w](v, name=name) 133 | 134 | -------------------------------------------------------------------------------- /docs/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 14 | "TEXTAREA", 15 | "INPUT", 16 | "SELECT", 17 | "BUTTON", 18 | ]); 19 | 20 | const _ready = (callback) => { 21 | if (document.readyState !== "loading") { 22 | callback(); 23 | } else { 24 | document.addEventListener("DOMContentLoaded", callback); 25 | } 26 | }; 27 | 28 | /** 29 | * Small JavaScript module for the documentation. 30 | */ 31 | const Documentation = { 32 | init: () => { 33 | Documentation.initDomainIndexTable(); 34 | Documentation.initOnKeyListeners(); 35 | }, 36 | 37 | /** 38 | * i18n support 39 | */ 40 | TRANSLATIONS: {}, 41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 42 | LOCALE: "unknown", 43 | 44 | // gettext and ngettext don't access this so that the functions 45 | // can safely bound to a different name (_ = Documentation.gettext) 46 | gettext: (string) => { 47 | const translated = Documentation.TRANSLATIONS[string]; 48 | switch (typeof translated) { 49 | case "undefined": 50 | return string; // no translation 51 | case "string": 52 | return translated; // translation exists 53 | default: 54 | return translated[0]; // (singular, plural) translation tuple exists 55 | } 56 | }, 57 | 58 | ngettext: (singular, plural, n) => { 59 | const translated = Documentation.TRANSLATIONS[singular]; 60 | if (typeof translated !== "undefined") 61 | return translated[Documentation.PLURAL_EXPR(n)]; 62 | return n === 1 ? singular : plural; 63 | }, 64 | 65 | addTranslations: (catalog) => { 66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 67 | Documentation.PLURAL_EXPR = new Function( 68 | "n", 69 | `return (${catalog.plural_expr})` 70 | ); 71 | Documentation.LOCALE = catalog.locale; 72 | }, 73 | 74 | /** 75 | * helper function to focus on search bar 76 | */ 77 | focusSearchBar: () => { 78 | document.querySelectorAll("input[name=q]")[0]?.focus(); 79 | }, 80 | 81 | /** 82 | * Initialise the domain index toggle buttons 83 | */ 84 | initDomainIndexTable: () => { 85 | const toggler = (el) => { 86 | const idNumber = el.id.substr(7); 87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 88 | if (el.src.substr(-9) === "minus.png") { 89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 90 | toggledRows.forEach((el) => (el.style.display = "none")); 91 | } else { 92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 93 | toggledRows.forEach((el) => (el.style.display = "")); 94 | } 95 | }; 96 | 97 | const togglerElements = document.querySelectorAll("img.toggler"); 98 | togglerElements.forEach((el) => 99 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 100 | ); 101 | togglerElements.forEach((el) => (el.style.display = "")); 102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 103 | }, 104 | 105 | initOnKeyListeners: () => { 106 | // only install a listener if it is really needed 107 | if ( 108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 110 | ) 111 | return; 112 | 113 | document.addEventListener("keydown", (event) => { 114 | // bail for input elements 115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 116 | // bail with special keys 117 | if (event.altKey || event.ctrlKey || event.metaKey) return; 118 | 119 | if (!event.shiftKey) { 120 | switch (event.key) { 121 | case "ArrowLeft": 122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 123 | 124 | const prevLink = document.querySelector('link[rel="prev"]'); 125 | if (prevLink && prevLink.href) { 126 | window.location.href = prevLink.href; 127 | event.preventDefault(); 128 | } 129 | break; 130 | case "ArrowRight": 131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 132 | 133 | const nextLink = document.querySelector('link[rel="next"]'); 134 | if (nextLink && nextLink.href) { 135 | window.location.href = nextLink.href; 136 | event.preventDefault(); 137 | } 138 | break; 139 | } 140 | } 141 | 142 | // some keyboard layouts may need Shift to get / 143 | switch (event.key) { 144 | case "/": 145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 146 | Documentation.focusSearchBar(); 147 | event.preventDefault(); 148 | } 149 | }); 150 | }, 151 | }; 152 | 153 | // quick alias for translations 154 | const _ = Documentation.gettext; 155 | 156 | _ready(Documentation.init); 157 | -------------------------------------------------------------------------------- /docs/_static/sphinx_highlight.js: -------------------------------------------------------------------------------- 1 | /* Highlighting utilities for Sphinx HTML documentation. */ 2 | "use strict"; 3 | 4 | const SPHINX_HIGHLIGHT_ENABLED = true 5 | 6 | /** 7 | * highlight a given string on a node by wrapping it in 8 | * span elements with the given class name. 9 | */ 10 | const _highlight = (node, addItems, text, className) => { 11 | if (node.nodeType === Node.TEXT_NODE) { 12 | const val = node.nodeValue; 13 | const parent = node.parentNode; 14 | const pos = val.toLowerCase().indexOf(text); 15 | if ( 16 | pos >= 0 && 17 | !parent.classList.contains(className) && 18 | !parent.classList.contains("nohighlight") 19 | ) { 20 | let span; 21 | 22 | const closestNode = parent.closest("body, svg, foreignObject"); 23 | const isInSVG = closestNode && closestNode.matches("svg"); 24 | if (isInSVG) { 25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 26 | } else { 27 | span = document.createElement("span"); 28 | span.classList.add(className); 29 | } 30 | 31 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 32 | parent.insertBefore( 33 | span, 34 | parent.insertBefore( 35 | document.createTextNode(val.substr(pos + text.length)), 36 | node.nextSibling 37 | ) 38 | ); 39 | node.nodeValue = val.substr(0, pos); 40 | 41 | if (isInSVG) { 42 | const rect = document.createElementNS( 43 | "http://www.w3.org/2000/svg", 44 | "rect" 45 | ); 46 | const bbox = parent.getBBox(); 47 | rect.x.baseVal.value = bbox.x; 48 | rect.y.baseVal.value = bbox.y; 49 | rect.width.baseVal.value = bbox.width; 50 | rect.height.baseVal.value = bbox.height; 51 | rect.setAttribute("class", className); 52 | addItems.push({ parent: parent, target: rect }); 53 | } 54 | } 55 | } else if (node.matches && !node.matches("button, select, textarea")) { 56 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 57 | } 58 | }; 59 | const _highlightText = (thisNode, text, className) => { 60 | let addItems = []; 61 | _highlight(thisNode, addItems, text, className); 62 | addItems.forEach((obj) => 63 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 64 | ); 65 | }; 66 | 67 | /** 68 | * Small JavaScript module for the documentation. 69 | */ 70 | const SphinxHighlight = { 71 | 72 | /** 73 | * highlight the search words provided in localstorage in the text 74 | */ 75 | highlightSearchWords: () => { 76 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight 77 | 78 | // get and clear terms from localstorage 79 | const url = new URL(window.location); 80 | const highlight = 81 | localStorage.getItem("sphinx_highlight_terms") 82 | || url.searchParams.get("highlight") 83 | || ""; 84 | localStorage.removeItem("sphinx_highlight_terms") 85 | url.searchParams.delete("highlight"); 86 | window.history.replaceState({}, "", url); 87 | 88 | // get individual terms from highlight string 89 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 90 | if (terms.length === 0) return; // nothing to do 91 | 92 | // There should never be more than one element matching "div.body" 93 | const divBody = document.querySelectorAll("div.body"); 94 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 95 | window.setTimeout(() => { 96 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 97 | }, 10); 98 | 99 | const searchBox = document.getElementById("searchbox"); 100 | if (searchBox === null) return; 101 | searchBox.appendChild( 102 | document 103 | .createRange() 104 | .createContextualFragment( 105 | '" 109 | ) 110 | ); 111 | }, 112 | 113 | /** 114 | * helper function to hide the search marks again 115 | */ 116 | hideSearchWords: () => { 117 | document 118 | .querySelectorAll("#searchbox .highlight-link") 119 | .forEach((el) => el.remove()); 120 | document 121 | .querySelectorAll("span.highlighted") 122 | .forEach((el) => el.classList.remove("highlighted")); 123 | localStorage.removeItem("sphinx_highlight_terms") 124 | }, 125 | 126 | initEscapeListener: () => { 127 | // only install a listener if it is really needed 128 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 129 | 130 | document.addEventListener("keydown", (event) => { 131 | // bail for input elements 132 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 133 | // bail with special keys 134 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; 135 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { 136 | SphinxHighlight.hideSearchWords(); 137 | event.preventDefault(); 138 | } 139 | }); 140 | }, 141 | }; 142 | 143 | _ready(SphinxHighlight.highlightSearchWords); 144 | _ready(SphinxHighlight.initEscapeListener); 145 | -------------------------------------------------------------------------------- /documents/PyHGL/1-basic/start.md: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | 3 | `python -m pip install pyhgl` 4 | 5 | 6 | ## Syntax 7 | 8 | 9 | PyHGL adds some syntactic sugar to Python and uses a parser generated by [Pegen](https://github.com/we-like-parsers/pegen). PyHGL source files (with `.pyh` suffix) should be imported by a normal python file that has imported `pyhgl`. 10 | 11 | ```py 12 | # in Adder.pyh 13 | from pyhgl.logic import * 14 | ... 15 | # in main.py 16 | import pyhgl 17 | import Adder 18 | ``` 19 | 20 | new operators and statements are listed below. 21 | 22 | ### Operator 23 | 24 | 25 | | Operator | Description | 26 | | -------------- | --------------------- | 27 | | `!x` | LogicNot | 28 | | `x && y` | LogicAnd | 29 | | `x \|\| y` | LogicOr | 30 | | `x \|-> y` | Imply(Assertion) | 31 | | `x >>> y` | Sequential(Assertion) | 32 | | `x <== y` | Assignment | 33 | | `x[idx] <== y` | Partial Assignment | 34 | | `x <=> y` | Connect | 35 | 36 | 37 | ### One-line Decorator 38 | 39 | ```py 40 | # pyhgl 41 | @decorator NAME: 42 | ... 43 | # python 44 | @decorator 45 | def NAME(): 46 | ... 47 | return locals() 48 | ``` 49 | 50 | ### When Statement 51 | 52 | ```py 53 | when signal: 54 | ... 55 | elsewhen signal: 56 | ... 57 | otherwise: 58 | ... 59 | ``` 60 | 61 | ### Switch Statement 62 | 63 | ```py 64 | switch signal: 65 | once a, b: 66 | ... 67 | once c: 68 | ... 69 | once ...: 70 | ... 71 | ``` 72 | 73 | 74 | ## Example 75 | 76 | a Game of Life example comes from [Chisel Tutorials](https://github.com/ucb-bar/chisel-tutorial). 77 | 78 | in `life.pyh` 79 | 80 | ```py 81 | from pyhgl.logic import * 82 | 83 | @module Life(rows, cols): # declares a module `Life` 84 | cells = Reg(Array.zeros(rows, cols)) @ Output # a rows*cols array of 1-bit regs 85 | running = UInt(0) @ Input # control signal. if 1, run; if 0, write data 86 | wdata = UInt(0) @ Input # data to write 87 | waddr_row = UInt(rows) @ Input # write address 88 | waddr_col = UInt(cols) @ Input # write address 89 | 90 | def make_cell(row, col): # row and col are indexes 91 | neighbors = cells[[row-1,row,(row+1)%rows], # select a 3*3 range circularly 92 | [col-1,col,(col+1)%cols]] 93 | cell = neighbors[1,1] # select current cell 94 | neighbors[1,1] = UInt('3:b0') # 3 bits to count 8 neighbors 95 | count_neighbors = Add(neighbors, axis=(0,1)) # sum up the 3*3 array, return a 3-bit uint 96 | when !running: # write data 97 | when waddr_row == row && waddr_col == col: 98 | cell <== wdata 99 | otherwise: # next state 100 | when count_neighbors == 3: # n=3, becomes a live cell 101 | cell <== 1 102 | elsewhen count_neighbors != 2: # n!=3 and n!=2, dies 103 | cell <== 0 104 | # otherwise keep the state 105 | for i in range(rows): 106 | for j in range(cols): 107 | make_cell(i,j) # map rules on each cell 108 | 109 | 110 | @task test_life(self, dut): # coroutine-based simulation tasks 111 | # dut is a `Life` module instance 112 | @task set_mode(self, run: bool): # declares a task 113 | setv(dut.running, run) # `setv` set value to signal/signals 114 | yield self.clock_n() # wait until next negedge of default clock 115 | @task write(self, row, col): # write `1` to a specific cell 116 | setv(dut.wdata, 1) 117 | setv(dut.waddr_row, row) 118 | setv(dut.waddr_col, col) 119 | yield self.clock_n() 120 | @task init_glider(self): 121 | # wait and execute these tasks sequentially 122 | yield write(3,3), write(3,5), write(4,4), write(4,5), write(5,4) 123 | @task show(self): 124 | cells_str = Map(lambda v: '*' if v==1 else ' ', getv(dut.cells)) 125 | for row in cells_str: 126 | print(' '.join(row)) 127 | yield self.clock_n() 128 | 129 | yield set_mode(0), init_glider(), set_mode(1) 130 | for _ in range(20): 131 | yield show() 132 | 133 | 134 | with Session() as sess: # enter a `Session` before initialization 135 | life = Life(10,10) # a 10*10 game of life 136 | sess.track(life) # track all singals in the module 137 | sess.join(test_life(life)) # wait until the task finishes 138 | sess.dumpVCD('life.vcd') # generate waveform to ./build/life.vcd 139 | sess.dumpVerilog('life.sv') # generate RTL to ./build/life.sv 140 | print(sess) # a summary 141 | ``` 142 | 143 | in `main.py` 144 | 145 | ```py 146 | import pyhgl 147 | import life 148 | ``` 149 | 150 | run: `python main.py` -------------------------------------------------------------------------------- /docs/_sources/PyHGL/1-basic/start.md.txt: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | 3 | `python -m pip install pyhgl` 4 | 5 | 6 | ## Syntax 7 | 8 | 9 | PyHGL adds some syntactic sugar to Python and uses a parser generated by [Pegen](https://github.com/we-like-parsers/pegen). PyHGL source files (with `.pyh` suffix) should be imported by a normal python file that has imported `pyhgl`. 10 | 11 | ```py 12 | # in Adder.pyh 13 | from pyhgl.logic import * 14 | ... 15 | # in main.py 16 | import pyhgl 17 | import Adder 18 | ``` 19 | 20 | new operators and statements are listed below. 21 | 22 | ### Operator 23 | 24 | 25 | | Operator | Description | 26 | | -------------- | --------------------- | 27 | | `!x` | LogicNot | 28 | | `x && y` | LogicAnd | 29 | | `x \|\| y` | LogicOr | 30 | | `x \|-> y` | Imply(Assertion) | 31 | | `x >>> y` | Sequential(Assertion) | 32 | | `x <== y` | Assignment | 33 | | `x[idx] <== y` | Partial Assignment | 34 | | `x <=> y` | Connect | 35 | 36 | 37 | ### One-line Decorator 38 | 39 | ```py 40 | # pyhgl 41 | @decorator NAME: 42 | ... 43 | # python 44 | @decorator 45 | def NAME(): 46 | ... 47 | return locals() 48 | ``` 49 | 50 | ### When Statement 51 | 52 | ```py 53 | when signal: 54 | ... 55 | elsewhen signal: 56 | ... 57 | otherwise: 58 | ... 59 | ``` 60 | 61 | ### Switch Statement 62 | 63 | ```py 64 | switch signal: 65 | once a, b: 66 | ... 67 | once c: 68 | ... 69 | once ...: 70 | ... 71 | ``` 72 | 73 | 74 | ## Example 75 | 76 | a Game of Life example comes from [Chisel Tutorials](https://github.com/ucb-bar/chisel-tutorial). 77 | 78 | in `life.pyh` 79 | 80 | ```py 81 | from pyhgl.logic import * 82 | 83 | @module Life(rows, cols): # declares a module `Life` 84 | cells = Reg(Array.zeros(rows, cols)) @ Output # a rows*cols array of 1-bit regs 85 | running = UInt(0) @ Input # control signal. if 1, run; if 0, write data 86 | wdata = UInt(0) @ Input # data to write 87 | waddr_row = UInt(rows) @ Input # write address 88 | waddr_col = UInt(cols) @ Input # write address 89 | 90 | def make_cell(row, col): # row and col are indexes 91 | neighbors = cells[[row-1,row,(row+1)%rows], # select a 3*3 range circularly 92 | [col-1,col,(col+1)%cols]] 93 | cell = neighbors[1,1] # select current cell 94 | neighbors[1,1] = UInt('3:b0') # 3 bits to count 8 neighbors 95 | count_neighbors = Add(neighbors, axis=(0,1)) # sum up the 3*3 array, return a 3-bit uint 96 | when !running: # write data 97 | when waddr_row == row && waddr_col == col: 98 | cell <== wdata 99 | otherwise: # next state 100 | when count_neighbors == 3: # n=3, becomes a live cell 101 | cell <== 1 102 | elsewhen count_neighbors != 2: # n!=3 and n!=2, dies 103 | cell <== 0 104 | # otherwise keep the state 105 | for i in range(rows): 106 | for j in range(cols): 107 | make_cell(i,j) # map rules on each cell 108 | 109 | 110 | @task test_life(self, dut): # coroutine-based simulation tasks 111 | # dut is a `Life` module instance 112 | @task set_mode(self, run: bool): # declares a task 113 | setv(dut.running, run) # `setv` set value to signal/signals 114 | yield self.clock_n() # wait until next negedge of default clock 115 | @task write(self, row, col): # write `1` to a specific cell 116 | setv(dut.wdata, 1) 117 | setv(dut.waddr_row, row) 118 | setv(dut.waddr_col, col) 119 | yield self.clock_n() 120 | @task init_glider(self): 121 | # wait and execute these tasks sequentially 122 | yield write(3,3), write(3,5), write(4,4), write(4,5), write(5,4) 123 | @task show(self): 124 | cells_str = Map(lambda v: '*' if v==1 else ' ', getv(dut.cells)) 125 | for row in cells_str: 126 | print(' '.join(row)) 127 | yield self.clock_n() 128 | 129 | yield set_mode(0), init_glider(), set_mode(1) 130 | for _ in range(20): 131 | yield show() 132 | 133 | 134 | with Session() as sess: # enter a `Session` before initialization 135 | life = Life(10,10) # a 10*10 game of life 136 | sess.track(life) # track all singals in the module 137 | sess.join(test_life(life)) # wait until the task finishes 138 | sess.dumpVCD('life.vcd') # generate waveform to ./build/life.vcd 139 | sess.dumpVerilog('life.sv') # generate RTL to ./build/life.sv 140 | print(sess) # a summary 141 | ``` 142 | 143 | in `main.py` 144 | 145 | ```py 146 | import pyhgl 147 | import life 148 | ``` 149 | 150 | run: `python main.py` -------------------------------------------------------------------------------- /docs/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, is available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /_test/test3_simple/fifo/FIFO.pyh: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import tester, utils 3 | 4 | import random 5 | import time 6 | 7 | @conf Global: 8 | conf.timing = Bundle( 9 | timescale = '1ns', 10 | clk1 = {'low':20, 'high':20, 'phase':0}, 11 | clk2 = {'low':40, 'high':40, 'phase':10}, 12 | ) 13 | 14 | 15 | def binary2gray(x): 16 | return (x >> 1) ^ x 17 | 18 | 19 | @module Async_FIFO(data_width: int = 16, addr_width: int = 3): 20 | 21 | io = Bundle( 22 | w_clk = Input('0'), 23 | w_rst_n = Input('0'), 24 | w_en = Input('0'), 25 | w_data = Input(UInt[data_width](0)), 26 | full = Output('0'), 27 | 28 | r_clk = Input('0'), 29 | r_rst_n = Input('0'), 30 | r_en = Input('0') , 31 | r_data = Output(UInt[data_width](0)), 32 | empty = Output('0'), 33 | ) 34 | 35 | RAM_t = UInt[data_width] * (1 << addr_width) 36 | w_domain = ClockDomain(clock=(io.w_clk, 1), reset=(io.w_rst_n, 0)) 37 | r_domain = ClockDomain(clock=(io.r_clk, 1), reset=(io.r_rst_n, 0)) 38 | 39 | # reg 40 | with w_domain: 41 | w_ptr = Reg(UInt[addr_width+1](0)) 42 | w_ptr_gray = binary2gray(w_ptr) 43 | RAM = Reg(RAM_t(0)) 44 | with r_domain: 45 | w_ptr_gray_syncr = RegNext(RegNext(w_ptr_gray)) 46 | r_ptr = Reg(UInt[addr_width+1](0)) 47 | r_ptr_gray = binary2gray(r_ptr) 48 | Reg(io.r_data) 49 | with w_domain: 50 | r_ptr_gray_syncw = RegNext(RegNext(r_ptr_gray)) 51 | 52 | io.full <== Cat(r_ptr_gray_syncw[:-2], ~r_ptr_gray_syncw[-2:]) == w_ptr_gray 53 | io.empty <== w_ptr_gray_syncr == r_ptr_gray 54 | 55 | when io.w_en && !io.full: 56 | w_ptr <== w_ptr + 1 57 | RAM[w_ptr[:-1]] <== io.w_data 58 | when io.r_en && !io.empty: 59 | r_ptr <== r_ptr + 1 60 | io.r_data <== RAM[r_ptr[:-1]] 61 | r_ptr_1 = r_ptr[:-1] 62 | r_data_test = RAM[r_ptr_1] 63 | 64 | 65 | 66 | #--------------------------------- test ---------------------------------------- 67 | 68 | DWIDTH = 8 69 | AWIDTH = 4 70 | N = 200 71 | 72 | # random.seed(42) 73 | # write_data = [] 74 | # read_data = [] 75 | 76 | # def write_fifo(dut): 77 | # clk = negedge(dut.io.w_clk) 78 | # yield from clk 79 | # for _ in range(N): 80 | # if random.randint(0,1) and getv(dut.io.full) == 0: 81 | # setv(dut.io.w_en, 1) 82 | # v = setr(dut.io.w_data) 83 | # write_data.append(v) 84 | # else: 85 | # setv(dut.io.w_en, 0) 86 | # yield from clk 87 | 88 | # def read_fifo(dut): 89 | # clk = negedge(dut.io.r_clk) 90 | # yield from clk 91 | # for _ in range(N): 92 | # if random.randint(0,1) and getv(dut.io.empty) == 0: 93 | # setv(dut.io.r_en, 1) 94 | # yield from clk 95 | # read_data.append(getv(dut.io.r_data)) 96 | # else: 97 | # setv(dut.io.r_en, 0) 98 | # yield from clk 99 | 100 | 101 | @task write_fifo(self, dut, data, N): 102 | for _ in range(N): 103 | if getv(dut.io.full) == 0: 104 | setv(dut.io.w_en, 1) 105 | data.append(setr(dut.io.w_data)) 106 | else: 107 | setv(dut.io.w_en, 0) 108 | yield self.edge(dut.io.w_clk, 0) 109 | 110 | @task read_fifo(self, dut, data, N): 111 | for _ in range(N): 112 | if getv(dut.io.empty) == 0: 113 | setv(dut.io.r_en, 1) 114 | yield self.edge(dut.io.r_clk, 0) 115 | data.append(getv(dut.io.r_data)) 116 | else: 117 | setv(dut.io.r_en, 0) 118 | yield self.edge(dut.io.r_clk, 0) 119 | 120 | @task test_fifo(self, dut, N): 121 | yield 10 122 | setv(dut.io.r_rst_n, 1) 123 | setv(dut.io.w_rst_n, 1) 124 | write_data, read_data = [], [] 125 | yield self.join( 126 | write_fifo(dut, write_data, N), 127 | read_fifo(dut, read_data, N), 128 | ) 129 | for w, r in zip(write_data, read_data): 130 | self.AssertEq(w, r) 131 | 132 | @tester test_FIFO(self): 133 | sess = Session(Global()) 134 | sess.enter() 135 | 136 | dut = Async_FIFO(DWIDTH, AWIDTH) 137 | dut.io.w_clk <== Clock(id='clk1') 138 | dut.io.r_clk <== Clock(id='clk2') 139 | 140 | sess.track(dut) 141 | sess.join(test_fifo(dut, N)) 142 | # sess.task(write_fifo(dut), read_fifo(dut)) 143 | 144 | # sess.step(100) 145 | # setv(dut.io.r_rst_n, 1) 146 | # setv(dut.io.w_rst_n, 1) 147 | # sess.step(N * 100) 148 | 149 | # for i, o in zip(write_data, read_data): 150 | # self.EQ += i, o 151 | 152 | 153 | # @blackbox testbench(builder): 154 | # dut_name = builder.get_name(dut) 155 | # w_rst = builder.get_name(dut.io.w_rst_n) 156 | # r_rst = builder.get_name(dut.io.r_rst_n) 157 | # w_en = builder.get_name(dut.io.w_en) 158 | # r_en = builder.get_name(dut.io.r_en) 159 | # w_data = builder.get_name(dut.io.w_data) 160 | 161 | # builder.append('initial begin') 162 | # builder.append(f'$dumpfile("FIFO_iverilog.vcd");') 163 | # builder.append(f'$dumpvars(0, {dut_name});') 164 | # builder.append(f'{w_rst}=0;{r_rst}=0;#100;') 165 | # builder.append(f'{w_rst}=1;{r_rst}=1;{w_en}=1;{r_en}=1;') 166 | # for v in write_data: 167 | # builder.append(f'{w_data} = {v}; #100;') 168 | # builder.append('#200;') 169 | # builder.append('$finish;') 170 | # builder.append('end') 171 | 172 | sess.dumpVerilog('FIFO.sv', delay=True, top = True) 173 | 174 | sess.dumpVCD('FIFO.vcd') 175 | sess.dumpGraph('FIFO.gv') 176 | print(sess) 177 | # sess.test_iverilog() 178 | sess.exit() 179 | -------------------------------------------------------------------------------- /_test/test0_grammar/grammar.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | # ==================================== test grammar ======================================= 4 | from pyhgl.parser import hgl_compile, ast_dump 5 | from pyhgl.tester import tester 6 | 7 | r = [] 8 | 9 | # ==================================== expr ======================================= 10 | # priority: python `==` > `>>>` > `&&` > `||` > `|->` > python `and` 11 | 12 | # priority: same as ~ 13 | def __hgl_logicnot__(a): 14 | ret = f'! {a}' 15 | r.append(ret) 16 | return ret 17 | 18 | # priority: between `not/and/or` and `comparation` 19 | def __hgl_imply__(a,b): 20 | ret = f'{a} |-> {b}' 21 | r.append(ret) 22 | return ret 23 | 24 | # priority: above |-> 25 | def __hgl_logicor__(a,b): 26 | ret = f'{a} || {b}' 27 | r.append(ret) 28 | return ret 29 | 30 | # priority: above || 31 | def __hgl_logicand__(a,b): 32 | ret = f'{a} && {b}' 33 | r.append(ret) 34 | return ret 35 | 36 | # priority: above && 37 | def __hgl_rshift__(*args): 38 | if len(args) == 2: 39 | a, b = args 40 | ret = f'{a} >>> {b}' 41 | else: 42 | ret = f'>>> {args[0]}' 43 | r.append(ret) 44 | return ret 45 | 46 | 47 | 48 | def __hgl_unit__(a,b): 49 | ret = f'{a}`{type(b)}' 50 | r.append(ret) 51 | return ret 52 | 53 | @tester 54 | def test_expr(self): 55 | exec(hgl_compile("!'a' >>> 'b'*3 || 'd' >>> !'e' + 'f' >>> 'g'")) 56 | self.EQ += r, [ 57 | '! a', 58 | '! a >>> bbb', 59 | '! e', 60 | 'd >>> ! ef', 61 | 'd >>> ! ef >>> g', 62 | '! a >>> bbb || d >>> ! ef >>> g' 63 | ] 64 | r.clear() 65 | exec(hgl_compile(">>> 'a' >>> ('b' || 'c') && 'd' |-> 'x'*2 >>> 2e-2`m/s` != 1 |-> (2+2j)`N*m`")) 66 | self.EQ += r, [ 67 | '>>> a', 68 | 'b || c', 69 | '>>> a >>> b || c', 70 | '>>> a >>> b || c && d', 71 | "0.02`", 72 | 'xx >>> True', 73 | '>>> a >>> b || c && d |-> xx >>> True', 74 | "(2+2j)`", 75 | ">>> a >>> b || c && d |-> xx >>> True |-> (2+2j)`" 76 | ] 77 | 78 | # ==================================== assignment ======================================= 79 | 80 | # | t_primary '[' b=slices ']' !t_lookahead '<==' c=expression {__hgl_partial_assign__} 81 | # | expression '<==' b=expression {__hgl_assign__} 82 | def __hgl_partial_assign__(a,b, slices=None): r.append(f'{a} ## {slices} <== {b}') 83 | def __hgl_connect__(a,b): r.append(f'{a} <=> {b}') 84 | 85 | @tester 86 | def test_assign(self): 87 | r.clear() 88 | exec(hgl_compile("a,b = 'left','right'; a <== b; a[1] <== b; a['x',:-1] <== b[-1]; a <=> b;")) 89 | self.EQ += r, [ 90 | 'left ## None <== right', 91 | 'left ## 1 <== right', 92 | "left ## ('x', slice(None, -1, None)) <== t", 93 | 'left <=> right' 94 | ] 95 | 96 | hgl_compile("a.b[3] <== c.d >>> 4 && x[2].y.z >>> z.z(a || b && !c)>>>1>>>1") 97 | 98 | 99 | # ==================================== decorator ======================================= 100 | 101 | def module(f): 102 | r.append(f()) 103 | return f 104 | 105 | code=""" 106 | @module _M1: 107 | x = 1 108 | y = 1 109 | @module _M2(*args: str, **kwargs): 110 | pass 111 | """ 112 | 113 | @tester 114 | def test_module(self): 115 | r.clear() 116 | exec(hgl_compile(code)) 117 | self.EQ += r, [{'x': 1, 'y': 1}, {'args': (), 'kwargs': {}}] 118 | 119 | 120 | # ==================================== stmt ======================================= 121 | 122 | count = 0 123 | class __hgl_when__: 124 | def __init__(self, arg): r.append(arg) 125 | def __enter__(self): r.append('begin_when') 126 | def __exit__(self, *args): r.append('end_when') 127 | class __hgl_elsewhen__: 128 | def __init__(self, arg): r.append(arg) 129 | def __enter__(self): r.append('begin_elsewhen') 130 | def __exit__(self, *args): r.append('end_elsewhen') 131 | class __hgl_otherwise__: 132 | def __enter__(self): r.append('begin_otherwise') 133 | def __exit__(self, *args): r.append('end_otherwise') 134 | class __hgl_switch__: 135 | def __init__(self, arg): r.append(arg) 136 | def __enter__(self): r.append('begin_switch') 137 | def __exit__(self, *args): r.append('end_switch') 138 | class __hgl_once__: 139 | def __init__(self, arg): r.append(arg) 140 | def __enter__(self): r.append('begin_once') 141 | def __exit__(self, *args): r.append('end_once') 142 | 143 | 144 | code=""" 145 | when 123: 146 | ... 147 | elsewhen 456: 148 | ... 149 | otherwise: 150 | switch s:='state': 151 | once 'idle': 152 | ... 153 | once 's0', 's1', 's2': 154 | switch ('a','b'): 155 | once ('s3'): 156 | when 's4': 157 | ... 158 | elsewhen 's5': 159 | ... 160 | once s: 161 | ... 162 | once *(1,2,3): 163 | ... 164 | once ...: 165 | ... 166 | """ 167 | 168 | @tester 169 | def test_stmt(self): 170 | r.clear() 171 | exec(hgl_compile(code)) 172 | self.EQ += r,[123, 'begin_when', 'end_when', 456, 'begin_elsewhen', 'end_elsewhen', 'begin_otherwise', 'state', 'begin_switch', ('idle',), 'begin_once', 'end_once', ('s0', 's1', 's2'), 'begin_once', ('a', 'b'), 'begin_switch', ('s3',), 'begin_once', 's4', 'begin_when', 'end_when', 's5', 'begin_elsewhen', 'end_elsewhen', 'end_once', ('state',), 'begin_once', 'end_once', 'end_switch', 'end_once', (1, 2, 3), 'begin_once', 'end_once', (Ellipsis,), 'begin_once', 'end_once', 'end_switch', 'end_otherwise'] 173 | 174 | 175 | 176 | # print(ast_dump( 177 | # """ 178 | # (a, b) <== 1 179 | # """ 180 | # )) 181 | 182 | 183 | -------------------------------------------------------------------------------- /_test/test2_basic/combinational.py: -------------------------------------------------------------------------------- 1 | from pyhgl.logic import * 2 | from pyhgl.tester import * 3 | 4 | import pyhgl.logic.utils as utils 5 | import random 6 | 7 | 8 | 9 | 10 | @tester 11 | def test_logic(self): 12 | x = Logic('0011xx') 13 | y = Logic('1x0x10') 14 | 15 | self.AssertEq(x & y, '000xx0') 16 | self.AssertEq(x | y, '1x111x') 17 | self.AssertEq(x ^ y, '1x1xxx') 18 | self.AssertEq(~x, Logic('1100xx') | Logic(~0b111111) ) 19 | 20 | z = Logic('01010x0x') 21 | self.AssertEq(z.to_bin(16), '0000000001010x0x') 22 | self.AssertEq(z.to_hex(16), '005x') 23 | z = Logic(-3) 24 | self.AssertEq(z.to_hex(15), '7ffd') 25 | self.AssertEq(z.to_int() , -3) 26 | self.AssertEq(z.to_int(3) , -3) 27 | self.AssertEq(z.to_int(2) , 1 ) 28 | self.AssertEq(list(z.split(8,4)), [0xd, 0xf, 0xf,0xf,0xf,0xf,0xf,0xf,]) 29 | 30 | 31 | @tester 32 | def test_single_input(self): 33 | with Session() as sess: 34 | a = UInt('x1x011', name='a') 35 | b = UInt('11xx00', name='b') 36 | result_not = Not(a, name='result_not') 37 | result_and = And(b, name='result_and') 38 | sess.step(10) 39 | for _ in range(10): 40 | a_in = setx(a) 41 | b_in = setx(b) 42 | sess.step(10) 43 | # print(a_in.to_bin(6), getv(result_not).to_bin(6)) 44 | self.AssertEq(getv(result_not), (~a_in) & Logic('111111')) 45 | self.AssertEq(getv(result_and), b_in) 46 | 47 | 48 | sess = Session() 49 | sess.enter() 50 | 51 | @tester 52 | def test_boolean_shift_cmp_arith(self): 53 | # 4 input signals 54 | inputs = Array(UInt[5](0, name=f'input_{i}') for i in range(4)) 55 | # bitwise and/or/xor... 56 | out_and = And(inputs, axis=None) 57 | out_nand = Nand(inputs, axis=None) 58 | out_or = Or(inputs, axis=None) 59 | out_nor = Nor(inputs, axis=None) 60 | out_xor = Xor(inputs, axis=None) 61 | out_nxor = Nxor(inputs, axis=None) 62 | out_not = ~ inputs 63 | out_andr = AndR(inputs) 64 | out_orr = OrR(inputs) 65 | out_xorr = XorR(inputs) 66 | 67 | out_cat = Cat(inputs, axis=None) 68 | out_duplicated = inputs ** [1,2,3,4] 69 | # logic and/or/not 70 | out_logicand = LogicAnd(inputs, axis=None) 71 | out_logicor = LogicOr(inputs, axis=None) 72 | out_logicnot = LogicNot(inputs) 73 | # shift 74 | out_lshift = inputs << [1,2,3,4] 75 | out_rshift = inputs >> [1,2,3,4] 76 | # compare 77 | out_eq = inputs[0] == inputs[1] 78 | out_ne = inputs[0] != inputs[1] 79 | out_lt = inputs[0] < inputs[1] 80 | out_le = inputs[0] <= inputs[1] 81 | out_gt = inputs[0] > inputs[1] 82 | out_ge = inputs[0] >= inputs[1] 83 | # arithmetic 84 | out_pos = +inputs 85 | out_neg = -inputs 86 | out_add = Add(inputs, axis=None) 87 | out_sub = Sub(inputs, axis=None) 88 | out_mul = Mul(inputs, axis=None) 89 | out_floordiv = inputs[0] // inputs[1] 90 | out_mod = inputs[0] % inputs[1] 91 | # simple slicing 92 | out_sliced = inputs[:,-3:] 93 | # mux 94 | out_mux = Mux(inputs[0], inputs[1], inputs[2]) 95 | 96 | # slice 97 | out_slice = inputs[0][inputs[1]::2] 98 | 99 | sess.step(10) 100 | # tests 101 | for _ in range(100): 102 | 103 | v = setr(inputs) 104 | mask = Logic(0b11111) 105 | sess.step(10) 106 | 107 | self.EQ += getv(out_and), v[0] & v[1] & v[2] & v[3] 108 | self.EQ += getv(out_nand), ~(v[0] & v[1] & v[2] & v[3]) & mask 109 | self.EQ += getv(out_or), v[0] | v[1] | v[2] | v[3] 110 | self.EQ += getv(out_nor), ~(v[0] | v[1] | v[2] | v[3]) & mask 111 | self.EQ += getv(out_xor), v[0] ^ v[1] ^ v[2] ^ v[3] 112 | self.EQ += getv(out_nxor), ~(v[0] ^ v[1] ^ v[2] ^ v[3]) & mask 113 | 114 | self.EQ += list(getv(out_not)), [~i & mask for i in v] 115 | self.EQ += list(getv(out_andr)), [(i | ~mask)._andr() for i in v] 116 | self.EQ += list(getv(out_orr)), [i._orr() for i in v] 117 | self.EQ += list(getv(out_xorr)), [i._xorr() for i in v] 118 | 119 | self.EQ += getv(out_cat), ( 120 | v[0] 121 | | v[1] << Logic(5) 122 | | v[2] << Logic(10) 123 | | v[3] << Logic(15) 124 | ) 125 | self.EQ += list(getv(out_duplicated)), [ 126 | v[0], 127 | v[1] << Logic(5) | v[1], 128 | v[2] << Logic(10) | v[2] << Logic(5) | v[2], 129 | v[3] << Logic(15) | v[3] << Logic(10) | v[3] << Logic(5) | v[3], 130 | ] 131 | self.EQ += getv(out_logicand), v[0]._orr() & v[1]._orr() & v[2]._orr() & v[3]._orr() 132 | self.EQ += getv(out_logicor), v[0]._orr() | v[1]._orr() | v[2]._orr() | v[3]._orr() 133 | self.AssertEq(list(getv(out_logicnot)), [ 134 | ~v[0]._orr() & Logic(1), 135 | ~v[1]._orr() & Logic(1), 136 | ~v[2]._orr() & Logic(1), 137 | ~v[3]._orr() & Logic(1), 138 | ]) 139 | 140 | self.AssertEq(list(getv(out_lshift)), [ 141 | v[0] << Logic(1) & mask, 142 | v[1] << Logic(2) & mask, 143 | v[2] << Logic(3) & mask, 144 | v[3] << Logic(4) & mask, 145 | ]) 146 | self.AssertEq(list(getv(out_rshift)), [ 147 | v[0] >> Logic(1), 148 | v[1] >> Logic(2), 149 | v[2] >> Logic(3), 150 | v[3] >> Logic(4), 151 | ]) 152 | 153 | self.EQ += getv(out_eq), v[0]._eq(v[1]) 154 | self.EQ += getv(out_ne), v[0]._ne(v[1]) 155 | self.EQ += getv(out_lt), v[0]._lt(v[1]) 156 | self.EQ += getv(out_le), v[0]._le(v[1]) 157 | self.EQ += getv(out_gt), v[0]._gt(v[1]) 158 | self.EQ += getv(out_ge), v[0]._ge(v[1]) 159 | 160 | self.EQ += list(getv(out_pos)), list(v) 161 | self.EQ += list(getv(out_neg)), [ 162 | -v[0] & mask, 163 | -v[1] & mask, 164 | -v[2] & mask, 165 | -v[3] & mask 166 | ] 167 | self.EQ += getv(out_add), (v[0] + v[1] + v[2] + v[3]) & mask 168 | self.EQ += getv(out_sub), (v[0] - v[1] - v[2] - v[3]) & mask 169 | self.EQ += getv(out_mul), (v[0] * v[1] * v[2] * v[3]) & mask 170 | self.EQ += getv(out_floordiv), (v[0] // v[1]) & mask 171 | self.EQ += getv(out_mod), (v[0] % v[1]) & mask 172 | 173 | self.EQ += list(getv(out_sliced)), [ 174 | v[0] >> Logic(2) & Logic(7), 175 | v[1] >> Logic(2) & Logic(7), 176 | v[2] >> Logic(2) & Logic(7), 177 | v[3] >> Logic(2) & Logic(7), 178 | ] 179 | 180 | if v[0].x: 181 | _out_mux = Logic(v[1].v | v[2].v, v[1].x|v[2].x|v[1].v^v[2].v) 182 | elif v[0].v: 183 | _out_mux = v[1] 184 | else: 185 | _out_mux = v[2] 186 | self.EQ += getv(out_mux), _out_mux 187 | 188 | 189 | sess.exit() 190 | 191 | -------------------------------------------------------------------------------- /pyhgl/array/functions.py: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | # 3 | # PyHGL - A Python-embedded Hardware Generation Language 4 | # Copyright (C) 2022 Jintao Sun 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | #################################################################################################### 20 | 21 | 22 | from ._hgl import HGL 23 | from .dispatch import singleton, dispatch, Dispatcher, default_dispatcher, register_builtins 24 | from .array import * 25 | 26 | 27 | 28 | class HGLPattern(HGL): 29 | """ hardware assertion 30 | """ 31 | @property 32 | def __hgl_type__(self): 33 | return HGLPattern 34 | 35 | class HGLFunction(HGL): 36 | 37 | @property 38 | def __hgl_type__(self): 39 | return HGLFunction 40 | 41 | def Assert(*args, **kwargs): 42 | """ Assertion 43 | """ 44 | return HGL._sess.module._conf.dispatcher.call('Assert', *args, **kwargs) 45 | #-------- 46 | # bitwise 47 | #-------- 48 | @vectorize_axis 49 | def Nand(*args, **kwargs): 50 | """ args: Signal or Immd 51 | """ 52 | args = (Signal(i) for i in args) 53 | return HGL._sess.module._conf.dispatcher.call('Nand', *args, **kwargs) 54 | 55 | @vectorize_axis 56 | def Nor(*args, **kwargs): 57 | """ args: Signal or Immd 58 | """ 59 | args = (Signal(i) for i in args) 60 | return HGL._sess.module._conf.dispatcher.call('Nor', *args, **kwargs) 61 | 62 | @vectorize_axis 63 | def Nxor(*args, **kwargs): 64 | """ args: Signal or Immd 65 | """ 66 | args = (Signal(i) for i in args) 67 | return HGL._sess.module._conf.dispatcher.call('Nxor', *args, **kwargs) 68 | 69 | @vectorize 70 | def AndR(a, **kwargs): 71 | """ a: Signal or Immd 72 | """ 73 | return HGL._sess.module._conf.dispatcher.call('AndR', Signal(a), **kwargs) 74 | 75 | @vectorize 76 | def OrR(a, **kwargs): 77 | """ a: Signal or Immd 78 | """ 79 | return HGL._sess.module._conf.dispatcher.call('OrR', Signal(a), **kwargs) 80 | 81 | @vectorize 82 | def XorR(a, **kwargs): 83 | """ a: Signal or Immd 84 | """ 85 | return HGL._sess.module._conf.dispatcher.call('XorR', Signal(a), **kwargs) 86 | 87 | @vectorize 88 | def AddFull(a, b, **kwargs): 89 | """ a: Signal, b: Signal or Immd 90 | """ 91 | return HGL._sess.module._conf.dispatcher.call('AddFull', Signal(a), Signal(b), **kwargs) 92 | 93 | @vectorize 94 | def MulFull(a, b, **kwargs): 95 | """ a: Signal, b: Signal or Immd 96 | """ 97 | return HGL._sess.module._conf.dispatcher.call('MulFull', Signal(a), Signal(b), **kwargs) 98 | 99 | 100 | #------- 101 | # logic 102 | #------- 103 | @vectorize 104 | def Bool(a, **kwargs): 105 | """ a: Signal or Immd 106 | 107 | return a 1-bit uint signal, may not generate new gate 108 | """ 109 | return HGL._sess.module._conf.dispatcher.call('Bool', Signal(a), **kwargs) 110 | # !a 111 | @vectorize 112 | def LogicNot(a, **kwargs): 113 | """ a: Signal or Immd 114 | """ 115 | return HGL._sess.module._conf.dispatcher.call('LogicNot', Signal(a), **kwargs) 116 | 117 | # a && b 118 | @vectorize_axis 119 | def LogicAnd(*args, **kwargs): 120 | """ args: Signal or Immd 121 | """ 122 | args = (Signal(i) for i in args) 123 | return HGL._sess.module._conf.dispatcher.call('LogicAnd', *args, **kwargs) 124 | 125 | 126 | # a || b 127 | @vectorize_axis 128 | def LogicOr(*args, **kwargs): 129 | """ args: Signal or Immd 130 | """ 131 | args = (Signal(i) for i in args) 132 | return HGL._sess.module._conf.dispatcher.call('LogicOr', *args, **kwargs) 133 | 134 | 135 | #-------- 136 | # slicing 137 | #-------- 138 | @vectorize_axis 139 | def Cat(*args, **kwargs): 140 | """ args: Signal or Immd 141 | """ 142 | args = (Signal(i) for i in args) 143 | return HGL._sess.module._conf.dispatcher.call('Cat', *args, **kwargs) 144 | 145 | @vectorize_first 146 | def Slice(x, **kwargs): 147 | """ x: Signal or Immd, key = ... 148 | """ 149 | return HGL._sess.module._conf.dispatcher.call('Slice', Signal(x), **kwargs) 150 | 151 | #----------- 152 | # converting 153 | #----------- 154 | @vectorize 155 | def Convert(a, b, **kwargs): 156 | """ a: Signal, b: SignalType 157 | """ 158 | return HGL._sess.module._conf.dispatcher.call('Convert', a, b, **kwargs) 159 | 160 | 161 | #-------- 162 | # netlist 163 | #-------- 164 | @vectorize 165 | def Wire(x, **kwargs): 166 | return HGL._sess.module._conf.dispatcher.call('Wire', Signal(x), **kwargs) 167 | 168 | @vectorize 169 | def WireNext(x, **kwargs): 170 | return HGL._sess.module._conf.dispatcher.call('WireNext', Signal(x), **kwargs) 171 | 172 | @vectorize 173 | def Reg(x, **kwargs): 174 | return HGL._sess.module._conf.dispatcher.call('Reg', Signal(x), **kwargs) 175 | 176 | @vectorize 177 | def RegNext(x, **kwargs): 178 | return HGL._sess.module._conf.dispatcher.call('RegNext', Signal(x), **kwargs) 179 | 180 | @vectorize 181 | def Latch(x, **kwargs): 182 | return HGL._sess.module._conf.dispatcher.call('Latch', Signal(x), **kwargs) 183 | 184 | @vectorize 185 | def Wtri(x, **kwargs): 186 | return HGL._sess.module._conf.dispatcher.call('Wtri', Signal(x), **kwargs) 187 | 188 | @vectorize 189 | def Wor(x, **kwargs): 190 | return HGL._sess.module._conf.dispatcher.call('Wor', Signal(x), **kwargs) 191 | 192 | @vectorize 193 | def Wand(x, **kwargs): 194 | return HGL._sess.module._conf.dispatcher.call('Wand', Signal(x), **kwargs) 195 | 196 | 197 | #-------------------------------- 198 | # extended hgl operators: ! && || 199 | #-------------------------------- 200 | 201 | 202 | @singleton 203 | class __hgl_logicnot__(HGL): 204 | """ PyHGL operator ! 205 | """ 206 | def __call__(self, a): 207 | return LogicNot(a) 208 | 209 | 210 | @singleton 211 | class __hgl_logicand__(HGL): 212 | """ PyHGL operator && 213 | """ 214 | def __call__(self, a, b): 215 | return LogicAnd(a,b) 216 | 217 | 218 | @singleton 219 | class __hgl_logicor__(HGL): 220 | """ PyHGL operator || 221 | """ 222 | def __call__(self, a, b): 223 | return LogicOr(a,b) 224 | 225 | 226 | @singleton 227 | class __hgl_rshift__(HGL): 228 | """ PyHGL operator >>>, unary op: >>> a; binary op: a >>> b 229 | """ 230 | def __call__(self, *args): 231 | return HGL._sess.module._conf.dispatcher.call('Sequence', *args) 232 | 233 | @singleton 234 | class __hgl_imply__(HGL): 235 | """ PyHGL operator |-> 236 | """ 237 | def __call__(self, a, b): 238 | return HGL._sess.module._conf.dispatcher.call('Imply', a, b) 239 | 240 | 241 | 242 | @singleton 243 | class __hgl_unit__(HGL): 244 | """ 245 | ex: 1.2`m/s` 246 | f: lambda:m/s 247 | exec f and return a unit 248 | """ 249 | def __call__(self, v, f: callable): 250 | pass 251 | -------------------------------------------------------------------------------- /documents/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file does only contain a selection of the most common options. For a 4 | # full list see the documentation: 5 | # http://www.sphinx-doc.org/en/master/config 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | #import recommonmark 18 | #from recommonmark.parser import CommonMarkParser 19 | #from recommonmark.transform import AutoStructify 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = 'PyHGL' 24 | copyright = '2022, jintaos2' 25 | author = 'jintaos2' 26 | 27 | # The short X.Y version 28 | version = '' 29 | # The full version, including alpha/beta/rc tags 30 | release = '' 31 | 32 | 33 | # -- General configuration --------------------------------------------------- 34 | 35 | # If your documentation needs a minimal Sphinx version, state it here. 36 | # 37 | # needs_sphinx = '1.0' 38 | 39 | # Add any Sphinx extension module names here, as strings. They can be 40 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 41 | # ones. 42 | extensions = [ 43 | 'sphinx.ext.intersphinx', 44 | 'sphinx.ext.mathjax', 45 | 'sphinx.ext.ifconfig', 46 | 'sphinx.ext.viewcode', 47 | 'sphinx.ext.githubpages', 48 | 'sphinx_multiversion', 49 | 'myst_parser', 50 | ] 51 | 52 | # Add any paths that contain templates here, relative to this directory. 53 | templates_path = [] 54 | 55 | # The suffix(es) of source filenames. 56 | # You can specify multiple suffix as a list of string: 57 | # 58 | source_suffix = { 59 | '.rst': 'restructuredtext', 60 | '.txt': 'markdown', 61 | '.md': 'markdown', 62 | } 63 | 64 | #source_parsers = { 65 | # '.md': CommonMarkParser, 66 | #} 67 | 68 | # The master toctree document. 69 | master_doc = 'index' 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = 'en' 77 | 78 | # List of patterns, relative to source directory, that match files and 79 | # directories to ignore when looking for source files. 80 | # This pattern also affects html_static_path and html_extra_path. 81 | exclude_patterns = [] 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = None 85 | 86 | 87 | # -- Options for HTML output ------------------------------------------------- 88 | 89 | # The theme to use for HTML and HTML Help pages. See the documentation for 90 | # a list of builtin themes. 91 | # 92 | # html_theme = 'alabaster' 93 | html_theme = 'sphinx_rtd_theme' 94 | 95 | # Theme options are theme-specific and customize the look and feel of a theme 96 | # further. For a list of options available for each theme, see the 97 | # documentation. 98 | # 99 | # html_theme_options = {} 100 | 101 | # Add any paths that contain custom static files (such as style sheets) here, 102 | # relative to this directory. They are copied after the builtin static files, 103 | # so a file named "default.css" will overwrite the builtin "default.css". 104 | html_static_path = [] 105 | 106 | # Custom sidebar templates, must be a dictionary that maps document names 107 | # to template names. 108 | # 109 | # The default sidebars (for documents that don't match any pattern) are 110 | # defined by theme itself. Builtin themes are using these templates by 111 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 112 | # 'searchbox.html']``. 113 | # 114 | # html_sidebars = {} 115 | 116 | # html_favicon = 'logo.png' 117 | 118 | # -- Options for HTMLHelp output --------------------------------------------- 119 | 120 | # Output file base name for HTML help builder. 121 | htmlhelp_basename = 'PyHGLdoc' 122 | 123 | 124 | # -- Options for LaTeX output ------------------------------------------------ 125 | 126 | latex_elements = { 127 | # The paper size ('letterpaper' or 'a4paper'). 128 | # 129 | # 'papersize': 'letterpaper', 130 | 131 | # The font size ('10pt', '11pt' or '12pt'). 132 | # 133 | # 'pointsize': '10pt', 134 | 135 | # Additional stuff for the LaTeX preamble. 136 | # 137 | # 'preamble': '', 138 | 139 | # Latex figure (float) alignment 140 | # 141 | # 'figure_align': 'htbp', 142 | } 143 | 144 | # Grouping the document tree into LaTeX files. List of tuples 145 | # (source start file, target name, title, 146 | # author, documentclass [howto, manual, or own class]). 147 | latex_documents = [ 148 | (master_doc, 'PyHGLdoc.tex', 'PyHGL Documentation', 'jintaos2', 'manual'), 149 | ] 150 | 151 | 152 | # -- Options for manual page output ------------------------------------------ 153 | 154 | # One entry per manual page. List of tuples 155 | # (source start file, name, description, authors, manual section). 156 | man_pages = [ 157 | (master_doc, 'PyHGLdoc', 'PyHGL Documentation', [author], 1) 158 | ] 159 | 160 | 161 | # -- Options for Texinfo output ---------------------------------------------- 162 | 163 | # Grouping the document tree into Texinfo files. List of tuples 164 | # (source start file, target name, title, author, 165 | # dir menu entry, description, category) 166 | 167 | 168 | 169 | # -- Options for Epub output ------------------------------------------------- 170 | 171 | # Bibliographic Dublin Core info. 172 | epub_title = project 173 | 174 | # The unique identifier of the text. This can be a ISBN number 175 | # or the project homepage. 176 | # 177 | # epub_identifier = '' 178 | 179 | # A unique identification for the text. 180 | # 181 | # epub_uid = '' 182 | 183 | # A list of files that should not be packed into the epub file. 184 | epub_exclude_files = ['search.html'] 185 | 186 | 187 | # -- Extension configuration ------------------------------------------------- 188 | html_theme_options = { 189 | 'canonical_url': '', 190 | 'analytics_id': '', 191 | 'logo_only': False, 192 | 'display_version': True, 193 | 'prev_next_buttons_location': 'bottom', 194 | 'style_external_links': False, 195 | # Toc options 196 | 'collapse_navigation': False, 197 | 'sticky_navigation': True, 198 | # 'navigation_depth': 4, 199 | 'includehidden': True, 200 | 'titles_only': False 201 | } 202 | # -- Options for intersphinx extension --------------------------------------- 203 | 204 | # Example configuration for intersphinx: refer to the Python standard library. 205 | intersphinx_mapping = {'https://docs.python.org/': None} 206 | 207 | # -- Options for todo extension ---------------------------------------------- 208 | 209 | # If true, `todo` and `todoList` produce output, else they produce nothing. 210 | todo_include_todos = True 211 | 212 | #Option for linkcheck 213 | linkcheck_anchors=False 214 | 215 | smv_branch_whitelist = r'^(master|dev).*$' 216 | smv_remote_whitelist = r'^.*$' 217 | smv_tag_whitelist = r'^.*$' 218 | smv_prefer_remote_refs = True 219 | 220 | 221 | myst_enable_extensions = [ 222 | # "amsmath", 223 | # "colon_fence", 224 | # "deflist", 225 | "dollarmath", 226 | "html_admonition", 227 | # "html_image", 228 | "linkify", 229 | # "replacements", 230 | # "smartquotes", 231 | # "substitution", 232 | # "tasklist", 233 | ] -------------------------------------------------------------------------------- /docs/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Index — PyHGL documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 105 | 106 |
110 | 111 |
112 |
113 |
114 |
    115 |
  • 116 | 117 |
  • 118 |
  • 119 |
120 |
121 |
122 |
123 |
124 | 125 | 126 |

Index

127 | 128 |
129 | 130 |
131 | 132 | 133 |
134 |
135 |
136 | 137 |
138 | 139 |
140 |

© Copyright 2022, jintaos2.

141 |
142 | 143 | Built with Sphinx using a 144 | theme 145 | provided by Read the Docs. 146 | 147 | 148 |
149 |
150 |
151 |
152 |
153 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /pyhgl/array/dispatch.py: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | # 3 | # PyHGL - A Python-embedded Hardware Generation Language 4 | # Copyright (C) 2022 Jintao Sun 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | #################################################################################################### 20 | 21 | 22 | from __future__ import annotations 23 | from typing import Any, Callable, Dict, List, Optional, Tuple, Union 24 | 25 | import re 26 | import inspect 27 | import builtins 28 | 29 | from ._hgl import HGL 30 | 31 | 32 | 33 | class Dispatcher: 34 | """Dynamic dispatch unary and binary/multi functions 35 | 36 | 1. support boardcast 37 | 2. easy access 38 | 3. *args will match binary functions 39 | 4. cache 40 | """ 41 | 42 | def __init__(self): 43 | # 'Add': [(f, (UInt, SInt), (UInt, SInt, str, int))] 44 | self._table: Dict[str,List[ 45 | Tuple[Callable, Tuple[type, ...], Tuple[type, ...]] 46 | ]] = {} 47 | self._cache = {} 48 | 49 | @property 50 | def copy(self) -> Dispatcher: 51 | ret = self.__class__() 52 | ret._table.update(self._table) 53 | return ret 54 | 55 | def update(self, other: Dispatcher): 56 | """ other prior to self 57 | """ 58 | assert isinstance(other, Dispatcher) 59 | for f_name, fs in other._table: 60 | if f_name not in self._table: 61 | self._table[f_name] = [] 62 | self._table[f_name] = fs + self._table[f_name] 63 | 64 | def dispatch(self, 65 | f_name:str, 66 | f: Callable, 67 | arg1: Union[List[type], Tuple[type], type], 68 | arg2: Union[List[type], Tuple[type], type, None] = None): 69 | """dispatch unary/binary functions 70 | 71 | Ex.: dispatch('Add', f, [UInt, SInt], [UInt, SInt, None]) 72 | dispatch('Add', f, [UInt, SInt], Any ) 73 | 74 | None means unary function 75 | Any means match all (except None) 76 | 77 | list means Union 78 | """ 79 | assert callable(f) 80 | if f_name not in self._table: 81 | self._table[f_name] = [] 82 | 83 | self._cache.clear() 84 | 85 | if not isinstance(arg1, (tuple, list)): 86 | arg1 = (arg1,) 87 | if not isinstance(arg2, (tuple, list)): 88 | arg2 = (arg2,) 89 | 90 | for i in arg1: 91 | assert i is Any or isinstance(i, type), f'{i} is not type' 92 | for i in arg2: 93 | assert i is None or i is Any or isinstance(i, type), f'{i} is not type' 94 | 95 | # (f, (UInt, SInt), (UInt, SInt)) 96 | self._table[f_name].insert(0, (f, tuple(arg1), tuple(arg2))) 97 | 98 | def type(self, obj): 99 | if isinstance(obj, HGL): 100 | return obj.__hgl_type__ 101 | else: 102 | return type(obj) 103 | 104 | def call(self, f_name:str, *args, **kwargs): 105 | """ ex. call('Add', 1, 1.2, type='fulladder') 106 | """ 107 | f = None 108 | if len(args) == 1: 109 | f = self.find(f_name, self.type(args[0]), None) 110 | elif len(args) > 1: 111 | f = self.find(f_name, self.type(args[0]), self.type(args[1])) 112 | 113 | if f is not None: 114 | #--------------------------- 115 | # if self._sess.verbose_dispatch: 116 | # types = ', '.join([arg.__class__.__name__ for arg in args]) 117 | # self._sess.print(f"@dispatcher_call {f_name}({types}) --> {self._func_name(f)}") 118 | #--------------------------- 119 | return f(*args, **kwargs) 120 | else: 121 | types = ', '.join([self.type(arg).__name__ for arg in args]) 122 | raise TypeError(f"{f_name}({types}) can not found") 123 | 124 | 125 | def find(self, f_name: str, t1: type, t2: type) -> Optional[Callable]: 126 | """ 127 | find a registered function by type of arguments 128 | return None if not find 129 | 130 | t1: type 131 | t2: type or None 132 | 133 | ex. find('Add', int, str) 134 | """ 135 | ret = self._cache.get((f_name, t1, t2)) 136 | if ret is not None: 137 | return ret 138 | 139 | else: 140 | if f_name not in self._table: 141 | return None 142 | for stored_f, stored_t1, stored_t2 in self._table[f_name]: 143 | 144 | if (Any in stored_t1 or t1 in stored_t1) and ( 145 | (t2 is not None and Any in stored_t2) or (t2 in stored_t2)): 146 | self._cache[(f_name, t1, t2)] = stored_f 147 | return stored_f 148 | 149 | return None 150 | 151 | 152 | def _show_types(self, t1: tuple, t2: tuple) -> str: 153 | t1 = ['Any' if i is Any else 'None' if i is None else i.__name__ for i in t1] 154 | t2 = ['Any' if i is Any else 'None' if i is None else i.__name__ for i in t2] 155 | return f"({'|'.join(t1)}, {'|'.join(t2)})" 156 | 157 | 158 | def _func_name(self, f: Callable) -> str: 159 | if not hasattr(f, '__qualname__'): 160 | f = type(f) 161 | module = f.__module__ 162 | name = f.__qualname__ 163 | if hasattr(f, '__hgl_wrapped__'): 164 | wrapped = getattr(f, '__hgl_wrapped__') 165 | return f"{f.__name__}({self._func_name(wrapped)})" 166 | else: 167 | ret =f"{module}.{name}" 168 | return re.sub(r'.','', ret) 169 | 170 | def __str__(self): 171 | ret = "" 172 | for name, l in self._table.items(): 173 | ret += f"{name}:\n" 174 | for f in l: 175 | ret += f" {self._func_name(f[0])}{self._show_types(f[1],f[2])}\n" 176 | return ret 177 | 178 | 179 | 180 | #------------------------ 181 | # decorators for dispatch 182 | #------------------------ 183 | 184 | # default dispatcher 185 | default_dispatcher = Dispatcher() 186 | 187 | def dispatch(f_name: str, type1 = Any, type2 = None, *, dispatcher = None): 188 | """ 189 | Ex.: @dispatch('Add', [UInt, SInt], [Any, None]) 190 | @dispatch('Add', Any, None ) 191 | """ 192 | if dispatcher is None: 193 | dispatcher = default_dispatcher 194 | def decorator(f): 195 | dispatcher.dispatch(f_name, f, type1, type2) 196 | return f 197 | return decorator 198 | 199 | 200 | 201 | 202 | def singleton(_class: type) -> object: 203 | """ return the instance of the input class 204 | """ 205 | assert inspect.isclass(_class) 206 | name = _class.__name__ 207 | obj = _class() 208 | 209 | if name[:2] == '__' and name[-2:] == '__': 210 | setattr(builtins, name, obj) 211 | return obj 212 | 213 | def register_builtins(f): 214 | if isinstance(f, str): 215 | def wrapped(_f): 216 | setattr(builtins, f, _f) 217 | return _f 218 | return wrapped 219 | else: 220 | name = f.__name__ 221 | setattr(builtins, name, f) 222 | return f 223 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search — PyHGL documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 108 | 109 |
113 | 114 |
115 |
116 |
117 |
    118 |
  • 119 | 120 |
  • 121 |
  • 122 |
123 |
124 |
125 |
126 |
127 | 128 | 135 | 136 | 137 |
138 | 139 |
140 | 141 |
142 |
143 |
144 | 145 |
146 | 147 |
148 |

© Copyright 2022, jintaos2.

149 |
150 | 151 | Built with Sphinx using a 152 | theme 153 | provided by Read the Docs. 154 | 155 | 156 |
157 |
158 |
159 |
160 |
161 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/PyHGL/1-basic/sim.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Assertoins — PyHGL documentation 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 108 | 109 |
113 | 114 |
115 |
116 |
117 | 125 |
126 |
127 |
128 |
129 | 130 |
131 |

Assertoins

132 |

TODO

133 |
134 | 135 | 136 |
137 |
138 |
142 | 143 |
144 | 145 |
146 |

© Copyright 2022, jintaos2.

147 |
148 | 149 | Built with Sphinx using a 150 | theme 151 | provided by Read the Docs. 152 | 153 | 154 |
155 |
156 |
157 |
158 |
159 | 164 | 165 | 166 | --------------------------------------------------------------------------------