add parameterized HW tests for all memory core modes
This commit is contained in:
parent
0b2c286075
commit
71ec1174d1
|
|
@ -317,6 +317,6 @@ class MemoryCore(MantaCore):
|
|||
if not all(isinstance(d, int) for d in datas):
|
||||
raise TypeError("Write data must all be integers.")
|
||||
|
||||
bus_addrs = self._convert_user_to_bus_addr([addrs])[0]
|
||||
bus_addrs = self._convert_user_to_bus_addr(addrs)
|
||||
bus_datas = [word for d in datas for word in value_to_words(d, self._n_mems)]
|
||||
self._interface.write(bus_addrs, bus_datas)
|
||||
|
|
|
|||
|
|
@ -8,16 +8,11 @@ from random import getrandbits
|
|||
from math import ceil, log2
|
||||
import os
|
||||
|
||||
"""
|
||||
Fundamentally we want a function to generate a configuration (as a dictionary)
|
||||
for a memory core given the width, depth, and platform. This could be a random
|
||||
configuration, or a standard one.
|
||||
"""
|
||||
|
||||
|
||||
class MemoryCoreLoopbackTest(Elaboratable):
|
||||
def __init__(self, platform, width, depth, port):
|
||||
def __init__(self, platform, mode, width, depth, port):
|
||||
self.platform = platform
|
||||
self.mode = mode
|
||||
self.width = width
|
||||
self.depth = depth
|
||||
self.port = port
|
||||
|
|
@ -28,19 +23,22 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
|||
def platform_specific_config(self):
|
||||
return {
|
||||
"cores": {
|
||||
"mem_core": {
|
||||
"type": "memory",
|
||||
"mode": "fpga_to_host",
|
||||
"width": self.width,
|
||||
"depth": self.depth,
|
||||
},
|
||||
"io_core": {
|
||||
"type": "io",
|
||||
"outputs": {
|
||||
"addr": ceil(log2(self.depth)),
|
||||
"data": self.width,
|
||||
"we": 1,
|
||||
"user_addr": ceil(log2(self.depth)),
|
||||
"user_data_in": self.width,
|
||||
"user_write_enable": 1,
|
||||
},
|
||||
"inputs": {
|
||||
"user_data_out": self.width,
|
||||
},
|
||||
},
|
||||
"mem_core": {
|
||||
"type": "memory",
|
||||
"mode": self.mode,
|
||||
"width": self.width,
|
||||
"depth": self.depth,
|
||||
},
|
||||
},
|
||||
"uart": {
|
||||
|
|
@ -69,17 +67,21 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
|||
|
||||
uart_pins = platform.request("uart")
|
||||
|
||||
addr = self.get_probe("addr")
|
||||
data = self.get_probe("data")
|
||||
we = self.get_probe("we")
|
||||
user_addr = self.get_probe("user_addr")
|
||||
user_data_in = self.get_probe("user_data_in")
|
||||
user_data_out = self.get_probe("user_data_out")
|
||||
user_write_enable = self.get_probe("user_write_enable")
|
||||
|
||||
m.d.comb += [
|
||||
self.manta.mem_core.user_addr.eq(addr),
|
||||
self.manta.mem_core.user_data_in.eq(data),
|
||||
self.manta.mem_core.user_write_enable.eq(we),
|
||||
self.manta.interface.rx.eq(uart_pins.rx.i),
|
||||
uart_pins.tx.o.eq(self.manta.interface.tx),
|
||||
]
|
||||
m.d.comb += self.manta.interface.rx.eq(uart_pins.rx.i)
|
||||
m.d.comb += uart_pins.tx.o.eq(self.manta.interface.tx)
|
||||
m.d.comb += self.manta.mem_core.user_addr.eq(user_addr)
|
||||
|
||||
if self.mode in ["bidirectional", "fpga_to_host"]:
|
||||
m.d.comb += self.manta.mem_core.user_data_in.eq(user_data_in)
|
||||
m.d.comb += self.manta.mem_core.user_write_enable.eq(user_write_enable)
|
||||
|
||||
if self.mode in ["bidirectional", "host_to_fpga"]:
|
||||
m.d.comb += user_data_out.eq(self.manta.mem_core.user_data_out)
|
||||
|
||||
return m
|
||||
|
||||
|
|
@ -87,48 +89,75 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
|||
self.platform.build(self, do_program=True)
|
||||
|
||||
def write_user_side(self, addr, data):
|
||||
self.manta.io_core.set_probe("we", 0)
|
||||
self.manta.io_core.set_probe("addr", addr)
|
||||
self.manta.io_core.set_probe("data", data)
|
||||
self.manta.io_core.set_probe("we", 1)
|
||||
self.manta.io_core.set_probe("we", 0)
|
||||
self.manta.io_core.set_probe("user_write_enable", 0)
|
||||
self.manta.io_core.set_probe("user_addr", addr)
|
||||
self.manta.io_core.set_probe("user_data_in", data)
|
||||
self.manta.io_core.set_probe("user_write_enable", 1)
|
||||
self.manta.io_core.set_probe("user_write_enable", 0)
|
||||
|
||||
def verify_register(self, addr, expected_data):
|
||||
data = self.manta.mem_core.read(addr)
|
||||
|
||||
if data != expected_data:
|
||||
raise ValueError(
|
||||
f"Memory read from {hex(addr)} returned {hex(data)} instead of {hex(expected_data)}."
|
||||
)
|
||||
def read_user_side(self, addr):
|
||||
self.manta.io_core.set_probe("user_write_enable", 0)
|
||||
self.manta.io_core.set_probe("user_addr", addr)
|
||||
return self.manta.io_core.get_probe("user_data_out")
|
||||
|
||||
def verify(self):
|
||||
self.build_and_program()
|
||||
|
||||
# Read and write randomly from the bus side
|
||||
for addr in jumble(range(self.depth)):
|
||||
data = getrandbits(self.width)
|
||||
self.write_user_side(addr, data)
|
||||
self.verify_register(addr, data)
|
||||
if self.mode in ["bidirectional", "host_to_fpga"]:
|
||||
for addr in jumble(range(self.depth)):
|
||||
|
||||
# Write a random balue to a random bus address
|
||||
data = getrandbits(self.width)
|
||||
self.manta.mem_core.write(addr, data)
|
||||
|
||||
# Verify the same number is returned when reading on the user side
|
||||
readback = self.read_user_side(addr)
|
||||
if readback != data:
|
||||
raise ValueError(
|
||||
f"Memory read from {hex(addr)} returned {hex(data)} instead of {hex(readback)}."
|
||||
)
|
||||
|
||||
if self.mode in ["bidirectional", "fpga_to_host"]:
|
||||
for addr in jumble(range(self.depth)):
|
||||
|
||||
# Write a random value to a random user address
|
||||
data = getrandbits(self.width)
|
||||
self.write_user_side(addr, data)
|
||||
|
||||
# Verify the same number is returned when reading on the bus side
|
||||
readback = self.manta.mem_core.read(addr)
|
||||
if readback != data:
|
||||
raise ValueError(
|
||||
f"Memory read from {hex(addr)} returned {hex(data)} instead of {hex(readback)}."
|
||||
)
|
||||
|
||||
|
||||
# Nexys4DDR Tests
|
||||
|
||||
# Omit the bidirectional mode for now, pending completion of:
|
||||
# https://github.com/amaranth-lang/amaranth/issues/1011
|
||||
modes = ["fpga_to_host", "host_to_fpga"]
|
||||
widths = [1, 8, 14, 16, 33]
|
||||
depths = [2, 512, 1024]
|
||||
nexys4ddr_cases = [(m, w, d) for m in modes for w in widths for d in depths]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not xilinx_tools_installed(), reason="no toolchain installed")
|
||||
def test_mem_core_xilinx():
|
||||
@pytest.mark.parametrize("mode, width, depth", nexys4ddr_cases)
|
||||
def test_mem_core_xilinx(mode, width, depth):
|
||||
port = os.environ["NEXYS4DDR_PORT"]
|
||||
MemoryCoreLoopbackTest(Nexys4DDRPlatform(), 33, 1024, port).verify()
|
||||
MemoryCoreLoopbackTest(Nexys4DDRPlatform(), mode, width, depth, port).verify()
|
||||
|
||||
|
||||
# IceStick Tests
|
||||
modes = ["fpga_to_host", "host_to_fpga"]
|
||||
widths = [1, 8, 14, 16, 33]
|
||||
depths = [2, 512, 1024]
|
||||
ice40_cases = [(m, w, d) for m in modes for w in widths for d in depths]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not ice40_tools_installed(), reason="no toolchain installed")
|
||||
def test_mem_core_ice40():
|
||||
@pytest.mark.parametrize("mode, width, depth", ice40_cases)
|
||||
def test_mem_core_ice40(mode, width, depth):
|
||||
port = os.environ["ICESTICK_PORT"]
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 1, 2, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 1, 512, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 1, 1024, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 8, 2, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 8, 512, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 8, 1024, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 14, 512, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 14, 1024, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 16, 512, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 16, 1024, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 33, 512, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 33, 1024, port).verify()
|
||||
MemoryCoreLoopbackTest(ICEStickPlatform(), mode, width, depth, port).verify()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from manta.memory_core import MemoryCore
|
|||
from manta.utils import *
|
||||
from random import randint, choice, getrandbits
|
||||
from math import ceil
|
||||
import pytest
|
||||
|
||||
|
||||
class MemoryCoreTests:
|
||||
|
|
@ -258,125 +259,39 @@ class MemoryCoreTests:
|
|||
yield self.mem_core.user_write_enable.eq(0)
|
||||
|
||||
|
||||
def test_bidirectional():
|
||||
mem_core = MemoryCore(
|
||||
mode="bidirectional",
|
||||
width=23,
|
||||
depth=512,
|
||||
base_addr=0,
|
||||
interface=None,
|
||||
)
|
||||
modes = ["bidirectional", "fpga_to_host", "host_to_fpga"]
|
||||
widths = [23, randint(0, 128)]
|
||||
depths = [512, randint(0, 1024)]
|
||||
base_addrs = [0, randint(0, 32678)]
|
||||
|
||||
cases = [
|
||||
(m, w, d, ba) for m in modes for w in widths for d in depths for ba in base_addrs
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mode, width, depth, base_addr", cases)
|
||||
def test_mem_core(mode, width, depth, base_addr):
|
||||
mem_core = MemoryCore(mode, width, depth, base_addr, interface=None)
|
||||
|
||||
tests = MemoryCoreTests(mem_core)
|
||||
|
||||
@simulate(mem_core)
|
||||
def test_bidirectional_testbench():
|
||||
yield from tests.bus_addrs_all_zero()
|
||||
yield from tests.user_addrs_all_zero()
|
||||
def testbench():
|
||||
if mode == "bidirectional":
|
||||
yield from tests.bus_addrs_all_zero()
|
||||
yield from tests.user_addrs_all_zero()
|
||||
|
||||
yield from tests.bus_to_bus_functionality()
|
||||
yield from tests.user_to_bus_functionality()
|
||||
yield from tests.bus_to_user_functionality()
|
||||
yield from tests.user_to_user_functionality()
|
||||
yield from tests.bus_to_bus_functionality()
|
||||
yield from tests.user_to_bus_functionality()
|
||||
yield from tests.bus_to_user_functionality()
|
||||
yield from tests.user_to_user_functionality()
|
||||
|
||||
test_bidirectional_testbench()
|
||||
if mode == "fpga_to_host":
|
||||
yield from tests.bus_addrs_all_zero()
|
||||
yield from tests.user_to_bus_functionality()
|
||||
|
||||
if mode == "host_to_fpga":
|
||||
yield from tests.user_addrs_all_zero()
|
||||
yield from tests.bus_to_user_functionality()
|
||||
|
||||
def test_bidirectional_random():
|
||||
mem_core = MemoryCore(
|
||||
mode="bidirectional",
|
||||
width=randint(0, 128),
|
||||
depth=randint(0, 1024),
|
||||
base_addr=randint(0, 32678),
|
||||
interface=None,
|
||||
)
|
||||
|
||||
tests = MemoryCoreTests(mem_core)
|
||||
|
||||
@simulate(mem_core)
|
||||
def test_bidirectional_random_testbench():
|
||||
yield from tests.bus_addrs_all_zero()
|
||||
yield from tests.user_addrs_all_zero()
|
||||
|
||||
yield from tests.bus_to_bus_functionality()
|
||||
yield from tests.user_to_bus_functionality()
|
||||
yield from tests.bus_to_user_functionality()
|
||||
yield from tests.user_to_user_functionality()
|
||||
|
||||
test_bidirectional_random_testbench()
|
||||
|
||||
|
||||
def test_fpga_to_host():
|
||||
mem_core = MemoryCore(
|
||||
mode="fpga_to_host",
|
||||
width=23,
|
||||
depth=512,
|
||||
base_addr=0,
|
||||
interface=None,
|
||||
)
|
||||
|
||||
tests = MemoryCoreTests(mem_core)
|
||||
|
||||
@simulate(mem_core)
|
||||
def test_fpga_to_host_testbench():
|
||||
yield from tests.bus_addrs_all_zero()
|
||||
yield from tests.user_to_bus_functionality()
|
||||
|
||||
test_fpga_to_host_testbench()
|
||||
|
||||
|
||||
def test_fpga_to_host_random():
|
||||
mem_core = MemoryCore(
|
||||
mode="fpga_to_host",
|
||||
width=randint(0, 128),
|
||||
depth=randint(0, 1024),
|
||||
base_addr=randint(0, 32678),
|
||||
interface=None,
|
||||
)
|
||||
|
||||
tests = MemoryCoreTests(mem_core)
|
||||
|
||||
@simulate(mem_core)
|
||||
def test_fpga_to_host_random_testbench():
|
||||
yield from tests.bus_addrs_all_zero()
|
||||
yield from tests.user_to_bus_functionality()
|
||||
|
||||
test_fpga_to_host_random_testbench()
|
||||
|
||||
|
||||
def test_host_to_fpga():
|
||||
mem_core = MemoryCore(
|
||||
mode="host_to_fpga",
|
||||
width=23,
|
||||
depth=512,
|
||||
base_addr=0,
|
||||
interface=None,
|
||||
)
|
||||
|
||||
tests = MemoryCoreTests(mem_core)
|
||||
|
||||
@simulate(mem_core)
|
||||
def test_host_to_fpga_testbench():
|
||||
yield from tests.user_addrs_all_zero()
|
||||
yield from tests.bus_to_user_functionality()
|
||||
|
||||
test_host_to_fpga_testbench()
|
||||
|
||||
|
||||
def test_host_to_fpga_random():
|
||||
mem_core = MemoryCore(
|
||||
mode="host_to_fpga",
|
||||
width=randint(0, 128),
|
||||
depth=randint(0, 1024),
|
||||
base_addr=randint(0, 32678),
|
||||
interface=None,
|
||||
)
|
||||
|
||||
tests = MemoryCoreTests(mem_core)
|
||||
|
||||
@simulate(mem_core)
|
||||
def test_host_to_fpga_random_testbench():
|
||||
yield from tests.user_addrs_all_zero()
|
||||
yield from tests.bus_to_user_functionality()
|
||||
|
||||
test_host_to_fpga_random_testbench()
|
||||
testbench()
|
||||
|
|
|
|||
Loading…
Reference in New Issue