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.
This commit is contained in:
Dennis Potter 2021-10-11 23:49:31 -07:00
parent ed08d4bd35
commit 4ba047dd2a
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
2 changed files with 214 additions and 27 deletions

View File

@ -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

View File

@ -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!"