summaryrefslogtreecommitdiff
path: root/tb
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2023-10-06 21:06:20 -0600
committerAlejandro Soto <alejandro@34project.org>2023-10-06 21:06:20 -0600
commit363cbe7aea99400c25a6006bf5b28338cdadc611 (patch)
tree00dafef54a2d0b51f704134022dc1c7698756727 /tb
parentf786c0f2f6f00cab2203de4a672801ca9c179415 (diff)
tb: implement ring test
Diffstat (limited to 'tb')
-rw-r--r--tb/models/__init__.py2
-rw-r--r--tb/models/ring.py54
-rw-r--r--tb/top/test_ring.py85
-rw-r--r--tb/top/test_smp.py4
4 files changed, 142 insertions, 3 deletions
diff --git a/tb/models/__init__.py b/tb/models/__init__.py
index 4f14c05..006ed6b 100644
--- a/tb/models/__init__.py
+++ b/tb/models/__init__.py
@@ -1,3 +1,3 @@
from .core import *
from .smp import *
-
+from .ring import *
diff --git a/tb/models/ring.py b/tb/models/ring.py
new file mode 100644
index 0000000..45305e6
--- /dev/null
+++ b/tb/models/ring.py
@@ -0,0 +1,54 @@
+def _shift_out(val, n):
+ rhs = val & ((1 << n) - 1)
+ lhs = val >> n
+ return (lhs, rhs)
+
+def _shift_in(val, n, req=0):
+ return req << n | (val & ((1 << n) - 1))
+
+def _out(*, ttl, read, inval, reply, tag, index, data):
+ req = _shift_in(read, 1, ttl)
+ req = _shift_in(inval, 1, req)
+ req = _shift_in(reply, 1, req)
+ req = _shift_in(tag, 13, req)
+ req = _shift_in(index, 12, req)
+ req = _shift_in(data, 128, req)
+ return req.to_bytes(20, 'big')
+
+class RingSegmentModel:
+ def __init__(self):
+ self.queue = []
+
+ def recv(self, req):
+ req = int.from_bytes(req, 'big')
+ req, data = _shift_out(req, 128)
+ req, index = _shift_out(req, 12)
+ req, tag = _shift_out(req, 13)
+ req, reply = _shift_out(req, 1)
+ req, inval = _shift_out(req, 1)
+ ttl, read = _shift_out(req, 1)
+
+ if ttl > 0:
+ req = _out(ttl=(ttl - 1), read=read, inval=inval, reply=reply,
+ tag=tag, index=index, data=data)
+
+ # Recvs de bus tienen prioridad
+ self.queue.insert(0, req)
+
+ def send(self, *, ty, tag, index, data):
+ read = 0
+ inval = 0
+
+ match ty:
+ case 'read':
+ read = 1
+ case 'inval':
+ inval = 1
+ case 'read-inval':
+ read = inval = 1
+
+ #FIXME: Bug en VPI
+ data = 0
+
+ req = _out(ttl=3, read=read, inval=inval, reply=0, tag=tag, index=index, data=data)
+ self.queue.append(req)
diff --git a/tb/top/test_ring.py b/tb/top/test_ring.py
new file mode 100644
index 0000000..7390143
--- /dev/null
+++ b/tb/top/test_ring.py
@@ -0,0 +1,85 @@
+import random
+
+import cocotb
+from cocotb.clock import Clock
+from cocotb.queue import Queue
+from cocotb.binary import BinaryValue
+from cocotb.triggers import ClockCycles, RisingEdge, Timer
+from cocotb_bus.drivers import BitDriver
+from cocotb_bus.scoreboard import Scoreboard
+from cocotb_bus.monitors.avalon import AvalonST as AvalonSTMonitor
+
+from tb.models import RingSegmentModel
+
+AvalonSTMonitor._signals = {'valid': '_valid', 'data': ''}
+AvalonSTMonitor._optional_signals = {'ready': '_ready'}
+
+def rand_ready():
+ while True:
+ yield (1, random.randint(0, 5))
+
+async def do_sends(segment, model, queue):
+ while True:
+ segment.send.value = 0
+ segment.set_reply.value = 0
+
+ ty, tag, index, data = await queue.get()
+ assert ty in ('read', 'inval', 'read-inval')
+
+ model.send(ty=ty, tag=tag, index=index, data=data)
+ await ClockCycles(segment.clk, 1)
+
+ trigger = RisingEdge(segment.out_data_ready)
+ while True:
+ await trigger
+ if not segment.out_stall or len(model.queue) > 1:
+ break
+
+ segment.core_tag.value = tag
+ segment.core_tag.value = 69
+ segment.core_index.value = index
+ segment.data_rd.value.assign(data)
+ segment.send.value = 1
+ segment.send_read.value = ty in ('read', 'read-inval')
+ segment.send_inval.value = ty in ('inval', 'read-inval')
+
+ await ClockCycles(segment.clk, 1)
+
+async def gen_requests(clk, queue):
+ while True:
+ await ClockCycles(clk, random.randint(1, 5))
+ tag = random.randint(0, (1 << 13) - 1)
+ index = random.randint(0, (1 << 12) - 1)
+ data = random.randint(0, (1 << 128) - 1)
+
+ ty = ('read', 'inval', 'read-inval')[random.randint(0, 2)]
+ await queue.put((ty, tag, index, data))
+
+@cocotb.test()
+async def test_send_recv(dut):
+ await cocotb.start(Clock(dut.clk, 2).start())
+
+ dut.rst_n.value = 1
+ await Timer(1)
+ dut.rst_n.value = 0
+ await Timer(1)
+ dut.rst_n.value = 1
+
+ scoreboard = Scoreboard(dut)
+ segments = [getattr(dut, f'segment_{i}') for i in range(4)]
+ models = [RingSegmentModel() for _ in range(4)]
+ queues = [Queue(maxsize=2) for _ in range(4)]
+
+ for i in range(4):
+ model, segment, next_model = models[i], segments[i], models[(i + 1) % 4]
+
+ BitDriver(getattr(dut, f'data_{i}_ready'), dut.clk).start(rand_ready())
+ monitor = AvalonSTMonitor(segment, 'out_data', dut.clk, callback=next_model.recv,
+ case_insensitive=False, bus_separator='')
+
+ scoreboard.add_interface(monitor, model.queue)
+
+ await cocotb.start(do_sends(segment, model, queues[i]))
+ await cocotb.start(gen_requests(segment.clk, queues[i]))
+
+ await ClockCycles(dut.clk, 1000)
diff --git a/tb/top/test_smp.py b/tb/top/test_smp.py
index 1994da2..c70b76b 100644
--- a/tb/top/test_smp.py
+++ b/tb/top/test_smp.py
@@ -7,6 +7,8 @@ from tb.models import CorePaceModel, SmpModel
@cocotb.test()
async def reset(dut):
+ cocotb.start(Clock(dut.clk, 2).start())
+
dut.rst_n.value = 1
await Timer(1)
dut.rst_n.value = 0
@@ -14,8 +16,6 @@ async def reset(dut):
dut.rst_n.value = 1
model = SmpModel()
-
- cocotb.start_soon(Clock(dut.clk, 2).start())
master = AvalonMaster(dut, 'avl', dut.clk, case_insensitive=False)
cpu0 = CorePaceModel(clk=dut.clk, halt=dut.halt_0, step=dut.step_0,