121 lines
4.0 KiB
Python
121 lines
4.0 KiB
Python
import os
|
|
from random import getrandbits
|
|
|
|
import pytest
|
|
from amaranth import *
|
|
from amaranth.lib import io
|
|
from amaranth_boards.icestick import ICEStickPlatform
|
|
from amaranth_boards.nexys4ddr import Nexys4DDRPlatform
|
|
|
|
from manta import *
|
|
from manta.utils import *
|
|
|
|
|
|
class IOCoreLoopbackTest(Elaboratable):
|
|
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
|
|
)
|
|
|
|
m = Module()
|
|
m.submodules.manta = manta
|
|
|
|
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)
|
|
|
|
m.d.comb += [
|
|
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),
|
|
]
|
|
|
|
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:
|
|
raise ValueError(
|
|
f"Output probe {p.name} took initial value of {measured} instead of {p.init}."
|
|
)
|
|
|
|
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
|
|
|
|
# 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))
|
|
|
|
self.manta.cores.io.set_probe(o, value)
|
|
readback = self.manta.cores.io.get_probe(i)
|
|
|
|
if readback != value:
|
|
raise ValueError(
|
|
f"Reading {o.name} through {i.name} yielded {readback} instead of {value}!"
|
|
)
|
|
|
|
else:
|
|
print(
|
|
f"Reading {o.name} through {i.name} 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()
|
|
|
|
|
|
@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()
|