Skip to content

Commit

Permalink
Include support for 64-bit slave
Browse files Browse the repository at this point in the history
Signed-off-by: Anderson Ignacio <anderson@aignacio.com>
  • Loading branch information
aignacio committed Dec 18, 2023
1 parent a657e3b commit 01a5c1c
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 12 deletions.
30 changes: 18 additions & 12 deletions cocotbext/ahb/ahb_slave.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,18 @@ def _chk_wr(self, addr: int, size: AHBSize) -> bool:
return False
return True

def _get_addr_aligned(self, addr: int) -> int:
if self.bus._data_width == 32:
if self.bus._addr_width == 32:
return addr & 0xFFFF_FFFC
elif self.bus._addr_width == 64:
return addr & 0xFFFF_FFFF_FFFF_FFFC
elif self.bus._data_width == 64:
if self.bus._addr_width == 32:
return addr & 0xFFFF_FFF8
elif self.bus._addr_width == 64:
return addr & 0xFFFF_FFFF_FFFF_FFF8

def _rd(self, addr: int, size: AHBSize) -> int:
data = self.memory.read(addr, 2**size)
data = int.from_bytes(data, byteorder="little")
Expand All @@ -255,18 +267,15 @@ def _wr(self, addr: int, size: AHBSize, value: BinaryValue) -> int:
byte_sel = addr & 0x7

# Get the (d)word aligned addr
if self.bus._data_width == 32:
addr_aligned = addr & 0xFFFF_FFFC
elif self.bus._data_width == 64:
addr_aligned = addr & 0xFFFF_FFF8
addr_aligned = self._get_addr_aligned(addr)

# Get the (d)word data from the memory
if self.bus._data_width == 32:
mem_data = self.memory.read(addr_aligned, 4)
elif self.bus._data_width == 64:
mem_data = self.memory.read(addr_aligned, 8)

mem_data = int.from_bytes(mem_data, byteorder='little')
mem_data = int.from_bytes(mem_data, byteorder="little")

# Zero the N-th byte
mem_data = ~(0xFF << (byte_sel * 8)) & mem_data
Expand Down Expand Up @@ -296,18 +305,15 @@ def _wr(self, addr: int, size: AHBSize, value: BinaryValue) -> int:
raise AssertionError("Half-word write addr LSB has to be 0x0")

# Get the (d)word aligned addr
if self.bus._data_width == 32:
addr_aligned = addr & 0xFFFF_FFFC
elif self.bus._data_width == 64:
addr_aligned = addr & 0xFFFF_FFF8
addr_aligned = self._get_addr_aligned(addr)

# Get the (d)word data from the memory
if self.bus._data_width == 32:
mem_data = self.memory.read(addr_aligned, 4)
elif self.bus._data_width == 64:
mem_data = self.memory.read(addr_aligned, 8)

mem_data = int.from_bytes(mem_data, byteorder='little')
mem_data = int.from_bytes(mem_data, byteorder="little")

# Zero the N-th h-word
mem_data = ~(0xFFFF << (hword_sel * 8)) & mem_data
Expand Down Expand Up @@ -342,12 +348,12 @@ def _wr(self, addr: int, size: AHBSize, value: BinaryValue) -> int:
raise AssertionError("Word write addr LSBs have to be 0x0")

# Get the (d)word aligned addr
addr_aligned = addr & 0xFFFF_FFF8
addr_aligned = self._get_addr_aligned(addr)

# Get the (d)word data from the memory
mem_data = self.memory.read(addr_aligned, 8)

mem_data = int.from_bytes(mem_data, byteorder='little')
mem_data = int.from_bytes(mem_data, byteorder="little")

# Zero the N-th word
mem_data = ~(0xFFFF_FFFF << (word_sel * 8)) & mem_data
Expand Down
164 changes: 164 additions & 0 deletions tests/test_ahb_lite_sram_all_sizes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# -*- coding: utf-8 -*-
# File : test_ahb_lite_sram_all_sizes.py
# License : MIT license <Check LICENSE>
# Author : Anderson I. da Silva (aignacio) <anderson@aignacio.com>
# Date : 08.10.2023
# Last Modified Date: 18.12.2023

import cocotb
import os
import random
import math
import pytest

from const import cfg
from cocotb_test.simulator import run
from cocotb.triggers import ClockCycles
from cocotb.clock import Clock
from cocotbext.ahb import AHBBus, AHBLiteMaster, AHBLiteSlaveRAM, AHBResp, AHBMonitor
from cocotb.regression import TestFactory


