From 4ba047dd2ad21d929f85a554672b06943e499d35 Mon Sep 17 00:00:00 2001 From: Dennis Date: Mon, 11 Oct 2021 23:49:31 -0700 Subject: [PATCH] Extend test_simple_rw_reg with 3 more tests The following tests are now included: - Check access to registers over AHB bus - Check access to register over HW interface - Check access to register over HW interface if hw_wr-input is disabled. - Check if the slave responds with a correct error sequence if an illegal address is accessed. --- tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py | 102 +++++++++---- tests/cocotb_tests/test_simple_rw_reg.py | 139 +++++++++++++++++- 2 files changed, 214 insertions(+), 27 deletions(-) diff --git a/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py b/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py index b847407..8f04e65 100644 --- a/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py +++ b/tests/cocotb_tests/libs/AMBA3AHBLiteDriver.py @@ -3,8 +3,14 @@ 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 BusErrorResponse(Exception): + pass + +class WrongErrorSequence(Exception): + pass + +class WrongHREADYOUTSequence(Exception): + pass class HTRANS(Enum): IDLE = 0 @@ -59,24 +65,47 @@ class AMBA3AHBLiteDriver: await RisingEdge(self._dut.clk) while True: - # Save address from previous phase - previous_address = hex(self._dut.HADDR.value) + if self._dut.HREADYOUT.value: + # Save address from previous phase + previous_address = int(self._dut.HADDR.value) - # Set data for dataphase - self._dut.HWDATA <= (value >> (nbytes_cnt * 8)) + # 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 + # 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] = int(self._dut.HWDATA.value) + + # If HREADYOUT == 0 immediately after the first address phase + # this is illegal + elif nbytes_cnt == 0: + raise WrongHREADYOUTSequence + # If the slave is not yet ready, just wait else: - # Update address - self._dut.HADDR <= self._dut.HADDR.value + step_size + await RisingEdge(self._dut.clk) + continue - # Wait for next clock cycle - await RisingEdge(self._dut.clk) + # Check for error condition + if self._dut.HRESP.value: + if self._dut.HREADYOUT.value: + raise WrongErrorSequence + + await RisingEdge(self._dut.clk) + + if self._dut.HREADYOUT.value: + raise BusErrorResponse + + raise WrongErrorSequence - # Save into dictionary - write_dict[previous_address] = hex(self._dut.HWDATA.value) if nbytes_cnt >= nbytes: break @@ -113,21 +142,42 @@ class AMBA3AHBLiteDriver: await RisingEdge(self._dut.clk) while True: - # Save address from previous phase - previous_address = hex(self._dut.HADDR.value) + if self._dut.HREADYOUT.value: + # Save address from previous phase + previous_address = int(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 + # 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] = int(self._dut.HRDATA.value) + # If HREADYOUT == 0 immediately after the first address phase + # this is illegal + elif nbytes_cnt == 0: + raise WrongHREADYOUTSequence + # If the slave is not yet ready, just wait else: - # Update address - self._dut.HADDR <= self._dut.HADDR.value + step_size + await RisingEdge(self._dut.clk) + continue - # Wait for next clock cycle - await RisingEdge(self._dut.clk) + # Check for error condition + if self._dut.HRESP.value: + if self._dut.HREADYOUT.value: + raise WrongErrorSequence - # Save into dictionary - read_dict[previous_address] = hex(self._dut.HRDATA.value) + await RisingEdge(self._dut.clk) + + if self._dut.HREADYOUT.value: + raise BusErrorResponse + + raise WrongErrorSequence if nbytes_cnt >= nbytes: break diff --git a/tests/cocotb_tests/test_simple_rw_reg.py b/tests/cocotb_tests/test_simple_rw_reg.py index 53bb2d9..e649d3f 100644 --- a/tests/cocotb_tests/test_simple_rw_reg.py +++ b/tests/cocotb_tests/test_simple_rw_reg.py @@ -1,12 +1,13 @@ from enum import Enum from cocotb.clock import Clock +from cocotb.triggers import RisingEdge import cocotb import random from libs import AMBA3AHBLiteDriver @cocotb.test() -async def test_simple_rw_reg(dut): +async def test_ahb_access(dut): """Test writing via the bus and reading back""" clock = Clock(dut.clk, 1, units="ns") # Create a 10us period clock on port clk @@ -47,5 +48,141 @@ async def test_simple_rw_reg(dut): assert write_dict == read_dict, "Read and write values differ!" +@cocotb.test() +async def test_hw_access(dut): + """Test writing via the hardware interface + and reading it 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_dict = {} + read_dict = {} + + # TODO: At this point, CocoTB has issues with single dimension unpacked but + # multidimensional packed arrays. Only check first dimension + dut.register_0__f1_hw_wr <= 1 + dut.register_0__f2_hw_wr <= 1 + + rand_val = [] + + for addr in (0, 2): + # Save value that was written in dictionary + write_dict[addr] = random.randint(0, (1 << 16)-1) + + dut.register_0__f2_in <= [write_dict[2], 0] #, write_dict[6]] + dut.register_0__f1_in <= [write_dict[0], 0] #, write_dict[4]] + + await RisingEdge(dut.clk) + + dut.register_0__f1_hw_wr <= 0 + dut.register_0__f2_hw_wr <= 0 + + for addr in range(0, 4, 2): + read_dict.update( + await bus.read( + address=addr, + nbytes=2, + step_size=2)) + + 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!" + +@cocotb.test() +async def test_hw_access_hw_wr_inactive(dut): + """Test writing via the hardware interface but + keeping the write-enable 0. The value that is + read back should *not* be the same as the value + that was fed by the testbench. + """ + + 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_dict = {} + read_dict = {} + + # Force initial value + dut.register_0__f1_q <= [0, 0] + dut.register_0__f2_q <= [0, 0] + + # Disable write + dut.register_0__f1_hw_wr <= 0 + dut.register_0__f2_hw_wr <= 0 + + rand_val = [] + + for addr in (0, 2, 4, 6): + # Save value that was written in dictionary + write_dict[addr] = random.randint(0, (1 << 16)-1) + + dut.register_0__f2_in <= [write_dict[2], write_dict[6]] + dut.register_0__f1_in <= [write_dict[0], write_dict[4]] + + await RisingEdge(dut.clk) + + dut.register_0__f1_hw_wr <= 0 + dut.register_0__f2_hw_wr <= 0 + + for addr in range(0, 8, 2): + read_dict.update( + await bus.read( + address=addr, + nbytes=2, + step_size=2)) + + 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!" + +@cocotb.test() +async def test_illegal_address(dut): + """Test reading and writing to an illegal address. + The logic should return a correct error sequence. + """ + + 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() + + rand_addr = random.randint(8, 1337) + rand_val = random.randint(0, (1 << 32)-1) + + dut._log.info(f"Write value {rand_val} to illegal addres {rand_addr}.") + + write_error = False + + try: + await bus.write( + address=rand_addr, + value=rand_val, + nbytes=4, + step_size=4) + except AMBA3AHBLiteDriver.BusErrorResponse: + write_error = True + + assert write_error == True, "Write to illegal address did not return an error!" + + read_error = False + + try: + await bus.read( + address=rand_addr, + nbytes=4, + step_size=4) + except AMBA3AHBLiteDriver.BusErrorResponse: + read_error = True + + assert read_error == True, "Read from illegal address did not return an error!"