make model tracking automatic in memory core tests
This commit is contained in:
parent
c1935bcb11
commit
5d5a50042f
|
|
@ -2,7 +2,7 @@ from amaranth import *
|
|||
from manta.utils import *
|
||||
from manta.ethernet.source_bridge import UDPSourceBridge
|
||||
from manta.ethernet.sink_bridge import UDPSinkBridge
|
||||
from random import randint
|
||||
from random import getrandbits
|
||||
import socket
|
||||
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ class EthernetInterface(Elaboratable):
|
|||
# https://en.wikipedia.org/wiki/MAC_address#Ranges_of_group_and_locally_administered_addresses
|
||||
|
||||
if "mac_address" not in liteeth_config:
|
||||
addr = list(f"{randint(0, (2**48) - 1):012x}")
|
||||
addr = list(f"{getrandbits(48):012x}")
|
||||
addr[1] = "2"
|
||||
liteeth_config["mac_address"] = int("".join(addr), 16)
|
||||
print(liteeth_config["mac_address"])
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from amaranth import *
|
|||
from amaranth.lib import data
|
||||
from amaranth.sim import Simulator
|
||||
from abc import ABC, abstractmethod
|
||||
from random import sample
|
||||
from random import sample, randint
|
||||
import os
|
||||
|
||||
|
||||
|
|
@ -75,6 +75,20 @@ def words_to_value(data):
|
|||
return int("".join([f"{i:016b}" for i in data[::-1]]), 2)
|
||||
|
||||
|
||||
def value_to_words(data, n_words):
|
||||
"""
|
||||
Takes a integer, interprets it as a set of 16-bit integers
|
||||
concatenated together, and splits it into a list of 16-bit numbers.
|
||||
"""
|
||||
|
||||
if not isinstance(data, int) or data < 0:
|
||||
raise ValueError("Behavior is only defined for nonnegative integers.")
|
||||
|
||||
# Convert to binary, split into 16-bit chunks, and then convert back to list of int
|
||||
binary = f"{data:0b}".zfill(n_words * 16)
|
||||
return [int(binary[i : i + 16], 2) for i in range(0, 16 * n_words, 16)][::-1]
|
||||
|
||||
|
||||
def check_value_fits_in_bits(value, n_bits):
|
||||
"""
|
||||
Rasies an exception if the provided value isn't an integer that cannot
|
||||
|
|
@ -91,20 +105,6 @@ def check_value_fits_in_bits(value, n_bits):
|
|||
raise ValueError("Signed integer too large.")
|
||||
|
||||
|
||||
def value_to_words(data, n_words):
|
||||
"""
|
||||
Takes a integer, interprets it as a set of 16-bit integers
|
||||
concatenated together, and splits it into a list of 16-bit numbers.
|
||||
"""
|
||||
|
||||
if not isinstance(data, int) or data < 0:
|
||||
raise ValueError("Behavior is only defined for nonnegative integers.")
|
||||
|
||||
# Convert to binary, split into 16-bit chunks, and then convert back to list of int
|
||||
binary = f"{data:0b}".zfill(n_words * 16)
|
||||
return [int(binary[i : i + 16], 2) for i in range(0, 16 * n_words, 16)][::-1]
|
||||
|
||||
|
||||
def split_into_chunks(data, chunk_size):
|
||||
"""
|
||||
Split a list into a list of lists, where each sublist has length `chunk_size`.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from amaranth_boards.icestick import ICEStickPlatform
|
|||
from manta import Manta
|
||||
from manta.utils import *
|
||||
import pytest
|
||||
from random import randint
|
||||
from random import getrandbits
|
||||
|
||||
|
||||
class IOCoreLoopbackTest(Elaboratable):
|
||||
|
|
@ -120,7 +120,7 @@ class IOCoreLoopbackTest(Elaboratable):
|
|||
# to the second input, and so on...
|
||||
for input, output in zip(inputs, outputs):
|
||||
width = self.config["cores"]["io_core"]["inputs"][input]
|
||||
value = randint(0, 2**width - 1)
|
||||
value = getrandbits(width)
|
||||
|
||||
self.manta.io_core.set_probe(output, value)
|
||||
readback = self.manta.io_core.get_probe(input)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from amaranth import *
|
|||
from amaranth.sim import Simulator
|
||||
from manta.io_core import IOCore
|
||||
from manta.utils import *
|
||||
from random import randint
|
||||
from random import getrandbits
|
||||
|
||||
probe0 = Signal(1)
|
||||
probe1 = Signal(2)
|
||||
|
|
@ -51,7 +51,7 @@ def test_output_probe_buffer_initial_value():
|
|||
def test_output_probes_are_writeable():
|
||||
for o in outputs:
|
||||
addrs = io_core._memory_map[o.name]["addrs"]
|
||||
test_value = randint(0, (2**o.width) - 1)
|
||||
test_value = getrandbits(o.width)
|
||||
datas = value_to_words(test_value, len(addrs))
|
||||
|
||||
# write value to registers
|
||||
|
|
@ -67,7 +67,7 @@ def test_output_probes_are_writeable():
|
|||
def test_output_probes_update():
|
||||
for o in outputs:
|
||||
addrs = io_core._memory_map[o.name]["addrs"]
|
||||
test_value = randint(0, (2**o.width) - 1)
|
||||
test_value = getrandbits(o.width)
|
||||
datas = value_to_words(test_value, len(addrs))
|
||||
|
||||
# write value to registers
|
||||
|
|
@ -93,7 +93,7 @@ def test_output_probes_update():
|
|||
def test_input_probes_update():
|
||||
for i in inputs:
|
||||
# set input probe value
|
||||
test_value = randint(0, (2**i.width) - 1)
|
||||
test_value = getrandbits(i.width)
|
||||
yield i.eq(test_value)
|
||||
|
||||
# pulse strobe register
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from amaranth_boards.icestick import ICEStickPlatform
|
|||
from manta import Manta
|
||||
from manta.utils import *
|
||||
import pytest
|
||||
from random import randint, sample
|
||||
from random import randint, getrandbits
|
||||
from math import ceil, log2
|
||||
|
||||
"""
|
||||
|
|
@ -105,7 +105,7 @@ class MemoryCoreLoopbackTest(Elaboratable):
|
|||
|
||||
# Read and write randomly from the bus side
|
||||
for addr in jumble(range(self.depth)):
|
||||
data = randint(0, 2**self.width - 1)
|
||||
data = getrandbits(self.width)
|
||||
self.write_user_side(addr, data)
|
||||
self.verify_register(addr, data)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from manta.memory_core import MemoryCore
|
||||
from manta.utils import *
|
||||
from random import randint, choice
|
||||
from random import randint, choice, getrandbits
|
||||
from math import ceil
|
||||
|
||||
|
||||
|
|
@ -18,56 +18,56 @@ class MemoryCoreTests:
|
|||
range(self.base_addr, self.max_addr)
|
||||
) # include the endpoint!
|
||||
self.user_addrs = list(range(self.mem_core._depth))
|
||||
self.model = {}
|
||||
|
||||
# A model of what each bus address contains
|
||||
self.model = {i: 0 for i in self.bus_addrs}
|
||||
|
||||
def bus_addrs_all_zero(self):
|
||||
for addr in self.bus_addrs:
|
||||
yield from self.verify_bus_side(addr, 0)
|
||||
yield from self.verify_bus_side(addr)
|
||||
|
||||
def user_addrs_all_zero(self):
|
||||
for addr in self.user_addrs:
|
||||
yield from self.verify_user_side(addr, 0)
|
||||
yield from self.verify_user_side(addr)
|
||||
|
||||
def bus_to_bus_functionality(self):
|
||||
yield from self.one_bus_write_then_one_bus_read()
|
||||
yield from self.multi_bus_writes_then_multi_bus_reads()
|
||||
# yield from self.one_bus_write_then_one_bus_read()
|
||||
# yield from self.multi_bus_writes_then_multi_bus_reads()
|
||||
yield from self.rand_bus_writes_rand_bus_reads()
|
||||
|
||||
def user_to_bus_functionality(self):
|
||||
yield from self.one_user_write_then_one_bus_read()
|
||||
yield from self.multi_user_write_then_multi_bus_reads()
|
||||
# yield from self.one_user_write_then_one_bus_read()
|
||||
# yield from self.multi_user_write_then_multi_bus_reads()
|
||||
yield from self.rand_user_writes_rand_bus_reads()
|
||||
|
||||
def bus_to_user_functionality(self):
|
||||
yield from self.one_bus_write_then_one_user_read()
|
||||
yield from self.multi_bus_write_then_multi_user_reads()
|
||||
# yield from self.one_bus_write_then_one_user_read()
|
||||
# yield from self.multi_bus_write_then_multi_user_reads()
|
||||
yield from self.rand_bus_writes_rand_user_reads()
|
||||
|
||||
def user_to_user_functionality(self):
|
||||
yield from self.one_user_write_then_one_user_read()
|
||||
yield from self.multi_user_write_then_multi_user_read()
|
||||
# yield from self.one_user_write_then_one_user_read()
|
||||
# yield from self.multi_user_write_then_multi_user_read()
|
||||
yield from self.rand_user_write_rand_user_read()
|
||||
|
||||
def one_bus_write_then_one_bus_read(self):
|
||||
for addr in self.bus_addrs:
|
||||
data_width = self._get_data_width(addr)
|
||||
data = randint(0, (2**data_width) - 1)
|
||||
data_width = self.get_data_width(addr)
|
||||
data = getrandbits(data_width)
|
||||
|
||||
yield from self.write_bus_side(addr, data)
|
||||
yield from self.verify_bus_side(addr, data)
|
||||
yield from self.verify_bus_side(addr)
|
||||
|
||||
def multi_bus_writes_then_multi_bus_reads(self):
|
||||
# write-write-write then read-read-read
|
||||
for addr in jumble(self.bus_addrs):
|
||||
data_width = self._get_data_width(addr)
|
||||
data = randint(0, (2**data_width) - 1)
|
||||
data_width = self.get_data_width(addr)
|
||||
data = getrandbits(data_width)
|
||||
|
||||
self.model[addr] = data
|
||||
yield from self.write_bus_side(addr, data)
|
||||
|
||||
for addr in jumble(self.bus_addrs):
|
||||
yield from self.verify_bus_side(addr, self.model[addr])
|
||||
yield from self.verify_bus_side(addr)
|
||||
|
||||
def rand_bus_writes_rand_bus_reads(self):
|
||||
# random reads and writes in random orders
|
||||
|
|
@ -76,43 +76,35 @@ class MemoryCoreTests:
|
|||
|
||||
operation = choice(["read", "write"])
|
||||
if operation == "read":
|
||||
yield from self.verify_bus_side(addr, self.model[addr])
|
||||
yield from self.verify_bus_side(addr)
|
||||
|
||||
elif operation == "write":
|
||||
data_width = self._get_data_width(addr)
|
||||
data = randint(0, (2**data_width) - 1)
|
||||
self.model[addr] = data
|
||||
data_width = self.get_data_width(addr)
|
||||
data = getrandbits(data_width)
|
||||
yield from self.write_bus_side(addr, data)
|
||||
|
||||
def one_user_write_then_one_bus_read(self):
|
||||
for user_addr in self.user_addrs:
|
||||
# write to user side
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
data = getrandbits(self.width)
|
||||
yield from self.write_user_side(user_addr, data)
|
||||
|
||||
# verify contents when read out from the bus
|
||||
words = value_to_words(data, self.n_mems)
|
||||
for i, word in enumerate(words):
|
||||
for i in range(self.n_mems):
|
||||
bus_addr = self.base_addr + user_addr + (i * self.depth)
|
||||
yield from self.verify_bus_side(bus_addr, word)
|
||||
yield from self.verify_bus_side(bus_addr)
|
||||
|
||||
def multi_user_write_then_multi_bus_reads(self):
|
||||
# write-write-write then read-read-read
|
||||
for user_addr in jumble(self.user_addrs):
|
||||
|
||||
# write a random number to the user side
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
data = getrandbits(self.width)
|
||||
yield from self.write_user_side(user_addr, data)
|
||||
|
||||
# convert value to words, and save to self.model
|
||||
words = value_to_words(data, self.n_mems)
|
||||
for i, word in enumerate(words):
|
||||
bus_addr = self.base_addr + user_addr + (i * self.depth)
|
||||
self.model[bus_addr] = word
|
||||
|
||||
# read out every bus_addr in random order
|
||||
for bus_addr in jumble(self.bus_addrs):
|
||||
yield from self.verify_bus_side(bus_addr, self.model[bus_addr])
|
||||
yield from self.verify_bus_side(bus_addr)
|
||||
|
||||
def rand_user_writes_rand_bus_reads(self):
|
||||
# random reads and writes in random orders
|
||||
|
|
@ -128,23 +120,18 @@ class MemoryCoreTests:
|
|||
# read from bus side
|
||||
if operation == "read":
|
||||
for bus_addr in bus_addrs:
|
||||
yield from self.verify_bus_side(bus_addr, self.model[bus_addr])
|
||||
yield from self.verify_bus_side(bus_addr)
|
||||
|
||||
# write to user side
|
||||
elif operation == "write":
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
data = getrandbits(self.width)
|
||||
yield from self.write_user_side(user_addr, data)
|
||||
|
||||
# save words just written to self.model
|
||||
words = value_to_words(data, self.n_mems)
|
||||
for addr, word in zip(bus_addrs, words):
|
||||
self.model[addr] = word
|
||||
|
||||
def one_bus_write_then_one_user_read(self):
|
||||
for user_addr in self.user_addrs:
|
||||
# Try and set the value at the user address to a given value,
|
||||
# by writing to the appropriate memory locaitons on the bus side
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
data = getrandbits(self.width)
|
||||
|
||||
words = value_to_words(data, self.n_mems)
|
||||
|
||||
|
|
@ -152,25 +139,18 @@ class MemoryCoreTests:
|
|||
bus_addr = self.base_addr + user_addr + (i * self.depth)
|
||||
yield from self.write_bus_side(bus_addr, word)
|
||||
|
||||
yield from self.verify_user_side(user_addr, data)
|
||||
yield from self.verify_user_side(user_addr)
|
||||
|
||||
def multi_bus_write_then_multi_user_reads(self):
|
||||
# write-write-write then read-read-read
|
||||
for bus_addr in jumble(self.bus_addrs):
|
||||
data_width = self._get_data_width(bus_addr)
|
||||
data = randint(0, (2**data_width) - 1)
|
||||
data_width = self.get_data_width(bus_addr)
|
||||
data = getrandbits(data_width)
|
||||
|
||||
self.model[bus_addr] = data
|
||||
yield from self.write_bus_side(bus_addr, data)
|
||||
|
||||
for user_addr in jumble(self.user_addrs):
|
||||
bus_addrs = [
|
||||
self.base_addr + user_addr + (i * self.depth)
|
||||
for i in range(self.n_mems)
|
||||
]
|
||||
|
||||
value = words_to_value([self.model[addr] for addr in bus_addrs])
|
||||
yield from self.verify_user_side(user_addr, value)
|
||||
yield from self.verify_user_side(user_addr)
|
||||
|
||||
def rand_bus_writes_rand_user_reads(self):
|
||||
for _ in range(5 * self.depth):
|
||||
|
|
@ -179,41 +159,31 @@ class MemoryCoreTests:
|
|||
# write random data to random bus address
|
||||
if operation == "write":
|
||||
bus_addr = randint(self.base_addr, self.max_addr - 1)
|
||||
data_width = self._get_data_width(bus_addr)
|
||||
data = randint(0, (2**data_width) - 1)
|
||||
self.model[bus_addr] = data
|
||||
data_width = self.get_data_width(bus_addr)
|
||||
data = getrandbits(data_width)
|
||||
|
||||
yield from self.write_bus_side(bus_addr, data)
|
||||
|
||||
# read from random user_addr
|
||||
if operation == "read":
|
||||
user_addr = randint(0, self.depth - 1)
|
||||
bus_addrs = [
|
||||
self.base_addr + user_addr + (i * self.depth)
|
||||
for i in range(self.n_mems)
|
||||
]
|
||||
|
||||
value = words_to_value([self.model[addr] for addr in bus_addrs])
|
||||
|
||||
yield from self.verify_user_side(user_addr, value)
|
||||
yield from self.verify_user_side(user_addr)
|
||||
|
||||
def one_user_write_then_one_user_read(self):
|
||||
for addr in self.user_addrs:
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
data = getrandbits(self.width)
|
||||
|
||||
yield from self.write_user_side(addr, data)
|
||||
yield from self.verify_user_side(addr, data)
|
||||
yield from self.verify_user_side(addr)
|
||||
|
||||
def multi_user_write_then_multi_user_read(self):
|
||||
# write-write-write then read-read-read
|
||||
self.foo = {}
|
||||
for user_addr in jumble(self.user_addrs):
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
self.foo[user_addr] = data
|
||||
data = getrandbits(self.width)
|
||||
yield from self.write_user_side(user_addr, data)
|
||||
|
||||
for user_addr in jumble(self.user_addrs):
|
||||
yield from self.verify_user_side(user_addr, self.foo[user_addr])
|
||||
yield from self.verify_user_side(user_addr)
|
||||
|
||||
def rand_user_write_rand_user_read(self):
|
||||
# random reads and writes in random orders
|
||||
|
|
@ -222,14 +192,13 @@ class MemoryCoreTests:
|
|||
|
||||
operation = choice(["read", "write"])
|
||||
if operation == "read":
|
||||
yield from self.verify_user_side(user_addr, self.foo[user_addr])
|
||||
yield from self.verify_user_side(user_addr)
|
||||
|
||||
elif operation == "write":
|
||||
data = randint(0, (2**self.width) - 1)
|
||||
self.foo[user_addr] = data
|
||||
data = getrandbits(self.width)
|
||||
yield from self.write_user_side(user_addr, data)
|
||||
|
||||
def _get_data_width(self, addr):
|
||||
def get_data_width(self, addr):
|
||||
# this part is a little hard to check since we might have a
|
||||
# memory at the end of the address space that's less than
|
||||
# 16-bits wide. so we'll have to calculate how wide our
|
||||
|
|
@ -240,17 +209,29 @@ class MemoryCoreTests:
|
|||
else:
|
||||
return self.width % 16
|
||||
|
||||
def verify_bus_side(self, addr, expected_data):
|
||||
yield from verify_register(self.mem_core, addr, expected_data)
|
||||
def verify_bus_side(self, addr):
|
||||
yield from verify_register(self.mem_core, addr, self.model[addr])
|
||||
for _ in range(4):
|
||||
yield
|
||||
|
||||
def write_bus_side(self, addr, data):
|
||||
self.model[addr] = data
|
||||
yield from write_register(self.mem_core, addr, data)
|
||||
for _ in range(4):
|
||||
yield
|
||||
|
||||
def verify_user_side(self, addr, expected_data):
|
||||
def verify_user_side(self, addr):
|
||||
# Determine the expected value on the user side by looking
|
||||
# up the appropriate bus addresses in the model
|
||||
|
||||
# Convert to bus addresses:
|
||||
bus_words = []
|
||||
for i in range(self.n_mems):
|
||||
bus_addr = self.base_addr + addr + (i * self.depth)
|
||||
bus_words.append(self.model[bus_addr])
|
||||
|
||||
expected_data = words_to_value(bus_words)
|
||||
|
||||
yield self.mem_core.user_addr.eq(addr)
|
||||
yield
|
||||
yield
|
||||
|
|
@ -262,6 +243,12 @@ class MemoryCoreTests:
|
|||
)
|
||||
|
||||
def write_user_side(self, addr, data):
|
||||
# convert value to words, and save to self.model
|
||||
words = value_to_words(data, self.n_mems)
|
||||
for i, word in enumerate(words):
|
||||
bus_addr = self.base_addr + addr + (i * self.depth)
|
||||
self.model[bus_addr] = word
|
||||
|
||||
yield self.mem_core.user_addr.eq(addr)
|
||||
yield self.mem_core.user_data_in.eq(data)
|
||||
yield self.mem_core.user_write_enable.eq(1)
|
||||
|
|
@ -294,11 +281,12 @@ def test_bidirectional():
|
|||
|
||||
test_bidirectional_testbench()
|
||||
|
||||
|
||||
def test_bidirectional_random():
|
||||
mem_core = MemoryCore(
|
||||
mode="bidirectional",
|
||||
width=randint(0, 128),
|
||||
depth=randint(0, 2048),
|
||||
depth=randint(0, 1024),
|
||||
base_addr=randint(0, 32678),
|
||||
interface=None,
|
||||
)
|
||||
|
|
@ -336,11 +324,12 @@ def test_fpga_to_host():
|
|||
|
||||
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, 2048),
|
||||
depth=randint(0, 1024),
|
||||
base_addr=randint(0, 32678),
|
||||
interface=None,
|
||||
)
|
||||
|
|
@ -373,11 +362,12 @@ def test_host_to_fpga():
|
|||
|
||||
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, 2048),
|
||||
depth=randint(0, 1024),
|
||||
base_addr=randint(0, 32678),
|
||||
interface=None,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue