Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
ebabe82c7This file is pinned to the selected release. Download the original to open it in your design tool.
#
# This file is part of LiteX.
#
# Copyright (c) 2019-2022 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import unittest
import random
from migen import *
from litex.gen import *
from litex.soc.interconnect.axi import *
from litex.soc.interconnect import wishbone
# Software Models ----------------------------------------------------------------------------------
class Burst:
def __init__(self, addr, type=BURST_FIXED, len=0, size=0):
self.addr = addr
self.type = type
self.len = len
self.size = size
def to_beats(self):
r = []
burst_length = self.len + 1
burst_size = 2**self.size
for i in range(burst_length):
if self.type == BURST_INCR:
offset = i*2**(self.size)
r += [Beat(self.addr + offset)]
elif self.type == BURST_WRAP:
assert burst_length in [2, 4, 8, 16]
assert (self.addr % burst_size) == 0
burst_base = self.addr - self.addr % (burst_length * burst_size)
burst_offset = self.addr % (burst_length * burst_size)
burst_addr = burst_base + (burst_offset + i*burst_size) % (burst_length * burst_size)
#print("0x{:08x}".format(burst_addr))
r += [Beat(burst_addr)]
else:
r += [Beat(self.addr)]
return r
class Beat:
def __init__(self, addr):
self.addr = addr
class Access(Burst):
def __init__(self, addr, data, id, **kwargs):
Burst.__init__(self, addr, **kwargs)
self.data = data
self.id = id
class Write(Access): pass
class Read(Access): pass
# TestAXI ------------------------------------------------------------------------------------------
class TestAXI(unittest.TestCase):
def test_burst2beat(self):
def bursts_generator(ax, bursts, valid_rand=50):
prng = random.Random(42)
for burst in bursts:
yield ax.valid.eq(1)
yield ax.addr.eq(burst.addr)
yield ax.burst.eq(burst.type)
yield ax.len.eq(burst.len)
yield ax.size.eq(burst.size)
while (yield ax.ready) == 0:
yield
yield ax.valid.eq(0)
while prng.randrange(100) < valid_rand:
yield
yield
@passive
def beats_checker(ax, beats, ready_rand=50):
self.errors = 0
yield ax.ready.eq(0)
prng = random.Random(42)
for beat in beats:
while ((yield ax.valid) and (yield ax.ready)) == 0:
if prng.randrange(100) > ready_rand:
yield ax.ready.eq(1)
else:
yield ax.ready.eq(0)
yield
ax_addr = (yield ax.addr)
#print("0x{:08x}".format(ax_addr))
if ax_addr != beat.addr:
self.errors += 1
yield
# DUT
ax_burst = AXIStreamInterface(layout=ax_description(32), id_width=32)
ax_beat = AXIStreamInterface(layout=ax_description(32), id_width=32)
dut = AXIBurst2Beat(ax_burst, ax_beat)
# Generate DUT input (bursts).
prng = random.Random(42)
bursts = []
for i in range(32):
bursts.append(Burst(prng.randrange(2**32), BURST_FIXED, prng.randrange(255), log2_int(32//8)))
bursts.append(Burst(prng.randrange(2**32), BURST_INCR, prng.randrange(255), log2_int(32//8)))
bursts.append(Burst(4, BURST_WRAP, 4-1, log2_int(2)))
bursts.append(Burst(0x80000160, BURST_WRAP, 0x3, 0b100))
# Generate expected DUT output (beats for reference).
beats = []
for burst in bursts:
beats += burst.to_beats()
# Simulation
generators = [
bursts_generator(ax_burst, bursts),
beats_checker(ax_beat, beats)
]
run_simulation(dut, generators)
self.assertEqual(self.errors, 0)
def _test_axi2wishbone(self,
naccesses=16, simultaneous_writes_reads=False,
# Random: 0: min (no random), 100: max.
# Burst randomness.
id_rand_enable = False,
len_rand_enable = False,
data_rand_enable = False,
# Flow valid randomness.
aw_valid_random = 0,
w_valid_random = 0,
ar_valid_random = 0,
r_valid_random = 0,
# Flow ready randomness.
w_ready_random = 0,
b_ready_random = 0,
r_ready_random = 0
):
def writes_cmd_generator(axi_port, writes):
prng = random.Random(42)
for write in writes:
while prng.randrange(100) < aw_valid_random:
yield
# Send command.
yield axi_port.aw.valid.eq(1)
yield axi_port.aw.addr.eq(write.addr<<2)
yield axi_port.aw.burst.eq(write.type)
yield axi_port.aw.len.eq(write.len)
yield axi_port.aw.size.eq(write.size)
yield axi_port.aw.id.eq(write.id)
yield
while (yield axi_port.aw.ready) == 0:
yield
yield axi_port.aw.valid.eq(0)
def writes_data_generator(axi_port, writes):
prng = random.Random(42)
yield axi_port.w.strb.eq(2**(len(axi_port.w.data)//8) - 1)
for write in writes:
for i, data in enumerate(write.data):
while prng.randrange(100) < w_valid_random:
yield
# Send data.
yield axi_port.w.valid.eq(1)
if (i == (len(write.data) - 1)):
yield axi_port.w.last.eq(1)
else:
yield axi_port.w.last.eq(0)
yield axi_port.w.data.eq(data)
yield
while (yield axi_port.w.ready) == 0:
yield
yield axi_port.w.valid.eq(0)
axi_port.reads_enable = True
def writes_response_generator(axi_port, writes):
prng = random.Random(42)
self.writes_id_errors = 0
for write in writes:
# wait response
yield axi_port.b.ready.eq(0)
yield
while (yield axi_port.b.valid) == 0:
yield
while prng.randrange(100) < b_ready_random:
yield
yield axi_port.b.ready.eq(1)
yield
if (yield axi_port.b.id) != write.id:
self.writes_id_errors += 1
def reads_cmd_generator(axi_port, reads):
prng = random.Random(42)
while not axi_port.reads_enable:
yield
for read in reads:
while prng.randrange(100) < ar_valid_random:
yield
# Send command.
yield axi_port.ar.valid.eq(1)
yield axi_port.ar.addr.eq(read.addr<<2)
yield axi_port.ar.burst.eq(read.type)
yield axi_port.ar.len.eq(read.len)
yield axi_port.ar.size.eq(read.size)
yield axi_port.ar.id.eq(read.id)
yield
while (yield axi_port.ar.ready) == 0:
yield
yield axi_port.ar.valid.eq(0)
def reads_response_data_generator(axi_port, reads):
prng = random.Random(42)
self.reads_data_errors = 0
self.reads_id_errors = 0
self.reads_last_errors = 0
while not axi_port.reads_enable:
yield
for read in reads:
for i, data in enumerate(read.data):
# Wait data / response.
yield axi_port.r.ready.eq(0)
yield
while (yield axi_port.r.valid) == 0:
yield
while prng.randrange(100) < r_ready_random:
yield
yield axi_port.r.ready.eq(1)
yield
if (yield axi_port.r.data) != data:
self.reads_data_errors += 1
if (yield axi_port.r.id) != read.id:
self.reads_id_errors += 1
if i == (len(read.data) - 1):
if (yield axi_port.r.last) != 1:
self.reads_last_errors += 1
else:
if (yield axi_port.r.last) != 0:
self.reads_last_errors += 1
# DUT
class DUT(Module):
def __init__(self):
self.axi = AXIInterface(data_width=32, address_width=32, id_width=8)
self.wishbone = wishbone.Interface(data_width=32, adr_width=30, addressing="word")
axi2wishbone = AXI2Wishbone(self.axi, self.wishbone)
self.submodules += axi2wishbone
wishbone_mem = wishbone.SRAM(1024, bus=self.wishbone)
self.submodules += wishbone_mem
dut = DUT()
# Generate writes/reads.
prng = random.Random(42)
writes = []
offset = 1
for i in range(naccesses):
_id = prng.randrange(2**8) if id_rand_enable else i
_len = prng.randrange(32) if len_rand_enable else i
_data = [prng.randrange(2**32) if data_rand_enable else j for j in range(_len + 1)]
writes.append(Write(offset, _data, _id, type=BURST_INCR, len=_len, size=log2_int(32//8)))
offset += _len + 1
# Dummy reads to ensure datas have been written before the effective reads start.
dummy_reads = [Read(1023, [0], 0, type=BURST_FIXED, len=0, size=log2_int(32//8)) for _ in range(32)]
reads = writes
# Simulation
if simultaneous_writes_reads:
dut.axi.reads_enable = True
else:
dut.axi.reads_enable = False # Will be set by writes_data_generator.
generators = [
writes_cmd_generator(dut.axi, writes),
writes_data_generator(dut.axi, writes),
writes_response_generator(dut.axi, writes),
reads_cmd_generator(dut.axi, reads),
reads_response_data_generator(dut.axi, reads)
]
run_simulation(dut, generators)
self.assertEqual(self.writes_id_errors, 0)
self.assertEqual(self.reads_data_errors, 0)
self.assertEqual(self.reads_id_errors, 0)
self.assertEqual(self.reads_last_errors, 0)
# Test with no randomness.
def test_axi2wishbone_writes_then_reads_no_random(self):
self._test_axi2wishbone(simultaneous_writes_reads=False)
# Test randomness one parameter at a time.
def test_axi2wishbone_writes_then_reads_random_bursts(self):
self._test_axi2wishbone(
simultaneous_writes_reads = False,
id_rand_enable = True,
len_rand_enable = True,
data_rand_enable = True)
def test_axi2wishbone_random_w_ready(self):
self._test_axi2wishbone(w_ready_random=90)
def test_axi2wishbone_random_b_ready(self):
self._test_axi2wishbone(b_ready_random=90)
def test_axi2wishbone_random_r_ready(self):
self._test_axi2wishbone(r_ready_random=90)
def test_axi2wishbone_random_aw_valid(self):
self._test_axi2wishbone(aw_valid_random=90)
def test_axi2wishbone_random_w_valid(self):
self._test_axi2wishbone(w_valid_random=90)
def test_axi2wishbone_random_ar_valid(self):
self._test_axi2wishbone(ar_valid_random=90)
def test_axi2wishbone_random_r_valid(self):
self._test_axi2wishbone(r_valid_random=90)
# Now let's stress things a bit... :)
def test_axi2wishbone_random_all(self):
self._test_axi2wishbone(
simultaneous_writes_reads = False,
id_rand_enable = True,
len_rand_enable = True,
aw_valid_random = 50,
w_ready_random = 50,
b_ready_random = 50,
w_valid_random = 50,
ar_valid_random = 90,
r_valid_random = 90,
r_ready_random = 90
)
def test_axi_down_converter(self):
class DUT(LiteXModule):
def __init__(self, dw_from=64, dw_to=32):
self.axi_master = AXIInterface(data_width=dw_from)
axi_slave = AXIInterface(data_width=dw_to)
wb_slave = wishbone.Interface(data_width=dw_to, address_width=axi_slave.address_width, addressing="word")
self.converter = AXIConverter(self.axi_master, axi_slave)
self.axi2wb = AXI2Wishbone(axi_slave, wb_slave)
self.mem = wishbone.SRAM(1024, bus=wb_slave, init=range(256))
def read_generator(dut):
axi_port = dut.axi_master
# AXI Read.
addr = 0x34
yield axi_port.ar.addr.eq(addr * 4)
yield axi_port.ar.valid.eq(1)
yield axi_port.ar.burst.eq(0b1)
yield axi_port.ar.len.eq(0)
yield axi_port.ar.size.eq(0b011)
yield axi_port.r.ready.eq(1)
yield
while (yield axi_port.r.valid) == 0:
yield
rd = (yield axi_port.r.data)
# Check Mem Content.
mem_content = 0
i = 0
while i < axi_port.data_width // dut.mem.bus.data_width:
mem_content |= (yield dut.mem.mem[addr + i]) << (i * dut.mem.bus.data_width)
i += 1
assert rd == mem_content, (hex(rd), hex(mem_content))
def write_generator(dut):
axi_port = dut.axi_master
# AXI Write.
addr = 0x24
data = 0x98761244
yield axi_port.aw.addr.eq(addr * 4)
yield axi_port.aw.valid.eq(1)
yield axi_port.aw.burst.eq(0b1)
yield axi_port.aw.len.eq(0)
yield axi_port.aw.size.eq(0b011)
yield axi_port.w.strb.eq(0b111111111)
yield axi_port.w.data.eq(data)
yield axi_port.w.valid.eq(1)
yield axi_port.w.last.eq(1)
yield
while (yield axi_port.aw.ready) == 0:
yield
yield axi_port.aw.valid.eq(0)
while (yield axi_port.w.ready) == 0:
yield
yield axi_port.w.valid.eq(0)
# Check Mem Content.
mem_content = 0
i = 0
while i < axi_port.data_width // dut.mem.bus.data_width:
mem_content |= (yield dut.mem.mem[addr + i]) << (i * dut.mem.bus.data_width)
i += 1
assert data == mem_content, (hex(data), hex(mem_content))
dut = DUT(64, 32)
run_simulation(dut, [read_generator(dut), write_generator(dut)], vcd_name="sim.vcd")