Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dependency_support/pip_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ termcolor==1.1.0
psutil==5.7.0
portpicker==1.3.1
pyyaml==6.0.1
git+https://github.com/cocotb/cocotb.git@a853db95b0019db6796a6803aa94304bde743e4e
cocotb_bus==0.2.1

# Note: numpy and scipy version availability seems to differ between Ubuntu
# versions that we want to support (e.g. 18.04 vs 20.04), so we accept a
Expand Down
44 changes: 44 additions & 0 deletions xls/examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -641,3 +641,47 @@ filegroup(
srcs = glob(["*.x"]),
visibility = ["//xls:xls_internal"],
)

xls_dslx_library(
name = "passthrough_dslx",
srcs = [
"passthrough.x",
],
)

xls_dslx_verilog(
name = "passthrough_verilog",
codegen_args = {
"module_name": "passthrough",
"delay_model": "unit",
"pipeline_stages": "1",
"reset": "rst",
"use_system_verilog": "false",
"streaming_channel_data_suffix": "_data",
},
dslx_top = "Passthrough",
library = "passthrough_dslx",
verilog_file = "passthrough.v",
)

xls_dslx_test(
name = "passthrough_test",
dslx_test_args = {
"compare": "none",
},
library = ":passthrough_dslx",
)

py_test(
name = "passthrough_cocotb_test",
srcs = ["passthrough_cocotb_test.py"],
data = [
":passthrough_verilog",
"@com_icarus_iverilog//:iverilog",
"@com_icarus_iverilog//:vvp",
],
deps = [
"//xls/common:runfiles",
"//xls/simulation/cocotb:cocotb_xls",
],
)
67 changes: 67 additions & 0 deletions xls/examples/passthrough.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2023 The XLS Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A simple proc that sends back the information received on
// an input channel over an output channel.

import std

proc Passthrough {
data_r: chan<u32> in;
data_s: chan<u32> out;

init {()}

config(data_r: chan<u32> in, data_s: chan<u32> out) {
(data_r, data_s)
}

next(tok: token, state: ()) {
let (tok, data) = recv(tok, data_r);
let tok = send(tok, data_s, data);
}
}

const NUMBER_OF_TESTED_TRANSACTIONS = u32:10;

#[test_proc]
proc PassthroughTest {
terminator: chan<bool> out;
data_s: chan<u32> out;
data_r: chan<u32> in;

init { u32:0 }

config (terminator: chan<bool> out) {
let (data_s, data_r) = chan<u32>;
spawn Passthrough(data_r, data_s);
(terminator, data_s, data_r)
}

next(tok: token, count: u32) {
let data_to_send = count;
let tok = send(tok, data_s, data_to_send);
let (tok, received_data) = recv(tok, data_r);

trace_fmt!("send: {}, received: {}, in transaction {}",
data_to_send, received_data, count + u32:1);

assert_eq(data_to_send, received_data);

let do_send = (count == NUMBER_OF_TESTED_TRANSACTIONS - u32:1);
send_if(tok, terminator, do_send, true);

count + u32:1
}
}
136 changes: 136 additions & 0 deletions xls/examples/passthrough_cocotb_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Copyright 2023 The XLS Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from copy import deepcopy
from dataclasses import dataclass
from pathlib import Path
from typing import Sequence, Union

import cocotb
from cocotb.binary import BinaryValue
from cocotb.clock import Clock
from cocotb.handle import ModifiableObject, SimHandleBase
from cocotb.runner import check_results_file, get_runner
from cocotb.triggers import ClockCycles, Event, RisingEdge
from cocotb_bus.scoreboard import Scoreboard
from cocotb_xls import XLSChannel, XLSChannelDriver, XLSChannelMonitor

from xls.common import runfiles


@dataclass
class SimulationData:
"""Auxiliary structure used to store design-related data for the simulation"""

clock: Clock
driver: XLSChannelDriver
monitor: XLSChannelMonitor
scoreboard: Scoreboard
data_r: XLSChannel
data_s: XLSChannel
terminate: Event
clk: ModifiableObject
rst: ModifiableObject


def init_sim(dut: SimHandleBase, data_to_recv: Sequence[BinaryValue]) -> SimulationData:
"""Extracts all design-related data required for simulation"""

RECV_CHANNEL = "passthrough__data_r"
SEND_CHANNEL = "passthrough__data_s"

clock = Clock(dut.clk, 10, units="us")

driver = XLSChannelDriver(dut, RECV_CHANNEL, dut.clk)
monitor = XLSChannelMonitor(dut, SEND_CHANNEL, dut.clk)
data_r = XLSChannel(dut, RECV_CHANNEL)
data_s = XLSChannel(dut, SEND_CHANNEL)

scoreboard = Scoreboard(dut, fail_immediately=True)
scoreboard.add_interface(monitor, deepcopy(data_to_recv))

expected_packet_count = len(data_to_recv)
terminate = Event("Received the last packet of data")

