From 7d5ddaf47c44fcc880b58c1b0e57beec955a384e Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 3 Oct 2021 23:24:16 -0700 Subject: [PATCH] Add first simple test with a simple 2-dimensional array --- tests/Makefile | 7 +- tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py | 140 ++++++++++++++++++ tests/cocotb_tests/libs/__init__.py | 0 tests/cocotb_tests/test_simple_rw_reg.py | 51 +++++++ 4 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py create mode 100644 tests/cocotb_tests/libs/__init__.py create mode 100644 tests/cocotb_tests/test_simple_rw_reg.py diff --git a/tests/Makefile b/tests/Makefile index 76b938a..7e70bb3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,8 @@ ALL_COCOTB_TESTS = $(shell ls cocotb_tests/test_*.py | sed -E 's|.*?/test_(.*?).py|\1|g') +.PHONY: clean +.PRECIOUS: build_dirs/%/compile.f + default: $(ALL_COCOTB_TESTS) # This target will always rebuild, which is fine since we want to be sure to execute any @@ -31,15 +34,13 @@ default: $(ALL_COCOTB_TESTS) @echo "##############################################################################" @echo "##############################################################################" -build_dirs/%/*.sv: systemrdl/%.rdl +build_dirs/%/compile.f: systemrdl/%.rdl ../srdl2sv/main.py $? --out_dir $(shell dirname $@) --file_log_level DEBUG --stream_log_level DEBUG -build_dirs/%/compile.f: build_dirs/%/*.sv ls $(PWD)/$(@D)/*_pkg.sv > $@ ls $(PWD)/$(@D)/*amba*.sv >> $@ ls $(PWD)/$(@D)/*.sv | grep -v '.*_pkg.sv$$' | grep -v '.*amba.*' >> $@ -.PHONY: clean clean: rm -rf build_dirs diff --git a/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py b/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py new file mode 100644 index 0000000..b847407 --- /dev/null +++ b/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py @@ -0,0 +1,140 @@ +from enum import Enum +import math +import cocotb +from cocotb.triggers import Timer, RisingEdge + +# TODO: Does not yet implement HREADY_OUT == 0 +# TODO: Add support for HRESP (and throw error if HRESP occurs) + +class HTRANS(Enum): + IDLE = 0 + BUSY = 1 + NONSEQ = 2 + SEQ = 3 + +class AMBA3AHBLiteDriver: + """Wraps up a collection of functions to drive an AMBA3AHBLite Bus. + + This is not an extensive set of features and merely enough to test + out SRDL2SV registers. + """ + + def __init__(self, dut, nbytes: int): + self._nbytes = nbytes + self._dut = dut + + @cocotb.coroutine + async def reset(self, time: int = 10): + """Resets bus for a given amount of time""" + + self._dut.HRESETn <= 0 + + await Timer(time, units='ns') + await RisingEdge(self._dut.clk) + + self._dut.HRESETn <= 1 + + @cocotb.coroutine + async def write(self, address: int, value: int, nbytes = None, step_size = None): + if not nbytes: + nbytes = self._nbytes + + if not step_size: + if (step_size := address % self._nbytes) == 0: + step_size = self._nbytes + + # Dictionary to return address/value pairs + write_dict = {} + + # Start counting bytes + nbytes_cnt = 0 + + # Initiate write + self._dut.HSEL <= 1 + self._dut.HWRITE <= 1 + self._dut.HADDR <= address + self._dut.HTRANS <= HTRANS.NONSEQ.value + self._dut.HSIZE <= int(math.log2(step_size)) + + await RisingEdge(self._dut.clk) + + while True: + # Save address from previous phase + previous_address = hex(self._dut.HADDR.value) + + # Set data for dataphase + self._dut.HWDATA <= (value >> (nbytes_cnt * 8)) + + # Check if we are done in next phase + if (nbytes_cnt := nbytes_cnt + step_size) >= nbytes: + self._dut.HTRANS <= HTRANS.IDLE.value + else: + # Update address + self._dut.HADDR <= self._dut.HADDR.value + step_size + + # Wait for next clock cycle + await RisingEdge(self._dut.clk) + + # Save into dictionary + write_dict[previous_address] = hex(self._dut.HWDATA.value) + + if nbytes_cnt >= nbytes: + break + + self._dut.HWRITE <= 0 + self._dut.HSEL <= 0 + + await RisingEdge(self._dut.clk) + + return write_dict + + @cocotb.coroutine + async def read(self, address: int, nbytes = None, step_size = None): + if not nbytes: + nbytes = self._nbytes + + if not step_size: + if (step_size := address % self._nbytes) == 0: + step_size = self._nbytes + + # Dictionary to return address/value pairs + read_dict = {} + + # Start counting bytes + nbytes_cnt = 0 + + # Initiate read + self._dut.HSEL <= 1 + self._dut.HWRITE <= 0 + self._dut.HADDR <= address + self._dut.HTRANS <= HTRANS.NONSEQ.value + self._dut.HSIZE <= int(math.log2(step_size)) + + await RisingEdge(self._dut.clk) + + while True: + # Save address from previous phase + previous_address = hex(self._dut.HADDR.value) + + # Check if we are done in next phase + if (nbytes_cnt := nbytes_cnt + step_size) >= nbytes: + self._dut.HTRANS <= HTRANS.IDLE.value + else: + # Update address + self._dut.HADDR <= self._dut.HADDR.value + step_size + + # Wait for next clock cycle + await RisingEdge(self._dut.clk) + + # Save into dictionary + read_dict[previous_address] = hex(self._dut.HRDATA.value) + + if nbytes_cnt >= nbytes: + break + + self._dut.HTRANS <= HTRANS.IDLE.value + self._dut.HSEL <= 0 + + await RisingEdge(self._dut.clk) + + return read_dict diff --git a/tests/cocotb_tests/libs/__init__.py b/tests/cocotb_tests/libs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/cocotb_tests/test_simple_rw_reg.py b/tests/cocotb_tests/test_simple_rw_reg.py new file mode 100644 index 0000000..53bb2d9 --- /dev/null +++ b/tests/cocotb_tests/test_simple_rw_reg.py @@ -0,0 +1,51 @@ +from enum import Enum +from cocotb.clock import Clock +import cocotb +import random + +from libs import AMBA3AHBLiteDriver + +@cocotb.test() +async def test_simple_rw_reg(dut): + """Test writing via the bus and reading back""" + + clock = Clock(dut.clk, 1, units="ns") # Create a 10us period clock on port clk + cocotb.fork(clock.start()) # Start the clock + + bus = AMBA3AHBLiteDriver.AMBA3AHBLiteDriver(dut=dut, nbytes=4) + await bus.reset() + + # Write in 1, 2, and 4 byte steps + for step_size in (1, 2, 4): + dut._log.info(f"Writing in {step_size} steps.") + + write_dict = {} + read_dict = {} + + for addr in range(0, 8, step_size): + rand_val = random.randint(0, (1 << (step_size * 8))-1) + + dut._log.info(f"Write value {rand_val} to addres {addr}.") + + write_dict.update( + await bus.write( + address=addr, + value=rand_val, + nbytes=step_size, + step_size=step_size)) + + for addr in range(0, 8, step_size): + read_dict.update( + await bus.read( + address=addr, + nbytes=step_size, + step_size=step_size)) + + # Check at end of every step_size + dut._log.info(f"Wrote dictionary {write_dict}") + dut._log.info(f"Read back dictionary {read_dict}") + + assert write_dict == read_dict, "Read and write values differ!" + + +