pax_global_header00006660000000000000000000000064150747733410014525gustar00rootroot0000000000000052 comment=295e2256c49b73cf3e920349b5cee64cc4e8f791 pyuvm-4.0.1/000077500000000000000000000000001507477334100127075ustar00rootroot00000000000000pyuvm-4.0.1/.git-blame-ignore-revs000066400000000000000000000001221507477334100170020ustar00rootroot000000000000008ba92cbbb507c8bf5949a83a80bb75f4a9087447 924f32e7465767625c9ea455fd9b7b049c9c7f84 pyuvm-4.0.1/.github/000077500000000000000000000000001507477334100142475ustar00rootroot00000000000000pyuvm-4.0.1/.github/dependabot.yml000066400000000000000000000010301507477334100170710ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "github-actions" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" pyuvm-4.0.1/.github/workflows/000077500000000000000000000000001507477334100163045ustar00rootroot00000000000000pyuvm-4.0.1/.github/workflows/documentation.yml000066400000000000000000000013161507477334100217010ustar00rootroot00000000000000name: documentation on: [workflow_dispatch] permissions: contents: write jobs: build: runs-on: ubuntu-latest permissions: contents: write steps: - name: Install dependencies run: | pip install sphinx sphinx_rtd_theme myst_parser cocotb sphinx-rtd-theme - uses: actions/checkout@v5 - name: Build HTML run: | cd docs pwd make clean html - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: html-docs path: docs/_build/html/ - name: Deploy uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/_build/html pyuvm-4.0.1/.github/workflows/main.yml000066400000000000000000000065171507477334100177640ustar00rootroot00000000000000name: Regression Tests on: push: branches: - master - ral_dev pull_request: branches: - master - ral_dev workflow_dispatch: jobs: tests: name: Python ${{matrix.python-version}} | cocotb ${{matrix.cocotb-version}} runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: include: # Test all Python versions with latest cocotb - python-version: "3.6" # Disabled until we can move away from GHDL for this test cocotb-version: "1.9" vhdl-sim: "nvc" - python-version: "3.7" cocotb-version: "2.0" vhdl-sim: "nvc" - python-version: "3.8" cocotb-version: "2.0" vhdl-sim: "nvc" - python-version: "3.9" cocotb-version: "2.0" vhdl-sim: "nvc" - python-version: "3.10" cocotb-version: "2.0" vhdl-sim: "nvc" - python-version: "3.11" cocotb-version: "2.0" vhdl-sim: "nvc" - python-version: "3.12" cocotb-version: "2.0" vhdl-sim: "nvc" - python-version: "3.13" cocotb-version: "2.0" vhdl-sim: "nvc" # Test other cocotb versions # Must use GHDL as VHDL simulator as NVC is only supported in cocotb 1.9+ - python-version: "3.8" cocotb-version: "1.6" vhdl-sim: "ghdl" - python-version: "3.8" cocotb-version: "1.7" vhdl-sim: "ghdl" - python-version: "3.8" cocotb-version: "1.8" vhdl-sim: "ghdl" - python-version: "3.8" cocotb-version: "1.9" vhdl-sim: "nvc" steps: - uses: actions/checkout@v5 - name: Set up Python ${{matrix.python-version}} (setup-python) if: matrix.python-version != 3.6 && matrix.python-version != 3.7 uses: actions/setup-python@v6 with: python-version: ${{matrix.python-version}} - name: Set up Python ${{matrix.python-version}} (pyenv pt.1) if: matrix.python-version == 3.6 || matrix.python-version == 3.7 uses: gabrielfalcao/pyenv-action@a1fc55906be92612782934c70e3985b940bd0165 # v18 with: default: ${{matrix.python-version}} command: pip install -U pip # upgrade pip after installing python - name: Set up Python ${{matrix.python-version}} (pyenv pt.2) if: matrix.python-version == 3.6 || matrix.python-version == 3.7 run: | pyenv install ${{matrix.python-version}} pyenv global ${{matrix.python-version}} - name: Install Python testing dependencies run: | pip install tox tox-gh-actions - name: Install Icarus Verilog run: | sudo apt install -y --no-install-recommends iverilog - name: Install GHDL if: matrix.vhdl-sim == 'ghdl' uses: ghdl/setup-ghdl@v1 with: version: latest backend: mcode - name: Install NVC if: matrix.vhdl-sim == 'nvc' uses: nickg/setup-nvc@v1 with: version: latest - name: Test run: | COCOTB_VERSION=${{matrix.cocotb-version}} \ VERILOG_SIM=icarus \ VHDL_SIM=${{matrix.vhdl-sim}} \ tox concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ !contains(github.ref, 'master') }} pyuvm-4.0.1/.gitignore000066400000000000000000000005401507477334100146760ustar00rootroot00000000000000IEEE_UVM.pdf *.code-workspace *.ipynb .idea/* .cache .idea __pycache__ /tests/test_05_base_classes.py-e *.py-e .Ulysses* venv/* venv *~ build dist *.egg-info .tox examples/*/modelsim.ini examples/*/transcript .vscode results.xml sim_build docs/_build/* docs/generated/* examples/TinyALU/e~tinyalu.o examples/TinyALU/tinyalu .env .editorconfig scratch/ pyuvm-4.0.1/.pre-commit-config.yaml000066400000000000000000000011131507477334100171640ustar00rootroot00000000000000repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: "v0.14.0" hooks: - id: ruff-check - id: ruff-format - repo: "https://github.com/pre-commit/pre-commit-hooks" rev: "v6.0.0" hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: mixed-line-ending args: - --fix=lf - id: fix-byte-order-marker - id: check-yaml - id: check-toml - id: check-added-large-files - repo: https://github.com/codespell-project/codespell rev: "v2.4.1" hooks: - id: codespell additional_dependencies: - tomli ci: autofix_prs: false pyuvm-4.0.1/LICENSE000066400000000000000000000010671507477334100137200ustar00rootroot00000000000000Copyright 2021 Siemens EDA and Ray Salemi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pyuvm-4.0.1/MANIFEST.in000066400000000000000000000000201507477334100144350ustar00rootroot00000000000000include LICENSE pyuvm-4.0.1/Makefile000066400000000000000000000025441507477334100143540ustar00rootroot00000000000000VERILOG_SIM ?= icarus VHDL_SIM ?= ghdl .PHONY: cocotb_tests cocotb_tests: make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/queue sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/run_phase sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/decorator sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/t05_base_classes sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/t08_factories sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/t09_phasing sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/t12_tlm sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/t13_components sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/config_db sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/t14_15_sequences sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C tests/cocotb_tests/ext_classes sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C examples/TinyALU sim checkclean make SIM=$(VERILOG_SIM) TOPLEVEL_LANG=verilog -C examples/TinyALU_reg sim checkclean make SIM=$(VHDL_SIM) TOPLEVEL_LANG=vhdl -C examples/TinyALU sim checkclean pyuvm-4.0.1/README.md000066400000000000000000000577431507477334100142060ustar00rootroot00000000000000# Introduction The Universal Verification Methodology (UVM) is the dominant RTL verification network across the IC industry. The UVM is popular because it allows engineers to reuse testbench components across testbenches. It also provides a standardized testbench structure that allows engineers to understand existing testbenches as these engineers move across projects, companies, and industries. If you apply for a job as a verification engineer, it is likely that the hiring team uses the UVM and will test your UVM knowledge. IEEE defined the UVM in the [*IEEE Standard for Universal Verification Methodology Language Reference Manual*](https://ieeexplore.ieee.org/document/9195920), also know as the [IEEE 1800.2 standard](https://ieeexplore.ieee.org/document/9195920). While the industry defined the 1800.2 standard in terms of SystemVerilog, there is little in the standard that requires us to implement the UVM library in SystemVerilog. It could be implemented in any language with sufficient object oriented support---for example, Python. ## API Documentation You can read the API documentation for **pyuvm** on [GitHub Pages](https://pyuvm.github.io/pyuvm/). ## Python and IEEE 1800.2 **pyuvm** is the Universal Verification Methodology implemented in Python instead of SystemVerilog. **pyuvm** uses [**cocotb**][cocotbLink] to interact with simulators and schedule simulation events. **pyuvm** takes advantage of Python's ease of use and object-oriented power to implement the most-often used parts of the IEEE 1800.2 standard. It is easier to write UVM code in Python because Python does not have strict typing and does not require parameterized classes. Python also supports important object-oriented programming (OOP) concepts such as [multiple-inheritance][multipleInh] that are missing in SystemVerilog. ## IEEE 1800.2 and pyuvm **pyuvm** is a clean implementation of IEEE 1800.2 in Python. It implements the following sections from 1800.2: |Section|Name|Description| |-------|----|-----------| |5|Base Classes|Basic classes such as `uvm_void` and `uvm_object`| |6|Reporting Classes|**pyuvm** uses the **logging** package to implement reporting, but integrates it within some of the UVM reporting functionality.| |8|Factory Classes|**pyuvm** implements all the UVM factory functionality without using the macros needed in SystemVerilog. The factory supports any class extended from `uvm_void`.| |9|Phasing|IEEE 1800.2 describes basic phasing that everyone uses and a complicated custom phasing system that almost nobody uses. **pyuvm** only implmenents the phasing that everyone uses, but you can extend phasing using Python OOP techniques.| |12|UVM TLM Interfaces|**pyuvm** fully implements the UVM *Transaction Level Modeling* (TLM) system. | |13|Predefined Component Classes|**pyuvm** implements uvm_component with hierarchy, the uvm_root singleton, and the run_test() task. It simplifies the `uvm_config_db` to the Python-friendly `Config` class. |14 & 15|Sequences, sequencer, sequence_item|**pyuvm** refactored the sequencer functionality to create a simpler implementation of the UVM Sequence functionality.| |17|UVM Register Enum|**pyuvm** implements all the basic Enum types in the **pyuvm** *Register Access Layer* (RAL)| |18|UVM Register Block|**pyuvm** implements the RAL register block classes| |19|UVM Register Field|**pyuvm** implements register fields as defined in IEEE 18002. There are still few functionalities missing like atomic Backdoor access, Field byte access, and single Field access during read or write operation| |20|UVM Register|Main register class is implemented but is still missing Backdoor and used Backdoor to be leveraged from cocotb force. Byte access and single field access yet to be implemented| |21|UVM Register Map|Main register map class, should be refactored to guarantee simplicity and backdoor access, extension class in Read and Write to be implemented| |22|UVM Memory|Not Implemented| |23|Register Item|Register Item used across multiple classes| |24|Register include file|Includes other Enum and types to be merged with s17| |25|UVM register adapter|Main register adapter| |26|UVM register predictor|Main register predictor, should be disabled if auto_prediction is not set| |27|Register Package|Main PKG if included should behave similarly to uvm_reg_pkg| # Installation You can install **pyuvm** using `pip`. This will also install **cocotb** as a requirement for **pyuvm**. ```bash % pip install pyuvm ``` Then you can run a simple test: ```bash % python >>> from pyuvm import * >>> my_object = uvm_object("my_object") >>> type(my_object) >>> print("object name:", my_object.get_name()) object name: my_object ``` ## Running from a cloned repository You can run pyuvm from a cloned repository by installing the cloned repository using pip. ```bash % cd % pip install -e . ``` # Usage This section demonstrates running an example simulation and then shows how the example has been put together demonstrating what the UVM looks like in Python. ## Running the simulation The TinyALU is, as its name implies, a tiny ALU. It has four operations: ADD, AND, NOT, and MUL. This example shows us running the Verilog version of the design, but there is also a VHDL version. **cocotb** uses a Makefile to run its simulation. We see it in `examples/TinyALU`: ```makefile CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/hdl/verilog/tinyalu.sv MODULE := testbench TOPLEVEL=tinyalu TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim ``` You can learn more about the Makefile targets at [cocotb.org](https://docs.cocotb.org/en/stable/building.html). The `cocotb-config` command on the last line points to the **cocotb** Makefile locations and launches the `sim` target. Modify the `SIM` variable to match your simulator. All the simulator types are in `cocotb/share/makefiles/simulators/makefile.$(SIM)`. You should be able to run the simulation like this: ```bash % cd /pyuvm/examples/TinyALU % make sim ``` **cocotb** will present a lot of messages, but in the middle of them you will see these UVM messages. It runs four examples with one for each command with randomized operands. ```text 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0x34 ADD 0x23 = 0x0057 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0xf9 AND 0x29 = 0x0029 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0x71 XOR 0x01 = 0x0070 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0xb8 MUL 0x47 = 0x3308 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0xff ADD 0xff = 0x01fe 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0xff AND 0xff = 0x00ff 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0xff XOR 0xff = 0x0000 250000.00ns INFO testbench.py(209)[uvm_test_top.env.scoreboard]: PASSED: 0xff MUL 0xff = 0xfe01 ``` ## The `TinyAluBfm` in `tinyalu_utils.py` The `TinyAluBfm` is a singleton that uses **cocotb** to communicate with the TinyALU. The BFM exposes three coroutines to the user: `send_op()`, `get_cmd()`, and `get_result()`. The singleton uses the `cocotb.top` variable to get the handle to the DUT. This is the handle that we normally pass to a `cocotb.test()` coroutine. The `TinyAluBfm` is defined in `tinyalu_utils.py` and imported into our testbench. # The **pyuvm** testbench The `testbench.py` contains the entire UVM testbench and connects to the TinyALU through a `TinyAluBfm` object defined in `tinyalu_utils.py`. We'll examine the `testbench.py` file and enough of the **cocotb** test to run the simulation. ## Importing **pyuvm** Testbenches written in the SystemVerilog UVM usually import the package like this: ```systemverilog import uvm_pkg::*; ``` This gives you access to the class names without needing a package path. To get similar behavior with **pyuvm** us the `from` import syntax. We import **pyuvm** to distinguish the `@pyuvm.test()` decorator from the `@cocotb.test()` decorator: ```python import pyuvm from pyuvm import * ``` ## The AluTest classes We're going to examine the UVM classes from the top, the test, to the bottom, the sequences. **pyuvm** names the UVM classes as they are named in the specification. Therefore **pyuvm** uses underscore naming as is done in SystemVerilog and not camel-casing. We extend `uvm_test` to create the `AluTest`, using camel-casing in our code even if **pyuvm** does not use it: You'll see the following in the test: * We define a class that extends `uvm_test`. * We use the `@pyuvm.test()` decorator to notify **cocotb** that this is a test. * There is no `uvm_component_utils()` macro. **pyuvm** automatically registers classes that extend `uvm_void` with the factory. * The phases do not have a `phase` variable. Phasing has been refactored to support only the *common phases* as described in the specification. * We create the environment using the `create()` method and the factory. Notice that `create()` is now a simple class method. There is no typing-driven incantation. * `raise_objection()` is now a `uvm_component` method. There is no longer a `phase` variable. * The `ConfigDB()` singleton acts the same way as the `uvm_config_db` interface in the SystemVerilog UVM. **pyuvm** refactored away the `uvm_resource_db` as there are no issues with classes to manage. * **pyuvm** leverages the Python logging system and does not implement the UVM reporting system. Every descendent of `uvm_report_object` has a `logger` data member. * Sequences work as they do in the SystemVerilog UVM. ```python @pyuvm.test() class AluTest(uvm_test): def build_phase(self): self.env = AluEnv("env", self) def end_of_elaboration_phase(self): self.test_all = TestAllSeq.create("test_all") async def run_phase(self): self.raise_objection() await self.test_all.start() self.drop_objection() ``` We extend the `AluTest` class to create a parallel version of the test and a Fibonacci program. You can find these sequences in `testbench.py` ```python @pyuvm.test() class ParallelTest(AluTest): def build_phase(self): uvm_factory().set_type_override_by_type(TestAllSeq, TestAllForkSeq) super().build_phase() @pyuvm.test() class FibonacciTest(AluTest): def build_phase(self): ConfigDB().set(None, "*", "DISABLE_COVERAGE_ERRORS", True) uvm_factory().set_type_override_by_type(TestAllSeq, FibonacciSeq) return super().build_phase() ``` All the familiar pieces of a UVM testbench are in **pyuvm**. ## The ALUEnv Class The `uvm_env` class is a container for the components that make up the testbench. There are four component classes instantiated: * `Monitor`—There are actually two monitors instantiated, one to monitor commands (`self.cmd_mod`) and the other to monitor results (`self.result_mon`). The `Monitor` code is the same for both. We pass them the name of the proxy function that gets the data they monitor. * `Scoreboard`—The scoreboard gathers all the commands and results and compares predicted results to actual results. * `Coverage`—The coverage class checks that we've covered all the kinds of operations and issues an error if we did not. * `Driver`—This `uvm_driver` processes sequences items. * `uvm_sequencer`—The `uvm_sequencer` queues sequence items and passes them to the `Driver` * We store `self.seqr` in the `ConfigDB()` so test can get it as we see above. The `AluEnv` creates all these components in `build_phase()` and connects the exports to the ports in `connect_phase()`. The `build_phase()` is a top-down phase and the `connect_phase()` is a bottom up phase. ```python class AluEnv(uvm_env): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) ConfigDB().set(None, "*", "SEQR", self.seqr) self.driver = Driver.create("driver", self) self.cmd_mon = Monitor("cmd_mon", self, "get_cmd") self.coverage = Coverage("coverage", self) self.scoreboard = Scoreboard("scoreboard", self) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) self.cmd_mon.ap.connect(self.scoreboard.cmd_export) self.cmd_mon.ap.connect(self.coverage.analysis_export) self.driver.ap.connect(self.scoreboard.result_export) ``` ## The Monitor The `Monitor` extends `uvm_component`. Takes the name of a `CocotProxy` method name as an argument. It uses the name to find the method in the proxy and then calls the method. You cannot do this in SystemVerilog as there is no introspection. The `Monitor` creates an analysis port and writes the data it gets into the analysis port. Notice in the `run_phase()` we use the `await` keyword to wait for the `get_cmd` coroutine. Unlike SystemVerilog, Python makes it clear when you are calling a time-consuming task vs a function. Also notice that the `run_phase()` has the `async` keyword to designate that it is a coroutine. (A task in SystemVerilog.) ```python class Monitor(uvm_component): def __init__(self, name, parent, method_name): super().__init__(name, parent) self.method_name = method_name def build_phase(self): self.ap = uvm_analysis_port("ap", self) self.bfm = TinyAluBfm() self.get_method = getattr(self.bfm, self.method_name) async def run_phase(self): while True: datum = await self.get_method() self.logger.debug(f"MONITORED {datum}") self.ap.write(datum) ``` ## The Scoreboard The scoreboard receives commands from the command monitor and results from the results monitor in the same order. It uses the commands to predict the results and compares them. * The `build_phase` uses `uvm_tlm_analysis_fifos` to receive data from the monitors and store it. * The scoreboard exposes the FIFO exports by copying them into class data members. As we see in the environment above, this allows us to connect the exports without reaching into the `Scoreboard's` inner workings. * We connect the exports in the `connect_phase()` * The `check_phase()` runs after the `run_phase()`. At this point the scoreboard has all operations and results. It loops through the operations and predicts the result, then it compares the predicted and actual result. * Notice that we do not use UVM reporting. Instead we us the Python `logging` module. Every `uvm_report_object` and its children has its own logger stored in `self.logger.` ```python class Scoreboard(uvm_component): def build_phase(self): self.cmd_fifo = uvm_tlm_analysis_fifo("cmd_fifo", self) self.result_fifo = uvm_tlm_analysis_fifo("result_fifo", self) self.cmd_get_port = uvm_get_port("cmd_get_port", self) self.result_get_port = uvm_get_port("result_get_port", self) self.cmd_export = self.cmd_fifo.analysis_export self.result_export = self.result_fifo.analysis_export def connect_phase(self): self.cmd_get_port.connect(self.cmd_fifo.get_export) self.result_get_port.connect(self.result_fifo.get_export) def check_phase(self): while self.result_get_port.can_get(): _, actual_result = self.result_get_port.try_get() cmd_success, cmd = self.cmd_get_port.try_get() if not cmd_success: self.logger.critical(f"result {actual_result} had no command") else: (A, B, op_numb) = cmd op = Ops(op_numb) predicted_result = alu_prediction(A, B, op) if predicted_result == actual_result: self.logger.info(f"PASSED: 0x{A:02x} {op.name} 0x{B:02x} =" f" 0x{actual_result:04x}") else: self.logger.error(f"FAILED: 0x{A:02x} {op.name} 0x{B:02x} " f"= 0x{actual_result:04x} " f"expected 0x{predicted_result:04x}") ``` ## Coverage The `Coverage` Class extends `uvm_subscriber` which extends `uvm_analysis_export`. As we see in the `AluEnv` above, this allows us to pass the object directly to the `connect()` method to connect it to an analysis port. The `Coverage` Class overrides the `write()` method expected of a `uvm_subscriber`. If it didn't you'd get a runtime error. The `Coverage` class uses a set to store all the operations seen. Then it subtracts that from the set of all operations. If the result has a length longer than `0` it issues an error. Since this tesbench loops through all the operations you will not see this error. ```python class Coverage(uvm_subscriber): def end_of_elaboration_phase(self): self.cvg = set() def write(self, cmd): (_, _, op) = cmd self.cvg.add(op) def report_phase(self): try: disable_errors = ConfigDB().get( self, "", "DISABLE_COVERAGE_ERRORS") except UVMConfigItemNotFound: disable_errors = False if not disable_errors: if len(set(Ops) - self.cvg) > 0: self.logger.error( f"Functional coverage error. Missed: {set(Ops)-self.cvg}") assert False else: self.logger.info("Covered all operations") assert True ``` ## Driver The `Driver` extends `uvm_driver` and so it works with sequences and sequence items. The `connect_phase()` gets the proxy from the `ConfigDB()` and the `run_phase()` uses it to get items and process them by calling `send_op`. We use `while True` because we do this forever. **cocotb** will shut down the `run_phase` coroutine at the end of simulation. ```python class Driver(uvm_driver): def build_phase(self): self.ap = uvm_analysis_port("ap", self) def start_of_simulation_phase(self): self.bfm = TinyAluBfm() async def launch_tb(self): await self.bfm.reset() self.bfm.start_tasks() async def run_phase(self): await self.launch_tb() while True: cmd = await self.seq_item_port.get_next_item() await self.bfm.send_op(cmd.A, cmd.B, cmd.op) result = await self.bfm.get_result() self.ap.write(result) cmd.result = result self.seq_item_port.item_done() ``` ### The ALU Sequence The ALU Sequence creates sequence items, randomizes them and sends them to the `Driver`. It inherits `start_item` and `finish_item` from `uvm_sequence`. It is clear that `start_item` and `finish_item` block because we call them using the `await` keyword. `start_item` waits for it's turn to use the sequencer, and `finish_item` sends the sequence_item to the driver and returns when the driver calls `item_done()` ```python class TestAllSeq(uvm_sequence): async def body(self): seqr = ConfigDB().get(None, "", "SEQR") random = RandomSeq("random") max = MaxSeq("max") await random.start(seqr) await max.start(seqr) ``` This virtual sequence launches two other sequences: `RandomSeq` and `MaxSeq`. `RandomSeq` randomizes the operands. ```python class RandomSeq(uvm_sequence): async def body(self): for op in list(Ops): cmd_tr = AluSeqItem("cmd_tr", None, None, op) await self.start_item(cmd_tr) cmd_tr.randomize_operands() await self.finish_item(cmd_tr) ``` `MaxSeq` sets the operands to `0xff`: ```python class MaxSeq(uvm_sequence): async def body(self): for op in list(Ops): cmd_tr = AluSeqItem("cmd_tr", 0xff, 0xff, op) await self.start_item(cmd_tr) await self.finish_item(cmd_tr) ``` ## ALU Sequence Item The `AluSeqItem` contains the TinyALU commands. It has two operands and an operation. The SystemVerilog `uvm_sequence_item` class uses `convert2string()` to convert the item to a string and `do_compare()` to compare the item to another item. We do not use these in **pyuvm** because Python has magic methods that do these functions. `__eq__()`—This does the same thing as `do_compare()` It returns `True` if the items are equal. This method works with the `==` operator. `__str__()`—This does the same thing as `convert2string()`. It returns a string version of the item. The `print` function calls this method automatically. ```python class AluSeqItem(uvm_sequence_item): def __init__(self, name, aa, bb, op): super().__init__(name) self.A = aa self.B = bb self.op = Ops(op) def randomize_operands(self): self.A = random.randint(0, 255) self.B = random.randint(0, 255) def randomize(self): self.randomize_operands() self.op = random.choice(list(Ops)) def __eq__(self, other): same = self.A == other.A and self.B == other.B and self.op == other.op return same def __str__(self): return f"{self.get_name()} : A: 0x{self.A:02x} \ OP: {self.op.name} ({self.op.value}) B: 0x{self.B:02x}" ``` # Contributing You can contribute to **pyuvm** by forking this repository and submitting pull requests. ### Development Environment Setup The pyuvm project uses forked Github rebase flow. The first step is to [fork the project](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo). Then clone the repo to your machine. ```sh git clone git@github.com/{your_username}/pyuvm.git cd pyuvm/ ``` It is recommended to make a virtual environment and work with that environment activated. This can be done with the built-in [`venv`](https://docs.python.org/3/library/venv.html) module in Python, or with an alternative like [uv](https://docs.astral.sh/uv/). ```sh python -m venv .venv source .venv/bin/activate ``` Next install the main development tools using `pip`. ```sh pip install .[dev] ``` ### Linting Linting is done with various tools: ruff, pre-commit, codespell, and others. These tools are all run using [pre-commit](https://pre-commit.com/). To run pre-commit lints on all sources run the following in the repo root. ```sh pre-commit run -a ``` You can also set pre-commit to run as a git commit hook, so lints are done before every `git commit`, by running the following in the repo root. ```sh pre-commit install ``` ### Testing The repository runs all needed tests using [tox](https://tox.wiki/en/stable/). This will run the full set of tests for all supported operating systems, Python versions, and cocotb versions. But it will automatically skip any tests that don't apply to your system (OS and Python version). ```sh tox ``` You can also run tests manually without the use of tox. There are [pytest](https://docs.pytest.org/en/stable/) tests that test features that don't use coroutines. You can run these by using calling `pytest` directly at the repo root. ```sh pytest ``` The rest of the tests are in `tests/cocotb_tests` and use cocotb, thus requiring a simulator. You can run these by calling `make` in the repo root. You can change which simulator to use by setting `VERILOG_SIM` and `VHDL_SIM` to set the Verilog and VHDL simulator of choice, respectively. These `*_SIM` variables take the values the cocotb `SIM` make variable would take. ```sh make VERILOG_SIM=icarus VHDL_SIM=nvc ``` # Credits * Ray Salemi—Original author, created as an employee of Siemens. * IEEE 1800.2 Specification * Siemens for supporting me in this effort. # License Copyright 2020 Siemens EDA and Ray Salemi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. [cocotbLink]: https://cocotb.org [multipleInh]: https://www.geeksforgeeks.org/multiple-inheritance-in-python/ pyuvm-4.0.1/checkclean.mk000066400000000000000000000004331507477334100153200ustar00rootroot00000000000000CURRDIR := $(dir $(lastword $(MAKEFILE_LIST))) check: $(CURRDIR)/combine_results.py testclean: clean @rm -rf __pycache__ @rm -rf results.xml @rm -rf combined_results.xml @rm -rf log.txt @rm -rf sim_build @rm -rf modelsim.ini @rm -rf transcript checkclean: check testclean pyuvm-4.0.1/combine_results.py000077500000000000000000000105461507477334100164670ustar00rootroot00000000000000#!/usr/bin/env python """ This script comes from cocotb/bin. It checks the result of every test simulation. """ import argparse import os import sys from xml.etree import ElementTree as ET def find_all(name, path): for root, dirs, files in os.walk(path): if name in files: yield os.path.join(root, name) def get_parser(): """Return the cmdline parser""" parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter ) parser.add_argument( "--directory", dest="directory", type=str, required=False, default=".", help="Name of base directory to search from", ) parser.add_argument( "--output_file", dest="output_file", type=str, required=False, default="combined_results.xml", help="Name of output file", ) parser.add_argument( "--testsuites_name", dest="testsuites_name", type=str, required=False, default="results", help="Name value for testsuites tag", ) parser.add_argument( "--verbose", dest="debug", action="store_const", required=False, const=True, default=False, help="Verbose/debug output", ) parser.add_argument( "--suppress_rc", dest="set_rc", action="store_const", required=False, const=False, default=True, help="Suppress return code if failures found", ) return parser def main(): parser = get_parser() args = parser.parse_args() rc = 0 result = ET.Element("testsuites", name=args.testsuites_name) for fname in find_all("results.xml", args.directory): if args.debug: print(f"Reading file {fname}") tree = ET.parse(fname) for ts in tree.iter("testsuite"): if args.debug: print( "Ts name : {}, package : {}".format( ts.get("name"), ts.get("package") ) ) use_element = None for existing in result: if existing.get("name") == ts.get("name") and existing.get( "package" ) == ts.get("package"): if args.debug: print("Already found") use_element = existing break if use_element is None: result.append(ts) else: # for tc in ts.getiterator("testcase"): use_element.extend(list(ts)) if args.debug: ET.dump(result) testsuite_count = 0 testcase_count = 0 for testsuite in result.iter("testsuite"): testsuite_count += 1 for testcase in testsuite.iter("testcase"): testcase_count += 1 for failure in testcase.iter("failure"): if args.set_rc: rc = 1 print( "Failure in testsuite: '{}' classname: '{}' testcase: '{}' with parameters '{}'".format( testsuite.get("name"), testcase.get("classname"), testcase.get("name"), testsuite.get("package"), ) ) if os.getenv("GITHUB_ACTIONS") is not None: # Get test file relative to root of repo repo_root = os.path.commonprefix( [ os.path.abspath(testcase.get("file")), os.path.abspath(__file__), ] ) relative_file = testcase.get("file").replace(repo_root, "") print( "::error file={},line={}::Test {}:{} failed".format( relative_file, testcase.get("lineno"), testcase.get("classname"), testcase.get("name"), ) ) print(f"Ran a total of {testsuite_count} TestSuites and {testcase_count} TestCases") ET.ElementTree(result).write(args.output_file, encoding="UTF-8") return rc if __name__ == "__main__": rc = main() sys.exit(rc) pyuvm-4.0.1/docs/000077500000000000000000000000001507477334100136375ustar00rootroot00000000000000pyuvm-4.0.1/docs/Makefile000066400000000000000000000130111507477334100152730ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* -rm -rf pyuvm.rst modules.rst html: sphinx-apidoc -o . ../pyuvm @rm modules.rst $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/sample.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sample.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/sample" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sample" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." pyuvm-4.0.1/docs/_static/000077500000000000000000000000001507477334100152655ustar00rootroot00000000000000pyuvm-4.0.1/docs/_static/custom.css000066400000000000000000000002021507477334100173030ustar00rootroot00000000000000code.literal { border: none; color: #404040 !important; background-color: #fbfbfb !important; font-size: 0.9em; } pyuvm-4.0.1/docs/conf.py000066400000000000000000000177631507477334100151540ustar00rootroot00000000000000# 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. import pathlib import sys # 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. # sys.path.insert(0, os.path.abspath('.')) src_path = pathlib.Path(__file__).parents[1] # /"pyuvm" # src_path = src_path/"pyuvm" src_path = src_path.resolve().as_posix() sys.path.insert(0, src_path) # -- 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.duration", "sphinx.ext.doctest", "sphinx.ext.autodoc", "sphinx.ext.autosummary", "myst_parser", "sphinx.ext.autosectionlabel", ] html_static_path = ["_static"] html_css_files = ["custom.css"] autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "pyuvm" copyright = "2020, Ray Salemi" # 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 = "v4.0" # The full version, including alpha/beta/rc tags. release = "v4.0.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- 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 = "sphinx_rtd_theme" # html_theme_options = { # "sidebarwidth": 550, # } # 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 themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "sampledoc" # -- 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': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ("index", "sample.tex", "sample Documentation", "Kenneth Reitz", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "sample", "sample Documentation", ["Kenneth Reitz"], 1)] # If true, show URL addresses after external links. # man_show_urls = False # -- 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 = [ ( "index", "sample", "sample Documentation", "Kenneth Reitz", "sample", "One line description of project.", "Miscellaneous", ), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' pyuvm-4.0.1/docs/docsources/000077500000000000000000000000001507477334100160105ustar00rootroot00000000000000pyuvm-4.0.1/docs/docsources/README.md000077700000000000000000000000001507477334100211672../../README.mdustar00rootroot00000000000000pyuvm-4.0.1/docs/docsources/UVM_and_Python.rst000066400000000000000000000141771507477334100214060ustar00rootroot00000000000000UVM and Python ============== The Universal Verification Methodology (UVM) grew out of the Open Verification Methodology (OVM) which grew out of the Advanced Verification Methdology (AVM) which leveraged the SystemVerilog programming language. Though this long lineage suggests that the UVM must be implemented in SystemVerilog, this is not the case. The UVM is a class library, and a class library can be implemented in any object oriented programming language. **pyuvm** implements the UVM using Python. Creating testbenches using Python has several advantages over SystemVerilog: * The Python ecosystem is larger than the SystemVerilog ecosystem. More developers understand Python than SystemVerilog, and Python has a long tradition of being the language of choice for large-scale object oriented projects. * Python is object-oriented, even more so than SystemVerilog, and so it is easier to deliver functions such as overridable factories in Python than SystemVerilog. * Python runs without a simulator, and so is faster than SystemVerilog. * Python forces the testbench developer to separate simulated and timed RTL code from testbench code. This means that testbenches written with Python support accelerated testbenches with little, if any, modification. While Python is an excellent testbench development language, the UVM is an excellent Verification Library. Implementing the UVM using Python gives us the best of both worlds. Pythonizing the UVM ------------------- Much of the UVM specification in IEEE-1800.2-2017 is driven by elements of the SystemVerilog programming language. Blindly implementing all that is in the UVM specification is not only impossible (there are no parameters in Class declarations, for example), but also unwise. Many elements of Python make it much easier to create testbench code using Python than SystemVerilog. For example, there are no arcane issues of typing, and Python readily provides generic tools for logging and interprocess communication. Rather than attempting to mimic the UVM completely, this implementation focuses on delivering the functionality of the UVM, even if this changes the details of how functionality is delivered. This section examines some differences between the implementations. Static Typing vs. Duck Typing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SystemVerilog is a statically typed language. You declare variables to be of a certain type in the source code before using them. It is a relatively weakly typed languaged (relative to VHDL). You can declare a variable to be a ``short int`` and another to be an ``integer`` and still add them with no problem. Python using Duck Typing, which is form of dynamic typing. Duck typing says that if something looks like a duck, and acts like a duck, then we'll consider it a duck. That means we don't declare a variable to be of type ``duck``, instead we call ``duck.quack()`` and see if we get an exception. The UVM can be difficult to write because of its static typing. A lot of energy goes into parameterizing classes to get the behavior you want, that problem goes away in Python. Instead, you see runtime errors if you mess up the typing. Exception Handling ^^^^^^^^^^^^^^^^^^ Unlike SystemVerilog, Python provides the ability to raise and catch exceptions. While a SystemVerilog programmer needs to check to be sure that an action will work before attempting it, a Python programmer is more likely to try the action and catch an exception if it arises. Uncaught exceptions rise through the call stack and eventually cause the Python to dump out the stack trace information and exit. Catching exceptions keeps the program from terminating. **pyuvm** Exceptions ^^^^^^^^^^^^^^^^^^^^ Review the documentation for the ``error_classes`` module to see the Exceptions defined in **pyuvm**. Coding differences between SystemVerilog UVM and **pyuvm** ---------------------------------------------------------- The topics outline above lead to differences between the way **pyuvm** implements behaviors vs. how SystemVerilog does it. This section highlights these differences. Underscore Naming ^^^^^^^^^^^^^^^^^ Python programs use camel casing to define classes, but **pyuvm** uses underscore naming to match the IEEE specification. ``uvm_object`` is named ``uvm_object`` not ``UvmObject``. Decorators and Accessor Functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SystemVerilog UVM sets and gets variable values using accessor functions such as ``set_name()`` and ``get_name``. Python implements similar functionality using the ``@property`` decorator. While **pyuvm** could have changed all the accessor functions to properties, it implements the IEEE 1800.2 spec and keeps the accessor functions. ``uvm_object_wrapper`` and Factories ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SystemVerilog has relatively poor class manipulation mechanisms. Most of the class information has already been determined at compile time and dynamically creating objects of different classes, or overriding one class with another, requires considerable gymnastics. Python, on the other hand easily handles classes. Classes are objects just like everything else in the language. As a result, **pyuvm** does not need the macros we use in SystemVerilog to register classes with the factory. It does not have `uvm_object_utils` or `uvm_component_utils` macros. It registers all classes that extend ``uvm_void`` with the factory. Using Python functionality instead of UVM functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The UVM defines methods that make up for SystemVerilog's relative immaturity relative to Python. For example ``uvm_object`` defines a `get_type()` method to get the type of an object. Python has a ``type()`` function that does the same thing for all objects. Therefore, **pyuvm** raises ``UsePythonMethod`` if you call ``get_type()``. ``uvm_policy`` Class ^^^^^^^^^^^^^^^^^^^^^^ The ``uvm_policy`` Classes provide functionality that is built into Python with the ``setattr`` and ``getattr`` methods. There are no field macros and thus no need to implement this class. Reporting Classes ^^^^^^^^^^^^^^^^^ We use Python logging instead of the UVM reporting system. pyuvm-4.0.1/docs/index.rst000066400000000000000000000001511507477334100154750ustar00rootroot00000000000000pyuvm ===== .. toctree:: :maxdepth: 2 docsources/README.md docsources/UVM_and_Python pyuvm pyuvm-4.0.1/docs/pyuvm.rst000066400000000000000000000072671507477334100155650ustar00rootroot00000000000000pyuvm package ============= Submodules ---------- pyuvm.error\_classes module --------------------------- .. automodule:: pyuvm.error_classes :members: :undoc-members: :show-inheritance: pyuvm.extension\_classes module ------------------------------- .. automodule:: pyuvm.extension_classes :members: :undoc-members: :show-inheritance: pyuvm.s05\_base\_classes module ------------------------------- .. automodule:: pyuvm.s05_base_classes :members: :undoc-members: :show-inheritance: pyuvm.s06\_reporting\_classes module ------------------------------------ .. automodule:: pyuvm.s06_reporting_classes :members: :undoc-members: :show-inheritance: pyuvm.s08\_factory\_classes module ---------------------------------- .. automodule:: pyuvm.s08_factory_classes :members: :undoc-members: :show-inheritance: pyuvm.s09\_phasing module ------------------------- .. automodule:: pyuvm.s09_phasing :members: :undoc-members: :show-inheritance: pyuvm.s12\_uvm\_tlm\_interfaces module -------------------------------------- .. automodule:: pyuvm.s12_uvm_tlm_interfaces :members: :undoc-members: :show-inheritance: pyuvm.s13\_predefined\_component\_classes module ------------------------------------------------ .. automodule:: pyuvm.s13_predefined_component_classes :members: :undoc-members: :show-inheritance: pyuvm.s13\_uvm\_component module -------------------------------- .. automodule:: pyuvm.s13_uvm_component :members: :undoc-members: :show-inheritance: pyuvm.s14\_15\_python\_sequences module --------------------------------------- .. automodule:: pyuvm.s14_15_python_sequences :members: :undoc-members: :show-inheritance: pyuvm.s17\_uvm\_reg\_enumerations module ---------------------------------------- .. automodule:: pyuvm.s17_uvm_reg_enumerations :members: :undoc-members: :show-inheritance: pyuvm.s18\_uvm\_reg\_block module --------------------------------- .. automodule:: pyuvm.s18_uvm_reg_block :members: :undoc-members: :show-inheritance: pyuvm.s19\_uvm\_reg\_field module --------------------------------- .. automodule:: pyuvm.s19_uvm_reg_field :members: :undoc-members: :show-inheritance: pyuvm.s20\_uvm\_reg module -------------------------- .. automodule:: pyuvm.s20_uvm_reg :members: :undoc-members: :show-inheritance: pyuvm.s21\_uvm\_reg\_map module ------------------------------- .. automodule:: pyuvm.s21_uvm_reg_map :members: :undoc-members: :show-inheritance: pyuvm.s22\_uvm\_mem module -------------------------- .. automodule:: pyuvm.s22_uvm_mem :members: :undoc-members: :show-inheritance: pyuvm.s23\_uvm\_reg\_item module -------------------------------- .. automodule:: pyuvm.s23_uvm_reg_item :members: :undoc-members: :show-inheritance: pyuvm.s24\_uvm\_reg\_includes module ------------------------------------ .. automodule:: pyuvm.s24_uvm_reg_includes :members: :undoc-members: :show-inheritance: pyuvm.s25\_uvm\_adapter module ------------------------------ .. automodule:: pyuvm.s25_uvm_adapter :members: :undoc-members: :show-inheritance: pyuvm.s26\_uvm\_predictor module -------------------------------- .. automodule:: pyuvm.s26_uvm_predictor :members: :undoc-members: :show-inheritance: pyuvm.s27\_uvm\_reg\_pkg module ------------------------------- .. automodule:: pyuvm.s27_uvm_reg_pkg :members: :undoc-members: :show-inheritance: pyuvm.utility\_classes module ----------------------------- .. automodule:: pyuvm.utility_classes :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: pyuvm :members: :undoc-members: :show-inheritance: pyuvm-4.0.1/examples/000077500000000000000000000000001507477334100145255ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU/000077500000000000000000000000001507477334100160125ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU/.gitignore000066400000000000000000000000721507477334100200010ustar00rootroot00000000000000sim_build __pycache__ results.xml # VCS outputs ucli.key pyuvm-4.0.1/examples/TinyALU/Makefile000066400000000000000000000013741507477334100174570ustar00rootroot00000000000000CWD=$(shell pwd) export COCOTB_REDUCED_LOG_FMT = 1 SIM ?= icarus TOPLEVEL_LANG ?= verilog ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES =$(CWD)/hdl/verilog/tinyalu.sv else ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES=$(CWD)/hdl/vhdl/single_cycle_add_and_xor.vhd \ $(CWD)/hdl/vhdl/three_cycle_mult.vhd \ $(CWD)/hdl/vhdl/tinyalu.vhd else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif ifeq ($(SIM), verilator) # Enable processing of #delay statements COMPILE_ARGS += --timing endif MODULE := testbench TOPLEVEL = tinyalu GHDL_ARGS := --ieee=synopsys COCOTB_HDL_TIMEUNIT = 1us COCOTB_HDL_TIMEPRECISION = 1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../checkclean.mk pyuvm-4.0.1/examples/TinyALU/hdl/000077500000000000000000000000001507477334100165615ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU/hdl/verilog/000077500000000000000000000000001507477334100202305ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU/hdl/verilog/tinyalu.sv000066400000000000000000000041441507477334100222720ustar00rootroot00000000000000module tinyalu (input [7:0] A, input [7:0] B, input [2:0] op, input clk, input reset_n, input start, output done, output [15:0] result); wire [15:0] result_aax, result_mult; wire start_single, start_mult; wire done_aax; wire done_mult; assign start_single = start & ~op[2]; assign start_mult = start & op[2]; single_cycle and_add_xor (.A, .B, .op, .clk, .reset_n, .start(start_single), .done(done_aax), .result(result_aax)); three_cycle mult (.A, .B, .op, .clk, .reset_n, .start(start_mult), .done(done_mult), .result(result_mult)); assign done = (op[2]) ? done_mult : done_aax; assign result = (op[2]) ? result_mult : result_aax; endmodule // tinyalu module single_cycle(input [7:0] A, input [7:0] B, input [2:0] op, input clk, input reset_n, input start, output logic done, output logic [15:0] result); always @(posedge clk) if (!reset_n) result <= 0; else case(op) 3'b001 : result <= {8'd0,A} + {8'd0,B}; 3'b010 : result <= {8'd0,A} & {8'd0,B}; 3'b011 : result <= {8'd0,A} ^ {8'd0,B}; default : result <= {A,B}; endcase // case (op) always @(posedge clk) if (!reset_n) done <= 0; else done <= ((start == 1'b1) && (op != 3'b000)); endmodule : single_cycle module three_cycle(input [7:0] A, input [7:0] B, input [2:0] op, input clk, input reset_n, input start, output logic done, output logic [15:0] result); logic [7:0] a_int, b_int; logic [15:0] mult1, mult2; logic done1, done2, done3; always @(posedge clk) if (!reset_n) begin done <= 0; done3 <= 0; done2 <= 0; done1 <= 0; a_int <= 0; b_int <= 0; mult1 <= 0; mult2 <= 0; result<= 0; end else begin // if (!reset_n) a_int <= A; b_int <= B; mult1 <= a_int * b_int; mult2 <= mult1; result <= mult2; done3 <= start & !done; done2 <= done3 & !done; done1 <= done2 & !done; done <= done1 & !done; end // else: !if(!reset_n) endmodule : three_cycle pyuvm-4.0.1/examples/TinyALU/hdl/vhdl/000077500000000000000000000000001507477334100175165ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU/hdl/vhdl/single_cycle_add_and_xor.vhd000066400000000000000000000056271507477334100252150ustar00rootroot00000000000000-- Copyright 2013 Ray Salemi -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity single_cycle is port( A : in unsigned ( 7 downto 0 ); B : in unsigned ( 7 downto 0 ); clk : in std_logic; op : in std_logic_vector ( 2 downto 0 ); reset_n : in std_logic; start : in std_logic; done_aax : out std_logic; result_aax : out unsigned (15 downto 0) ); -- Declarations end single_cycle; -- architecture add_and_xor of single_cycle is signal a_int, b_int : unsigned (7 downto 0); signal mul_int1, mul_int2 : unsigned(15 downto 0); signal done_aax_int : std_logic; -- VHDL can't read an output -- Doh! begin ----------------------------------------------------------------- single_cycle_ops : process (clk) ----------------------------------------------------------------- begin if (clk'event and clk = '1') then -- Synchronous Reset if (reset_n = '0') then -- Reset Actions result_aax <= "0000000000000000"; else if START = '1' then case op is when "001" => result_aax <= ("00000000" & A) + ("00000000" & B); when "010" => result_aax <= unsigned(std_logic_vector("00000000" & A) and std_logic_vector("00000000" & B)); when "011" => result_aax <= unsigned(std_logic_vector("00000000" & A) xor std_logic_vector("00000000" & B)); when others => null; end case; end if; end if; end if; end process single_cycle_ops; -- purpose: This block sets the done signal. This is set on the clock edge if the start signal is high. -- type : sequential -- inputs : clk, reset_n, start,op -- outputs: done_aax_int set_done : process (clk, reset_n) begin -- process set_done_sig if reset_n = '0' then -- asynchronous reset (active low) done_aax_int <= '0'; elsif clk'event and clk = '1' then -- rising clock edge if ((start = '1') and (op /= "000")) then done_aax_int <= '1'; else done_aax_int <= '0'; end if; end if; end process set_done; done_aax <= done_aax_int; end architecture add_and_xor; pyuvm-4.0.1/examples/TinyALU/hdl/vhdl/three_cycle_mult.vhd000066400000000000000000000044771507477334100235640ustar00rootroot00000000000000-- Copyright 2013 Ray Salemi -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- VHDL Architecture tinyalu_lib.three_cycle.mult -- LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; ENTITY three_cycle IS PORT( A : IN unsigned ( 7 DOWNTO 0 ); B : IN unsigned ( 7 DOWNTO 0 ); clk : IN std_logic; reset_n : IN std_logic; start : IN std_logic; done_mult : OUT std_logic; result_mult : OUT unsigned (15 DOWNTO 0) ); -- Declarations END three_cycle ; -- architecture mult of three_cycle is signal a_int,b_int : unsigned (7 downto 0); -- start pipeline signal mult1,mult2 : unsigned (15 downto 0); -- pipeline registers signal done3,done2,done1,done_mult_int : std_logic; -- pipeline the done signal begin -- purpose: Three stage pipelined multiplier -- type : sequential -- inputs : clk, reset_n, a,b -- outputs: result_mult multiplier: process (clk, reset_n) begin -- process multiplier if reset_n = '0' then -- asynchronous reset (active low) done_mult_int <= '0'; done3 <= '0'; done2 <= '0'; done1 <= '0'; a_int <= "00000000"; b_int <= "00000000"; mult1 <= "0000000000000000"; mult2 <= "0000000000000000"; result_mult <= "0000000000000000"; elsif clk'event and clk = '1' then -- rising clock edge a_int <= a; b_int <= b; mult1 <= a_int * b_int; mult2 <= mult1; result_mult <= mult2; done3 <= start and (not done_mult_int); done2 <= done3 and (not done_mult_int); done1 <= done2 and (not done_mult_int); done_mult_int <= done1 and (not done_mult_int); end if; end process multiplier; done_mult <= done_mult_int; end architecture mult; pyuvm-4.0.1/examples/TinyALU/hdl/vhdl/tinyalu.vhd000066400000000000000000000102241507477334100217050ustar00rootroot00000000000000-- Copyright 2013 Ray Salemi -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; entity tinyalu is port( A : in unsigned ( 7 downto 0 ); B : in unsigned ( 7 downto 0 ); clk : in std_logic; op : in std_logic_vector ( 2 downto 0 ); reset_n : in std_logic; start : in std_logic; done : out std_logic; result : out unsigned ( 15 downto 0 ) ); -- Declarations end tinyalu; library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; library work; architecture rtl of tinyalu is -- Architecture declarations -- Internal signal declarations signal done_aax : std_logic; signal done_mult : std_logic; signal result_aax : unsigned(15 downto 0); signal result_mult : unsigned(15 downto 0); signal start_single : std_logic; -- Start signal for single cycle ops signal start_mult : std_logic; -- start signal for multiply -- Implicit buffer signal declarations signal done_internal : std_logic; -- Component Declarations component single_cycle port ( A : in unsigned ( 7 downto 0 ); B : in unsigned ( 7 downto 0 ); clk : in std_logic; op : in std_logic_vector ( 2 downto 0 ); reset_n : in std_logic; start : in std_logic; done_aax : out std_logic; result_aax : out unsigned (15 downto 0) ); end component; component three_cycle port ( A : in unsigned ( 7 downto 0 ); B : in unsigned ( 7 downto 0 ); clk : in std_logic; reset_n : in std_logic; start : in std_logic; done_mult : out std_logic; result_mult : out unsigned (15 downto 0) ); end component; -- Optional embedded configurations -- pragma synthesis_off for all : single_cycle use entity work.single_cycle; for all : three_cycle use entity work.three_cycle; -- pragma synthesis_on begin -- purpose: This block shunts the start signal to the correct block. -- The multiply only sees the start signal when op(2) is '1' -- type : combinational -- inputs : op(2),start -- outputs: start_mult, start_single start_demux: process (op(2),start) begin -- process start_demux case op(2) is when '0' => start_single <= start; start_mult <= '0'; when '1' => start_single <= '0'; start_mult <= start; when others => null; end case; end process start_demux; result_mux : process(result_aax, result_mult, op) begin case op(2) is when '0' => result <= result_aax; when '1' => result <= result_mult; when others => result <= (others => 'X'); end case; end process result_mux; done_mux : process(done_aax, done_mult, op) begin case op(2) is when '0' => done_internal <= done_aax; when '1' => done_internal <= done_mult; when others => done_internal <= 'X'; end case; end process done_mux; -- Instance port mappings. add_and_xor : single_cycle port map ( A => A, B => B, clk => clk, op => op, reset_n => reset_n, start => start_single, done_aax => done_aax, result_aax => result_aax ); mult : three_cycle port map ( A => A, B => B, clk => clk, reset_n => reset_n, start => start_mult, done_mult => done_mult, result_mult => result_mult ); -- Implicit buffered output assignments done <= done_internal; end rtl; pyuvm-4.0.1/examples/TinyALU/testbench.py000066400000000000000000000216541507477334100203530ustar00rootroot00000000000000import random # All testbenches use tinyalu_utils, so store it in a central # place and add its path to the sys path so we can import it import sys from pathlib import Path import cocotb from cocotb.clock import Clock from cocotb.triggers import Combine import pyuvm from pyuvm import * sys.path.append(str(Path("..").resolve())) from tinyalu_utils import Ops, TinyAluBfm, alu_prediction # noqa: E402 # Sequence classes class AluSeqItem(uvm_sequence_item): def __init__(self, name, aa, bb, op): super().__init__(name) self.A = aa self.B = bb self.op = Ops(op) def randomize_operands(self): self.A = random.randint(0, 255) self.B = random.randint(0, 255) def randomize(self): self.randomize_operands() self.op = random.choice(list(Ops)) def __eq__(self, other): same = self.A == other.A and self.B == other.B and self.op == other.op return same __hash__: None # type: ignore def __str__(self): return f"{self.get_name()} : A: 0x{self.A:02x} \ OP: {self.op.name} ({self.op.value}) B: 0x{self.B:02x}" class RandomSeq(uvm_sequence): async def body(self): for op in list(Ops): cmd_tr = AluSeqItem("cmd_tr", None, None, op) await self.start_item(cmd_tr) cmd_tr.randomize_operands() await self.finish_item(cmd_tr) class MaxSeq(uvm_sequence): async def body(self): for op in list(Ops): cmd_tr = AluSeqItem("cmd_tr", 0xFF, 0xFF, op) await self.start_item(cmd_tr) await self.finish_item(cmd_tr) class TestAllSeq(uvm_sequence): async def body(self): seqr = ConfigDB().get(None, "", "SEQR") random = RandomSeq("random") max = MaxSeq("max") await random.start(seqr) await max.start(seqr) class TestAllForkSeq(uvm_sequence): async def body(self): seqr = ConfigDB().get(None, "", "SEQR") random = RandomSeq("random") max = MaxSeq("max") random_task = cocotb.start_soon(random.start(seqr)) max_task = cocotb.start_soon(max.start(seqr)) await Combine(random_task, max_task) # Sequence library example class OpSeq(uvm_sequence): def __init__(self, name, aa, bb, op): super().__init__(name) self.aa = aa self.bb = bb self.op = Ops(op) async def body(self): seq_item = AluSeqItem("seq_item", self.aa, self.bb, self.op) await self.start_item(seq_item) await self.finish_item(seq_item) self.result = seq_item.result async def do_add(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.ADD) await seq.start(seqr) return seq.result async def do_and(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.AND) await seq.start(seqr) return seq.result async def do_xor(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.XOR) await seq.start(seqr) return seq.result async def do_mul(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.MUL) await seq.start(seqr) return seq.result class FibonacciSeq(uvm_sequence): def __init__(self, name): super().__init__(name) self.seqr = ConfigDB().get(None, "", "SEQR") async def body(self): prev_num = 0 cur_num = 1 fib_list = [prev_num, cur_num] for _ in range(7): sum = await do_add(self.seqr, prev_num, cur_num) fib_list.append(sum) prev_num = cur_num cur_num = sum uvm_root().logger.info("Fibonacci Sequence: " + str(fib_list)) uvm_root().set_logging_level_hier(CRITICAL) class Driver(uvm_driver): def build_phase(self): self.ap = uvm_analysis_port("ap", self) def start_of_simulation_phase(self): self.bfm = TinyAluBfm() async def launch_tb(self): await self.bfm.reset() self.bfm.start_bfm() async def run_phase(self): await self.launch_tb() while True: cmd = await self.seq_item_port.get_next_item() await self.bfm.send_op(cmd.A, cmd.B, cmd.op) result = await self.bfm.get_result() self.ap.write(result) cmd.result = result self.seq_item_port.item_done() class Coverage(uvm_subscriber): def end_of_elaboration_phase(self): self.cvg = set() def write(self, cmd): (_, _, op) = cmd self.cvg.add(op) def report_phase(self): try: disable_errors = ConfigDB().get(self, "", "DISABLE_COVERAGE_ERRORS") except UVMConfigItemNotFound: disable_errors = False if not disable_errors: if len(set(Ops) - self.cvg) > 0: self.logger.error( f"Functional coverage error. Missed: {set(Ops) - self.cvg}" ) assert False else: self.logger.info("Covered all operations") assert True class Scoreboard(uvm_component): def build_phase(self): self.cmd_fifo = uvm_tlm_analysis_fifo("cmd_fifo", self) self.result_fifo = uvm_tlm_analysis_fifo("result_fifo", self) self.cmd_get_port = uvm_get_port("cmd_get_port", self) self.result_get_port = uvm_get_port("result_get_port", self) self.cmd_export = self.cmd_fifo.analysis_export self.result_export = self.result_fifo.analysis_export def connect_phase(self): self.cmd_get_port.connect(self.cmd_fifo.get_export) self.result_get_port.connect(self.result_fifo.get_export) def check_phase(self): passed = True try: self.errors = ConfigDB().get(self, "", "CREATE_ERRORS") except UVMConfigItemNotFound: self.errors = False while self.result_get_port.can_get(): _, actual_result = self.result_get_port.try_get() cmd_success, cmd = self.cmd_get_port.try_get() if not cmd_success: self.logger.critical(f"result {actual_result} had no command") else: (A, B, op_numb) = cmd op = Ops(op_numb) predicted_result = alu_prediction(A, B, op, self.errors) if predicted_result == actual_result: self.logger.info( f"PASSED: 0x{A:02x} {op.name} 0x{B:02x} = 0x{actual_result:04x}" ) else: self.logger.error( f"FAILED: 0x{A:02x} {op.name} 0x{B:02x} " f"= 0x{actual_result:04x} " f"expected 0x{predicted_result:04x}" ) passed = False assert passed class Monitor(uvm_component): def __init__(self, name, parent, method_name): super().__init__(name, parent) self.method_name = method_name def build_phase(self): self.ap = uvm_analysis_port("ap", self) self.bfm = TinyAluBfm() self.get_method = getattr(self.bfm, self.method_name) async def run_phase(self): while True: datum = await self.get_method() self.logger.debug(f"MONITORED {datum}") self.ap.write(datum) class AluEnv(uvm_env): def build_phase(self): self.clk_drv = Clock(cocotb.top.clk, 2, "us") cocotb.start_soon(self.clk_drv.start()) self.seqr = uvm_sequencer("seqr", self) ConfigDB().set(None, "*", "SEQR", self.seqr) self.driver = Driver.create("driver", self) self.cmd_mon = Monitor("cmd_mon", self, "get_cmd") self.coverage = Coverage("coverage", self) self.scoreboard = Scoreboard("scoreboard", self) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) self.cmd_mon.ap.connect(self.scoreboard.cmd_export) self.cmd_mon.ap.connect(self.coverage.analysis_export) self.driver.ap.connect(self.scoreboard.result_export) @pyuvm.test() class AluTest(uvm_test): """Test ALU with random and max values""" def build_phase(self): self.env = AluEnv("env", self) def end_of_elaboration_phase(self): self.test_all = TestAllSeq.create("test_all") async def run_phase(self): self.raise_objection() await self.test_all.start() self.drop_objection() @pyuvm.test() class ParallelTest(AluTest): """Test ALU random and max forked""" def build_phase(self): uvm_factory().set_type_override_by_type(TestAllSeq, TestAllForkSeq) super().build_phase() @pyuvm.test() class FibonacciTest(AluTest): """Run Fibonacci program""" def build_phase(self): ConfigDB().set(None, "*", "DISABLE_COVERAGE_ERRORS", True) uvm_factory().set_type_override_by_type(TestAllSeq, FibonacciSeq) return super().build_phase() @pyuvm.test(expect_fail=True) class AluTestErrors(AluTest): """Test ALU with errors on all operations""" def start_of_simulation_phase(self): ConfigDB().set(None, "*", "CREATE_ERRORS", True) pyuvm-4.0.1/examples/TinyALU/tinyalu_utils.py000066400000000000000000000067271507477334100213050ustar00rootroot00000000000000import enum import logging import cocotb from cocotb.queue import Queue, QueueEmpty from cocotb.triggers import FallingEdge from pyuvm import utility_classes logging.basicConfig(level=logging.NOTSET) logger = logging.getLogger() logger.setLevel(logging.DEBUG) @enum.unique class Ops(enum.IntEnum): """Legal ops for the TinyALU""" ADD = 1 AND = 2 XOR = 3 MUL = 4 def alu_prediction(A, B, op, error=False): """Python model of the TinyALU""" assert isinstance(op, Ops), "The tinyalu op must be of type Ops" if op == Ops.ADD: result = A + B elif op == Ops.AND: result = A & B elif op == Ops.XOR: result = A ^ B elif op == Ops.MUL: result = A * B if error: result = result + 1 return result def get_int(signal): try: sig = int(signal.value) except ValueError: sig = 0 return sig class TinyAluBfm(metaclass=utility_classes.Singleton): def __init__(self): self.dut = cocotb.top self.driver_queue = Queue(maxsize=1) self.cmd_mon_queue = Queue(maxsize=0) self.result_mon_queue = Queue(maxsize=0) async def send_op(self, aa, bb, op): command_tuple = (aa, bb, op) await self.driver_queue.put(command_tuple) async def get_cmd(self): cmd = await self.cmd_mon_queue.get() return cmd async def get_result(self): result = await self.result_mon_queue.get() return result async def reset(self): await FallingEdge(self.dut.clk) self.dut.reset_n.value = 0 self.dut.A.value = 0 self.dut.B.value = 0 self.dut.op.value = 0 await FallingEdge(self.dut.clk) self.dut.reset_n.value = 1 await FallingEdge(self.dut.clk) async def driver_bfm(self): self.dut.start.value = 0 self.dut.A.value = 0 self.dut.B.value = 0 self.dut.op.value = 0 while True: await FallingEdge(self.dut.clk) start = get_int(self.dut.start) done = get_int(self.dut.done) if start == 0 and done == 0: try: (aa, bb, op) = self.driver_queue.get_nowait() self.dut.A.value = aa self.dut.B.value = bb self.dut.op.value = op self.dut.start.value = 1 except QueueEmpty: pass elif start == 1: if done == 1: self.dut.start.value = 0 async def cmd_mon_bfm(self): prev_start = 0 while True: await FallingEdge(self.dut.clk) start = get_int(self.dut.start) if start == 1 and prev_start == 0: cmd_tuple = ( get_int(self.dut.A), get_int(self.dut.B), get_int(self.dut.op), ) self.cmd_mon_queue.put_nowait(cmd_tuple) prev_start = start async def result_mon_bfm(self): prev_done = 0 while True: await FallingEdge(self.dut.clk) done = get_int(self.dut.done) if prev_done == 0 and done == 1: result = get_int(self.dut.result) self.result_mon_queue.put_nowait(result) prev_done = done def start_bfm(self): cocotb.start_soon(self.driver_bfm()) cocotb.start_soon(self.cmd_mon_bfm()) cocotb.start_soon(self.result_mon_bfm()) pyuvm-4.0.1/examples/TinyALU_reg/000077500000000000000000000000001507477334100166475ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU_reg/.gitignore000066400000000000000000000000721507477334100206360ustar00rootroot00000000000000sim_build __pycache__ results.xml # VCS outputs ucli.key pyuvm-4.0.1/examples/TinyALU_reg/Makefile000066400000000000000000000013541507477334100203120ustar00rootroot00000000000000CWD=$(shell pwd) export COCOTB_REDUCED_LOG_FMT = 1 SIM ?= icarus TOPLEVEL_LANG ?= verilog ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES =$(CWD)/hdl/verilog/tinyalu.sv $(CWD)/hdl/verilog/TinyALUreg.sv else ifeq ($(TOPLEVEL_LANG),vhdl) $(error VHDL is not supported for this design) else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif ifeq ($(SIM), verilator) # Enable processing of #delay statements COMPILE_ARGS += --timing EXTRA_ARGS += --trace --trace-structs endif MODULE := testbench TOPLEVEL = tinyalu GHDL_ARGS := --ieee=synopsys COCOTB_HDL_TIMEUNIT = 1ns COCOTB_HDL_TIMEPRECISION = 1ps include $(shell cocotb-config --makefiles)/Makefile.sim include ../../checkclean.mk pyuvm-4.0.1/examples/TinyALU_reg/TinyALUreg.rdl000066400000000000000000000020301507477334100213300ustar00rootroot00000000000000addrmap TinyALUreg { name = "TinyALUreg"; desc = "Register description of TinyALU"; default regwidth = 16; reg { name = "source data reg"; field { desc = "source data register 0"; hw=r; sw=rw; } data0[7:0] = 0; field { desc = "source data register 1"; hw=r; sw=rw; } data1[15:8] = 0; } SRC @ 0x0; reg { name = "result data reg"; field { desc = "result data"; hw=w; sw=r; } data[15:0] = 0; } RESULT @ 0x2; reg { name = "Command and status register"; field { desc = "operation"; hw=r; sw=rw; } op[4:0] = 0; field { desc = "start"; hw=r; sw=rw; } start[5:5] = 0; field { desc = "Done"; hw=w; sw=r; } done[6:6] = 0; field { desc = "reserved field"; hw=w; sw=rw; } reserved[15:7] = 0; } CMD @ 0x4; }; pyuvm-4.0.1/examples/TinyALU_reg/export.py000066400000000000000000000004731507477334100205460ustar00rootroot00000000000000import sys from peakrdl.verilog import VerilogExporter from systemrdl import RDLCompileError, RDLCompiler rdlc = RDLCompiler() try: rdlc.compile_file("./TinyALUreg.rdl") root = rdlc.elaborate() except RDLCompileError: sys.exit(1) exporter = VerilogExporter() exporter.export(root, "./hdl/verilog/") pyuvm-4.0.1/examples/TinyALU_reg/hdl/000077500000000000000000000000001507477334100174165ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU_reg/hdl/verilog/000077500000000000000000000000001507477334100210655ustar00rootroot00000000000000pyuvm-4.0.1/examples/TinyALU_reg/hdl/verilog/TinyALUreg.sv000066400000000000000000000270601507477334100234270ustar00rootroot00000000000000// This file was autogenerated by PeakRDL-verilog module TinyALUreg #( parameter ADDR_OFFSET = 0, //! Module's offset in the main address map parameter ADDR_WIDTH = 32, //! Width of SW address bus parameter DATA_WIDTH = 32 //! Width of SW data bus )( // Clocks and resets input logic clk, //! Default clock input logic resetn, //! Default reset // Register SRC output logic [ 7: 0] SRC_data0_q, //! Current field value output logic [ 7: 0] SRC_data1_q, //! Current field value // Register RESULT input logic [15: 0] RESULT_data_wdata, //! HW write data // Register CMD output logic [ 4: 0] CMD_op_q, //! Current field value output logic [ 0: 0] CMD_start_q, //! Current field value input logic [ 0: 0] CMD_done_wdata, //! HW write data input logic CMD_reserved_we, //! Control HW write (active high) input logic [ 8: 0] CMD_reserved_wdata, //! HW write data // Register Bus input logic valid, //! Active high valid input logic read, //! Indicates request is a read input logic [ADDR_WIDTH-1:0] addr, //! Address (byte aligned, absolute address) /* verilator lint_off UNUSED */ input logic [DATA_WIDTH-1:0] wdata, //! Write data input logic [DATA_WIDTH/8-1:0] wmask, //! Write mask /* verilator lint_on UNUSED */ output logic [DATA_WIDTH-1:0] rdata //! Read data ); /* verilator lint_off UNUSED */ // local output signals for fields (unless block outputs) // these can be used as references in other fields logic [ 7: 0] SRC_data0_next; logic SRC_data0_anded; logic SRC_data0_ored; logic SRC_data0_xored; logic [ 7: 0] SRC_data1_next; logic SRC_data1_anded; logic SRC_data1_ored; logic SRC_data1_xored; logic [15: 0] RESULT_data_q; logic [15: 0] RESULT_data_next; logic RESULT_data_anded; logic RESULT_data_ored; logic RESULT_data_xored; logic [ 4: 0] CMD_op_next; logic CMD_op_anded; logic CMD_op_ored; logic CMD_op_xored; logic [ 0: 0] CMD_start_next; logic CMD_start_anded; logic CMD_start_ored; logic CMD_start_xored; logic [ 0: 0] CMD_done_q; logic [ 0: 0] CMD_done_next; logic CMD_done_anded; logic CMD_done_ored; logic CMD_done_xored; logic [ 8: 0] CMD_reserved_q; logic [ 8: 0] CMD_reserved_next; logic CMD_reserved_anded; logic CMD_reserved_ored; logic CMD_reserved_xored; /* verilator lint_on UNUSED */ // ============================================================ // SW Access logic // ============================================================ logic sw_wr; logic sw_rd; /* verilator lint_off UNUSED */ logic [DATA_WIDTH-1:0] sw_mask; logic [DATA_WIDTH-1:0] sw_wdata; logic [DATA_WIDTH-1:0] sw_rdata; logic [DATA_WIDTH-1:0] sw_masked_data; /* verilator lint_on UNUSED */ // convert bus interface to internal sw_* signals assign sw_wr = valid && !read; assign sw_rd = valid && read; assign sw_wdata = wdata; assign rdata = sw_rdata; // convert byte mask to bit mask always @(wmask) begin int byte_idx; for (byte_idx = 0; byte_idx < DATA_WIDTH/8; byte_idx+=1) begin sw_mask[8*(byte_idx+1)-1 -: 8] = {8{wmask[byte_idx]}}; end end // helpful masked version of data assign sw_masked_data = sw_wdata & sw_mask; logic [DATA_WIDTH-1:0] SRC_rdata; logic [DATA_WIDTH-1:0] RESULT_rdata; logic [DATA_WIDTH-1:0] CMD_rdata; assign sw_rdata = // or of each register return (masked) SRC_rdata | RESULT_rdata | CMD_rdata; // ============================================================ // Register: SRC // [ 7: 0] data0: hw=r sw=rw reset=0x0 // [15: 8] data1: hw=r sw=rw reset=0x0 // ============================================================ logic SRC_decode; logic SRC_sw_wr; logic SRC_sw_rd; logic [DATA_WIDTH-1:0] SRC_q; assign SRC_decode = (addr == (ADDR_OFFSET+'h0+'h0)); assign SRC_sw_wr = sw_wr && SRC_decode; assign SRC_sw_rd = sw_rd && SRC_decode; always @(SRC_data0_q) begin SRC_q = '0; SRC_q[ 7: 0] = SRC_data0_q; SRC_q[15: 8] = SRC_data1_q; end // masked version of return data assign SRC_rdata = SRC_sw_rd ? SRC_q : 'b0; // ------------------------------------------------------------ // Field: data0 // ------------------------------------------------------------ assign SRC_data0_anded = & SRC_data0_q; assign SRC_data0_ored = | SRC_data0_q; assign SRC_data0_xored = ^ SRC_data0_q; // next hardware value //! main storage always @ (posedge clk, negedge resetn) if (~resetn) begin SRC_data0_q <= 0; end else begin // Software write if (SRC_sw_wr) begin SRC_data0_q <= sw_masked_data[ 7: 0] | (SRC_data0_q & ~sw_mask[ 7: 0]); end end // ------------------------------------------------------------ // Field: data1 // ------------------------------------------------------------ assign SRC_data1_anded = & SRC_data1_q; assign SRC_data1_ored = | SRC_data1_q; assign SRC_data1_xored = ^ SRC_data1_q; // next hardware value //! main storage always @ (posedge clk, negedge resetn) if (~resetn) begin SRC_data1_q <= 0; end else begin // Software write if (SRC_sw_wr) begin SRC_data1_q <= sw_masked_data[15: 8] | (SRC_data1_q & ~sw_mask[15: 8]); end end // ============================================================ // Register: RESULT // [15: 0] data: hw=w sw=r reset=0x0 // ============================================================ logic RESULT_decode; logic RESULT_sw_wr; logic RESULT_sw_rd; logic [DATA_WIDTH-1:0] RESULT_q; assign RESULT_decode = (addr == (ADDR_OFFSET+'h0+'h2)); assign RESULT_sw_wr = sw_wr && RESULT_decode; assign RESULT_sw_rd = sw_rd && RESULT_decode; always @(RESULT_data_q) begin RESULT_q = '0; RESULT_q[15: 0] = RESULT_data_q; end // masked version of return data assign RESULT_rdata = RESULT_sw_rd ? RESULT_q : 'b0; // ------------------------------------------------------------ // Field: data (wire) // ------------------------------------------------------------ assign RESULT_data_anded = & RESULT_data_q; assign RESULT_data_ored = | RESULT_data_q; assign RESULT_data_xored = ^ RESULT_data_q; // next hardware value assign RESULT_data_next = RESULT_data_wdata; assign RESULT_data_q = RESULT_data_next; // ============================================================ // Register: CMD // [ 4: 0] op: hw=r sw=rw reset=0x0 // [ 5: 5] start: hw=r sw=rw reset=0x0 // [ 6: 6] done: hw=w sw=r reset=0x0 // [15: 7] reserved: hw=w sw=rw reset=0x0 // ============================================================ logic CMD_decode; logic CMD_sw_wr; logic CMD_sw_rd; logic [DATA_WIDTH-1:0] CMD_q; assign CMD_decode = (addr == (ADDR_OFFSET+'h0+'h4)); assign CMD_sw_wr = sw_wr && CMD_decode; assign CMD_sw_rd = sw_rd && CMD_decode; always @(CMD_op_q or CMD_start_q or CMD_done_q or CMD_reserved_q) begin CMD_q = '0; CMD_q[ 4: 0] = CMD_op_q; CMD_q[ 5: 5] = CMD_start_q; CMD_q[ 6: 6] = CMD_done_q; CMD_q[15: 7] = CMD_reserved_q; end // masked version of return data assign CMD_rdata = CMD_sw_rd ? CMD_q : 'b0; // ------------------------------------------------------------ // Field: op // ------------------------------------------------------------ assign CMD_op_anded = & CMD_op_q; assign CMD_op_ored = | CMD_op_q; assign CMD_op_xored = ^ CMD_op_q; // next hardware value //! main storage always @ (posedge clk, negedge resetn) if (~resetn) begin CMD_op_q <= 0; end else begin // Software write if (CMD_sw_wr) begin CMD_op_q <= sw_masked_data[ 4: 0] | (CMD_op_q & ~sw_mask[ 4: 0]); end end // ------------------------------------------------------------ // Field: start // ------------------------------------------------------------ assign CMD_start_anded = & CMD_start_q; assign CMD_start_ored = | CMD_start_q; assign CMD_start_xored = ^ CMD_start_q; // next hardware value //! main storage always @ (posedge clk, negedge resetn) if (~resetn) begin CMD_start_q <= 0; end else begin // Software write if (CMD_sw_wr) begin CMD_start_q <= sw_masked_data[ 5: 5] | (CMD_start_q & ~sw_mask[ 5: 5]); end end // ------------------------------------------------------------ // Field: done (wire) // ------------------------------------------------------------ assign CMD_done_anded = & CMD_done_q; assign CMD_done_ored = | CMD_done_q; assign CMD_done_xored = ^ CMD_done_q; // next hardware value assign CMD_done_next = CMD_done_wdata; assign CMD_done_q = CMD_done_next; // ------------------------------------------------------------ // Field: reserved // ------------------------------------------------------------ assign CMD_reserved_anded = & CMD_reserved_q; assign CMD_reserved_ored = | CMD_reserved_q; assign CMD_reserved_xored = ^ CMD_reserved_q; // next hardware value assign CMD_reserved_next = CMD_reserved_wdata; //! main storage always @ (posedge clk, negedge resetn) if (~resetn) begin CMD_reserved_q <= 0; end else begin // Hardware Write if (CMD_reserved_we) begin CMD_reserved_q <= CMD_reserved_next; end // Software write if (CMD_sw_wr) begin CMD_reserved_q <= sw_masked_data[15: 7] | (CMD_reserved_q & ~sw_mask[15: 7]); end end endmodule: TinyALUreg pyuvm-4.0.1/examples/TinyALU_reg/hdl/verilog/tinyalu.sv000066400000000000000000000127631507477334100231350ustar00rootroot00000000000000module tinyalu ( /** Leave it but unused start */ input [7:0] A, input logic [7:0] B, input logic [2:0] op, input logic start, /** Leave it but unused end */ input logic clk, input logic reset_n, // Register Bus input logic valid, //! Active high valid input logic read, //! Indicates request is a read input logic [31:0] addr, //! Address (byte aligned, absolute address) /* verilator lint_off UNUSED */ input logic [31:0] wdata, //! Write data input logic [3:0] wmask, //! Write mask /* verilator lint_on UNUSED */ output logic [31:0] rdata, //! Read data output logic [15:0] result //! final result ); localparam ADDR_WIDTH = 32; localparam DATA_WIDTH = 32; localparam ADDR_OFFSET = 'd0; localparam RESERVED_VALUE = 'd1; wire [15:0] result_aax, result_mult; wire start_single, start_mult; wire done_aax; wire done_mult; // Register CMD input logic CMD_op_we; //! Control HW write (active high) logic [4:0] CMD_op_wdata; //! HW write data logic CMD_reserved_we; //! Control HW write (active high) logic [6:0] CMD_reserved_wdata; //! HW write data // Register CMD output logic [6:0] CMD_reserved_q; //! Current field value logic [4:0] CMD_op_q; //! Current field value logic [0:0] CMD_start_q; //! Current field value // Register SRC logic [7:0] SRC_data0_q; logic [7:0] SRC_data1_q; // Register RESULT logic [15:0] RESULT_data_wdata; //! HW write data // Start Signal coming from the register from the RF written by SW interface logic done; logic [2:0] op_from_rf; assign op_from_rf = CMD_op_q[2:0]; assign start_single = CMD_start_q & ~op_from_rf[2]; assign start_mult = CMD_start_q & op_from_rf[2]; single_cycle and_add_xor ( .A(SRC_data0_q), .B(SRC_data1_q), .op(op_from_rf), .clk, .reset_n, .start(start_single), .done(done_aax), .result(result_aax)); three_cycle mult ( .A(SRC_data0_q), .B(SRC_data1_q), .op(op_from_rf), .clk, .reset_n, .start(start_mult), .done(done_mult), .result(result_mult)); assign done = (CMD_op_q[2]) ? done_mult : done_aax; assign result = (CMD_op_q[2]) ? result_mult : result_aax; TinyALUreg #( .ADDR_OFFSET(ADDR_OFFSET), //! Module's offset in the main address map .ADDR_WIDTH (ADDR_WIDTH), //! Width of SW address bus .DATA_WIDTH (ADDR_WIDTH) //! Width of SW data bus )regblock( // Clocks and resets .clk (clk), //! Default clock .resetn (reset_n), //! Default reset .SRC_data0_q (SRC_data0_q), //! HW write data .SRC_data1_q (SRC_data1_q), //! HW write data .RESULT_data_wdata (result), //! HW write data .CMD_op_q (CMD_op_q), //! Current field value .CMD_start_q (CMD_start_q), //! Current field value // .CMD_done_we (1'b1), //! Control HW write (active high) .CMD_done_wdata (done), //! HW write data .CMD_reserved_we (1'b1), //! Control HW write (active high) .CMD_reserved_wdata ('h0), //! HW write data .valid (valid), //! Active high valid .read (read), //! Indicates request is a read .addr (addr), //! Address (byte aligned, absolute address) .wdata (wdata), //! Write data .wmask (wmask), //! Write mask .rdata (rdata) //! Read data ); endmodule // tinyalu module single_cycle(input [7:0] A, input [7:0] B, input [2:0] op, input clk, input reset_n, input start, output logic done, output logic [15:0] result); always @(posedge clk) if (!reset_n) result <= 0; else case(op) 3'b001 : result <= {8'd0,A} + {8'd0,B}; 3'b010 : result <= {8'd0,A} & {8'd0,B}; 3'b011 : result <= {8'd0,A} ^ {8'd0,B}; default : result <= {A,B}; endcase // case (op) always @(posedge clk) if (!reset_n) done <= 0; else done <= ((start == 1'b1) && (op != 3'b000)); endmodule : single_cycle module three_cycle(input [7:0] A, input [7:0] B, input [2:0] op, input clk, input reset_n, input start, output logic done, output logic [15:0] result); logic [7:0] a_int, b_int; logic [15:0] mult1, mult2; logic done1, done2, done3; always @(posedge clk) if (!reset_n) begin done <= 0; done3 <= 0; done2 <= 0; done1 <= 0; a_int <= 0; b_int <= 0; mult1 <= 0; mult2 <= 0; result<= 0; end else begin // if (!reset_n) a_int <= A; b_int <= B; mult1 <= a_int * b_int; mult2 <= mult1; result <= mult2; done3 <= start & !done; done2 <= done3 & !done; done1 <= done2 & !done; done <= done1 & !done; end // else: !if(!reset_n) endmodule : three_cycle pyuvm-4.0.1/examples/TinyALU_reg/testbench.py000066400000000000000000000520111507477334100211770ustar00rootroot00000000000000import random # All testbenches use tinyalu_utils, so store it in a central # place and add its path to the sys path so we can import it import sys from pathlib import Path import cocotb from cocotb.clock import Clock from cocotb.triggers import Combine import pyuvm from pyuvm import ( CRITICAL, ConfigDB, UVMConfigItemNotFound, UVMError, UVMNotImplemented, uvm_analysis_port, uvm_component, uvm_driver, uvm_env, uvm_factory, uvm_reg, uvm_reg_adapter, uvm_reg_block, uvm_reg_bus_op, uvm_reg_field, uvm_reg_map, uvm_report_object, uvm_root, uvm_sequence, uvm_sequence_item, uvm_sequencer, uvm_subscriber, uvm_test, uvm_tlm_analysis_fifo, ) from pyuvm.s24_uvm_reg_includes import access_e, check_t, path_t, status_t sys.path.append(str(Path("..").resolve())) import os from tinyalu_utils import Ops, TinyAluBfm, alu_prediction # noqa: E402 LANGUAGE = os.getenv("TOPLEVEL_LANG", "verilog") ############################################################################## # TESTS ENTIRE RAL of an ALU # The ALU has 2 SRC input operands stored into 2 flops # both fields are part of the SRC register (16bits) called DATA0 -DATA1 # the result of the operation is instead sent back into a 16bits register # called RESULT # the Operation is instead written into a register called CMD: # 1. the first 5 (4:0) bits is the OP target # 2. the bit number 5 is the start if # not set the OPERATION is not kicked off # 3. the bit 6 is the DONE to be polled once the OP is accomplished # 4. the remaining bits are reserved and not used ############################################################################## REG_WIDTH = 16 ALU_REG_SRC_ADDR = "0x0" ALU_REG_SRC_ADDR_DATA0_S = 0 ALU_REG_SRC_ADDR_DATA1_S = 8 ALU_REG_SRC_ADDR_DATA0_M = 2**8 - 1 ALU_REG_SRC_ADDR_DATA1_M = 2**8 - 1 ALU_REG_RESULT_ADDR = "0x2" ALU_REG_RESULT_DATA_S = 0 ALU_REG_RESULT_DATA_M = 2**16 - 1 ALU_REG_CMD_ADDR = "0x4" ALU_REG_CMD_OP_S = 0 ALU_REG_CMD_START_S = 5 ALU_REG_CMD_DONE_S = 6 ALU_REG_CMD_RESERVED_S = 7 ALU_REG_CMD_OP_M = 2 * 5 - 1 ALU_REG_CMD_START_M = 2 * 1 - 1 ALU_REG_CMD_DONE_M = 2 * 1 - 1 ALU_REG_CMD_RESERVED_M = 2 * 9 - 1 ############################################################################## # Register MOodel ############################################################################## class ALU_REG_SRC(uvm_reg): def __init__(self, name="ALU_REG_SRC", reg_width=REG_WIDTH): super().__init__(name, reg_width) self.DATA0 = uvm_reg_field("DATA0") self.DATA1 = uvm_reg_field("DATA1") def build(self): self.DATA0.configure(self, 8, 0, "RW", 0, 0) self.DATA1.configure(self, 8, 8, "RW", 0, 0) self._set_lock() class ALU_REG_RESULT(uvm_reg): def __init__(self, name="ALU_REG_RESULT", reg_width=REG_WIDTH): super().__init__(name, reg_width) self.DATA = uvm_reg_field("DATA") def build(self): self.DATA.configure(self, 16, 0, "RW", 0, 0) self._set_lock() class ALU_REG_CMD(uvm_reg): def __init__(self, name="ALU_REG_CMD", reg_width=REG_WIDTH): super().__init__(name, reg_width) self.OP = uvm_reg_field("OP") self.START = uvm_reg_field("START") self.DONE = uvm_reg_field("DONE") self.RESERVED = uvm_reg_field("RESERVED") def build(self): self.OP.configure(self, 5, 0, "RW", 0, 1) self.START.configure(self, 1, 5, "RW", 0, 1) self.DONE.configure(self, 1, 6, "RO", 0, 1) self.RESERVED.configure(self, 8, 7, "RW", 0, 1) self._set_lock() class ALU_REG_REG_BLOCK(uvm_reg_block): def __init__(self, name="ALU_REG_REG_BLOCK"): super().__init__(name) # do not use create map if only the default one # is intended to be used self.def_map = uvm_reg_map("map") self.def_map.configure(self, 0) self.SRC = ALU_REG_SRC("SRC") self.SRC.configure(self, "0x0", "", False, False) self.def_map.add_reg(self.SRC, "0x0", "RW") self.RESULT = ALU_REG_RESULT("RESULT") self.RESULT.configure(self, "0x2", "", False, False) self.def_map.add_reg(self.RESULT, "0x0", "RW") self.CMD = ALU_REG_CMD("CMD") self.CMD.configure(self, "0x4", "", False, False) self.def_map.add_reg(self.CMD, "0x0", "RW") ############################################################################## # ADAPTER ############################################################################## class simple_bus_adapter(uvm_reg_adapter): def __init__(self, name="simple_bus_adapter"): super().__init__(name) # uvm_ def reg2bus(self, rw: uvm_reg_bus_op) -> uvm_sequence_item: item = simple_bus_item("item") # Set read bit if rw.kind == access_e.UVM_READ: item.read = 1 item.rdata = rw.data else: item.read = 0 item.wdata = rw.data item.addr = rw.addr return item # uvm_reg_bus_op is not created but updated and returned def bus2reg(self, bus_item: uvm_sequence_item, rw: uvm_reg_bus_op): if bus_item.read == 1: rw.kind = access_e.UVM_READ rw.data = bus_item.rdata else: rw.data = bus_item.wdata rw.kind = access_e.UVM_WRITE # Set addr rw.addr = bus_item.addr # Set nbits rw.n_bits = pyuvm.count_bits(bus_item.wmask) # Set byte_en rw.byte_en = bus_item.wmask # Set status rw.status = status_t.IS_OK if bus_item.status is False: rw.status = status_t.IS_NOT_OK ############################################################################## # Sequence Items ############################################################################## class simple_bus_item(uvm_sequence_item): def __init__(self, name): super().__init__(name) self.rdata: int = 0 self.read: int = 0 self.addr: str = "" self.wmask: int = 0 self.wdata: int = 0 self.status = None def get_addr(self): return int(self.addr, 16) def print_item(self): cocotb.log.info( f" simple_bus_item: \ rdata {self.rdata} \ read {self.read} \ addr {self.addr} \ wmask {self.wmask} \ wdata {self.wdata} \ status {self.status}" ) class AluSeqItem(uvm_sequence_item): def __init__(self, name, aa, bb, op): super().__init__(name) self.A = aa self.B = bb self.op = Ops(op) self.result = 0 def randomize_operands(self): self.A = random.randint(0, 255) self.B = random.randint(0, 255) def randomize(self): self.randomize_operands() self.op = random.choice(list(Ops)) def __eq__(self, other): same = self.A == other.A and self.B == other.B and self.op == other.op return same def __str__(self): return f"{self.get_name()} : A: 0x{self.A:02x} OP: {self.op.name}" f" ({self.op.value}) B: 0x{self.B:02x}" ############################################################################## # Sequence classes ############################################################################## class AluReg_base_sequence(uvm_sequence, uvm_report_object): def __init__(self, name="AluReg_base_sequence"): super().__init__(name) self.ral = ConfigDB().get(None, "", "regsiter_model") self.map = self.ral.def_map async def body(self): raise UVMNotImplemented def compute_done(self, data: int): if (data >> ALU_REG_CMD_DONE_S) & (ALU_REG_CMD_DONE_M) == 1: return True else: return False def seq_print(self, msg: str): # self.logger.info(msg) uvm_root().logger.info(msg) def print_w_access(self, reg_addr: int, wdata: int): reg = self.map.get_reg_by_offset(reg_addr) self.seq_print( f"Write access to register {reg.get_name()}" f" at address {reg.get_address()} with data {wdata}" ) def print_r_access(self, reg_addr: int, read_data: int): reg = self.map.get_reg_by_offset(reg_addr) self.seq_print( f"Read access to register {reg.get_name()}" f"address: {reg.get_address()} data: {read_data}" ) async def reg_write(self, reg_addr: int, write_data: int): target_reg = self.map.get_reg_by_offset(reg_addr) status = await target_reg.write( write_data, self.map, path_t.FRONTDOOR, check_t.NO_CHECK ) return status async def reg_read(self, reg_addr: int): target_reg = self.map.get_reg_by_offset(reg_addr) (status, rdata) = await target_reg.read( self.ral.def_map, path_t.FRONTDOOR, check_t.NO_CHECK ) self.seq_print(f"Finish Read with data: {rdata}") return status, rdata async def program_alu_reg(self, item: AluSeqItem): if self.ral is None: raise UVMError("program_alu_reg -- RAL cannot be None") self.seq_print("#####################################################") self.seq_print("START -- program_alu_reg") # Clear wdata = 0 status = await self.reg_write(ALU_REG_CMD_ADDR, wdata) if status == status_t.IS_OK: self.seq_print("Clearing CMD") self.print_w_access(ALU_REG_CMD_ADDR, wdata) # Write the 2 Operands A and B wdata = (item.B << ALU_REG_SRC_ADDR_DATA1_S) | item.A status = await self.reg_write(ALU_REG_SRC_ADDR, wdata) if status == status_t.IS_OK: self.seq_print(f"Operand A: {item.A}") self.seq_print(f"Operand B: {item.B}") self.print_w_access(ALU_REG_SRC_ADDR, wdata) # Write OP and START wdata = 1 << ALU_REG_CMD_START_S | item.op status = await self.reg_write(ALU_REG_CMD_ADDR, wdata) if status == status_t.IS_OK: self.seq_print(f"Operation is: {item.op.name}") self.print_w_access(ALU_REG_CMD_ADDR, wdata) # Read Till done is asserted (status, rdata) = await self.reg_read(ALU_REG_CMD_ADDR) while self.compute_done(rdata) is False: (status, rdata) = await self.reg_read(ALU_REG_CMD_ADDR) self.print_r_access(ALU_REG_CMD_ADDR, rdata) # Read the result (status, rdata) = await self.reg_read(ALU_REG_RESULT_ADDR) self.print_r_access(ALU_REG_RESULT_ADDR, rdata) # Load result back item.result = rdata self.seq_print("#####################################################") class RandomSeq(AluReg_base_sequence): async def body(self): for op in list(Ops): cmd_tr = AluSeqItem("cmd_tr", None, None, op) cmd_tr.randomize_operands() await self.program_alu_reg(cmd_tr) class MaxSeq(AluReg_base_sequence): async def body(self): for op in list(Ops): cmd_tr = AluSeqItem("cmd_tr", 0xFF, 0xFF, op) await self.program_alu_reg(cmd_tr) class TestAllSeq(AluReg_base_sequence): async def body(self): random = RandomSeq("random") await random.start() class TestAllForkSeq(AluReg_base_sequence): async def body(self): random = RandomSeq("random") max = MaxSeq("max") random_task = cocotb.start_soon(random.start()) max_task = cocotb.start_soon(max.start()) await Combine(random_task, max_task) class OpSeq(AluReg_base_sequence): def __init__(self, name, aa, bb, op): super().__init__(name) self.aa = aa self.bb = bb self.op = Ops(op) async def body(self): cmd_tr = AluSeqItem("cmd_tr", self.aa, self.bb, self.op) await self.program_alu_reg(cmd_tr) self.result = cmd_tr.result async def do_add(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.ADD) await seq.start(seqr) return seq.result async def do_and(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.AND) await seq.start(seqr) return seq.result async def do_xor(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.XOR) await seq.start(seqr) return seq.result async def do_mul(seqr, aa, bb): seq = OpSeq("seq", aa, bb, Ops.MUL) await seq.start(seqr) return seq.result class FibonacciSeq(uvm_sequence, uvm_report_object): def __init__(self, name): super().__init__(name) async def body(self): self.seqr = ConfigDB().get(None, "", "SEQR") prev_num = 0 cur_num = 1 fib_list = [prev_num, cur_num] for _ in range(7): sum = await do_add(self.seqr, prev_num, cur_num) fib_list.append(sum) prev_num = cur_num cur_num = sum self.logger.info("Fibonacci Sequence: " + str(fib_list)) uvm_root().set_logging_level_hier(CRITICAL) ############################################################################## # DRIVER ############################################################################## class Driver(uvm_driver): def build_phase(self): self.ap = uvm_analysis_port("ap", self) def start_of_simulation_phase(self): self.bfm = TinyAluBfm() async def launch_tb(self): await self.bfm.reset() async def run_phase(self): await self.launch_tb() while True: cmd = await self.seq_item_port.get_next_item() if cmd.read == 0: await self.bfm.SW_WRITE(cmd.get_addr(), cmd.wdata) else: read_data = await self.bfm.SW_READ(cmd.get_addr()) cmd.rdata = read_data self.seq_item_port.item_done() ############################################################################## # COVERAGE ############################################################################## class Coverage(uvm_subscriber): def end_of_elaboration_phase(self): self.cvg = set() def write(self, cmd): self.logger.info(f"Coverage receiving command {cmd}") op = cmd.op self.cvg.add(op) def report_phase(self): try: disable_errors = ConfigDB().get(self, "", "DISABLE_COVERAGE_ERRORS") except UVMConfigItemNotFound: disable_errors = False if not disable_errors: if len(set(Ops) - self.cvg) > 0: self.logger.critical( f"Functional coverage error. Missed: {set(Ops) - self.cvg}" ) assert False else: self.logger.info("Covered all operations") assert True ############################################################################## # Monitor ############################################################################## class Monitor(uvm_component): def __init__( self, name, parent, ): super().__init__(name, parent) def build_phase(self): self.ap = uvm_analysis_port("ap", self) self.bfm = TinyAluBfm() self.item = AluSeqItem("Monitor_Item", None, None, Ops.MUL) self.done = False async def run_phase(self): while True: # Wait for one clock await self.bfm.wait_clock() if str(self.bfm.get_reset()) == "1": self.done = False await self.bfm.capture_valid() self.logger.info("Monitor Got a Valid") if self.bfm.get_addr() == ALU_REG_SRC_ADDR: self.logger.info("Monitored ALU SRC ADDR") self.item.A = self.bfm.get_src0() self.item.B = self.bfm.get_src1() if self.bfm.get_addr() == ALU_REG_CMD_ADDR: self.logger.info("Monitored ALU CMD") self.item.op = self.bfm.get_op() if self.bfm.get_addr() == ALU_REG_RESULT_ADDR: self.logger.info("Monitored ALU RESULT") self.item.result = self.bfm.get_result() self.done = True if self.done is True: self.logger.info("Monitor Finished Operation") self.ap.write(self.item) ############################################################################## # SCOREBOARD ############################################################################## class Scoreboard(uvm_component): def build_phase(self): self.result_fifo = uvm_tlm_analysis_fifo("result_fifo", self) async def run_phase(self): try: self.errors = ConfigDB().get(self, "", "CREATE_ERRORS") except UVMConfigItemNotFound: self.errors = False if self.errors is True: self.logger.info("Alu Error negative scenario") while True: cmd = await self.result_fifo.get() (A, B, op_numb, actual_result) = (cmd.A, cmd.B, cmd.op, cmd.result) predicted_result = alu_prediction(A, B, op_numb, self.errors) if self.errors is True: if predicted_result != actual_result: self.logger.info( f"Error scenario PASSED: 0x{A:02x} " f"{op_numb.name} " f"0x{B:02x} = " f"0x{actual_result:04x}" ) else: self.logger.error( f"Error scenario FAILED: 0x{A:02x} " f"{op_numb.name} " f"0x{B:02x} " f"= 0x{actual_result:04x} " f"expected 0x{predicted_result:04x}" ) elif predicted_result == actual_result: self.logger.info( f"PASSED: 0x{A:02x} " f"{op_numb.name} " f"0x{B:02x} = " f"0x{actual_result:04x}" ) else: self.logger.error( f"FAILED: 0x{A:02x} " f"{op_numb.name} " f"0x{B:02x} " f"= 0x{actual_result:04x} " f"expected 0x{predicted_result:04x}" ) def check_phase(self): if self.result_fifo.size() != 0: self.logger.critical( f"TEST FAILED main result fifo is not" f" Empty, there are {self.result_fifo.size()}" f" left to be compared" ) ############################################################################## # ENVIRONMENT ############################################################################## class AluEnv(uvm_env): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = Driver.create("driver", self) self.monitor = Monitor("monitor", self) self.coverage = Coverage("coverage", self) self.scoreboard = Scoreboard("scoreboard", self) self.reg_adapter = simple_bus_adapter("reg_adapter") self.reg_block = ALU_REG_REG_BLOCK("reg_block") def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) self.monitor.ap.connect(self.coverage.analysis_export) self.monitor.ap.connect(self.scoreboard.result_fifo.analysis_export) self.reg_block.def_map.set_sequencer(self.seqr) self.reg_block.def_map.set_adapter(self.reg_adapter) # share SEQR and RAL across the TB if needed ConfigDB().set(None, "*", "SEQR", self.seqr) ConfigDB().set(None, "*", "regsiter_model", self.reg_block) ############################################################################## # TESTS ############################################################################## class AluTestBase(uvm_test): """Base class for ALU tests with random and max values""" def build_phase(self): self.env = AluEnv("env", self) def end_of_elaboration_phase(self): self.test_all = TestAllSeq.create("test_all") async def run_phase(self): self.raise_objection() if LANGUAGE == "verilog": # Start clock clock = Clock(cocotb.top.clk, 1, "ns") cocotb.start_soon(clock.start()) await self.test_all.start() self.drop_objection() @pyuvm.test() class AluTest(AluTestBase): """Test ALU with random and max values""" # @pyuvm.test() # class ParallelTest(AluTestBase): # """Test ALU random and max forked""" # def build_phase(self): # uvm_factory().set_type_override_by_type(TestAllSeq, TestAllForkSeq) # super().build_phase() @pyuvm.test() class FibonacciTest(AluTestBase): """Run Fibonacci program""" def build_phase(self): ConfigDB().set(None, "*", "DISABLE_COVERAGE_ERRORS", True) uvm_factory().set_type_override_by_type(TestAllSeq, FibonacciSeq) return super().build_phase() @pyuvm.test() class AluTestErrors(AluTestBase): """Test ALU with errors on all operations""" def build_phase(self): super().build_phase() ConfigDB().set(None, "*", "CREATE_ERRORS", True) pyuvm-4.0.1/examples/TinyALU_reg/tinyalu_utils.py000066400000000000000000000053021507477334100221260ustar00rootroot00000000000000import enum import logging import cocotb from cocotb.triggers import FallingEdge, RisingEdge from pyuvm import utility_classes logging.basicConfig(level=logging.NOTSET) logger = logging.getLogger() logger.setLevel(logging.DEBUG) @enum.unique class Ops(enum.IntEnum): """Legal ops for the TinyALU""" ADD = 1 AND = 2 XOR = 3 MUL = 4 def alu_prediction(A, B, op, error=False): """Python model of the TinyALU""" assert isinstance(op, Ops), "The tinyalu op must be of type Ops" if op == Ops.ADD: result = A + B elif op == Ops.AND: result = A & B elif op == Ops.XOR: result = A ^ B elif op == Ops.MUL: result = A * B if error: result = result + 1 return result def get_int(signal): try: sig = int(signal.value) except ValueError: sig = 0 return sig class TinyAluBfm(metaclass=utility_classes.Singleton): def __init__(self): self.dut = cocotb.top # clock generation async def wait_clock(self, cycles=1): """wait for clock pulses""" for cycle in range(cycles): await RisingEdge(self.dut.clk) async def SW_READ(self, sw_addr: int): await FallingEdge(self.dut.clk) self.dut.valid.value = 1 self.dut.read.value = 1 self.dut.addr.value = sw_addr await FallingEdge(self.dut.clk) self.dut.valid.value = 0 return int(self.dut.rdata.value) async def SW_WRITE(self, sw_addr: int, sw_data: int): await FallingEdge(self.dut.clk) self.dut.valid.value = 1 self.dut.read.value = 0 self.dut.addr.value = sw_addr self.dut.wdata.value = sw_data self.dut.wmask.value = 15 await FallingEdge(self.dut.clk) self.dut.valid.value = 0 async def reset(self): self.dut.reset_n.value = 0 await self.wait_clock(1) self.dut.reset_n.value = 1 await self.wait_clock(1) async def capture_valid(self): await RisingEdge(self.dut.valid) await FallingEdge(self.dut.clk) async def operation_finished(self): await RisingEdge(self.dut.regblock.CMD_done_q) await self.wait_clock(1) def get_addr(self): return hex(self.dut.addr.value) def get_src0(self): return int(self.dut.regblock.SRC_data0_q.value) def get_src1(self): return int(self.dut.regblock.SRC_data1_q.value) def get_op(self): if self.dut.regblock.CMD_op_q.value != 0: return Ops(int(self.dut.regblock.CMD_op_q.value)) else: return 0 def get_result(self): return int(self.dut.result.value) def get_reset(self): return self.dut.reset_n.value pyuvm-4.0.1/manual_rst/000077500000000000000000000000001507477334100150545ustar00rootroot00000000000000pyuvm-4.0.1/manual_rst/s05_base_classes.rst000066400000000000000000000004311507477334100207220ustar00rootroot0000000000000005 Base Classes =============== .. automodule:: s05_base_classes .. rubric:: Classes .. autoclass:: uvm_field_op :members: .. autoclass:: uvm_object :members: .. autoclass:: uvm_policy :members: .. autoclass:: uvm_transaction :members: pyuvm-4.0.1/pyproject.toml000066400000000000000000000056171507477334100156340ustar00rootroot00000000000000[build-system] requires = ["setuptools>=42","wheel"] build-backend = "setuptools.build_meta" [tool.ruff] target-version = "py37" [tool.ruff.lint] extend-select = [ "I", # isort "UP", # pyupgrade "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "PL", # pylint ] ignore = [ "E741", # ambiguous variable name (preference) "E501", # line too long (preference) "F405", # Undefined local with import star usage (preference) "F403", # Undefined local with import star (preference) "PLR0912", # Too many branches (>12) (preference) "PLR0913", # Too many arguments to function call (>5) (preference) "PLR0915", # Too many statements (>50) (preference) "PLR2004", # Magic value used in comparison (preference) "PLR0911", # Too many return statements (preference) "PLW0603", # Using the global statement (pedantic) "PLW1641", # object does not implement `__hash__` method (pedantic) ] [tool.ruff.lint.isort] known-first-party = [ "pyuvm", ] known-third-party = [ "pytest", "cocotb", ] [tool.ruff.lint.per-file-ignores] "pyuvm/__init__.py" = [ "F401", ] [tool.codespell] ignore-words-list = [ "synopsys", ] [tool.pytest.ini_options] testpaths = [ "tests/pytests", ] markers = [ "test_reg_get_name", "test_reg_configure", "test_reg_with_single_field", "test_reg_with_multiple_fields", "test_reg_with_multiple_felds_reset", "test_reg_with_multiple_fields_get_mirrored_value", "test_reg_with_multiple_fields_get_desired_value", "reg_block_get_field_single_reg", "reg_block_get_field_multiple_regs", "reg_block_get_field_sub_reg_block", "reg_block_get_reg_by_name", "reg_block_get_field_by_name", "test_reg_simple_predict", "test_reg_predict_edge_cases", "test_reg_predict_with_reserved_spaces", "test_reg_field_get_name", "test_reg_field_configure", "test_reg_field_is_volatile", "test_reg_field_all_access", "test_reg_field_lsb_pos", "test_reg_field_reset", "test_reg_field_get", "test_reg_field_get_value", "test_reg_field_field_predict_read_set", "test_reg_field_field_predict_read_clear", "test_reg_field_field_predict_write_set", "test_reg_field_field_predict_write_clear", "test_reg_field_field_predict_TOGGLE", "test_reg_field_field_predict_NO_ACCESS", "test_reg_field_field_predict_status_error_on_write", "test_reg_field_field_predict_status_error_on_read", "test_reg_block_get_name", "test_reg_block_with_single_reg", "test_reg_block_with_multiple_regs", "test_reg_map_get_name", "test_reg_map_configure", "test_reg_map_with_single_reg", "test_reg_map_with_multiple_regs", "reg_block_get_name", "reg_block_with_single_reg", "reg_block_with_multiple_regs", "reg_block_with_sub_blocks", "reg_block_get_field_empty_reg", ] pyuvm-4.0.1/pyuvm/000077500000000000000000000000001507477334100140675ustar00rootroot00000000000000pyuvm-4.0.1/pyuvm/__init__.py000066400000000000000000000022701507477334100162010ustar00rootroot00000000000000from pyuvm._version import __version__ # Support Modules from pyuvm.error_classes import * # Extension Modules from pyuvm.extension_classes import * # Section 5 from pyuvm.s05_base_classes import * # Section 6 from pyuvm.s06_reporting_classes import * # Section 8 from pyuvm.s08_factory_classes import * # Section 9 from pyuvm.s09_phasing import * # Section 12 from pyuvm.s12_uvm_tlm_interfaces import * # Section 13 from pyuvm.s13_predefined_component_classes import * # Section 14, 15 (Done as fresh Python design) from pyuvm.s14_15_python_sequences import * # Section 17, Register enumeration from pyuvm.s17_uvm_reg_enumerations import * # from pyuvm.s17_register_enumerations import * # # Section 18 # from pyuvm.s18_register_model import * from pyuvm.s18_uvm_reg_block import uvm_reg_block from pyuvm.s19_uvm_reg_field import uvm_reg_field from pyuvm.s20_uvm_reg import uvm_reg from pyuvm.s21_uvm_reg_map import uvm_reg_map from pyuvm.s22_uvm_mem import uvm_mem from pyuvm.s23_uvm_reg_item import uvm_reg_item from pyuvm.s24_uvm_reg_includes import * from pyuvm.s25_uvm_adapter import uvm_reg_adapter from pyuvm.s26_uvm_predictor import uvm_reg_predictor from pyuvm.utility_classes import * pyuvm-4.0.1/pyuvm/_utils.py000066400000000000000000000003711507477334100157410ustar00rootroot00000000000000import cocotb _cocotb_version_info = [] for xx in cocotb.__version__.split("."): try: _cocotb_version_info.append(int(xx)) # for strings like 'dev0' except ValueError: pass cocotb_version_info = tuple(_cocotb_version_info) pyuvm-4.0.1/pyuvm/_version.py000066400000000000000000000000261507477334100162630ustar00rootroot00000000000000__version__ = "4.0.1" pyuvm-4.0.1/pyuvm/error_classes.py000066400000000000000000000015051507477334100173100ustar00rootroot00000000000000class UVMError(Exception): """ All UVM Errors """ class UVMNotImplemented(UVMError): """For methods that we haven't yet implemented.""" class UsePythonMethod(UVMError): """ For cases where the user should use a Python method rather than a UVM method. """ class UVMFactoryError(UVMError): """For cases where a type is not registered with the factory""" class UVMTLMConnectionError(UVMError): """For problems connecting TLM""" class UVMBadPhase(UVMError): """Errors in phasing""" class UVMSequenceError(UVMError): """Errors using sequences""" class UVMConfigError(UVMError): """Errors using the config_db""" class UVMConfigItemNotFound(UVMError): """Couldn't find something in config_db""" class UVMFatalError(UVMError): """Used to dump out of the testbench""" pyuvm-4.0.1/pyuvm/extension_classes.py000066400000000000000000000035321507477334100201750ustar00rootroot00000000000000import functools import inspect import cocotb from pyuvm._utils import cocotb_version_info from pyuvm.s13_uvm_component import uvm_root def test( timeout_time=None, timeout_unit="step", expect_fail=False, expect_error=(), skip=False, stage=None, keep_singletons=False, keep_set=set(), ): if cocotb_version_info >= (1, 7, 0) and stage is None: stage = 0 def decorator(cls): test_dec_args = { "timeout_time": timeout_time, "timeout_unit": timeout_unit, "expect_fail": expect_fail, "expect_error": expect_error, "skip": skip, "stage": stage, } if cocotb_version_info >= (2, 0): # This sets the test name so that it can be selected appropriately using # COCOTB_TEST_FILTER in 2.0+. <2.0 won't have this luxury. test_dec_args["name"] = cls.__name__ # create cocotb.test object to be picked up RegressionManager @cocotb.test(**test_dec_args) @functools.wraps(cls) async def test_obj(_): await uvm_root().run_test( cls, keep_singletons=keep_singletons, keep_set=keep_set ) # adds cocotb.test object to caller's module caller_frame = inspect.stack()[1] caller_module = inspect.getmodule(caller_frame[0]) if cocotb_version_info < (2, 0): setattr(caller_module, f"test_{test_obj._id}", test_obj) else: # In 2.0+ cocotb tests don't have a numbered ID. # This is fine since because we set "name" above, we can use the actual # test name when selecting individual tests rather than "test_{n}". setattr(caller_module, f"__{cls.__name__}", test_obj) # returns decorator class unmodified return cls return decorator pyuvm-4.0.1/pyuvm/s05_base_classes.py000066400000000000000000000360221507477334100175620ustar00rootroot00000000000000""" This file defines the UVM base classes """ from cocotb.utils import get_sim_time from pyuvm import error_classes, utility_classes from pyuvm.s08_factory_classes import uvm_factory # 5.3.1 class uvm_object(utility_classes.uvm_void): """The most basic UVM object""" # 5.3.2 def __init__(self, name=""): """ :param name: Name of the object. Default is empty string. """ assert isinstance(name, str), f"{name} is not a string it is a {type(name)}" self.set_name(name) # 5.3.3.1 def get_uvm_seeding(self): """Not implemented""" raise error_classes.UVMNotImplemented("get_uvm_seeding not implemented") # 5.3.3.2 def set_uvm_seeding(self, enable): """ Not implemented """ raise error_classes.UVMNotImplemented("set_uvm_seeding not implemented") # 5.3.3.3 def reseed(self): """Not implemented""" raise error_classes.UVMNotImplemented("reseed not implemented") # 5.3.3.4 def get_name(self): """ :return: String with name of uvm_object. Return the name of this object as passed by the constructor """ assert self._obj_name is not None, f"Internal error. {str(self)} has no name" return self._obj_name # 5.3.4.1 def set_name(self, name): """ :param name: Name of the object Set the name """ assert isinstance(name, str), "Must set the name to a string" self._obj_name = name # 5.3.4.3 def get_full_name(self): """ :return: The full path and name of the object The full name for a uvm_object is simply the name """ return self.get_name() # 5.3.4.4 def get_inst_id(self): """ :return: The python ID which fits the bill for what the ID is supposed to be. """ return id(self) # 5.3.4.5 def get_type(self): """ Not implemented because Python can implement the factory without these shenanigans. """ raise error_classes.UsePythonMethod( "Python provides better ways to do this " "so the uvm_object_wrapper is unimplemented" ) # 5.3.4.6 def get_object_type(self): """ Not implemented because Python can implement the factory without these shenanigans. """ raise error_classes.UsePythonMethod( "Python provides better ways to do this " "so the uvm_object_wrapper is unimplemented" ) # 5.3.4.7 def get_type_name(self): """ :return: Returns the type's ``__name__`` magic variable """ return type(self).__name__ # 5.3.5.1 @classmethod def create(cls, name): """ :return: new object from factory """ new_obj = uvm_factory().create_object_by_type(cls, name=name) return new_obj # 5.3.5.2 def clone(self): """ :return: A new object with the same name and data as this object. """ new = self.__class__(self.get_name()) new.copy(self) return new # 5.3.6.1 def print(self): """ Not implemented. Use __str__() and print() """ raise error_classes.UsePythonMethod( "There are better ways to do printing in Python usingprint() or str()" ) # 5.3.6.2 def sprint(self): """Not implemented. use __str__() and print()""" raise error_classes.UsePythonMethod( "There are better ways to do printing in Python" ) # 5.3.6.3 def do_print(self): """not implemented. Use __str__() and print()""" raise error_classes.UsePythonMethod( "There are better ways to do printing in Python" ) # 5.3.6.4 def convert2string(self): """ :return: The result of ``__str__()`` Override if you want something different than ``__str__()`` """ return self.__str__() # 5.3.7 def record(self): """ Not implemented. """ raise error_classes.UVMNotImplemented("Perhaps a future project?") # 5.3.7.2 def do_record(self): """ Not implemented as we are not in a simulator """ raise error_classes.UVMNotImplemented("No recording") # 5.3.8.1 def copy(self, rhs): """ :param rhs: The object to copy from :return: None Copy fields from rhs to this object using ``self.do_copy()`` """ self.do_copy(rhs) # 5.3.8.2 def do_copy(self, rhs): """ :param rhs: The object to copy from :return: None By default we copy the name. Override this function to copy the rest of the object members. """ self.set_name(rhs.get_name()) # 5.3.9.1 def compare(self, rhs): """ :param rhs: The object being compared. :returns: True if do_compare() believes the objects are the same. Compares one uvm_object to another uvm_object using the user-overridden ``do_compare()`` function. """ return self.do_compare(rhs) # 5.3.9.2 def do_compare(self, rhs): """ :param rhs: The object being compared. :returns: True if the objects are the same. Uses ``__eq__()`` to compare the objects. Override this to change the compare behavior. """ return self.__eq__(rhs) # 5.3.10.1 def pack(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.10.1 def pack_bytes(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.10.1 def pack_ints(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.10.1 def pack_longints(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.10.2 def do_pack(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.11.1 def unpack(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use pickle, json, or yaml.") # 5.3.14.1 def push_active_policy(self): """ Not implemented. """ raise error_classes.UVMNotImplemented("policies not implemented yet") # 5.3.14.2 def pop_active_policy(self): """ Not implemented. """ raise error_classes.UVMNotImplemented("policies not implemented yet") # 5.3.14.3 def get_active_policy(self): """ Not implemented. """ raise error_classes.UVMNotImplemented("policies not implemented yet") # 5.3.11.1 def unpack_bytes(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.11.1 def unpack_ints(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.11.1 def unpack_longints(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.11.2 def do_unpack(self): """ Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod("use struct, pickle, json, or yaml.") # 5.3.12 def set_local(self): """ Not implemented use Python getattr and setattr. """ raise error_classes.UsePythonMethod( "The getattr and setattr functions handle this" ) # 5.3.13.1 def do_execute_op(self, op): """ Not implemented. """ raise error_classes.UsePythonMethod("Not needed in Python") # 5.3.13 class uvm_field_op: """ We do not implement the UVM field op as this is a UVM way of providing field-based functionality that can better be implemented using Python functionality. """ def __new__(cls, *args, **kwargs): raise error_classes.UsePythonMethod( "Python has simpler ways of handling field function." ) # 5.3.14 class uvm_policy: """ The uvm_policy is used to add functionality to SystemVerilog that already exists in Python. It is not needed in pyuvm. """ def __new__(cls, *args, **kwargs): raise error_classes.UsePythonMethod( "Python has simpler ways of handling functionality provided by policies." ) # 5.4.1 class uvm_transaction(uvm_object): """ Transactions without interface to logging or waveforms. """ # 5.4.2.1 def __init__(self, name="", initiator=None): """ :param name: Object name :param initiator: component that is the initiator """ super().__init__(name) self.set_initiator(initiator) self.transaction_id = id(self) self._accept_time: int = None self._begin_time: int = None self._end_time: int = None def set_id_info(self, other): """ :param other: uvm_transaction with transaction_id :return: None Set transaction_id from other """ self.transaction_id = other.transaction_id def set_initiator(self, initiator): """ :param initiator: initiator to set :return: None 5.4.2.14 """ self._initiator = initiator def get_initiator(self): """ :return: initiator 5.4.2.15 """ return self._initiator def __not_implemented(self): raise error_classes.UVMNotImplemented( "This method is not implemented at this time." ) # 5.4.2.2 def accept_tr(self, accept_time=0): """ :param accept_time: Simulation time when the transaction is accepted IEEE 1800.2 5.4.2.2 """ if (accept_time is not None) and (accept_time != 0): self._accept_time = accept_time else: self._accept_time = get_sim_time() # TODO Call 'accept' event pool triggers self.do_accept_tr() # 5.4.2.3 def do_accept_tr(self): """ User definable method to add to ``accept_tr()`` """ pass # 5.4.2.5 def begin_tr(self, begin_time=0, parent_handle=None) -> int: """ :param begin_time: Simulation time at which the transaction is acted upon by the driver :param parent_handle: """ if (begin_time is not None) and (begin_time != 0): # begin_time must be greater than or equal to accept_time if begin_time < self._accept_time: raise error_classes.UVMFatalError( f"""begin_time : {begin_time} is less than accept_time: {self._accept_time} for the transaction : {self.get_name()} """ ) else: self._begin_time = begin_time else: self._begin_time = get_sim_time() # TODO: update recodring API calls self.do_begin_tr() # TODO Call 'begin' event pool triggers # Update return value when recording is enabled return 0 # 5.4.2.5 def do_begin_tr(self): """ User definable method """ pass # 5.4.2.6 def end_tr(self, end_time=0, free_handle=True) -> None: """ :param end_time: Simulation time at which the transaction is marked as acted upon :param free_handle: :return: None """ if end_time is not None and end_time != 0: # end_time must be greater than or equal to # accept_time and begin_time if end_time < self._accept_time: raise error_classes.UVMFatalError( """end_time : {end_time} is less than accept_time : {self._accept_time} for the transaction : {self.get_name()}""" ) elif end_time < self._begin_time: raise error_classes.UVMFatalError( """end_time : {end_time} is less than accept_time : {self._begin_time} for the transaction : {self.get_name()}""" ) else: self._end_time = end_time else: self._end_time = get_sim_time() # TODO: update recodring API calls self.do_end_tr() # TODO Call 'end' event pool triggers # Update return value when recording is enabled # 5.4.2.7 def do_end_tr(self): """ Not implemented """ pass # 5.4.2.8 def get_tr_handle(self): """ Not implemented """ self.__not_implemented() # 5.4.2.9 def enable_recording(self): """ Not implemented """ self.__not_implemented() # 5.4.2.10 def disable_recording(self): """ Not implemented """ self.__not_implemented() # 5.4.2.11 def is_recording_enabled(self): """ Not implemented """ self.__not_implemented() # 5.4.2.12 def is_active(self): """ Not implemented """ self.__not_implemented() # 5.4.2.13 def get_event_pool(self): """ Not implemented """ self.__not_implemented() # 5.4.2.16 def get_accept_time(self) -> int: """ :return: Accept time of transaction """ return self._accept_time def get_begin_time(self) -> int: """ :return: Begin time of transaction """ return self._begin_time def get_end_time(self) -> int: """ :return: End time of transaction """ return self._end_time # 5.4.2.17 def set_transaction_id(self, txn_id): """ :param txn_id: Transaction ID Sets transaction's transaction_id """ assert isinstance(txn_id, int), "Transaction ID must be an integer." self.transaction_id = txn_id # 5.4.2.18 def get_transaction_id(self): """ :return: Transaction ID Returns transaction_id """ if self.transaction_id is None: return id(self) else: return self.transaction_id pyuvm-4.0.1/pyuvm/s06_reporting_classes.py000066400000000000000000000115261507477334100206640ustar00rootroot00000000000000# pyuvm uses the Python logging system to do reporting. # Still, we need this base class to be true to the hierarchy. # Every instance of a child class has its own logger. # # There may be a need to implement uvm_info, uvm_error, # uvm_warning, and uvm_fatal, but it would be best to # first see how the native Python logging system does the job. import logging import sys from pyuvm._utils import cocotb_version_info from pyuvm.s05_base_classes import uvm_object if cocotb_version_info < (2, 0): from cocotb.log import SimColourLogFormatter, SimLogFormatter, SimTimeContextFilter from cocotb.utils import want_color_output if want_color_output(): FormatterBase = SimColourLogFormatter else: FormatterBase = SimLogFormatter else: from cocotb.logging import SimLogFormatter, SimTimeContextFilter FormatterBase = SimLogFormatter from logging import ( # noqa: F401, E501 CRITICAL, DEBUG, ERROR, INFO, NOTSET, WARNING, NullHandler, ) class PyuvmFormatter(FormatterBase): def __init__(self, full_name): """ :param full_name: The full name of the object """ self.full_name = full_name super().__init__() def format(self, record): """ :param record: The log record """ msg_temp = record.msg new_msg = f"[{self.full_name}]: {record.msg}" record.msg = new_msg name_temp = record.name record.name = f"{record.pathname}({record.lineno})" formatted_msg = super().format(record) record.msg = msg_temp record.name = name_temp return formatted_msg # 6.2.1 class uvm_report_object(uvm_object): __default_logging_level = logging.INFO """ The basis of all classes that can report """ def __init__(self, name): """ :param name: The name of the object :returns: None """ super().__init__(name) uvm_root_logger = logging.getLogger("uvm") # Every object gets its own logger logger_name = self.get_initial_logger_name() self.logger = uvm_root_logger.getChild(logger_name) self.logger.setLevel(level=uvm_report_object.get_default_logging_level()) # We are not sending log messages up the hierarchy self.logger.propagate = False self._streaming_handler = logging.StreamHandler(sys.stdout) self._streaming_handler.addFilter(SimTimeContextFilter()) # Don't let the handler interfere with logger level self._streaming_handler.setLevel(logging.NOTSET) # Make log messages look like UVM messages self._uvm_formatter = PyuvmFormatter(self.get_full_name()) self.add_logging_handler(self._streaming_handler) def get_initial_logger_name(self): """ :returns: The name of the initial logger Override this method if you want to change the way the logger name is generated. The default looks like this: .. code-block:: python return self.get_full_name() + str(id(self)) """ return self.get_full_name() + str(id(self)) @staticmethod def set_default_logging_level(default_logging_level): """ :param default_logging_level: The default logging level :returns: None """ uvm_report_object.__default_logging_level = default_logging_level @staticmethod def get_default_logging_level(): """ :returns: The default logging level """ return uvm_report_object.__default_logging_level def set_logging_level(self, logging_level): """ :param logging_level: The logging level :returns: None """ self.logger.setLevel(logging_level) def add_logging_handler(self, handler): """ :param handler: The logging handler :returns: None """ assert isinstance(handler, logging.Handler), ( f"You must pass a logging.Handler not {type(handler)}" ) if handler.formatter is None: handler.addFilter(SimTimeContextFilter()) handler.setFormatter(self._uvm_formatter) self.logger.addHandler(handler) def remove_logging_handler(self, handler): """ :param handler: The logging handler to remove :returns: None """ assert isinstance(handler, logging.Handler), ( f"You must pass a logging.Handler not {type(handler)}" ) self.logger.removeHandler(handler) def remove_streaming_handler(self): """ :returns: None Removes the streaming handler """ self.logger.removeHandler(self._streaming_handler) def disable_logging(self): """ :returns: None Disables logging """ self.remove_streaming_handler() self.add_logging_handler(NullHandler()) pyuvm-4.0.1/pyuvm/s08_factory_classes.py000066400000000000000000000461621507477334100203300ustar00rootroot00000000000000import fnmatch import logging from pyuvm import error_classes, utility_classes # pyuvm refactors the factory, taking advantage Python's # superiority in terms of OOP features and lack of types. # Implementing section 8 in the IEEE Specification # # The IEEE spec assumes that UVM is being written in SystemVerilog, # a language that does not allow you to control how classes get defined. # Python has more control in this area. With Python we can set things up # so that any class that extends uvm_void automatically gets registered # into the factory. # Therefore there is no need for 8.2.2 the type_id, that # is a SystemVerilog artifact as is 8.2.3 # There is also no need for 8.2.4, the uvm_object_registry. # The FactoryMeta class causes all classes from uvm_void # down to automatically register themselves with the factory # by copying themselves into a dict. # However there is a need to provide the methods in 8. # 8.3.1.1 class uvm_factory(metaclass=utility_classes.Singleton): """ The uvm_factory is a singleton that delivers all UVM factory functions. """ # 8.3.1.2.1 get # There is no get() method in Python singletons. Instead you instantiate # the singleton as normal and you automatically get the singleton. # 8.3.1.3 register # Not implemented # There is no register in pyuvm. Instead the factory builds its class # database through introspection. def __init__(self): self.fd = utility_classes.FactoryData() self.logger = logging.getLogger("Factory") self.debug_level = 1 def clear_all(self): """ Clear all the classes and overrides from the factory """ self.fd.clear_classes() self.clear_overrides() def clear_overrides(self): """ Clear all the overrides from the factory """ self.fd.clear_overrides() def __set_override(self, original, override, path=None): if original not in self.fd.overrides: self.fd.overrides[original] = utility_classes.Override() self.fd.overrides[original].add(override, path) # 8.3.1.3 def set_inst_override_by_type(self, original_type, override_type, full_inst_path): """ :param original_type: The original type being overridden :param override_type: The overriding type :param full_inst_path: The inst where this happens :return: None Override an instance with a new type if original type is at that path """ # The intention here is to only override when a type of original_type # is at the full_inst_path provided. If someone stores a different # class at full_inst_path then the override will not happen. # # # We capture this by storing the original and override types as a # tuple at the full inst path. Later we'll retrieve the tuple # and check the type of the object at the full_inst_path. # # instance_overrides is an OrderedDict, so we will check # the paths in the order they are registered later. assert issubclass(original_type, utility_classes.uvm_void), ( "You tried to override a non-uvm_void class" ) assert issubclass(override_type, utility_classes.uvm_void), ( "You tried to use a non-uvm_void class as an override" ) self.__set_override(original_type, override_type, full_inst_path) # 8.3.1.4.1 def set_inst_override_by_name( self, original_type_name, override_type_name, full_inst_path ): """ :param original_type_name: the name of type being replaced :param override_type_name: the name of the substitute type :param full_inst_path: The path to the instance :return: None Override a specific instance using strings that contain the names of the types. """ # Here we use the names of classes instead of the classes. # The original_name doesn't need to be the name of a class, # it can be an arbitrary string. The override_name must # be the name of a class. # # Later we will retrieve this by searching through the # keys for a match to a path and then checking that # the name given in the search matches the original_name assert isinstance(full_inst_path, str), "The inst_path must be a string" assert isinstance(original_type_name, str), "Original_name must be a string" assert isinstance(override_type_name, str), "Override_name must be a string" try: override_type = self.fd.classes[override_type_name] except KeyError: raise error_classes.UVMFactoryError( f"{override_type_name}" + " has not been defined." ) # Set type override by name can use an arbitrary string as a key # instead of a type. Fortunately Python dicts don't care about # the type of the key. try: original_type = self.fd.classes[original_type_name] except KeyError: original_type = original_type_name self.__set_override(original_type, override_type, full_inst_path) # 8.3.1.4.2 def set_type_override_by_type(self, original_type, override_type, replace=True): """ :param original_type: The original type to be overridden :param override_type: The new type that will override it :param replace: If the override exists, only replace it if this is True :return: None Override one type with another type globally """ assert issubclass(original_type, utility_classes.uvm_void), ( "You tried to override a non-uvm_void class" ) assert issubclass(override_type, utility_classes.uvm_void), ( "You tried to use a non-uvm_void class as an override" ) if (original_type not in self.fd.overrides) or replace: self.__set_override(original_type, override_type) # 8.3.1.4.2 def set_type_override_by_name( self, original_type_name, override_type_name, replace=True ): """ :param original_type_name: The name of the type to be overridden or an arbitrary string. :param override_type_name: The name of the overriding type. It must have been declared. :param replace: If the override already exists only replace if this is True Override one type with another type globally using strings containing the type names. """ assert isinstance(original_type_name, str), "Original_name must be a string" assert isinstance(override_type_name, str), "Override_name must be a string" try: override_type = self.fd.classes[override_type_name] except KeyError: raise error_classes.UVMFactoryError( f"{override_type_name}" + " has not been defined." ) # Set type override by name can use an arbitrary string as a key # instead of a type # Fortunately Python dicts don't care about the type of the key. try: original_type = self.fd.classes[original_type_name] except KeyError: original_type = original_type_name if (original_type not in self.fd.overrides) or replace: self.__set_override(original_type, override_type) def __find_override(self, requested_type, parent_inst_path="", name=""): if not isinstance(requested_type, str): assert issubclass(requested_type, utility_classes.uvm_void), ( f"You must create uvm_void descendants not {requested_type}" ) if parent_inst_path == "": inst_path = name elif name != "": inst_path = parent_inst_path + "." + name else: inst_path = parent_inst_path new_cls = self.fd.find_override(requested_type, inst_path) if isinstance(new_cls, str): self.logger.error( f'"{new_cls}" is not declared and is not an override string' ) return None else: return new_cls def create_object_by_type(self, requested_type, parent_inst_path="", name=""): """ :param requested_type: The type that we request but that can be overridden :param parent_inst_path: The get_full_name path of the parent :param name: The name of the instance requested_type("name") :raises: UVMFactoryError if the type is not in the factory :return: Type that is child of uvm_object. 8.3.1.5 Creation If the type is is not in the factory we raise UVMFactoryError """ new_type = self.__find_override(requested_type, parent_inst_path, name) if new_type is None: raise error_classes.UVMFactoryError( f"{requested_type} not in uvm_factory()" ) return new_type(name) # 8.3.1.5 def create_object_by_name(self, requested_type_name, parent_inst_path="", name=""): """ :param requested_type_name: the type that could be overridden :param parent_inst_path: A path if we are checking for inst overrides :param name: The name of the new object. :raises: UVMFactoryError if the type is not in the factory :return: A uvm_object with the name given Create an object using a string to define its uvm_object type. """ try: requested_type = utility_classes.FactoryData().classes[requested_type_name] # noqa except KeyError: requested_type = requested_type_name new_obj = self.create_object_by_type(requested_type, parent_inst_path, name) return new_obj # 8.3.1.5 def create_component_by_type( self, requested_type, parent_inst_path="", name="", parent=None ): """ :param requested_type: Type type to be overridden :param parent_inst_path: The inst path if we are looking for inst overrides :param name: Concatenated with parent_inst_path if it exists for inst overrides :param parent: The parent component :raises: UVMFactoryError if the type is not in the factory :return: a uvm_component with the name an parent given. Create a component of the requested uvm_component type. If the type is is not in the factory we raise UVMFactoryError """ if name is None: raise error_classes.UVMFactoryError( "Parameter name must be specified in function call." ) new_type = self.__find_override(requested_type, parent_inst_path, name) if new_type is None: raise error_classes.UVMFactoryError( f"{requested_type} not in uvm_factory()" ) new_comp = new_type(name, parent) return new_comp # 8.3.1.5 def create_component_by_name( self, requested_type_name, parent_inst_path="", name="", parent=None ): """ Create a components using the name of the requested uvm_component type :param requested_type_name: the type that could be overridden :param parent_inst_path: A path if we are checking for inst overrides :param name: The name of the new object. :param parent: The component's parent component :raises: UVMFactoryError if the type is not in the factory :return: A uvm_object with the name given """ if name is None: raise error_classes.UVMFactoryError( "Parameter name must be specified in create_component_by_name" ) try: requested_type = utility_classes.FactoryData().classes[requested_type_name] # noqa except KeyError: requested_type = requested_type_name new_obj = self.create_component_by_type( requested_type, parent_inst_path, name, parent ) return new_obj # 8.3.1.6.1 def set_type_alias(self, alias_type_name, original_type): """ :param alias_type_name:A string that will reference the original type :param original_type:The original type toe be referenced :raises: UVMNotImplemented Not implemented as it does not seem to exist in the SystemVerilog UVM :return:None """ # This method does not seem to be implemented in SystemVerilog # so I'm skipping it now. raise error_classes.UVMNotImplemented( "set_type_alias is not implemented in SystemVerilog" ) # 8.3.1.6.2 def set_inst_alias(self, alias_type_name, original_type, full_inst_path): """ :param alias_type_name:A string that will reference the original type :param original_type:The original type toe be referenced :param full_inst_path: The instance path where this alias is active :raises: UVMNotImplemented Not implemented as it does not seem to exist in SystemVerilog UVM :return:None """ # This method does not seem to be implemented in SystemVerilog # so I'm skipping it now. raise error_classes.UVMNotImplemented( "set_type_inst is not implemented in SystemVerilog" ) # 8.3.1.7 Introspection # 8.3.1.7.1 def find_override_by_type(self, requested_type, full_inst_path): """ :param requested_type: The type whose override you want :param full_inst_path: The inst path where one looks :raises: UVMFactoryError if the type is not in the factory :return: class object Given a type and instance path, return the override class object. """ override = self.__find_override(requested_type, full_inst_path) return override # 8.3.1.7.1 def find_override_by_name(self, requested_type_name, full_inst_path): """ :param requested_type_name: :param full_inst_path: :raises: UVMFactoryError if the type is not in the factory :return: class object Given a path and the name of a class return its overriding class object """ assert isinstance(requested_type_name, str), ( f"requested_type_name must be a string not a {type(requested_type_name)}" ) # noqa requested_type = None # Appeasing the linter try: requested_type = self.fd.classes[requested_type_name] except KeyError: error_classes.UVMFactoryError( f"{requested_type_name} is not a defined class name" ) return self.find_override_by_type(requested_type, full_inst_path) # 8.3.1.7.2 def find_wrapper_by_name(self): """ :raises: UVMNotImplemented There are no wrappers in **pyuvm** so this is not implemented. """ raise error_classes.UVMNotImplemented( "There are no wrappers in pyuvm. So find_wrapper_by_name is not implemented" ) # 8.3.1.7.3 def is_type_name_registered(self, type_name): """ :param type_name: string that is name of a type :return: boolean True if type is registered Checks that a type of this name is registered with the factory. """ assert isinstance(type_name, str), ( "is_type_name_registered() takes a" " string as its argument not {type(type_name)}" ) return type_name in self.fd.classes # 8.3.1.7.4 def is_type_registered(self, uvm_type): """ :param uvm_type: The type to be checked :return: boolean True if type is registered Checks that a type is registered. The argument is named "obj" in the spec, but that name is ridiculous and confusing. """ assert issubclass(uvm_type, utility_classes.uvm_void), ( "is_type_registered() takes a subclass of uvm_void " "as its argument not {type(uvm_type)}" ) return uvm_type in self.fd.classes.values() @property def debug_level(self): """ * uvm_factory().debug_level = 0 : overrides * uvm_factory().debug_level = 1 : user defined types + above * uvm_factory().debug_level = 2 : uvm_* types + above """ return self.__debug_level @debug_level.setter def debug_level(self, debug_level): """ * uvm_factory().debug_level = 0 : overrides * uvm_factory().debug_level = 1 : user defined types + above * uvm_factory().debug_level = 2 : uvm_* types + above """ assert 0 <= debug_level <= 2, "uvm_factory().all_type must be 0, 1, or 2" self.__debug_level = debug_level def __str__(self): """ Returns the Pythonic string Set uvm_factory().debug_level to a value to control the string. The default is 1 uvm_factory().debug_level = 0 : overrides uvm_factory().debug_level = 1 : user defined types + above uvm_factory().debug_level = 2 : uvm_* types + above :return: String containing factory data """ factory_str = "--- overrides " if self.debug_level != 0: factory_str += "+ user defined types " if self.debug_level == 2: factory_str += "+ uvm_* types " factory_str += "---\n\n" if len(self.fd.overrides) > 0: factory_str += "Overrides:\n" for inst in self.fd.overrides: factory_str += ( f"{inst.__name__:25}" + ": " + str(self.fd.overrides[inst]) ) # noqa factory_str += "\n" # Need to add 1 and 2 user_list = [ self.fd.classes[cls].__name__ for cls in self.fd.classes if not fnmatch.fnmatch(self.fd.classes[cls].__name__, "uvm_*") ] # noqa uvm_list = [ self.fd.classes[cls].__name__ for cls in self.fd.classes if fnmatch.fnmatch(self.fd.classes[cls].__name__, "uvm_*") ] if self.debug_level > 0: factory_str += "\n" + "-" * 25 + "\nUser Defined Types:\n" factory_str += "\n".join(user_list) if self.debug_level == 2: factory_str += "\n" + "-" * 25 + "\nUVM Types:\n" factory_str += "\n".join(uvm_list) return factory_str # 8.3.1.7.5 def print(self, debug_level=1): """ :param debug_level: * ``debug_level`` = 0 : overrides * ``debug_level`` = 1 : user defined types + above ( default) * ``debug_level`` = 2 : uvm_* types + above :return: None Prints the factory data using debug_level to control the amount of output. The uvm_factory().debug_level variable can control this for __str__() """ saved_debug_level = self.debug_level # Avoiding a side effect self.debug_level = debug_level print(self) self.debug_level = saved_debug_level # 8.3.1.8 Usage # All the elements of usage have been implemented in pyuvm # The biggest difference is that all uvm_void classes get registered # with the factory automatically. # 8.3.2 # Not implemented. There is no need for a uvm_object_wrapper # in Python. The user will never notice # the difference pyuvm-4.0.1/pyuvm/s09_phasing.py000066400000000000000000000111121507477334100165610ustar00rootroot00000000000000import cocotb from pyuvm import error_classes from pyuvm.s05_base_classes import uvm_object # 9.1 # # This is a dramatically simplified version of UVM phasing. We are not going # to deal with the complexities of custom phasing as described in IEEE 1800.2 # # # So this system simply traverses the common phases, calling the appropriate # method in each component. # # Much of the work in the SV phasing code has to do with handling the passage # of time. We use cocotb for our simulation interaction, so that all goes away. # # Also, the generalized phasing system is rarely used and so that # is left as an exercise for future developers. Instead we have a simple # topdown and bottom up traversal of calling methods in component # classes based on the phase name. # # We're not doing schedules or domains. We're just creating a list of classes # and traversing them in order. The order it dependent upon whether they # are topdown or bottom up phases. # 9.3.1.2 Class declaration class uvm_phase(uvm_object): # Strips the "uvm_" from this class's name and uses the remainder # to get a function call out of the component and execute it. # 'uvm_run_phase' becomes 'run_phase' and is called as 'run_phase()' @classmethod def execute(cls, comp): """ :param comp: The component whose turn it is to execute """ method_name = cls.__name__[4:] try: method = getattr(comp, method_name) except AttributeError: raise error_classes.UVMBadPhase( f"{comp.get_name()} is missing {method_name} function" ) method() def __str__(self): return self.__name__[4:] class uvm_topdown_phase(uvm_phase): """ Runs phases from the top down. """ @classmethod def traverse(cls, comp): """ :param comp: The component whose hierarchy will be traversed Given a component, we traverse the component tree top to bottom calling the phase functions as we go """ cls.execute(comp) # first we execute this node then its children for child in comp.get_children(): cls.traverse(child) class uvm_bottomup_phase(uvm_phase): """ Runs the phases from bottom up. """ @classmethod def traverse(cls, comp): """ :param comp: The component whose hierarchy will be traversed Given a component, we traverse the component tree bottom to top calling the phase functions as we go """ for child in comp.get_children(): cls.traverse(child) cls.execute(comp) class uvm_threaded_execute_phase(uvm_phase): """ This phase launches the phase function in a thread and returns the thread to the caller. The caller can then join all the threads. """ @classmethod def execute(cls, comp): phase_name = cls.__name__ assert phase_name.startswith("uvm_"), ( "We only support phases whose names start with uvm_" ) method_name = cls.__name__[4:] try: method = getattr(comp, method_name) except AttributeError: raise error_classes.UVMBadPhase( f"{comp.get_name()} is missing {method_name} function" ) cocotb.start_soon(method()) # 9.8 Predefined Phases # 9.8.1 Common Phases # The common phases are described in the order of their execution. # 9.8.1.1 class uvm_build_phase(uvm_topdown_phase): ... # 9.8.1.2 class uvm_connect_phase(uvm_bottomup_phase): ... # 9.8.1.3 class uvm_end_of_elaboration_phase(uvm_topdown_phase): ... # 9.8.1.4 class uvm_start_of_simulation_phase(uvm_topdown_phase): ... # 9.8.1.5 class uvm_run_phase(uvm_threaded_execute_phase, uvm_bottomup_phase): ... # 9.8.1.6 class uvm_extract_phase(uvm_topdown_phase): ... # 9.8.1.7 class uvm_check_phase(uvm_topdown_phase): ... # 9.8.1.8 class uvm_report_phase(uvm_topdown_phase): ... # 9.8.1.9 class uvm_final_phase(uvm_topdown_phase): ... # 9.8.2 # UVM run-time phases are left as an exercise for an enterprising soul # I cannot imagine why anyone would implement this. # One could add phases by simply extending uvm_topdown_phase # or uvm_bottom_up phase with a new phase named 'uvm_my_phase' and adding # the my_phase() method to a uvm component with setattr. Then insert the new # phase into the list of phases to be executed below: uvm_common_phases = [ uvm_build_phase, uvm_connect_phase, uvm_end_of_elaboration_phase, uvm_start_of_simulation_phase, uvm_run_phase, uvm_extract_phase, uvm_check_phase, uvm_report_phase, uvm_final_phase, ] pyuvm-4.0.1/pyuvm/s12_uvm_tlm_interfaces.py000066400000000000000000000716111507477334100210220ustar00rootroot00000000000000# The UVM TLM class hierarchy wraps the behavior of objects # (usually cocotb.Queues) in a set of classes and objects. # # The UVM LRM calls for three levels of classes to # implement TLM: ports, exports, and imp: # (from 12.2.1) # # * ports---Instantiated in components that require or use the associated # interface to initiate transaction requests. # # * exports---Instantiated by components that forward an implementation of # the methods defined in the associated interface. # # * imps---Instantiated by components that provide an implementation of or # directly implement the methods defined. # # These three levels are unnecessary for Python because Python has # multiple inheritances and duck typing. You don't need to define # different ports for each type being transferred. # # Ports have a data member named an export that implements the port # functionality. uvm_put_port.put() calls its export.put(). Ports get # their various flavors through multiple inheritance. from cocotb.queue import QueueEmpty, QueueFull from pyuvm.error_classes import UVMTLMConnectionError from pyuvm.s13_uvm_component import uvm_component from pyuvm.utility_classes import FIFO_DEBUG, UVMQueue # 12.2.2 # This section describes a variety of classes without # giving each one its own section using * to represent # a variety of implementations (get, put, blocking_put, etc) # Python provides multiple inheritance and we'll use that below # to smooth implementation. Python does not require that we # repeat the __init__ for classes that do not change the # __init__ functionality. # 12.2.5 # Port Classes # # The following port classes can be connected to "export" classes. # They check that the export is of the correct type. # # uvm_port_base adds the correct methods to this class # rather than reference them because Python allows # you to assign functions to objects dynamically. # We use these classes to check the connect phase # to avoid illegal connections # uvm_export_base provides the provided_to # associative array. class uvm_export_base(uvm_component): def __init__(self, name, parent): super().__init__(name, parent) self.provided_to = {} class uvm_port_base(uvm_export_base): """ A ``uvm_port_base`` is a uvm_component with a ``connect()`` function. The ``connect`` function creates an ``__export`` data member that implements the put/get,etc methods. We'll build functionality from ``uvm_port_base`` to create the other combinations of ports through multiple inheritance. **pyuvm** will make extensive use of Pythons "ask forgiveness" policy If you try to use the wrong method for the port you created then you'll get a exception, maybe a missing attribute exception, though we could catch that one and deliver a more useful message. Unlike the SV implementation of UVM we return results from get and peek as function call returns. This is more pythonic. """ # This is the list of all TLM functions. Each port class # uses this to create a list of methods that an export # must support. __tlm_method_list = [ "put", "get", "peek", "try_put", "try_get", "try_peek", "can_put", "can_get", "can_peek", "transport", "nb_transport", "write", "put_req", "put_response", "get_next_item", "item_done", "get_response", ] def __init__(self, name, parent): super().__init__(name, parent) self.connected_to = {} self.export = None self.needed_methods = [] # Compare the list of all tlm methods to the # methods in this class to create a list of # needed for method in self.__tlm_method_list: if hasattr(self, method): self.needed_methods.append(method) def _check_export(self, export): """Check that the export implements needed methods""" if not isinstance(export, uvm_export_base): raise UVMTLMConnectionError( f"{export} must be a subclass of uvm_export_base" ) for needed in self.needed_methods: if not hasattr(export, needed): raise UVMTLMConnectionError( f"{export} must implement '{needed}()'" f" to connect to a {self.__class__}" ) def connect(self, export): """ :param export: The export that has the functions :raises: UVMTLMConnectionError if there is a connect error :return: None Attach this port to the associated export. """ self._check_export(export) try: self.export = export self.connected_to[export.get_full_name()] = export export.provided_to[self.get_full_name()] = self except KeyError: raise UVMTLMConnectionError( f"Error connecting {self.get_name()} using {export}" ) # put # 12.2.5.1 class uvm_blocking_put_port(uvm_port_base): """ Access the blocking put interfaces """ # 12.2.4.2.1 async def put(self, datum): """ :param datum: Datum to put :raises: UVMTLMConnectionError if export is missing :return: None put the datum """ try: await self.export.put(datum) except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) # 12.2.5.1 class uvm_nonblocking_put_port(uvm_port_base): """ Access the non_blocking put interface """ # 12.2.4.2.4 def try_put(self, data): """ :param data: data to deliver :raises: UVMTLMConnectionError if export is missing :return: boolean True = success Tries to put data on a port, but if the port is full it returns False """ try: success = self.export.try_put(data) return success except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export " f"in {self.get_full_name()}. Did you connect it?" ) # 12.2.4.2.5 def can_put(self): """ Returns true if there is room for data to be put on the port :return: True if there is room to put """ try: can_do_it = self.export.can_put() return can_do_it except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) # 12.2.5.1 # The put port delivers blocking puts and gets. # Here is the multiple inheritance that SV lacked class uvm_put_port(uvm_blocking_put_port, uvm_nonblocking_put_port): ... # 12.2.5.1 class uvm_blocking_get_port(uvm_port_base): """ Access the blocking get export methods """ # 12.2.4.2.2 async def get(self): """ :raises: UVMTLMConnectionError if export is missing :return: data A blocking get that returns the data got """ try: data = await self.export.get() return data except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in " f"{self.get_full_name()}. Did you connect it?" ) # 12.2.5.1 class uvm_nonblocking_get_port(uvm_port_base): """ Access the non_blocking methods in export """ def try_get(self): """ :raises: UVMTLMConnectionError if export is missing :return: (success, data) 12.2.4.2.6 Returns a tuple containing success and the data This is different than SV UVM that returns the data through the argument list. :return: (success, data) """ try: (success, data) = self.export.try_get() except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) return success, data # 12.2.4.2.7 def can_get(self): """ :raises: UVMTLMConnectionError if export is missing :return: bool Returns true if there is data to get """ try: can = self.export.can_get() except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) return can class uvm_get_port(uvm_blocking_get_port, uvm_nonblocking_get_port): ... # # 12.2.5.1 class uvm_blocking_peek_port(uvm_port_base): """ Provides access to the peek methods """ # 12.2.4.2.3 async def peek(self): """ :raises: UVMTLMConnectionError if export is missing :return: datum A blocking peek that returns data without consuming it. """ try: datum = await self.export.peek() return datum except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) # 12.2.5.1 class uvm_nonblocking_peek_port(uvm_port_base): """ Try a peek """ # 12.2.4.2.8 def try_peek(self): """ :raises: UVMTLMConnectionError if export is missing :return: (success, data) Tries to peek for data and returns a tuple with success and the data """ try: success, data = self.export.try_peek() except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) return success, data # 12.2.4.2.9 def can_peek(self): """ :raises: UVMTLMConnectionError if export is missing :return: True if can peek Checks if peeking will be successful """ can = self.export.can_peek() return can class uvm_peek_port(uvm_blocking_peek_port, uvm_nonblocking_peek_port): ... # get_peek class uvm_blocking_get_peek_port(uvm_blocking_get_port, uvm_blocking_peek_port): ... class uvm_nonblocking_get_peek_port( uvm_nonblocking_get_port, uvm_nonblocking_peek_port ): ... class uvm_get_peek_port(uvm_blocking_get_peek_port, uvm_nonblocking_get_peek_port): ... class uvm_blocking_transport_port(uvm_port_base): def __init__(self, name, parent): super().__init__(name, parent) async def transport(self, put_data): """ Puts data and blocks if there is no room, then blocks if there is no data to get and gets data. :param put_data: data to send :raises: UVMTLMConnectionError if export is missing :return: data received """ try: get_data = await self.export.transport(put_data) except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) return get_data class uvm_nonblocking_transport_port(uvm_port_base): def __init__(self, name, parent): super().__init__(name, parent) def nb_transport(self, put_data): """ Non-blocking transport. Returns a tuple with success if the transport was successful and the data could be returned :param put_data: data to send :raises: UVMTLMConnectionError if export is missing :return: (success, data) """ try: success, get_data = self.export.nb_transport(put_data) except AttributeError: raise UVMTLMConnectionError( "Missing or wrong export in" f" {self.get_full_name()}. Did you connect it?" ) return success, get_data class uvm_transport_port( uvm_blocking_transport_port, uvm_nonblocking_transport_port ): ... # master class uvm_blocking_master_port(uvm_blocking_put_port, uvm_blocking_get_peek_port): ... class uvm_nonblocking_master_port( uvm_nonblocking_put_port, uvm_nonblocking_get_peek_port ): ... class uvm_master_port(uvm_blocking_master_port, uvm_nonblocking_master_port): ... class uvm_blocking_slave_port(uvm_blocking_put_port, uvm_blocking_get_peek_port): ... class uvm_nonblocking_slave_port( uvm_nonblocking_get_peek_port, uvm_nonblocking_put_port ): ... class uvm_slave_port(uvm_nonblocking_slave_port, uvm_blocking_slave_port): ... class uvm_analysis_port(uvm_port_base): def __init__(self, name, parent): super().__init__(name, parent) self.subscribers = [] # 12.2.8.1 def write(self, datum): """ Write to all connected analysis ports. This is a broadcast. Returns regardless of whether there are any subscribers. :param datum: data to send :raises: UVMTLMConnectionError if export is missing :return: None """ for export in self.subscribers: if not hasattr(export, "write"): raise UVMTLMConnectionError( f"No write() method in {export}. Did you connect it?" ) export.write(datum) def connect(self, export): self._check_export(export) self.subscribers.append(export) class uvm_nonblocking_put_export(uvm_export_base): ... class uvm_blocking_put_export(uvm_export_base): ... class uvm_put_export(uvm_nonblocking_put_export, uvm_blocking_put_export): ... # get class uvm_nonblocking_get_export(uvm_export_base): ... class uvm_blocking_get_export(uvm_export_base): ... class uvm_get_export(uvm_blocking_get_export, uvm_nonblocking_get_export): ... # peek class uvm_nonblocking_peek_export(uvm_export_base): ... class uvm_blocking_peek_export(uvm_export_base): ... class uvm_peek_export(uvm_nonblocking_peek_export, uvm_blocking_peek_export): ... # get_peek class uvm_blocking_get_peek_export(uvm_export_base): ... class uvm_nonblocking_get_peek_export(uvm_export_base): ... class uvm_get_peek_export( uvm_nonblocking_get_peek_export, uvm_blocking_get_peek_export ): ... # transport class uvm_blocking_transport_export(uvm_export_base): ... class uvm_nonblocking_transport_export(uvm_export_base): ... class uvm_transport_export( uvm_nonblocking_transport_export, uvm_blocking_transport_export ): ... # master class uvm_blocking_master_export( uvm_blocking_put_export, uvm_blocking_get_peek_export ): ... class uvm_nonblocking_master_export( uvm_blocking_peek_export, uvm_nonblocking_get_peek_export ): ... class uvm_master_export(uvm_blocking_master_export, uvm_nonblocking_master_export): ... # slave class uvm_blocking_slave_export( uvm_blocking_put_export, uvm_blocking_get_peek_export ): ... class uvm_nonblocking_slave_export( uvm_nonblocking_put_export, uvm_nonblocking_get_peek_export ): ... class uvm_slave_export(uvm_blocking_slave_export, uvm_nonblocking_slave_export): ... class uvm_analysis_export(uvm_export_base): ... # 12.2.8 FIFO Classes # # These classes provide synchronization control between # threads using the ``cocotb.Queue`` class. # # One note. The RLM has only 12.2.8.1.3 and 12.2.8.1.4, put_export # and get_peek_export, but the UVM code has all the variants # of exports. # # The SystemVerilog UVM relies upon static type checking # and polymorphism to make sure that users connect the # correct ports to these exports, but we don't have # static checking, so we implement classes for all the # port variants. This creates the runtime assertion checking # that we need. class uvm_QueueAccessor: def __init__(self, name, parent, uvm_queue, ap): super().__init__(name, parent) assert isinstance(uvm_queue, UVMQueue), ( "Tried to pass a non-UVMQueue to QueueAccessor constructor" ) self.queue = uvm_queue self.ap = ap class uvm_tlm_fifo_base(uvm_component): """ Declares and instantiate the exports needed to communicate through the Queue. """ class uvm_BlockingPutExport(uvm_QueueAccessor, uvm_blocking_put_export): async def put(self, item): """ :param item: item to put :return: None A coroutine that blocks if the FIFO is full. """ self.logger.log(FIFO_DEBUG, f"blocking put: {item}") await self.queue.put(item) self.logger.log(FIFO_DEBUG, f"success put {item}") self.ap.write(item) # 12.2.8.1.3 class uvm_NonBlockingPutExport(uvm_QueueAccessor, uvm_nonblocking_put_export): def can_put(self): """ :return: True if can put """ return not self.queue.full() def try_put(self, item): """ :param item: item to put :raises: QueueFull if the queue is full :return: True if successful The ``try_put`` is implemented with an exception rather than returning a boolean. """ try: self.queue.put_nowait(item) self.ap.write(item) return True except QueueFull: return False class uvm_PutExport(uvm_BlockingPutExport, uvm_NonBlockingPutExport): ... class uvm_BlockingGetExport(uvm_QueueAccessor, uvm_blocking_get_export): async def get(self): """ :return: item A coroutine that blocks if the FIFO is empty """ self.logger.log(FIFO_DEBUG, "Attempting blocking get") item = await self.queue.get() self.logger.log(FIFO_DEBUG, f"got {item}") self.ap.write(item) return item class uvm_NonBlockingGetExport(uvm_QueueAccessor, uvm_nonblocking_get_export): def can_get(self): """ :return: True if can get """ get_ok = not self.queue.empty() return get_ok def try_get(self): """ :return: (success, item) """ try: item = self.queue.get_nowait() self.ap.write(item) return True, item except QueueEmpty: return False, None class uvm_GetExport(uvm_BlockingGetExport, uvm_NonBlockingGetExport): ... class uvm_BlockingPeekExport(uvm_QueueAccessor, uvm_blocking_peek_export): async def peek(self): """ :return: item A coroutine that blocks if the FIFO is empty """ self.logger.log(FIFO_DEBUG, "Attempting blocking peek") peek_data = await self.queue.peek() self.logger.log(FIFO_DEBUG, f"peeked at {peek_data}") return peek_data class uvm_NonBlockingPeekExport(uvm_QueueAccessor, uvm_nonblocking_peek_export): def can_peek(self): """ :return: True if can peek """ return not self.queue.empty() def try_peek(self): """ :return: (success, item) """ try: datum = self.queue.peek_nowait() return True, datum except QueueEmpty: return False, None class uvm_PeekExport(uvm_BlockingPeekExport, uvm_NonBlockingPeekExport): ... class uvm_BlockingGetPeekExport(uvm_BlockingGetExport, uvm_BlockingPeekExport): ... class uvm_NonBlockingGetPeekExport( uvm_NonBlockingGetExport, uvm_NonBlockingPeekExport ): ... # 12.2.8.1.4 class uvm_GetPeekExport(uvm_GetExport, uvm_PeekExport): ... def __init__(self, name, parent, maxsize=1): super().__init__(name, parent) self.queue = UVMQueue(maxsize=maxsize) self.get_ap = uvm_analysis_port("get_ap", self) self.put_ap = uvm_analysis_port("put_ap", self) self.blocking_put_export = self.uvm_BlockingPutExport( "blocking_put_export", # noqa: E501 self, self.queue, self.put_ap, ) self.nonblocking_put_export = self.uvm_NonBlockingPutExport( "nonblocking_put_export", # noqa: E501 self, self.queue, self.put_ap, ) # noqa: E501 self.put_export = self.uvm_PutExport( "put_export", self, self.queue, self.put_ap ) # noqa: E501 self.blocking_get_export = self.uvm_BlockingGetExport( "blocking_get_export", self, # noqa: E501 self.queue, self.get_ap, ) # noqa: E501 self.nonblocking_get_export = self.uvm_NonBlockingGetExport( "nonblocking_get_export", self, # noqa: E501 self.queue, self.put_ap, ) # noqa: E501 self.get_export = self.uvm_GetExport( "get_export", self, self.queue, self.get_ap ) self.blocking_peek_export = self.uvm_BlockingPeekExport( "blocking_peek_export", self, # noqa: E501 self.queue, self.get_ap, ) # noqa: E501 self.nonblocking_peek_export = self.uvm_NonBlockingPeekExport( "nonblocking_peek_export", self, # noqa: E501 self.queue, self.get_ap, ) # noqa: E501 self.peek_export = self.uvm_PeekExport( "peek_export", self, self.queue, self.get_ap ) self.blocking_get_peek_export = self.uvm_BlockingGetPeekExport( "blocking_get_peek_export", self, # noqa: E501 self.queue, self.get_ap, ) # noqa: E501 self.nonblocking_get_peek_export = self.uvm_NonBlockingGetPeekExport( "nonblocking_get_peek_export", self, # noqa: E501 self.queue, self.get_ap, ) # noqa: E501 self.get_peek_export = self.uvm_GetPeekExport( "get_peek_export", self, self.queue, self.get_ap ) # noqa: E501 async def put(self, item): """ :param item: item to put Blocking put coroutine """ await self.put_export.put(item) def can_put(self): """ :return: True if can put """ return self.put_export.can_put() def try_put(self, item): """ :param item: item to put :return: True if successful """ return self.put_export.try_put(item) async def get(self): """ :return: item coroutine that blocks if FIFO is empty """ return await self.get_export.get() def can_get(self): """ :return: True if can get """ return self.get_export.can_get() def try_get(self): """ :return: (success, item) """ return self.get_export.try_get() async def peek(self): """ :return: item A coroutine that blocks if FIFO is empty """ return await self.peek_export.peek() def can_peek(self): """ :return: True if can peek """ return self.peek_export.can_peek() def try_peek(self): """ :return: (success, item) """ return self.peek_export.try_peek() class uvm_tlm_fifo(uvm_tlm_fifo_base): # 12.2.8.2.1 def __init__(self, name=None, parent=None, size=1): """uvm_tlm_fifo is a uvm_component""" super().__init__(name, parent, size) # 12.2.8.2.2 def size(self): """ :return: size of FIFO Return the size of the fifo """ return self.queue.maxsize # 12.2.8.2.3 def used(self): """ :return: Number of items in the FIFO How much of the FIFO is being used? """ return self.queue.qsize() # 12.2.8.2.4 def is_empty(self): """ Returns true if FIFO is empty :return: True if empty """ return self.queue.empty() # 12.2.8.2.5 def is_full(self): """ Test for full FIFO :return: True if full """ return self.queue.full() # 12.2.8.2.6 # The SystemVerilog UVM flushes the Queue # using a while loop and counting the number # of threads waiting for a get. If there are # still threads waiting after the loop runs # it issues an error. # # In pyuvm we just flush the loop and clear # all the unfinished tasks. def flush(self): """ Flush out the FIFO """ self.queue._queue.clear() class uvm_tlm_analysis_fifo(uvm_tlm_fifo): class uvm_AnalysisExport(uvm_QueueAccessor, uvm_analysis_port): def write(self, item): """ :param item: item to write Writes the item to all the subscribers, or no one if there are no subscribers. """ try: self.queue.put_nowait(item) except QueueFull: raise QueueFull( f"Full analysis fifo: {self.__name__}. This should never happen" ) # noqa: E501 def __init__(self, name, parent=None): super().__init__(name, parent, 0) self.analysis_export = self.uvm_AnalysisExport( name="analysis_export", parent=self, uvm_queue=self.queue, ap=None ) # 12.2.9.1 class uvm_tlm_req_rsp_channel(uvm_component): class uvm_MasterSlaveExport(uvm_master_port, uvm_get_peek_port): def __init__(self, name, parent, put_export, get_peek_export): super().__init__(name, parent) self.put_export = put_export self.get_peek_export = get_peek_export async def put(self, item): """ :param item: item to put A coroutine that blocks if the FIFO is full """ await self.put_export.put(item) def can_put(self): """ :return: True if can put """ return self.put_export.can_put() def try_put(self, item): """ :param item: item to put :return: True if successful """ return self.put_export.try_put(item) async def get(self): """ :return: item A coroutine that blocks if the FIFO is empty """ return await self.get_peek_export.get() def can_get(self): """ :return: True if can get """ return self.get_peek_export.can_get() def try_get(self): """ :return: (success, item) """ return self.get_peek_export.try_get def __init__(self, name, parent=None, request_fifo_size=1, response_fifo_size=1): super().__init__(name, parent) self.req_tlm_fifo = uvm_tlm_fifo("request_fifo", self, request_fifo_size) self.rsp_tlm_fifo = uvm_tlm_fifo("response_fifo", self, response_fifo_size) self.put_request_export = self.req_tlm_fifo.put_export self.get_peek_response_export = self.rsp_tlm_fifo.get_peek_export self.get_peek_request_export = self.req_tlm_fifo.get_peek_export self.put_response_export = self.rsp_tlm_fifo.put_export self.request_ap = uvm_analysis_port("request_ap", self) self.response_ap = uvm_analysis_port("response_ap", self) self.master_export = self.uvm_MasterSlaveExport( name="master_export", parent=self, put_export=self.put_request_export, get_peek_export=self.get_peek_response_export, ) self.slave_export = self.uvm_MasterSlaveExport( name="slave_export", parent=self, get_peek_export=self.get_peek_request_export, put_export=self.put_response_export, ) def connect_phase(self): self.request_ap.connect(self.req_tlm_fifo.put_ap) self.response_ap.connect(self.rsp_tlm_fifo.get_ap) class uvm_tlm_transport_channel(uvm_tlm_req_rsp_channel): class uvm_TransportExport(uvm_transport_port): def __init__(self, name, parent, req_fifo, rsp_fifo): super().__init__(name, parent) self.req_fifo = req_fifo self.rsp_fifo = rsp_fifo async def transport(self, req): self.req_fifo.put_export.put(req) return await self.rsp_fifo.get_peek_export.get() def nb_transport(self, req): if not self.req_fifo.put_export.try_put(req): return False return self.rsp_fifo.get_peek_export.try_get() def __init__(self, name, parent=None): super().__init__(name, parent, 1, 1) self.transport_export = self.uvm_TransportExport( "transport_export", self, req_fifo=self.req_tlm_fifo, rsp_fifo=self.rsp_tlm_fifo, ) # UVM TLM 2 # 12.3 # This is left for future development. pyuvm-4.0.1/pyuvm/s13_predefined_component_classes.py000066400000000000000000000166701507477334100230450ustar00rootroot00000000000000# from base_classes import * from enum import IntEnum from pyuvm import error_classes from pyuvm.s12_uvm_tlm_interfaces import uvm_analysis_export from pyuvm.s13_uvm_component import * from pyuvm.s14_15_python_sequences import uvm_seq_item_port # This section and sequences are the crux of pyuvm. # The classes here allow us to build classic UVM # testbenches in Python. # # Section 13 of the IEEE-UVM Reference Manual (1800.2-2017) # lists five pieces of uvm_component functionality. # pyuvm implements much of this functionality using Python: # # a: Hierarchy---This is implemented in pyuvm # b: Phasing---This is also implemented in pyuvm, but is hardcoded # to the standard UVM phases. # c: Hierarchical Reporting---We manage this with the logging module. It is # orthogonal to the components. # d: Transaction Recording---We do not record transactions since pyuvm does # not run in the simulator. This # could be added later if we see a need or way to do it. # e: Factory---pyuvm manages the factory through the create() method without # all the SystemVerilog typing overhead. # class uvm_active_passive_enum(IntEnum): UVM_PASSIVE = 0 UVM_ACTIVE = 1 # Class Declarations # 13.2 class uvm_test(uvm_component): """ The base class for all tests """ # 13.3 class uvm_env(uvm_component): """ The base class for hierarchical containers of other components that together comprise a complete environment. The environment may initially consist of the entire testbench. Later, it can be reused as a sub-environment in even larger system-level environments. """ # 13.4 class uvm_agent(uvm_component): """ The :class:`!uvm_agent` virtual class should be used as the base class for the user-defined agents. Deriving from :class:`!uvm_agent` will allow you to distinguish agents from other component types also using its inheritance. Such agents will automatically inherit features that may be added to :class:`!uvm_agent` in the future. While an agent's build function, inherited from :class:`~uvm_component`, can be implemented to define any agent topology, an agent typically contains three subcomponents: a driver, sequencer, and monitor. If the agent is active, subtypes should contain all three subcomponents. If the agent is passive, subtypes should contain only the monitor. """ def build_phase(self): """ This ``build_phase()`` implements agent-specific behavior. * It sets the agent's ``is_active`` property to ``UVM_ACTIVE`` * It allows the user to override the ``is_active`` property using the ``cdb_get()`` method. * It logs a warning if the user sets an illegal value for ``is_active`` and sets the value to ``UVM_ACTIVE``. """ super().build_phase() self.is_active = uvm_active_passive_enum.UVM_ACTIVE try: self.is_active = self.cdb_get("is_active") except error_classes.UVMConfigItemNotFound: self.is_active = uvm_active_passive_enum.UVM_ACTIVE if self.is_active not in list(uvm_active_passive_enum): self.logger.warning( f"{self.get_full_name()}" "has illegal is_active" f" value: {self.is_active}." "Setting to UVM_ACTIVE" ) self.is_active = uvm_active_passive_enum.UVM_ACTIVE def get_is_active(self): """ Returns :data:`~uvm_active_passive_enum.UVM_ACTIVE` if the agent is acting as an active agent and :data:`~uvm_active_passive_enum.UVM_PASSIVE` if it is acting as a passive agent. The default implementation is to just return the ``is_active`` flag, but the component developer may override this behavior if a more complex algorithm is needed to determine the active/passive nature of the agent. """ return self.is_active def active(self): return self.get_is_active() == uvm_active_passive_enum.UVM_ACTIVE # 13.5 class uvm_monitor(uvm_component): """ This class should be used as the base class for user-defined monitors. Deriving from :class:`!uvm_monitor` allows you to distinguish monitors from generic component types inheriting from :class:`~uvm_component`. Such monitors will automatically inherit features that may be added to :class:`!uvm_monitor` in the future. """ ... # 13.6 class uvm_scoreboard(uvm_component): """ This class should be used as the base class for user-defined scoreboards. Deriving from :class:`!uvm_scoreboard` will allow you to distinguish scoreboards from other component types inheriting directly from :class:`~uvm_component`. Such scoreboards will automatically inherit and benefit from features that may be added to :class:`!uvm_scoreboard` in the future. """ ... # 13.7 class uvm_driver(uvm_component): """ The base class for drivers that initiate requests for new transactions via a :class:`~uvm_seq_item_pull_port`. The ports are typically connected to the exports of an appropriate sequencer component. This driver operates in pull mode. Its ports are typically connected to the corresponding exports in a pull sequencer as follows: .. code-block:: python driver.seq_item_port.connect(sequencer.seq_item_export) driver.rsp_port.connect(sequencer.rsp_export) The ``rsp_port`` needs connecting only if the driver will use it to write responses to the analysis export in the sequencer. """ def __init__(self, name, parent): """ Creates and initializes an instance of this class using the normal constructor arguments for :class:`~uvm_component`: *name* is the name of the instance, and *parent* is the handle to the hierarchical parent, if any. """ super().__init__(name, parent) self.seq_item_port = uvm_seq_item_port("seq_item_port", self) # 13.8 uvm_push_driver # Never seen one used. Not implemented. # 13.9 class uvm_subscriber(uvm_component): """ This class provides an analysis export for receiving transactions from a connected analysis export. Making such a connection "subscribes" this component to any transactions emitted by the connected analysis port. Subtypes of this class must define the write method to process the incoming transactions. This class is particularly useful when designing a coverage collector that attaches to a monitor. """ class uvm_AnalysisImp(uvm_analysis_export): def __init__(self, name, parent, write_fn): super().__init__(name, parent) self.write_fn = write_fn def write(self, tt): """ Write a transaction to all the connected subscribers. :param tt: The transaction to write :return: None """ self.write_fn(tt) def __init__(self, name, parent): super().__init__(name, parent) self.analysis_export = self.uvm_AnalysisImp("analysis_export", self, self.write) def write(self, tt): """ Method that must be defined in each subclass. Access to this method by outside components should be done via the :any:`analysis_export`. """ raise error_classes.UVMFatalError( "You must override the write() method in" f"uvm_subscriber {self.get_full_name()}" ) pyuvm-4.0.1/pyuvm/s13_uvm_component.py000066400000000000000000000600411507477334100200210ustar00rootroot00000000000000import fnmatch import logging import string from pyuvm import error_classes, utility_classes from pyuvm._utils import cocotb_version_info from pyuvm.s06_reporting_classes import uvm_report_object from pyuvm.s08_factory_classes import uvm_factory from pyuvm.s09_phasing import uvm_build_phase, uvm_common_phases, uvm_run_phase if cocotb_version_info < (2, 0): from cocotb.log import SimColourLogFormatter, SimLogFormatter, SimTimeContextFilter from cocotb.utils import want_color_output if want_color_output(): LogFormatter = SimColourLogFormatter else: LogFormatter = SimLogFormatter else: from cocotb.logging import SimLogFormatter, SimTimeContextFilter LogFormatter = SimLogFormatter # 13.1.1 class uvm_component(uvm_report_object): component_dict = {} @classmethod def clear_components(cls): cls.component_dict = {} def __init__(self, name, parent): """ 13.1.2.1---This is new() in the IEEE-UVM, but we mean the same thing with __init__() :param name: The name of the component. Used in the UVM hierarchy :param parent: The parent component. If None, the parent is uvm_root """ self._children = {} if parent is None and name != "uvm_root": parent = uvm_root() self.parent = parent if parent is not None: parent.add_child(name, self) self.print_enabled = True # 13.1.2.2 super().__init__(name) # Cache the hierarchy for easy access if name != "uvm_root": uvm_component.component_dict[self.get_full_name()] = self def clear_children(self): """ Removes the direct children from this component. """ self._children = {} def clear_hierarchy(self): """ Removes self from the UVM hierarchy """ self._parent = None self.clear_children() def do_execute_op(self, op): raise error_classes.UVMNotImplemented("Policies not implemented") @classmethod def create(cls, name="", parent=None): if parent is None: parent_inst_path = "" else: parent_inst_path = parent.get_full_name() new_comp = uvm_factory().create_component_by_type( cls, parent_inst_path=parent_inst_path, name=name, parent=parent ) return new_comp def get_parent(self): """ :return: parent object 13.1.3.1 """ return self._parent def raise_objection(self): """ Raise an objection, usually at the start of the ``run_phase()`` """ utility_classes.ObjectionHandler().raise_objection(self) def drop_objection(self): """ Drop an objection, usually at the end of the ``run_phase()`` """ utility_classes.ObjectionHandler().drop_objection(self) def objection(self): class Objection: def __init__(self, component): self.component = component def __enter__(self): return self.component.raise_objection() def __exit__(self, *args): return self.component.drop_objection() return Objection(self) def cdb_set(self, label, value, inst_path="*"): """ A convenience routing to store an object in the config_db using this component's ``get_full_name()`` path. :param value: The object to store :param label: The label to use to retrieve it :param inst_path: A path with globs or if left blank the get_full_name() path """ ConfigDB().set(self, inst_path, label, value) def cdb_get(self, label, inst_path=""): """ A convenience routine that retrieves an object from the config_db using this component's ``get_full_name()`` path. Can find objects stored with wildcards :param inst_path: The path below this component :param label: The label used to store the value :return: The object at this path stored at the label """ datum = ConfigDB().get(self, inst_path, label) return datum @property def parent(self): return self.get_parent() @parent.setter def parent(self, parent): if parent is not None: assert isinstance(parent, uvm_component), ( f" {parent} is of type {type(parent)}" ) assert parent != self, ( f"Cannot make a {self.get_name()} its own parent. That is incest." ) self._parent = parent def get_full_name(self): """ :return: This component's name concatenated to parent name. 13.1.3.2 """ if self.get_name() is None or self.get_name() == "uvm_root": return "" if self._parent is None: fullname = "" else: fullname = self._parent.get_full_name() if len(fullname) == 0: fullname = self.get_name() else: fullname = fullname + "." + self.get_name() return fullname # Children in pyuvm # 13.1.3.4 modified to be pythonic """ UVM components contain a list of child components as the basis for creating the UVM hierarchy. However the UVM accesses those children in an unPythonic way. Instead of asking for an iterator of children, which is what Python does and what we'll do here, the UVM uses `get_child()`, `get_first_child()`, and `get_next_child()` to create iterative-like behavior, but there is no sense in recreating this if we're in a language like Python with powerful iterative functionality. We will store children in a Python dict so as to implement get_child(), but we will not implement get_first_child() or get_next_child(). We will implement __iter__ so that the user can use the component as an iterator. """ def get_children(self): """ 13.1.3.3 :return: A dict containing children objects """ return list(self.children) def add_child(self, name, child): assert name not in self._children, ( f"{self.get_full_name()} already has a child named {name}" ) self._children[name] = child pass @property def hierarchy(self): """ We return a generator to find the children. This is more pythonic and saves memory for large hierarchies. :return: A generator that returns the children. """ yield self for child in self.children: assert isinstance(child, uvm_component) yield child for grandchild in child.children: assert isinstance( grandchild, uvm_component, ) yield grandchild # The UVM relies upon a hokey iteration system to get the children # out of a component class. You get the name of the first child and # then pass it to get_next_child to get the name of the next # child. This continues until get_next_child returns zero. # # Python has a rich iteration system and it would be foolish to # eschew it. So we are not going to implement the above. Instead # the children() method is a generator that allows you to loop # through the children. @property def children(self): """ 13.1.3.4 Implements the intention of this requirement without the approach taken in the UVM We use a generator instead. """ for child in self._children: yield self._children[child] def __repr__(self): return self.get_full_name() def get_child(self, name): """ 13.1.3.4 :param self: :param name: child's name :return: child ``uvm_component`` of that name """ assert isinstance(name, str) try: return self._children[name] except KeyError: return None def get_num_children(self): """ 13.1.3.5 :param self: :return: The number of children in component """ return len(self._children) def has_child(self, name): """ 13.1.3.6 :param name: Name of child the object :return: True if exists, False otherwise """ assert isinstance(name, str) return name in self._children def lookup(self, name): """ 13.1.3.7 Return a component base on the path. If . then use full name from root otherwise relative :param name: The search name :return: either the component or None """ assert isinstance(name, str) if name[0] == ".": lookup_name = name[1:] else: lookup_name = f"{self.get_full_name()}.{name}" try: return uvm_component.component_dict[lookup_name] except KeyError: return None def get_depth(self): """ 13.1.3.8 Get the depth that I am from the top component. uvm_root is 0. :return: The hierarchy depth from me to the bottom. """ # Rather than getting all recursive just count # levels in the full name. fullname = self.get_full_name() if len(fullname) == 0: return 0 else: return len(self.get_full_name().split(".")) # noinspection SpellCheckingInspection def set_logging_level_hier(self, logging_level): """ Set the logging level for this component's logger and all the way down the hierarchy :param logging_level: typically a constant from logging module :return: None """ self.set_logging_level(logging_level) for child in self.children: child.set_logging_level_hier(logging_level) def add_logging_handler_hier(self, handler): """ Add an additional handler all the way down the component hierarchy :param handler: A logging.Handler object :return: None """ assert isinstance(handler, logging.Handler), ( f"You can only add logging.Handler objects not {type(handler)}" ) self.add_logging_handler(handler) for child in self.children: child.add_logging_handler_hier(handler) def remove_logging_handler_hier(self, handler): """ Remove a handler from all loggers below this component :param handler: logging handler :return: None """ assert isinstance(handler, logging.Handler), ( f"You must pass a logging.Handler not {type(handler)}" ) self.logger.removeHandler(handler) for child in self.children: child.remove_logging_handler_hier(handler) def remove_streaming_handler_hier(self): """ Remove this component's streaming handler and all the way down the hierarchy """ self.remove_streaming_handler() for child in self.children: child.remove_streaming_handler_hier() def disable_logging_hier(self): """ Disable logging for this component and all the way down the hierarchy """ self.disable_logging() for child in self.children: child.disable_logging_hier() def build_phase(self): ... def connect_phase(self): ... def end_of_elaboration_phase(self): ... def start_of_simulation_phase(self): ... async def run_phase(self): ... def extract_phase(self): ... def check_phase(self): ... def report_phase(self): ... def final_phase(self): ... """ The following sections have been skipped and could be implemented at a later time if necessary. 13.1.4.2--Run time phases 13.1.4.3--phase_* methods 13.1.4.4--*_domain methods 13.1.5.5--suspend 13.1.4.6--pre_abort 13.1.5--Configuration interface 13.1.6--Recording interface 13.1.7--Other interfaces """ # 13.2 (rest of 13.2 is in s13_predefined_components) class uvm_test(uvm_component): """ The base class for all tests """ class uvm_root(uvm_component, metaclass=utility_classes.UVM_ROOT_Singleton): """ F.7. We do not use ``uvm_pkg`` to hold ``uvm_root``. Instead it is a class variable of uvm_component. This avoids circular reference issues regarding uvm_pkg. Plus, it's cleaner. ``uvm_root`` is still a singleton that you access through its constructor instead of through a ``get()`` method. Much of the functionality in Annex F delivers functionality in SystemVerilog that is already built into Python. So we're going to skip much of that Annex. """ @classmethod def clear_singletons(cls, keep_set={}): """ Clear the singletons in the system. This is used for testing """ keepers = {uvm_factory, utility_classes.FactoryData}.union(keep_set) utility_classes.Singleton.clear_singletons(keep=keepers) def __init__(self): super().__init__("uvm_root", None) self.uvm_test_top = None self.running_phase = None def _utt(self): """Used in testing""" return self.get_child("uvm_test_top") # This implementation skips much of the state-setting and # what not in the LRM and focuses on building the # hierarchy and running the test. # At this time pyuvm has not implemented the phasing # system described in the LRM. It's not clear that anyone # is using it, and in fact it is recommended that people # stick to the basic phases. So this implementation loops # through the hierarchy and runs the phases. async def run_test(self, test_name, keep_singletons=False, keep_set=set()): """ :param test_name: The uvm test name or test class :param keep_singletons: If True do not clear singletons (default False) :param keep_set: Set of singleton classes to keep if keep_singletons is False. Pass a list of singletons to `set()` :return: none """ factory = uvm_factory() if not keep_singletons: uvm_report_object.set_default_logging_level(logging.INFO) self.clear_singletons(keep_set) factory.clear_overrides() self.clear_children() utility_classes.ObjectionHandler().clear() self.uvm_test_top = factory.create_component_by_name( test_name, "", "uvm_test_top", self ) for self.running_phase in uvm_common_phases: self.logger.log(utility_classes.PYUVM_DEBUG, str(self.running_phase)) self.running_phase.traverse(self.uvm_test_top) if self.running_phase == uvm_run_phase: await utility_classes.ObjectionHandler().run_phase_complete() # noqa: E501 # In the SystemVerilog UVM the uvm_config_db is a # convenience layer on top of the much more complicated # uvm_resource_db class. However, few people # use the uvm_resource_db and in fact, Mentor recommends # that people use the uvm_config_db interface. # # Therefore pyuvm implements only the behavior of # the ConfigDB. It also does NOT implement the # regular expression form of wildcards, which are # also rarely used. Instead it implements globbing # as defined by fnmatch(). # # To avoid confusion with the full uvm_config_db # our class is named ConfigDB. class ConfigDB(metaclass=utility_classes.Singleton): default_get = object() default_precedence = 1000 legal_chars = set(string.ascii_letters) | set(string.digits) | set("_.") """ A path-based singleton storage system """ # The ConfigDB is a dual-level dict. The outer dict is a # GlobPathDict that can store globs in keys that match # later retrievals. Each entry contains another dict # that stores items by keys. # # Unlike the UVM config_db, this config_db makes no effort # to store multiple items at one location. The last stored # wins # # Also, pyuvm does not support wildcards in the field names # at this time. def __init__(self): self.logger_holder = uvm_report_object("logger_holder") self.logger_holder.remove_streaming_handler() configdb_handler = logging.StreamHandler() configdb_handler.addFilter(SimTimeContextFilter()) # Don't let the handler interfere with logger level configdb_handler.setLevel(logging.NOTSET) # Make log messages look like UVM messages configdb_formatter = LogFormatter() configdb_handler.setFormatter(configdb_formatter) self.logger_holder.add_logging_handler(configdb_handler) self.logger_holder.logger.propagate = False self._path_dict = {} self.is_tracing = False self._cond_dict = {} def clear(self): """Reset the ConfigDB. Used for testing.""" if self.is_tracing: self.logger_holder.logger.info("CFGDB/CLEAR: Clearing ConfigDB()") self._path_dict = {} @staticmethod def _get_context_inst_name(context, inst_name): """ Get the config_key from context and passed inst_name :param context: uvm_component or None :param inst_name: string that can be a glob :return: string that is the key """ assert context is None or isinstance(context, uvm_component), ( "config_db context must be None or a uvm_component. " ) f"Not {type(context)}" if context is None: context = uvm_root() if inst_name is None or inst_name == "": inst_name = context.get_full_name() elif context.get_full_name() != "": inst_name = context.get_full_name() + "." + inst_name return context, inst_name def trace(self, method, context, inst_name, field_name, value): """ Output the ConfigDB activity if tracing is on. """ if self.is_tracing: # noinspection SpellCheckingInspection self.logger_holder.logger.info( f"CFGDB/{method} Context: {context} -- {inst_name} {field_name}={value}" ) # noqa: E501 def set(self, context, inst_name, field_name, value): """ Stores an object in the db using the context and inst_name to create a retrieval path, and the key name. :param context: A handle to a component :param inst_name: The instance name within the component :param field_name: The key we're setting :param value: The object to be stored :return: None """ if not set(field_name).issubset(self.legal_chars): raise error_classes.UVMNotImplemented( f"pyuvm does not allow wildcards in key names ({field_name})" ) context, inst_name = self._get_context_inst_name(context, inst_name) if inst_name not in self._path_dict: self._path_dict[inst_name] = {} if field_name not in self._path_dict[inst_name]: self._path_dict[inst_name][field_name] = {} precedence = self.default_precedence if uvm_root().running_phase is uvm_build_phase: precedence = self.default_precedence - context.get_depth() self._path_dict[inst_name][field_name][precedence] = value self.trace("SET", context, inst_name, field_name, value) def get(self, context, inst_name, field_name, default=default_get): """ The component path matches against the paths in the ConfigDB. The path cannot have wildcards, but can match against keys with wildcards. Return the value stored at key. If the key is missing, returns default or raises ``UVMConfigItemNotFound``. :param context: The component making the call :param inst_name: component full path with no wildcards :param field_name: the field_name being retrieved :param default: the value to return if there is no key, defaults to default_get :raises UVMConfigItemNotFound: if the key is not found and the default is not set :return: value found at location """ if not set(inst_name).issubset(self.legal_chars): raise error_classes.UVMError( f'"{inst_name}" is illegal: ' f"inst_name wildcards only allowed when storing." ) context, inst_name = self._get_context_inst_name(context, inst_name) try: key_matches = [] # Make the linter happy by setting this. try: # key_matches = [dk for dk in self._path_dict.keys() # if fnmatch.fnmatch(inst_name, dk)] for dk in self._path_dict.keys(): if fnmatch.fnmatch(inst_name, dk): key_matches.append(dk) except TypeError: raise error_classes.UVMConfigItemNotFound( f'"{inst_name}" is not in ConfigDB().' ) finally: if len(key_matches) == 0: raise error_classes.UVMConfigItemNotFound( f'"{inst_name}" is not in ConfigDB().' ) # Here we sort the list of paths by which paths are "in" other # paths. That is A comes before '*' A.B comes before A.*, etc. # We use an insertion sort. A path is inserted in front of the # first path it is "in" sorted_paths = [] try: sorted_paths.append(key_matches.pop()) except IndexError: raise error_classes.UVMConfigItemNotFound( f"{inst_name} not in ConfigDB()" ) # Sort the matching keys from most specific to # most greedy. A.B.C before A.B.* before A.* before * for path in key_matches: inserted = False for ii in range(len(sorted_paths)): if fnmatch.fnmatch(path, sorted_paths[ii]): sorted_paths.insert(ii, path) inserted = True break if not inserted: sorted_paths.append(path) value = None for path in sorted_paths: try: component_fields = self._path_dict[path] matching_path_fields = component_fields[field_name] max_precedence = max(matching_path_fields.keys()) value = matching_path_fields[max_precedence] break except KeyError: pass if value is not None: self.trace("GET", context, inst_name, field_name, value) return value else: raise error_classes.UVMConfigItemNotFound( f'"Component {inst_name} has no key: {field_name}' ) except error_classes.UVMConfigItemNotFound as e: if default is self.default_get: raise e return default def exists(self, context, inst_name, field_name): """ Returns true if there is data in the database at this location :param context: None or uvm_component :param inst_name: instance name string in context :param field_name: key name for location :return: True if exists """ try: _ = self.get(context, inst_name, field_name) except error_classes.UVMConfigItemNotFound: return False return True def wait_modified(self): """ :raises UVMNotImplemented: This is not implemented in pyuvm """ raise error_classes.UVMNotImplemented( "wait_modified not implemented pending requests for it." ) def __str__(self): str_list = [f"\n{'PATH':20}: {'KEY':10}: {'DATA':30}"] for inst_path in self._path_dict: for key in self._path_dict[inst_path]: str_list.append( f"{inst_path:20}: {key:10}: {self._path_dict[inst_path][key]}" ) # noqa: E501 return "\n".join(str_list) pyuvm-4.0.1/pyuvm/s14_15_python_sequences.py000066400000000000000000000343301507477334100210340ustar00rootroot00000000000000# The SystemVerilog sequences provided much more functionality # than most users ever used. This led to extremely complicated # code. # # This file implements the uvm sequence functionality in # Python using Python features instead of emulating # SystemVerilog features. from cocotb.triggers import Event as CocotbEvent from pyuvm.error_classes import * from pyuvm.s05_base_classes import * from pyuvm.s12_uvm_tlm_interfaces import * # The sequence system allows users to create and populate sequence # items and then send them to a driver. The driver # loops through getting the next sequence item, # processing it, and sending the result back. # # Remembering that all run_phases run in their own # thread we see this code in the driver. # # def run_phase(phase): # while True: # req = self.seq_item_port.get_next_item() # # send the req to the tinyALU and get rsp # self.seq_item_port.item_done(rsp) # # or # while True: # req = self.seq_item_port.get_next_item() # # do stuff # self.seq_item_port.item_done() # self.seq_item_port.put(rsp) # # Either way the sequence in this case does: # # start_item(req) # finish_item(req) # rsp = get_response() # # We see above that the driver is a simple uvm_component # with a special port. The port does all the synchronization. # It blocks until there is a req and then it sends the response # back and notifies the sequencer that the transaction is done. we have: # # # From the other side, the sequence side we get this: # # First someone starts the sequence: # # test_seq.start(seqr) # # This puts a handle to the sequencer (seqr) in the sequence. # Then this happens. # # def body(): # req = Req() # self.start_item(req) # Request the sequencer # req.A = 1 # req.B = 5 # req.op = operators.ADD # self.finish_item(req) # Send and wait for item_done # rsp = self.get_response() # # The above puts this sequence in a queue and blocks until # the sequence's turn comes up. # So the sequence contains: # start() # start_item() # finish_item() # get_response() # # The seq_item_port (and export) contain: # get_next_item() # item_done() # put() # # The sequencer that connects them contains: # A fifo that holds sequences in order # A mechanism to notify start_item that it's turn has arrived # A mechanism to notify finish_item that the item is done # A mechanism to return responses # a seq_item_export # # The driver contains # a seq_item_port # # We'll build from the seq_item_port out. # uvm_seq_item_port # The uvm_seq_item_port is a uvm_put_port with two extra methods. class ResponseQueue(UVMQueue): """ The ``ResponseQueue`` is a queue that can cherry-pick an item using an id number, or simply return the next item in the queue. """ def __init__(self, maxsize: int = 0): super().__init__(maxsize=maxsize) self.put_event = CocotbEvent() def put_nowait(self, item): """ Extend the ``cocotb.queue.Queue.put_nowait`` method to set the ``put_event`` flag. This flag is used to signal that an item has been put in the queue so that get_response can be unblocked. :param item: The item to put in the queue :raises QueueFull: If the queue is full """ super().put_nowait(item) self.put_event.set() self.put_event.clear() async def get_response(self, txn_id=None): """ A coroutine that will either get a response item with the given transaction_id, or return the next item in the queue. :param txn_id: (Optional) The transaction ID of the response you want to pluck from the queue. :return: The response item """ if txn_id is None: return await self.get() else: while True: item_list = list(self._queue) txn_list = [xx for xx in item_list if xx.transaction_id == txn_id] if len(txn_list) == 0: await self.put_event.wait() else: assert len(txn_list) == 1, ( f"Multiple transactionsn have the same ID: {txn_id}" ) _ = self._queue.index(txn_list[0]) self._queue.remove(txn_list[0]) return txn_list[0] def __str__(self): return str([str(xx) for xx in self._queue]) class uvm_sequence_item(uvm_transaction): """ The pyuvm uvm_sequence_item has events to implement start_item() and finish_item() """ def __init__(self, name): super().__init__(name) self.start_condition = CocotbEvent() self.finish_condition = CocotbEvent() self.item_ready = CocotbEvent() self.parent_sequence_id = None self.response_id = None def set_context(self, item): """ Use this to link a new response transaction to the request transaction. rsp.set_context(req) :param item: The request transaction :return: None """ self.response_id = (item.parent_sequence_id, item.get_transaction_id()) class uvm_seq_item_export(uvm_blocking_put_export): """ The sequence item port with a request queue and a response queue. """ def __init__(self, name, parent): super().__init__(name, parent) self.req_q = UVMQueue() self.rsp_q = ResponseQueue() self.current_item = None async def put_req(self, item): """ put request into request queue. Block if the queue is full. :param item: request item :return: None """ await self.req_q.put(item) def put_response(self, item): """ Put response into response queue. Do not block. :param item: response item :raise QueueFull: If the queue is full :return: """ self.rsp_q.put_nowait(item) async def get_next_item(self): """ A couroutine that gets the next item out of the item queue and blocks if the queue is empty. :return: item to process """ if self.current_item is not None: raise error_classes.UVMSequenceError( "You must call item_done() before calling get_next_item again" ) self.current_item = await self.req_q.get() self.current_item.start_condition.set() self.current_item.start_condition.clear() await self.current_item.item_ready.wait() return self.current_item def item_done(self, rsp=None): """ Signal that the item has been completed. If ``rsp`` is not ``None`` put it into the response queue. :param rsp: (optional) item to put in response queue if not None """ if self.current_item is None: raise error_classes.UVMSequenceError( "You must call get_next_item before calling item_done" ) self.current_item.finish_condition.set() self.current_item.finish_condition.clear() self.current_item = None if rsp is not None: self.put_response(rsp) async def get_response(self, transaction_id=None): """ A couroutine that will block if there is no transaction available If ``transaction_id`` is not ``None``, block until a response with the transaction id becomes available. :param transaction_id: The transaction ID of the response :return: The response item """ datum = await self.rsp_q.get_response(transaction_id) return datum class uvm_seq_item_port(uvm_port_base): def connect(self, export): self._check_export(export) super().connect(export) async def put_req(self, item): """ A coroutine that blocks until the request is put in the queue :param item: The request item """ await self.export.put_req(item) def put_response(self, item): """ Put a response back in the queue. aka put_response :param item: The response item :Raises UVMFatalError: If the item is not a subclass of uvm_sequence_item """ try: assert issubclass(type(item), uvm_sequence_item) except AssertionError: raise UVMFatalError( "put_response only takes uvm_sequence_items as arguments" ) self.export.put_response(item) async def get_next_item(self): """ A coroutine that get the next sequence item from the request queue and blocks if the queue is empty. :return: The next sequence item """ try: return await self.export.get_next_item() except AttributeError: assert self.export is not None, "export is not connected" raise def item_done(self, rsp=None): """ Notify the driver that it can get the next sequence. If ``rsp`` is not ``None``, put it in the response queue. :param rsp: (optional) The response item :raise UVMFatalError: If ``rsp`` is not a subclass of uvm_sequence_item """ if rsp is not None: try: assert issubclass(type(rsp), uvm_sequence_item) except AssertionError: raise UVMFatalError( "item_done only takes uvm_sequence_items as arguments" ) self.export.item_done(rsp) async def get_response(self, transaction_id=None): """ A coroutine that will ither get a response item with the given transaction_id, or get the first response item in the queue. Otherwise it will block until a response is ready. :param transaction_id: The transaction ID of the response you want :return: The response item """ datum = await self.export.get_response(transaction_id) return datum # The UVM sequencer is really just a holder for the # seq_item_export that does all the work. class uvm_sequencer(uvm_component): """ The uvm_sequencer arbitrates between multiple sequences that want to send items to driver (connected to seq_export) It exposes put_req, get_next_item, get_response from the export. The sequence will use these to coordinate items with the sequencer. """ def __init__(self, name, parent): super().__init__(name, parent) self.seq_item_export = uvm_seq_item_export("seq_item_export", self) self.seq_q = UVMQueue(0) async def run_phase(self): while True: next_item = await self.seq_q.get() await self.seq_item_export.put_req(next_item) async def start_item(self, item): await self.seq_q.put(item) await item.start_condition.wait() async def finish_item(self, item): item.item_ready.set() item.item_ready.clear() await item.finish_condition.wait() async def put_req(self, req): await self.seq_item_export.put_req(req) async def get_response(self, txn_id=None): datum = await self.seq_item_export.get_response(txn_id) return datum async def get_next_item(self): next_item = await self.seq_item_export.get_next_item() return next_item class uvm_sequence(uvm_object): """ The uvm_sequence creates a series of sequence items and feeds them to the sequencer using start_item() and finish_item(). It can also get back results with get_response() body() gets launched in a thread at start. """ def __init__(self, name="uvm_sequence"): super().__init__(name) self.sequencer = None self.running_item = None self.sequence_id = id(self) async def pre_body(self): """ This function gets launced BEFORE the function body() is started following a start() call. This method should not be called directly by the user. """ async def post_body(self): """ This function gets launced AFTER the function body() is started following a start() call. This method should not be called directly by the user. """ async def body(self): """ This function gets launched in a thread when you run start() You generally override it. """ async def start(self, seqr=None, call_pre_post=True): """ Launch this sequence on the sequencer. Seqr cannot be None. :param seqr: The sequencer to launch this sequence on. :param call_pre_post: If set to true (default), then pre_body and post_body are called before and after the sequence body is called. :raise AssertionError: If seqr is None """ if seqr is not None: assert isinstance(seqr, uvm_sequencer), ( "Tried to start a sequence with a non-sequencer" ) self.sequencer = seqr if call_pre_post: await self.pre_body() await self.body() if call_pre_post: await self.post_body() async def start_item(self, item): """ Sends an item to the sequencer and waits to be notified when the item has been selected to be run. :param item: The sequence item to send to the driver. """ if self.sequencer is None: raise error_classes.UVMSequenceError( f"Tried start_item in a virtual sequence {self.get_full_name()}" ) item.parent_sequence_id = self.sequence_id self.running_item = item await self.sequencer.start_item(item) async def finish_item(self, item): if self.sequencer is None: raise error_classes.UVMSequenceError( f"Tried finish_item in virtual sequence: {self.get_full_name()}" ) await self.sequencer.finish_item(item) async def get_response(self, transaction_id=None): if self.sequencer is None: raise error_classes.UVMSequenceError( "Tried to do get_response in a virtual " f"sequence: {self.get_full_name()}" ) tran_id = ( transaction_id if transaction_id is not None else self.running_item.transaction_id ) datum = await self.sequencer.get_response(tran_id) return datum pyuvm-4.0.1/pyuvm/s17_uvm_reg_enumerations.py000066400000000000000000000176311507477334100214000ustar00rootroot00000000000000# Global register enumerations from enum import Enum from pyuvm.error_classes import UVMFatalError from pyuvm.s05_base_classes import uvm_object UVM_REG_DATA_WIDTH = 64 UVM_REG_ADDR_WIDTH = 64 # 17.2.1.1 uvm_reg_data_t uvm_reg_data_t = int # 17.2.1.3 uvm_reg_addr_t uvm_reg_addr_t = int uvm_reg_policy_t = [ "RO", # no effect, R: no effect. "RW", # as is, R: no effect. "RC", # no effect, R: clears all bits. "RS", # no effect, R: sets all bits. "WRC", # as is, R: clears all bits. "WRS", # as is, R: sets all bits. "WC", # clears all bits, R: no effect. "WS", # sets all bits, R: no effect. "WSRC", # sets all bits, R: clears all bits. "WCRS", # clears all bits, R: sets all bits. "W1C", # 1/0 clears/no effect on matching bit, R: no effect. "W1S", # 1/0 sets/no effect on matching bit, R: no effect. "W1T", # 1/0 toggles/no effect on matching bit, R: no effect. "W0C", # 1/0 no effect on/clears matching bit, R: no effect. "W0S", # 1/0 no effect on/sets matching bit, R: no effect. "W0T", # 1/0 no effect on/toggles matching bit, R: no effect. "W1SRC", # 1/0sets/no effect on matching bit, R: clears all bits. "W1CRS", # 1/0clears/no effect on matching bit, R: sets all bits. "W0SRC", # 1/0no effect on/sets matching bit, R: clears all bits. "W0CRS", # 1/0no effect on/clears matching bit, R: sets all bits. "WO", # as is, R: error. "WOC", # clears all bits, R: error. "WOS", # sets all bits, R: error. # first one after HARD reset is as is, # other W have no effects, R: no effect. "W1", # first one after HARD reset is as is, # other W have no effects, R: error. "WO1", "NOACCESS", # no effect, R: no effect. ] # 17.2.1.5 uvm_reg_byte_en_t uvm_reg_byte_en_t = int uvm_reg_field_ignore_rand_mode = ["RW", "WRC", "WRS", "WO", "W1", "WO1"] # 17.2.1.7 uvm_hdl_path_slice class uvm_hdl_path_slice: def __init__(self, path: str, offset: int, size: int): self.path: str = path self.offset: int = offset self.size: int = size # 17.2.2.1 uvm_status_e class uvm_status_e(Enum): UVM_IS_OK = 0 # Operation completed successfully. UVM_NOT_OK = 1 # Operation completed with error. # Operation completed successfully, but had unknown bits. UVM_HAS_X = 2 # 17.2.2.2 uvm_door_e class uvm_door_e(Enum): # Use the front door. UVM_FRONTDOOR = 0 # Use the back door. UVM_BACKDOOR = 1 # Operation derived from observations by a bus # monitor via the uvm_reg_predictor UVM_PREDICT = 2 # Operation specified by the context. UVM_DEFAULT_DOOR = 3 # 17.2.2.3 uvm_check_e class uvm_check_e(Enum): UVM_NO_CHECK = 0 # Read only. UVM_CHECK = 1 # Read and check. # 17.2.2.4 uvm_endianness_e class uvm_endianness_e(Enum): # Byte ordering not applicable. UVM_NO_ENDIAN = 0 # Least-significant bytes first in consecutive addresses. UVM_LITTLE_ENDIAN = 1 # Most-significant bytes first in consecutive addresses. UVM_BIG_ENDIAN = 2 # Least-significant bytes first at the same address. UVM_LITTLE_FIFO = 3 # Most-significant bytes first at the same address. UVM_BIG_FIFO = 4 # 17.2.2.5 uvm_elem_kind_e class uvm_elem_kind_e(Enum): UVM_REG = 0 # Register. UVM_FIELD = 1 # Field. UVM_MEM = 2 # Memory location. # 17.2.2.6 uvm_access_e class uvm_access_e(Enum): UVM_READ = 0 # Read operation. UVM_WRITE = 1 # Write operation. # 17.2.2.7 uvm_hier_e class uvm_hier_e(Enum): # Provide info from the local context. UVM_NO_HIER = 0 # Provide info based on the hierarchical context. UVM_HIER = 1 # 17.2.2.8 uvm_predict_e class uvm_predict_e(Enum): # Predicted value is as is. UVM_PREDICT_DIRECT = 0 # Predict based on the specified value having been read. UVM_PREDICT_READ = 1 # Predict based on the specified value having been written. UVM_PREDICT_WRITE = 2 # 17.2.2.9 uvm_coverage_model_e class uvm_coverage_model_e(Enum): # None. UVM_NO_COVERAGE = 0 # Individual register bits. UVM_CVR_REG_BITS = 1 # Individual register and memory addresses. UVM_CVR_ADDR_MAP = 2 # Field values. UVM_CVR_FIELD_VALS = 3 # All coverage models. UVM_CVR_ALL = 4 # 17.2.2.10 uvm_reg_mem_tests_e class uvm_reg_mem_tests_e(Enum): # Run uvm_reg_hw_reset_seq (see E.1). UVM_DO_REG_HW_RESET = 0 # Run uvm_reg_bit_bash_seq (see E.2.2). UVM_DO_REG_BIT_BASH = 1 # Run uvm_reg_access_seq (see E.3.2). UVM_DO_REG_ACCESS = 2 # Run uvm_mem_access_seq (see E.5.2). UVM_DO_MEM_ACCESS = 3 # Run uvm_reg_mem_shared_access_seq (see E.4.3). UVM_DO_SHARED_ACCESS = 4 # Run uvm_mem_walk_seq (see E.6.2). UVM_DO_MEM_WALK = 5 # Run all of the above. UVM_DO_ALL_REG_MEM_TESTS = 6 # 17.2.3 uvm_hdl_path_concat class uvm_hdl_path_concat(uvm_object): # 17.2.3.3.1 def __init__(self, name="unnamed"): super().__init__(name) self._slices = [] def __is_overlapping_slice(self, slice: uvm_hdl_path_slice) -> bool: """ Checks if a slice proivided overlaps with any of the slices :param slice:uvm_hdl_path_slice: slice object to compare :return: bool """ for slice_i in self._slices: # check if provided slice is overlapping with any of the slices if slice.offset > slice_i.offset: slice_0 = slice_i else: slice_0 = slice if slice_i.offset > slice.offset: slice_1 = slice else: slice_1 = slice_i is_invalid = slice_1.offset >= slice_0.offset + slice_0.size is_invalid = is_invalid or slice_1.offset < slice_0.offset if is_invalid: return False else: pass return True # 17.2.3.3.2 def set_slices(self, slices: list): # Check for validity of slices # check if provided list is not empty assert len(slices) >= 1, "Empty slice object provided" # - if only one slices implements entire register, # both offset and size must be -1 # - Slices must not overlap but gap is allowed if len(self._slices) == 1: if self._slices[0].offset != -1: raise UVMFatalError( f"""slice offset: {self._slices[0].offset} must be -1 for single slice. See 17.2.3.3.1""" ) if self._slices[1].size != -1: raise UVMFatalError( f"""slice size: {self._slices[0].size} must be -1 for single slice. See 17.2.3.3.1""" ) else: # Check for slice overlap for count, slice in enumerate(self._slices): if self.__is_overlapping_slice(slice): raise UVMFatalError(f" Overlapping slice found:: {slice.path}") # Check for slice offset order ::: # The slices shall be specified in most-to-least # significant order. if len(slices) > 1: for count, slice in enumerate(slices): if count == 0: continue if slice.offset >= slices[count - 1].offset: raise UVMFatalError("Slice order doesn't follow MSB to LSB") self._slices = slices # 17.2.3.3.3 def get_slices(self) -> list: return self._slices # 17.2.3.3.4 def add_slice(self, slice_i: uvm_hdl_path_slice) -> None: for count, slice in enumerate(self._slices): if slice.offset >= slice_i.offset: raise UVMFatalError("""The slices shall be specified in most-to-least significant order.""") if self.__is_overlapping_slice(slice_i): raise UVMFatalError(f"Found overlapping slice :: {slice.path}") self._slices.append(slice_i) pyuvm-4.0.1/pyuvm/s18_uvm_reg_block.py000066400000000000000000000243241507477334100177570ustar00rootroot00000000000000from pyuvm.s05_base_classes import uvm_object from pyuvm.s17_uvm_reg_enumerations import uvm_hier_e from pyuvm.s19_uvm_reg_field import uvm_reg_field from pyuvm.s20_uvm_reg import uvm_reg from pyuvm.s21_uvm_reg_map import uvm_reg_map from pyuvm.s24_uvm_reg_includes import uvm_fatal, uvm_not_implemeneted """ TODO: the following must be completed 1. implement add_vreg 2. implement remove_reg 3. implement add_mem 4. implement remove_mem 5. implement unregister_blk 6. implement write_reg or write_mem by name 7. implement read_reg or read_mem by name 8. implement get_virtual_fields in a unique field list 9. implement get_mem to return a list of memories 10. implement get_default_hdl_path 11. implement set_default_hdl_path 12. implement set_hdl_path_root 13. implement is_hdl_path_root 14. implement get_full_hdl_path 15. implement has_hdl_path 16. implement add_hdl_path 17. implement clear_hdl_path 18. implement get_backdoor 19. implement set_backdoor 20. implement get_default_door 21. implement set_default_door 22. implement needs_update 23. implement update 24. implement mirror 25. implement all the coverage APIs using COCOTB coverage report NOTE: write/read_reg_by_name will not be implemented there is no need to run this command from the BLK """ class uvm_reg_block(uvm_object): def __init__(self, name="uvm_reg_block"): super().__init__(name) self._regs = [] self.def_map = None self._is_locked = False self.hdl_paths = {} self.fields = [] self.root_path = "" self.child_blk = [] self.maps = [] self.parent_blk = None # optimize this by using dicationries with # TUPLES (existence_bool, handle) self.blk_maping = {} self.map_mapping = {} self.reg_mapping = {} self.blk_name = name self.header = name + " -- " self._has_cover = False self._is_cover_ion = False self._cover_on = False # is_locked def is_locked(self) -> bool: return self._is_locked # gen_message def gen_message(self, txt="") -> str: return f"{self.header, txt}" # clear_hdl_path def clear_hdl_path(self, kind="RTL"): uvm_not_implemeneted(self.header) # add_hdl_path def add_hdl_path(self, path: str, kind="RTL"): uvm_not_implemeneted(self.header) # has_hdl_path def has_hdl_path(self, kind: str) -> bool: uvm_not_implemeneted(self.header) # get_hdl_path def get_hdl_path(self, paths: list, kind=""): uvm_not_implemeneted(self.header) # get_full_hdl_path def get_full_hdl_path(self, paths: list, kind="", separator="."): uvm_not_implemeneted(self.header) # set_lock def set_lock(self): self._is_locked = True # blk_set_reg_mapping def blk_set_reg_mapping(self, reg: uvm_reg): self.reg_mapping[reg.get_name()] = True # blk_is_reg_mapped def blk_is_reg_mapped(self, reg: uvm_reg) -> bool: return self.reg_mapping[reg.get_name()] # blk_is_child_mapped def blk_is_child_mapped(self, in_blk) -> bool: if isinstance(in_blk, uvm_reg_block) is False: uvm_fatal( self.gen_message( "blk_is_child_mapped -- input block \ should be uvm_reg_block" ) ) else: return self.blk_maping[in_blk.get_name()] # blk_set_map_mapping def blk_set_map_mapping(self, map_i: uvm_reg_map): self.map_mapping[map_i.get_name()] = 1 # blk_is_map_mapped def blk_is_map_mapped(self, map_i: uvm_reg_map): self.map_mapping[map_i.get_name()] = 1 # configure def configure_blk(self, parent, hdl_path): if self.parent_blk is None: self.parent_blk = parent self.parent_blk.configure_blk(self) else: self.parent_blk = None # add HDL PATH as well self.add_hdl_path(hdl_path) # add_block def add_block(self, in_blk): if isinstance(in_blk, uvm_reg_block) is False: uvm_fatal( self.gen_message( "add_block -- input block must be \ uvm_reg_block type" ) ) # add to the BLK main mapping if in_blk not in self.child_blk: self.blk_maping[in_blk.get_name()] = 1 self.child_blk.append(in_blk) # blk_add_register def _add_register(self, reg): self._regs.append(reg) self.blk_set_reg_mapping(reg) # blk_get_def_map def blk_get_def_map(self): return self.def_map # get_blk_full_name def get_blk_full_name(self) -> str: if self.parent_blk is None: self.blk_name else: self.parent_blk + "." + self.blk_name # get_registers # 18.1.3.7 def get_registers(self, hier=uvm_hier_e.UVM_HIER) -> list: local_reg_collector = [] if self.is_locked() is True: for r in self._regs: if self.blk_is_reg_mapped(r) is True: local_reg_collector.append(r) if (hier == uvm_hier_e.UVM_HIER) and (len(self.child_blk) != 0): for b in self.child_blk: for r in b.get_registers(hier): local_reg_collector.append(r) else: uvm_fatal( self.gen_message( "get_registers -- register block must \ be locked" ) ) return local_reg_collector # get_fields # 18.1.3.8 def get_fields(self, hier=uvm_hier_e.UVM_HIER) -> list: local_field_collector = [] for r in self.get_registers(hier): for f in r.get_fields(): local_field_collector.append(f) if hier == uvm_hier_e.UVM_HIER: for blk in self.get_all_child_blk(): for f in blk.get_fields(hier): local_field_collector.append(f) return local_field_collector # get_all_child_blk def get_all_child_blk(self) -> list: local_blk_collector = [] for b in self.child_blk: if self.blk_is_child_mapped(b) is True: local_blk_collector.append(b) local_blk_collector.append(b.get_all_child_blk()) return local_blk_collector # blk_add_map def blk_add_map(self, map_i: uvm_reg_map): if self.is_locked() is True: uvm_fatal( self.gen_message( "blk_add_map -- register block should \ be locked" ) ) if map_i in self.map_mapping.keys() and self.map_mapping[map_i] is True: uvm_fatal(self.header) else: self.maps.append(map_i) if self.def_map is None: self.def_map = map_i # blk_create_map byte_addressing and byte_en # along with endianness not yet supported def blk_create_map(self, name: str, base_addr: int): lmap = uvm_reg_map.create(name) lmap.configure(self, base_addr) self.blk_add_map(lmap) # reset_blk def reset_blk(self): uvm_not_implemeneted(self.gen_message("reset_blk -- not implemented")) # set_default_map def set_default_map(self, mapi: uvm_reg_map): if self.blk_is_map_mapped(mapi) is True: self.def_map = mapi else: uvm_fatal( self.gen_message( "set_default_map required only \ internal Mapped maps as degfault map" ) ) # get_map_by_name def get_map_by_name(self, namei: str): if self.map_mapping[namei] == 1: return [m for m in self.maps if (m.get_name() == namei)] # TODO: search into child_blk else: return None # get_reg_by_name # 18.1.3.14 def get_reg_by_name(self, namei: str) -> uvm_reg: reg_list = list( filter(lambda reg: reg.get_name() == namei, self.get_registers()) ) return reg_list[0] if reg_list else None # get_field_by_name # 18.1.3.15 def get_field_by_name(self, namei: str) -> uvm_reg_field: field_list = list( filter(lambda field: field.get_name() == namei, self.get_fields()) ) return field_list[0] if field_list else None # set_coverage def set_coverage(self, is_on: bool): self._cover_on = self._has_cover and self._is_cover_on for rg in self.regs: rg.set_coverage(is_on) for mm in self.mems: mm.set_coverage(is_on) for blk in self.blks: blk.set_coverage(is_on) # sample_values def sample_values(self): for rg in self.regs: rg.sample_values() for blk in self.blks: blk.sample_values() # add_coverage def add_coverage(self): uvm_not_implemeneted(self.gen_message("add coverage not implemented")) # has_coverage def has_coverage(self): return self._has_cover # get_coverage def get_coverage(self): if self.has_coverage() is True: return self._cover_on else: return 0 # similar to convert2string def __str__(self) -> str: return f" {self.gen_message} \ self._regs : {self._regs} \ self.def_map : {self.def_map} \ self._is_locked : {self._is_locked} \ self.hdl_paths : {self.hdl_paths} \ self.fields : {self.fields} \ self.root_path : {self.root_path} \ self.child_blk : {self.child_blk} \ self.maps : {self.maps} \ self.parent_blk : {self.parent_blk} \ self.blk_maping : {self.blk_maping} \ self.map_mapping : {self.map_mapping} \ self.reg_mapping : {self.reg_mapping} \ self.blk_name : {self.blk_name}" pyuvm-4.0.1/pyuvm/s19_uvm_reg_field.py000066400000000000000000000531141507477334100177500ustar00rootroot00000000000000# Main Packages same as import uvm_pkg or uvm_defines. from typing import Tuple from pyuvm.s05_base_classes import uvm_object from pyuvm.s17_uvm_reg_enumerations import ( uvm_door_e, uvm_predict_e, uvm_reg_byte_en_t, uvm_reg_data_t, uvm_reg_policy_t, uvm_status_e, ) from pyuvm.s20_uvm_reg import uvm_reg from pyuvm.s21_uvm_reg_map import uvm_reg_map from pyuvm.s23_uvm_reg_item import uvm_reg_item from pyuvm.s24_uvm_reg_includes import ( access_e, check_t, error_out, path_t, uvm_error, uvm_fatal, uvm_reg_field_error_decoder, uvm_resp_t, ) ############################################ # Yet to be implemented TODO: ############################################ """ 1. Checking the set during a write happening on the Parent register 2. Checking if the write or read operation is ongoing 3. implement any reference needed for the BACKDOOR 4. implement the single field write method (should take the UVM_REG parent as reference) """ # Class declaration for register field class uvm_reg_field(uvm_object): # constructor def __init__(self, name="uvm_reg_field"): super().__init__(name) self.access_list = uvm_reg_policy_t self._parent = None self._size = 0 self._lsb_pos = None # Start with empty access self._access = "" self._err_list = [] self._is_volatile = False self._reset = 0 self._field_mirrored = 0 self._value = 0 # Keep desired value as 0 # TODO: add eventually support for PYVSC for randomization self._desired = 0 self._config_done = False self._has_been_writ = False self._response = uvm_resp_t self._header = name + " -- " # These 2 flags cannot change for fields # since are part of the parent register self._error_on_read = False self._error_on_write = False self._fname = "" self._lineno = 0 # configure def configure( self, parent: uvm_reg, size: int, lsb_pos: int, access: str, is_volatile: bool, reset: int, ): self._parent = parent self._size = size self._lsb_pos = lsb_pos self._access = access self._is_volatile = is_volatile self._reset = reset self._config_done = False self._compare = check_t # Additional Checking # Ignore randomization if the field is known not to be writeable # i.e. not "RW", "WRC", "WRS", "WO", "W1", "WO1" # TODO: add eventually support for PYVSC for randomization self._desired = 0 # Check if size is 0 if self._size == 0: uvm_fatal( self._header, "Size of a filed \ cannot be 0 MINIMUM allowed is 1", ) # Check if policy is a valid policy if self._access not in self.access_list: self._access = "NOACCESS" # These 2 flags cannot # change for fields since are part of the parent register self._error_on_read = self._parent.throw_error_on_read self._error_on_write = self._parent.throw_error_on_write # one configure is called let's lock self.field_lock() # check the reset value is not beyond the MAX if self._reset >= (2**self._size): uvm_error( self._header, f"Reset value for REG \ field : {self.get_name()} is [{self._reset}] \ is beyond the MAX valoue given \ by the size [{((2**self._size) - 1)}]", ) # Add field parent._add_field(self) # lock method meaning the configuration is # done and we can unlock all the internal methods def field_lock(self): self._config_done = True # field_unlock def field_unlock(self): self._config_done = False # adding error to the main error list def _add_error(self, value): self._err_list.append(value) # checking mechanisms for error list def _check_(self): if len(self._err_list) > 0: (print(self._err_list[el] for el in range(len(self._err_list)))) # set_compare def set_compare(self, check_type: check_t): self._check = check_type # set_throw_error_on_read def set_throw_error_on_read(self, teor=False): self._error_on_read = teor # set_throw_error_on_write def set_throw_error_on_write(self, teow=False): self._error_on_write = teow # get_compare def get_compare(self): return self._check # get_full_name def get_full_name(self): return self._parent.get_full_name() + "." + self.get_name() # get_parent def get_parent(self): if self._config_done is False: error_out( self._header, "Configure for a \ field must be called called \ before any other member method", ) self._add_error(uvm_reg_field_error_decoder.CONFIGURE_MUST_BE_CALLED_BEFORE) return None else: return self._parent # get_lsb_pos def get_lsb_pos(self) -> int: if self._config_done is False: error_out( self._header, "Configure for a \ field must be called called \ before any other member method", ) self._add_error(uvm_reg_field_error_decoder.CONFIGURE_MUST_BE_CALLED_BEFORE) return 0 else: return self._lsb_pos # get_msb_pos def get_msb_pos(self) -> int: if self._config_done is False: error_out( self._header, "Configure for a \ field must be called called before\ any other member method", ) self._add_error(uvm_reg_field_error_decoder.CONFIGURE_MUST_BE_CALLED_BEFORE) return 0 else: return self.get_lsb_pos() + self.get_n_bits() - 1 # get_n_bits def get_n_bits(self) -> int: if self._config_done is False: error_out( self._header, "Configure for a field must \ be called called before any other member method", ) self._add_error(uvm_reg_field_error_decoder.CONFIGURE_MUST_BE_CALLED_BEFORE) return 0 else: return self._size # get_access def get_access(self) -> str: if self._config_done is False: error_out( self._header, "Configure for a field \ must be called called before any other member method", ) self._add_error(uvm_reg_field_error_decoder.CONFIGURE_MUST_BE_CALLED_BEFORE) return "" else: return self._access # is_known_access def is_known_access(self) -> bool: return self._access in self.access_list # is_volatile def is_volatile(self) -> bool: if self._config_done is False: error_out( self._header, "Configure for\ a field must be called called \ before any other member method", ) return False else: return self._is_volatile # get_reset def get_reset(self) -> int: if self._config_done is False: error_out( self._header, "Configure for a \ field must be called called before \ any other member method", ) self._add_error(uvm_reg_field_error_decoder.CONFIGURE_MUST_BE_CALLED_BEFORE) return 0 else: return self._reset # atomic get value def get(self): return self._desired # atomic reset value def reset(self): self._field_mirrored = self._reset self._has_been_writ = False self._response = uvm_resp_t.PASS_RESP # atomic get value def get_value(self): return self._field_mirrored # Atomic set access value def set_access(self, access_value): if not isinstance(access_value, str): error_out( self._header, "Access set for reg \ field needs to be a string", ) self._add_error( uvm_reg_field_error_decoder.ACCESS_TYPE_NEEDS_TO_BE_A_STRING ) elif access_value is self.access_list: self._access = access_value else: error_out( self._header, "Access value \ provided is not part of possible access values", ) self._add_error(uvm_reg_field_error_decoder.ACCESS_VALUE_OUT_OF_LIST) self._access = "NOACCESS" # Atomic set response status for fields def set_response(self, f_response: uvm_resp_t): self._response = f_response # Atomic get status from fields def get_response(self): return self._response # atomic set value def field_set(self, value: int): # Define an all 1 values _mask = (1 << self._size) - 1 # check if value given is bigger than the size of field # Ideally the set value should be checked against the parent # register being accessed. # if the parent is under WRITE there should be no set called. # Not yet implemenmted. # # field_access = self.get_access() # Return value based on the access if field_access in ("RO", "RC", "RS", "NOACCESS"): self._desired = self._desired # Leave the desired value stable elif field_access in ("RW", "WRC", "WRS", "WO"): self._desired = value elif field_access in ("WC", "WCRS", "WOC"): self._desired = 0 elif field_access in ("WS", "WSRC", "WOS"): self._desired = _mask elif field_access in ("W1C", "W1CRS"): self._desired = self._desired & (~value) elif field_access in ("W1S", "W1SRC"): self._desired = self._desired | value elif field_access == "W1T": self._desired = self._desired ^ value elif field_access in ("W0C", "W0CRS"): self._desired = self._desired & value elif field_access in ("W0S", "W0SRC"): self._desired = self._desired | (~value & _mask) elif field_access == "W0T": self._desired = self._desired ^ (~value & _mask) elif field_access in ("W1", "WO1"): if self._has_been_writ is False: self._desired = value else: self._desired = value # Check the Direction and the access type along with the enable error # flags (if error is supposed to be thrown then send it out) def predict_response(self, path: path_t, direction: access_e): # Check the Direction and the access type along with the enable error # flags (if error is supposed to be thrown then send it out) # if we try to write a 1 when the access on write will require the 0 # to generate some effect if ( (direction == uvm_predict_e.UVM_PREDICT_WRITE) & (self.get_access() in ["RO", "RW", "RC", "RS"]) & (path == uvm_door_e.UVM_FRONTDOOR) ): self.set_response( uvm_resp_t.PASS_RESP if (self._error_on_write is False) else uvm_resp_t.ERROR_RESP ) elif ( (direction == uvm_predict_e.UVM_PREDICT_READ) & ( self.get_access() in [ "WO", "WOC", "WOS", "WO1", "NOACCESS", "W1", "W1T", "W0T", "WC", "WS", "W1C", "W1S", "W0C", "W0S", ] ) & (path == uvm_door_e.UVM_FRONTDOOR) ): self.set_response( uvm_resp_t.PASS_RESP if (self._error_on_read is False) else uvm_resp_t.ERROR_RESP ) else: # This will include the BACKDOOR self.set_response(uvm_resp_t.PASS_RESP) # Since there is no Switch case in python we use a simple switch case # Where error is mentioned it depends on _error_on_write flag, no effect # will be translated in Error response if flag is enable # "RO" - W: no effect, R: no effect # "RW" - W: as-is, R: no effect # "RC" - W: no effect, R: clears all bits # "RS" - W: no effect, R: sets all bits # "WRC" - W: as-is, R: clears all bits # "WRS" - W: as-is, R: sets all bits # "WC" - W: clears all bits, R: no effect # "WS" - W: sets all bits, R: no effect # "WSRC" - W: sets all bits, R: clears all bits # "WCRS" - W: clears all bits, R: sets all bits # "W1C" - W: 1/0 clears/no effect on matching bit, R: no effect # "W1S" - W: 1/0 sets/no effect on matching bit, R: no effect # "W1T" - W: 1/0 toggles/no effect on matching bit, R: no effect # "W0C" - W: 1/0 no effect on/clears matching bit, R: no effect # "W0S" - W: 1/0 no effect on/sets matching bit, R: no effect # "W0T" - W: 1/0 no effect on/toggles matching bit, R: no effect # "W1SRC" - W: 1/0 sets/no effect on matching bit, R: clears all bits # "W1CRS" - W: 1/0 clears/no effect on matching bit, R: sets all bits # "W0SRC" - W: 1/0 no effect on/sets matching bit, R: clears all bits # "W0CRS" - W: 1/0 no effect on/clears matching bit, R: sets all bits # "WO" - W: as-is, R: error # "WOC" - W: clears all bits, R: error # "WOS" - W: sets all bits, R: error # "W1" - W: first one after ~HARD~ reset is as-is, # other W have no effects, R: no effect # "WO1" - W: first one after ~HARD~ reset is as-is, # other W have no effects, R: error # "NOACCESS" - W: no effect, R: no effect def predict_write(self, cur_val, wr_val) -> int: # Define an all 1 values mask = (1 << self._size) - 1 # Return value based on the access type field_access = self.get_access() if field_access in ("RO", "RC", "RS", "NOACCESS"): return cur_val elif field_access in ("RW", "WRC", "WRS", "WO"): return wr_val elif field_access in ("WC", "WCRS", "WOC"): return 0 elif field_access in ("WS", "WSRC", "WOS"): return mask elif field_access in ("W1C", "W1CRS"): return cur_val & (~wr_val) elif field_access in ("W1S", "W1SRC"): return cur_val | wr_val elif field_access in ("W1T"): return cur_val ^ wr_val elif field_access in ("W0C", "W0CRS"): return cur_val & wr_val elif field_access in ("W0S", "W0SRC"): return cur_val | (~wr_val & mask) elif field_access in ("W0T"): return cur_val ^ (~wr_val & mask) elif field_access in ("W1", "WO1"): if self._has_been_writ: return cur_val else: return wr_val else: return wr_val # atomic predict value based on the operation (READ) # Where error is mentioned it depends on _error_on_read flag, no effect # will be translated in Error response if flag is enable # Since there is no Switch case in python we use a simple switch case # "RO" - W: no effect, R: return the mirrored value (reset one) # "RW" - W: as-is, R: as-is # "RC" - W: no effect, R: clears all bits # "RS" - W: no effect, R: sets all bits # "WRC" - W: as-is, R: clears all bits # "WRS" - W: as-is, R: sets all bits # "WC" - W: clears all bits, R: no effect # "WS" - W: sets all bits, R: no effect # "WSRC" - W: sets all bits, R: clears all bits # "WCRS" - W: clears all bits, R: sets all bits # "W1C" - W: 1/0 clears/no effect on matching bit, R: no effect # "W1S" - W: 1/0 sets/no effect on matching bit, R: no effect # "W1T" - W: 1/0 toggles/no effect on matching bit, R: no effect # "W0C" - W: 1/0 no effect on/clears matching bit, R: no effect # "W0S" - W: 1/0 no effect on/sets matching bit, R: no effect # "W0T" - W: 1/0 no effect on/toggles matching bit, R: no effect # "W1SRC" - W: 1/0 sets/no effect on matching bit, R: clears all bits # "W1CRS" - W: 1/0 clears/no effect on matching bit, R: sets all bits # "W0SRC" - W: 1/0 no effect on/sets matching bit, R: clears all bits # "W0CRS" - W: 1/0 no effect on/clears matching bit, R: sets all bits # "WO" - W: as-is, R: error # "WOC" - W: clears all bits, R: error # "WOS" - W: sets all bits, R: error # "W1" - W: first one after ~HARD~ reset is as-is, other W have no # effects, R: no effect # "WO1" - W: first one after ~HARD~ reset is as-is, other W have no # effects, R: error # "NOACCESS" - W: no effect, R: no effect def predict_read(self) -> Tuple[bool, int]: acc: str = self.get_access() if acc in ["RC", "WRC", "WSRC", "W1SRC", "W0SRC"]: # Set Value to 0 since READ will clear return True, 0 elif acc in ["RS", "WRS", "WCRS", "W1CRS", "W0CRS"]: # Set Value to 1 since READ will set to 1 return True, (1 << self._size) - 1 elif acc in [ "WO", "WOC", "WOS", "WO1", "NOACCESS", "W1", "W1T", "W0T", "WC", "WS", "W1C", "W1S", "W0C", "W0S", ]: # Set Value to the reset since READ will have no effect return True, self._reset return False, 0 # Process of predictiong def do_predict( self, rw: uvm_reg_item, kind: uvm_predict_e = uvm_predict_e.UVM_PREDICT_DIRECT, be: uvm_reg_byte_en_t = -1, ): field_val = rw.get_value(0) & ((1 << self._size) - 1) self.predict_response(rw.get_door(), kind) if rw.get_status() != uvm_status_e.UVM_NOT_OK: rw.set_status(uvm_status_e.UVM_IS_OK) if not be & 0b1: return self._fname = rw.get_fname() self._lineno = rw.get_line() if kind == uvm_predict_e.UVM_PREDICT_WRITE: if ( rw.get_door() == uvm_door_e.UVM_FRONTDOOR or rw.get_door() == uvm_door_e.UVM_PREDICT ): field_val = self.predict_write(self._field_mirrored, field_val) self._has_been_writ = True # here need to do uvm_reg_field_cb_iter logic field_val &= (1 << self._size) - 1 elif kind == uvm_predict_e.UVM_PREDICT_READ: if ( rw.get_door() == uvm_door_e.UVM_FRONTDOOR or rw.get_door() == uvm_door_e.UVM_PREDICT ): predict_valid, tmp_val = self.predict_read() if predict_valid: field_val = tmp_val # here need to do uvm_reg_field_cb_iter logic field_val &= (1 << self._size) - 1 elif kind == uvm_predict_e.UVM_PREDICT_DIRECT: # There should be a check for the register's busy status, # but since there is no 'busy' field in the register class, # there is no such check here. pass else: self._add_error( uvm_reg_field_error_decoder.WRONG_COMBINATION_PREDICTION_DIRECTION ) uvm_error( self._header, "Wrong combination of PATH \ - PREDICTION TYPE and DIRECTION on pyuvm_field \ -- field_predict function", ) self._field_mirrored = field_val self._desired = field_val self._value = field_val # Main field prediction function to be used to predict # mirrored value for pyuvm_fields # 18.5.5.17 def predict( self, value: uvm_reg_data_t, be: uvm_reg_byte_en_t = -1, kind: uvm_predict_e = uvm_predict_e.UVM_PREDICT_DIRECT, path: uvm_door_e = uvm_door_e.UVM_FRONTDOOR, map: uvm_reg_map = None, fname: str = "", lineno: int = 0, ) -> bool: rw = uvm_reg_item() rw.set_value(value) rw.set_door(path) rw.set_map(map) rw.set_fname(fname) rw.set_line(lineno) self.do_predict(rw, kind, be) if rw.get_status() == uvm_status_e.UVM_NOT_OK: return False return True # String representation of pyuvm_reg_filed class content def __str__(self) -> str: return f" {self._header} \ parent : {self._parent} \ size: {self._size} \ lsb_pos: {self._lsb_pos} \ access: {self._access} \ error: {self._err_list} \ is_volatile:{self._is_volatile} \ reset: {self._reset} \ mirrored: {self._field_mirrored} \ value: {self._value}" pyuvm-4.0.1/pyuvm/s20_uvm_reg.py000066400000000000000000000313651507477334100166010ustar00rootroot00000000000000# Main Packages same as import uvm_pkg or uvm_defines.svh from pyuvm.s05_base_classes import uvm_object from pyuvm.s17_uvm_reg_enumerations import ( uvm_door_e, uvm_predict_e, uvm_reg_byte_en_t, uvm_reg_data_t, uvm_reg_policy_t, uvm_status_e, ) from pyuvm.s21_uvm_reg_map import uvm_reg_map from pyuvm.s23_uvm_reg_item import uvm_reg_item from pyuvm.s24_uvm_reg_includes import ( check_t, error_out, path_t, uvm_fatal, uvm_not_implemeneted, uvm_reg_error_decoder, uvm_resp_t, ) # Class declaration class uvm_reg(uvm_object): # Constructor def __init__(self, name="uvm_reg", reg_width=32): super().__init__(name) self._parent = None # Reference to the parent Reg Block self._fields = [] self._err_list = [] self._mirrored: int = 0 self._desired: int = 0 self._reset: int = 0 self._sum: int = 0 self._header: str = name + " -- " self._address: str = "0x0" self._path: str = "" self._width = reg_width # If set those 2 flags will override fields values, and if set to True # the fields will report error response # in case of (Operation,Access) expect an error self.throw_error_on_read: bool = False self.throw_error_on_write: bool = False # Internal variables used to detect if an operation is in progress # there were will be no difference between read and write # we cannot read and write from to the same register at tsame time self._op_in_progress: bool = False self._is_cover_ion: bool = False self._cover_on: bool = False self._maps = [] # Collection to the maps owning the specific register self._access_policy: str = "RW" self._fname = "" self._lineno = 0 # configure def configure( self, parent, address: str, hdl_path: str, throw_error_on_read: bool = False, throw_error_on_write: bool = False, ): self._parent = parent self._address = address self._path = hdl_path self._sum = 0 # If set those 2 flags will override fields values, # and if set to True the fields will report error response # in case of (Operation,Access) expect an error self.throw_error_on_read = throw_error_on_read self.throw_error_on_write = throw_error_on_write # Call the build function before adding any register to the main BLOCK self.build() # TODO: Check if the lock for the above register is set # TODO: Add a reference to the parent MAP # TODO: Add register to the Master Reg Block parent._add_register(self) # adding error to the main error list def _add_error(self, value): self._err_list.append(value) # add_map def add_map(self, map_i: uvm_reg_map): if map_i in self._maps: uvm_fatal(self.gen_message(f"Adding same map {map_i} twice")) else: self._maps.append(map_i) # create a message def gen_message(self, mss: str) -> str: return self._header + mss # checking mechanisms for error list def check_err_list(self): if len(self._err_list) > 0: for el in range(len(self._err_list)): print(f"List has error[{el}]: {self._err_list[el]}") else: print("No error in list") # get parent def get_parent(self): return self._parent # set_access_policy def set_access_policy(self, policy: str = "RW"): if policy in uvm_reg_policy_t: self._access_policy = policy else: uvm_fatal( self.gen_message( f"given access \ policy not correct: {policy}" ) ) # set_access_policy def get_access_policy(self) -> str: return self._access_policy # get_fields Return fields in canonical order (LSB to MSB) def get_fields(self): return self._fields # get size function def get_reg_size(self) -> int: if self._width == 0: error_out(self._header, "_width cannot be 0") self._add_error(uvm_reg_error_decoder.REG_SIZE_CANNOT_BE_ZERO.name) return 0 else: return self._width # setting the desired value, if this one is set we can # avoid using the Value in write def set_desired(self, value): for f in self._fields: f.field_set(value >> f.get_lsb_pos()) # _add_field def _add_field(self, field): # - field not None if field is None: error_out(self._header, "_add_field Fields cannot be None") self._add_error(uvm_reg_error_decoder.FIELD_CANNOT_BE_NONE.name) # - field not already added if field in self._fields: error_out( self._header, f"_add_field: Fields {field.get_name()} \ is already added", ) self._add_error(uvm_reg_error_decoder.FIELD_ALREADY_ADDED.name) # - if we did not error out we can append the field to the list self._fields.append(field) # - field fits in reg self._sum += field.get_n_bits() if self._width < self._sum: error_out( self._header, f"_add_field: Fields {field.get_name()} \ doesn't fit into a {self._width} bits register", ) self._add_error(uvm_reg_error_decoder.FIELD_DOESNT_FIT_INTO_REG.name) # - field doesn't overlap with any other field if len(self._fields) > 1: msb_pos = self._fields[self._fields.index(field) - 1].get_msb_pos() if field.get_lsb_pos() - msb_pos <= 0: error_out( self._header, f"_add_field: \ Fields {field.get_name()} overlap \ with field \ {self._fields[self._fields.index(field) - 1].get_name()}", ) self._add_error(uvm_reg_error_decoder.FIELD_OVERLAPPING_ERROR.name) # _set_lock def _set_lock(self): for _f in self._fields: _f.field_lock() # _set_unlock def _set_unlock(self): for _f in self._fields: _f.field_unlock() # 18.4.4.15 # predict def predict( self, value: uvm_reg_data_t, be: uvm_reg_byte_en_t = -1, kind: uvm_predict_e = uvm_predict_e.UVM_PREDICT_DIRECT, path: uvm_door_e = uvm_door_e.UVM_FRONTDOOR, map: uvm_reg_map = None, fname: str = "", lineno: int = 0, ) -> bool: rw = uvm_reg_item() rw.set_value(value) rw.set_door(path) rw.set_map(map) rw.set_fname(fname) rw.set_line(lineno) self.do_predict(rw, kind, be) if rw.get_status() == uvm_status_e.UVM_NOT_OK: return False return True # do_predict def do_predict( self, rw: uvm_reg_item, kind: uvm_predict_e = uvm_predict_e.UVM_PREDICT_DIRECT, be: uvm_reg_byte_en_t = -1, ): reg_value = rw.get_value(0) self._fname = rw.get_fname() self._lineno = rw.get_line() if rw.get_status() == uvm_status_e.UVM_IS_OK: # Here reg busy check for field in self.get_fields(): rw.set_value( (reg_value >> field.get_lsb_pos()) & ((1 << field.get_n_bits()) - 1) ) field.do_predict(rw, kind, (be >> int(field.get_lsb_pos() / 8))) rw.set_value(reg_value) # Get mirrored value def get_mirrored_value(self): self._mirrored = 0 for f in self.get_fields(): updt_v = f.get_value() << f.get_lsb_pos() self._mirrored = self._mirrored | updt_v return self._mirrored # get_address def get_address(self): return self._address # get desired value def get_desired(self): self._desired = 0 for f in self._fields: self._desired = self._desired | (f.get() << f.get_lsb_pos()) return self._desired # Reset def reset(self): for f in self._fields: f.reset() # placeholder to fix Flake8 line error value = f.get_value() << f.get_lsb_pos() self._mirrored = self._mirrored | value # Build internal function def build(self): """ This function needs to be implemented into the child class create each fields and invoke the configure from each field """ uvm_not_implemeneted( self.gen_message( "Calling Build when not \ implemented by the user" ) ) # Write Method (TASK) async def write( self, value: int, map: uvm_reg_map, path: path_t, check: check_t ) -> uvm_resp_t: # This Task should implement the main read method via only FRONTDOOR # TODO: BACKDOOT and USER FRONTDOOR are missing # This Task returns only the operation status # Local Variables to be returned status = uvm_resp_t # TODO: # Given the map we do not check if the current register # exists in the map # (redundant check) since the register is directly taken from the MAP # We check instead if the map is set and if # only one exists (multiple access) # The map the register belongs to should not be unique # could easily be a list of MAPS # every map correspond a separate unique HW interface (BUS) # to the specific target register # the access should be carried out on each MAP # check if any operation is in progress for the given register if self._op_in_progress is False: self._op_in_progress = True # TODO: Implement as FOR LOOP if map is not None: if value is not None: status = await map.process_write_operation( self.get_address(), value, path, check ) elif value is None: status = await map.process_write_operation( self.get_address(), self.get_desired(), path, check ) else: error_out(self._header, "WRITE: map cannot be NULL") self._op_in_progress = False else: uvm_fatal( self._header, "write cannot perform an operation while \ another is in progress", ) # Return from Task return status # Read Method (TASK) async def read(self, map: uvm_reg_map, path: path_t, check: check_t): # This Task should implement the main read method via only FRONTDOOR # TODO: BACKDOOT and USER FRONTDOOR are missing # This Task returns only the operation status and the read value # (0 is status is error) # Local Variables to be returned status = uvm_resp_t # TODO: # Given the map we do not check if the current # register exists in the map # (redundant check) since the register is directly taken from the MAP # We check instead if the map # is set and if only one exists (multiple access) # The map the register belongs to should not be unique could easily\ # be a list of MAPS # every map correspond a separate unique HW interface (BUS) # to the specific target register # the access should be carried out on each MAP # check if any operation is in progress for the given register if self._op_in_progress is False: self._op_in_progress = True if map is not None: status, read_data = await map.process_read_operation( self.get_address(), path, check ) if status == uvm_resp_t.ERROR_RESP: read_data = 0 else: error_out(self._header, "READ: map cannot be NULL") self._op_in_progress = False else: uvm_fatal( self._header, "read cannot perform an operation while \ another is in progress", ) # Return from Task return status, read_data # Coverage API # set_coverage def set_coverage(self, is_on: bool): self._cover_on = self._has_cover and self._is_cover_on # sample_values def sample_values(self): uvm_not_implemeneted( self.gen_message( "sample_values \ used but not implemented" ) ) pyuvm-4.0.1/pyuvm/s21_uvm_reg_map.py000066400000000000000000000363631507477334100174420ustar00rootroot00000000000000# Import Main Packages from pyuvm.s05_base_classes import uvm_object from pyuvm.s14_15_python_sequences import uvm_sequence, uvm_sequencer from pyuvm.s23_uvm_reg_item import uvm_reg_item from pyuvm.s24_uvm_reg_includes import ( access_e, check_t, enable_auto_predict, path_t, uvm_error, uvm_fatal, uvm_not_implemeneted, uvm_reg_bus_op, ) from pyuvm.s25_uvm_adapter import uvm_reg_adapter from pyuvm.s26_uvm_predictor import uvm_reg_predictor """ TODO: the following must be completed 1. implement add_mem 2. implement m_set_mem_offset 3. implement get_fields 4. implement get_memories 5. implement get_virtual_registers 6. implement get_virtual_fields 7. implement get_mem_map_info 8. implement get_reg_map_info 9. implement set_base_addr 10. implement get_size """ # Class declaration: uvm_reg_map class uvm_reg_map(uvm_object): # Constructor def __init__(self, name="uvm_reg_map"): super().__init__(name) # this must be uvm_reg_block self._parent = None # this is equivalent to offset in uvm map reference self._offset = 0 self._regs = {} self.header = name + " -- " self._submaps = {} # this is set in case of this map is a submap of another map self._parent_map = None self._is_a_submap = False self._reset_kind = ["SOFT", "HARD"] self.predictor = None # Function called by the REG_BLOCK create_map funcction # the parent value here should be a uvm_reg_block instance type def configure(self, parent, base_addr): # TODO: check if the parent is a uvm_reg_block type self._parent = parent self._offset = base_addr # No support for Byte_Addressing nor for Byte_en TODO: add # add_parent_map def add_parent_map(self, parent_map): if self._parent_map is None: self._parent_map = parent_map # if there is a parent map then this map is automatically a submap self._parent_map.add_submap(self) else: uvm_error( self.header, "add_parent_map -- cannot add parent map \ if the parentmap is already set", ) # str2int def _str2int(self, istr: str = "") -> int: return int(istr, 16) # int2hex def _int2hex(self, ival: int = 0) -> str: return hex(ival) # gen_message def gen_message(self, txt="") -> str: return f"{self.header} {txt}" # get_full_name def get_full_name(self): return self._parent.get_full_name() + "." + self.get_name() # get_parent def get_parent(self): return self._parent # get_offset def get_offset(self): if self._is_a_submap is True: return self.get_parent_map().get_offset() else: return self._offset # add_reg def add_reg(self, reg, offset: str = "0x0", rights: str = "RW"): reg.add_map(self) sum_offset = self._str2int(offset) + self._str2int(reg.get_address()) reg.set_access_policy(rights) self._regs[self._int2hex(sum_offset)] = reg # get_registers def get_registers(self, as_dict=False): reg_dict = self._regs for m in self._submaps: reg_dict = {**reg_dict, **m._regs} if as_dict is False: return list(reg_dict.values()) else: return reg_dict # get_reg_by_offset def get_reg_by_offset(self, offset): local_dict = self.get_registers(as_dict=True) return local_dict[offset] # set_predictor def set_predictor(self, predictor): if isinstance(predictor, uvm_reg_predictor): self.predictor = predictor else: uvm_error( self.header, "predictor should be \ type of uvm_reg_predictor", ) # get_predictor def get_predictor(self): if self.predictor is None: # TODO: this should be only a warning since depends # on the prediction type if enable_auto_predict is True: uvm_error(self.header, "predictor Not set") else: return self.predictor # set_adapter def set_adapter(self, adapter): if isinstance(adapter, uvm_reg_adapter): self.adapter = adapter else: uvm_error(self.header, "adapter should be type of uvm_reg_adapter") # get_adapter def get_adapter(self): if self.adapter is None: uvm_error(self.header, "Adapter Not set") else: return self.adapter # set_sequencer def set_sequencer(self, sequencer): if isinstance(sequencer, uvm_sequencer): self.sequencer = sequencer else: uvm_error(self.header, "setting a wrong sequencer type") # get_sequencer def get_sequencer(self): if self.sequencer is None: uvm_error(self.header, "uvm_reg_map sequencer is not set") else: return self.sequencer # add_submap def add_submap(self, submap): # we cannot add a submap to a MAP that belongs to another BLK # maps or submaps should belong to the same BLK parent if self.get_parent() != submap.get_parent(): uvm_error( self.header, f"add_submap -- cannot add submap \ {submap.get_parent()} to map {self.get_parent()} \ if the parent BLK is different", ) # cannot add a submap that has been already added as SUBMAP # of another map if submap._is_a_submap is True: uvm_error( self.header, f"add_submap -- cannot add submap \ {submap.get_name()} to map {self.get_name()} \ because the submap is already a submap of another map", ) else: submap._is_a_submap = True self._submaps[submap.get_name()] = submap self._submaps[submap.get_name() + "_mapping_flag"] = True submap.add_parent_map(self) # TODO: add a check for n_bytes # submaps should never differ in the n-bytes transferred (bus interface # is shared per submaps) hence there should be only 1 n-bytes supported # reset def reset(self, reset_type: str): if reset_type in self._reset_kind: for rg in self.get_registers(): rg.reset() else: uvm_not_implemeneted( self.header, f"reset -- {reset_type} is not \ mapped as type of reset \ available values are {self._reset_kind}", ) # verify_map_config def verify_map_config(self): # Make sure there is a generic payload sequence for each map # in the model and vice-versa if this is a root sequencer rmap = self.get_root_map() if rmap.get_adapter() is None: uvm_fatal( self.header, f"Map {rmap.get_name()} doesn't have \ adapter set", ) if rmap.get_sequencer() is None: uvm_fatal( self.header, f"Map {rmap.get_name()} doesn't have \ sequencer set", ) # ------------ # get methods # ------------ # get_parent_map def get_parent_map(self): return self._parent_map # get_root_map def get_root_map(self): if self.get_parent_map() is None: return self else: return self.get_parent_map().get_root_map() # get_n_bytes def get_n_bytes(self): uvm_not_implemeneted(self.header, "get_n_bytes -- not implemented") # get_endian def get_endian(self): uvm_not_implemeneted(self.header, "get_endian -- not implemented") # get_submaps def get_submaps(self, as_dict=False): if as_dict is True: return self._submaps else: return list(self._submaps.values) # check integrity of process def check_process_integrity(self, adapter=None, reg_item=None): # check if the input adapter is none if adapter is None: # TODO: here basically we should be creating a local base sequence # we should be getting the sequencers from the adapter # error pout if NONE and call the start ITEM using the # reg_item as sequence_item is then up to the Driver to # figure out how to proceed uvm_fatal(self.gen_message("adapter is not assigned to MAP")) elif isinstance(adapter, uvm_reg_adapter) is False: uvm_fatal(self.gen_message("adapter is not correct type")) # check if the reg item is set if reg_item is None: uvm_fatal(self.gen_message("reg_item is not assigned to MAP")) elif isinstance(reg_item, uvm_reg_item) is False: uvm_fatal(self.gen_message("reg_item is not correct type")) # check in case the Adapter is set # if the parent sequence is set by User if adapter.get_parent_sequence() is not None: reg_item.set_parent_sequence(adapter.get_parent_sequence()) else: # if the parent sequence is not set in the adapter we need to # create a base sequence and assign it internally # to the reg_item there is no need to use the factory # we are not gonna use the factory constructor base_seq = uvm_sequence("base_seq") reg_item.set_parent_sequence(base_seq) adapter.set_parent_sequence(base_seq) # # OPERATION PROCESS # # process_write_operation async def process_write_operation( self, reg_address, data_to_be_written, path: path_t, check: check_t ): # Get the sequencer and the adapter local_adapter = self.get_adapter() # Build a local reg_item # TODO: this should come as input of the main process operation item = uvm_reg_item() item.set_kind(access_e.UVM_WRITE) item.set_value(data_to_be_written) item.set_door(path) item.set_map(self) item.set_parent_sequence(None) # check if we pass this point we are ready to go self.check_process_integrity(local_adapter, item) local_sequencer = self.get_sequencer() # check if the Path is set to BACKDOOR, FRONTDOOR or USER_FRONTDOOR if path is path_t.BACKDOOR: uvm_not_implemeneted(self.header, "BACKDOOR not implemented") elif path is path_t.USER_FRONTDOOR: uvm_not_implemeneted(self.header, "USER_FRONTDOOR not implemented") elif path is path_t.FRONTDOOR: # Populate internal Item local_bus_op = uvm_reg_bus_op() local_bus_op.kind = access_e.UVM_WRITE local_bus_op.addr = reg_address local_bus_op.n_bits = self._regs[reg_address].get_reg_size() local_bus_op.byte_en = local_adapter.get_byte_en() local_bus_op.data = data_to_be_written # Parse the local bus operation with the adapter # give the ITEM once to the adapter so it can # eventually fecth the extension element local_adapter.set_item(item) bus_req = local_adapter.reg2bus(local_bus_op) local_adapter.set_item(None) # Get the sequence and start local_sequence = local_adapter.get_parent_sequence() # set the sequencer to the local sequence local_sequence.sequencer = local_sequencer # Start the sequence on local sequencer await local_sequence.start_item(bus_req) await local_sequence.finish_item(bus_req) # Get the sequence item from the local sequence # Assign the response and read data back local_adapter.bus2reg(bus_req, local_bus_op) # Invoke the prediction if enable_auto_predict is True: local_predictor = self.get_predictor() local_predictor.predict(local_bus_op, check) else: pass # uvm_not_implemeneted(self.header, # "EXPLICIT_PREDICTION not implemented") # assign status return local_bus_op.status # process_read_operation async def process_read_operation(self, reg_address, path: path_t, check: check_t): # Get the sequencer and the adapter local_adapter = self.get_adapter() # Build a local reg_item # TODO: this should come as input of the main process operation item = uvm_reg_item() item.set_kind(access_e.UVM_WRITE) item.set_door(path) item.set_map(self) item.set_parent_sequence(None) # check if we pass this point we are ready to go self.check_process_integrity(local_adapter, item) local_sequencer = self.get_sequencer() # check if the Path is set to BACKDOOR, FRONTDOOR or USER_FRONTDOOR if path is path_t.BACKDOOR: uvm_not_implemeneted(self.header, "BACKDOOR not implemented") elif path is path_t.USER_FRONTDOOR: uvm_not_implemeneted(self.header, "USER_FRONTDOOR not implemented") elif path is path_t.FRONTDOOR: # Populate internal Item local_bus_op = uvm_reg_bus_op() local_bus_op.kind = access_e.UVM_READ local_bus_op.addr = reg_address local_bus_op.n_bits = self._regs[reg_address].get_reg_size() local_bus_op.byte_en = local_adapter.get_byte_en() # Parse the local bus operation with the adapter # give the ITEM once to the adapter so it can # eventually fecth the extension element local_adapter.set_item(item) bus_req = local_adapter.reg2bus(local_bus_op) local_adapter.set_item(None) # Get the sequence and start local_sequence = local_adapter.get_parent_sequence() # set the sequencer to the local sequence local_sequence.sequencer = local_sequencer # Start the sequence on local sequencer await local_sequence.start_item(bus_req) await local_sequence.finish_item(bus_req) # Get the sequence item from the local sequence # Assign the response and read data back local_adapter.bus2reg(bus_req, local_bus_op) # Invoke the prediction if enable_auto_predict is True: local_predictor = self.get_predictor() local_predictor.predict(local_bus_op, check) else: pass # uvm_not_implemeneted(self.header, # "EXPLICIT_PREDICTION not implemented") # assign status return local_bus_op.status, local_bus_op.data # print of uvm_reg_map similar to convert2string def __str__(self) -> str: return f" {self.header} \ self._parent : {self._parent} \ self._offset : {self._offset} \ self._regs : {self._regs} \ self.name : {self.get_name()}" pyuvm-4.0.1/pyuvm/s22_uvm_mem.py000066400000000000000000000005621507477334100165770ustar00rootroot00000000000000# Main Packages same as import uvm_pkg or uvm_defines.svh from pyuvm.s05_base_classes import uvm_object from pyuvm.s24_uvm_reg_includes import uvm_not_implemeneted # Pyuvm Mem Class declaration abstraction class uvm_mem(uvm_object): def __init__(self, name="uvm_mem"): super().__init__(name) uvm_not_implemeneted(f"{name} uvm_mem not implemented") pyuvm-4.0.1/pyuvm/s23_uvm_reg_item.py000066400000000000000000000144311507477334100176150ustar00rootroot00000000000000# Import Main Package from copy import deepcopy from pyuvm.s05_base_classes import uvm_object from pyuvm.s14_15_python_sequences import uvm_sequence_item from pyuvm.s17_uvm_reg_enumerations import uvm_status_e from pyuvm.s24_uvm_reg_includes import access_e, elem_kind_e, error_out, path_t # Main Class class uvm_reg_item(uvm_sequence_item): # constructor def __init__(self, name="item"): # Kind of element being accessed: REG, MEM, or FIELD. self.element_kind: elem_kind_e # A handle to the RegModel model element # associated with this transaction. # Use to determine the type to cast to: , # , or . self.element_object = None # Kind of access: READ or WRITE. # with it shall be via the set_kind() and get_kind() accessor methods self.kind: access_e # The value to write to, or after completion, # the value read from the DUT. self.value = [] # For memory accesses, the offset address. For bursts, # the ~starting~ offset address. # Access to this variable is provided # for randomization, otherwise interactions # with it shall be via the set_offset() # and get_offset() accessor methods self.offset = 0 # The result of the transaction: IS_OK, HAS_X, or ERROR. self.status = uvm_status_e.UVM_IS_OK # The local map used to obtain addresses. Users may customize # address-translation using this map. Access to the sequencer # and bus adapter can be obtained by getting this map's root map, # then calling and self.local_map = None # The original map specified for the operation. The actual # used may differ when a test or sequence written at the block # level is reused at the system level. # self.map = uvm_reg_map() TODO: # The path being used: or . self.path: path_t # The sequence from which the operation originated. # with it shall be via the set_parent() and get_parent() # accessor methods self.parent_sequence = None # be simple assigned to be carried self.extension_object = None # If path is UVM_BACKDOOR, this member specifies the abstraction # kind for the backdoor access, e.g. "RTL" or "GATES". self.bd_kind = "RTL" # Name storing self.name = name # Additional Fields self.addr = 0 self.data = 0 self.n_bits = 0 self.header = "PYUVM_REG_ITEM -- " self.fname: str = "" self.lineno: int = 0 ######################################################## # Internal Methods ######################################################## # do_copy def do_copy(self, rhs): # Check if not isinstance(rhs, uvm_reg_item): error_out( self.header, "WRONG_TYPE Provided rhs \ is not of type uvm_reg_item", ) else: # Deep Copy copied = deepcopy(rhs) return copied # set_element_kind def set_element_kind(self, _kind): self.element_kind = _kind # get_element_kind def get_element_kind(self): return self.element_kind # set_element def set_element(self, el): self.element = el # get_element def get_element(self): return self.element # set_kind def set_kind(self, _kind): self.kind = _kind # get_kind def get_kind(self): return self.kind # set_value def set_value(self, value, idx=0): if idx >= len(self.value): self.value += [0] * (idx - len(self.value) + 1) self.value[idx] = value # get_value def get_value(self, idx=0): if idx < len(self.value): return self.value[idx] else: error_out(self.header, "Index out of LIST") # set_value_size def set_value_size(self, sz): self.value = [0] * sz # get_value_size def get_value_size(self): return len(self.value) # set_value_array def set_value_array(self, v): self.value = v # get_value_array def get_value_array(self): return self.value # set_offset def set_offset(self, offset): self.offset = offset # get_offset def get_offset(self): return self.offset # set_status def set_status(self, status): if not isinstance(status, uvm_status_e): error_out(self.header, "Wrong assignment to status Enum") else: self.status = status # get_status def get_status(self): return self.status # set_door def set_door(self, door): self.path = door # set_parent def set_parent_sequence(self, seq: None): if seq is not None: self.parent_sequence = seq # set_parent def get_parent_sequence(self): return self.parent_sequence # get_door def get_door(self): return self.path # set_extension def set_extension(self, ext): if not isinstance(ext, uvm_object): error_out( self.header, "bd kind is not string possible values \ RTL or GATE", ) else: self.extension = ext # get+extension def get_extension(self): return self.extension # set_bd_kind def set_bd_kind(self, val): if not isinstance(val, str): error_out( self.header, "bd kind is not string possible values \ RTL or GATE", ) else: self.bd_kind = val # set_map def set_map(self, map_input): self.local_map = map_input # get_bd_kind def get_bd_kind(self): return self.bd_kind # set_fname # 19.1.1.2.13 def set_fname(self, fname: str): self.fname = fname # get_fname # 19.1.1.2.13 def get_fname(self) -> str: return self.fname # set_line # 19.1.1.2.14 def set_line(self, line: int): self.lineno = line # get_line # 19.1.1.2.14 def get_line(self): return self.lineno pyuvm-4.0.1/pyuvm/s24_uvm_reg_includes.py000066400000000000000000000111151507477334100204620ustar00rootroot00000000000000""" Collection of defines to be sued """ from enum import Enum from pyuvm.error_classes import UVMError, UVMFatalError, UVMNotImplemented # path_t class path_t(Enum): """ Access TYPE """ FRONTDOOR = 1 BACKDOOR = 2 USER_FRONTDOOR = 3 # check_t class check_t(Enum): """ Check TYPE """ CHECK = 1 NO_CHECK = 2 # status_t class status_t(Enum): """ Status TYPE """ IS_OK = 1 IS_NOT_OK = 2 # Predict Type class predict_t(Enum): """ predict_t main prediction to be used PREDICT_WRITE = 1 PREDICT_READ = 2 PREDICT_DIRECT = 3 """ PREDICT_WRITE = 1 PREDICT_READ = 2 PREDICT_DIRECT = 3 class elem_kind_e(Enum): pass class access_e(Enum): """ access_e type of access allowed PYUVM_READ = 0 PYUVM_WRITE = 1 """ UVM_READ = 0 UVM_WRITE = 1 class uvm_resp_t(Enum): """ uvm_resp_t is the main response based on the access issued PASS_RESP = 0 ERROR_RESP = 1 """ PASS_RESP = 0 ERROR_RESP = 1 def rand_enable(use_pyvsc: bool): """ New Decorator class with randomization option If the randomization is switched off then the decorator will no more use py_vsc but it just disables it allowing user to use local methods if needed """ class enable_rand: # Accept class as argument def __init__(self, cls) -> None: self.cls = cls # operate on CLS init input argument def __call__(self): if use_pyvsc is True: raise UVMNotImplemented() else: # Return the function unchanged, not decorated. if use_pyvsc # is not enabled return self.cls return enable_rand """ enable_pyvsc: False Global to be set in case we wanna use the VSC package enable_auto_predict: True Global to be set in case we wanna use the auto prediction enable_throw_error_response_on_read: False Global enable bit for error response in case of NO-EFFECT action based on the access type enable_throw_error_response_on_write: False Global enable bit for error response in case of NO-EFFECT action based on the access type disable_code_interruption_assert: False Global assert disable this is used in order to avoid code interruption by just checking for the error list """ enable_pyvsc = False enable_auto_predict = False enable_throw_error_response_on_read = False enable_throw_error_response_on_write = False disable_code_interruption_assert = False disable_code_interruption_fatal = False def uvm_error(header="", message=""): """ Used to error out based on header and message """ raise UVMError(f"{header + message}") def uvm_fatal(header="", message=""): """ Used to fatal out based on header and message """ if disable_code_interruption_fatal is False: raise UVMFatalError(f"{header + message}") def uvm_not_implemeneted(header="", message=""): """ Used to fatal out based on header and message """ if disable_code_interruption_fatal is False: raise UVMNotImplemented(f"{header + message}") def error_out(header, message): """ Used to error out based on header and message """ assert disable_code_interruption_assert, header + message class uvm_reg_bus_op: """ Standard class for register bus operation to be used into the Prediction or Adapter """ def __init__(self) -> None: self.kind: access_e = access_e.UVM_READ self.addr: int = 0 self.data: int = 0 self.n_bits: int = 0 self.byte_en: bool = False self.status: uvm_resp_t = uvm_resp_t.PASS_RESP class uvm_reg_error_decoder(Enum): """ List of uvm_reg errors to be collected FIELD_CANNOT_BE_NONE = 1 FIELD_ALREADY_ADDED = 2 FIELD_DOESNT_FIT_INTO_REG = 3 FIELD_OVERLAPPING_ERROR = 4 REG_SIZE_CANNOT_BE_ZERO = 5 """ FIELD_CANNOT_BE_NONE = 1 FIELD_ALREADY_ADDED = 2 FIELD_DOESNT_FIT_INTO_REG = 3 FIELD_OVERLAPPING_ERROR = 4 REG_SIZE_CANNOT_BE_ZERO = 5 class uvm_reg_field_error_decoder(Enum): """ List of uvm_reg errors to be collected CONFIGURE_MUST_BE_CALLED_BEFORE = 1 ACCESS_TYPE_NEEDS_TO_BE_A_STRING = 2 WRONG_ACCESS_FOR_PREDICT_READ = 3 WRONG_COMBINATION_PREDICTION_DIRECTION = 4 ACCESS_VALUE_OUT_OF_LIST = 5 """ CONFIGURE_MUST_BE_CALLED_BEFORE = 1 ACCESS_TYPE_NEEDS_TO_BE_A_STRING = 2 WRONG_ACCESS_FOR_PREDICT_READ = 3 WRONG_COMBINATION_PREDICTION_DIRECTION = 4 ACCESS_VALUE_OUT_OF_LIST = 5 pyuvm-4.0.1/pyuvm/s25_uvm_adapter.py000066400000000000000000000075161507477334100174520ustar00rootroot00000000000000# Import Main Package from pyuvm.s05_base_classes import uvm_object from pyuvm.s14_15_python_sequences import uvm_sequence, uvm_sequence_item from pyuvm.s23_uvm_reg_item import uvm_reg_item from pyuvm.s24_uvm_reg_includes import uvm_not_implemeneted, uvm_reg_bus_op # Main Class class uvm_reg_adapter(uvm_object): # Constructor def __init__(self, name="uvm_reg_adapter"): super().__init__(name) # Set this bit in extensions of this class if the bus protocol # supports byte enables. self.byte_enable = True self.parent_sequence = None self.reg_item = uvm_sequence_item self.header = name + "-- " self.provide_response = False # Function -- reg2bus # Extensions of this class must # implement this method to convert the specified # to a corresponding # subtype that defines the bus # transaction. # The method must allocate a new bus-specific , # assign its members from # the corresponding members from the given # generic ~rw~ bus operation, then # return it. def reg2bus(self, rw: uvm_reg_bus_op) -> uvm_sequence_item: uvm_not_implemeneted(self.header) # Function -- bus2reg # Extensions of this class ~must~ implement this method to copy members # of the given bus-specific ~bus_item~ # to corresponding members of the provided # ~bus_rw~ instance. Unlike , the resulting transaction # is not allocated from scratch. This is to accommodate applications # where the bus response must be returned in the original request. def bus2reg(self, bus_item: uvm_sequence_item, rw: uvm_reg_bus_op): uvm_not_implemeneted(self.header) # Use this method to retrieve the item from the adapter def get_item(self): return self.reg_item # Use this method to set the item into the adapter class def set_item(self, item: uvm_reg_item): self.reg_item = item # Use this method to set the parent sequence into the adapter class # Generally is a simple Write Sequence def set_parent_sequence(self, sequence: uvm_sequence): self.parent_sequence = sequence # Use this method to set the parent sequence into the adapter class # Generally is a simple Write Sequence def get_parent_sequence(self): return self.parent_sequence # get_provide_reponse def get_provide_reponse(self): return self.provide_response # get_byte_en def get_byte_en(self): return self.byte_enable # ------------------------------------------------------------------------------ # Example: # The following example illustrates how to implement a # RegModel-BUS adapter class # for the APB bus protocol. # # class rreg2apb_adapter(uvm_reg_adapter): # def __init__(self, name="uvm_reg_adapter"): # super().__init__(name) # # def reg2bus(rw: uvm_reg_bus_op): # apb_item apb = apb_item.create("apb_item") # if(rw.kind == UVM_READ): # apb.op = READ # elsif (rw.kind == UVM_WRITE): # apb.op = WRITE; # else: # assert(0,"reg2bus -- Wrong operation type used for APB OP") # apb.addr = rw.addr; # apb.data = rw.data; # return apb; # # def bus2reg(bus_item: uvm_sequencer_item, rw: uvm_reg_bus_op): # apb_item apb; # if (isinstance(apb,uvm_sequencer_item)): # assert(0,"Bus item is not of type apb_item") # else: # if(apb.op == READ): # rw.kind = UVM_READ # elsif (apb.op == WRITE): # rw.kind = UVM_WRITE; # else: # assert 0,"bus2reg -- Wrong operation \ # type used for uvm_reg_bus_op" # rw.addr = apb.addr; # rw.data = apb.data; # rw.status = UVM_IS_OK; # # ------------------------------------------------------------------------------ pyuvm-4.0.1/pyuvm/s26_uvm_predictor.py000066400000000000000000000003331507477334100200140ustar00rootroot00000000000000# Import Main Package from pyuvm.s05_base_classes import uvm_object # Main Class class uvm_reg_predictor(uvm_object): # Constructor def __init__(self, name="uvm_reg_predictor"): super().__init__(name) pyuvm-4.0.1/pyuvm/s27_uvm_reg_pkg.py000066400000000000000000000005651507477334100174470ustar00rootroot00000000000000from pyuvm.s17_uvm_reg_enumerations import * from pyuvm.s18_uvm_reg_block import * from pyuvm.s19_uvm_reg_field import * from pyuvm.s20_uvm_reg import * from pyuvm.s21_uvm_reg_map import * from pyuvm.s22_uvm_mem import * from pyuvm.s23_uvm_reg_item import * from pyuvm.s24_uvm_reg_includes import * from pyuvm.s25_uvm_adapter import * from pyuvm.s26_uvm_predictor import * pyuvm-4.0.1/pyuvm/utility_classes.py000066400000000000000000000225211507477334100176630ustar00rootroot00000000000000import fnmatch import logging from collections import OrderedDict import cocotb.queue from cocotb.queue import QueueEmpty from cocotb.triggers import Event, NullTrigger FIFO_DEBUG = 5 PYUVM_DEBUG = 4 logging.addLevelName(FIFO_DEBUG, "FIFO_DEBUG") logging.addLevelName(PYUVM_DEBUG, "PYUVM_DEBUG") if int(cocotb.__version__.split(".")[0]) >= 2: from cocotb.task import current_task else: def current_task(): return cocotb.scheduler._current_task def count_bits(nn): """ Count the number of bits in a number :param nn: The number to count the bits in :return: The number of bits """ # Convert the absolute value of n to binary and count the '1's return bin(abs(nn)).count("1") class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) # noqa: E501 return cls._instances[cls] @classmethod def clear_singletons(cls, keep): classes = list(cls._instances.keys()) for del_cls in classes: if del_cls not in keep: del cls._instances[del_cls] class Override: """ This class stores an override and an optional path. It is intended to be stored in a dict with the original class as the key. """ def __init__(self): self.type_override = None self.inst_overrides = OrderedDict() def add(self, override, path=None): if path is None: self.type_override = override else: self.inst_overrides[path] = override def find_inst_override(self, path): for inst in self.inst_overrides: if fnmatch.fnmatch(path, inst): return self.inst_overrides[inst] return None def __str__(self): """ For printing out the overrides :return: str """ if self.type_override is not None: to = "Type Override: " + f"{self.type_override.__name__}" else: to = "Type Override: None" ss = f"{to:25}" + " || " io = " | ".join( [ ( f"{inst_path[:29] if len(inst_path) > 29 else inst_path}" f" => {inst_override.__name__}" ) for inst_path, inst_override in self.inst_overrides.items() ] ) ss += f"Instance Overrides: {io}" if io else "" return ss class FactoryData(metaclass=Singleton): def __init__(self): self.classes = {} self.clear_overrides() self.logger = logging.getLogger("Factory") def clear_overrides(self): self.overrides = {} def clear_classes(self): self.classes = {} # From 8.3.1.5 def find_override(self, requested_type, inst_path=None, overridden_list=None): """ :param requested_type: The type we're overriding :param inst_path: The inst_path we're using to override if any :param overridden_list: A list of previously found overrides :return: overriding_type Override searches are recursively applied, with instance overrides taking precedence over type overrides. If foo overrides bar, and xyz overrides foo, then a request for bar returns xyx. """ # xyz -> foo -> bar # # So if find_override is f: # f(xyz) -> f(foo) -> f(bar) <-- no override returns bar. # Recursive loops result ina n error in which case the # type returned is the one that formed the loop: # xyz -> foo -> bar -> xyz # # f(xyz) -> f(foo) -> f(bar) -> f(xyz) -- xyz is in # list of overrides so return bar # bar is returned with a printed error. # # We use the Override class which contains both the type override if # there is one or # a list of instance overrides in the order the were added. # If inst_path is None we return the type_override or its override # If inst_path is given, but we don't find a match we return # type_override if it exists # Keep track of what classes have been overridden # # Is there an override loop? # noinspection PyShadowingNames def check_override(override, overridden_list): if overridden_list is None: overridden_list = [] if override in overridden_list: self.logger.error( f"{requested_type} already overridden: {overridden_list}" ) return requested_type else: overridden_list.append(requested_type) rec_override = self.find_override(override, inst_path, overridden_list) return rec_override # Save the type for a later check # Is this requested type even in the list of overrides? try: override = self.overrides[requested_type] except KeyError: return requested_type if inst_path is not None: for path in override.inst_overrides: if fnmatch.fnmatch(inst_path, path): found_type = override.inst_overrides[path] return check_override(found_type, overridden_list) # No inst requested or found, do we have a type override? if override.type_override is not None: return check_override(override.type_override, overridden_list) else: return requested_type class FactoryMeta(type): """ This is the metaclass that causes all uvm_void classes to register themselves """ def __init__(cls, name, bases, cls_dict): FactoryData().classes[cls.__name__] = cls super().__init__(name, bases, cls_dict) class uvm_void(metaclass=FactoryMeta): """ 5.2 SystemVerilog Python uses this class to allow all uvm objects to be stored in a uvm_void variable through polymorphism. In pyuvm, we're using uvm_void() as a metaclass so that all UVM classes can be stored in a factory. """ class UVM_ROOT_Singleton(FactoryMeta): singleton = None def __call__(cls, *args, **kwargs): if cls.singleton is None: cls.singleton = super().__call__(*args, **kwargs) # noqa : E501 return cls.singleton @classmethod def clear_singletons(cls): cls.singleton = None pass class ObjectionHandler(metaclass=Singleton): """ This singleton accepts objections and then allows them to be removed. It returns True to run_phase_complete() when there are no objections left. """ def __init__(self): self.__objections = {} self._objection_event = Event() self.objection_raised = False self.run_phase_done_flag = None # used in test suites self.printed_warning = False def __str__(self): ss = f"run_phase complete: {self.run_phase_complete()}\n" ss += "Current Objections:\n" for cc in self.__objections: ss += f"{self.__objections[cc]}\n" return ss def clear(self): if len(self.__objections) != 0: logging.warning( "Clearing objections raised by %s", ", ".join(self.__objections.values()), ) self.__objections = {} self.objection_raised = False def raise_objection(self, raiser): name = raiser.get_full_name() self.__objections[name] = self.__objections.setdefault(name, 0) + 1 self.objection_raised = True self._objection_event.clear() def drop_objection(self, dropper): name = dropper.get_full_name() try: self.__objections[name] -= 1 except KeyError: self.objection_raised = True pass if self.__objections[name] == 0: del self.__objections[name] # only signal all objections done if none exist anywhere if len(self.__objections) == 0: self._objection_event.set() async def run_phase_complete(self): # Allow the run_phase coros to get scheduled and raise objections: await NullTrigger() if self.objection_raised: await self._objection_event.wait() else: logging.warning("You did not call self.raise_objection() in any run_phase") class UVMQueue(cocotb.queue.Queue): """ The UVMQueue provides a peek function as well as the ability to break out of a blocking operation if the time_to_die predicate is true. The time to die is set to the dropping of all run_phase objections by default. """ def __str__(self): return str(self._queue) def _peek(self): return self._queue[0] async def peek(self): """Remove and return an item from the queue. If the queue is empty, wait until an item is available. """ while self.empty(): event = Event() self._getters.append((event, current_task())) await event.wait() return self.peek_nowait() def peek_nowait(self): """Remove and return an item from the queue. Return an item if one is immediately available, else raise :exc:`asyncio.QueueEmpty`. """ if self.empty(): raise QueueEmpty() item = self._peek() return item pyuvm-4.0.1/setup.py000077500000000000000000000022161507477334100144250ustar00rootroot00000000000000import setuptools with open("README.md", encoding="utf-8") as fh: long_description = fh.read() setuptools.setup( name="pyuvm", version="4.0.1", author="Ray Salemi", author_email="ray@raysalemi.com", description="A Python implementation of the UVM using cocotb", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/pyuvm/pyuvm", project_urls={ "Bug Tracker": "https://github.com/pyuvm/pyuvm/issues", }, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", "Framework :: cocotb", ], packages=setuptools.find_packages(), python_requires=">=3.6", install_requires="cocotb>=1.6.0,<3.0", extras_require={ "dev": [ "tox", # run regression tests "pre-commit", # perform lints "pytest", # manually run pytest-based tests "ruff", # linter for IDE integration ], }, ) pyuvm-4.0.1/setup.sh000066400000000000000000000000511507477334100143770ustar00rootroot00000000000000export PYTHONPATH=$PWD/pyuvm:$PYTHONPATH pyuvm-4.0.1/tests/000077500000000000000000000000001507477334100140515ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/000077500000000000000000000000001507477334100165445ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/config_db/000077500000000000000000000000001507477334100204565ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/config_db/Makefile000077500000000000000000000004411507477334100221200ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/config_db/clk.sv000066400000000000000000000001301507477334100215730ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/config_db/test.py000066400000000000000000000027151507477334100220140ustar00rootroot00000000000000import inspect import cocotb import test_config_db as test_mod from cocotb.clock import Clock from pyuvm import * async def run_tests(dut): tests_pass = {} tcfgdb = test_mod.config_db_TestCase() methods = inspect.getmembers(test_mod.config_db_TestCase) for mm in methods: (name, _) = mm if name.startswith("test_"): test = getattr(tcfgdb, name) tcfgdb.setUp() try: if inspect.iscoroutinefunction(test): await test() else: test() tests_pass[name] = True except AssertionError: tests_pass[name] = False tcfgdb.tearDown() any_failed = False for test, passed in tests_pass.items(): if passed: pf = "Pass " else: pf = "FAILED " any_failed = True print(f"{pf}{test:<20}") assert not any_failed @cocotb.test() # pylint: disable=no-value-for-parameter async def test_12_tlm(dut): """Tests the TLM FIFOS""" clock = Clock(dut.clk, 2, "us") cocotb.start_soon(clock.start()) await run_tests(dut) class Test(uvm_test): async def run_phase(self): self.raise_objection() self.drop_objection() @cocotb.test() async def create_config_db(_): config_db_id = id(ConfigDB()) await uvm_root().run_test("Test") second_id = id(ConfigDB()) assert config_db_id is not second_id pyuvm-4.0.1/tests/cocotb_tests/config_db/test_config_db.py000066400000000000000000000165051507477334100240100ustar00rootroot00000000000000import uvm_unittest from pyuvm import * class config_db_TestCase(uvm_unittest.uvm_TestCase): def tearDown(self) -> None: super().tearDown() ConfigDB().clear() def test_context_None(self): cdb = ConfigDB() # simple set/get cdb.set(None, "*", "LABEL", 5) with self.assertRaises(error_classes.UVMError): cdb.exists(None, "*", "LABEL") self.assertTrue(cdb.exists(None, "A", "LABEL")) datum = cdb.get(None, "A", "LABEL") self.assertEqual(5, datum) with self.assertRaises(error_classes.UVMConfigItemNotFound): cdb.get(None, "A", "NOT THERE") self.assertFalse(cdb.exists(None, "A", "NOT THERE")) cdb.set(None, "top.B.C", "OTHER_LABEL", 88) datum = cdb.get(None, "top.B.C", "OTHER_LABEL") self.assertEqual(88, datum) with self.assertRaises(error_classes.UVMConfigItemNotFound): _ = cdb.get(None, "A", "OTHER_LABEL") cdb.set(None, "", "BLANK", 99) datum = cdb.get(None, "", "BLANK") self.assertEqual(99, datum) def test_empty_db(self): cdb = ConfigDB() with self.assertRaises(error_classes.UVMConfigItemNotFound): cdb.get(None, "A", "LABEL") cdb.set(None, "A", "LABEL", 5) datum = cdb.get(None, "A", "LABEL") self.assertEqual(5, datum) with self.assertRaises(error_classes.UVMConfigItemNotFound): cdb.get(None, "B", "LABEL") cdb.set(None, "B", "OTHER_LABEL", 88) datum = cdb.get(None, "B", "OTHER_LABEL") self.assertEqual(88, datum) with self.assertRaises(error_classes.UVMConfigItemNotFound): cdb.get(None, "B", "LABEL") async def test_context(self): class comp(uvm_component): def build_phase(self): self.cdb_set("XXC", 93, "") class test(uvm_test): def build_phase(self): self.xx = comp("xx", self) self.cdb_set("XXC", 855, "") async def run_phase(self): self.raise_objection() self.drop_objection() cdb = ConfigDB() cdb.is_tracing = True await uvm_root().run_test("test", keep_singletons=True) cdb.set(uvm_root(), "*", "LABEL", 55) datum = cdb.get(uvm_root(), "tt", "LABEL") self.assertEqual(55, datum) utt = uvm_root().get_child("uvm_test_top") cdb.set(utt, "*", "WC", 99) datum = cdb.get(utt, "xx", "WC") self.assertEqual(99, datum) datum = utt.xx.cdb_get("XXC", "") self.assertEqual(93, datum) async def test_wildards(self): class comp(uvm_component): def build_phase(self): self.numb = ConfigDB().get(self, "", "CONFIG") class test(uvm_test): def build_phase(self): ConfigDB().set(self, "*", "CONFIG", 88) self.cc1 = comp("cc1", self) self.cc2 = comp("cc", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("test", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(88, utt.cc1.numb) self.assertEqual(88, utt.cc2.numb) async def test_one_wildard(self): class comp(uvm_component): def build_phase(self): self.numb = ConfigDB().get(self, "", "CONFIG") class test(uvm_test): def build_phase(self): ConfigDB().set(self, "*", "CONFIG", 88) ConfigDB().set(self, "cc2", "CONFIG", 66) self.cc1 = comp("cc1", self) self.cc2 = comp("cc2", self) self.cc3 = comp("cc3", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("test", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(88, utt.cc1.numb) self.assertEqual(66, utt.cc2.numb) self.assertEqual(88, utt.cc3.numb) async def test_precedence(self): class bottom(uvm_component): def build_phase(self): self.numb = ConfigDB().get(self, "", "CONFIG") class comp(uvm_component): def build_phase(self): ConfigDB().set(self, "*", "CONFIG", 55) self.bot = bottom("bot", self) class test(uvm_test): def build_phase(self): ConfigDB().set(self, "cc1.*", "CONFIG", 88) self.cc1 = comp("cc1", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("test", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(88, utt.cc1.bot.numb) async def test_wildcard_hierarchy_in_context(self): class Printer(uvm_component): def build_phase(self): self.msg = ConfigDB().get(self, "", "MSG") class PrintTest(uvm_test): def build_phase(self): self.pmsg = f"Hooray for {self.get_name()}!" self.mmsg = "Settle down, you too." self.rmsg = "What's going on?" ConfigDB().set(self, "p?", "MSG", self.pmsg) ConfigDB().set(self, "m*", "MSG", self.mmsg) ConfigDB().set(self, "*", "MSG", self.rmsg) self.p1 = Printer("p1", self) self.p2 = Printer("p2", self) self.mediator = Printer("mediator", self) self.reporters = Printer("reporters", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("PrintTest", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(utt.pmsg, utt.p1.msg) self.assertEqual(utt.pmsg, utt.p2.msg) self.assertEqual(utt.mmsg, utt.mediator.msg) self.assertEqual(utt.rmsg, utt.reporters.msg) async def test_wildcard_hierarchy_at_root(self): class Printer(uvm_component): def build_phase(self): self.msg = ConfigDB().get(self, "", "MSG") class PrintTest(uvm_test): def build_phase(self): self.pmsg = f"Hooray for {self.get_name()}!" self.mmsg = "Settle down, you too." self.rmsg = "What's going on?" ConfigDB().set(None, "*p?", "MSG", self.pmsg) ConfigDB().set(None, "*me*", "MSG", self.mmsg) ConfigDB().set(None, "*", "MSG", self.rmsg) self.p1 = Printer("p1", self) self.p2 = Printer("p2", self) self.mediator = Printer("mediator", self) self.reporters = Printer("reporters", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("PrintTest", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(utt.pmsg, utt.p1.msg) self.assertEqual(utt.pmsg, utt.p2.msg) self.assertEqual(utt.mmsg, utt.mediator.msg) self.assertEqual(utt.rmsg, utt.reporters.msg) pyuvm-4.0.1/tests/cocotb_tests/decorator/000077500000000000000000000000001507477334100205265ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/decorator/Makefile000077500000000000000000000004531507477334100221730ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test_decorator TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/decorator/clk.sv000066400000000000000000000001301507477334100216430ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/decorator/test_decorator.py000066400000000000000000000101511507477334100241170ustar00rootroot00000000000000import pyuvm from pyuvm import * class MySingleton(metaclass=pyuvm.Singleton): def __init__(self): self.datum = None class MySingleton2(MySingleton): pass class MySingleton3(MySingleton): pass @pyuvm.test() class NewSingleton(uvm_test): def build_phase(self): self.mysingleton = MySingleton() def check_phase(self): assert self.mysingleton.datum is None async def run_phase(self): self.raise_objection() self.drop_objection() @pyuvm.test() class SetDatumTo42Test(uvm_test): def build_phase(self): self.mysingleton = MySingleton() async def run_phase(self): self.raise_objection() self.mysingleton.datum = 42 self.drop_objection() @pyuvm.test() class CheckDatumisNone(uvm_test): def build_phase(self): self.mysingleton = MySingleton() def check_phase(self): assert self.mysingleton.datum is None async def run_phase(self): self.raise_objection() self.drop_objection() @pyuvm.test() class SetDatumTo42AgainTest(uvm_test): def build_phase(self): self.mysingleton = MySingleton() async def run_phase(self): self.raise_objection() self.mysingleton.datum = 42 self.drop_objection() @pyuvm.test(keep_singletons=True) class CheckDatumis42(uvm_test): def build_phase(self): self.mysingleton = MySingleton() def check_phase(self): assert self.mysingleton.datum == 42 async def run_phase(self): self.raise_objection() self.drop_objection() @pyuvm.test() class SetDatumTo442(uvm_test): def build_phase(self): self.mysingleton = MySingleton() async def run_phase(self): self.raise_objection() self.mysingleton.datum = 442 self.drop_objection() @pyuvm.test(keep_set=set([MySingleton])) class CheckDatumis442(uvm_test): def build_phase(self): self.mysingleton = MySingleton() def check_phase(self): assert self.mysingleton.datum == 442 async def run_phase(self): self.raise_objection() self.drop_objection() class SetMultipleSingletonsBase(uvm_test): def build_phase(self): self.mysingleton = MySingleton() self.mysingleton2 = MySingleton2() self.mysingleton3 = MySingleton3() async def run_phase(self): self.raise_objection() self.mysingleton.datum = 111 self.mysingleton2.datum = 222 self.mysingleton3.datum = 333 self.drop_objection() @pyuvm.test() class SetMultipleSingletons(SetMultipleSingletonsBase): ... pyuvm.test() class CheckMultipleSingletonAreNone(uvm_test): def build_phase(self): self.mysingleton = MySingleton() self.mysingleton2 = MySingleton2() self.mysingleton3 = MySingleton3() def check_phase(self): assert self.mysingleton.datum is None assert self.mysingleton2.datum is None assert self.mysingleton3.datum is None async def run_phase(self): self.raise_objection() self.drop_objection() @pyuvm.test() class SetMultipleSingletonsAgain(SetMultipleSingletonsBase): pass @pyuvm.test(keep_singletons=True) class CheckMultipleSingletonAreSet(uvm_test): def build_phase(self): self.mysingleton = MySingleton() self.mysingleton2 = MySingleton2() self.mysingleton3 = MySingleton3() def check_phase(self): assert self.mysingleton.datum == 111 assert self.mysingleton2.datum == 222 assert self.mysingleton3.datum == 333 async def run_phase(self): self.raise_objection() self.drop_objection() @pyuvm.test(keep_set=set([MySingleton, MySingleton3])) class CheckMultipleSingletonAreSetAgain(uvm_test): def build_phase(self): self.mysingleton = MySingleton() self.mysingleton2 = MySingleton2() self.mysingleton3 = MySingleton3() def check_phase(self): assert self.mysingleton.datum == 111 assert self.mysingleton2.datum is None assert self.mysingleton3.datum == 333 async def run_phase(self): self.raise_objection() self.drop_objection() pyuvm-4.0.1/tests/cocotb_tests/ext_classes/000077500000000000000000000000001507477334100210615ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/ext_classes/Makefile000077500000000000000000000004411507477334100225230ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/ext_classes/clk.sv000066400000000000000000000001301507477334100221760ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/ext_classes/test.py000066400000000000000000000052151507477334100224150ustar00rootroot00000000000000import inspect import test_ext_classes as test_mod import pyuvm from pyuvm import * async def run_tests(dut): tests_pass = {} t12 = test_mod.s12_uvm_tlm_interfaces_TestCase() methods = inspect.getmembers( test_mod.s12_uvm_tlm_interfaces_TestCase ) # predicate=inspect.ismethod) for mm in methods: (name, _) = mm if name.startswith("test_"): test = getattr(t12, name) t12.setUp() try: if inspect.iscoroutinefunction(test): await test(dut) else: test() tests_pass[name] = True except AssertionError: tests_pass[name] = False t12.tearDown() any_failed = False for test, passed in tests_pass.items(): if passed: pf = "Pass " else: pf = "FAILED " any_failed = True print(f"{pf} {test}") assert not any_failed class FIFO(uvm_component): def build_phase(self): self.fifo = uvm_tlm_analysis_fifo("fifo", self) self.gp = uvm_get_port("gp", self) def connect_phase(self): self.gp.connect(self.fifo.get_export) def start_of_simulation_phase(self): self.data = [] async def run_phase(self): while True: datum = await self.gp.get() self.data.append(datum) class Subscriber(uvm_subscriber): def start_of_simulation_phase(self): self.data = [] def write(self, datum): self.data.append(datum) class Export(uvm_analysis_export): def start_of_simulation_phase(self): self.data = [] def write(self, datum): self.data.append(datum) @pyuvm.test() class AnalysisTest(uvm_test): def build_phase(self): self.gp = uvm_get_port("gp", self) self.ap1 = uvm_analysis_port("ap1", self) self.ap2 = uvm_analysis_port("ap2", self) self.export = Export("export", self) self.fifo = FIFO("fifo", self) self.subscriber = Subscriber("subscriber", self) def connect_phase(self): self.ap1.connect(self.ap2) self.ap2.connect(self.export) self.ap2.connect(self.subscriber.analysis_export) self.ap2.connect(self.fifo.fifo.analysis_export) async def run_phase(self): self.raise_objection() self.data_list = [1, "1", 2, "two"] for datum in self.data_list: self.ap1.write(datum) self.drop_objection() def check_phase(self): assert self.data_list == self.subscriber.data assert self.data_list == self.export.data assert self.data_list == self.fifo.data pyuvm-4.0.1/tests/cocotb_tests/ext_classes/test_ext_classes.py000066400000000000000000000552331507477334100250170ustar00rootroot00000000000000import cocotb import uvm_unittest from cocotb.triggers import Timer from pyuvm import * # pylint: disable=unused-wildcard-import async def waitabit(abit=5): await Timer(1, "us") class s12_uvm_tlm_interfaces_TestCase(uvm_unittest.uvm_TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if uvm_root().has_child("my_root"): self.my_root = uvm_root().get_child("my_root") else: self.my_root = uvm_component("my_root", None) def setUp(self): ObjectionHandler().run_phase_done_flag = False self.my_root.clear_children() def tearDown(self): ObjectionHandler().run_phase_done_flag = True class my_comp(uvm_component): ... # Put class TestPutExportBase(uvm_port_base): def __init__(self, name, parent=None): super().__init__(name, parent) self.data = None class TestBlockingPutExport(TestPutExportBase, uvm_blocking_put_export): async def put(self, data): self.data = data class TestNonBlockingPutExport(TestPutExportBase, uvm_nonblocking_put_export): def __init__(self, name=None, parent=None): super().__init__(name, parent) self.blocked = None def try_put(self, data): if not self.blocked: self.data = data return not self.blocked def can_put(self): return not self.blocked class TestPutExport(TestBlockingPutExport, TestNonBlockingPutExport): ... # Get class TestGetExportBase: def __init__(self, name="", parent=None): super().__init__(name, parent) self.data = None self.empty = None class TestBlockingGetExport(TestGetExportBase, uvm_blocking_get_export): async def get(self): return self.data class TestNonBlockingGetExport(TestGetExportBase, uvm_nonblocking_get_export): def try_get(self): if not self.empty: return True, self.data return False, None def can_get(self): return not self.empty class TestGetExport(TestBlockingGetExport, TestNonBlockingGetExport): ... # Peek class TestBlockingPeekExport(TestGetExportBase, uvm_blocking_peek_export): async def peek(self): return self.data class TestNonBlockingPeekExport(TestGetExportBase, uvm_nonblocking_peek_export): def try_peek(self): if not self.empty: return True, self.data else: return False, None def can_peek(self): return self.can_peek() class TestPeekExport(TestBlockingPeekExport, TestNonBlockingPeekExport): ... # GetPeek class TestBlockingGetPeekExport(TestBlockingGetExport, TestBlockingPeekExport): ... class TestNonBlockingGetPeekExport( TestNonBlockingGetExport, TestNonBlockingPeekExport ): ... class TestGetPeekExport( TestBlockingGetPeekExport, TestNonBlockingGetPeekExport ): ... # Transport class TestTransportExportBase(uvm_port_base): def __init__(self, name, parent): super().__init__(name, parent) self.put_data = None self.get_data = None self.blocked = True class TestBlockingTransportExport( TestTransportExportBase, uvm_blocking_transport_export ): async def transport(self, put_data): self.put_data = put_data return self.get_data class TestNonBlockingTransportExport( TestTransportExportBase, uvm_nonblocking_transport_export ): def nb_transport(self, put_data): if self.blocked: return False, None else: self.put_data = put_data return True, self.get_data class TestTransportExport( TestBlockingTransportExport, TestNonBlockingTransportExport ): ... # Master class TestBlockingMasterExport( TestBlockingPutExport, TestBlockingGetPeekExport ): ... class TestNonBlockingMasterExport( TestNonBlockingPutExport, TestNonBlockingGetPeekExport ): ... class TestMasterExport(TestBlockingMasterExport, TestNonBlockingMasterExport): ... # Slave class TestBlockingSlaveExport(TestBlockingPutExport, TestBlockingGetPeekExport): ... class TestNonBlockingSlaveExport( TestNonBlockingPutExport, TestNonBlockingGetPeekExport ): ... class TestSlaveExport(TestBlockingSlaveExport, TestNonBlockingSlaveExport): ... class TestInvalidExport(uvm_port_base): """ Used to guarantee a port type error """ ... # Put # Common predefined port tests: put, get, peek, get_peek, transport, master, slave def make_components(self, port_cls, export_cls): """ Make the components for a port test in a child-free component :param port_cls: :param export_cls: :return: port, export, invalid """ self.my_root.clear_children() port = port_cls("port", self.my_root) port2 = port_cls("port2", self.my_root) export = export_cls("export", self.my_root) invalid = self.TestInvalidExport("invalid", self.my_root) return port, port2, export, invalid async def exercise_blocking_put(self, port_cls, export_cls): (bpp, bpp2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): await bpp.put(0) with self.assertRaises(UVMTLMConnectionError): bpp.connect(invalid) bpp.connect(export) bpp2.connect(bpp) # test first port await bpp.put(5) self.assertEqual(export.data, 5) # test port connected to port await bpp2.put(15) self.assertEqual(export.data, 15) def exercise_nonblocking_put(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) export.data = 0 with self.assertRaises(UVMTLMConnectionError): __ = port.try_put("data") with self.assertRaises(UVMTLMConnectionError): __ = port.connect(invalid) port.connect(export) port2.connect(port) export.blocked = True # test first port self.assertFalse(port.try_put(55)) self.assertNotEqual(55, export.data) export.blocked = False self.assertTrue(port.try_put(55)) self.assertEqual(55, export.data) # test port connected to port export.blocked = True self.assertFalse(port.try_put(10)) self.assertNotEqual(10, export.data) export.blocked = False self.assertTrue(port.try_put(10)) self.assertEqual(10, export.data) async def exercise_blocking_get(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __ = await port.get() with self.assertRaises(UVMTLMConnectionError): __ = port.connect(invalid) port.connect(export) port2.connect(port) # test port export.data = 0xDEADBEEF get_data = await port.get() self.assertEqual(0xDEADBEEF, get_data) # test port connected to port export.data = 0xDEADBEE2 get_data = await port2.get() self.assertEqual(0xDEADBEE2, get_data) def exercise_nonblocking_get(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __, __ = port.try_get() with self.assertRaises(UVMTLMConnectionError): __ = port.connect(invalid) port.connect(export) port2.connect(port) # tests port export.empty = True export.data = "Data" success, data = port.try_get() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port.try_get() self.assertEqual("Data", data) self.assertTrue(success) # test port connected to port export.empty = True export.data = "Data2" success, data = port2.try_get() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port2.try_get() self.assertEqual("Data2", data) self.assertTrue(success) async def exercise_blocking_peek(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __ = await port.peek() with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.data = 0xDEADBEEF peek_data = await port.peek() self.assertEqual(0xDEADBEEF, peek_data) # test port connected to port export.data = 0xDEADBE2F peek_data = await port.peek() self.assertEqual(0xDEADBE2F, peek_data) def exercise_nonblocking_peek(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __, __ = port.try_peek() with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.empty = True export.data = "Data" success, data = port.try_peek() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port.try_peek() self.assertEqual("Data", data) self.assertTrue(success) # test port connected to port export.empty = True export.data = "Data2" success, data = port2.try_peek() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port2.try_peek() self.assertEqual("Data2", data) self.assertTrue(success) async def exercise_blocking_get_peek(self, port_cls, export_cls): await self.exercise_blocking_get(port_cls, export_cls) await self.exercise_blocking_peek(port_cls, export_cls) def exercise_nonblocking_get_peek(self, port_cls, export_cls): self.exercise_nonblocking_get(port_cls, export_cls) self.exercise_nonblocking_peek(port_cls, export_cls) async def exercise_get_peek(self, port_cls, export_cls): await self.exercise_blocking_get_peek(port_cls, export_cls) self.exercise_nonblocking_get_peek(port_cls, export_cls) async def exercise_blocking_transport(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __ = await port.transport("sent") with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.get_data = "returned" returned_data = await port.transport("sent") self.assertEqual("returned", returned_data) # test port connected to port export.get_data = "2returned" returned_data = await port2.transport("sent") self.assertEqual("2returned", returned_data) def exercise_nonblocking_transport(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __, __ = port.nb_transport("sent") with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.blocked = True export.get_data = "returned" success, data = port.nb_transport("sent") self.assertFalse(success) self.assertIsNone(data) export.blocked = False success, data = port.nb_transport("sent") self.assertEqual("returned", data) self.assertTrue(success) # test port connected to port export.blocked = True export.get_data = "returned" success, data = port2.nb_transport("sent") self.assertFalse(success) self.assertIsNone(data) export.blocked = False success, data = port2.nb_transport("sent") self.assertEqual("returned", data) self.assertTrue(success) def exercise_slave_do(self, port_cls, export_cls): pass async def test_uvm_blocking_put_port(self): await self.exercise_blocking_put( uvm_blocking_put_port, self.TestBlockingPutExport ) def test_uvm_non_blocking_put_port(self): self.exercise_nonblocking_put( uvm_nonblocking_put_port, self.TestNonBlockingPutExport ) async def test_uvm_put_port(self): await self.exercise_blocking_put(uvm_put_port, self.TestPutExport) self.exercise_nonblocking_put(uvm_put_port, self.TestPutExport) async def test_uvm_blocking_get_port(self): await self.exercise_blocking_get( uvm_blocking_get_port, self.TestBlockingGetExport ) def test_uvm_non_blocking_get_port(self): self.exercise_nonblocking_get( uvm_nonblocking_get_port, self.TestNonBlockingGetExport ) async def test_uvm_get_port(self): await self.exercise_blocking_get(uvm_get_port, self.TestGetExport) self.exercise_nonblocking_get(uvm_get_port, self.TestGetExport) async def test_uvm_blocking_peek_port(self): await self.exercise_blocking_peek( uvm_blocking_peek_port, self.TestBlockingPeekExport ) def test_uvm_non_blocking_peek_port(self): self.exercise_nonblocking_peek( uvm_nonblocking_peek_port, self.TestNonBlockingPeekExport ) async def test_uvm_peek_port(self): await self.exercise_blocking_peek(uvm_peek_port, self.TestPeekExport) self.exercise_nonblocking_peek(uvm_peek_port, self.TestPeekExport) async def test_uvm_blocking_get_peek_port(self): await self.exercise_blocking_get_peek( uvm_blocking_get_peek_port, self.TestBlockingGetPeekExport ) def test_uvm_non_blocking_get_peek_port(self): self.exercise_nonblocking_get_peek( uvm_nonblocking_get_peek_port, self.TestNonBlockingGetPeekExport ) async def test_uvm_get_peek_port(self): await self.exercise_get_peek(uvm_get_peek_port, self.TestGetPeekExport) async def test_uvm_blocking_transport_port(self): await self.exercise_blocking_transport( uvm_blocking_transport_port, self.TestBlockingTransportExport ) def test_uvm_non_blocking_transport_port(self): self.exercise_nonblocking_transport( uvm_nonblocking_transport_port, self.TestNonBlockingTransportExport ) async def test_uvm_transport_port(self): await self.exercise_blocking_transport( uvm_transport_port, self.TestTransportExport ) self.exercise_nonblocking_transport( uvm_transport_port, self.TestTransportExport ) async def test_uvm_blocking_master_port(self): await self.exercise_blocking_put( uvm_blocking_master_port, self.TestBlockingMasterExport ) await self.exercise_blocking_get_peek( uvm_blocking_master_port, self.TestBlockingMasterExport ) def test_uvm_nonblocking_master_port(self): self.exercise_nonblocking_put( uvm_nonblocking_master_port, self.TestNonBlockingMasterExport ) self.exercise_nonblocking_get_peek( uvm_nonblocking_master_port, self.TestNonBlockingMasterExport ) async def test_uvm_master_port(self): self.exercise_nonblocking_put(uvm_master_port, self.TestMasterExport) self.exercise_nonblocking_get_peek(uvm_master_port, self.TestMasterExport) await self.exercise_blocking_put(uvm_master_port, self.TestMasterExport) await self.exercise_blocking_get_peek(uvm_master_port, self.TestMasterExport) async def test_uvm_blocking_slave_port(self): await self.exercise_blocking_put( uvm_blocking_slave_port, self.TestBlockingSlaveExport ) await self.exercise_blocking_get_peek( uvm_blocking_slave_port, self.TestBlockingSlaveExport ) def test_uvm_nonblocking_slave_port(self): self.exercise_nonblocking_put( uvm_nonblocking_slave_port, self.TestNonBlockingSlaveExport ) self.exercise_nonblocking_get_peek( uvm_nonblocking_slave_port, self.TestNonBlockingSlaveExport ) async def test_uvm_slave_port(self): self.exercise_nonblocking_put(uvm_slave_port, self.TestSlaveExport) self.exercise_nonblocking_get_peek(uvm_slave_port, self.TestSlaveExport) await self.exercise_blocking_put(uvm_slave_port, self.TestSlaveExport) await self.exercise_blocking_get_peek(uvm_slave_port, self.TestSlaveExport) def test_uvm_tlm_fifo_size(self): """ 12.2.8.2.2 :return: """ ff = uvm_tlm_fifo("ff", self.my_root) size = ff.size() self.assertEqual(1, size) ff0 = uvm_tlm_fifo("ff0", self.my_root, 0) size = ff0.size() self.assertEqual(0, size) ff2 = uvm_tlm_fifo("ff2", self.my_root, 2) size = ff2.size() self.assertEqual(2, size) async def test_uvm_tlm_fifo_used(self): """ 12.2.8.2.3 :return: """ ff = uvm_tlm_fifo("ff", self.my_root, 3) pp = uvm_put_port("pp", self.my_root) pp.connect(ff.put_export) await pp.put(1) await pp.put(2) await pp.put(3) self.assertEqual(3, ff.used()) async def test_uvm_tlm_fifo_is_empty(self): """ 12.2.8.2.4 :return: """ ff = uvm_tlm_fifo("ff", None) self.assertTrue(ff.is_empty()) pp = uvm_put_port("pp", None) pp.connect(ff.put_export) gp = uvm_get_port("gp", None) gp.connect(ff.get_export) await pp.put(1) self.assertFalse(ff.is_empty()) __ = await gp.get() self.assertTrue(ff.is_empty()) def make_fifo(self, fifo_type) -> uvm_tlm_fifo_base: self.my_root.clear_children() fifo = fifo_type("fifo", self.my_root) return fifo @staticmethod async def do_blocking_put(put_port, data_list): for data in data_list: await put_port.put(data) @staticmethod async def do_blocking_get(get_port, data_list): while True: datum = await get_port.get() if datum is not None: data_list.append(datum) else: break @staticmethod async def do_blocking_peek(peek_port, data_list): datum = await peek_port.peek() data_list.append(datum) async def test_fifo_blocking(self): fifo = self.make_fifo(uvm_tlm_fifo) pp = uvm_blocking_put_port("pp", self.my_root) gp = uvm_blocking_get_port("gp", self.my_root) pk = uvm_blocking_peek_port("pk", self.my_root) gpp = uvm_blocking_get_peek_port("gpp", self.my_root) pp.connect(fifo.blocking_put_export) gp.connect(fifo.blocking_get_export) pk.connect(fifo.blocking_peek_export) gpp.connect(fifo.blocking_get_peek_export) put_data = [1, "f", 3, "c", None] peek_data = [] get_data = [] cocotb.start_soon(self.do_blocking_put(pp, put_data)) await self.do_blocking_peek(gpp, peek_data) await self.do_blocking_get(gpp, get_data) self.assertEqual(put_data[:-1], get_data) self.assertEqual(put_data[0], peek_data[0]) peek_data = [] get_data = [] fifo.flush() cocotb.start_soon(self.do_blocking_put(pp, put_data)) await self.do_blocking_peek(gpp, peek_data) await self.do_blocking_get(gpp, get_data) self.assertTrue(put_data[0], peek_data[0]) self.assertEqual(put_data[:-1], get_data) pass @staticmethod async def do_nonblocking_put(put_port, data_list): for data in data_list: while not put_port.try_put(data): await waitabit() @staticmethod async def do_nonblocking_get(get_port, data_list): while True: success, datum = get_port.try_get() if success: if datum is not None: data_list.append(datum) else: break else: await waitabit() @staticmethod async def do_nonblocking_peek(peek_port, data_list): data_list.append(peek_port.try_peek()) async def test_fifo_nonblocking(self): fifo = self.make_fifo(uvm_tlm_fifo) pp = uvm_nonblocking_put_port("pp", self.my_root) gp = uvm_nonblocking_get_port("gp", self.my_root) pk = uvm_nonblocking_peek_port("pk", self.my_root) pp.connect(fifo.nonblocking_put_export) gp.connect(fifo.nonblocking_get_export) pk.connect(fifo.nonblocking_peek_export) put_data = [10, 20, 30, "c0", None] get_data = [] peek_data = [] self.assertFalse(pk.can_peek()) await self.do_nonblocking_peek(pk, peek_data) success, data = peek_data.pop() self.assertFalse(success) self.assertIsNone(data) await waitabit() cocotb.start_soon(self.do_nonblocking_put(pp, put_data)) self.assertTrue(pk.can_peek()) await self.do_nonblocking_peek(pk, peek_data) success, data = peek_data.pop() self.assertTrue(success) self.assertEqual(data, put_data[0]) await self.do_nonblocking_get(gp, get_data) self.assertEqual(put_data[:-1], get_data) # now with get_peek gpp = uvm_nonblocking_get_peek_port("gpp", self.my_root) gpp.connect(fifo.nonblocking_get_peek_export) get_data = [] peek_data = [] self.assertFalse(pk.can_peek()) await self.do_nonblocking_peek(gpp, peek_data) success, data = peek_data.pop() self.assertFalse(success) self.assertIsNone(data) cocotb.start_soon(self.do_nonblocking_put(pp, put_data)) await Timer(1, "us") self.assertTrue(pk.can_peek()) await self.do_nonblocking_peek(gpp, peek_data) success, data = peek_data.pop() self.assertTrue(success) self.assertEqual(data, put_data[0]) await self.do_nonblocking_get(gpp, get_data) self.assertEqual(put_data[:-1], get_data) pyuvm-4.0.1/tests/cocotb_tests/queue/000077500000000000000000000000001507477334100176705ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/queue/Makefile000077500000000000000000000004411507477334100213320ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/queue/clk.sv000066400000000000000000000001301507477334100210050ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/queue/test.py000066400000000000000000000120411507477334100212170ustar00rootroot00000000000000import cocotb from cocotb.clock import Clock from cocotb.triggers import FallingEdge from pyuvm import * from pyuvm import utility_classes class CocotbProxy: def __init__(self, dut, label): self.dut = dut ConfigDB().set(None, "*", label, self) self.driver_queue = UVMQueue(maxsize=1) self.cmd_mon_queue = UVMQueue(maxsize=0) self.result_mon_queue = UVMQueue(maxsize=0) self.done = cocotb.triggers.Event(name="Done") def send_op(self, aa, bb, op): self.driver_queue.put((aa, bb, op)) def get_cmd(self): return self.cmd_mon_queue.get() def get_result(self): return self.result_mon_queue.get() async def reset(self): await FallingEdge(self.dut.clk) self.dut.reset_n = 0 self.dut.A = 0 self.dut.B = 0 self.dut.op = 0 await FallingEdge(self.dut.clk) self.dut.reset_n = 1 await FallingEdge(self.dut.clk) async def driver_bfm(self): self.dut.start = self.dut.A = self.dut.B = 0 self.dut.op = 0 while True: await FallingEdge(self.dut.clk) if self.dut.start == 0 and self.dut.done == 0: try: (aa, bb, op) = self.driver_queue.get_nowait() self.dut.A = aa self.dut.B = bb self.dut.op = op self.dut.start = 1 except queue.Empty: pass elif self.dut.start == 1: if self.dut.done.value == 1: self.dut.start = 0 async def cmd_mon_bfm(self): prev_start = 0 while True: await FallingEdge(self.dut.clk) try: start = int(self.dut.start.value) except ValueError: start = 0 if start == 1 and prev_start == 0: self.cmd_mon_queue.put_nowait( (int(self.dut.A), int(self.dut.B), int(self.dut.op)) ) prev_start = start async def result_mon_bfm(self): prev_done = 0 while True: await FallingEdge(self.dut.clk) try: done = int(self.dut.done) except ValueError: done = 0 if done == 1 and prev_done == 0: self.result_mon_queue.put_nowait(int(self.dut.result.value)) prev_done = done def run_uvm_test(test_name): root = uvm_root() root.run_test(test_name) # noinspection PyArgumentList,PyAsyncCall # @cocotb.test() async def test_alu(dut): clock = Clock(dut.clk, 2, "us") cocotb.start_soon(clock.start()) proxy = CocotbProxy(dut, "PROXY") await proxy.reset() cocotb.start_soon(proxy.driver_bfm()) cocotb.start_soon(proxy.cmd_mon_bfm()) cocotb.start_soon(proxy.result_mon_bfm()) await FallingEdge(dut.clk) test_thread = threading.Thread( target=run_uvm_test, args=("CocotbAluTest",), name="run_test" ) test_thread.start() await proxy.done.wait() await FallingEdge(dut.clk) # noinspection PyArgumentList,PyAsyncCall @cocotb.test() async def test_queue(dut): "Test basic QUEUE functions" qq = utility_classes.UVMQueue(maxsize=1) await qq.put(5) got = await qq.get() assert got == 5 await qq.put("x") peeked = await qq.peek() assert "x" == peeked got = await qq.get() qq.put_nowait(5) got = qq.get_nowait() assert got == 5 qq.put_nowait("x") peeked = qq.peek_nowait() assert "x" == peeked got = qq.get_nowait() assert got == peeked async def delay_put(qq, delay, data): for dd in data: await qq.put(dd) async def delay_get(qq, delay): ret_dat = [] datum = await qq.get() ret_dat.append(datum) while datum is not None: datum = await qq.get() ret_dat.append(datum) return ret_dat async def delay_peek(qq, delay): return await qq.peek() @cocotb.test() async def wait_on_queue(dut): """Test put and get with waits""" clock = Clock(dut.clk, 2, "us") # make the simulator wait cocotb.start_soon(clock.start()) qq = utility_classes.UVMQueue(maxsize=1) send_data = [0.01, "two", 3, None] cocotb.start_soon(delay_put(qq, 0.01, send_data)) got_data = await delay_peek(qq, 0.01) assert got_data == 0.01 got_data = await delay_get(qq, 0.01) @cocotb.test() async def nowait_tests(dut): """Test the various nowait flavors""" qq = utility_classes.UVMQueue(maxsize=1) await qq.put(5) try: qq.put_nowait(6) assert False except cocotb.queue.QueueFull: pass try: xx = qq.peek_nowait() assert xx == 5 except cocotb.queue.QueueEmpty: assert False try: xx = qq.get_nowait() assert xx == 5 except cocotb.queue.QueueEmpty: assert False try: xx = qq.peek_nowait() assert False except cocotb.queue.QueueEmpty: pass try: xx = qq.get_nowait() assert False except cocotb.queue.QueueEmpty: pass pyuvm-4.0.1/tests/cocotb_tests/run_phase/000077500000000000000000000000001507477334100205305ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/run_phase/Makefile000077500000000000000000000004411507477334100221720ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/run_phase/clk.sv000066400000000000000000000001301507477334100216450ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/run_phase/test.py000066400000000000000000000050371507477334100220660ustar00rootroot00000000000000import cocotb from cocotb.triggers import Timer from cocotb.utils import get_sim_time from pyuvm import * class my_test(uvm_test): async def run_phase(self): self.raise_objection() self.drop_objection() class my_error(uvm_test): async def run_phase(self): self.raise_objection() raise UVMError("This is an error") class my_no_objection(uvm_test): async def run_phase(self): print("Running without using objections") class nested_parent(uvm_test): async def run_phase(self): self.raise_objection() await Timer(1, "ms") self.drop_objection() class nested_objections(nested_parent): async def run_phase(self): self.raise_objection() await super().run_phase() await Timer(10, "ms") self.drop_objection() def check_phase(self): assert get_sim_time("ms") > 10 class TopTest(uvm_test): def build_phase(self): # super().build_phase() self.sub_component = SubComponent("sub_component", self) async def run_phase(self): self.raise_objection() await Timer(10, "ms") self.drop_objection() class SubComponent(uvm_component): def __init__(self, name, parent): super().__init__(name, parent) async def run_phase(self): self.raise_objection() # sub component takes longer than TopTest await Timer(50, "ms") self.drop_objection() def check_phase(self): assert get_sim_time("ms") >= 50 @cocotb.test() async def run_test(dut): """Test basic run_phase operation with objection""" await uvm_root().run_test("my_test") assert True @cocotb.test(expect_error=UVMError) async def error(dut): """Test that raising exceptions creates cocotb errors""" await uvm_root().run_test("my_error") @cocotb.test() async def run_after_error(dut): """Test run_phase operation after previous test raised exception""" await uvm_root().run_test("my_test") assert True @cocotb.test() async def run_no_objection(dut): """Test using no objections, after a test that did use them""" # Expect a warning message. Can that be tested for? await uvm_root().run_test("my_no_objection") @cocotb.test() async def test_nested_objections(_): await uvm_root().run_test(nested_objections) @cocotb.test() async def test_sub_component_objections(_): """ Test that all objections from all components are waited for regardless of the order in which they are raised or dropped. """ await uvm_root().run_test(TopTest) pyuvm-4.0.1/tests/cocotb_tests/t05_base_classes/000077500000000000000000000000001507477334100216635ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/t05_base_classes/Makefile000077500000000000000000000004411507477334100233250ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1ns COCOTB_HDL_TIMEPRECISION=1ps include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/t05_base_classes/clk.sv000066400000000000000000000001301507477334100230000ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/t05_base_classes/test.py000066400000000000000000000034231507477334100232160ustar00rootroot00000000000000import cocotb import pytest from pyuvm import * @cocotb.test() async def test_01_uvm_transaction_accept_time(dut): """ Test uvm_transaction accept time """ tr0 = uvm_transaction() tr1 = uvm_transaction() tr0.accept_tr(None) await cocotb.triggers.Timer(100, "ns") tr1.accept_tr(None) assert tr1.get_accept_time() > tr0.get_accept_time() assert tr1.get_accept_time() > 0 @cocotb.test() async def test_01_uvm_transaction_begin_time(dut): """ Test uvm_transaction begin time """ tr0 = uvm_transaction() tr1 = uvm_transaction() tr0.accept_tr(1) assert tr0.get_accept_time() == 1 await cocotb.triggers.Timer(100, "ns") tr1.accept_tr(2) assert tr1.get_accept_time() == 2 with pytest.raises(error_classes.UVMFatalError): tr1.begin_tr(1) tr1.begin_tr(None, None) await cocotb.triggers.Timer(100, "ns") tr0.begin_tr() # Check time values # Check begin time between transactions assert tr0.get_begin_time() > tr1.get_begin_time() assert tr1.get_begin_time() > 0 assert tr0.get_begin_time() > tr1.get_begin_time() @cocotb.test() async def test_01_uvm_transaction_end_time(dut): """ Test uvm_transaction begin time """ tr0 = uvm_transaction() tr1 = uvm_transaction() tr0.accept_tr(None) tr0.begin_tr(None, None) tr0.end_tr(None, None) await cocotb.triggers.Timer(100, "ns") tr1.accept_tr(None) assert tr1.get_accept_time() > tr0.get_accept_time() await cocotb.triggers.Timer(50, "ns") tr1.begin_tr(None, None) await cocotb.triggers.Timer(100, "ns") tr1.end_tr(None, None) assert tr1.get_accept_time() < tr1.get_end_time() assert tr1.get_accept_time() < tr1.get_begin_time() assert tr1.get_end_time() > tr0.get_end_time() pyuvm-4.0.1/tests/cocotb_tests/t08_factories/000077500000000000000000000000001507477334100212165ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/t08_factories/Makefile000077500000000000000000000004411507477334100226600ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/t08_factories/clk.sv000066400000000000000000000001301507477334100223330ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/t08_factories/factory_tests.py000066400000000000000000000550571507477334100244750ustar00rootroot00000000000000# pylint: disable=unused-wildcard-import import logging from pyuvm import * class s08_factory_classes_TestCase: # class s08_factory_classes_TestCase (unittest.TestCase): """ 8.0 Factory Class Testing """ class original_comp(uvm_component): ... class comp_1(uvm_component): ... class comp_2(uvm_component): ... class comp_3(uvm_component): ... class original_object(uvm_object): ... class object_1(uvm_object): ... class object_2(uvm_object): ... class object_3(uvm_object): ... def setUp(self): self.fd = utility_classes.FactoryData() uvm_root().clear_children() self.top = uvm_component("top", None) self.mid = uvm_component("mid", self.top) self.sib = uvm_component("sib", self.top) self.mid_orig = self.original_comp("orig", self.mid) self.sib_orig = self.original_comp("orig", self.sib) self.factory = uvm_factory() def tearDown(self): self.fd.clear_overrides() uvm_root.clear_singletons() self.top.clear_hierarchy() uvm_component.clear_components() def test_set_inst_override_by_type_8_3_1_4_1(self): """ 8.3.1.4.1 set_inst_override_by_type Basic test to make sure that the factory data is set properly """ self.factory.set_inst_override_by_type( self.original_comp, self.comp_1, "top.mid.orig" ) override = self.fd.overrides[self.original_comp] assert "top.mid.orig" in override.inst_overrides assert self.comp_1 == override.inst_overrides["top.mid.orig"] assert self.comp_1 == override.find_inst_override("top.mid.orig") def test_set_inst_override_by_name_8_3_1_4_1(self): """ 8.3.1.4.1 :return: check that we put the right data into fd for this override. """ self.factory.set_inst_override_by_name( "original_comp", "comp_1", "top.sib.orig" ) override = self.fd.overrides[self.original_comp] assert "top.sib.orig" in override.inst_overrides assert self.comp_1 == override.inst_overrides["top.sib.orig"] assert self.comp_1 == override.find_inst_override("top.sib.orig") self.factory.set_inst_override_by_name("arb", "comp_2", "top.sib.orig2") override = self.fd.overrides["arb"] assert "top.sib.orig2" in override.inst_overrides assert self.comp_2 == override.inst_overrides["top.sib.orig2"] assert self.comp_2 == override.find_inst_override("top.sib.orig2") def test_set_type_override_by_type_8_3_1_4_2(self): """ 8.3.1.4.2 Check that class override is set properly """ self.factory.set_type_override_by_type(self.original_comp, self.comp_1) ovr = self.fd.overrides[self.original_comp] assert self.comp_1 == ovr.type_override self.factory.set_type_override_by_type(self.original_comp, self.comp_2, False) ovr = self.fd.overrides[self.original_comp] assert self.comp_1 == ovr.type_override self.factory.set_type_override_by_type(self.original_comp, self.comp_2, True) ovr = self.fd.overrides[self.original_comp] assert self.comp_2 == ovr.type_override def test_set_type_override_by_name_8_3_1_4_2(self): """ 8.3.1.4.2 Check that name override is setting correct information. """ self.factory.set_type_override_by_name("original_comp", "comp_1") assert self.comp_1 == self.fd.overrides[self.original_comp].type_override self.factory.set_type_override_by_name("original_comp", "comp_2", False) assert self.comp_1 == self.fd.overrides[self.original_comp].type_override self.factory.set_type_override_by_name("original_comp", "comp_2", True) assert self.comp_2 == self.fd.overrides[self.original_comp].type_override # Test adding an override with an arbitrary string self.factory.set_type_override_by_name("any_str", "comp_2") assert self.comp_2 == self.fd.overrides["any_str"].type_override def test_override_str(self): self.factory.set_type_override_by_type(self.original_comp, self.comp_1) ovr = self.fd.overrides[self.original_comp] assert self.comp_1 == ovr.type_override # using name because less typing self.factory.set_inst_override_by_name( "original_comp", "comp_2", "top.sib.orig" ) self.factory.set_inst_override_by_name( "original_comp", "comp_3", "top.mid.orig" ) assert ( "Type Override: comp_1 || Instance Overrides: top.sib.orig => comp_2 | top.mid.orig => comp_3" == str(self.fd.overrides[self.original_comp]) ) def test_find_type_override(self): """ Test simple retrieval of type override :return: """ self.factory.set_type_override_by_type(self.original_comp, self.comp_1) assert self.comp_1 == self.fd.find_override(self.original_comp) def test_find_override_by_name(self): self.factory.set_type_override_by_name("any_str", "comp_2") new_obj = self.fd.find_override("any_str") assert self.comp_2 == new_obj def test_find_recursive_override_8_3_1_5(self): """ 8.3.1.5 Test recursive overrides :return: """ self.factory.set_type_override_by_type(self.original_comp, self.comp_1) self.factory.set_type_override_by_type(self.comp_1, self.comp_2) self.factory.set_type_override_by_type(self.comp_2, self.comp_3) assert self.comp_3 == self.fd.find_override(self.original_comp) def test_find_recursion_loop_8_3_1_5(self): """ 8.3.1.5 Test recursive overrides with a :return: """ self.factory.set_type_override_by_type(self.original_comp, self.comp_1) self.factory.set_type_override_by_type(self.comp_1, self.comp_2) self.factory.set_type_override_by_type(self.comp_2, self.comp_3) self.factory.set_type_override_by_type(self.comp_3, self.original_comp) logger = logging.getLogger("Factory") level = logger.level logger.setLevel(logging.CRITICAL) overridden_orig = self.fd.find_override(self.original_comp) logger.setLevel(level) assert self.comp_3 == overridden_orig def test_no_type_override(self): """ 8.3.1.5 Test looking for an override and not finding one :return: """ no_over = self.fd.find_override(self.original_comp) assert self.original_comp == no_over def test_find_inst_override_8_3_1_5(self): """ 8.3.1.5 Test for an override with an inst path """ self.factory.set_inst_override_by_type( self.original_comp, self.comp_1, "top.sib.orig" ) self.factory.set_inst_override_by_type( self.original_comp, self.comp_2, "top.mid.orig" ) overridden = self.fd.find_override(self.original_comp, "top.sib.orig") assert self.comp_1 == overridden def test_find_inst_override_wildcard_8_3_1_5(self): """ 8.3.1.5 Test for an override with a wildcard :return: """ self.factory.set_inst_override_by_type(self.original_comp, self.comp_2, "*") overridden = self.fd.find_override(self.original_comp, "top.mid.orig") assert self.comp_2 == overridden overridden = self.fd.find_override(self.original_comp, "top.sib.orig") assert self.comp_2 == overridden def test_find_inst_override_wildcard_in_path_8_3_1_5(self): """ 8.3.1.5 Test for an inst_path wildcard in part of a path :return: """ self.factory.set_inst_override_by_type( self.original_comp, self.comp_2, "top.mid*" ) overridden = self.fd.find_override(self.original_comp, "top.mid.orig") assert self.comp_2 == overridden overridden = self.fd.find_override(self.original_comp, "top.sib.orig") assert self.original_comp == overridden def test_find_inst_recursive_override_in_path_8_3_1_5(self): """ 8.3.1.5 Test for recursive override with inst path. :return: """ self.factory.set_inst_override_by_type(self.original_comp, self.comp_1, "*") self.factory.set_inst_override_by_type(self.comp_1, self.comp_2, "*") self.factory.set_inst_override_by_type(self.comp_2, self.comp_3, "*") overridden = self.fd.find_override(self.original_comp, "top.mid.orig") assert self.comp_3 == overridden def test_not_finding_inst_override_8_3_1_5(self): """ 8.3.1.5 Test for looking for an inst override and not finding one with a type override :return: """ self.factory.set_type_override_by_type(self.original_comp, self.comp_3) self.factory.set_inst_override_by_type( self.original_comp, self.comp_1, "top.sib.orig" ) self.factory.set_inst_override_by_type( self.original_comp, self.comp_2, "top.mid.orig" ) overridden = self.fd.find_override(self.original_comp, "top.not_there.orig") assert self.comp_3 == overridden def test_not_finding_inst_override_at_all_8_3_1_5(self): """ 8.3.1.5 Test for looking for an inst override and not finding one and with no type_override :return: None """ self.factory.set_inst_override_by_type( self.original_comp, self.comp_1, "top.sib.orig" ) self.factory.set_inst_override_by_type( self.original_comp, self.comp_2, "top.mid.orig" ) overridden = self.fd.find_override(self.original_comp, "top.not_there.orig") assert self.original_comp == overridden def test_create_object_by_type_and_name_8_3_1_5(self): """ 8.3.1.5 :return: None """ new_obj = self.factory.create_object_by_type(self.original_object, name="foo") assert isinstance(new_obj, self.original_object) assert "foo" == new_obj.get_name() new_obj = self.factory.create_object_by_name("object_1", name="foo_1") assert isinstance(new_obj, self.object_1) assert "foo_1" == new_obj.get_name() def test_create_object_by_type_and_name_with_type_override_8_3_1_5(self): """ 8.3.1.5 :return: None """ self.factory.set_type_override_by_type(self.original_object, self.object_2) self.factory.set_inst_override_by_type( self.original_object, self.object_1, "top.sib.orig" ) self.factory.set_inst_override_by_name( "original_object", "object_1", "top.sib.orig_1" ) new_obj = self.factory.create_object_by_type( self.original_object, parent_inst_path="top.sib", name="orig" ) assert isinstance(new_obj, self.object_1) assert "orig" == new_obj.get_name() new_obj = self.factory.create_object_by_type( self.original_object, parent_inst_path="top.noway", name="orig" ) assert isinstance(new_obj, self.object_2) assert "orig" == new_obj.get_name() new_obj = self.factory.create_object_by_name( "original_object", parent_inst_path="top.sib", name="orig_1" ) assert isinstance(new_obj, self.object_1) assert "orig_1" == new_obj.get_name() new_obj = self.factory.create_object_by_name( "original_object", parent_inst_path="top.noway", name="orig2b" ) assert isinstance(new_obj, self.object_2) assert "orig2b" == new_obj.get_name() logger = logging.getLogger("Factory") level = logger.level logger.setLevel(logging.CRITICAL) saw_error = False try: _ = self.factory.create_object_by_name("not_an_object", name="bad_name") except UVMFactoryError: saw_error = True assert saw_error logger.setLevel(level) def test_create_component_by_type_and_name_override_8_3_1_5(self): """ 8.3.1.5 Create overridden object. :return: """ test_top = self.factory.create_component_by_type( self.original_comp, name="test_top" ) self.factory.set_type_override_by_type(self.original_comp, self.comp_1) self.factory.set_inst_override_by_type( self.original_comp, self.comp_1, "test_top.sib1" ) self.factory.set_inst_override_by_name( "original_comp", "comp_2", "test_top.sib2" ) new_obj = self.factory.create_component_by_type( self.original_comp, parent_inst_path="test_top", name="sib1", parent=test_top, ) assert isinstance(new_obj, self.comp_1) assert "sib1" == new_obj.get_name() assert "test_top.sib1" == new_obj.get_full_name() new_obj = self.factory.create_component_by_name( "original_comp", parent_inst_path="test_top", name="sib2", parent=test_top ) assert isinstance(new_obj, self.comp_2) assert "sib2" == new_obj.get_name() assert "test_top.sib2" == new_obj.get_full_name() def test_set_type_alias_8_3_1_6_1(self): """ 8.3.1.6.1 This is not implemented in SystemVerilog or here :return: None """ try: self.factory.set_type_alias("any_str", self.original_comp) except error_classes.UVMNotImplemented: assert True return assert False def test_find_override_by_type_and_name_8_3_1_7_1(self): """ 8.3.1.7.1 Testing both the type and name variants :return: None """ self.factory.set_inst_override_by_type( self.original_object, self.object_1, "top.sib.orig" ) self.factory.set_inst_override_by_name( "original_object", "object_2", "top.sib.orig_2" ) override = self.factory.find_override_by_type( self.original_object, "top.sib.orig" ) assert self.object_1 == override override = self.factory.find_override_by_name( "original_object", "top.sib.orig_2" ) assert self.object_2 == override try: _ = self.factory.find_override_by_name( self.original_object, "top.sib.orig_2" ) except AssertionError: assert True return assert False def test_is_type_name_registered_8_3_1_7_3(self): """ 8.3.1.7.3 :return: None """ assert self.factory.is_type_name_registered("original_object") assert not self.factory.is_type_name_registered("fake_object") def test_is_type_registered_8_3_1_7_3(self): """ 8.3.1.7.3 :return: None """ assert self.factory.is_type_registered(self.original_comp) class uvm_fake(uvm_object): ... del self.factory.fd.classes["uvm_fake"] assert not self.factory.is_type_registered(uvm_fake) def test_factory_str(self): self.factory.set_type_override_by_type(self.original_comp, self.comp_1) ovr = self.fd.overrides[self.original_comp] assert self.comp_1 == ovr.type_override # using name because less typing self.factory.set_inst_override_by_name( "original_comp", "comp_2", "top.sib.orig" ) self.factory.set_inst_override_by_name( "original_comp", "comp_3", "top.mid.orig" ) self.factory.set_inst_override_by_type( self.original_object, self.object_1, "label" ) self.factory.debug_level = 0 ss0 = self.factory.__str__() self.factory.debug_level = 1 ss1 = self.factory.__str__() self.factory.debug_level = 2 ss2 = self.factory.__str__() # Testing for the actual strings will cause errors as classes change due to # other tests being run. This catches the basic functionality. assert len(ss2) > len(ss1) > len(ss0) def test_factory_str_long_pathes(self): """Test, whether an overrides with a long instance paths produce correct output""" self.factory.set_inst_override_by_name( "original_comp", "comp_2", "top.sib.orig.some_really_long_named_instance" ) self.factory.set_inst_override_by_name( "original_comp", "comp_3", "top.mid.orig.another_really_long_named_instance" ) self.factory.debug_level = 0 ss0 = self.factory.__str__() assert ss0.find("top.sib.orig.some_really") assert ss0.find("top.sib.orig.another_rea") self.factory.debug_level = 1 ss1 = self.factory.__str__() assert ss1.find("top.sib.orig.some_really") assert ss1.find("top.sib.orig.another_rea") self.factory.debug_level = 2 ss2 = self.factory.__str__() assert ss2.find("top.sib.orig.some_really") assert ss2.find("top.sib.orig.another_rea") def test_object_creation(self): new_obj = uvm_object.create("claribel") assert isinstance(new_obj, uvm_object) assert "claribel" == new_obj.get_name() def test_class_creation(self): class Foo(uvm_object): ... new_obj = Foo.create("foobar") assert isinstance(new_obj, Foo) assert "foobar" == new_obj.get_name() def test_component_creation(self): new_comp = uvm_component.create("test", None) assert isinstance(new_comp, uvm_component) assert "test" == new_comp.get_name() def test_ext_comp_creation(self): class FooComp(uvm_component): ... new_comp = FooComp.create("Foo", None) assert "Foo" == new_comp.get_name() assert isinstance(new_comp, FooComp) async def test_type_override_by_type(self): class Comp(uvm_component): ... class Other(Comp): ... # noinspection PyUnusedLocal class Test(uvm_test): # pylint: disable=unused-variable def build_phase(self): uvm_factory().set_type_override_by_type(Comp, Other) self.cc = Comp.create("cc", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc, Other) async def test_inst_override_by_type(self): class Comp(uvm_component): ... class Other(Comp): ... # noinspection PyUnusedLocal class Test(uvm_test): # pylint: disable=unused-variable def build_phase(self): uvm_factory().set_inst_override_by_type( Comp, Other, self.get_full_name() + ".cc2" ) self.cc1 = Comp.create("cc1", self) self.cc2 = Comp.create("cc2", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc1, Comp) assert isinstance(utt.cc2, Other) async def test_async_inst_override_by_name(self): class Comp(uvm_component): ... class Other(Comp): ... # noinspection PyUnusedLocal class Test(uvm_test): # pylint: disable=unused-variable def build_phase(self): uvm_factory().set_inst_override_by_name( "Comp", "Other", self.get_full_name() + ".cc2" ) self.cc1 = Comp.create("cc1", self) self.cc2 = Comp.create("cc2", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc1, Comp) assert isinstance(utt.cc2, Other) async def test_type_override_by_name(self): class Comp(uvm_component): ... class Other(Comp): ... # noinspection PyUnusedLocal class Test(uvm_test): # pylint: disable=unused-variable def build_phase(self): uvm_factory().set_type_override_by_name("Comp", "Other") self.cc1 = Comp.create("cc1", self) self.cc2 = Comp.create("cc2", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc1, Other) assert isinstance(utt.cc2, Other) async def test_obj_type_override_by_type(self): class Obj(uvm_object): ... class OtherObj(Obj): ... # noinspection PyUnusedLocal class Test(uvm_test): # pylint: disable=unused-variable def build_phase(self): uvm_factory().set_type_override_by_type(Obj, OtherObj) self.cc1 = Obj.create("cc1") async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc1, OtherObj) async def test_obj_type_override_by_name(self): class Obj(uvm_object): ... class OtherObj(Obj): ... # noinspection PyUnusedLocal # pylint: disable=unused-variable class Test(uvm_test): def build_phase(self): uvm_factory().set_type_override_by_name("Obj", "OtherObj") self.cc1 = Obj.create("cc1") async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc1, OtherObj) async def test_obj_inst_override_by_type(self): class Obj(uvm_object): ... class OtherObj(Obj): ... # noinspection PyUnusedLocal class Test(uvm_test): # pylint: disable=unused-variable def build_phase(self): uvm_factory().set_inst_override_by_type( Obj, OtherObj, self.get_full_name() ) self.cc1 = Obj.create("cc1") async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("Test", keep_singletons=True) utt = uvm_root()._utt() assert isinstance(utt.cc1, Obj) # Can't inst override object pyuvm-4.0.1/tests/cocotb_tests/t08_factories/test.py000066400000000000000000000012651507477334100225530ustar00rootroot00000000000000import inspect import cocotb import factory_tests as t08 @cocotb.test() # pylint: disable=no-value-for-parameter async def test_08_factory(dut): """Tests different aspects of the factory""" tc08 = t08.s08_factory_classes_TestCase() methods = inspect.getmembers( t08.s08_factory_classes_TestCase ) # , predicate=inspect.ismethod) for mm in methods: (name, _) = mm if name.startswith("test_"): print(name) test = getattr(tc08, name) tc08.setUp() if inspect.iscoroutinefunction(test): await test() else: test() tc08.tearDown() assert True pyuvm-4.0.1/tests/cocotb_tests/t09_phasing/000077500000000000000000000000001507477334100206715ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/t09_phasing/Makefile000077500000000000000000000004411507477334100223330ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/t09_phasing/clk.sv000066400000000000000000000001301507477334100220060ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/t09_phasing/test.py000066400000000000000000000057141507477334100222310ustar00rootroot00000000000000import inspect import cocotb from pyuvm import * from pyuvm import utility_classes phase_list = {} class my_comp(uvm_component): def log_phase(self): """ Log this function to the phase list """ comp_name = self.get_name() function_name = inspect.stack()[1][3] phase_list[function_name].append(comp_name) def build_phase(self): self.log_phase() def connect_phase(self): self.log_phase() def end_of_elaboration_phase(self): self.log_phase() def start_of_simulation_phase(self): self.log_phase() async def run_phase(self): self.log_phase() def extract_phase(self): self.log_phase() def check_phase(self): self.log_phase() def report_phase(self): self.log_phase() def final_phase(self): self.log_phase() def setUp(): for phase_class in uvm_common_phases: phase_func = phase_class.__name__[4:] phase_list[phase_func] = [] top = my_comp("top", None) # # top +-> aa +-> cc # +-> dd # +-> bb +-> ee # +-> ff # aa = my_comp("aa", top) bb = my_comp("bb", top) my_comp("cc", aa) my_comp("dd", aa) my_comp("ee", bb) my_comp("ff", bb) return top def tearDown(): uvm_root().clear_hierarchy() class my_test(uvm_test): async def run_phase(self): self.raise_objection() print("Hey, I'm here") self.drop_objection() @cocotb.test() async def run_test(dut): """Test the various nowait flavors""" await uvm_root().run_test("my_test") assert True @cocotb.test() async def test_stub(dut): """testing the basic testing mechanism""" top = setUp() top.build_phase() assert "top" == phase_list["build_phase"][0] tearDown() async def test_traverse(): top = setUp() top_down = ["top", "aa", "cc", "dd", "bb", "ee", "ff"] bottom_up = ["cc", "dd", "aa", "ee", "ff", "bb", "top"] sorted_list = sorted(top_down) for phase_class in uvm_common_phases: phase = phase_class() phase.traverse(top) if phase_class == uvm_run_phase: await utility_classes.ObjectionHandler().run_phase_complete() function_name = phase_class.__name__[4:] returned_comps = phase_list[function_name] try: if isinstance(phase, uvm_run_phase): assert sorted_list == sorted(returned_comps) elif isinstance(phase, uvm_topdown_phase): assert top_down == returned_comps elif isinstance(phase, uvm_bottomup_phase): assert bottom_up == returned_comps else: # Should not get here. assert False except AssertionError: tearDown() return False tearDown() return True @cocotb.test() async def traverse(self): """Testing topdown and bottom up traversal""" assert await test_traverse() pyuvm-4.0.1/tests/cocotb_tests/t12_tlm/000077500000000000000000000000001507477334100200265ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/t12_tlm/Makefile000077500000000000000000000004411507477334100214700ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/t12_tlm/clk.sv000066400000000000000000000001301507477334100211430ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/t12_tlm/test.py000066400000000000000000000053631507477334100213660ustar00rootroot00000000000000import inspect import cocotb import test_12_uvm_tlm_interfaces as test_mod import pyuvm from pyuvm import * async def run_tests(dut): tests_pass = {} t12 = test_mod.s12_uvm_tlm_interfaces_TestCase() methods = inspect.getmembers( test_mod.s12_uvm_tlm_interfaces_TestCase ) # predicate=inspect.ismethod) for mm in methods: (name, _) = mm if name.startswith("test_"): test = getattr(t12, name) t12.setUp() try: if inspect.iscoroutinefunction(test): await test(dut) else: test() tests_pass[name] = True except AssertionError: tests_pass[name] = False t12.tearDown() any_failed = False for test, passed in tests_pass.items(): if passed: pf = "Pass " else: pf = "FAILED " any_failed = True print(f"{pf} {test}") assert not any_failed class FIFO(uvm_component): def build_phase(self): self.fifo = uvm_tlm_analysis_fifo("fifo", self) self.gp = uvm_get_port("gp", self) def connect_phase(self): self.gp.connect(self.fifo.get_export) def start_of_simulation_phase(self): self.data = [] async def run_phase(self): while True: datum = await self.gp.get() self.data.append(datum) class Subscriber(uvm_subscriber): def start_of_simulation_phase(self): self.data = [] def write(self, datum): self.data.append(datum) class Export(uvm_analysis_export): def start_of_simulation_phase(self): self.data = [] def write(self, datum): self.data.append(datum) @pyuvm.test() class AnalysisTest(uvm_test): def build_phase(self): self.gp = uvm_get_port("gp", self) self.ap1 = uvm_analysis_port("ap1", self) self.ap2 = uvm_analysis_port("ap2", self) self.export = Export("export", self) self.fifo = FIFO("fifo", self) self.subscriber = Subscriber("subscriber", self) def connect_phase(self): self.ap1.connect(self.ap2) self.ap2.connect(self.export) self.ap2.connect(self.subscriber.analysis_export) self.ap2.connect(self.fifo.fifo.analysis_export) async def run_phase(self): self.raise_objection() self.data_list = [1, "1", 2, "two"] for datum in self.data_list: self.ap1.write(datum) self.drop_objection() def check_phase(self): assert self.data_list == self.subscriber.data assert self.data_list == self.export.data assert self.data_list == self.fifo.data @cocotb.test() async def run_test(_): uvm_root().run_test(AnalysisTest) pyuvm-4.0.1/tests/cocotb_tests/t12_tlm/test_12_uvm_tlm_interfaces.py000066400000000000000000000552331507477334100256370ustar00rootroot00000000000000import cocotb import uvm_unittest from cocotb.triggers import Timer from pyuvm import * # pylint: disable=unused-wildcard-import async def waitabit(abit=5): await Timer(1, "us") class s12_uvm_tlm_interfaces_TestCase(uvm_unittest.uvm_TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if uvm_root().has_child("my_root"): self.my_root = uvm_root().get_child("my_root") else: self.my_root = uvm_component("my_root", None) def setUp(self): ObjectionHandler().run_phase_done_flag = False self.my_root.clear_children() def tearDown(self): ObjectionHandler().run_phase_done_flag = True class my_comp(uvm_component): ... # Put class TestPutExportBase(uvm_port_base): def __init__(self, name, parent=None): super().__init__(name, parent) self.data = None class TestBlockingPutExport(TestPutExportBase, uvm_blocking_put_export): async def put(self, data): self.data = data class TestNonBlockingPutExport(TestPutExportBase, uvm_nonblocking_put_export): def __init__(self, name=None, parent=None): super().__init__(name, parent) self.blocked = None def try_put(self, data): if not self.blocked: self.data = data return not self.blocked def can_put(self): return not self.blocked class TestPutExport(TestBlockingPutExport, TestNonBlockingPutExport): ... # Get class TestGetExportBase: def __init__(self, name="", parent=None): super().__init__(name, parent) self.data = None self.empty = None class TestBlockingGetExport(TestGetExportBase, uvm_blocking_get_export): async def get(self): return self.data class TestNonBlockingGetExport(TestGetExportBase, uvm_nonblocking_get_export): def try_get(self): if not self.empty: return True, self.data return False, None def can_get(self): return not self.empty class TestGetExport(TestBlockingGetExport, TestNonBlockingGetExport): ... # Peek class TestBlockingPeekExport(TestGetExportBase, uvm_blocking_peek_export): async def peek(self): return self.data class TestNonBlockingPeekExport(TestGetExportBase, uvm_nonblocking_peek_export): def try_peek(self): if not self.empty: return True, self.data else: return False, None def can_peek(self): return self.can_peek() class TestPeekExport(TestBlockingPeekExport, TestNonBlockingPeekExport): ... # GetPeek class TestBlockingGetPeekExport(TestBlockingGetExport, TestBlockingPeekExport): ... class TestNonBlockingGetPeekExport( TestNonBlockingGetExport, TestNonBlockingPeekExport ): ... class TestGetPeekExport( TestBlockingGetPeekExport, TestNonBlockingGetPeekExport ): ... # Transport class TestTransportExportBase(uvm_port_base): def __init__(self, name, parent): super().__init__(name, parent) self.put_data = None self.get_data = None self.blocked = True class TestBlockingTransportExport( TestTransportExportBase, uvm_blocking_transport_export ): async def transport(self, put_data): self.put_data = put_data return self.get_data class TestNonBlockingTransportExport( TestTransportExportBase, uvm_nonblocking_transport_export ): def nb_transport(self, put_data): if self.blocked: return False, None else: self.put_data = put_data return True, self.get_data class TestTransportExport( TestBlockingTransportExport, TestNonBlockingTransportExport ): ... # Master class TestBlockingMasterExport( TestBlockingPutExport, TestBlockingGetPeekExport ): ... class TestNonBlockingMasterExport( TestNonBlockingPutExport, TestNonBlockingGetPeekExport ): ... class TestMasterExport(TestBlockingMasterExport, TestNonBlockingMasterExport): ... # Slave class TestBlockingSlaveExport(TestBlockingPutExport, TestBlockingGetPeekExport): ... class TestNonBlockingSlaveExport( TestNonBlockingPutExport, TestNonBlockingGetPeekExport ): ... class TestSlaveExport(TestBlockingSlaveExport, TestNonBlockingSlaveExport): ... class TestInvalidExport(uvm_port_base): """ Used to guarantee a port type error """ ... # Put # Common predefined port tests: put, get, peek, get_peek, transport, master, slave def make_components(self, port_cls, export_cls): """ Make the components for a port test in a child-free component :param port_cls: :param export_cls: :return: port, export, invalid """ self.my_root.clear_children() port = port_cls("port", self.my_root) port2 = port_cls("port2", self.my_root) export = export_cls("export", self.my_root) invalid = self.TestInvalidExport("invalid", self.my_root) return port, port2, export, invalid async def exercise_blocking_put(self, port_cls, export_cls): (bpp, bpp2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): await bpp.put(0) with self.assertRaises(UVMTLMConnectionError): bpp.connect(invalid) bpp.connect(export) bpp2.connect(bpp) # test first port await bpp.put(5) self.assertEqual(export.data, 5) # test port connected to port await bpp2.put(15) self.assertEqual(export.data, 15) def exercise_nonblocking_put(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) export.data = 0 with self.assertRaises(UVMTLMConnectionError): __ = port.try_put("data") with self.assertRaises(UVMTLMConnectionError): __ = port.connect(invalid) port.connect(export) port2.connect(port) export.blocked = True # test first port self.assertFalse(port.try_put(55)) self.assertNotEqual(55, export.data) export.blocked = False self.assertTrue(port.try_put(55)) self.assertEqual(55, export.data) # test port connected to port export.blocked = True self.assertFalse(port.try_put(10)) self.assertNotEqual(10, export.data) export.blocked = False self.assertTrue(port.try_put(10)) self.assertEqual(10, export.data) async def exercise_blocking_get(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __ = await port.get() with self.assertRaises(UVMTLMConnectionError): __ = port.connect(invalid) port.connect(export) port2.connect(port) # test port export.data = 0xDEADBEEF get_data = await port.get() self.assertEqual(0xDEADBEEF, get_data) # test port connected to port export.data = 0xDEADBEE2 get_data = await port2.get() self.assertEqual(0xDEADBEE2, get_data) def exercise_nonblocking_get(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __, __ = port.try_get() with self.assertRaises(UVMTLMConnectionError): __ = port.connect(invalid) port.connect(export) port2.connect(port) # tests port export.empty = True export.data = "Data" success, data = port.try_get() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port.try_get() self.assertEqual("Data", data) self.assertTrue(success) # test port connected to port export.empty = True export.data = "Data2" success, data = port2.try_get() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port2.try_get() self.assertEqual("Data2", data) self.assertTrue(success) async def exercise_blocking_peek(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __ = await port.peek() with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.data = 0xDEADBEEF peek_data = await port.peek() self.assertEqual(0xDEADBEEF, peek_data) # test port connected to port export.data = 0xDEADBE2F peek_data = await port.peek() self.assertEqual(0xDEADBE2F, peek_data) def exercise_nonblocking_peek(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __, __ = port.try_peek() with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.empty = True export.data = "Data" success, data = port.try_peek() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port.try_peek() self.assertEqual("Data", data) self.assertTrue(success) # test port connected to port export.empty = True export.data = "Data2" success, data = port2.try_peek() self.assertFalse(success) self.assertIsNone(data) export.empty = False success, data = port2.try_peek() self.assertEqual("Data2", data) self.assertTrue(success) async def exercise_blocking_get_peek(self, port_cls, export_cls): await self.exercise_blocking_get(port_cls, export_cls) await self.exercise_blocking_peek(port_cls, export_cls) def exercise_nonblocking_get_peek(self, port_cls, export_cls): self.exercise_nonblocking_get(port_cls, export_cls) self.exercise_nonblocking_peek(port_cls, export_cls) async def exercise_get_peek(self, port_cls, export_cls): await self.exercise_blocking_get_peek(port_cls, export_cls) self.exercise_nonblocking_get_peek(port_cls, export_cls) async def exercise_blocking_transport(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __ = await port.transport("sent") with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.get_data = "returned" returned_data = await port.transport("sent") self.assertEqual("returned", returned_data) # test port connected to port export.get_data = "2returned" returned_data = await port2.transport("sent") self.assertEqual("2returned", returned_data) def exercise_nonblocking_transport(self, port_cls, export_cls): (port, port2, export, invalid) = self.make_components(port_cls, export_cls) with self.assertRaises(UVMTLMConnectionError): __, __ = port.nb_transport("sent") with self.assertRaises(UVMTLMConnectionError): port.connect(invalid) port.connect(export) port2.connect(port) # test port export.blocked = True export.get_data = "returned" success, data = port.nb_transport("sent") self.assertFalse(success) self.assertIsNone(data) export.blocked = False success, data = port.nb_transport("sent") self.assertEqual("returned", data) self.assertTrue(success) # test port connected to port export.blocked = True export.get_data = "returned" success, data = port2.nb_transport("sent") self.assertFalse(success) self.assertIsNone(data) export.blocked = False success, data = port2.nb_transport("sent") self.assertEqual("returned", data) self.assertTrue(success) def exercise_slave_do(self, port_cls, export_cls): pass async def test_uvm_blocking_put_port(self): await self.exercise_blocking_put( uvm_blocking_put_port, self.TestBlockingPutExport ) def test_uvm_non_blocking_put_port(self): self.exercise_nonblocking_put( uvm_nonblocking_put_port, self.TestNonBlockingPutExport ) async def test_uvm_put_port(self): await self.exercise_blocking_put(uvm_put_port, self.TestPutExport) self.exercise_nonblocking_put(uvm_put_port, self.TestPutExport) async def test_uvm_blocking_get_port(self): await self.exercise_blocking_get( uvm_blocking_get_port, self.TestBlockingGetExport ) def test_uvm_non_blocking_get_port(self): self.exercise_nonblocking_get( uvm_nonblocking_get_port, self.TestNonBlockingGetExport ) async def test_uvm_get_port(self): await self.exercise_blocking_get(uvm_get_port, self.TestGetExport) self.exercise_nonblocking_get(uvm_get_port, self.TestGetExport) async def test_uvm_blocking_peek_port(self): await self.exercise_blocking_peek( uvm_blocking_peek_port, self.TestBlockingPeekExport ) def test_uvm_non_blocking_peek_port(self): self.exercise_nonblocking_peek( uvm_nonblocking_peek_port, self.TestNonBlockingPeekExport ) async def test_uvm_peek_port(self): await self.exercise_blocking_peek(uvm_peek_port, self.TestPeekExport) self.exercise_nonblocking_peek(uvm_peek_port, self.TestPeekExport) async def test_uvm_blocking_get_peek_port(self): await self.exercise_blocking_get_peek( uvm_blocking_get_peek_port, self.TestBlockingGetPeekExport ) def test_uvm_non_blocking_get_peek_port(self): self.exercise_nonblocking_get_peek( uvm_nonblocking_get_peek_port, self.TestNonBlockingGetPeekExport ) async def test_uvm_get_peek_port(self): await self.exercise_get_peek(uvm_get_peek_port, self.TestGetPeekExport) async def test_uvm_blocking_transport_port(self): await self.exercise_blocking_transport( uvm_blocking_transport_port, self.TestBlockingTransportExport ) def test_uvm_non_blocking_transport_port(self): self.exercise_nonblocking_transport( uvm_nonblocking_transport_port, self.TestNonBlockingTransportExport ) async def test_uvm_transport_port(self): await self.exercise_blocking_transport( uvm_transport_port, self.TestTransportExport ) self.exercise_nonblocking_transport( uvm_transport_port, self.TestTransportExport ) async def test_uvm_blocking_master_port(self): await self.exercise_blocking_put( uvm_blocking_master_port, self.TestBlockingMasterExport ) await self.exercise_blocking_get_peek( uvm_blocking_master_port, self.TestBlockingMasterExport ) def test_uvm_nonblocking_master_port(self): self.exercise_nonblocking_put( uvm_nonblocking_master_port, self.TestNonBlockingMasterExport ) self.exercise_nonblocking_get_peek( uvm_nonblocking_master_port, self.TestNonBlockingMasterExport ) async def test_uvm_master_port(self): self.exercise_nonblocking_put(uvm_master_port, self.TestMasterExport) self.exercise_nonblocking_get_peek(uvm_master_port, self.TestMasterExport) await self.exercise_blocking_put(uvm_master_port, self.TestMasterExport) await self.exercise_blocking_get_peek(uvm_master_port, self.TestMasterExport) async def test_uvm_blocking_slave_port(self): await self.exercise_blocking_put( uvm_blocking_slave_port, self.TestBlockingSlaveExport ) await self.exercise_blocking_get_peek( uvm_blocking_slave_port, self.TestBlockingSlaveExport ) def test_uvm_nonblocking_slave_port(self): self.exercise_nonblocking_put( uvm_nonblocking_slave_port, self.TestNonBlockingSlaveExport ) self.exercise_nonblocking_get_peek( uvm_nonblocking_slave_port, self.TestNonBlockingSlaveExport ) async def test_uvm_slave_port(self): self.exercise_nonblocking_put(uvm_slave_port, self.TestSlaveExport) self.exercise_nonblocking_get_peek(uvm_slave_port, self.TestSlaveExport) await self.exercise_blocking_put(uvm_slave_port, self.TestSlaveExport) await self.exercise_blocking_get_peek(uvm_slave_port, self.TestSlaveExport) def test_uvm_tlm_fifo_size(self): """ 12.2.8.2.2 :return: """ ff = uvm_tlm_fifo("ff", self.my_root) size = ff.size() self.assertEqual(1, size) ff0 = uvm_tlm_fifo("ff0", self.my_root, 0) size = ff0.size() self.assertEqual(0, size) ff2 = uvm_tlm_fifo("ff2", self.my_root, 2) size = ff2.size() self.assertEqual(2, size) async def test_uvm_tlm_fifo_used(self): """ 12.2.8.2.3 :return: """ ff = uvm_tlm_fifo("ff", self.my_root, 3) pp = uvm_put_port("pp", self.my_root) pp.connect(ff.put_export) await pp.put(1) await pp.put(2) await pp.put(3) self.assertEqual(3, ff.used()) async def test_uvm_tlm_fifo_is_empty(self): """ 12.2.8.2.4 :return: """ ff = uvm_tlm_fifo("ff", None) self.assertTrue(ff.is_empty()) pp = uvm_put_port("pp", None) pp.connect(ff.put_export) gp = uvm_get_port("gp", None) gp.connect(ff.get_export) await pp.put(1) self.assertFalse(ff.is_empty()) __ = await gp.get() self.assertTrue(ff.is_empty()) def make_fifo(self, fifo_type) -> uvm_tlm_fifo_base: self.my_root.clear_children() fifo = fifo_type("fifo", self.my_root) return fifo @staticmethod async def do_blocking_put(put_port, data_list): for data in data_list: await put_port.put(data) @staticmethod async def do_blocking_get(get_port, data_list): while True: datum = await get_port.get() if datum is not None: data_list.append(datum) else: break @staticmethod async def do_blocking_peek(peek_port, data_list): datum = await peek_port.peek() data_list.append(datum) async def test_fifo_blocking(self): fifo = self.make_fifo(uvm_tlm_fifo) pp = uvm_blocking_put_port("pp", self.my_root) gp = uvm_blocking_get_port("gp", self.my_root) pk = uvm_blocking_peek_port("pk", self.my_root) gpp = uvm_blocking_get_peek_port("gpp", self.my_root) pp.connect(fifo.blocking_put_export) gp.connect(fifo.blocking_get_export) pk.connect(fifo.blocking_peek_export) gpp.connect(fifo.blocking_get_peek_export) put_data = [1, "f", 3, "c", None] peek_data = [] get_data = [] cocotb.start_soon(self.do_blocking_put(pp, put_data)) await self.do_blocking_peek(gpp, peek_data) await self.do_blocking_get(gpp, get_data) self.assertEqual(put_data[:-1], get_data) self.assertEqual(put_data[0], peek_data[0]) peek_data = [] get_data = [] fifo.flush() cocotb.start_soon(self.do_blocking_put(pp, put_data)) await self.do_blocking_peek(gpp, peek_data) await self.do_blocking_get(gpp, get_data) self.assertTrue(put_data[0], peek_data[0]) self.assertEqual(put_data[:-1], get_data) pass @staticmethod async def do_nonblocking_put(put_port, data_list): for data in data_list: while not put_port.try_put(data): await waitabit() @staticmethod async def do_nonblocking_get(get_port, data_list): while True: success, datum = get_port.try_get() if success: if datum is not None: data_list.append(datum) else: break else: await waitabit() @staticmethod async def do_nonblocking_peek(peek_port, data_list): data_list.append(peek_port.try_peek()) async def test_fifo_nonblocking(self): fifo = self.make_fifo(uvm_tlm_fifo) pp = uvm_nonblocking_put_port("pp", self.my_root) gp = uvm_nonblocking_get_port("gp", self.my_root) pk = uvm_nonblocking_peek_port("pk", self.my_root) pp.connect(fifo.nonblocking_put_export) gp.connect(fifo.nonblocking_get_export) pk.connect(fifo.nonblocking_peek_export) put_data = [10, 20, 30, "c0", None] get_data = [] peek_data = [] self.assertFalse(pk.can_peek()) await self.do_nonblocking_peek(pk, peek_data) success, data = peek_data.pop() self.assertFalse(success) self.assertIsNone(data) await waitabit() cocotb.start_soon(self.do_nonblocking_put(pp, put_data)) self.assertTrue(pk.can_peek()) await self.do_nonblocking_peek(pk, peek_data) success, data = peek_data.pop() self.assertTrue(success) self.assertEqual(data, put_data[0]) await self.do_nonblocking_get(gp, get_data) self.assertEqual(put_data[:-1], get_data) # now with get_peek gpp = uvm_nonblocking_get_peek_port("gpp", self.my_root) gpp.connect(fifo.nonblocking_get_peek_export) get_data = [] peek_data = [] self.assertFalse(pk.can_peek()) await self.do_nonblocking_peek(gpp, peek_data) success, data = peek_data.pop() self.assertFalse(success) self.assertIsNone(data) cocotb.start_soon(self.do_nonblocking_put(pp, put_data)) await Timer(1, "us") self.assertTrue(pk.can_peek()) await self.do_nonblocking_peek(gpp, peek_data) success, data = peek_data.pop() self.assertTrue(success) self.assertEqual(data, put_data[0]) await self.do_nonblocking_get(gpp, get_data) self.assertEqual(put_data[:-1], get_data) pyuvm-4.0.1/tests/cocotb_tests/t13_components/000077500000000000000000000000001507477334100214205ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/t13_components/Makefile000077500000000000000000000004411507477334100230620ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/t13_components/clk.sv000066400000000000000000000001301507477334100225350ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/t13_components/test.py000066400000000000000000000023331507477334100227520ustar00rootroot00000000000000import inspect import cocotb import test_13_predefined_component_classes as test_mod from cocotb.clock import Clock async def run_tests(dut): tests_pass = {} t12 = test_mod.s13_predefined_component_TestCase() methods = inspect.getmembers( test_mod.s13_predefined_component_TestCase ) # , predicate=inspect.ismethod) for mm in methods: (name, _) = mm if name.startswith("test_"): test = getattr(t12, name) t12.setUp() try: if inspect.iscoroutinefunction(test): await test() else: test() tests_pass[name] = True except AssertionError: tests_pass[name] = False t12.tearDown() any_failed = False for test, passed in tests_pass.items(): if passed: pf = "Pass " else: pf = "FAILED " any_failed = True print(f"{pf}{test:<20}") assert not any_failed @cocotb.test() # pylint: disable=no-value-for-parameter async def test_12_tlm(dut): """Tests the TLM FIFOS""" clock = Clock(dut.clk, 2, "us") cocotb.start_soon(clock.start()) await run_tests(dut) pyuvm-4.0.1/tests/cocotb_tests/t13_components/test_13_predefined_component_classes.py000066400000000000000000000277421507477334100312540ustar00rootroot00000000000000import unittest import uvm_unittest from pyuvm.s13_predefined_component_classes import * from pyuvm.utility_classes import Singleton class my_test(uvm_test): ... class s13_predefined_component_TestCase(uvm_unittest.uvm_TestCase): """Basic test cases.""" def setUp(self): super().setUp() ConfigDB().clear() uvm_root().clear_children() def test_uvm_component_no_parent(self): """ 13.1.2.2 Basic component creation. 13.1.2.1 Constructor 13.1 class defined """ comp = uvm_component("test", None) self.assertTrue("test" in uvm_component.component_dict) self.assertTrue(comp.parent == uvm_root()) self.assertTrue(comp.print_enabled) # 13.1.2.2 def test_do_execute_op_13_1_2_3(self): """ 13.1.2.3 We have not implemented policies. """ comp = uvm_component("test", None) with self.assertRaises(error_classes.UVMNotImplemented): comp.do_execute_op("op") def test_component_with_parent(self): parent = uvm_component("parent", None) child = uvm_component("child", parent) self.assertTrue("parent" in uvm_component.component_dict) self.assertTrue("parent.child" in uvm_component.component_dict) self.assertTrue(parent.parent == uvm_root()) self.assertTrue(child.parent == parent) self.assertEqual(list(parent.hierarchy), [parent, child]) def test_hierarchy(self): parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) child2 = uvm_component("child2", parent) child3 = uvm_component("child3", child1) golden_list = [parent, child1, child3, child2] self.assertEqual(list(parent.hierarchy), golden_list) hier = list(parent.hierarchy) hier.reverse() golden_list.reverse() self.assertEqual(hier, golden_list) def test_get_parent_13_1_3_1(self): """ 13.1.3.1 get_parent test :return: """ parent = uvm_component("parent", None) child = uvm_component("child", parent) grandchild = uvm_component("grandchild", child) par = grandchild.get_parent() self.assertEqual(child, par) par = child.get_parent() self.assertEqual(par, parent) par = grandchild.get_parent().get_parent() self.assertEqual(parent, par) def test_get_full_name_13_1_3_2(self): """ 13.1.3.1 get_parent test :return: """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) child2 = uvm_component("child2", parent) child21 = uvm_component("child21", child2) parent_name = parent.get_full_name() self.assertEqual("parent", parent_name) self.assertEqual("parent.child1", child1.get_full_name()) self.assertEqual("parent.child2", child2.get_full_name()) self.assertEqual("parent.child2.child21", child21.get_full_name()) def test_get_children_13_1_3_3(self): """ 13.1.3.3 """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) child2 = uvm_component("child2", parent) _ = uvm_component("child3", parent) child11 = uvm_component("child11", child1) _ = uvm_component("child111", child11) children = parent.get_children() self.assertTrue(len(children) == 3) children = child1.get_children() self.assertTrue(len(children) == 1) children = child2.get_children() self.assertTrue(len(children) == 0) children = list(parent.children) self.assertEqual(children, parent.get_children()) def test_child_iterator_13_1_3_4(self): """ 13.1.3.4 children is an iterator that we get from a UVM component. We can loop over it without getting a new copy of the children list. """ parent = uvm_component("parent", None) _ = uvm_component("child1", parent) _ = uvm_component("child2", parent) _ = uvm_component("child3", parent) cl = parent.get_children() for cc in parent.children: _ = cc self.assertIn(cc, cl) def test_get_child_13_1_3_4(self): """ oddly 13.1.3.4 defines several functions. We shall eschew get_next_child() and get_first_child(). But get_child(str name) is a righteous idea and so we'll implement that. As per the spec we return None if there is no child of that name rather than throwing a Lookup exception. :return: """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) _ = uvm_component("child2", parent) _ = uvm_component("child3", parent) self.assertEqual(parent.get_child("child1"), child1) self.assertIsNone(parent.get_child("orphan")) def test_get_num_children_13_1_3_5(self): """ 13.1.3.5 get_num_children() returns the number of children. """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) _ = uvm_component("child2", parent) _ = uvm_component("child3", parent) cl = parent.get_children() self.assertEqual(parent.get_num_children(), len(cl)) self.assertEqual(child1.get_num_children(), len(child1.get_children())) def test_has_child_13_1_3_6(self): """ 13.1.3.6 Returns the child of the name :return: """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) _ = uvm_component("child2", parent) _ = uvm_component("child3", child1) self.assertTrue(child1.has_child("child3")) self.assertEqual(len(parent.get_children()), 2) self.assertEqual(parent.get_child("child1").get_name(), "child1") self.assertEqual(2, parent.get_num_children()) self.assertFalse(parent.has_child("orphan")) def test_lookup_13_1_3_7(self): """ 13.1.3.7 lookup finds components based on their full names. a.b.c is relative to the parent of a .a.b.c means a is the top level and we find our way down. :return: """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) _ = uvm_component("child2", parent) child3 = uvm_component("child3", child1) child4 = uvm_component("child4", child3) self.assertEqual(child1, parent.lookup("child1")) self.assertEqual(child3, parent.lookup("child1.child3")) self.assertNotEqual(child1, parent.lookup("child2")) self.assertEqual(child3, parent.lookup(".parent.child1.child3")) self.assertEqual(child3, child1.lookup("child3")) self.assertEqual(child4, child1.lookup("child3.child4")) def test_get_depth_13_1_3_8(self): """ 13.1.3.8 get_depth measures dept from uvm_root where uvm_root is 0 :return: """ parent = uvm_component("parent", None) child1 = uvm_component("child1", parent) _ = uvm_component("child2", parent) child3 = uvm_component("child3", child1) _ = uvm_component("child4", child3) self.assertEqual(0, uvm_root().get_depth()) self.assertEqual(1, parent.get_depth()) self.assertEqual(2, child1.get_depth()) self.assertEqual(3, child3.get_depth()) class my_component(uvm_component): async def run_phase(self): ... def test_component_factory(self): mc = self.my_component("mc", None) mc2 = self.my_component.create("my_component", None) self.assertEqual(type(mc), type(mc2)) def test_config_db(self): aa = uvm_component("aa", None) bb = uvm_component("bb", aa) cc = uvm_component("cc", aa) _ = uvm_component("D", cc) ee = uvm_component("ee", bb) aa.cdb_set("FIVE", 5, "") datum = aa.cdb_get("FIVE", "") self.assertEqual(5, datum) with self.assertRaises(error_classes.UVMConfigItemNotFound): bb.cdb_get("FIVE", "") cc.cdb_set("TT", 33, "aa.bb.cc.*") with self.assertRaises(error_classes.UVMConfigItemNotFound): cc.cdb_get("TT", "") ConfigDB().set(None, "aa.*", "TEN", 10) datum = ee.cdb_get("TEN", "") self.assertEqual(10, datum) ConfigDB().set(None, "aa.cc", "FF", 44) datum = cc.cdb_get("FF", "") self.assertEqual(44, datum) def test_wildcard_precedence(self): aa = uvm_component("aa", None) bb = uvm_component("bb", aa) cc = uvm_component("cc", aa) aa.cdb_set("TEST", 11, "*") aa.cdb_set("TEST", 22, "bb") ConfigDB().set(aa, "aa", "OTHER", 55) _ = aa.cdb_get("TEST", "X") bb_int = bb.cdb_get("TEST", "") self.assertEqual(22, bb_int) cc_int = cc.cdb_get("TEST", "") self.assertEqual(11, cc_int) aao = aa.cdb_get("OTHER", "aa") self.assertEqual(55, aao) def test_contextless_behavior_in_hierarchy(self): aa = uvm_component("aa", None) _ = uvm_component("B", aa) _ = uvm_component("C", aa) ConfigDB().set(aa, "*", "OTHER", 55) aa = ConfigDB().get(aa, "B", "OTHER") self.assertEqual(55, aa) async def test_agent_config(self): class bottom(uvm_agent): def build_phase(self): super().build_phase() class comp(uvm_agent): def build_phase(self): super().build_phase() ConfigDB().set(self, "bot", "is_active", 0) self.bot = bottom("bot", self) class test(uvm_test): def build_phase(self): self.cdb_set("is_active", uvm_active_passive_enum.UVM_ACTIVE) self.agent = comp("agent", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("test", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(uvm_active_passive_enum.UVM_ACTIVE, utt.agent.get_is_active()) self.assertEqual( uvm_active_passive_enum.UVM_PASSIVE, utt.agent.bot.get_is_active() ) self.assertTrue(utt.agent.active()) self.assertFalse(utt.agent.bot.active()) async def test_class_as_run_test_argument(self): class DataHolder(metaclass=Singleton): def __init__(self): self.call_count = 0 def __str__(self): return f"DataHolder.call_count: {self.call_count}" class MyTest(uvm_test): async def run_phase(self): self.raise_objection() DataHolder().call_count += 1 self.drop_objection() await uvm_root().run_test("MyTest", keep_set={DataHolder}) await uvm_root().run_test(MyTest, keep_set={DataHolder}) self.assertTrue(DataHolder().call_count == 2) async def test_default_agent_config(self): class bottom(uvm_agent): def build_phase(self): super().build_phase() class comp(uvm_agent): def build_phase(self): super().build_phase() self.bot = bottom("bot", self) class test(uvm_test): def build_phase(self): self.agent = comp("agent", self) async def run_phase(self): self.raise_objection() self.drop_objection() await uvm_root().run_test("test", keep_singletons=True) utt = uvm_root().get_child("uvm_test_top") self.assertEqual(uvm_active_passive_enum.UVM_ACTIVE, utt.agent.get_is_active()) self.assertEqual( uvm_active_passive_enum.UVM_ACTIVE, utt.agent.bot.get_is_active() ) self.assertTrue(utt.agent.active()) self.assertTrue(utt.agent.bot.active()) if __name__ == "__main__": unittest.main() pyuvm-4.0.1/tests/cocotb_tests/t14_15_sequences/000077500000000000000000000000001507477334100215345ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/t14_15_sequences/Makefile000077500000000000000000000004411507477334100231760ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/t14_15_sequences/clk.sv000066400000000000000000000001531507477334100226560ustar00rootroot00000000000000`timescale 1ns/1ns module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/t14_15_sequences/test.py000066400000000000000000000023241507477334100230660ustar00rootroot00000000000000import inspect import cocotb import test_14_15_python_sequences as test_mod from cocotb.clock import Clock async def run_tests(dut): tests_pass = {} t14 = test_mod.py1415_sequence_TestCase() methods = inspect.getmembers(test_mod.py1415_sequence_TestCase) for mm in methods: (name, _) = mm if name.startswith("test_"): test = getattr(t14, name) t14.setUp() try: if inspect.iscoroutinefunction(test): await test() else: test() tests_pass[name] = True except (AssertionError, RuntimeWarning) as doh: tests_pass[name] = False print("ERROR", doh) t14.tearDown() any_failed = False for test, passed in tests_pass.items(): if passed: pf = "Pass " else: pf = "FAILED " any_failed = True print(f"{pf}{test:<20}") assert not any_failed @cocotb.test() # pylint: disable=no-value-for-parameter async def test_14_sequences(dut): """Tests the Sequences""" clock = Clock(dut.clk, 2, "us") cocotb.start_soon(clock.start()) await run_tests(dut) pyuvm-4.0.1/tests/cocotb_tests/t14_15_sequences/test_14_15_python_sequences.py000066400000000000000000000460211507477334100273550ustar00rootroot00000000000000import asyncio import cocotb import uvm_unittest from cocotb.triggers import Timer from cocotb.utils import get_sim_time from pyuvm import * class DataHolder(metaclass=Singleton): def __init__(self): self.datum = None self.dict_ = {} self.virtual_seq_error = None class SeqItem(uvm_sequence_item): def __init__(self, name): super().__init__(name) self.op = None self.result = None self.data = 0 def __str__(self): return f"id: {id(self)} data: {self.data} result: {self.result}" class ItemDoneRespSeqDriver(uvm_driver): async def run_phase(self): while True: op_item = await self.seq_item_port.get_next_item() op_item.data += 1 self.seq_item_port.item_done(op_item) class HandleRespSeqDriver(uvm_driver): async def run_phase(self): while True: op_item = await self.seq_item_port.get_next_item() op_item.result = op_item.data + 1 self.seq_item_port.item_done() class py1415_sequence_TestCase(uvm_unittest.uvm_TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.result_list = [] if uvm_root().has_child("my_root"): self.my_root = uvm_root().get_child("my_root") else: self.my_root = uvm_component("my_root", None) def setUp(self): ObjectionHandler().run_phase_done_flag = False self.my_root.clear_children() uvm_root().clear_hierarchy() self.result_list = [] def tearDown(self): ObjectionHandler().run_phase_done_flag = None @staticmethod async def putter(put_method, data): for datum in data: if asyncio.iscoroutinefunction(put_method): await put_method(datum) else: put_method(datum) async def getter(self, get_method, done_method=None): while True: datum = await get_method() if datum.get_name() == "end": break if done_method is not None: done_method() self.result_list.append(datum) async def response_getter(self, get_response_method, txn_id_list=[]): if len(txn_id_list) == 0: self.result_list = [await get_response_method(None)] else: for txn_id in txn_id_list: datum = await get_response_method(txn_id) self.result_list.append(datum) class ItemClass(uvm_sequence_item): def __init__(self, name="seq_item", txn_id=0): super().__init__(name) self.transaction_id = txn_id self.condition = cocotb.triggers.Event() def __str__(self): return str(self.transaction_id) async def run_put_get(self, put_method, get_method, done_method=None): cocotb.start_soon(self.getter(get_method, done_method)) send_list = [self.ItemClass("5"), self.ItemClass("3"), self.ItemClass("two")] await self.putter(put_method, send_list + [self.ItemClass("end")]) await cocotb.triggers.Timer(1) self.assertEqual(send_list, self.result_list) async def run_get_response(self, put_method, get_response_method): send_list = [] for ii in range(5): send_list.append(self.ItemClass(txn_id=ii)) cocotb.start_soon(self.response_getter(get_response_method, [4, 2])) await self.putter(put_method, send_list) await cocotb.triggers.Timer(1) result = [xx.transaction_id for xx in self.result_list] self.assertEqual([4, 2], result) self.result_list = [] cocotb.start_soon(self.response_getter(get_response_method, [5])) await self.putter( put_method, [self.ItemClass(txn_id=10), self.ItemClass(txn_id=11)] ) await cocotb.triggers.Timer(1) await self.putter(put_method, [self.ItemClass(txn_id=5)]) await cocotb.triggers.Timer(1) self.assertTrue(5, self.result_list[0].transaction_id) async def test_ResponseQueue_put_get(self): rq = ResponseQueue() try: await self.run_put_get(rq.put, rq.get) except AssertionError as ae: print("ERROR: ", ae) raise async def test_ResponseQueue_get_response(self): rq = ResponseQueue() try: await self.run_get_response(rq.put, rq.get_response) except AssertionError as ae: print("ERROR: ", ae) raise async def test_uvm_item_export_check(self): sip = uvm_seq_item_port("sip", self.my_root) bpe = uvm_blocking_put_export("bpe", self.my_root) with self.assertRaises(error_classes.UVMTLMConnectionError): sip.connect(bpe) async def test_uvm_item_port_put_get(self): sip = uvm_seq_item_port("sip", self.my_root) sie_ = uvm_seq_item_export("sie", self.my_root) # codespell:ignore sie sip.connect(sie_) await self.run_get_response(sip.put_response, sip.get_response) async def test_premature_item_done(self): sip = uvm_seq_item_port("sip", self.my_root) sie_ = uvm_seq_item_export("sie", self.my_root) # codespell:ignore sie sip.connect(sie_) with self.assertRaises(error_classes.UVMSequenceError): sip.item_done() async def test_base_virtual_sequence(self): class basic_seq(uvm_sequence): def __init__(self, name, result_list): super().__init__(name) self.rl = result_list async def body(self): self.rl.append("body") seq = basic_seq("basic_seq", self.result_list) await seq.start() self.assertEqual("body", self.result_list[0]) class timeout_sequencer(uvm_sequencer): def __init__(self, name, parent): super().__init__(name, parent) async def run_phase(self): while True: next_item = await self.seq_q.get() next_item.start_condition.set() next_item.start_condition.clear() async def seq_item_port_getter(self, sip=None): datum = await sip.get_next_item() self.result_list.append(datum) async def test_run_sequence(self): ObjectionHandler().run_phase_done_flag = None class Seq(uvm_sequence): async def body(self): op = SeqItem("op") await self.start_item(op) op.data = 0 await self.finish_item(op) DataHolder().datum = op.result == (op.data + 1) class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = HandleRespSeqDriver("driver", self) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) def end_of_elaboration_phase(self): self.set_logging_level_hier(PYUVM_DEBUG) async def run_phase(self): self.raise_objection() seq = Seq("seq") await seq.start(self.seqr) self.drop_objection() await uvm_root().run_test("SeqTest") self.assertTrue(DataHolder().datum) async def test_put_response(self): ObjectionHandler().run_phase_done_flag = None class PutRespSeqDriver(uvm_driver): async def run_phase(self): while True: op_item = await self.seq_item_port.get_next_item() op_item.data += 1 self.seq_item_port.put_response(op_item) self.seq_item_port.item_done() class Seq(uvm_sequence): async def body(self): op = SeqItem("op") await self.start_item(op) op.data = 0 await self.finish_item(op) result = await self.get_response() DataHolder().datum = result.data == 1 class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = PutRespSeqDriver("driver", self) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) async def run_phase(self): self.raise_objection() seq = Seq("seq") await seq.start(self.seqr) self.drop_objection() await uvm_root().run_test("SeqTest") self.assertTrue(DataHolder().datum) async def test_seq_pre_post_body(self): ObjectionHandler().run_phase_done_flag = None class Seq(uvm_sequence): def __init__(self, name="uvm_sequence"): super().__init__(name) self.op = None async def pre_body(self): self.op = SeqItem("op") async def body(self): await self.start_item(self.op) self.op.data = 0 await self.finish_item(self.op) async def post_body(self): DataHolder().datum = True class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = ItemDoneRespSeqDriver("driver", self) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) async def run_phase(self): self.raise_objection() seq = Seq("seq") await seq.start(self.seqr) self.drop_objection() await uvm_root().run_test("SeqTest") self.assertTrue(DataHolder().datum) async def test_item_done_response(self): ObjectionHandler().run_phase_done_flag = None class Seq(uvm_sequence): async def body(self): op = SeqItem("op") await self.start_item(op) op.data = 0 await self.finish_item(op) await self.get_response() DataHolder().datum = op.data == 1 class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = ItemDoneRespSeqDriver("driver", self) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) async def run_phase(self): self.raise_objection() seq = Seq("seq") await seq.start(self.seqr) self.drop_objection() await uvm_root().run_test("SeqTest") self.assertTrue(DataHolder().datum) async def test_multiple_seq_runs(self): ObjectionHandler().run_phase_done_flag = None class Seq(uvm_sequence): def __init__(self, name): super().__init__(name) self.runner_name = None # pleasing the linter async def body(self): op = SeqItem("op") await self.start_item(op) op.data = DataHolder().dict_[self.runner_name] await self.finish_item(op) self.result = await self.get_response() class SeqAgent(uvm_agent): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = ItemDoneRespSeqDriver("driver", self) ConfigDB().set(None, "*", "SEQR", self.seqr) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) class SeqRunner(uvm_component): def connect_phase(self): self.seqr = self.cdb_get("SEQR") async def run_phase(self): self.raise_objection() seq = Seq("seq") seq.runner_name = self.get_name() await seq.start(self.seqr) DataHolder().dict_[self.get_name()] = seq.result.data self.drop_objection() class SeqTest(uvm_test): def build_phase(self): self.agent = SeqAgent("seq1", self) DataHolder().dict_["run1"] = 5 DataHolder().dict_["run2"] = 25 self.run1 = SeqRunner("run1", self) self.run2 = SeqRunner("run2", self) await uvm_root().run_test("SeqTest") self.assertEqual(26, DataHolder().dict_["run2"]) self.assertEqual(6, DataHolder().dict_["run1"]) async def test_virtual_sequence(self): DataHolder().virtual_seq_error = False class IncSeq(uvm_sequence): async def body(self): for nn in range(5): op = SeqItem("op") await self.start_item(op) op.data = nn await self.finish_item(op) error = op.data + 1 != op.result if error: print("!!!!!!INC ERROR!!!!") DataHolder().virtual_seq_error = error break class DecSeq(uvm_sequence): async def body(self): for nn in range(5): op = SeqItem("op") await self.start_item(op) op.data = nn await self.finish_item(op) error = op.data + 1 != op.result if error: print("!!!!!!DEC ERROR!!!!") DataHolder().virtual_seq_error = error break class TopSeq(uvm_sequence): async def body(self): seqr = ConfigDB().get(None, "", "SEQR") inc = IncSeq("inc") await inc.start(seqr) if DataHolder().virtual_seq_error: return dec = DecSeq("dec") await dec.start(seqr) class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = HandleRespSeqDriver("driver", self) ConfigDB().set(None, "*", "SEQR", self.seqr) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) async def run_phase(self): self.raise_objection() self.top = TopSeq("top") await self.top.start() self.drop_objection() await uvm_root().run_test("SeqTest") self.assertFalse(DataHolder().virtual_seq_error) async def test_start_soon_sequence(self): DataHolder().virtual_seq_error = False class IncSeq(uvm_sequence): async def body(self): for nn in range(5): op = SeqItem("op") await self.start_item(op) op.data = nn await self.finish_item(op) error = op.data + 1 != op.result if error: DataHolder().virtual_seq_error = error break class DecSeq(uvm_sequence): async def body(self): for nn in range(5): op = SeqItem("op") await self.start_item(op) op.data = nn await self.finish_item(op) error = op.data + 1 != op.result if error: DataHolder().virtual_seq_error = error break class TopSeq(uvm_sequence): async def body(self): seqr = ConfigDB().get(None, "", "SEQR") inc = IncSeq("inc") dec = DecSeq("dec") cocotb.start_soon(inc.start(seqr)) cocotb.start_soon(dec.start(seqr)) class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = HandleRespSeqDriver("driver", self) ConfigDB().set(None, "*", "SEQR", self.seqr) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) async def run_phase(self): self.raise_objection() self.top = TopSeq("top") await self.top.start() self.drop_objection() await uvm_root().run_test("SeqTest") self.assertFalse(DataHolder().virtual_seq_error) async def test_start_item_timing(self): ObjectionHandler().run_phase_done_flag = None class DelaySeqDriver(uvm_driver): def __init__(self, name, parent, start_delay_ns, finish_delay_ns): super().__init__(name, parent) self.start_delay_ns = start_delay_ns self.finish_delay_ns = finish_delay_ns async def run_phase(self): while True: await Timer(self.start_delay_ns, "ns") op_item = await self.seq_item_port.get_next_item() op_item.result = op_item.data + 1 await Timer(self.finish_delay_ns, "ns") self.seq_item_port.item_done() class Seq(uvm_sequence): async def body(self): op = SeqItem("op") DataHolder().start_item_call_time = get_sim_time("ns") uvm_root().logger.info("CALLING START_ITEM") await self.start_item(op) uvm_root().logger.info("BACK FROM START_ITEM") DataHolder().start_item_return_time = get_sim_time("ns") op.data = 0 DataHolder().finish_item_call_time = get_sim_time("ns") await self.finish_item(op) DataHolder().finish_item_return_time = get_sim_time("ns") DataHolder().datum = op.result == (op.data + 1) class SeqTest(uvm_test): def build_phase(self): self.seqr = uvm_sequencer("seqr", self) self.driver = DelaySeqDriver("driver", self, 5, 7) def connect_phase(self): self.driver.seq_item_port.connect(self.seqr.seq_item_export) def end_of_elaboration_phase(self): self.set_logging_level_hier(PYUVM_DEBUG) async def run_phase(self): self.raise_objection() seq = Seq("seq") await seq.start(self.seqr) self.drop_objection() await uvm_root().run_test("SeqTest") start_item_time = DataHolder().start_item_call_time start_return_time = DataHolder().start_item_return_time finish_item_call_time = DataHolder().finish_item_call_time finish_item_return_time = DataHolder().finish_item_return_time self.assertFalse(start_item_time == start_return_time) self.assertFalse(start_return_time == finish_item_return_time) self.assertTrue(start_return_time == finish_item_call_time) self.assertTrue(start_item_time + 5 == start_return_time) self.assertTrue(finish_item_call_time + 7 == finish_item_return_time) pyuvm-4.0.1/tests/cocotb_tests/test_ral_read_write/000077500000000000000000000000001507477334100225665ustar00rootroot00000000000000pyuvm-4.0.1/tests/cocotb_tests/test_ral_read_write/Makefile000077500000000000000000000004411507477334100242300ustar00rootroot00000000000000CWD=$(shell pwd) COCOTB_REDUCED_LOG_FMT = True SIM ?= icarus VERILOG_SOURCES =$(CWD)/clk.sv MODULE := test TOPLEVEL := clocker TOPLEVEL_LANG=verilog COCOTB_HDL_TIMEUNIT=1us COCOTB_HDL_TIMEPRECISION=1us include $(shell cocotb-config --makefiles)/Makefile.sim include ../../../checkclean.mk pyuvm-4.0.1/tests/cocotb_tests/test_ral_read_write/clk.sv000066400000000000000000000001301507477334100237030ustar00rootroot00000000000000module clocker(); bit clk; initial clk = 0; always #5 clk = ~clk; endmodule pyuvm-4.0.1/tests/cocotb_tests/test_ral_read_write/test.py000066400000000000000000000165711507477334100241310ustar00rootroot00000000000000# Main Packages for the entire RAL model from random import choice, randint from cocotb import test from cocotb.binary import BinaryValue from pyuvm import * ############################################################################## # TESTS ENTIRE RAL READ operation ############################################################################## # Single REG class command_reg(uvm_reg): def __init__(self, name="command_reg", reg_width=32): super().__init__(name, reg_width) self.WLS = uvm_reg_field("WLS") self.STB = uvm_reg_field("STB") self.PEN = uvm_reg_field("PEN") self.EPS = uvm_reg_field("EPS") def build(self): self.WLS.configure(self, 2, 0, "RW", 0, (2**2) - 1) self.STB.configure(self, 1, 2, "RW", 0, (2**1) - 1) self.PEN.configure(self, 1, 3, "RW", 0, (2**1) - 1) self.EPS.configure(self, 4, 4, "RW", 0, (2**4) - 1) self._set_lock() # Single REG class status_reg(uvm_reg): def __init__(self, name="status_reg", reg_width=32): super().__init__(name, reg_width) self.DR = uvm_reg_field("DR") self.OE = uvm_reg_field("OE") self.PE = uvm_reg_field("PE") self.FE = uvm_reg_field("FE") def build(self): self.DR.configure(self, 1, 0, "RO", 1, 1) self.OE.configure(self, 1, 1, "RO", 1, 1) self.PE.configure(self, 1, 2, "RO", 1, 1) self.FE.configure(self, 1, 3, "RO", 1, 1) self._set_lock() # Register model with 2 Regs and 1 Map class cmd_ral(uvm_reg_block): def __init__(self, name): super().__init__(name) # do not use create map if only the default one # is intended to be used self.def_map = uvm_reg_map("map") self.def_map.configure(self, 0) self.CMD = command_reg("CMD") self.CMD.configure(self, "0x100c", "", False, False) self.def_map.add_reg(self.CMD, "0x0", "RW") self.ST = status_reg("ST") self.ST.configure(self, "0x1014", "", False, False) self.def_map.add_reg(self.ST, "0x0", "RO") # UVM Sequence item class class cmd_item(uvm_sequence_item): def __init__(self, name): super().__init__(name) self.Endian = False self.CMD_data = BinaryValue(0, 32, self.Endian) self.WR_RD = BinaryValue(0, 1, self.Endian) self.write_data: int = 0 self.read_data: int = 0 self.addr: str = "" self.status: bool = True def randomize(self): self.CMD_data.integer = randint(0, (2**self.CMD_data.n_bits) - 1) self.WR_RD.integer = randint(0, (2**self.WR_RD.n_bits) - 1) self.addr = choice(["0x1014", "0x100c"]) def print_cmd(self): print(f"Received CMD_data is {self.CMD_data.get_value()}") print(f"Received WR_RD is {self.WR_RD.get_value()}") print(f"Received write_data is {self.write_data}") print(f"Received addr is {self.addr}") def set_write_data(self, value: int): self.write_data = value def set_read_data(self, value: int): self.read_data = value def set_addr(self, value: int): self.addr = value def set_status(self, st): self.status = st def get_status(self): return self.status # UVM Sequence class random_cmd_seq(uvm_sequence): def __init__(self, name="random_cmd_seq"): super().__init__(name) self.iteranctions = 0 async def body(self): for i in range(self.iteranctions): cmd = cmd_item(f"cmd{self.iteranctions}") await self.start_item(cmd) cmd.randomize() await self.finish_item(cmd) # UVM Driver class cmd_driver(uvm_driver): def build_phase(self): self.ap = uvm_analysis_port("ap", self) self.driver_ral = None self.reg_target = None async def run_phase(self): while True: cmd = await self.seq_item_port.get_next_item() self.raise_objection() cmd.print_cmd() self.reg_target = self.driver_ral.def_map.get_reg_by_offset(cmd.addr) if self.reg_target.get_address() == "0x1014": print("cmd_driver -- addr: 0x1014") else: print("cmd_driver -- addr: 0x100c") print(f"cmd_driver -- policy: {self.reg_target.get_access_policy()}") if cmd.WR_RD.integer == 0: print("cmd_driver -- READ_CMD") cmd.set_read_data(5) self.RW = False else: print("cmd_driver -- WRITE_CMD") self.RW = True cmd_item.status = True if (self.RW is True) and (self.reg_target.get_access_policy() == "RO"): cmd_item.status = False self.seq_item_port.item_done() self.drop_objection() # UVM Adaptor class cmd_adapter(uvm_reg_adapter): def __init__(self, name="cmd_adapter"): super().__init__(name) def reg2bus(self, rw: uvm_reg_bus_op) -> uvm_sequence_item: cmdItem = cmd_item("cmdItem") if rw.kind == access_e.UVM_READ: cmdItem.WR_RD.integer = 0 cmdItem.set_read_data(rw.data) else: cmdItem.WR_RD.integer = 1 cmdItem.set_write_data(rw.data) cmdItem.addr = rw.addr return cmdItem def bus2reg(self, bus_item: uvm_sequence_item, rw: uvm_reg_bus_op): if bus_item.WR_RD == 0: rw.kind = access_e.UVM_READ rw.data = bus_item.read_data else: rw.kind = access_e.UVM_WRITE rw.data = bus_item.write_data rw.addr = bus_item.addr rw.status = status_t.IS_OK if cmd_item.status is False: rw.status = status_t.IS_NOT_OK # UVM Environment class cmd_env(uvm_env): def build_phase(self): self.cmd_seqr = uvm_sequencer("cmd_seqr", self) self.ral = cmd_ral("ral") # there is no need for the build self.driver = cmd_driver.create("cmd_driver", self) self.ral_adapter = cmd_adapter("ral_adapter") def connect_phase(self): self.driver.seq_item_port.connect(self.cmd_seqr.seq_item_export) # assign the adapter and sequencer self.ral.def_map.set_adapter(self.ral_adapter) self.ral.def_map.set_sequencer(self.cmd_seqr) # connect the Driver RAL to the ENV ral self.driver.driver_ral = self.ral # UVM Test class ral_test_rd(uvm_test): def build_phase(self): self.environment = cmd_env.create("environment", self) async def run_phase(self): self.raise_objection() self.ral_map = self.environment.ral.def_map self.ral_map.reset("HARD") registers = self.environment.ral.def_map.get_registers() print(registers) for rg in registers: print(rg.get_address()) if isinstance(rg, uvm_reg): (status, data) = await rg.read( self.ral_map, path_t.FRONTDOOR, check_t.NO_CHECK ) print(f"Status: {status.name} -- Data: {data}") for rg in registers: if isinstance(rg, uvm_reg): wdata = randint(0, 200) status = await rg.write( wdata, self.ral_map, path_t.FRONTDOOR, check_t.NO_CHECK ) print(f"Status: {status.name} -- Data: {wdata}") self.drop_objection() @test() async def test_start(dut): await uvm_root().run_test("ral_test_rd") pyuvm-4.0.1/tests/conftest.py000066400000000000000000000004031507477334100162450ustar00rootroot00000000000000import pytest from pyuvm.s13_predefined_component_classes import uvm_component, uvm_root @pytest.fixture() def initialize_pyuvm(request): uvm_component.component_dict.clear() uvm_root.component_dict.clear() uvm_root.clear_singletons() pass pyuvm-4.0.1/tests/pytests/000077500000000000000000000000001507477334100155645ustar00rootroot00000000000000pyuvm-4.0.1/tests/pytests/test_05_base_classes.py000066400000000000000000000142111507477334100221270ustar00rootroot00000000000000import copy import pytest from pyuvm import * pytestmark = pytest.mark.usefixtures("initialize_pyuvm") class my_object(uvm_object): def __init__(self, name=""): super().__init__(name) self.val = 5 def __eq__(self, other): if type(other) is type(self): return self.val == other.val __hash__: None # type: ignore[assignment] def __str__(self): return "Hello" def do_copy(self, other): self.val = other.val def test_basic_creation(): """ 15.3 Tests whether the factory gets populated and whether it can be used. """ uvf = uvm_factory() mof = uvf.fd.classes["my_object"]("factory") moi = my_object("name") assert type(mof) is type(moi) # Testing specification def test_seeding(): """ 5.3.3.1 get_uvm_seeding 5.3.3.2 set_uvm_seeding 5.3.3.3 reseed """ mo = my_object("mo") with pytest.raises(error_classes.UVMNotImplemented): mo.get_uvm_seeding() with pytest.raises(error_classes.UVMNotImplemented): _ = mo.set_uvm_seeding(1) with pytest.raises(error_classes.UVMNotImplemented): mo.reseed() def test_identification(): """ 5.3.4 :return: """ nameless = my_object() assert len(nameless.get_name()) == 0 moe = my_object("moe") # 5.3.4.2 name = moe.get_name() assert "moe" == name # 5.3.4.1 moe.set_name("curly") name = moe.get_name() assert "curly" == name moe.set_name("larry") # 5.3.4.3 name = moe.get_full_name() assert "larry" == name # 5.3.4.4 moe_id = moe.get_inst_id() assert id(moe) == moe_id # 5.3.4.5 not implemented with pytest.raises(error_classes.UsePythonMethod): moe.get_type() # 5.3.4.6 not implemented with pytest.raises(error_classes.UsePythonMethod): moe.get_object_type() # 5.3.4.7 assert "my_object" == moe.get_type_name() def test_creation(): """ 5.3.5 :return: """ mo = my_object("mo") # 5.3.5.1 new_mo = mo.create("new_mo") assert "new_mo" == new_mo.get_name() assert type(new_mo) is type(mo) # 5.3.5.2 mo.val = 5 cln_mo = copy.deepcopy(mo) assert mo.__eq__(cln_mo) def test_printing(): """ 5.3.6 :return: """ mo = my_object("mo") # 5.3.6.1 with pytest.raises(error_classes.UsePythonMethod): mo.print() # 5.3.6.2 with pytest.raises(error_classes.UsePythonMethod): mo.sprint() assert "Hello" == mo.convert2string() def test_recording(): """ 5.3.7 :return: """ mo = my_object("mo") # 5.3.7.1 with pytest.raises(error_classes.UVMNotImplemented): mo.record() # 5.3.7.2 with pytest.raises(error_classes.UVMNotImplemented): mo.do_record() def test_copying(): """ 5.3.8 :return: """ mo = my_object("mo") rhs = my_object("rhs") # 5.3.8.1 mo.copy(rhs) # 5.3.8.2 assert mo.val == rhs.val def test_comparing(): """ 5.3.9 :return: """ mo = my_object("mo") rhs = my_object("rhs") # 5.3.9.1 assert mo.compare(rhs) # 5.3.9.2 assert mo.do_compare(rhs) def test_packing(): """ 5.3.10 :return: """ mo = my_object("mo") # 5.3.10.1 with pytest.raises(error_classes.UsePythonMethod): mo.pack() with pytest.raises(error_classes.UsePythonMethod): mo.pack_bytes() with pytest.raises(error_classes.UsePythonMethod): mo.pack_ints() with pytest.raises(error_classes.UsePythonMethod): mo.pack_longints() # 5.3.10.2 with pytest.raises(error_classes.UsePythonMethod): mo.do_pack() def test_unpacking(): """ 5.3.11 :return: """ mo = my_object("mo") # 5.3.10.1 with pytest.raises(error_classes.UsePythonMethod): mo.unpack() with pytest.raises(error_classes.UsePythonMethod): mo.unpack_bytes() with pytest.raises(error_classes.UsePythonMethod): mo.unpack_ints() with pytest.raises(error_classes.UsePythonMethod): mo.unpack_longints() # 5.3.11.2 with pytest.raises(error_classes.UsePythonMethod): mo.do_unpack() def test_configuration(): """ 5.3.12 :return: """ mo = my_object("mo") # 5.3.12.1 with pytest.raises(error_classes.UsePythonMethod): mo.set_local() def test_field_operations(): """ 5.3.13 :return: """ mo = my_object("mo") with pytest.raises(error_classes.UsePythonMethod): mo.do_execute_op(None) def test_active_policy(): """ 5.3.14 :return: """ mo = my_object("mo") with pytest.raises(error_classes.UVMNotImplemented): mo.push_active_policy() with pytest.raises(error_classes.UVMNotImplemented): mo.pop_active_policy() with pytest.raises(error_classes.UVMNotImplemented): mo.get_active_policy() def test_create(): """ 5.3.5 This needs to be further implemented to include the factory """ mo = my_object("first") mo2 = mo.create("second") assert mo == mo2 def test_uvm_transaction_creation(): """ 5.4.2.1 5.4.2.14 5.4.2.15 :return: """ tr = uvm_transaction() assert 0 == len(tr.get_name()) assert not tr.get_initiator() uc = uvm_component("uc", None) tr.set_initiator(uc) assert uc == tr.get_initiator() def test_transaction_recording(): """ 5.4.2 all methods :return: """ tr = uvm_transaction() with pytest.raises(error_classes.UVMNotImplemented): tr.get_tr_handle() with pytest.raises(error_classes.UVMNotImplemented): tr.enable_recording() with pytest.raises(error_classes.UVMNotImplemented): tr.disable_recording() with pytest.raises(error_classes.UVMNotImplemented): tr.is_recording_enabled() with pytest.raises(error_classes.UVMNotImplemented): tr.is_active() with pytest.raises(error_classes.UVMNotImplemented): tr.get_event_pool() tr.get_accept_time() tr.get_begin_time() tr.get_end_time() def test_clone(): orig = my_object("orig") clone = orig.clone() assert id(orig) != id(clone) assert orig.val == clone.val pyuvm-4.0.1/tests/pytests/test_17_register_enumerations.py000066400000000000000000000063571507477334100241340ustar00rootroot00000000000000import pytest from pyuvm import * pytestmark = pytest.mark.usefixtures("initialize_pyuvm") def test_basic_slice(): """ Test uvm_hdl_path_slice object parameters """ obj = uvm_hdl_path_slice("test_slice", -1, -1) assert obj.path == "test_slice" assert obj.offset == -1 assert obj.size == -1 def test_basic_uvm_hdl_path_concat(): """ Test uvm_hdl_path_concat object functions specified in section 17.2.3 """ # single slice register reg_slice = uvm_hdl_path_slice("reg_slice", -1, -1) reg_slice_concat_0 = uvm_hdl_path_concat("reg_slice_concat") reg_slice_concat_0.add_slice(reg_slice) test_slice_obj = reg_slice_concat_0.get_slices() assert len(test_slice_obj) == 1 assert test_slice_obj[0].path == "reg_slice" assert test_slice_obj[0].offset == -1 assert test_slice_obj[0].size == -1 def test_overlap_uvm_hdl_path_concat_0(): """ Test uvm_hdl_path_concat overlap detection """ # test overlap with one DWORD register slice and a 4 bit register slice reg_slice_0 = uvm_hdl_path_slice("reg_slice_0", 0, 32) reg_slice_1 = uvm_hdl_path_slice("reg_slice_1", 8, 4) reg_slice_concat_0 = uvm_hdl_path_concat("reg_slice_concat") reg_slice_concat_0.add_slice(reg_slice_0) with pytest.raises(Exception): reg_slice_concat_0.add_slice(reg_slice_1) def test_overlap_uvm_hdl_path_concat_1(): reg_slice_0 = uvm_hdl_path_slice("reg_slice_0", 0, 32) reg_slice_1 = uvm_hdl_path_slice("reg_slice_1", 8, 4) reg_slice_concat = uvm_hdl_path_concat("reg_slice_concat") reg_slice_concat.add_slice(reg_slice_0) with pytest.raises(Exception): reg_slice_concat.add_slice(reg_slice_1) def test_overlap_uvm_hdl_path_concat_set_slices(): # test with non-overlapping and contiguous slices reg_slice_0 = uvm_hdl_path_slice("reg_slice_0", 0, 8) reg_slice_1 = uvm_hdl_path_slice("reg_slice_1", 8, 1) reg_slice_2 = uvm_hdl_path_slice("reg_slice_2", 9, 4) reg_slice_3 = uvm_hdl_path_slice("reg_slice_3", 13, 19) reg_slice_concat = uvm_hdl_path_concat("reg_slice_concat") reg_slices = [reg_slice_3, reg_slice_2, reg_slice_1, reg_slice_0] reg_slice_concat.set_slices(reg_slices) def test_uvm_hdl_path_concat_set_slices_contiguous_wrong_order(): # test with non-overlapping and contiguous slices but with error in order reg_slice_0 = uvm_hdl_path_slice("reg_slice_0", 0, 8) reg_slice_1 = uvm_hdl_path_slice("reg_slice_1", 8, 1) reg_slice_2 = uvm_hdl_path_slice("reg_slice_2", 9, 4) reg_slice_3 = uvm_hdl_path_slice("reg_slice_3", 13, 19) reg_slice_concat = uvm_hdl_path_concat("reg_slice_concat") reg_slices = [reg_slice_3, reg_slice_1, reg_slice_2, reg_slice_0] with pytest.raises(Exception): reg_slice_concat.set_slices(reg_slices) def test_uvm_hdl_path_concat_set_slices_non_contiguous_offsets(): # test with non-overlapping and contiguous slices but with error in order reg_slice_0 = uvm_hdl_path_slice("reg_slice_0", 0, 8) reg_slice_1 = uvm_hdl_path_slice("reg_slice_1", 9, 1) reg_slice_2 = uvm_hdl_path_slice("reg_slice_2", 15, 4) reg_slice_concat = uvm_hdl_path_concat("reg_slice_concat") reg_slices = [reg_slice_2, reg_slice_1, reg_slice_0] reg_slice_concat.set_slices(reg_slices) pyuvm-4.0.1/tests/pytests/test_uvm_ral.py000077500000000000000000000114161507477334100206500ustar00rootroot00000000000000# Main Packages for the entire RAL model import itertools from pyuvm.s17_uvm_reg_enumerations import uvm_predict_e from pyuvm.s27_uvm_reg_pkg import uvm_reg, uvm_reg_block, uvm_reg_field, uvm_reg_map ############################################################################## # TIPS ############################################################################## """ Use this to execute the test which will not be counted into the entire number of FAILING tests @pytest.mark.xfail Use this to just skip the execution of a specific test @pytest.mark.skip Use this to give a specific test method a name ID the execute it by using py.test -m ID_NAME @pytest.mark.ID_NAME Use this to give a specific test parameters to be used @pytest.mark.parametrize("name1, name2",value_type_1, value_type_2) If pip install pytest-sugar is ran then pytest is gonna likely execute a bar progression while running tests (especially if in Parallel) """ ############################################################################## # TESTS ENTIRE RAL ############################################################################## def test_simple_reg_model(): """ A more realistic register model based on the venerable UART 16550 design """ class LineControlRegister(uvm_reg): def __init__(self, name="LineControlRegister", reg_width=32): super().__init__(name, reg_width) self.WLS = uvm_reg_field("WLS") self.STB = uvm_reg_field("STB") self.PEN = uvm_reg_field("PEN") self.EPS = uvm_reg_field("EPS") def build(self): self.WLS.configure(self, 2, 0, "RW", 0, 0) self.STB.configure(self, 1, 2, "RW", 0, 0) self.PEN.configure(self, 1, 3, "RW", 0, 0) self.EPS.configure(self, 1, 4, "RW", 0, 0) self._set_lock() class LineStatusRegister(uvm_reg): def __init__(self, name="LineStatusRegister", reg_width=32): super().__init__(name, reg_width) self.DR = uvm_reg_field("DR") self.OE = uvm_reg_field("OE") self.PE = uvm_reg_field("PE") self.FE = uvm_reg_field("FE") def build(self): self.DR.configure(self, 1, 0, "RW", 1, 0) self.OE.configure(self, 1, 1, "RW", 1, 0) self.PE.configure(self, 1, 2, "RW", 1, 0) self.FE.configure(self, 1, 3, "RW", 1, 0) self._set_lock() class Regs(uvm_reg_block): def __init__(self, name): super().__init__(name) self.map = uvm_reg_map("map") self.map.configure(self, 0) self.LCR = LineControlRegister("LCR") self.LCR.configure(self, "0x100c", "") self.map.add_reg(self.LCR, "0x0") self.LSR = LineStatusRegister("LSR") self.LSR.configure(self, "0x1014", "") self.map.add_reg(self.LSR, "0x0") regs = Regs("regs") assert regs.get_name() == "regs" assert regs.map.get_reg_by_offset("0x100c") == regs.LCR assert regs.map.get_reg_by_offset("0x1014") == regs.LSR LCR = regs.LCR assert LCR.get_name() == "LCR" assert LCR.WLS.get_name() == "WLS" assert LCR.STB.get_name() == "STB" assert LCR.PEN.get_name() == "PEN" assert LCR.EPS.get_name() == "EPS" assert LCR.WLS.get_n_bits() == 2 for field in [field for field in LCR.get_fields() if field != LCR.WLS]: assert field.get_n_bits() == 1 def pairwise(iterable): """s -> (s0,s1), (s1,s2), (s2, s3), ...""" a, b = itertools.tee(iterable) next(b, None) return zip(a, b) def get_msb_pos(field): return field.get_lsb_pos() + field.get_n_bits() - 1 def are_adjacent(prev_field, field): return field.get_lsb_pos() == get_msb_pos(prev_field) + 1 assert LCR.get_fields()[0].get_lsb_pos() == 0 for prev_field, field in pairwise(LCR.get_fields()): assert are_adjacent(prev_field, field) for field in LCR.get_fields(): assert field.get_access() == "RW" assert not field.is_volatile() assert field.get_reset() == 0 LSR = regs.LSR assert LSR.DR.get_name() == "DR" assert LSR.OE.get_name() == "OE" assert LSR.PE.get_name() == "PE" assert LSR.FE.get_name() == "FE" for field in LSR.get_fields(): assert field.get_n_bits() == 1 assert field.get_access() == "RW" assert field.is_volatile() assert field.get_reset() == 0 assert LSR.get_fields()[0].get_lsb_pos() == 0 for prev_field, field in pairwise(LSR.get_fields()): assert are_adjacent(prev_field, field) LSR.reset() assert LSR.get_mirrored_value() == 0 LSR.predict(12, kind=uvm_predict_e.UVM_PREDICT_WRITE) assert LSR.get_mirrored_value() == 12 for field in LSR.get_fields(): print(field.get_value()) pyuvm-4.0.1/tests/pytests/test_uvm_reg.py000077500000000000000000000257531507477334100206600ustar00rootroot00000000000000""" Main Packages for the entire RAL model """ from typing import List import pytest from pyuvm.s17_uvm_reg_enumerations import uvm_predict_e from pyuvm.s27_uvm_reg_pkg import uvm_reg, uvm_reg_block, uvm_reg_field ############################################################################## # TIPS ############################################################################## """ Use this to execute the test which will not be counted into the entire number of FAILING tests @pytest.mark.xfail Use this to just skip the execution of a specific test @pytest.mark.skip Use this to give a specific test method a name ID the execute it by using py.test -m ID_NAME @pytest.mark.ID_NAME Use this to give a specific test parameters to be used @pytest.mark.parametrize("name1, name2",value_type_1, value_type_2) If pip install pytest-sugar is ran then pytest is gonna likely execute a bar progression while running tests (especially if in Parallel) """ ############################################################################## # TESTS UVM_REG ############################################################################## @pytest.mark.test_reg_get_name def test_reg_get_name(): for elem in range(1, 32): reg = uvm_reg(("some_reg_" + str(elem)), elem) assert reg.get_name() == ("some_reg_" + str(elem)), ( "Name mismatch: expected {} got: {}".format( ("some_reg_" + str(elem)), reg.get_name() ) ) assert reg.get_reg_size() == elem, ( f"Register size mismatch: expected {elem} got: {reg.get_reg_size()}" ) reg.check_err_list() @pytest.mark.test_reg_configure def test_reg_configure(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START reg = temp_reg() parent = uvm_reg_block() parent.set_lock() reg.configure(parent, "0x4", "", False, False) print(f"reg: {reg}") print(f"parent: {parent}") assert reg.get_parent() == parent, ( f"Mismatch on get parent: expected {type(parent)} got: {type(reg.get_parent)}" ) assert reg.get_address() == "0x4", ( f"Mismacth on get address: expected 0x4 got {reg.get_address()}" ) @pytest.mark.test_reg_with_single_field def test_reg_with_single_field(): class my_reg(uvm_reg): def __init__(self, name="uvm_reg", reg_width=32): super().__init__(name, reg_width) self.myfield_1 = uvm_reg_field("myfield_1") def build(self): self.myfield_1.configure(myreg, 16, 0, "RW", False, 10) self._set_lock() myreg = my_reg("myreg", 32) myreg.build() myreg.reset() assert myreg.get_fields() == [myreg.myfield_1] myreg.check_err_list() @pytest.mark.test_reg_with_multiple_fields def test_reg_with_multiple_fields(): class my_reg(uvm_reg): def __init__(self, name="uvm_reg", reg_width=32): super().__init__(name, reg_width) self.myfield_1 = uvm_reg_field("myfield_1") self.myfield_2 = uvm_reg_field("myfield_2") def build(self): self.myfield_1.configure(myreg, 16, 0, "RW", False, 10) self.myfield_2.configure(myreg, 16, 16, "RW", False, 20) self._set_lock() myreg = my_reg("myreg", 32) myreg.build() assert myreg.get_fields() == [myreg.myfield_1, myreg.myfield_2] myreg.check_err_list() @pytest.mark.test_reg_with_multiple_felds_reset def test_reg_with_multiple_felds_reset(): class my_reg(uvm_reg): def __init__(self, name="uvm_reg", reg_width=32): super().__init__(name, reg_width) self.myfield_1 = uvm_reg_field("myfield_1") self.myfield_2 = uvm_reg_field("myfield_2") def build(self): # int('0x0f',16) self.myfield_1.configure(self, 16, 0, "RW", False, int("0x0f", 16)) self.myfield_2.configure(self, 16, 16, "RW", False, int("0x0f", 16)) self._set_lock() print("calling build") myreg = my_reg("myreg", 32) myreg.build() for f in myreg.get_fields(): print(f"{f}") myreg.reset() assert myreg.get_mirrored_value() == int("0xf000f", 16) myreg.check_err_list() @pytest.mark.test_reg_with_multiple_fields_get_mirrored_value def test_reg_with_multiple_fields_get_mirrored_value(): class my_reg(uvm_reg): def __init__(self, name="uvm_reg", reg_width=32): super().__init__(name, reg_width) self.myfield_1 = uvm_reg_field("myfield_1") self.myfield_2 = uvm_reg_field("myfield_2") def build(self): self.myfield_1.configure(self, 16, 0, "RW", False, int("0xf", 16)) self.myfield_2.configure(self, 16, 16, "RW", False, int("0xf", 16)) self._set_lock() myreg = my_reg("myreg", 32) myreg.build() myreg.reset() myreg.predict(value=int("0x0f00f0", 16), kind=uvm_predict_e.UVM_PREDICT_WRITE) assert myreg.get_mirrored_value() == int("0x0f00f0", 16) myreg.reset() assert myreg.get_mirrored_value() == int("0xf000f", 16) myreg.check_err_list() @pytest.mark.test_reg_with_multiple_fields_get_desired_value def test_reg_with_multiple_fields_get_desired_value(): class my_reg(uvm_reg): def __init__(self, name="uvm_reg", reg_width=32): super().__init__(name, reg_width) self.myfield_1 = uvm_reg_field("myfield_1") self.myfield_2 = uvm_reg_field("myfield_2") def build(self): self.myfield_1.configure(self, 16, 0, "RW", False, int("0xf", 16)) self.myfield_2.configure(self, 16, 16, "RW", False, int("0xf", 16)) self._set_lock() myreg = my_reg("myreg", 32) myreg.build() myreg.reset() myreg.set_desired(int("0xf0f0f0f0", 16)) assert myreg.get_desired() == int("0xf0f0f0f0", 16) def make_expected_value(reg: uvm_reg, policy_list: List[str]) -> int: field_list: List[uvm_reg_field] = reg.get_fields() pos = 0 exp_val = 0 for field in field_list: field_lsb_pos = field.get_lsb_pos() while pos != field_lsb_pos: pos += 1 field_policy = field.get_access() field_size = field.get_n_bits() if field_policy in policy_list: exp_val |= ((1 << field_size) - 1) << pos pos += field_size return exp_val def predict_reg(reg: uvm_reg, predict_val: int = 0xFFFF_FFFF): reg.reset() assert reg.get_mirrored_value() == 0 reg.predict(predict_val, kind=uvm_predict_e.UVM_PREDICT_WRITE) assert reg.get_mirrored_value() == make_expected_value(reg, ["RW", "WO"]) reg.predict(predict_val, kind=uvm_predict_e.UVM_PREDICT_READ) assert reg.get_mirrored_value() == make_expected_value(reg, ["RW", "RO"]) reg.predict(predict_val, kind=uvm_predict_e.UVM_PREDICT_DIRECT) assert reg.get_mirrored_value() == make_expected_value(reg, ["RW", "WO", "RO"]) @pytest.mark.test_reg_simple_predict def test_reg_simple_predict(): """ Test registers with different simple access types like a RW/RO/WO """ class reg0(uvm_reg): def __init__(self, name="reg0", reg_width=32): super().__init__(name, reg_width) self.field1 = uvm_reg_field("field1") self.field2 = uvm_reg_field("field2") self.field3 = uvm_reg_field("field3") self.field4 = uvm_reg_field("field4") def build(self): self.field1.configure(self, 8, 0, "RW", 0, 0) self.field2.configure(self, 8, 8, "RO", 0, 0) self.field3.configure(self, 8, 16, "WO", 0, 0) self.field4.configure(self, 8, 24, "RW", 0, 0) self._set_lock() class reg1(uvm_reg): def __init__(self, name="reg1", reg_width=32): super().__init__(name, reg_width) self.field1 = uvm_reg_field("field1") self.field2 = uvm_reg_field("field2") self.field3 = uvm_reg_field("field3") self.field4 = uvm_reg_field("field4") self.field5 = uvm_reg_field("field5") self.field6 = uvm_reg_field("field6") self.field7 = uvm_reg_field("field7") self.field8 = uvm_reg_field("field8") def build(self): self.field1.configure(self, 4, 0, "RW", 0, 0) self.field2.configure(self, 4, 4, "RW", 0, 0) self.field3.configure(self, 4, 8, "RO", 0, 0) self.field4.configure(self, 4, 12, "WO", 0, 0) self.field5.configure(self, 4, 16, "RW", 0, 0) self.field6.configure(self, 4, 20, "WO", 0, 0) self.field7.configure(self, 4, 24, "RO", 0, 0) self.field8.configure(self, 4, 28, "RW", 0, 0) self._set_lock() register_0 = reg0("reg0") register_1 = reg1("reg1") predict_reg(register_0) predict_reg(register_1) @pytest.mark.test_reg_predict_edge_cases def test_reg_predict_edge_cases(): """ Test register predict with edge cases """ class reg0(uvm_reg): def __init__(self, name="reg0", reg_width=32): super().__init__(name, reg_width) self.field = uvm_reg_field("field") def build(self): self.field.configure(self, 32, 0, "RW", 0, 0) self._set_lock() class reg1(uvm_reg): def __init__(self, name="reg0", reg_width=32): super().__init__(name, reg_width) self.field = uvm_reg_field("field") def build(self): self.field.configure(self, 32, 0, "RO", 0, 0) self._set_lock() class reg2(uvm_reg): def __init__(self, name="reg0", reg_width=32): super().__init__(name, reg_width) self.field = uvm_reg_field("field") def build(self): self.field.configure(self, 32, 0, "WO", 0, 0) self._set_lock() class reg3(uvm_reg): def __init__(self, name="reg0", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() register_0 = reg0("reg0") register_1 = reg1("reg1") register_2 = reg2("reg2") register_3 = reg3("reg3") predict_reg(register_0) predict_reg(register_1) predict_reg(register_2) predict_reg(register_3) @pytest.mark.test_reg_predict_with_reserved_spaces def test_reg_predict_with_reserved_spaces(): """ Test register predict with spaces """ class reg0(uvm_reg): def __init__(self, name="reg0", reg_width=32): super().__init__(name, reg_width) self.field1 = uvm_reg_field("field1") self.field2 = uvm_reg_field("field2") self.field3 = uvm_reg_field("field3") self.field4 = uvm_reg_field("field4") def build(self): self.field1.configure(self, 7, 1, "RW", 0, 0) self.field2.configure(self, 8, 11, "RW", 0, 0) self.field3.configure(self, 3, 19, "RW", 0, 0) self.field4.configure(self, 7, 24, "RW", 0, 0) self._set_lock() reg0 = reg0("reg0") predict_reg(reg0) pyuvm-4.0.1/tests/pytests/test_uvm_reg_block.py000077500000000000000000000527041507477334100220260ustar00rootroot00000000000000# Main Packages for the entire RAL model import itertools import pytest from pyuvm.s17_uvm_reg_enumerations import uvm_hier_e, uvm_predict_e from pyuvm.s27_uvm_reg_pkg import uvm_reg, uvm_reg_block, uvm_reg_field, uvm_reg_map ############################################################################## # TIPS ############################################################################## """ Use this to execute the test which will not be counted into the entire number of FAILING tests @pytest.mark.xfail Use this to just skip the execution of a specific test @pytest.mark.skip Use this to give a specific test method a name ID the execute it by using py.test -m ID_NAME @pytest.mark.ID_NAME Use this to give a specific test parameters to be used @pytest.mark.parametrize("name1, name2",value_type_1, value_type_2) If pip install pytest-sugar is ran then pytest is gonna likely execute a bar progression while running tests (especially if in Parallel) """ ############################################################################## # TESTS ############################################################################## @pytest.mark.reg_block_get_name def test_reg_block_get_name(): block = uvm_reg_block("some_block") assert block.get_name() == "some_block" @pytest.mark.reg_block_with_single_reg def test_reg_block_with_single_reg(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START block = uvm_reg_block() reg = temp_reg() reg.configure(block, "0x4", "") block.set_lock() assert block.get_registers() == [reg] @pytest.mark.reg_block_with_multiple_regs def test_reg_block_with_multiple_regs(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START block = uvm_reg_block() reg0 = temp_reg() reg0.configure(block, "0x4", "") reg1 = temp_reg() reg1.configure(block, "0x8", "") block.set_lock() assert block.get_registers() == [reg0, reg1] @pytest.mark.reg_block_with_sub_blocks def test_reg_block_with_sub_blocks(): # FIRST REGISTER class temp_reg_1(uvm_reg): def __init__(self, name="temp_reg_1", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # SECOND REGISTER class temp_reg_2(uvm_reg): def __init__(self, name="temp_reg_2", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # THIRD REGISTER class temp_reg_3(uvm_reg): def __init__(self, name="temp_reg_3", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # SUB REG BLOCK class temp_blk_1(uvm_reg_block): def __init__(self, name="temp_blk_1"): super().__init__(name) self.def_map = uvm_reg_map("blk_1_map") self.def_map.configure(self, 0) self.reg0 = temp_reg_3("test_reg_3") self.reg0.configure(self, "0xC", "") self.def_map.add_reg(self.reg0, "0x0", "RW") # START block = uvm_reg_block() reg0 = temp_reg_1() reg0.configure(block, "0x4", "") reg1 = temp_reg_2() reg1.configure(block, "0x8", "") blk0 = temp_blk_1() blk0.set_lock() block.add_block(blk0) block.set_lock() assert block.get_registers() == [reg0, reg1, blk0.reg0] assert block.get_registers(hier=uvm_hier_e.UVM_NO_HIER) == [reg0, reg1] @pytest.mark.reg_block_get_field_empty_reg def test_reg_block_get_field_empty_reg(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START block = uvm_reg_block() reg0 = temp_reg() reg0.configure(block, "0x4", "") block.set_lock() assert block.get_fields() == [] @pytest.mark.reg_block_get_field_single_reg def test_reg_block_get_field_single_reg(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) self.test_field_1 = uvm_reg_field("test_field_1") def build(self): self.test_field_1.configure(self, 8, 0, "RW", 0, 0) self._set_lock() # START block = uvm_reg_block() reg0 = temp_reg() reg0.configure(block, "0x4", "") block.set_lock() assert block.get_fields() == [reg0.test_field_1] @pytest.mark.reg_block_get_field_multiple_regs def test_reg_block_get_field_multiple_regs(): # FIRST REGISTER class temp_reg_1(uvm_reg): def __init__(self, name="temp_reg_1", reg_width=32): super().__init__(name, reg_width) self.test_field_1 = uvm_reg_field("test_field_1") self.test_field_2 = uvm_reg_field("test_field_2") def build(self): self.test_field_1.configure(self, 8, 0, "RW", 0, 0) self.test_field_2.configure(self, 16, 8, "RW", 0, 0) self._set_lock() # SECOND REGISTER class temp_reg_2(uvm_reg): def __init__(self, name="temp_reg_2", reg_width=32): super().__init__(name, reg_width) self.test_field_3 = uvm_reg_field("test_field_3") self.test_field_4 = uvm_reg_field("test_field_4") self.test_field_5 = uvm_reg_field("test_field_5") def build(self): self.test_field_3.configure(self, 8, 0, "RW", 0, 0) self.test_field_4.configure(self, 8, 8, "RW", 0, 0) self.test_field_5.configure(self, 8, 16, "RW", 0, 0) self._set_lock() # START block = uvm_reg_block() reg0 = temp_reg_1() reg0.configure(block, "0x4", "") reg1 = temp_reg_2() reg1.configure(block, "0x8", "") block.set_lock() assert block.get_fields() == [ reg0.test_field_1, reg0.test_field_2, reg1.test_field_3, reg1.test_field_4, reg1.test_field_5, ] @pytest.mark.reg_block_get_field_sub_reg_block def test_reg_block_get_field_sub_reg_block(): # FIRST REGISTER class temp_reg_1(uvm_reg): def __init__(self, name="temp_reg_1", reg_width=32): super().__init__(name, reg_width) self.test_field_1 = uvm_reg_field("test_field_1") self.test_field_2 = uvm_reg_field("test_field_2") def build(self): self.test_field_1.configure(self, 8, 0, "RW", 0, 0) self.test_field_2.configure(self, 16, 8, "RW", 0, 0) self._set_lock() # SECOND REGISTER class temp_reg_2(uvm_reg): def __init__(self, name="temp_reg_2", reg_width=32): super().__init__(name, reg_width) self.test_field_3 = uvm_reg_field("test_field_3") self.test_field_4 = uvm_reg_field("test_field_4") self.test_field_5 = uvm_reg_field("test_field_5") def build(self): self.test_field_3.configure(self, 8, 0, "RW", 0, 0) self.test_field_4.configure(self, 8, 8, "RW", 0, 0) self.test_field_5.configure(self, 8, 16, "RW", 0, 0) self._set_lock() # THIRD REGISTER class temp_reg_3(uvm_reg): def __init__(self, name="temp_reg_3", reg_width=32): super().__init__(name, reg_width) self.test_field_6 = uvm_reg_field("test_field_6") self.test_field_7 = uvm_reg_field("test_field_7") def build(self): self.test_field_6.configure(self, 8, 0, "RW", 0, 0) self.test_field_7.configure(self, 8, 8, "RW", 0, 0) self._set_lock() # SUB REG BLOCK class temp_blk_1(uvm_reg_block): def __init__(self, name="temp_blk_1"): super().__init__(name) self.def_map = uvm_reg_map("blk_1_map") self.def_map.configure(self, 0) self.test_reg_1 = temp_reg_3("test_reg_3") self.test_reg_1.configure(self, "0xC", "") self.def_map.add_reg(self.test_reg_1, "0x0", "RW") # START block = uvm_reg_block() reg0 = temp_reg_1() reg0.configure(block, "0x4", "") reg1 = temp_reg_2() reg1.configure(block, "0x8", "") blk0 = temp_blk_1() blk0.set_lock() block.add_block(blk0) block.set_lock() assert block.get_fields() == [ reg0.test_field_1, reg0.test_field_2, reg1.test_field_3, reg1.test_field_4, reg1.test_field_5, blk0.test_reg_1.test_field_6, blk0.test_reg_1.test_field_7, ] assert block.get_fields(hier=uvm_hier_e.UVM_NO_HIER) == [ reg0.test_field_1, reg0.test_field_2, reg1.test_field_3, reg1.test_field_4, reg1.test_field_5, ] def test_reg_map_get_name(): map_with_explicit_name = uvm_reg_map("some_map") assert map_with_explicit_name.get_name() == "some_map" map_with_implicit_name = uvm_reg_map() assert map_with_implicit_name.get_name() == "uvm_reg_map" def test_reg_map_configure(): reg_map = uvm_reg_map() parent = uvm_reg_block() reg_map.configure(parent, 1024) assert reg_map.get_parent() == parent assert reg_map.get_offset() == 1024 def test_reg_map_with_single_reg(): reg_map = uvm_reg_map() reg = uvm_reg() reg_map.add_reg(reg, "0x0") assert reg_map.get_registers() == [reg] def test_reg_map_with_multiple_regs(): reg_map = uvm_reg_map() reg0 = uvm_reg() reg_map.add_reg(reg0, "0xf") reg1 = uvm_reg() reg_map.add_reg(reg1, "0xff") assert reg_map.get_registers() == [reg0, reg1] assert reg_map.get_reg_by_offset("0xf") == reg0 assert reg_map.get_reg_by_offset("0xff") == reg1 @pytest.mark.reg_block_get_reg_by_name def test_reg_block_get_reg_by_name(): # FIRST REGISTER class temp_reg_1(uvm_reg): def __init__(self, name="temp_reg_1", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # SECOND REGISTER class temp_reg_2(uvm_reg): def __init__(self, name="temp_reg_2", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # THIRD REGISTER class temp_reg_3(uvm_reg): def __init__(self, name="temp_reg_3", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # FOURTH REGISTER class temp_reg_4(uvm_reg): def __init__(self, name="temp_reg_4", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # FIRST SUB REG BLOCK class temp_blk_1(uvm_reg_block): def __init__(self, name="temp_blk_1"): super().__init__(name) self.def_map = uvm_reg_map("blk_1_map") self.def_map.configure(self, 0) self.reg0 = temp_reg_2() self.reg0.configure(self, "0x8", "") self.reg1 = temp_reg_3() self.reg1.configure(self, "0xC", "") self.def_map.add_reg(self.reg0, "0x0", "RW") self.def_map.add_reg(self.reg1, "0x0", "RW") # SECOND SUB REG BLOCK class temp_blk_2(uvm_reg_block): def __init__(self, name="temp_blk_2"): super().__init__(name) self.def_map = uvm_reg_map("blk_2_map") self.def_map.configure(self, 0) self.reg0 = temp_reg_4() self.reg0.configure(self, "0x10", "") self.def_map.add_reg(self.reg0, "0x0", "RW") self.blk1 = temp_blk_1() self.blk1.set_lock() self.add_block(self.blk1) block = uvm_reg_block() reg0 = temp_reg_1() reg0.configure(block, "0x4", "") blk0 = temp_blk_2() blk0.set_lock() block.add_block(blk0) block.set_lock() print(type(block.get_reg_by_name("temp_reg_1"))) assert block.get_reg_by_name("temp_reg_1") == reg0 assert block.get_reg_by_name("temp_reg_2") == blk0.blk1.reg0 assert block.get_reg_by_name("temp_reg_3") == blk0.blk1.reg1 assert block.get_reg_by_name("temp_reg_4") == blk0.reg0 assert block.get_reg_by_name("temp_reg_X") is None @pytest.mark.reg_block_get_field_by_name def test_reg_block_get_field_by_name(): # FIRST REGISTER class temp_reg_1(uvm_reg): def __init__(self, name="temp_reg_1", reg_width=32): super().__init__(name, reg_width) self.fieldA = uvm_reg_field("fieldA") self.fieldB = uvm_reg_field("fieldB") def build(self): self.fieldA.configure(self, 8, 0, "RW", 0, 0) self.fieldB.configure(self, 16, 8, "RW", 0, 0) self._set_lock() # SECOND REGISTER class temp_reg_2(uvm_reg): def __init__(self, name="temp_reg_2", reg_width=32): super().__init__(name, reg_width) self.fieldC = uvm_reg_field("fieldC") self.fieldD = uvm_reg_field("fieldD") self.fieldE = uvm_reg_field("fieldE") def build(self): self.fieldC.configure(self, 8, 0, "RW", 0, 0) self.fieldD.configure(self, 8, 8, "RW", 0, 0) self.fieldE.configure(self, 8, 16, "RW", 0, 0) self._set_lock() # THIRD REGISTER class temp_reg_3(uvm_reg): def __init__(self, name="temp_reg_3", reg_width=32): super().__init__(name, reg_width) self.fieldF = uvm_reg_field("fieldF") self.fieldG = uvm_reg_field("fieldG") def build(self): self.fieldF.configure(self, 8, 0, "RW", 0, 0) self.fieldG.configure(self, 8, 8, "RW", 0, 0) self._set_lock() # FOURTH REGISTER class temp_reg_4(uvm_reg): def __init__(self, name="temp_reg_4", reg_width=32): super().__init__(name, reg_width) self.fieldH = uvm_reg_field("fieldH") self.fieldI = uvm_reg_field("fieldI") def build(self): self.fieldH.configure(self, 8, 0, "RW", 0, 0) self.fieldI.configure(self, 8, 8, "RW", 0, 0) self._set_lock() # FIRST SUB REG BLOCK class temp_blk_1(uvm_reg_block): def __init__(self, name="temp_blk_1"): super().__init__(name) self.def_map = uvm_reg_map("blk_1_map") self.def_map.configure(self, 0) self.reg0 = temp_reg_2("reg2") self.reg0.configure(self, "0x8", "") self.reg1 = temp_reg_3("reg3") self.reg1.configure(self, "0xC", "") self.def_map.add_reg(self.reg0, "0x0", "RW") self.def_map.add_reg(self.reg1, "0x0", "RW") # SECOND SUB REG BLOCK class temp_blk_2(uvm_reg_block): def __init__(self, name="temp_blk_2"): super().__init__(name) self.def_map = uvm_reg_map("blk_2_map") self.def_map.configure(self, 0) self.reg0 = temp_reg_4("reg4") self.reg0.configure(self, "0x10", "") self.def_map.add_reg(self.reg0, "0x0", "RW") self.blk1 = temp_blk_1() self.blk1.set_lock() self.add_block(self.blk1) # START block = uvm_reg_block() reg0 = temp_reg_1() reg0.configure(block, "0x4", "") blk0 = temp_blk_2() blk0.set_lock() block.add_block(blk0) block.set_lock() assert block.get_field_by_name("fieldB") == reg0.fieldB assert block.get_field_by_name("fieldC") == blk0.blk1.reg0.fieldC assert block.get_field_by_name("fieldF") == blk0.blk1.reg1.fieldF assert block.get_field_by_name("fieldH") == blk0.reg0.fieldH assert block.get_field_by_name("fieldX") is None ############################################################################## # TESTS UVM_REG ############################################################################## def test_reg_get_name(): reg = uvm_reg("some_reg") assert reg.get_name() == "some_reg" def test_reg_configure(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START reg = temp_reg() parent = uvm_reg_block() reg.configure(parent, "0x4", "") assert reg.get_parent() == parent def test_reg_with_single_field(): reg = uvm_reg() field = uvm_reg_field() field.configure(reg, 8, 0, "RW", 0, 0) assert reg.get_fields() == [field] def test_reg_with_multiple_fields(): reg = uvm_reg() field0 = uvm_reg_field() field0.configure(reg, 8, 0, "RW", 0, 0) field1 = uvm_reg_field() field1.configure(reg, 8, 8, "RW", 0, 0) assert reg.get_fields() == [field0, field1] def test_reg_field_get_name(): field_with_explicit_name = uvm_reg_field("some_field") print(field_with_explicit_name.get_name()) assert field_with_explicit_name.get_name() == "some_field" field_with_implicit_name = uvm_reg_field() assert field_with_implicit_name.get_name() == "uvm_reg_field" def test_reg_field_configure(): field = uvm_reg_field() parent = uvm_reg() field.configure(parent, 8, 16, "RW", True, 15) field.field_lock() assert field.get_parent() == parent assert field.get_n_bits() == 8 assert field.get_lsb_pos() == 16 assert field.get_access() == "RW" assert field.is_volatile() assert field.get_reset() == 15 def test_reg_field_is_volatile(): field = uvm_reg_field() field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() assert field.is_volatile() field.configure(uvm_reg(), 8, 16, "RW", False, 15) assert not field.is_volatile() ############################################################################## # TESTS ENTIRE RAL ############################################################################## def test_simple_reg_model(): """ A more realistic register model based on the venerable UART 16550 design """ class LineControlRegister(uvm_reg): def __init__(self, name="LineControlRegister", reg_width=32): super().__init__(name, reg_width) self.WLS = uvm_reg_field("WLS") self.STB = uvm_reg_field("STB") self.PEN = uvm_reg_field("PEN") self.EPS = uvm_reg_field("EPS") def build(self): self.WLS.configure(self, 2, 0, "RW", 0, 0) self.STB.configure(self, 1, 2, "RW", 0, 0) self.PEN.configure(self, 1, 3, "RW", 0, 0) self.EPS.configure(self, 1, 4, "RW", 0, 0) self._set_lock() class LineStatusRegister(uvm_reg): def __init__(self, name="LineStatusRegister", reg_width=32): super().__init__(name, reg_width) self.DR = uvm_reg_field("DR") self.OE = uvm_reg_field("OE") self.PE = uvm_reg_field("PE") self.FE = uvm_reg_field("FE") def build(self): self.DR.configure(self, 1, 0, "RW", 1, 0) self.OE.configure(self, 1, 1, "RW", 1, 0) self.PE.configure(self, 1, 2, "RW", 1, 0) self.FE.configure(self, 1, 3, "RW", 1, 0) self._set_lock() class Regs(uvm_reg_block): def __init__(self, name): super().__init__(name) self.map = uvm_reg_map("map") self.map.configure(self, 0) self.LCR = LineControlRegister("LCR") self.LCR.configure(self, "0x100c", "") self.map.add_reg(self.LCR, "0x0") self.LSR = LineStatusRegister("LSR") self.LSR.configure(self, "0x1014", "") self.map.add_reg(self.LSR, "0x0") regs = Regs("regs") assert regs.get_name() == "regs" assert regs.map.get_reg_by_offset("0x100c") == regs.LCR assert regs.map.get_reg_by_offset("0x1014") == regs.LSR LCR = regs.LCR assert LCR.get_name() == "LCR" assert LCR.WLS.get_name() == "WLS" assert LCR.STB.get_name() == "STB" assert LCR.PEN.get_name() == "PEN" assert LCR.EPS.get_name() == "EPS" assert LCR.WLS.get_n_bits() == 2 for field in [field for field in LCR.get_fields() if field != LCR.WLS]: assert field.get_n_bits() == 1 def pairwise(iterable): """s -> (s0,s1), (s1,s2), (s2, s3), ...""" a, b = itertools.tee(iterable) next(b, None) return zip(a, b) def get_msb_pos(field): return field.get_lsb_pos() + field.get_n_bits() - 1 def are_adjacent(prev_field, field): return field.get_lsb_pos() == get_msb_pos(prev_field) + 1 assert LCR.get_fields()[0].get_lsb_pos() == 0 for prev_field, field in pairwise(LCR.get_fields()): assert are_adjacent(prev_field, field) for field in LCR.get_fields(): assert field.get_access() == "RW" assert not field.is_volatile() assert field.get_reset() == 0 LSR = regs.LSR assert LSR.DR.get_name() == "DR" assert LSR.OE.get_name() == "OE" assert LSR.PE.get_name() == "PE" assert LSR.FE.get_name() == "FE" for field in LSR.get_fields(): assert field.get_n_bits() == 1 assert field.get_access() == "RW" assert field.is_volatile() assert field.get_reset() == 0 assert LSR.get_fields()[0].get_lsb_pos() == 0 for prev_field, field in pairwise(LSR.get_fields()): assert are_adjacent(prev_field, field) LSR.reset() assert LSR.get_mirrored_value() == 0 LSR.predict(12, kind=uvm_predict_e.UVM_PREDICT_WRITE) assert LSR.get_mirrored_value() == 12 for field in LSR.get_fields(): print(field.get_value()) LSR.predict(12, kind=uvm_predict_e.UVM_PREDICT_READ) assert LSR.get_mirrored_value() == 12 pyuvm-4.0.1/tests/pytests/test_uvm_reg_field.py000077500000000000000000000323171507477334100220150ustar00rootroot00000000000000# Main Packages for the entire RAL model from random import randint import pytest from pyuvm.s17_uvm_reg_enumerations import uvm_door_e, uvm_predict_e from pyuvm.s24_uvm_reg_includes import uvm_resp_t from pyuvm.s27_uvm_reg_pkg import uvm_reg, uvm_reg_field ############################################################################## # TIPS ############################################################################## """ Use this to execute the test which will not be counted into the entire number of FAILING tests @pytest.mark.xfail Use this to just skip the execution of a specific test @pytest.mark.skip Use this to give a specific test method a name ID the execute it by using py.test -m ID_NAME @pytest.mark.ID_NAME Use this to give a specific test parameters to be used @pytest.mark.parametrize("name1, name2",value_type_1, value_type_2) If pip install pytest-sugar is ran then pytest is gonna likely execute a bar progression while running tests (especially if in Parallel) """ ############################################################################## # TESTS ############################################################################## ############################################################################## # TESTS uvm_reg_field ############################################################################## @pytest.mark.test_reg_field_get_name def test_reg_field_get_name(): field_with_explicit_name = uvm_reg_field("some_field") assert field_with_explicit_name.get_name() == "some_field" field_with_implicit_name = uvm_reg_field() assert field_with_implicit_name.get_name() == "uvm_reg_field" @pytest.mark.test_reg_field_configure def test_reg_field_configure(): field = uvm_reg_field() parent = uvm_reg() field.configure(parent, 8, 16, "RW", True, 15) field.field_lock() assert field.get_parent() == parent assert field.get_n_bits() == 8 assert field.get_lsb_pos() == 16 assert field.get_access() == "RW" assert field.is_volatile() assert field.get_reset() == 15 @pytest.mark.test_reg_field_is_volatile def test_reg_field_is_volatile(): field = uvm_reg_field("FIELD_volatile") field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() assert field.is_volatile() field.configure(uvm_reg(), 8, 16, "RW", False, 15) field.field_lock() assert not field.is_volatile() @pytest.mark.test_reg_field_all_access def test_reg_field_all_access(): for acs in [ "RO", "RW", "RC", "RS", "WC", "WS", "W1C", "W1S", "W1T", "W0C", "W0S", "W0T", "WRC", "WRS", "W1SRC", "W1CRS", "W0SRC", "W0CRS", "WSRC", "WCRS", "WO", "WOC", "WOS", "W1", "WO1", ]: field = uvm_reg_field("FIELD_" + acs) field.configure(uvm_reg(), 8, 16, acs, True, 15) field.field_lock() assert field.get_access() == acs, f"Access value {acs} not in the list" @pytest.mark.test_reg_field_lsb_pos def test_reg_field_lsb_pos(): field = uvm_reg_field("FIELD_lsb_pos") field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() assert field.get_lsb_pos() == 16 @pytest.mark.test_reg_field_reset def test_reg_field_reset(): field = uvm_reg_field() field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() field.reset() assert field.get_reset() == 15, ( f"after reset internal reset value doesn't match {field.get_reset()}" ) assert field.get_value() == 15, ( f"after reset internal mirrored value doesn't match {field.get_value()}" ) @pytest.mark.test_reg_field_get def test_reg_field_get(): field = uvm_reg_field() field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() assert field.get() == 0 @pytest.mark.test_reg_field_get_value def test_reg_field_get_value(): field = uvm_reg_field() field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() assert field.get_lsb_pos() == 16 @pytest.mark.test_reg_field_field_predict_read_set def test_reg_field_field_predict_read_set(): field = uvm_reg_field() ## With the set register once we perform the operation R the register will be set entirely according to the fields ## getting a prendicted value of 1 regardless of the predicted value we set through the function for acs in ["RS", "WRS", "WCRS", "W1CRS", "W0CRS"]: field.configure(uvm_reg(), 8, 16, acs, True, 15) field.field_lock() field.reset() field.predict( randint(1, 2 ** field.get_n_bits()), kind=uvm_predict_e.UVM_PREDICT_READ, path=uvm_door_e.UVM_FRONTDOOR, ) assert field.get_value() == (2 ** field.get_n_bits() - 1), ( f"Failing for access {acs}" ) assert field.get_response() == uvm_resp_t.PASS_RESP, ( f"Failing as default status is not PASS_RESP is {field.get_response()}" ) @pytest.mark.test_reg_field_field_predict_read_clear def test_reg_field_field_predict_read_clear(): field = uvm_reg_field() ## With the clear register once we perform the operation R the register will be cleared entirely according to the fields ## getting a prendicted value of 0 regardless of the predicted value we set through the function for acs in ["RC", "WRC", "WSRC", "W1SRC", "W0SRC"]: field.configure(uvm_reg(), 8, 16, acs, True, 15) field.field_lock() field.reset() field.predict( randint(1, 2 ** field.get_n_bits()), kind=uvm_predict_e.UVM_PREDICT_READ, path=uvm_door_e.UVM_FRONTDOOR, ) assert field.get_value() == 0, f"Failing for access {acs}" assert field.get_response() == uvm_resp_t.PASS_RESP, ( f"Failing as default status is not PASS_RESP is {field.get_response()}" ) @pytest.mark.test_reg_field_field_predict_write_set def test_reg_field_field_predict_write_set(): field = uvm_reg_field() ## With the set register once we perform the operation W the register will be set entirely according to the fields ## getting a prendicted value of 1 regardless of the predicted value we set through the function for acs in ["WSRC", "WOS", "WS", "W0S", "W1SRC", "W0SRC", "W1S"]: field.configure(uvm_reg(), 8, 16, acs, True, 0) field.field_lock() field.reset() local_rand_el = randint(1, (2 ** field.get_n_bits() - 1)) field.predict( local_rand_el, kind=uvm_predict_e.UVM_PREDICT_WRITE, path=uvm_door_e.UVM_FRONTDOOR, ) if ( field.get_access() in ["W0S", "W0SRC"] ): ## anytime a bit is clear to 0 that bit is gonna be set to 1 if not already 1 predicted_v = field.get_reset() | ( ~local_rand_el & int("".join(["1"] * field.get_n_bits()), 2) ) elif field.get_access() in [ "W1SRC", "W1S", ]: ## anytime a bit is set to 1 that bit is gonna be set to 1 if not already 1 predicted_v = field.get_reset() | ( local_rand_el & int("".join(["1"] * field.get_n_bits()), 2) ) else: predicted_v = 2 ** field.get_n_bits() - 1 assert field.get_value() == predicted_v, f"Failing for access {acs}" assert field.get_response() == uvm_resp_t.PASS_RESP, ( f"Failing as default status is not PASS_RESP is {field.get_response()}" ) @pytest.mark.test_reg_field_field_predict_write_clear def test_reg_field_field_predict_write_clear(): field = uvm_reg_field() ## With the clear register once we perform the operation W the register will be cleared entirely according to the fields ## getting a prendicted value of 0 regardless of the predicted value we set through the function for acs in ["WOC", "WC", "W1C", "W1CRS", "W0C", "W0CRS", "WCRS"]: field.configure(uvm_reg(), 8, 16, acs, True, 0) field.field_lock() field.reset() local_rand_el = randint(1, (2 ** field.get_n_bits() - 1)) field.predict( local_rand_el, kind=uvm_predict_e.UVM_PREDICT_WRITE, path=uvm_door_e.UVM_FRONTDOOR, ) if ( field.get_access() in ["W0C", "W0CRC"] ): ## anytime a bit is clear to 0 that bit is gonna be set to 0 if not already 0 predicted_v = field.get_reset() & ( local_rand_el & int("".join(["1"] * field.get_n_bits()), 2) ) elif field.get_access() in [ "W1CRC", "W1C", ]: ## anytime a bit is set to 1 that bit is gonna be set to 0 if not already 0 predicted_v = field.get_reset() & ( ~local_rand_el & int("".join(["1"] * field.get_n_bits()), 2) ) else: predicted_v = 0 assert field.get_value() == predicted_v, f"Failing for access {acs}" assert field.get_response() == uvm_resp_t.PASS_RESP, ( f"Failing as default status is not PASS_RESP is {field.get_response()}" ) @pytest.mark.test_reg_field_field_predict_TOGGLE def test_reg_field_field_predict_TOGGLE(): field = uvm_reg_field() ## With the clear register once we perform the operation W the register will be cleared entirely according to the fields ## getting a prendicted value of 0 regardless of the predicted value we set through the function for acs in ["W1T", "W0T"]: field.configure(uvm_reg(), 8, 16, acs, True, 0) field.field_lock() field.reset() local_rand_el = randint(1, (2 ** field.get_n_bits() - 1)) field.predict( local_rand_el, kind=uvm_predict_e.UVM_PREDICT_WRITE, path=uvm_door_e.UVM_FRONTDOOR, ) if ( "0" in field.get_access() ): ## anytime a bit is cleared to 0 that bit is gonna be set to 1 or 0 toggling predicted_v = field.get_reset() ^ ( ~local_rand_el & int("".join(["1"] * field.get_n_bits()), 2) ) else: ## anytime a bit is cleared to 1 that bit is gonna be set to 1 or 0 toggling predicted_v = field.get_reset() ^ ( local_rand_el & int("".join(["1"] * field.get_n_bits()), 2) ) assert field.get_value() == predicted_v, f"Failing for access {acs}" assert field.get_response() == uvm_resp_t.PASS_RESP, ( f"Failing as default status is not PASS_RESP is {field.get_response()}" ) @pytest.mark.test_reg_field_field_predict_NO_ACCESS def test_reg_field_field_predict_NO_ACCESS(): field = uvm_reg_field() ## With the NO_ACCESS the reset value is always returned field.configure(uvm_reg(), 8, 16, "NO_ACCESS", True, randint(1, 2**8 - 1)) field.field_lock() field.reset() field.predict( randint(1, 2 ** field.get_n_bits()), kind=uvm_predict_e.UVM_PREDICT_WRITE, path=uvm_door_e.UVM_FRONTDOOR, ) assert field.get_value() == field.get_reset(), "Failing for access NO_ACCESS" field.predict( randint(1, 2 ** field.get_n_bits()), kind=uvm_predict_e.UVM_PREDICT_READ, path=uvm_door_e.UVM_FRONTDOOR, ) assert field.get_value() == field.get_reset(), "Failing for access NO_ACCESS" assert field.get_response() == uvm_resp_t.PASS_RESP, ( f"Failing as default status is not PASS_RESP is {field.get_response()}" ) @pytest.mark.test_reg_field_field_predict_status_error_on_write def test_reg_field_field_predict_status_error_on_write(): field = uvm_reg_field() ## With the NO_ACCESS the reset value is always returned for acs in ["RO", "RW", "RC", "RS"]: field.configure(uvm_reg(), 8, 16, acs, True, randint(1, 2**8 - 1)) field.field_lock() field.set_throw_error_on_read(True) field.set_throw_error_on_write(True) field.reset() field.predict( randint(1, 2 ** field.get_n_bits()), kind=uvm_predict_e.UVM_PREDICT_WRITE, path=uvm_door_e.UVM_FRONTDOOR, ) assert field.get_response() == uvm_resp_t.ERROR_RESP, ( f"Failing for access access: {acs} where UVM_READ is issued response is: {field.get_response().name}" ) @pytest.mark.test_reg_field_field_predict_status_error_on_read def test_reg_field_field_predict_status_error_on_read(): field = uvm_reg_field() # With the NO_ACCESS the reset value is always returned for acs in [ "WO", "WOC", "WOS", "WO1", "NOACCESS", "W1", "W1T", "W0T", "WC", "WS", "W1C", "W1S", "W0C", "W0S", ]: field.configure(uvm_reg(), 8, 16, acs, True, randint(1, 2**8 - 1)) field.field_lock() field.set_throw_error_on_read(True) field.set_throw_error_on_write(True) field.reset() field.predict( randint(1, 2 ** field.get_n_bits()), kind=uvm_predict_e.UVM_PREDICT_READ, path=uvm_door_e.UVM_FRONTDOOR, ) assert field.get_response() == uvm_resp_t.ERROR_RESP, ( f"Failing for access access: {acs} where UVM_READ is issued response is: {field.get_response().name}" ) pyuvm-4.0.1/tests/pytests/test_uvm_reg_map.py000077500000000000000000000221011507477334100214750ustar00rootroot00000000000000# Main Packages for the entire RAL model import itertools import pytest from pyuvm.s17_uvm_reg_enumerations import uvm_predict_e from pyuvm.s27_uvm_reg_pkg import uvm_reg, uvm_reg_block, uvm_reg_field, uvm_reg_map ############################################################################## # TIPS ############################################################################## """ Use this to execute the test which will not be counted into the entire number of FAILING tests @pytest.mark.xfail Use this to just skip the execution of a specific test @pytest.mark.skip Use this to give a specific test method a name ID the execute it by using py.test -m ID_NAME @pytest.mark.ID_NAME Use this to give a specific test parameters to be used @pytest.mark.parametrize("name1, name2",value_type_1, value_type_2) If pip install pytest-sugar is ran then pytest is gonna likely execute a bar progression while running tests (especially if in Parallel) """ ############################################################################## # TESTS ############################################################################## @pytest.mark.test_reg_block_get_name def test_reg_block_get_name(): block = uvm_reg_block("some_block") assert block.get_name() == "some_block" @pytest.mark.test_reg_block_with_single_reg def test_reg_block_with_single_reg(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START block = uvm_reg_block() reg = temp_reg() reg.configure(block, "0x4", "") block.set_lock() assert block.get_registers() == [reg] @pytest.mark.test_reg_block_with_multiple_regs def test_reg_block_with_multiple_regs(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START block = uvm_reg_block() reg0 = temp_reg() reg0.configure(block, "0x4", "") reg1 = temp_reg() reg1.configure(block, "0x8", "") block.set_lock() assert block.get_registers() == [reg0, reg1] @pytest.mark.test_reg_map_get_name def test_reg_map_get_name(): map_with_explicit_name = uvm_reg_map("some_map") assert map_with_explicit_name.get_name() == "some_map" map_with_implicit_name = uvm_reg_map() assert map_with_implicit_name.get_name() == "uvm_reg_map" @pytest.mark.test_reg_map_configure def test_reg_map_configure(): reg_map = uvm_reg_map() parent = uvm_reg_block() reg_map.configure(parent, 1024) assert reg_map.get_parent() == parent assert reg_map.get_offset() == 1024 @pytest.mark.test_reg_map_with_single_reg def test_reg_map_with_single_reg(): reg_map = uvm_reg_map() reg = uvm_reg() reg_map.add_reg(reg, "0", "RW") assert reg_map.get_registers() == [reg] @pytest.mark.test_reg_map_with_multiple_regs def test_reg_map_with_multiple_regs(): reg_map = uvm_reg_map() reg0 = uvm_reg() reg_map.add_reg(reg0, "0xf", "RW") reg1 = uvm_reg() reg_map.add_reg(reg1, "0xff", "RW") assert reg_map.get_registers() == [reg0, reg1] assert reg_map.get_reg_by_offset("0xf") == reg0 assert reg_map.get_reg_by_offset("0xff") == reg1 ############################################################################## # TESTS UVM_REG ############################################################################## def test_reg_get_name(): reg = uvm_reg("some_reg") assert reg.get_name() == "some_reg" def test_reg_configure(): class temp_reg(uvm_reg): def __init__(self, name="temp_reg", reg_width=32): super().__init__(name, reg_width) def build(self): self._set_lock() # START reg = temp_reg() parent = uvm_reg_block() reg.configure(parent, "0x4", "") assert reg.get_parent() == parent def test_reg_with_single_field(): reg = uvm_reg() field = uvm_reg_field() field.configure(reg, 8, 0, "RW", 0, 0) assert reg.get_fields() == [field] def test_reg_with_multiple_fields(): reg = uvm_reg() field0 = uvm_reg_field() field0.configure(reg, 8, 0, "RW", 0, 0) field1 = uvm_reg_field() field1.configure(reg, 8, 8, "RW", 0, 0) assert reg.get_fields() == [field0, field1] def test_reg_field_get_name(): field_with_explicit_name = uvm_reg_field("some_field") print(field_with_explicit_name.get_name()) assert field_with_explicit_name.get_name() == "some_field" field_with_implicit_name = uvm_reg_field() assert field_with_implicit_name.get_name() == "uvm_reg_field" def test_reg_field_configure(): field = uvm_reg_field() parent = uvm_reg() field.configure(parent, 8, 16, "RW", True, 15) field.field_lock() assert field.get_parent() == parent assert field.get_n_bits() == 8 assert field.get_lsb_pos() == 16 assert field.get_access() == "RW" assert field.is_volatile() assert field.get_reset() == 15 def test_reg_field_is_volatile(): field = uvm_reg_field() field.configure(uvm_reg(), 8, 16, "RW", True, 15) field.field_lock() assert field.is_volatile() field.configure(uvm_reg(), 8, 16, "RW", False, 15) assert not field.is_volatile() ############################################################################## # TESTS ENTIRE RAL ############################################################################## def test_simple_reg_model(): """ A more realistic register model based on the venerable UART 16550 design """ class LineControlRegister(uvm_reg): def __init__(self, name="LineControlRegister", reg_width=32): super().__init__(name, reg_width) self.WLS = uvm_reg_field("WLS") self.STB = uvm_reg_field("STB") self.PEN = uvm_reg_field("PEN") self.EPS = uvm_reg_field("EPS") def build(self): self.WLS.configure(self, 2, 0, "RW", 0, 0) self.STB.configure(self, 1, 2, "RW", 0, 0) self.PEN.configure(self, 1, 3, "RW", 0, 0) self.EPS.configure(self, 1, 4, "RW", 0, 0) self._set_lock() class LineStatusRegister(uvm_reg): def __init__(self, name="LineStatusRegister", reg_width=32): super().__init__(name, reg_width) self.DR = uvm_reg_field("DR") self.OE = uvm_reg_field("OE") self.PE = uvm_reg_field("PE") self.FE = uvm_reg_field("FE") def build(self): self.DR.configure(self, 1, 0, "RW", 1, 0) self.OE.configure(self, 1, 1, "RW", 1, 0) self.PE.configure(self, 1, 2, "RW", 1, 0) self.FE.configure(self, 1, 3, "RW", 1, 0) self._set_lock() class Regs(uvm_reg_block): def __init__(self, name): super().__init__(name) self.map = uvm_reg_map("map") self.map.configure(self, 0) self.LCR = LineControlRegister("LCR") self.LCR.configure(self, "0x100c", "") self.map.add_reg(self.LCR, "0x0") self.LSR = LineStatusRegister("LSR") self.LSR.configure(self, "0x1014", "") self.map.add_reg(self.LSR, "0x0") regs = Regs("regs") assert regs.get_name() == "regs" assert regs.map.get_reg_by_offset("0x100c") == regs.LCR assert regs.map.get_reg_by_offset("0x1014") == regs.LSR LCR = regs.LCR assert LCR.get_name() == "LCR" assert LCR.WLS.get_name() == "WLS" assert LCR.STB.get_name() == "STB" assert LCR.PEN.get_name() == "PEN" assert LCR.EPS.get_name() == "EPS" assert LCR.WLS.get_n_bits() == 2 for field in [field for field in LCR.get_fields() if field != LCR.WLS]: assert field.get_n_bits() == 1 def pairwise(iterable): """s -> (s0,s1), (s1,s2), (s2, s3), ...""" a, b = itertools.tee(iterable) next(b, None) return zip(a, b) def get_msb_pos(field): return field.get_lsb_pos() + field.get_n_bits() - 1 def are_adjacent(prev_field, field): return field.get_lsb_pos() == get_msb_pos(prev_field) + 1 assert LCR.get_fields()[0].get_lsb_pos() == 0 for prev_field, field in pairwise(LCR.get_fields()): assert are_adjacent(prev_field, field) for field in LCR.get_fields(): assert field.get_access() == "RW" assert not field.is_volatile() assert field.get_reset() == 0 LSR = regs.LSR assert LSR.DR.get_name() == "DR" assert LSR.OE.get_name() == "OE" assert LSR.PE.get_name() == "PE" assert LSR.FE.get_name() == "FE" for field in LSR.get_fields(): assert field.get_n_bits() == 1 assert field.get_access() == "RW" assert field.is_volatile() assert field.get_reset() == 0 assert LSR.get_fields()[0].get_lsb_pos() == 0 for prev_field, field in pairwise(LSR.get_fields()): assert are_adjacent(prev_field, field) LSR.reset() assert LSR.get_mirrored_value() == 0 LSR.predict(12, kind=uvm_predict_e.UVM_PREDICT_WRITE) assert LSR.get_mirrored_value() == 12 for field in LSR.get_fields(): print(field.get_value()) pyuvm-4.0.1/tests/pytests/tmp_06_reporting_classes.py000066400000000000000000000013111507477334100230450ustar00rootroot00000000000000import pytest from pyuvm import * def test_object_creation(): """ Test that we actually get a logger in our object. """ ro = uvm_report_object("ro") assert hasattr(ro, "logger") assert not ro.logger.propagate @pytest.mark.parametrize( "level,msg", [ (logging.DEBUG, "debug"), (logging.INFO, "info"), (logging.ERROR, "error"), (logging.CRITICAL, "critical"), ], ) def test_logging(level, msg, caplog): ro = uvm_report_object("ro") ro.logger.propagate = True # workaround for 'caplog' caplog.set_level(logging.DEBUG, ro.logger.name) ro.logger.log(level, msg) assert caplog.record_tuples == [(ro.logger.name, level, msg)] pyuvm-4.0.1/tests/uvm_unittest.py000066400000000000000000000004171507477334100171730ustar00rootroot00000000000000import logging import unittest import pytest @pytest.mark.usefixtures("initialize_pyuvm") class uvm_TestCase(unittest.TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.logger = logging.getLogger("uvm_TestCase") pyuvm-4.0.1/tox.ini000066400000000000000000000037211507477334100142250ustar00rootroot00000000000000[tox] skip_missing_interpreters=true envlist = py{36,37,38,39,310,311,312,313}-cocotb20-{linux,macos,windows} py{36,37,38,39,310,311,312,313}-cocotb19-{linux,windows} py{36,37,38,39,310,311,312}-cocotb18-{linux,windows} py{36,37,38,39,310,311}-cocotb17-{linux,windows} py{36,37,38,39,310}-cocotb16-{linux,windows} # for the requires key minversion = 3.2.0 isolated_build = true # virtualenv is used by tox; versions below 16.1.0 cause a DeprecationWarning # to be shown for all code which uses virtualenv, which is the case for all code # we run through tox. (https://github.com/pypa/virtualenv/pull/1064) requires = virtualenv >= 16.1 [testenv] allowlist_externals= make bash platform = linux: linux|cygwin macos: darwin windows: win32 setenv = PYTHONPATH = {toxinidir}:{toxinidir}/tests/pytests COCOTB_REDUCED_LOG_FMT=1 passenv = VERILOG_SIM LM_LICENSE_FILE PYTHONPATH VHDL_SIM deps = cocotb16: cocotb==1.6 cocotb17: cocotb==1.7 cocotb18: cocotb==1.8 cocotb19: cocotb==1.9 cocotb20: cocotb==2.0 pytest install_command = windows: python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} python -m pip install {opts} {packages} commands = pytest make cocotb_tests tox_fail_on_error = true # needed for coverage to work # usedevelop=True # Note: this target is *not* used by Read The Docs, it runs sphinx-build # directly. Hence, all special build steps here are only relevant for # local builds, not for RTD builds. Add all build steps which should be executed # in RTD builds as Python code into the conf.py file. [gh-actions] python = 3.6: py36 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311 3.12: py312 3.13: py313 [gh-actions:env] RUNNER_OS = Linux: linux Windows: windows macOS: macos COCOTB_VERSION = 1.6: cocotb16 1.7: cocotb17 1.8: cocotb18 1.9: cocotb19 2.0: cocotb20