Repository: Nic30/hwt Branch: master Commit: 7451759ddf15 Files: 208 Total size: 856.1 KB Directory structure: gitextract_3m5zlo_2/ ├── .circleci/ │ └── config.yml ├── .coveragerc ├── .gitignore ├── .readthedocs.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── doc/ │ ├── conf.py │ ├── gtkwave.rst │ ├── hwtCircuitNormalForm.rst │ ├── index.rst │ ├── requirements.doc.txt │ └── requirements.txt ├── hwt/ │ ├── __init__.py │ ├── code.py │ ├── code_utils.py │ ├── constants.py │ ├── constraints.py │ ├── doc_markers.py │ ├── hObjList.py │ ├── hdl/ │ │ ├── __init__.py │ │ ├── commonConstants.py │ │ ├── const.py │ │ ├── constUtils.py │ │ ├── frameTmpl.py │ │ ├── frameTmplUtils.py │ │ ├── hdlObject.py │ │ ├── operator.py │ │ ├── operatorDefs.py │ │ ├── operatorUtils.py │ │ ├── portItem.py │ │ ├── sensitivityCtx.py │ │ ├── statements/ │ │ │ ├── __init__.py │ │ │ ├── assignmentContainer.py │ │ │ ├── codeBlockContainer.py │ │ │ ├── ifContainter.py │ │ │ ├── statement.py │ │ │ ├── switchContainer.py │ │ │ └── utils/ │ │ │ ├── __init__.py │ │ │ ├── comparison.py │ │ │ ├── ioDiscovery.py │ │ │ ├── listOfHdlStatements.py │ │ │ ├── reduction.py │ │ │ └── signalCut.py │ │ ├── transPart.py │ │ ├── transTmpl.py │ │ ├── types/ │ │ │ ├── __init__.py │ │ │ ├── array.py │ │ │ ├── arrayCast.py │ │ │ ├── arrayConst.py │ │ │ ├── bitConstFunctions.py │ │ │ ├── bitConstFunctionsGetitem.py │ │ │ ├── bitConst_opReduce.py │ │ │ ├── bits.py │ │ │ ├── bitsCast.py │ │ │ ├── bitsCastUtils.py │ │ │ ├── bitsConst.py │ │ │ ├── bitsRtlSignal.py │ │ │ ├── defs.py │ │ │ ├── enum.py │ │ │ ├── enumConst.py │ │ │ ├── float.py │ │ │ ├── floatConst.py │ │ │ ├── function.py │ │ │ ├── hdlType.py │ │ │ ├── slice.py │ │ │ ├── sliceConst.py │ │ │ ├── sliceUtils.py │ │ │ ├── stream.py │ │ │ ├── streamConst.py │ │ │ ├── string.py │ │ │ ├── stringConst.py │ │ │ ├── struct.py │ │ │ ├── structCast.py │ │ │ ├── structUtils.py │ │ │ ├── structValBase.py │ │ │ ├── typeCast.py │ │ │ ├── union.py │ │ │ └── utils.py │ │ └── variables.py │ ├── hwIO.py │ ├── hwIOs/ │ │ ├── __init__.py │ │ ├── agents/ │ │ │ ├── __init__.py │ │ │ ├── bramPort.py │ │ │ ├── fifo.py │ │ │ ├── rdSync.py │ │ │ ├── rdVldSync.py │ │ │ ├── regCntrl.py │ │ │ ├── signal.py │ │ │ ├── struct.py │ │ │ ├── tuleWithCallback.py │ │ │ ├── union.py │ │ │ ├── universalComposite.py │ │ │ └── vldSync.py │ │ ├── hwIOArray.py │ │ ├── hwIODifferential.py │ │ ├── hwIOStruct.py │ │ ├── hwIOTristate.py │ │ ├── hwIOUnion.py │ │ ├── hwIO_map.py │ │ ├── signalOps.py │ │ ├── std.py │ │ ├── std_ip_defs.py │ │ └── utils.py │ ├── hwModule.py │ ├── hwParam.py │ ├── mainBases.py │ ├── math.py │ ├── mathAutoExt.py │ ├── pyUtils/ │ │ ├── __init__.py │ │ ├── arrayQuery.py │ │ ├── fileHelpers.py │ │ ├── setList.py │ │ ├── testUtils.py │ │ └── typingFuture.py │ ├── serializer/ │ │ ├── __init__.py │ │ ├── combLoopAnalyzer/ │ │ │ ├── __init__.py │ │ │ └── tarjan.py │ │ ├── exceptions.py │ │ ├── generic/ │ │ │ ├── __init__.py │ │ │ ├── constant_cache.py │ │ │ ├── indent.py │ │ │ ├── ops.py │ │ │ ├── tmpVarConstructor.py │ │ │ ├── to_hdl_ast.py │ │ │ ├── utils.py │ │ │ └── value.py │ │ ├── hwt/ │ │ │ ├── __init__.py │ │ │ ├── context.py │ │ │ ├── ops.py │ │ │ ├── serializer.py │ │ │ ├── types.py │ │ │ └── value.py │ │ ├── ip_packager.py │ │ ├── mode.py │ │ ├── resourceAnalyzer/ │ │ │ ├── __init__.py │ │ │ ├── analyzer.py │ │ │ ├── resourceTypes.py │ │ │ └── utils.py │ │ ├── serializer_config.py │ │ ├── serializer_filter.py │ │ ├── simModel/ │ │ │ ├── __init__.py │ │ │ ├── serializer.py │ │ │ ├── tmpVarConstructorConstOnly.py │ │ │ ├── types.py │ │ │ └── value.py │ │ ├── store_manager.py │ │ ├── systemC/ │ │ │ ├── __init__.py │ │ │ ├── expr.py │ │ │ ├── serializer.py │ │ │ ├── statements.py │ │ │ ├── type.py │ │ │ └── utils.py │ │ ├── utils.py │ │ ├── verilog/ │ │ │ ├── __init__.py │ │ │ ├── context.py │ │ │ ├── ops.py │ │ │ ├── serializer.py │ │ │ ├── statements.py │ │ │ ├── types.py │ │ │ ├── utils.py │ │ │ └── value.py │ │ ├── vhdl/ │ │ │ ├── __init__.py │ │ │ ├── ops.py │ │ │ ├── serializer.py │ │ │ ├── statements.py │ │ │ ├── types.py │ │ │ └── value.py │ │ └── xdc/ │ │ ├── __init__.py │ │ └── serializer.py │ ├── simulator/ │ │ ├── __init__.py │ │ ├── agentBase.py │ │ ├── agentConnector.py │ │ ├── rtlSimulator.py │ │ ├── rtlSimulatorJson.py │ │ ├── rtlSimulatorVcd.py │ │ ├── simTestCase.py │ │ └── utils.py │ ├── synth.py │ └── synthesizer/ │ ├── __init__.py │ ├── componentPath.py │ ├── dummyPlatform.py │ ├── exceptions.py │ ├── interfaceLevel/ │ │ ├── __init__.py │ │ ├── directionFns.py │ │ ├── getDefaultClkRts.py │ │ ├── hwModuleImplHelpers.py │ │ ├── implDependent.py │ │ ├── propDeclrCollector.py │ │ └── utils.py │ ├── rtlLevel/ │ │ ├── __init__.py │ │ ├── exceptions.py │ │ ├── extract_part_drivers.py │ │ ├── fill_stm_list_with_enclosure.py │ │ ├── mark_visibility_of_signals_and_check_drivers.py │ │ ├── netlist.py │ │ ├── reduce_processes.py │ │ ├── remove_unconnected_signals.py │ │ ├── rtlNetlistPass.py │ │ ├── rtlSignal.py │ │ ├── rtlSignalWalkers.py │ │ └── statements_to_HdlStmCodeBlockContainers.py │ ├── typePath.py │ └── vectorUtils.py ├── pyproject.toml ├── setup.cfg └── tests/ ├── __init__.py └── all.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2.1 orbs: python: circleci/python@3.1.0 py: nic30/python-all-in-1@0.3.1 jobs: install-test-deploy: executor: name: python/default tag: '3.13.7' resource_class: small steps: - checkout # - python/load-cache - py/install-package-git: cwd: .. url: https://github.com/Nic30/hwtLib - py/install-setup-py # - python/save-cache - py/test-and-coverage - py/deploy-pypi-on-tag workflows: main: jobs: - install-test-deploy: context: - pypi filters: tags: only: /.*/ ================================================ FILE: .coveragerc ================================================ [run] branch = True source = hwt [report] exclude_lines = pragma: no cover def __repr__ raise AssertionError raise NotImplementedError if __name__ == .__main__.: ================================================ FILE: .gitignore ================================================ *.pyc *.dill *.class # python egg folders build/ dist/ hwt.egg-info/ # eclipse project files .project .pydevproject .settings/ #log files *.jou *.log ================================================ FILE: .readthedocs.yml ================================================ version: 2 build: os: ubuntu-lts-latest tools: python: "3.12" apt_packages: - graphviz python: install: - requirements: doc/requirements.doc.txt sphinx: configuration: doc/conf.py ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Nic30 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ include README.md include MANIFEST.in ================================================ FILE: README.md ================================================ # HWToolkit (hwt), # the library for hardware development in Python [![CircleCI](https://circleci.com/gh/Nic30/hwt.svg?style=svg)](https://circleci.com/gh/Nic30/hwt) [![Coverage Status](https://coveralls.io/repos/github/Nic30/hwt/badge.svg?branch=master)](https://coveralls.io/github/Nic30/hwt?branch=master) [![PyPI version](https://badge.fury.io/py/hwt.svg)](https://badge.fury.io/py/hwt) [![Documentation Status](https://readthedocs.org/projects/hwtoolkit/badge/?version=latest)](https://hwtoolkit.readthedocs.io/en/latest/?badge=latest) [![](https://img.shields.io/github/license/Nic30/hwt.svg)](https://github.com/Nic30/hwt) [![Python version](https://img.shields.io/pypi/pyversions/hwt.svg)](https://img.shields.io/pypi/pyversions/hwt.svg) [![Join the chat at https://gitter.im/hwt-community/community](https://badges.gitter.im/hwt-community/community.svg)](https://gitter.im/hwt-community/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Keywords * Metaprogramming (Hardware Construction Language HCL, templatization) + HLS. * Simulator API, UVM * Buildtool, IP core generator ## How HWT can help you? * The lower layer (IR, HDL serializers) is a shield against a problems related to VHDL/Verilog. It is checking for correctness and synthetisability and removing specific of HDLs. * The system level and HLS layer allows you to quickly build desing generators with advance optimisation techniques of your choice. * Simulator API and it's UVM simulation environment is just python object with C++ binding. This makes it easy to use while not sacrificing performance. * Rich type system can describe also data locality and packet features. This significantly simplifies configuration of component which are working with packets or any data over remote bus. * HWT is not compiler nor transpiler but it is actually a core library. It contains only necessary stuff and you can can modify/extend any part any time. Because the word of HW developement is always full of unexpected situations. ## Features * Hardware Construction Language (HCL) (example [simple](https://github.com/Nic30/hwtLib/blob/master/hwtLib/examples/simple.py), [showcase](https://github.com/Nic30/hwtLib/blob/master/hwtLib/examples/showcase0.py)). It is somewhere between HLS and HDL. It offers HLS style of coding but at the same time it allows you to manipulate HDL objects. This means it is a little bit slower to write a prototype than you would in HLS, but you always know what, how and why is happening. * Digital circuit simulator with UVM like verification environment (example usage [CAM](https://github.com/Nic30/hwtLib/blob/master/hwtLib/mem/cam_test.py), [structWriter_test.py](https://github.com/Nic30/hwtLib/blob/master/hwtLib/structManipulators/structWriter_test.py)) * Tools for static analysis ([resourceAnalyzer](https://github.com/Nic30/hwt/blob/master/hwt/serializer/resourceAnalyzer/analyzer.py), example usage [cntr_test.py](https://github.com/Nic30/hwtLib/blob/master/hwtLib/examples/arithmetic/cntr_test.py)) * Serializers to export HWT designs into multiple target HDLs ([verilog, VHDL, system-c, IP-core packager, hwt itself...](https://github.com/Nic30/hwt/tree/master/hwt/serializer)) HWT uses hilevel-netlists for internal representation of target design. Optimized netlists are generated from usual code statements, function calls, statements etc (hw processes are automatically resolved). This netlist is easy to use and easy to modify or analyse by user if there is something missing in main library. Also [serialization modes](https://github.com/Nic30/hwt/blob/master/hwt/serializer/mode.py) allows to tweaks how component should behave during serialization. HWT performs no HLS planing or schedueling. HWT is also good as API for code generating by more advanced tools. Hierarchy of components/interfaces/types is not limited. User specifed names are checked for collision with target language. HWT designs are objects. No specific compiler execution is required, just run `python3`, import the thing and use `to_rtl` metod or other (take a look at [examples](https://github.com/Nic30/hwtLib/blob/master/hwtLib/)). ## HWT ecosystem ![hwt_ecosystem_packages.png](doc/_static/hwt_ecosystem_packages.png) ## Installation This library is a regular python package. You can install it using: ``` # system-wide, use -u for local use only sudo pip3 install hwt # or directly from git pip3 install --upgrade --force-reinstall --no-cache-dir -r https://raw.githubusercontent.com/Nic30/hwt/master/doc/requirements.txt git+https://github.com/Nic30/hwt.git@master#egg=hwt ``` Then you are able to use functions and classes defined in the hwt library from a python console or script. Installation of [hwtLib](https://github.com/Nic30/hwtLib) is recomended as it contains common interfaces, agents, components etc... ## FAQ * Where is the entry point of the compiler? * This is not a compiler, it is library of the objects which can be converted to Verilog/VHDL and back. * How do I get Verilog/VHDL? * Use `to_rtl` method [example](https://github.com/Nic30/hwtLib/blob/master/hwtLib/examples/simple.py) * How do I define my interface type, protocol and simulation agent? * Derive from any Interface class. [example](https://github.com/Nic30/hwt/blob/master/hwt/interfaces/std.py#L107) * I do have c structure of UDP header, how do I send/receive UDP packet over AXI-stream interface? * Define HStruct type composed of eth_header_t, IPv4_header_t and HStream(uint8_t) and use [AxisFrameGen](https://github.com/Nic30/hwtLib/blob/master/hwtLib/amba/axis_comp/frameGen.py). There is and example of [ping responder](https://github.com/Nic30/hwtLib/blob/master/hwtLib/examples/builders/pingResponder.py) ## Similar projects * [autofpga](https://github.com/ZipCPU/autofpga) - C++, A utility for Composing FPGA designs from Peripherals * :skull: [baremetal](https://github.com/dawsonjon/baremetal) - Python, simple HCL * [BinPy](https://github.com/BinPy/BinPy) - Python, An electronic simulation library * :skull: [pervognsen/Bitwise](https://github.com/pervognsen/bitwise) - Python, HDL which translates python directly * :skull: [jamesjiang52/Bitwise](https://github.com/jamesjiang52/Bitwise) - Python, simple HCL. * [blarney](https://github.com/blarney-lang/blarney) - Haskell, HCL * [bsc](https://github.com/B-Lang-org/bsc) - Haskell, C++, BSV - Bluespec Compiler * [Cement HDL/CmtHDL](https://github.com/pku-liang/Cement) - Rust, eHDL * [chisel](https://chisel.eecs.berkeley.edu/) - 2012-?, Scala, HCL * [Chips-2.0](https://github.com/dawsonjon/Chips-2.0) - , , FPGA Design Suite based on C to Verilog design flow * [circt](https://github.com/llvm/circt) - 2020-?, C++/LLVM, compiler infrastructure * [circuitgraph](https://github.com/circuitgraph/circuitgraph) - Tools for working with circuits as graphs in python * [concat](https://github.com/conal/concat) - 2016-?, Haskell, Haskell to hardware * [DUH](https://github.com/sifive/duh) - JS, simple convertor between verilog/scala/ipxact * [DFiant](https://github.com/DFiantHDL/DFiant) 2019-?, Scala, dataflow based HDL * [edalize](https://github.com/olofk/edalize) - 2018-?, Python, abstraction layer for eda tools * [garnet](https://github.com/StanfordAHA/garnet) -2018-?, Python, Coarse-Grained Reconfigurable Architecture generator based on magma * [hammer](https://github.com/ucb-bar/hammer) - 2017-?, Python, Highly Agile Masks Made Effortlessly from RTL * [heterocl](https://github.com/cornell-zhang/heterocl) - 2017-?, C++, A Multi-Paradigm Programming Infrastructure for Software-Defined Reconfigurable Computing * [hoodlum](https://github.com/tcr/hoodlum) - 2016-?, Rust, HCL * [ILAng](https://github.com/Bo-Yuan-Huang/ILAng) - modeling and verification platform for SoCs where Instruction-Level Abstraction (ILA) is used as the formal model for hardware components. * :skull: [jhdl](https://github.com/larsjoost/jhdl) - ?-2017, C++ Verilog/VHDL -> systemC, prototype * [Kactus2](http://funbase.cs.tut.fi) - IP-core packager * [kratos](https://github.com/Kuree/kratos) - C++/Python, hardware generator/simulator * [lgraph](https://github.com/masc-ucsc/lgraph) - C, generic graph library * [llhd](https://github.com/fabianschuiki/llhd) - Rust, HCL * [livehd](https://github.com/masc-ucsc/livehd) - mainly C++, An infrastructure designed for Live Hardware Development. * [Lucid HDL in Alchitry-Labs](https://github.com/alchitry/Alchitry-Labs) - Custom language and IDE inspired by Verilog * [magma](https://github.com/phanrahan/magma/) - 2017-?, Python, HCL * [amaranth](https://github.com/amaranth-lang/amaranth)/[migen](https://github.com/m-labs/migen) - 2013-?, Python, HCL * [mockturtle](https://github.com/lsils/mockturtle) - logic network library * [moore](https://github.com/fabianschuiki/moore) - Rust, HDL -> model compiler * [msdsl](https://github.com/sgherbst/msdsl) - Python, real number model -> verilog * [MyHDL](https://github.com/myhdl/myhdl) - 2004-?, Python, Process based HDL * [Amaranth HDL](https://github.com/amaranth-lang/amaranth) -, Python, (previously nMigen) A refreshed Python toolbox for building complex digital hardware * [OpenTimer](https://github.com/OpenTimer/OpenTimer) - , C++, A High-Performance Timing Analysis Tool for VLSI Systems * [percy](https://github.com/whaaswijk/percy) - Collection of different synthesizers and exact synthesis methods for use in applications such as circuit resynthesis and design exploration. * [PyChip-py-hcl](https://github.com/scutdig/PyChip-py-hcl) - , Python, Chisel3 like HCL * [pygears](https://github.com/bogdanvuk/pygears) - , Python, function style HDL generator * [PyMTL3](https://github.com/cornell-brg/pymtl3) 2018-? * [PyMTL](https://github.com/cornell-brg/pymtl) - 2014-?, Python, Process based HDL * [PipelineC](https://github.com/JulianKemmerer/PipelineC) - 2018-?, Python, C++ HLS-like automatic pipelining as a language construct/compiler * [PyRTL](https://github.com/UCSBarchlab/PyRTL) - 2015-?, Python, HCL * [Pyverilog](https://github.com/PyHDI/Pyverilog) - 2013-? Python-based Hardware Design Processing Toolkit for Verilog HDL * [rogue](https://github.com/slaclab/rogue) , C++/Python - Hardware Abstraction & Data Acquisition System * [rohd](https://github.com/intel/rohd), 2023-?, dart, HCL * [sail](https://github.com/rems-project/sail) 2018-?, (OCaml, Standard ML, Isabelle) - architecture definition language * :skull: [SFGen](https://github.com/dillonhuff/SFGen) - Python, arithmetic function generator * [spatial](https://github.com/stanford-ppl/spatial) - Scala, an Argon DSL like, high level abstraction * [SpinalHDL](https://github.com/SpinalHDL/SpinalHDL) - 2015-?, Scala, HCL * [Silice](https://github.com/sylefeb/Silice) - ?, C++, Custom HDL * :skull: [SyDpy](https://github.com/bogdanvuk/sydpy) - ?-2016, Python, HCL and verif. framework operating on TML/RTL level * [systemrdl-compiler](https://github.com/SystemRDL/systemrdl-compiler) - Python,c++, register description language compiler * [UHDM](https://github.com/alainmarcel/UHDM) - C++ SystemVerilog -> C++ model * :skull: [Verilog.jl](https://github.com/interplanetary-robot/Verilog.jl) - 2017-2017, Julia, simple Julia to Verilog transpiler * [veriloggen](https://github.com/PyHDI/veriloggen) - 2015-?, Python, Verilog centric HCL with HLS like features * :skull: [wyre](https://github.com/nickmqb/wyre) - 2020-2020, Mupad, Minimalistic HDL * [phi](https://github.com/donn/Phi) - 2019-?, custom language, llvm based compiler of custom hdl * [prga](https://github.com/PrincetonUniversity/prga) - 2019-?. Python, prototyping platform with integrated yosys * [Hardcaml](https://github.com/janestreet/hardcaml) - OCaml, HCL * [magia-hdl](https://github.com/magia-hdl/magia) - 2023-?, Python, HCL * [Metron](https://github.com/aappleby/Metron) - C++, C++ -> SystemVerilog syntax translator * [librelane](https://github.com/librelane/librelane) - python, ASIC implementation flow infrastructure ### Dictionary * IR - Internal Representation * HDL - Hardware Design Languge (Lang. construct maps directly to specific HW part) * eHDL - Embedded HDL (HDL construct avare source code generator) * HCL - Hardware Construction Language (User code constructs IR. IR can be directly transipled to HDL) * HLS - High Level Synthesis (User code is translated to IR. IR is compiled to HDL IR in multiple complex steps, typically contains scheduling, arch. mapping etc.) ## Related open-source * [fusesoc](https://github.com/olofk/fusesoc) - package manager and a set of build tools for FPGA/ASIC development * [OpenSTA](https://github.com/abk-openroad/OpenSTA) - a gate level static timing verifier * [RePlAce](https://github.com/abk-openroad/RePlAce) - global placement tool * [verilator](https://www.veripool.org/wiki/verilator) - Verilog -> C/C++ simulator * [vtr-verilog-to-routing](https://github.com/verilog-to-routing/vtr-verilog-to-routing) * [yosys](https://github.com/YosysHQ/yosys) - RTL synthesis framework * [UHDM](https://github.com/alainmarcel/UHDM) - SV -> C++ ## Board support libraries (Potential candidates for public integration) * [litex](https://github.com/enjoy-digital/litex) - Buildsystem for migen * [loam](https://github.com/phanrahan/loam) - Buildsystem for magma * [vivado-boards](https://github.com/Digilent/vivado-boards) - Vivado XML/TCL files with board description * [nmigen-boards](https://github.com/nmigen/nmigen-boards) - board and connector meta fo nmigen ## Sources of informations in this area * [computer-engineering-resources](https://github.com/rajesh-s/computer-engineering-resources) - list of conferences and hardware projects ================================================ FILE: doc/conf.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # from datetime import datetime import os import re import sys from sphinx.ext.apidoc import main as apidoc_main import sphinx_bootstrap_theme sys.path.insert(0, os.path.abspath('../')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.viewcode', # 'sphinx.ext.napoleon', 'sphinx.ext.graphviz', 'sphinx.ext.intersphinx', ] intersphinx_mapping = { 'ipCorePackager': ("https://ipcorepackager.readthedocs.io/en/latest/", None), } # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'hwt' copyright = '2017-%d, Michal Orsak' % datetime.now().year author = 'Michal Orsak' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = 'latest' # The full version, including alpha/beta/rc tags. release = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # # html_theme = 'alabaster' html_theme = 'bootstrap' html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'hwtdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'hwt.tex', 'HWToolkit generated documentation', 'Michal Orsak', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'hwt', 'HWToolikt Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'hwt', 'HWToolikt Documentation', author, 'hwt', 'Collection of tools for hardware generation.', 'Miscellaneous'), ] # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] notskipregex = re.compile("_[^_]+") def skip(app, what, name, obj, skip, options): if name == "__init__" or notskipregex.match(name): return False return skip def setup(app): app.connect("autodoc-skip-member", skip) # update *.rst pages apidoc_main(["--module-first", "--full", "--output-dir", "../doc", "../hwt"]) ================================================ FILE: doc/gtkwave.rst ================================================ GTKWave useful tweaks ====================== .. code-block:: text echo splash_disable 1 >> ~/.gtkwaverc ================================================ FILE: doc/hwtCircuitNormalForm.rst ================================================ HWT Circuit normal form (hwtCircuitNF) ====================================== HWT Circuit normal form (hwtCircuitNF) is a format of the netlist which is meant as a most simple and compact format of netlist for circuit analysis. It is supposed to be human and programmatically readable. hwtCircuitNF is not dependent on HWT and it is rather a specification of the code-style in Verilog/VHDL. It specifies how statements and signals should be used in order to reduce number of possible descriptions of the same thing. E.g. a multiplexer can be described using assignment with indexed/conditional expr. and also using switch/if statement among other possibilities. A typical synthesis tool converts all this to a gates and it performs the further code analysis there. However if we want to perform circuit analysis on statement level we have to check every possible syntax. By performing code analysis and optimization in place on syntax level on normalized circuit we can gain these advantages: * No need for further conversions with nice object traceability. * All optimizations would be nearly human readable in every step. * There is probably less objects in the circuit which would also improve the performance of the compiler. However there are also downsides of this approach: * Object have a complex internal structure which could cancel out performance gains. * The properties of hwtCircuitNF have to be maintained during the updates which add another performance hits. * For an efficient implementation of analysis the object have to hwtCircuitNF rules ------------------ (A definition of common netlist in EDIF/VHDL/SystemVerilog) The netlist described in hwtCircuitNF is a set of components, statements and signals connecting them. The signal can not cross the boundary of the component, instead it has to be connected to IO of component to achieve this functionality. The components can be nested, each component has only a single parent, same applies to statements. Statement can not have nested component. (A hwtCircuitNF addition) The signal is driven from component IO or from statement. The signal endpoint is a component IO or statement which is using this signal to drive something else. Each signal has to drive something and has to be driven by something. This implies: * hwtCircuitNF does not contain unconnected statements/components. * Each signal is somehow connected to some output of the top. In addition there are several additional rules about statement syntax and signal usage. Multiplexer coding style ^^^^^^^^^^^^^^^^^^^^^^^^ Rationale: To avoid expression analysis during detection of enclosures, muxes and latches. no .. code-block:: verilog x <= y[(i + 1)*8 - 1: i*8]; no .. code-block:: verilog x <= i == 0 ? y[8-1:0] : y[16-1:8]; yes .. code-block:: verilog if (i == 0) x <= y[ 8 - 1: 0] else if (i == 1) x <= y[16 - 1: 8] // ... yes, prefered .. code-block:: verilog case (i) 1'b0: x <= y[ 8-1:0]; 1'b1: x <= y[16-1:8]; Note that the instanciation of the MUX as a component is not a MUX description. Const indexed assignment vs assignment of concatenation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Rationale: Merge all drivers of signal in to a single statement. no .. code-block:: verilog s[1] <= y; s[0] <= x; yes - all assignment with a constant part specification converted to an assignment of concatenation of all parts .. code-block:: verilog s <= {y, x}; yes - there is only a single part and this is actually a cast .. code-block:: verilog wire s[0:0]; s[0] <= y; Const indexed drives of disjunctive parts of same signal ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Rationale: Split independent parts of signal if possible. no .. code-block:: verilog if (c0) s[0] <= x; if (c1) s[1] <= y; yes - all separately driven parts of signal extracted as a signal and the original signal is driven from its parts .. code-block:: verilog wire s_0_tmp; wire s_1_tmp; assign s <= {s_1_tmp, s_0_tmp}; if (c0) s_0_tmp <= x; if (c1) s_1_tmp <= y; yes - the part specifier is not a constant expression .. code-block:: verilog if (c0) s[i][0] <= x; if (c1) s[i][1] <= y; Enclosure filed ^^^^^^^^^^^^^^^ Rationale: To be able to analyze any statement without the need for an information from its parent. no .. code-block:: verilog s <= x0; if (c0) if (c1) begin s <= x1; end else if (c2) s <= x2; yes - enclosure filled .. code-block:: verilog if (c0) if (c1) begin s <= x1; end else begin s <= x0; end else if (c2) s <= x2; else s <= x0; Assignment to concatenation ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Rationale: To have assignments with a single output and to be able to separate them from statements easily. no .. code-block:: verilog {a, b} <= {c, d}; yes - assignment only to single destination .. code-block:: verilog a <= c; b <= d; Concatenations always on top of expression tree ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Rationale: We need to move indexing operators under the concatenations in order to make structural hashing as efficient as possible. We need to do this also for other bitwise operators and not just for index operators. no: The indexing is at the top of operator tree and the concatenation is at bottom. .. code-block:: verilog wire[4-1:0] b; wire c; ~({a, b, c}[2-1:0]) yes: The indexing is at the bottom of operator tree and concatenation is on top. .. code-block:: verilog {~b[1-1:0], ~c} ================================================ FILE: doc/index.rst ================================================ Welcome to hwt (HWToolkit) generated documentation! =================================================== This documentation is automatically generated from actual source code. What is hwt (HWToolkit) ----------------------- * Is a Hardware Construction Framework. It is similar to a HCLs like Chisel3 but it is rather set of object with a normal behavior rather than a new language. The objects represents signals and other RTL components and can be serialized to Verilog/VHDL/SystemC/... (That means the hwt is not transpiler or compiler and there is also no entry point.) * HWT internal representation is graph database of statements and signals. Hwt type system is made of several elemental datatypes templates which can be used for a type of signal: * HBits - represents bit vector * HStruct - structure/record datatype * HUnion - union datatype * HArray - array datatype * TransTmpl metatype - transactional template which describes how data can be partitioned to memores, buses, streams etc. Think of it like a object which tells how to build frames from potentially sparse data structure in memory. * The type itself drives it's hw representation, that means the typesystem is user extensible. * HWT is build as abstraction layer over all HDL languages to shield users from tricky features of such a languages. However nothings is sacrificed and everything can be overridden. * HWT uses UVM like verification environment implemented in hwtSimApi. hwtSimApi can use verilator or python based simulator to achieve high-speed simulation or nearly-zero simulator spin-up. * Most of parts of HWT ecosystem are independent and you can use them separately. Other useful libraries ----------------------- .. image:: _static/hwt_ecosystem_packages.png Where to start -------------- Tutorial is in hwtLib.examples.*, every file in this module contains user-entry-level comments. E.g. hwtLib.examples.simple.SimpleHwModule is a good starting point. Component in HWT is a class which inherits from HwModule class. Object of such a class can be converted to a vhdl/Verilog by from hwt.synth.to_rtl function. That said, this library is regular python library without any non-pyhon dependencies, it does not have any executable file. You can also download this doc in `PDF `_. .. toctree:: :maxdepth: 4 :caption: Contents: hwt Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ================================================ FILE: doc/requirements.doc.txt ================================================ -r requirements.txt sphinx_bootstrap_theme ================================================ FILE: doc/requirements.txt ================================================ git+https://github.com/Nic30/pyMathBitPrecise.git@master#egg=pyMathBitPrecise git+https://github.com/Nic30/pyDigitalWaveTools.git@master#egg=pyDigitalWaveTools git+https://github.com/Nic30/hdlConvertorAst.git@master#egg=hdlConvertorAst git+https://github.com/Nic30/ipCorePackager.git@master#egg=ipCorePackager git+https://github.com/Nic30/hwtSimApi.git@master#egg=hwtSimApi ================================================ FILE: hwt/__init__.py ================================================ ================================================ FILE: hwt/code.py ================================================ from operator import and_, or_, xor, add, eq from types import GeneratorType from typing import Union, Sequence, Optional, Tuple from hdlConvertorAst.to.hdlUtils import iter_with_last from hwt.code_utils import _mkOp, _HwIOToRtlSignal from hwt.hdl.const import HConst from hwt.hdl.operatorDefs import concatFn from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.statement import HwtSyntaxError, HdlStatement from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.hdl.types.bits import HBits from hwt.hdl.types.enum import HEnum from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import HwIOBase, HwModuleBase from hwt.mainBases import RtlSignalBase from hwt.math import log2ceil from hwt.pyUtils.arrayQuery import arr_any from hwt.synthesizer.rtlLevel.rtlSignalWalkers import \ discoverEventDependency class CodeBlock(HdlStmCodeBlockContainer): """ Container for list of statements """ def __init__(self, *statements: Sequence[HdlStatement]): super(CodeBlock, self).__init__() self._register_stements(statements, self.statements) self.rank = sum(map(lambda s: s.rank, statements)) if self._outputs: ctx = self._get_rtl_context() ctx.statements.add(self) class If(IfContainer): """ If statement generator """ def __init__(self, cond: Union[RtlSignalBase, HwIOBase], *statements: Sequence[HdlStatement]): """ :param cond: condition in if statement :param statements: list of statements which should be active if condition is met """ cond_sig = _HwIOToRtlSignal(cond) if not isinstance(cond_sig, RtlSignalBase): raise HwtSyntaxError("Condition is not signal, it is not certain" " if this is an error or desire ", cond_sig) assert cond_sig._dtype.bit_length() == 1, cond_sig super(If, self).__init__(cond_sig) self.rank = 1 self._inputs.append(cond_sig) cond_sig._rtlEndpoints.append(self) ev_dep = arr_any(discoverEventDependency(cond_sig), lambda x: True) self._event_dependent_from_branch = 0 if ev_dep else None self._register_stements(statements, self.ifTrue) self._get_rtl_context().statements.add(self) def Elif(self, cond: Union[RtlSignalBase, HwIOBase], *statements: Sequence[HdlStatement]): assert self.parentStm is None self.rank += 1 cond_sig = _HwIOToRtlSignal(cond) assert cond_sig._dtype.bit_length() == 1, cond_sig ev_dep = arr_any(discoverEventDependency(cond_sig), lambda x: True) self._event_dependent_from_branch = len(self.elIfs) + 1 if ev_dep else None self._inputs.append(cond_sig) cond_sig._rtlEndpoints.append(self) stms = ListOfHdlStatement() self.elIfs.append((cond_sig, stms)) self._register_stements(statements, stms) return self def Else(self, *statements: Sequence[HdlStatement]): assert self.parentStm is None if self.ifFalse is not None: raise HwtSyntaxError( "Else on this if-then-else statement was already used") self.rank += 1 self.ifFalse = ListOfHdlStatement() self._register_stements(statements, self.ifFalse) return self class Switch(SwitchContainer): """ Switch statement generator """ def __init__(self, switchOn: Union[RtlSignalBase, HwIOBase]): switchOn = _HwIOToRtlSignal(switchOn) if not isinstance(switchOn, RtlSignalBase): raise HwtSyntaxError("Select is not signal, it is not certain" " if this is an error or desire") if arr_any(discoverEventDependency(switchOn), lambda x: True): raise HwtSyntaxError("Can not switch on result of event operator") super(Switch, self).__init__(switchOn, []) switchOn._rtlCtx.statements.add(self) self._inputs.append(switchOn) switchOn._rtlEndpoints.append(self) def add_cases(self, tupesValStms: Sequence[Tuple[Union[HConst, int], Sequence[HdlStatement]]]): """ Add multiple case statements from iterable of tuples (caseVal, statements) """ s = self for val, statements in tupesValStms: s = s.Case(val, statements) return s def Case(self, caseVal: Union[HConst, int], *statements: Sequence[HdlStatement]): "c-like case of switch statement" assert self.parentStm is None caseVal = toHVal(caseVal, self.switchOn._dtype) assert isinstance(caseVal, HConst), caseVal assert caseVal._is_full_valid(), "Cmp with invalid value" assert caseVal not in self._case_value_index, ( "Switch statement already has case for value ", caseVal) self.rank += 1 stms = ListOfHdlStatement() self._case_value_index[caseVal] = len(self.cases) self.cases.append((caseVal, stms)) self._register_stements(statements, stms) return self def Default(self, *statements: Sequence[HdlStatement]): """c-like default of switch statement """ assert self.parentStm is None self.rank += 1 self.default = ListOfHdlStatement() self._register_stements(statements, self.default) return self def SwitchLogic(cases: Sequence[Tuple[Union[RtlSignalBase, HwIOBase, HConst, bool], Sequence[HdlStatement]]], default: Optional[Sequence[HdlStatement]]=None): """ Generate if tree for cases like (syntax sugar for large generated elifs) ..code-block:: python if cond0: statements0 elif cond1: statements1 else: default :param case: iterable of tuples (condition, statements) :param default: default statements """ assigTop = None hasElse = False for last, (cond, statements) in iter_with_last(cases): if isinstance(cond, (RtlSignalBase, HwIOBase)): if assigTop is None: assigTop = If(cond, statements ) else: assigTop = assigTop.Elif(cond, statements) else: if cond: if assigTop is None: assigTop = statements else: assigTop.Else(statements) hasElse = True if last or isinstance(cases, GeneratorType): # allow True as a condition for default break raise HwtSyntaxError("Condition is not a signal, it is not certain" " if this is an error or desire ", cond, cases) if assigTop is None: if default is None: return [] else: return default else: if hasElse: return assigTop elif default is not None: assigTop = assigTop.Else(default) return assigTop def In(sigOrConst: Union[RtlSignalBase, HwIOBase, HConst], iterable: Sequence[Union[RtlSignalBase, HwIOBase, HConst]]): """ HDL convertible "in" operator, check if any of items in "iterable" equals "sigOrConst" """ res = None for i in iterable: i = toHVal(i) if res is None: res = sigOrConst._eq(i) else: res = res | sigOrConst._eq(i) assert res is not None, "argument iterable is empty" return res def StaticForEach(parentModule: HwModuleBase, items, bodyFn, name=""): """ Generate for loop for static items :param parentModule: HwModule where this code should be instantiated :param items: items which this "for" iterating on :param bodyFn: function which fn(item, index) or fn(item) returns (statementList, ack). It's content is performed in every iteration. When ack is high loop will fall to next iteration """ items = list(items) itemsCnt = len(items) if itemsCnt == 0: # if there are no items there is nothing to generate return [] elif itemsCnt == 1: # if there is only one item do not generate counter logic generate return bodyFn(items[0], 0) else: # if there is multiple items we have to generate counter logic index = parentModule._reg(name + "for_index", HBits(log2ceil(itemsCnt + 1), signed=False), def_val=0) ackSig = parentModule._sig(name + "for_ack") statementLists = [] for i, (statementList, ack) in [(i, bodyFn(item, i)) for i, item in enumerate(items)]: statementLists.append(statementList + [(ackSig(ack)), ]) If(ackSig, If(index._eq(itemsCnt - 1), index(0) ).Else( index(index + 1) ) ) return Switch(index)\ .add_cases( enumerate(statementLists) ).Default( bodyFn(items[0], 0)[0], ackSig(True) ) class FsmBuilder(Switch): """ A syntax sugar which automatically construct the state transition switch and state register :ivar ~.stateReg: register with state """ def __init__(self, parentModule: HwModuleBase, stateT, stateRegName="st"): """ :param parentModule: parent HwModule where FSM should be builded :param stateT: enum type of state :param stateRegName: name of register where sate is stored """ if isinstance(stateT, HEnum): beginVal = stateT.from_py(stateT._allValues[0]) else: beginVal = 0 self.stateReg = parentModule._reg(stateRegName, stateT, beginVal) Switch.__init__(self, self.stateReg) def Trans(self, stateFrom, *condAndNextState): """ :param stateFrom: apply when FSM is in this state :param condAndNextState: tuples (condition, newState), last does not to have condition :attention: transitions has priority, first has the biggest :attention: if stateFrom is None it is evaluated as default """ top = [] last = True for cAndS in reversed(condAndNextState): if last is True: last = False # if this is last trans. it does not have to condition try: condition, newvalue = cAndS except TypeError: top = self.stateReg(cAndS) continue top = [] else: condition, newvalue = cAndS # building decision tree top = \ If(condition, self.stateReg(newvalue) ).Else( top ) if stateFrom is None: return Switch.Default(self, top) else: return Switch.Case(self, stateFrom, top) def Default(self, *condAndNextState): d = self.Trans(None, *condAndNextState) d.stateReg = self.stateReg return d # variadic operator functions And = _mkOp(and_) Add = _mkOp(add) Or = _mkOp(or_) Xor = _mkOp(xor) # :note: xor is bitwise != Xnor = _mkOp(eq) # :note: xnor is bitwise == Concat = _mkOp(concatFn) def ror(sig:Union[RtlSignalBase, HConst], howMany: int) -> RtlSignalBase: "Rotate right" if sig._dtype.bit_length() == 1: return sig if isinstance(howMany, int): return sig[howMany:]._concat(sig[:howMany]) elif isinstance(howMany, HConst): return ror(sig, int(howMany)) else: t = howMany._dtype if not isinstance(t, HBits) or t.signed: raise NotImplementedError(t) res = sig for i in range(1, t.domain_size() - 1): res = howMany._eq(i)._ternary(ror(sig, i), res) return res def rol(sig:Union[RtlSignalBase, HConst], howMany:Union[RtlSignalBase, int]) -> RtlSignalBase: "Rotate left" if isinstance(howMany, int): width = sig._dtype.bit_length() if width == 1: return sig return sig[(width - howMany):]._concat(sig[:(width - howMany)]) elif isinstance(howMany, HConst): return rol(sig, int(howMany)) else: t = howMany._dtype if not isinstance(t, HBits) or t.signed: raise NotImplementedError(t) res = sig for i in range(1, t.domain_size() - 1): res = howMany._eq(i)._ternary(rol(sig, i), res) return res def replicate(n:int, v:Union[RtlSignalBase, HConst]): assert n > 0, n return Concat(*(v for _ in range(n))) def segment_get(n:Union[RtlSignalBase, HConst], segmentWidth:int, segmentIndex:Union[RtlSignalBase, HConst, int]): """ This function gets bits from bit vector as if it was an array of items of "segmentWidth" bits """ return n[segmentWidth * (segmentIndex + 1): segmentWidth * segmentIndex] def split_to_segments(n:Union[RtlSignalBase, HConst], maxSegmentWidth:int, allowLastToBeSmaller=False, extendLast=False): """ Split bit vector to a segments of up to maxSegmentWidth bits, lower bits first. """ offset = 0 segments = [] width = n._dtype.bit_length() if not allowLastToBeSmaller and not extendLast: assert width % maxSegmentWidth == 0, (width, maxSegmentWidth) while True: end = min(offset + maxSegmentWidth, width) segments.append(n[end:offset]) if end == width: if extendLast and end != offset + maxSegmentWidth: segments[-1] = segments[-1]._ext(maxSegmentWidth) break offset = end return segments ================================================ FILE: hwt/code_utils.py ================================================ from typing import Union from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.defs import BIT from hwt.mainBases import HwIOBase from hwt.mainBases import RtlSignalBase from ipCorePackager.constants import DIRECTION def rename_signal(hwModule: "HwModule", sig: Union[RtlSignalBase, int, bool], name: str): """ Wrap signal or value in signal of specified name :attention: output signal is driven by new signal of a specified name this means that the assigning to a new signal does not drive a original signal """ if isinstance(sig, (int, bool)): t = BIT else: t = sig._dtype if isinstance(sig, (HConst, int, bool)): s = hwModule._sig(name, t, def_val=sig, nop_val=sig) else: s = hwModule._sig(name, t) s(sig) return s def connect_optional(src: HwIOBase, dst: HwIOBase, check_fn=lambda hwIO0, hwIO1: (True, [])): """ Connect interfaces and ignore all missing things :param check_fn: filter function(hwIO0, hwIO1) which check if interfaces should be connected returns tuple (do_check, extra_connection_list) """ return list(_connect_optional(src, dst, check_fn, False)) @internal def _connect_optional(src: HwIOBase, dst: HwIOBase, check_fn, dir_reverse): do_connect, extra_connections = check_fn(src, dst) yield from extra_connections if not do_connect: return if not src._hwIOs: assert not dst._hwIOs, (src, dst) if dir_reverse: yield src(dst) else: yield dst(src) for _s in src._hwIOs: _d = getattr(dst, _s._name, None) if _d is None: # if the interfaces does not have subinterface of same name continue if _d._masterDir == DIRECTION.IN: rev = not dir_reverse else: rev = dir_reverse yield from _connect_optional(_s, _d, check_fn, rev) @internal def _HwIOToRtlSignal(obj): if isinstance(obj, HwIOBase): return obj._sig else: return obj @internal def _mkOp(fn): """ Function to create variadic operator function :param fn: function to perform binary operation """ def op(*operands, key=None) -> RtlSignalBase: """ :param operands: variadic parameter of input uperands :param key: optional function applied on every operand before processing """ assert operands, operands top = None if key is not None: operands = map(key, operands) for s in operands: if top is None: top = s else: top = fn(top, s) return top return op ================================================ FILE: hwt/constants.py ================================================ """ Commonly used constants during HW development. """ # import constants from other packages to have them on one place from ipCorePackager.constants import INTF_DIRECTION, DIRECTION from hwtSimApi.constants import Time, CLK_PERIOD READ = "READ" WRITE = "WRITE" READ_WRITE = "RW" NOP = "NOP" class NOT_SPECIFIED(): """ Constant which means that the thing is not specified Used for optional arguments as a value which marks that the value of this argument was not specified on the place where we can not just use None """ def __init__(self): raise AssertionError("Use only a class a constant") ================================================ FILE: hwt/constraints.py ================================================ """ This module contains the objects to store hardware constraints. Hardware constrains are usually stored in XDC/UCF files and they specify somethings which can not be described using HDL (SystemVerilog/VHDL) like relation between clock. Placement of component if FPGA etc. """ from copy import copy from typing import Union, Tuple from hwt.hwIO import HwIO from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from hwt.hwModule import HwModule from hwt.synthesizer.componentPath import ComponentPath class iHdlConstrain(): def _get_parent(self) -> HwModule: raise NotImplementedError(self) def _copy_with_root_upadate(self, old_path_prefix, new_path_prefix): raise NotImplementedError() def register_on_parent(self): self._get_parent()._constraints.append(self) def _get_parent_HwModule(path: Tuple[Union[HwModule, HwIO, RtlSignal, iHdlConstrain], ...]) -> HwModule: """ Search parent :class:`hwt.hwModule.HwModule` instance in path """ if isinstance(path, iHdlConstrain): return path._get_parent() for o in reversed(path): if isinstance(o, HwModule): return o raise AssertionError("No parent HwModule in path", path) def _get_absolute_path(obj) -> Union[Tuple[Union[HwModule, HwIO, RtlSignal, iHdlConstrain], ...], None]: """ Get tuple containing a path of objects from top to this object """ if obj is None: return None elif isinstance(obj, iHdlConstrain): return obj return ComponentPath(obj).resolve() def _apply_path_update(path: ComponentPath, old_path_prefix: ComponentPath, new_path_prefix: ComponentPath): """ Update prefix of the path tuple """ if isinstance(path, iHdlConstrain): return path._copy_with_root_upadate(old_path_prefix, new_path_prefix) return path.update_prefix(old_path_prefix, new_path_prefix) class set_max_delay(iHdlConstrain): """ Object which represents the max_delay constrain * usually used to set propagation time between two clock domains etc. :ivar ~.start: start of the signal path :ivar ~.end: end of the signal path :ivar ~.time_ns: max delay of the specified path in ns """ def __init__(self, start: Union[HwIO, RtlSignal], end: Union[HwIO, RtlSignal], time_ns: float, datapath_only=True, ommit_registration=False): self.start = _get_absolute_path(start) self.end = _get_absolute_path(end) self.time_ns = time_ns self.datapath_only = datapath_only if not ommit_registration: self.register_on_parent() def _copy_with_root_upadate(self, old_path_prefix: ComponentPath, new_path_prefix: ComponentPath): new_o = copy(self) new_o.start = _apply_path_update( self.start, old_path_prefix, new_path_prefix) new_o.end = _apply_path_update( self.end, old_path_prefix, new_path_prefix) return new_o def _get_parent(self) -> HwModule: return _get_parent_HwModule(self.end) class set_false_path(iHdlConstrain): def __init__(self, start: Union[None, HwIO, RtlSignal], end: Union[None, HwIO, RtlSignal], ommit_registration=False): self.start = _get_absolute_path(start) self.end = _get_absolute_path(end) if not ommit_registration: self.register_on_parent() def _copy_with_root_upadate(self, old_path_prefix: ComponentPath, new_path_prefix: ComponentPath): return set_max_delay._copy_with_root_upadate(self, old_path_prefix, new_path_prefix) def _get_parent(self) -> HwModule: o = self.start if o is None: o = self.end return _get_parent_HwModule(o) class get_clock_of(iHdlConstrain): def __init__(self, obj: Union[HwIO, RtlSignal], ommit_registration=False): self.obj = _get_absolute_path(obj) def _copy_with_root_upadate(self, old_path_prefix: ComponentPath, new_path_prefix: ComponentPath): new_o = copy(self) new_o.obj = _apply_path_update( self.obj, old_path_prefix, new_path_prefix) return new_o def _get_parent(self) -> HwModule: return _get_parent_HwModule(self.obj) class set_async_reg(iHdlConstrain): """ Placement constrain which tell that the register should be put as close as possible to it's src/dst It should not be placed on the FF on the src domain, but should be set on FFs (possibly more) on the destination domain. """ def __init__(self, sig: RtlSignal, ommit_registration=False): self.sig = _get_absolute_path(sig) if not ommit_registration: self.register_on_parent() def _copy_with_root_upadate(self, old_path_prefix: ComponentPath, new_path_prefix: ComponentPath): new_o = copy(self) new_o.sig = _apply_path_update( self.sig, old_path_prefix, new_path_prefix) return new_o def _get_parent(self) -> HwModule: return _get_parent_HwModule(self.sig) ================================================ FILE: hwt/doc_markers.py ================================================ def internal(fn): """ Decorator which does not affect functionality but it is used as marker which tells that this object is not interesting for users and it is only used internally """ return fn def hwt_expr_producer(fn): """ Decorator which does not affect functionality. For documentation purposes it specifies that the function produces hwt expression. """ return fn ================================================ FILE: hwt/hObjList.py ================================================ from typing import TypeVar, Iterable, List, Union, Tuple, Optional, \ Self from hwt.mainBases import HwIOBase, HwModuleBase T = TypeVar("T", HwIOBase, HwModuleBase, None) class HObjList(list[T]): """ Regular list with some interface/unit methods delegated on items. Main purpose of this class it let :class:`hwt.synthesizer.PropDeclrCollector.PropDeclrCollector` know that this is not an regular python array and that items should be registered as HW objects. :ivar _name: name of the property on parent :ivar _parent: parent HwModule/HwIO object :note: this object may be nested in HObjList instances but the parent and name will always corresponds to a HwModule/HwIO object, if there is any :note: :class:`hwt.synthesizer.PropDeclrCollector.PropDeclrCollector` is used by :class:`hwt.hwIO.Interface` and :class:`hwt.hwModule.HwModule` """ def __init__(self, *args, **kwargs): hdlNameOverride = kwargs.pop("hdlName", None) list.__init__(self, *args, **kwargs) self._name: Optional[str] = None self._parent: Optional[Union["HwModule", "Interface"]] = None self._hdlNameOverride: Optional[str] = hdlNameOverride def _on_append(self, self_obj: Self, item: T, index: int): pass def append(self, item: T): if self._on_append is not HObjList._on_append: self._on_append(self, item, len(self)) return list.append(self, item) def clear(self, *args, **kwargs): assert self._parent is None return list.clear(self, *args, **kwargs) def extend(self, iterable: Iterable[T]): if self._on_append is not HObjList._on_append: offset = len(self) for i, item in enumerate(iterable): self._on_append(self, item, offset + i) return list.extend(self, iterable) def insert(self, *args, **kwargs): assert self._parent is None return list.insert(self, *args, **kwargs) def pop(self, *args, **kwargs) -> T: assert self._parent is None return list.pop(self, *args, **kwargs) def remove(self, *args, **kwargs): assert self._parent is None return list.remove(self, *args, **kwargs) def reverse(self, *args, **kwargs): assert self._parent is None return list.reverse(self, *args, **kwargs) def sort(self, *args, **kwargs): assert self._parent is None return list.sort(self, *args, **kwargs) def _getHdlName(self): """Get name in HDL """ # list of name or tulple (name, separator) name: List[Union[str, Tuple[str, str]]] = [] tmp = self while isinstance(tmp, (HwIOBase, HObjList)): n = tmp._name if name: name_sep = getattr(tmp, "_NAME_SEPARATOR", "_") n = (n, name_sep) else: # no need to add separator at the end because this is a last part of the name n = n add_name_part_from_this = True name_override = tmp._hdlNameOverride if name_override is not None: # recursively apply renames if isinstance(name_override, str): if isinstance(n, tuple) and name_override: n = (name_override, n[1]) else: n = name_override elif isinstance(name_override, dict): last_name = name[-1] if isinstance(last_name, tuple): last_name = last_name[0] no = name_override.get(last_name, None) if no is not None: if isinstance(no, str): # everything what we resolved so far is overridden on this parent name = [no, ] add_name_part_from_this = False else: raise NotImplementedError() else: raise TypeError(name_override) if add_name_part_from_this: name.append(n) tmp = getattr(tmp, "_parent", None) _name = [] for n in reversed(name): if isinstance(n, str): _name.append(n) else: _name.extend(n) return "".join(_name) def _getFullName(self) -> str: """get all name hierarchy separated by '.' """ name = "" tmp = self while isinstance(tmp, (HwIOBase, HObjList)): n = tmp._name if name == '': if n is not None: assert isinstance(n, str), (name, n) name = n else: if n is None: n = "" name = f"{n:s}.{name:s}" tmp = getattr(tmp, "_parent", None) return name def _make_association(self, *args, **kwargs): """ Delegate _make_association on items :note: doc in :func:`~hwt.synthesizer.interfaceLevel.propDeclCollector._make_association` """ for o in self: o._make_association(*args, **kwargs) return self def _updateHwParamsFrom(self, *args, **kwargs): """ :note: doc in :func:`~hwt.synthesizer.interfaceLevel.propDeclCollector._updateHwParamsFrom` """ for o in self: if isinstance(o, (HwIOBase, HwModuleBase, HObjList)): o._updateHwParamsFrom(*args, **kwargs) return self def _cleanRtlSignals(self, lockNonExternal=True): for o in self: if isinstance(o, (HwIOBase, HwModuleBase, HObjList)): o._cleanRtlSignals(lockNonExternal=lockNonExternal) ================================================ FILE: hwt/hdl/__init__.py ================================================ """ This package contains classes for representation of HDL languages (hdl types, RtlSignal, HdlAssignmentContainer, HdlStmCodeBlockContainer etc.). It also contains classes for representation of complex HW structures like dense transaction template (TransTmpl, FrameTmpl). """ # [TODO] derive from hdlConvertorAst.hdlAst ================================================ FILE: hwt/hdl/commonConstants.py ================================================ from hwt.hdl.types.defs import BIT b1 = BIT.from_py(1) b0 = BIT.from_py(0) ================================================ FILE: hwt/hdl/const.py ================================================ from typing import TypeVar, Generic, Self, Set from hwt.doc_markers import internal from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.mainBases import RtlSignalBase T = TypeVar("T", bound="HdlType") class HConst(Generic[T]): """ Wrap around hdl value with overloaded operators operators are overloaded in every type separately """ __slots__ = ["_dtype", "val", "vld_mask"] def __init__(self, dtype: "HdlType", val, vld_mask): """ :param val: pythonic value representing this value :param dtype: data type object from which this value was derived from :param vld_mask: validity mask for value """ self._dtype = dtype self.val = val self.vld_mask = vld_mask def _is_full_valid(self): return self.vld_mask == self._dtype.all_mask() def _is_partially_valid(self) -> bool: return self.vld_mask != 0 def _auto_cast(self, toType: "HdlType"): """ Cast value or signal of this type to another compatible type. :param toType: instance of HdlType to cast into """ return self._dtype.auto_cast_HConst(self, toType) def _reinterpret_cast(self, toType: "HdlType"): """ Cast value or signal of this type to another type of same size. :param toType: instance of HdlType to cast into """ return self._dtype.reinterpret_cast_HConst(self, toType) def staticEval(self) -> Self: return self.__copy__() def __copy__(self) -> Self: return self.__class__(self._dtype, self.val, self.vld_mask) @internal def __hash__(self) -> int: return hash((self._dtype, self.val, self.vld_mask)) def __repr__(self) -> str: if self._is_full_valid(): vld_mask = "" else: vld_mask = ", mask {0:x}".format(self.vld_mask) return "<{0:s} {1:s}{2:s}>".format( self.__class__.__name__, repr(self.val), vld_mask) @classmethod def _from_py(cls, typeObj, val, vld_mask) -> Self: """ from_py without value normalization and type checking """ return cls(typeObj, val, vld_mask) @classmethod def from_py(cls, typeObj, val, vld_mask=None) -> Self: raise NotImplementedError( f"from_py fn is not implemented for", cls) def __eq__(self, other): if isinstance(other, HConst): return self._dtype == other._dtype and \ self.vld_mask == other.vld_mask and\ self.val == other.val else: return super().__eq__(other) def _eq(self, other): raise TypeError() def __ne__(self, other): eq = self._eq(other) eq.val = not eq.val return eq def _walk_sensitivity(self, casualSensitivity: Set[RtlSignalBase], seen: Set[RtlSignalBase], ctx: SensitivityCtx): """ :see: :meth:`hwt.synthesizer.rtlLevel.rtlSignal.RtlSignal._walk_sensitivity` """ seen.add(self) def areHConsts(*items): """ :return: True if all arguments are instances of HConst class else False """ for i in items: if not isinstance(i, HConst): return False return True ================================================ FILE: hwt/hdl/constUtils.py ================================================ from typing import Union, List from hwt.hdl.const import HConst def isSameHConst(a: HConst, b: HConst) -> bool: """ :return: True if two Value instances are same :note: not just equal """ return a is b or (isinstance(a, HConst) and isinstance(b, HConst) and a.val == b.val and a.vld_mask == b.vld_mask) def areSameHConsts(a: Union[None, List[HConst]], b: Union[None, List[HConst]]) -> bool: """ :return: True if two vectors of HConst/RtlSignal instances are same :note: not just equal """ if a is b: return True if a is None or b is None: return False if len(a) == len(b): for a_, b_ in zip(a, b): if not isSameHConst(a_, b_): return False return True else: return False ================================================ FILE: hwt/hdl/frameTmpl.py ================================================ from itertools import zip_longest from math import ceil, floor, inf from typing import Union, Generator, List, Tuple from hwt.doc_markers import internal from hwt.hdl.frameTmplUtils import TransTmplWordIterator, \ ChoicesOfFrameParts from hwt.hdl.transPart import TransPart from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.struct import HStruct from hwt.pyUtils.arrayQuery import flatten from pyMathBitPrecise.bit_utils import mask, get_bit_range, set_bit_range class FrameTmpl(object): """ Frame template is metainfomation about data structure, it's the template of transaction for specific interface Usuall flow of frame generatig consists of these steps: 1. Describe format of data by HDL type (HStruct, HUnion ...) 2. Convert it to TransTmpl to resolve addresses of each field in structure 3. Split parts of TransTmpl into words 4. Use parts in words to assembly frames :ivar ~._fieldToTPart: dictionary {HStructField: TransPart} to resolve this association, None by default, builded when packData is called and is not builded :note: others ivars described in __init__ """ def __init__(self, origin: HdlType, wordWidth: int, startBitAddr: int, endBitAddr: int, transParts: List[TransPart]): """ :param origin: instance of HType (usually HStruct) from which this FrameTmpl was generated from :param wordWidth: width of word on interface where this template should be used :param startBitAddr: bit offset where this frame starts :param endBitAddr: bit offset where this frame ends (bit index of first bit behind this frame) :param transParts: instances of TransPart which are parts of this frame """ self.origin = origin self.wordWidth = wordWidth assert startBitAddr <= endBitAddr self.startBitAddr = startBitAddr self.endBitAddr = endBitAddr self.parts = transParts self._fieldToTPart = None for p in self.parts: p.parent = self assert p.startOfPart >= startBitAddr, (p, startBitAddr) assert p.endOfPart <= endBitAddr, (p, endBitAddr) @staticmethod def framesFromTransTmpl(transaction: 'TransTmpl', wordWidth: int, maxFrameLen: Union[int, float]=inf, maxPaddingWords: Union[int, float]=inf, trimPaddingWordsOnStart: bool=False, trimPaddingWordsOnEnd: bool=False) -> Generator[ 'FrameTmpl', None, None]: """ Convert transaction template into FrameTmpls :param transaction: transaction template used which are FrameTmpls created from :param wordWidth: width of data signal in target interface where frames will be used :param maxFrameLen: maximum length of frame in bits, if exceeded another frame will be created :param maxPaddingWords: maximum of continual padding words in frame, if exceed frame is split and words are cut of :attention: if maxPaddingWords 0 assert maxPaddingWords >= 0 if maxPaddingWords < inf: assert trimPaddingWordsOnStart or trimPaddingWordsOnEnd, \ "Padding has to be cut off somewhere" it = TransTmplWordIterator(wordWidth) lastWordI = 0 endOfThisFrame = maxFrameLen parts = [] for wordI, word in it.groupByWordIndex(transaction, 0): if wordI * wordWidth >= endOfThisFrame: # now in first+ word behind the frame # cut off padding at end of frame paddingWords = wordI - lastWordI if trimPaddingWordsOnEnd and paddingWords > maxPaddingWords: # cut off padding and align end of frame to word _endOfThisFrame = (lastWordI + 1) * wordWidth else: _endOfThisFrame = wordI * wordWidth yield FrameTmpl(transaction, wordWidth, startOfThisFrame, _endOfThisFrame, parts) # prepare for start of new frame parts = [] isFirstInFrame = True partsPending = False # start on new word startOfThisFrame = _endOfThisFrame endOfThisFrame = startOfThisFrame + maxFrameLen lastWordI = wordI # check if padding at potential end of frame can be cut off if (not isFirstInFrame and trimPaddingWordsOnEnd and wordI - lastWordI > maxPaddingWords + 1): # there is too much continual padding, # cut it out and start new frame _endOfThisFrame = (lastWordI + 1) * wordWidth yield FrameTmpl(transaction, wordWidth, startOfThisFrame, _endOfThisFrame, parts) # prepare for start of new frame parts = [] isFirstInFrame = True partsPending = False # start on new word lastWordI = wordI - 1 startOfThisFrame = lastWordI * wordWidth endOfThisFrame = startOfThisFrame + maxFrameLen if isFirstInFrame: partsPending = True isFirstInFrame = False # cut off padding at start of frame paddingWords = wordI - lastWordI if trimPaddingWordsOnStart and paddingWords > maxPaddingWords: startOfThisFrame += paddingWords * wordWidth endOfThisFrame = startOfThisFrame + maxFrameLen # resolve end of this part parts.extend(word) lastWordI = wordI # reminder in "parts" after last iteration endOfThisFrame = transaction.bitAddrEnd withPadding = not (trimPaddingWordsOnEnd or trimPaddingWordsOnStart) if partsPending or (withPadding and endOfThisFrame != startOfThisFrame): # cut off padding at end of frame endOfLastWord = (lastWordI + 1) * wordWidth if endOfThisFrame < endOfLastWord: endOfThisFrame = endOfLastWord else: paddingWords = it.fullWordCnt(endOfLastWord, endOfThisFrame) if trimPaddingWordsOnEnd and paddingWords > maxPaddingWords: endOfThisFrame -= paddingWords * wordWidth # align end of frame to word endOfThisFrame = min(startOfThisFrame + maxFrameLen, endOfThisFrame) yield FrameTmpl(transaction, wordWidth, startOfThisFrame, endOfThisFrame, parts) parts = [] startOfThisFrame = endOfThisFrame # final padding on the end while withPadding and startOfThisFrame < transaction.bitAddrEnd: endOfThisFrame = min(startOfThisFrame + maxFrameLen, transaction.bitAddrEnd) yield FrameTmpl(transaction, wordWidth, startOfThisFrame, endOfThisFrame, []) startOfThisFrame = endOfThisFrame @internal def _wordIndx(self, addr: int): """ convert bit address to index of word where this address is """ return floor(addr / self.wordWidth) def getWordCnt(self): """ Get count of words in this frame """ return ceil((self.endBitAddr - self.startBitAddr) / self.wordWidth) def walkWords(self, showPadding: bool=False)\ -> Generator[Tuple[int, List[Union[TransPart, ChoicesOfFrameParts]]], None, None]: """ Walk enumerated words in this frame :attention: not all indexes has to be present, only words with items will be generated when not showPadding :param showPadding: padding TransParts are also present :return: generator of tuples (wordIndex, list of TransParts in this word) """ wIndex = 0 lastEnd = self.startBitAddr parts: List[TransPart] = [] for p in self.parts: end = p.startOfPart if showPadding and end != lastEnd: # insert padding before data while end != lastEnd: assert end >= lastEnd, (end, lastEnd) endOfWord = ceil( (lastEnd + 1) / self.wordWidth) * self.wordWidth endOfPadding = min(endOfWord, end) _p = TransPart(self, None, False, lastEnd, endOfPadding, 0) parts.append(_p) if endOfPadding >= endOfWord: yield (wIndex, parts) wIndex += 1 parts = [] lastEnd = endOfPadding if self._wordIndx(lastEnd) != self._wordIndx(p.startOfPart): # if input data continues to a next word, yield current word # and start processing next one yield (wIndex, parts) wIndex += 1 parts = [] lastEnd = p.endOfPart parts.append(p) lastEnd = p.endOfPart if lastEnd % self.wordWidth == 0: # if we can not add anything to this word, # yield it directly and continue on next word yield (wIndex, parts) wIndex += 1 parts = [] if showPadding and (parts or lastEnd != self.endBitAddr or lastEnd % self.wordWidth != 0): # align end to end of last word end = ceil(self.endBitAddr / self.wordWidth) * self.wordWidth # padding is non removable if it is part of data # and it is removable if it was generated by frame alignment endOfNonRemovablePadding = self.origin.bitAddrEnd while end != lastEnd: assert end >= lastEnd, (end, lastEnd) endOfWord = ((lastEnd // self.wordWidth) + 1) * self.wordWidth endOfPadding = min(endOfWord, end) if lastEnd < endOfNonRemovablePadding: endOfPadding = min(endOfPadding, endOfNonRemovablePadding) can_be_removed = False else: can_be_removed = True _p = TransPart(self, None, can_be_removed, lastEnd, endOfPadding, 0) _p.parent = self parts.append(_p) if endOfPadding >= endOfWord: yield (wIndex, parts) wIndex += 1 parts = [] lastEnd = endOfPadding if parts: # in the case end of frame is not aligned to end of word yield (wIndex, parts) @staticmethod def fieldToDataDict(dtype, data, res): return FrameTmpl._fieldToDataDict(dtype, (dtype,), data, res) @staticmethod def _fieldToDataDict(dtype, path, data, res): """ Construct dictionary {StructField:value} for faster lookup of values for fields """ # assert data is None or isinstance(data, dict) for f in dtype.fields: try: fVal = data[f.name] except KeyError: fVal = None new_path = (*path, f) if isinstance(f.dtype, HBits): if fVal is not None: assert isinstance(fVal, int) res[new_path] = fVal elif isinstance(f.dtype, HStruct): if fVal: FrameTmpl._fieldToDataDict(f.dtype, new_path, fVal, res) elif isinstance(f.dtype, HArray): if fVal: # assert isinstance(fVal, class_or_tuple) res[new_path] = fVal return res def packData(self, data): """ Pack data into list of BitsVal of specified dataWidth :param data: dict of values for struct fields {fieldName: value} :return: list of BitsVal which are representing values of words """ typeOfWord = HBits(self.wordWidth, None) fieldToVal = self._fieldToTPart if fieldToVal is None: fieldToVal = self._fieldToTPart = self.fieldToDataDict( self.origin.dtype, data, {}) for _, transParts in self.walkWords(showPadding=True): # build a single data word actualVldMask = 0 actualVal = 0 for tPart in transParts: high, low = tPart.getBusWordBitRange() fhigh, flow = tPart.getFieldBitRange() if not tPart.isPadding: val = fieldToVal.get(tPart.tmpl.origin, None) else: val = None if val is None: newBits = 0 vld = 0 else: newBits = get_bit_range(val, flow, fhigh - flow) vld = mask(high - low) actualVal = set_bit_range(actualVal, low, high - low, newBits) actualVldMask = set_bit_range(actualVldMask, low, high - low, vld) v = typeOfWord.getConstCls()(typeOfWord, actualVal, actualVldMask) yield v @internal def __repr__getName(self, transPart: TransPart, fieldWidth: int): """ Get name string for a field """ if transPart.isPadding: return "X" * fieldWidth else: path = transPart.tmpl.getFieldPath() names = [] for p in path: if isinstance(p, int): names.append(f"[{p:d}]") else: if names: names.append(f".{p:s}") else: names.append(p) return "".join(names) @internal def __repr__word(self, index: int, width: int, padding: int, transParts: List[TransPart]): buff = [f"{index: <{padding}}|", ] DW = self.wordWidth partsWithChoice = [] endAlignment = transParts[-1].endOfPart % DW if endAlignment: # -1 for ending | percentOfWidth = (DW - endAlignment) / DW # -1 for ending | fieldWidth = max(0, int(percentOfWidth * width) - 1) assert fieldWidth >= 0 s = '%s|' % ("^" * fieldWidth) buff.append(s) for tp in reversed(transParts): percentOfWidth = tp.bit_length() / DW # -1 for ending | fieldWidth = max(0, int(percentOfWidth * width) - 1) assert fieldWidth >= 0 # percentOffset = (tp.inFrameBitAddr % DW) / DW # offset = int(percentOffset * width) if isinstance(tp, ChoicesOfFrameParts): name = "" partsWithChoice.append(tp) else: name = self.__repr__getName(tp, fieldWidth) buff.append(f'{name: ^{fieldWidth}}|') return ("".join(buff), partsWithChoice) def __repr__(self, scale=1): buff = [] s = f"<{self.__class__.__name__:s} start:{self.startBitAddr:d}, end:{self.endBitAddr:d}" buff.append(s) padding = 5 DW = self.wordWidth width = int(DW * scale) buff.append( '{0: <{padding}}{1: <{halfLineWidth}}{2: >{halfLineWidth}}'.format( "", DW - 1, 0, padding=padding, halfLineWidth=width // 2)) line = '{0: <{padding}}{1:-<{lineWidth}}'.format( "", "", padding=padding, lineWidth=width + 1) buff.append(line) for w, transParts in self.walkWords(showPadding=True): wStr, partsWithChoice = self.__repr__word( w, width, padding, transParts) buff.append(wStr) while partsWithChoice: for parts in zip_longest(*partsWithChoice): parts = list(flatten(parts, level=1)) wStr, _partsWithChoice = self.__repr__word( w, width, padding, parts) buff.append(wStr) partsWithChoice = _partsWithChoice buff.append(line) buff.append(">") return "\n".join(buff) ================================================ FILE: hwt/hdl/frameTmplUtils.py ================================================ from typing import Tuple from hwt.doc_markers import internal from hwt.hdl.transPart import TransPart from hwt.hdl.transTmpl import OneOfTransaction @internal def iterSort(iterators, cmpFn): """ Sort items from iterators(generators) by alwas selecting item with lowest value (min first) :return: generator of tuples (origin index, item) where origin index is index of iterator in "iterators" from where item commes from """ actual = [] _iterators = [] for i, it in enumerate(iterators): try: a = next(it) _iterators.append((i, it)) actual.append(a) except StopIteration: continue while True: if not _iterators: return elif len(_iterators) == 1: originIndex, it = _iterators[0] yield originIndex, actual[0] for item in it: yield originIndex, item return # select minimum and iterator from where it comes from minimum = None minimumIndex = None secondMin = None for i, val in enumerate(actual): skipSecMinCheck = False if minimum is None: minimum = val minimumIndex = i elif cmpFn(val, minimum): secondMin = minimum minimum = val minimumIndex = i skipSecMinCheck = True elif not skipSecMinCheck and ( secondMin is None or cmpFn(val, secondMin)): secondMin = val actualI, actualIt = _iterators[minimumIndex] while not cmpFn(secondMin, minimum): yield (actualI, minimum) try: minimum = next(actualIt) except StopIteration: minimum = None break # consume from actual iterator while if minimum is None: del _iterators[minimumIndex] del actual[minimumIndex] else: # minimum is not minimum anymore actual[minimumIndex] = minimum class TransPartGroup(list): """ Abstract parent class for groups of TransParts """ def setIsLast(self, val: bool) -> None: self._isLast = val def isLastPart(self) -> bool: """ :return: True if this part is last in parts derived from original field else False """ return self._isLast def getBusWordBitRange(self) -> Tuple[int, int]: """ :return: bit range which contains data of this part on bus data signal """ offset = self.startOfPart % self.parent.wordWidth return (offset + self.bit_length(), offset) def bit_length(self): return self.endOfPart - self.startOfPart def __repr__(self): return "<%s %s>" % (self.__class__.__name__, list.__repr__(self)) class ChoicesOfFrameParts(TransPartGroup): """ List of ChoiceOfFrameParts One of ChoiceOfFrameParts is used to represent the word, item depends on context :ivar ~.origin: OneOfTransaction instance :ivar ~.startOfPart: bit addr of start of this group of frame parts :ivar ~.endOfPart: bit addr of end of this group of frame parts :ivar ~._isLast: flag which means this is the last part of original union """ def __init__(self, startOfPart: int, origin: OneOfTransaction): self.origin = origin self.startOfPart = startOfPart self.endOfPart = None self._isLast = False super(ChoicesOfFrameParts, self).__init__() def resolveEnd(self): end = self.startOfPart for items in self: if items: end = max(end, max(itm.endOfPart for itm in items)) self.endOfPart = end class ChoiceOfFrameParts(list): """ :ivar ~.origin: ChoicesOfFrameParts instance :ivar ~.tmpl: TransTmpl which was item generated from """ def __init__(self, origin, tmpl): self.origin = origin self.tmpl = tmpl def __repr__(self): return "<%s %s>" % (self.__class__.__name__, list.__repr__(self)) def groupIntoChoices(splitsOnWord, wordWidth: int, origin: OneOfTransaction): """ :param splitsOnWord: list of lists of parts (fields splited on word boundaries) :return: generators of ChoicesOfFrameParts for each word which are not crossing word boundaries """ def cmpWordIndex(a, b): return a.startOfPart // wordWidth < b.startOfPart // wordWidth actual = None itCnt = len(splitsOnWord) for i, item in iterSort(splitsOnWord, cmpWordIndex): _actualW = item.startOfPart // wordWidth if actual is None: # first pass actual = ChoicesOfFrameParts(item.startOfPart, origin) actual.extend( ChoiceOfFrameParts(actual, origin.possibleTransactions[_i]) for _i in range(itCnt)) actualW = _actualW elif _actualW > actualW: actual.resolveEnd() yield actual actual = ChoicesOfFrameParts(item.startOfPart, origin) actual.extend( ChoiceOfFrameParts(actual, origin.possibleTransactions[_i]) for _i in range(itCnt)) actualW = _actualW actual[i].append(item) if actual is not None: actual.setIsLast(True) actual.resolveEnd() yield actual class TransTmplWordIterator(): """ Iterator which reinterprets any structure as generator of bits of specified width """ def __init__(self, wordWidth): self.wordWidth = wordWidth def fullWordCnt(self, start: int, end: int): """Count of complete words between two addresses """ assert end >= start, (start, end) gap = max(0, (end - start) - (start % self.wordWidth)) return gap // self.wordWidth def groupByWordIndex(self, transaction: 'TransTmpl', offset: int): """ Group transaction parts splited on words to words :param transaction: TransTmpl instance which parts should be grupped into words :return: generator of tuples (wordIndex, list of transaction parts in this word) """ actualW = None partsInWord = [] wordWidth = self.wordWidth for item in self.splitOnWords(transaction, offset): _actualW = item.startOfPart // wordWidth if actualW is None: actualW = _actualW partsInWord.append(item) elif _actualW > actualW: yield (actualW, partsInWord) actualW = _actualW partsInWord = [item, ] else: partsInWord.append(item) if partsInWord: yield (actualW, partsInWord) @internal def splitOnWords(self, transaction, addrOffset=0): """ :return: generator of TransPart instance """ wordWidth = self.wordWidth end = addrOffset for tmp in transaction.HwIO_walkFlatten(offset=addrOffset): if isinstance(tmp, OneOfTransaction): # unions split = [self.splitOnWords(ch, end) for ch in tmp.possibleTransactions] yield from groupIntoChoices(split, wordWidth, tmp) end = addrOffset + tmp.possibleTransactions[0].bitAddrEnd else: # constant size types (base, end), tmpl = tmp startOfPart = base while startOfPart != end: wordIndex = startOfPart // wordWidth endOfWord = (wordIndex + 1) * wordWidth endOfPart = min(endOfWord, end) inFieldOffset = startOfPart - base yield TransPart(self, tmpl, False, startOfPart, endOfPart, inFieldOffset) startOfPart = endOfPart ================================================ FILE: hwt/hdl/hdlObject.py ================================================ from io import StringIO from hdlConvertorAst.translate.common.name_scope import NameScope class HdlObject(): """ Base Hdl object class for object which can be directly serialized to target HDL language """ def __repr__(self): from hwt.serializer.hwt import HwtDebugSerializer name_scope = NameScope(None, "debug", False, debug=True) to_hdl = HwtDebugSerializer.TO_HDL_AST(name_scope) to_hdl.debug = True hdl = to_hdl.as_hdl(self) buff = StringIO() # import sys # buff = sys.stdout ser = HwtDebugSerializer.TO_HDL(buff) ser.visit_iHdlObj(hdl) return buff.getvalue() ================================================ FILE: hwt/hdl/operator.py ================================================ from typing import Generator, Union, Tuple, Optional, Set, Sequence from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.hdlObject import HdlObject from hwt.hdl.operatorDefs import isEventDependentOp, HOperatorDef from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.arrayQuery import arr_all from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal, RtlSignalBase, \ OperatorCaheKeyType @internal def getCtxFromOps(operands: Sequence): for o in operands: if isinstance(o, RtlSignalBase): return o._rtlCtx return None # case for casts of constants def isConst(item: Union[HConst, RtlSignalBase]): """ :return: True if expression is constant """ return isinstance(item, HConst) or item._const class HOperatorNode(HdlObject): """ Class of operator in expression tree :ivar ~.operands: list of operands :ivar ~.evalFn: function to evaluate this operator :ivar ~.operator: HOperatorDef instance :ivar ~.result: result signal of this operator """ def __init__(self, operator: HOperatorDef, operands: Tuple[Union[RtlSignalBase, HConst]]): self.operands = tuple(operands) self.operator = operator self.result: Optional[RtlSignal] = None @internal def staticEval(self): """ Recursively statistically evaluate result of this operator """ for o in self.operands: o.staticEval() self.result._val = self.operator.eval(self, simulator=None) @internal def _walk_sensitivity(self, casualSensitivity: Set[RtlSignal], seen: Set[RtlSignal], ctx: SensitivityCtx): """ :see: :meth:`hwt.synthesizer.rtlLevel.rtlSignal.RtlSignal._walk_sensitivity` """ seen.add(self) if isEventDependentOp(self.operator): if ctx.contains_ev_dependency: assert self in ctx, "has to have only a single clock signal" ctx.contains_ev_dependency = True ctx.append(self) else: # walk source of signal for operand in self.operands: if operand not in seen: operand._walk_sensitivity(casualSensitivity, seen, ctx) @internal def _walk_public_drivers(self, seen: set) -> Generator["RtlSignal", None, None]: """ Walk all non hidden signals in an expression """ for op in self.operands: if not isinstance(op, HConst) and op not in seen: seen.add(op) yield from op._walk_public_drivers(seen) @internal @staticmethod def withRes(opDef: HOperatorDef, operands: Sequence[Union[RtlSignalBase, HConst]], resT: HdlType): """ Create operator with result signal :ivar ~.resT: data type of result signal :ivar ~.outputs: iterable of signals which are outputs from this operator """ # try return existing operator result for i, o in enumerate(operands): if isinstance(o, RtlSignalBase): if i == 0: k = (opDef, i, *operands[1:]) else: k = (opDef, i, *operands[:i], *operands[i + 1:]) try: return o._usedOps[k] except KeyError: pass break # instantiate new HOperatorNode op = HOperatorNode(opDef, operands) out: RtlSignal = resT.getRtlSignalCls()(getCtxFromOps(operands), None, resT) out._const = arr_all(op.operands, isConst) out._rtlDrivers.append(op) out._rtlObjectOrigin = op op.result = out # Register potential signals to drivers/endpoints first_signal = True for i, o in enumerate(op.operands): if isinstance(o, RtlSignalBase): o._rtlEndpoints.append(op) if first_signal: # register operator in _usedOps operator cache if i == 0: k = (opDef, i, *operands[1:]) else: k = (opDef, i, *operands[:i], *operands[i + 1:]) o._usedOps[k] = out o._usedOpsAlias[k] = {k, } first_signal = False else: assert isinstance(o, HConst), ( "HOperatorNode operands can be only signal or values got:", o) # pre-compute constant signal if all used types support it if out._const: precompute = resT._PRECOMPUTE_CONSTANT_SIGNALS for o in operands: precompute &= o._dtype._PRECOMPUTE_CONSTANT_SIGNALS if not precompute: break if precompute: # if this signal is constant precompute its value out.staticEval() return out @internal def _replace_input(self, inp: RtlSignal, replacement: RtlSignal): """ Replace operand signal (non-recursively) :attention: costly operation because all records in operand cache for all inputs may be potentially updated """ assert self.result._rtlCtx is replacement._rtlCtx, self newOperands = [] modified = False for op in self.operands: if op is inp: modified = True newOperands.append(replacement) else: newOperands.append(op) assert modified, self res = self.result for op in self.operands: if isinstance(op, RtlSignal): op: RtlSignal for k, v in tuple(op._usedOps.items()): k: OperatorCaheKeyType if v is res: if op is inp: # this operand is originally replaced "inp" the cache key must be transfered # from original operand to a new replacement op._usedOps.pop(k) replacement._usedOps[k] = v aliases = op._usedOpsAlias.pop(k) aliases.remove(k) _aliases = None for a in aliases: _aliases = replacement._usedOpsAlias.get(a, None) if _aliases is not None: break if _aliases is None: _aliases = {k, } else: _aliases.add(k) replacement._usedOpsAlias[k] = _aliases else: # some other operand is originally replaced "inp" the cache key must be updated op._usedOps.pop(k) kNew = (*k[0:2], *(replacement if _op is inp else _op for _op in k[2:])) op._usedOps[kNew] = v aliases = op._usedOpsAlias.pop(k) aliases.remove(k) aliases.add(kNew) op._usedOpsAlias[kNew] = aliases break # _usedOps/_usedOpsAlias is relevant only for the first operand self.operands = tuple(newOperands) inp._rtlEndpoints.discard(self) replacement._rtlEndpoints.append(self) @internal def _destroy(self): self.result._rtlDrivers.remove(self) operands = self.operands first_op_sig = True for i, o in enumerate(operands): if isinstance(o, RtlSignalBase): # discard because same signal can be on multiple places in operand list o._rtlEndpoints.discard(self) if first_op_sig: # clean all references on this operator instance from RtlSignal._usedOps operator cache _k = (self.operator, i, *operands[:i], *operands[i + 1:]) for k in o._usedOpsAlias[_k]: res = o._usedOps.pop(k) assert res is self.result, (self.result._rtlCtx.parent, "HOperatorNode was not stored properly in operand cache", res, self.result, o) first_op_sig = False self.result._rtlObjectOrigin = None self.result = None self.operands = None self.operator = None ================================================ FILE: hwt/hdl/operatorDefs.py ================================================ from operator import floordiv, add, sub, inv, mul, ne, and_, or_, \ xor, gt, ge, lt, le, getitem, neg from typing import Optional from hdlConvertorAst.hdlAst._expr import HdlOpType from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.defs import INT, SLICE def _getVal(v): while not isinstance(v, HConst): v = v._val return v class HOperatorDef(): """ Operator definition :ivar ~.id: name of operator :ivar ~._evalFn: function which evaluates operands :ivar ~.hdlConvertoAstOp: an operator which is used for export to hdlConvertoAst library """ def __init__(self, evalFn, allowsAssignTo=False, idStr:Optional[str]=None, hdlConvertoAstOp: Optional[HdlOpType]=None): self.id = idStr # assigned automatically in HwtOps self._evalFn = evalFn self.allowsAssignTo = allowsAssignTo self.hdlConvertoAstOp = hdlConvertoAstOp def __eq__(self, other): return type(self) == type(other) and self.id == other.id @internal def __hash__(self): return hash(self.id) def eval(self, operator, simulator=None): """Load all operands and process them by self._evalFn""" operands = [_getVal(o) for o in operator.operands] return self._evalFn(*operands) def __repr__(self): return f"<{self.__class__.__name__:s} {self.id:s}>" def isEventDependentOp(operator): return operator in (HwtOps.RISING_EDGE, HwtOps.FALLING_EDGE) def onRisingEdgeFn(a): return a._onRisingEdge() def onFallingEdgeFn(a): return a._onFallingEdge() def dotOpFn(a, name): return getattr(a, name) # [TODO] downto / to are relict of vhdl and should be replaced with slice def downtoFn(a: int, b: int): return SLICE.from_py(slice(a, b, -1)) def toFn(a: int, b: int): return SLICE.from_py(slice(a, b, 1)) def concatFn(a: "AnyHBitsValue", b: "AnyHBitsValue") -> "AnyHBitsValue": return a._concat(b) def power(base, exp): return base ** exp def eqFn(a, b): return a._eq(b) def ternaryFn(cond: "AnyHBitsValue", vTrue, vFalse): return cond._ternary(vTrue, vFalse) def callFn(fn: "HdlFunctionDef", *operands, **kwargs): return fn(*operands, **kwargs) def bitsToIntFn(a: "AnyHBitsValue"): return a._auto_cast(INT) def intToBitsFn(a: "AnyHBitsValue", t: "HdlType"): return a._auto_cast(t) def bitsAsSignedFn(a: "AnyHBitsValue"): return a._signed() def bitsAsUnsignedFn(a: "AnyHBitsValue"): return a._unsigned() def bitsAsVec(a: "AnyHBitsValue"): return a._vec() def zextFn(a: "AnyHBitsValue", newWidth: int): return a._zext(newWidth) def sextFn(a: "AnyHBitsValue", newWidth: int): return a._sext(newWidth) def truncFn(a: "AnyHBitsValue", newWidth: int): return a._trunc(newWidth) class HwtOps(): """ :attention: Remember that and operator "and" is & and "or" is \\|, "and" and "or" can not be used because they can not be overloaded :attention: These are operators of internal AST, they are not equal to verilog or vhdl operators """ RISING_EDGE = HOperatorDef(onRisingEdgeFn) # unnecessary FALLING_EDGE = HOperatorDef(onFallingEdgeFn) # unnecessary MINUS_UNARY = HOperatorDef(neg) DIV = HOperatorDef(floordiv) UDIV = HOperatorDef(lambda a, b: a._unsigned() // b._unsigned()) SDIV = HOperatorDef(lambda a, b: a._signed() // b._signed()) ADD = HOperatorDef(add) SUB = HOperatorDef(sub) POW = HOperatorDef(power) # see https://stackoverflow.com/questions/25848879/difference-between-mod-and-rem-operators-in-vhdl UREM = HOperatorDef(lambda a, b: a._unsigned() % b._unsigned()) # "rem operator" SREM = HOperatorDef(lambda a, b: a._signed() % b._signed()) # "modulo operator" # MUL bit_length and sign of src0, src1 and dst is the same # sign/unsign variant with double result width is recognized from sext/zext of operands in final phases of serialization MUL = HOperatorDef(mul) NOT = HOperatorDef(inv, allowsAssignTo=True) XOR = HOperatorDef(xor) AND = HOperatorDef(and_) OR = HOperatorDef(or_) DOT = HOperatorDef(dotOpFn, allowsAssignTo=True) DOWNTO = HOperatorDef(downtoFn) TO = HOperatorDef(toFn) CONCAT = HOperatorDef(concatFn, allowsAssignTo=True) # :note: SEXT, ZEXT, TRUNC are redundant as it can be implemented using INDEX/CONCAT however they exist # from performance reasons as patern match for them would be very common during optimizations and # specific evaluation functions may be significantly faster # :note: normalization rules: # * SEXT, ZEXT is prefered over concatenation # * sext(a:1b) should be used internally instead of concat(a, a) # * TRUNC is prefered over index with a single exception # * x[0] should be used internally instead of trunc(x, 1) SEXT = HOperatorDef(sextFn) # sign extension of bit vector to larger width ZEXT = HOperatorDef(zextFn) # zero extension of bit vector to larger width TRUNC = HOperatorDef(truncFn, allowsAssignTo=True) # truncate width of bit vector EQ = HOperatorDef(eqFn) NE = HOperatorDef(ne) # :note: for compare operands without U/S the info about sign is stored in type of operands # for U/S variant the signed flag in the type is ignored and signines is forced by operator definition GT = HOperatorDef(gt) GE = HOperatorDef(ge) LT = HOperatorDef(lt) LE = HOperatorDef(le) ULE = HOperatorDef(lambda a, b: a._unsigned() <= b._unsigned()) ULT = HOperatorDef(lambda a, b: a._unsigned() < b._unsigned()) UGT = HOperatorDef(lambda a, b: a._unsigned() > b._unsigned()) UGE = HOperatorDef(lambda a, b: a._unsigned() >= b._unsigned()) SLE = HOperatorDef(lambda a, b: a._signed() <= b._signed()) SLT = HOperatorDef(lambda a, b: a._signed() < b._signed()) SGT = HOperatorDef(lambda a, b: a._signed() > b._signed()) SGE = HOperatorDef(lambda a, b: a._signed() >= b._signed()) # :note: INDEX is used for arrays and also for bit vectors INDEX = HOperatorDef(getitem, allowsAssignTo=True) TERNARY = HOperatorDef(ternaryFn) CALL = HOperatorDef(callFn) BitsAsSigned = HOperatorDef(bitsAsSignedFn, allowsAssignTo=True) BitsAsUnsigned = HOperatorDef(bitsAsUnsignedFn, allowsAssignTo=True) BitsAsVec = HOperatorDef(bitsAsVec, allowsAssignTo=True) for a_name in dir(HwtOps): o = getattr(HwtOps, a_name) if isinstance(o, HOperatorDef): o.id = a_name CAST_OPS = (HwtOps.BitsAsVec, HwtOps.BitsAsSigned, HwtOps.BitsAsUnsigned) BITWISE_OPS = (HwtOps.NOT, HwtOps.XOR, HwtOps.AND, HwtOps.OR) COMPARE_OPS = ( HwtOps.EQ, HwtOps.NE, HwtOps.GT, HwtOps.GE, HwtOps.LT, HwtOps.LE, HwtOps.ULE, HwtOps.ULT, HwtOps.UGT, HwtOps.UGE, HwtOps.SLE, HwtOps.SLT, HwtOps.SGT, HwtOps.SGE, ) # change of compare operator on operand order swap CMP_OP_SWAP = { HwtOps.EQ: HwtOps.EQ, # (a == b) == (b == a) HwtOps.NE: HwtOps.NE, # (a != b) == (b != a) HwtOps.GT: HwtOps.LT, # (a > b) == (b < a) HwtOps.GE: HwtOps.LE, # (a >= b) == (b <= a) HwtOps.LT: HwtOps.GT, # (a < b) == (b > a) HwtOps.LE: HwtOps.GE, # (a <= b) == (b >= a) HwtOps.UGT: HwtOps.ULT, HwtOps.UGE: HwtOps.ULE, HwtOps.ULT: HwtOps.UGT, HwtOps.ULE: HwtOps.UGE, HwtOps.SGT: HwtOps.SLT, HwtOps.SGE: HwtOps.SLE, HwtOps.SLT: HwtOps.SGT, HwtOps.SLE: HwtOps.SGE, } CMP_OPS_NEG = { HwtOps.EQ: HwtOps.NE, HwtOps.NE: HwtOps.EQ, HwtOps.GT: HwtOps.LE, HwtOps.GE: HwtOps.LT, HwtOps.LT: HwtOps.GE, HwtOps.LE: HwtOps.GT, HwtOps.UGT: HwtOps.ULE, HwtOps.UGE: HwtOps.ULT, HwtOps.ULT: HwtOps.UGE, HwtOps.ULE: HwtOps.UGT, HwtOps.SGT: HwtOps.SLE, HwtOps.SGE: HwtOps.SLT, HwtOps.SLT: HwtOps.SGE, HwtOps.SLE: HwtOps.SGT, } # always commutative operators for which order of operands does not matter ALWAYS_COMMUTATIVE_OPS = (HwtOps.EQ, HwtOps.NE, HwtOps.XOR, HwtOps.AND, HwtOps.OR, HwtOps.ADD, HwtOps.MUL) # always commutative associative operators for which order of operands in expression tree does not matter ALWAYS_ASSOCIATIVE_COMMUTATIVE_OPS = (HwtOps.XOR, HwtOps.AND, HwtOps.OR, HwtOps.ADD, HwtOps.MUL) EVENT_OPS = (HwtOps.RISING_EDGE, HwtOps.FALLING_EDGE) ================================================ FILE: hwt/hdl/operatorUtils.py ================================================ from typing import Union, Tuple from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.statement import HdlStatement, SignalReplaceSpecType from hwt.mainBases import RtlSignalBase from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr # from hwt.hdl.operator import Operator @internal def _replace_input_in_expr(expr: Union[RtlSignalBase, HConst], toReplace: SignalReplaceSpecType) -> RtlSignalBase: """ :return: newly rewritten expression with the subexpression replaced, True if changed else False """ if isinstance(toReplace, dict): replacement = toReplace.get(expr, None) else: _toReplace, replacement = toReplace if expr is not _toReplace: replacement = None if replacement is not None: return replacement, True elif isinstance(expr, RtlSignalBase) and expr._isUnnamedExpr: op = expr._rtlObjectOrigin if op is None: try: op = expr.singleDriver() except SignalDriverErr: return expr, False if isinstance(op, (HdlPortItem, HdlStatement)): return expr, False # assert isinstance(op, Operator), op operandChanged = False ops = [] for o in op.operands: _o, _change = _replace_input_in_expr(o, toReplace) ops.append(_o) operandChanged |= _change if operandChanged: res = op.operator._evalFn(*ops) return res, True else: return expr, False else: return expr, False @internal def replace_input_in_expr(topStatement: "HdlStatement", parentStm: "HdlStatement", expr: Union[RtlSignalBase, HConst], toReplace: SignalReplaceSpecType, # maybeDisconnectedSignals: SetList[RtlSignalBase] ) -> Tuple[RtlSignalBase, bool]: """ :return: tuple (newExpression, True if expr is toReplace and should be replaced else False) """ res, didContainExpr = _replace_input_in_expr(expr, toReplace) if didContainExpr: # maybeDisconnectedSignals.append(expr) if not isinstance(expr, HConst): expr._rtlEndpoints.discard(topStatement) if not isinstance(res, HConst): res._rtlEndpoints.append(topStatement) return res, True else: assert res is expr return expr, False ================================================ FILE: hwt/hdl/portItem.py ================================================ from hwt.constants import DIRECTION from hwt.doc_markers import internal from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.statements.statement import HwtSyntaxError from hwt.hdl.types.hdlType import HdlType from hwt.hdl.variables import HdlSignalItem from hwt.mainBases import RtlSignalBase class HdlPortItem(): """ HDL entity/module/component port item Used to split signal paths on component boundary. :note: src/dst are named based on input output signal direction both dst and src can be parent/component signal, it depends on direction """ def __init__(self, name: str, direction: DIRECTION, dtype: HdlType, module: "HwModule"): self.name = name self.module = module self._dtype = dtype self.direction = direction self.src = None self.dst = None @classmethod def fromSignal(cls, s: HdlSignalItem, component, d: DIRECTION): return cls(s._name, d, s._dtype, component) @internal def connectOuterSig(self, signal: RtlSignalBase): """ Connect to port item on submodule """ if self.direction == DIRECTION.IN: if self.src is not None: raise HwtSyntaxError( "Port %s is already associated with %r" % (self.name, self.src)) self.src = signal signal._rtlEndpoints.append(self) elif self.direction == DIRECTION.OUT: if self.dst is not None: raise HwtSyntaxError( "Port %s is already associated with %r" % (self.name, self.dst)) self.dst = signal signal._rtlDrivers.append(self) else: raise NotImplementedError(self) signal._hidden = False signal._rtlCtx.subHwModules.add(self.module) @internal def connectInternSig(self, signal): """ Connect signal from internal side of of this component to this port. """ if self.direction == DIRECTION.OUT: if self.src is not None: raise HwtSyntaxError( "Port %s is already associated with signal %s" % (self.name, str(self.src))) self.src = signal self.src._rtlEndpoints.append(self) elif self.direction == DIRECTION.IN: if self.dst is not None: raise HwtSyntaxError( "Port %s is already associated with signal %s" % (self.name, str(self.dst))) self.dst = signal self.dst._rtlDrivers.append(self) else: raise NotImplementedError(self.direction) @internal def getInternSig(self): """ return signal inside module which has this port """ d = self.direction if d == DIRECTION.IN: return self.dst elif d == DIRECTION.OUT: return self.src else: raise NotImplementedError(d) @internal def getOuterSig(self): """ return signal inside module which has this port """ d = self.direction if d == DIRECTION.OUT: return self.dst elif d == DIRECTION.IN: return self.src else: raise NotImplementedError(d) @internal def _walk_sensitivity(self, casualSensitivity: set, seen: set, ctx: SensitivityCtx): """ :see: :meth:`hwt.synthesizer.rtlLevel.rtlSignal.RtlSignal._walk_sensitivity` """ return yield def __repr__(self): return f"<{self.__class__.__name__:s} src:{self.src}, dst:{self.dst}>" ================================================ FILE: hwt/hdl/sensitivityCtx.py ================================================ from hwt.doc_markers import internal from hwt.pyUtils.setList import SetList class SensitivityCtx(SetList): """ Sensitivity list used for resolution of sensitivity for statement instances :ivar ~.contains_ev_dependency: True if this contains event dependent sensitivity """ def __init__(self, initSeq=None): SetList.__init__(self, initSeq=initSeq) self.contains_ev_dependency = False @internal def extend(self, items): SetList.extend(self, items) if isinstance(items, SensitivityCtx): self.contains_ev_dependency |= items.contains_ev_dependency @internal def clear(self): SetList.clear(self) self.contains_ev_dependency = False ================================================ FILE: hwt/hdl/statements/__init__.py ================================================ ================================================ FILE: hwt/hdl/statements/assignmentContainer.py ================================================ from typing import Tuple, List, Dict, Union, Optional, Generator from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.constUtils import isSameHConst, areSameHConsts from hwt.hdl.operatorUtils import replace_input_in_expr from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.statements.statement import HdlStatement, SignalReplaceSpecType from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.mainBases import RtlSignalBase from hwt.pyUtils.setList import SetList from hwt.pyUtils.typingFuture import override class HdlAssignmentContainer(HdlStatement): """ Assignment container :ivar ~.src: source :ivar ~.dst: destination signal :ivar ~.indexes: description of index selector on dst (list of Index/Slice objects) (f.e. [0, 1] means dst[0][1]) :cvar __instCntr: counter used for generating instance ids :ivar ~._instId: internally used only for intuitive sorting of statements """ _DEEPCOPY_SKIP = (*HdlStatement._DEEPCOPY_SKIP, 'src', 'dst', 'indexes') _DEEPCOPY_SHALLOW_ONLY = (*HdlStatement._DEEPCOPY_SHALLOW_ONLY, "indexes") __instCntr = 0 def __init__(self, src: Union[RtlSignalBase, HConst], dst: RtlSignalBase, indexes: Optional[List[Union[RtlSignalBase, HConst]]]=None, virtual_only=False, parentStm: Optional[HdlStatement]=None, parentStmList: Optional[ListOfHdlStatement]=None, sensitivity: Optional[SetList]=None, event_dependent_from_branch:Optional[int]=None): """ :param dst: destination to assign to :param src: source which is assigned from :param indexes: description of index selector on dst (list of Index/Slice objects) (f.e. [[0], [1]] means dst[0][1]) :param virtual_only: flag indicates that this assignments is only virtual and should not be added into netlist, because it is only for internal notation """ super(HdlAssignmentContainer, self).__init__( parentStm, parentStmList, sensitivity, event_dependent_from_branch=event_dependent_from_branch) self._instId = HdlAssignmentContainer._nextInstId() self.src = src self.dst = dst assert isinstance(dst, RtlSignalBase), dst self.indexes = indexes self._collect_inputs() if not virtual_only: for i in self._inputs: i._rtlEndpoints.append(self) dst._rtlDrivers.append(self) dst._rtlCtx.statements.add(self) self._outputs.append(dst) @override def __deepcopy__(self, memo: dict): result = super(HdlAssignmentContainer, self).__deepcopy__(memo) result.src = self.src result.dst = self.dst result._instId = self._nextInstId() return result @internal @override def _collect_inputs(self) -> None: src = self.src if isinstance(src, RtlSignalBase): self._inputs.append(src) indexes = self.indexes if indexes: for i in indexes: if isinstance(i, RtlSignalBase): self._inputs.append(i) @internal @override def _cut_off_drivers_of(self, sig: RtlSignalBase): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._cut_off_drivers_of` """ if self._try_cut_off_whole_stm(sig): return self @internal @override def _discover_enclosure(self) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._discover_enclosure` """ assert self._enclosed_for is None self._enclosed_for = set() self._enclosed_for.update(self._outputs) @internal @override def _discover_sensitivity(self, seen: set) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._discover_sensitivity` """ assert self._sensitivity is None ctx = self._sensitivity = SensitivityCtx() casualSensitivity = set() for inp in self._inputs: if inp not in seen: seen.add(inp) inp._walk_sensitivity(casualSensitivity, seen, ctx) ctx.extend(casualSensitivity) @internal @override def _fill_enclosure(self, enclosure: Dict[RtlSignalBase, HdlStatement]): """ The assignment does not have any uncovered code branches :see: :meth:`hwt.hdl.statements.statement.HdlStatement._fill_enclosure` """ pass @internal @override def _iter_stms(self) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms` """ return yield @internal @override def _iter_stms_for_output(self, output: RtlSignalBase) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms_for_output` """ return yield @internal @override def _on_parent_event_dependent(self): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._on_parent_event_dependent` """ self._event_dependent_from_branch = 0 @internal @override def _try_reduce(self) -> Tuple[ListOfHdlStatement, bool]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._try_reduce` """ return ListOfHdlStatement((self,)), False @internal @override def _is_mergable(self, other: HdlStatement) -> bool: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._is_mergable` """ return isinstance(other, self.__class__) @override def isSame(self, other): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement.isSame` """ if isinstance(other, self.__class__): if isSameHConst(self.dst, other.dst)\ and isSameHConst(self.src, other.src)\ and areSameHConsts(self.indexes, other.indexes): return True return False @internal @override @classmethod def _nextInstId(cls): """ Get next instance id """ i = cls.__instCntr cls.__instCntr += 1 return i @internal @override def _replace_input_nested(self, topStm: HdlStatement, toReplace: SignalReplaceSpecType) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._replace_input` """ didUpdate = False if self.indexes: new_indexes = [] for ind in self.indexes: new_i, _didUpdate = replace_input_in_expr(topStm, self, ind, toReplace) new_indexes.append(new_i) didUpdate |= _didUpdate self.indexes = new_indexes self.src, _didUpdate = replace_input_in_expr(topStm, self, self.src, toReplace) didUpdate |= _didUpdate if didUpdate: self._replace_input_update_sensitivity_and_inputs(toReplace) return didUpdate ================================================ FILE: hwt/hdl/statements/codeBlockContainer.py ================================================ from itertools import compress from typing import List, Set, Tuple, Generator from hwt.doc_markers import internal from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.hdl.statements.utils.reduction import HdlStatement_try_reduce_list from hwt.hdl.statements.utils.signalCut import HdlStatement_cut_off_drivers_of_list from hwt.mainBases import RtlSignalBase from hwt.pyUtils.setList import SetList from hwt.pyUtils.typingFuture import override class HdlStmCodeBlockContainer(HdlStatement): """ Hdl block statement used also to represent a HDL process :ivar ~.name: name used as id in target HDL :ivar ~.statements: list of statements in body of process :note: HdlStmCodeBlockContainer do not have to be process in target HDL, for example simple process which contains only unconditional assignment will be rendered just as assignment. It depends on capabilities of the target HDL. """ def __init__(self): super(HdlStmCodeBlockContainer, self).__init__() self.name = None self.statements = ListOfHdlStatement() self.rank = 0 @internal @classmethod def from_known_io(cls, name: str, statements: ListOfHdlStatement, sensitivity: Set["RtlSignal"], inputs: SetList, outputs: SetList) -> 'HdlStmCodeBlockContainer': self = cls() self.name = name self.statements = statements self._inputs = inputs self._outputs = outputs self._sensitivity = sensitivity self.rank = sum(map(lambda s: s.rank, statements)) return self @internal @override def _try_reduce(self) -> Tuple[List["HdlStatement"], bool]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._try_reduce` """ new_statements, _, io_change = HdlStatement_try_reduce_list(self.statements) return new_statements, io_change @internal @override def _iter_stms(self) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms` """ yield from self.statements @internal @override def _iter_stms_for_output(self, output: RtlSignalBase) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms_for_output` """ yield from self.statements.iterStatementsWithOutput(output) @internal @override def _cut_off_drivers_of(self, sig: RtlSignalBase): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._cut_off_drivers_of` """ if self._try_cut_off_whole_stm(sig): return self # try to cut off all statements which are drivers of specified signal # in all branches child_keep_mask = [] newStatements = [] all_cut_off = True all_cut_off &= HdlStatement_cut_off_drivers_of_list( sig, self.statements, child_keep_mask, newStatements) self.statements = list(compress(self.statements, child_keep_mask)) assert not all_cut_off, "everything was cut of but this should be already known at the start" if newStatements: # parts were cut off # generate new statement for them n = self.__class__(*newStatements) if self.parentStm is None: ctx = n._get_rtl_context() ctx.statements.add(n) self._cut_off_drivers_of_regenerate_io(sig, n) return n @internal @override def _replace_child_statement(self, stm:HdlStatement, replacement:ListOfHdlStatement, update_io:bool) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._replace_child_statement` """ if update_io: raise NotImplementedError() statements: ListOfHdlStatement = self.statements i = statements.index(stm) statements.replace(self, stm, i, replacement) # reset IO because it was shared with this statement stm._destroy() ================================================ FILE: hwt/hdl/statements/ifContainter.py ================================================ from copy import deepcopy from functools import reduce from itertools import compress from operator import and_ from typing import List, Tuple, Dict, Optional, Callable, Set, Generator from hwt.doc_markers import internal from hwt.hdl.operatorUtils import replace_input_in_expr from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.statements.statement import HdlStatement, SignalReplaceSpecType from hwt.hdl.statements.utils.comparison import statementsAreSame, isSameStatementList from hwt.hdl.statements.utils.ioDiscovery import HdlStatement_discover_enclosure_for_statements from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.hdl.statements.utils.reduction import HdlStatement_merge_statement_lists, \ HdlStatement_try_reduce_list, is_mergable_statement_list from hwt.hdl.statements.utils.signalCut import HdlStatement_cut_off_drivers_of_list from hwt.mainBases import RtlSignalBase from hwt.pyUtils.typingFuture import override from hwt.serializer.utils import RtlSignal_sort_key from hwt.synthesizer.rtlLevel.fill_stm_list_with_enclosure import fill_stm_list_with_enclosure from hwt.synthesizer.rtlLevel.rtlSignalWalkers import discover_sensitivity_of_sig class IfContainer(HdlStatement): """ Structural container of if statement for hdl rendering :ivar ~._ifTrue_enclosed_for: set of signals for which if ifTrue branch enclosed (has not branch where signal is not assignment) :ivar ~._elIfs_enclosed_for: list of sets of enclosed signals for each elif :ivar ~._ifFalse_enclosed_for: set of enclosed signals for ifFalse branch """ _DEEPCOPY_SHALLOW_ONLY = (*HdlStatement._DEEPCOPY_SHALLOW_ONLY, '_ifTrue_enclosed_for', '_elIfs_enclosed_for', '_ifFalse_enclosed_for') _DEEPCOPY_SKIP = (*HdlStatement._DEEPCOPY_SKIP, 'cond', 'elIfs') def __init__(self, cond: RtlSignalBase, ifTrue=None, ifFalse=None, elIfs=None, parentStm=None, event_dependent_from_branch: Optional[int]=None): """ :param cond: RtlSignal as conditions for this if :param ifTrue: list of statements which should be active if cond. is met :param elIfs: list of tuples (list of conditions, list of statements) :param ifFalse: list of statements which should be active if cond. and any other cond. in elIfs is met """ assert isinstance(cond, RtlSignalBase) self.cond = cond super(IfContainer, self).__init__( parentStm, event_dependent_from_branch=event_dependent_from_branch) if ifTrue is None: ifTrue = ListOfHdlStatement() self.ifTrue: ListOfHdlStatement = ifTrue if elIfs is None: elIfs = [] self.elIfs: List[Tuple[RtlSignalBase, ListOfHdlStatement]] = elIfs self.ifFalse: Optional[ListOfHdlStatement] = ifFalse self._ifTrue_enclosed_for: Optional[Set[RtlSignalBase]] = None self._elIfs_enclosed_for: Optional[Set[RtlSignalBase]] = None self._ifFalse_enclosed_for: Optional[Set[RtlSignalBase]] = None @override def __deepcopy__(self, memo: dict): result = super(IfContainer, self).__deepcopy__(memo) result.cond = self.cond result.elIfs = [(c, deepcopy(stms, memo)) for c, stms in self.elIfs] return result @internal @override def _collect_io(self): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._collect_io` """ if isinstance(self.cond, RtlSignalBase): self._inputs.append(self.cond) for c, _ in self.elIfs: if isinstance(c, RtlSignalBase): self._inputs.append(c) super(IfContainer, self)._collect_io() @internal @override def _collect_inputs(self) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._collect_inputs` """ if isinstance(self.cond, RtlSignalBase): self._inputs.append(self.cond) for c, _ in self.elIfs: if isinstance(c, RtlSignalBase): self._inputs.append(c) super(IfContainer, self)._collect_inputs() @internal @override def _clean_signal_meta(self): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._clean_signal_meta` """ self._sensitivity = None self._ifTrue_enclosed_for = None self._elIfs_enclosed_for = None self._ifFalse_enclosed_for = None HdlStatement._clean_signal_meta(self) @internal @override def _cut_off_drivers_of(self, sig: RtlSignalBase): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._cut_off_drivers_of` """ if self._try_cut_off_whole_stm(sig): return self # try to cut off all statements which are drivers of specified signal # in all branches child_keep_mask = [] newIfTrue = [] all_cut_off = True all_cut_off &= HdlStatement_cut_off_drivers_of_list( sig, self.ifTrue, child_keep_mask, newIfTrue) self.ifTrue = ListOfHdlStatement(compress(self.ifTrue, child_keep_mask)) newElifs = [] anyElifHit = False for cond, stms in self.elIfs: newCase = [] child_keep_mask.clear() all_cut_off &= HdlStatement_cut_off_drivers_of_list( sig, stms, child_keep_mask, newCase) _stms = list(compress(stms, child_keep_mask)) stms.clear() stms.extend(_stms) if newCase: anyElifHit = True newElifs.append((cond, newCase)) newIfFalse = None if self.ifFalse: newIfFalse = [] child_keep_mask.clear() all_cut_off &= HdlStatement_cut_off_drivers_of_list( sig, self.ifFalse, child_keep_mask, newIfFalse) self.ifFalse = ListOfHdlStatement(compress(self.ifFalse, child_keep_mask)) assert not all_cut_off, "everything was cut of but this should be already known at the start" if newIfTrue or newIfFalse or anyElifHit or newIfFalse: # parts were cut off # generate new statement for them cond_sig = self.cond n = self.__class__(cond_sig, newIfTrue) for c_sig, stms in newElifs: n.Elif(c_sig, stms) if newIfFalse is not None: n.Else(newIfFalse) if self.parentStm is None: ctx = n._get_rtl_context() ctx.statements.add(n) self._cut_off_drivers_of_regenerate_io(sig, n) return n @internal @override def _discover_enclosure(self): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._discover_enclosure` """ outputs = self._outputs self._ifTrue_enclosed_for = HdlStatement_discover_enclosure_for_statements( self.ifTrue, outputs) elif_encls = self._elIfs_enclosed_for = [] for _, stms in self.elIfs: e = HdlStatement_discover_enclosure_for_statements( stms, outputs) elif_encls.append(e) self._ifFalse_enclosed_for = HdlStatement_discover_enclosure_for_statements( self.ifFalse, outputs) assert self._enclosed_for is None encl = self._enclosed_for = set() for s in self._ifTrue_enclosed_for: enclosed = True for elif_e in elif_encls: if s not in elif_e: enclosed = False break if enclosed and s in self._ifFalse_enclosed_for: encl.add(s) @internal @override def _discover_sensitivity(self, seen: set) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._discover_sensitivity` """ assert self._sensitivity is None, self ctx = self._sensitivity = SensitivityCtx() discover_sensitivity_of_sig(self.cond, seen, ctx) if ctx.contains_ev_dependency: return for stm in self.ifTrue: stm._discover_sensitivity(seen) ctx.extend(stm._sensitivity) # elifs for cond, stms in self.elIfs: if ctx.contains_ev_dependency: break discover_sensitivity_of_sig(cond, seen, ctx) if ctx.contains_ev_dependency: break for stm in stms: if ctx.contains_ev_dependency: break stm._discover_sensitivity(seen) ctx.extend(stm._sensitivity) if self.ifFalse: assert not ctx.contains_ev_dependency, "can not negate event" # else for stm in self.ifFalse: stm._discover_sensitivity(seen) ctx.extend(stm._sensitivity) @internal @override def _fill_enclosure(self, enclosure: Dict[RtlSignalBase, Callable[[], HdlStatement]]) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._fill_enclosure` """ enc = [] outputs = self._outputs for e in sorted(enclosure.keys(), key=RtlSignal_sort_key): if e in outputs and e not in self._enclosed_for: enc.append(e) if not enc: return fill_stm_list_with_enclosure(self, self._ifTrue_enclosed_for, self.ifTrue, enc, enclosure) for (_, stms), e in zip(self.elIfs, self._elIfs_enclosed_for): fill_stm_list_with_enclosure(self, e, stms, enc, enclosure) self.ifFalse = fill_stm_list_with_enclosure(self, self._ifFalse_enclosed_for, self.ifFalse, enc, enclosure) self._enclosed_for.update(enc) @internal @override def _iter_stms(self) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms` """ yield from self.ifTrue for _, stms in self.elIfs: yield from stms if self.ifFalse is not None: yield from self.ifFalse @internal @override def _iter_stms_for_output(self, output: RtlSignalBase) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms_for_output` """ yield from self.ifTrue.iterStatementsWithOutput(output) for _, stms in self.elIfs: yield from stms.iterStatementsWithOutput(output) if self.ifFalse is not None: yield from self.ifFalse.iterStatementsWithOutput(output) @internal def _iter_all_elifs(self) -> Generator[Tuple[RtlSignalBase, ListOfHdlStatement], None, None]: yield (self.cond, self.ifTrue) yield from self.elIfs @internal @override def _try_reduce(self) -> Tuple[bool, ListOfHdlStatement]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._try_reduce` """ # flag if IO of statement has changed io_change = False self.ifTrue, rank_decrease, _io_change = HdlStatement_try_reduce_list( self.ifTrue) self.rank -= rank_decrease io_change |= _io_change new_elifs = [] for cond, statements in self.elIfs: _statements, rank_decrease, _io_change = HdlStatement_try_reduce_list( statements) self.rank -= rank_decrease io_change |= _io_change new_elifs.append((cond, _statements)) self.elIfs = new_elifs if self.ifFalse is not None: self.ifFalse, rank_decrease, _io_update_required = HdlStatement_try_reduce_list( self.ifFalse) self.rank -= rank_decrease io_change |= _io_change reduce_self = not self._condHasEffect( self.ifTrue, self.ifFalse, self.elIfs) if reduce_self: res = self.ifTrue else: res = ListOfHdlStatement((self,)) self._on_reduce(reduce_self, io_change, res) # try merge nested ifs as elifs if self.ifFalse is not None and len(self.ifFalse) == 1: child = self.ifFalse[0] if isinstance(child, IfContainer): self._merge_nested_if_from_else(child) return res, io_change @internal def _merge_nested_if_from_else(self, ifStm: "IfContainer"): """ Merge nested IfContarner form else branch to this IfContainer as elif and else branches """ self.elIfs.append((ifStm.cond, ifStm.ifTrue)) self.elIfs.extend(ifStm.elIfs) self.ifFalse = ifStm.ifFalse @internal @override def _is_mergable(self, other: HdlStatement) -> bool: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._is_mergable` """ if not isinstance(other, IfContainer): return False if (self.cond is not other.cond or not is_mergable_statement_list(self.ifTrue, other.ifTrue)): return False if len(self.elIfs) != len(other.elIfs): return False for (a_c, a_stm), (b_c, b_stm) in zip(self.elIfs, other.elIfs): if a_c is not b_c or not is_mergable_statement_list(a_stm, b_stm): return False return is_mergable_statement_list(self.ifFalse, other.ifFalse) @internal @override def _merge_with_other_stm(self, other: "IfContainer") -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._merge_with_other_stm` """ merge = HdlStatement_merge_statement_lists self.ifTrue = merge(self.ifTrue, other.ifTrue) new_elifs = [] for ((c, elifA), (_, elifB)) in zip(self.elIfs, other.elIfs): new_elifs.append((c, merge(elifA, elifB))) self.elIfs = new_elifs self.ifFalse = merge(self.ifFalse, other.ifFalse) other.ifTrue = None other.elIfs = None other.ifFalse = None self._on_merge(other) @internal @staticmethod def _condHasEffect(ifTrue, ifFalse, elIfs): stmCnt = len(ifTrue) # [TODO] condition in empty if stm if ifFalse is not None \ and stmCnt == len(ifFalse) \ and reduce(and_, [len(stm) == stmCnt for _, stm in elIfs], True): for stms in zip(ifTrue, ifFalse, *map(lambda x: x[1], elIfs)): if not statementsAreSame(stms): return True return False return True @override def isSame(self, other: HdlStatement) -> bool: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement.isSame` """ if self is other: return True if self.rank != other.rank: return False if isinstance(other, IfContainer): if self.cond is other.cond: if len(self.ifTrue) == len(other.ifTrue) \ and ((self.ifFalse is None and other.ifFalse is None) or len(self.ifFalse) == len(other.ifFalse)) \ and len(self.elIfs) == len(other.elIfs): if not isSameStatementList(self.ifTrue, other.ifTrue) \ or not isSameStatementList(self.ifFalse, other.ifFalse): return False for (ac, astms), (bc, bstms) in zip(self.elIfs, other.elIfs): if not (ac == bc) or\ not isSameStatementList(astms, bstms): return False return True return False @internal @override def _replace_input_nested(self, topStm: HdlStatement, toReplace: SignalReplaceSpecType) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._replace_input` """ didUpdate = False self.cond, _didUpdate = replace_input_in_expr(topStm, self, self.cond, toReplace) didUpdate |= _didUpdate for stm in self.ifTrue: didUpdate |= stm._replace_input_nested(topStm, toReplace) new_elifs = [] for (cond, stms) in self.elIfs: new_cond, _didUpdate = replace_input_in_expr(topStm, self, cond, toReplace) didUpdate |= _didUpdate for stm in stms: didUpdate |= stm._replace_input_nested(topStm, toReplace) new_elifs.append((new_cond, stms)) self.elIfs = new_elifs if self.ifFalse is not None: for stm in self.ifFalse: _didUpdate = stm._replace_input_nested(topStm, toReplace) didUpdate |= _didUpdate if didUpdate: self._replace_input_update_sensitivity_and_inputs(toReplace) return didUpdate @internal @override def _replace_child_statement(self, stm:HdlStatement, replacement:ListOfHdlStatement, update_io:bool) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._replace_child_statement` """ if update_io: raise NotImplementedError() for branch_list in (self.ifTrue, *(elif_stms for _, elif_stms in self.elIfs), self.ifFalse): if branch_list is None: continue try: i = branch_list.index(stm) except ValueError: # not in list continue branch_list.replace(self, stm, i, replacement) # reset IO because it was shared with this statement stm._destroy() return raise ValueError("Statement", stm, "not found in ", self) ================================================ FILE: hwt/hdl/statements/statement.py ================================================ from copy import deepcopy, copy from itertools import chain from typing import List, Tuple, Union, Optional, Dict, Callable, Generator from hwt.doc_markers import internal from hwt.hdl.hdlObject import HdlObject from hwt.mainBases import RtlSignalBase from hwt.pyUtils.arrayQuery import flatten from hwt.pyUtils.setList import SetList class HwtSyntaxError(Exception): pass SignalReplaceSpecType = Union[Tuple[RtlSignalBase, RtlSignalBase], Dict[RtlSignalBase, RtlSignalBase]] class HdlStatement(HdlObject): """ :ivar ~._event_dependent_from_branch: index of code branch if statement is event (clk) dependent else None :ivar ~.parentStm: parent instance of HdlStatement or None :ivar ~.parentStmList: list in parent statement where this statement is stored :ivar ~._inputs: SetList of input signals for this statement (All input signals which are directly used in any statement. Note that the expression is also the signal.) :ivar ~._outputs: SetList of output signals for this statement :ivar ~._sensitivity: SetList of input signals or (rising/falling) operator :ivar ~._enclosed_for: set of outputs for which this statement is enclosed (for which there is not any unused branch) :ivar ~.rank: number of used branches in statement, used as pre-filter for statement comparing """ _DEEPCOPY_SKIP = ('parentStm', 'parentStmList') _DEEPCOPY_SHALLOW_ONLY = ("_inputs", "_outputs", "_enclosed_for", "_sensitivity") def __init__(self, parentStm:Optional["HdlStatement"]=None, parentStmList: Optional["ListOfHdlStatement"]=None, sensitivity:Optional[SetList]=None, event_dependent_from_branch:Optional[int]=None): assert event_dependent_from_branch is None or isinstance(event_dependent_from_branch, int), event_dependent_from_branch self._event_dependent_from_branch = event_dependent_from_branch self.parentStm = parentStm self.parentStmList = parentStmList self._inputs = SetList() self._outputs = SetList() self._enclosed_for = None self._sensitivity = sensitivity self.rank = 0 def __deepcopy__(self, memo: dict): cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): if v is None: new_v = None elif k in self._DEEPCOPY_SKIP: new_v = None elif k in self._DEEPCOPY_SHALLOW_ONLY: new_v = copy(v) else: new_v = deepcopy(v, memo) setattr(result, k, new_v) return result @internal def _clean_signal_meta(self): """ Clean informations about enclosure for outputs and sensitivity of this statement """ self._enclosed_for = None self._sensitivity = None for stm in self._iter_stms(): stm._clean_signal_meta() @internal def _collect_io(self) -> None: """ Collect inputs/outputs from all child statements to :py:attr:`~_input` / :py:attr:`_output` attribute on this object """ in_add = self._inputs.extend out_add = self._outputs.extend for stm in self._iter_stms(): in_add(stm._inputs) out_add(stm._outputs) if self.parentStmList is not None: for o in self._outputs: self.parentStmList._registerOutput(o, self) @internal def _collect_inputs(self) -> None: """ Collect inputs from all child statements to :py:attr:`~_input` attribute on this object """ in_add = self._inputs.extend for stm in self._iter_stms(): in_add(stm._inputs) @internal def _collect_outputs(self) -> None: """ Collect inputs from all child statements to :py:attr:`_output` attribute on this object """ out_add = self._outputs.extend for stm in self._iter_stms(): out_add(stm._outputs) if self.parentStmList is not None: for o in self._outputs: self.parentStmList._registerOutput(o, self) @internal def _try_cut_off_whole_stm(self, sig: RtlSignalBase) -> bool: """ Try cut of output from statement and if this is only output of statement cut off whole statement. Otherwise prepare for cutting of the signal from this statement. :param sig: signal which drivers should be removed :return: true if was removed or False if pruning of sig from this statement is required """ if self._sensitivity is not None or self._enclosed_for is not None: raise NotImplementedError( "Sensitivity and enclosure has to be cleaned first") if self.parentStmList is not None: self.parentStmList._unregisterOutput(sig, self) if len(self._outputs) == 1 and sig is self._outputs[0]: # this statement has only this output, eject this statement from its parent self.parentStm = None # because new parent will be asigned immediately after cutting of self.parentStmList = None return True sig._rtlDrivers.discard(self) return False @internal def _cut_off_drivers_of(self, sig: RtlSignalBase) -> Union[None, "HdlStatement", List["HdlStatement"]]: """ Cut all logic from statements which drives signal sig. :param sig: signal which drivers should be removed :return: A statement or statement list which was cut off from the original statement """ raise NotImplementedError("This is an abstract method and it should be implemented in child class", self.__class__) @internal def _cut_off_drivers_of_regenerate_io(self, cut_off_sig: RtlSignalBase, cut_of_smt: "HdlStatement"): """ Update _inputs/_outputs after some part of statement was cut of :param cut_off_sig: a signal which driver is a cut_of_stm :param cut_of_smt: the statement wich was cut off from original statement (selected by cut_off_sig) """ # update io of this self._outputs.remove(cut_off_sig) if cut_of_smt._inputs: # update inputs on this self._inputs.clear() self._collect_inputs() if self.parentStm is None: for i in cut_of_smt._inputs: if i not in self._inputs: i._rtlEndpoints.remove(self) if self.parentStm is None: cut_off_sig._rtlDrivers.append(cut_of_smt) if self.parentStmList is not None: self.parentStmList._unregisterOutput(cut_off_sig, self) @internal def _discover_enclosure(self) -> None: """ Discover all outputs for which is this statement enclosed _enclosed_for property (has driver in all code branches) """ raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) @internal def _discover_sensitivity(self, seen: set) -> None: """ discover all sensitivity signals and store them to _sensitivity property """ raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) @internal def _fill_enclosure(self, enclosure: Dict[RtlSignalBase, Callable[[], 'HdlStatement']]) -> None: """ Add assignments to a default values to a code branches which are not assigning to specified output signal. :attention: enclosure has to be discoverd first use _discover_enclosure() method """ raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) @internal def _get_rtl_context(self) -> 'RtlNetlist': """ get RtlNetlist context from signals """ for sig in chain(self._inputs, self._outputs): if sig._rtlCtx: return sig._rtlCtx else: # Param instances does not have context continue raise HwtSyntaxError( "Statement does not have any signal in any context," " it should have at least some output or should be alreary optimized out", self) @internal def _iter_stms(self): """ :return: iterator over all children statements """ raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) @internal def _iter_stms_for_output(self, output: RtlSignalBase) -> Generator["HdlStatement", None, None]: """ :return: iterator of sub statements which have specified output as an output """ return yield @internal def _merge_with_other_stm(self, other: "HdlStatement") -> None: """ :attention: statements has to be mergable (to check use _is_mergable method) """ raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) @internal def _on_reduce(self, self_reduced: bool, io_changed: bool, result_statements: List["HdlStatement"]) -> None: """ Update signal IO after reduce attempt :param self_reduced: if True this object was reduced :param io_changed: if True IO of this object may changed and has to be updated :param result_statements: list of statements which are result of reduce operation on this statement """ parentStm = self.parentStm parentStmList = self.parentStmList if self_reduced: was_top = parentStm is None # update signal drivers/endpoints if was_top: # disconnect self from signals ctx = self._get_rtl_context() ctx.statements.remove(self) ctx.statements.update(result_statements) for i in self._inputs: i._rtlEndpoints.discard(self) for o in self._outputs: o._rtlDrivers.remove(self) for stm in result_statements: stm.parentStm = parentStm stm.parentStmList = parentStmList if was_top: # connect signals to child statements for inp in stm._inputs: inp._rtlEndpoints.append(stm) for outp in stm._outputs: outp._rtlDrivers.append(stm) else: for outp in stm._outputs: if parentStmList is not None: parentStmList._registerOutput(outp, stm) else: # parent has to update it's inputs/outputs if io_changed: if parentStmList is not None: for o in self._outputs: parentStmList._unregisterOutput(o, self) self._inputs = SetList() self._outputs = SetList() self._collect_io() @internal def _on_merge(self, other: "HdlStatement"): """ After merging statements update IO, sensitivity and context :attention: rank is not updated """ self._inputs.extend(other._inputs) if self.parentStmList is not None: for o in other._outputs: if o not in self._outputs: self._outputs.append(o) self.parentStmList._registerOutput(o, self) else: self._outputs.extend(other._outputs) if self._sensitivity is not None: self._sensitivity.extend(other._sensitivity) else: assert other._sensitivity is None if self._enclosed_for is not None: self._enclosed_for.update(other._enclosed_for) else: assert other._enclosed_for is None other_was_top = other.parentStm is None if other_was_top: other._get_rtl_context().statements.remove(other) for s in other._inputs: s._rtlEndpoints.discard(other) s._rtlEndpoints.append(self) for s in other._outputs: s._rtlDrivers.discard(other) s._rtlDrivers.append(self) @internal def _try_reduce(self) -> Tuple[List["HdlStatement"], bool]: raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) def _is_enclosed(self) -> bool: """ :return: True if every branch in statement assignas to all output signals else False """ return len(self._outputs) == len(self._enclosed_for) @internal def _is_mergable(self, other: "HdlStatement") -> bool: if self is other: raise ValueError("Can not merge statement with itself") else: raise NotImplementedError("This method should be implemented" " on class of statement", self.__class__, self) @internal def _on_parent_event_dependent(self): """ After parent statement become event dependent propagate event dependency flag to child statements """ if self._event_dependent_from_branch != 0: self._event_dependent_from_branch = 0 for stm in self._iter_stms(): stm._on_parent_event_dependent() @internal def _set_parent_stm(self, parentStm: "HdlStatement", parentStmList: "ListOfHdlStatements"): """ Assign parent statement and propagate dependency flags if necessary """ assert parentStm is not self.parentStm was_top = self.parentStm is None self.parentStm = parentStm if self._event_dependent_from_branch is None\ and parentStm._event_dependent_from_branch is not None: self._on_parent_event_dependent() topStatement = parentStm parents = [] while True: parents.append(topStatement) if topStatement.parentStm is None: break topStatement = topStatement.parentStm if was_top: for inp in self._inputs: inp._rtlEndpoints.discard(self) inp._rtlEndpoints.append(topStatement) for p in parents: p._inputs.append(inp) for outp in self._outputs: outp._rtlDrivers.discard(self) outp._rtlDrivers.append(topStatement) for p in parents: p._outputs.append(outp) ctx = self._get_rtl_context() ctx.statements.discard(self) parentStm.rank += self.rank self.parentStmList = parentStmList for o in self._outputs: parentStmList._registerOutput(o, self) @internal def _register_stements(self, statements: List["HdlStatement"], target: "ListOfHdlStatements"): """ Append statements to this container """ for stm in flatten(statements): assert stm.parentStm is None, ( "HdlStatement instance has to have only a single parent", stm) stm._set_parent_stm(self, target) target.append(stm) def isSame(self, other: "HdlStatement") -> bool: """ :return: True if other has same meaning as self """ raise NotImplementedError("This method should be implemented in child class", self.__class__, self) @internal def _destroy(self): """ Disconnect this statement from signals and delete it from RtlNetlist context :attention: signal endpoints/drivers will be altered that means they can not be used for iteration """ for i in self._inputs: i._rtlEndpoints.discard(self) if self.parentStm is None: ctx = self._get_rtl_context() for o in self._outputs: o._rtlDrivers.remove(self) ctx.statements.remove(self) self.parentStm = None if self.parentStmList is not None: self.parentStmList = None for o in self._outputs: self.parentStmList._unregisterOutput(o, self) @internal def _replace_input_nested(self, topStm: "HdlStatement", toReplace: SignalReplaceSpecType) -> None: raise NotImplementedError("This method should be implemented in child class", self.__class__, self) @internal def _replace_input(self, toReplace: SignalReplaceSpecType) -> bool: """ Replace input signal with another :return: True if the expression was present in this statement (and was replaced) :note: sensitivity/endpoints are actualized :note: calling on children does not update parent's _inputs, _sensitivity or _enclosed_for """ assert self.parentStm is None, self return self._replace_input_nested(self, toReplace) @internal def _replace_input_update_sensitivity_and_inputs( self, toReplace: SignalReplaceSpecType): """ This function updates _sensitivity and _inputs containers after the input was replaced in this statement. :attention: it does not perform the replace of the input. It should be called after the replace. """ # if we replace something some input expression may be altered thus full re-computation is required self._inputs.clear() self._collect_inputs() if self._sensitivity is not None: self._sensitivity = None self._discover_sensitivity(set()) @internal def _replace_child_statement(self, stm: "HdlStatement", replacement: List["HdlStatement"], update_io: bool) -> None: """ Replace a child statement with a list of other statements :attention: original statement is destroyed and entirely removed from circuit :note: sensitivity/endoints are actualized """ raise NotImplementedError("This method should be implemented in child class", self.__class__, self) ================================================ FILE: hwt/hdl/statements/switchContainer.py ================================================ from copy import deepcopy, copy from functools import reduce from itertools import compress from operator import and_ from typing import List, Tuple, Dict, Optional, Callable, Set, Generator from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.constUtils import isSameHConst from hwt.hdl.operatorUtils import replace_input_in_expr from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.statements.statement import HdlStatement, HwtSyntaxError, SignalReplaceSpecType from hwt.hdl.statements.utils.comparison import isSameStatementList, statementsAreSame from hwt.hdl.statements.utils.ioDiscovery import HdlStatement_discover_enclosure_for_statements from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.hdl.statements.utils.reduction import HdlStatement_merge_statement_lists, \ HdlStatement_try_reduce_list, is_mergable_statement_list from hwt.hdl.statements.utils.signalCut import HdlStatement_cut_off_drivers_of_list from hwt.hdl.types.enum import HEnum from hwt.mainBases import RtlSignalBase from hwt.pyUtils.typingFuture import override from hwt.serializer.utils import RtlSignal_sort_key from hwt.synthesizer.rtlLevel.fill_stm_list_with_enclosure import fill_stm_list_with_enclosure from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class SwitchContainer(HdlStatement): """ Structural container for switch statement for hdl rendering :ivar ~.switchOn: select signal of switch :ivar ~.cases: list of tuples (value, statements) :ivar ~.default: list of statements (for branch "default") :ivar ~._case_value_index: dictionary {value:index} for every case in cases :ivar ~._case_enclosed_for: list of sets of enclosed signal for each case branch :ivar ~._default_enclosed_for: set of enclosed signals for branch default """ _DEEPCOPY_SKIP = (*HdlStatement._DEEPCOPY_SKIP, 'switchOn', 'cases') _DEEPCOPY_SHALLOW_ONLY = (*HdlStatement._DEEPCOPY_SHALLOW_ONLY, '_case_value_index', '_case_enclosed_for', '_default_enclosed_for') def __init__(self, switchOn: RtlSignal, cases: List[Tuple[HConst, ListOfHdlStatement]], default: Optional[ListOfHdlStatement]=None, parentStm: HdlStatement=None, event_dependent_from_branch: Optional[int]=None): super(SwitchContainer, self).__init__( parentStm=parentStm, event_dependent_from_branch=event_dependent_from_branch) self.switchOn = switchOn self.cases = cases self.default = default self._case_value_index = {} for i, (v, _) in enumerate(cases): assert v not in self._case_value_index, v self._case_value_index[v] = i self._case_enclosed_for: Optional[List[Set[RtlSignal]]] = None self._default_enclosed_for: Optional[Set[RtlSignal]] = None @override def __deepcopy__(self, memo: dict): result = super(SwitchContainer, self).__deepcopy__(memo) result.switchOn = self.switchOn result.cases = [(c, deepcopy(stms, memo)) for c, stms in self.cases] result._case_value_index = copy(self._case_value_index) result._case_enclosed_for = copy(self._case_enclosed_for) result._default_enclosed_for = copy(self._default_enclosed_for) return result @internal @override def _cut_off_drivers_of(self, sig: RtlSignalBase): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._cut_off_drivers_of` """ if self._try_cut_off_whole_stm(sig): return self # try to cut off all statements which are drivers of specified signal # in all branches child_keep_mask = [] all_cut_off = True new_default = None if self.default: new_default = ListOfHdlStatement() child_keep_mask.clear() case_eliminated = HdlStatement_cut_off_drivers_of_list( sig, self.default, child_keep_mask, new_default) all_cut_off &= case_eliminated if case_eliminated: self.rank -= 1 self.default = None else: self.default = list(compress(self.default, child_keep_mask)) new_cases = [] case_keepmask = [] for val, stms in self.cases: new_case = ListOfHdlStatement() child_keep_mask.clear() case_eliminated = HdlStatement_cut_off_drivers_of_list( sig, stms, child_keep_mask, new_case) if case_eliminated: self.rank -= 1 all_cut_off &= case_eliminated case_keepmask.append(not case_eliminated) _stms = list(compress(stms, child_keep_mask)) stms.clear() stms.extend(_stms) if new_case or new_default: # if there is a default we need to add case even in empty # to prevent falling to default new_cases.append((val, new_case)) self.cases = list(compress(self.cases, case_keepmask)) assert not all_cut_off, "everything was cut of but this should be already known at start" if new_cases or new_default: # parts were cut off # generate new statement for them sel_sig = self.switchOn n = self.__class__(sel_sig) n.add_cases(new_cases) if new_default: n.Default(*new_default) if self.parentStm is None: ctx = n._get_rtl_context() ctx.statements.add(n) self._cut_off_drivers_of_regenerate_io(sig, n) return n @internal @override def _clean_signal_meta(self): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._clean_signal_meta` """ self._case_enclosed_for = None self._default_enclosed_for = None HdlStatement._clean_signal_meta(self) @internal @override def _collect_io(self): """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._collect_io` """ if isinstance(self.switchOn, RtlSignalBase): self._inputs.append(self.switchOn) for c, _ in self.cases: if isinstance(c, RtlSignalBase): self._inputs.append(c) super(SwitchContainer, self)._collect_io() @internal @override def _collect_inputs(self) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._collect_inputs` """ if isinstance(self.switchOn, RtlSignalBase): self._inputs.append(self.switchOn) for c, _ in self.cases: if isinstance(c, RtlSignalBase): self._inputs.append(c) super(SwitchContainer, self)._collect_inputs() @internal @override def _discover_enclosure(self) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._discover_enclosure` """ assert self._enclosed_for is None enclosure = self._enclosed_for = set() case_enclosures = self._case_enclosed_for = [] outputs = self._outputs for _, stms in self.cases: c_e = HdlStatement_discover_enclosure_for_statements(stms, outputs) case_enclosures.append(c_e) self._default_enclosed_for = HdlStatement_discover_enclosure_for_statements( self.default, outputs) t = self.switchOn._dtype if not self.default and len(self.cases) < t.domain_size(): # cases does not cover all branches return for s in outputs: enclosed = True for e in case_enclosures: if s not in e: enclosed = False break if enclosed and (not self.default or s in self._default_enclosed_for): enclosure.add(s) @internal @override def _discover_sensitivity(self, seen) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._discover_sensitivity` """ assert self._sensitivity is None, self ctx = self._sensitivity = SensitivityCtx() casual_sensitivity = set() self.switchOn._walk_sensitivity(casual_sensitivity, seen, ctx) if ctx.contains_ev_dependency: raise HwtSyntaxError( "Can not switch on event operator result", self.switchOn) ctx.extend(casual_sensitivity) for stm in self._iter_stms(): stm._discover_sensitivity(seen) ctx.extend(stm._sensitivity) @internal @override def _fill_enclosure(self, enclosure: Dict[RtlSignalBase, Callable[[], HdlStatement]]) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._fill_enclosure` """ select = [] outputs = self._outputs for e in sorted(enclosure.keys(), key=RtlSignal_sort_key): if e in outputs: select.append(e) for (_, stms), e in zip(self.cases, self._case_enclosed_for): fill_stm_list_with_enclosure(self, e, stms, select, enclosure) e.update(select) t = self.switchOn._dtype default_required = len(self.cases) < t.domain_size() if self.default is not None or default_required: self.default = fill_stm_list_with_enclosure( self, self._default_enclosed_for, self.default, select, enclosure) self._default_enclosed_for.update(select) self._enclosed_for.update(select) @internal @override def _iter_stms(self) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms` """ for _, stms in self.cases: yield from stms if self.default is not None: yield from self.default @internal @override def _iter_stms_for_output(self, output: RtlSignalBase) -> Generator[HdlStatement, None, None]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._iter_stms_for_output` """ for _, stms in self.cases: yield from stms.iterStatementsWithOutput(output) if self.default is not None: yield from self.default.iterStatementsWithOutput(output) @internal @override def _is_mergable(self, other) -> bool: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._is_mergable` """ if not isinstance(other, SwitchContainer): return False if not (self.switchOn is other.switchOn and len(self.cases) == len(other.cases) and is_mergable_statement_list(self.default, other.default)): return False for (vA, caseA), (vB, caseB) in zip(self.cases, other.cases): if vA != vB or not is_mergable_statement_list(caseA, caseB): return False return True @internal @override def _merge_with_other_stm(self, other: "SwitchContainer") -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._merge_with_other_stm` """ merge = HdlStatement_merge_statement_lists newCases = [] for (c, caseA), (_, caseB) in zip(self.cases, other.cases): newCases.append((c, merge(caseA, caseB))) self.cases = newCases other.cases = None self.default = merge(self.default, other.default) other.default = None self._on_merge(other) @internal @override def _try_reduce(self) -> Tuple[List["HdlStatement"], bool]: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._try_reduce` """ io_change = False # try reduce the content of the case branches new_cases = [] for val, statements in self.cases: _statements, rank_decrease, _io_change = HdlStatement_try_reduce_list( statements) io_change |= _io_change self.rank -= rank_decrease new_cases.append((val, _statements)) self.cases = new_cases # try reduce content of the defult branch if self.default is not None: self.default, rank_decrease, _io_change = HdlStatement_try_reduce_list( self.default) self.rank -= rank_decrease io_change |= _io_change # try reduce self reduce_self = not self._condHasEffect() if reduce_self: if self.cases: res = self.cases[0][1] elif self.default is not None: res = self.default else: res = [] else: res = [self, ] self._on_reduce(reduce_self, io_change, res) if not self.default: t = self.switchOn._dtype if isinstance(t, HEnum): dom_size = t.domain_size() val_cnt = len(t._allValues) if len(self.cases) == val_cnt and val_cnt < dom_size: # bit representation is not fully matching enum description # need to set last case as default to prevent latches _, stms = self.cases.pop() self.default = stms return res, io_change @internal def _condHasEffect(self) -> bool: """ :return: True if statements in branches has different effect """ if not self.cases: return False # [TODO] type_domain_covered = bool(self.default) or len( self.cases) == self.switchOn._dtype.domain_size() stmCnt = len(self.cases[0][1]) if type_domain_covered and reduce( and_, [len(stm) == stmCnt for _, stm in self.cases], True) and (self.default is None or len(self.default) == stmCnt): stms = list(self._iter_stms()) if statementsAreSame(stms): return False else: return True return True @internal @override def _replace_input_nested(self, topStm: "HdlStatement", toReplace: SignalReplaceSpecType) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._replace_input` """ didUpdate = False self.switchOn, _didUpdate = replace_input_in_expr(topStm, self, self.switchOn, toReplace) didUpdate |= _didUpdate for (_, stms) in self.cases: for stm in stms: stm: HdlStatement didUpdate |= stm._replace_input_nested(topStm, toReplace) if self.default is not None: for stm in self.default: didUpdate |= stm._replace_input_nested(topStm, toReplace) if didUpdate: self._replace_input_update_sensitivity_and_inputs(toReplace) return didUpdate @internal @override def _replace_child_statement(self, stm: HdlStatement, replacement:ListOfHdlStatement, update_io:bool) -> None: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement._replace_child_statement` """ if update_io: raise NotImplementedError() for branch_list in (*(case_stms for _, case_stms in self.cases), self.default): if branch_list is None: continue try: i = branch_list.index(stm) except ValueError: # not in list continue branch_list.replace(self, stm, i, replacement) # reset IO because it was shared with this statement stm._destroy() return raise ValueError("Statement", stm, "not found in ", self) @override def isSame(self, other: HdlStatement) -> bool: """ :see: :meth:`hwt.hdl.statements.statement.HdlStatement.isSame` """ if self is other: return True if self.rank != other.rank: return False if isinstance(other, SwitchContainer) \ and isSameHConst(self.switchOn, other.switchOn)\ and len(self.cases) == len(other.cases)\ and isSameStatementList(self.default, other.default): for (ac, astm), (bc, bstm) in zip(self.cases, other.cases): if not isSameHConst(ac, bc)\ or not isSameStatementList(astm, bstm): return False return True return False ================================================ FILE: hwt/hdl/statements/utils/__init__.py ================================================ ================================================ FILE: hwt/hdl/statements/utils/comparison.py ================================================ from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement def isSameStatementList(stmListA: ListOfHdlStatement, stmListB: ListOfHdlStatement) -> bool: """ :return: True if two lists of HdlStatement instances are same """ if stmListA is stmListB: return True if stmListA is None or stmListB is None: return False for a, b in zip(stmListA, stmListB): if not a.isSame(b): return False return True def statementsAreSame(statements: ListOfHdlStatement) -> bool: """ :return: True if all statements are same """ iterator = iter(statements) try: first = next(iterator) except StopIteration: return True return all(first.isSame(rest) for rest in iterator) ================================================ FILE: hwt/hdl/statements/utils/ioDiscovery.py ================================================ from typing import List, Set from hwt.doc_markers import internal from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.mainBases import RtlSignalBase @internal def HdlStatement_discover_enclosure_for_statements( statements: ListOfHdlStatement, outputs: List[RtlSignalBase]) -> Set[RtlSignalBase]: """ Discover enclosure for list of statements :param statements: list of statements in one code branch :param outputs: list of outputs which should be driven from this statement list :return: set of signals for which this statement list have always some driver (is enclosed) """ result = set() if not statements: return result for stm in statements: stm._discover_enclosure() for o in outputs: has_driver = False for stm in statements.iterStatementsWithOutput(o): assert not has_driver has_driver = False result.update(stm._enclosed_for) return result ================================================ FILE: hwt/hdl/statements/utils/listOfHdlStatements.py ================================================ from copy import deepcopy from itertools import islice from typing import Sequence, Dict, List, Union from hwt.doc_markers import internal from hwt.hdl.statements.statement import HdlStatement from hwt.constants import NOT_SPECIFIED from hwt.mainBases import RtlSignalBase class ListOfHdlStatement(list): """ A list of hdl statements used in statements to keep track of children """ def __init__(self, *args): list.__init__(self) self.firstStmWithBranchesI = None self._outputToStatementList: Dict[RtlSignalBase, List[HdlStatement]] = {} if args: assert len(args) == 1, ("expected at most 1 argument", args) self.extend(args[0]) def append(self, v: HdlStatement): if self.firstStmWithBranchesI is None and v.rank > 0: self.firstStmWithBranchesI = len(self) for o in v._outputs: self._registerOutput(o, v) return list.append(self, v) def extend(self, stms: Sequence[HdlStatement]): for v in stms: self.append(v) def insert(self, i: int, v: HdlStatement): assert isinstance(i, int), i res = list.insert(self, i, v) self.firstStmWithBranchesI = NOT_SPECIFIED for o in v._outputs: self._registerOutput(o, v) return res def pop(self): raise NotImplementedError() def remove(self, item): raise NotImplementedError() def discard(self, item): raise NotImplementedError() def iterStatementsWithOutput(self, out: RtlSignalBase): yield from self._outputToStatementList.get(out, ()) def _unregisterOutput(self, o: RtlSignalBase, stm: HdlStatement): self._outputToStatementList[o].remove(stm) def _registerOutput(self, o: RtlSignalBase, stm: HdlStatement): self._outputToStatementList.setdefault(o, []).append(stm) def __setitem__(self, index:Union[int, slice], value:Union["ListOfHdlStatement", HdlStatement]): cur = self[index] if isinstance(index, int): for o in cur._outputs: self._outputToStatementList[o].remove(cur) else: for _cur in cur: for o in _cur._outputs: self._outputToStatementList[o].remove(_cur) res = list.__setitem__(self, index, value) self.firstStmWithBranchesI = NOT_SPECIFIED if isinstance(index, int): for o in value._outputs: self._registerOutput(o, value) else: for v in value: for o in v._outputs: self._registerOutput(o, v) return res @internal def _iter_stms_with_branches(self): """ :return: iterate statement with rank > 0 """ startI = self.firstStmWithBranchesI if startI is None: return elif startI is NOT_SPECIFIED: # recompute firstStmWithBranchesI startI = None for i, stm in enumerate(self): if stm.rank == 0: continue if startI is None: startI = i self.firstStmWithBranchesI = startI else: # use known firstStmWithBranchesI to skip not interesting for stm in islice(self, startI, None): if stm.rank == 0: continue yield stm def sort(self, *args, **kwargs): res = list.sort(self, *args, **kwargs) self.firstStmWithBranchesI = None return res def replace(self, parentStm: HdlStatement, toReplaceStm: HdlStatement, index: int, replacement: List[HdlStatement]): """ Replace a single statement in this list with the list of statements while updating all cached values. """ parentStm.rank -= toReplaceStm.rank self[index:index + 1] = replacement for o in toReplaceStm._outputs: self._unregisterOutput(o, toReplaceStm) for rstm in replacement: for o in rstm._outputs: self._registerOutput(o, rstm) rstm._set_parent_stm(parentStm, self) def __deepcopy__(self, memo: dict): cls = self.__class__ result = cls(deepcopy(i, memo) for i in self) memo[id(self)] = result return result ================================================ FILE: hwt/hdl/statements/utils/reduction.py ================================================ from itertools import islice, zip_longest from typing import Tuple from hwt.doc_markers import internal from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.pyUtils.arrayQuery import groupedby from hwt.constants import NOT_SPECIFIED @internal def HdlStatement_merge_statement_lists(stmsA: ListOfHdlStatement, stmsB: ListOfHdlStatement)\ ->ListOfHdlStatement: """ Merge two lists of statements into one :return: list of merged statements """ if stmsA is None and stmsB is None: return None elif stmsA is None: return stmsB elif stmsB is None: return stmsA tmp = ListOfHdlStatement() # copy all with already known not to merge if stmsA.firstStmWithBranchesI is NOT_SPECIFIED: # we need to check items ourselfs a_it = iter(stmsA) elif stmsA.firstStmWithBranchesI is not None: # we know the first item which requires extra care, we can copy all predecessors tmp.extend(islice(stmsA, 0, stmsA.firstStmWithBranchesI)) a_it = islice(stmsA, stmsA.firstStmWithBranchesI, None) else: # items do no require any care, we can copy them all, but instead we use original list # to avoid copy tmp = stmsA a_it = iter(tuple()) if stmsB.firstStmWithBranchesI is not None: tmp.extend(islice(stmsB, 0, stmsB.firstStmWithBranchesI)) b_it = islice(stmsB, stmsB.firstStmWithBranchesI, None) else: b_it = iter(stmsB) a = None b = None a_empty = False b_empty = False while not a_empty and not b_empty: while not a_empty: a = next(a_it, None) if a is None: a_empty = True break elif a.rank == 0: # simple statement does not require merging tmp.append(a) a = None else: break while not b_empty: b = next(b_it, None) if b is None: b_empty = True break elif b.rank == 0: # simple statement does not require merging tmp.append(b) b = None else: break if a is not None or b is not None: if a is None: a = b b = None if a is not None and b is not None: a._merge_with_other_stm(b) tmp.append(a) a = None b = None return tmp @internal def HdlStatement_try_reduce_list(statements: ListOfHdlStatement)\ ->Tuple[ListOfHdlStatement, int, bool]: """ Simplify statements in the list """ io_change = False new_statements = ListOfHdlStatement() for stm in statements: reduced, _io_change = stm._try_reduce() new_statements.extend(reduced) io_change |= _io_change new_statements, rank_decrease = HdlStatement_merge_statements( new_statements) new_statements, io_change, _rank_decrease = HdlStatement_reduce_overridden_assignments(new_statements) rank_decrease += _rank_decrease return new_statements, rank_decrease, io_change @internal def HdlStatement_reduce_overridden_assignments(statements: ListOfHdlStatement)\ ->Tuple[ListOfHdlStatement, bool, int]: io_change = False new_statements = [] rank_decrease = 0 fully_driven_outputs = set() for stm in reversed(statements): if fully_driven_outputs.issuperset(stm._outputs): rank_decrease += stm.rank io_change = True continue if isinstance(stm, HdlAssignmentContainer): fully_driven_outputs.update(stm._outputs) new_statements.append(stm) return ListOfHdlStatement(reversed(new_statements)), io_change, rank_decrease @internal def HdlStatement_merge_statements(statements: ListOfHdlStatement)\ ->Tuple[ListOfHdlStatement, int]: """ Merge statements in list to remove duplicated if-then-else trees :return: tuple (list of merged statements, rank decrease due merging) :note: rank decrease is sum of ranks of reduced statements :attention: statement list has to me mergable """ order = {} for i, stm in enumerate(statements): order[stm] = i new_statements = ListOfHdlStatement() rank_decrease = 0 for rank, stms in groupedby(statements, lambda s: s.rank): if rank == 0: new_statements.extend(stms) else: if len(stms) == 1: new_statements.extend(stms) continue # try to merge statements if they are same condition tree for iA, stmA in enumerate(stms): if stmA is None: continue for iB, stmB in enumerate(islice(stms, iA + 1, None)): if stmB is None: continue if stmA._is_mergable(stmB): rank_decrease += stmB.rank stmA._merge_with_other_stm(stmB) stms[iA + 1 + iB] = None new_statements.append(stmA) new_statements.sort(key=lambda stm: order[stm]) return new_statements, rank_decrease @internal def is_mergable_statement_list(stmsA: ListOfHdlStatement, stmsB: ListOfHdlStatement): """ Walk statements and compare if they can be merged into one statement list """ if stmsA is None and stmsB is None: return True elif stmsA is None or stmsB is None: return False # [todo] there is a performance error when the list has no statements with rank != 0 # all items needs to be checked everytime, for "rtl register if (clk)" statements # this is a problem as this list can grow large (100K+ items) and needs to be compared with every # not yet merged statement for (a, b) in zip_longest(stmsA._iter_stms_with_branches(), stmsB._iter_stms_with_branches()): if a is None or b is None or not a._is_mergable(b): return False # lists are empty return True ================================================ FILE: hwt/hdl/statements/utils/signalCut.py ================================================ from typing import List from hwt.doc_markers import internal from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.mainBases import RtlSignalBase @internal def HdlStatement_cut_off_drivers_of_list(sig: RtlSignalBase, statements: ListOfHdlStatement, keep_mask: List[bool], new_statements: ListOfHdlStatement) -> bool: """ Cut all logic from statements which drives signal sig. :param sig: signal which drivers should be removed :param statements: list of statements to filter :param keep_mask: list of flags if True statements was driver only of sig :param new_statements: output list of filtered statements :return: True if all input statements were reduced """ all_cut_off = True for stm in statements: if sig in stm._outputs: newStm = stm._cut_off_drivers_of(sig) keep = True if newStm is None: # statement is des not have drivers of sig all_cut_off = False elif newStm is stm: # statement drives only sig keep = False new_statements.append(newStm) else: # statement was splited on multiple statements all_cut_off = False new_statements.append(newStm) else: all_cut_off = False keep = True keep_mask.append(keep) return all_cut_off ================================================ FILE: hwt/hdl/transPart.py ================================================ from typing import Tuple, Optional from hwt.hdl.transTmpl import TransTmpl class TransPart(object): """ Container for informations about parts of TransTmpl split on databus words :ivar ~.parent: instance of FrameTmpl :ivar ~.tmpl: origin template which is this representation of (StructField/HdlType instance) :ivar ~.canBeRemoved: True if it is padding to assert data alignment and is not actually part of data and can be removed :ivar ~.isPadding: True if this TransaPart is just a padding and contains non valid data :ivar ~.startOfPart: bit addr of start of this part :ivar ~.endOfPart: bit addr of end of this part :ivar ~.inFieldOffset: bit offset of this part in parent field """ def __init__(self, parent: 'FrameTmpl', tmpl: Optional[TransTmpl], canBeRemoved: bool, startOfPart: int, endOfPart: int, inFieldOffset: int): self.parent = parent self.tmpl = tmpl # assert tmpl is None or isinstance(tmpl, TransTmpl), tmpl self.isPadding = tmpl is None self.canBeRemoved = canBeRemoved self.startOfPart = startOfPart self.endOfPart = endOfPart self.inFieldOffset = inFieldOffset def bit_length(self) -> int: """ :return: bit length of this part """ return self.endOfPart - self.startOfPart def getBusWordBitRange(self) -> Tuple[int, int]: """ :return: bit range which contains data of this part on bus data signal """ offset = self.startOfPart % self.parent.wordWidth return (offset + self.bit_length(), offset) def getFieldBitRange(self) -> Tuple[int, int]: """ :return: bit range which contains data of this part on interface of field """ offset = self.inFieldOffset return (self.bit_length() + offset, offset) def isLastPart(self) -> bool: """ :return: True if this part is last in parts derived from original field else False """ return self.tmpl.bitAddrEnd == self.endOfPart def __repr__(self): return f"<{self.__class__.__name__:s} {self.tmpl}, startOfPart:{self.startOfPart:d}, endOfPart:{self.endOfPart:d}>" ================================================ FILE: hwt/hdl/transTmpl.py ================================================ from builtins import isinstance from copy import deepcopy from typing import Callable, Tuple, Generator, Union, Optional from hwt.doc_markers import internal from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.stream import HStream from hwt.hdl.types.struct import HStruct, HStructField from hwt.hdl.types.union import HUnion from hwt.pyUtils.arrayQuery import iter_with_last from hwt.synthesizer.typePath import TypePath def _default_shouldEnterFn(transTmpl: 'TransTmpl') -> Tuple[bool, bool]: return (bool(transTmpl.children), not bool(transTmpl.children)) class TransTmpl(object): """ Transaction template for types of constant size Container of informations about frames generated from any HType (HStruct etc.) * contains precalculated address range for all members of type :note: Array/Stream items are are stored as a single instance so the memory consumption of this object is entirely independent on size of arrays which it describes. :ivar ~.dtype: type of this item :ivar ~.bitAddr: offset of start of this item in bits :ivar ~.bitAddrEnd: end of this item in bits :ivar ~.parent: object which generated this item, optional TransTmpl :ivar ~.origin: object which was template for generating of this item :ivar ~.itemCnt: if this transaction template is for array or stream this is item count for such an array or stream :ivar ~.childrenAreChoice: flag which tells if children are sequence or only one of them can be used in same time :ivar rel_field_path: path in original data type relative to parent """ def __init__(self, dtype: HdlType, bitAddr: int=0, parent: Optional['TransTmpl']=None, origin: Optional[HStructField]=None, rel_field_path: TypePath=TypePath()): self.parent = parent assert isinstance(dtype, HdlType), dtype assert isinstance(rel_field_path, TypePath), rel_field_path assert parent is None or isinstance(parent, TransTmpl), parent if origin is None: origin = (dtype,) else: assert isinstance(origin, tuple), origin self.origin = origin self.dtype = dtype self.children = [] self.itemCnt = None self.rel_field_path = rel_field_path self._loadFromHType(dtype, bitAddr) @internal def _loadFromBits(self, dtype: HdlType, bitAddr: int): """ Parse HBits type to this transaction template instance :return: address of it's end """ return bitAddr + dtype.bit_length() @internal def _loadFromHStruct(self, dtype: HdlType, bitAddr: int): """ Parse HStruct type to this transaction template instance :return: address of it's end """ for f in dtype.fields: t = f.dtype isPadding = f.name is None if isPadding: width = t.bit_length() bitAddr += width else: origin = (*self.origin, f) fi = TransTmpl(t, bitAddr, parent=self, origin=origin, rel_field_path=TypePath(f.name,), ) self.children.append(fi) bitAddr = fi.bitAddrEnd return bitAddr @internal def _loadFromUnion(self, dtype: HdlType, bitAddr: int) -> int: """ Parse HUnion type to this transaction template instance :return: address of it's end """ for f in dtype.fields.values(): ch = TransTmpl(f.dtype, 0, parent=self, origin=(*self.origin, f), rel_field_path=TypePath(f.name,), ) self.children.append(ch) return bitAddr + dtype.bit_length() @internal def _loadFromArray(self, dtype: HdlType, bitAddr: int) -> int: """ Parse HArray type to this transaction template instance :return: address of it's end """ self.itemCnt = int(dtype.size) self.children = TransTmpl( dtype.element_t, 0, parent=self, origin=(*self.origin, 0), rel_field_path=TypePath(0,) ) return bitAddr + self.itemCnt * self.children.bitAddrEnd @internal def _loadFromHStream(self, dtype: HStream, bitAddr: int) -> int: """ Parse HStream type to this transaction template instance :return: address of it's end """ self.children = TransTmpl( dtype.element_t, 0, parent=self, origin=self.origin, rel_field_path=TypePath(0,)) if not isinstance(dtype.len_min, int) or dtype.len_min != dtype.len_max: raise ValueError("This template is ment only" " for types of constant and finite size") self.itemCnt = dtype.len_min return bitAddr + dtype.element_t.bit_length() * self.itemCnt def _loadFromHType(self, dtype: HdlType, bitAddr: int) -> None: """ Parse any HDL type to this transaction template instance """ self.bitAddr = bitAddr childrenAreChoice = False if isinstance(dtype, HBits): ld = self._loadFromBits elif isinstance(dtype, HStruct): ld = self._loadFromHStruct elif isinstance(dtype, HArray): ld = self._loadFromArray elif isinstance(dtype, HStream): ld = self._loadFromHStream elif isinstance(dtype, HUnion): ld = self._loadFromUnion childrenAreChoice = True else: raise TypeError("expected instance of HdlType", dtype) self.bitAddrEnd = ld(dtype, bitAddr) self.childrenAreChoice = childrenAreChoice def bit_length(self) -> int: """ :return: number of bits in this transaction """ return self.bitAddrEnd - self.bitAddr def HwIO_walkFlatten(self, offset: int=0, shouldEnterFn=_default_shouldEnterFn ) -> Generator[ Union[Tuple[Tuple[int, int], 'TransTmpl'], 'OneOfTransaction'], None, None]: """ Walk fields in instance of TransTmpl :param offset: optional offset for all children in this TransTmpl :param shouldEnterFn: function (transTmpl) which returns True when field should be split on it's children :param shouldEnterFn: function(transTmpl) which should return (shouldEnter, shouldUse) where shouldEnter is flag that means iterator should look inside of this actual object and shouldUse flag means that this field should be used (=generator should yield it) :return: generator of tuples ((startBitAddress, endBitAddress), TransTmpl instance) """ t = self.dtype base = self.bitAddr + offset end = self.bitAddrEnd + offset shouldEnter, shouldYield = shouldEnterFn(self) if shouldYield: yield ((base, end), self) if shouldEnter: if isinstance(t, HBits): pass elif isinstance(t, HStruct): for c in self.children: yield from c.HwIO_walkFlatten( offset, shouldEnterFn) elif isinstance(t, (HArray, HStream)): itemSize = (self.bitAddrEnd - self.bitAddr) // self.itemCnt for i in range(self.itemCnt): if i == 0: c = self.children else: # spot a new array item c = deepcopy(self.children) assert c.rel_field_path == (0,), c.rel_field_path # replace the index c.rel_field_path = TypePath(i, ) yield from c.HwIO_walkFlatten( base + i * itemSize, shouldEnterFn) elif isinstance(t, HUnion): yield OneOfTransaction(self, offset, shouldEnterFn, self.children) else: raise TypeError(t) def getFieldPath(self): """ Get field path which specifies the location in original HdlType data type """ path = [] tmpl = self while tmpl is not None: path.extend(reversed(tmpl.rel_field_path)) tmpl = tmpl.parent return TypePath(*reversed(path)) def __deepcopy__(self, memo): cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): setattr(result, k, deepcopy(v, memo)) c = self.children if isinstance(c, TransTmpl): c.parent = self else: for _c in c: _c.parent = self return result def __repr__(self, offset: int=0): offsetStr = "".join([" " for _ in range(offset)]) try: name = self.origin[-1].name except (AttributeError, IndexError): name = None if name: name = f" name:{name:s}," else: name = "" s = f"{offsetStr:s}" ] return "".join(s_buff) elif not self.children: return s + ">" buff = [s, ] for isLast, child in iter_with_last(self.children): buff.append(child.__repr__(offset=offset + 1)) if self.childrenAreChoice and not isLast: buff.append(offsetStr + " ") buff.append(offsetStr + ">") return "\n".join(buff) class OneOfTransaction(object): """ Container of possible transactions for transactions deriverd from HUnion type :ivar ~.parent: parent TransTmpl instance :ivar ~.offset: bit addr offset in parent type structure :ivar ~.shouldEnterFn: function(transTmpl) which should return (shouldEnter, shouldUse) where shouldEnter is flag that means iterator should look inside of this actual object and shouldUse flag means that this field should be used (=generator should yield it) :ivar ~.possibleTransactions: tuple of TransTmpl instances from which only one can be used in same time """ def __init__(self, parent: TransTmpl, offset: int, shouldEnterFn: Callable[[TransTmpl], Tuple[bool, bool]], possibleTransactions: Tuple[TransTmpl]): self.parent = parent self.offset = offset self.shouldEnterFn = shouldEnterFn self.possibleTransactions = possibleTransactions def walkFlattenChilds(self) -> Generator[ Union[Tuple[Tuple[int, int], TransTmpl], 'OneOfTransaction'], None, None]: """ :return: generator of generators of tuples ((startBitAddress, endBitAddress), TransTmpl instance) for each possiblility in this transaction """ for p in self.possibleTransactions: yield p.HwIO_walkFlatten(offset=self.offset, shouldEnterFn=self.shouldEnterFn) ================================================ FILE: hwt/hdl/types/__init__.py ================================================ """ This package contains HDL types (e.g. Bits), value classes and conversion functions for them. """ ================================================ FILE: hwt/hdl/types/array.py ================================================ from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.mainBases import RtlSignalBase from hwt.pyUtils.typingFuture import override class HArray(HdlType): """ HDL array type :ivar ~.element_t: type of elements :ivar ~.size: number of items """ def __init__(self, element_t, size, const=False): super(HArray, self).__init__(const=const) self.element_t = element_t self.size = size def __eq__(self, other): return self is other or ( type(self) is type(other) and self.size == other.size and self.element_t == other.element_t ) @internal def __hash__(self): return hash((self.const, self.element_t, self.size)) def bit_length(self): """ :return: bit width for this type """ try: itemSize = self.element_t.bit_length except AttributeError: itemSize = None if itemSize is None: raise TypeError( "Can not determine size of array because item has" " not determinable size") s = self.size if isinstance(s, RtlSignalBase): s = int(s.staticEval()) return s * itemSize() @internal @override @classmethod def get_reinterpret_cast_HConst_fn(cls): from hwt.hdl.types.arrayCast import reinterpret_cast_HArray return reinterpret_cast_HArray @internal @override @classmethod def get_reinterpret_cast_RtlSignal_fn(cls): from hwt.hdl.types.arrayCast import reinterpret_cast_HArray return reinterpret_cast_HArray @internal @override @classmethod def getConstCls(cls): try: return cls._constCls except AttributeError: from hwt.hdl.types.arrayConst import HArrayConst cls._constCls = HArrayConst return cls._constCls @internal @override @classmethod def getRtlSignalCls(cls): try: return cls._rtlSignalCls except AttributeError: from hwt.hdl.types.arrayConst import HArrayRtlSignal cls._rtlSignalCls = HArrayRtlSignal return cls._rtlSignalCls @override def isScalar(self): return False def __repr__(self, indent=0, withAddr=None, expandStructs=False): """ :param indent: number of indentation :param withAddr: if is not None is used as a additional information about on which address this type is stored (used only by HStruct) :param expandStructs: expand HStructTypes (used by HStruct and Array) """ return "%s[%r]" % (self.element_t.__repr__(indent=indent, withAddr=withAddr, expandStructs=expandStructs), self.size) ================================================ FILE: hwt/hdl/types/arrayCast.py ================================================ from typing import Union, Optional from hwt.code import Concat from hwt.doc_markers import internal from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import default_reinterpret_cast_fn, HdlType from hwt.hdl.types.struct import HStruct from hwt.synthesizer.exceptions import TypeConversionErr from pyMathBitPrecise.bit_utils import get_bit_range, mask HArrayAnyValue = Union["HArrayConst", "HArrayRtlSignal"] @internal def getBits_from_array(array: HArrayAnyValue, wordWidth: int, start: int, end: int, reinterpretElmToType: Optional[HdlType]=None): """ Gets value of bits between selected range from memory :param start: bit address of start of bit of bits :param end: bit address of first bit behind bits :return: instance of BitsVal (derived from SimBits type) which contains copy of selected bits """ inPartOffset = 0 value = HBits(end - start, None).from_py(None) while start != end: assert start < end, (start, end) dataWordIndex = start // wordWidth v = array[dataWordIndex] if reinterpretElmToType is not None: v = v._reinterpret_cast(reinterpretElmToType) endOfWord = (dataWordIndex + 1) * wordWidth width = min(end, endOfWord) - start offset = start % wordWidth val = get_bit_range(v.val, offset, width) vld_mask = get_bit_range(v.vld_mask, offset, width) m = mask(width) value.val |= (val & m) << inPartOffset value.vld_mask |= (vld_mask & m) << inPartOffset inPartOffset += width start += width return value @internal def reinterptet_HArray_to_HBits(typeFrom: HArray, sigOrConst: HArrayAnyValue, bitsT: HBits): """ Cast HArray signal or value to signal or value of type HBits """ size = int(typeFrom.size) widthOfElm = typeFrom.element_t.bit_length() w = bitsT.bit_length() if size * widthOfElm != w: raise TypeConversionErr( "Size of types is different", size * widthOfElm, w) partT = HBits(widthOfElm) parts = [p._reinterpret_cast(partT) for p in sigOrConst] return Concat(*reversed(parts))._reinterpret_cast(bitsT) @internal def reinterpret_HArray_to_HArray(typeFrom: HArray, sigOrConst: HArrayAnyValue, arrayT: HArray): mySize = int(typeFrom.size) myWidthOfElm = typeFrom.element_t.bit_length() size = int(arrayT.size) widthOfElm = arrayT.element_t.bit_length() if size * widthOfElm != mySize * myWidthOfElm: raise TypeConversionErr("Size of types is different", size * widthOfElm, mySize * myWidthOfElm) if isinstance(typeFrom.element_t, HBits): reinterpretElmToType = None else: reinterpretElmToType = HBits(myWidthOfElm) res = arrayT.from_py(None) res.vld_mask = sigOrConst.vld_mask for i in range(size): start = i * widthOfElm end = (i + 1) * widthOfElm item = getBits_from_array( sigOrConst, myWidthOfElm, start, end, reinterpretElmToType) res[i] = item._reinterpret_cast(arrayT.element_t) return res @internal def reinterpret_HArray_to_HStruct(typeFrom: HArray, sigOrConst: HArrayAnyValue, structT): as_bits = sigOrConst._reinterpret_cast(HBits(typeFrom.bit_length())) return as_bits._reinterpret_cast(structT) @internal def reinterpret_cast_HArray(typeFrom: HArray, sigOrConst: HArrayAnyValue, toType): if isinstance(toType, HBits): return reinterptet_HArray_to_HBits(typeFrom, sigOrConst, toType) elif isinstance(toType, HArray): return reinterpret_HArray_to_HArray(typeFrom, sigOrConst, toType) elif isinstance(toType, HStruct): return reinterpret_HArray_to_HStruct(typeFrom, sigOrConst, toType) else: return default_reinterpret_cast_fn(typeFrom, sigOrConst, toType) ================================================ FILE: hwt/hdl/types/arrayConst.py ================================================ from copy import copy from typing import Union, Self from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.types.bitConstFunctions import HBitsAnyIndexCompatibleValue from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL, INT from hwt.hdl.types.slice import HSlice from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import RtlSignalBase from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from pyMathBitPrecise.bit_utils import ValidityError def _HArrayGetitem(self: Union["HArrayRtlSignal", "HArrayConst"], iamVal: bool, key): key = toHVal(key) isSLICE = isinstance(key, HSlice.getConstCls()) if isSLICE: raise NotImplementedError() elif isinstance(key, (HConst, RtlSignalBase)): pass else: raise NotImplementedError( f"Index operation not implemented for index {key}") if iamVal and isinstance(key, HConst): return self._getitem__const(key) return HOperatorNode.withRes(HwtOps.INDEX, [self, key], self._dtype.element_t) class HArrayRtlSignal(RtlSignal): def __getitem__(self, key): try: return _HArrayGetitem(self, False, key) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __iter__(self): mySize = len(self) for i in range(mySize): yield self[i] def __call__(self, source, dst_resolve_fn=lambda x:x._getDestinationSignalForAssignmentToThis(), exclude=None, fit=False) -> list[HdlAssignmentContainer]: assert len(self) == len(source), ("source and destination array must be of the same size", len(self) == len(source)) res = [] for src, dst in zip(source, self): a = dst.__call__(src, dst_resolve_fn=dst_resolve_fn, exclude=exclude, fit=fit) if isinstance(a, (list, tuple)): res.extend(a) else: res.append(a) return res def __len__(self): return int(self._dtype.size) class HArrayConst(HConst): """ Class for values of array HDL type """ @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param val: None or dictionary {index:HConst} or iterrable of values :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ size = typeObj.size if isinstance(size, HConst): size = int(size) elements = {} if vld_mask == 0: val = None if val is None: vld_mask = 0 elif isinstance(val, dict): if vld_mask is None: vld_mask = 1 for k, v in val.items(): if not isinstance(k, int): k = int(k) # expecting {index: value} dict if k >= size: raise ValueError("Initialization value dictionary contains index which is larger than size of initialized array", typeObj, k) e = elements[k] = typeObj.element_t.from_py(v) vld_mask &= e._is_full_valid() else: if vld_mask is None: vld_mask = 1 for k, v in enumerate(val): if k >= size: raise ValueError("Initialization value sequence is larger than size of initialized array", typeObj, val) if isinstance(v, RtlSignalBase): # is signal assert v._dtype == typeObj.element_t e = v else: e = typeObj.element_t.from_py(v) elements[k] = e vld_mask &= e._is_full_valid() if len(elements) != size: vld_mask = 0 return cls(typeObj, elements, vld_mask) def to_py(self): v = self.val invalid_elm = self._dtype.element_t.from_py(None) return [v.get(i, invalid_elm).to_py() for i in range(self._dtype.size)] @internal def __hash__(self): return hash(self._dtype) # return hash((self._dtype, self.val, self.vld_mask)) def _is_full_valid(self): return self.vld_mask == 1 def _is_partially_valid(self) -> bool: return self._is_full_valid() or any(v._is_partially_valid() for v in self.val.values()) @internal def _getitem__const(self, key): """ :attention: this will clone item from array, iterate over .val if you need to modify items """ try: kv = key.val if not key._is_full_valid(): raise KeyError() else: if kv >= self._dtype.size: raise KeyError() return self.val[kv].__copy__() except KeyError: return self._dtype.element_t.from_py(None) def __getitem__(self, key): try: return _HArrayGetitem(self, True, key) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __setitem__(self, index: HBitsAnyIndexCompatibleValue, value) -> Self: """ Only syntax sugar for user, not used inside HWT * Not used in HW design (__getitem__ and overloaded call operator is used instead for item assigning) """ try: if isinstance(index, int): index = INT.from_py(index) else: assert isinstance(index._dtype, HBits), index._dtype if isinstance(value, HConst): assert value._dtype == self._dtype.element_t, ( value._dtype, self._dtype.element_t) else: value = self._dtype.element_t.from_py(value) if index._is_full_valid(): self.val[index.val] = value.__copy__() else: self.val = {} self.vld_mask = int( len(self.val) == self._dtype.size and all(e._is_full_valid() for e in self.val.values()) ) return self except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __iter__(self): mySize = len(self) for i in range(mySize): yield self[i] def __len__(self): return int(self._dtype.size) def _eq(self, other): assert self._dtype.element_t == other._dtype.element_t assert self._dtype.size == other._dtype.size try: eq = True vld = 1 keysA = set(self.val) keysB = set(other.val) sharedKeys = keysA.union(keysB) lsh = len(sharedKeys) if (lsh == int(self._dtype.size) and len(keysA) == lsh and len(keysB) == lsh): for k in sharedKeys: a = self.val[k] b = other.val[k] eq = eq and bool(a) == bool(b) if not eq: break vld = vld & a.vld_mask & b.vld_mask else: eq = False vld = 0 return BOOL.getConstCls()(BOOL, int(eq), vld) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified ================================================ FILE: hwt/hdl/types/bitConstFunctions.py ================================================ from operator import ne, eq from typing import Callable, Union, Optional from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps, HOperatorDef, CMP_OP_SWAP from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL from hwt.hdl.types.slice import HSlice from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import RtlSignalBase from hwt.math import isPow2, log2ceil from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr from pyMathBitPrecise.bit_utils import mask from pyMathBitPrecise.bits3t import bitsCmp__val, bitsBitOp__val, \ bitsArithOp__val, Bits3val, bitsCmp__val_NE, bitsCmp__val_EQ HBitsAnyCompatibleValue = Union["HBitsRtlSignal", "HBitsConst", int, None] HBitsAnyIndexCompatibleValue = Union[int, slice, RtlSignalBase[HSlice], RtlSignalBase[HBits], None] AnyHBitsValue = Union["HBitsRtlSignal", "HBitsConst"] @internal def bitsCmp_detect_useless_cmp(op0: "HBitsRtlSignal", op1: "HBitsConst", op: HOperatorDef) -> Optional[HOperatorDef]: v = int(op1) width = op1._dtype.bit_length() if op0._dtype.signed: min_val = -1 if width == 1 else -mask(width - 1) - 1 max_val = 0 if width == 1 else mask(width - 1) else: min_val = 0 max_val = mask(width) if v == min_val: # value can not be lower than min_val if op == HwtOps.GE: # -> always True return BOOL.from_py(1, 1) elif op == HwtOps.LT: # -> always False return BOOL.from_py(0, 1) elif op == HwtOps.LE: # convert <= to == to highlight the real function return HwtOps.EQ elif v == max_val: # value can not be greater than max_val if op == HwtOps.GT: # always False return BOOL.from_py(0, 1) elif op == HwtOps.LE: # always True return BOOL.from_py(1, 1) elif op == HwtOps.GE: # because value can not be greater than max return HwtOps.EQ @internal def bitsCmp(self: AnyHBitsValue, selfIsHConst: bool, other: HBitsAnyCompatibleValue, op: HOperatorDef, selfReduceVal: HConst, evalFn:Callable[[AnyHBitsValue, AnyHBitsValue], AnyHBitsValue]=None) -> AnyHBitsValue: """ Apply a generic comparison binary operator :attention: If other is bool signal convert this to bool (not ideal, due VHDL event operator) :ivar self: operand 0 :ivar other: operand 1 :ivar op: operator used :ivar selfReduceVal: the value which is a result if operands are all same signal (e.g. a==a = 1, b tuple[AnyHBitsValue, bool]: """ :return: tuple(the signal without negation, True if signal was negated) """ try: d = sig.singleDriver() except SignalDriverErr: return (sig, False) if isinstance(d, HOperatorNode) and d.operator == HwtOps.NOT: return d.operands[0], True return sig, False @internal def bitsBitOp(self: Union[RtlSignalBase, HConst], selfIsHConst: bool, other: HBitsAnyCompatibleValue, op: HOperatorDef, getVldFn: Callable[[HConst, HConst], int], reduceValCheckFn: Callable[[RtlSignalBase, HConst], bool], reduceSigCheckFn: Callable[[RtlSignalBase, # op0Original bool, # op0Negated bool # op1Negated ], Union[RtlSignalBase, HConst]]) -> AnyHBitsValue: """ Apply a generic bitwise binary operator :attention: If other is Bool signal, convert this to bool (not ideal, due VHDL event operator) :ivar self: operand 0 :ivar other: operand 1 :ivar op: operator used :ivar getVldFn: function to resolve invalid (X) states :ivar reduceValCheckFn: function to reduce useless operators (partially evaluate the expression if possible) :ivar reduceSigCheckFn: function to reduce useless operators for signals and its negation flags (e.g. a&a = a, a&~a=0, b^b=0) function parameters are in format (op0Original:RtlSignalBase, op0Negated: bool, op1Negated:bool) -> Union[RtlSignalBase, HConst]: returns result signal if reduction is possible else None """ other = toHVal(other, self._dtype) otherIsHConst = isinstance(other, HConst) if selfIsHConst and otherIsHConst: other = other._auto_cast(self._dtype) return bitsBitOp__val(self, other, op._evalFn, getVldFn) else: s_t = self._dtype o_t = other._dtype if not isinstance(o_t, s_t.__class__): raise TypeError(o_t) if s_t == o_t: pass elif o_t == BOOL and s_t != BOOL: self = self._auto_cast(BOOL) return op._evalFn(self, other) elif o_t != BOOL and s_t == BOOL: other = other._auto_cast(BOOL) return op._evalFn(self, other) else: if s_t.signed is not o_t.signed and bool(s_t.signed) == bool(o_t.signed): # automatically cast unsigned to vector if s_t.signed == False and o_t.signed is None: self = self._vec() s_t = self._dtype elif s_t.signed is None and o_t.signed == False: other = other._vec() o_t = other._dtype else: raise ValueError("Invalid value for signed flag of type", s_t.signed, o_t.signed, s_t, o_t) if s_t == o_t: # due to previsous cast the type may become the same pass elif s_t.bit_length() == 1 and o_t.bit_length() == 1\ and s_t.signed is o_t.signed \ and s_t.force_vector != o_t.force_vector: # automatically cast to vector with a single item to a single bit if s_t.force_vector: self = self[0] else: other = other[0] else: raise TypeError("Can not apply operator %r (%r, %r)" % (op, self._dtype, other._dtype)) if otherIsHConst: r = reduceValCheckFn(self, other) if r is not None: return r elif selfIsHConst: r = reduceValCheckFn(other, self) if r is not None: return r else: _self, _self_n = extractNegation(self) _other, _other_n = extractNegation(other) if _self is _other: return reduceSigCheckFn(self, _self_n, _other_n) return HOperatorNode.withRes(op, [self, other], self._dtype) @internal def bitsArithOp(self: AnyHBitsValue, selfIsHConst: bool, other: HBitsAnyCompatibleValue, op: HOperatorDef) -> AnyHBitsValue: other = toHVal(other, self._dtype) if not isinstance(other._dtype, HBits): raise TypeError(other._dtype) otherIsHConst = isinstance(other, HConst) if selfIsHConst and otherIsHConst: return bitsArithOp__val(self, other, op._evalFn) else: if self._dtype.signed is None: self = self._unsigned() if op in (HwtOps.ADD, HwtOps.SUB) and otherIsHConst and other._is_full_valid() and int(other) == 0: return self resT = self._dtype if op == HwtOps.ADD and selfIsHConst and self._is_full_valid() and int(self) == 0: return other._auto_cast(resT) if isinstance(other._dtype, HBits): t0 = self._dtype t1 = other._dtype if t0.bit_length() != t1.bit_length(): if not t1.strict_width: # resize to type of this other = other._auto_cast(t1) t1 = other._dtype pass elif not t0.strict_width: # resize self to type of result self = self._auto_cast(t0) t0 = self._dtype pass else: raise TypeError("%r %r %r" % (self, op, other)) if t1.signed != resT.signed: other = other._cast_sign(t0.signed) else: raise TypeError("%r %r %r" % (self, op, other)) o = HOperatorNode.withRes(op, [self, other], self._dtype) return o._auto_cast(resT) @internal def bitsFloordiv(self: AnyHBitsValue, selfIsHConst: bool, other: HBitsAnyCompatibleValue) -> AnyHBitsValue: other = toHVal(other, suggestedType=self._dtype) if selfIsHConst and isinstance(other, HConst): return Bits3val.__floordiv__(self, other) else: if not isinstance(other._dtype, self._dtype.__class__): raise TypeError() return HOperatorNode.withRes(HwtOps.SDIV if self._dtype.signed else HwtOps.UDIV, [self, other], self._dtype) @internal def _bitsMulModGetResultType(myT: "HBits", otherT: "HBits"): if otherT.strict_sign: res_sign = otherT.signed if myT.strict_sign: assert bool(res_sign) == bool(myT.signed), (myT, otherT) elif myT.strict_sign: res_sign = myT.signed else: res_sign = self._dtype.signed or otherT.signed if otherT.strict_width: res_w = otherT.bit_length() if myT.strict_width: assert res_w == myT.bit_length(), (myT, otherT) subResT = resT = otherT elif myT.strict_width: res_w = myT.bit_length() subResT = resT = myT else: res_w = max(myT.bit_length(), otherT.bit_length()) subResT = HBits(res_w, signed=res_sign) resT = HBits(res_w, signed=myT.signed) return subResT, resT @internal def bitsMul(self: AnyHBitsValue, selfIsHConst: bool, other: HBitsAnyCompatibleValue) -> AnyHBitsValue: HBits = self._dtype.__class__ other = toHVal(other, suggestedType=self._dtype) if not isinstance(other._dtype, HBits): raise TypeError(other) otherIsHConst = isinstance(other, HConst) if selfIsHConst and otherIsHConst: return Bits3val.__mul__(self, other) else: # reduce *1 and *0 if selfIsHConst and self._is_full_valid(): _s = int(self) if _s == 0: return self._dtype.from_py(0) elif _s == 1: return other._auto_cast(self._dtype) if otherIsHConst and other._is_full_valid(): _o = int(other) if _o == 0: return self._dtype.from_py(0) elif _o == 1: return self myT = self._dtype if self._dtype.signed is None: self = self._unsigned() if isinstance(other._dtype, HBits): s = other._dtype.signed if s is None: other = other._unsigned() else: raise TypeError(self, HwtOps.MUL, other) subResT, resT = _bitsMulModGetResultType(myT, other._dtype) o = HOperatorNode.withRes(HwtOps.MUL, [self, other], subResT) return o._auto_cast(resT) @internal def bitsRem(self: AnyHBitsValue, selfIsHConst: bool, other: HBitsAnyCompatibleValue) -> AnyHBitsValue: HBits = self._dtype.__class__ other = toHVal(other, suggestedType=self._dtype) if not isinstance(other._dtype, HBits): raise TypeError(other) otherIsHConst = isinstance(other, HConst) if selfIsHConst and otherIsHConst: return Bits3val.__mod__(self, other) else: if selfIsHConst and self._is_full_valid(): _s = int(self) if _s == 0: # 0 % x == 0 return self if otherIsHConst and other._is_full_valid(): _o = int(other) if _o == 0: # x % 0 = x return self elif isPow2(_s): # x % 2**cutOffBits cutOffBits = log2ceil(_s) return HBits(cutOffBits).from_py(0)._concat(self[:cutOffBits]) myT = self._dtype if self._dtype.signed is None: self = self._unsigned() if self._dtype.signed: op = HwtOps.SREM else: op = HwtOps.UREM if isinstance(other._dtype, HBits): s = other._dtype.signed if s is None: other = other._unsigned() else: raise TypeError(self, op, other) subResT, resT = _bitsMulModGetResultType(myT, other._dtype) o = HOperatorNode.withRes(op, [self, other], subResT) return o._auto_cast(resT) @internal def bitsLshift(self: AnyHBitsValue, shiftAmount: HBitsAnyCompatibleValue) -> AnyHBitsValue: """ shift left by a constant amount with 0 padding """ if isinstance(shiftAmount, HConst) and not shiftAmount._is_full_valid(): return self._dtype.from_py(None) shiftAmount = int(shiftAmount) if shiftAmount == 0: return self assert shiftAmount > 0, ("shift amount must be positive value", shiftAmount) width = self._dtype.bit_length() suffix = HBits(min(width, shiftAmount)).from_py(0) if shiftAmount >= width: return suffix else: return self[(width - shiftAmount):]._concat(suffix) @internal def bitsRshift(self: AnyHBitsValue, shiftAmount: HBitsAnyCompatibleValue) -> AnyHBitsValue: """ shift right by a constant amount :note: arithmetic shift if type is signed else logical shift with 0 padding """ if isinstance(shiftAmount, HConst) and not shiftAmount._is_full_valid(): return self._dtype.from_py(None) shiftAmount = int(shiftAmount) if shiftAmount == 0: return self assert shiftAmount > 0, ("shift amount must be positive value", shiftAmount) width = self._dtype.bit_length() if shiftAmount < width: return self[:shiftAmount]._ext(width, bool(self._dtype.signed)) elif shiftAmount > width: if self._dtype.signed: msb = self[width - 1] return msb._sext(width) else: return self._dtype.from_py(0) else: assert shiftAmount == 0, shiftAmount return self ================================================ FILE: hwt/hdl/types/bitConstFunctionsGetitem.py ================================================ from typing import Union, Optional, Literal from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps, HOperatorDef from hwt.hdl.types.bitConstFunctions import AnyHBitsValue, \ HBitsAnyIndexCompatibleValue from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import INT, SLICE, BIT, BIT_N from hwt.hdl.types.slice import HSlice from hwt.hdl.types.sliceUtils import slice_to_HSlice from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import RtlSignalBase from pyMathBitPrecise.bits3t import Bits3val @internal def _match_msb_get(v: "HBitsRtlSignal"): """ :returns: x if v == x[x.width - 1] else None """ if v._dtype.bit_length() != 1: return None opVIsResultOf = _get_operator_i_am_the_result_of(v) if opVIsResultOf == HwtOps.INDEX: iOp = v.singleDriver() iOpSrc, iOpI = iOp.operands if isinstance(iOpI, HConst) and iOpI._is_full_valid() and isinstance(iOpI._dtype, HBits) and int(iOpI) == iOpSrc._dtype.bit_length() - 1: return iOpSrc return None @internal def _fold_concat_of_msb_using_sext(v: "HBitsRtlSignal", vReplicatinCount:int, other: "HBitsRtlSignal", other_w: int): msbSrc = _match_msb_get(v) if msbSrc is other or other._dtype.bit_length() == 1 and v is other: # fold concat(x.msb, x) -> sext(x) return other._sext(other_w + vReplicatinCount) else: opOtherIsResultOf = _get_operator_i_am_the_result_of(other) if opOtherIsResultOf == HwtOps.SEXT and msbSrc == other.singleDriver().operands[0]: # fold concat(x.msb, sext(x)) -> sext(x) return msbSrc._sext(other_w + vReplicatinCount) elif opOtherIsResultOf == HwtOps.CONCAT: otherD: HOperatorNode = other.singleDriver() highBits, lowBits = otherD.operands if highBits is v: # fold concat(x1b, concat(x1b, y))) -> concat(sext(x1b), y) assert highBits._dtype.bit_length() == 1, highBits return highBits._sext(1 + vReplicatinCount)._concat(lowBits) elif highBits is msbSrc: # fold concat(x.msb, concat(x, y)) -> concat(sext(x), y) return highBits._sext(highBits._dtype.bit_length() + vReplicatinCount)._concat(lowBits) else: opHighBitsIsResultOf = _get_operator_i_am_the_result_of(highBits) if opHighBitsIsResultOf == HwtOps.SEXT: hiBitsSrc = highBits.singleDriver().operands[0] if hiBitsSrc is msbSrc: # fold concat(x.msb, concat(sext(x), y)) -> concat(sext(x), y) return msbSrc._sext(highBits._dtype.bit_length() + vReplicatinCount)._concat(lowBits) return None @internal def _get_operator_i_am_the_result_of(const_or_sig: Union[RtlSignalBase, HConst]) -> Optional[HOperatorDef]: if len(const_or_sig._rtlDrivers) == 1 and isinstance(const_or_sig._rtlObjectOrigin, HOperatorNode): return const_or_sig._rtlObjectOrigin.operator else: return None @internal def bitsGetitem_foldSliceOnCONCAT(v: AnyHBitsValue, start:int, stop: int, key: HBitsAnyIndexCompatibleValue) -> AnyHBitsValue: op_h, op_l = v._rtlObjectOrigin.operands op_l_w = op_l._dtype.bit_length() assert start > stop, (start, stop, "Should be in MSB:LSB format") if start <= op_l_w: # entirely in first operand of concat if op_l_w == 1: if isinstance(key._dtype, HSlice): assert int(key.val.start) == 1 and int(key.val.stop) == 0 and int(key.val.step) == -1, key return op_l else: assert int(key) == 0, key return op_l else: return op_l[key] elif stop >= op_l_w: # intirely in second operand of concat start -= op_l_w stop -= op_l_w if op_h._dtype.bit_length() == 1: assert start - stop == 1 return op_h else: return op_h[SLICE.from_py(slice(start, stop, -1))] else: # partially in op_h and op_l, allpy slice on concat operands and return concatenation of it if stop != 0 or op_l._dtype.bit_length() > 1: op_l = op_l[:stop] if op_h._dtype.bit_length() == 1: assert start - op_l_w == 1, ("Out of range slice (but this error should be catched sooner)", v, key) else: op_h = op_h[start - op_l_w:0] return op_h._concat(op_l) @internal def bitsGetitem_foldSliceOnEXT(v: AnyHBitsValue, start:int, stop: int, key: HBitsAnyIndexCompatibleValue, iAmResultOfOp: Literal[HwtOps.ZEXT, HwtOps.SEXT]) -> AnyHBitsValue: assert iAmResultOfOp in (HwtOps.ZEXT, HwtOps.SEXT), iAmResultOfOp # :note: start points at MSB and stop on LSB (start:stop, eg 8:0) extSrc = v.singleDriver().operands[0] extSrcWidth = extSrc._dtype.bit_length() resultWidth = start - stop if start < extSrcWidth: # selecting only bits from extSrc if stop == 0: return extSrc._trunc(start) else: return extSrc[key] elif stop >= extSrcWidth: # only msb bits are selected from ext and if iAmResultOfOp == HwtOps.ZEXT: return v._dtype._createMutated(resultWidth).from_py(0) else: return extSrc.getMsb()._sext(resultWidth) else: # selected value overlaps between extSrc and extension bits if stop != 0: extSrc = extSrc[:stop] return extSrc._ext(resultWidth, iAmResultOfOp == HwtOps.SEXT) @internal def bitsGetitem_foldBitGetOnEXT(v: AnyHBitsValue, i:int, key: HBitsAnyIndexCompatibleValue, iAmResultOfOp: Literal[HwtOps.ZEXT, HwtOps.SEXT]) -> AnyHBitsValue: # fold zext(x)[i] -> x[i] if x.width < i else 0 # fold sext(x)[i] -> x[i] if x.width < i else x.msb extSrc = v.singleDriver().operands[0] extSrcWidth = extSrc._dtype.bit_length() if i < extSrcWidth: # selecting only bits from extSrc return extSrc[key] else: # only msb bits are selected from ext and if iAmResultOfOp == HwtOps.ZEXT: return v._dtype._createMutated(1).from_py(0) else: return extSrc.getMsb() @internal def bitsGetitem_foldBitGetOnConcat(v: AnyHBitsValue, key: HBitsAnyIndexCompatibleValue, _index:int, iAmResultOfOp: Optional[HOperatorDef]): # index directly in the member of concatenation update_key = False while iAmResultOfOp == HwtOps.CONCAT: op_h, op_l = v._rtlObjectOrigin.operands op_l_w = op_l._dtype.bit_length() if _index < op_l_w: v = op_l else: v = op_h _index -= op_l_w update_key = True iamConst = isinstance(v, HConst) iAmResultOfOp = None if iamConst else _get_operator_i_am_the_result_of(v) # [todo] check if swap of negated flag can cause anything wrong if update_key: key = key._dtype.from_py(_index) return v, key @internal def bitsGetitem(v: AnyHBitsValue, iamConst:bool, key: HBitsAnyIndexCompatibleValue) -> AnyHBitsValue: """ [] operator :attention: Table below is for little endian bit order (MSB:LSB) which is default. This is **reversed** as it is in pure python where it is [0, len(v)]. :attention: Slice on slice signal is automatically reduced to single slice. This function also looks trough concatenations. +-----------------------------+----------------------------------------------------------------------------------+ | a[up:low] | items low through up; a[16:8] selects upper byte from 16b vector a | +-----------------------------+----------------------------------------------------------------------------------+ | a[up:] | low is automatically substituted with 0; a[8:] will select lower 8 bits | +-----------------------------+----------------------------------------------------------------------------------+ | a[:end] | up is automatically substituted; a[:8] will select upper byte from 16b vector a | +-----------------------------+----------------------------------------------------------------------------------+ | a[:], a[-1], a[-2:], a[:-2] | raises NotImplementedError (not implemented due to complicated support in hdl) | +-----------+----------------------------------------------------------------------------------------------------+ :note: signed is preserved as in VHDL, and not like in Verilog where result of slice is always unsigned """ st = v._dtype vWidth = st.bit_length() if isinstance(key, slice): key = slice_to_HSlice(key, vWidth) isSLICE = True else: isSLICE = isinstance(key, HSlice.getConstCls()) is1bScalar = vWidth == 1 and not st.force_vector if not isSLICE: if is1bScalar and \ ((isinstance(key, int) and key == 0) or\ (isinstance(key, HConst) and key._is_full_valid() and int(key) == 0)): return v key = toHVal(key, INT) else: if is1bScalar and key.val.start == 1 and key.val.stop == 0 and key.val.step == -1: return v if is1bScalar: # assert not indexing on single bit raise IndexError("indexing on single bit") iAmResultOfOp = None if iamConst else _get_operator_i_am_the_result_of(v) if iAmResultOfOp == HwtOps.TRUNC: # fold trunc(x)[i] to x[i] return v.singleDriver().operands[0][key] elif iAmResultOfOp == HwtOps.BitsAsSigned or iAmResultOfOp == HwtOps.BitsAsUnsigned: # fold x._signed()[i] to x[i]._signed() return iAmResultOfOp._evalFn(v.singleDriver().operands[0][key]) HBits = v._dtype.__class__ if isSLICE: # :note: downto notation start = key.val.start stop = key.val.stop if key.val.step != -1: raise NotImplementedError() startIsConst = isinstance(start, HConst) stopIsConst = isinstance(stop, HConst) indexesAreHConst = startIsConst and stopIsConst if indexesAreHConst and start.val == vWidth and stop.val == 0: # selecting all bits no conversion needed # fold x[h:l] -> x return v # check start boundaries if startIsConst: _start = int(start) if _start < 0 or _start > vWidth: raise IndexError("start index is out of range start:", _start, " width:", vWidth, "") # check end boundaries if stopIsConst: _stop = int(stop) if _stop < 0 or _stop >= vWidth: raise IndexError("stop index is out of range stop:", _stop, " width:", vWidth) # check width of selected range if startIsConst and stopIsConst and _start - _stop <= 0: raise IndexError("start (represents MSB bit index +1) must be > stop (represents LSB bit index)", _start, _stop) if iAmResultOfOp == HwtOps.INDEX: # try reduce v and parent slice to one # fold x[a:b][start:stop] -> x[b+start:b+stop] original, parentIndex = v._rtlObjectOrigin.operands if isinstance(parentIndex._dtype, HSlice): parentLower = parentIndex.val.stop start = parentLower + start stop = parentLower + stop return original[start:stop] elif startIsConst and stopIsConst: # index directly in the member of concatenation # :note: start points at MSB and stop on LSB (start:stop, eg 8:0) stop = int(stop) start = int(start) if iAmResultOfOp == HwtOps.CONCAT: return bitsGetitem_foldSliceOnCONCAT(v, start, stop, key) elif iAmResultOfOp == HwtOps.ZEXT or iAmResultOfOp == HwtOps.SEXT: return bitsGetitem_foldSliceOnEXT(v, start, stop, key, iAmResultOfOp) elif stop == 0: return v._trunc(start) if iamConst: if isinstance(key, SLICE.getConstCls()): key = key.val res = Bits3val.__getitem__(v, key) if res._dtype.bit_length() == 1 and not res._dtype.force_vector: assert res._dtype is not v._dtype res._dtype.force_vector = True return res else: key = SLICE.from_py(slice(start, stop, -1)) _resWidth = start - stop resT = HBits(bit_length=_resWidth, force_vector=_resWidth == 1, signed=st.signed, negated=st.negated) elif isinstance(key, HBits.getConstCls()): # int like value addressing a single bit if st.negated: resT = BIT_N else: resT = BIT if not key._is_full_valid(): return resT.from_py(None) # check index range _index = int(key) # if _index == 0 and not st.force_vector: # # fold x[0] -> x._trunc(1) # return v._trunc(1) if _index < 0 or _index > vWidth - 1: raise IndexError(_index) if iAmResultOfOp == HwtOps.INDEX: # index directly in parent signal # fold x[a:b][i] -> x[b+i] original, parentIndex = v._rtlObjectOrigin.operands if isinstance(parentIndex._dtype, HSlice): parentLower = parentIndex.val.stop return original[parentLower + _index] elif iAmResultOfOp == HwtOps.TRUNC: # fold x._trunc(n)[i] to x[i] original = v._rtlObjectOrigin.operands[0] return original[_index] elif iAmResultOfOp == HwtOps.ZEXT or iAmResultOfOp == HwtOps.SEXT: return bitsGetitem_foldBitGetOnEXT(v, _index, key, iAmResultOfOp) else: # index directly in the member of concatenation # fold concat(a, x)[i] -> x[i] _v, _key = bitsGetitem_foldBitGetOnConcat(v, key, _index, iAmResultOfOp) changed = v is not _v or _key is not key v = _v key = _key iamConst = isinstance(v, HConst) st = v._dtype if isinstance(key, HBits.getConstCls()) and int(key) == 0 and ( v._dtype.bit_length() == 1 and not v._dtype.force_vector ): return v elif changed: return v[key] if iamConst: # at the end because multiple non-constant indexes may be applied on constant and we want to merge them return Bits3val.__getitem__(v, key) elif key._is_full_valid() and int(key) == 0 and (v._dtype == BIT or v._dtype == BIT_N): return v elif isinstance(key, RtlSignalBase): t = key._dtype if isinstance(t, HSlice): bit_length = key.staticEval()._size() resT = HBits(bit_length, force_vector=bit_length == 1, signed=st.signed, negated=st.negated) elif isinstance(t, HBits): resT = BIT else: raise TypeError( "Index operation not implemented" " for index of type ", t) else: raise TypeError( "Index operation not implemented for index ", key) if st.negated and resT is BIT: resT = BIT_N return HOperatorNode.withRes(HwtOps.INDEX, [v, key], resT) ================================================ FILE: hwt/hdl/types/bitConst_opReduce.py ================================================ from typing import Union from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.mainBases import RtlSignalBase from pyMathBitPrecise.bit_utils import mask @internal def tryReduceAnd(sig:RtlSignalBase, val: HConst): """ Return sig and val reduced by & operator or None if it is not possible to statically reduce expression """ m = sig._dtype.all_mask() if val._is_full_valid(): v = val.val if v == m: return sig elif v == 0: return val @internal def tryReduceOr(sig:RtlSignalBase, val: HConst): """ Return sig and val reduced by | operator or None if it is not possible to statically reduce expression """ m = sig._dtype.all_mask() if not val.vld_mask: return val if val._is_full_valid(): v = val.val if v == m: return val elif v == 0: return sig @internal def tryReduceXor(sig:RtlSignalBase, val: HConst): """ Return sig and val reduced by ^ operator or None if it is not possible to statically reduce expression """ m = sig._dtype.all_mask() if not val.vld_mask: return val if val._is_full_valid(): v = val.val if v == m: return ~sig elif v == 0: return sig def reduceSigCheckFnAnd(op0Original:RtlSignalBase, op0Negated: bool, op1Negated:bool) -> Union[RtlSignalBase, HConst]: if op0Negated == op1Negated: # a & a -> a # ~a & ~a -> ~a return op0Original else: # a | ~a -> 0 # ~a | a -> 0 return op0Original._dtype.from_py(0) def reduceSigCheckFnOr(op0Original:RtlSignalBase, op0Negated: bool, op1Negated:bool) -> Union[RtlSignalBase, HConst]: if op0Negated == op1Negated: # a | a -> a # ~a | ~a -> ~a return op0Original else: # a | ~a -> 1 # ~a | a -> 1 t = op0Original._dtype return t.from_py(mask(t.bit_length())) def reduceSigCheckFnXor(op0Original:RtlSignalBase, op0Negated: bool, op1Negated:bool) -> Union[RtlSignalBase, HConst]: t = op0Original._dtype if op0Negated == op1Negated: # a ^ a -> 0 # ~a ^ ~a -> 0 return t.from_py(0) else: # a ^ ~a -> 1 # ~a ^ a -> 1 return t.from_py(mask(t.bit_length())) ================================================ FILE: hwt/hdl/types/bits.py ================================================ from typing import Union, Literal, Optional, Self from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override from hwt.serializer.generic.indent import getIndent from pyMathBitPrecise.bits3t import Bits3t BITS_DEFAUTL_SIGNED = None BITS_DEFAUTL_FORCEVECTOR = False BITS_DEFAUTL_NEGATED = False class HBits(HdlType, Bits3t): """ Elemental HDL type representing bits (vector or single bit) """ def __init__(self, bit_length: Union[int, "AnyHBitsValue"], signed:Literal[None, True, False]=BITS_DEFAUTL_SIGNED, force_vector:bool=BITS_DEFAUTL_FORCEVECTOR, negated:bool=BITS_DEFAUTL_NEGATED, name:Optional[str]=None, const:bool=False, strict_sign:bool=True, strict_width:bool=True): """ :param negated: if true the value is in negated form """ self.negated = negated HdlType.__init__(self, const=const) bit_length = int(bit_length) assert bit_length > 0, bit_length Bits3t.__init__(self, bit_length, signed, name=name, force_vector=force_vector or bit_length == 1 and signed is not None, strict_sign=strict_sign, strict_width=strict_width) def _createMutated(self, bit_length: Union[int, "AnyHBitsValue"]=NOT_SPECIFIED, signed:Literal[None, True, False]=NOT_SPECIFIED, force_vector:bool=NOT_SPECIFIED, negated:bool=NOT_SPECIFIED, name:Optional[str]=NOT_SPECIFIED, const:bool=NOT_SPECIFIED, strict_sign:bool=NOT_SPECIFIED, strict_width:bool=NOT_SPECIFIED ) -> Self: if bit_length is NOT_SPECIFIED: bit_length = self.bit_length() else: if force_vector is NOT_SPECIFIED and self.force_vector and bit_length > 1 and self.bit_length() == 1: force_vector = False if signed is NOT_SPECIFIED: signed = self.signed if force_vector is NOT_SPECIFIED: force_vector = self.force_vector if negated is NOT_SPECIFIED: negated = self.negated if name is NOT_SPECIFIED: name = None if const is NOT_SPECIFIED: const = self.const if strict_sign is NOT_SPECIFIED: strict_sign = self.strict_sign if strict_width is NOT_SPECIFIED: strict_width = self.strict_width return self.__class__( bit_length, signed=signed, force_vector=force_vector, negated=negated, name=name, const=const, strict_sign=strict_sign, strict_width=strict_width ) @internal def domain_size(self): """ :return: how many values can have specified type """ return int(2 ** self.bit_length()) @internal @classmethod def get_auto_cast_HConst_fn(cls): from hwt.hdl.types.bitsCast import convertBits__HConst return convertBits__HConst @internal @override @classmethod def get_reinterpret_cast_HConst_fn(cls): from hwt.hdl.types.bitsCast import reinterpretBits__HConst return reinterpretBits__HConst @internal @classmethod def get_auto_cast_RtlSignal_fn(cls): from hwt.hdl.types.bitsCast import convertBits__RtlSignal return convertBits__RtlSignal @internal @override @classmethod def get_reinterpret_cast_RtlSignal_fn(cls): from hwt.hdl.types.bitsCast import reinterpretBits__RtlSignal return reinterpretBits__RtlSignal @internal @override @classmethod def getConstCls(cls): try: return cls._constCls except AttributeError: from hwt.hdl.types.bitsConst import HBitsConst cls._constCls = HBitsConst return cls._constCls @internal @override @classmethod def getRtlSignalCls(cls): try: return cls._rtlSignalCls except AttributeError: from hwt.hdl.types.bitsRtlSignal import HBitsRtlSignal cls._rtlSignalCls = HBitsRtlSignal return cls._rtlSignalCls def getAllOnesValue(self): return self.from_py(self._all_mask) def __hash__(self): return hash((Bits3t.__hash__(self), self.const)) def __eq__(self, other): return Bits3t.__eq__(self, other) and \ isinstance(other, self.__class__) and \ self.const == other.const def __repr__(self, indent=0, withAddr=None, expandStructs=False): """ :param indent: number of indentation :param withAddr: if is not None is used as a additional information about on which address this type is stored (used only by HStruct) :param expandStructs: expand HStructTypes (used by HStruct and HArray) """ constr = [] if self.name is not None: constr.append('"%s"' % self.name) c = self.bit_length() if c == 1: constr.append("1bit") if self.force_vector: constr.append("force_vector") else: constr.append(f"{c:d}bits") if self.const: constr.append("const") if self.signed: constr.append("signed") elif self.signed is False: constr.append("unsigned") if not self.strict_sign: constr.append("strict_sign=False") if not self.strict_width: constr.append("strict_width=False") return "%s<%s, %s>" % (getIndent(indent), self.__class__.__name__, ", ".join(constr)) ================================================ FILE: hwt/hdl/types/bitsCast.py ================================================ from typing import Union from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsCastUtils import fitTo_t, BitWidthErr from hwt.hdl.types.defs import INT, BOOL from hwt.hdl.types.hdlType import HdlType, default_auto_cast_fn from hwt.hdl.types.struct import HStruct from hwt.hdl.types.union import HUnion from hwt.hwIOs.hwIOArray import HwIOArray from hwt.hwIOs.hwIOStruct import HdlType_to_HwIO from hwt.mainBases import HwIOBase from hwt.synthesizer.exceptions import TypeConversionErr from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from hwt.synthesizer.vectorUtils import iterBits from pyMathBitPrecise.bit_utils import set_bit_range, mask @internal def convertBits__HConst(curType: HBits, val: "HBitsConst", toType: HdlType): """ :param curType: current type :param val: constant object to cast into toType :param toType: type to cast val to """ if toType == BOOL: return val != curType.getConstCls().from_py(curType, 0) elif isinstance(toType, HBits): if curType.signed != toType.signed: if curType.strict_sign and bool(curType.signed) != bool(toType.signed): raise TypeConversionErr(curType, toType) val = val._cast_sign(toType.signed) w_from, w_to = curType.bit_length(), toType.bit_length() if w_from != w_to: if curType.strict_width: raise TypeConversionErr(curType, toType) if w_from > w_to: # cut off some bits from value new_m = val.vld_mask & toType.all_mask() else: # w_from < w_to, extend the value to some bit length extra_mask_bits = mask(w_to - w_from) new_m = set_bit_range(val.vld_mask, w_from, w_to - w_from, extra_mask_bits) val = toType.from_py(val.val, new_m) if val._dtype != toType: # sign and width checked, only name, strict_* flags can be different val = toType.from_py(val.val, val.vld_mask) return val elif toType == INT: return INT.getConstCls()(INT, val.val, int(val._is_full_valid())) return default_auto_cast_fn(curType, val, toType) @internal def convertBits__RtlSignal(curType: HBits, sig: RtlSignal, toType: HdlType): """ Cast Bit subtypes, (integers, bool, ...) """ if toType == BOOL: if curType.bit_length() == 1: v = 0 if sig._dtype.negated else 1 if isinstance(sig, RtlSignal): sig: RtlSignal try: d = sig.singleDriver() except SignalDriverErr: d = None if d is not None and isinstance(d, HOperatorNode) and d.operator == HwtOps.NOT: # signal itself is negated, ~a = 1 to a = 0 v = int(not bool(v)) sig = d.operands[0] return sig._eq(curType.getConstCls().from_py(curType, v)) elif isinstance(toType, HBits): if curType.bit_length() == toType.bit_length(): # if curType.force_vector != toType.force_vector: # raise NotImplementedError(curType, toType) if curType.const == toType.const: return sig._cast_sign(toType.signed) return default_auto_cast_fn(curType, sig, toType) @internal def reinterpret_bits_to_hstruct__HConst(val: HConst, hStructT: HStruct): """ Reinterpret signal of type HBits to signal of type HStruct """ container = hStructT.from_py(None) hStructT.vld_mask = int(val._is_full_valid()) offset = 0 for f in hStructT.fields: t = f.dtype width = t.bit_length() if f.name is not None: v = val[(width + offset):offset] v = v._reinterpret_cast(t) setattr(container, f.name, v) offset += width if offset != val._dtype.bit_length(): raise BitWidthErr("Src type contains more bits than dst", val._dtype.bit_length(), hStructT.bit_length(), val._dtype, hStructT) return container @internal def transfer_signals(src: Union[HwIOBase, HObjList], dst: Union[HwIOBase, HObjList]): if isinstance(src, HObjList): assert len(src) == len(dst) for si, di in zip(src, dst): transfer_signals(si, di) elif isinstance(src, (RtlSignal, HConst)): assert not dst._hwIOs, (src, dst) dst._sig = src elif src._hwIOs: # HwIOBase assert len(src._hwIOs) == len(dst._hwIOs), (src, dst) for si, di in zip(src._hwIOs, dst._hwIOs): transfer_signals(si, di) else: dst._sig = src._sig dst._sigInside = src._sigInside @internal def reinterpret_bits_to_hstruct__RtlSignal(val: RtlSignal, hStructT: HStruct): """ Reinterpret signal of type :class:`HBits` to signal of type :class:`HStruct` """ container = HdlType_to_HwIO().apply(hStructT) container._loadHwDeclarations() offset = 0 for f in hStructT.fields: t = f.dtype width = t.bit_length() if f.name is not None: if offset == 0 and val._dtype.bit_length() == width: v = val else: v = val[(width + offset):offset] v = v._reinterpret_cast(t) current = getattr(container, f.name) transfer_signals(v, current) offset += width if offset != val._dtype.bit_length(): raise BitWidthErr("Src type contains more bits than dst", val._dtype.bit_length(), hStructT.bit_length(), val._dtype, hStructT) return container @internal def reinterpret_bits_to_harray(sigOrConst: Union[RtlSignal, HConst], hArrayT: HArray): elmT = hArrayT.element_t elmWidth = elmT.bit_length() if isinstance(sigOrConst, HConst): a = hArrayT.from_py(None) a.vld_mask = int(sigOrConst._is_full_valid()) else: a = HwIOArray(None for _ in range(hArrayT.size)) for i, item in enumerate(iterBits(sigOrConst, bitsInOne=elmWidth, skipPadding=False)): item = item._reinterpret_cast(elmT) a[i] = item return a @internal def reinterpretBits__HConst(curType: HBits, val: HConst, toType: HdlType): if isinstance(toType, HBits): if curType.signed != toType.signed: val = val._cast_sign(toType.signed) val = fitTo_t(val, toType) # , extend=False, shrink=False if val._dtype == toType: return val else: return convertBits__HConst(val._dtype, val, toType) elif isinstance(toType, HStruct): return reinterpret_bits_to_hstruct__HConst(val, toType) elif isinstance(toType, HUnion): raise NotImplementedError() elif isinstance(toType, HArray): return reinterpret_bits_to_harray(val, toType) return default_auto_cast_fn(curType, val, toType) @internal def reinterpretBits__RtlSignal(curType: HBits, sig: RtlSignal, toType: HdlType): """ Cast object of same bit size between to other type (f.e. bits to struct, union or array) """ if isinstance(toType, HBits): if curType.signed != toType.signed: sig = sig._cast_sign(toType.signed) sig = fitTo_t(sig, toType) if sig._dtype == toType: return sig else: return convertBits__RtlSignal(sig._dtype, sig, toType) elif sig._dtype.bit_length() == toType.bit_length(): if isinstance(toType, HStruct): return reinterpret_bits_to_hstruct__RtlSignal(sig, toType) elif isinstance(toType, HUnion): raise NotImplementedError() elif isinstance(toType, HArray): return reinterpret_bits_to_harray(sig, toType) return default_auto_cast_fn(curType, sig, toType) ================================================ FILE: hwt/hdl/types/bitsCastUtils.py ================================================ from typing import Union from hwt.hdl.types.bits import HBits class BitWidthErr(Exception): """ Wrong bit width of signal/value """ def fitTo_t(what: Union["HBitsRtlSignal", "HBitsConst"], where_t: HBits, extend: bool=True, shrink: bool=True) -> Union["HBitsRtlSignal", "HBitsConst"]: """ Slice signal "what" to fit in "where" or arithmetically (for signed by MSB / unsigned, vector with 0) extend "what" to same width as "where" little-endian impl. :param extend: allow increasing of the signal width :param shrink: allow shrinking of the signal width """ whatWidth = what._dtype.bit_length() toWidth = where_t.bit_length() if toWidth == whatWidth: return what elif toWidth < whatWidth: # slice if not shrink: raise BitWidthErr() return what[toWidth:] else: if not extend: raise BitWidthErr() res = what._ext(toWidth) if where_t.signed is not None: return res._reinterpret_cast(where_t) return res def fitTo(what: Union["HBitsRtlSignal", "HBitsConst"], where: Union["HBitsRtlSignal", "HBitsConst"], extend: bool=True, shrink: bool=True) -> Union["HBitsRtlSignal", "HBitsConst"]: return fitTo_t(what, where._dtype, extend, shrink) ================================================ FILE: hwt/hdl/types/bitsConst.py ================================================ from copy import copy from operator import eq from typing import Union, Self from hdlConvertorAst.to.hdlUtils import bit_string from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.bitConstFunctions import bitsCmp, \ bitsBitOp, bitsArithOp, bitsFloordiv, bitsMul, bitsLshift, \ bitsRshift, HBitsAnyIndexCompatibleValue, HBitsAnyCompatibleValue, bitsRem from hwt.hdl.types.bitConstFunctionsGetitem import bitsGetitem from hwt.hdl.types.bitConst_opReduce import tryReduceOr, tryReduceAnd, \ tryReduceXor, reduceSigCheckFnAnd, reduceSigCheckFnOr, reduceSigCheckFnXor from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsRtlSignal import HBitsRtlSignal from hwt.hdl.types.defs import BOOL, INT, BIT, SLICE from hwt.hdl.types.sliceUtils import slice_to_HSlice from hwt.hdl.types.typeCast import toHVal from pyMathBitPrecise.bits3t import Bits3val from pyMathBitPrecise.bits3t_vld_masks import vld_mask_for_xor, vld_mask_for_and, \ vld_mask_for_or class HBitsConst(HConst, Bits3val): """ :attention: operator on signals are using value operator functions as well """ _BOOL = HBits(1, name="bool") _SIGNED_FOR_SLICE_RESULT = NOT_SPECIFIED _SIGNED_FOR_CONCAT_RESULT = None @classmethod def from_py(cls, typeObj, val, vld_mask=None) -> Self: val, vld_mask = typeObj._normalize_val_and_mask(val, vld_mask) return cls(typeObj, val, vld_mask=vld_mask) @internal def _cast_sign(self, signed:Union[bool, None]) -> Self: try: t = self._dtype v = Bits3val._cast_sign(self, signed, name=None, force_vector=t.bit_length() == 1 if signed else t.force_vector) if self._dtype == v._dtype: # no change of type, use this instance return self else: return v except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _signed(self) -> Self: return self._cast_sign(True) def _unsigned(self) -> Self: return self._cast_sign(False) def _vec(self) -> Self: return self._cast_sign(None) @internal def _concat(self, other: Union[Self, "HBitsRtlSignal"]) -> Union[Self, "HBitsRtlSignal"]: try: if isinstance(other, HConst): return Bits3val._concat(self._vec(), other._vec()) else: if self._is_full_valid(): if int(self) == 0: # fold concat(0, x) -> zext(x) return other._zext(self._dtype.bit_length() + other._dtype.bit_length()) return HBitsRtlSignal._concat(self, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __getitem__(self, key: HBitsAnyIndexCompatibleValue) -> Self: try: return bitsGetitem(self, True, key) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __setitem__(self, index: HBitsAnyIndexCompatibleValue, value: Union[Self, "HBitsRtlSignal"]) -> Self: """ this []= operator can not be called in design description, it can be only used to update HConsts """ try: # convert index to HSlice or hInt if isinstance(index, HConst): pass elif isinstance(index, slice): length = self._dtype.bit_length() index = slice_to_HSlice(index, length) if not index._is_full_valid(): raise ValueError("invalid index", index) else: index = INT.from_py(index) # convert value to bits of length specified by index if index._dtype == SLICE: HBits = self._dtype.__class__ itemT = HBits(index._size()) else: itemT = BIT if isinstance(value, HConst): value = value._auto_cast(itemT) else: value = itemT.from_py(value) return Bits3val.__setitem__(self, index, value) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __invert__(self) -> Self: try: return Bits3val.__invert__(self) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __hash__(self): return Bits3val.__hash__(self) # comparisons def _isOn(self): return self._auto_cast(BOOL) def _eq(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsCmp(self, True, other, HwtOps.EQ, _b1, eq) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __ne__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsCmp(self, True, other, HwtOps.NE, _b0) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __lt__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsCmp(self, True, other, HwtOps.SLT if self._dtype.signed else HwtOps.ULT, _b0, evalFn=HwtOps.LT._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __gt__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsCmp(self, True, other, HwtOps.SGT if self._dtype.signed else HwtOps.UGT, _b0, evalFn=HwtOps.GT._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __ge__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsCmp(self, True, other, HwtOps.SGE if self._dtype.signed else HwtOps.UGE, _b1, evalFn=HwtOps.GE._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __le__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsCmp(self, True, other, HwtOps.SLE if self._dtype.signed else HwtOps.ULE, _b1, evalFn=HwtOps.LE._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified # bitwise def __xor__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsBitOp(self, True, other, HwtOps.XOR, vld_mask_for_xor, tryReduceXor, reduceSigCheckFnXor) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __and__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsBitOp(self, True, other, HwtOps.AND, vld_mask_for_and, tryReduceAnd, reduceSigCheckFnAnd) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __or__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsBitOp(self, True, other, HwtOps.OR, vld_mask_for_or, tryReduceOr, reduceSigCheckFnOr) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __lshift__(self, other: Union[int, Self]) -> Union[Self, "HBitsRtlSignal"]: try: return bitsLshift(self, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __rshift__(self, other: Union[int, Self]) -> Union[Self, "HBitsRtlSignal"]: try: return bitsRshift(self, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __neg__(self) -> Self: try: return Bits3val.__neg__(self) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified # arithmetic def __sub__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsArithOp(self, True, other, HwtOps.SUB) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __add__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsArithOp(self, True, other, HwtOps.ADD) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __floordiv__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsFloordiv(self, True, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __mul__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsMul(self, True, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __mod__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsRtlSignal"]: try: return bitsRem(self, True, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _ternary(self, vTrue: Union[Self, "HBitsRtlSignal"], vFalse: Union[Self, "HBitsRtlSignal"]) -> Union[Self, "HBitsRtlSignal"]: try: vTrue = toHVal(vTrue) vFalse = toHVal(vFalse, suggestedType=vTrue._dtype) if not self._is_full_valid(): return vTrue._dtype.from_py(None) elif self: return vTrue else: return vFalse except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __abs__(self): if not self._dtype.signed: return self return (self < 0)._ternary(-self, self) def getMsb(self) -> Self: return self[self._dtype.bit_length() - 1] def __len__(self) -> int: return self._dtype.bit_length() def __eq__(self, other): if isinstance(other, HConst): t = self._dtype o_t = other._dtype typeCompatible = ( t == o_t or ( t.bit_length() == 1 and o_t.bit_length() == 1 and \ t.signed is o_t.signed and \ t.force_vector != o_t.force_vector ) ) return typeCompatible and \ self.vld_mask == other.vld_mask and\ self.val == other.val else: return False def prettyRepr(self) -> str: t = self._dtype bs = bit_string(self.val, t.bit_length(), self.vld_mask) signChar = ('i' if t.signed else 'b' if t.signed is None else 'u') b = bs.base if t.bit_length() == 1 and t.force_vector: vecSpec = "vec" else: vecSpec = "" if b == 2: if bs.bits == 1: base_char = "" else: base_char = 'b' elif b == 8: base_char = 'O' elif b == 10: base_char = 'd' elif b == 16: base_char = 'h' else: raise NotImplementedError(b) return f"{signChar:s}{t.bit_length()}{vecSpec:s}'{base_char}{bs.val}" def __repr__(self) -> str: return Bits3val.__repr__(self) _b1 = BIT.from_py(1) _b0 = BIT.from_py(0) ================================================ FILE: hwt/hdl/types/bitsRtlSignal.py ================================================ from copy import copy from operator import eq from typing import Union, Optional, Self, Literal from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.bitConstFunctions import bitsCmp, \ bitsBitOp, bitsArithOp, bitsFloordiv, bitsMul, bitsLshift, \ bitsRshift, HBitsAnyIndexCompatibleValue, HBitsAnyCompatibleValue, bitsRem from hwt.hdl.types.bitConstFunctionsGetitem import _get_operator_i_am_the_result_of, \ bitsGetitem, _fold_concat_of_msb_using_sext from hwt.hdl.types.bitConst_opReduce import tryReduceOr, tryReduceAnd, \ tryReduceXor, reduceSigCheckFnAnd, reduceSigCheckFnOr, reduceSigCheckFnXor from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL, BIT from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import HwIOBase from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from pyMathBitPrecise.bit_utils import ValidityError from pyMathBitPrecise.bits3t import _NOT_SPECIFIED, Bits3val from pyMathBitPrecise.bits3t_vld_masks import vld_mask_for_xor, vld_mask_for_and, \ vld_mask_for_or class HBitsRtlSignal(RtlSignal): @internal def _cast_sign(self, signed: Optional[bool]) -> Self: """ Convert signum, no bit manipulation just data are represented differently :param signed: if True value will be signed, if False value will be unsigned, if None value will be vector without any sign specification """ if self._dtype.signed == signed: return self t = copy(self._dtype) t.signed = signed if t.signed is not None and t.bit_length() == 1: t.force_vector = True if signed is None: cnv = HwtOps.BitsAsVec elif signed: cnv = HwtOps.BitsAsSigned else: cnv = HwtOps.BitsAsUnsigned return HOperatorNode.withRes(cnv, [self], t) def _signed(self) -> Self: return self._cast_sign(True) def _unsigned(self) -> Self: return self._cast_sign(False) def _vec(self) -> Self: return self._cast_sign(None) def _concat(self, other: Union["HBitsConst", Self]) -> Self: """ Concatenate this with other to one wider value/signal """ try: other._dtype.bit_length except AttributeError: raise TypeError("Can not concat HBits with an object of unknown size or endianity", other) try: w = self._dtype.bit_length() other_w = other._dtype.bit_length() if isinstance(self, HwIOBase): self = self._sig if isinstance(other, HwIOBase): other = other._sig if isinstance(self, HBitsRtlSignal) and isinstance(other, HBitsRtlSignal): if w == 1: sext = _fold_concat_of_msb_using_sext(self, 1, other, other_w) if sext is not None: return sext else: # it may be sext of msb bit operator0 = _get_operator_i_am_the_result_of(self) if operator0 == HwtOps.SEXT: op0 = self.singleDriver() op0Src = op0.operands[0] if op0Src._dtype.bit_length() == 1: sext = _fold_concat_of_msb_using_sext(op0Src, int(op0.operands[1]), other, other_w) if sext is not None: # fold concat(x.msb.sext(), x) -> x.sext() return sext self = self._vec() resWidth = w + other_w HBits = self._dtype.__class__ resT = HBits(resWidth, signed=self._dtype.signed, force_vector=resWidth == 1) # is instance of signal if other._dtype == BOOL: other = other._auto_cast(BIT) elif isinstance(other._dtype, HBits): if other._dtype.signed is not None: other = other._vec() else: raise TypeError(other._dtype) if self._dtype.signed is not None: self = self._vec() return HOperatorNode.withRes(HwtOps.CONCAT, [self, other], resT)\ ._auto_cast(HBits(resWidth, signed=self._dtype.signed)) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _ext(self, newWidth: Union[int, "HBitsConst"], signed: Union[bool, Literal[NOT_SPECIFIED]]=NOT_SPECIFIED) -> Self: """ construct zext/sext operator :note: preserves sign of type """ assert newWidth > 0, newWidth if signed is NOT_SPECIFIED: signed = bool(self._dtype.signed) else: assert isinstance(signed, bool), signed try: w = self._dtype.bit_length() extBitCnt = int(newWidth) - w except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified if extBitCnt < 0: raise AssertionError("newWidth >= current width", newWidth, w, self,) elif extBitCnt == 0: return self extOp = HwtOps.SEXT if signed else HwtOps.ZEXT try: resTy = self._dtype._createMutated(bit_length=w + extBitCnt) while True: # fold ext((ext(x))) -> ext(x) selfOp = _get_operator_i_am_the_result_of(self) if selfOp == extOp: self = self.singleDriver().operands[0] continue elif selfOp == HwtOps.CONCAT: d: HOperatorNode = self.singleDriver() assert len(d.operands) == 2 hiBits, loBits = d.operands newHiBitsExtWidth = newWidth - w + hiBits._dtype.bit_length() if isinstance(hiBits, HConst): hiBits = hiBits._ext(newHiBitsExtWidth, signed) # fold ext(concat(c, x)) to concat(cExtended, x) return hiBits._concat(loBits) else: selfHiBitsOp = _get_operator_i_am_the_result_of(hiBits) if selfHiBitsOp == extOp: # fold ext(concat(ext(x), y)) to concat(ext(x), y) return hiBits.singleDriver().operands[0]._ext(newHiBitsExtWidth, signed)._concat(loBits) break return HOperatorNode.withRes(extOp, [self, toHVal(newWidth)], resTy) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _sext(self, newWidth: Union[int, "HBitsConst"]) -> Self: """ signed extension, pad with MSB bit on MSB side to newWidth result width :see: :meth:`HBitsRtlSignal._ext` """ return self._ext(newWidth, True) def _zext(self, newWidth: Union[int, "HBitsConst"]) -> Self: """ zero extension, pad with 0 on msb side to newWidth result width :see: :meth:`HBitsRtlSignal._ext` """ return self._ext(newWidth, False) def _trunc(self, newWidth: Union[int, "HBitsConst"]): assert newWidth > 0, newWidth try: w = self._dtype.bit_length() cutBitCnt = w - int(newWidth) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified if cutBitCnt < 0: raise AssertionError("newWidth <= current width", newWidth, w, self,) elif cutBitCnt == 0: return self try: if newWidth == 1 and self._dtype.signed is None: # fold x._trunc(1) to x[0] return self[0] resTy = self._dtype._createMutated(bit_length=w - cutBitCnt) while True: # fold trunc((trunc(x))) -> trunc(x) # fold trunc(concat(a, x)) -> trunc(x) if trunc select only lower first (lsb) member of concat selfOp = _get_operator_i_am_the_result_of(self) if selfOp == HwtOps.TRUNC: self = self.singleDriver().operands[0] continue elif selfOp == HwtOps.BitsAsSigned or selfOp == HwtOps.BitsAsUnsigned: # fold x._signed()._trunc() to x._trunc()._signed() return selfOp._evalFn(self.singleDriver().operands[0]._trunc(newWidth)) elif selfOp == HwtOps.CONCAT: concLowBits = self.singleDriver().operands[0] concLowBitsWidth = concLowBits._dtype.bit_length() if concLowBitsWidth == resTy.bit_length(): return concLowBits elif concLowBitsWidth > resTy.bit_length(): self = concLowBits continue break return HOperatorNode.withRes(HwtOps.TRUNC, [self, toHVal(newWidth)], resTy) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _extOrTrunc(self, newWidth: int, signed: Union[bool, None, Literal[_NOT_SPECIFIED]]=_NOT_SPECIFIED) -> Self: return Bits3val._extOrTrunc(self, newWidth, signed) def __getitem__(self, key: HBitsAnyIndexCompatibleValue) -> Union["HBitsConst", Self]: """ :see: :func:`bitsGetitem` """ try: return bitsGetitem(self, False, key) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __setitem__(self, index, value): raise TypeError("To assign a member of hdl array/vector/list/... use a[index](c) instead of a[index] = c") def __invert__(self) -> Self: try: # try reduce double negation d = self.singleDriver() if isinstance(d, HOperatorNode) and d.operator == HwtOps.NOT: return d.operands[0] except SignalDriverErr: pass return HOperatorNode.withRes(HwtOps.NOT, [self], self._dtype) def __hash__(self) -> int: return hash(id(self)) # comparisons def _isOn(self) -> Self: return self._auto_cast(BOOL) def _eq(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsCmp(self, False, other, HwtOps.EQ, BIT.from_py(1), eq) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __ne__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsCmp(self, False, other, HwtOps.NE, BIT.from_py(0)) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __lt__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsCmp(self, False, other, HwtOps.SLT if self._dtype.signed else HwtOps.ULT, BIT.from_py(0), evalFn=HwtOps.LT._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __gt__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsCmp(self, False, other, HwtOps.SGT if self._dtype.signed else HwtOps.UGT, BIT.from_py(0), evalFn=HwtOps.GT._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __ge__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsCmp(self, False, other, HwtOps.SGE if self._dtype.signed else HwtOps.UGE, BIT.from_py(1), evalFn=HwtOps.GE._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __le__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsCmp(self, False, other, HwtOps.SLE if self._dtype.signed else HwtOps.ULE, BIT.from_py(1), evalFn=HwtOps.LE._evalFn) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified # bitwise def __xor__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsBitOp(self, False, other, HwtOps.XOR, vld_mask_for_xor, tryReduceXor, reduceSigCheckFnXor) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __and__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsBitOp(self, False, other, HwtOps.AND, vld_mask_for_and, tryReduceAnd, reduceSigCheckFnAnd) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __or__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsBitOp(self, False, other, HwtOps.OR, vld_mask_for_or, tryReduceOr, reduceSigCheckFnOr) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __lshift__(self, other: Union[int, "HBitsConst"]) -> Union[Self, "HBitsConst"]: try: return bitsLshift(self, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __rshift__(self, other: Union[int, "HBitsConst"]) -> Union[Self, "HBitsConst"]: try: return bitsRshift(self, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __neg__(self) -> Self: if not self._dtype.signed: self = self._signed() resT = self._dtype o = HOperatorNode.withRes(HwtOps.MINUS_UNARY, [self], self._dtype) return o._auto_cast(resT) # arithmetic def __sub__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsArithOp(self, False, other, HwtOps.SUB) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __add__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsArithOp(self, False, other, HwtOps.ADD) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __floordiv__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsFloordiv(self, False, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __mul__(self, other: HBitsAnyCompatibleValue) -> Union["HBitsConst", Self]: try: return bitsMul(self, False, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __mod__(self, other: HBitsAnyCompatibleValue) -> Union[Self, "HBitsConst"]: try: return bitsRem(self, True, other) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _ternary(self, vTrue: Union["HBitsConst", Self], vFalse: Union["HBitsConst", Self]) -> Union["HBitsConst", Self]: try: vTrue = toHVal(vTrue) vFalse = toHVal(vFalse, suggestedType=vTrue._dtype) try: if vTrue == vFalse: return vTrue except (ValidityError, NotImplementedError): pass if not (vTrue._dtype == vFalse._dtype): # all case values of ternary has to have same type vFalse = vFalse._auto_cast(vTrue._dtype) if vTrue._dtype.isScalar(): return HOperatorNode.withRes( HwtOps.TERNARY, [self, vTrue, vFalse], vTrue._dtype) else: # elementwise res = copy(vTrue) if isinstance(res, RtlSignal): raise NotImplementedError(vTrue) elif isinstance(res, HwIOBase): res._hwIOs = [] for iTrue in vTrue._hwIOs: iFalse = iTrue._onParentPropertyPath.getOnObject(vFalse) newI = self._ternary(iTrue, iFalse) iTrue._onParentPropertyPath.setOnObject(res, newI) res._hwIOs.append(newI) _fieldsToHwIOs = getattr(res, "_fieldsToHwIOs", None) else: for i, (t, f) in enumerate(zip(vTrue, vFalse)): res[i] = self._ternary(t, f) if _fieldsToHwIOs is not None: res._fieldsToHwIOs = {p: p.getOnObject(res) for p in _fieldsToHwIOs.keys()} return res except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def __abs__(self): if not self._dtype.signed: return self return (self < 0)._ternary(-self, self) def getMsb(self) -> Self: return self[self._dtype.bit_length() - 1] def _onFallingEdge(self) -> Self: return HOperatorNode.withRes(HwtOps.FALLING_EDGE, [self], BOOL) def _onRisingEdge(self) -> Self: return HOperatorNode.withRes(HwtOps.RISING_EDGE, [self], BOOL) def __len__(self) -> int: return self._dtype.bit_length() ================================================ FILE: hwt/hdl/types/defs.py ================================================ """ Definitions of most common types """ from hwt.hdl.types.bits import HBits from hwt.hdl.types.float import HFloat from hwt.hdl.types.slice import HSlice from hwt.hdl.types.string import HString BOOL = HBits(bit_length=1, name="bool") INT = HBits(bit_length=32, signed=True, name="int", strict_sign=False, strict_width=False) BIT = HBits(bit_length=1) BIT_N = HBits(bit_length=1, negated=True) STR = HString() SLICE = HSlice() FLOAT64 = HFloat(11, 52, name="float64") ================================================ FILE: hwt/hdl/types/enum.py ================================================ from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override # [TODO] use python enum and only emulate HDL enum for HDL class HEnum(HdlType): """ Hdl enum type :ivar ~.name: name of this type :ivar ~._allValues: tuple of all values for this enum :note: for each value there is a property on this type object """ def __init__(self, name, valueNames, const=False): """ :param name: name for this type :param valueNames: sequence of string which will be used as names for enum members """ super(HEnum, self).__init__(const=const) self.name = name self._allValues = tuple(valueNames) for name in valueNames: v = self.from_py(name) assert not hasattr(self, name) setattr(self, name, v) def all_mask(self): return 1 def bit_length(self): return len(self._allValues).bit_length() @internal def domain_size(self): """ :return: how many values can have specified type """ return int(2 ** self.bit_length()) @internal @override @classmethod def getRtlSignalCls(cls): try: return cls._rtlSignalCls except AttributeError: from hwt.hdl.types.enumConst import HEnumRtlSignal cls._rtlSignalCls = HEnumRtlSignal return cls._rtlSignalCls @internal @override @classmethod def getConstCls(cls): try: return cls._constCls except AttributeError: from hwt.hdl.types.enumConst import HEnumConst cls._constCls = HEnumConst return cls._constCls ================================================ FILE: hwt/hdl/types/enumConst.py ================================================ from typing import Union, Self from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.defs import BOOL from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal _HBoolConst = BOOL.getConstCls() class HEnumRtlSignal(RtlSignal): def _eq(self, other: Union[Self, "HEnumConst"]) -> "HBitsConst": assert self._dtype is other._dtype, (self._dtype, other._dtype) return HOperatorNode.withRes(HwtOps.EQ, [self, other], BOOL) def __ne__(self, other: Union[Self, "HEnumConst"]) -> "HBitsConst": assert self._dtype is other._dtype, (self._dtype, other._dtype) return HOperatorNode.withRes(HwtOps.NE, [self, other], BOOL) class HEnumConst(HConst): @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param val: value of python type bool or None :param typeObj: instance of HEnum :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ if val is None: assert vld_mask is None or vld_mask == 0 valid = False val = typeObj._allValues[0] else: if vld_mask is None or vld_mask == 1: assert isinstance(val, str) valid = True else: valid = False val = None return cls(typeObj, val, valid) def _eq(self, other: Union[HEnumRtlSignal, Self]) -> "HBitsConst": if isinstance(other, RtlSignal): return HEnumRtlSignal._eq(other) assert self._dtype is other._dtype, (self._dtype, other._dtype) eq = self.val == other.val \ and self.vld_mask == other.vld_mask == 1 vld_mask = int(self.vld_mask == other.vld_mask == 1) return _HBoolConst(BOOL, int(eq), vld_mask) def __ne__(self, other: Union[HEnumRtlSignal, Self]) -> "HBitsConst": if isinstance(other, RtlSignal): return HEnumRtlSignal.__ne__(other) assert self._dtype is other._dtype, (self._dtype, other._dtype) neq = self.val != other.val \ and self.vld_mask == other.vld_mask == 1 vld_mask = int(self.vld_mask == other.vld_mask == 1) return _HBoolConst(BOOL, int(neq), vld_mask) ================================================ FILE: hwt/hdl/types/float.py ================================================ from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override from pyMathBitPrecise.floatt import Floatt class HFloat(HdlType, Floatt): """ Basic HDL type representing IEEE 754 like float type. :note: This type is meant for HwModule parameters, operations with this type are not synthetisable. """ def __init__(self, exponent_w, mantisa_w, name=None, const=False): """ :param negated: if true the value is in negated form """ HdlType.__init__(self, const=const) assert exponent_w > 0, exponent_w assert mantisa_w > 0, mantisa_w Floatt.__init__(self, exponent_w, mantisa_w, name=name) @internal @override @classmethod def getConstCls(cls): from hwt.hdl.types.floatConst import HFloatConst return HFloatConst @internal @override @classmethod def getRtlSignalCls(cls): from hwt.hdl.types.floatConst import HFloatRtlSignal return HFloatRtlSignal ================================================ FILE: hwt/hdl/types/floatConst.py ================================================ from copy import copy from decimal import DecimalTuple import math from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.defs import BOOL from hwt.hdl.types.typeCast import toHVal from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from pyMathBitPrecise.floatt import FloattVal _HBoolConst = BOOL.getConstCls() def _HFloatEq(self, self_is_val: bool, other): other = toHVal(other, self._dtype) other_is_val = isinstance(self, HConst) if self_is_val and other_is_val: return _HBoolConst(BOOL, int(self.val == other.val), self.vld_mask & other.vld_mask) else: assert self._dtype == other._dtype, (self, self._dtype, other, other._dtype) return HOperatorNode.withRes(HwtOps.EQ, [self, other], BOOL) class HFloatRtlSignal(RtlSignal): def _eq(self, other): return _HFloatEq(self, False, other) class HFloatConst(HConst, FloattVal): """ HConst class for HFloat type. """ @classmethod def from_py(cls, typeObj, val, vld_mask=None): assert vld_mask is None, vld_mask if isinstance(val, int): val = float(val) if float(val) != val: raise NotImplementedError("Need to implement better conversion method") if val is None: sign = 0 exp = 0 man = 0 assert vld_mask is None or vld_mask == 0 vld_mask = 0 elif isinstance(val, float): man, exp = math.frexp(val) man = abs(man) man = int(man * (2 ** typeObj.mantisa_w)) sign = int(val < 0) if vld_mask is None: vld_mask = 1 elif isinstance(val, tuple): sign, man, exp = val if vld_mask is None: vld_mask = 1 else: raise TypeError(val) return cls(typeObj, DecimalTuple(sign, man, exp), vld_mask) def _is_full_valid(self): return self.vld_mask == 1 def to_py(self): """ Convert to python slice object """ return float(self) def _eq(self, other): return _HFloatEq(self, True, other) def __copy__(self): v = HConst.__copy__(self) v.val = copy(v.val) return v @internal def __hash__(self): v = self.val return hash((self._dtype, v)) ================================================ FILE: hwt/hdl/types/function.py ================================================ from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override class HFunction(HdlType): """ A type which represent reference to HDL function. :note: For compatibility with HDL only. It is not meant to be used as a function pointer to call a function in synthetisable code. """ def all_mask(self): return 1 @internal @override @classmethod def getConstCls(cls): return HFunctionConst class HFunctionConst(HConst): pass ================================================ FILE: hwt/hdl/types/hdlType.py ================================================ from enum import Enum from typing import Union, Type, Self, Optional from hwt.doc_markers import internal from hwt.synthesizer.exceptions import TypeConversionErr class MethodNotOverloaded(NotImplementedError): """ Method is missing overload of abstract parent method. """ pass class HdlType(): """ Base class for all hardware related types. :ivar const: if True the type has const specifier which means that the value should not be modified after initialization and is read only :note: Cast functions are linked trough HldType class because Python lacks forward declarations. :cvar ~._PRECOMPUTE_CONSTANT_SIGNALS: if true a constant expressions made from this type have value pre-computed. :note: Each implementation of HdlType also defines is HConst and RtlSignal class. These classes then implement operators and methods for constants/expressions. """ _PRECOMPUTE_CONSTANT_SIGNALS = True def __init__(self, const=False): self.const = const def _from_py(self, v, vld_mask) -> "HConst": """ same as from_py just without type checks """ return self.getConstCls()._from_py(self, v, vld_mask) def from_py(self, v, vld_mask=None) -> "HConst": """ Construct value of this type. Delegated on value class for this type """ if isinstance(v, Enum): v = v.value return self.getConstCls().from_py(self, v, vld_mask=vld_mask) def auto_cast_HConst(self, v: "HConst", toType: Self) -> "HConst": """ Cast constant of this type to another compatible type. :note: auto cast may change bitwidth if type implements it in auto cast :param v: constant to cast :param toType: instance of HdlType to cast into """ if v._dtype == toType: return v try: castFn = self._auto_cast_HConst_fn except AttributeError: castFn = self.get_auto_cast_HConst_fn() self._auto_cast_HConst_fn = castFn try: return castFn(self, v, toType) except TypeConversionErr: pass return toType._reverse_auto_cast_HConst(v, self) def auto_cast_RtlSignal(self, v: "RtlSignal", toType: Self) -> "RtlSignal": """ Equivalent of :meth:`~.auto_cast_HConst` for :class:`RtlSignal` instances """ if v._dtype == toType: return v try: castFn = self._auto_cast_RtlSignal_fn except AttributeError: castFn = self.get_auto_cast_RtlSignal_fn() self._auto_cast_RtlSignal_fn = castFn try: return castFn(self, v, toType) except TypeConversionErr: pass return toType._reverse_auto_cast_RtlSignal(v, self) def reinterpret_cast_HConst(self, v: "HConst", toType: Self) -> "HConst": """ Cast constant of this type to another type of same size. :param v: constant to cast :param toType: instance of HdlType to cast into """ if v._dtype == toType: return v try: castFn = self._reinterpret_cast_HConst_fn except AttributeError: castFn = self.get_reinterpret_cast_HConst_fn() self._reinterpret_cast_HConst_fn = castFn try: return castFn(self, v, toType) except TypeConversionErr: pass return toType._reverse_reinterpret_cast_HConst(v, self) def reinterpret_cast_RtlSignal(self, v: "RtlSignal", toType: Self) -> "RtlSignal": """ Cast value or signal of this type to another type of same size. :param v: signal to cast :param toType: instance of HdlType to cast into """ if v._dtype == toType: return v try: castFn = self._reinterpret_cast_RtlSignal_fn except AttributeError: castFn = self.get_reinterpret_cast_RtlSignal_fn() self._reinterpret_cast_RtlSignal_fn = castFn try: # call _reinterpret_cast_RtlSignal_fn to cast this type to toType return castFn(self, v, toType) except TypeConversionErr: pass return toType._reverse_reinterpret_cast_RtlSignal(v, self) # reverse casts which are doing the same thing but cast methods are implemented on dst type # :note: this is there to allow casting old types(self) to a new types(toType) # when old type does not know anything about new type def _reverse_auto_cast_HConst(self, v: "HConst", fromType: Self) -> "HConst": try: castFn = self._reverse_auto_cast_HConst_fn except AttributeError: castFn = self.get_reverse_auto_cast_HConst_fn() self._reverse_auto_cast_HConst_fn = castFn return castFn(self, v, fromType) def _reverse_auto_cast_RtlSignal(self, v: "RtlSignal", fromType: Self) -> "RtlSignal": try: castFn = self._reverse_auto_cast_RtlSignal_fn except AttributeError: castFn = self.get_reverse_auto_cast_RtlSignal_fn() self._reverse_auto_cast_RtlSignal_fn = castFn return castFn(self, v, fromType) def _reverse_reinterpret_cast_HConst(self, v: "HConst", fromType: Self) -> "HConst": try: castFn = self._reverse_reinterpret_cast_HConst_fn except AttributeError: castFn = self.get_reverse_reinterpret_cast_HConst_fn() self._reverse_reinterpret_cast_HConst_fn = castFn return castFn(self, v, fromType) def _reverse_reinterpret_cast_RtlSignal(self, v: "RtlSignal", fromType: Self) -> "RtlSignal": try: castFn = self._reverse_reinterpret_cast_RtlSignal_fn except AttributeError: castFn = self.get_reverse_reinterpret_cast_RtlSignal_fn() self._reverse_reinterpret_cast_RtlSignal_fn = castFn return castFn(self, v, fromType) # methods for getting cast function which cast value of one type to another # for more details :see: methods for casting of this class @internal @classmethod def get_auto_cast_HConst_fn(cls): return default_auto_cast_fn @internal @classmethod def get_auto_cast_RtlSignal_fn(cls): return default_auto_cast_fn @internal @classmethod def get_reverse_auto_cast_RtlSignal_fn(cls): return default_reverse_auto_cast_fn @internal @classmethod def get_reverse_auto_cast_HConst_fn(cls): return default_reverse_auto_cast_fn @internal @classmethod def get_reinterpret_cast_HConst_fn(cls): return default_reinterpret_cast_fn @internal @classmethod def get_reverse_reinterpret_cast_HConst_fn(cls): return default_reverse_reinterpret_cast_fn @internal @classmethod def get_reinterpret_cast_RtlSignal_fn(cls): return default_reinterpret_cast_fn @internal @classmethod def get_reverse_reinterpret_cast_RtlSignal_fn(cls): return default_reverse_reinterpret_cast_fn @internal @classmethod def getConstCls(cls) -> Type["HConst"]: """ :attention: Overrode in implementation of concrete HdlType. :return: class for value derived from this type """ raise NotImplementedError(cls) @internal @classmethod def getRtlSignalCls(cls) -> Type["RtlSignal"]: """ :attention: Overrode in implementation of concrete HdlType. :return: class for value derived from this type """ raise NotImplementedError(cls) def _as_hdl(self, to_Hdl: "ToHdlAst", declaration:bool): raise MethodNotOverloaded() def _as_hdl_requires_def(self, to_Hdl: "ToHdlAst", other_types: list): raise MethodNotOverloaded() def isScalar(self): return True def __getitem__(self, key): """ [] operator to create an array of this type. """ assert int(key) > 0, key # array has to have some items from hwt.hdl.types.array import HArray return HArray(self, key) def __repr__(self, indent:int=0, withAddr:Optional[int]=None, expandStructs=False): """ :param indent: number of indentation :param withAddr: if is not None, it is used as a additional information about on which address this type is stored (used only by HStruct) :param expandStructs: expand HStructTypes (used by HStruct and Array) """ name = getattr(self, "name", "") if name is None: name = "" return f"<{self.__class__.__name__:s} {name:s}>" @internal def default_auto_cast_fn(typeFrom: HdlType, sigOrConst: Union["RtlSignal", "HConst"], toType: HdlType): raise TypeConversionErr("auto_cast", typeFrom, "->", toType, "is not implemented") @internal def default_reverse_auto_cast_fn(toType: HdlType, sigOrConst: Union["RtlSignal", "HConst"], fromType: HdlType): raise TypeConversionErr("auto_cast", fromType, "->", toType, "is not implemented") @internal def default_reinterpret_cast_fn(fromType: HdlType, sigOrConst: Union["RtlSignal", "HConst"], toType: HdlType): raise TypeConversionErr("reinterpret_cast", fromType, "->", toType, "is not implemented") @internal def default_reverse_reinterpret_cast_fn(toType: HdlType, sigOrConst: Union["RtlSignal", "HConst"], fromType: HdlType): raise TypeConversionErr("reinterpret_cast", fromType, "->", toType, "is not implemented") ================================================ FILE: hwt/hdl/types/slice.py ================================================ from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override class HSlice(HdlType): """ Slice type, used for selecting items from arrays or vectors """ @internal @classmethod def getConstCls(cls): try: return cls._constCls except AttributeError: from hwt.hdl.types.sliceConst import HSliceConst cls._constCls = HSliceConst return cls._constCls @internal @override @classmethod def getRtlSignalCls(cls): try: return cls._rtlSignalCls except AttributeError: from hwt.hdl.types.sliceConst import HSliceRtlSignal cls._rtlSignalCls = HSliceRtlSignal return cls._rtlSignalCls ================================================ FILE: hwt/hdl/types/sliceConst.py ================================================ from copy import copy from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import INT from hwt.mainBases import RtlSignalBase from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal def slice_member_to_HConst(v): if isinstance(v, RtlSignalBase): # is signal assert isinstance(v._dtype, HBits) return v elif isinstance(v, HConst): if isinstance(v, HBitsConst): return v else: return v._auto_cast(INT) else: return INT.from_py(v) class HSliceRtlSignal(RtlSignal): pass class HSliceConst(HConst): """ HConst class for HSlice type """ @classmethod def from_py(cls, typeObj, val, vld_mask=None): assert vld_mask is None, vld_mask if val is None: val = slice(None, None, None) else: assert isinstance(val, slice), val start = slice_member_to_HConst(val.start) stop = slice_member_to_HConst(val.stop) step = slice_member_to_HConst(val.step) val = slice(start, stop, step) return cls(typeObj, val, vld_mask=1) def _is_full_valid(self) -> bool: v = self.val return v.start._is_full_valid() and v.stop._is_full_valid() def _is_partially_valid(self) -> bool: v = self.val return v.start._is_partially_valid() and v.stop._is_partially_valid() def to_py(self): """ Convert to python slice object """ v = self.val return slice(int(v.start), int(v.stop), int(v.step)) def _size(self): """ :return: how many bits is this slice selecting """ v = self.val if v.step == -1: return int(v.start) - int(v.stop) elif v.step == 1: return int(v.stop) - int(v.start) else: raise NotImplementedError(self) def _eq(self, other): return self.val == other.val def __lt__(self, other): if self.val.step != other.val.step: raise ValueError() if isinstance(other, INT.getConstCls()): return self.val.start < other else: return (self.val.start, self.val.stop) < (other.val.start, other.val.stop) def __copy__(self): v = HConst.__copy__(self) v.val = copy(v.val) return v def staticEval(self): v = self.val new_v = slice( v.start.staticEval(), v.stop.staticEval(), v.step.staticEval(), ) return self.__class__.from_py(self._dtype, new_v) @internal def __hash__(self): v = self.val return hash((self._dtype, v.start, v.stop, v.step)) def __repr__(self): v = self.val if self._is_full_valid(): return f"<{self.__class__.__name__:s} {int(v.start):d}:{int(v.stop):d}:{int(v.step):d}>" else: vld_mask = ", mask {0:x}".format(self.vld_mask) return f"<{self.__class__.__name__:s} {v}{vld_mask:s}>" ================================================ FILE: hwt/hdl/types/sliceUtils.py ================================================ from hwt.doc_markers import internal from hwt.hdl.types.defs import INT, SLICE from hwt.hdl.types.slice import HSlice from hwt.hdl.types.typeCast import toHVal @internal def slice_to_HSlice(sliceVals: slice, widthOfSlicedVec: int): """convert python slice to value of SLICE hdl type""" if sliceVals.step is None: step = -1 else: step = sliceVals.step start = sliceVals.start if start is None: start = INT.from_py(widthOfSlicedVec) else: start = toHVal(start) stop = sliceVals.stop if stop is None: stop = INT.from_py(0) else: stop = toHVal(stop) v = slice(start, stop, step) return HSlice.getConstCls()(SLICE, v, 1) ================================================ FILE: hwt/hdl/types/stream.py ================================================ from math import inf, isinf from typing import List, Optional from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override from hwt.serializer.generic.indent import getIndent class HStream(HdlType): """ Stream is an abstract type. It is an array with unspecified size. :ivar ~.element_t: type of the smallest chunk of data which can be send over this stream :ivar ~.len_min: minimum repetitions of element_t (inclusive interval) :ivar ~.len_max: maximum repetitions of element_t (inclusive interval) :ivar ~.start_offsets: list of numbers which represents the number of invalid bytes before valid data on stream (invalid bytes means the bytes which does not have bit validity set, e.g. Axi4Stream keep=0b10 -> offset=1 ) """ def __init__(self, element_t, frame_len=inf, start_offsets: Optional[List[int]]=None, const=False): super(HStream, self).__init__(const=const) self.element_t = element_t if isinstance(frame_len, float) and isinf(frame_len): frame_len = (1, inf) elif isinstance(frame_len, int): frame_len = (frame_len, frame_len) self.len_min, self.len_max = frame_len if start_offsets is None: start_offsets = (0, ) self.start_offsets = tuple(start_offsets) def bit_length(self): if self.len_min != self.len_max or isinf(self.len_max): raise TypeError("This HStream does not have constant size", self) else: # len_min == len_max return self.len_min * self.element_t.bit_length() def __eq__(self, other: HdlType): if self is other: return True if (type(self) is type(other)): if self.start_offsets == other.start_offsets \ and self.len_min == other.len_min \ and self.len_max == other.len_max: return self.element_t == other.element_t return False def __hash__(self): return hash((self.start_offsets, self.len_min, self.len_max, self.element_t)) @internal @override @classmethod def getConstCls(cls): try: return cls._constCls except AttributeError: from hwt.hdl.types.streamConst import HStreamConst cls._constCls = HStreamConst return cls._constCls def __repr__(self, indent=0, withAddr=None, expandStructs=False): return "%s<%s len:%s, align:%r\n%s>" % ( getIndent(indent), self.__class__.__name__, (self.len_min, self.len_max), self.start_offsets, self.element_t.__repr__(indent=indent+1, withAddr=withAddr, expandStructs=expandStructs), ) ================================================ FILE: hwt/hdl/types/streamConst.py ================================================ from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL, INT from hwt.hdl.types.slice import HSlice from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import RtlSignalBase class HStreamConst(HConst): """ Class for values of HStream HDL type """ @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param typeObj: HStream instance :param val: None or iterrable of values :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ min_len = typeObj.len_min if vld_mask == 0: val = None element_t = typeObj.element_t if val is None: elements = [element_t.from_py(None) for _ in range(min_len)] else: elements = [] for v in val: if isinstance(v, RtlSignalBase): # is signal assert v._dtype == typeObj.element_t e = v else: e = typeObj.element_t.from_py(v) elements.append(e) cur_len = len(elements) assert cur_len >= min_len and cur_len <= typeObj.len_max _mask = int(bool(val)) if vld_mask is None: vld_mask = _mask else: assert (vld_mask == _mask) return cls(typeObj, elements, vld_mask) def to_py(self): if not self._is_full_valid(): raise ValueError(f"Value of {self} is not fully defined") return [v.to_py() for v in self.val] @internal def __hash__(self): return hash((self._dtype, self.val, self.vld_mask)) def _is_full_valid(self): return self.vld_mask == 1 @internal def _getitem__const(self, key): """ :attention: this will clone item from array, iterate over .val if you need to modify items """ kv = key.val if not key._is_full_valid(): raise KeyError() return self.val[kv].__copy__() def __getitem__(self, key): key = toHVal(key) isSLICE = isinstance(key, HSlice.getConstCls()) if isSLICE: raise NotImplementedError() elif isinstance(key, (HConst, RtlSignalBase)): pass else: raise NotImplementedError( f"Index operation not implemented for index {key}") kv = key.val if not key._is_full_valid(): raise KeyError() return self.val[kv].__copy__() def __setitem__(self, index, value): """ Only syntax sugar for user, not used inside HWT * In HW design is not used (__getitem__ returns "reference" and it is used) """ if isinstance(index, int): index = INT.from_py(index) else: assert isinstance(index._dtype, HBits), index._dtype if not isinstance(value, HConst): value = self._dtype.element_t.from_py(value) else: assert value._dtype == self._dtype.element_t, ( value._dtype, self._dtype.element_t) if index._is_full_valid(): self.val[index.val] = value.__copy__() else: self.val = {} return self def __iter__(self): return iter(self.val) def __len__(self): return len(self.val) def _eq(self, other): assert isinstance(other, HStreamConst) assert self._dtype.element_t == other._dtype.element_t eq = True vld = 1 if (len(self.val) == len(other.val)): for a, b in zip(self.val, other.val): eq = eq and bool(a) == bool(b) if not eq: break vld = vld & a.vld_mask & b.vld_mask else: eq = False vld = 0 return BOOL.getConstCls()(BOOL, int(eq), vld) ================================================ FILE: hwt/hdl/types/string.py ================================================ from hwt.doc_markers import internal from hwt.hdl.types.hdlType import HdlType from hwt.pyUtils.typingFuture import override class HString(HdlType): """ :note: This type is meant for HwModule parameters, operations with this type are not synthetisable. """ def all_mask(self): return 1 @internal @override @classmethod def getConstCls(cls): try: return cls._constCls except AttributeError: from hwt.hdl.types.stringConst import HStringConst cls._constCls = HStringConst return cls._constCls @internal @override @classmethod def getRtlSignalCls(cls): try: return cls._rtlSignalCls except AttributeError: from hwt.hdl.types.stringConst import HStringRtlSignal cls._rtlSignalCls = HStringRtlSignal return cls._rtlSignalCls ================================================ FILE: hwt/hdl/types/stringConst.py ================================================ from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.defs import BOOL from hwt.hdl.types.typeCast import toHVal from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class HStringRtlSignal(RtlSignal): def _eq(self, other): other = toHVal(other, self._dtype) assert self._dtype == other._dtype, (self, self._dtype, other, other._dtype) return HOperatorNode.withRes(HwtOps.EQ, [self, other], BOOL) class HStringConst(HConst): """ Value class for hdl HString type """ @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param val: python string or None :param typeObj: instance of HString HdlType :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ assert isinstance(val, str) or val is None vld = 0 if val is None else 1 if not vld: assert vld_mask is None or vld_mask == 0 val = "" else: if vld_mask == 0: val = "" vld = 0 return cls(typeObj, val, vld) def to_py(self): if not self._is_full_valid(): raise ValueError(f"Value of {self} is not fully defined") return self.val def _eq(self, other): other = toHVal(other, self._dtype) if isinstance(self, HConst): eq = self.val == other.val vld = int(self.vld_mask and other.vld_mask) return BOOL.getConstCls()(BOOL, int(eq), vld) else: return HStringRtlSignal._eq(other) ================================================ FILE: hwt/hdl/types/struct.py ================================================ from typing import Self from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.structValBase import HStructConstBase, HStructRtlSignalBase from hwt.hwIO import HwIO from hwt.pyUtils.typingFuture import override from hwt.serializer.generic.indent import getIndent from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class HStructFieldMeta(): """ Metadata for a field in :class:`HStruct` type :ivar ~.split: flag which specifies if structured data type of this field should be synchronized as a one interface or each it's part should be synchronized separately """ def __init__(self, split=False): self.split = split def __eq__(self, other): if other is None: return False return self.split == other.split @internal def __hash__(self): return hash(self.split) class HStructField(object): """ Object holding info about a single member in :class:`HStruct` type """ def __init__(self, typ: HdlType, name: str, meta=None): assert isinstance(name, str) or name is None, name assert isinstance(typ, HdlType), typ self.name = name self.dtype = typ self.meta = meta def __eq__(self, other): return self.name == other.name and\ self.dtype == other.dtype and\ self.meta == other.meta def __hash__(self): return hash((self.name, self.dtype, self.meta)) def __repr__(self): name = self.name if name is None: name = "" return f"" _protectedNames = { # RtlSignal props "_dtype", * dir(RtlSignal), # HConst * dir(HConst), # HwIO props "_setAttrListener", "_associatedClk", "_associatedRst", "_parent", "_name", "_masterDir", "_direction", "_ctx", "_isExtern", "_ag", "_hdlPort", "_hdlNameOverride", *dir(HwIO), # HwIOSignal props # "_sig", "_sigInside", "_isAccessible", # *dir(HwIOSignal), } class HStruct(HdlType): """ HDL structure type :ivar ~.fields: tuple of :class:`~.HStructField` instances in this struct :ivar ~.name: name of this HStruct type :ivar ~.field_by_name: dictionary which maps the name of the field to :class:`~.HStructField` instance :ivar ~._constCls: Class of value for this type as usual in HdlType implementations .. code-block::python # type definition t = HStruct( (BIT, "a"), (BIT, "b"), ) # constant instantiation v = t.from_py({"a": 1, "b":0}) :attention: v._reinterpet_cast(HBits(2)) packs the first member ("a") to a bit 0 (first member at the lowest address as in C) Note that this is exactly opposite as in SystemVerilog struct packed {bit a; bit b;} where bit "b" would be bit 0 :ivar _HStructConstBase: base class for HStructConst to allow for instance method re-definion for constants of child types :ivar _HStructRtlSignalBase: base class for HStructRtlSignal to allow for instance method re-definion for constants of child types .. code-block::python # example of use of _HStructConstBase/_HStructRtlSignalBase t = HStruct( (BIT, "a"), (BIT, "b"), ) class MyHStructConstBase(HStructConstBase) def any(self): return self.a | self.b t._HStructConstBase = MyHStructConstBase v = t.from_py({"a": 1, "b":0}) t.any() """ _HStructConstBase = HStructConstBase _HStructRtlSignalBase = HStructRtlSignalBase def __init__(self, *template, name=None, const=False): """ :param template: list of tuples (type, name) or :class:`~.HStructField` objects name can be None (= padding) :param name: optional name used for debugging purposes """ super(HStruct, self).__init__(const=const) fields = [] field_by_name = {} self.name = name bit_length = 0 for f in template: try: field = HStructField(*f) except TypeError: field = f if not isinstance(field, HStructField): raise TypeError(f"Template for struct field {f} is" " not in valid format") fields.append(field) if field.name is not None: assert field.name not in field_by_name, field.name field_by_name[field.name] = field t = field.dtype if bit_length is not None: try: _bit_length = t.bit_length() bit_length += _bit_length except TypeError: bit_length = None self.fields = tuple(fields) self.field_by_name = field_by_name self.__hash = hash((self.name, self.const, self.fields)) self.__bit_length_val = bit_length usedNames = set(field_by_name.keys()) assert not _protectedNames.intersection(usedNames), \ _protectedNames.intersection(usedNames) class HStructConst(self._HStructConstBase): __slots__ = list(usedNames) class HStructRtlSignal(self._HStructRtlSignalBase): __slots__ = list(usedNames) if name is not None: HStructConst.__name__ = name + "Const" HStructRtlSignal.__name__ = name + "RtlSignal" self._constCls = HStructConst self._rtlSignalCls = HStructRtlSignal def bit_length(self) -> int: bl = self.__bit_length_val if bl is None: raise TypeError("Can not request bit_lenght on type" " which has not fixed size") else: return self.__bit_length_val @internal @override def getConstCls(self): return self._constCls @internal @override def getRtlSignalCls(self): """ :attention: RtlSignal of this class is actually never instantiated and :class:`HwIOStruct` is used instead. However the methods of RtlSignal class are called from HwIOStruct. This is to have RtlSignal with single HDL id only and to keep RTL level data structures as simple as possible. """ return self._rtlSignalCls @internal @classmethod def get_reinterpret_cast_HConst_fn(cls): from hwt.hdl.types.structCast import hstruct_reinterpret return hstruct_reinterpret @internal @classmethod def get_reinterpret_cast_RtlSignal_fn(cls): from hwt.hdl.types.structCast import hstruct_reinterpret return hstruct_reinterpret @internal def __fields__eq__(self, other: Self) -> bool: if len(self.fields) != len(other.fields): return False for sf, of in zip(self.fields, other.fields): if (sf.name != of.name or sf.dtype != of.dtype or sf.meta != of.meta): return False return True def __eq__(self, other: HdlType) -> bool: if self is other: return True if (type(self) is type(other)): if self.name != other.name or self.const != other.const: return False try: self_l = self.bit_length() except TypeError: self_l = -1 try: other_l = other.bit_length() except TypeError: other_l = -1 return self_l == other_l and self.__fields__eq__(other) return False @internal def __hash__(self): return self.__hash def __add__(self, other): """ override of addition, merge struct into one """ assert isinstance(other, HStruct) return HStruct(*self.fields, *other.fields) @override def isScalar(self): return False def __repr__(self, indent=0, withAddr=None, expandStructs=False): """ :param indent: number of indentation :param withAddr: if is not None is used as a additional information about on which address this type is stored (used only by HStruct) :param expandStructs: expand HStructTypes (used by HStruct and HArray) """ if self.name: name = self.name + " " else: name = "" myIndent = getIndent(indent) childIndent = getIndent(indent + 1) header = f"{myIndent:s}struct {name:s}{{" buff = [header, ] for f in self.fields: if withAddr is not None: withAddr_B = withAddr // 8 addrTag = f" // start:0x{withAddr:x}(bit) 0x{withAddr_B:x}(byte)" else: addrTag = "" if f.name is None: buff.append(f"{childIndent:s}//{f.dtype} empty space{addrTag:s}") else: buff.append("%s %s%s" % ( f.dtype.__repr__(indent=indent + 1, withAddr=withAddr, expandStructs=expandStructs), f.name, addrTag)) if withAddr is not None: withAddr += f.dtype.bit_length() buff.append(f"{myIndent:s}}}") return "\n".join(buff) def offsetof(structTy: HStruct, field: HStructField): """ Get bit offset field in HStruct """ off = 0 for f in structTy.fields: f: HStructField if f is field: return off else: off += f.dtype.bit_length() raise AssertionError("field was not found in struct type fields", structTy, field) ================================================ FILE: hwt/hdl/types/structCast.py ================================================ from typing import Union from hwt.code import Concat from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.const import HConst from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import HdlType, default_reinterpret_cast_fn from hwt.hdl.types.struct import HStruct from hwt.hwIOs.std import HwIOSignal from hwt.mainBases import HwIOBase from hwt.mainBases import RtlSignalBase @internal def hstruct_reinterpret_to_bits(self: HStruct, sigOrConst: Union[RtlSignalBase, HConst], toType: HdlType): assert toType.bit_length() == self.bit_length() parts = [] for f in self.fields: if f.name is None: width = f.dtype.bit_length() part = HBits(width).from_py(None) else: part = getattr(sigOrConst, f.name) if isinstance(part, HwIOSignal): part = part._sig if isinstance(part, HObjList): elmTyFlat = HBits(f.dtype.element_t.bit_length()) for partPart in part: pp = partPart._reinterpret_cast(elmTyFlat) parts.append(pp) continue elif not isinstance(part, (HConst, RtlSignalBase, HwIOBase)): part = f.dtype.from_py(part) elif not isinstance(part._dtype, toType.__class__): part = part._reinterpret_cast(toType.__class__(part._dtype.bit_length())) # else add part as is assert isinstance(part._dtype, HBits), part parts.append(part) return Concat(*reversed(parts)) @internal def hstruct_reinterpret_using_bits(self: HStruct, sigOrConst: Union[RtlSignalBase, HConst], toType: HdlType): as_bits = sigOrConst._reinterpret_cast(HBits(self.bit_length())) return as_bits._reinterpret_cast(toType) @internal def hstruct_reinterpret(self: HStruct, sigOrConst: Union[RtlSignalBase, HConst], toType: HdlType): if isinstance(toType, HBits): return hstruct_reinterpret_to_bits(self, sigOrConst, toType) elif isinstance(toType, (HStruct, HArray)): return hstruct_reinterpret_using_bits(self, sigOrConst, toType) else: return default_reinterpret_cast_fn(self, sigOrConst, toType) ================================================ FILE: hwt/hdl/types/structUtils.py ================================================ from copy import copy from typing import Union, Dict from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.enum import HEnum from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.stream import HStream from hwt.hdl.types.struct import HStructField, HStruct from hwt.synthesizer.typePath import TypePath filed_filter_t = Dict[Union[int, str], "filed_filter_t"] def HdlType_select(t: HStruct, fieldsToUse: filed_filter_t): """ Select fields from type structure (rest will become padding) :param t: HdlType type instance :param fieldsToUse: dict {name:{...}} or set of names to select, dictionary is used to select nested fields in HStruct/HUnion fields/array items (f.e. {"struct1": {"field1", "field2"}, "field3":{}} will select field1 and 2 from struct1 and field3 from root) """ template = [] fieldsToUse = fieldsToUse foundNames = set() if isinstance(t, (HArray, HStream)): assert len(fieldsToUse) <= 1, ("select only on item 0, because it has to be same for all array items", fieldsToUse) k, v = list(fieldsToUse.items())[0] assert k == 0 new_t = copy(t) new_t.elment = HdlType_select(t.element_t, v) return new_t elif isinstance(t, (HBits, HEnum)): # scalar return t else: # struct/Union for f in t.fields: name = None subfields = [] if f.name is not None: try: if isinstance(fieldsToUse, dict): subfields = fieldsToUse[f.name] name = f.name else: if f.name in fieldsToUse: name = f.name except KeyError: name = None if name is not None and subfields: new_t = HdlType_select(f.dtype, subfields) template.append(HStructField(new_t, name)) else: template.append(HStructField(f.dtype, name)) if f.name is not None: foundNames.add(f.name) if isinstance(fieldsToUse, dict): fieldsToUse = set(fieldsToUse.keys()) assert fieldsToUse.issubset(foundNames) return t.__class__(*template) def field_path_get_type(root: HdlType, field_path: TypePath): """ Get a data type of element using field path """ t = root for p in field_path: if isinstance(p, int): t = t.element_t else: assert isinstance(p, str), p t = t.field_by_name[p].dtype return t def HStruct_tuple_to_dict(v: tuple, t: HdlType, call_to_py_on_scalars=True) -> dict: """ Convert a tuple of items for HStruct field to a dictionary field name to field value, recursively. """ if isinstance(t, HStruct): assert len(v) == len(t.fields), (len(v), t) return {f.name: HStruct_tuple_to_dict(vItem, f.dtype, call_to_py_on_scalars=call_to_py_on_scalars) for f, vItem in zip(t.fields, v)} elif isinstance(t, HArray): assert len(v) == t.size, (len(v), t) return [HStruct_tuple_to_dict(vItem, t.element_t, call_to_py_on_scalars=call_to_py_on_scalars) for vItem in v] else: assert t.isScalar(), t if call_to_py_on_scalars: return v.to_py() else: return v ================================================ FILE: hwt/hdl/types/structValBase.py ================================================ from typing import Optional, Union from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.types.defs import STR from hwt.hwIO import HwIO from hwt.mainBases import RtlSignalBase from hwt.serializer.generic.indent import getIndent from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class HStructRtlSignalBase(RtlSignal): __slots__ = [] def __len__(self): return len(self.__slots__) def __iter__(self): for f in self._dtype.fields: if f.name is None: yield f._dtype.from_py(None) else: yield getattr(self, f.name) def __getattr__(self, name:str) -> RtlSignal: structField = self._dtype.field_by_name.get(name, None) if structField is None: raise AttributeError(self.__class__, ' object has no attribute ', name) structField: "HStructField" return HOperatorNode.withRes(HwtOps.DOT, (self, STR.from_py(name)), structField.dtype) def __call__(self, source, dst_resolve_fn=lambda x:x._getDestinationSignalForAssignmentToThis(), exclude=None, fit=False) -> list[HdlAssignmentContainer]: res = [] if isinstance(source, dict): source = [source[field.name] for field in self._dtype.fields] else: assert len(self) == len(source), ("source and destination array must be of the same size", len(self) == len(source)) for src, dst in zip(source, self): a = dst.__call__(src, dst_resolve_fn=dst_resolve_fn, exclude=exclude, fit=fit) if isinstance(a, (list, tuple)): res.extend(a) else: res.append(a) return res def __repr__(self, indent=0): return HStructConstBase.__repr__(self, indent=indent) class HStructConstBase(HConst): """ Base class for values for structure types. Every structure type has it's own value class derived from this. """ __slots__ = [] def __init__(self, typeObj: "HStruct", val: Optional[Union[dict, tuple]], skipCheck=False): """ :param val: None or dict {field name: field value} :param typeObj: instance of HString HdlType :param skipCheck: flag to skip field name consistency in val """ self._dtype = typeObj if not skipCheck and val is not None: if isinstance(val, dict): assert set(self.__slots__).issuperset(set(val.keys())), \ ("struct value specifies undefined members", set(val.keys()).difference(set(self.__slots__))) else: assert len(val) == len(self.__slots__), ("struct value has different number of values than initialization value", len(val), len(self.__slots__)) if isinstance(val, dict): for f in self._dtype.fields: if f.name is None: continue if val is None: v = None else: v = val.get(f.name, None) if not isinstance(v, (HConst, HwIO, RtlSignalBase)): v = f.dtype.from_py(v) setattr(self, f.name, v) else: if val is None: val = (None for _ in range(len(self.__slots__))) valIt = iter(val) for f in self._dtype.fields: if f.name is None: continue if val is None: v = None else: v = next(valIt) if not isinstance(v, (HConst, RtlSignalBase)): v = f.dtype.from_py(v) setattr(self, f.name, v) def __len__(self): return len(self.__slots__) def __iter__(self): for f in self._dtype.fields: if f.name is None: yield f._dtype.from_py(None) else: yield getattr(self, f.name) def __copy__(self): d = {} for f in self._dtype.fields: if f.name is None: continue v = getattr(self, f.name) if not isinstance(v, RtlSignalBase): v = v.__copy__() d[f.name] = v return self.__class__(self._dtype, d, skipCheck=True) @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param val: None or dict {field name: field value} :param typeObj: instance of HString HdlType :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ if vld_mask == 0: val = None return cls(typeObj, val) def _is_full_valid(self): for f in self._dtype.fields: if f.name is not None: val = getattr(self, f.name, None) if val is None or not val._is_full_valid(): return False return True def _is_partially_valid(self) -> bool: for f in self._dtype.fields: if f.name is not None: val = getattr(self, f.name, None) if val is None and val._is_partially_valid(): return True return False def to_py(self): d = {} for f in self._dtype.fields: if f.name is not None: val = getattr(self, f.name).to_py() d[f.name] = val return d def __ne__(self, other): if isinstance(other, HConst): if self._dtype == other._dtype: for f in self._dtype.fields: isPadding = f.name is None if not isPadding: sf = getattr(self, f.name) of = getattr(other, f.name) if (sf != of): return True return False else: return True else: return super(HConst, self).__ne__(other) def _eq(self, other): return self.__eq__(other) def __eq__(self, other): if isinstance(other, HConst): if self._dtype == other._dtype: for f in self._dtype.fields: isPadding = f.name is None if not isPadding: sf = getattr(self, f.name) of = getattr(other, f.name) if not (sf == of): return False return True else: return False else: return super(HConst, self).__eq__(other) def __repr__(self, indent=0): buff = ["{"] indentOfFields = getIndent(indent + 1) for f in self._dtype.fields: if f.name is not None: val = getattr(self, f.name) try: v = val.__repr__(indent=indent + 1) except TypeError: v = repr(val) buff.append(f"{indentOfFields:s}{f.name:s}: {v:s}") buff.append(getIndent(indent) + "}") return ("\n").join(buff) ================================================ FILE: hwt/hdl/types/typeCast.py ================================================ from typing import Optional, Any, Union from hwt.hdl.const import HConst from hwt.hdl.types.defs import INT, STR, BOOL, SLICE, FLOAT64 from hwt.hdl.types.hdlType import HdlType from hwt.hdl.variables import HdlSignalItem from hwt.mainBases import HwIOBase, RtlSignalBase defaultPyConversions = { int: INT, str: STR, bool: BOOL, slice: SLICE, float: FLOAT64 } def toHVal(op: Any, suggestedType: Optional[HdlType]=None) -> Union[HConst, RtlSignalBase, HwIOBase]: """Convert python or hdl HConst/RtlSignal object to hdl HConst/RtlSignal object""" if isinstance(op, (HConst, HdlSignalItem)): return op elif isinstance(op, HwIOBase): sig = getattr(op, "_sig", None) if sig is not None: return sig else: return op else: if suggestedType is not None: return suggestedType.from_py(op) if isinstance(op, int): if op >= 1 << 31: raise TypeError( f"Number {op:d} is too big to fit in 32 bit integer of HDL" " use Bits type instead") elif op < -(1 << 31): raise TypeError( f"Number {op:d} is too small to fit in 32 bit integer" " of HDL use Bits type instead") try: hType = defaultPyConversions[type(op)] except KeyError: hType = None if hType is None: raise TypeError(f"Unknown hardware type for instance of {op.__class__}") return hType.from_py(op) ================================================ FILE: hwt/hdl/types/union.py ================================================ from collections import OrderedDict from typing import Optional, Tuple, Any from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.struct import HStructField from hwt.serializer.generic.indent import getIndent from hwt.pyUtils.typingFuture import override from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal _protectedNames = {"clone", "staticEval", "from_py", "_dtype", "_usedField", "_val"} class HUnionConstBase(HConst): """ Base class for values for union types. Every union type has it's own value class derived from this. :ivar ~._dtype: union type of this value :ivar ~.__usedField: member which is actually used to represent value :ivar ~.__val: value for __usedField """ __slots__ = ["_dtype", "_val", "_usedField"] def __init__(self, typeObj: "HUnion", val: Optional[Tuple[str, Any]]): """ :param val: None or tuple (member name, member value) :param typeObj: instance of HUnion HdlType for this value """ self._dtype = typeObj if val is not None: memberName, v = val else: memberName = next(iter((typeObj.fields.keys()))) v = None f = self._dtype.fields[memberName] if not isinstance(v, HConst): v = f.dtype.from_py(v) else: v._auto_cast(f.dtype) self._val = v self._usedField = f @override @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param val: None or tuple (member name, member value) :param typeObj: instance of HUnion HdlType for this value :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ if vld_mask == 0: val = None return cls(typeObj, val) def __repr__(self, indent=0): # [TODO] refactor too similar to StructValBase.__repr__ buff = ["{"] indentOfFields = getIndent(indent + 1) for f in self._dtype.fields.values(): if f.name is not None: val = getattr(self, f.name) try: v = val.__repr__(indent=indent + 1) except TypeError: v = repr(val) buff.append(f"{indentOfFields:s}{f.name:s}: {v:s}") buff.append(getIndent(indent) + "}") return ("\n").join(buff) class HUnionRtlSignalBase(RtlSignal): __slots__ = ["_dtype", "_val", "_usedField"] def __repr__(self, indent=0): return HUnionConstBase.__repr__(indent=indent) @internal class HUnionMemberHandler(object): """ Object which manages the acces to HUnion field """ def __init__(self, field): self.field = field def set(self, parent, v): f = parent._dtype.fields[self.field.name] if not isinstance(v, HConst): v = f.dtype.from_py(v) else: v._auto_cast(f.dtype) parent._val = v parent._usedField = f def get(self, parent): name = self.field.name v = parent._val if parent._usedField.name == name: return v else: f = parent._dtype.fields[name] v = v._reinterpret_cast(f.dtype) parent._val = v parent._usedField = f return v class HUnion(HdlType): """ HDL union type (same data multiple representations) :ivar ~.fields: read only OrderedDict {key:StructField} for each member in this union :ivar ~.name: name of this type :ivar ~.__bit_length_val: precalculated bit_length of this type """ def __init__(self, *template, name=None, const=False): """ :param template: list of tuples (type, name) or HStructField objects name can be None (= padding) :param name: optional name used for debugging purposes """ super(HUnion, self).__init__(const=const) self.fields = OrderedDict() self.field_by_name = self.fields self.name = name bit_length = None class HUnionConst(HUnionConstBase): pass class HUnionRtlSignal(HUnionRtlSignalBase): pass for f in template: try: field = HStructField(*f) except TypeError: field = f if not isinstance(field, HStructField): raise TypeError( "Template for struct field %s is not" " in valid format" % repr(f)) assert field.name is not None self.fields[field.name] = field t = field.dtype if bit_length is None: bit_length = t.bit_length() else: _bit_length = t.bit_length() if _bit_length != bit_length: raise TypeError( field.name, " has different size than others") memberHandler = HUnionMemberHandler(field) p = property(fget=memberHandler.get, fset=memberHandler.set) setattr(HUnionConst, field.name, p) setattr(HUnionRtlSignal, field.name, p) self.__bit_length_val = bit_length self.__hash = hash((self.name, tuple(self.fields.items()))) usedNames = set(self.fields.keys()) assert not _protectedNames.intersection( usedNames), _protectedNames.intersection(usedNames) if name is not None: HUnionConst.__name__ = name + "Const" HUnionRtlSignal.__name__ = name + "RtlSignal" self._constCls = HUnionConst self._rtlSignalCls = HUnionRtlSignal def bit_length(self): bl = self.__bit_length_val if bl is None: raise TypeError("Can not request bit_lenght on type" " which has not fixed size") else: return self.__bit_length_val @internal @override def getConstCls(self): return self._constCls @internal def __fields__eq__(self, other): if len(self.fields) != len(other.fields): return False for k, sf in self.fields.items(): try: of = other.fields[k] except KeyError: return False if (sf.dtype != of.dtype or sf.meta != of.meta): return False return True def __eq__(self, other): return self is other or ( type(self) is type(other) and self.bit_length() == other.bit_length() and self.__fields__eq__(other)) @internal def __hash__(self): return self.__hash @override def isScalar(self): return False def __repr__(self, indent=0, withAddr=None, expandStructs=False): """ :param indent: number of indentation :param withAddr: if is not None is used as a additional information about on which address this type is stored (used only by HStruct) :param expandStructs: expand HStructTypes (used by HStruct and HArray) """ if self.name: name = self.name + " " else: name = "" myIndent = getIndent(indent) childIndent = getIndent(indent + 1) header = f"{myIndent:s}union {name:s}{{" buff = [header, ] for f in self.fields.values(): if f.name is None: buff.append(f"{childIndent:s}//{f.dtype} empty space") else: buff.append("%s %s" % ( f.dtype.__repr__(indent=indent + 1, withAddr=withAddr, expandStructs=expandStructs), f.name)) buff.append(f"{myIndent:s}}}") return "\n".join(buff) ================================================ FILE: hwt/hdl/types/utils.py ================================================ from typing import Union, List from hwt.hdl.const import HConst from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.stream import HStream from hwt.hdl.types.struct import HStruct from hwt.hdl.types.typeCast import toHVal from hwt.hdl.types.union import HUnion from hwt.mainBases import RtlSignalBase def walkFlattenFields(sigOrConst: Union[RtlSignalBase, HConst], skipPadding=True): """ Walk all simple values in HStruct or HArray """ t = sigOrConst._dtype if isinstance(t, HBits): yield sigOrConst elif isinstance(t, HUnion): yield from walkFlattenFields(sigOrConst._val, skipPadding=skipPadding) elif isinstance(t, HStruct): for f in t.fields: isPadding = f.name is None if not isPadding or not skipPadding: if isPadding: v = f.dtype.from_py(None) else: v = getattr(sigOrConst, f.name) yield from walkFlattenFields(v) elif isinstance(t, HArray): for item in sigOrConst: yield from walkFlattenFields(item) elif isinstance(t, HStream): assert isinstance(sigOrConst, HConst), sigOrConst for v in sigOrConst: yield from walkFlattenFields(v) else: raise NotImplementedError(t) def HConst_from_words(t: HdlType, data: List[Union[HConst, RtlSignalBase, int]], getDataFn=None, dataWidth=None) -> HConst: """ Parse raw HBits array to a value of specified HdlType """ if getDataFn is None: assert dataWidth is not None def _getDataFn(x): return toHVal(x)._auto_cast(HBits(dataWidth)) getDataFn = _getDataFn val = t.from_py(None) fData = iter(data) # actual is storage variable for items from frameData actualOffset = 0 actual = None for v in walkFlattenFields(val, skipPadding=False): # walk flatten fields and take values from fData and parse them to # field required = v._dtype.bit_length() if actual is None: actualOffset = 0 try: actual = getDataFn(next(fData)) except StopIteration: raise ValueError("Insufficcient amount of data to build value for specified type", t, v, required) if dataWidth is None: dataWidth = actual._dtype.bit_length() actuallyHave = dataWidth else: actuallyHave = actual._dtype.bit_length() - actualOffset while actuallyHave < required: # collect data for this field try: d = getDataFn(next(fData)) except StopIteration: raise ValueError("Insufficcient amount of data to build value for specified type", t, v, required, actuallyHave) actual = d._concat(actual) actuallyHave += dataWidth if actuallyHave >= required: # parse value of actual to field # skip padding _v = actual[(required + actualOffset):actualOffset] _v = _v._auto_cast(v._dtype) v.val = _v.val v.vld_mask = _v.vld_mask # update slice out what was taken actuallyHave -= required actualOffset += required if actuallyHave == 0: actual = None if actual is not None: assert actual._dtype.bit_length( ) - actualOffset < dataWidth, ( "It should be just a padding at the end of frame, but there is some additional data" ) return val def is_only_padding(t: HdlType) -> bool: if isinstance(t, HStruct): for f in t.fields: if f.name is not None and not is_only_padding(f.dtype): return False return True elif isinstance(t, (HArray, HStream)): return is_only_padding(t.element_t) return False ================================================ FILE: hwt/hdl/variables.py ================================================ from hwt.doc_markers import internal from hwt.hdl.hdlObject import HdlObject from hwt.mainBases import RtlSignalBase class HdlSignalItem(HdlObject): """ Basic hdl signal used to design circuits """ __slots__ = [ "_name", "_dtype", "virtual_only", "def_val", ] def __init__(self, name: str, dtype: "HdlType", def_val=None, virtual_only=False): """ :param _name: name for better orientation in netlists (used only in serialization) :param dtype: data type of this signal :param def_val: value for initialization :param virtual_only: flag indicates that this assignments is only virtual and should not be added into netlist, because it is only for internal notation """ assert isinstance(name, str), name self._name = name self._dtype = dtype self.virtual_only = virtual_only if def_val is None: def_val = dtype.from_py(None) self.def_val = def_val self._set_def_val() @internal def _set_def_val(self): v = self.def_val if isinstance(v, RtlSignalBase): v = v.staticEval() self._val = v.__copy__() ================================================ FILE: hwt/hwIO.py ================================================ from copy import copy from typing import Optional, Union, Generator, Callable, Self, Collection from hdlConvertorAst.translate.common.name_scope import NameScope from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.const import HConst from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.types.array import HArray from hwt.hdl.types.bitsCastUtils import fitTo from hwt.hdl.types.typeCast import toHVal from hwt.mainBases import HwIOBase from hwt.synthesizer.exceptions import IntfLvlConfErr, InterfaceStructureErr from hwt.synthesizer.interfaceLevel.directionFns import \ HwIODirectionFns from hwt.synthesizer.rtlLevel.netlist import RtlNetlist from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from hwt.synthesizer.typePath import TypePath from hwtSimApi.agents.base import AgentBase from ipCorePackager.constants import DIRECTION, INTF_DIRECTION from hwt.synthesizer.interfaceLevel.implDependent import\ HwIOImplDependentFns from hwt.synthesizer.interfaceLevel.propDeclrCollector import\ PropDeclrCollector def _default_param_updater(self, myP, parentPval): myP.set_value(parentPval) class HwIO(HwIOBase, HwIOImplDependentFns, PropDeclrCollector, HwIODirectionFns): """ Base class for all interfaces in interface synthesizer :cvar _NAME_SEPARATOR: separator for nested interface names :ivar ~._hwParams: [] of parameter :ivar ~._hwIOs: [] sub interfaces :ivar ~._name: name assigned during synthesis :ivar ~._onParentPropertyPath: path composed of property name and indexes in on parent to identify object location in parent properties :ivar ~._parent: parent object (HwModule or HwIO instance) :ivar ~._isExtern: If true synthesizer sets it as external port of unit :ivar ~._associatedClk: clock Signal (interface) associated with this interface if is none simulation agent try to search it on parent :ivar ~._associatedRst: rst(_n) Signal (interface) associated with this interface if is none simulation agent try to search it on parent :note: only interfaces without _hwIOs have :ivar ~._boundedSigLvlHwModule: RTL unit for which was this interface created Agenda of directions and HDL :ivar ~._masterDir: specifies which direction has this interface at master :ivar ~._direction: means actual direction of this interface resolved by its drivers :ivar ~._rtlCtx: RTL netlist context of all signals and params on this interface after interface is registered on parent _ctx is merged :ivar ~._hdlPort: a HdlPortItem instance available once the unit is synthesized Agenda of simulations :ivar ~._ag: agent object connected to this interface (initialized only before simulation) """ _NAME_SEPARATOR = "_" def __init__(self, masterDir=DIRECTION.OUT, hdlName:Optional[Union[str, dict[str, str]]]=None, loadConfig=True): """ This constructor is called when constructing new interface, it is usually done manually while creating :class:`hwt.hwModule.HwModule` or automatically while extracting interfaces from HwModuleWithSoure :param masterDir: direction which this interface should have for master :param loadConfig: do load config in __init__ """ self._setAttrListener: Optional[Callable[[str, object], None]] = None self._associatedClk: Optional[HwIO] = None self._associatedRst: Optional[HwIO] = None self._parent: Optional["HwModule"] = None self._onParentPropertyPath: Optional[TypePath] = None self._name: Optional[str] = None # super().__init__() self._masterDir: DIRECTION = masterDir # HwIO is instantiated inside of :class:`hwt.hwModule.HwModule` first, # master direction actually means slave from outside view self._direction: INTF_DIRECTION = INTF_DIRECTION.UNKNOWN self._rtlCtx: Optional[RtlNetlist] = None if loadConfig: self._loadConfig() # flags for better design error detection self._isExtern = False self._ag: Optional[AgentBase] = None self._hdlPort: Optional[HdlPortItem] = None self._hdlNameOverride = hdlName def _m(self) -> Self: """ Note that this interface will be master :return: self """ assert not hasattr(self, "_hwIOs") or not self._hwIOs, \ "Too late to change direction of interface" self._direction = DIRECTION.asIntfDirection(DIRECTION.opposite(self._masterDir)) return self def __call__(self, other: Union[Self, HConst, object], exclude:Optional[Collection[Union[Self, HConst]]]=None, fit:bool=False) -> list[HdlAssignmentContainer]: """ :attention: it is not call of function it is operator of assignment """ assert self._direction != INTF_DIRECTION.MASTER return self._connectTo(other, exclude, fit) try: return self._connectTo(other, exclude, fit) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified @internal def _loadHwDeclarations(self): """ load declarations from _declr method This function is called first for parent and then for children """ if not hasattr(self, "_hwIOs"): self._hwIOs = [] self._setAttrListener = self._declrCollector self.hwDeclr() self._setAttrListener = None for sHwIO in self._hwIOs: sHwIO._loadHwDeclarations() sHwIO._setAsExtern(self._isExtern) if self._isExtern: # direction from inside of unit (reverset compared to outside direction) if self._direction == INTF_DIRECTION.UNKNOWN: self._direction = INTF_DIRECTION.MASTER self._setDirectionsLikeIn(self._direction) @internal def _cleanRtlSignals(self, lockNonExternal=True): """ Remove all signals from this interface (used after unit is synthesized and its parent is connecting its interface to this unit) """ if self._hwIOs: for sHwIO in self._hwIOs: sHwIO._cleanRtlSignals(lockNonExternal=lockNonExternal) def _connectTo(self, master: Union[Self, HConst, object], exclude:Optional[Collection[Union[Self, HConst]]]=None, fit:bool=False) -> list[HdlAssignmentContainer]: """ connect to another interface interface (on RTL level) works like self <= master in VHDL """ return list(self._connectToIter(master, exclude, fit)) @internal def _connectToIter(self, master: Union[Self, HConst, object], exclude:Optional[Collection[Union[Self, HConst]]], fit: bool) -> Generator[HdlAssignmentContainer, None, None]: if exclude and (self in exclude or master in exclude): return if self._hwIOs or isinstance(self, HObjList): masterIsHwIO = isinstance(master, HwIO) if masterIsHwIO: seenMasterHwIOs = set() else: seenMasterPropCnt = 0 for hio in self._hwIOs: if exclude and hio in exclude: mHwIO = getattr(master, hio._name, None) if mHwIO is not None: seenMasterHwIOs.add(mHwIO) continue if master is None: mHwIO = hio._dtype.from_py(None) else: try: mHwIO = getattr(master, hio._name) except AttributeError: if not hio._onParentPropertyPath: raise IntfLvlConfErr("Invalid interface structure", hio, "<=", master, "src missing", hio._name) try: mHwIO = hio._onParentPropertyPath.getOnObject(master) except (AttributeError, KeyError, IndexError): raise IntfLvlConfErr("Invalid interface structure", hio, "<=", master, "src missing", hio._name, "and _onParentPropertyPath is invalid", hio._onParentPropertyPath) if masterIsHwIO: seenMasterHwIOs.add(mHwIO) else: seenMasterPropCnt += 1 if exclude and mHwIO in exclude: continue if isinstance(mHwIO, (HConst, RtlSignal)): # HStruct values if (hio._masterDir in (DIRECTION.OUT, DIRECTION.INOUT) and hio._direction == INTF_DIRECTION.MASTER) or\ (hio._masterDir == DIRECTION.IN and hio._direction == INTF_DIRECTION.SLAVE): raise IntfLvlConfErr( "Invalid connection", hio, "<=", mHwIO) yield from hio._connectToIter(mHwIO, exclude, fit) elif mHwIO._masterDir == DIRECTION.OUT: if hio._masterDir != mHwIO._masterDir: raise IntfLvlConfErr( "Invalid connection", hio, "<=", mHwIO) yield from hio._connectToIter(mHwIO, exclude, fit) else: if hio._masterDir != mHwIO._masterDir: raise IntfLvlConfErr( "Invalid connection", mHwIO, "<=", hio) yield from mHwIO._connectToIter(hio, exclude, fit) if master is None: pass # no check for prop cnt else: if masterIsHwIO: masterHwIOCnt = len(master._hwIOs) else: if isinstance(master._dtype, HArray): masterHwIOCnt = len(master) else: # assert isinstance(master._dtype, HStruct) masterHwIOCnt = len(master._dtype.fields) if (masterIsHwIO and len(seenMasterHwIOs) != masterHwIOCnt) or (not masterIsHwIO and seenMasterPropCnt != masterHwIOCnt): if exclude: # there is a possibility that the master interface was excluded, # but we did not see it as the interface of the same name was not present on self for hio in self._hwIOs: if hio in exclude or hio not in seenMasterHwIOs: continue else: # hio is an interface which is extra on master and is missing an equivalent on slave raise InterfaceStructureErr(self, master, exclude) else: raise InterfaceStructureErr(self, master, exclude) else: if not isinstance(master, (HConst, RtlSignal)) and master._hwIOs: raise InterfaceStructureErr(self, master, exclude) dstSig = toHVal(self) srcSig = toHVal(master) if fit: srcSig = fitTo(srcSig, dstSig) yield dstSig(srcSig) @internal def _signalsForHwIO(self, ctx: RtlNetlist, res: Optional[dict[RtlSignal, DIRECTION]], name_scope: Optional[NameScope], prefix='', typeTransform=None, reverse_dir=False): """ Generate RtlSignal _sig and HdlPortInstance _hdlPort for each interface which has no subinterface :note: if already has _sig return use it instead :param ctx: instance of RtlNetlist where signals should be created :param res: output dictionary where result should be stored :param prefix: name prefix for created signals :param name_scope: name scope used to check collisions on port names if this a current top (every component is checked when it is seen first time) :param typeTransform: optional function (type) returns modified type for signal """ if self._hwIOs or isinstance(self, HObjList): for hwIO in self._hwIOs: hwIO._signalsForHwIO(ctx, res, name_scope, prefix=prefix, typeTransform=typeTransform, reverse_dir=reverse_dir) else: assert self._sig is None, self t = self._dtype if typeTransform is not None: t = typeTransform(t) hdlName = prefix + self._getHdlName() s = ctx.sig(hdlName, t) s._hwIO = self self._sig = s if self._isExtern: d = INTF_DIRECTION.asDirection(self._direction) m = ctx.parent if reverse_dir: d = DIRECTION.opposite(d) assert self._hdlPort is None, ( "Now creating a hdl interface for top" " it but seems that it was already created") if res is not None: res[s] = d if reverse_dir: pi = HdlPortItem.fromSignal(s, m, d) # port of current top component s._name = name_scope.checked_name(s._name, s) pi.connectInternSig(s) ctx.hwModDec.ports.append(pi) else: pi = self._hdlPort # port of some subcomponent which names were already checked pi.connectOuterSig(s) self._hdlPort = pi def _getHdlName(self) -> str: """Get name in HDL """ return HObjList._getHdlName(self) def _getFullName(self) -> str: """get all name hierarchy separated by '.' """ return HObjList._getFullName(self) def _updateHwParamsFrom(self, otherObj, updater=_default_param_updater, exclude:Optional[tuple[set[str], set[str]]]=None, prefix=""): """ :note: doc in :func:`~hwt.synthesizer.interfaceLevel.propDeclCollector._updateHwParamsFrom` """ PropDeclrCollector._updateHwParamsFrom( self, otherObj, updater, exclude, prefix) return self def _bit_length(self) -> int: """Sum of all width of hwIOs in this interface""" try: hwIOs = self._hwIOs except AttributeError: hwIOs = None if hwIOs is None: # not loaded interface _hwIO = self.__copy__() _hwIO._loadHwDeclarations() hwIOs = _hwIO._hwIOs if hwIOs: w = 0 for hwIO in hwIOs: w += hwIO._bit_length() return w else: return self._dtype.bit_length() def __repr__(self) -> str: if hasattr(self, "_dtype"): t = f" {self._dtype}" else: t = "" if hasattr(self, '_width'): w = " _width=%s" % str(self._width) else: w = "" name = self._getFullName() return f"<{self.__class__.__name__} {name:s}{w:s}{t:s}>" ================================================ FILE: hwt/hwIOs/__init__.py ================================================ """ This package contains primitive hardware interfaces. """ ================================================ FILE: hwt/hwIOs/agents/__init__.py ================================================ """ This package contains a UVM like simulation agents to handle IO between circuit running in simulator and the code which drives the simulation. """ ================================================ FILE: hwt/hwIOs/agents/bramPort.py ================================================ from collections import deque from typing import Literal, Union from hwt.constants import READ, WRITE, NOP from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.simulator.agentBase import SyncAgentBase from hwtSimApi.agents.clk import ClockAgent from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.triggers import WaitCombRead, WaitWriteOnly, WaitCombStable, Timer from pyMathBitPrecise.bit_utils import apply_set_and_clear, mask, \ byte_mask_to_bit_mask_int, bit_mask_to_byte_mask_int @staticmethod def storeToRamMaskedByIndex(ram:dict[int, Union[tuple[int, int], HBitsConst]], index: int, data: Union[int, HBitsConst], bitmask: Union[int, HBitsConst], isInHBits=False): if not bitmask: # nothing will be set, no point in further update return # clear all bytes unsed by mask data &= bitmask if isInHBits: # explicitely invalidate bytes of data data = data._dtype.from_py(data.val, data.vld_mask & bitmask.val) cur = ram.get(index) if cur is not None: # merge previous and new data if isInHBits: data = apply_set_and_clear(cur, data & bitmask, bitmask) else: data = apply_set_and_clear(cur[0], data & bitmask, bitmask) bitmask |= cur[1] # print(f"storing {index:02x}: ({data if isinstance(data, int) else data.val:04x}, {int(bitmask):04x}) {bit_mask_to_byte_mask_int(int(bitmask), 32):01x}") if isInHBits: ram[index] = data else: ram[index] = (data, bitmask) @staticmethod def storeToRamMaskedByAddress(ram: dict[int, Union[tuple[int, int], HBitsConst]], address: int, wordAlignAddrBitCnt: int, data: Union[int, HBitsConst], bitmask: Union[int, HBitsConst], isInHBits=False): # print(f"storing a:{address:08x}: ({data:064x}, {bitmask:064x}) {bit_mask_to_byte_mask_int(bitmask, 256):08x}") alignShift = address & mask(wordAlignAddrBitCnt) index0 = address >> wordAlignAddrBitCnt if alignShift: # the address is not aligned to a word boundary must split to 2 transactions # if first store actually sotres some data if isInHBits: DATA_WIDTH = data._dtype.bit_length() MASK_WIDTH = bitmask._dtype.bit_length() data = data._zext(DATA_WIDTH * 2) bitmask = bitmask._zext(MASK_WIDTH * 2) data <<= alignShift * 8 bitmask <<= alignShift * 8 if isInHBits: bitmask0 = bitmask[MASK_WIDTH:] bitmask1 = bitmask[:MASK_WIDTH] else: wordBitCnt = (2 ** wordAlignAddrBitCnt) * 8 wordBitMask = mask(wordBitCnt) bitmask0 = bitmask & wordBitMask bitmask1 = bitmask >> wordBitCnt if bitmask0: if isInHBits: data0 = data[DATA_WIDTH:] else: data0 = data & wordBitMask storeToRamMaskedByIndex(ram, index0, data0, bitmask0, isInHBits=isInHBits) # if second store actually stores some data if bitmask1: if isInHBits: data1 = data[:DATA_WIDTH] else: data1 = data >> wordBitCnt storeToRamMaskedByIndex(ram, index0 + 1, data1, bitmask1, isInHBits=isInHBits) else: storeToRamMaskedByIndex(ram, index0, data, bitmask) HwIOBramPort_noClkAgent_requestTy = Union[ tuple[READ, HBitsConst], tuple[WRITE, HBitsConst, HBitsConst], # addr, data tuple[WRITE, HBitsConst, HBitsConst, HBitsConst], # addr, data, mask ] class HwIOBramPort_noClkAgent(SyncAgentBase): """ A simulation agent for BramPort_withoutClk interface In slave mode acts as a memory, in master mode dispatches requests stored in "requests" dequeue :ivar ~.requests: list of tuples (request type, address, [write data]) - used for driver :ivar ~.data: list of data in memory, used for monitor :ivar ~.mem: if agent is in monitor mode (= is slave) all reads and writes are performed on mem object """ def __init__(self, sim: HdlSimulator, hwIO: "HwIOBramPort_noClk"): super().__init__(sim, hwIO, allowNoReset=True) self.HAS_WE = hasattr(hwIO, "we") self.HAS_BE = hwIO.HAS_BE and hwIO.DATA_WIDTH > 8 if self.HAS_BE: assert hwIO.DATA_WIDTH % 8 == 0, ("Expects only complete bytes", hwIO, hwIO.DATA_WIDTH) self.requests: deque[HwIOBramPort_noClkAgent_requestTy] = deque() self.readPending = False self.r_data = deque() self.mem: dict[int, HBitsConst] = {} self.requireInit = True self.clk_ag = None def doReq(self, req: HwIOBramPort_noClkAgent_requestTy): rw = req[0] addr = req[1] hwIO = self.hwIO if rw == READ: assert hwIO.HAS_R, hwIO we = 0 wdata = None self.readPending = True if self._debugOutput is not None: self._debugOutput.write("%s, after %r read_req: %d\n" % ( self.hwIO._getFullName(), self.sim.now, addr)) elif rw == WRITE: assert hwIO.HAS_W, hwIO wdata = req[2] if len(req) == 3: if self.HAS_WE: we = mask(hwIO.we._dtype.bit_length()) else: assert self.HAS_WE we = req[3] if self._debugOutput is not None: self._debugOutput.write(f"{self.hwIO._getFullName():s}, after {self.sim.now:d}" f" write: 0x{int(addr):x}:{wdata} {int(we)}\n") else: raise NotImplementedError(rw) hwIO.addr.write(addr) if self.HAS_WE: hwIO.we.write(we) if hwIO.HAS_W: hwIO.din.write(wdata) def onReadReq(self, addr: HBitsConst): """ on readReqRecieved in monitor mode """ self.requests.append((READ, addr)) def onWriteReq(self, addr: HBitsConst, data: HBitsConst, mask: Union[HBitsConst, Literal[0, 1, None]]): """ on writeReqRecieved in monitor mode """ self.requests.append((WRITE, addr, data, mask)) def monitor(self): """ Handle read/write request on this interfaces This method is executed on clock edge. This means that the read data should be put on dout after clock edge. """ hwIO = self.hwIO yield WaitCombStable() if self.notReset(): en = hwIO.en.read() en = int(en) if en: if self.HAS_WE: we = hwIO.we.read() we = int(we) elif hwIO.HAS_W: we = 1 else: we = 0 addr = hwIO.addr.read() if we: data = hwIO.din.read() self.onWriteReq(addr, data, we) elif hwIO.HAS_R: self.onReadReq(addr) if self.requests: req = self.requests.popleft() t = req[0] addr = req[1] if t == READ: v = self.mem.get(addr.val, None) yield Timer(1) yield WaitWriteOnly() hwIO.dout.write(v) if self._debugOutput is not None: self._debugOutput.write(f"{self.hwIO._getFullName(),}, after {self.sim.now}, read 0x{int(addr):x} {v}\n") else: assert t == WRITE # yield WaitWriteOnly() # hwIO.dout.write(None) yield Timer(1) # after clock edge yield WaitWriteOnly() wData = req[2] wMask = req[3] if addr._is_full_valid(): if self.HAS_BE: maskWidth = hwIO.we._dtype.bit_length() wMaskExt = byte_mask_to_bit_mask_int(wMask, maskWidth) if self._debugOutput is not None: self._debugOutput.write(f"{self.hwIO._getFullName(),}, after {self.sim.now}, write 0x{int(addr):x} {wData}, 0x{wMask:x}\n") storeToRamMaskedByIndex(self.mem, int(addr), wData, HBits(maskWidth * 8).from_py(wMaskExt), isInHBits=True) else: if self._debugOutput is not None: self._debugOutput.write(f"{self.hwIO._getFullName(),}, after {self.sim.now}, write 0x{int(addr):x} {wData}\n") self.mem[addr.val] = wData else: if self._debugOutput is not None: self._debugOutput.write(f"{self.hwIO._getFullName(),}, after {self.sim.now}, write with invalid addr\n") self.mem.clear() def driver(self): hwIO = self.hwIO if self.requireInit: yield WaitWriteOnly() hwIO.en.write(0) if self.HAS_WE: hwIO.we.write(0) self.requireInit = False readPending = self.readPending yield WaitCombRead() if self.requests and self.notReset(): yield WaitWriteOnly() req = self.requests.popleft() if req is NOP: hwIO.en.write(0) if self.HAS_WE: hwIO.we.write(0) self.readPending = False else: self.doReq(req) hwIO.en.write(1) else: yield WaitWriteOnly() hwIO.en.write(0) if self.HAS_WE: hwIO.we.write(0) self.readPending = False if readPending: # in previous clock the read request was dispatched, now we are collecting the data yield WaitCombStable() # now we are after clk edge d = hwIO.dout.read() self.r_data.append(d) if self._debugOutput is not None: self._debugOutput.write("%s, on %r read_data: %d\n" % ( self.hwIO._getFullName(), self.sim.now, d.val)) class HwIOBramPortAgent(HwIOBramPort_noClkAgent): def getDrivers(self): yield from super(HwIOBramPortAgent, self).getDrivers() self.clk_ag = ClockAgent(self.sim, self.hwIO.clk) yield from self.clk_ag.getDrivers() ================================================ FILE: hwt/hwIOs/agents/fifo.py ================================================ from collections import deque from hwt.simulator.agentBase import SyncAgentBase from hwtSimApi.constants import CLK_PERIOD from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.process_utils import OnRisingCallbackLoop from hwtSimApi.triggers import Timer, WaitWriteOnly, WaitCombRead, WaitCombStable, \ WaitTimeslotEnd class HwIOFifoReaderAgent(SyncAgentBase): """ Simulation agent for FifoReader interface """ def __init__(self, sim: HdlSimulator, hwIO: "HwIOFifoReader", allowNoReset=False): super(HwIOFifoReaderAgent, self).__init__(sim, hwIO, allowNoReset) self.data = deque() self.readPending = False self.lastData = None # flags to keep data coherent when enable state changes self.lastData_invalidate = False self.readPending_invalidate = False if hwIO.DATA_WIDTH == 0: raise NotImplementedError() def setEnable_asDriver(self, en: bool): lastEn = self._enabled super(HwIOFifoReaderAgent, self).setEnable_asDriver(en) self.hwIO.wait.write(not en) self.lastData_invalidate = not en if not lastEn: self.dataWriter.setEnable(en) def setEnable_asMonitor(self, en: bool): lastEn = self._enabled super(HwIOFifoReaderAgent, self).setEnable_asMonitor(en) self.hwIO.en.write(en) self.readPending_invalidate = not en if not lastEn: self.dataReader.setEnable(en) # else dataReader will disable itself def driver_init(self): yield WaitWriteOnly() self.hwIO.wait.write(not self._enabled) def monitor_init(self): yield WaitWriteOnly() self.hwIO.en.write(self._enabled) def get_data(self): return self.hwIO.data.read() def dataReader(self): yield Timer(1) if self.readPending: yield WaitCombRead() d = self.get_data() self.data.append(d) if self.readPending_invalidate: self.readPending = False if not self.readPending and not self._enabled: self.dataWriter.setEnable(False) def getMonitors(self): self.dataReader = OnRisingCallbackLoop(self.sim, self.clk, self.dataReader, self.getEnable) yield self.monitor_init() yield from super(HwIOFifoReaderAgent, self).getMonitors() yield self.dataReader() def monitor(self): """ Initialize data reading if wait is 0 """ hwIO = self.hwIO yield WaitCombRead() if self.notReset(): # wait until wait signal is stable wait_last = None while True: yield WaitCombRead() wait = hwIO.wait.read() try: wait = int(wait) except ValueError: raise AssertionError(self.sim.now, hwIO, "wait signal in invalid state") if wait is wait_last: break else: wait_last = wait yield WaitWriteOnly() rd = not wait else: rd = False yield WaitWriteOnly() hwIO.en.write(rd) self.readPending = rd def getDrivers(self): self.dataWriter = OnRisingCallbackLoop(self.sim, self.clk, self.dataWriter, self.getEnable) yield self.driver_init() yield from super(HwIOFifoReaderAgent, self).getDrivers() yield self.dataWriter() def set_data(self, d): self.hwIO.data.write(d) def dataWriter(self): # delay data litle bit to have nicer wave # otherwise write happens before next clk period # and it means in 0 time and we will not be able to see it in wave yield Timer(1) yield WaitWriteOnly() self.set_data(self.lastData) if self.lastData_invalidate: self.lastData = None if not self._enabled: self.dataWriter.setEnable(False) def driver(self): # now we are before clock event # * set wait signal # * set last data (done in separate process) # * if en == 1, pop next data for next clk hwIO = self.hwIO yield WaitCombRead() rst_n = self.notReset() # speculative write if rst_n and self.data: wait = 0 else: wait = 1 yield WaitWriteOnly() hwIO.wait.write(wait) if rst_n: # wait for potential update of en # check if write can be performed and if it possible do real write yield WaitTimeslotEnd() en = hwIO.en.read() try: en = int(en) except ValueError: raise AssertionError(self.sim.now, hwIO, "en signal in invalid state") if en: assert self.data, (self.sim.now, hwIO, "underflow") self.lastData = self.data.popleft() class HwIOFifoWriterAgent(SyncAgentBase): """ Simulation agent for FifoWriter interface """ def __init__(self, sim: HdlSimulator, hwIO: "HwIOFifoWriter", allowNoReset=False): super(HwIOFifoWriterAgent, self).__init__( sim, hwIO, allowNoReset=allowNoReset) self.data = deque() if hwIO.DATA_WIDTH == 0: raise NotImplementedError() def driver_init(self): yield WaitWriteOnly() self.hwIO.en.write(self._enabled) def monitor_init(self): yield WaitWriteOnly() self.hwIO.wait.write(not self._enabled) def setEnable_asDriver(self, en: bool): SyncAgentBase.setEnable_asDriver(self, en) self.hwIO.en.write(en) def setEnable_asMonitor(self, en: bool): SyncAgentBase.setEnable_asMonitor(self, en) self.hwIO.wait.write(not en) def get_data(self): return self.hwIO.data.read() def set_data(self, d): self.hwIO.data.write(d) def monitor(self): # set wait signal # if en == 1 take data hwIO = self.hwIO yield WaitWriteOnly() hwIO.wait.write(0) yield WaitCombStable() # wait for potential update of en en = hwIO.en.read() try: en = int(en) except ValueError: raise AssertionError(self.sim.now, hwIO, "en signal in invalid state") if en: yield Timer(CLK_PERIOD // 10) yield WaitCombRead() self.data.append(self.get_data()) def driver(self): # if wait == 0 set en=1 and set data hwIO = self.hwIO d = None v = 0 yield WaitCombRead() if self.notReset() and self.data: yield WaitCombRead() wait = hwIO.wait.read() try: wait = int(wait) except ValueError: raise AssertionError(self.sim.now, hwIO, "wait signal in invalid state") if not wait: d = self.data.popleft() v = 1 yield WaitWriteOnly() self.set_data(d) hwIO.en.write(v) def getDrivers(self): yield from SyncAgentBase.getDrivers(self) yield self.driver_init() def getMonitors(self): yield from SyncAgentBase.getMonitors(self) yield self.monitor_init() ================================================ FILE: hwt/hwIOs/agents/rdSync.py ================================================ from collections import deque from hwt.constants import NOP from hwt.simulator.agentBase import SyncAgentBase from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.triggers import WaitCombRead, WaitWriteOnly class HwIODataRdAgent(SyncAgentBase): """ Simulation/verification agent for RdSynced interface """ def __init__(self, sim: HdlSimulator, hwIO: "HwIODataRd", allowNoReset=True): super().__init__(sim, hwIO, allowNoReset=allowNoReset) self.actualData = NOP self.data = deque() self._rd = self.get_ready_signal(hwIO) @classmethod def get_ready_signal(cls, hwIO: "HwIODataRd"): return hwIO.rd def get_ready(self): return self._rd.read() def set_ready(self, val): self._rd.write(val) def setEnable_asMonitor(self, en: bool): super(HwIODataRdAgent, self).setEnable_asMonitor(en) if not en: self.set_ready(0) def monitor(self): """Collect data from interface""" yield WaitCombRead() if self.notReset() and self._enabled: yield WaitWriteOnly() self.set_ready(1) yield WaitCombRead() d = self.get_data() self.data.append(d) else: yield WaitWriteOnly() self.set_ready(0) def get_data(self): """extract data from interface""" return self.hwIO.data.read() def set_data(self, data): """write data to interface""" self.hwIO.data.write(data) def driver(self): """Push data to interface""" yield WaitWriteOnly() if self.actualData is NOP and self.data: self.actualData = self.data.popleft() do = self.actualData is not NOP if do: self.set_data(self.actualData) else: self.set_data(None) yield WaitCombRead() en = self.notReset() and self._enabled if not (en and do): return if en: rd = self.get_ready() try: rd = int(rd) except ValueError: raise AssertionError( ("%r: ready signal for interface %r is in invalid state," " this would cause desynchronization") % (self.sim.now, self.hwIO)) if rd: if self._debugOutput is not None: self._debugOutput.write("%s, wrote, %d: %r\n" % ( self.hwIO._getFullName(), self.sim.now, self.actualData)) if self.data: self.actualData = self.data.popleft() else: self.actualData = NOP ================================================ FILE: hwt/hwIOs/agents/rdVldSync.py ================================================ from hwt.simulator.agentBase import SyncAgentBase from hwtSimApi.agents.rdVldSync import DataRdVldAgent from hwtSimApi.hdlSimulator import HdlSimulator class HwIODataRdVldAgent(SyncAgentBase, DataRdVldAgent): """ Simulation/verification agent for :class:`hwt.hwIOs.std.Handshaked` interface there is onMonitorReady(simulator) and onDriverWriteAck(simulator) unimplemented method which can be used for interfaces with bi-directional data streams :note: 2-phase (xor) handshake :attention: requires clk and rst/rstn signal ( If you do not have any create simulation wrapper with it. Without it you can very easily end up with a combinational loop.) """ def __init__(self, sim: HdlSimulator, hwIO: "HwIODataRdVld", allowNoReset=False): rst = self._discoverReset(hwIO, allowNoReset) clk = hwIO._getAssociatedClk() DataRdVldAgent.__init__(self, sim, hwIO, clk, rst) self._vld = self.get_valid_signal(hwIO) self._rd = self.get_ready_signal(hwIO) @classmethod def get_ready_signal(cls, hwIO: "HwIODataRdVld"): return hwIO.rd._sigInside def get_ready(self): return self._rd.read() def set_ready(self, val): self._rd.write(val) @classmethod def get_valid_signal(cls, hwIO: "HwIODataRdVld"): return hwIO.vld._sigInside def get_valid(self): """get "valid" signal""" return self._vld.read() def set_valid(self, val): return self._vld.write(val) def get_data(self): """extract data from interface""" return self.hwIO.data.read() def set_data(self, data): """write data to interface""" self.hwIO.data.write(data) class UniversalRdVldSyncAgent(HwIODataRdVldAgent): """ Same thing like :class:`hwt.hwIOs.agents.rdVldSync.HwIODataRdVldAgent` just the get_data/set_data method is predefined to use a tuple constructed from signals available on this interface. :ivar ~._signals: tuple of data signals of this interface (excluding ready and valid signal) :ivar ~._sigCnt: len(_signals) """ def __init__(self, sim: HdlSimulator, hwIO: "HwIODataRdVld", allowNoReset=False): HwIODataRdVldAgent.__init__(self, sim, hwIO, allowNoReset=allowNoReset) signals = [] rd = self.get_ready_signal(hwIO) vld = self.get_valid_signal(hwIO) for hwIO in hwIO._hwIOs: if hwIO._hwIOs or (hwIO._sigInside is not rd and hwIO._sigInside is not vld): signals.append(hwIO) self._signals = tuple(signals) self._sigCnt = len(signals) def get_data(self): if self._sigCnt == 1: return self._signals[0].read() else: return tuple(sig.read() for sig in self._signals) def set_data(self, data): if data is None: for sig in self._signals: sig.write(None) else: if self._sigCnt == 1: self._signals[0].write(data) else: assert len(data) == self._sigCnt, ( "invalid number of data for an interface", len(data), self._signals, self.hwIO._getFullName()) for sig, val in zip(self._signals, data): try: sig.write(val) except: raise ValueError("Error while writing ", val, "to ", sig) class HwIORdVldSyncAgent(HwIODataRdVldAgent): """ Simulation/verification agent for HwIOVldRd interface :attention: there is no data channel on this interface it is synchronization only and it actually does not have any meaningful data collected data in monitor mode are just values of simulation time when item was collected """ def set_data(self, data): pass def get_data(self): return self.sim.now class RdVldSyncReadListener(): def __init__(self, hsAgent: HwIODataRdVldAgent): self.original_afterRead = hsAgent._afterRead hsAgent._afterRead = self._afterReadWrap self.agent = hsAgent self.callbacks = {} def _afterReadWrap(self): if self.original_afterRead is not None: self.original_afterRead() try: cb = self.callbacks.pop(len(self.agent.data)) except KeyError: return cb() def register(self, transCnt, callback): self.callbacks[transCnt] = callback ================================================ FILE: hwt/hwIOs/agents/regCntrl.py ================================================ from hwt.hwIOs.agents.signal import HwIOSignalAgent from hwt.hwIOs.agents.vldSync import HwIODataVldAgent from hwt.simulator.agentBase import SyncAgentBase from hwtSimApi.agents.base import AgentBase class HwIORegCntrlAgent(SyncAgentBase): """ Simulation/verification agent for RegCntrl interface """ def __init__(self, sim, hwIO: "HwIORegCntrl"): super().__init__(sim, hwIO) self._din = HwIOSignalAgent(sim, hwIO.din) self._dout = HwIODataVldAgent(sim, hwIO.dout, allowNoReset=True) def setEnable_asDriver(self, en: bool): AgentBase.setEnable(self, en) self._din.setEnable(en) self._dout.setEnable(en) def setEnable_asMonitor(self, en: bool): AgentBase.setEnable(self, en) self._din.setEnable(en) self._dout.setEnable(en) def din_getter(self): return self._din.data def din_setter(self, newVal): self._din.data = newVal din = property(din_getter, din_setter) def dout_getter(self): return self._dout.data def dout_setter(self, newVal): self._dout.data = newVal dout = property(dout_getter, dout_setter) def getDrivers(self): yield from self._din.getMonitors() yield from self._dout.getDrivers() def getMonitors(self): yield from self._din.getDrivers() yield from self._dout.getMonitors() ================================================ FILE: hwt/hwIOs/agents/signal.py ================================================ from collections import deque from hwt.simulator.agentBase import SyncAgentBase from hwt.synthesizer.exceptions import IntfLvlConfErr from hwtSimApi.agents.base import AgentBase from hwtSimApi.constants import CLK_PERIOD from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.triggers import Timer, WaitWriteOnly, WaitCombRead, WaitCombStable class HwIOSignalAgent(SyncAgentBase): """ Agent for signal interface, it can use clock and reset interface for synchronization or can be synchronized by delay :attention: clock synchronization has higher priority """ def __init__(self, sim: HdlSimulator, hwIO: "HwIOSignal", delay=None): AgentBase.__init__(self, sim, hwIO) self.delay = delay self.initDelay = 0 # resolve clk and rstn try: self.clk = self.hwIO._getAssociatedClk() except IntfLvlConfErr: self.clk = None self.rst, self.rstOffIn = self._discoverReset(hwIO, True) self.data = deque() self.initPending = True if self.clk is None: if self.delay is None: self.delay = CLK_PERIOD self.monitor = self.monitorWithTimer self.driver = self.driverWithTimer else: if self.initDelay: raise NotImplementedError("initDelay only without clock") if self.delay: raise ValueError("clock and delay synchronization at once") c = self.SELECTED_EDGE_CALLBACK self.monitor = c(self.sim, self.clk, self.monitorWithClk, self.getEnable) self.driver = c(self.sim, self.clk, self.driverWithClk, self.getEnable) def getDrivers(self): yield self.driverInit() if self.clk is None: if self.delay is None: self.delay = CLK_PERIOD yield self.driverWithTimer() else: if self.initDelay: raise NotImplementedError("initDelay only without clock") if self.delay: raise ValueError("clock and delay synchronization at once") c = self.SELECTED_EDGE_CALLBACK if not isinstance(self.driver, c): self.driver = c(self.sim, self.clk, self.driverWithClk, self.getEnable) yield self.driver() def getMonitors(self): if self.clk is None: if self.delay is None: self.delay = CLK_PERIOD yield self.monitorWithTimer() else: if self.initDelay: raise NotImplementedError("initDelay only without clock") if self.delay: raise ValueError("clock and delay synchronization at once") c = self.SELECTED_EDGE_CALLBACK if not isinstance(self.monitor, c): self.monitor = c(self.sim, self.clk, self.monitorWithClk, self.getEnable) yield self.monitor() def driverInit(self): yield WaitWriteOnly() if not self._enabled: return try: d = self.data[0] except IndexError: d = None self.set_data(d) def get_data(self): return self.hwIO.read() def set_data(self, data): self.hwIO.write(data) def driverWithClk(self): # if clock is specified this function is periodically called every # clk tick, if agent is enabled yield WaitCombRead() if not self._enabled: return if self.data and self.notReset(): yield WaitWriteOnly() if not self._enabled: return d = self.data.popleft() self.set_data(d) def driverWithTimer(self): if self.initPending: if self.initDelay: yield Timer(self.initDelay) self.initPending = False # if clock is specified this function is periodically called every # clk tick while True: yield WaitWriteOnly() if self._enabled and self.data and self.notReset(): yield WaitWriteOnly() if self._enabled: d = self.data.popleft() self.set_data(d) yield Timer(self.delay) def monitorWithTimer(self): if self.initPending and self.initDelay: yield Timer(self.initDelay) self.initPending = False # if there is no clk, we have to manage periodic call by our self while True: yield WaitCombRead() if self._enabled and self.notReset(): d = self.get_data() self.data.append(d) yield Timer(self.delay) def monitorWithClk(self): # if clock is specified this function is periodically called every # clk tick, when agent is enabled yield WaitCombStable() if self._enabled and self.notReset(): d = self.get_data() self.data.append(d) ================================================ FILE: hwt/hwIOs/agents/struct.py ================================================ from typing import Union from hwt.hdl.types.structValBase import HStructConstBase from hwtSimApi.agents.base import AgentBase from hwtSimApi.hdlSimulator import HdlSimulator class HwIOStructAgent(AgentBase): """ Agent for HwIOStruct :summary: only purpose is to instantiate agents for child interfaces """ def __init__(self, sim: HdlSimulator, hwIO: "HwIOStruct"): AgentBase.__init__(self, sim, hwIO) for subHwIO in hwIO._hwIOs: subHwIO._initSimAgent(sim) def set_data(self, d: Union[HStructConstBase, list]): hwIO = self.hwIO if d is None: for hio in hwIO._hwIOs: hio._ag.set_data(None) elif getattr(d, "_dtype", None) is hwIO._dtype: for hio in hwIO._hwIOs: v = getattr(d, hio._name) hio._ag.set_data(v) else: assert len(d) == len(hwIO._hwIOs), (d, hwIO._hwIOs) for v, hio in zip(d, hwIO._hwIOs): hio._ag.set_data(v) def get_data(self): hwIO = self.hwIO return tuple(hio._ag.get_data() for hio in hwIO._hwIOs) def getMonitors(self): for hio in self.hwIO._hwIOs: yield from hio._ag.getMonitors() def getDrivers(self): for hio in self.hwIO._hwIOs: yield from hio._ag.getDrivers() ================================================ FILE: hwt/hwIOs/agents/tuleWithCallback.py ================================================ class TupleWithCallback(tuple): def __new__(cls, *args, onDone=None): t = tuple.__new__(cls, args) if onDone is not None: t.onDone = onDone return t ================================================ FILE: hwt/hwIOs/agents/union.py ================================================ from hwt.hwIOs.agents.struct import HwIOStructAgent class HwIOUnionSourceAgent(HwIOStructAgent): def getMonitors(self): sel = self.hwIO._select for hio in self.hwIO._hwIOs: if hio is sel: yield from hio._ag.getDrivers() else: yield from hio._ag.getMonitors() def getDrivers(self): sel = self.hwIO._select for hio in self.hwIO._hwIOs: if hio is sel: yield from hio._ag.getMonitors() else: yield from hio._ag.getDrivers() ================================================ FILE: hwt/hwIOs/agents/universalComposite.py ================================================ from hwt.hwIO import HwIO from hwtSimApi.agents.base import AgentBase from hwtSimApi.hdlSimulator import HdlSimulator from ipCorePackager.constants import INTF_DIRECTION class UniversalCompositeAgent(AgentBase): """ Composite agent which just instantiates agents for every subinterface """ def __init__(self, sim: HdlSimulator, hwIO: HwIO): self.__enable = True super(UniversalCompositeAgent, self).__init__(sim, hwIO) for hwIO in hwIO._hwIOs: hwIO._initSimAgent(sim) def getEnable(self): return self.__enable def setEnable(self, v: bool): """ Distribute change of enable on child agents """ self.__enable = v for sHwIO in self.hwIO._hwIOs: sHwIO._ag.setEnable(v) def getDrivers(self): for sHwIO in self.hwIO._hwIOs: if sHwIO._direction == INTF_DIRECTION.MASTER: yield from sHwIO._ag.getMonitors() else: yield from sHwIO._ag.getDrivers() def getMonitors(self): for sHwIO in self.hwIO._hwIOs: if sHwIO._direction == INTF_DIRECTION.MASTER: yield from sHwIO._ag.getMonitors() else: yield from sHwIO._ag.getDrivers() ================================================ FILE: hwt/hwIOs/agents/vldSync.py ================================================ from collections import deque from hwt.constants import NOP from hwt.simulator.agentBase import SyncAgentBase from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.triggers import WaitCombRead, WaitWriteOnly, WaitCombStable from pyMathBitPrecise.bit_utils import ValidityError class HwIODataVldAgent(SyncAgentBase): def __init__(self, sim: HdlSimulator, hwIO: "HwIODataVld", allowNoReset=False): super(HwIODataVldAgent, self).__init__( sim, hwIO, allowNoReset=allowNoReset) self.data = deque() self._vld = self.get_valid_signal(hwIO) def get_data(self): return self.hwIO.data.read() def set_data(self, data): self.hwIO.data.write(data) @classmethod def get_valid_signal(cls, hwIO): return hwIO.vld def get_valid(self): return self._vld.read() def set_valid(self, val): self._lastVld = val return self._vld.write(val) def setEnable_asDriver(self, en): super(HwIODataVldAgent, self).setEnable_asDriver(en) if not en: self.set_valid(0) def monitor(self): yield WaitCombStable() if self.notReset(): hwIO = self.hwIO vld = self.get_valid() try: vld = int(vld) except ValidityError: raise ValidityError(self.sim.now, hwIO._getFullName(), "vld signal in invalid state (would cause desynchronisation)") if vld: d = self.get_data() if self._debugOutput is not None: self._debugOutput.write("%s, read, %d: %r\n" % ( hwIO._getFullName(), self.sim.now, d)) self.data.append(d) def driver(self): yield WaitCombRead() if self.data and self.notReset(): d = self.data.popleft() else: d = NOP yield WaitWriteOnly() if d is NOP: self.set_data(None) self.set_valid(0) else: self.set_data(d) self.set_valid(1) if self._debugOutput is not None: self._debugOutput.write("%s, wrote, %d: %r\n" % ( self.hwIO._getFullName(), self.sim.now, self.actualData)) ================================================ FILE: hwt/hwIOs/hwIOArray.py ================================================ from copy import copy from itertools import zip_longest from typing import Sequence, Optional, Union, Self from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.types.structValBase import HStructConstBase from hwt.hwIO import HwIO from hwt.pyUtils.typingFuture import override from hwt.synthesizer.typePath import TypePath from hwtSimApi.agents.base import AgentBase from hwtSimApi.hdlSimulator import HdlSimulator from ipCorePackager.constants import DIRECTION class HwIOArray(HObjList[Optional[HwIO]], HwIO): """ HwIO for array like interfaces, this object is mutable but for conviniece it is better to populate this in constructor or atleast before registration on parent object. """ def __init__(self, items: Sequence[HwIO]=(), masterDir=DIRECTION.OUT, hdlName:Optional[Union[str, dict[str, str]]]=None, loadConfig=True): HObjList.__init__(self, items) HwIO.__init__(self, masterDir=masterDir, hdlName=hdlName, loadConfig=loadConfig) self._on_append = self._registerArray_append @override def hwDeclr(self): for i, item in enumerate(self): self._registerArray_append(self, item, i) assert len(self) - sum(1 if item is None else 0 for item in self) == len(self._hwIOs), (self, len(self), len(self._hwIOs)) @override def __hash__(self): # :note: __hash__, __eq__ are overriden because HObjList by default is non hashable return id(self) @override def __eq__(self, other:object) -> bool: """ HwIO is an unique object representig IO of the component that is only the same object should equal to self """ return other is self @override def __copy__(self): c = self.__class__(copy(i) for i in self) if self: c._updateHwParamsFrom(self[0]) return c @internal def _registerSubmodule(self, mName:str, submodule:"HwModule", onParentPropertyPath: TypePath): raise AssertionError(self, "should not have submodules", mName, submodule, onParentPropertyPath) @override def __call__(self, other: Self, exclude=None, fit=False): """ () operator behaving as assignment operator """ if not isinstance(other, (list, tuple)): raise TypeError(other) if len(self) != len(other): raise ValueError("Different number of interfaces in list", len(self), len(other)) statements = [] for a, b in zip(self, other): stms = a(b, exclude=exclude, fit=fit) if isinstance(stms, HdlStatement): statements.append(stms) else: statements += stms return statements @override def _initSimAgent(self, sim: HdlSimulator): # rst = self._getAssociatedRst() self._ag = HwIOArrayAgent(sim, self) # , (rst, rst._dtype.negated) def __repr__(self, *args, **kwargs) -> str: return HwIO.__repr__(self, *args, **kwargs) class HwIOArrayAgent(AgentBase): """ Agent for HwIOArray :summary: only purpose is to instantiate agents for child interfaces """ def __init__(self, sim: HdlSimulator, hwIO: HwIOArray): AgentBase.__init__(self, sim, hwIO) for subHwIO in hwIO._hwIOs: subHwIO._initSimAgent(sim) def extendDataFromTuples(self, dataTuples: Sequence[tuple]): hwIOAgData = [hwio._ag.data for hwio in self.hwIO._hwIOs] hwIOsCnt = len(hwIOAgData) for dTuple in dataTuples: assert len(dTuple) == hwIOsCnt, (dTuple, hwIOsCnt) for v, hioData in zip(dTuple, hwIOAgData): hioData.append(v) def getDataAsTuples(self): yield from zip_longest(*(hio._ag.data for hio in self.hwIO._hwIOs)) def set_data(self, d: Union[HStructConstBase, list]): hwIO = self.hwIO if d is None: for hio in hwIO._hwIOs: hio._ag.set_data(None) else: assert len(d) == len(hwIO._hwIOs), (d, hwIO._hwIOs) for v, hio in zip(d, hwIO._hwIOs): hio._ag.set_data(v) def get_data(self): hwIO = self.hwIO return tuple(hio._ag.get_data() for hio in hwIO._hwIOs) def getMonitors(self): for hio in self.hwIO._hwIOs: yield from hio._ag.getMonitors() def getDrivers(self): for hio in self.hwIO._hwIOs: yield from hio._ag.getDrivers() ================================================ FILE: hwt/hwIOs/hwIODifferential.py ================================================ from hwt.hdl.types.bits import HBits from hwt.hwIO import HwIO from hwt.hwIOs.std import HwIOSignal from hwt.pyUtils.typingFuture import override class HwIODifferentialSig(HwIO): """ HwIO of differential pair """ @override def hwDeclr(self): self.n = HwIOSignal(dtype=HBits(1, negated=True)) self.p = HwIOSignal() ================================================ FILE: hwt/hwIOs/hwIOStruct.py ================================================ from copy import copy from typing import Optional, Union, Set from hwt.code import And, Or from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.array import HArray from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.struct import HStruct from hwt.hdl.types.structCast import hstruct_reinterpret from hwt.hdl.types.structValBase import HStructConstBase from hwt.hwIO import HwIO from hwt.hwIOs.agents.rdSync import HwIODataRdAgent from hwt.hwIOs.agents.rdVldSync import HwIODataRdVldAgent from hwt.hwIOs.agents.struct import HwIOStructAgent from hwt.hwIOs.agents.vldSync import HwIODataVldAgent from hwt.hwIOs.hwIOArray import HwIOArray from hwt.hwIOs.std import HwIODataVld, HwIODataRd, HwIOSignal, HwIORdVldSync from hwt.hwParam import HwParam from hwt.pyUtils.typingFuture import override from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from hwt.synthesizer.typePath import TypePath from hwtSimApi.hdlSimulator import HdlSimulator from ipCorePackager.constants import DIRECTION class BoundedMethodProxy(): def __init__(self, newInstance, method): self.newInstance = newInstance self.method = method def __call__(self, *args, **kwargs): return self.method(self.newInstance, *args, **kwargs) def __repr__(self): return f"<{self.__class__.__name__:s} {self.newInstance} {self.method}>" class HwIOStruct(HwIO): """ Create dynamic interface based on HStruct or HUnion description :ivar ~._fieldsToHwIOs: dictionary {field_path: sub interface for it} field path is a tuple of HStructFields which leads to this interface :ivar ~._dtype: HStruct instance used as template for this interface :ivar ~._instantiateFieldFn: function(FieldTemplateItem instance) return HwIO instance :attention: _instantiateFieldFn should also share _fieldsToHwIOs with all other instances of HwIOStruct on this interface """ def __init__(self, structT: HStruct, field_path: Optional[TypePath], instantiateFieldFn, masterDir=DIRECTION.OUT, loadConfig=True): HwIO.__init__(self, masterDir=masterDir, loadConfig=loadConfig) if field_path: assert isinstance(field_path, TypePath), field_path else: field_path = TypePath() self._isAccessible = True self._field_path = field_path self._dtype = structT assert self._dtype.fields, "Needs to have at least some members (otherwise this interface is useless)" self._instantiateFieldFn = instantiateFieldFn self._fieldsToHwIOs: dict[TypePath, HwIO] = {} @override def hwDeclr(self): _t = self._dtype assert _t is not None if isinstance(_t, HStruct): fields = _t.fields else: fields = _t.fields.values() self._fieldsToHwIOs[self._field_path] = self for field in fields: # skip padding if field.name is not None: # generate interface based on struct field hwIO = self._instantiateFieldFn(self, field) p = self._field_path / field.name assert p not in self._fieldsToHwIOs, p self._fieldsToHwIOs[p] = hwIO setattr(self, field.name, hwIO) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOStructAgent(sim, self) def _raiseBinOperatorError(self, opStr, other): raise TypeError("unsupported operand type(s) for ", opStr, ": ", self.__class__.__name__, self._dtype, " and ", other.__class__.__name__, other._dtype if isinstance(other, (HwIO, RtlSignal, HConst)) else None) @override def _eq(self, other: Union["HwIOStruct", HStructConstBase]): if isinstance(other, self.__class__): assert self._dtype == other._dtype return And(*(sHwIO._eq(oi) for sHwIO, oi in zip(self._hwIOs, other._hwIOs))) else: return And(*(sHwIO._eq(getattr(other, sHwIO._name)) for sHwIO in self._hwIOs)) @override def __add__(self, other): try: sigOpFn = self._dtype.getRtlSignalCls().__add__ except: self._raiseBinOperatorError("+", other) return sigOpFn(self, other) @override def __sub__(self, other): try: sigOpFn = self._dtype.getRtlSignalCls().__sub__ except: self._raiseBinOperatorError("-", other) return sigOpFn(self, other) @override def __mul__(self, other): try: sigOpFn = self._dtype.getRtlSignalCls().__mul__ except: self._raiseBinOperatorError("*", other) return sigOpFn(self, other) @override def __truediv__(self, other): try: sigOpFn = self._dtype.getRtlSignalCls().__truediv__ except: self._raiseBinOperatorError("/", other) return sigOpFn(self, other) @override def __floordiv__(self, other): try: sigOpFn = self._dtype.getRtlSignalCls().__floordiv__ except: self._raiseBinOperatorError("//", other) return sigOpFn(self, other) @override def __ne__(self, other: Union["HwIOStruct", HStructConstBase]): if isinstance(other, self.__class__): assert self._dtype == other._dtype return Or(*(sHwIO != oi for sHwIO, oi in zip(self._hwIOs, other._hwIOs))) else: return Or(*(sHwIO != getattr(other, sHwIO._name) for sHwIO in self._hwIOs)) @override def _reinterpret_cast(self, toT: HdlType): return hstruct_reinterpret(self._dtype, self, toT) def __iter__(self): return iter(self._hwIOs) def __len__(self): return len(self._hwIOs) def __getattr__(self, name:str): if name == "_dtype" or name == "_setAttrListener" or self._setAttrListener is not None: raise AttributeError(name) try: rtlSignalCls = self._dtype.getRtlSignalCls() except NotImplementedError: raise AttributeError(name) sigMethod = getattr(rtlSignalCls, name) return BoundedMethodProxy(self, sigMethod) # bound method to this instance def __copy__(self): """ Create new instance of interface of same type and configuration """ hwIO = self.__class__(self._dtype, self._field_path, self._instantiateFieldFn, masterDir=self._masterDir, loadConfig=hasattr(self, '_hwParams')) hwIO._updateHwParamsFrom(self) return hwIO class HdlType_to_HwIO(): """ Convert instance of HdlType to an interface which represents same data. :note: HwIO is only instantiated, that means it does not have sub-interfaces loaded yet, it can be done manually or by assigning to a property of parent HwIO/HwModule instance. """ def apply(self, dtype: HdlType, field_path: Optional[TypePath]=None, masterDir=DIRECTION.OUT) -> HwIO: """ Run the conversion """ if isinstance(dtype, HStruct): return HwIOStruct(dtype, field_path, instantiateFieldFn=self.instantiateFieldFn, masterDir=masterDir) elif dtype.isScalar(): return HwIOSignal(dtype=dtype, masterDir=masterDir) elif isinstance(dtype, HArray): return HwIOArray(self.apply(dtype.element_t, masterDir=masterDir) for _ in range(dtype.size)) else: raise NotImplementedError(dtype) @internal def instantiateFieldFn(self, hwIO, fieldInfo) -> HwIO: if isinstance(hwIO, HwIOStruct): c = self.apply( fieldInfo.dtype, field_path=hwIO._field_path / fieldInfo.name) c._fieldsToHwIOs = hwIO._fieldsToHwIOs return c else: raise NotImplementedError(hwIO) class HwIO_to_HdlType(): """ Convert instance of HdlType to an interface which represents same data. :note: HwIO instance has to have definitions loaded. """ def apply(self, hwIO: Union[HwIO, RtlSignal], const=False, exclude: Optional[Set[HwIO]]=None): """ Run the conversion """ assert exclude is None or hwIO not in exclude if isinstance(hwIO, HwIO) and hwIO._hwIOs: if isinstance(hwIO, HwIOArray): assert hwIO, "HwIOArray must have atleast some items to resolve type" return self.apply(hwIO[0], const=const, exclude=exclude)[len(hwIO)] else: return HStruct( *((self.apply(sHwIO, const=const, exclude=exclude), sHwIO._name) for sHwIO in hwIO._hwIOs if exclude is None or sHwIO not in exclude) ) else: t = hwIO._dtype if t.const != const: t = copy(t) t.const = const return t class HwIOStructRd(HwIODataRd): """ A HwIODataRd interface which has a data signal of type specified in configuration of this interface """ @override def hwConfig(self): self.T: HdlType = HwParam(None) @override def hwDeclr(self): assert isinstance(self.T, HdlType), (self.T, self._name) self._dtype = self.T self.data = HdlType_to_HwIO().apply(self.T) self.rd = HwIOSignal(masterDir=DIRECTION.IN) @override def _initSimAgent(self, sim:HdlSimulator): self._ag = HwIOStructRdAgent(sim, self) class HwIOStructRdAgent(HwIODataRdAgent): def __init__(self, sim:HdlSimulator, hwIO:HwIOStructRd, allowNoReset=False): HwIODataRdAgent.__init__(self, sim, hwIO, allowNoReset=allowNoReset) hwIO.data._initSimAgent(sim) self._data_ag = hwIO.data._ag @override def set_data(self, data): return self._data_ag.set_data(data) @override def get_data(self): return self._data_ag.get_data() class HwIOStructVld(HwIODataVld): """ A handshaked interface which has a data signal of type specified in configuration of this interface """ def hwConfig(self): self.T: HdlType = HwParam(None) @override def hwDeclr(self): assert isinstance(self.T, HdlType), (self.T, self._name) self._dtype = self.T self.data = HdlType_to_HwIO().apply(self.T) self.vld = HwIOSignal() @override def _initSimAgent(self, sim:HdlSimulator): self._ag = HwIOStructVldAgent(sim, self) class HwIOStructVldAgent(HwIODataVldAgent): def __init__(self, sim:HdlSimulator, hwIO:HwIOStructVld, allowNoReset=False): HwIODataVldAgent.__init__(self, sim, hwIO, allowNoReset=allowNoReset) hwIO.data._initSimAgent(sim) self._data_ag = hwIO.data._ag @override def set_data(self, data): return self._data_ag.set_data(data) @override def get_data(self): return self._data_ag.get_data() class HwIOStructRdVld(HwIORdVldSync): """ A handshaked interface which has a data signal of type specified in configuration of this interface """ @override def hwConfig(self): self.T: HdlType = HwParam(None) @override def hwDeclr(self): assert isinstance(self.T, HdlType), (self.__class__, self._name, "does not have correct initialization of T", self.T,) self._dtype = self.T self.data = HdlType_to_HwIO().apply(self.T) HwIORdVldSync.hwDeclr(self) @override def _initSimAgent(self, sim:HdlSimulator): self._ag = HwIOStructRdVldAgent(sim, self) class HwIOStructRdVldAgent(HwIODataRdVldAgent): def __init__(self, sim:HdlSimulator, hwIO:HwIOStructRdVld, allowNoReset=False): HwIODataRdVldAgent.__init__(self, sim, hwIO, allowNoReset=allowNoReset) hwIO.data._initSimAgent(sim) self._data_ag = hwIO.data._ag @override def set_data(self, data): return self._data_ag.set_data(data) @override def get_data(self): return self._data_ag.get_data() ================================================ FILE: hwt/hwIOs/hwIOTristate.py ================================================ from hwt.hdl.types.bits import HBits from hwt.hwIO import HwIO from hwt.hwIOs.std import HwIOSignal, HwIOClk from hwt.hwParam import HwParam from hwt.pyUtils.typingFuture import override from hwtSimApi.agents.peripheral.tristate import TristateAgent, TristateClkAgent from hwtSimApi.hdlSimulator import HdlSimulator from ipCorePackager.constants import DIRECTION from ipCorePackager.intfIpMeta import IntfIpMetaNotSpecifiedError class HwIOTristateSig(HwIO): """ Tristate interface :ivar ~.force_vector: in order to make this a vector[0] instead of single bit use FORCE_VECTOR=True """ @override def hwConfig(self): self.DATA_WIDTH = HwParam(1) self.FORCE_VECTOR = False @override def hwDeclr(self): t = HBits(self.DATA_WIDTH, force_vector=self.FORCE_VECTOR) # connect self.t = HwIOSignal(dtype=t) # input self.i = HwIOSignal(dtype=t, masterDir=DIRECTION.IN) # output self.o = HwIOSignal(dtype=t) @override def _initSimAgent(self, sim: HdlSimulator): rst = self._getAssociatedRst() self._ag = TristateAgent(sim, self, (rst, rst._dtype.negated)) class HwIOTristateClk(HwIOClk, HwIOTristateSig): @override def hwConfig(self): HwIOClk.hwConfig(self) HwIOTristateSig.hwConfig(self) @override def _getIpCoreIntfClass(self): raise IntfIpMetaNotSpecifiedError() @override def _initSimAgent(self, sim: HdlSimulator): self._ag = TristateClkAgent(sim, self) ================================================ FILE: hwt/hwIOs/hwIOUnion.py ================================================ from hwt.hwIOs.agents.union import HwIOUnionSourceAgent from hwt.hwIOs.hwIOStruct import HwIOStruct from hwt.hwIOs.std import HwIODataRdVld from hwt.math import log2ceil from hwt.pyUtils.typingFuture import override from hwtSimApi.hdlSimulator import HdlSimulator from ipCorePackager.constants import DIRECTION class HwIOUnionSink(HwIOStruct): """ Hardware IO generated from HUnion HDL type Used when consumer chooses which member of union should be used. """ @override def hwDeclr(self): HwIOStruct.hwDeclr(self) self._select = HwIODataRdVld() self._select.DATA_WIDTH = log2ceil(len(self._dtype.fields)) class HwIOUnionSource(HwIOUnionSink): """ Same like `HwIOUnionSink` but producer is selecting member of union which should be used. """ @override def hwDeclr(self): HwIOStruct.hwDeclr(self) self._select = HwIODataRdVld(masterDir=DIRECTION.IN) self._select.DATA_WIDTH = log2ceil(len(self._dtype.fields)) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOUnionSourceAgent(sim, self) ================================================ FILE: hwt/hwIOs/hwIO_map.py ================================================ from typing import Union, Tuple, Optional, List from hwt.doc_markers import internal from hwt.hwIO import HwIO from hwt.hwIOs.std import HwIOBramPort_noClk, HwIORegCntrl, HwIODataVld, HwIOSignal from hwt.hwIOs.hwIOStruct import HwIOStruct from hwt.hwIOs.hwIOUnion import HwIOUnionSink, HwIOUnionSource from hwt.hObjList import HObjList from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.struct import HStruct, HStructFieldMeta, HStructField from hwt.mainBases import HwIOBase from hwt.mainBases import RtlSignalBase from hwt.synthesizer.interfaceLevel.hwModuleImplHelpers import getSignalName from hwt.synthesizer.typePath import TypePath class HwIOObjMap(List["HwIoMapItem"]): """ Container of interface map. The interface map object describes the data type using existing HwIO instances. Items can be HwIO/RtlSignal or (type/interface/None/HwIOObjMap, name). None is used for padding. """ pass HwIoMapCompatibleHwIO = Union[ RtlSignalBase, HwIODataVld, HwIORegCntrl, HwIOBramPort_noClk, HwIOStruct, HwIOUnionSink, HwIOUnionSource, ] HwIOMapItem = Union[ Tuple[HdlType, Optional[str]], Tuple[Union[HwIoMapCompatibleHwIO, RtlSignalBase], str], Tuple[List[Union[HwIoMapCompatibleHwIO, RtlSignalBase]], str], HwIoMapCompatibleHwIO ] @internal def _HTypeFromHwIOObjMap(hwIO: HwIoMapCompatibleHwIO): name = getSignalName(hwIO) if isinstance(hwIO, (RtlSignalBase, HwIOSignal)): dtype = hwIO._dtype elif isinstance(hwIO, HwIODataVld): dtype = hwIO.data._dtype elif isinstance(hwIO, HwIORegCntrl): dtype = hwIO.din._dtype elif isinstance(hwIO, HwIOBramPort_noClk): dtype = HBits(int(hwIO.DATA_WIDTH))[2 ** int(hwIO.ADDR_WIDTH)] else: raise ValueError(hwIO) return (dtype, name) @internal def HTypeFromHwIOObjMapItem(hwIOMapItem: HwIOMapItem): isTerminal = False if isinstance(hwIOMapItem, (HwIOBase, RtlSignalBase)): dtype, nameOrPrefix = _HTypeFromHwIOObjMap(hwIOMapItem) isTerminal = True else: typeOrListOfHwIOs, nameOrPrefix = hwIOMapItem if isinstance(typeOrListOfHwIOs, list) and \ not isinstance(typeOrListOfHwIOs, HwIOObjMap): # list of HType instances for array parts = [] arrayItem_t = None for item in typeOrListOfHwIOs: if isinstance(item, HwIOObjMap): t = HTypeFromHwIOObjMap(item) else: t = HTypeFromHwIOObjMapItem(item).dtype if arrayItem_t is None: arrayItem_t = t else: assert arrayItem_t == t, ( "all items in array has to have same type", arrayItem_t, t) parts.append(t) dtype = arrayItem_t[len(parts)] elif isinstance(typeOrListOfHwIOs, HdlType): dtype = typeOrListOfHwIOs isTerminal = True elif isinstance(typeOrListOfHwIOs, (HwIOBase, RtlSignalBase)): # renamed interface, ignore original name dtype = _HTypeFromHwIOObjMap(typeOrListOfHwIOs)[0] isTerminal = True elif isinstance(typeOrListOfHwIOs, HwIOObjMap): dtype = HTypeFromHwIOObjMap(typeOrListOfHwIOs) else: # tuple (tuple of interfaces, prefix) assert isinstance(typeOrListOfHwIOs, tuple), typeOrListOfHwIOs dtype = HTypeFromHwIOObjMap(typeOrListOfHwIOs) assert isinstance(nameOrPrefix, str) or nameOrPrefix is None, nameOrPrefix assert isinstance(dtype, HdlType) f = HStructField(dtype, nameOrPrefix) if not isTerminal: f.meta = HStructFieldMeta(split=True) return f def HTypeFromHwIOObjMap(hwIOMap: HwIOObjMap) -> HStruct: """ Generate flattened register map for HStruct :param hwIOMap: sequence of a tuple (HdlType, name) (will create HStructField) or a tuple (HdlType, None) (will create HStructField as padding) or a tuple (list of HwIO instances, name) (will create HStructField of HStruct type) or an HwIO instance (will create a HStructField for an interface, with a name of interface) :param DATA_WIDTH: width of word :param terminalNodes: None or set where are placed StructField instances which are derived directly from interface :return: generator of tuple (type, name, BusFieldInfo) """ structFields = [] for m in hwIOMap: f = HTypeFromHwIOObjMapItem(m) structFields.append(f) return HStruct(*structFields) def isPaddingInHwIOObjMap(item: Union[HdlType, Tuple[object, Optional[str]]]): if isinstance(item, HdlType): return True else: try: if isinstance(item, tuple): _item, name = item if name is None: return True except ValueError: pass return False def _walkHwIOStructAndHwIOObjMap_unpack(io: Union[HObjList, HwIOStruct, HwIOUnionSink, HwIOUnionSource], hwIOMap): """ Try to unpack hwIOMap and apply the selection on io :return: Optional tuple HwIO, hwIOMap """ # items are HwIO/RtlSignal or (type/interface/None or list of items, name) if isPaddingInHwIOObjMap(hwIOMap): return elif isinstance(hwIOMap, tuple): item, name = hwIOMap else: item = hwIOMap assert isinstance(item, (HwIOBase, RtlSignalBase)), item name = getSignalName(item) if isinstance(item, HdlType): # this part of io was generated from type descriptin # and we are re searching only for those parts which were generated # from HwIO/RtlSignal return return getattr(io, name), item def walkHwIOStructAndHwIOObjMap(io: Union[HObjList, HwIOStruct, HwIOUnionSink, HwIOUnionSource], hwIOMap): """ Walk HwIOStruct and interface map and yield tuples (HwIO in HwIOStruct, interface in hwIOMap) which are on same place :param io: an interface to walk :param hwIOMap: interface map :note: typical usecase is when there is HwIOStruct generated from description in hwIOMap and then you need to connect interface from hwIOMap to io and there you can use this function to iterate over interfaces which belongs together """ if isinstance(hwIOMap, (HwIOBase, RtlSignalBase)): yield io, hwIOMap return elif isinstance(hwIOMap, tuple): item = _walkHwIOStructAndHwIOObjMap_unpack(io, hwIOMap) if item is None: # is padding or there is no interface specified for it in hwIOMap return else: io, item = item yield from walkHwIOStructAndHwIOObjMap(io, item) elif isinstance(io, list): io assert len(io) == len(hwIOMap) for sItem, item in zip(io, hwIOMap): yield from walkHwIOStructAndHwIOObjMap(sItem, item) else: assert isinstance(hwIOMap, HwIOObjMap), hwIOMap for item in hwIOMap: _item = _walkHwIOStructAndHwIOObjMap_unpack(io, item) if _item is not None: sItem, _item = _item yield from walkHwIOStructAndHwIOObjMap(sItem, _item) def HwIOObjMapItem_find_by_name(hwIOMapItem, name: str): if isinstance(hwIOMapItem, HStructField): hwIOMapItem = hwIOMapItem.dtype if isinstance(hwIOMapItem, HwIOObjMap): for x in hwIOMapItem: if isinstance(x, HwIOBase): if x._name == name: return x elif isinstance(x, RtlSignalBase): if x._hdlName == name: return x else: v, n = x if n == name: return v raise KeyError(name) elif isinstance(hwIOMapItem, HStruct): for f in hwIOMapItem.fields: if f.name == name: return f raise KeyError(name) else: raise NotImplementedError(hwIOMapItem) def HwIOObjMap_get_by_field_path(root: HwIOObjMap, field_path: TypePath): actual = root # find in interfaceMap, skip first because it is the type itself for rec in field_path: # if isinstance(rec, (HwIOBase, RtlSignalBase)): # shouldEnter, shouldUse = False, True # return shouldEnter, shouldUse if isinstance(rec, int): actual = actual[rec] elif isinstance(rec, str): actual = HwIOObjMapItem_find_by_name(actual, rec) else: raise NotImplementedError(rec) return actual ================================================ FILE: hwt/hwIOs/signalOps.py ================================================ from hwt.doc_markers import internal class SignalOps(object): """ Operands for Signal interface, These operands are delegated on RtlSignal object for this interface """ def _auto_cast(self, toT): return self._sig._auto_cast(toT) @internal def _cast_sign(self, signed): return self._sig._cast_sign(signed) def _signed(self): return self._cast_sign(True) def _unsigned(self): return self._cast_sign(False) def _vec(self): return self._cast_sign(None) def _reinterpret_cast(self, toT): return self._sig._reinterpret_cast(toT) # events def _onRisingEdge(self): return self._sig._onRisingEdge() def _onFallingEdge(self): return self._sig._onFallingEdge() # comparison def _isOn(self): """ convert this signal to hBool """ return self._sig._isOn() def getMsb(self): return self._sig.getMsb() def _eq(self, other): """ Equality operator "==" is not used because it would damage python ecosystem """ return self._sig._eq(other) def __ne__(self, other): """!=""" return self._sig.__ne__(other) def __gt__(self, other): """>""" return self._sig.__gt__(other) def __lt__(self, other): """<""" return self._sig.__lt__(other) def __ge__(self, other): """>=""" return self._sig.__ge__(other) def __le__(self, other): """<=""" return self._sig.__le__(other) # bitwise def __invert__(self): """~ operator - logical negation for one bit signals and hBool bitwise inversion for wider signals """ return self._sig.__invert__() def __and__(self, other): """ & operator - logical 'and' for one bit signals and hBool bitwise and for wider signals """ return self._sig.__and__(other) def __xor__(self, other): """ ^ operator - logical '!=' for one bit signals and hBool bitwise and for wider signals """ return self._sig.__xor__(other) def __or__(self, other): """ | operator - logical 'or' for one bit signals and hBool bitwise and for wider signals """ return self._sig.__or__(other) # arithmetic def __neg__(self): "- operator (unary minus)" return self._sig.__neg__() def __add__(self, other): "+ operator" return self._sig.__add__(other) def __sub__(self, other): "- operator" return self._sig.__sub__(other) def __mul__(self, other): "* operator" return self._sig.__mul__(other) def __pow__(self, other): "** operator" return self._sig.__pow__(other) def __mod__(self, other): "% operator" return self._sig.__mod__(other) def __truediv__(self, other): "/ operator - floating point division" return self._sig.__truediv__(other) def __floordiv__(self, other): "// operator - integer division" return self._sig.__floordiv__(other) def __lshift__(self, other): "<< left shift (on fixed number of bits)" return self._sig.__lshift__(other) def __rshift__(self, other): ">> right shift (on fixed number of bits)" return self._sig.__rshift__(other) # hdl centric def _reversed(self): """ reverse bitorder """ # https://stackoverflow.com/questions/27638960/python-reverse-magic-method return self._sig._reversed() def _concat(self, *others): """ concatenate signals to one big one """ return self._sig._concat(*others) def __getitem__(self, key): """ [] operator key can be slice or index """ return self._sig.__getitem__(key) def _ternary(self, ifTrue, ifFalse): return self._sig._ternary(ifTrue, ifFalse) def __call__(self, source, exclude=None, fit=False): """ connect this signal to driver :attention: it is not call of function it is operator of assignment :return: list of assignments """ assert self._isAccessible, self return self._sig(source, exclude=None, fit=fit) def __getattr__(self, name:str): if name == "_dtype" or name == "_setAttrListener" or self._setAttrListener is not None: raise AttributeError(name) try: sigProp = getattr(self._sig, name) except AttributeError: raise AttributeError(name) from None return sigProp ================================================ FILE: hwt/hwIOs/std.py ================================================ from typing import TypeVar, Generic, Union, Optional, Dict from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BIT, BIT_N from hwt.hdl.types.hdlType import HdlType from hwt.hwIO import HwIO from hwt.hwIOs.agents.bramPort import HwIOBramPortAgent from hwt.hwIOs.agents.bramPort import HwIOBramPort_noClkAgent from hwt.hwIOs.agents.fifo import HwIOFifoReaderAgent from hwt.hwIOs.agents.fifo import HwIOFifoWriterAgent from hwt.hwIOs.agents.rdSync import HwIODataRdAgent from hwt.hwIOs.agents.rdVldSync import HwIORdVldSyncAgent, HwIODataRdVldAgent from hwt.hwIOs.agents.regCntrl import HwIORegCntrlAgent from hwt.hwIOs.agents.signal import HwIOSignalAgent from hwt.hwIOs.agents.vldSync import HwIODataVldAgent from hwt.hwIOs.signalOps import SignalOps from hwt.hwParam import HwParam from hwt.pyUtils.typingFuture import override from hwtSimApi.agents.clk import ClockAgent from hwtSimApi.agents.rst import PullDownAgent from hwtSimApi.agents.rst import PullUpAgent from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.utils import freq_to_period from ipCorePackager.constants import DIRECTION T = TypeVar("T", bound=HdlType) class HwIOSignal(SignalOps, HwIO, Generic[T]): """ Basic wire interface :ivar ~._dtype: type of signal :ivar ~._sig: RtlSignal instance (physical representation of this logical signal) :ivar ~._sigInside: _sig after to_rtl conversion is made (after to_rtl conversion _sig is signal for parent module and _sigInside is signal in original module, this separates process of translating modules) :note: _sigInside is None if the body of component was not elaborated yet :ivar _isAccessible: flag which is set False if the signal is inside of some elaborated module """ def __init__(self, dtype: HdlType=BIT, masterDir: DIRECTION=DIRECTION.OUT, hdlName:Optional[Union[str, Dict[str, str]]]=None, loadConfig: bool=True): self._sig: Optional["RtlSignal"] = None self._sigInside: Optional["RtlSignal"] = None self._isAccessible = True super().__init__(masterDir=masterDir, hdlName=hdlName, loadConfig=loadConfig) self._dtype = dtype @override def _cleanRtlSignals(self, lockNonExternal=True): """ :see: :func:`HwIO._clean` """ self._sigInside = self._sig self._sig = None if lockNonExternal and not self._isExtern: self._isAccessible = False if self._hwIOs: HwIO._cleanRtlSignals(self, lockNonExternal=lockNonExternal) @override def __copy__(self): """ Create new instance of interface of same type and configuration """ hwIO = self.__class__(masterDir=self._masterDir, dtype=self._dtype) hwIO._updateHwParamsFrom(self) return hwIO @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOSignalAgent(sim, self) def HwIOVectSignal(width: int, signed: Union[bool, None]=None, masterDir=DIRECTION.OUT, hdlName:Optional[Union[str, Dict[str, str]]]=None, loadConfig=True): """ Create basic :class:`.HwIOSignal` interface where type is vector """ return HwIOSignal(HBits(width, signed, force_vector=width==1), masterDir, hdlName, loadConfig) class HwIOClk(HwIOSignal): """ Basic :class:`.HwIOSignal` interface which is interpreted as clock signal """ DEFAULT_FREQ = int(100e6) @override def hwConfig(self): self.FREQ = HwParam(HwIOClk.DEFAULT_FREQ) @override def _getIpCoreIntfClass(self): from hwt.hwIOs.std_ip_defs import IP_Clk return IP_Clk @override def _initSimAgent(self, sim: HdlSimulator): self._ag = ClockAgent(sim, self, period=int(freq_to_period(self.FREQ))) class HwIORst(HwIOSignal[HBits]): """ Basic :class:`.HwIOSignal` interface which is interpreted as reset signal """ @override def _getIpCoreIntfClass(self): from hwt.hwIOs.std_ip_defs import IP_Rst return IP_Rst @override def _initSimAgent(self, sim: HdlSimulator): clk = self._getAssociatedClk() self._ag = PullDownAgent(sim, self, initDelay=int(0.6 * freq_to_period(clk.FREQ))) class HwIORst_n(HwIOSignal[HBits]): """ Basic :class:`.HwIOSignal` interface which is interpreted as reset signal with negative polarity (active in 0) """ def __init__(self, masterDir=DIRECTION.OUT, dtype=BIT_N, hdlName:Optional[Union[str, Dict[str, str]]]=None, loadConfig=True): super(HwIORst_n, self).__init__(masterDir=masterDir, dtype=dtype, hdlName=hdlName, loadConfig=loadConfig) @override def _getIpCoreIntfClass(self): from hwt.hwIOs.std_ip_defs import IP_Rst_n return IP_Rst_n @override def _initSimAgent(self, sim: HdlSimulator): clk = self._getAssociatedClk() self._ag = PullUpAgent(sim, self, initDelay=int(0.6 * freq_to_period(clk.FREQ))) class HwIOVldSync(HwIO): @override def hwDeclr(self): self.vld = HwIOSignal() class HwIODataVld(HwIOVldSync): """ HwIO data+valid signal, if vld=1 then data are valid and slave should accept them """ @override def hwConfig(self): self.DATA_WIDTH = HwParam(64) @override def hwDeclr(self): self.data = HwIOVectSignal(self.DATA_WIDTH) HwIOVldSync.hwDeclr(self) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIODataVldAgent(sim, self) class HwIORdSync(HwIO): @override def hwDeclr(self) -> None: self.rd = HwIOSignal(masterDir=DIRECTION.IN) class HwIODataRd(HwIORdSync): """ HwIO data+ready signal, if rd=1 then slave has read data and master should actualize data """ @override def hwConfig(self): self.DATA_WIDTH = HwParam(64) @override def hwDeclr(self): self.data = HwIOVectSignal(self.DATA_WIDTH) HwIORdSync.hwDeclr(self) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIODataRdAgent(sim, self) class HwIORdVldSync(HwIO): """ Only synchronization interface, like vld+rd signal with meaning like in :class:`.HwIODataRdVld` interface :ivar ~.rd: when high slave is ready to receive data :ivar ~.vld: when high master is sending data to slave transaction happens when both ready and valid are high """ @override def hwDeclr(self): HwIOVldSync.hwDeclr(self) HwIORdSync.hwDeclr(self) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIORdVldSyncAgent(sim, self) class HwIODataRdVld(HwIORdVldSync): """ HwIO data+ready+valid signal, if rd=1 slave is ready to accept data, if vld=1 master is sending data, if rd=1 and vld=1 then data is transfered otherwise master and slave has to wait on each other :attention: one rd/vld is set it must not go down until transaction is made """ @override def hwConfig(self): self.DATA_WIDTH = HwParam(64) @override def hwDeclr(self): HwIODataVld.hwDeclr(self) HwIORdSync.hwDeclr(self) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIODataRdVldAgent(sim, self) class HwIOReqDoneSync(HwIO): """ Synchronization interface, if req=1 slave begins operation and when it's done it asserts done=1 for one clk tick req does not need to stay high AMBA CXS Protocol Specification https://developer.arm.com/documentation/ihi0079/latest/ (req=CXSVALID, done=CXSCRDGNT) """ @override def hwConfig(self) -> None: self.CREDIT_CNT = HwParam(1) @override def hwDeclr(self): self.req = HwIOSignal() self.done = HwIOSignal(masterDir=DIRECTION.IN) class HwIOBramPort_noClk(HwIO): """ Basic BRAM port """ @override def hwConfig(self): self.ADDR_WIDTH = HwParam(32) self.DATA_WIDTH = HwParam(64) self.HAS_R = HwParam(True) self.HAS_W = HwParam(True) self.HAS_BE = HwParam(False) @override def hwDeclr(self): assert self.HAS_R or self.HAS_W, "has to have at least read or write part" self.addr = HwIOVectSignal(self.ADDR_WIDTH) DATA_WIDTH = self.DATA_WIDTH if self.HAS_W: self.din = HwIOVectSignal(DATA_WIDTH) if self.HAS_R: self.dout = HwIOVectSignal(DATA_WIDTH, masterDir=DIRECTION.IN) self.en = HwIOSignal() if (self.HAS_R and self.HAS_W) or (self.HAS_W and self.HAS_BE): # in write only mode we do not need this as well as we can use "en" if self.HAS_BE: assert DATA_WIDTH % 8 == 0, DATA_WIDTH self.we = HwIOVectSignal(DATA_WIDTH // 8) else: self.we = HwIOSignal() def _getWordAddrStep(self): """ :return: size of one word in module of address """ return 1 def _getAddrStep(self): """ :return: how many bits is one module of address (e.g. 8 bits for char * pointer, 36 for 36 bit bram) """ return int(self.DATA_WIDTH) @override def _getIpCoreIntfClass(self): from hwt.hwIOs.std_ip_defs import IP_BlockRamPort return IP_BlockRamPort @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOBramPort_noClkAgent(sim, self) class HwIOBramPort(HwIOBramPort_noClk): """ BRAM port with it's own clk """ @override def hwDeclr(self): self.clk = HwIOSignal(masterDir=DIRECTION.OUT) with self._associated(clk=self.clk): super().hwDeclr() self._make_association(clk=self.clk) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOBramPortAgent(sim, self) class HwIOFifoWriter(HwIO): """ FIFO write port interface """ @override def hwConfig(self): self.DATA_WIDTH = HwParam(8) @override def hwDeclr(self): self.en = HwIOSignal() self.wait = HwIOSignal(masterDir=DIRECTION.IN) if self.DATA_WIDTH: self.data = HwIOVectSignal(self.DATA_WIDTH) @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOFifoWriterAgent(sim, self) @override def _getIpCoreIntfClass(self): from hwt.hwIOs.std_ip_defs import IP_FifoWriter return IP_FifoWriter class HwIOFifoReader(HwIO): """ FIFO read port interface """ @override def hwConfig(self): HwIOFifoWriter.hwConfig(self) @override def hwDeclr(self): HwIOFifoWriter.hwDeclr(self) self.en._masterDir = DIRECTION.IN self.wait._masterDir = DIRECTION.OUT @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIOFifoReaderAgent(sim, self) @override def _getIpCoreIntfClass(self): from hwt.hwIOs.std_ip_defs import IP_FifoReader return IP_FifoReader class HwIORegCntrl(HwIO): """ Register control interface, :class:`.HwIOSignal` for read, :class:`.HwIODataVld` for write """ @override def hwConfig(self): self.DATA_WIDTH = HwParam(8) self.USE_IN = HwParam(True) self.USE_OUT = HwParam(True) @override def hwDeclr(self): if self.USE_IN: self.din = HwIOVectSignal(self.DATA_WIDTH, masterDir=DIRECTION.IN) if self.USE_OUT: with self._hwParamsShared(): self.dout = HwIODataVld() @override def _initSimAgent(self, sim: HdlSimulator): self._ag = HwIORegCntrlAgent(sim, self) ================================================ FILE: hwt/hwIOs/std_ip_defs.py ================================================ from typing import List from hwt.hwIOs.std import HwIORst, HwIORst_n, HwIOClk from hwt.pyUtils.arrayQuery import where from hwt.serializer.ip_packager import IpPackager from hwt.hwIO import HwIO from ipCorePackager.component import Component from ipCorePackager.intfIpMeta import IntfIpMeta, VALUE_RESOLVE class IP_Clk(IntfIpMeta): def __init__(self): super().__init__() self.name = "clock" self.version = "1.0" self.vendor = "xilinx.com" self.library = "signal" self.map = 'CLK' def postProcess(self, component: Component, packager: IpPackager, thisIf: HwIOClk): rst = where( component.busInterfaces, lambda intf: (isinstance(intf, (HwIORst_n, HwIORst)) and intf._getAssociatedClk() is thisIf)) logicName = packager.getInterfaceLogicalName rst = list(rst) if rst: if len(rst) > 1: rst = [intf for intf in rst if intf._getAssociatedClk() is thisIf] if len(rst) > 0: raise AssertionError( "Multiple associated resets for this interface", thisIf) elif not rst: raise AssertionError("Multiple reset signals" " but none of them is associated", thisIf) rst = rst[0] self.addSimpleParam(logicName(thisIf), "ASSOCIATED_RESET", logicName(rst), resolve=VALUE_RESOLVE.NONE) else: rst = None associated_intfs = where( component.busInterfaces, lambda intf: (intf is not rst and intf is not self and not isinstance(intf, HwIOClk) and intf._getAssociatedClk() is thisIf)) self.addSimpleParam(logicName(thisIf), "ASSOCIATED_BUSIF", ":".join( map(logicName, associated_intfs)), resolve=VALUE_RESOLVE.NONE) self.addSimpleParam(logicName(thisIf), "FREQ_HZ", str(thisIf.DEFAULT_FREQ), resolve=VALUE_RESOLVE.USER) def asQuartusTcl(self, buff: List[str], version: str, component: Component, packager: IpPackager, thisIf: HwIO): self.quartus_tcl_add_interface(buff, thisIf, packager) name = packager.getInterfacePhysicalName(thisIf) self.quartus_prop(buff, name, "clockRate", 0) self.quartus_add_interface_port(buff, name, thisIf, "clk", packager) class IP_Rst(IntfIpMeta): def __init__(self): super().__init__() self.name = "reset" self.version = "1.0" self.vendor = "xilinx.com" self.library = "signal" self.map = "rst" def postProcess(self, component: Component, packager: IpPackager, thisIf: HwIORst): self.addSimpleParam(packager.getInterfaceLogicalName(thisIf), "POLARITY", "ACTIVE_HIGH") def asQuartusTcl(self, buff: List[str], version: str, component: Component, packager: IpPackager, thisIf: HwIO): self.quartus_tcl_add_interface(buff, thisIf, packager) name = packager.getInterfacePhysicalName(thisIf) # self.quartus_prop("associatedClock", clock) self.quartus_prop(buff, name, "synchronousEdges", "DEASSERT") self.quartus_add_interface_port(buff, name, thisIf, "reset", packager) clk = thisIf._getAssociatedClk() if clk is not None: self.quartus_prop(buff, name, "associatedClock", packager.getInterfacePhysicalName(clk), escapeStr=False) class IP_Rst_n(IP_Rst): def postProcess(self, component: Component, packager: IpPackager, thisIf: HwIORst_n): self.addSimpleParam( packager.getInterfaceLogicalName(thisIf), "POLARITY", "ACTIVE_LOW") def asQuartusTcl(self, buff: List[str], version: str, component: Component, packager: IpPackager, thisIf: HwIO): self.quartus_tcl_add_interface(buff, thisIf, packager) name = packager.getInterfacePhysicalName(thisIf) # [TODO] # self.quartus_prop("associatedClock", clock) self.quartus_prop(buff, name, "synchronousEdges", "DEASSERT") self.quartus_add_interface_port(buff, name, thisIf, "reset_n", packager) clk = thisIf._getAssociatedClk() if clk is not None: self.quartus_prop(buff, name, "associatedClock", packager.getInterfacePhysicalName(clk), escapeStr=False) class IP_Handshake(IntfIpMeta): def __init__(self): super().__init__() self.name = "handshake" self.version = "1.0" self.vendor = "hwt" self.library = "user" self.map = { "vld": "valid", "rd": "ready", "data": "data" } class IP_BlockRamPort(IntfIpMeta): def __init__(self): super().__init__() self.name = "bram" self.version = "1.0" self.vendor = "xilinx.com" self.library = "interface" self.map = { 'addr': "ADDR", "clk": 'CLK', 'din': "DIN", 'dout': "DOUT", 'en': "EN", 'we': "WE", } class IP_FifoReader(IntfIpMeta): def __init__(self): super().__init__() self.name = "fifo_read" self.version = "1.0" self.vendor = "xilinx.com" self.library = "interface" self.map = { 'data': "RD_DATA", "en": 'RD_EN', 'wait': "EMPTY", } class IP_FifoWriter(IntfIpMeta): def __init__(self): super().__init__() self.name = "fifo_write" self.version = "1.0" self.vendor = "xilinx.com" self.library = "interface" self.map = { 'data': "WR_DATA", "en": 'WR_EN', 'wait': "FULL", } ================================================ FILE: hwt/hwIOs/utils.py ================================================ from hwt.constants import NOT_SPECIFIED from hwt.hwIOs.std import HwIOClk, HwIORst_n, HwIORst from hwt.mainBases import HwModuleBase, HwIOBase, HwModuleOrHwIOBase def addClkRstn(obj: HwModuleOrHwIOBase): """ Construct clk, rst_n signal on object (usually HwModule/HwIO instance) * propagate CLK_FREQ to clk.FREQ """ obj.clk = HwIOClk() freq = getattr(obj, "CLK_FREQ", NOT_SPECIFIED) if freq is not NOT_SPECIFIED: obj.clk.FREQ = freq obj.rst_n = HwIORst_n() def addClkRst(obj: HwModuleOrHwIOBase): """ Construct clk, rst signal on object (usually HwModule/HwIO instance) * propagate CLK_FREQ to clk.FREQ """ obj.clk = HwIOClk() freq = getattr(obj, "CLK_FREQ", NOT_SPECIFIED) if freq is not NOT_SPECIFIED: obj.clk.FREQ = freq obj.rst = HwIORst() def _tryConnect(src: HwIOBase, module: HwModuleBase, hwIOName: str): """ Try connect src to interface of specified name on module. Ignore if interface is not present or if it already has driver. """ try: dst = getattr(module, hwIOName) except AttributeError: return if not dst._sig._rtlDrivers: dst(src) return dst def propagateClk(obj: HwModuleOrHwIOBase): """ Propagate "clk" clock signal to all subcomponents """ clk = obj.clk for m in obj._subHwModules: dstClk = _tryConnect(clk, m, 'clk') if dstClk is not None: assert dstClk.FREQ == clk.FREQ, (clk, "->", dstClk, clk.FREQ, dstClk.FREQ) def propagateClkRstn(obj: HwModuleOrHwIOBase): """ Propagate "clk" clock and negative reset "rst_n" signal to all subcomponents """ clk = obj.clk rst_n = obj.rst_n for m in obj._subHwModules: _tryConnect(clk, m, 'clk') _tryConnect(rst_n, m, 'rst_n') _tryConnect(~rst_n, m, 'rst') def propagateClkRst(obj: HwModuleOrHwIOBase): """ Propagate "clk" clock and reset "rst" signal to all subcomponents """ clk = obj.clk rst = obj.rst for m in obj._subHwModules: dstClk = _tryConnect(clk, m, 'clk') if dstClk is not None: assert dstClk.FREQ == clk.FREQ, (clk, "->", dstClk, clk.FREQ, dstClk.FREQ) _tryConnect(~rst, m, 'rst_n') _tryConnect(rst, m, 'rst') def propagateRstn(obj: HwModuleOrHwIOBase): """ Propagate negative reset "rst_n" signal to all subcomponents """ rst_n = obj.rst_n for m in obj._subHwModules: _tryConnect(rst_n, m, 'rst_n') _tryConnect(~rst_n, m, 'rst') def propagateRst(obj: HwModuleOrHwIOBase): """ Propagate reset "rst" signal to all subcomponents """ rst = obj.rst for m in obj._subHwModules: _tryConnect(~rst, m, 'rst_n') _tryConnect(rst, m, 'rst') ================================================ FILE: hwt/hwModule.py ================================================ from copy import copy from natsort.natsort import natsorted from typing import Optional, Union from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._structural import HdlCompInst, HdlModuleDec from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.portItem import HdlPortItem from hwt.mainBases import HwIOBase from hwt.synthesizer.dummyPlatform import DummyPlatform from hwt.synthesizer.exceptions import IntfLvlConfErr from hwt.synthesizer.interfaceLevel.hwModuleImplHelpers import HwModuleImplHelpers, \ _default_param_updater from hwt.synthesizer.interfaceLevel.propDeclrCollector import PropDeclrCollector from hwt.synthesizer.rtlLevel.netlist import RtlNetlist from hwt.synthesizer.typePath import TypePath from ipCorePackager.constants import DIRECTION class HdlConstraintList(list): """ Containers of hw design constraints """ pass class HwModule(PropDeclrCollector, HwModuleImplHelpers): """ Objects of this class are representation of design in hwt HCL. This object is a container of the netlist with interfaces and internal hierarchical structure. :cvar ~._serializeDecision: function to decide if HDL object derived from this unit should be serialized or not, if None all is always serialized :cvar ~._PROTECTED_NAMES: set of names which can not be overridden :ivar ~._hwIOs: all public interfaces :type ~._hwIOs: List[HwIO] :ivar ~._private_hwIOs: all internal interfaces which are not accessible from outside of unit :type _private_hwIOs: List[HwIO] :ivar ~._subHwModules: all units defined on this object :type ~._subHwModules: List[HwModule] :ivar ~._hwParams: all params defined on this object :type ~._hwParams: List[HwParam] :ivar ~._constraints: additional HW specifications :ivar ~._parent: parent object :type ~._parent: Optional[HwModule] :ivar ~._lazy_loaded: container of RTL object which were lazy loaded in implementation phase (this object has to be returned from :func:`~._to_rtl` of parent before it it's own objects) :ivar ~._shared_component_with: Optional tuple of the other :class:`hwt.hwModule.HwModule` instance which produces an exactly same component in HDL and interface signal map current to shared and shared to current :type ~._shared_component_with: Optional[Tuple[HwModule, Dict[Interface, Interface], Dict[Interface, Interface]]] :attention: if :func:`~._shared_component_with` is not None the body of this instance is not generated at all and the component from :func:`~._shared_component_with` is used instead :ivar ~._target_platform: meta-informations about target platform :ivar ~._name: a name of this component :ivar ~._onParentPropertyPath: :see: :class:`HwIO` :ivar ~._hdl_module_name: a name of HDL module for this component (vhdl entity name, Verilog module name) """ _serializeDecision = None # properties which are used internally by this library _PROTECTED_NAMES = { "_PROTECTED_NAMES", "_name", "_hdl_module_name", "_hwIOs", "_private_hwIOs", "_units", "_hwParams", "_parent", "_constraints", "_lazy_loaded", "_rtlCtx", "_shared_component_with", "_target_platform", "_store_manager", } def __init__(self, hdlName:Optional[str]=None): self._parent: Optional[HwModule] = None self._name: Optional[str] = None self._onParentPropertyPath: Optional[TypePath] = None self._shared_component_with = None self._hdl_module_name: Optional[str] = None assert hdlName is None or isinstance(hdlName, str), hdlName self._hdlNameOverride = hdlName self._lazy_loaded: list[Union[HwModule, HwIOBase]] = [] self._rtlCtx = RtlNetlist(self) self._constraints = HdlConstraintList() self._loadConfig() @internal def _loadHwIODeclarations(self, hwIO: HwIOBase, isExtern: bool): hwIO._loadHwDeclarations() hwIO._setAsExtern(isExtern) @internal def _loadHwDeclarations(self): """ Load all declarations from _decl() method, recursively for all interfaces/units. """ if not hasattr(self, "_hwIOs"): self._hwIOs = [] if not hasattr(self, "_private_hwIOs"): self._private_hwIOs = [] if not hasattr(self, "_subHwModules"): self._subHwModules = [] self._setAttrListener = self._declrCollector self.hwDeclr() self._setAttrListener = None for hio in self._hwIOs: self._loadHwIODeclarations(hio, True) # if I am a unit load subunits for u in self._subHwModules: u._loadHwDeclarations() @internal def _registerHwIOInHwImpl(self, hwIOName: str, hwIO: HwIOBase, onParentPath: TypePath): """ Register interface in implementation phase """ self._registerHwIO(hwIOName, hwIO, onParentPath, True) self._loadHwIODeclarations(hwIO, False) hwIO._signalsForHwIO( self._rtlCtx, None, self._store_manager.name_scope) def _getDefaultName(self) -> str: return self.__class__.__name__ def _get_hdl_doc(self) -> Optional[str]: if self.__doc__ is not HwModule.__doc__: return self.__doc__ @internal def _to_rtl(self, target_platform: DummyPlatform, store_manager: "StoreManager", add_param_asserts=True): """ synthesize all subunits, make connections between them, build verilog like module/vhdl like entity and architecture for this unit """ if self._hdl_module_name is None: if self._hdlNameOverride: self._hdl_module_name = self._hdlNameOverride else: self._hdl_module_name = self._getDefaultName() if self._name is None: self._name = self._getDefaultName() self._target_platform = target_platform self._store_manager = store_manager do_serialize_this, replacement = store_manager.filter.do_serialize( self) if replacement is not None: assert not do_serialize_this assert len(self._hwIOs) == len(replacement._hwIOs), \ "No lazy loaded interfaces declared in hwImpl()" copy_HdlModuleDec(replacement, self) yield False, self self._cleanThisSubunitRtlSignals() self._subHwModules = None self._private_hwIOs = None hwIO_map_repl_to_self = sharedCompBuildHwIOMap( replacement, self) hwIO_map_self_to_repl = { v: k for k, v in hwIO_map_repl_to_self.items()} self._shared_component_with = replacement, \ hwIO_map_self_to_repl, hwIO_map_repl_to_self return for proc in target_platform.beforeToRtl: proc(self) mdec = self._rtlCtx.create_HdlModuleDec( self._hdl_module_name, store_manager, self._hwParams) mdec.origin = self mdec.doc = self._get_hdl_doc() # prepare signals for interfaces for hwIO in self._hwIOs: if hwIO._isExtern: ei = self._rtlCtx.hwIOs else: ei = None # we are reversing direction because we are looking # at the interface from inside of component hwIO._signalsForHwIO( self._rtlCtx, ei, store_manager.name_scope, reverse_dir=True) store_manager.hierarchy_pop(mdec) if do_serialize_this: # prepare subunits for sm in self._subHwModules: yield from sm._to_rtl(target_platform, store_manager) # now every sub unit has a HdlModuleDec prepared for sm in self._subHwModules: subHwModuleName = sm._name sm._signalsForSubHwModuleEntity(self._rtlCtx, "sig_" + subHwModuleName) for proc in target_platform.beforeToRtlImpl: proc(self) try: store_manager.hierarchy_push(mdec) if do_serialize_this: self._loadImpl() yield from self._lazy_loaded if not self._rtlCtx.hwIOs: raise IntfLvlConfErr( "Can not find any external interface for unit %s" "- unit without interfaces are not synthesisable" % self._name) for proc in target_platform.afterToRtlImpl: proc(self) mdec.params[:] = natsorted(mdec.params, key=lambda x: x.name) mdec.ports[:] = natsorted(mdec.ports, key=lambda x: x.name) if do_serialize_this: # synthesize signal level context mdef = self._rtlCtx.create_HdlModuleDef( target_platform, store_manager) mdef.origin = self for hwIO in self._hwIOs: if hwIO._isExtern: # reverse because other components # looks at this interface from the outside hwIO._reverseDirection() if do_serialize_this: if add_param_asserts and self._hwParams: mdef.objs.extend(store_manager.as_hdl_ast._as_hdl_HdlModuleDef_param_asserts(mdec)) store_manager.write(mdef) yield True, self # after synthesis clean up interface so this :class:`hwt.hwModule.HwModule` object can be # used elsewhere self._cleanThisSubunitRtlSignals() if do_serialize_this: self._checkCompInstances() for proc in target_platform.afterToRtl: proc(self) finally: store_manager.hierarchy_pop(mdec) def _updateHwParamsFrom(self, otherObj: PropDeclrCollector, updater=_default_param_updater, exclude: Optional[tuple[set[str], set[str]]]=None, prefix=""): """ :note: doc in :func:`hwt.synthesizer.interfaceLevel.propDeclCollector._updateHwParamsFrom` """ return PropDeclrCollector._updateHwParamsFrom(self, otherObj, updater, exclude, prefix) @internal def _checkCompInstances(self): cInstances = [o for o in self._rtlCtx.hwModDef.objs if isinstance(o, HdlCompInst)] cInst_cnt = len(cInstances) unit_cnt = len(self._subHwModules) if cInst_cnt != unit_cnt: # resolve the error message inRtl = set(x.name for x in cInstances) inHwIO = set(x._name for x in self._subHwModules) cls_name = self.__class__.__name__ if cInst_cnt > unit_cnt: diff = inRtl - inHwIO raise IntfLvlConfErr( f"{cls_name:s}, {self._name:s}: unit(s) were found in HDL but were" f" not registered {diff}") else: assert cInst_cnt < unit_cnt diff = inHwIO - inRtl raise IntfLvlConfErr( f"{cls_name:s}, {self._name:s}: _to_rtl: unit(s) are missing in produced HDL {diff}") def copy_HdlModuleDec_HwIO(orig_io: HwIOBase, new_io: HwIOBase, ports: list[HdlPortItem], new_m: HwModule): new_io._direction = orig_io._direction if orig_io._hdlPort is not None: s = orig_io._sigInside assert s is not None, ( "the component which shares a body with this component" " is actually some parent of this component") pi = copy(orig_io._hdlPort) pi.module = new_m ports.append(pi) d = pi.direction if d == DIRECTION.OUT: pi.dst = None elif d == DIRECTION.IN: pi.src = None else: raise NotImplementedError(d) new_io._hdlPort = pi new_io._sigInside = s elif isinstance(orig_io, HObjList): for hwIO, n_i in zip(orig_io, new_io): copy_HdlModuleDec_HwIO(hwIO, n_i, ports, new_m) else: for hwIO in orig_io._hwIOs: n_i = getattr(new_io, hwIO._name) copy_HdlModuleDec_HwIO(hwIO, n_i, ports, new_m) def copy_HdlModuleDec(orig_m: HwModule, new_m: HwModule): """ Copy the HdlModuleDec for HwModule from existing HwModule. This is used when the HwModule implementation was resolved as shared with some other already existing HwModule. (Happens when the body is the same and we want to avoid transpilling the same code again.) """ assert not new_m._rtlCtx.statements assert not new_m._rtlCtx.hwIOs assert not new_m._rtlCtx.signals assert new_m._rtlCtx.hwModDec is None new_m._hdl_module_name = orig_m._hdl_module_name hwModDec = new_m._rtlCtx.hwModDec = copy(orig_m._rtlCtx.hwModDec) hwModDec: HdlModuleDec params = [] param_by_name = {p._name: p for p in new_m._hwParams} for p in hwModDec.params: p: HdlIdDef new_p_def = copy(p) old_p = new_p_def.origin = param_by_name[p.origin._name] old_p._name = p.origin._name new_p_def.value = old_p.get_hdl_value() params.append(new_p_def) hwModDec.params = params hwModDec.ports = [] for hwO, hwI in zip(orig_m._hwIOs, new_m._hwIOs): if hwO._isExtern: copy_HdlModuleDec_HwIO(hwO, hwI, hwModDec.ports, new_m) # params should be already sorted # hwModDec.params[:] = natsorted(hwModDec.params, key=lambda x: x.name) hwModDec.ports[:] = natsorted(hwModDec.ports, key=lambda x: x.name) def _sharedCompBuildHwIOMapList(replacement: list[HwIOBase], substituted: list[HwIOBase], res: dict[HwIOBase, HwIOBase]): assert len(replacement) == len(substituted) for r, s in zip(replacement, substituted): assert r._name == s._name, (r._name, s._name) res[r] = s if r._hwIOs: _sharedCompBuildHwIOMapList( r._hwIOs, s._hwIOs, res) def sharedCompBuildHwIOMap(replacement_m: HwModule, substituted_m: HwModule): """ Build a dictionary which maps interface of replacement_m to interface of substituted_m """ res = {} _sharedCompBuildHwIOMapList( replacement_m._hwIOs, substituted_m._hwIOs, res) return res ================================================ FILE: hwt/hwParam.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from hwt.hdl.types.defs import INT, STR, BOOL, FLOAT64 from hwt.hdl.const import HConst class HwParam(): """ Class used to mark object as a configuration of HDL module. ( The parameter instance will not appear on :class:`hwt.hwModule.HwModule` instance, instead the value will appear. The parameter instance will be stored in ._hwParams property of HwModule/HwIO object) :ivar ~._initval: value of the parameter which should be used for initialization :attention: the actual value is then store on parent object instance :ivar ~._name: name of parameter on parent HwModule/HwIO instance :ivar ~._parent: parent object instance """ __slots__ = ["_initval", "_name", "_hdlName", "_parent"] def __init__(self, initval): self._initval = initval self._name = None self._hdlName = None self._parent: "PropDeclrCollector" = None def get_hdl_type(self): v = self.get_value() INT32_MAX = 2 ** (32 - 1) - 1 INT32_MIN = -2 ** (32 - 1) if isinstance(v, HConst): return v._dtype elif isinstance(v, bool): return BOOL elif isinstance(v, str): return STR elif isinstance(v, int) and v >= INT32_MIN and v <= INT32_MAX: return INT elif isinstance(v, float): return FLOAT64 else: return None def get_hdl_value(self): t = self.get_hdl_type() v = self.get_value() if t is None: t = STR v = t.from_py(str(v)) else: if not isinstance(v, HConst): v = t.from_py(v) return v def get_value(self): return getattr(self._parent, self._name) def set_value(self, v): setattr(self._parent, self._name, v) def __repr__(self): return "<%s at 0x%x %s=%s>" % ( self.__class__.__name__, id(self), "" if self._name is None else self._name, repr(self.get_value())) ================================================ FILE: hwt/mainBases.py ================================================ from typing import TypeVar, Generic, Union from hwt.hdl.types.hdlType import HdlType T = TypeVar("T", bound=HdlType) class RtlSignalBase(Generic[T]): """ Main base class for all rtl signals """ pass class HwIOBase(): """ Main base class for all interfaces """ pass class HwModuleBase(): """ Main base class for all units """ pass HwModuleOrHwIOBase = Union[HwModuleBase, HwIOBase] ================================================ FILE: hwt/math.py ================================================ from copy import copy import math from typing import List, Union from hwt.doc_markers import hwt_expr_producer from hwt.hdl.const import HConst from hwt.hdl.types.bits import HBits from hwt.hdl.types.hdlType import HdlType from hwt.mainBases import HwIOBase from hwt.mainBases import RtlSignalBase from pyMathBitPrecise.bit_utils import mask AnyHValue = Union[HConst, RtlSignalBase, HwIOBase] def inRange(n: Union[int, AnyHValue], start: Union[int, AnyHValue], end: Union[int, AnyHValue]): """ Check if n is in range = start) if isinstance(n, (RtlSignalBase, HwIOBase, HConst)) and\ (not isinstance(end, int) or end < 2 ** n._dtype.bit_length()): res = res & (n < end) return res def toPow2Ceil(x: int): """ Get the smallest 2**N where 2**N >= x """ i = 0 while 2 ** i < x: i += 1 return 2 ** i def toPow2Floor(n: int): """ Get the largest 2**N where 2**N <= x """ if n < 1: return 0 exponent = int(math.log2(n)) return 2 ** exponent def addressAlignBestEffort(record_width: int, bus_data_width: int): """ Optionally extend the record width to be power of 2 and to consume smallest amount of memory possible. """ if 2 * record_width <= bus_data_width: # multiple records in bus data word records_in_bus_word = math.floor(bus_data_width / record_width) record_width = bus_data_width // records_in_bus_word bus_words_in_record = 1 else: # record in a 1+ bus data words bus_words_in_record = math.ceil(record_width / bus_data_width) # in order to make item indexable we need to have size which is 2**X bus_words_in_record = toPow2Ceil(bus_words_in_record) record_width = bus_data_width * bus_words_in_record records_in_bus_word = 1 return record_width, records_in_bus_word, bus_words_in_record def log2ceil(x: Union[int, float]): """ Returns no of bits required to store x-1 for example x=8 returns 3 """ if not isinstance(x, (int, float)): x = int(x) if x == 0 or x == 1: res = 1 else: res = math.ceil(math.log2(x)) return res def isPow2(num: Union[int, float]) -> bool: """ Check if number or constant is power of two """ if not isinstance(num, int): num = int(num) return num != 0 and ((num & (num - 1)) == 0) def sizeof(_type: HdlType) -> int: "get size of type in bytes" s = _type.bit_length() return math.ceil(s / 8) def shiftIntArray(values: List[Union[int, "HBitsConst"]], item_width: int, shift: int): """ :param values: array of values which will be shifted as a whole :param item_width: a bit length of a single item in array :param shift: specifies how many bits the array should be shifted, << is a positive shift, >> is a negative shift """ if shift == 0: return copy(values) new_v = [] t = HBits(item_width) if shift > 0: # << for _ in range(shift // item_width): new_v.append(None) prev = None for v in values: if v is None and prev is None: _v = None else: if prev is None: prev = t.from_py(None) if v is None: v = t.from_py(None) elif isinstance(prev, HConst) and not isinstance(v, HConst): v = t.from_py(v) _v = (v << shift) | (prev >> (item_width - shift)) new_v.append(_v) prev = v if prev is None: v = None else: v = prev >> (item_width - shift) new_v.append(v) else: # shift < 0, >> nextIt = iter(values) for v in values: try: nv = next(nextIt) except StopIteration: nv = None if nv is None and v is None: _v = None else: if nv is None: nv = t.from_py(None) if v is None: v = t.from_py(None) _v = (v >> shift) | (nv & mask(shift)) new_v.append(_v) return new_v @hwt_expr_producer def hMin(a: AnyHValue, b: AnyHValue): c = a < b if isinstance(c, bool): return a if c else b else: return c._ternary(a, b) @hwt_expr_producer def hMax(a: AnyHValue, b: AnyHValue): c = a > b if isinstance(c, bool): return a if c else b else: return c._ternary(a, b) ================================================ FILE: hwt/mathAutoExt.py ================================================ from operator import add, sub from typing import Callable, Optional from hwt.code import Concat from hwt.doc_markers import hwt_expr_producer from hwt.hdl.types.bitConstFunctions import AnyHBitsValue @hwt_expr_producer def extendForNoOverflowAddSub(a: AnyHBitsValue, b: AnyHBitsValue): """ Increase bitwidth of two variables so overflow can not happen during add/sub """ aSigned = bool(a._dtype.signed) bSigned = bool(b._dtype.signed) resWidth = max(a._dtype.bit_length(), b._dtype.bit_length()) + \ 1 + (1 if aSigned != bSigned else 0) a = a._ext(resWidth) b = b._ext(resWidth) if aSigned != bSigned: if not aSigned: a = a._cast_sign(True) if not bSigned: b = b._cast_sign(True) return a, b @hwt_expr_producer def addAutoExt(a: AnyHBitsValue, b: AnyHBitsValue): a, b = extendForNoOverflowAddSub(a, b) return a + b @hwt_expr_producer def addAutoExtMany(*ops: tuple[AnyHBitsValue, ...]): a = ops[0] for b in ops[1:]: a, b = extendForNoOverflowAddSub(a, b) a = a + b return a @hwt_expr_producer def subAutoExt(a: AnyHBitsValue, b: AnyHBitsValue): a, b = extendForNoOverflowAddSub(a, b) return a - b @hwt_expr_producer def addShifted(a: AnyHBitsValue, b: AnyHBitsValue, bShift: int, maxResultWidth:Optional[int]=None, addSubFn:Callable[[AnyHBitsValue, AnyHBitsValue], AnyHBitsValue]=add) -> AnyHBitsValue: """ perform a + (b< bShift, ("sanity check that value is not fully shifted out", maxResultWidth, bShift) if maxResultWidth <= bShift: return a._extOrTrunc(maxResultWidth)._cast_sign(isSigned) if bShift > 0: widthL = a._dtype.bit_length() if bShift >= widthL: # b is shifted entirely before a result.append(a._ext(bShift)) if maxResultWidth is not None: b = b._extOrTrunc(maxResultWidth - bShift) result.append(b) return Concat(*reversed(result))._cast_sign(isSigned) else: lowBits = a[bShift:] result.append(lowBits) a = a[:bShift] else: assert bShift == 0, bShift widthL = a._dtype.bit_length() widthR = b._dtype.bit_length() if maxResultWidth is not None: # trim a, b if it exceeds the result width (after offset is applied) w = maxResultWidth - bShift if widthL > w: a = a[w:] widthL = w if widthR > w: b = b[w:] widthR = w numWidth = max(widthL, widthR) + 1 + int(aIsSigned != bIsSigned) if maxResultWidth is not None: numWidth = min(numWidth, maxResultWidth - bShift) if widthL < numWidth: a = a._ext(numWidth) if widthR < numWidth: b = b._ext(numWidth) result.append(addSubFn(a._cast_sign(isSigned), b._cast_sign(isSigned))) res = Concat(*reversed(result)) if maxResultWidth is not None: assert res._dtype.bit_length() <= maxResultWidth return res._cast_sign(isSigned) @hwt_expr_producer def subShifted(a: AnyHBitsValue, b: AnyHBitsValue, bShift: int, maxResultWidth:Optional[int]=None) -> AnyHBitsValue: return addShifted(a, b, bShift, maxResultWidth, sub) @hwt_expr_producer def addShiftedMany(ops:tuple[tuple[AnyHBitsValue, int]], maxResultWidth:Optional[int]=None, addSubFn:Callable[[AnyHBitsValue, AnyHBitsValue], AnyHBitsValue]=add) -> AnyHBitsValue: a, sh = ops[0] assert sh == 0 for b, bShift in ops[1:]: a = addShifted(a, b, bShift, maxResultWidth, addSubFn) return a @hwt_expr_producer def mulFullWidth(a: AnyHBitsValue, b: AnyHBitsValue) -> AnyHBitsValue: w = a._dtype.bit_length() + b._dtype.bit_length() return a._ext(w) * b._ext(w) ================================================ FILE: hwt/pyUtils/__init__.py ================================================ """ This package contains python utils used in this library. """ ================================================ FILE: hwt/pyUtils/arrayQuery.py ================================================ # select = map, groupBy = itertools.groupby from collections import deque from itertools import zip_longest from math import inf from types import GeneratorType from hdlConvertorAst.to.hdlUtils import iter_with_last from hwt.constants import NOT_SPECIFIED from typing import Sequence class DuplicitValueExc(Exception): """ Exception which means that there are multiple items which this query selected but it should return only one """ class NoValueExc(Exception): """ Exception which means that query did not selected any item """ def single(iterable, fn): """ Get value from iterable where fn(item) and check if there is not fn(other item) :raise DuplicitValueExc: when there are multiple items satisfying fn() :raise NoValueExc: when no value satisfying fn(item) found """ found = False ret = None for i in iterable: if fn(i): if found: raise DuplicitValueExc(i) found = True ret = i if not found: raise NoValueExc() return ret def arr_any(iterable, fn): """ :return: True if fn(item) for any item else False """ for item in iterable: if fn(item): return True return False def arr_all(iterable, fn): """ :return: True if fn(item) for all items in interable or iterable is empty else False """ for item in iterable: if not fn(item): return False return True def take(iterrable, howMay): """ :return: generator of first n items from iterrable """ assert howMay >= 0 if not howMay: return last = howMay - 1 for i, item in enumerate(iterrable): yield item if i == last: return def where(iterable, fn): """ :return: generator of items from iterable where fn(item) """ for i in iterable: if fn(i): yield i def groupedby(collection, fn): """ same like itertools.groupby :note: This function does not needs initial sorting like itertools.groupby :attention: Order of pairs is not deterministic. """ d = {} for item in collection: k = fn(item) try: arr = d[k] except KeyError: arr = [] d[k] = arr arr.append(item) yield from d.items() def flatten(iterables, level=inf): """ Flatten nested lists, tuples, generators and maps :param level: maximum depth of flattening """ if level >= 0 and isinstance(iterables, (list, tuple, GeneratorType, map, zip, set, deque)): level -= 1 for i in iterables: yield from flatten(i, level=level) else: yield iterables def grouper(n: int, iterable: Sequence, padvalue=None): """grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x') """ return zip_longest(*[iter(iterable)] * n, fillvalue=padvalue) def areSetsIntersets(setA: set, setB: set): """ Check if intersection of sets is not empty """ return any(x in setA for x in setB) def balanced_reduce(arr: Sequence, opFn): while len(arr) > 1: nextArr = [] for a, b in grouper(2, arr, NOT_SPECIFIED): if b is NOT_SPECIFIED: # if number of items was odd we have 1 leftover nextArr.append(a) else: nextArr.append(opFn(a, b)) arr = nextArr return arr[0] ================================================ FILE: hwt/pyUtils/fileHelpers.py ================================================ import fnmatch import os def find_files(directory, pattern, recursive=True): """ Find files by pattern in directory """ if not os.path.isdir(directory): if os.path.exists(directory): raise IOError(directory + ' is not directory') else: raise IOError(directory + " does not exists") if recursive: for root, _, files in os.walk(directory): for basename in files: if fnmatch.fnmatch(basename, pattern): filename = os.path.join(root, basename) yield filename else: root = directory for basename in os.listdir(root): if fnmatch.fnmatch(basename, pattern): filename = os.path.join(root, basename) if os.path.isfile(filename): yield filename ================================================ FILE: hwt/pyUtils/setList.py ================================================ from typing import Generic, TypeVar, Set, Sequence, Optional, Union T = TypeVar('T') class SetList(Generic[T], list): """ List of unique items """ __slots__ = ["__s"] def __init__(self, initSeq: Optional[Sequence[T]]=None): super(SetList, self).__init__() self.__s: Set[T] = set() if initSeq is not None: for item in initSeq: self.append(item) def append(self, item: T) -> bool: """ :return: True if the item was newly added """ if item in self.__s: return False else: self.__s.add(item) list.append(self, item) return True def extend(self, items: Sequence[T]): if self is items: return # will not add any item because all items are already there for item in items: self.append(item) def insert(self, i: int, x: T): super(SetList, self).insert(i, x) self.__s.add(x) def _get_set(self) -> Set[T]: return self.__s def intersection_set(self, other: Set[T]): return self.__s.intersection(other._get_set()) def discard(self, item: T) -> bool: """ :return: True if the item was previously in this list """ if item in self.__s: self.remove(item) return True else: return False def remove(self, item: T): self.__s.remove(item) return list.remove(self, item) def pop(self, *args, **kwargs) -> T: item = list.pop(self, *args, **kwargs) self.__s.remove(item) return item def clear(self): list.clear(self) self.__s.clear() def copy(self): c = SetList() c.extend(self) return c def __setitem__(self, i: Union[int, slice], v: Union[T, Sequence[T]]): if isinstance(i, slice): if i.start is None and i.step is None and i.stop is None: self.clear() self.extend(v) else: for item in self[i]: self.__s.remove(item) v = SetList(v) list.__setitem__(self, i, v) self.__s.update(v) else: assert isinstance(i, int) cur = self[i] self.__s.remove(cur) list.__setitem__(self, i, v) self.__s.add(v) def __copy__(self): return self.copy() def __contains__(self, key) -> bool: return key in self.__s ================================================ FILE: hwt/pyUtils/testUtils.py ================================================ from itertools import product # [todo] https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests class TestMatrix(): """ Class which instance is a decorator which executes unittest.TestCase test method with every combination of argumets """ def __init__(self, *args, **kwargs): """ :note: args, kwargs are lists of arguments which should be passed as a test arguments """ self.args = args kwargs = sorted(kwargs.items(), key=lambda x: x[0]) self.kwargs_keys = [x[0] for x in kwargs] self.kwargs_values = [x[1] for x in kwargs] self.test_arg_values = list(product(*args, *self.kwargs_values)) def split_args_kwargs(self, args): kwargs_cnt = len(self.kwargs_keys) if kwargs_cnt: _args = args[:kwargs_cnt] kwargs = {k: v for k, v in zip( self.kwargs_keys, args[:kwargs_cnt])} return _args, kwargs else: return args, {} def __call__(self, test_fn): test_matrix = self def test_wrap(self): for args in test_matrix.test_arg_values: args, kwargs = test_matrix.split_args_kwargs(args) try: test_fn(self, *args, **kwargs) except Exception as e: # add note to an exception about which test arguments were # used # import traceback # traceback.print_exc() msg_buff = [] for a in args: msg_buff.append(repr(a)) for k in test_matrix.kwargs_keys: msg_buff.append("%s=%r" % (k, kwargs[k])) raise Exception( "Test failed %s" % (", ".join(msg_buff)), ) from e return test_wrap ================================================ FILE: hwt/pyUtils/typingFuture.py ================================================ import inspect import re RE_CLASS_HEADER = re.compile(r'class.+\(([^,]*)(?:,\s*?(([^,]*)))*\)\s*\:') # match comma separated IDs of base classes ENABLE_CHECKING = False def override(method): """ Check that the method overrides the parent method. A temporal supplement for typing.override from python3.12 Inspired by https://stackoverflow.com/questions/1167617/in-python-how-do-i-indicate-im-overriding-a-method """ if ENABLE_CHECKING: # stack()[0]=overrides, stack()[1]=inside class def'n, stack()[2]=outside class def'n stack = inspect.stack()[2] lineWithHeaderOfCurrentlyDefinedClass = stack[4][0] base_classes = RE_CLASS_HEADER.search(lineWithHeaderOfCurrentlyDefinedClass) if not base_classes: raise AssertionError("Can not detect base classes for checking if method overrides", lineWithHeaderOfCurrentlyDefinedClass) base_classes = base_classes.group(1) # handle multiple inheritance if not base_classes: raise ValueError('overrides decorator: unable to determine base class', lineWithHeaderOfCurrentlyDefinedClass) derived_class_locals = stack[0].f_locals for base_class in base_classes.split(','): base_class = base_class.strip() if '.' not in base_class: base_class = derived_class_locals[base_class] else: components = base_class.split('.') # obj is either a module or a class obj = derived_class_locals[components[0]] for c in components[1:]: assert(inspect.ismodule(obj) or inspect.isclass(obj)) obj = getattr(obj, c) base_class = obj if hasattr(base_class, method.__name__): return method # successfully found the method which is being overridden raise AssertionError( f"Does not override because any base class does not have this method ({method.__name__:s})") else: return method ================================================ FILE: hwt/serializer/__init__.py ================================================ """ This package contains serializers. Purpose of serializer class is to convert hwt representations of designed architecture to hdlConvertorAst to convert it to target language (VHDL/Verilog/SystemC...+xdc/ucf/...). Rather than using serializer classes manually it is recommended to use :func:`hwt.synth.to_rtl` The serialization process is usually a destructive operation as parts of AST can be rewritten to fit target language. """ ================================================ FILE: hwt/serializer/combLoopAnalyzer/__init__.py ================================================ from copy import copy from itertools import chain from typing import Tuple from hdlConvertorAst.hdlAst._bases import iHdlStatement from hdlConvertorAst.hdlAst._statements import HdlStmBlock from hdlConvertorAst.hdlAst._structural import HdlModuleDef, HdlCompInst from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.serializer.combLoopAnalyzer.tarjan import StronglyConnectedComponentSearchTarjan from hwt.serializer.resourceAnalyzer.analyzer import ResourceAnalyzer from hwt.synthesizer.componentPath import ComponentPath from hwt.hwModule import HwModule from ipCorePackager.constants import DIRECTION def collect_comb_inputs(ctx, seen, input_signal, comb_inputs): if input_signal not in seen: seen.add(input_signal) input_signal._walk_sensitivity(comb_inputs, seen, ctx) def collect_comb_drivers(path_prefix: Tuple[HwModule, ...], stm: iHdlStatement, comb_connection_matrix: dict, comb_inputs: tuple): if isinstance(stm, HdlAssignmentContainer): ctx = SensitivityCtx() seen = set() # merge condition inputs to current_comb_inputs current_comb_inputs = set(comb_inputs) for inp in stm._inputs: collect_comb_inputs(ctx, seen, inp, current_comb_inputs) for o in stm._outputs: o_key = path_prefix / o for i in current_comb_inputs: con = comb_connection_matrix.setdefault(path_prefix / i, set()) con.add(o_key) elif isinstance(stm, IfContainer): current_comb_inputs = set(comb_inputs) # intended copy elifs = ((stm.cond, stm.ifTrue), *stm.elIfs) ev_dep_branch = stm._event_dependent_from_branch ctx = SensitivityCtx() seen = set() found_event_dep = False for branch_i, (cond, stms) in enumerate(elifs): # [TODO] check if this works for clock gating if ev_dep_branch is not None and ev_dep_branch == branch_i: found_event_dep = True break collect_comb_inputs(ctx, seen, cond, current_comb_inputs) for sub_stm in stms: collect_comb_drivers(path_prefix, sub_stm, comb_connection_matrix, current_comb_inputs) if not found_event_dep and stm.ifFalse is not None: for sub_stm in stms: collect_comb_drivers(path_prefix, sub_stm, comb_connection_matrix, current_comb_inputs) elif isinstance(stm, HdlStmCodeBlockContainer): for sub_stm in stm.statements: collect_comb_drivers(path_prefix, sub_stm, comb_connection_matrix, comb_inputs) elif isinstance(stm, SwitchContainer): current_comb_inputs = set(comb_inputs) ctx = SensitivityCtx() seen = set() collect_comb_inputs(ctx, seen, stm.switchOn, current_comb_inputs) cases = stm.cases if stm.default is not None: cases = chain(cases, ((None, stm.default),)) for (_, case_stms) in cases: for sub_stm in case_stms: collect_comb_drivers(path_prefix, sub_stm, comb_connection_matrix, current_comb_inputs) else: raise NotImplementedError(stm) class CombLoopAnalyzer(): """ Visitor which can walk synthetized hwt :class:`hwt.hwModule.HwModule` instances and detect clusters connected by combinational logic """ def __init__(self): # RtlSignal: Set[RtlSignal] self.comb_connection_matrix = {} # RtlSignal: set of signals in comb loop self._report = {} self.actual_path_prefix = ComponentPath() def visit_HwModule(self, m: HwModule): if m._shared_component_with is None: arch = m._rtlCtx.hwModDef else: _m, _, _ = m._shared_component_with arch = _m._rtlCtx.hwModDef assert arch is not None, m self.visit_HdlModuleDef(arch) def visit_HdlModuleDef(self, m: HdlModuleDef) -> None: ResourceAnalyzer.visit_HdlModuleDef(self, m) def visit_HdlStmCodeBlockContainer(self, proc: HdlStmCodeBlockContainer) -> None: collect_comb_drivers(self.actual_path_prefix, proc, self.comb_connection_matrix, tuple()) def visit_HdlCompInst(self, o: HdlCompInst) -> None: orig_path_prefix = self.actual_path_prefix in_component_path_prefix = orig_path_prefix if o.origin._shared_component_with is not None: in_component_path_prefix = in_component_path_prefix / o.origin try: assert o.origin, o self.actual_path_prefix = in_component_path_prefix self.visit_HwModule(o.origin) for pm in o.port_map: if pm.direction == DIRECTION.OUT: k = in_component_path_prefix / pm.src v = orig_path_prefix / pm.dst elif pm.direction == DIRECTION.IN: k = orig_path_prefix / pm.src v = in_component_path_prefix / pm.dst else: raise NotImplementedError(pm.direction) self.comb_connection_matrix.setdefault(k, set()).add(v) finally: self.actual_path_prefix = orig_path_prefix def report(self): scc_search = StronglyConnectedComponentSearchTarjan(self.comb_connection_matrix) for scc in scc_search.search_strongly_connected_components(): if len(scc) > 1: yield scc ================================================ FILE: hwt/serializer/combLoopAnalyzer/tarjan.py ================================================ from typing import Generator def _tarjan_head(index, lowlink, S, S_set, T, g, v): lowlink[v] = index[v] = len(index) S.append(v) S_set.add(v) it = iter(g.get(v, ())) T.append((it, False, v, None)) class StronglyConnectedComponentSearchTarjan(): """ Tarjan's strongly connected component search graph algorithm for DAGs based on https://github.com/bwesterb/py-tarjan """ def __init__(self, g: dict): """ :param g: graph represented as a dictionary { : }. :note: vertex type does not matter but it has to be hashable (implement __eq__ and __hash__) """ self.g = g def search_strongly_connected_components(self) -> Generator: """ yields the strongly connected components of the graph in a topological order. """ g = self.g # the graph S = [] # The main stack of the alg. S_set = set() # == set(S) for performance index = {} # { v : } lowlink = {} # { v : } T = [] # stack to replace recursion main_iter = iter(g) for v in main_iter: if v not in index: _tarjan_head(index, lowlink, S, S_set, T, g, v) while T: it, inside, v, w = T.pop() if inside: lowlink[v] = min(lowlink[w], lowlink[v]) body_break = False for w in it: if w not in index: T.append((it, True, v, w)) _tarjan_head(index, lowlink, S, S_set, T, g, w) body_break = True break if w in S_set: lowlink[v] = min(lowlink[v], index[w]) if body_break: continue if lowlink[v] == index[v]: scc = [] w = None while v != w: w = S.pop() scc.append(w) S_set.remove(w) yield scc ================================================ FILE: hwt/serializer/exceptions.py ================================================ class SerializerException(Exception): pass class UnsupportedEventOpErr(SerializerException): """ Target HDL can not use event operator in this context, it usually has to be replaced by correct expression of sensitivity list """ ================================================ FILE: hwt/serializer/generic/__init__.py ================================================ """ This package contains common parts of serializers. """ ================================================ FILE: hwt/serializer/generic/constant_cache.py ================================================ from hwt.hdl.const import HConst from hwt.serializer.generic.tmpVarConstructor import TmpVarConstructor from hwt.mainBases import RtlSignalBase class ConstantCache(object): """ Container of constants for serializer. Used to extract constants as constant variables. """ def __init__(self, toHdlAst, tmpVars: TmpVarConstructor): self.tmpVars = tmpVars self.toHdlAst = toHdlAst # {value:usedName} self._cache = {} def extract_const_val_as_const_var(self, val: HConst) -> RtlSignalBase: """ Create a constant variable with a value specified or use existing variable with same value """ try: return self._cache[val] except KeyError: if isinstance(val.val, int): if val.val < 0: name = "const_m%d_" % -val.val else: name = f"const_{val.val:d}_" else: name = "const_" toHdlAst = self.toHdlAst cc = toHdlAst.constCache try: # dissable const cache as the value is beeing extracted # and we want to prevent recursion toHdlAst.constCache = None c = self.tmpVars.create_var(name, val._dtype, def_val=val, const=True) finally: toHdlAst.constCache = cc self._cache[val] = c return c ================================================ FILE: hwt/serializer/generic/indent.py ================================================ _indent = " " _indentCache = {} def getIndent(indentNum: int): """ Cached indent getter function """ try: return _indentCache[indentNum] except KeyError: i = "".join([_indent for _ in range(indentNum)]) _indentCache[indentNum] = i return i ================================================ FILE: hwt/serializer/generic/ops.py ================================================ from hdlConvertorAst.hdlAst._expr import HdlOpType from hwt.hdl.operatorDefs import HwtOps HWT_TO_HDLCONVERTOR_OPS = { **{op: getattr(HdlOpType, op.id) for op in [ HwtOps.AND, HwtOps.OR, HwtOps.XOR, HwtOps.CONCAT, HwtOps.DIV, HwtOps.DOWNTO, HwtOps.TO, HwtOps.EQ, HwtOps.GT, HwtOps.GE, HwtOps.LE, HwtOps.POW, HwtOps.LT, HwtOps.SUB, HwtOps.MUL, HwtOps.NE, HwtOps.ADD, HwtOps.TERNARY, ]}, HwtOps.UDIV: HdlOpType.DIV, HwtOps.SDIV: HdlOpType.DIV, HwtOps.ULE: HdlOpType.LE, HwtOps.ULT: HdlOpType.LT, HwtOps.UGT: HdlOpType.GT, HwtOps.UGE: HdlOpType.GE, HwtOps.SLE: HdlOpType.LE, HwtOps.SLT: HdlOpType.LT, HwtOps.SGT: HdlOpType.GT, HwtOps.SGE: HdlOpType.GE, HwtOps.NOT: HdlOpType.NEG, HwtOps.MINUS_UNARY: HdlOpType.MINUS_UNARY, HwtOps.RISING_EDGE: HdlOpType.RISING, HwtOps.FALLING_EDGE: HdlOpType.FALLING, HwtOps.CALL: HdlOpType.CALL, HwtOps.DOT: HdlOpType.DOT, } ================================================ FILE: hwt/serializer/generic/tmpVarConstructor.py ================================================ from typing import Optional, Union, Tuple from hdlConvertorAst.hdlAst import HdlIdDef from hdlConvertorAst.translate.common.name_scope import NameScope from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.types.hdlType import HdlType from hwt.hdl.const import HConst from hwt.mainBases import RtlSignalBase from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class TmpVarConstructor(): def __init__(self, toHdlAst, name_scope: NameScope): self.toHdlAst = toHdlAst self.name_scope = name_scope self.extraVarsHdl = [] self.cache = {} def create_var_cached(self, suggestedName: str, dtype: HdlType, const=False, def_val: Optional[Union[RtlSignalBase, HConst]]=None, postponed_init=False, extra_args=None) -> Tuple[bool, RtlSignal]: cache_k = (suggestedName, dtype, const, def_val, extra_args) try: return False, self.cache[cache_k] except KeyError: pass v = self.create_var(suggestedName, dtype, const=const, def_val=def_val, postponed_init=postponed_init) self.cache[cache_k] = v return True, v def create_var(self, suggestedName: str, dtype: HdlType, const=False, def_val: Optional[Union[RtlSignalBase, HConst]]=None, postponed_init=False) -> RtlSignal: # create a new tmp variable in current process s: RtlSignal = dtype.getRtlSignalCls()(None, None, dtype, virtual_only=True) s._name = self.name_scope.checked_name(suggestedName, s) s._isUnnamedExpr = False s._const = const if def_val is not None: s.def_val = def_val if not (isinstance(def_val, RtlSignalBase) and not def_val._const): s._set_def_val() if not postponed_init: self.finish_var_init(s) return s def finish_var_init(self, var: RtlSignal): hdl = self.extraVarsHdl if isinstance(var.def_val, RtlSignalBase) or var.def_val.vld_mask: a = HdlAssignmentContainer(var.def_val, var, virtual_only=True) hdl.append(self.toHdlAst.as_hdl_HdlAssignmentContainer(a)) else: assert var._const or var._rtlDrivers, (var, var.def_val) as_hdl = self.toHdlAst.as_hdl_HdlSignalItem(var, declaration=True) for d in var._rtlDrivers: hdl.append(self.toHdlAst.as_hdl(d)) hdl.append(as_hdl) def sort_hdl_declarations_first(self): self.extraVarsHdl.sort(key=lambda x: not isinstance(x, HdlIdDef)) class TmpVarNotConstructableError(NotImplementedError): pass class NoTmpVars(): def create_var_cached(self, suggestedName, dtype, *args, **kwargs): raise TmpVarNotConstructableError( "Can not create a tmp variable (%s of type %r) in this code section" % (suggestedName, dtype)) def create_cached(self, suggestedName, dtype, *args, **kwargs): raise TmpVarNotConstructableError( "Can not create a tmp variable (%s of type %r) in this code section" % (suggestedName, dtype)) ================================================ FILE: hwt/serializer/generic/to_hdl_ast.py ================================================ from copy import copy, deepcopy from typing import Optional, List, Union from hdlConvertorAst.hdlAst import iHdlStatement, iHdlObj, HdlIdDef, \ HdlValueId, HdlTypeType, iHdlExpr, HdlStmBlock, HdlStmIf, HdlStmCase, \ HdlStmProcess, HdlStmAssign, HdlModuleDef, HdlModuleDec, \ HdlCompInst, HdlEnumDef, HdlOp from hdlConvertorAst.hdlAst._statements import ALL_STATEMENT_CLASSES from hdlConvertorAst.to.basic_hdl_sim_model._main import ToBasicHdlSimModel from hdlConvertorAst.translate.common.name_scope import NameScope, WithNameScope from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import \ hdl_index, hdl_map_asoc from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import STR, BOOL from hwt.hdl.types.enum import HEnum from hwt.hdl.types.float import HFloat from hwt.hdl.types.hdlType import HdlType, MethodNotOverloaded from hwt.hdl.types.slice import HSlice from hwt.hdl.types.struct import HStruct from hwt.mainBases import RtlSignalBase from hwt.pyUtils.arrayQuery import arr_any from hwt.serializer.exceptions import SerializerException from hwt.serializer.exceptions import UnsupportedEventOpErr from hwt.serializer.generic.tmpVarConstructor import TmpVarConstructor, \ NoTmpVars from hwt.serializer.generic.utils import HWT_TO_HDLCONVEROTR_DIRECTION, \ TmpVarsSwap from hwt.serializer.utils import HdlStatement_sort_key, _natsort_key from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class ToHdlAst(): """ Base class for translators which translates hwt AST to a HDL ast :ivar ~.name_scope: name scope for resolution of hdl names for objects and for name colision checking for newly generated objects :ivar ~.tmpVars: A object which is used to create a tmp variable in current scope. It is set if it is possible to create a tmp variable. :ivar ~.constCache: A ConstCache instance used o extract values as a constants. :type ~.constCache: Optional[ConstantCache] """ # used to filter statems from other object by class without using # isisnstance ALL_STATEMENT_CLASSES = [*ALL_STATEMENT_CLASSES, HdlStmCodeBlockContainer] TMP_VAR_CONSTRUCTOR = TmpVarConstructor _keywords_dict = {} @classmethod def getBaseNameScope(cls): """ Get root of name space """ s = NameScope.make_top(False) s.update(cls._keywords_dict) return s def __init__(self, name_scope: Optional[NameScope]=None): if name_scope is None: name_scope = self.getBaseNameScope() self.name_scope = name_scope self.tmpVars = NoTmpVars() self.constCache = None def as_hdl(self, obj) -> iHdlObj: """ Convert any object to HDL AST :param obj: object to convert """ serFn = getattr(self, "as_hdl_" + obj.__class__.__name__, None) if serFn is not None: return serFn(obj) elif isinstance(obj, RtlSignalBase): return self.as_hdl_HdlSignalItem(obj) else: raise SerializerException(self, "Not implemented for obj of", obj.__class__, obj) def as_hdl_HdlType(self, typ: HdlType, declaration=False): try: return typ._as_hdl(self, declaration) except MethodNotOverloaded: pass if typ == STR: sFn = self.as_hdl_HdlType_str elif isinstance(typ, HBits): sFn = self.as_hdl_HdlType_bits elif isinstance(typ, HEnum): sFn = self.as_hdl_HdlType_enum elif isinstance(typ, HArray): sFn = self.as_hdl_HdlType_array elif isinstance(typ, HSlice): sFn = self.as_hdl_HdlType_slice elif isinstance(typ, HFloat): sFn = self.as_hdl_HdlType_float elif isinstance(typ, HStruct): sFn = self.as_hdl_HdlType_struct else: # [todo] better error msg raise NotImplementedError("type declaration is not implemented" " for type %s" % (HdlType.__repr__(typ))) return sFn(typ, declaration=declaration) def as_hdl_HdlType_array(self, typ: HArray, declaration=False): ns = self.name_scope if declaration: dec = HdlIdDef() dec.type = HdlTypeType if self.does_type_requires_extra_def(typ.element_t, ()): # problem there is that we do not have a list of already defined types # so we can not just declare an element type raise NotImplementedError(typ.element_t) dec.value = hdl_index(self.as_hdl_HdlType(typ.element_t, declaration=False), self.as_hdl_int(int(typ.size))) name = getattr(typ, "name", "arr_t_") dec.name = ns.checked_name(name, typ) return dec else: name = ns.get_object_name(typ) return HdlValueId(name, obj=typ) def as_hdl_HdlType_enum(self, typ: HEnum, declaration=False): ns = self.name_scope if declaration: e = HdlEnumDef() e.origin = typ e.name = ns.checked_name(typ.name, typ) e.values = [(ns.checked_name(n, getattr(typ, n)), None) for n in typ._allValues] dec = HdlIdDef() dec.type = HdlTypeType dec.value = e dec.name = e.name return dec else: name = ns.get_object_name(typ) return HdlValueId(name, obj=None) def as_hdl_HdlType_slice(self, typ: HSlice, declaration=False): raise NotImplementedError(self, typ) def as_hdl_HdlType_float(self, typ: HFloat, declaration=False): raise NotImplementedError(self, typ) def as_hdl_HdlType_struct(self, typ: HStruct, declaration=False): raise NotImplementedError(self, typ) def as_hdl_If(self, *args, **kwargs) -> HdlStmIf: return self.as_hdl_IfContainer(*args, **kwargs) def as_hdl_cond(self, v, force_bool) -> iHdlExpr: if force_bool and v._dtype != BOOL: v = v._isOn() return self.as_hdl(v) def as_hdl_statements(self, stm_list) -> iHdlStatement: if stm_list is None: return None elif len(stm_list) == 1: return self.as_hdl(stm_list[0]) else: b = HdlStmBlock() b.body = [self.as_hdl(s) for s in stm_list] return b def _as_hdl_HdlAssignmentContainer_auto_conversions(self, a: HdlAssignmentContainer): dst = a.dst src = a.src dst_indexes = a.indexes if a.indexes is not None: dst_indexes = [self.as_hdl(x) for x in dst_indexes] correct = True else: if dst._dtype == a.src._dtype: correct = True else: srcT = a.src._dtype dstT = dst._dtype correct = False if (isinstance(srcT, HBits) and isinstance(dstT, HBits)): bl0 = srcT.bit_length() if bl0 == dstT.bit_length(): if bl0 == 1 and srcT.force_vector != dstT.force_vector: if srcT.force_vector: src = src[0] correct = True elif dstT.force_vector: dst_indexes = [self.as_hdl_int(0), ] correct = True elif srcT.signed == dstT.signed: correct = True if not correct: raise SerializerException(( "%s <= %s is not valid assignment\n" " because types are different (%r; %r) ") % (dst, src, dst._dtype, a.src._dtype)) return dst, dst_indexes, self.as_hdl_Value(src) def as_hdl_HdlAssignmentContainer(self, a: HdlAssignmentContainer): dst, dst_indexes, src = self._as_hdl_HdlAssignmentContainer_auto_conversions(a) dst = self.as_hdl(dst) if dst_indexes: for i in dst_indexes: dst = hdl_index(dst, i) a = HdlStmAssign(src, dst) return a def as_hdl_IfContainer(self, ifc: IfContainer) -> HdlStmIf: try: cond = self.as_hdl_cond(ifc.cond, True) except UnsupportedEventOpErr: cond = None if cond is None: assert not ifc.elIfs assert not ifc.ifFalse return self.as_hdl_statements(ifc.ifTrue) elIfs = [] ifTrue = self.as_hdl_statements(ifc.ifTrue) ifFalse = self.as_hdl_statements(ifc.ifFalse) for c, statements in ifc.elIfs: try: elIfs.append((self.as_hdl_cond(c, True), self.as_hdl_statements(statements))) except UnsupportedEventOpErr: if len(ifc.elIfs) == 1 and not ifFalse: # register expression is in valid format and this # is just register with asynchronous reset or etc... ifFalse = self.as_hdl_statements(statements) else: raise i = HdlStmIf() i.cond = cond i.if_true = ifTrue i.elifs = elIfs i.if_false = ifFalse return i def as_hdl_Switch(self, *args, **kwargs) -> HdlStmCase: return self.as_hdl_SwitchContainer(*args, **kwargs) def as_hdl_FsmBuilder(self, *args, **kwargs) -> HdlStmCase: return self.as_hdl_SwitchContainer(*args, **kwargs) def as_hdl_SwitchContainer(self, sw: SwitchContainer) -> HdlStmCase: s = HdlStmCase() s.switch_on = self.as_hdl_cond(sw.switchOn, False) s.cases = cases = [] for key, statements in sw.cases: key = self.as_hdl_Value(key) cases.append((key, self.as_hdl_statements(statements))) s.default = self.as_hdl_statements(sw.default) return s def as_hdl_PortConnection(self, o: HdlPortItem): assert isinstance(o, HdlPortItem), o if o.dst._dtype != o.src._dtype: raise SerializerException( f"Port map {o._name:s} is not valid (types does not match) ({o.src._dtype}, {o.dst._dtype}) " f"{o.src} => {o.dst}" ) intern, outer = o.getInternSig(), o.getOuterSig() intern_hdl = self.as_hdl_Value(intern) intern_hdl.obj = o outer_hdl = self.as_hdl_Value(outer) pm = hdl_map_asoc(intern_hdl, outer_hdl) return pm def as_hdl_HdlCompInst(self, o: HdlCompInst) -> HdlCompInst: new_o = copy(o) param_map = [] for p in o.param_map: p: HdlIdDef assert isinstance(p, HdlIdDef), p pm = hdl_map_asoc(HdlValueId(p.name, obj=p), self.as_hdl(p.value)) param_map.append(pm) new_o.param_map = param_map port_map = [] for pi in o.port_map: pi: HdlPortItem pm = self.as_hdl_PortConnection(pi) port_map.append(pm) new_o.port_map = port_map return new_o def as_hdl_GenericItem(self, o: HdlIdDef): assert not self.does_type_requires_extra_def(o.type, tuple()) return self.as_hdl_HdlModuleDef_variable(o, None, None, None, None, None) def as_hdl_HdlPortItem(self, o: HdlPortItem): var = HdlIdDef() var.direction = HWT_TO_HDLCONVEROTR_DIRECTION[o.direction] s = o.getInternSig() var.name = s._name var.origin = o var.type = o._dtype return self.as_hdl_HdlModuleDef_variable(var, (), None, None, None, None) def as_hdl_HdlModuleDec(self, o: HdlModuleDec): # :attention: name_scope should be already set to body of module # with WithNameScope(self, self.name_scope.get_child(o._name)): new_o = copy(o) # convert types, exprs new_o.params = [self.as_hdl_GenericItem(p) for p in o.params] new_o.ports = [self.as_hdl_HdlPortItem(p) for p in o.ports] return new_o def does_type_requires_extra_def(self, t: HdlType, other_types: list): try: return t._as_hdl_requires_def(self, other_types) except MethodNotOverloaded: pass return isinstance(t, (HEnum, HArray)) and t not in other_types def as_hdl_HdlModuleDef_variable( self, v, types, hdl_types, hdl_variables, processes, component_insts): t = v.type # if type requires extra definition if self.does_type_requires_extra_def(t, types): _t = self.as_hdl_HdlType(t, declaration=True) hdl_types.append(_t) types.add(t) return self.as_hdl_HdlSignalItem(v, declaration=True) def _as_hdl_HdlModuleDef(self, new_m: HdlModuleDef) -> HdlModuleDef: # with WithNameScope(self, # self.name_scope.get_child(o.module_name.val)): hdl_types, hdl_variables, processes, component_insts, others = \ ToBasicHdlSimModel.split_HdlModuleDefObjs(self, new_m.objs) if len(hdl_variables) > 1: hdl_variables.sort(key=lambda x: (_natsort_key(x.name), x.origin._instId)) if len(processes) > 1: processes.sort(key=HdlStatement_sort_key) if len(component_insts) > 1: component_insts.sort(key=lambda x: _natsort_key(x.name)) types = set() extraVars = self.TMP_VAR_CONSTRUCTOR(self, self.name_scope) with TmpVarsSwap(self, extraVars): return self._as_hdl_HdlModuleDef_body( new_m, types, hdl_types, hdl_variables, extraVars, processes, component_insts, others) def _as_hdl_HdlModuleDef_body( self, new_m, types, hdl_types, hdl_variables, extraVars: TmpVarConstructor, processes: List[iHdlStatement], component_insts: List[HdlCompInst], others: List[Union[HdlOp, iHdlStatement]]): _hdl_variables = [] for v in hdl_variables: new_v = self.as_hdl_HdlModuleDef_variable( v, types, hdl_types, hdl_variables, processes, component_insts) _hdl_variables.append(new_v) hdl_variables = _hdl_variables processes = [self.as_hdl_HdlStmCodeBlockContainer(p) for p in processes] component_insts = [self.as_hdl_HdlCompInst(c) for c in component_insts] extraVars.sort_hdl_declarations_first() new_m.objs = hdl_types + hdl_variables + \ extraVars.extraVarsHdl + component_insts + processes + others return new_m def as_hdl_HdlModuleDef(self, o: HdlModuleDef) -> HdlModuleDef: # :attention: name_scope should be already set to body of module new_m = copy(o) if o.dec is not None: new_m.dec = self.as_hdl_HdlModuleDec(o.dec) return self._as_hdl_HdlModuleDef(new_m) def has_to_be_process(self, proc: iHdlStatement): raise NotImplementedError( "This method should be overloaded in child class") def can_pop_process_wrap(self, statements, hasToBeVhdlProcess): raise NotImplementedError( "This method should be overloaded in child class") def as_hdl_HdlStmCodeBlockContainer(self, proc: HdlStmCodeBlockContainer) -> iHdlStatement: """ Serialize HdlStmCodeBlockContainer objects as process if top statement """ if isinstance(proc, ALL_STATEMENT_CLASSES): return proc assert proc.parentStm is None, proc body = proc.statements hasToBeVhdlProcess = self.has_to_be_process(proc) with WithNameScope(self, self.name_scope.level_push(proc.name)): tmpVars = self.TMP_VAR_CONSTRUCTOR(self, self.name_scope) with TmpVarsSwap(self, tmpVars): statements = [self.as_hdl(s) for s in body] hasToBeVhdlProcess |= bool(tmpVars.extraVarsHdl) if hasToBeVhdlProcess: tmpVars.sort_hdl_declarations_first() statements = tmpVars.extraVarsHdl + statements if self.can_pop_process_wrap(statements, hasToBeVhdlProcess): return statements[0] else: p = HdlStmProcess() p.labels.append(proc.name) if not statements: pass # no body elif len(statements) == 1: # body made of just a singe statement p.body = statements[0] else: p.body = HdlStmBlock() assert isinstance(statements, list) p.body.body = statements anyIsEventDependnt = arr_any( proc._sensitivity, lambda s: isinstance(s, HOperatorNode)) p.sensitivity = sorted([ self.sensitivityListItem(s, anyIsEventDependnt) for s in proc._sensitivity]) return p def _static_assert_false(self, msg:str): raise NotImplementedError("Should be implemented in child class") def _static_assert_symbol_eq(self, symbol_name:str, v): raise NotImplementedError("Should be implemented in child class") def _as_hdl_HdlModuleDef_param_asserts(self, new_m: HdlModuleDec) -> List[iHdlStatement]: return [] def _as_hdl_HdlModuleDef_param_asserts_real(self, new_m: HdlModuleDec) -> List[iHdlStatement]: res = [] for p in new_m.params: p: HdlIdDef if p.value is None: continue v = p.value if isinstance(v, (HConst, RtlSignal)): v = self.as_hdl(v) else: v = deepcopy(v) res.append(self._static_assert_symbol_eq(p.name, v)) return res ================================================ FILE: hwt/serializer/generic/utils.py ================================================ from ipCorePackager.constants import DIRECTION from hdlConvertorAst.hdlAst._expr import HdlDirection from hwt.doc_markers import internal HWT_TO_HDLCONVEROTR_DIRECTION = { DIRECTION.IN: HdlDirection.IN, DIRECTION.INOUT: HdlDirection.INOUT, DIRECTION.OUT: HdlDirection.OUT, } @internal class TmpVarsSwap(): """ An object which is used as a context manager for tmpVars inside of :class:`~.ToHdlAst` """ def __init__(self, ctx, tmpVars): self.ctx = ctx self.tmpVars = tmpVars def __enter__(self): self.orig = self.ctx.tmpVars self.ctx.tmpVars = self.tmpVars def __exit__(self, exc_type, exc_val, exc_tb): self.ctx.tmpVars = self.orig ================================================ FILE: hwt/serializer/generic/value.py ================================================ from copy import copy from typing import Union from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlValueInt, HdlDirection from hwt.hdl.const import HConst from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import INT, BOOL, FLOAT64 from hwt.hdl.types.enum import HEnum from hwt.hdl.types.float import HFloat from hwt.hdl.types.function import HFunction, HFunctionConst from hwt.hdl.types.slice import HSlice from hwt.hdl.types.string import HString from hwt.hdl.types.stringConst import HStringConst from hwt.hdl.variables import HdlSignalItem from hwt.mainBases import RtlSignalBase from hwt.serializer.exceptions import SerializerException from pyMathBitPrecise.bit_utils import mask INT32_MAX = mask(32-1) INT32_MIN = -INT32_MAX - 1 class ToHdlAst_Value(): def is_suitable_for_const_extract(self, val: HConst): """ :return: True if an value should be extracted as a constant if possible """ return False def as_hdl_Value(self, val): """ :param dst: is signal connected with value :param val: value object, can be instance of Signal or HConst """ t = val._dtype if isinstance(val, RtlSignalBase): return self.as_hdl_HdlSignalItem(val) # try to extract value as constant cc = self.constCache if cc is not None: if self.is_suitable_for_const_extract(val): c = cc.extract_const_val_as_const_var(val) if c is not None: return self.as_hdl(c) if isinstance(t, HSlice): return self.as_hdl_HSliceConst(val) elif isinstance(t, HArray): return self.as_hdl_HArrayConst(val) elif isinstance(t, HBits): return self.as_hdl_HBitsConst(val) elif isinstance(t, HEnum): return self.as_hdl_HEnumConst(val) elif isinstance(t, HString): return self.as_hdl_HStringConst(val) elif isinstance(t, HFloat): return self.as_hdl_HFloatConst(val) elif isinstance(t, HFunction): return self.as_hdl_HFunctionConst(val) else: raise SerializerException( "can not resolve value serialization for %r" % (val)) def as_hdl_int(self, val: int): assert isinstance(val, int), val return HdlValueInt(val, None, None) def Value_try_extract_as_const(self, val): return None def as_hdl_IntegerVal(self, val: HBitsConst): return self.as_hdl_int(int(val.val)) def as_hdl_HBitsConst(self, val: HBitsConst): t = val._dtype if t == INT: if val < INT32_MAX and val > INT32_MIN: return self.as_hdl_IntegerVal(val) elif t == BOOL: return self.as_hdl_HBoolConst(val) w = t.bit_length() return self.as_hdl_BitString(val.val, w, t.force_vector, val.vld_mask, t.signed) def as_hdl_HStringConst(self, val: HStringConst): return val.val def as_hdl_HFunctionConst(self, val: HFunctionConst): return val.val def as_hdl_HFloatConst(self, val): if val._dtype != FLOAT64: raise NotImplementedError(val._dtype) return float(val) def as_hdl_HdlSignalItem(self, si: Union[HdlSignalItem, HdlIdDef], declaration=False): if declaration: if isinstance(si, HdlIdDef): var = copy(si) si = si.origin else: var = HdlIdDef() var.name = si._name var.origin = si var.value = si._val var.type = si._dtype var.is_const = si._const v = var.value if isinstance(si, RtlSignalBase): if si.virtual_only: var.is_latched = True elif si._rtlDrivers or var.direction != HdlDirection.UNKNOWN: # has drivers or is port/param pass elif si._rtlEndpoints: if not v.vld_mask: raise SerializerException( f"Signal {si._name:s} is constant and has undefined value") var.is_const = True else: raise SerializerException( f"Signal {si._name:s} should be declared but it is not used") if v is None: pass elif isinstance(v, RtlSignalBase): if v._const: var.value = self.as_hdl(v) else: # default value has to be set by reset, # because it is not resolvable in compile time var.value = None pass elif isinstance(v, HConst): if v.vld_mask or var.is_const: orig_const_cache = self.constCache try: self.constCache = None var.value = self.as_hdl_Value(v) finally: self.constCache = orig_const_cache else: # remove value if it is entirely undefined var.value = None else: raise NotImplementedError(v) var.type = self.as_hdl_HdlType(var.type) return var else: if si._isUnnamedExpr and si._rtlObjectOrigin is not None: # hidden signal, render it's driver instead return self.as_hdl(si._rtlObjectOrigin) return HdlValueId(si._name, obj=si) ================================================ FILE: hwt/serializer/hwt/__init__.py ================================================ """ Hwt serializer converts HDL objects back to code in python for hwt. """ from hdlConvertorAst.hdlAst import iHdlObj from hdlConvertorAst.to.hwt._main import ToHwt from hwt.serializer.exceptions import SerializerException from hwt.serializer.hwt.serializer import ToHdlAstHwt from hwt.serializer.serializer_config import DummySerializerConfig class HwtSerializer(DummySerializerConfig): fileExtension = '.py' TO_HDL_AST = ToHdlAstHwt TO_HDL = ToHwt class ToHdlAstDebugHwt(ToHdlAstHwt): CONVERT_UNKNOWN_OPS_TO_FN_CALL = True def as_hdl(self, obj) -> iHdlObj: try: return super(ToHdlAstDebugHwt, self).as_hdl(obj) except SerializerException: return obj.__repr__() class HwtDebugSerializer(DummySerializerConfig): fileExtension = '.py' TO_HDL_AST = ToHdlAstDebugHwt TO_HDL = ToHwt ================================================ FILE: hwt/serializer/hwt/context.py ================================================ class ValueWidthRequirementScope(): """ Context manager which temporarily swaps the _valueWidthRequired on specified context .. code-block:: python with ValueWidthRequirementScope(ctx, True): #... """ def __init__(self, ctx, val): self.ctx = ctx self.val = val def __enter__(self): self.orig = self.ctx._valueWidthRequired self.ctx._valueWidthRequired = self.val def __exit__(self, exc_type, exc_val, exc_tb): self.ctx._valueWidthRequired = self.orig ================================================ FILE: hwt/serializer/hwt/ops.py ================================================ from hdlConvertorAst.hdlAst._expr import HdlOp, HdlValueId from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_getattr, \ hdl_call from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.serializer.hwt.context import ValueWidthRequirementScope from hwt.serializer.simModel.value import ToHdlAstSimModel_value class ToHdlAstHwt_ops(): CONVERT_UNKNOWN_OPS_TO_FN_CALL = False CONCAT = HdlValueId("Concat", obj=LanguageKeyword()) op_transl_dict = ToHdlAstSimModel_value.op_transl_dict _cast_ops = ToHdlAstSimModel_value._cast_ops def as_hdl_HOperatorNode(self, op: HOperatorNode): ops = op.operands o = op.operator with ValueWidthRequirementScope(self, o == HwtOps.CONCAT): if o in self._cast_ops: op0 = hdl_getattr(self.as_hdl(ops[0]), "_reinterpret_cast") op1 = self.as_hdl_HdlType(op.result._dtype) return hdl_call(op0, [op1, ]) elif o == HwtOps.EQ: return hdl_call(hdl_getattr(self.as_hdl(ops[0]), "_eq"), [self.as_hdl(ops[1])]) elif o == HwtOps.CONCAT: return hdl_call(self.CONCAT, [self.as_hdl(o2) for o2 in ops]) elif o == HwtOps.ZEXT or o == HwtOps.SEXT or o == HwtOps.TRUNC: op0, newWidth = ops newWidth = int(newWidth) with ValueWidthRequirementScope(self, True): op0 = self.as_hdl(op0) op1 = self.as_hdl_int(newWidth) fnName = "_zext" if o == HwtOps.ZEXT else \ "_sext" if o == HwtOps.SEXT else \ "_trunc" return hdl_call(hdl_getattr(op0, fnName), [op1]) elif o == HwtOps.TERNARY: cond, op0, op1 = ops cond = self.as_hdl(cond) with ValueWidthRequirementScope(self, True): op0 = self.as_hdl(op0) op1 = self.as_hdl(op1) return hdl_call(hdl_getattr(cond, "_ternary"), [op0, op1]) else: _o = self.op_transl_dict.get(o, None) if self.CONVERT_UNKNOWN_OPS_TO_FN_CALL: if _o is None: return hdl_call(HdlValueId(o.id, obj=o._evalFn), [self.as_hdl(o2) for o2 in ops]) assert _o is not None, o return HdlOp(_o, [self.as_hdl(o2) for o2 in ops]) ================================================ FILE: hwt/serializer/hwt/serializer.py ================================================ from typing import Optional, Union from hdlConvertorAst.hdlAst import HdlStmBlock from hdlConvertorAst.hdlAst._expr import HdlValueId from hdlConvertorAst.hdlAst._structural import HdlModuleDef from hdlConvertorAst.to.hwt.keywords import HWT_KEYWORDS from hdlConvertorAst.translate.common.name_scope import LanguageKeyword, NameScope from hwt.code import CodeBlock from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.serializer.generic.to_hdl_ast import ToHdlAst from hwt.serializer.hwt.ops import ToHdlAstHwt_ops from hwt.serializer.hwt.types import ToHdlAstHwt_types from hwt.serializer.hwt.value import ToHdlAstHwt_value from hwt.serializer.simModel.serializer import ToHdlAstSimModel from hwt.hdl.variables import HdlSignalItem class ToHdlAstHwt(ToHdlAstHwt_value, ToHdlAstHwt_ops, ToHdlAstHwt_types, ToHdlAst): """ Serializer which converts HWT objects back to HWT code for debugging purposes/code ports :ivar ~._valueWidthRequired: flag which tells if the values are required to have the width specified """ _keywords_dict = {kw: LanguageKeyword() for kw in HWT_KEYWORDS} def __init__(self, name_scope: Optional[NameScope]=None): super(ToHdlAstHwt, self).__init__(name_scope=name_scope) self._valueWidthRequired = False self.currentHwModule = None self.debug = False def has_to_be_process(self, proc): return True def can_pop_process_wrap(self, statements, hasToBeVhdlProcess: bool): return False def _as_hdl_HdlModuleDef(self, new_m: HdlModuleDef) -> HdlModuleDef: return ToHdlAstSimModel._as_hdl_HdlModuleDef(self, new_m) def sensitivityListItem(self, item: Union[HdlSignalItem, HOperatorNode], anyIsEventDependent: bool): if isinstance(item, HOperatorNode): op = item.operator assert op in (HwtOps.RISING_EDGE, HwtOps.FALLING_EDGE), item assert not item.operands[0]._isUnnamedExpr, item return self.as_hdl_HOperatorNode(item) else: return HdlValueId(item._name, obj=item) def as_hdl_CodeBlock(self, o: CodeBlock): res = HdlStmBlock() for _o in o.statements: res.body.append(self.as_hdl(_o)) return res ================================================ FILE: hwt/serializer/hwt/types.py ================================================ from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlValueInt from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call, \ hdl_map_asoc, hdl_index from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import BITS_DEFAUTL_SIGNED, BITS_DEFAUTL_FORCEVECTOR, \ BITS_DEFAUTL_NEGATED, HBits from hwt.hdl.types.defs import BOOL, INT, STR from hwt.hdl.types.enum import HEnum from hwt.hdl.types.float import HFloat from hwt.hdl.types.hdlType import MethodNotOverloaded class ToHdlAstHwt_types(): """ part of ToHdlAstSimModel responsible for type serialization """ BOOL = HdlValueId("BOOL", obj=BOOL) INT = HdlValueId("INT", obj=INT) BITS = HdlValueId("HBits", obj=HBits) STR = HdlValueId("STR", obj=STR) def does_type_requires_extra_def(self, t, other_types): try: return t._as_hdl_requires_def(self, other_types) except MethodNotOverloaded: pass return isinstance(t, HEnum) and t not in other_types def as_hdl_HdlType_str(self, typ, declaration=False): assert not declaration return self.STR def as_hdl_HdlType_array(self, typ: HArray, declaration=False): assert not declaration, "declaration should not be required" t = self.as_hdl_HdlType(typ.element_t, declaration=declaration) return hdl_index(t, HdlValueInt(int(typ.size), None, None)) def as_hdl_HdlType_bits(self, typ: HBits, declaration=False): if declaration: raise NotImplementedError() elif typ == BOOL: return self.BOOL elif typ == INT: return self.INT w = typ.bit_length() assert isinstance(w, int), w def add_kw(name, val): kw = hdl_map_asoc(HdlValueId(name), HdlValueInt(val, None, None)) args.append(kw) args = [HdlValueInt(w, None, None)] if typ.signed is not BITS_DEFAUTL_SIGNED: add_kw("signed", typ.signed) if typ.force_vector is not BITS_DEFAUTL_FORCEVECTOR and w <= 1: add_kw("force_vector", typ.force_vector) if typ.negated is not BITS_DEFAUTL_NEGATED: add_kw("negated", typ.negated) return hdl_call(self.BITS, args) def as_hdl_HdlType_float(self, typ: HFloat, declaration=False): return hdl_call(HdlValueId("HFloat"), typ.exponent_w, typ.mantisa_w) ================================================ FILE: hwt/serializer/hwt/value.py ================================================ from copy import copy from typing import Union from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlValueInt, HdlOp, HdlOpType, \ HdlValueId from hdlConvertorAst.translate.common.name_scope import ObjectForNameNotFound from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_getattr, \ hdl_call from hwt.hdl.const import HConst from hwt.hdl.types.arrayConst import HArrayConst from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import SLICE from hwt.hdl.types.enum import HEnum from hwt.hdl.types.enumConst import HEnumConst from hwt.hdl.types.sliceConst import HSliceConst from hwt.hdl.variables import HdlSignalItem from hwt.serializer.generic.value import ToHdlAst_Value from hwt.serializer.simModel.value import ToHdlAstSimModel_value class ToHdlAstHwt_value(ToHdlAst_Value): NONE = HdlValueId("None") SLICE = HdlValueId("SLICE", obj=SLICE) def is_suitable_for_const_extract(self, val: HConst): # full valid values can be represented as int and do not have any # constructor overhead, entirely invalid values can be represented by None return not val._is_full_valid() and not isinstance(val._dtype, HEnum) def as_hdl_HBitsConst(self, val: HBitsConst): isFullVld = val._is_full_valid() if not self._valueWidthRequired: if isFullVld: return HdlValueInt(val.val, None, 16) elif val.vld_mask == 0: return self.NONE t = self.as_hdl_HdlType_bits(val._dtype, declaration=False) c = hdl_getattr(t, "from_py") args = [HdlValueInt(val.val, None, 16), ] if not isFullVld: args.append(HdlValueInt(val.vld_mask, None, 16)) return hdl_call(c, args) def as_hdl_HdlSignalItem(self, si: Union[HdlSignalItem, HdlIdDef], declaration=False): if declaration: if isinstance(si, HdlIdDef): new_si = copy(si) new_si.type = self.as_hdl_HdlType(si.type) if si.value is not None: new_si.value = self.as_hdl_Value(si.value) return new_si else: raise NotImplementedError() else: # if isinstance(si, HdlSignalItem) and si._const: # # to allow const cache to extract constants # return self.as_hdl_Value(si._val) if si._isUnnamedExpr and si._rtlObjectOrigin is not None: return self.as_hdl(si._rtlObjectOrigin) else: return HdlValueId(si._name, obj=si) def as_hdl_HDictConst(self, val): return ToHdlAstSimModel_value.as_hdl_HDictConst(self, val) def as_hdl_HArrayConst(self, val: HArrayConst): if not val.vld_mask: return self.NONE # else: # if len(val.val) == val._dtype.size: # allValuesSame = True # values = iter(val.val.values()) # reference = next(values) # for v in values: # if allValuesSame: # allValuesSame = isSameHConst(reference, v) # else: # break # if allValuesSame: # # all values of items in array are same, use generator # # exression # raise NotImplementedError() # return "[%s for _ in range(%d)]" % (self.Value(reference)) # if value can not be simplified it is required to serialize it item # by item return self.as_hdl_HDictConst(val.val) def as_hdl_HSliceConst(self, val: HSliceConst): if val._is_full_valid(): return HdlOp( HdlOpType.DOWNTO, [ HdlValueInt(int(val.val.start), None, None), HdlValueInt(int(val.val.stop), None, None) ]) else: raise NotImplementedError() return "HSliceConst(slice(%s, %s, %s), SLICE, %d)" % ( self.as_hdl_Value(val.val.start), self.as_hdl_Value(val.val.stop), self.as_hdl_Value(val.val.step), val.vld_mask) def as_hdl_HEnumConst(self, val: HEnumConst): try: t_name = self.name_scope.get_object_name(val._dtype) except ObjectForNameNotFound: if self.debug: t_name = val._dtype.name else: raise if val.vld_mask: try: name = self.name_scope.get_object_name(val) except ObjectForNameNotFound: if self.debug: name = val.val else: raise return hdl_getattr(HdlValueId(t_name, obj=val._dtype), name) else: return hdl_call(hdl_getattr(HdlValueId(t_name, obj=val._dtype), "from_py"), [None, ]) ================================================ FILE: hwt/serializer/ip_packager.py ================================================ from io import StringIO import math from typing import List, Tuple, Union from hdlConvertorAst.hdlAst import HdlValueId from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.to.vhdl.vhdl2008 import ToVhdl2008 from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call from hwt.doc_markers import internal from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL, STR, BIT, INT from hwt.hdl.types.hdlType import HdlType from hwt.hdl.variables import HdlSignalItem from hwt.hwIO import HwIO from hwt.hwModule import HwModule from hwt.hwParam import HwParam from hwt.mainBases import RtlSignalBase from hwt.serializer.store_manager import SaveToFilesFlat from hwt.serializer.vhdl import Vhdl2008Serializer, ToHdlAstVhdl2008 from hwt.synth import to_rtl from hwt.synthesizer.dummyPlatform import DummyPlatform from hwt.synthesizer.interfaceLevel.hwModuleImplHelpers import getSignalName from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from ipCorePackager.intfIpMeta import VALUE_RESOLVE from ipCorePackager.otherXmlObjs import Value from ipCorePackager.packager import IpCorePackager class ToHdlAstVivadoTclExpr(ToHdlAstVhdl2008): _spirit_decode = HdlValueId("spirit:decode") _id = HdlValueId("id") def as_hdl_HdlSignalItem(self, si: HdlSignalItem, declaration=False): assert(declaration == False) if si._isUnnamedExpr: assert si._rtlObjectOrigin is not None, si return self.as_hdl(si._rtlObjectOrigin) else: id_ = hdl_call(self._id, [f'MODELPARAM_VALUE.{si._name}']) return hdl_call(self._spirit_decode, [id_]) class IpPackager(IpCorePackager): """ IP-core packager :summary: Packs HDL, constraint and other files to IP-Core package for distribution and simple integration """ def __init__(self, topHwModule: HwModule, name: str=None, extra_files: List[str]=[], serializer_cls=Vhdl2008Serializer, target_platform=DummyPlatform()): """ :param topObj: :class:`hwt.hwModule.HwModule` instance of top component :param name: optional name of top :param extra_files: list of extra HDL/constrain file names for files which should be distributed in this IP-core (\\*.v - verilog, \\*.sv,\\*.svh -system verilog, \\*.vhd - vhdl, \\*.xdc - XDC) :param serializer: serializer which specifies target HDL language :param target_platform: specifies properties of target platform, like available resources, vendor, etc. """ if not name: name = topHwModule._getDefaultName() super(IpPackager, self).__init__( topHwModule, name, extra_files) self.serializer = serializer_cls self.target_platform = target_platform @internal def toHdlConversion(self, top, topName: str, saveTo: str) -> List[str]: """ :param top: object which is representation of design :param topName: name which should be used for ipcore :param saveTo: path of directory where generated files should be stored :return: list of file names in correct compile order """ ser = self.serializer store = SaveToFilesFlat(ser, saveTo) to_rtl(top, name=topName, store_manager=store, target_platform=self.target_platform) return store.files @internal def paramToIpValue(self, idPrefix: str, g: HdlIdDef, resolve) -> Value: val = Value() val.id = idPrefix + g.name if resolve is not VALUE_RESOLVE.NONE: val.resolve = resolve v = g.value t = g.type def getVal(): if v.vld_mask: return v.val else: return 0 def bitString(w): val.format = "bitString" digits = math.ceil(w / 4) val.text = ('0x%0' + str(digits) + 'X') % getVal() val.bitStringLength = str(w) if t == BOOL: val.format = "bool" val.text = str(bool(getVal())).lower() elif t == INT: val.format = "long" val.text = str(getVal()) elif t == STR: val.format = "string" val.text = v.val elif isinstance(t, HBits): bitString(t.bit_length()) else: raise NotImplementedError( f"Not implemented for datatype {t}") return val @internal def getParamPhysicalName(self, p: HdlIdDef): return p.name @internal def getParamType(self, p: HdlIdDef) -> HdlType: assert p.type in [INT, BOOL, STR], p return p.type @internal def iterParams(self, module: HwModule): return module._rtlCtx.hwModDec.params @internal def iterInterfaces(self, top: HwModule): return top._hwIOs @internal def serializeType(self, hdlType: HdlType) -> str: """ :see: doc of method on parent class """ buff = StringIO() to_ast = ToHdlAstVhdl2008() hdl = to_ast.as_hdl_HdlType(hdlType) ser = ToVhdl2008(buff) ser.visit_iHdlObj(hdl) return buff.getvalue() @internal def getVectorFromType(self, dtype) -> Union[bool, None, Tuple[int, int]]: """ :see: doc of method on parent class """ if dtype == BIT: return False elif isinstance(dtype, HBits): return [dtype.bit_length() - 1, INT.from_py(0)] @internal def getInterfaceType(self, hwIO: HwIO) -> HdlType: """ :see: doc of method on parent class """ return hwIO._dtype @internal def getInterfaceLogicalName(self, hwIO: HwIO): """ :see: doc of method on parent class """ return getSignalName(hwIO) @internal def getInterfacePhysicalName(self, hwIO: HwIO): """ :see: doc of method on parent class """ return hwIO._sigInside._name @internal def getInterfaceDirection(self, thisHwIO: HwIO): """ :see: doc of method on parent class """ return thisHwIO._direction @internal def getTypeWidth(self, dtype: HdlType, do_eval=False)\ -> Tuple[int, Union[int, RtlSignal], bool]: """ :see: doc of method on parent class """ width = dtype.bit_length() widthStr = str(width) return width, widthStr, False @internal def getObjDebugName(self, obj: Union[HwIO, HwModule, HwParam]) -> str: """ :see: doc of method on parent class """ return obj._getFullName() @internal def serialzeValueToTCL(self, val, do_eval=False) -> Tuple[str, str, bool]: """ :see: doc of method on parent class """ if isinstance(val, int): val = INT.from_py(val) if do_eval: val = val.staticEval() buff = StringIO() to_hdl = ToHdlAstVivadoTclExpr() ser = Vhdl2008Serializer.TO_HDL(buff) hdl = to_hdl.as_hdl(val) ser.visit_iHdlObj(hdl) tclVal = buff.getvalue() if isinstance(val, RtlSignalBase): buff = StringIO() hdl = to_hdl.as_hdl(val.staticEval()) ser = Vhdl2008Serializer.TO_HDL(buff) ser.visit_iHdlObj(hdl) tclValVal = buff.getvalue() return tclVal, tclValVal, False else: return tclVal, tclVal, True ================================================ FILE: hwt/serializer/mode.py ================================================ """ Serializer mode specifies if hdl objects derived from parent unit should be serialized to target HDL or not use serialize* methods to specify serialization mode for unit class .. code-block:: python @serializeExclude class MyHwModule(HwModule): # ... pass """ from collections import namedtuple from typing import Type from hwt.doc_markers import internal @internal def freeze_dict(data): keys = sorted(data.keys()) if keys: frozen_type = namedtuple(''.join(keys), keys) else: return tuple() return frozen_type(**data) @internal def hwParamsToValTuple(module: "HwModule"): # [TODO] check sub params d = {} for p in module._hwParams: v = p.get_value() d[p._name] = v return freeze_dict(d) def serializeExclude(cls: Type["HwModule"]): """ Never serialize HDL objects from this class """ cls._serializeDecision = staticmethod(_serializeExclude_eval) return cls def serializeOnce(cls: Type["HwModule"]): """ Serialize HDL objects only once per class """ cls._serializeDecision = staticmethod(_serializeOnce_eval) return cls def serializeParamsUniq(cls: Type["HwModule"]): """ Decide to serialize only when parameters are unique """ cls._serializeDecision = staticmethod(_serializeParamsUniq_eval) return cls @internal def _serializeExclude_eval(parentModule: "HwModule", priv): """ Always decide not to serialize obj :param priv: private data for this function first unit of this class :return: tuple (do serialize this object, next priv, replacement unit) """ # do not use this :class:`hwt.hwModule.HwModule` instance and do not use any replacement # (useful when the :class:`hwt.hwModule.HwModule` instance is a placeholder for something # which already exists in hdl word) if priv is None: priv = parentModule return False, priv, None else: return False, priv, priv @internal def _serializeOnce_eval(parentModule: "HwModule", priv): """ Decide to serialize only first obj of it's class :param priv: private data for this function (first object with class == obj.__class__) :return: tuple (do serialize this object, next priv, replacement unit) where priv is private data for this function (first object with class == obj.__class__) """ if priv is None: priv = parentModule serialize = True replacement = None # use this :class:`hwt.hwModule.HwModule` instance and store it for later use else: # use existing :class:`hwt.hwModule.HwModule` instance serialize = False replacement = priv return serialize, priv, replacement @internal def _serializeParamsUniq_eval(parentModule: "HwModule", priv): """ Decide to serialize only objs with unique parameters and class :param priv: private data for this function ({frozen_params: obj}) :return: tuple (do serialize this object, next priv, replacement unit) """ params = hwParamsToValTuple(parentModule) if priv is None: priv = {} try: prevModule = priv[params] except KeyError: priv[params] = parentModule # serialize new return True, priv, None # use previous :class:`hwt.hwModule.HwModule` instance with same config return False, priv, prevModule ================================================ FILE: hwt/serializer/resourceAnalyzer/__init__.py ================================================ """ Resource analyzer has serializer API, but it's output is resource report. """ ================================================ FILE: hwt/serializer/resourceAnalyzer/analyzer.py ================================================ from itertools import chain from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._structural import HdlModuleDef, \ HdlCompInst from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode, isConst from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.hdl.types.array import HArray from hwt.serializer.resourceAnalyzer.utils import ResourceContext from hwt.hwModule import HwModule from hwt.synthesizer.rtlLevel.mark_visibility_of_signals_and_check_drivers import walk_assignments from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal @internal def _count_mux_inputs_for_outputs(stm: HdlStatement, cnt): if isinstance(stm, HdlAssignmentContainer): cnt[stm.dst] += 1 else: for _stm in stm._iter_stms(): if isinstance(_stm, HdlAssignmentContainer): cnt[_stm.dst] += 1 else: _count_mux_inputs_for_outputs(_stm, cnt) @internal def count_mux_inputs_for_outputs(stm): cnt = {o: 0 for o in stm._outputs} _count_mux_inputs_for_outputs(stm, cnt) return cnt # operators which do not consume a hw resorce directly IGNORED_OPERATORS = { HwtOps.BitsAsSigned, HwtOps.BitsAsUnsigned, HwtOps.BitsAsVec, HwtOps.RISING_EDGE, HwtOps.FALLING_EDGE, } class ResourceAnalyzer(): """ Serializer which does not products any output just collect informations about used resources :attention: Use instance of ResourceAnalyzer instead of class """ _keywords_dict = {} def __init__(self): self.context = ResourceContext(None) @internal def visit_HdlStmCodeBlockContainer_operators(self, sig: RtlSignal, synchronous): ctx = self.context seen = ctx.seen for d in sig._rtlDrivers: if (not isinstance(d, HOperatorNode) or d in seen): continue skip_op = d.operator in IGNORED_OPERATORS if not skip_op: if d.operator == HwtOps.EQ: o1 = d.operands[1] if (isinstance(o1, HConst) and o1._dtype.bit_length() == 1 and o1.val): # to bool conversion skip_op = True elif d.operator == HwtOps.INDEX: o1 = d.operands[1] skip_op = True if isConst(o1): # constant signal slice pass else: o0 = d.operands[0] if isinstance(o0._dtype, HArray): ctx.registerRAM_read_port(o0, o1, synchronous) else: ctx.registerMUX(d, sig, 2) elif d.operator == HwtOps.TERNARY: o1 = d.operands[1] o2 = d.operands[2] if (isConst(o1) and bool(o1) and isConst(o2) and not bool(o2)): # to bit conversion skip_op = True else: raise NotImplementedError("Ternary as mux") if not skip_op: ctx.registerOperator(d) for op in d.operands: if (not isinstance(op, RtlSignal) or not op._isUnnamedExpr or op in seen): continue self.visit_HdlStmCodeBlockContainer_operators(op, synchronous) def visit_HdlStmCodeBlockContainer(self, proc: HdlStmCodeBlockContainer) -> None: """ Guess resource usage from HdlStmCodeBlockContainer """ ctx = self.context seen = ctx.seen for stm in proc.statements: encl = stm._enclosed_for full_ev_dep = stm._event_dependent_from_branch == 0 now_ev_dep = stm._event_dependent_from_branch is not None ev_dep = full_ev_dep or now_ev_dep out_mux_dim = count_mux_inputs_for_outputs(stm) for o in stm._outputs: if o in seen: continue i = out_mux_dim[o] if isinstance(o._dtype, HArray): assert i == 1, (o, i, " only one ram port per HdlStmCodeBlockContainer") for a in walk_assignments(stm, o): assert len(a.indexes) == 1, ("has to have single address per RAM port", a.indexes) addr = a.indexes[0] ctx.registerRAM_write_port(o, addr, ev_dep) elif ev_dep: ctx.registerFF(o) if i > 1: ctx.registerMUX(stm, o, i) elif o not in encl: ctx.registerLatch(o) if i > 1: ctx.registerMUX(stm, o, i) elif i > 1: ctx.registerMUX(stm, o, i) else: # just a connection continue if isinstance(stm, SwitchContainer): caseEqs = set([stm.switchOn._eq(c[0]) for c in stm.cases]) inputs = chain( [sig for sig in stm._inputs if sig not in caseEqs], [stm.switchOn]) else: inputs = stm._inputs for i in inputs: # discover only internal signals in this statements for # operators if not i._isUnnamedExpr or i in seen: continue self.visit_HdlStmCodeBlockContainer_operators(i, ev_dep) def visit_HdlModuleDef(self, m: HdlModuleDef) -> None: for o in m.objs: if isinstance(o, HdlStmCodeBlockContainer): self.visit_HdlStmCodeBlockContainer(o) elif isinstance(o, HdlCompInst): self.visit_HdlCompInst(o) else: assert isinstance(o, HdlIdDef), o def visit_HdlCompInst(self, o: HdlCompInst) -> None: raise NotImplementedError() # [TODO] constant to ROMs def visit_HwModule(self, m: HwModule): self.visit_HdlModuleDef(m._rtlCtx.hwModDef) def report(self): ctx = self.context ctx.finalize() return ctx.resources ================================================ FILE: hwt/serializer/resourceAnalyzer/resourceTypes.py ================================================ class ResourceError(Exception): """ An error which means that the resource of this kind does not exists in current hardware. """ class RtlResourceType(): """ A base class for resource type descriptions. """ class ResourceMUX(RtlResourceType): def __init__(self, bitWidth, inputs): self.bitWidth = bitWidth self.inputs = inputs def __repr__(self): return f"<{self.__class__.__name__:s} {self.bitWidth:d} bits, {self.inputs} inputs>" class ResourceFF(RtlResourceType): pass class ResourceLatch(RtlResourceType): pass class ResourceRAM(RtlResourceType): """ Specifier of type of RAM like memory """ def __init__(self, width, items, rwSync: int, rSync: int, wSync: int, rSync_wAsync: int, rwAsync: int, rAsync: int, wAsync: int, rAsync_wSync: int): """ :param width: width of word in RAM/ROM :param items: number of words in RAM/ROM :param rwSync: count of read + write synchronous ports :param rSync: count of read only synchronous ports :param wSync: count of write only synchronous ports :param rSync_wAsync: count of synchronous read + asynchronous write ports :param rwAsync: count of read + write asynchronous ports :param rAsync: count of read only asynchronous ports :param wAsync: count of write only asynchronous ports :param rAsync_wSync: count of asynchronous read + synchronous write ports """ self.width = width self.items = items self.rwSync = rwSync self.rSync = rSync self.wSync = wSync self.rSync_wAsync = rSync_wAsync self.rwAsync = rwAsync self.rAsync = rAsync self.wAsync = wAsync self.rAsync_wSync = rAsync_wSync def __hash__(self): return hash(( self.width, self.items, self.rwSync, self.rSync, self.wSync, self.rSync_wAsync, self.rwAsync, self.rAsync, self.wAsync, self.rAsync_wSync)) def __eq__(self, other): return isinstance(other, ResourceRAM) and ( self.width == other.width and self.items == other.items and self.rwSync == other.rwSync and self.rSync == other.rSync and self.wSync == other.wSync and self.rSync_wAsync == other.rSync_wAsync and self.rwAsync == other.rwAsync and self.rAsync == other.rAsync and self.wAsync == other.wAsync and self.rAsync_wSync == other.rAsync_wSync ) def __repr__(self): return ("<%s, %dbit x %d, syncPorts: (rw:%d, r:%d, w:%d), asyncPorts:" " (rw:%d, r:%d, w:%d), rSync_wAsyncPorts: %d," " rAsync_wSyncPorts: %d>") % ( self.__class__.__name__, self.width, self.items, self.rwSync, self.rSync, self.wSync, self.rwAsync, self.rAsync, self.wAsync, self.rSync_wAsync, self.rAsync_wSync ) ================================================ FILE: hwt/serializer/resourceAnalyzer/utils.py ================================================ from typing import Union from hwt.hdl.operator import HOperatorNode from hwt.hdl.statements.statement import HdlStatement from hwt.serializer.resourceAnalyzer.resourceTypes import \ ResourceFF, ResourceMUX, ResourceLatch, ResourceRAM from hwt.hwModule import HwModule from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class ResourceContext(): """ Container of informations about resources used in architecture :ivar ~.module: optional module for which is this context build :ivar ~.seen: set of seen objects :ivar ~.resources: dictionary {type of resource: cnt} :ivar ~.discoveredRamSignals: set of signals which seems to be some kind of RAM/ROM memory """ def __init__(self, module: HwModule): self.module = module self.seen = set() self.resources = {} # {RtlSignal or Statement or HOperatorNode: HwResource or typle (HwResource, cnt)} self.resource_for_object = {} # {(mem, addr, syncFlag, r/w): cnt} self.memories = {} def registerOperator(self, op: HOperatorNode): w = op.operands[0]._dtype.bit_length() res = self.resources k = (op.operator, w) res[k] = res.get(k, 0) + 1 self.resource_for_object[op] = k def registerMUX(self, stm: Union[HdlStatement, HOperatorNode], sig: RtlSignal, inputs_cnt: int): """ mux record is in format (self.MUX, n, m) where n is number of bits of this mux and m is number of possible inputs """ assert inputs_cnt > 1 res = self.resources w = sig._dtype.bit_length() k = (ResourceMUX, w, inputs_cnt) res[k] = res.get(k, 0) + 1 self.resource_for_object[(stm, sig)] = k def registerFF(self, ff): res = self.resources w = ff._dtype.bit_length() ffs = res.get(ResourceFF, 0) res[ResourceFF] = ffs + w self.resource_for_object[ff] = (ResourceFF, w) def registerLatch(self, latch: RtlSignal): res = self.resources w = latch._dtype.bit_length() latches = res.get(ResourceLatch, 0) res[ResourceLatch] = latches + w self.resource_for_object[latch] = (ResourceLatch, w) def registerRAM_write_port(self, mem: RtlSignal, addr: RtlSignal, synchronous: bool): res = self.memories addresses = res.get(mem, {}) res[mem] = addresses # [rSycn, wSync, rAsync, wAsync] portCnts = addresses.get(addr, [0, 0, 0, 0]) if synchronous: portCnts[1] += 1 else: portCnts[3] += 1 addresses[addr] = portCnts def registerRAM_read_port(self, mem: RtlSignal, addr: RtlSignal, synchronous: bool): res = self.memories addresses = res.get(mem, {}) res[mem] = addresses # [rSycn, wSync, rAsync, wAsync] portCnts = addresses.get(addr, [0, 0, 0, 0]) if synchronous: portCnts[0] += 1 else: portCnts[2] += 1 addresses[addr] = portCnts def finalize(self): """ Resolve ports of discovered memories """ ff_to_remove = 0 res = self.resources for m, addrDict in self.memories.items(): rwSyncPorts, rSyncPorts, wSyncPorts = 0, 0, 0 rwAsyncPorts, rAsyncPorts, wAsyncPorts = 0, 0, 0 rSync_wAsyncPorts, rAsync_wSyncPorts = 0, 0 for _, (rSync, wSync, rAsync, wAsync) in addrDict.items(): if rSync: ff_to_remove += rSync * m._dtype.element_t.bit_length() # resolve port count for this addr signal rwSync = min(rSync, wSync) rSync -= rwSync wSync -= rwSync rwAsync = min(rAsync, wAsync) rAsync -= rwAsync wAsync -= rwAsync rSync_wAsync = min(rSync, wAsync) rSync -= rSync_wAsync wAsync -= rSync_wAsync rAsync_wSync = min(rAsync, wSync) rAsync -= rAsync_wSync wSync -= rAsync_wSync # update port counts for mem rwSyncPorts += rwSync rSyncPorts += rSync wSyncPorts += wSync rwAsyncPorts += rwAsync rAsyncPorts += rAsync wAsyncPorts += wAsync rSync_wAsyncPorts += rSync_wAsync rAsync_wSyncPorts += rAsync_wSync k = ResourceRAM(m._dtype.element_t.bit_length(), int(m._dtype.size), rwSyncPorts, rSyncPorts, wSyncPorts, rSync_wAsyncPorts, rwAsyncPorts, rAsyncPorts, wAsyncPorts, rAsync_wSyncPorts) res[k] = res.get(k, 0) + 1 self.memories.clear() # remove register on read ports which will be merged into ram if ff_to_remove: ff_cnt = res[ResourceFF] ff_cnt -= ff_to_remove if ff_cnt: res[ResourceFF] = ff_cnt else: del res[ResourceFF] ================================================ FILE: hwt/serializer/serializer_config.py ================================================ from hwt.serializer.generic.to_hdl_ast import ToHdlAst class DummySerializerConfig(): """ The serializer which does not do any additional code transformations and does not produce any output. It is used to generate just internal representation of RTL code. """ fileExtension = None TO_HDL_AST = ToHdlAst TO_HDL = None TO_CONSTRAINTS = None ================================================ FILE: hwt/serializer/serializer_filter.py ================================================ from typing import Optional, Tuple from hwt.serializer.mode import _serializeExclude_eval from hwt.hwModule import HwModule class SerializerFilter(object): """ Base class for filters used to exclude some :class:`hwt.hwModule.HwModule` instances from target HDL (in order to prevent code duplication, archetype colisions etc.) This base implementation keeps track about others objects and calls _serializeDecision on the :class:`hwt.hwModule.HwModule` instance to decide if instance should be excluded. :ivar serializedClasses: dict {moduleCls : moduleObj} :ivar serializedConfiguredHwModules: (moduleCls, paramsValues) : moduleObj where paramsValues are named tuple name:value """ def __init__(self): self.serializedClasses = {} # type: Type[HwModule]: HwModule # (hwModuleCls, paramsValues) : unitObj # where paramsValues are dict name:value self.serializedConfiguredHwModules = {} def do_serialize(self, module: HwModule) -> Tuple[bool, Optional[HwModule]]: """ Decide if this module should be serialized or not eventually fix name to fit same already serialized module :param module: object to serialize """ assert isinstance(module, HwModule) sd = module._serializeDecision if sd is None: # the :class:`hwt.hwModule.HwModule` instance does not have any filter function return True, None else: # use HwModuleInstance filer function prevPriv = self.serializedClasses.get(module.__class__, None) do_serialize, nextPriv, replacement = sd(module, prevPriv) self.serializedClasses[module.__class__] = nextPriv return do_serialize, replacement class SerializerFilterAll(SerializerFilter): """ Ignore any serialization constraints and dump everything """ def do_serialize(self, module: HwModule) -> bool: return True, None class SerializerFilterDoNotExclude(SerializerFilter): """ Use all serialization specifications except @serializeExclude Useful when it is required to dump all components for sim etc. """ def do_serialize(self, module: HwModule) -> bool: orig = module._serializeDecision if orig is _serializeExclude_eval: module._serializeDecision = None try: return super(SerializerFilterDoNotExclude, self).do_serialize(module) finally: module._serializeDecision = orig ================================================ FILE: hwt/serializer/simModel/__init__.py ================================================ """ Sim model serializer serialize HDL objects to simulation model writen in python. For hwtSimApi.basic_rtl_simulator """ from hdlConvertorAst.to.basic_hdl_sim_model._main import ToBasicHdlSimModel from hwt.serializer.serializer_config import DummySerializerConfig from hwt.serializer.simModel.serializer import ToHdlAstSimModel class SimModelSerializer(DummySerializerConfig): fileExtension = '.py' TO_HDL_AST = ToHdlAstSimModel TO_HDL = ToBasicHdlSimModel ================================================ FILE: hwt/serializer/simModel/serializer.py ================================================ from copy import copy from typing import Optional, List from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlValueInt, HdlOp, \ HdlOpType from hdlConvertorAst.hdlAst._statements import HdlStmIf, HdlStmAssign, \ HdlStmProcess, HdlStmBlock from hdlConvertorAst.hdlAst._structural import HdlModuleDec, HdlModuleDef from hdlConvertorAst.to.basic_hdl_sim_model.keywords import SIMMODEL_KEYWORDS from hdlConvertorAst.translate.common.name_scope import LanguageKeyword, NameScope from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_getattr, \ hdl_map_asoc, hdl_call from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.serializer.generic.constant_cache import ConstantCache from hwt.serializer.generic.to_hdl_ast import ToHdlAst from hwt.serializer.simModel.tmpVarConstructorConstOnly import TmpVarConstructorConstOnly from hwt.serializer.simModel.types import ToHdlAstSimModel_types from hwt.serializer.simModel.value import ToHdlAstSimModel_value from hwt.mainBases import RtlSignalBase from hwtSimApi.basic_hdl_simulator.sim_utils import sim_eval_cond class ToHdlAstSimModel(ToHdlAstSimModel_value, ToHdlAstSimModel_types, ToHdlAst): """ Serializer which converts :class:`hwt.hwModule.HwModule` instances to simulator code """ _keywords_dict = {kw: LanguageKeyword() for kw in SIMMODEL_KEYWORDS} SIM_EVAL_COND = HdlValueId("sim_eval_cond", obj=sim_eval_cond) C = HdlValueId("c", obj=LanguageKeyword()) CVLD = HdlValueId("cVld", obj=LanguageKeyword()) TMP_VAR_CONSTRUCTOR = TmpVarConstructorConstOnly def __init__(self, name_scope: Optional[NameScope]=None): super(ToHdlAstSimModel, self).__init__(name_scope) self.currentHwModule = None self.stm_outputs = {} def as_hdl_HdlModuleDec(self, o: HdlModuleDec): # convert types, exprs # delete params because they should not be used in expressions and thus # are useless new_o = copy(o) new_o.params = [] new_o.ports = [self.as_hdl_HdlPortItem(p) for p in o.ports] return new_o def as_hdl_PortConnection(self, o: HdlPortItem): assert isinstance(o, HdlPortItem), o intern, outer = o.getInternSig(), o.getOuterSig() assert not intern._isUnnamedExpr, intern assert not outer._isUnnamedExpr, outer intern_hdl = HdlValueId(intern._name, obj=intern) outer_hdl = HdlValueId(outer._name, obj=outer) pm = hdl_map_asoc(intern_hdl, outer_hdl) return pm def as_hdl_HdlAssignmentContainer(self, a: HdlAssignmentContainer): dst, dst_indexes, src = self._as_hdl_HdlAssignmentContainer_auto_conversions(a) ev = HdlValueInt(int(a._event_dependent_from_branch == 0), None, None) if dst_indexes is not None: src = (src, dst_indexes, ev) else: src = (src, ev) hdl_dst = hdl_getattr(hdl_getattr(self.SELF_IO, dst._name), "val_next") hdl_a = HdlStmAssign(src, hdl_dst) hdl_a.is_blocking = dst.virtual_only return hdl_a def as_hdl_IfContainer_out_invalidate_section(self, outputs: List[RtlSignalBase], parent: IfContainer): outputInvalidateStms = [] for o in outputs: # [TODO] look up indexes indexes = None v = o._dtype.from_py(None) oa = HdlAssignmentContainer(v, o, indexes, virtual_only=True, parentStm=parent, event_dependent_from_branch=parent._event_dependent_from_branch) outputInvalidateStms.append(self.as_hdl_HdlAssignmentContainer(oa)) if len(outputInvalidateStms) == 1: return outputInvalidateStms[0] else: b = HdlStmBlock() b.body = outputInvalidateStms return b def as_hdl_IfContainer_cond_eval(self, cond): """ constructs condition evaluation statement c, cVld = sim_eval_cond(cond) """ c, cVld = self.C, self.CVLD cond = self.as_hdl_cond(cond, True) cond_eval = hdl_call(self.SIM_EVAL_COND, [cond]) cond_eval = HdlStmAssign(cond_eval, (c, cVld)) cond_eval.is_blocking = True return c, cVld, cond_eval def as_hdl_IfContainer(self, ifc: IfContainer) -> HdlStmIf: """ .. code-block:: python if cond: ... else: ... will become .. code-block:: python c, cVld = sim_eval_cond(cond) if not cVld: # ivalidate outputs elif c: ... # original if true branch else: ... # original if else brach """ invalidate_block = self.as_hdl_IfContainer_out_invalidate_section( ifc._outputs, ifc) c, cVld, cond_eval = self.as_hdl_IfContainer_cond_eval(ifc.cond) _if = HdlStmIf() res = HdlStmBlock() res.body = [cond_eval, _if] _if.cond = HdlOp(HdlOpType.NEG_LOG, [cVld, ]) _if.if_true = invalidate_block if_true = self.as_hdl_statements(ifc.ifTrue) _if.elifs.append((c, if_true)) elifs = iter(ifc.elIfs) for eif_c, eif_stms in elifs: c, cVld, cond_eval = self.as_hdl_IfContainer_cond_eval(eif_c) newIf = HdlStmIf() newIf.cond = HdlOp(HdlOpType.NEG_LOG, [cVld, ]) newIf.if_true = invalidate_block if_true = self.as_hdl_statements(eif_stms) newIf.elifs.append((c, if_true)) _if.if_false = HdlStmBlock() _if.if_false.body = [cond_eval, newIf] _if = newIf _if.if_false = self.as_hdl_statements(ifc.ifFalse) return res def as_hdl_SwitchContainer(self, sw: SwitchContainer) -> HdlStmIf: "switch -> if" switchOn = sw.switchOn def mkCond(c): return switchOn._eq(c) elIfs = [] for key, statements in sw.cases[1:]: elIfs.append((mkCond(key), statements)) ifFalse = sw.default topCond = mkCond(sw.cases[0][0]) topIf = IfContainer(topCond, ifTrue=sw.cases[0][1], ifFalse=ifFalse, elIfs=elIfs) topIf._sensitivity = sw._sensitivity topIf._inputs = sw._inputs topIf._outputs = sw._outputs return self.as_hdl_IfContainer(topIf) def sensitivityListItem(self, item, anyEventDependent): if isinstance(item, HOperatorNode): op = item.operator if op == HwtOps.RISING_EDGE: sens = HdlOpType.RISING elif op == HwtOps.FALLING_EDGE: sens = HdlOpType.FALLING else: raise TypeError("This is not an event sensitivity", op) return HdlOp(sens, [HdlValueId(item.operands[0]._name)]) else: return HdlValueId(item._name) def has_to_be_process(self, proc): return True def can_pop_process_wrap(self, statements, hasToBeVhdlProcess): return False def as_hdl_HdlStmCodeBlockContainer(self, proc: HdlStmCodeBlockContainer) -> HdlStmProcess: p = ToHdlAst.as_hdl_HdlStmCodeBlockContainer(self, proc) self.stm_outputs[p] = sorted( [HdlValueId(o._name, obj=o) for o in proc._outputs] ) return p def as_hdl_extraVarsInit(self, extraVars): return [] def _as_hdl_HdlModuleDef_body(self, *args) -> HdlModuleDef: orig_const_cache = self.constCache try: self.constCache = ConstantCache(self, self.tmpVars) return ToHdlAst._as_hdl_HdlModuleDef_body(self, *args) finally: self.constCache = orig_const_cache ================================================ FILE: hwt/serializer/simModel/tmpVarConstructorConstOnly.py ================================================ from hwt.serializer.generic.tmpVarConstructor import TmpVarConstructor from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class TmpVarConstructorConstOnly(TmpVarConstructor): def finish_var_init(self, var: RtlSignal): hdl = self.extraVarsHdl as_hdl = self.toHdlAst.as_hdl_HdlSignalItem(var, declaration=True) hdl.append(as_hdl) def sort_hdl_declarations_first(self): pass # always sorted ================================================ FILE: hwt/serializer/simModel/types.py ================================================ from hdlConvertorAst.hdlAst import HdlValueId, HdlValueInt from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call, \ hdl_getattr from hwt.hdl.types.bits import HBits from hwt.hdl.types.slice import HSlice from pyMathBitPrecise.bits3t import Bits3t class ToHdlAstSimModel_types(): """ part of ToHdlAstSimModel responsible for type serialization """ SELF = HdlValueId("self", obj=LanguageKeyword()) BITS3T = HdlValueId("Bits3t", obj=Bits3t) SLICE = HdlValueId("slice", obj=slice) def as_hdl_HdlType_bits(self, typ: HBits, declaration=False): assert not declaration w = typ.bit_length() if isinstance(w, int): pass else: w = int(w) return hdl_call(self.BITS3T, [HdlValueInt(w, None, None), HdlValueInt(int(bool(typ.signed)), None, None)]) def as_hdl_HdlType_slice(self, typ: HSlice, declaration=False): if declaration: raise NotImplementedError() else: return self.SLICE def as_hdl_HdlType_array(self, typ, declaration=False): if declaration: return super(ToHdlAstSimModel_types, self).as_hdl_HdlType_array(typ, declaration=declaration) else: t_name = self.name_scope.get_object_name(typ) return hdl_getattr(self.SELF, t_name) def as_hdl_HdlType_enum(self, typ, declaration=False): if declaration: return super(ToHdlAstSimModel_types, self).as_hdl_HdlType_enum(typ, declaration=True) else: t_name = self.name_scope.get_object_name(typ) return hdl_getattr(self.SELF, t_name) ================================================ FILE: hwt/serializer/simModel/value.py ================================================ from typing import Union from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlOp, HdlOpType from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call, \ hdl_getattr from hwt.code import Concat from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import BIT from hwt.hdl.types.enum import HEnum from hwt.hdl.types.enumConst import HEnumConst from hwt.hdl.variables import HdlSignalItem from hwt.pyUtils.typingFuture import override from hwt.serializer.generic.ops import HWT_TO_HDLCONVERTOR_OPS from hwt.serializer.generic.value import ToHdlAst_Value from pyMathBitPrecise.array3t import Array3val from pyMathBitPrecise.bits3t import Bits3val, Bits3t, bitsBitOp__lshr, \ bitsBitOp__rol, bitsBitOp__ror, bitsBitOp__ashr zero, one = BIT.from_py(0), BIT.from_py(1) class ToHdlAstSimModel_value(ToHdlAst_Value): Bits3val = HdlValueId("Bits3val", obj=Bits3val) Bits3t = HdlValueId("Bits3t", obj=Bits3t) SELF = HdlValueId("self", obj=LanguageKeyword()) Array3val = HdlValueId("Array3val", obj=Array3val) SLICE = HdlValueId("slice", obj=slice) TRUE = HdlValueId("True", obj=True) FALSE = HdlValueId("False", obj=False) Bits3val = HdlValueId("Bits3val", obj=Bits3val) ABits3t = HdlValueId("Bits3t", obj=Bits3t) SELF_IO = hdl_getattr(HdlValueId("self", obj=LanguageKeyword()), "io") CONCAT = HdlValueId("Concat", obj=Concat) FN_bitsBitOp__ashr = HdlValueId("bitsBitOp__ashr", obj=bitsBitOp__ashr) FN_bitsBitOp__lshr = HdlValueId("bitsBitOp__lshr", obj=bitsBitOp__lshr) FN_bitsBitOp__rol = HdlValueId("bitsBitOp__rol", obj=bitsBitOp__rol) FN_bitsBitOp__ror = HdlValueId("bitsBitOp__ror", obj=bitsBitOp__ror) op_transl_dict = { **HWT_TO_HDLCONVERTOR_OPS, HwtOps.INDEX: HdlOpType.INDEX, } _cast_ops = { HwtOps.BitsAsSigned, HwtOps.BitsAsUnsigned, HwtOps.BitsAsVec, } def is_suitable_for_const_extract(self, val: HConst): return not isinstance(val._dtype, HEnum) or val.vld_mask == 0 @override def as_hdl_HdlSignalItem(self, si: Union[HdlSignalItem, HdlIdDef], declaration=False): if not declaration and not si._isUnnamedExpr: if si._const: return hdl_getattr(self.SELF, si._name) else: return hdl_getattr(hdl_getattr(self.SELF_IO, si._name), "val") else: return super(ToHdlAstSimModel_value, self).as_hdl_HdlSignalItem( si, declaration=declaration) @override def as_hdl_HBitsConst(self, val: HBitsConst): dtype = val._dtype as_hdl_int = self.as_hdl_int t = hdl_call(self.Bits3t, [ as_hdl_int(dtype.bit_length()), as_hdl_int(int(bool(dtype.signed)))]) return hdl_call(self.Bits3val, [t, as_hdl_int(val.val), as_hdl_int(val.vld_mask)]) def as_hdl_HDictConst(self, val): return { self.as_hdl_int(int(k)): self.as_hdl_Value(v) for k, v in val.items() } @override def as_hdl_HArrayConst(self, val): return hdl_call(self.Array3val, [ self.as_hdl_HdlType(val._dtype), self.as_hdl_HDictConst(val.val), self.as_hdl_int(val.vld_mask) ]) @override def as_hdl_HSliceConst(self, val): args = ( val.val.start, val.val.stop, val.val.step ) return hdl_call(self.SLICE, [self.as_hdl_int(int(a)) for a in args]) @override def as_hdl_HEnumConst(self, val: HEnumConst): t_name = self.name_scope.get_object_name(val._dtype) if val.vld_mask: name = self.name_scope.get_object_name(val) return hdl_getattr(hdl_getattr(self.SELF, t_name), name) else: return hdl_call(hdl_getattr(hdl_getattr(self.SELF, t_name), "from_py"), [None, ]) @override def as_hdl_HOperatorNode(self, op: HOperatorNode): ops = op.operands o = op.operator if o == HwtOps.EQ: op0 = self.as_hdl_Value(ops[0]) op1 = self.as_hdl_Value(ops[1]) return hdl_call(hdl_getattr(op0, "_eq"), [op1, ]) elif o == HwtOps.TERNARY: if ops[1] == one and ops[2] == zero: # ignore redundant x ? 1 : 0 return self.as_hdl_cond(ops[0], True) else: op0 = self.as_hdl_cond(ops[0], True) op1 = self.as_hdl_Value(ops[1]) op2 = self.as_hdl_Value(ops[2]) return hdl_call(hdl_getattr(op0, "_ternary"), [op1, op2]) elif o == HwtOps.RISING_EDGE or o == HwtOps.FALLING_EDGE: if o == HwtOps.RISING_EDGE: fn = "_onRisingEdge" else: fn = "_onFallingEdge" op0 = self.as_hdl_Value(ops[0]) # pop .val op0 = op0.ops[0] return hdl_call(hdl_getattr(op0, fn), []) elif o in self._cast_ops: op0, = ops do_cast = bool(op0._dtype.signed) != bool(op.result._dtype.signed) op_hdl = self.as_hdl_Value(op0) if do_cast: if bool(op.result._dtype.signed): sign = self.TRUE else: sign = self.FALSE return hdl_call(hdl_getattr(op_hdl, "_cast_sign"), [sign, ]) else: return op_hdl elif o == HwtOps.CONCAT: return hdl_call(hdl_getattr(self.as_hdl_Value(ops[0]), "_concat"), [self.as_hdl_Value(ops[1]), ]) elif o == HwtOps.ZEXT or o == HwtOps.SEXT or o == HwtOps.TRUNC: v, newWidth = ops newWidth = int(newWidth) v = self.as_hdl_Value(v) op1 = self.as_hdl_int(newWidth) fnName = "_zext" if o == HwtOps.ZEXT else \ "_sext" if o == HwtOps.SEXT else \ "_trunc" return hdl_call(hdl_getattr(v, fnName), [op1, ]) elif o == HwtOps.EQ: return hdl_call(hdl_getattr(self.as_hdl_Value(ops[0]), "_eq"), [self.as_hdl_Value(ops[1]), ]) else: _o = o.hdlConvertoAstOp if _o is None: o = self.op_transl_dict[o] else: o = _o if o == HdlOpType.SRL and isinstance(ops[0]._dtype, HBits): return hdl_call(self.FN_bitsBitOp__lshr, [self.as_hdl_Value(o2) for o2 in ops]) elif o == HdlOpType.SRA and isinstance(ops[0]._dtype, HBits): return hdl_call(self.FN_bitsBitOp__ashr, [self.as_hdl_Value(o2) for o2 in ops]) elif o == HdlOpType.ROL and isinstance(ops[0]._dtype, HBits): return hdl_call(self.FN_bitsBitOp__rol, [self.as_hdl_Value(o2) for o2 in ops]) elif o == HdlOpType.ROR and isinstance(ops[0]._dtype, HBits): return hdl_call(self.FN_bitsBitOp__ror, [self.as_hdl_Value(o2) for o2 in ops]) return HdlOp(o, [self.as_hdl_Value(o2) for o2 in ops]) ================================================ FILE: hwt/serializer/store_manager.py ================================================ from io import StringIO import os from typing import Type, Optional, Union from hdlConvertorAst.hdlAst import HdlModuleDef from hdlConvertorAst.hdlAst._bases import iHdlObj from hdlConvertorAst.hdlAst._structural import HdlModuleDec from hdlConvertorAst.translate.common.name_scope import NameScope from hwt.hwModule import HdlConstraintList from hwt.pyUtils.setList import SetList from hwt.serializer.serializer_config import DummySerializerConfig from hwt.serializer.serializer_filter import SerializerFilter class StoreManager(object): """ A base class for an objects which manage how the output of the serialization is stored by serializer_cls """ def __init__(self, serializer_cls: DummySerializerConfig, _filter: Type["SerializerFilter"]=None, name_scope: Optional[NameScope]=None): self.serializer_cls = serializer_cls self.as_hdl_ast = serializer_cls.TO_HDL_AST(name_scope=name_scope) self.name_scope = self.as_hdl_ast.name_scope if _filter is None: _filter = SerializerFilter() self.filter = _filter def hierarchy_push(self, obj: Union[HdlModuleDec, HdlModuleDef]) -> NameScope: c = self.name_scope.level_push(obj.name) self.name_scope = c return c def hierarchy_pop(self, obj: Union[HdlModuleDec, HdlModuleDef]) -> NameScope: p = self.name_scope.parent assert p is not None self.name_scope = p return p def write(self, obj: Union[iHdlObj, HdlConstraintList]): pass class SaveToStream(StoreManager): """ Store all produced code to an output stream """ def __init__(self, serializer_cls: DummySerializerConfig, stream: StringIO, _filter: "SerializerFilter"=None, name_scope: Optional[NameScope]=None): super(SaveToStream, self).__init__( serializer_cls, _filter=_filter, name_scope=name_scope) self.stream = stream ser = self.ser = self.serializer_cls.TO_HDL(self.stream) if hasattr(ser, "stm_outputs"): ser.stm_outputs = self.as_hdl_ast.stm_outputs def write(self, obj: Union[iHdlObj, HdlConstraintList]): self.as_hdl_ast.name_scope = self.name_scope if isinstance(obj, HdlConstraintList): if self.serializer_cls.TO_CONSTRAINTS is not None: to_constr = self.serializer_cls.TO_CONSTRAINTS(self.stream) to_constr.visit_HdlConstraintList(obj) else: hdl = self.as_hdl_ast.as_hdl(obj) self.ser.visit_iHdlObj(hdl) class SaveToFilesFlat(StoreManager): """ Store all produced code to a single directory, file per component. """ def __init__(self, serializer_cls: DummySerializerConfig, root: str, _filter: "SerializerFilter"=None, name_scope: Optional[NameScope]=None): super(SaveToFilesFlat, self).__init__( serializer_cls, _filter=_filter, name_scope=name_scope) self.root = root self.files = SetList() self.module_path_prefix = None os.makedirs(root, exist_ok=True) def write(self, obj: Union[iHdlObj, HdlConstraintList]): if isinstance(obj, HdlConstraintList): f_name = "constraints" + self.serializer_cls.TO_CONSTRAINTS.fileExtension else: if isinstance(obj, HdlModuleDef): name = obj.module_name.val else: name = obj.name f_name = name + self.serializer_cls.fileExtension fp = os.path.join(self.root, f_name) if fp in self.files: m = 'a' else: m = 'w' self.files.append(fp) with open(fp, m) as f: s = SaveToStream(self.serializer_cls, f, self.filter, self.name_scope) s.ser.module_path_prefix = self.module_path_prefix s.write(obj) class SaveToSingleFiles(StoreManager): """ Store all produced code to a single directory, all component source code to single file and all constrains to single file. """ def __init__(self, serializer_cls: DummySerializerConfig, root: str, name: str, _filter: "SerializerFilter"=None, name_scope: Optional[NameScope]=None): super(SaveToSingleFiles, self).__init__( serializer_cls, _filter=_filter, name_scope=name_scope) self.root = root self.comp_name = name self.file_const = os.path.join(self.root, self.comp_name + self.serializer_cls.TO_CONSTRAINTS.fileExtension) self.file_src = os.path.join(self.root, self.comp_name + self.serializer_cls.fileExtension) self.files = SetList() self.module_path_prefix = None os.makedirs(root, exist_ok=True) def write(self, obj: Union[iHdlObj, HdlConstraintList]): if isinstance(obj, HdlConstraintList): f_name = self.file_const else: f_name = self.file_src if f_name not in self.files: m = 'w' self.files.append(f_name) else: m = 'a' with open(f_name, m) as f: s = SaveToStream(self.serializer_cls, f, self.filter, self.name_scope) s.ser.module_path_prefix = self.module_path_prefix s.write(obj) ================================================ FILE: hwt/serializer/systemC/__init__.py ================================================ """ SystemC serializer serializes HDL objects to systemC code. """ from hdlConvertorAst.to.systemc._main import ToSystemc from hwt.serializer.serializer_config import DummySerializerConfig from hwt.serializer.systemC.serializer import ToHdlAstSystemC from hwt.serializer.xdc.serializer import XdcSerializer class SystemCSerializer(DummySerializerConfig): fileExtension = '.cpp' TO_HDL_AST = ToHdlAstSystemC TO_HDL = ToSystemc TO_CONSTRAINTS = XdcSerializer ================================================ FILE: hwt/serializer/systemC/expr.py ================================================ from typing import Union from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlOp, HdlOpType from hdlConvertorAst.to.hdlUtils import bit_string from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call, \ hdl_getattr from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.types.bitConstFunctions import AnyHBitsValue from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import SLICE from hwt.hdl.types.enumConst import HEnumConst from hwt.hdl.variables import HdlSignalItem from hwt.pyUtils.typingFuture import override from hwt.serializer.generic.value import ToHdlAst_Value from hwt.serializer.hwt.ops import ToHdlAstHwt_ops from hwt.serializer.systemC.utils import systemCTypeOfSig from hwt.serializer.verilog.context import SignalTypeSwap from hwt.serializer.verilog.ops import ToHdlAstVerilog_ops from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class ToHdlAstSystemC_expr(ToHdlAst_Value): static_cast = HdlValueId("static_cast", obj=LanguageKeyword()) op_transl_dict = ToHdlAstVerilog_ops.op_transl_dict def as_hdl_Value(self, v): if isinstance(v, tuple): return tuple((self.as_hdl(o2) for o2 in v)) return super(ToHdlAstSystemC_expr, self).as_hdl_Value(v) def as_hdl_operand(self, operand: Union[RtlSignal, HConst], i: int, operator: HOperatorNode): return self.as_hdl(operand) def as_hdl_HOperatorNode_INDEX(self, op: HOperatorNode): ops = op.operands assert len(ops) == 2 o0, o1 = ops if o1._dtype == SLICE: # index to .range(x, y) o0_hdl = self.as_hdl_operand(o0, 0, op) o0_hdl = hdl_getattr(o0_hdl, "range") return hdl_call(o0_hdl, [self.as_hdl_Value(o1.val.start), self.as_hdl_Value(o1.val.stop)]) else: op0_t = ops[0]._dtype if isinstance(op0_t, HBits) and op0_t.bit_length() == 1 and not op0_t.force_vector: assert int(ops[1]) == 0, ops # drop whole index operator when indexing on 1b vector return self.as_hdl_operand(ops[0], 0, op) _o = self.op_transl_dict[op.operator] res = HdlOp(_o, [self.as_hdl_operand(o2, i, op) for i, o2 in enumerate(ops)]) op0_t = ops[0]._dtype if isinstance(op0_t, HBits) and op0_t.signed: # cast to signed if necesary t = self.as_hdl_HdlType(op.result._dtype) return hdl_call( HdlOp(HdlOpType.PARAMETRIZATION, [self.static_cast, t]), [res, ]) return res def as_hdl_HOperatorNode_BITSCAST(self, op: HOperatorNode): ops = op.operands if op.operator == HwtOps.TRUNC: assert len(ops) == 2, ops else: assert len(ops) == 1, ops t = self.as_hdl_HdlType(op.result._dtype) return hdl_call( HdlOp(HdlOpType.PARAMETRIZATION, [self.static_cast, t]), [self.as_hdl_Value(ops[0]), ]) def as_hdl_HOperatorNode_TERNARY(self, op: HOperatorNode, ops: list[AnyHBitsValue]): return ToHdlAstVerilog_ops.as_hdl_HOperatorNode_TERNARY(self, op, ops) def as_hdl_HOperatorNode(self, op: HOperatorNode): ops = op.operands o = op.operator if o == HwtOps.INDEX: return self.as_hdl_HOperatorNode_INDEX(op) elif o in ToHdlAstHwt_ops._cast_ops or o == HwtOps.TRUNC: return self.as_hdl_HOperatorNode_BITSCAST(op) elif o == HwtOps.CONCAT: isNew, o = self.tmpVars.create_var_cached("tmpConcat_", op.result._dtype, postponed_init=True, extra_args=(HwtOps.CONCAT, op.result)) if isNew: o._rtlDrivers.append(HdlAssignmentContainer(op, o, virtual_only=True)) self.tmpVars.finish_var_init(o) return self.as_hdl(o) elif o == HwtOps.SEXT or o == HwtOps.ZEXT: t = self.as_hdl_HdlType(op.result._dtype) isSigned = op.result._dtype.signed isSignedExt = o == HwtOps.SEXT _op0 = self.as_hdl_Value(ops[0]) if isSignedExt != bool(isSigned): # must cast first to signed, then to larger result type # the sign/unsigned ext is driven by src type # https://docs.oracle.com/cd/E19205-01/819-5265/bjamz/index.html signFixedTy = self.as_hdl_HdlType(ops[0]._dtype._createMutated(signed=isSignedExt)) _op0 = hdl_call( HdlOp(HdlOpType.PARAMETRIZATION, [self.static_cast, signFixedTy]), [_op0, ]) res = hdl_call( HdlOp(HdlOpType.PARAMETRIZATION, [self.static_cast, t]), [_op0, ]) return res else: return ToHdlAstVerilog_ops.as_hdl_HOperatorNode(self, op) @override def as_hdl_HdlSignalItem(self, si: HdlSignalItem, declaration=False): if declaration: sigType = systemCTypeOfSig(si) with SignalTypeSwap(self, sigType): return ToHdlAst_Value.as_hdl_HdlSignalItem(self, si, declaration=True) else: if si._isUnnamedExpr and si._rtlObjectOrigin is not None: return self.as_hdl(si._rtlObjectOrigin) else: sigType = systemCTypeOfSig(si) _si = HdlValueId(si._name, obj=si) if self._in_sensitivity_list or self._is_target or sigType is SIGNAL_TYPE.REG: return _si else: return hdl_call(hdl_getattr(_si, "read"), []) def as_hdl_HBitsConst(self, val): t = val._dtype w = t.bit_length() _v = bit_string(val.val, w, val.vld_mask) t = self.as_hdl_HdlType_bits(HBits(w, signed=t.signed)) return hdl_call(t, [_v, ]) def as_hdl_HEnumConst(self, val: HEnumConst): i = val._dtype._allValues.index(val.val) assert i >= 0 return self.as_hdl_int(i) def as_hdl_HArrayConst(self, val): return [self.as_hdl_Value(v) for v in val] ================================================ FILE: hwt/serializer/systemC/serializer.py ================================================ from copy import copy from typing import Optional from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlOp, HdlOpType from hdlConvertorAst.hdlAst._structural import HdlModuleDec, HdlCompInst from hdlConvertorAst.to.systemc.keywords import SYSTEMC_KEYWORDS from hdlConvertorAst.translate.common.name_scope import LanguageKeyword, NameScope from hwt.hdl.portItem import HdlPortItem from hwt.hwIOs.std import HwIOClk from hwt.serializer.generic.to_hdl_ast import ToHdlAst, \ HWT_TO_HDLCONVEROTR_DIRECTION from hwt.serializer.simModel.serializer import ToHdlAstSimModel from hwt.serializer.systemC.expr import ToHdlAstSystemC_expr from hwt.serializer.systemC.statements import ToHdlAstSystemC_statements from hwt.serializer.systemC.type import ToHdlAstSystemC_type from ipCorePackager.constants import DIRECTION class ToHdlAstSystemC(ToHdlAstSystemC_expr, ToHdlAstSystemC_type, ToHdlAstSystemC_statements, ToHdlAst): """ Serialized used to convert HWT design to SystemC code """ _keywords_dict = {kw: LanguageKeyword() for kw in SYSTEMC_KEYWORDS} sc_in_clk = HdlValueId("sc_in_clk", obj=LanguageKeyword()) sc_out_clk = HdlValueId("sc_out_clk", obj=LanguageKeyword()) sc_inout_clk = HdlValueId("sc_inout_clk", obj=LanguageKeyword()) sc_in = HdlValueId("sc_in", obj=LanguageKeyword()) sc_out = HdlValueId("sc_out", obj=LanguageKeyword()) sc_inout = HdlValueId("sc_inout", obj=LanguageKeyword()) def __init__(self, name_scope: Optional[NameScope]=None): ToHdlAst.__init__(self, name_scope=name_scope) self._is_target = False self._in_sensitivity_list = False self.signalType = None def as_hdl_HdlModuleDec(self, o: HdlModuleDec): return ToHdlAstSimModel.as_hdl_HdlModuleDec(self, o) def as_hdl_HdlPortItem(self, o: HdlPortItem): i = o.getInternSig()._hwIO d = o.direction if isinstance(i, HwIOClk): assert i._dtype.bit_length() == 1, i if d == DIRECTION.IN: t = self.sc_in_clk elif d == DIRECTION.OUT: t = self.sc_out_clk elif d == DIRECTION.INOUT: t = self.sc_inout_clk else: raise ValueError(d) else: if d == DIRECTION.IN: pt = self.sc_in elif d == DIRECTION.OUT: pt = self.sc_out elif d == DIRECTION.INOUT: pt = self.sc_inout else: raise ValueError(d) t = self.as_hdl_HdlType(o._dtype) t = HdlOp(HdlOpType.PARAMETRIZATION, [pt, t]) var = HdlIdDef() var.direction = HWT_TO_HDLCONVEROTR_DIRECTION[o.direction] s = o.getInternSig() var.name = s._name var.origin = o var.type = t return var def as_hdl_HdlCompInst(self, o: HdlCompInst) -> HdlCompInst: new_o = copy(o) new_o.param_map = [] orig_is_target = self._is_target try: self._is_target = True port_map = [] for pi in o.port_map: pm = self.as_hdl_PortConnection(pi) port_map.append(pm) new_o.port_map = port_map finally: self._is_target = orig_is_target return new_o ================================================ FILE: hwt/serializer/systemC/statements.py ================================================ from hdlConvertorAst.hdlAst._bases import iHdlStatement from hdlConvertorAst.hdlAst._statements import HdlStmAssign, HdlStmCase, \ HdlStmBlock, HdlStmBreak from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_getattr, \ hdl_call from hwt.doc_markers import internal from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL from hwt.hdl.variables import HdlSignalItem from hwt.serializer.exceptions import SerializerException from hwt.serializer.systemC.utils import systemCTypeOfSig from hwt.serializer.verilog.value import ToHdlAstVerilog_Value class ToHdlAstSystemC_statements(): def has_to_be_process(self, proc: iHdlStatement): return True def can_pop_process_wrap(self, statements, hasToBeVhdlProcess): return False def sensitivityListItem(self, item, anyIsEventDependent): orig_in_sensitivity_list = self._in_sensitivity_list try: self._in_sensitivity_list = True return ToHdlAstVerilog_Value.sensitivityListItem( self, item, anyIsEventDependent) finally: self._in_sensitivity_list = orig_in_sensitivity_list def as_hdl_SwitchContainer(self, sw: SwitchContainer) -> HdlStmCase: """ Same as parent as_hdl_SwitchContainer but add "break" to all cases """ sw_hdl = super(ToHdlAstSystemC_statements, self).as_hdl_SwitchContainer(sw) new_cases = [] for c, stm in sw_hdl.cases: if not isinstance(stm, HdlStmBlock): _stm = HdlStmBlock() _stm.body.append(stm) stm = _stm stm.body.append(HdlStmBreak()) new_cases.append((c, stm)) sw_hdl.cases = new_cases return sw_hdl @internal def _as_hdl_HdlAssignmentContainer(self, dst, typeOfDst, src): orig_is_target = self._is_target try: self._is_target = True dst_hdl = self.as_hdl(dst) finally: self._is_target = orig_is_target src_hdl = self.as_hdl_Value(src) if typeOfDst == SIGNAL_TYPE.REG: return HdlStmAssign(src_hdl, dst_hdl) else: return hdl_call(hdl_getattr(dst_hdl, "write"), [src_hdl, ]) def as_hdl_HdlAssignmentContainer(self, a: HdlAssignmentContainer): dst = a.dst assert isinstance(dst, HdlSignalItem) # assert not dst.virtual_only, "should not be required" if a.indexes is not None: for i in a.indexes: dst = dst[i] typeOfDst = systemCTypeOfSig(dst) if dst.virtual_only and isinstance(a.src, HOperatorNode): assert a.src.operator == HwtOps.CONCAT return self._as_hdl_HdlAssignmentContainer(dst, typeOfDst, a.src.operands) if dst._dtype == a.src._dtype or ( isinstance(dst._dtype, HBits) and a.src._dtype == BOOL): return self._as_hdl_HdlAssignmentContainer(dst, typeOfDst, a.src) else: raise SerializerException("%r <= %r is not valid assignment\n" " because types are different (%r; %r) " % (dst, a.src, dst._dtype, a.src._dtype)) ================================================ FILE: hwt/serializer/systemC/type.py ================================================ from hdlConvertorAst.hdlAst._expr import HdlOp, HdlOpType, HdlValueId from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_index from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import FLOAT64 from hwt.hdl.types.enum import HEnum from hwt.hdl.types.float import HFloat from hwt.hdl.types.hdlType import MethodNotOverloaded from hwt.serializer.verilog.types import ToHdlAstVerilog_types class ToHdlAstSystemC_type(): sc_int = HdlValueId("sc_int", obj=LanguageKeyword()) sc_uint = HdlValueId("sc_uint", obj=LanguageKeyword()) sc_bigint = HdlValueId("sc_bigint", obj=LanguageKeyword()) sc_biguint = HdlValueId("sc_biguint", obj=LanguageKeyword()) sc_signal = HdlValueId("sc_signal", obj=LanguageKeyword()) STRING = HdlOp(HdlOpType.DOUBLE_COLON, HdlValueId("string", obj=LanguageKeyword())) def does_type_requires_extra_def(self, t, other_types): try: return t._as_hdl_requires_def(self, other_types) except MethodNotOverloaded: pass return False def as_hdl_HdlType_str(self, typ, declaration=False): assert not declaration return self.STRING def as_hdl_HdlType_bits(self, typ: HBits, declaration=False): if declaration: raise NotImplementedError() w = typ.bit_length() if w <= 64: if typ.signed: typeBaseName = self.sc_int else: typeBaseName = self.sc_uint else: if typ.signed: typeBaseName = self.sc_bigint else: typeBaseName = self.sc_biguint t = HdlOp(HdlOpType.PARAMETRIZATION, [typeBaseName, self.as_hdl_int(w)]) if self.signalType == SIGNAL_TYPE.WIRE: t = HdlOp(HdlOpType.PARAMETRIZATION, [self.sc_signal, t]) return t def as_hdl_HdlType_array(self, typ: HArray, declaration=False): if declaration: return super(ToHdlAstSystemC_type, self).as_hdl_HdlType_array( self, typ, declaration=declaration) else: _int = self.as_hdl_int size = _int(int(typ.size)) return hdl_index(self.as_hdl_HdlType(typ.element_t), size) def as_hdl_HdlType_enum(self, typ: HEnum, declaration=False): return ToHdlAstVerilog_types.as_hdl_HdlType_enum( self, typ, declaration=declaration) def as_hdl_HdlType_float(self, typ: HFloat, declaration=False): if typ == FLOAT64: return HdlValueId("double") else: raise NotImplementedError(typ) ================================================ FILE: hwt/serializer/systemC/utils.py ================================================ from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.statement import HdlStatement from hwt.pyUtils.arrayQuery import arr_any from hwt.hwParam import HwParam from ipCorePackager.constants import DIRECTION def systemCTypeOfSig(s): """ Check if is register or wire """ if isinstance(s, HdlIdDef): s = s.origin if isinstance(s, HdlPortItem): if s.direction == DIRECTION.IN or s.direction == DIRECTION.INOUT: return SIGNAL_TYPE.PORT_WIRE t = systemCTypeOfSig(s.getInternSig()) if t == SIGNAL_TYPE.WIRE: return SIGNAL_TYPE.PORT_WIRE elif t == SIGNAL_TYPE.REG: return SIGNAL_TYPE.PORT_REG else: raise ValueError(t) elif isinstance(s, HwParam): return SIGNAL_TYPE.PORT_REG elif s._const or\ arr_any(s._rtlDrivers, lambda d: isinstance(d, HdlStatement) and d._event_dependent_from_branch is not None): return SIGNAL_TYPE.REG else: return SIGNAL_TYPE.WIRE ================================================ FILE: hwt/serializer/utils.py ================================================ from natsort.natsort import natsort_keygen from hwt.doc_markers import internal from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.statement import HdlStatement from hwt.mainBases import RtlSignalBase @internal def getMaxStmIdForStm(stm: HdlStatement): """ Get maximum _instId from all assignments in statement, used for sorting of processes in architecture """ maxId = 0 if isinstance(stm, HdlAssignmentContainer): return stm._instId else: for _stm in stm._iter_stms(): maxId = max(maxId, getMaxStmIdForStm(_stm)) return maxId _natsort_key = natsort_keygen() def RtlSignal_sort_key(s: RtlSignalBase): return (_natsort_key(s._name), s._instId) def HdlStatement_sort_key(stm: HdlStatement): if isinstance(stm, HdlStmCodeBlockContainer) and stm.name is not None: return (_natsort_key(stm.name), getMaxStmIdForStm(stm)) else: return (_natsort_key(""), getMaxStmIdForStm(stm)) ================================================ FILE: hwt/serializer/verilog/__init__.py ================================================ """ Verilog serializer serializes HDL objects to verilog code. """ from hdlConvertorAst.to.verilog.verilog2005 import ToVerilog2005 from hwt.serializer.serializer_config import DummySerializerConfig from hwt.serializer.verilog.serializer import ToHdlAstVerilog from hwt.serializer.xdc.serializer import XdcSerializer class VerilogSerializer(DummySerializerConfig): fileExtension = '.v' TO_HDL_AST = ToHdlAstVerilog TO_HDL = ToVerilog2005 TO_CONSTRAINTS = XdcSerializer ================================================ FILE: hwt/serializer/verilog/context.py ================================================ from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE class SignalTypeSwap(): """ An object which is used as a context manager for signalType inside of :class:`hwt.serializer.verilog.serializer.ToHdlAstVerilog` """ def __init__(self, ctx, signalType: SIGNAL_TYPE): self.ctx = ctx self.signalType = signalType def __enter__(self): self.orig = self.ctx.signalType self.ctx.signalType = self.signalType def __exit__(self, exc_type, exc_val, exc_tb): self.ctx.signalType = self.orig ================================================ FILE: hwt/serializer/verilog/ops.py ================================================ from builtins import isinstance from typing import Union from hdlConvertorAst.hdlAst import HdlValueInt from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlOpType, HdlOp from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call from hwt.hdl.commonConstants import b1, b0 from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.types.bitConstFunctions import AnyHBitsValue from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import INT, SLICE from hwt.serializer.exceptions import UnsupportedEventOpErr from hwt.serializer.generic.ops import HWT_TO_HDLCONVERTOR_OPS from hwt.serializer.vhdl.ops import matchFullWidthMul from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class ToHdlAstVerilog_ops(): SIGNED = HdlValueId("$signed", obj=LanguageKeyword()) UNSIGNED = HdlValueId("$unsigned", obj=LanguageKeyword()) op_transl_dict = { **HWT_TO_HDLCONVERTOR_OPS, HwtOps.INDEX: HdlOpType.INDEX, } def _operandIsAnotherOperand(self, operand): return isinstance(operand, RtlSignal) and operand._isUnnamedExpr\ and isinstance(operand._rtlObjectOrigin, HOperatorNode) def as_hdl_operand(self, operand: Union[RtlSignal, HConst], i: int, operator: HOperatorNode): # [TODO] if operand is concatenation and parent operator # is not concatenation operand should be extracted # as tmp variable # * maybe flatten the concatenations if operator.operator != HwtOps.CONCAT\ and self._operandIsAnotherOperand(operand)\ and operand._rtlObjectOrigin.operator == HwtOps.CONCAT: _, tmpVar = self.tmpVars.create_var_cached("tmp_concat_", operand._dtype, def_val=operand) # HdlAssignmentContainer(tmpVar, operand, virtual_only=True) operand = tmpVar elif operator.operator in (HwtOps.INDEX, HwtOps.TRUNC) and i == 0 and self._operandIsAnotherOperand(operand): tmpVarName = "tmp_index_" if operator.operator == HwtOps.INDEX else "tmp_trunc_" _, tmpVar = self.tmpVars.create_var_cached(tmpVarName, operand._dtype, def_val=operand) operand = tmpVar elif self._operandIsAnotherOperand(operand)\ and operand._rtlObjectOrigin.operator == HwtOps.MUL: _, tmpVar = self.tmpVars.create_var_cached("tmp_mul_", operand._dtype, def_val=operand) operand = tmpVar oper = operator.operator width = None if not isinstance(operand, RtlSignal) and operand._dtype == INT and\ oper not in [HwtOps.BitsAsUnsigned, HwtOps.BitsAsVec, HwtOps.BitsAsSigned, HwtOps.INDEX] and\ operator.result is not None and\ not operator.result._dtype == INT: # have to lock the width for o in operator.operands: try: bl = o._dtype.bit_length except AttributeError: bl = None if bl is not None: width = bl() break assert width is not None, (operator, operand) hdl_op = self.as_hdl_Value(operand) if width is not None: if isinstance(hdl_op, HdlValueInt): assert isinstance(width, int), width hdl_op.bits = width else: return HdlOp(HdlOpType.APOSTROPHE, [self.as_hdl_int(width), hdl_op]) return hdl_op def as_hdl_HOperatorNode_TERNARY(self, op: HOperatorNode, ops: list[AnyHBitsValue]): if ops[1] == b1 and ops[2] == b0: # ignore redundant x ? 1 : 0 return self.as_hdl_cond(ops[0], True) else: op0 = self.as_hdl_cond(ops[0], True) op1 = self.as_hdl_operand(ops[1], 1, op) op2 = self.as_hdl_operand(ops[2], 2, op) return HdlOp(HdlOpType.TERNARY, [op0, op1, op2]) def as_hdl_HOperatorNode_BITSCAST(self, op: HOperatorNode): op0, = op.operands do_cast = bool(op0._dtype.signed) != bool(op.result._dtype.signed) op_hdl = self.as_hdl_operand(op0, 0, op) if do_cast: if bool(op.result._dtype.signed): cast = self.SIGNED else: cast = self.UNSIGNED return hdl_call(cast, [op_hdl, ]) else: return op_hdl def as_hdl_HOperatorNode_TRUNC(self, op: HOperatorNode): ops = op.operands # convert trunc to slice resWidth = int(ops[1]) res = HdlOp(HdlOpType.INDEX, [ self.as_hdl_operand(ops[0], 0, op), self.as_hdl_HSliceConst(SLICE.from_py(slice(resWidth, 0, -1))) ]) op0_t = ops[0]._dtype if isinstance(op0_t, HBits) and op0_t.signed: return hdl_call(self.SIGNED, [res, ]) return res def as_hdl_HOperatorNode_SEXT_ZEXT(self, op: HOperatorNode): ops = op.operands # convert sext to concat with repliacation, zext to concat 0 # x4b._sext(8) -> { {8-4{x[4-1]}}, x }; # replication operator resWidth = int(ops[1]) srcWidth = ops[0]._dtype.bit_length() prefixLen = resWidth - srcWidth if op.operator == HwtOps.ZEXT: prefix = self.as_hdl_HBitsConst(HBits(prefixLen).from_py(0)) else: if srcWidth == 1: msb = self.as_hdl_operand(ops[0], 0, op) return HdlOp(HdlOpType.REPL_CONCAT, [ self.as_hdl_int(resWidth), msb, ]) else: msb = self.as_hdl_operand(ops[0][srcWidth - 1]._vec(), 0, op) # :note: can not construct HdlOp directly because verilog slice operator can not be applied to any value without tmp variable # HdlOp(HdlOpType.INDEX, [ # self.as_hdl_operand(ops[0], 0, op), # self.as_hdl_int(srcWidth - 1), # ]) if prefixLen == 1: prefix = msb else: prefix = HdlOp(HdlOpType.REPL_CONCAT, [ self.as_hdl_int(prefixLen), msb, ]) res = HdlOp(HdlOpType.CONCAT, [ prefix, self.as_hdl_operand(ops[0], 0, op) ]) resT = op.result._dtype if isinstance(resT, HBits) and resT.signed: _, tmpVar = self.tmpVars.create_var_cached(f"tmp_{op.operator.id}_signCast", resT, def_val=res) return hdl_call(self.SIGNED, [tmpVar, ]) return res def as_hdl_HOperatorNode_INDEX(self, op: HOperatorNode): ops = op.operands op0_t = ops[0]._dtype if isinstance(op0_t, HBits) and op0_t.bit_length() == 1 and not op0_t.force_vector: assert int(ops[1]) == 0, ops # drop whole index operator when indexing on 1b vector return self.as_hdl_operand(ops[0], 0, op) _o = self.op_transl_dict[op.operator] res = HdlOp(_o, [self.as_hdl_operand(o2, i, op) for i, o2 in enumerate(ops)]) op0_t = ops[0]._dtype if isinstance(op0_t, HBits) and op0_t.signed: return hdl_call(self.SIGNED, [res, ]) return res def as_hdl_HOperatorNode(self, op: HOperatorNode): ops = op.operands o = op.operator if o == HwtOps.TERNARY: return self.as_hdl_HOperatorNode_TERNARY(op, ops) elif o == HwtOps.RISING_EDGE or o == HwtOps.FALLING_EDGE: raise UnsupportedEventOpErr() elif o in (HwtOps.BitsAsUnsigned, HwtOps.BitsAsVec, HwtOps.BitsAsSigned): return self.as_hdl_HOperatorNode_BITSCAST(op) elif o == HwtOps.TRUNC: return self.as_hdl_HOperatorNode_TRUNC(op) elif o in (HwtOps.SEXT, HwtOps.ZEXT): return self.as_hdl_HOperatorNode_SEXT_ZEXT(op) elif o == HwtOps.INDEX: return self.as_hdl_HOperatorNode_INDEX(op) else: if o == HwtOps.MUL: op0, op1 = ops # optionally drop zext/sext and add casts ops = matchFullWidthMul(op0, op1) _o = o.hdlConvertoAstOp if _o is None: _o = self.op_transl_dict[o] res = HdlOp(_o, [self.as_hdl_operand(o2, i, op) for i, o2 in enumerate(ops)]) return res ================================================ FILE: hwt/serializer/verilog/serializer.py ================================================ from copy import copy from typing import Optional, List from hdlConvertorAst.hdlAst import HdlStmIf, HdlOp, \ HdlOpType, HdlValueId, HdlModuleDec, iHdlStatement from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlTypeAuto from hdlConvertorAst.hdlAst._statements import HdlStmProcess, HdlStmBlock, HdlStmAssign, \ HdlStmWait from hdlConvertorAst.to.verilog.keywords import IEEE1800_2017_KEYWORDS from hdlConvertorAst.translate.common.name_scope import LanguageKeyword, NameScope from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call from hwt.hdl.portItem import HdlPortItem from hwt.hdl.types.array import HArray from hwt.hdl.types.defs import STR, INT, BOOL from hwt.serializer.generic.to_hdl_ast import ToHdlAst from hwt.serializer.verilog.context import SignalTypeSwap from hwt.serializer.verilog.ops import ToHdlAstVerilog_ops from hwt.serializer.verilog.statements import ToHdlAstVerilog_statements from hwt.serializer.verilog.types import ToHdlAstVerilog_types from hwt.serializer.verilog.utils import SIGNAL_TYPE, verilogTypeOfSig from hwt.serializer.verilog.value import ToHdlAstVerilog_Value class ToHdlAstVerilog(ToHdlAstVerilog_types, ToHdlAstVerilog_Value, ToHdlAstVerilog_statements, ToHdlAstVerilog_ops, ToHdlAst): _keywords_dict = {kw: LanguageKeyword() for kw in IEEE1800_2017_KEYWORDS} def __init__(self, name_scope: Optional[NameScope]=None): ToHdlAst.__init__(self, name_scope=name_scope) self.signalType = SIGNAL_TYPE.PORT_WIRE def as_hdl_HdlModuleDef_variable( self, v, types, hdl_types, hdl_variables, processes, component_insts): new_v = copy(v) with SignalTypeSwap(self, verilogTypeOfSig(v.origin)): t = v.type # if type requires extra definition if self.does_type_requires_extra_def(t, types): _t = self.as_hdl_HdlType(t, declaration=True) hdl_types.append(_t) types.add(t) new_v.type = self.as_hdl_HdlType(t, declaration=False) # this is a array variable which requires value intialization in init # process if isinstance(t, HArray): if v.value.vld_mask: rom = v.origin p = HdlStmProcess() label = self.name_scope.checked_name(rom._name + "_rom_init", p) p.labels.append(label) p.body = HdlStmBlock() body = p.body.body for i, _v in enumerate(rom.def_val): a = HdlStmAssign(self.as_hdl(_v), self.as_hdl(rom[i])) a.is_blocking = True body.append(a) w = HdlStmWait() w.val = [] # initial process body.append(w) processes.append(p) # because we would not be able to initialize const/localparam variable later new_v.is_const = False new_v.value = None elif new_v.value is not None: if new_v.value.vld_mask: new_v.value = self.as_hdl_Value(new_v.value) else: # 'x' is a default value no need to specify it extra new_v.value = None return new_v def _static_assert_false(self, msg:str): return hdl_call(HdlValueId("$error"), [f"%m {msg:s}"]) def _static_assert_symbol_eq(self, symbol_name:str, v): i = HdlStmIf() i.in_preproc = True # [TODO] this actually requires SV>=2009 # generate # if (p==x) begin # $error("%m Generated only for this param value"); # end # endgenerate i.cond = HdlOp(HdlOpType.NE, [HdlValueId(symbol_name), v]) i.if_true = hdl_call(HdlValueId("$error"), [ "%m Generated only for this param value", ]) return i def as_hdl_GenericItem(self, g: HdlIdDef): with SignalTypeSwap(self, SIGNAL_TYPE.PORT_WIRE): new_v = copy(g) v = g.value if v._dtype == STR or v._dtype == INT or v._dtype == BOOL: t = HdlTypeAuto else: t = self.as_hdl_HdlType(v._dtype) new_v.type = t assert new_v.value is not None, g new_v.value = self.as_hdl_Value(v) return new_v def as_hdl_HdlPortItem(self, pi: HdlPortItem): with SignalTypeSwap(self, verilogTypeOfSig(pi)): v = super(ToHdlAstVerilog, self).as_hdl_HdlPortItem(pi) v.is_latched = self.signalType == SIGNAL_TYPE.PORT_REG return v def _as_hdl_HdlModuleDef_param_asserts(self, new_m: HdlModuleDec) -> List[iHdlStatement]: return ToHdlAst._as_hdl_HdlModuleDef_param_asserts_real(self, new_m) ================================================ FILE: hwt/serializer/verilog/statements.py ================================================ from hdlConvertorAst.hdlAst._bases import iHdlStatement from hdlConvertorAst.hdlAst._expr import HdlAll from hdlConvertorAst.hdlAst._statements import HdlStmProcess, HdlStmWait, \ HdlStmBlock from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.serializer.verilog.utils import verilogTypeOfSig class ToHdlAstVerilog_statements(): def as_hdl_HdlAssignmentContainer(self, a: HdlAssignmentContainer): blocking = False ver_sig_t = verilogTypeOfSig(a.dst) if ver_sig_t in (SIGNAL_TYPE.REG, SIGNAL_TYPE.PORT_REG): evDep = False for driver in a.dst._rtlDrivers: if driver._event_dependent_from_branch is not None: evDep = True break if not evDep or a.dst.virtual_only: blocking = True elif ver_sig_t in (SIGNAL_TYPE.WIRE, SIGNAL_TYPE.PORT_WIRE): blocking = True else: raise ValueError(ver_sig_t) a = super(ToHdlAstVerilog_statements, self).as_hdl_HdlAssignmentContainer(a) a.is_blocking = blocking return a def can_pop_process_wrap(self, stms, hasToBeVhdlProcess): if hasToBeVhdlProcess: return False else: assert len(stms) == 1 return True def has_to_be_process(self, proc: HdlStmCodeBlockContainer): for o in proc._outputs: if verilogTypeOfSig(o) in (SIGNAL_TYPE.REG, SIGNAL_TYPE.PORT_REG): return True return False def as_hdl_HdlStmCodeBlockContainer(self, proc: HdlStmCodeBlockContainer) -> iHdlStatement: p = super(ToHdlAstVerilog_statements, self).as_hdl_HdlStmCodeBlockContainer(proc) if isinstance(p, HdlStmProcess): no_wait = True if isinstance(p.body, HdlStmWait): no_wait = False elif isinstance(p.body, HdlStmBlock): for _o in p.body.body: if isinstance(_o, HdlStmWait): no_wait = False break if no_wait and not p.sensitivity: # all input are constant and that is why this process does not have # any sensitivity p.sensitivity = [HdlAll, ] # add label if not isinstance(p.body, HdlStmBlock): b = p.body p.body = HdlStmBlock() p.body.body.append(b) p.body.labels.extend(p.labels) p.labels.clear() return p ================================================ FILE: hwt/serializer/verilog/types.py ================================================ from hdlConvertorAst.hdlAst._expr import HdlTypeAuto, HdlValueId, HdlOp, \ HdlOpType from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_index, \ hdl_downto from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import INT, FLOAT64 from hwt.hdl.types.float import HFloat from hwt.hdl.types.hdlType import HdlType, MethodNotOverloaded from hwt.serializer.verilog.utils import SIGNAL_TYPE from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class ToHdlAstVerilog_types(): INT = HdlValueId("int", obj=int) REG = HdlValueId("reg", obj=LanguageKeyword()) WIRE = HdlValueId("wire", obj=LanguageKeyword()) def does_type_requires_extra_def(self, t: HdlType, other_types: list): try: return t._as_hdl_requires_def(self, other_types) except MethodNotOverloaded: pass return False def as_hdl_HdlType_bits(self, typ: HBits, declaration=False): isVector = typ.force_vector or typ.bit_length() > 1 sigType = self.signalType if typ == INT: t = self.INT elif sigType is SIGNAL_TYPE.PORT_WIRE: t = HdlTypeAuto elif sigType is SIGNAL_TYPE.REG or sigType is SIGNAL_TYPE.PORT_REG: t = self.REG elif sigType is SIGNAL_TYPE.WIRE: t = self.WIRE else: raise ValueError(sigType) if typ.signed is None or typ.signed == False: # [yosys] do not produce unsigned type as yosys support is limited is_signed = None else: is_signed = self.as_hdl_int(int(typ.signed)) if isVector: w = typ.bit_length() assert isinstance(w, int) or (isinstance(w, RtlSignal) and w._const), w w = hdl_downto(self.as_hdl(w - 1), self.as_hdl_int(0)) else: w = None return HdlOp(HdlOpType.PARAMETRIZATION, [t, w, is_signed]) def as_hdl_HdlType_array(self, typ: HArray, declaration=False): if declaration: raise NotImplementedError() else: _int = self.as_hdl_int size = HdlOp(HdlOpType.DOWNTO, [_int(0), _int(int(typ.size) - 1)]) return hdl_index(self.as_hdl_HdlType(typ.element_t), size) def as_hdl_HdlType_enum(self, typ, declaration=False): if declaration: raise TypeError( "Target language does not use enum types, this library should uses HBits instead" " (this should not be required because it should have been filtered before)") else: valueCnt = len(typ._allValues) return self.as_hdl_HdlType_bits(HBits(valueCnt.bit_length()), declaration=declaration) def as_hdl_HdlType_float(self, typ: HFloat, declaration=False): if typ == FLOAT64: return HdlValueId("real") else: raise NotImplementedError(typ) ================================================ FILE: hwt/serializer/verilog/utils.py ================================================ from typing import Union from hdlConvertorAst.hdlAst import iHdlStatement from hdlConvertorAst.to.verilog.constants import SIGNAL_TYPE from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.variables import HdlSignalItem from ipCorePackager.constants import DIRECTION @internal def verilogTypeOfSig(s: Union[HdlSignalItem, HdlPortItem]): """ Check if is register or wire """ if isinstance(s, HdlPortItem): if s.direction == DIRECTION.IN or s.direction == DIRECTION.INOUT: return SIGNAL_TYPE.PORT_WIRE t = verilogTypeOfSig(s.getInternSig()) if t == SIGNAL_TYPE.WIRE: return SIGNAL_TYPE.PORT_WIRE elif t == SIGNAL_TYPE.REG: return SIGNAL_TYPE.PORT_REG else: raise ValueError(t) driver_cnt = len(s._rtlDrivers) if driver_cnt == 1: d = s._rtlDrivers[0] if isinstance(d, HdlPortItem): # input port return SIGNAL_TYPE.WIRE elif isinstance(d, HdlAssignmentContainer)\ and d.parentStm is None\ and not d.indexes\ and d._event_dependent_from_branch is None\ and (isinstance(d.src, HConst) or not d.src._isUnnamedExpr): # primitive assignment return SIGNAL_TYPE.WIRE elif isinstance(d, iHdlStatement) and d.in_preproc: return SIGNAL_TYPE.WIRE return SIGNAL_TYPE.REG ================================================ FILE: hwt/serializer/verilog/value.py ================================================ from typing import Optional from hdlConvertorAst.hdlAst._expr import HdlValueInt, HdlOpType, \ HdlOp from hdlConvertorAst.to.hdlUtils import bit_string from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_downto, \ hdl_call from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import BOOL, BIT from hwt.hdl.types.enumConst import HEnumConst from hwt.hdl.types.sliceConst import HSliceConst from hwt.mainBases import RtlSignalBase from hwt.serializer.generic.ops import HWT_TO_HDLCONVERTOR_OPS from hwt.serializer.generic.value import ToHdlAst_Value from hwt.serializer.verilog.context import SignalTypeSwap from hwt.serializer.verilog.utils import verilogTypeOfSig class ToHdlAstVerilog_Value(ToHdlAst_Value): # TRUE = HdlValueId("true", obj=LanguageKeyword()) # FALSE = HdlValueId("false", obj=LanguageKeyword()) def as_hdl_HBoolConst(self, val: HBitsConst): return self.as_hdl_int(val.val) def as_hdl_cond(self, c, forceBool): assert isinstance(c, (RtlSignalBase, HConst)) if not forceBool or c._dtype == BOOL: return self.as_hdl(c) elif c._dtype == BIT: return self.as_hdl(c) elif isinstance(c._dtype, HBits): return self.as_hdl(c != 0) else: raise NotImplementedError() def as_hdl_HEnumConst(self, val: HEnumConst): i = val._dtype._allValues.index(val.val) assert i >= 0 return HdlValueInt(i, None, None) def as_hdl_HdlSignalItem(self, si, declaration=False): if declaration: with SignalTypeSwap(self, verilogTypeOfSig(si)): return ToHdlAst_Value.as_hdl_HdlSignalItem(self, si, declaration=declaration) else: return ToHdlAst_Value.as_hdl_HdlSignalItem(self, si, declaration=declaration) def as_hdl_HSliceConst(self, val: HSliceConst): upper = val.val.start - 1 return hdl_downto(self.as_hdl_Value(upper), self.as_hdl_Value(val.val.stop)) def sensitivityListItem(self, item, anyIsEventDependent): if isinstance(item, HOperatorNode): return HdlOp(HWT_TO_HDLCONVERTOR_OPS[item.operator], [self.as_hdl(item.operands[0]), ]) elif anyIsEventDependent: if item._dtype.negated: op = HdlOpType.FALLING else: op = HdlOpType.RISING return HdlOp(op, [self.as_hdl(item), ]) return self.as_hdl(item) def as_hdl_HArrayConst(self, val): raise ValueError( "Verilog do not have a array constants(they are part of SV)" " and that is why array constants should converted to initialization" " in initial processes") @internal def as_hdl_BitString(self, v: int, width: int, force_vector: bool, vld_mask: int, signed: Optional[int]): v = bit_string(v, width, vld_mask=vld_mask) if signed: return hdl_call(self.SIGNED, [v, ]) else: return v ================================================ FILE: hwt/serializer/vhdl/__init__.py ================================================ """ VHDL serializer serializes HDL objects to VHDL code. """ from hdlConvertorAst.to.vhdl.vhdl2008 import ToVhdl2008 from hwt.serializer.vhdl.serializer import ToHdlAstVhdl2008 from hwt.serializer.xdc.serializer import XdcSerializer class Vhdl2008Serializer(): fileExtension = '.vhd' TO_HDL_AST = ToHdlAstVhdl2008 TO_HDL = ToVhdl2008 TO_CONSTRAINTS = XdcSerializer ================================================ FILE: hwt/serializer/vhdl/ops.py ================================================ from typing import Union, Optional from hdlConvertorAst.hdlAst import HdlValueInt from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlOp, HdlOpType, \ HDLCONVERTAST_OPS_SHIFT_AND_ROT, HdlOthers from hdlConvertorAst.hdlAst._statements import HdlStmAssign from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call, \ hdl_index, hdl_downto from hwt.code import If from hwt.doc_markers import internal from hwt.hdl.commonConstants import b0, b1 from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps, CAST_OPS, HOperatorDef from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.hdl.types.bitConstFunctions import AnyHBitsValue from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.bitsRtlSignal import HBitsRtlSignal from hwt.hdl.types.defs import BOOL, INT, BIT from hwt.mainBases import RtlSignalBase from hwt.serializer.hwt.ops import ToHdlAstHwt_ops from hwt.serializer.vhdl.types import ToHdlAstVhdl2008_types from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from pyMathBitPrecise.bit_utils import ctlz, get_bit @internal def isResultOfTypeConversionForIndex(sig: RtlSignal): if len(sig._rtlDrivers) != 1: return False if sig._isUnnamedExpr: d = sig.singleDriver() return d.operator not in (HwtOps.INDEX, HwtOps.TRUNC) return False def matchZextOrSextArg(op: Union[RtlSignal, HConst]) -> tuple[Optional[AnyHBitsValue], Optional[bool]]: """ Check if the value is zext or sext to double width :returns: base operand, isSigned or None, None if not matched """ if isinstance(op, HConst): if not op._is_full_valid(): return None, None width = op._dtype.bit_length() isSigned = op._dtype.signed msb = bool(get_bit(op.val, width - 1)) if msb: prefixLen = ctlz(~op.val, width) else: prefixLen = ctlz(op.val, width) if prefixLen == width: # this is x * 0 or x * -1, this should have been optimized before, we do not do any opt there return None, None if isSigned: prefixLen -= 1 # msb 0/1 must stay to mark signed return op[prefixLen:], False elif op._isUnnamedExpr: try: d = op.singleDriver() except SignalDriverErr: return None, None casts = [] # drop unnecessary casts while isinstance(d, HOperatorNode) and d.operator in CAST_OPS: casts.append(d.operator) d = d.operands[0].singleDriver() if isinstance(d, HOperatorNode) and d.operator in (HwtOps.ZEXT, HwtOps.SEXT): src = d.operands[0] return src, d.operator == HwtOps.SEXT return None, None def matchFullWidthMul(op0: Union[RtlSignal, HConst], op1: Union[RtlSignal, HConst])\ ->tuple[Union[RtlSignal, HConst], Union[RtlSignal, HConst]]: # result of multiplication in VHDL has 2x width, while in hwt has same width as operands have # truncatenation is required, # * first we check if operands are not zero extended to double width # if this is the case we can use original operands and avoid any cast _op0, _op0SignExtended = matchZextOrSextArg(op0) if _op0 is not None: _op1, _op1SignExtended = matchZextOrSextArg(op1) if _op1 is not None: if _op0SignExtended == _op1SignExtended: op0 = _op0._cast_sign(_op0SignExtended) op1 = _op1._cast_sign(_op1SignExtended) # * else we need to truncatenate result to width of operand return op0, op1 class ToHdlAstVhdl2008_ops(ToHdlAstVhdl2008_types): op_transl_dict = { **ToHdlAstHwt_ops.op_transl_dict, HwtOps.RISING_EDGE: HdlOpType.RISING, HwtOps.FALLING_EDGE: HdlOpType.FALLING, } TO_INTEGER = HdlValueId("TO_INTEGER", obj=LanguageKeyword()) _cast_ops = { HwtOps.BitsAsSigned: ToHdlAstVhdl2008_types.SIGNED, HwtOps.BitsAsUnsigned: ToHdlAstVhdl2008_types.UNSIGNED, HwtOps.BitsAsVec: ToHdlAstVhdl2008_types.STD_LOGIC_VECTOR, } RESIZE = HdlValueId("RESIZE", obj=LanguageKeyword()) SHIFT_LEFT = HdlValueId("SHIFT_LEFT", obj=LanguageKeyword()) SHIFT_RIGHT = HdlValueId("SHIFT_RIGHT", obj=LanguageKeyword()) ROTATE_LEFT = HdlValueId("ROTATE_LEFT", obj=LanguageKeyword()) ROTATE_RIGHT = HdlValueId("ROTATE_RIGHT", obj=LanguageKeyword()) # :note: bool in HDLCONVERTORAST_TO_VHDL value marks if shift is arithmetical or logical HDLCONVERTORAST_TO_VHDL = { HdlOpType.SLL: (SHIFT_LEFT, False), # shift left logical HdlOpType.SRL: (SHIFT_RIGHT, False), # shift right logical HdlOpType.SLA: (SHIFT_LEFT, True), # shift left arithmetical HdlOpType.SRA: (SHIFT_RIGHT, True), # shift right arithmetical HdlOpType.ROL: (ROTATE_LEFT, False), # rotate left HdlOpType.ROR: (ROTATE_RIGHT, False), # rotate right } @internal def _tmp_var_for_ternary(self, val: RtlSignal) -> RtlSignal: """ Optionally convert boolean to std_logic_vector """ isNew, o = self.tmpVars.create_var_cached( "tmpTernary_", val._dtype, postponed_init=True, extra_args=(val, bool, 1, 0)) if isNew: cond, ifTrue, ifFalse = val._rtlDrivers[0].operands if_ = If(cond) if_.ifTrue.append(HdlAssignmentContainer(ifTrue, o, virtual_only=True, parentStm=if_)) if_.ifFalse = ListOfHdlStatement() if_.ifFalse.append(HdlAssignmentContainer(ifFalse, o, virtual_only=True, parentStm=if_)) if_._outputs.append(o) for obj in (cond, ifTrue, ifFalse): if isinstance(obj, RtlSignalBase): if_._inputs.append(obj) o._rtlDrivers.append(if_) if_._discover_enclosure() self.tmpVars.finish_var_init(o) return o @internal def _tmp_var_for_arrayAggregate(self, val: RtlSignal) -> RtlSignal: """ Create tmp variable for expression which will be converted to VHDL aggreate expression: .. code-block:: x = x1b._sext(10) xTmp10b := (0=> x1b, others=>x1b); """ # [todo]: _as_Bits_vec _, o = self.tmpVars.create_var_cached( "tmpAggregate_", val._dtype, def_val=val, extra_args=(val, HdlOpType.MAP_ASSOCIATION)) return o def _as_Bits(self, val: Union[RtlSignal, HConst]): if val._dtype == BOOL: isNew, o = self.tmpVars.create_var_cached( "tmpBool2std_logic_", BIT, postponed_init=True, extra_args=(val, int, 1, 0)) if isNew: ifTrue, ifFalse = b1, b0 if_ = If(val) if_.ifTrue.append(HdlAssignmentContainer(ifTrue, o, virtual_only=True, parentStm=if_)) if_.ifFalse = [] if_.ifFalse.append(HdlAssignmentContainer(ifFalse, o, virtual_only=True, parentStm=if_)) if_._outputs.append(o) o._rtlDrivers.append(if_) self.tmpVars.finish_var_init(o) return o else: assert isinstance(val._dtype, HBits), val._dtype return val def _as_Bits_vec(self, val: Union[RtlSignal, HConst]): val = self._as_Bits(val) t = val._dtype if not t.force_vector and t.bit_length() == 1: # std_logic -> std_logic_vector std_logic_vector = HBits(1, signed=t.signed, force_vector=True) isNew, o = self.tmpVars.create_var_cached( "tmp_std_logic2vector_", std_logic_vector, postponed_init=True, extra_args=(val, std_logic_vector)) if isNew: o._rtlDrivers.append(HdlAssignmentContainer(val, o, virtual_only=True)) self.tmpVars.finish_var_init(o) return o else: # already a std_logic_vector return val def as_hdl_operand(self, operand: Union[RtlSignal, HConst]): # automatically extract some operators as tmp variable # * nested ternary in expressions like # ( '1' WHEN r = f ELSE '0' ) & "0" # * nested array aggregate expressions # (0=>x, 1=>y) & "0" isTernaryOp = False isArrayAggregateOp = False try: if operand._isUnnamedExpr: d = operand._rtlDrivers[0] o = d.operator if o == HwtOps.TERNARY: isTernaryOp = True elif o == HwtOps.SEXT or o == HwtOps.ZEXT: op0T = d.operands[0]._dtype if op0T.signed is None and op0T.bit_length() == 1 and not op0T.force_vector: # :note: == if this is not SEXT/ZEXT implemented using VHDL resize() isArrayAggregateOp = True except (AttributeError, IndexError): pass if isTernaryOp: # rewrite ternary operator as if operand = self._tmp_var_for_ternary(operand) elif isArrayAggregateOp: operand = self._tmp_var_for_arrayAggregate(operand) return self.as_hdl(operand) def apply_cast(self, t: HdlValueId, op): return hdl_call(t, [op, ]) def _wrapConcatInTmpVariable(self, op): if isinstance(op, RtlSignalBase) and op._isUnnamedExpr: # if left operand is concatenation and this is not concatenation we must extract it as tmp variable # because VHDL would not be able to resolve type of concatenated signal otherwise try: d = op.singleDriver() except SignalDriverErr: d = None if d is not None and isinstance(d, HOperatorNode) and d.operator is HwtOps.CONCAT: _, op = self.tmpVars.create_var_cached("tmpConcatExpr_", op._dtype, def_val=op) return op def _as_hdl_HOperatorNode_mulWithTrunc(self, op: HOperatorNode, src0Type: HBits, src1Type: HBits, resType: HBits, _op0: HdlOp, _op1: HdlOp): # new tmp variable must be created because downto may be applied only on ID and not expression width = src0Type.bit_length() + src1Type.bit_length() resWidth = resType.bit_length() if not src0Type.strict_width or not src1Type.strict_width or width == resWidth: return HdlOp(HdlOpType.MUL, [_op0, _op1]) signed = src0Type.signed or src1Type.signed assert signed == resType.signed isNew, tmpMulTruncVar = self.tmpVars.create_var_cached( "tmpMulTrunc_" if width > resWidth else "tmpMulExt_", HBits(width, signed), postponed_init=True, extra_args=(op,)) if isNew: hdl = self.tmpVars.extraVarsHdl hdl_a = HdlStmAssign(HdlOp(HdlOpType.MUL, [_op0, _op1]), self.as_hdl_HdlSignalItem(tmpMulTruncVar)) hdl_a.is_blocking = True hdl.append(hdl_a) as_hdl = self.as_hdl_HdlSignalItem(tmpMulTruncVar, declaration=True) hdl.append(as_hdl) res = self.as_hdl_HdlSignalItem(tmpMulTruncVar) if width > resWidth: res = hdl_index(res, hdl_downto(self.as_hdl_int(resWidth - 1), self.as_hdl_int(0))) else: assert width < resWidth _op1 = self.as_hdl_int(resWidth) res = hdl_call(self.RESIZE, [res, _op1]) return res def _as_hdl_HOperatorNode_castArg(self, op0: Union[HBitsConst, HBitsRtlSignal]): if isinstance(op0, RtlSignalBase) and op0._isUnnamedExpr: _, op0 = self.tmpVars.create_var_cached("tmpCastExpr_", op0._dtype, def_val=op0) return op0 def as_hdl_HOperatorNode_indexRhs(self, op1: Union[HBitsConst, HBitsRtlSignal]): if isinstance(op1._dtype, HBits) and op1._dtype != INT: if op1._dtype.signed is None: if op1._dtype.bit_length() == 1 and not op1._dtype.force_vector: _, op1 = self.tmpVars.create_var_cached("tmp1bToUnsigned_", HBits(1, force_vector=True), def_val=op1) _op1 = self.as_hdl_operand(op1) _op1 = self.apply_cast(self.UNSIGNED, _op1) else: _op1 = self.as_hdl_operand(op1._unsigned()) else: _op1 = self.as_hdl_operand(op1) return self.apply_cast(self.TO_INTEGER, _op1) else: return self.as_hdl_operand(op1) def as_hdl_HOperatorNode_TRUNC_SEXT_ZEXT(self, op: HOperatorNode, o: HOperatorDef): op0, op1 = op.operands op1 = int(op1) assert op1 >= 1, op resultSign = op.result._dtype.signed signedForVhdlResize = o == HwtOps.SEXT # :note: VHDL std_numeric.resize supports only signed or unsinged if o == HwtOps.TRUNC: if isinstance(op0, RtlSignalBase) and isResultOfTypeConversionForIndex(op0): _, op0 = self.tmpVars.create_var_cached("tmpTypeConv_", op0._dtype, def_val=op0) if resultSign is None: _op0 = self.as_hdl_operand(self._as_Bits(op0)) # prefer downto notation over resize with casts op1 = int(op1) assert op1 >= 1, op if op1 == 1 and not op.result._dtype.force_vector: _sliceOp = self.as_hdl_int(0) else: _sliceOp = HdlOp(HdlOpType.DOWNTO, [self.as_hdl_int(op1 - 1), self.as_hdl_int(0)]) return HdlOp(HdlOpType.INDEX, [_op0, _sliceOp]) signedForVhdlResize = resultSign # it does not matter if trunc is signed/unsigned, but we preffer less casting else: # _op0 = self.as_hdl_operand(self._as_Bits(op0)) op0 = self._as_Bits(op0) op0T = op0._dtype if o != HwtOps.TRUNC and\ op0T.signed is None and\ op0T.bit_length() == 1 and\ not op0T.force_vector: _op0 = self.as_hdl_operand(op0) # use aggregate expression if o == HwtOps.SEXT: msb = _op0 return [HdlOp(HdlOpType.MAP_ASSOCIATION, [HdlOthers, msb]), ] else: msb = self.as_hdl_HBitsConst(b0) return [ HdlOp(HdlOpType.MAP_ASSOCIATION, [self.as_hdl_int(0), _op0]), HdlOp(HdlOpType.MAP_ASSOCIATION, [HdlOthers, msb]), ] else: # use vhdl RESIZE() if resultSign != signedForVhdlResize: op0 = op0._cast_sign(signedForVhdlResize) # :note: this must be done after sign casts on on RtlNetlist level because # otherwise the operands of cast may not recognize that tmp variable must be used for operand _op0 = self.as_hdl_operand(op0) _op1 = self.as_hdl_int(op1) res = hdl_call(self.RESIZE, [_op0, _op1]) if resultSign != signedForVhdlResize: res = self.apply_cast(self._sign_flag_to_cast_id[resultSign], res) return res def as_hdl_HOperatorNode_INDEX(self, op: HOperatorNode): ops = op.operands op0, op1 = ops if isinstance(op0, RtlSignalBase) and isResultOfTypeConversionForIndex(op0): _, op0 = self.tmpVars.create_var_cached("tmpTypeConv_", op0._dtype, def_val=op0) if isinstance(op1, RtlSignalBase) and isResultOfTypeConversionForIndex(op1): _, op1 = self.tmpVars.create_var_cached("tmpIndexTypeConv_", op1._dtype, def_val=op1) # if the op0 is not signal or other index index operator it is extracted # as tmp variable op0 = self.as_hdl_operand(op0) op0_t = ops[0]._dtype if isinstance(op0_t, HBits) and op0_t.bit_length() == 1 and not op0_t.force_vector: assert int(ops[1]) == 0, ops # drop whole index operator because it is useless return op0 _op1 = self.as_hdl_HOperatorNode_indexRhs(op1) return HdlOp(HdlOpType.INDEX, [op0, _op1]) def as_hdl_HOperatorNode_TERNARY(self, op: HOperatorNode): _c, _op0, _op1 = op.operands op0 = self.as_hdl_cond(_c, True) op1 = self.as_hdl_operand(_op0) t0 = _op0._dtype t1 = _op1._dtype if not (t0 == t1): assert isinstance(t0, HBits) and\ isinstance(t1, HBits) and\ t0.bit_length() == t1.bit_length() and\ bool(t0.signed) == bool(t1.signed), (t0, t1) _, _op1 = self.tmpVars.create_var_cached("tmpTernaryAutoCast_", t0, def_val=_op1) op2 = self.as_hdl_operand(_op1) return HdlOp(HdlOpType.TERNARY, [op0, op1, op2]) def as_hdl_HOperatorNode_asVhdlFn(self, op: HOperatorNode, vhldFn: HdlValueId, isArithmetical: Optional[bool]): ops = op.operands op0, op1 = ops op0Signed = op0._dtype.signed if isArithmetical: if not op0Signed: op0 = op0._cast_sign(True) _, op0 = self.tmpVars.create_var_cached("tmpOpFnArgCast_", op0._dtype, def_val=op0) else: if op0Signed or op0Signed is None: op0 = op0._cast_sign(False) _, op0 = self.tmpVars.create_var_cached("tmpOpFnArgCast_", op0._dtype, def_val=op0) _op0 = self.as_hdl_Value(op0) _op1 = self.as_hdl_HOperatorNode_indexRhs(op1) res = hdl_call(vhldFn, [_op0, _op1]) resSigned = op.result._dtype.signed if op0Signed != isArithmetical: res = self.apply_cast(self._sign_flag_to_cast_id[resSigned], res) return res def as_hdl_HOperatorNode(self, op: HOperatorNode): o = op.operator if o == HwtOps.INDEX: return self.as_hdl_HOperatorNode_INDEX(op) elif o == HwtOps.TRUNC or o == HwtOps.SEXT or o == HwtOps.ZEXT: return self.as_hdl_HOperatorNode_TRUNC_SEXT_ZEXT(op, o) elif o == HwtOps.TERNARY: return self.as_hdl_HOperatorNode_TERNARY(op) else: ops = op.operands _o = self._cast_ops.get(o, None) if _o is not None: op0 = ops[0] op0 = self._as_Bits_vec(op0) if isinstance(op0, RtlSignalBase) and op0._isUnnamedExpr: _, op0 = self.tmpVars.create_var_cached("tmpCastExpr_", op0._dtype, def_val=op0) return self.apply_cast(_o, self.as_hdl_operand(op0)) _o = o.hdlConvertoAstOp if _o is None: o = self.op_transl_dict[o] else: vhldFn, isArithmetical = self.HDLCONVERTORAST_TO_VHDL.get(_o, (None, None)) if vhldFn is not None: return self.as_hdl_HOperatorNode_asVhdlFn(op, vhldFn, isArithmetical) o = _o if len(ops) == 2: res_t = op.result._dtype op0, op1 = ops if o == HdlOpType.MUL: # optionally drop zext/sext and add casts op0, op1 = matchFullWidthMul(op0, op1) if o != HdlOpType.CONCAT: op0 = self._wrapConcatInTmpVariable(op0) op1 = self._wrapConcatInTmpVariable(op1) if isinstance(res_t, HBits) and res_t != BOOL: op0 = self._as_Bits(op0) op1 = self._as_Bits(op1) _op0 = self.as_hdl_operand(op0) _op1 = self.as_hdl_operand(op1) if o == HdlOpType.EQ and isinstance(_op0, HdlValueId) and\ (isinstance(_op0.obj._dtype, HBits) and self._expandBitsOperandType(_op0.obj) == BOOL) and\ isinstance(_op1, HdlValueInt) and\ _op1.val == 1: # drop unnecessary casts return _op0 elif o == HdlOpType.MUL: return self._as_hdl_HOperatorNode_mulWithTrunc(op, op0._dtype, op1._dtype, res_t, _op0, _op1) else: assert o not in HDLCONVERTAST_OPS_SHIFT_AND_ROT, (o, "shifts and rotations should have been handled sooner in this function") return HdlOp(o, [_op0, _op1]) return HdlOp(o, [self.as_hdl_operand(o2) for o2 in ops]) ================================================ FILE: hwt/serializer/vhdl/serializer.py ================================================ from natsort.natsort import natsorted import re from typing import List from hdlConvertorAst.hdlAst import HdlOp, HdlModuleDec, HdlOpType, iHdlStatement from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlAll from hdlConvertorAst.hdlAst._statements import HdlImport, \ HdlStmIf, HdlStmBlock, HdlStmFor, HdlStmForIn from hdlConvertorAst.hdlAst._structural import HdlLibrary, HdlModuleDef, \ HdlCompInst, HdlContext from hdlConvertorAst.to.vhdl.keywords import VHLD2008_KEYWORDS from hdlConvertorAst.translate.common.name_scope import LanguageKeyword, NameScope from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_call from hwt.pyUtils.arrayQuery import groupedby from hwt.serializer.generic.to_hdl_ast import ToHdlAst from hwt.serializer.vhdl.ops import ToHdlAstVhdl2008_ops from hwt.serializer.vhdl.statements import ToHdlAstVhdl2008_statements from hwt.serializer.vhdl.types import ToHdlAstVhdl2008_types from hwt.serializer.vhdl.value import ToHdlAstVhdl2008_Value class VhdlNameScope(NameScope): RE_MANY_UNDERSCORES = re.compile(r"(_{2,})") def checked_name(self, suggested_name, actualObj): suggested_name = self._sanitize_name(suggested_name) suggested_name = self.RE_MANY_UNDERSCORES.sub(r"_", suggested_name) if suggested_name[0] == "_": suggested_name = "u" + suggested_name return NameScope.checked_name(self, suggested_name, actualObj) IEEE = HdlValueId("IEEE", obj=LanguageKeyword()) std_logic_1164 = HdlValueId("std_logic_1164", obj=LanguageKeyword()) numeric_std = HdlValueId("numeric_std", obj=LanguageKeyword()) class ToHdlAstVhdl2008(ToHdlAstVhdl2008_Value, ToHdlAstVhdl2008_ops, ToHdlAstVhdl2008_types, ToHdlAstVhdl2008_statements, ToHdlAst): """ :ivar ~.name_scope: name scope used to generate a unique names for tmp variables (all object should be registered in namescope before serialization) """ _keywords_dict = {kw: LanguageKeyword() for kw in VHLD2008_KEYWORDS} DEFAULT_IMPORTS = [ HdlLibrary("IEEE"), HdlImport([IEEE, std_logic_1164, HdlAll]), HdlImport([IEEE, numeric_std, HdlAll]), ] ASSERT = HdlValueId("assert") FAILURE = HdlValueId("failure") @classmethod def getBaseNameScope(cls): s = VhdlNameScope.make_top(True) s.update(cls._keywords_dict) return s @staticmethod def _find_HdlCompInst(o): if isinstance(o, (list, tuple)): for _o in o: yield from ToHdlAstVhdl2008._find_HdlCompInst(_o) if isinstance(o, HdlCompInst): yield o elif isinstance(o, HdlStmBlock) and o.in_preproc: yield from ToHdlAstVhdl2008._find_HdlCompInst(o.body) elif isinstance(o, HdlStmIf) and o.in_preproc: if o.if_true: yield from ToHdlAstVhdl2008._find_HdlCompInst(o.if_true) for _, stms in o.elifs: yield from ToHdlAstVhdl2008._find_HdlCompInst(stms) if o.if_false: yield from ToHdlAstVhdl2008._find_HdlCompInst(o.if_false) elif isinstance(o, (HdlStmFor, HdlStmForIn)) and o.in_preproc: if o.body: yield from ToHdlAstVhdl2008._find_HdlCompInst(o.body) def _static_assert_false(self, msg:str): return hdl_call(self.ASSERT, [ self.FALSE, msg, self.FAILURE]) def _static_assert_symbol_eq(self, symbol_name:str, v): return hdl_call(self.ASSERT, [ HdlOp(HdlOpType.EQ, [HdlValueId(symbol_name), v]), "Generated only for this value", self.FAILURE]) def _as_hdl_HdlModuleDef_param_asserts(self, new_m: HdlModuleDec) -> List[iHdlStatement]: return ToHdlAst._as_hdl_HdlModuleDef_param_asserts_real(self, new_m) def as_hdl_HdlModuleDef(self, o: HdlModuleDef): """ Translate hwt types and expressions to HDL AST and add explicit components """ _o = super(ToHdlAstVhdl2008, self).as_hdl_HdlModuleDef(o) component_insts = [] for c in _o.objs: component_insts.extend(self._find_HdlCompInst(c)) # select component instances with an unique module_name components = [ x[1][0] for x in groupedby(component_insts, lambda c: c.module_name) ] components = natsorted(components, key=lambda c: c.module_name) components = [self.as_hdl_HldComponent(c) for c in components] if components: # :note: it is important that the asserts are at the end because # we are detecting the declarations from the beginning and assert there would # disturb that objs = [*components, *_o.objs] _o.objs = objs res = HdlContext() res.objs.extend(self.DEFAULT_IMPORTS) res.objs.append(_o) return res def as_hdl_HldComponent(self, o: HdlCompInst): c = self.as_hdl_HdlModuleDec(o.origin._rtlCtx.hwModDec) return c ================================================ FILE: hwt/serializer/vhdl/statements.py ================================================ from copy import copy from hdlConvertorAst.hdlAst import HdlStmCase from hdlConvertorAst.hdlAst._statements import HdlStmAssign from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL, BIT from hwt.hdl.types.sliceConst import HSliceConst from hwt.hdl.variables import HdlSignalItem from hwt.serializer.exceptions import SerializerException from hwt.mainBases import RtlSignalBase from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr class ToHdlAstVhdl2008_statements(): @staticmethod def _expandBitsOperandType(v): if v._dtype == BIT and isinstance(v, RtlSignalBase) and v._isUnnamedExpr: try: d = v.singleDriver() except SignalDriverErr: d = None if d is not None and isinstance(d, HOperatorNode) and d.operator is HwtOps.INDEX and d.operands[0]._dtype == BOOL: return BOOL return v._dtype def as_hdl_HdlAssignmentContainer(self, a: HdlAssignmentContainer): _dst = dst = a.dst assert isinstance(dst, HdlSignalItem) if a.indexes is not None: for i in a.indexes: if isinstance(i, HSliceConst): i = i.__copy__() dst = dst[i] src = a.src dst_t = dst._dtype correct = False src_t = self._expandBitsOperandType(src) if dst_t == src_t: correct = True else: src = a.src if (isinstance(dst_t, HBits) and isinstance(src_t, HBits)): # std_logic <-> boolean <-> std_logic_vector(0 downto 0) auto conversions while not (dst_t == src_t): # while is used because the casting could be required multiple times correct = False if dst_t.bit_length() == src_t.bit_length() == 1: if dst_t.force_vector and not src_t.force_vector: dst = dst[0] correct = True elif not dst_t.force_vector and src_t.force_vector: src = src[0] correct = True elif src_t == BOOL: src = src._ternary(BIT.from_py(1), BIT.from_py(0)) correct = True elif not src_t.strict_width: if isinstance(src, HConst): src = copy(src) if a.indexes: raise NotImplementedError() src._dtype = dst_t correct = True else: raise NotImplementedError() pass src_t = src._dtype dst_t = dst._dtype if not correct: # automatic type cast can not be performed break if correct: src = self.as_hdl(src) hdl_a = HdlStmAssign(src, self.as_hdl(dst)) hdl_a.is_blocking = _dst.virtual_only return hdl_a raise SerializerException( f"{dst} = {a.src} is not valid assignment\n" f" because types are different ({dst._dtype}; {a.src._dtype})") def as_hdl_SwitchContainer(self, sw: SwitchContainer) -> HdlStmCase: s = HdlStmCase() switchOn = sw.switchOn if isinstance(switchOn, RtlSignalBase) and switchOn._isUnnamedExpr: _, switchOn = self.tmpVars.create_var_cached("tmpTypeConv_", switchOn._dtype, def_val=switchOn) s.switch_on = self.as_hdl_cond(switchOn, False) s.cases = cases = [] for key, statements in sw.cases: key = self.as_hdl_Value(key) cases.append((key, self.as_hdl_statements(statements))) s.default = self.as_hdl_statements(sw.default) return s def can_pop_process_wrap(self, stms, hasToBeVhdlProcess): if hasToBeVhdlProcess or len(stms) > 1: return False else: assert len(stms) == 1, stms return True def has_to_be_process(self, proc: HdlStmCodeBlockContainer): for x in proc.statements: if isinstance(x, (IfContainer, SwitchContainer)): return True return False ================================================ FILE: hwt/serializer/vhdl/types.py ================================================ from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlValueId, HdlOp, HdlOpType, \ HdlTypeType from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hdlConvertorAst.translate.verilog_to_basic_hdl_sim_model.utils import hdl_index, \ hdl_downto from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.defs import BOOL, INT, FLOAT64 from hwt.hdl.types.float import HFloat from hwt.hdl.types.string import HString class ToHdlAstVhdl2008_types(): BOOLEAN = HdlValueId("BOOLEAN", obj=LanguageKeyword()) INTEGER = HdlValueId("INTEGER", obj=LanguageKeyword()) STRING = HdlValueId("STRING", obj=LanguageKeyword()) STD_LOGIC_VECTOR = HdlValueId("STD_LOGIC_VECTOR", obj=LanguageKeyword()) STD_LOGIC = HdlValueId("STD_LOGIC", obj=LanguageKeyword()) SIGNED = HdlValueId("SIGNED", obj=LanguageKeyword()) UNSIGNED = HdlValueId("UNSIGNED", obj=LanguageKeyword()) _sign_flag_to_cast_id = { None: STD_LOGIC_VECTOR, True: SIGNED, False: UNSIGNED, } def as_hdl_HdlType_str(self, typ: HString, declaration=False): assert not declaration return self.STRING def as_hdl_HdlType_bits(self, typ: HBits, declaration=False): if declaration: raise NotImplementedError() if typ == BOOL: return self.BOOLEAN if typ == INT: return self.INTEGER bitLength = typ.bit_length() w = typ.bit_length() isVector = typ.force_vector or bitLength > 1 if typ.signed is None: if isVector: name = self.STD_LOGIC_VECTOR else: return self.STD_LOGIC elif typ.signed: name = self.SIGNED else: name = self.UNSIGNED return HdlOp(HdlOpType.CALL, [ name, HdlOp(HdlOpType.DOWNTO, [ self.as_hdl(w - 1), self.as_hdl_int(0) ])]) def as_hdl_HdlType_array(self, typ: HArray, declaration=False): if declaration: v = HdlIdDef() name = getattr(typ, "name", None) if name is None: name = "arr_t_" v.name = self.name_scope.checked_name(name, typ) v.type = HdlTypeType v.origin = typ size = hdl_downto( self.as_hdl_int(int(typ.size) - 1), self.as_hdl_int(0) ) if self.does_type_requires_extra_def(typ.element_t, ()): raise NotImplementedError(typ.element_t) e_t = self.as_hdl_HdlType(typ.element_t, declaration=False) v.value = hdl_index(e_t, size) return v else: return super(ToHdlAstVhdl2008_types, self).as_hdl_HdlType_array(typ, declaration) def as_hdl_HdlType_float(self, typ: HFloat, declaration=False): if typ == FLOAT64: return HdlValueId("real") else: raise NotImplementedError(typ) ================================================ FILE: hwt/serializer/vhdl/value.py ================================================ from hdlConvertorAst.hdlAst import HdlValueId, HdlValueInt, HdlOp, \ HdlOpType from hdlConvertorAst.to.hdlUtils import bit_string from hdlConvertorAst.translate.common.name_scope import LanguageKeyword from hwt.hdl.const import HConst from hwt.hdl.operator import HOperatorNode from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import BOOL, BIT from hwt.hdl.types.enumConst import HEnumConst from hwt.hdl.types.sliceConst import HSliceConst from hwt.mainBases import RtlSignalBase from hwt.serializer.generic.value import ToHdlAst_Value class ToHdlAstVhdl2008_Value(ToHdlAst_Value): TRUE = HdlValueId("TRUE", obj=LanguageKeyword()) FALSE = HdlValueId("FALSE", obj=LanguageKeyword()) # TO_UNSIGNED = HdlValueId("TO_UNSIGNED", obj=LanguageKeyword()) # TO_SIGNED = HdlValueId("TO_SIGNED", obj=LanguageKeyword()) def as_hdl_cond(self, c, forceBool): assert isinstance(c, (RtlSignalBase, HConst)), c if not forceBool or c._dtype == BOOL: return self.as_hdl(c) elif c._dtype == BIT: return self.as_hdl(c._eq(1)) elif isinstance(c._dtype, HBits): return self.as_hdl(c != 0) else: raise NotImplementedError() def as_hdl_HEnumConst(self, val: HEnumConst): name = self.name_scope.get_object_name(val) return HdlValueId(name, obj=val) def as_hdl_HArrayConst(self, val): return [self.as_hdl_Value(v) for v in val] def sensitivityListItem(self, item, anyIsEventDependnt): if isinstance(item, HOperatorNode): item = item.operands[0] return self.as_hdl(item) def as_hdl_BitString(self, v, width: int, force_vector: bool, vld_mask: int, signed): is_bit = not force_vector and width == 1 # if vld_mask != mask(width) or width >= 32 or is_bit: v = bit_string(v, width, vld_mask) if is_bit: v.base = 256 return v if signed is None: return v elif signed: cast = self.SIGNED else: cast = self.UNSIGNED return HdlOp(HdlOpType.APOSTROPHE, [cast, v]) # else: # v = HdlValueInt(v, None, None) # # if signed is None: # return v # elif signed: # cast_fn = self.TO_SIGNED # else: # cast_fn = self.TO_UNSIGNED # return hdl_call(cast_fn, [v, HdlValueInt(width, None, None)]) def as_hdl_HBoolConst(self, val: HBitsConst): if val.val: return self.TRUE else: return self.FALSE def as_hdl_HBitsConst(self, val: HBitsConst): t = val._dtype v = super(ToHdlAstVhdl2008_Value, self).as_hdl_HBitsConst(val) # handle '1' vs "1" difference (bit literal vs vector) if not t.force_vector and t.bit_length() == 1 and t != BOOL: if isinstance(v, HdlValueInt): v.base = 256 else: # assert is cast assert isinstance(v, HdlOp) and v.fn == HdlOpType.CALL, v _v = v.ops[1] if isinstance(_v, HdlValueInt): _v.base = 256 else: raise NotImplementedError() return v def as_hdl_HSliceConst(self, val: HSliceConst): upper = val.val.start if int(val.val.step) == -1: if isinstance(upper, HConst): upper = HdlValueInt(int(upper) - 1, None, None) else: upper = HdlOp(HdlOpType.SUB, [self.as_hdl_Value(upper), HdlValueInt(1, None, None)]) else: raise NotImplementedError(val.val.step) return HdlOp(HdlOpType.DOWNTO, [upper, self.as_hdl(val.val.stop)]) ================================================ FILE: hwt/serializer/xdc/__init__.py ================================================ ================================================ FILE: hwt/serializer/xdc/serializer.py ================================================ from itertools import islice from typing import Union, Tuple from hwt.constraints import set_max_delay, set_false_path, \ set_async_reg, get_clock_of, iHdlConstrain from hwt.hdl.types.bits import HBits from hwt.pyUtils.arrayQuery import iter_with_last from hwt.hwIO import HwIO from hwt.hwModule import HwModule from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal class XdcSerializer(): """ Convert constrains containers to a XDC format (For Xilinx Vivado) """ fileExtension = ".xdc" def __init__(self, out): self.out = out def _get(self, o: Union[Tuple[HwModule, RtlSignal, HwIO], iHdlConstrain], only_first=False): """ :param only_first: if true select only first bit from vector, else select whole vector """ if isinstance(o, iHdlConstrain): return self.visit_iHdlConstrain(o) is_reg = False _o = o[-1] if isinstance(_o, RtlSignal): q = "get_cells" for d in _o._rtlDrivers: if d._event_dependent_from_branch is not None: is_reg = True elif isinstance(_o, HwIO): q = "get_pins" else: raise NotImplementedError(o) w = self.out.write w(q) w(" -hier -filter {NAME =~ */") path = o # [TODO] find out how to make select with ip top module/entity name for last, p in iter_with_last(islice(path, 1, None)): if isinstance(p, HwModule): w(p._name) w("_inst") elif isinstance(p, RtlSignal): w(p._name) elif isinstance(p, HwIO): w(p._name) else: raise NotImplementedError(p) if not last: w("/") t = _o._dtype if is_reg: w("_reg") if isinstance(t, HBits) and (t.bit_length() > 1 or t.force_vector): # * on end because of Vivado _replica if only_first: w("[0]*") else: w("[*]*") w("}") def visit_get_clock_of(self, o: get_clock_of): w = self.out.write if isinstance(o.obj[-1], RtlSignal) and o.obj[-1]._rtlNextSig is not None: w("get_clocks -of [") self._get(o.obj) w("]") else: raise NotImplementedError() def visit_iHdlConstrain(self, o): visitFn = getattr(self, "visit_" + o.__class__.__name__, None) if visitFn is None: return o.to_xdc(self, o) else: return visitFn(o) def visit_HdlConstraintList(self, o_list): return [self.visit_iHdlConstrain(o) for o in o_list] def visit_set_async_reg(self, o: set_async_reg): w = self.out.write w("set_property ASYNC_REG TRUE [") self._get(o.sig) w("]\n") def visit_set_false_path(self, o: set_false_path): w = self.out.write w("set_false_path") if o.start is not None: w(" -from [") self._get(o.start) w("]") if o.end is not None: w(" -to [") self._get(o.end) w("]") w("\n") def visit_set_max_delay(self, o: set_max_delay): w = self.out.write w("set_max_delay -from [") self._get(o.start) w("] -to [") self._get(o.end) w("]") if o.datapath_only: w(" -datapath_only") w(f" {o.time_ns:f}\n") ================================================ FILE: hwt/simulator/__init__.py ================================================ """ A package for binding to simulators and simulation utils. :note: hwt package does not contain any RTL simulator, this is just an api to simulators. """ ================================================ FILE: hwt/simulator/agentBase.py ================================================ from hwt.synthesizer.exceptions import IntfLvlConfErr from hwtSimApi.agents.base import AgentBase, SyncAgentBase as pcSyncAgentBase, \ AgentWitReset as pcAgentWitReset from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.process_utils import OnRisingCallbackLoop class AgentWitReset(pcAgentWitReset): def __init__(self, sim: HdlSimulator, hwIO, allowNoReset=False): pcAgentWitReset.__init__(self, sim, hwIO, (None, False)) self.rst, self.rstOffIn = self._discoverReset(hwIO, allowNoReset) @classmethod def _discoverReset(cls, hwIO, allowNoReset: bool): try: rst = hwIO._getAssociatedRst() rstOffIn = int(rst._dtype.negated) rst = rst._sigInside except IntfLvlConfErr: rst = None rstOffIn = True if not allowNoReset: raise return (rst, rstOffIn) def notReset(self): if self.rst is None: return True else: rstVal = self.rst.read() rstVal = int(rstVal) return rstVal == self.rstOffIn class SyncAgentBase(AgentWitReset, pcSyncAgentBase): """ Agent which discovers clk, rst signal and runs only at specified edge of clk :attention: requires clk and rst/rstn signal (if you do not have any create simulation wrapper with it) """ SELECTED_EDGE_CALLBACK = OnRisingCallbackLoop def __init__(self, sim: HdlSimulator, hwIO, allowNoReset=False): self.hwIO = hwIO clk = self.hwIO._getAssociatedClk() rst = self._discoverReset(hwIO, allowNoReset) pcSyncAgentBase.__init__( self, sim, hwIO, clk, rst) ================================================ FILE: hwt/simulator/agentConnector.py ================================================ from hwt.doc_markers import internal from hwt.constants import INTF_DIRECTION from hwt.hwModule import HwModule from hwtSimApi.hdlSimulator import HdlSimulator @internal def autoAddAgents(module: HwModule, sim: HdlSimulator): """ Walk all interfaces on module and instantiate agent for every interface. :return: all monitor/driver functions which should be added to simulation as processes """ for hio in module._hwIOs: assert hio._isExtern, hio hio._initSimAgent(sim) assert hio._ag is not None, hio @internal def collect_processes_from_sim_agents(module: HwModule): proc = [] for hio in module._hwIOs: a = hio._ag if not hio._isExtern or a is None: continue if hio._direction == INTF_DIRECTION.MASTER: agProcs = a.getMonitors() elif hio._direction == INTF_DIRECTION.SLAVE: agProcs = a.getDrivers() else: raise NotImplementedError(f"hio._direction {hio._direction} for {hio}") proc.extend(agProcs) return proc ================================================ FILE: hwt/simulator/rtlSimulator.py ================================================ from datetime import datetime import importlib from io import StringIO import os import sys from types import ModuleType from typing import Union, Optional, Set, Tuple, Callable from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.const import HConst from hwt.hdl.types.bits import HBits from hwt.hdl.types.enum import HEnum from hwt.hwIO import HwIO from hwt.hwModule import HwModule from hwt.mainBases import RtlSignalBase from hwt.serializer.serializer_filter import SerializerFilterDoNotExclude from hwt.serializer.simModel import SimModelSerializer from hwt.serializer.store_manager import SaveToStream, SaveToFilesFlat from hwt.synth import to_rtl from hwt.synthesizer.dummyPlatform import DummyPlatform from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from hwtSimApi.basic_hdl_simulator.model import BasicRtlSimModel from hwtSimApi.basic_hdl_simulator.proxy import BasicRtlSimProxy from hwtSimApi.basic_hdl_simulator.rtlSimulator import BasicRtlSimulator from hwtSimApi.basic_hdl_simulator.sim_utils import ValueUpdater, \ ArrayValueUpdater from pyDigitalWaveTools.vcd.common import VCD_SIG_TYPE from pyDigitalWaveTools.vcd.value_format import VcdBitsFormatter, \ VcdEnumFormatter from pyDigitalWaveTools.vcd.writer import VcdVarWritingScope, \ VarAlreadyRegistered from pyMathBitPrecise.bits3t import Bits3t from pyMathBitPrecise.enum3t import Enum3t class BasicRtlSimulatorWithSignalRegisterMethods(BasicRtlSimulator): supported_type_classes = tuple() def __init__(self, model_cls, synthesised_unit): """ Only store variables for later construction """ self.model_cls = model_cls self.synthesised_unit = synthesised_unit self.wave_writer = None self._obj2scope = {} self._traced_signals = set() def __call__(self) -> "BasicRtlSimulatorVcd": """ Create and initialize the BasicRtlSimulatorWithVCD object """ sim = self.__class__(self.model_cls, self.synthesised_unit) super(BasicRtlSimulatorWithSignalRegisterMethods, sim).__init__() model = self.model_cls(sim) model._init_body() sim.bound_model(model) return sim def _init_listeners(self): self.logPropagation = False self.logApplyingValues = False @classmethod def build(cls, module: HwModule, unique_name: str, build_dir: Optional[str], target_platform=DummyPlatform(), do_compile=True) -> "BasicRtlSimulatorVcd": """ Create a hwtSimApi.basic_hdl_simulator based simulation model for specified unit and load it to python :param module: interface level unit which you wont prepare for simulation :param unique_name: unique name for build directory and python module with simulator :param target_platform: target platform for this synthesis :param build_dir: directory to store sim model build files, if None sim model will be constructed only in memory """ if unique_name is None: unique_name = module._getDefaultName() _filter = SerializerFilterDoNotExclude() if build_dir is None or not do_compile: buff = StringIO() store_man = SaveToStream(SimModelSerializer, buff, _filter=_filter) else: if not os.path.isabs(build_dir): build_dir = os.path.join(os.getcwd(), build_dir) build_private_dir = os.path.join(build_dir, unique_name) store_man = SaveToFilesFlat(SimModelSerializer, build_private_dir, _filter=_filter) store_man.module_path_prefix = unique_name to_rtl(module, name=unique_name, target_platform=target_platform, store_manager=store_man) if build_dir is not None: d = build_dir dInPath = d in sys.path if not dInPath: sys.path.insert(0, d) if unique_name in sys.modules: del sys.modules[unique_name] simModule = importlib.import_module( unique_name + "." + unique_name, package='simModule_' + unique_name) if not dInPath: sys.path.pop(0) else: simModule = ModuleType('simModule_' + unique_name) # python supports only ~100 opened brackets; MemoryError: s_push: parser stack overflow # python supports only ~100 levels of indentation; IndentationError: too many levels of indentation exec(buff.getvalue(), simModule.__dict__) model_cls = simModule.__dict__[module._name] # can not use just function as it would get bounded to class return cls(model_cls, module) @internal @staticmethod def _get_rtl_instance_name_from_submodule_name(name: str): name = name.replace(".", "_") + "_inst" if name.startswith("_"): return "v" + name return name @internal @staticmethod def get_trace_formatter(t)\ ->Tuple[str, int, Callable[[RtlSignalBase, HConst], str]]: """ :return: (vcd type name, vcd width, formatter fn) """ if isinstance(t, (Bits3t, HBits)): return (VCD_SIG_TYPE.WIRE, t.bit_length(), VcdBitsFormatter()) elif isinstance(t, (Enum3t, HEnum)): return (VCD_SIG_TYPE.REAL, 1, VcdEnumFormatter()) else: raise ValueError(t) def set_trace_file(self, file_name, trace_depth): self.create_wave_writer(file_name) ww = self.wave_writer if ww is not None: ww.date(datetime.now()) ww.timescale(1) empty_hiearchy_containers = set() self._collect_empty_hiearchy_containers(self.synthesised_unit, self.model, empty_hiearchy_containers) self._wave_register_signals(self.synthesised_unit, self.model, None, empty_hiearchy_containers) ww.enddefinitions() def create_wave_writer(self, file_name: str): self.wave_writer = None def finalize(self): pass def _collect_empty_hiearchy_containers(self, obj: Union[HwIO, HwModule], model: BasicRtlSimModel, res: Set[Union[HwModule, HwIO]]): hwIOs = getattr(obj, "_hwIOs", None) isEmpty = True if hwIOs or isinstance(obj, HObjList): for chHwIO in hwIOs: isEmpty &= self._collect_empty_hiearchy_containers(chHwIO, model, res) if isinstance(obj, HwModule): seenNames: Set[str] = set() for chHwIO in obj._private_hwIOs: # skip io without name and with duplicit name if chHwIO._name is not None and chHwIO._name not in seenNames: seenNames.add(chHwIO._name) isEmpty &= self._collect_empty_hiearchy_containers(chHwIO, model, res) for sm in obj._subHwModules: m = getattr(model, self._get_rtl_instance_name_from_submodule_name(sm._name)) if sm._shared_component_with is not None: sm, _, _ = sm._shared_component_with isEmpty &= self._collect_empty_hiearchy_containers(sm, m, res) if isEmpty: res.add(obj) else: s = obj._sigInside if s is not None: # _sigInside is None if the signal was optimized out sig_name = s._name if isinstance(s, RtlSignal) else s._hdlName s = getattr(model.io, sig_name, None) if s is not None: return False return isEmpty def _wave_register_signals(self, obj: Union[HwIO, HwModule], model: BasicRtlSimModel, parent: Optional[VcdVarWritingScope], empty_hiearchy_containers: Set[Union[HwModule, HwIO]]): """ Register signals from interfaces for HwIO or :class:`hwt.hwModule.HwModule` instances """ if obj in empty_hiearchy_containers: return if obj._hwIOs: # if HwModules may be shared components, for them the top name is the one of shared and not this module name name = model._name if isinstance(obj, HwModule) else obj._name parent_ = self.wave_writer if parent is None else parent subScope = parent_.varScope(name) self._obj2scope[obj] = subScope with subScope: # register all subinterfaces for chHwIO in obj._hwIOs: self._wave_register_signals(chHwIO, model, subScope, empty_hiearchy_containers) if isinstance(obj, HwModule): for chHwIO in obj._private_hwIOs: # skip io without name and with duplicit name if chHwIO._name is not None and chHwIO._name not in subScope.children: self._wave_register_signals(chHwIO, model, subScope, empty_hiearchy_containers) # register interfaces from all subunits for sm in obj._subHwModules: m = getattr(model, self._get_rtl_instance_name_from_submodule_name(sm._name)) if sm._shared_component_with is not None: sm, _, _ = sm._shared_component_with self._wave_register_signals(sm, m, subScope, empty_hiearchy_containers) self._wave_register_remaining_signals(subScope, model, empty_hiearchy_containers) else: t = obj._dtype if obj._sigInside is not None and isinstance(t, self.supported_type_classes): s = obj._sigInside sig_name = s._name if isinstance(s, RtlSignal) else s._hdlName s = getattr(model.io, sig_name, None) if s is not None: tName, width, formatter = self.get_trace_formatter(t) try: parent.addVar(s, sig_name, tName, width, formatter) except VarAlreadyRegistered: pass def _wave_register_remaining_signals(self, unitScope, model: BasicRtlSimModel, interface_signals: Set[BasicRtlSimProxy]): for s in model._hwIOs: if s not in interface_signals and s not in self.wave_writer._idScope: t = s._dtype if isinstance(t, self.supported_type_classes): tName, width, formatter = self.get_trace_formatter(t) try: unitScope.addVar(s, s._hdlName, tName, width, formatter) except VarAlreadyRegistered: pass def logChange(self, nowTime: int, sig: BasicRtlSimProxy, nextVal: HConst, valueUpdater: Union[ValueUpdater, ArrayValueUpdater]): """ This method is called for every value change of any signal. """ pass ================================================ FILE: hwt/simulator/rtlSimulatorJson.py ================================================ from typing import Tuple, Callable, Union from pyMathBitPrecise.array3t import Array3t from pyMathBitPrecise.bits3t import Bits3t from pyMathBitPrecise.enum3t import Enum3t from hwt.doc_markers import internal from hwt.hdl.types.array import HArray from hwt.hdl.types.bits import HBits from hwt.hdl.types.enum import HEnum from hwt.hdl.types.hdlType import HdlType from hwt.hdl.const import HConst from hwt.simulator.rtlSimulator import BasicRtlSimulatorWithSignalRegisterMethods from hwt.mainBases import RtlSignalBase from pyDigitalWaveTools.json.writer import JsonWriter from pyDigitalWaveTools.vcd.common import VCD_SIG_TYPE from hwtSimApi.basic_hdl_simulator.proxy import BasicRtlSimProxy from hwtSimApi.basic_hdl_simulator.sim_utils import ValueUpdater, \ ArrayValueUpdater from pyDigitalWaveTools.json.value_format import JsonBitsFormatter,\ JsonEnumFormatter, JsonArrayFormatter class BasicRtlSimulatorJson(BasicRtlSimulatorWithSignalRegisterMethods): supported_type_classes = (HBits, HEnum, HArray, Bits3t, Enum3t, Array3t) @internal def get_trace_formatter(self, t: HdlType)\ -> Tuple[str, int, Callable[[RtlSignalBase, HConst], str]]: """ :return: (vcd type name, vcd width, formatter fn) """ if isinstance(t, (Bits3t, HBits)): return (VCD_SIG_TYPE.WIRE, t.bit_length(), JsonBitsFormatter()) elif isinstance(t, (Enum3t, HEnum)): return (VCD_SIG_TYPE.ENUM, 1, JsonEnumFormatter()) elif isinstance(t, (HArray, Array3t)): dimensions = [] while isinstance(t, (HArray, Array3t)): dimensions.append(t.size) t = t.element_t _, _, elm_format = self.get_trace_formatter(t) return (VCD_SIG_TYPE.ARRAY, dimensions + [t.bit_length(), ], JsonArrayFormatter(dimensions, elm_format)) else: raise ValueError(t) def create_wave_writer(self, data): self.wave_writer = JsonWriter(data) def logChange(self, nowTime: int, sig: BasicRtlSimProxy, nextVal: HConst, valueUpdater: Union[ValueUpdater, ArrayValueUpdater]): """ This method is called for every value change of any signal. """ try: self.wave_writer.logChange(nowTime, sig, nextVal, valueUpdater) except KeyError: # not every signal has to be registered # (if it is not registered it means it is ignored) pass ================================================ FILE: hwt/simulator/rtlSimulatorVcd.py ================================================ import sys from typing import Union from hwt.hdl.types.bits import HBits from hwt.hdl.types.enum import HEnum from hwt.hdl.const import HConst from hwt.simulator.rtlSimulator import BasicRtlSimulatorWithSignalRegisterMethods from hwtSimApi.basic_hdl_simulator.proxy import BasicRtlSimProxy from hwtSimApi.basic_hdl_simulator.sim_utils import ValueUpdater, \ ArrayValueUpdater from pyDigitalWaveTools.vcd.writer import VcdWriter #from pyMathBitPrecise.array3t import Array3t from pyMathBitPrecise.bits3t import Bits3t from pyMathBitPrecise.enum3t import Enum3t class BasicRtlSimulatorVcd(BasicRtlSimulatorWithSignalRegisterMethods): supported_type_classes = (HBits, HEnum, Bits3t, Enum3t, #Array3t ) def create_wave_writer(self, file_name): self.wave_writer = VcdWriter(open(file_name, "w")) self.logChange = self._logChange def finalize(self): # because set_trace_file() may not be called # and it this case the vcd config is not set if self.wave_writer is None: return f = self.wave_writer._oFile if f not in (sys.__stderr__, sys.__stdin__, sys.__stdout__): f.close() def _logChange(self, nowTime: int, sig: BasicRtlSimProxy, nextVal: HConst, valueUpdater: Union[ValueUpdater, ArrayValueUpdater]): """ This method is called for every value change of any signal. """ try: self.wave_writer.logChange(nowTime, sig, nextVal, valueUpdater) except KeyError: # not every signal has to be registered # (if it is not registered it means it is ignored) pass ================================================ FILE: hwt/simulator/simTestCase.py ================================================ import os from random import Random from typing import Optional import unittest from hwt.simulator.agentConnector import autoAddAgents, \ collect_processes_from_sim_agents from hwt.simulator.rtlSimulatorVcd import BasicRtlSimulatorVcd from hwt.simulator.utils import reconnectHwModuleSignalsToModel, Bits3valToInt, \ allHConstsToInts from hwt.synthesizer.dummyPlatform import DummyPlatform from hwt.synthesizer.exceptions import IntfLvlConfErr from hwt.hwModule import HwModule from hwtSimApi.constants import CLK_PERIOD from hwtSimApi.hdlSimulator import HdlSimulator from hwtSimApi.triggers import Timer from hwtSimApi.utils import freq_to_period class DummySimPlatform(DummyPlatform): """ DummyPlatform which ignores the constraints (hardware constranints which specifying something for circuit synthesis for a vendor tool) """ _UNSPECIFIED = object() class SimTestCase(unittest.TestCase): """ This is TestCase class contains methods which are usually used during hdl simulation. :attention: self.procs has to be specified before runSim() :cvar _defaultSeed: default seed for random generator :cvar rtl_simulator_cls: class for RTL simulator to use (constructed in compileSim()) :ivar ~.dut: instance of current :class:`hwt.hwModule.HwModule` for test, created in restartSim() :ivar ~.rtl_simulator: RTL simulator used for simulation of unit, created in restartSim() :ivar ~.hdl_simulator: the simulator which manages the communication between Python code and rtl_simulator instance :ivar ~.procs: list of simulation processes (Python generator instances), created in restartSim() :ivar ~.DEFAULT_BUILD_DIR: default directory where files for simulation should be stored :ivar ~.DEFAULT_LOG_DIR: default directory where simulation outputs should be stored :ivar ~.DEFAULT_SIMULATOR: default RTL simulator generator used on background of the test :ivar ~.RECOMPILE: if False the compilation of the simulation is dissabled. This is useful while debugging of the simulation because compilation of simulation may take significant amount of time and may not be required. """ # value chosen because in this position bits are changing frequently _defaultSeed = 317 RECOMPILE = True rtl_simulator_cls = None hdl_simulator = None DEFAULT_BUILD_DIR = None # "tmp" DEFAULT_LOG_DIR = "tmp" DEFAULT_SIMULATOR = BasicRtlSimulatorVcd def assertValEqual(self, first, second, msg=None): try: first = first.read() except AttributeError: pass if not isinstance(first, int) and first is not None: first = Bits3valToInt(first) return unittest.TestCase.assertEqual(self, first, second, msg=msg) def assertEmpty(self, val, msg=None): return unittest.TestCase.assertEqual(self, len(val), 0, msg=msg) def assertValSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): """ An equality assertion for ordered sequences (like lists and tuples). For the purposes of this function, a valid ordered sequence type is one which can be indexed, has a length, and has an equality operator. Args: :param seq1: can contain instance of values or nested list of them :param seq2: items are not converted, if item is None it is not checked :param seq_type: The expected data type of the sequences, or None if no data type should be enforced. :param msg: Optional message to use on failure instead of a list of differences. """ seq1 = allHConstsToInts(seq1) if len(seq1) == len(seq2): _seq2 = [] # replace None in seq2 with values from seq1 for v1, v2 in zip(seq1, seq2): if v2 is None: v2 = v1 _seq2.append(v2) seq2 = _seq2 self.assertSequenceEqual(seq1, seq2, msg, seq_type) def getTestName(self): className, testName = self.id().split(".")[-2:] return f"{className:s}_{testName:s}" def runSim(self, until: int, name=None): """ Collect sim. processes from iterface agents and run simulation """ if name is None: if self.DEFAULT_LOG_DIR is None: outputFileName = None else: outputFileName = os.path.join(self.DEFAULT_LOG_DIR, self.getTestName() + ".vcd") else: outputFileName = name if outputFileName is not None: d = os.path.dirname(outputFileName) if d: os.makedirs(d, exist_ok=True) self.rtl_simulator.set_trace_file(outputFileName, -1) procs = collect_processes_from_sim_agents(self.dut) # run simulation, stimul processes are register after initial # initialization self.hdl_simulator.run(until=until, extraProcesses=self.procs + procs) self.rtl_simulator.finalize() return self.hdl_simulator def randomize(self, hwIO): """ Randomly disable and enable interface for testing purposes """ assert hwIO._isExtern, hwIO assert hwIO._ag is not None, hwIO try: clk = hwIO._getAssociatedClk() except IntfLvlConfErr: clk = None clk_period = int(freq_to_period(clk.FREQ)) randomEnProc = simpleRandomizationProcess(self, hwIO._ag, timeQuantum=clk_period) self.procs.append(randomEnProc()) def restartSim(self): """ Set simulator to initial state and connect it to :return: tuple (fully loaded HwModule with connected simulator, connected simulator, simulation processes ) """ rtl_simulator = self.rtl_simulator_cls() hdl_simulator = HdlSimulator(rtl_simulator) dut = self.dut reconnectHwModuleSignalsToModel(dut, rtl_simulator) autoAddAgents(dut, hdl_simulator) self.procs = [] self.dut, self.rtl_simulator, self.hdl_simulator = \ dut, rtl_simulator, hdl_simulator return dut, rtl_simulator, self.procs def rmSim(self): """ Remove all buid sim objects from this object :note: Can be used to avoid unnecessary sim initialization (from prev. test) before next test. """ self.dut = None self.__class__.dut = None try: delattr(self, "rtl_simulator_cls") except AttributeError: pass self.__class__.rtl_simulator_cls = None self.rtl_simulator = None self.hdl_simulator = None self.__class__.hdl_simulator = None @classmethod def get_unique_name(cls, module: HwModule): uniq_name = module._getDefaultName() return f"{cls.__name__:s}__{uniq_name:s}" @classmethod def compileSim(cls, dut: HwModule, build_dir: Optional[str]=_UNSPECIFIED, unique_name: Optional[str]=None, onAfterToRtl=None, target_platform=DummySimPlatform()): """ Create simulation model and connect it with interfaces of original unit and decorate it with agents :param dut: interface level unit which you wont prepare for simulation :param target_platform: target platform for this synthesis :param build_dir: folder to where to put sim model files, if None temporary folder is used and then deleted (or simulator will be constructed in memory if possible) :param unique_name: name which is used as name of the dut for simulation (if is None it is automatically generated) :param onAfterToRtl: callback fn(unit) which will be called after unit will be synthesised to RTL and before :class:`hwt.hwModule.HwModule` instance signals are replaced with simulator specific ones """ if build_dir == _UNSPECIFIED: build_dir = cls.DEFAULT_BUILD_DIR if unique_name is None: unique_name = cls.get_unique_name(dut) cls.rtl_simulator_cls = cls.DEFAULT_SIMULATOR.build( dut, unique_name=unique_name, build_dir=build_dir, target_platform=target_platform, do_compile=cls.RECOMPILE) if onAfterToRtl: onAfterToRtl(dut) cls.dut = dut def compileSimAndStart( self, dut: HwModule, build_dir: Optional[str]=_UNSPECIFIED, unique_name: Optional[str]=None, onAfterToRtl=None, target_platform=DummySimPlatform()): """ Use this method if you did not used compileSim() to setup the simulator and DUT """ if unique_name is None: t_name = self.getTestName() u_name = dut._getDefaultName() unique_name = f"{t_name:s}__{u_name:s}" self.dut = dut self.compileSim(dut, build_dir, unique_name, onAfterToRtl, target_platform) SimTestCase.setUp(self) return self.dut def setUp(self): self._rand = Random(self._defaultSeed) if self.rtl_simulator_cls is not None: # if the simulator is not compiled it is expected # that it will be compiled in the test and this function # will be called later self.restartSim() def simpleRandomizationProcess(tc: SimTestCase, agent, timeQuantum=CLK_PERIOD): """ A process for simulator which will randomly enable/dissable the egent for an interface """ seed = tc._rand.getrandbits(64) random = Random(seed) def randomEnProc(): # small space at start to modify agents when they are inactive yield Timer(timeQuantum // 4) while True: en = random.random() < 0.5 if agent.getEnable() != en: agent.setEnable(en) delay = int(random.random() * 2) * timeQuantum yield Timer(delay) return randomEnProc ================================================ FILE: hwt/simulator/utils.py ================================================ from collections import deque from inspect import isgenerator import sys from typing import Union, Sequence from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hdl.const import HConst from hwt.hdl.types.arrayConst import HArrayConst from hwt.hwModule import HwModule from hwt.mainBases import HwIOBase, HwModuleOrHwIOBase from hwt.serializer.generic.indent import getIndent from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from hwtSimApi.basic_hdl_simulator.proxy import BasicRtlSimProxy from pyMathBitPrecise.bit_utils import ValidityError from pyMathBitPrecise.bits3t import Bits3val def pprintHwIO(hwio: Union[HwModule, HwIOBase], indent:int=0, prefix:str="", file=sys.stdout): """ Pretty print interface """ try: s = hwio._sig except AttributeError: s = None if s is None: s = "" else: s = " " + repr(s) file.write("".join([getIndent(indent), prefix, repr(hwio._getFullName()), s])) file.write("\n") if isinstance(hwio, HObjList): for chHwIO, p in enumerate(hwio): # interfaces have already name of this array and index in it's name pprintHwIO(p, indent=indent + 1, prefix=prefix, file=file) else: for chHwIO in hwio._hwIOs: pprintHwIO(chHwIO, indent=indent + 1, file=file) def pprintAgents(hwModuleOrHwIO: HwModuleOrHwIOBase, indent:int=0, prefix:str="", file=sys.stdout): if isinstance(hwModuleOrHwIO, HwIOBase): ag = hwModuleOrHwIO._ag elif isinstance(hwModuleOrHwIO, HObjList): for i, item in enumerate(hwModuleOrHwIO): item_prefix = f"{prefix}_{i:d}" pprintAgents(item, indent=indent + 1, prefix=item_prefix, file=file) return else: ag = None if ag is not None: indent_str = getIndent(indent) file.write(f"{indent_str:s}{prefix:s}{ag}\n") for hio in hwModuleOrHwIO._hwIOs: pprintAgents(hio, indent + 1, file=file) @internal def reconnectHwModuleSignalsToModel(synthesisedHwModuleOrHwIO: HwModuleOrHwIOBase, rtl_simulator): """ Reconnect model signals to unit to run simulation with simulation model but use original unit interfaces for communication :param synthesisedHwModuleOrHwIO: interface where should be signals replaced from signals from modelCls :param rtl_simulator: RTL simulator form where signals for synthesisedHwModuleOrHwIO should be taken """ obj = synthesisedHwModuleOrHwIO for hwio in obj._hwIOs: if hwio._hwIOs or isinstance(hwio, HObjList): reconnectHwModuleSignalsToModel(hwio, rtl_simulator) else: # reconnect signal from model si = hwio._sigInside if isinstance(si, RtlSignal): name = si._name else: assert isinstance(si, BasicRtlSimProxy), si name = si._hdlName # update name and dtype s = getattr(rtl_simulator.io, name, None) if s is None: raise AttributeError() if s._dtype is None: s._dtype = hwio._dtype s._name = hwio._name s._hdlName = name hwio.read = s.read hwio.write = s.write hwio.wait = s.wait hwio._sigInside = s def HConstSequenceToInts(values: Sequence[HConst]): """ Iterable of values to ints (nonvalid = None) """ return [Bits3valToInt(d) for d in values] def agentDataToInts(interface): """ Convert all values which has agent collected in time >=0 to integer array. Invalid value will be None. """ return HConstSequenceToInts(interface._ag.data) def Bits3valToInt(v: HConst): try: return int(v) except ValidityError: return None def allHConstsToInts(sequenceOrVal): """ Convert HConst instances to int recursively (for sequences) """ if isinstance(sequenceOrVal, HArrayConst): sequenceOrVal = sequenceOrVal.val if isinstance(sequenceOrVal, (HConst, Bits3val)): return Bits3valToInt(sequenceOrVal) elif not sequenceOrVal: return sequenceOrVal elif (isinstance(sequenceOrVal, (list, tuple, deque)) or isgenerator(sequenceOrVal)): seq = [] for i in sequenceOrVal: seq.append(allHConstsToInts(i)) if isinstance(sequenceOrVal, tuple): return tuple(seq) return seq else: return sequenceOrVal ================================================ FILE: hwt/synth.py ================================================ # -*- coding: utf-8 -*- from io import StringIO from hwt.constraints import _get_absolute_path from hwt.hwModule import HwModule, HdlConstraintList from hwt.serializer.serializer_config import DummySerializerConfig from hwt.serializer.serializer_filter import SerializerFilterDoNotExclude from hwt.serializer.store_manager import SaveToStream, StoreManager from hwt.serializer.vhdl import Vhdl2008Serializer from hwt.synthesizer.componentPath import ComponentPath from hwt.synthesizer.dummyPlatform import DummyPlatform def to_rtl(hmodule_or_cls: HwModule, store_manager: StoreManager, name: str=None, target_platform=DummyPlatform()): """ Convert unit to RTL using specified serializer :param unitOrCls: unit instance or class, which should be converted :param name: name override of top unit (if is None name is derived form class name) :param target_platform: meta-informations about target platform, distributed on every unit under _target_platform attribute before HwModule.hwImpl() is called """ if isinstance(hmodule_or_cls, HwModule): m = hmodule_or_cls else: m = hmodule_or_cls() m._target_platform = target_platform m._store_manager = store_manager m._loadHwDeclarations() if name is not None: assert isinstance(name, str) m._hdl_module_name = m._name = name # serialize all unit instances to HDL code constraints = HdlConstraintList() for _, obj in m._to_rtl(target_platform, store_manager): obj: HwModule # collect constraints directly in current component constraints.extend(obj._constraints) if obj._shared_component_with: # if the instance is shared with something else make # the paths in constraints relative to a component assert obj._shared_component_with[0]._shared_component_with is None path_old = _get_absolute_path(obj._shared_component_with[0]) path_new = _get_absolute_path(obj) for c in _HwModule_constraints_copy_recursively( obj, path_old, path_new): constraints.append(c) if constraints: # serialize all constraints in design store_manager.write(constraints) return store_manager def _HwModule_constraints_copy_recursively(m: HwModule, path_orig: ComponentPath, path_new: ComponentPath): if m._shared_component_with: assert not m._constraints assert not m._subHwModules orig_u, _, _ = m._shared_component_with _path_orig = _get_absolute_path(orig_u) yield from _HwModule_constraints_copy_recursively( orig_u, _path_orig, path_new) else: for c in m._constraints: yield c._copy_with_root_upadate(path_orig, path_new) for su in m._subHwModules: yield from _HwModule_constraints_copy_recursively( su, ComponentPath(*path_orig, su), ComponentPath(*path_new, su)) def to_rtl_str(hwmodule_or_cls: HwModule, serializer_cls=Vhdl2008Serializer, name: str=None, target_platform=DummyPlatform()): """ Generate HDL string and return it """ buff = StringIO() store_manager = SaveToStream(serializer_cls, buff) to_rtl(hwmodule_or_cls, store_manager, name, target_platform) return buff.getvalue() def serializeAsIpcore(m: HwModule, folderName=".", name=None, serializer_cls=Vhdl2008Serializer, target_platform=DummyPlatform()): """ Create an IPCore package """ from hwt.serializer.ip_packager import IpPackager p = IpPackager(m, name=name, serializer_cls=serializer_cls, target_platform=target_platform) p.createPackage(folderName) return p def synthesised(m: HwModule, target_platform=DummyPlatform()): """ Elaborate design without producing any HDL """ sm = StoreManager(DummySerializerConfig, _filter=SerializerFilterDoNotExclude()) if not hasattr(m, "_hwIOs"): m._target_platform = target_platform m._store_manager = sm m._loadHwDeclarations() for _ in m._to_rtl(target_platform, sm): pass return m ================================================ FILE: hwt/synthesizer/__init__.py ================================================ """ Sythesizer converts :class:`hwt.hwModule.HwModule` instances to HDL objects. :func:`hwt.synth.to_rtl` function is one of examples how to use this module. The conversion of :class:`hwt.hwModule.HwModule` instances happens mainly in :meth:`hwt.hwModule.Hmodule._to_rtl` which calls other optimization and transformations stored in :Py:attr:`:hwt.hwModule.HwModule._target_platform`. """ ================================================ FILE: hwt/synthesizer/componentPath.py ================================================ from copy import deepcopy from hwt.hwIO import HwIO from hwt.hwModule import HwModule from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal def to_tuple_of_names(objs): res = [] for o in objs: name = getattr(o, "_name", None) if name is None: name = repr(o) res.append(name) return tuple(res) class ComponentPath(tuple): def __new__ (cls, *objs): return super(ComponentPath, cls).__new__(cls, objs) def __truediv__(self, other): if isinstance(other, ComponentPath): return ComponentPath(*self, *other) else: return ComponentPath(*self, other) def resolve(self) -> "ComponentPath": """ Make the path absolute The ComponentPath is in absolute format only if: * The first member is a top component or path is empty * All members except the last are :class:`hwt.hwModule.HwModule` instances (last can be RtlSignal/HwIO) * Each successor member is instantiated in predecessor except for :class:`hwt.hwModule.HwModule` instance with shared component * If member is a :class:`hwt.hwModule.HwModule` instance with shared component the successor must be an interface of this instance or an object from shared component """ it = iter(reversed(self)) try: obj = next(it) except StopIteration: # empty path return self path = [] while obj is not None: _handle = next(it, None) if isinstance(_handle, HwModule) and _handle._shared_component_with is not None: handle, _, _ = _handle._shared_component_with else: handle = _handle if obj is not handle: if isinstance(obj, RtlSignal): # to not modify path if it is already in absolute format if not path or path[-1] is not obj: path.append(obj) obj = obj._rtlCtx.parent while isinstance(obj, HwIO): if not path: path.append(obj) if obj is handle: break obj = obj._parent while obj is not handle: assert isinstance(obj, HwModule), obj # to not modify path if it is already in absolute format if not path or path[-1] is not obj: path.append(obj) obj = obj._parent obj = _handle return ComponentPath(*reversed(path)) def is_absolute(self): """ :return: True if path starts with a top component else False """ m = self[0] return isinstance(m, HwModule) and m._parent is None def update_prefix(self, old_path_prefix: "ComponentPath", new_path_prefix: "ComponentPath"): """ Update prefix of the path tuple """ assert len(self) >= len(old_path_prefix), (self, old_path_prefix) for p, op in zip(self, old_path_prefix): assert p is op, (self, old_path_prefix) return ComponentPath(*new_path_prefix, *self[len(old_path_prefix):]) def __getitem__(self, key): if isinstance(key, slice): return self.__class__(*tuple.__getitem__(self, key)) else: return tuple.__getitem__(self, key) def __repr__(self): return f"<{self.__class__.__name__:s} {str(self):s}>" def __str__(self): return "/".join(to_tuple_of_names(self)) def __copy__(self): return self.__class__(*self) def __deepcopy__(self, memo): res = self.__class__(*(deepcopy(x, memo) for x in self)) memo[self] = res return res ================================================ FILE: hwt/synthesizer/dummyPlatform.py ================================================ from typing import List from hwt.synthesizer.rtlLevel.extract_part_drivers import RtlNetlistPassExtractPartDrivers from hwt.synthesizer.rtlLevel.mark_visibility_of_signals_and_check_drivers import RtlNetlistPassMarkVisibilityOfSignalsAndCheckDrivers from hwt.synthesizer.rtlLevel.remove_unconnected_signals import RtlNetlistPassRemoveUnconnectedSignals from hwt.synthesizer.rtlLevel.rtlNetlistPass import RtlNetlistPass class DummyPlatform(): """ Dummy synthesis platform, base class of all chip and toolset specific platforms. Platform in this context is a set of configurations which do describe the target toolset and chip/node. It can also contains pre/post processing callbacks and optimizations required for this target. :note: all processors has to be callable with only one parameter which is actual HwModule/RtlNetlist instance """ def __init__(self): self.beforeToRtl = [] self.beforeToRtlImpl = [] self.afterToRtlImpl = [] self.beforeHdlArchGeneration: List[RtlNetlistPass] = [ RtlNetlistPassExtractPartDrivers(), RtlNetlistPassRemoveUnconnectedSignals(), RtlNetlistPassMarkVisibilityOfSignalsAndCheckDrivers(), ] self.afterToRtl = [] ================================================ FILE: hwt/synthesizer/exceptions.py ================================================ from typing import Set class TypeConversionErr(TypeError): pass class ConfErr(Exception): pass class IntfLvlConfErr(ConfErr): """ Interface level synthesizer user configuration error """ pass class SigLvlConfErr(ConfErr): """ Signal level synthesizer user configuration error """ pass class InterfaceStructureErr(IntfLvlConfErr): """ An exception which means that the two interfaces have non compatible sub-interfaces. (E.g. they do have a differently named signals) :ivar exclude: a set of sub-interfaces which should be excluded during the comparison """ def __init__(self, dst: "HwIOBase", src: "HwIOBase", exclude: Set["HwIOBase"]): super(InterfaceStructureErr, self).__init__() self.src = src self.dst = dst self.exclude = exclude def __str__(self): return self.__repr__() def __repr__(self): missing_on_src = [] missing_on_dst = [] dst = self.dst src = self.src exclude = self.exclude srcHwIos = getattr(src, "_hwIOs", None) if srcHwIos is None: # src is likely a constant try: for f in src._dtype.fields: io2 = getattr(dst, f.name, None) if io2 is None and (not exclude or io2 not in exclude): missing_on_dst.append(f.name) except AttributeError: # the type of src is unexpected, can not produce any good err msg. pass else: for sHwIO in srcHwIos: io2 = getattr(dst, sHwIO._name, None) if io2 is None and (not exclude or sHwIO not in exclude): missing_on_dst.append(sHwIO._name) for sHwIO in dst._hwIOs: io2 = getattr(src, sHwIO._name, None) if io2 is None and (not exclude or sHwIO not in exclude): missing_on_src.append(sHwIO._name) buff = [f"<{self.__class__.__name__} {dst} <= {src}"] if missing_on_dst: buff.append(f", missing on dst: {missing_on_dst}") if missing_on_src: buff.append(f", missing on src: {missing_on_src}") buff.append(">") return "".join(buff) def __copy__(self): return self.__class__(self.dst, self.src, self.exclude) ================================================ FILE: hwt/synthesizer/interfaceLevel/__init__.py ================================================ """ interfaceLevel is responsible for manipulation of high-level interfaces. """ ================================================ FILE: hwt/synthesizer/interfaceLevel/directionFns.py ================================================ from hwt.doc_markers import internal from ipCorePackager.constants import INTF_DIRECTION, DIRECTION @internal class HwIODirectionFns(): @internal def _setDirectionsLikeIn(self, hioDir: INTF_DIRECTION): assert hioDir in [INTF_DIRECTION.MASTER, INTF_DIRECTION.SLAVE, INTF_DIRECTION.TRISTATE], hioDir d = DIRECTION.asIntfDirection(self._masterDir) if hioDir == INTF_DIRECTION.MASTER or d == INTF_DIRECTION.TRISTATE: pass else: d = INTF_DIRECTION.opposite(d) self._direction = d for hio in self._hwIOs: hio._setDirectionsLikeIn(d) @internal def _setAsExtern(self, isExtern: bool): """Set interface as external""" self._isExtern = isExtern if not isExtern: self._direction = INTF_DIRECTION.UNKNOWN for chHwIO in self._hwIOs: chHwIO._setAsExtern(isExtern) @internal def _reverseDirection(self): """Reverse direction of this interface in implementation stage""" self._direction = INTF_DIRECTION.opposite(self._direction) for chHwIO in self._hwIOs: chHwIO._reverseDirection() ================================================ FILE: hwt/synthesizer/interfaceLevel/getDefaultClkRts.py ================================================ from hwt.synthesizer.exceptions import IntfLvlConfErr from hwt.mainBases import HwModuleBase def getClk(module: HwModuleBase): """ Get clock signal from unit instance """ try: return module.clk except AttributeError: pass raise IntfLvlConfErr(f"Can not find clock signal on module {module}") def getRst(module: HwModuleBase): """ Get reset signal from unit instance """ try: return module.rst except AttributeError: pass try: return module.rst_n except AttributeError: pass raise IntfLvlConfErr(f"Can not find reset signal on module {module}") ================================================ FILE: hwt/synthesizer/interfaceLevel/hwModuleImplHelpers.py ================================================ from itertools import chain from typing import Union, Optional, Tuple from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.operatorDefs import HOperatorDef from hwt.hdl.types.array import HArray from hwt.hdl.types.defs import BIT from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.struct import HStruct from hwt.hwIOs.hwIOArray import HwIOArray from hwt.hwIOs.hwIOStruct import HdlType_to_HwIO, HwIOStruct from hwt.hwIOs.std import HwIOSignal, HwIOClk, HwIORst, HwIORst_n from hwt.mainBases import HwModuleBase, HwIOBase from hwt.mainBases import RtlSignalBase from hwt.synthesizer.interfaceLevel.getDefaultClkRts import getClk, getRst from hwt.synthesizer.rtlLevel.netlist import RtlNetlist from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from ipCorePackager.constants import INTF_DIRECTION def getSignalName(sig: RtlSignalBase): """ Name getter which works for RtlSignal and HwIO instances as well """ return sig._name def HwIO_getName(top: HwModuleBase, io: Union[HwIOBase, RtlSignal, Tuple[Union[HwIOBase, RtlSignal]]]) -> str: if isinstance(io, HwIOBase): prefix = [] parent = io._parent while parent is not None: if parent is top: break try: prefix.append(parent._name) except AttributeError: prefix.append(repr(parent)) break parent = parent._parent n = io._getFullName() if prefix: prefix.reverse() prefix.append(n) return ".".join(prefix) else: return n elif isinstance(io, tuple): return f"({', '.join(HwIO_getName(top, _io) for _io in io)})" else: return getSignalName(io) @internal def _default_param_updater(self, myP: "HwParam", otherP_val): myP.set_value(otherP_val) @internal def _normalize_default_value_dict_for_HwIO_array(root_val: dict, val: Union[dict, list, None], name_prefix: str, hobj_list: HwIOArray, neutral_value): """ This function is called to convert data in format .. code-block:: python {"x": [3, 4]} # into {"x_0": 3, "x_1": 4} This is required because the items of HwIOArray are stored in _hwIOs as a separate items and thus we can not resolve the value association otherwise. """ for i, intf in enumerate(hobj_list): if val is neutral_value: continue elif isinstance(val, dict): _val = val.get(i, neutral_value) else: _val = val[i] if _val is neutral_value: continue elm_name = f"{name_prefix:s}_{i:d}" if isinstance(intf, HwIOArray): _normalize_default_value_dict_for_HwIO_array(root_val, _val, elm_name, intf, neutral_value) else: root_val[elm_name] = _val @internal def _instantiate_signals(hwIO: Union[HwIOSignal, HwIOArray, HwIOStruct], clk: HwIOClk, rst: Union[HwIORst, HwIORst_n], def_val:Union[int, None, dict, list], nop_val:Union[int, None, dict, list], nextSig: Optional[RtlSignalBase], signal_create_fn): hwIO._direction = INTF_DIRECTION.UNKNOWN if isinstance(hwIO, HwIOSignal): name = hwIO._getHdlName() hwIO._sig = signal_create_fn( name, hwIO._dtype, clk, rst, def_val, nop_val, nextSig) hwIO._sig._hwIO = hwIO elif isinstance(hwIO, HwIOArray): intf_len = len(hwIO) if isinstance(def_val, dict): for k in def_val.keys(): assert k > 0 and k < intf_len, ("Default value for", hwIO, " specifies ", k, " which is not present on interface") elif def_val is not None: assert len(def_val) == intf_len, ("Default value does not have same size, ", len(def_val), intf_len, hwIO) if isinstance(nop_val, dict): for k in nop_val.keys(): assert k > 0 and k < intf_len, ("Nop value for", hwIO, " specifies ", k, " which is not present on interface") elif nop_val is not NOT_SPECIFIED: assert len(nop_val) == intf_len, ("Nop value does not have same size, ", len(nop_val), intf_len, hwIO) for i, elm in enumerate(hwIO): if def_val is None: _def_val = None elif isinstance(def_val, dict): _def_val = def_val.get(i, None) else: _def_val = def_val[i] if nop_val is NOT_SPECIFIED: _nop_val = NOT_SPECIFIED elif isinstance(nop_val, dict): _nop_val = nop_val.get(i, NOT_SPECIFIED) else: _nop_val = nop_val[i] if nextSig is NOT_SPECIFIED: _nextSig = NOT_SPECIFIED else: _nextSig = nextSig.get(i, NOT_SPECIFIED) _instantiate_signals(elm, clk, rst, _def_val, _nop_val, _nextSig, signal_create_fn) else: if def_val is not None: for k in tuple(def_val.keys()): _i = getattr(hwIO, k, NOT_SPECIFIED) assert _i is not NOT_SPECIFIED, ("Default value for", hwIO, " specifies ", k, " which is not present on interface") if isinstance(_i, HwIOArray): _normalize_default_value_dict_for_HwIO_array( def_val, def_val[k], k, _i, None) if nop_val is not NOT_SPECIFIED: for k in tuple(nop_val.keys()): _i = getattr(hwIO, k, NOT_SPECIFIED) assert _i is not NOT_SPECIFIED, ("Nop value for", hwIO, " specifies ", k, " which is not present on interface") if isinstance(_i, HwIOArray): _normalize_default_value_dict_for_HwIO_array( nop_val, nop_val[k], k, _i, NOT_SPECIFIED) for elm in hwIO._hwIOs: name = elm._name if def_val is None: _def_val = None else: _def_val = def_val.get(name, None) if nop_val is NOT_SPECIFIED: _nop_val = NOT_SPECIFIED else: _nop_val = nop_val.get(name, NOT_SPECIFIED) if nextSig is NOT_SPECIFIED: _nextSig = NOT_SPECIFIED else: _nextSig = getattr(nextSig, name) _instantiate_signals(elm, clk, rst, _def_val, _nop_val, _nextSig, signal_create_fn) @internal def _loadHwDeclarations(intf_or_list: HwIOBase, suggested_name: str): if isinstance(intf_or_list, HwIOArray): for i, intf in enumerate(intf_or_list): _loadHwDeclarations(intf, f"{suggested_name:s}_{i:d}") else: intf_or_list._name = suggested_name intf_or_list._loadHwDeclarations() def HwIO_without_registration( parent:HwModuleBase, container: HwIOBase, suggested_name:str, def_val: Union[int, None, dict, list]=None, nop_val: Union[int, None, dict, list, "NOT_SPECIFIED"]=NOT_SPECIFIED, nextSig:Optional[RtlSignalBase]=NOT_SPECIFIED): """ Load all parts of interface and construct signals in RtlNetlist context with an automatic name check, without need to explicitly add the HwIO into _hwIOs list. """ _loadHwDeclarations(container, suggested_name) _instantiate_signals( container, None, None, def_val, nop_val, nextSig, lambda name, dtype, clk, rst, def_val, nop_val, nextSig: parent._sig(name, dtype, def_val=def_val, nop_val=nop_val, )) container._parent = parent parent._private_hwIOs.append(container) return container class HwModuleImplHelpers(HwModuleBase): def _reg(self, name: str, dtype: HdlType=BIT, def_val: Union[int, None, dict, list]=None, clk: Union[RtlSignalBase, None, Tuple[RtlSignalBase, HOperatorDef]]=None, rst: Optional[RtlSignalBase]=None, nextSig:Optional[RtlSignalBase]=NOT_SPECIFIED) -> RtlSignal: """ Create RTL FF register in this unit :param def_val: s default value of this register, if this value is specified reset signal of this component is used to generate a reset logic :param clk: optional clock signal specification, (signal or tuple(signal, edge type (AllOps.RISING_EDGE/FALLING_EDGE))) :param rst: optional reset signal specification :param nextSig: the signal which should be used as "next" signal for this register if is not specified the new signal is generated. (Next signal holds value which should be in register in next clk.) :note: rst/rst_n resolution is done from signal type, if it is negated type the reset signal is interpreted as rst_n :note: if clk or rst is not specified default signal from parent unit instance will be used """ if clk is None: clk = getClk(self) if def_val is None: # if no value is specified reset is not required rst = None elif rst is None: rst = getRst(self) if isinstance(dtype, (HStruct, HArray)): container = HdlType_to_HwIO().apply(dtype) _loadHwDeclarations(container, name) _instantiate_signals( container, clk, rst, def_val, nextSig, NOT_SPECIFIED, lambda name, dtype, clk, rst, def_val, nop_val, nextSig: self._reg(name, dtype, def_val=def_val, clk=clk, rst=rst, nextSig=nextSig)) container._parent = self return container else: # primitive data type signal return self._rtlCtx.sig( name, dtype=dtype, clk=clk, syncRst=rst, def_val=def_val, nextSig=nextSig, ) def _sig(self, name: str, dtype: HdlType=BIT, def_val: Union[int, None, dict, list]=None, nop_val: Union[int, None, dict, list, "NOT_SPECIFIED"]=NOT_SPECIFIED) -> RtlSignal: """ Create signal in this unit :see: :func:`hwt.synthesizer.rtlLevel.netlist.RtlNetlist.sig` """ if isinstance(dtype, HStruct): container = HdlType_to_HwIO().apply(dtype) return HwIO_without_registration(self, container, name, def_val=def_val, nop_val=nop_val) else: # primitive data type signal return self._rtlCtx.sig(name, dtype=dtype, def_val=def_val, nop_val=nop_val) @internal def _cleanThisSubunitRtlSignals(self): """ Disconnect internal signals so unit can be reused by parent unit """ for hwio in chain(self._hwIOs, self._private_hwIOs): hwio._cleanRtlSignals() @internal def _signalsForSubHwModuleEntity(self, context: RtlNetlist, prefix: str): """ generate signals in this context for all ports of this subunit """ for hio in self._hwIOs: if hio._isExtern: hio._signalsForHwIO(context, None, None, prefix=prefix + hio._NAME_SEPARATOR) ================================================ FILE: hwt/synthesizer/interfaceLevel/implDependent.py ================================================ from hwt.mainBases import HwModuleBase from hwt.synthesizer.interfaceLevel.getDefaultClkRts import getRst, getClk from hwt.synthesizer.interfaceLevel.utils import NotSpecifiedError from hwtSimApi.hdlSimulator import HdlSimulator from ipCorePackager.intfIpMeta import IntfIpMetaNotSpecifiedError class HwIOImplDependentFns(): """ HwIO functions which have high potential to be overloaded in concrete interface implementation """ def _getIpCoreIntfClass(self): raise IntfIpMetaNotSpecifiedError() def _initSimAgent(self, sim: HdlSimulator): raise NotSpecifiedError("Override this function in your interface" " implementation to have simultion agent" f" specified ({self})") def _getAssociatedRst(self): """ If interface has associated rst(_n) return it otherwise try to find rst(_n) on parent recursively """ a = self._associatedRst if a is not None: return a p = self._parent assert p is not None if isinstance(p, HwModuleBase): return getRst(p) else: return p._getAssociatedRst() def _getAssociatedClk(self): """ If interface has associated clk return it otherwise try to find clk on parent recursively """ a = self._associatedClk if a is not None: return a p = self._parent assert p is not None if isinstance(p, HwModuleBase): return getClk(p) else: return p._getAssociatedClk() def __copy__(self): """ Create new instance of interface of same type and configuration """ hwIO = self.__class__() hwIO._updateHwParamsFrom(self) return hwIO ================================================ FILE: hwt/synthesizer/interfaceLevel/propDeclrCollector.py ================================================ from types import MethodType from typing import Tuple, Set, Optional, Callable, Self from hdlConvertorAst.translate.common.name_scope import WithNameScope from hwt.doc_markers import internal from hwt.hObjList import HObjList from hwt.hwParam import HwParam from hwt.mainBases import HwModuleBase, HwIOBase from hwt.synthesizer.exceptions import IntfLvlConfErr from hwt.synthesizer.typePath import TypePath @internal def nameAvailabilityCheck(obj: "PropDeclrCollector", propName: str, prop: object): """ Check if not redefining property on obj but allow to cast current property to a HwParam """ cur = getattr(obj, propName, None) if cur is not None and (not isinstance(prop, HwParam) or cur is not prop._initval): p = getattr(obj, propName) raise IntfLvlConfErr(f"{obj} already has property {propName:s} old:{p} new:{prop}") @internal class MakeParamsShared(object): """ All newly added interfaces and units will share all parametes with unit specified in constructor of this object. """ def __init__(self, module: "HwModule", exclude:Optional[Tuple[Set[str], Set[str]]], prefix:str): self.module = module self.exclude = exclude self.prefix = prefix def __enter__(self): orig = self.module._setAttrListener self.orig = orig exclude = self.exclude prefix = self.prefix def MakeParamsSharedWrap(self: "HwModule", iName: str, i: object): if isinstance(i, (HwIOBase, HwModuleBase, HObjList)): i._updateHwParamsFrom(self, exclude=exclude, prefix=prefix) return orig(iName, i) self.module._setAttrListener = MethodType(MakeParamsSharedWrap, self.module) return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.module._setAttrListener = self.orig @internal class MakeClkRstAssociations(object): """ All newly added interfaces will be associated with clk, rst specified in constructor of this object. """ def __init__(self, module: "HwModule", clk=None, rst=None): self.module = module self.clk = clk self.rst = rst def __enter__(self): orig = self.module._setAttrListener self.orig = orig clk = self.clk rst = self.rst def MakeClkRstAssociationsWrap(self, iName, i): if isinstance(i, (HwIOBase, HObjList)): i._make_association(clk=clk, rst=rst) return orig(iName, i) self.module._setAttrListener = MethodType(MakeClkRstAssociationsWrap, self.module) return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.module._setAttrListener = self.orig class PropDeclrCollector(object): """ Class which manages the registration of components and interfaces in specified elaboration phases. It uses __setattr__ listeners to detect new properties and then calls a listener function to process the registration. Used for HwModule, HwIO classes to detect and load interfaces and components. """ def hwConfig(self) -> None: """ Configure object parameters * setup all parameters on this object, use HwParam class instances to allow use of parameter inheritance * called in __init__ of class """ pass def hwDeclr(self) -> None: """ In this function user should specify the declaration of interfaces for communication with outside word. It is also better to declare sub components there as it allows for better parallelization during the build. * _declr method is called after _config * if this object is :class:`hwt.hwModule.HwModule` all interfaces are treated as externally accessible interfaces if this object is HwIO instance all subinterfaces are loaded as well """ pass def hwImpl(self) -> None: """ Implementation - construct main body of the component in this function. * called after _declr """ pass @internal def __setattr__(self, attr:str, value) -> None: """setattr with listener injector""" try: saListerner = self._setAttrListener except AttributeError: super().__setattr__(attr, value) return if saListerner: value = saListerner(attr, value) super().__setattr__(attr, value) # configuration phase @internal def _loadConfig(self) -> None: """ Load params in hwConfig() """ if not hasattr(self, '_hwParams'): self._hwParams = [] self._setAttrListener = self._paramCollector self.hwConfig() self._setAttrListener = None @internal def _registerParameter(self, pName:str, parameter: HwParam) -> None: """ Register HwParam object on interface level object """ nameAvailabilityCheck(self, pName, parameter) # resolve name in this scope assert parameter._name is None, ( "HwParam object is already assigned to %r.%s" % (parameter.module, parameter._name)) # add name in this scope parameter._name = pName parameter._parent = self self._hwParams.append(parameter) def _hwParamsShared(self, exclude: Optional[Tuple[Set[str], Set[str]]]=None, prefix="") -> MakeParamsShared: """ Auto-propagate params by name to child components and interfaces Usage: .. code-block:: python with self._hwParamsShared(): # your interfaces and unit which should share all params with "self" there :param exclude: tuple (src param names to exclude, dst param names to exclude) :param prefix: prefix which should be added to name of child parameters before parameter name matching """ return MakeParamsShared(self, exclude=exclude, prefix=prefix) def _make_association(self, clk=None, rst=None) -> None: """ Associate this object with specified clk/rst """ if clk is not None: assert self._associatedClk is None, ("Already associated with clock", self._associatedClk) self._associatedClk = clk if rst is not None: assert self._associatedRst is None, ("Already associated with reset", self._associatedRst) self._associatedRst = rst def _associated(self, clk=None, rst=None) -> MakeClkRstAssociations: """ associate newly added interfaces to "self" with selected clk, rst (if interface is not associated agents try to find clk/rst by _getAssociatedClk/_getAssociatedRst which will search for any clk/rst on parent recursively) Usage: .. code-block:: python with self._associated(clk=self.myClk, rst=self.myRst): self.myAxi = Axi4Stream() # this interface is associated with myClk and myRst # simulation agents and component builders will use them :param exclude: params which should not be shared """ return MakeClkRstAssociations(self, clk, rst) def _updateHwParamsFrom(self, otherObj: "PropDeclrCollector", updater: Callable[["PropDeclrCollector", HwParam, HwParam], None], exclude: Optional[Tuple[Set[str], Set[str]]], prefix: str) -> "PropDeclrCollector": """ Update all parameters which are defined on self from otherObj :param otherObj: other object which HwParam instances should be updated :param updater: updater function(self, myParameter, otherParameter) :param exclude: tuple of set of param names for src and dst which which should be excluded :param prefix: prefix which should be added to name of paramters of this object before matching parameter name on parent """ excluded_src = set() excluded_dst = set() if exclude is not None: exclude_src = set(exclude[0]) exclude_dst = set(exclude[1]) for myP in self._hwParams: if exclude is not None and myP._name in exclude_dst: excluded_dst.add(myP._name) continue pPName = prefix + myP._name try: otherP = getattr(otherObj, pPName) # if not isinstance(otherP, HwParam): # continue except AttributeError: continue if exclude is not None and pPName in exclude_src: excluded_src.add(pPName) continue updater(self, myP, otherP) if exclude is not None: # assert that what should be excluded really exists assert exclude_src == excluded_src, (exclude_src, excluded_src) assert exclude_dst == excluded_dst, (exclude_dst == excluded_dst) return self # declaration phase @internal def _registerSubmodule(self, mName:str, submodule:"HwModule", onParentPropertyPath: TypePath): """ Register unit object on interface level object """ nameAvailabilityCheck(self, mName, submodule) assert submodule._parent is None submodule._parent = self submodule._name = mName submodule._onParentPropertyPath = onParentPropertyPath self._subHwModules.append(submodule) @internal def _registerHwIO(self, hwIOName: str, hwIO: HwIOBase, onParentPropertyPath: TypePath, isPrivate:bool): """ Register HwIO object on interface level object """ nameAvailabilityCheck(self, hwIOName, hwIO) assert hwIO._parent is None hwIO._parent = self hwIO._name = hwIOName assert isinstance(onParentPropertyPath, TypePath), onParentPropertyPath hwIO._onParentPropertyPath = onParentPropertyPath hwIO._rtlCtx = self._rtlCtx # _setAsExtern() not used because _hwIOs are not initialized yet if isPrivate: self._private_hwIOs.append(hwIO) hwIO._isExtern = False else: self._hwIOs.append(hwIO) hwIO._isExtern = True @internal def _declrCollector(self, name: str, prop: object): if name in ("_associatedClk", "_associatedRst"): object.__setattr__(self, name, prop) return prop onParentPropertyPath = TypePath(name,) if isinstance(prop, HwIOBase): self._registerHwIO(name, prop, onParentPropertyPath, False) elif isinstance(prop, HwModuleBase): self._registerSubmodule(name, prop, onParentPropertyPath) elif isinstance(prop, HObjList): self._registerArray(name, prop, onParentPropertyPath) return prop @internal def _registerArray(self, name: Optional[str], items: HObjList[Self], onParentPropertyPath: TypePath): """ Register array of items on interface level object """ items._parent = self items._name = name items._on_append = self._registerArray_append assert isinstance(onParentPropertyPath, TypePath), onParentPropertyPath items._onParentPropertyPath = onParentPropertyPath for i, item in enumerate(items): self._registerArray_append(items, item, i) @internal def _registerArray_append(self, arr: HObjList[Self], item: Self, index: int): """ Register a single object in the list """ saListerner = self._setAttrListener if arr is self: # register item in the array (parrent) if saListerner: item = saListerner(f"{index:d}", item) if isinstance(item, PropDeclrCollector): item._onParentPropertyPath = TypePath(index,) else: # register item for a list which is stored inside of parent setattr(self, f"{arr._name:s}_{index:d}", item) if isinstance(item, PropDeclrCollector): item._onParentPropertyPath = arr._onParentPropertyPath / index # implementation phase @internal def _loadImpl(self): self._setAttrListener = self._implCollector self.hwImpl() self._setAttrListener = None @internal def _registerSubmoduleInImpl(self, name: str, m: HwModuleBase, onParentPropertyPath: TypePath): """ :attention: unit has to be parametrized before it is registered (some components can change interface by parametrization) """ self._registerSubmodule(name, m, onParentPropertyPath) m._loadHwDeclarations() sm = self._store_manager with WithNameScope(sm, sm.name_scope.parent): self._lazy_loaded.extend(m._to_rtl( self._target_platform, self._store_manager)) m._signalsForSubHwModuleEntity(self._rtlCtx, "sig_" + name) @internal def _registerHwIOInHwImpl(self, hwIOName: str, hwio: HwIOBase, onParentPropertyPath: TypePath): """ Register interface in implementation phase """ raise NotImplementedError() @internal def _paramCollector(self, pName: str, prop): if isinstance(prop, HwParam): self._registerParameter(pName, prop) return prop._initval else: return prop @internal def _implCollector(self, name: str, prop): """ Handle property definitions in _impl phase """ onParentPropertyPath = TypePath(name,) if isinstance(prop, HwIOBase): if prop._parent is self: return prop self._registerHwIOInHwImpl(name, prop, onParentPropertyPath) elif isinstance(prop, HwModuleBase): if prop._parent is self: return prop self._registerSubmoduleInImpl(name, prop, onParentPropertyPath) elif isinstance(prop, HObjList): if prop._parent is self: return prop self._registerArray(name, prop, onParentPropertyPath) return prop ================================================ FILE: hwt/synthesizer/interfaceLevel/utils.py ================================================ from collections.abc import Container, Callable from typing import Union, Optional from hwt.constants import DIRECTION from hwt.hObjList import HObjList from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.mainBases import HwIOBase from hwt.mainBases import RtlSignalBase class NotSpecifiedError(Exception): """ This error means that you need to implement this function to use this functionality e.g. you have to implement Simulation agent for interface when you create new one and you can not use existing """ pass def HwIO_walkSignals(hwio: HwIOBase): if hwio._hwIOs or isinstance(hwio, HObjList): for sHwIO in hwio._hwIOs: yield from HwIO_walkSignals(sHwIO) else: yield hwio def HwIO_connectPacked(srcPacked: RtlSignalBase, dstInterface: Union[HwIOBase, RtlSignalBase], exclude:Optional[Container[Union[HwIOBase, RtlSignalBase]]]=None): """ Connect 1D vector signal to this structuralized interface (LSB of first interface is LSB of result) :param packedSrc: vector which should be connected :param dstInterface: structuralized interface where should packedSrc be connected to :param exclude: sub interfaces of self which should be excluded """ offset = 0 connections = [] for i in list(HwIO_walkSignals(dstInterface)): if exclude is not None and i in exclude: continue sig = i._sig t = sig._dtype w = t.bit_length() if w == 1: if srcPacked._dtype.bit_length() == 1: # avoid indexing on single bit assert offset == 0, srcPacked s = srcPacked else: # select bit from bit vector assert offset < srcPacked._dtype.bit_length(), ("Insufficient amount of bits in srcPacked", srcPacked, w, offset, i) s = srcPacked[offset] offset += 1 else: # select bit slice from bit vector assert srcPacked._dtype.bit_length() >= w + offset, ("Insufficient amount of bits in srcPacked", srcPacked, w, offset, i) s = srcPacked[(w + offset): offset] # src is likely to have insufficient amount of bits if offset += w assert sig._dtype.bit_length() == s._dtype.bit_length(), (sig, s, sig._dtype, s._dtype) connections.append(sig(s._reinterpret_cast(sig._dtype))) return connections def HwIO_walkFlatten(hwio: HwIOBase, shouldEnterHwIOFn: Optional[Callable[[HwIOBase], tuple[bool, bool]]]): """ :param shouldEnterHwIOFn: function (actual hwio) returns tuple (shouldEnter, shouldYield) """ _shouldEnter, _shouldYield = shouldEnterHwIOFn(hwio) if _shouldYield: yield hwio if shouldEnterHwIOFn: for sHwIO in hwio._hwIOs: yield from HwIO_walkFlatten(sHwIO, shouldEnterHwIOFn) def HwIO_pack(hio: HwIOBase, masterDirEqTo=DIRECTION.OUT, exclude:Optional[Container[Union[HwIOBase, RtlSignalBase]]]=None) -> Union[HBitsConst, RtlSignalBase[HBits]]: """ Concatenate all signals to one big signal, recursively (LSB of first interface is LSB of result) :param masterDirEqTo: only signals with this direction are packed :param exclude: sequence of signals/interfaces to exclude """ if not hio._hwIOs: if hio._masterDir == masterDirEqTo: return hio._sig return None res = None for sHwIO in hio._hwIOs: if exclude is not None and sHwIO in exclude: continue if sHwIO._hwIOs: if sHwIO._masterDir == DIRECTION.IN: d = DIRECTION.opposite(masterDirEqTo) else: d = masterDirEqTo s = HwIO_pack(sHwIO, masterDirEqTo=d, exclude=exclude) else: if sHwIO._masterDir == masterDirEqTo: s = sHwIO._sig else: s = None if s is not None: if not isinstance(s._dtype, HBits) or s._dtype.signed is not None: s = s._reinterpret_cast(HBits(s._dtype.bit_length())) if res is None: res = s else: res = s._concat(res) return res ================================================ FILE: hwt/synthesizer/rtlLevel/__init__.py ================================================ """ rtlLevel is responsible for RtlSignal manipulation and design. """ ================================================ FILE: hwt/synthesizer/rtlLevel/exceptions.py ================================================ from enum import Enum class SignalDriverErrType(Enum): ( MISSING_DRIVER, MULTIPLE_COMB_DRIVERS, OUTPUT_WITHOUT_DRIVER, INPUT_WITH_DRIVER ) = range(4) SignalDriverErrType_labels = { SignalDriverErrType.MISSING_DRIVER: " missing driver for:", SignalDriverErrType.MULTIPLE_COMB_DRIVERS: " multiple comb. drivers for:", SignalDriverErrType.OUTPUT_WITHOUT_DRIVER: " outputs without driver:", SignalDriverErrType.INPUT_WITH_DRIVER: " inputs with driver:", } class SignalDriverErr(Exception): """ Signal has multiple combinational drivers (this is not possible in real word) or signal has no driver specified and it drives something which has effect on any output of component (it needs to have a driver but it does not have one) :note: SignalDriverErr([(SignalDriverErrType, sig), ]) """ def __str__(self): try: ctx = self.args[0][0][1]._rtlCtx name = ctx.getDebugScopeName() scope_name = f"{name:s} of class {ctx.parent.__class__}" except Exception: scope_name = "" b = [f"{self.__class__} raised in {scope_name:s}"] err_sigs = sorted(self.args[0], key=lambda x: (x[0].value, x[1]._name)) prev_err_t = None for err_t, sig in err_sigs: if prev_err_t is None or prev_err_t != err_t: b.append(SignalDriverErrType_labels[err_t]) prev_err_t = err_t if err_t == SignalDriverErrType.MULTIPLE_COMB_DRIVERS or\ err_t == SignalDriverErrType.INPUT_WITH_DRIVER: b.append(f" {sig}: {sig._rtlDrivers}") else: b.append(f" {sig}") return "\n".join(b) ================================================ FILE: hwt/synthesizer/rtlLevel/extract_part_drivers.py ================================================ from itertools import islice from typing import Dict, List, Tuple, Union, Optional, Sequence from hwt.code import Concat from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operator import isConst from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.hdl.types.bits import HBits from hwt.hdl.types.bitsConst import HBitsConst from hwt.hdl.types.defs import SLICE from hwt.hdl.types.sliceConst import HSliceConst from hwt.pyUtils.setList import SetList from hwt.serializer.utils import RtlSignal_sort_key, HdlStatement_sort_key from hwt.synthesizer.rtlLevel.rtlNetlistPass import RtlNetlistPass from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal def _format_indexes(indexes): return tuple( (int(i) + 1, int(i)) if isinstance(i, HBitsConst) else (int(i.val.start), int(i.val.stop)) for i in indexes) @internal def construct_tmp_dst_sig_for_slice(dst: RtlSignal, indexes: List[Union[HBitsConst, HSliceConst]], src: Optional[RtlSignal], is_signal_needed: bool) -> RtlSignal: """ Construct a tmp signal or value which will be used instead of slice from original signal :param dst: a signal which slice we want to generate tmp signal for :param indexes: a indexes to specify the slice of the dst :param is_signal_needed: True if we need a signal which will we drive later, else returns HConst instance resolved from default and nop value """ if is_signal_needed: name = dst._name def_val = dst.def_val nop_val = dst._nop_val for i in indexes: def_val = def_val[i] if nop_val is not NOT_SPECIFIED: nop_val = nop_val[i] if is_signal_needed: dst = dst[i] if isinstance(i, HSliceConst): if int(i.val.step) == -1: stop = int(i.val.stop) start = int(i.val.start) name = f"{name}_{start - 1:d}downto{stop:d}" else: raise NotImplementedError(i.val.step) else: _i = int(i) name = f"{name:s}_{_i:d}" if is_signal_needed: tmp_sig = dst._rtlCtx.sig(name, dst._dtype, def_val=def_val, nop_val=nop_val) return tmp_sig elif src is not None: return src elif nop_val is not NOT_SPECIFIED: return nop_val else: return def_val def resolve_splitpoints(s: RtlSignal, parts): split_points = set() add_split_point = split_points.add for i, _, _ in parts: if len(i) != 1: raise NotImplementedError(s, i) i = i[0] if isinstance(i, HBitsConst): # index is normal integer i = int(i) add_split_point(i) add_split_point(i + 1) else: # index is slice assert isinstance(i, HSliceConst), (s, i) add_split_point(int(i.val.start)) add_split_point(int(i.val.stop)) if isinstance(s._dtype, HBits): # add boundary points in the case something is unconnected add_split_point(0) add_split_point(s._dtype.bit_length()) else: raise NotImplementedError(s._dtype) return split_points class RtlNetlistPassExtractPartDrivers(RtlNetlistPass): """ Split parts of bit vectors so each segment has an unique variable. .. code-block:: verilog if (c0) s[0] <= x; if (c1) s[1] <= y; to .. code-block:: verilog wire s_0_tmp; wire s_1_tmp; assign s <= {s_1_tmp, s_0_tmp}; if (c0) s_0_tmp <= x; if (c1) s_1_tmp <= y; """ @classmethod def find_independent_slice_drivers(cls, stm: HdlStatement): if isinstance(stm, HdlAssignmentContainer): if stm.indexes and len(stm.indexes) == 1 and isinstance(stm.dst._dtype, HBits): dst = stm.dst for i in stm.indexes: if not isConst(i): return can_directly_replace_with_src_expr = stm.parentStm is None yield ( dst, tuple(stm.indexes), can_directly_replace_with_src_expr, stm.src if can_directly_replace_with_src_expr else None ) else: for _stm in stm._iter_stms(): yield from cls.find_independent_slice_drivers(_stm) @classmethod def find_all_independent_slice_drivers(cls, statements: Sequence[HdlStatement]): for stm in sorted(statements, key=HdlStatement_sort_key): for s, indexes, can_directly_replace_with_src_expr, src in cls.find_independent_slice_drivers(stm): yield s, indexes, can_directly_replace_with_src_expr, src @classmethod def _collect_indexes_on_variables(cls, statements: Sequence[HdlStatement]): signal_parts = {} for s, indexes, can_directly_replace_with_src_expr, src in cls.find_all_independent_slice_drivers(statements): signal_parts.setdefault(s, []).append((indexes, can_directly_replace_with_src_expr, src)) return signal_parts @classmethod def resolve_final_parts_from_splitpoints_and_parts(cls, signal_parts): final_signal_parts: Dict[RtlSignal, Dict[Tuple[Tuple[int, int], ...], Union[HConst, RtlSignal]]] = {} # split part intervals to non-overlapping chunks for s, parts in sorted(signal_parts.items(), key=lambda x: RtlSignal_sort_key(x[0])): split_point = resolve_splitpoints(s, parts) split_point = sorted(split_point) # prepare part signals new_parts = [] new_parts_dict = {} split_i = 0 end = 0 # :attention: parts are likely to contain parts with same indexes for indexes, can_directly_replace_with_src_expr, src in sorted(parts, key=lambda x: x[0]): if len(indexes) != 1: raise NotImplementedError() i = indexes[0] split_p = split_point[split_i] if isinstance(i, HBitsConst): low = int(i) high = low + 1 index_key = ((high, low),) else: assert isinstance(i, HSliceConst), (s, i) if i.val.step != -1: raise NotImplementedError(s, i) high, low = int(i.val.start), int(i.val.stop) index_key = ((high, low),) while split_p < low: # some parts at the beginning are skipped # that means that that part is not driven by anything # and we need to check default and nop value part_indexes = (SLICE.from_py(slice(low, split_p , -1)),) _src = construct_tmp_dst_sig_for_slice(s, part_indexes, None, isinstance(s._nop_val, RtlSignal)) new_parts.append(_src) _index_key = ((low, split_p),) new_parts_dict[_index_key] = _src, True split_i += 1 split_p = split_point[split_i] this_start_split_p_i = split_i if split_p > low: # some parts at the beginning were already resolved # This can happen if there was some part which started on some <= index and overlaps with this part. try: _, _can_directly_replace_with_src_expr = new_parts_dict[index_key] assert not _can_directly_replace_with_src_expr, (s, index_key) # was already resolved and checked no need to check it again continue except KeyError: pass for i in range(split_i, -1, -1): _sp = split_point[i] if _sp == low: this_start_split_p_i = i assert split_point[this_start_split_p_i] == low # just at the start of this slice next_split_p = split_point[this_start_split_p_i + 1] assert next_split_p <= high, "The next split point can be at most end of current part" if next_split_p == high: assert this_start_split_p_i == split_i, "We should see this part for the first time or the split_i should already be higher" # all bits on this slice are alwyas driven at once, we can instantiate whole part assert split_p == low _src = construct_tmp_dst_sig_for_slice(s, indexes, src, not can_directly_replace_with_src_expr) new_parts.append(_src) assert index_key not in new_parts_dict, (s, index_key) new_parts_dict[index_key] = _src, can_directly_replace_with_src_expr split_i += 1 else: # list of part keys for later search _split_parts = [] prev_sp = split_point[this_start_split_p_i] dst_offset = low assert not can_directly_replace_with_src_expr, (indexes, src) # continue instanciating parts until we reach the end of this part for sp_i, sp in zip(range(this_start_split_p_i + 1, len(split_point)), islice(split_point, this_start_split_p_i + 1, None)): # need to generate sub slice # because this slice has actually multiple individualy driven parts # we need to generate all slice parts because there could be a case where only some sub parts are # driven elsewhere and we would othervise resolve those segments as a constantly driven # but they are in fact driven from this slice if sp > high: break part_key = ((sp, prev_sp),) if sp_i <= split_i: # check if the slice is not driven from some top level constant assignment # which would result is multiple drivers of this slice assert src is None existing_part, _can_directly_replace_with_src_expr = new_parts_dict[part_key] assert not _can_directly_replace_with_src_expr, (s, low, high, existing_part) assert not can_directly_replace_with_src_expr, (s, low, high, existing_part) assert isinstance(existing_part, RtlSignal), (s, low, high, existing_part) else: assert sp_i == split_i + 1, (s, sp_i, split_i) # get actual input signal if src is None: _src = None else: _src = src[sp - dst_offset:prev_sp - dst_offset] part_indexes = (SLICE.from_py(slice(sp, prev_sp, -1)),) _src = construct_tmp_dst_sig_for_slice(s, part_indexes, _src, True) new_parts.append(_src) new_parts_dict[part_key] = _src, can_directly_replace_with_src_expr split_i += 1 _split_parts.append(part_key) prev_sp = sp new_parts_dict[index_key] = _split_parts, False end = max(end, high) if end < split_point[-1]: # something unconnected at the end high, low = split_point[-1], end part_indexes = (SLICE.from_py(slice(high, low , -1)),) _src = construct_tmp_dst_sig_for_slice(s, part_indexes, None, isinstance(s._nop_val, RtlSignal)) new_parts.append(_src) index_key = ((high, low),) new_parts_dict[index_key] = _src, True # construct assignment of concatenation from all parts assert new_parts, (s, parts) s(Concat(*reversed(new_parts))) final_signal_parts[s] = new_parts_dict return final_signal_parts @classmethod def extract_part_drivers_stm(cls, stm: HdlStatement, signal_parts: Dict[RtlSignal, List[Tuple[RtlSignal, List[HConst]]]] ) -> bool: """ :return: True if statement was modified """ if isinstance(stm, HdlAssignmentContainer): dst = stm.dst parts = signal_parts.get(dst, None) if parts is None: return False if stm.indexes and len(stm.indexes) == 1: indexes = _format_indexes(stm.indexes) new_dsts, do_remove_stm = parts[indexes] else: # collect only parts which do not have sub parts (are primitive parts) new_dsts = [] for k, d in parts.items(): if not isinstance(d, list): new_dsts.append(k) new_dsts.sort() do_remove_stm = False if isinstance(new_dsts, list): if stm.parentStm is None: return False assert len(new_dsts) > 1, (dst, new_dsts, stm) # assert not do_remove_stm, (dst, new_dsts, stm) # the driven slice was split to multiple sub slices replacement = [] dst_offset = new_dsts[0][-1][1] for i in new_dsts: new_dst = parts[i][0] new_src = stm.src for _i in i: high, low = _i[0] - dst_offset, _i[1] - dst_offset assert high > 0 and low >= 0, dst_offset assert high > low, (dst, stm, (high, low)) new_src = new_src[high:low] a = new_dst(new_src) replacement.append(a) # it has to have parent statement because it needs to be nested # because otherwise it would not have some overlapping parts driven diferently # under some condition stm.parentStm._replace_child_statement(stm, replacement, False) if do_remove_stm: stm._destroy() elif do_remove_stm: # remove current assignment because we are using src directly # assert stm.parentStm is None, (stm, stm.parentStm) stm._destroy() else: # rewrite the HdlAssignmentContainer instance to use new dst replacement = [new_dsts(stm.src), ] stm.parentStm._replace_child_statement(stm, replacement, False) return True elif isinstance(stm, (IfContainer, SwitchContainer, HdlStmCodeBlockContainer)): modified = False for _stm in stm._iter_stms(): modified |= cls.extract_part_drivers_stm(_stm, signal_parts) if modified: assert not stm._enclosed_for, "_enclosed_for is expected not to be initialized yet" outputs = stm._outputs inputs = stm._inputs stm._outputs = SetList() stm._inputs = SetList() stm._collect_io() if stm.parentStm is None: for o in outputs: if o not in stm._outputs: o._rtlDrivers.remove(stm) for i in inputs: if i not in stm._inputs: i._rtlEndpoints.remove(stm) return True else: raise NotImplementedError("Unknown statement ", stm) return False def runOnRtlNetlist(self, netlist: "RtlNetlist"): signal_parts = self._collect_indexes_on_variables(netlist.statements) if not signal_parts: return final_signal_parts = self.resolve_final_parts_from_splitpoints_and_parts(signal_parts) for stm in sorted(netlist.statements, key=HdlStatement_sort_key): self.extract_part_drivers_stm(stm, final_signal_parts) ================================================ FILE: hwt/synthesizer/rtlLevel/fill_stm_list_with_enclosure.py ================================================ from typing import Set, List, Dict, Optional, Callable from hwt.doc_markers import internal from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.mainBases import RtlSignalBase class HdlAssignmentContainer_constructor(): def __init__(self, src, dst): self.dst = dst self.src = src def __call__(self) -> HdlAssignmentContainer: return HdlAssignmentContainer(self.src, self.dst) @internal def fill_stm_list_with_enclosure(parentStm: Optional[HdlStatement], current_enclosure: Set[RtlSignalBase], statements: ListOfHdlStatement, do_enclose_for: List[RtlSignalBase], enclosure: Dict[RtlSignalBase, Callable[[], HdlStatement]])\ ->ListOfHdlStatement: """ Apply enclosure on list of statements (fill all unused code branches with assignments from value specified by enclosure) :param parentStm: optional parent statement where this list is some branch :param current_enclosure: list of signals for which this statement list is enclosed :param statements: list of statements :param do_enclose_for: selected signals for which enclosure should be used :param enclosure: enclosure values for signals :attention: original statements parameter can be modified :return: new statements """ assert do_enclose_for if statements is None: statements = ListOfHdlStatement() for e_sig in do_enclose_for: if e_sig in current_enclosure: continue enclosed = False for stm in statements.iterStatementsWithOutput(e_sig): if e_sig not in stm._enclosed_for: stm._fill_enclosure(enclosure) enclosed = True break # any statement was not related with this signal, if not enclosed: e = enclosure[e_sig] a: HdlStatement = e() assert isinstance(a, HdlStatement), a statements.append(a) if parentStm is not None: a._set_parent_stm(parentStm, statements) return statements ================================================ FILE: hwt/synthesizer/rtlLevel/mark_visibility_of_signals_and_check_drivers.py ================================================ from typing import Generator, Tuple, List from hwt.doc_markers import internal from hwt.hdl.operator import HOperatorNode from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.statement import HdlStatement from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErrType, \ SignalDriverErr from hwt.synthesizer.rtlLevel.rtlNetlistPass import RtlNetlistPass from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from ipCorePackager.constants import DIRECTION @internal def walk_assignments(stm: HdlStatement, dst: RtlSignal)\ ->Generator[HdlAssignmentContainer, None, None]: if isinstance(stm, HdlAssignmentContainer): if dst is stm.dst: yield stm else: for _stm in stm._iter_stms(): yield from walk_assignments(_stm, dst) @internal class RtlNetlistPassMarkVisibilityOfSignalsAndCheckDrivers(RtlNetlistPass): def runOnRtlNetlist(self, netlist: "RtlNetlist"): """ * check if all signals are driven by something * mark signals with hidden = False if they are connecting statements or if they are external interface """ signals = netlist.signals ioSignals = netlist.hwIOs signals_with_driver_issue: List[Tuple[SignalDriverErrType, RtlSignal]] = [] for sig in signals: # if isinstance(sig._nop_val, (RtlSignal, InterfaceBase)): # sig._nop_val._isUnnamedExpr = False driver_cnt = len(sig._rtlDrivers) has_comb_driver = False if driver_cnt > 1: sig._isUnnamedExpr = False for d in sig._rtlDrivers: if not isinstance(d, HOperatorNode): sig._isUnnamedExpr = False is_comb_driver = False if isinstance(d, HdlPortItem): is_comb_driver = True elif d._event_dependent_from_branch is None: for a in walk_assignments(d, sig): if not a.indexes\ and a._event_dependent_from_branch != 0: is_comb_driver = True break if has_comb_driver and is_comb_driver: signals_with_driver_issue.append( (SignalDriverErrType.MULTIPLE_COMB_DRIVERS, sig)) break has_comb_driver |= is_comb_driver elif driver_cnt == 1: if not isinstance(sig._rtlDrivers[0], HOperatorNode): sig._isUnnamedExpr = False else: sig._isUnnamedExpr = False if sig not in ioSignals.keys(): if not sig.def_val._is_partially_valid(): signals_with_driver_issue.append( (SignalDriverErrType.MISSING_DRIVER, sig)) sig._const = True # chec interface direction if required d = ioSignals.get(sig, None) if d is None: pass elif d is DIRECTION.IN: assert sig._rtlDrivers, sig if len(sig._rtlDrivers) != 1: signals_with_driver_issue.append( (SignalDriverErrType.INPUT_WITH_DRIVER, sig)) elif d is DIRECTION.OUT: if not sig._rtlDrivers: signals_with_driver_issue.append( (SignalDriverErrType.OUTPUT_WITHOUT_DRIVER, sig)) if signals_with_driver_issue: raise SignalDriverErr(signals_with_driver_issue) ================================================ FILE: hwt/synthesizer/rtlLevel/netlist.py ================================================ from typing import List, Optional, Union, Dict, Set, Type from hdlConvertorAst.hdlAst._defs import HdlIdDef from hdlConvertorAst.hdlAst._expr import HdlValueId from hdlConvertorAst.hdlAst._structural import HdlModuleDec, HdlModuleDef, \ HdlCompInst from hwt.code import If from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operatorDefs import HwtOps from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.types.defs import BIT from hwt.hdl.types.hdlType import HdlType from hwt.hwParam import HwParam from hwt.mainBases import HwIOBase from hwt.serializer.utils import HdlStatement_sort_key, RtlSignal_sort_key from hwt.synthesizer.dummyPlatform import DummyPlatform from hwt.synthesizer.exceptions import SigLvlConfErr from hwt.synthesizer.rtlLevel.rtlNetlistPass import RtlNetlistPass from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal, CREATE_NEXT_SIGNAL from hwt.synthesizer.rtlLevel.statements_to_HdlStmCodeBlockContainers import statements_to_HdlStmCodeBlockContainers from ipCorePackager.constants import DIRECTION class RtlNetlist(): """ Hierarchical container for signals :ivar ~.parent: optional parent for debug and late component inspection :ivar ~.signals: set of all signals in this context :ivar ~.statements: list of all statements which are connected to signals in this context :ivar ~.subHwModules: is set of all units in this context :ivar ~.hwIOs: initialized in create_HdlModuleDef :ivar ~.hwModDec: initialized in create_HdlModuleDec :ivar ~.hwModDef: initialized in create_HdlModuleDef """ def __init__(self, parent: Optional["HwModule"]=None): self.parent = parent self.signals: Set[RtlSignal] = set() self.statements: Set[HdlStatement] = set() self.subHwModules: Set["HwModule"] = set() self.hwIOs: Dict[RtlSignal, DIRECTION] = {} self.hwModDec: Optional[HdlModuleDec] = None self.hwModDef: Optional[HdlModuleDef] = None def sig(self, name: str, dtype=BIT, clk=None, syncRst=None, def_val=None, nop_val=NOT_SPECIFIED, nextSig=NOT_SPECIFIED) -> Union[RtlSignal, HwIOBase]: """ Create new signal in this context :param clk: clock signal :param syncRst: synchronous reset signal :param def_val: a default value used for reset and initialization :param nop_val: a value which is used to drive the signal if there is no other drive (used to prevent latches and to specify default values for unconnected signals) :param nextSig: the signal which should be used as "next" signal for this register if is not specified the new signal is generated. (Next signal holds value which should be in register in next clk.) """ _def_val = _try_cast_any_to_HValue(def_val, dtype, True) if nop_val is not NOT_SPECIFIED: nop_val = _try_cast_any_to_HValue(nop_val, dtype, False) signalCls: Type[RtlSignal] = dtype.getRtlSignalCls() if clk is not None: if nextSig is not None and isinstance(nextSig, HwIOBase): nextSig = nextSig._sig s = signalCls(self, name, dtype, _def_val if isinstance(_def_val, HConst) else dtype.from_py(None), nop_val, next_signal=CREATE_NEXT_SIGNAL if nextSig is NOT_SPECIFIED else nextSig) if syncRst is not None and def_val is None: raise SigLvlConfErr( "Probably forgotten default value on sync signal %s", name) # dst_resolve_fn is overridden because default assign would assign to the "next" signal if syncRst is not None: r = If(syncRst._isOn(), s(_def_val, dst_resolve_fn=lambda x: x) ).Else( s(s._rtlNextSig, dst_resolve_fn=lambda x: x) ) else: r = [ s(s._rtlNextSig, dst_resolve_fn=lambda x: x) ] if isinstance(clk, (HwIOBase, RtlSignal)): clk_trigger = clk._onRisingEdge() else: # has to be tuple of (clk_sig, HwtOps.RISING/FALLING_EDGE) clk, clk_edge = clk if clk_edge is HwtOps.RISING_EDGE: clk_trigger = clk._onRisingEdge() elif clk_edge is HwtOps.FALLING_EDGE: clk_trigger = clk._onRisingEdge() else: raise ValueError( "Invalid clock edge specification", clk_edge) If(clk_trigger, r ) else: if syncRst: raise SigLvlConfErr( f"Signal {name:s} has reset but has no clk") if nextSig is not NOT_SPECIFIED: raise SigLvlConfErr( f"Signal {name:s} has nextSig which is used for next register value, but has no clock and thus is not a register.") assert isinstance(_def_val, HConst) or (isinstance(_def_val, RtlSignal) and _def_val._const), (_def_val, "The default value needs to be constant") s = signalCls(self, name, dtype, def_val=_def_val, nop_val=nop_val) return s def create_HdlModuleDec(self, name: str, store_manager: "StoreManager", params: List[HwParam]): """ Generate a module header (entity) for this module """ self.hwModDec = hwModDec = HdlModuleDec() hwModDec.name = store_manager.name_scope.checked_name(name, hwModDec) ns = store_manager.hierarchy_push(hwModDec) # create generics for p in sorted(params, key=lambda x: x._name): hdl_val = p.get_hdl_value() v = HdlIdDef() v.origin = p # sanitize param name v.name = p._name = ns.checked_name(p._name, p) v.type = hdl_val._dtype v.value = hdl_val hwModDec.params.append(v) return hwModDec def create_HdlModuleDef(self, target_platform: DummyPlatform, store_manager: "StoreManager"): """ Generate a module body (architecture) for this module * Resolve name collisions * Convert netlist representation to HdlProcesses * Remove unconnected * Mark visibility of signals """ for optPass in target_platform.beforeHdlArchGeneration: optPass: RtlNetlistPass optPass.runOnRtlNetlist(self) ns = store_manager.name_scope mdef = HdlModuleDef() mdef.dec = self.hwModDec mdef.module_name = HdlValueId(self.hwModDec.name, obj=self.hwModDec) mdef.name = "rtl" processes = sorted(self.statements, key=HdlStatement_sort_key) processes = sorted(statements_to_HdlStmCodeBlockContainers(processes), key=HdlStatement_sort_key) # add signals, variables, etc. in architecture for s in sorted((s for s in self.signals if not s._isUnnamedExpr and s not in self.hwIOs.keys()), key=RtlSignal_sort_key): s: RtlSignal assert s._rtlCtx is self, ("RtlSignals in this context must know that they are in this context", s) v = HdlIdDef() v.origin = s v.name = s._name = ns.checked_name(s._name, s) v.type = s._dtype v.value = s.def_val v.is_const = s._const mdef.objs.append(v) for p in processes: p: HdlStmCodeBlockContainer p.name = ns.checked_name(p.name, p) mdef.objs.extend(processes) # instantiate subModules in architecture for sm in self.subHwModules: ci = HdlCompInst() ci.origin = sm ci.module_name = HdlValueId(sm._rtlCtx.hwModDec.name, obj=sm._rtlCtx.hwModDec) ci.name = HdlValueId(ns.checked_name(sm._name + "_inst", ci), obj=sm) hwModDec = sm._rtlCtx.hwModDec ci.param_map.extend(hwModDec.params) ci.port_map.extend(hwModDec.ports) mdef.objs.append(ci) self.hwModDef = mdef return mdef def getDebugScopeName(self): scope = [] p = self.parent while p is not None: scope.append(p._name) try: p = p._parent except AttributeError: break return ".".join(reversed(scope)) @internal def _try_cast_any_to_HValue(v, dtype: HdlType, require_const: bool): if isinstance(v, RtlSignal): assert not require_const or v._const, \ "Initial value of signal has to be a constant" return v._auto_cast(dtype) elif isinstance(v, HConst): return v._auto_cast(dtype) elif isinstance(v, HwIOBase): return v._sig else: return dtype.from_py(v) ================================================ FILE: hwt/synthesizer/rtlLevel/reduce_processes.py ================================================ from itertools import islice from hwt.doc_markers import internal from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.utils.reduction import HdlStatement_merge_statement_lists, \ is_mergable_statement_list from hwt.pyUtils.arrayQuery import areSetsIntersets, groupedby from hwt.serializer.utils import HdlStatement_sort_key class HwtStmIncompatibleStructure(Exception): """ Statements are not comparable due incompatible structure """ @internal def checkIfIsTooSimple(proc): """check if process is just unconditional assignments and it is useless to merge them""" try: a, = proc.statements if isinstance(a, HdlAssignmentContainer): return True except ValueError: pass return False @internal def tryToMerge(procA: HdlStmCodeBlockContainer, procB: HdlStmCodeBlockContainer): """ Try merge procB into procA :raise IncompatibleStructure: if merge is not possible :attention: procA is now result if merge has succeed :return: procA which is now result of merge """ if (areSetsIntersets(procA._outputs, procB._sensitivity) or areSetsIntersets(procB._outputs, procA._sensitivity) or not is_mergable_statement_list(procA.statements, procB.statements)): raise HwtStmIncompatibleStructure() procA.statements = HdlStatement_merge_statement_lists( procA.statements, procB.statements) procB.statements = None procA._outputs.extend(procB._outputs) procA._inputs.extend(procB._inputs) procA._sensitivity.extend(procB._sensitivity) return procA @internal def reduceProcesses(processes): """ Try to merge processes as much is possible :param processes: list of processes instances """ # sort to make order of merging same deterministic processes.sort(key=HdlStatement_sort_key, reverse=True) # now try to reduce processes with nearly same structure of statements into one # to minimize number of processes for _, procs in groupedby(processes, lambda p: p.rank): _procs = [] for p in procs: if checkIfIsTooSimple(p): yield p else: _procs.append(p) procs = _procs for iA, pA in enumerate(procs): if pA is None: continue for iB, pB in enumerate(islice(procs, iA + 1, None)): if pB is None: continue try: pA = tryToMerge(pA, pB) except HwtStmIncompatibleStructure: continue procs[iA + 1 + iB] = None # procs[iA] = pA for p in procs: if p is not None: yield p ================================================ FILE: hwt/synthesizer/rtlLevel/remove_unconnected_signals.py ================================================ from collections import deque from io import StringIO from typing import Optional from hwt.doc_markers import internal from hwt.hdl.operator import HOperatorNode from hwt.hdl.portItem import HdlPortItem from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.ifContainter import IfContainer from hwt.hdl.statements.statement import HdlStatement from hwt.hdl.statements.switchContainer import SwitchContainer from hwt.mainBases import RtlSignalBase from hwt.synthesizer.interfaceLevel.utils import HwIO_walkSignals from hwt.synthesizer.rtlLevel.rtlNetlistPass import RtlNetlistPass from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal from ipCorePackager.constants import DIRECTION @internal def walkInputsForSpecificOutput(output_sig: RtlSignalBase, stm: HdlStatement): if output_sig not in stm._outputs: return elif isinstance(stm, HdlAssignmentContainer): assert stm.dst is output_sig yield from stm._inputs return elif isinstance(stm, IfContainer): yield stm.cond for c, _ in stm.elIfs: yield c elif isinstance(stm, SwitchContainer): yield stm.switchOn elif isinstance(stm, HdlStmCodeBlockContainer): pass else: raise NotImplementedError(stm) for _stm in stm._iter_stms_for_output(output_sig): yield from walkInputsForSpecificOutput(output_sig, _stm) @internal class RtlNetlistPassRemoveUnconnectedSignals(RtlNetlistPass): def __init__(self, traceOutput:Optional[StringIO]=None): self.traceOutput = traceOutput def runOnRtlNetlist(self, netlist: "RtlNetlist"): """ Remove signal if does not affect output :attention: does not remove signals in cycles which does not affect outputs """ trace = self.traceOutput # walk circuit from outputs to inputs and collect seen signals toSearch = deque(s for s, d in netlist.hwIOs.items() if d != DIRECTION.IN) seen = set(toSearch) for c in netlist.subHwModules: for sig in HwIO_walkSignals(c): # if sig._direction == INTF_DIRECTION.SLAVE and sig._masterDir == DIRECTION.OUT: # continue # if sig._direction == INTF_DIRECTION.MASTER and sig._masterDir == DIRECTION.IN: # continue s = sig._sig assert s is not None, (netlist.parent, sig, "broken HwIO instance") assert s._rtlCtx is netlist, (netlist.parent, s, "must be in the same netlist") toSearch.append(s) while toSearch: sig = toSearch.popleft() for e in sig._rtlDrivers: if isinstance(e, HOperatorNode): inputs = e.operands elif isinstance(e, HdlPortItem): # we are already added inputs of all components continue else: assert e in netlist.statements, ("Statement must be registered in the netlist", e) inputs = walkInputsForSpecificOutput(sig, e) for i in inputs: if isinstance(i, RtlSignalBase) and i not in seen: assert i._rtlCtx is not None, (netlist.parent, e, "input does not have netlist assigned", i) assert i._rtlCtx is netlist, (netlist.parent, e, "all inputs must be in the same netlist", i) seen.add(i) toSearch.append(i) nv = sig._nop_val if isinstance(nv, RtlSignalBase): if nv not in seen: assert nv._rtlCtx is netlist, nv seen.add(nv) toSearch.append(nv) # add all io because it can not be removed seen.update(s for s, d in netlist.hwIOs.items() if d == DIRECTION.IN) for c in netlist.subHwModules: for sig in HwIO_walkSignals(c): s = sig._sig assert s is not None, (netlist.parent, sig, "broken HwIO instance after initial scan") assert s._rtlCtx is netlist, (netlist.parent, s, "must be in the same netlist") seen.add(s) # remove signals which were not seen for sig in netlist.signals: sig: RtlSignal if sig in seen: # if it was seen it was used and it should not be removed continue if trace is not None: trace.write("removing unseen: ") trace.write(repr(sig)) trace.write("\n") assert sig._rtlCtx is netlist, (netlist.parent, sig, "must be in the same netlist") for e in tuple(sig._rtlDrivers): # drivers of this signal are useless rm them if isinstance(e, HOperatorNode): removed_e = e elif isinstance(e, HdlPortItem): raise NotImplementedError(sig) else: removed_e = e._cut_off_drivers_of(sig) if removed_e is not None: # must not destroy before processing inputs if trace is not None: trace.write("removing: ") trace.write(repr(removed_e)) trace.write("\n") removed_e._destroy() hwIO = getattr(sig, "_hwIO", None) if hwIO: if hwIO._sig is sig: hwIO._sig = None else: assert hwIO._sigInside is None or hwIO._sigInside is sig, (hwIO, hwIO._sigInside, sig) hwIO._sigInside = None netlist.signals = seen ================================================ FILE: hwt/synthesizer/rtlLevel/rtlNetlistPass.py ================================================ class RtlNetlistPass(): def runOnRtlNetlist(self, netlist: "RtlNetlist"): raise NotImplementedError("Override this function in your implementation of this abstract class") ================================================ FILE: hwt/synthesizer/rtlLevel/rtlSignal.py ================================================ from copy import copy from typing import Generator, Dict, Tuple, Set, Union, Self, List, \ Literal, Optional from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.operatorDefs import HOperatorDef, HwtOps, CAST_OPS from hwt.hdl.portItem import HdlPortItem from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer from hwt.hdl.statements.statement import HdlStatement, HwtSyntaxError from hwt.hdl.types.bitsCastUtils import fitTo_t from hwt.hdl.types.defs import SLICE, INT from hwt.hdl.types.hdlType import HdlType from hwt.hdl.types.typeCast import toHVal from hwt.hdl.variables import HdlSignalItem from hwt.mainBases import RtlSignalBase, HwIOBase from hwt.pyUtils.setList import SetList from hwt.synthesizer.exceptions import TypeConversionErr from hwt.synthesizer.rtlLevel.exceptions import SignalDriverErr, \ SignalDriverErrType OperatorCaheKeyType = Union[ Tuple['OpDefinition', int, object], Tuple['OpDefinition', int, object, object], Tuple['OpDefinition', int, object, object, object], ] class CREATE_NEXT_SIGNAL(): def __init__(self): raise AssertionError("This class should be used as a constant") class RtlSignal(RtlSignalBase, HdlSignalItem): """ RtlSignal signal is a connection between statements and operators in circuit graph. :ivar ~._rtlEndpoints: SetList of operators and statements for which this signal is driver. :ivar ~._rtlDrivers: SetList of operators and statements which can drive this signal. If driver is statement tree only top statement is present. :ivar ~._usedOps: A dictionary of used operators which can be reused. :ivar ~._usedOpsAlias: A dictionary tuple of operator and operands to set of tuples of operator and operands, used to resolve which combination of the operator and operands resulted in to same result. :note: The _usedOps, _usedOpsAlias cache record is generated only for the left most signal in expression. :ivar ~._isUnnamedExpr: means that this signal is part of expression and should not be rendered :ivar ~._nop_val: value which is used to fill up statements when no other value is assigned, use NOT_SPECIFIED to disable :ivar ~._const: flag which tell that this signal can not have any other driver than a default value :cvar __instCntr: counter used for generating instance ids :ivar ~._instId: internally used only for intuitive sorting of statements in serialized code :ivar ~._rtlObjectOrigin: optionally an object which generated this signal :ivar ~._rtlNextSig: optional signal signal which is used as a next signal if this RtlSignal is actually a FF output. """ __instCntr = 0 __slots__ = [ "_ctx", "_rtlEndpoints", "_rtlDrivers", "_usedOps", "_usedOpsAlias", "_isUnnamedExpr", "_hdlName", "_hasGenericName", "_instId", "_nop_val", "_const", "_hwIO", "_rtlObjectOrigin", "_rtlNextSig", ] def __init__(self, ctx: 'RtlNetlist', name: str, dtype: HdlType, def_val=None, nop_val=NOT_SPECIFIED, next_signal:Union[RtlSignalBase, Literal[NOT_SPECIFIED, CREATE_NEXT_SIGNAL]]=NOT_SPECIFIED, virtual_only=False, is_const=False): """ :param ctx: context - RtlNetlist which is this signal part of :param name: name hint for this signal, if is None name is chosen automatically :param def_val: value which is used for reset and as default value in HDL :param nop_val: value which is used to fill up statements when no other value is assigned, use NOT_SPECIFIED to disable :param is_const: flag which tell that this signal can not have any other driver than a default value """ self._instId: int = RtlSignal._nextInstId() if name is None: name = "sig_" self._hasGenericName = True else: self._hasGenericName = False assert isinstance(dtype, HdlType) super(RtlSignal, self).__init__(name, dtype, def_val, virtual_only=virtual_only) self._rtlCtx = ctx if ctx: # params do not have any context on created # and it is assigned after param is bounded to unit or interface ctx.signals.add(self) # set can not be used because hash of items are changing self._rtlEndpoints: SetList[Union[HdlStatement, HdlPortItem, "Operator"]] = SetList() self._rtlDrivers: SetList[HdlStatement, HdlPortItem, "Operator"] = SetList() self._usedOps: Dict[OperatorCaheKeyType, RtlSignal] = {} self._usedOpsAlias: Dict[OperatorCaheKeyType, Set[OperatorCaheKeyType]] = {} self._isUnnamedExpr: bool = True self._nop_val = nop_val self._const = is_const self._rtlObjectOrigin = None if nop_val is NOT_SPECIFIED: nop_val = self # construct next signal if requested if next_signal is NOT_SPECIFIED: _next_signal = None elif next_signal is CREATE_NEXT_SIGNAL: _next_signal = self.__class__(ctx, name + "_next", dtype, nop_val=nop_val) else: assert isinstance(next_signal, RtlSignalBase), next_signal assert next_signal._dtype is dtype _next_signal = next_signal if _next_signal._nop_val is NOT_SPECIFIED: _next_signal._nop_val = self self._rtlNextSig: Optional[RtlSignal] = _next_signal @internal @classmethod def _nextInstId(cls): """ Get next instance id """ i = cls.__instCntr cls.__instCntr += 1 return i def staticEval(self): # operator writes in self._val new value driven_by_def_val = True if self._rtlDrivers: for d in self._rtlDrivers: if isinstance(d, HdlPortItem): assert d.getInternSig() is self, (d, self) continue d.staticEval() driven_by_def_val = False if driven_by_def_val: if isinstance(self.def_val, RtlSignal): self._val = self.def_val._val.staticEval() else: # _val is invalid initialization value self._val = self.def_val.__copy__() if not isinstance(self._val, HConst): raise ValueError( "Evaluation of signal returned not supported object (%r)" % (self._val,)) return self._val def singleDriver(self): """ Returns a first driver if signal has only one driver. """ d_cnt = len(self._rtlDrivers) if d_cnt == 0: raise SignalDriverErr([(SignalDriverErrType.MISSING_DRIVER, self), ]) elif d_cnt > 1: raise SignalDriverErr([(SignalDriverErrType.MULTIPLE_COMB_DRIVERS, self), ]) return self._rtlDrivers[0] @internal def _walk_sensitivity(self, casualSensitivity: Set[RtlSignalBase], seen: Set[RtlSignalBase], ctx: SensitivityCtx): """ Walk expression and collect signals which is this expression sensitive to. (:see: what is signal sensitivity in vhdl/verilog) :param casualSensitivity: set of public signals which is this expression sensitive to but rising/faling edge operator is not present :param seen: set of all seen signals :param ctx: context where sensitivity """ seen.add(self) if self._const: return if not self._isUnnamedExpr: casualSensitivity.add(self) return try: op = self.singleDriver() except SignalDriverErr: op = None if op is None or isinstance(op, HdlStatement): casualSensitivity.add(self) return op._walk_sensitivity(casualSensitivity, seen, ctx) @internal def _walk_public_drivers(self, seen: set) -> Generator["RtlSignal", None, None]: """ Walk all non hidden signals in an expression """ seen.add(self) if not self._isUnnamedExpr: yield self return assert self._rtlDrivers, self for d in self._rtlDrivers: # d has to be operator otherwise this signal would be public itself assert not isinstance(d, HdlStatement), (d.__class__) yield from d._walk_public_drivers(seen) def _auto_cast(self, toType: HdlType): """ Cast value or signal of this type to another compatible type. :param toType: instance of HdlType to cast into """ return self._dtype.auto_cast_RtlSignal(self, toType) def _reinterpret_cast(self, toType: HdlType): """ Cast value or signal of this type to another type of same size. :param toType: instance of HdlType to cast into """ return self._dtype.reinterpret_cast_RtlSignal(self, toType) @internal def _create_HOperator(self, operator: HOperatorDef, opCreateDelegate , *otherOps) -> Union[Self, HConst]: """ Try lookup operator with this parameters in _usedOps if not found create new one and stored it in _usedOps :param operator: instance of HOperatorDef :param opCreateDelegate: function (\\*ops) to create operator :param otherOps: other operands (ops = self + otherOps) :return: RtlSignal which is result of newly created operator """ indexOfSelfInOperands = 0 k = (operator, indexOfSelfInOperands, *otherOps) used = self._usedOps try: return used[k] except KeyError: pass o = opCreateDelegate(self, *otherOps) # input operands may be type converted, # search if this happened, and return always same result signal try: op_instantiated = (o._rtlObjectOrigin.operator == operator and o._rtlObjectOrigin.operands[indexOfSelfInOperands] is self) except AttributeError: op_instantiated = False usedOpsAlias = self._usedOpsAlias if op_instantiated: # try check real operands and operator which were used after all default type conversions k_real = (operator, indexOfSelfInOperands, *o._rtlObjectOrigin.operands[1:]) if k != k_real: alias = usedOpsAlias[k_real] usedOpsAlias[k] = alias alias.add(k) used[k] = o return o @internal def _getIndexCascade(self): """ Find out if this signal is something indexed """ hwIO = self indexes = [] sign_cast_seen = False while True: try: # now self is the result of the index xxx[xx] <= source # get index op d = hwIO.singleDriver() try: op = d.operator except AttributeError: # probably port or statement break if op == HwtOps.INDEX or op == HwtOps.DOT: # get signal on which is index applied indexedOn = d.operands[0] if isinstance(indexedOn, RtlSignalBase): hwIO = indexedOn indexes.append(d.operands[1]) else: raise HwtSyntaxError("can not assign to a static value", indexedOn) elif op == HwtOps.TRUNC: indexedOn = d.operands[0] width = int(d.operands[1]) if isinstance(indexedOn, RtlSignalBase): if hwIO._dtype.bit_length() > 1 or hwIO._dtype.force_vector: indexes.append(SLICE.from_py(slice(width, 0, -1))) else: indexes.append(INT.from_py(width)) hwIO = indexedOn else: raise HwtSyntaxError("can not assign to a static value", indexedOn) elif op in CAST_OPS: sign_cast_seen = True hwIO = d.operands[0] else: # the concatenations should have been already resolved before entering of this function raise HwtSyntaxError( f"can not assign to result of operator {d}") except SignalDriverErr: break if not indexes: indexes = None else: indexes.reverse() return hwIO, indexes, sign_cast_seen def _getDestinationSignalForAssignmentToThis(self): """ :return: a signal which should be used as a destination if assigning to this signal """ return self if self._rtlNextSig is None else self._rtlNextSig def __call__(self, source, dst_resolve_fn=lambda x: x._getDestinationSignalForAssignmentToThis(), exclude=None, fit=False) -> Union[HdlAssignmentContainer, List[HdlAssignmentContainer]]: """ Create assignment to this signal :attention: it is not call of function it is operator of assignment :return: list of assignments """ assert not self._const, self if exclude is not None and (self in exclude or source in exclude): return [] if isinstance(source, HwIOBase) and not source._hwIOs: assert source._isAccessible, (source, "must be a Signal Interface which is accessible in current scope") source = source._sig assert self._dtype.isScalar(), ("For non scalar types this should be overriden", self._dtype, self.__class__, self) try: if source is None: requires_type_check = False source = self._dtype.from_py(None) else: requires_type_check = True source = toHVal(source, suggestedType=self._dtype) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified if requires_type_check: err = False try: if fit: source = fitTo_t(source, self._dtype) source = source._auto_cast(self._dtype) except TypeConversionErr: err = True if err: raise TypeConversionErr( "Can not connect ", source._dtype, " to ", self._dtype, " ", source, self) if self._isUnnamedExpr: try: d = self.singleDriver() except: d = None operator = getattr(d, "operator", None) if operator is not None: if operator.allowsAssignTo: if operator == HwtOps.NOT: # instead of assigning to negation we assign the negation return d.operands[0](~source, dst_resolve_fn=dst_resolve_fn, exclude=exclude, fit=fit) elif operator in CAST_OPS: # we need to assert that src and dst type matches, but we do not anything else dst = d.operands[0] src_sign = source._dtype.signed dst_sign = dst._dtype.signed if src_sign == dst_sign: return dst(source) elif dst_sign is None: return dst(source._vec()) elif dst_sign: return dst(source._signed()) else: return dst(source._unsigned()) elif operator == HwtOps.CONCAT: offset = 0 res = [] # reversed because LSB first for op in reversed(d.operands): w = op._dtype.bit_length() res.append(op(source[w + offset: offset])) offset += w return res else: raise AssertionError("Assignment to operator is not allowed by operator definition", self,) try: mainSig, indexCascade, signCastSeen = self._getIndexCascade() mainSig = dst_resolve_fn(mainSig) if signCastSeen: src_sign = source._dtype.signed dst_sign = mainSig._dtype.signed if src_sign == dst_sign: pass elif dst_sign is None: source = source._vec() elif dst_sign: source = source._signed() else: source = source._unsigned() return HdlAssignmentContainer(source, mainSig, indexCascade) except Exception as e: # simplification of previous exception traceback e_simplified = copy(e) raise e_simplified def _getAssociatedClk(self) -> Self: assert self._rtlNextSig is not None, self d = self.singleDriver() # this expects a simple if rising_edge(clk) # assert isinstance(d, IfContainer), d cond = d.cond.singleDriver() # assert isinstance(cond, HOperatorNode) and cond.operator is HwtOps.RISING_EDGE, cond return cond.operands[0] def _getAssociatedRst(self) -> Self: assert self._rtlNextSig is not None, self d = self.singleDriver() # this expects a simple if rising_edge(clk) # assert isinstance(d, IfContainer), d # cond = d.cond.singleDriver() # assert isinstance(cond, HOperatorNode) and cond.operator is HwtOps.RISING_EDGE, cond assert len(d.ifTrue) == 1 reset_if = d.ifTrue[0] return reset_if.cond def _is_full_valid(self) -> bool: return self._const and self._val._is_full_valid() def _is_partially_valid(self) -> bool: return self._const and self._val._is_partially_valid() ================================================ FILE: hwt/synthesizer/rtlLevel/rtlSignalWalkers.py ================================================ from hwt.doc_markers import internal from hwt.hdl.operator import HOperatorNode from hwt.hdl.operatorDefs import isEventDependentOp from hwt.hdl.sensitivityCtx import SensitivityCtx from hwt.mainBases import RtlSignalBase @internal def discoverEventDependency(sig: RtlSignalBase): """ :return: generator of tuples (event operator, signal) """ try: drivers = sig._rtlDrivers except AttributeError: return if len(drivers) == 1: d = drivers[0] if isinstance(d, HOperatorNode): if isEventDependentOp(d.operator): yield (d.operator, d.operands[0]) else: for op in d.operands: yield from discoverEventDependency(op) @internal def discover_sensitivity_of_sig(signal: RtlSignalBase, seen: set, ctx: SensitivityCtx): casualSensitivity = set() signal._walk_sensitivity(casualSensitivity, seen, ctx) if not ctx.contains_ev_dependency: # if event dependent sensitivity found do not add other sensitivity ctx.extend(casualSensitivity) ================================================ FILE: hwt/synthesizer/rtlLevel/statements_to_HdlStmCodeBlockContainers.py ================================================ from copy import copy from itertools import compress from typing import Generator, List, Tuple from hwt.constants import NOT_SPECIFIED from hwt.doc_markers import internal from hwt.hdl.statements.codeBlockContainer import HdlStmCodeBlockContainer from hwt.hdl.statements.statement import HwtSyntaxError from hwt.hdl.statements.utils.listOfHdlStatements import ListOfHdlStatement from hwt.mainBases import RtlSignalBase from hwt.pyUtils.setList import SetList from hwt.synthesizer.rtlLevel.fill_stm_list_with_enclosure import fill_stm_list_with_enclosure, \ HdlAssignmentContainer_constructor from hwt.synthesizer.rtlLevel.reduce_processes import reduceProcesses from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal @internal def cut_off_drivers_of(dstSignal: RtlSignal, statements: ListOfHdlStatement)\ -> Tuple[ListOfHdlStatement, ListOfHdlStatement]: """ Cut off drivers from statements """ separated = ListOfHdlStatement() stm_filter = [] for stm in statements: stm._clean_signal_meta() d = stm._cut_off_drivers_of(dstSignal) if d is not None: separated.append(d) f = d is not stm stm_filter.append(f) return ListOfHdlStatement(compress(statements, stm_filter)), separated @internal def name_for_process(outputs: List[RtlSignal]) -> str: """ Resolve name for process """ out_names = [] for sig in outputs: if not sig._hasGenericName: out_names.append(sig._name) if out_names: return min(out_names) else: return "" @internal def _statements_to_HdlStmCodeBlockContainers(_statements, tryToSolveCombLoops: bool)\ ->Generator[HdlStmCodeBlockContainer, None, None]: assert _statements # try to simplify statements proc_statements = ListOfHdlStatement() for _stm in _statements: _stm._clean_signal_meta() stms, _ = _stm._try_reduce() proc_statements.extend(stms) if not proc_statements: return outputs = SetList() _inputs = SetList() sensitivity = SetList() enclosed_for = set() _proc_statements = ListOfHdlStatement() for _stm in proc_statements: seen = set() _stm._discover_sensitivity(seen) _stm._discover_enclosure() if _stm._outputs: # remove a statement entirely if it has no ouput # (empty if statment or something similar) # simulation only processes should not be processed by this function # and process should always drive something, unless it is useless outputs.extend(_stm._outputs) _inputs.extend(_stm._inputs) sensitivity.extend(_stm._sensitivity) enclosed_for.update(_stm._enclosed_for) _proc_statements.append(_stm) proc_statements = _proc_statements if not proc_statements: # this can happen e.g. when If does not contains any HdlAssignmentContainer return sensitivity_recompute = False enclosure_recompute = False enclosure_values = {} for sig in outputs: # inject nop_val if needed if sig._nop_val is not NOT_SPECIFIED and sig not in enclosed_for: enclosure_recompute = True n = sig._nop_val enclosure_values[sig] = HdlAssignmentContainer_constructor(n, sig) if isinstance(n, RtlSignalBase): _inputs.append(n) sensitivity_recompute = True if enclosure_recompute: # we have some enclosure values, try fill missing code branches with # this values do_enclose_for = [o for o in outputs if o in enclosure_values] fill_stm_list_with_enclosure(None, enclosed_for, proc_statements, do_enclose_for, enclosure_values) if enclosure_recompute or sensitivity_recompute: for _stm in proc_statements: _stm._clean_signal_meta() seen = set() _stm._discover_sensitivity(seen) _stm._discover_enclosure() if sensitivity_recompute: sensitivity.clear() for _stm in proc_statements: sensitivity.extend(_stm._sensitivity) for o in outputs: assert not o._isUnnamedExpr, o seen = set() inputs = SetList() for i in _inputs: inputs.extend(i._walk_public_drivers(seen)) intersect = outputs.intersection_set(sensitivity) if intersect: # there is a combinational loop inside a single process which # can not be solved by separation of statments in process if not tryToSolveCombLoops: raise HwtSyntaxError( "Combinational loop on signal(s)", intersect) # try to solve combinational loops by separating drivers of signals # from statements for sig in intersect: proc_statements, proc_stms_select = cut_off_drivers_of( sig, proc_statements) yield from _statements_to_HdlStmCodeBlockContainers(proc_stms_select, False) if proc_statements: yield from _statements_to_HdlStmCodeBlockContainers(proc_statements, False) else: # no combinational loops, wrap current statemetns to a process instance name = name_for_process(outputs) yield HdlStmCodeBlockContainer.from_known_io( "assig_process_" + name, proc_statements, sensitivity, inputs, outputs) @internal def statements_to_HdlStmCodeBlockContainers(statements: ListOfHdlStatement)\ ->Generator[HdlStmCodeBlockContainer, None, None]: """ Pack statements into HdlStmCodeBlockContainer instances * for each out signal resolve it's drivers and collect them * split statements if there is and combinational loop * merge statements if it is possible * resolve sensitivity lists * wrap into HdlStmCodeBlockContainer instance * for every IO of process generate name if signal has not any """ # create copy because this set will be reduced statements = copy(statements) # process ranks = how many assignments is probably in process # used to minimize number of merge tries processes = [] while statements: stm = statements.pop() proc_statements = [stm, ] ps = _statements_to_HdlStmCodeBlockContainers(proc_statements, True) processes.extend(ps) yield from reduceProcesses(processes) ================================================ FILE: hwt/synthesizer/typePath.py ================================================ from copy import deepcopy from typing import Union, SupportsIndex, Self class TypePath(tuple[Union[str, int], ...]): """ A path in hierarchy of structuralized type """ def __new__ (cls, *objs): return super(TypePath, cls).__new__(cls, objs) def __truediv__(self, other: Union[int, str, "TypePath"]): if isinstance(other, TypePath): return TypePath(*self, *other) else: assert isinstance(other, (int, str)), other return TypePath(*self, other) def getOnObject(self, o): for n in self: if isinstance(n, int): o = o[n] else: assert isinstance(n, str), n o = getattr(o, n) return o def __getitem__(self, key:SupportsIndex)->Union[Self, int, str]: if isinstance(key, int): return tuple.__getitem__(self, key) else: return TypePath(*tuple.__getitem__(self, key)) def setOnObject(self, o, newV): assert self o = self[:-1].getOnObject(o) n = self[-1] if isinstance(n, int): o[n] = newV else: assert isinstance(n, str), n setattr(o, n, newV) def __copy__(self): return self.__class__(*self) def __deepcopy__(self, memo): res = self.__class__(*(deepcopy(x, memo) for x in self)) memo[self] = res return res ================================================ FILE: hwt/synthesizer/vectorUtils.py ================================================ from math import ceil from typing import Union from hwt.doc_markers import internal from hwt.hdl.const import HConst from hwt.hdl.types.bits import HBits from hwt.hdl.types.utils import walkFlattenFields from hwt.mainBases import RtlSignalBase class NotEnoughtBitsErr(Exception): """ More bits is required for such an operation """ class BitWalker(): """ Walker which can walk chunks of bits on signals/values of all types :ivar ~.sigOrConst: signal or value to iterate over :ivar ~.fillup: flag that means that if there is not enough bits for last item fill it up with invalid bits (otherwise raise) """ def __init__(self, sigOrConst: Union[RtlSignalBase, HConst], skipPadding: bool=True, fillup: bool=False): """ :param skipPadding: if true padding is skipped in dense types """ self.it = walkFlattenFields(sigOrConst, skipPadding=skipPadding) self.fillup = fillup self.actuallyHave = 0 self.actual = None self.actualOffset = 0 @internal def _get(self, numberOfBits: int, doCollect: bool): """ :param numberOfBits: number of bits to get from actual position :param doCollect: if False output is not collected just iterator moves in data structure """ if not isinstance(numberOfBits, int): numberOfBits = int(numberOfBits) while self.actuallyHave < numberOfBits: # accumulate while not has enough try: f = next(self.it) except StopIteration: if self.fillup and self.actual is not None: break else: raise NotEnoughtBitsErr() thisFieldLen = f._dtype.bit_length() if self.actual is None: if not doCollect and thisFieldLen <= numberOfBits: numberOfBits -= thisFieldLen else: self.actual = f self.actuallyHave = thisFieldLen else: if not doCollect and self.actuallyHave < numberOfBits: self.actuallyHave = thisFieldLen self.actual = f else: self.actuallyHave += thisFieldLen self.actual = f._concat(self.actual) # slice out from actual actual = self.actual actualOffset = self.actualOffset if self.actuallyHave < numberOfBits: assert self.fillup if doCollect: t = self.actual._dtype fillupW = numberOfBits - self.actuallyHave padding_t = HBits(fillupW, signed=t.signed, negated=t.negated) padding = padding_t.from_py(None) actual = padding._concat(actual) self.actuallyHave = 0 self.actualOffset = 0 else: # update about what was taken self.actuallyHave -= numberOfBits self.actualOffset += numberOfBits if self.actuallyHave == 0: self.actual = None self.actualOffset = 0 if doCollect: if numberOfBits == 1: return actual[actualOffset] else: return actual[(actualOffset + numberOfBits):actualOffset] def get(self, numberOfBits: int) -> Union[RtlSignalBase, HConst]: """ :param numberOfBits: number of bits to get from actual position :return: chunk of bits of specified size (instance of Value or RtlSignal) """ return self._get(numberOfBits, True) def skip(self, numberOfBits: int) -> None: """ Move this iterator without care about item :param numberOfBits: number of bits to get from actual position """ self._get(numberOfBits, False) def assertIsOnEnd(self): """ Assert there is nothing left in this iterator """ try: next(self.it) except StopIteration: return raise AssertionError("there are still some items") def iterBits(sigOrConst: Union[RtlSignalBase, HConst], bitsInOne: int=1, skipPadding: bool=True, fillup: bool=False): """ Iterate over bits in vector :param sigOrConst: signal or value to iterate over :param bitsInOne: number of bits in one part :param skipPadding: if true padding is skipped in dense types :param fillup: flag that means that if there is not enough bits for last item fill it up with invalid bits (otherwise raise) """ bw = BitWalker(sigOrConst, skipPadding, fillup) try: bit_len = sigOrConst._dtype.bit_length() except TypeError: bit_len = None if bit_len is None: try: while True: yield bw.get(bitsInOne) except NotEnoughtBitsErr: return else: for _ in range(ceil(bit_len / bitsInOne)): yield bw.get(bitsInOne) bw.assertIsOnEnd() ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["setuptools >= 77.0.3"] build-backend = "setuptools.build_meta" [project] name = "hwt" version = "3.8" dependencies = [ "natsort>=8.4.0", # natural sorting for HDL objects with name "hdlConvertorAst>=1.0", # conversions to SystemVerilog, VHDL "ipCorePackager>=0.6", # generator of IPcore packages (IP-xact, ...) "hwtSimApi>=1.3", # simulator API "pyDigitalWaveTools>=1.1", # simulator output dump ] requires-python = ">=3.8" authors = [ {name = "Michal Orsak", email = "Nic30original@gmail.com"}, ] description = "A library for a construction and analysis of digital circuits" readme = "README.md" license = "MIT" license-files = ["LICENSE"] keywords = [ 'hdl', 'vhdl', 'verilog', 'verilog-hdl', 'systemverilog', 'code-generator', 'ipcore', 'compiler', 'hardware', 'high-level-synthesis', 'hardware-construction-language' ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", "Topic :: System :: Hardware", "Topic :: System :: Emulators", "Topic :: Utilities" ] [project.urls] Homepage = "https://github.com/Nic30/hwt" Documentation = "https://hwtoolkit.readthedocs.io/en/latest/?badge=latest" Repository = "https://github.com/Nic30/hwt.git" "Bug Tracker" = "https://github.com/Nic30/hwt/issues" ================================================ FILE: setup.cfg ================================================ [metadata] description_file = README.md [options] zip_safe = True ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/all.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from hwtLib.tests.all import suite, unittestMain if __name__ == '__main__': unittestMain(suite)