blob: 80ab1508121ada5c8fbc11968ecbe675217b8984 [file] [log] [blame]
# Copyright 2025 Google LLC
#
# 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
#
# https://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 cocotb
from cocotb.clock import Clock
from cocotb.triggers import FallingEdge, RisingEdge, ClockCycles, with_timeout
import random
from kelvin_test_utils.TileLinkULInterface import TileLinkULInterface, create_a_channel_req
async def setup_dut(dut):
"""Common setup for all tests."""
clock = Clock(dut.clock, 10)
cocotb.start_soon(clock.start())
dut.reset.value = 1
await ClockCycles(dut.clock, 2)
dut.reset.value = 0
await RisingEdge(dut.clock)
@cocotb.test()
async def test_steering(dut):
"""Verify requests are steered to the correct device port."""
await setup_dut(dut)
N = 4 # This is hardcoded in the Chisel emitter for now
host_if = TileLinkULInterface(dut, host_if_name="io_tl_h")
device_ifs = [
TileLinkULInterface(dut, device_if_name=f"io_tl_d_{i}")
for i in range(N)
]
async def device_responder(device_if, i):
req_seen = await device_if.device_get_request()
await device_if.device_respond(opcode=0,
param=0,
size=req_seen["size"],
source=req_seen["source"])
# Start all device responders
for i in range(N):
cocotb.start_soon(device_responder(device_ifs[i], i))
for i in range(N):
dut.io_dev_select_i.value = i
req = create_a_channel_req(address=0x1000 + i * 0x100,
data=0x11223344 + i,
mask=0xF,
source=i)
await host_if.host_put(req)
response = await host_if.host_get_response()
assert response["source"] == i
# TODO(atv): Can we do this better?
# Allow some time for the device responder to process the request
await ClockCycles(dut.clock, 5)
@cocotb.test()
async def test_error_response(dut):
"""Verify error response for out-of-bounds dev_select."""
await setup_dut(dut)
N = 4 # This is hardcoded in the Chisel emitter for now
host_if = TileLinkULInterface(dut, host_if_name="io_tl_h")
# dev_select_i is NWD bits wide, where NWD = ceil(log2(N+1))
# So, a value of N should be out of bounds and trigger an error
dut.io_dev_select_i.value = N
req = create_a_channel_req(address=0xBAD,
data=0xBAD,
mask=0xF,
source=(1 << 6) - 1)
await host_if.host_put(req)
response = await host_if.host_get_response()
assert response["error"] == 1
assert response["source"] == req["source"]