|
| 1 | +# Copyright 2023 The XLS Authors |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +import os |
| 16 | +from copy import deepcopy |
| 17 | +from dataclasses import dataclass |
| 18 | +from pathlib import Path |
| 19 | +from typing import Sequence, Union |
| 20 | + |
| 21 | +import cocotb |
| 22 | +from cocotb.binary import BinaryValue |
| 23 | +from cocotb.clock import Clock |
| 24 | +from cocotb.handle import ModifiableObject, SimHandleBase |
| 25 | +from cocotb.runner import check_results_file, get_runner |
| 26 | +from cocotb.triggers import ClockCycles, Event, RisingEdge |
| 27 | +from cocotb_bus.scoreboard import Scoreboard |
| 28 | +from cocotb_xls import XLSChannel, XLSChannelDriver, XLSChannelMonitor |
| 29 | + |
| 30 | +from xls.common import runfiles |
| 31 | + |
| 32 | + |
| 33 | +@dataclass |
| 34 | +class SimulationData: |
| 35 | + """Auxiliary structure used to store design-related data for the simulation""" |
| 36 | + |
| 37 | + clock: Clock |
| 38 | + driver: XLSChannelDriver |
| 39 | + monitor: XLSChannelMonitor |
| 40 | + scoreboard: Scoreboard |
| 41 | + data_r: XLSChannel |
| 42 | + data_s: XLSChannel |
| 43 | + terminate: Event |
| 44 | + clk: ModifiableObject |
| 45 | + rst: ModifiableObject |
| 46 | + |
| 47 | + |
| 48 | +def init_sim(dut: SimHandleBase, data_to_recv: Sequence[BinaryValue]) -> SimulationData: |
| 49 | + """Extracts all design-related data required for simulation""" |
| 50 | + |
| 51 | + RECV_CHANNEL = "passthrough__data_r" |
| 52 | + SEND_CHANNEL = "passthrough__data_s" |
| 53 | + |
| 54 | + clock = Clock(dut.clk, 10, units="us") |
| 55 | + |
| 56 | + driver = XLSChannelDriver(dut, RECV_CHANNEL, dut.clk) |
| 57 | + monitor = XLSChannelMonitor(dut, SEND_CHANNEL, dut.clk) |
| 58 | + data_r = XLSChannel(dut, RECV_CHANNEL) |
| 59 | + data_s = XLSChannel(dut, SEND_CHANNEL) |
| 60 | + |
| 61 | + scoreboard = Scoreboard(dut, fail_immediately=True) |
| 62 | + scoreboard.add_interface(monitor, deepcopy(data_to_recv)) |
| 63 | + |
| 64 | + expected_packet_count = len(data_to_recv) |
| 65 | + terminate = Event("Received the last packet of data") |
| 66 | + |
| 67 | + def terminate_cb(_): |
| 68 | + if monitor.stats.received_transactions == expected_packet_count: |
| 69 | + terminate.set() |
| 70 | + |
| 71 | + monitor.add_callback(terminate_cb) |
| 72 | + |
| 73 | + return SimulationData( |
| 74 | + clock=clock, |
| 75 | + driver=driver, |
| 76 | + monitor=monitor, |
| 77 | + scoreboard=scoreboard, |
| 78 | + data_r=data_r, |
| 79 | + data_s=data_s, |
| 80 | + terminate=terminate, |
| 81 | + clk=dut.clk, |
| 82 | + rst=dut.rst, |
| 83 | + ) |
| 84 | + |
| 85 | + |
| 86 | +@cocotb.coroutine |
| 87 | +async def recv(clk, send_channel): |
| 88 | + """Cocotb coroutine that acts as a proc receiving data from a channel""" |
| 89 | + send_channel.rdy.setimmediatevalue(0) |
| 90 | + while True: |
| 91 | + send_channel.rdy.value = send_channel.vld.value |
| 92 | + await RisingEdge(clk) |
| 93 | + |
| 94 | + |
| 95 | +@cocotb.coroutine |
| 96 | +async def reset(clk, rst, cycles=1): |
| 97 | + """Cocotb coroutine that performs the reset""" |
| 98 | + rst.setimmediatevalue(1) |
| 99 | + await ClockCycles(clk, cycles) |
| 100 | + rst.value = 0 |
| 101 | + |
| 102 | + |
| 103 | +@cocotb.test(timeout_time=10, timeout_unit="ms") |
| 104 | +async def passthrough_test(dut): |
| 105 | + """Cocotb test of the Passthrough proc""" |
| 106 | + test_data = [BinaryValue(x, n_bits=32, bigEndian=False) for x in range(10)] |
| 107 | + sim = init_sim(dut, test_data) |
| 108 | + |
| 109 | + cocotb.start_soon(sim.clock.start()) |
| 110 | + await reset(sim.clk, sim.rst) |
| 111 | + |
| 112 | + cocotb.start_soon(recv(sim.clk, sim.data_s)) |
| 113 | + await sim.driver.send(test_data) |
| 114 | + await sim.terminate.wait() |
| 115 | + |
| 116 | + |
| 117 | +if __name__ == "__main__": |
| 118 | + runfiles._BASE_PATH = "com_icarus_iverilog" |
| 119 | + iverilog_path = Path(runfiles.get_path("iverilog")) |
| 120 | + vvp_path = Path(runfiles.get_path("vvp")) |
| 121 | + os.environ["PATH"] += os.pathsep + str(iverilog_path.parent) |
| 122 | + os.environ["PATH"] += os.pathsep + str(vvp_path.parent) |
| 123 | + |
| 124 | + runner = get_runner("icarus") |
| 125 | + runner.build( |
| 126 | + verilog_sources=["xls/examples/passthrough.v"], |
| 127 | + hdl_toplevel="passthrough", |
| 128 | + timescale=("1ns", "1ps"), |
| 129 | + waves=True, |
| 130 | + ) |
| 131 | + results_xml = runner.test( |
| 132 | + hdl_toplevel="passthrough", |
| 133 | + test_module=[Path(__file__).stem], |
| 134 | + waves=True, |
| 135 | + ) |
| 136 | + check_results_file(results_xml) |
0 commit comments