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):
|
if not all(isinstance(d, int) for d in datas):
|
||||||
raise TypeError("Write data must all be integers.")
|
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)]
|
bus_datas = [word for d in datas for word in value_to_words(d, self._n_mems)]
|
||||||
self._interface.write(bus_addrs, bus_datas)
|
self._interface.write(bus_addrs, bus_datas)
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,11 @@ from random import getrandbits
|
||||||
from math import ceil, log2
|
from math import ceil, log2
|
||||||
import os
|
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):
|
class MemoryCoreLoopbackTest(Elaboratable):
|
||||||
def __init__(self, platform, width, depth, port):
|
def __init__(self, platform, mode, width, depth, port):
|
||||||
self.platform = platform
|
self.platform = platform
|
||||||
|
self.mode = mode
|
||||||
self.width = width
|
self.width = width
|
||||||
self.depth = depth
|
self.depth = depth
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
@ -28,19 +23,22 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
||||||
def platform_specific_config(self):
|
def platform_specific_config(self):
|
||||||
return {
|
return {
|
||||||
"cores": {
|
"cores": {
|
||||||
"mem_core": {
|
|
||||||
"type": "memory",
|
|
||||||
"mode": "fpga_to_host",
|
|
||||||
"width": self.width,
|
|
||||||
"depth": self.depth,
|
|
||||||
},
|
|
||||||
"io_core": {
|
"io_core": {
|
||||||
"type": "io",
|
"type": "io",
|
||||||
"outputs": {
|
"outputs": {
|
||||||
"addr": ceil(log2(self.depth)),
|
"user_addr": ceil(log2(self.depth)),
|
||||||
"data": self.width,
|
"user_data_in": self.width,
|
||||||
"we": 1,
|
"user_write_enable": 1,
|
||||||
},
|
},
|
||||||
|
"inputs": {
|
||||||
|
"user_data_out": self.width,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mem_core": {
|
||||||
|
"type": "memory",
|
||||||
|
"mode": self.mode,
|
||||||
|
"width": self.width,
|
||||||
|
"depth": self.depth,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"uart": {
|
"uart": {
|
||||||
|
|
@ -69,17 +67,21 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
||||||
|
|
||||||
uart_pins = platform.request("uart")
|
uart_pins = platform.request("uart")
|
||||||
|
|
||||||
addr = self.get_probe("addr")
|
user_addr = self.get_probe("user_addr")
|
||||||
data = self.get_probe("data")
|
user_data_in = self.get_probe("user_data_in")
|
||||||
we = self.get_probe("we")
|
user_data_out = self.get_probe("user_data_out")
|
||||||
|
user_write_enable = self.get_probe("user_write_enable")
|
||||||
|
|
||||||
m.d.comb += [
|
m.d.comb += self.manta.interface.rx.eq(uart_pins.rx.i)
|
||||||
self.manta.mem_core.user_addr.eq(addr),
|
m.d.comb += uart_pins.tx.o.eq(self.manta.interface.tx)
|
||||||
self.manta.mem_core.user_data_in.eq(data),
|
m.d.comb += self.manta.mem_core.user_addr.eq(user_addr)
|
||||||
self.manta.mem_core.user_write_enable.eq(we),
|
|
||||||
self.manta.interface.rx.eq(uart_pins.rx.i),
|
if self.mode in ["bidirectional", "fpga_to_host"]:
|
||||||
uart_pins.tx.o.eq(self.manta.interface.tx),
|
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
|
return m
|
||||||
|
|
||||||
|
|
@ -87,48 +89,75 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
||||||
self.platform.build(self, do_program=True)
|
self.platform.build(self, do_program=True)
|
||||||
|
|
||||||
def write_user_side(self, addr, data):
|
def write_user_side(self, addr, data):
|
||||||
self.manta.io_core.set_probe("we", 0)
|
self.manta.io_core.set_probe("user_write_enable", 0)
|
||||||
self.manta.io_core.set_probe("addr", addr)
|
self.manta.io_core.set_probe("user_addr", addr)
|
||||||
self.manta.io_core.set_probe("data", data)
|
self.manta.io_core.set_probe("user_data_in", data)
|
||||||
self.manta.io_core.set_probe("we", 1)
|
self.manta.io_core.set_probe("user_write_enable", 1)
|
||||||
self.manta.io_core.set_probe("we", 0)
|
self.manta.io_core.set_probe("user_write_enable", 0)
|
||||||
|
|
||||||
def verify_register(self, addr, expected_data):
|
def read_user_side(self, addr):
|
||||||
data = self.manta.mem_core.read(addr)
|
self.manta.io_core.set_probe("user_write_enable", 0)
|
||||||
|
self.manta.io_core.set_probe("user_addr", addr)
|
||||||
if data != expected_data:
|
return self.manta.io_core.get_probe("user_data_out")
|
||||||
raise ValueError(
|
|
||||||
f"Memory read from {hex(addr)} returned {hex(data)} instead of {hex(expected_data)}."
|
|
||||||
)
|
|
||||||
|
|
||||||
def verify(self):
|
def verify(self):
|
||||||
self.build_and_program()
|
self.build_and_program()
|
||||||
|
|
||||||
# Read and write randomly from the bus side
|
if self.mode in ["bidirectional", "host_to_fpga"]:
|
||||||
for addr in jumble(range(self.depth)):
|
for addr in jumble(range(self.depth)):
|
||||||
data = getrandbits(self.width)
|
|
||||||
self.write_user_side(addr, data)
|
# Write a random balue to a random bus address
|
||||||
self.verify_register(addr, data)
|
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")
|
@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"]
|
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")
|
@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"]
|
port = os.environ["ICESTICK_PORT"]
|
||||||
MemoryCoreLoopbackTest(ICEStickPlatform(), 1, 2, port).verify()
|
MemoryCoreLoopbackTest(ICEStickPlatform(), mode, width, depth, 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()
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from manta.memory_core import MemoryCore
|
||||||
from manta.utils import *
|
from manta.utils import *
|
||||||
from random import randint, choice, getrandbits
|
from random import randint, choice, getrandbits
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class MemoryCoreTests:
|
class MemoryCoreTests:
|
||||||
|
|
@ -258,125 +259,39 @@ class MemoryCoreTests:
|
||||||
yield self.mem_core.user_write_enable.eq(0)
|
yield self.mem_core.user_write_enable.eq(0)
|
||||||
|
|
||||||
|
|
||||||
def test_bidirectional():
|
modes = ["bidirectional", "fpga_to_host", "host_to_fpga"]
|
||||||
mem_core = MemoryCore(
|
widths = [23, randint(0, 128)]
|
||||||
mode="bidirectional",
|
depths = [512, randint(0, 1024)]
|
||||||
width=23,
|
base_addrs = [0, randint(0, 32678)]
|
||||||
depth=512,
|
|
||||||
base_addr=0,
|
cases = [
|
||||||
interface=None,
|
(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)
|
tests = MemoryCoreTests(mem_core)
|
||||||
|
|
||||||
@simulate(mem_core)
|
@simulate(mem_core)
|
||||||
def test_bidirectional_testbench():
|
def testbench():
|
||||||
yield from tests.bus_addrs_all_zero()
|
if mode == "bidirectional":
|
||||||
yield from tests.user_addrs_all_zero()
|
yield from tests.bus_addrs_all_zero()
|
||||||
|
yield from tests.user_addrs_all_zero()
|
||||||
|
|
||||||
yield from tests.bus_to_bus_functionality()
|
yield from tests.bus_to_bus_functionality()
|
||||||
yield from tests.user_to_bus_functionality()
|
yield from tests.user_to_bus_functionality()
|
||||||
yield from tests.bus_to_user_functionality()
|
yield from tests.bus_to_user_functionality()
|
||||||
yield from tests.user_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():
|
testbench()
|
||||||
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()
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue