manta/test/test_io_core_hw.py

119 lines
4.0 KiB
Python
Raw Normal View History

2024-08-03 22:18:58 +02:00
from amaranth import *
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
2024-08-03 22:18:58 +02:00
from manta import *
2023-12-28 23:22:29 +01:00
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
def elaborate(self, platform):
# Since we know that all the tests will be called only after the FPGA
# is programmed, we can just push all the wiring into the elaborate
# method, instead of needing to define Manta in the __init__() method
probe0 = Signal()
probe1 = Signal(2)
probe2 = Signal(8)
probe3 = Signal(20)
probe4 = Signal(init=1)
probe5 = Signal(2, init=2)
probe6 = Signal(8)
probe7 = Signal(20, init=65538)
self.manta = manta = Manta()
manta.cores.io = IOCore(
inputs=[probe0, probe1, probe2, probe3],
outputs=[probe4, probe5, probe6, probe7],
)
manta.interface = UARTInterface(
port=self.port, baudrate=3e6, clock_freq=platform.default_clk_frequency
)
2023-12-28 23:22:29 +01:00
m = Module()
m.submodules.manta = 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
m.d.comb += [
2024-02-18 22:50:26 +01:00
probe0.eq(probe4),
probe1.eq(probe5),
probe2.eq(probe6),
probe3.eq(probe7),
manta.interface.rx.eq(uart_rx.i),
uart_tx.o.eq(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.
"""
for p in self.manta.cores.io._outputs:
measured = self.manta.cores.io.get_probe(p)
if measured != p.init:
2023-12-28 23:22:29 +01:00
raise ValueError(
f"Output probe {p.name} took initial value of {measured} instead of {p.init}."
2023-12-28 23:22:29 +01:00
)
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.manta.cores.io._inputs
outputs = self.manta.cores.io._outputs
2023-12-28 23:22:29 +01:00
# 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 i, o in zip(inputs, outputs):
value = getrandbits(len(i))
2023-12-28 23:22:29 +01:00
self.manta.cores.io.set_probe(o, value)
readback = self.manta.cores.io.get_probe(i)
2023-12-28 23:22:29 +01:00
if readback != value:
raise ValueError(
f"Reading {o.name} through {i.name} yielded {readback} instead of {value}!"
2023-12-28 23:22:29 +01:00
)
else:
print(
f"Reading {o.name} through {i.name} yielded {readback} as expected."
2023-12-28 23:22:29 +01:00
)
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()