From 7543aa986c0e991881e5168dc41d07655e8e2142 Mon Sep 17 00:00:00 2001 From: Sina Karvandi Date: Sun, 14 Apr 2024 21:11:26 +0900 Subject: [PATCH] add cocotb testbench --- src/main/scala/top.scala | 2 + src/main/scala/top_test.scala | 2 + src/test/scala/hwdbg/tb_top_test.scala | 58 +++++++ src/test/tb/.gitignore | 164 ++++++++++++++++++ src/test/tb/Makefile | 8 + src/test/tb/run.sh | 1 + src/test/tb/test_DebuggerModuleTestingBRAM.py | 67 +++++++ 7 files changed, 302 insertions(+) create mode 100644 src/test/scala/hwdbg/tb_top_test.scala create mode 100644 src/test/tb/.gitignore create mode 100644 src/test/tb/Makefile create mode 100755 src/test/tb/run.sh create mode 100644 src/test/tb/test_DebuggerModuleTestingBRAM.py diff --git a/src/main/scala/top.scala b/src/main/scala/top.scala index e340b37..e30153b 100644 --- a/src/main/scala/top.scala +++ b/src/main/scala/top.scala @@ -12,6 +12,8 @@ * @copyright * This project is released under the GNU Public License v3. */ +package hwdbg + import chisel3._ import circt.stage.ChiselStage diff --git a/src/main/scala/top_test.scala b/src/main/scala/top_test.scala index d52eeed..14337c8 100644 --- a/src/main/scala/top_test.scala +++ b/src/main/scala/top_test.scala @@ -12,6 +12,8 @@ * @copyright * This project is released under the GNU Public License v3. */ +package hwdbg + import chisel3._ import circt.stage.ChiselStage diff --git a/src/test/scala/hwdbg/tb_top_test.scala b/src/test/scala/hwdbg/tb_top_test.scala new file mode 100644 index 0000000..2138b52 --- /dev/null +++ b/src/test/scala/hwdbg/tb_top_test.scala @@ -0,0 +1,58 @@ +/** @file + * tb_top_test.scala + * @author + * Sina Karvandi (sina@hyperdbg.org) + * @brief + * Testbench for hwdbg's top module (with BRAM) + * @details + * @version 0.1 + * @date + * 2024-04-12 + * + * @copyright + * This project is released under the GNU Public License v3. + */ +package hwdbg + +import chisel3._ +import chisel3.experimental.BundleLiterals._ +import chisel3.simulator.EphemeralSimulator._ +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers + +import hwdbg._ +import hwdbg.configs._ + +/** This is an example of how to run hwdbg test within sbt, use: + * {{{ + * testOnly hwdbg.HwdbgTest + * }}} + * From a terminal shell use: + * {{{ + * sbt 'testOnly hwdbg.HwdbgTest' + * }}} + * Testing from mill: + * {{{ + * mill hwdbg.test.testOnly hwdbg.HwdbgTest + * }}} + */ +class HwdbgTest extends AnyFreeSpec with Matchers { + + "Data for the shared block RAM (BRAM) is provided statically" in { + simulate( + new DebuggerModule( + DebuggerConfigurations.ENABLE_DEBUG, + DebuggerConfigurations.NUMBER_OF_INPUT_PINS, + DebuggerConfigurations.NUMBER_OF_OUTPUT_PINS, + DebuggerConfigurations.BLOCK_RAM_ADDR_WIDTH, + DebuggerConfigurations.BLOCK_RAM_DATA_WIDTH + ) + ) { dut => + // dut.reset.poke(true.B) + dut.clock.step() + // dut.reset.poke(false.B) + dut.clock.step() + + } + } +} diff --git a/src/test/tb/.gitignore b/src/test/tb/.gitignore new file mode 100644 index 0000000..85f87a2 --- /dev/null +++ b/src/test/tb/.gitignore @@ -0,0 +1,164 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# cocotb folders and files +sim_build/ +results.xml \ No newline at end of file diff --git a/src/test/tb/Makefile b/src/test/tb/Makefile new file mode 100644 index 0000000..1c521a1 --- /dev/null +++ b/src/test/tb/Makefile @@ -0,0 +1,8 @@ +# Makefile + +TOPLEVEL_LANG = verilog +VERILOG_SOURCES = $(shell pwd)/../../../generated/DebuggerModuleTestingBRAM.sv +TOPLEVEL = DebuggerModuleTestingBRAM +MODULE = test_DebuggerModuleTestingBRAM + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/src/test/tb/run.sh b/src/test/tb/run.sh new file mode 100755 index 0000000..a83ca69 --- /dev/null +++ b/src/test/tb/run.sh @@ -0,0 +1 @@ +make SIM=icarus WAVES=1 diff --git a/src/test/tb/test_DebuggerModuleTestingBRAM.py b/src/test/tb/test_DebuggerModuleTestingBRAM.py new file mode 100644 index 0000000..b6508dd --- /dev/null +++ b/src/test/tb/test_DebuggerModuleTestingBRAM.py @@ -0,0 +1,67 @@ +# test_DebuggerModuleTestingBRAM.py + +import random + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge +from cocotb.types import LogicArray + +@cocotb.test() +async def DebuggerModuleTestingBRAM_test(dut): + """Test that d propagates to q""" + + # Assert initial output is unknown + assert LogicArray(dut.io_outputPin_0.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_1.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_2.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_3.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_4.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_5.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_6.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_7.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_8.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_9.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_10.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_11.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_12.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_13.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_14.value) == LogicArray("X") + assert LogicArray(dut.io_outputPin_15.value) == LogicArray("X") + + # Set initial input value to prevent it from floating + dut.io_inputPin_0.value = 0 + dut.io_inputPin_1.value = 0 + dut.io_inputPin_2.value = 0 + dut.io_inputPin_3.value = 0 + dut.io_inputPin_4.value = 0 + dut.io_inputPin_5.value = 0 + dut.io_inputPin_6.value = 0 + dut.io_inputPin_7.value = 0 + dut.io_inputPin_8.value = 0 + dut.io_inputPin_9.value = 0 + dut.io_inputPin_10.value = 0 + dut.io_inputPin_11.value = 0 + dut.io_inputPin_12.value = 0 + dut.io_inputPin_13.value = 0 + dut.io_inputPin_14.value = 0 + dut.io_inputPin_15.value = 0 + + clock = Clock(dut.clock, 10, units="ns") # Create a 10ns period clock on port clock + + # Start the clock. Start it low to avoid issues on the first RisingEdge + cocotb.start_soon(clock.start(start_high=False)) + + # Synchronize with the clock. This will regisiter the initial `inputPinX` value + await RisingEdge(dut.clock) + + expected_val = 0 # Matches initial input value + for i in range(10): + val = random.randint(0, 1) + dut.io_inputPin_0.value = val # Assign the random value val to the input port d + await RisingEdge(dut.clock) + #assert dut.io_inputPin_0.value == expected_val, f"output q was incorrect on the {i}th cycle" + expected_val = val # Save random value for next RisingEdge + + # Check the final input on the next clock + await RisingEdge(dut.clock)