manta/test/test_io_core_hw.py

157 lines
5.2 KiB
Python
Raw Normal View History

from manta import Manta
from amaranth.lib import io
2023-12-28 23:22:29 +01:00
from amaranth_boards.nexys4ddr import Nexys4DDRPlatform
from amaranth_boards.icestick import ICEStickPlatform
from manta.utils import *
import pytest
from random import getrandbits
import os
2023-12-28 23:22:29 +01:00
class IOCoreLoopbackTest(Elaboratable):
2023-12-28 23:22:29 +01:00
def __init__(self, platform, port):
self.platform = platform
self.port = port
self.config = self.platform_specific_config()
self.manta = Manta(self.config)
2023-12-28 23:22:29 +01:00
def platform_specific_config(self):
return {
"cores": {
"io_core": {
"type": "io",
"inputs": {"probe0": 1, "probe1": 2, "probe2": 8, "probe3": 20},
"outputs": {
"probe4": {"width": 1, "initial_value": 1},
"probe5": {
"width": 2,
"initial_value": 2,
},
"probe6": 8,
"probe7": {"width": 20, "initial_value": 65538},
},
}
},
"uart": {
"port": self.port,
"baudrate": 3e6,
"clock_freq": self.platform.default_clk_frequency,
},
}
2024-02-18 22:50:26 +01:00
def get_probe(self, name):
# This is a hack! And should be removed once the full Amaranth-native
# API is built out
for i in self.manta.io_core._inputs:
if i.name == name:
return i
for o in self.manta.io_core._outputs:
if o.name == name:
return o
return None
2023-12-28 23:22:29 +01:00
def elaborate(self, platform):
m = Module()
m.submodules.manta = self.manta
2023-12-28 23:22:29 +01:00
uart_pins = platform.request("uart", dir={"tx": "-", "rx": "-"})
m.submodules.uart_rx = uart_rx = io.Buffer("i", uart_pins.rx)
m.submodules.uart_tx = uart_tx = io.Buffer("o", uart_pins.tx)
2023-12-28 23:22:29 +01:00
2024-02-18 22:50:26 +01:00
probe0 = self.get_probe("probe0")
probe1 = self.get_probe("probe1")
probe2 = self.get_probe("probe2")
probe3 = self.get_probe("probe3")
probe4 = self.get_probe("probe4")
probe5 = self.get_probe("probe5")
probe6 = self.get_probe("probe6")
probe7 = self.get_probe("probe7")
2023-12-28 23:22:29 +01:00
m.d.comb += [
2024-02-18 22:50:26 +01:00
probe0.eq(probe4),
probe1.eq(probe5),
probe2.eq(probe6),
probe3.eq(probe7),
self.manta.interface.rx.eq(uart_rx.i),
uart_tx.o.eq(self.manta.interface.tx),
2023-12-28 23:22:29 +01:00
]
return m
def build_and_program(self):
self.platform.build(self, do_program=True)
def verify_output_probe_initial_values(self):
"""
Test that all output probes take their expected initial values.
We can't really test for the same of input probes, since the
strobe register pulses every time the get_probe() method is called.
"""
# Test that all output probes take their initial values
inputs = self.config["cores"]["io_core"]["inputs"]
outputs = self.config["cores"]["io_core"]["outputs"]
for name, attrs in outputs.items():
actual = self.manta.io_core.get_probe(name)
2023-12-28 23:22:29 +01:00
if isinstance(attrs, dict):
if "initial_value" in attrs:
expected = attrs["initial_value"]
else:
expected = 0
if actual != expected:
raise ValueError(
f"Output probe {name} took initial value of {actual} instead of {expected}."
)
def verify_probes_update(self):
"""
This design ties all the output probes to input probes, so this
test sets the outputs to random values, and verifies the inputs match
"""
inputs = self.config["cores"]["io_core"]["inputs"]
outputs = self.config["cores"]["io_core"]["outputs"]
# The config is specified in such a way that the first output is
# connected to the first output, the second output is connected
# to the second input, and so on...
for input, output in zip(inputs, outputs):
width = self.config["cores"]["io_core"]["inputs"][input]
value = getrandbits(width)
2023-12-28 23:22:29 +01:00
self.manta.io_core.set_probe(output, value)
readback = self.manta.io_core.get_probe(input)
2023-12-28 23:22:29 +01:00
if readback != value:
raise ValueError(
f"Reading {output} through {input} yielded {readback} instead of {value}!"
)
else:
print(
f"Reading {output} through {input} yielded {readback} as expected."
)
def verify(self):
self.build_and_program()
self.verify_output_probe_initial_values()
self.verify_probes_update()
@pytest.mark.skipif(not xilinx_tools_installed(), reason="no toolchain installed")
def test_output_probe_initial_values_xilinx():
port = os.environ["NEXYS4DDR_PORT"]
IOCoreLoopbackTest(Nexys4DDRPlatform(), port).verify()
2023-12-28 23:22:29 +01:00
@pytest.mark.skipif(not ice40_tools_installed(), reason="no toolchain installed")
def test_output_probe_initial_values_ice40():
port = os.environ["ICESTICK_PORT"]
IOCoreLoopbackTest(ICEStickPlatform(), port).verify()