diff options
| author | Alejandro Soto <alejandro@34project.org> | 2023-10-06 21:06:20 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2023-10-06 21:06:20 -0600 |
| commit | 363cbe7aea99400c25a6006bf5b28338cdadc611 (patch) | |
| tree | 00dafef54a2d0b51f704134022dc1c7698756727 | |
| parent | f786c0f2f6f00cab2203de4a672801ca9c179415 (diff) | |
tb: implement ring test
Diffstat (limited to '')
| -rw-r--r-- | rtl/top/test_ring.sv | 122 | ||||
| -rw-r--r-- | tb/models/__init__.py | 2 | ||||
| -rw-r--r-- | tb/models/ring.py | 54 | ||||
| -rw-r--r-- | tb/top/test_ring.py | 85 | ||||
| -rw-r--r-- | tb/top/test_smp.py | 4 |
5 files changed, 264 insertions, 3 deletions
diff --git a/rtl/top/test_ring.sv b/rtl/top/test_ring.sv new file mode 100644 index 0000000..0011853 --- /dev/null +++ b/rtl/top/test_ring.sv @@ -0,0 +1,122 @@ +`include "cache/defs.sv" + +module test_ring +( + input logic clk, + rst_n +); + + logic data_0_valid, data_1_valid, data_2_valid, data_3_valid, + data_0_ready, data_1_ready, data_2_ready, data_3_ready; + + ring_req data_0, data_1, data_2, data_3; + + cache_ring segment_0 + ( + .core_tag(), + .core_index(), + .data_rd(), + + .send(), + .send_read(), + .send_inval(), + .set_reply(), + + .in_data(data_3), + .in_data_ready(data_3_ready), + .in_data_valid(data_3_valid), + + .out_data(data_0), + .out_data_ready(data_0_ready), + .out_data_valid(data_0_valid), + + .in_hold(), + .in_hold_valid(), + .last_hop(), + .out_stall(), + + .* + ); + + cache_ring segment_1 + ( + .core_tag(), + .core_index(), + .data_rd(), + + .send(), + .send_read(), + .send_inval(), + .set_reply(), + + .in_data(data_0), + .in_data_ready(data_0_ready), + .in_data_valid(data_0_valid), + + .out_data(data_1), + .out_data_ready(data_1_ready), + .out_data_valid(data_1_valid), + + .in_hold(), + .in_hold_valid(), + .last_hop(), + .out_stall(), + + .* + ); + + cache_ring segment_2 + ( + .core_tag(), + .core_index(), + .data_rd(), + + .send(), + .send_read(), + .send_inval(), + .set_reply(), + + .in_data(data_1), + .in_data_ready(data_1_ready), + .in_data_valid(data_1_valid), + + .out_data(data_2), + .out_data_ready(data_2_ready), + .out_data_valid(data_2_valid), + + .in_hold(), + .in_hold_valid(), + .last_hop(), + .out_stall(), + + .* + ); + + cache_ring segment_3 + ( + .core_tag(), + .core_index(), + .data_rd(), + + .send(), + .send_read(), + .send_inval(), + .set_reply(), + + .in_data(data_2), + .in_data_ready(data_2_ready), + .in_data_valid(data_2_valid), + + .out_data(data_3), + .out_data_ready(data_3_ready), + .out_data_valid(data_3_valid), + + .in_hold(), + .in_hold_valid(), + .last_hop(), + .out_stall(), + + .* + ); + +endmodule 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, |