def terminate_cb(_):
if monitor.stats.received_transactions == expected_packet_count:
terminate.set()

monitor.add_callback(terminate_cb)

return SimulationData(
clock=clock,
driver=driver,
monitor=monitor,
scoreboard=scoreboard,
data_r=data_r,
data_s=data_s,
terminate=terminate,
clk=dut.clk,
rst=dut.rst,
)


@cocotb.coroutine
async def recv(clk, send_channel):
"""Cocotb coroutine that acts as a proc receiving data from a channel"""
send_channel.rdy.setimmediatevalue(0)
while True:
send_channel.rdy.value = send_channel.vld.value
await RisingEdge(clk)


@cocotb.coroutine
async def reset(clk, rst, cycles=1):
"""Cocotb coroutine that performs the reset"""
rst.setimmediatevalue(1)
await ClockCycles(clk, cycles)
rst.value = 0


@cocotb.test(timeout_time=10, timeout_unit="ms")
async def passthrough_test(dut):
"""Cocotb test of the Passthrough proc"""
test_data = [BinaryValue(x, n_bits=32, bigEndian=False) for x in range(10)]
sim = init_sim(dut, test_data)

cocotb.start_soon(sim.clock.start())
await reset(sim.clk, sim.rst)

cocotb.start_soon(recv(sim.clk, sim.data_s))
await sim.driver.send(test_data)
await sim.terminate.wait()


if __name__ == "__main__":
runfiles._BASE_PATH = "com_icarus_iverilog"
iverilog_path = Path(runfiles.get_path("iverilog"))
vvp_path = Path(runfiles.get_path("vvp"))
os.environ["PATH"] += os.pathsep + str(iverilog_path.parent)
os.environ["PATH"] += os.pathsep + str(vvp_path.parent)

runner = get_runner("icarus")
runner.build(
verilog_sources=["xls/examples/passthrough.v"],
hdl_toplevel="passthrough",
timescale=("1ns", "1ps"),
waves=True,
)
results_xml = runner.test(
hdl_toplevel="passthrough",
test_module=[Path(__file__).stem],
waves=True,
)
check_results_file(results_xml)
30 changes: 30 additions & 0 deletions xls/simulation/cocotb/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2023 The XLS Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

load("@xls_pip_deps//:requirements.bzl", "requirement")

package(
default_visibility = ["//xls:xls_public"],
licenses = ["notice"], # Apache 2.0
)

py_library(
name = "cocotb_xls",
srcs = ["cocotb_xls.py"],
imports = ["."],
deps = [
requirement("cocotb"),
requirement("cocotb_bus"),
],
)
80 changes: 80 additions & 0 deletions xls/simulation/cocotb/cocotb_xls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright 2023 The XLS Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Sequence, Union

import cocotb
from cocotb.binary import BinaryValue
from cocotb.handle import SimHandleBase
from cocotb.triggers import RisingEdge
from cocotb_bus.bus import Bus
from cocotb_bus.drivers import BusDriver
from cocotb_bus.monitors import BusMonitor

Transaction = Union[BinaryValue, Sequence[BinaryValue]]

XLS_CHANNEL_SIGNALS = ["data", "rdy", "vld"]
XLS_CHANNEL_OPTIONAL_SIGNALS = []


class XLSChannel(Bus):
_signals = XLS_CHANNEL_SIGNALS
_optional_signals = XLS_CHANNEL_OPTIONAL_SIGNALS

def __init__(self, entity, name, **kwargs: Any):
super().__init__(entity, name, self._signals, self._optional_signals, **kwargs)


class XLSChannelDriver(BusDriver):
_signals = XLS_CHANNEL_SIGNALS
_optional_signals = XLS_CHANNEL_OPTIONAL_SIGNALS

def __init__(self, entity: SimHandleBase, name: str, clock: SimHandleBase, **kwargs: Any):
BusDriver.__init__(self, entity, name, clock, **kwargs)

self.bus.data.setimmediatevalue(0)
self.bus.vld.setimmediatevalue(0)

async def _driver_send(self, transaction: Transaction, sync: bool = True, **kwargs: Any) -> None:
if sync:
await RisingEdge(self.clock)

data_to_send = (transaction if isinstance(transaction, Sequence) else [transaction])

for word in data_to_send:
self.bus.vld.value = 1
self.bus.data.value = word

while True:
await RisingEdge(self.clock)
if self.bus.rdy.value:
break

self.bus.vld.value = 0


class XLSChannelMonitor(BusMonitor):
_signals = XLS_CHANNEL_SIGNALS
_optional_signals = XLS_CHANNEL_OPTIONAL_SIGNALS

def __init__(self, entity: SimHandleBase, name: str, clock: SimHandleBase, **kwargs: Any):
BusMonitor.__init__(self, entity, name, clock, **kwargs)

@cocotb.coroutine
async def _monitor_recv(self) -> None:
while True:
await RisingEdge(self.clock)
if self.bus.rdy.value:
vec = self.bus.data.value
self._recv(vec)