def rnd_val(bit: int = 0, zero: bool = True):
if zero is True:
return random.randint(0, (2**bit) - 1)
else:
return random.randint(1, (2**bit) - 1)


def pick_random_value(input_list):
if input_list:
return random.choice(input_list)
else:
return None # Return None if the list is empty


def slave_back_pressure_generator():
while True:
yield pick_random_value([False, True])


def slave_no_back_pressure_generator():
while True:
yield True


async def setup_dut(dut, cycles):
cocotb.start_soon(Clock(dut.hclk, *cfg.CLK_100MHz).start())
dut.hresetn.value = 0
await ClockCycles(dut.hclk, cycles)
dut.hresetn.value = 1


@cocotb.test()
async def run_test(dut, bp_fn=None, pip_mode=False):
mem_size_kib = 16
N = 1000

ahb_bus_slave = AHBBus.from_entity(dut)

data_width = ahb_bus_slave.data_width

await setup_dut(dut, cfg.RST_CYCLES)

ahb_lite_mon = AHBMonitor(ahb_bus_slave, dut.hclk, dut.hresetn)

# Below is only required bc of flake8 - non-used rule
type(ahb_lite_mon)

ahb_lite_sram = AHBLiteSlaveRAM(
AHBBus.from_entity(dut),
dut.hclk,
dut.hresetn,
def_val=0,
bp=bp_fn,
mem_size=mem_size_kib * 1024,
)

# Below is only required bc of flake8 - non-used rule
type(ahb_lite_sram)

ahb_lite_master = AHBLiteMaster(
AHBBus.from_entity(dut), dut.hclk, dut.hresetn, def_val="Z"
)

# Generate a list of unique addresses with the double of memory size
# to create error responses
address = random.sample(range(0, 2 * mem_size_kib * 1024, 8), N)
# Generate a list of random 32-bit values
value = [rnd_val(data_width) for _ in range(N)]
# Generate a list of random sizes
if data_width == 32:
size = [pick_random_value([1, 2, 4]) for _ in range(N)]
else:
size = [pick_random_value([1, 2, 4, 8]) for _ in range(N)]

# Create the comparison list with expected results
expected = []
for addr, val, sz in zip(address, value, size):
resp, data = 0, 0
if addr >= mem_size_kib * 1024:
resp = AHBResp.ERROR
else:
resp = AHBResp.OKAY
if sz == 1:
data = val & 0xFF
elif sz == 2:
data = val & 0xFFFF
elif sz == 4:
data = val & 0xFFFFFFFF
elif sz == 8:
data = val & 0xFFFFFFFFFFFFFFFF
expected.append({"resp": resp, "data": hex(data)})

# Perform the writes and reads
resp = await ahb_lite_master.write(address, value, size, pip=pip_mode)
resp = await ahb_lite_master.read(address, size, pip=pip_mode)
print(resp)
# Compare all txns
for index, (real, expect) in enumerate(zip(resp, expected)):
if real != expect:
print("------ERROR------")
print(f"Txn ID: {index}")
print("DUT")
print(real)
print("Expected")
print(expect)
assert real == expect, "DUT != Expected"


if cocotb.SIM_NAME:
factory = TestFactory(run_test)
factory.add_option(
"bp_fn", [slave_back_pressure_generator(), slave_no_back_pressure_generator()]
)
factory.add_option("pip_mode", [False, True])
factory.generate_tests()


@pytest.mark.parametrize("data_width", [{"DATA_WIDTH": "32"}, {"DATA_WIDTH": "64"}])
def test_ahb_lite_sram_all_sizes(data_width):
"""
Test AHB lite SRAM
Test ID: 3
"""
module = os.path.splitext(os.path.basename(__file__))[0]
SIM_BUILD = os.path.join(
cfg.TESTS_DIR,
f"../run_dir/sim_build_{cfg.SIMULATOR}_{module}_data_width_{data_width['DATA_WIDTH']}_bits",
)
extra_args_sim = cfg.EXTRA_ARGS

run(
python_search=[cfg.TESTS_DIR],
verilog_sources=cfg.VERILOG_SOURCES,
toplevel=cfg.TOPLEVEL,
module=module,
parameters=data_width,
sim_build=SIM_BUILD,
extra_args=extra_args_sim,
extra_env=cfg.EXTRA_ENV,
timescale=cfg.TIMESCALE,
waves=1,
)

0 comments on commit 01a5c1c

Please sign in to comment.