| # 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 |
| # |
| # 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 cocotb |
| from cocotb.clock import Clock |
| from cocotb.triggers import RisingEdge, ClockCycles, with_timeout |
| |
| from kelvin_test_utils.TileLinkULInterface import TileLinkULInterface, create_a_channel_req |
| from kelvin_test_utils.secded_golden import get_cmd_intg, get_data_intg, get_rsp_intg |
| |
| |
| def create_d_channel_rsp(opcode, |
| data, |
| size, |
| source, |
| param=0, |
| sink=0, |
| error=False): |
| """Creates a standard TileLink-UL D-channel response dictionary.""" |
| return { |
| "opcode": opcode, |
| "param": param, |
| "size": size, |
| "source": source, |
| "sink": sink, |
| "data": data, |
| "error": error, |
| "user": { |
| "rsp_intg": 0, |
| "data_intg": 0 |
| } |
| } |
| |
| |
| async def setup_dut(dut): |
| """Common setup for all tests.""" |
| clock = Clock(dut.clock, 10, unit="us") |
| 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_request_integrity_gen(dut): |
| """Test that the RequestIntegrityGen module generates correct integrity.""" |
| await setup_dut(dut) |
| |
| # Drive the input A-channel |
| req = create_a_channel_req(address=0x1000, |
| data=0x112233445566778899aabbccddeeff00, |
| mask=0xFFFF, |
| width=128) |
| dut.io_req_gen_a_i_valid.value = 1 |
| dut.io_req_gen_a_i_bits_opcode.value = req["opcode"] |
| dut.io_req_gen_a_i_bits_param.value = req["param"] |
| dut.io_req_gen_a_i_bits_size.value = req["size"] |
| dut.io_req_gen_a_i_bits_source.value = req["source"] |
| dut.io_req_gen_a_i_bits_address.value = req["address"] |
| dut.io_req_gen_a_i_bits_mask.value = req["mask"] |
| dut.io_req_gen_a_i_bits_data.value = req["data"] |
| dut.io_req_gen_a_i_bits_user_cmd_intg.value = get_cmd_intg(req) |
| dut.io_req_gen_a_i_bits_user_data_intg.value = get_data_intg(req["data"], |
| width=128) |
| dut.io_req_gen_a_i_bits_user_rsvd.value = 0 |
| dut.io_req_gen_a_i_bits_user_instr_type.value = 0 |
| |
| # Signal that we are ready to accept the output. |
| dut.io_req_gen_a_o_ready.value = 1 |
| |
| # TODO: Timeout loop |
| await RisingEdge(dut.clock) |
| |
| # Check the output A-channel |
| assert dut.io_req_gen_a_o_valid.value |
| assert dut.io_req_gen_a_o_bits_opcode.value == req["opcode"] |
| assert dut.io_req_gen_a_o_bits_address.value == req["address"] |
| assert dut.io_req_gen_a_o_bits_data.value == req["data"] |
| |
| assert dut.io_req_gen_a_o_bits_user_cmd_intg.value == get_cmd_intg(req) |
| assert dut.io_req_gen_a_o_bits_user_data_intg.value == get_data_intg( |
| req["data"], width=128) |
| |
| |
| @cocotb.test() |
| async def test_request_integrity_check(dut): |
| """Test that the RequestIntegrityCheck module correctly identifies faults.""" |
| await setup_dut(dut) |
| req = create_a_channel_req(address=0x1000, |
| data=0x112233445566778899aabbccddeeff00, |
| mask=0xFFFF, |
| width=128) |
| |
| # --- Transaction 1: Correct integrity --- |
| dut.io_req_check_a_i_valid.value = 1 |
| dut.io_req_check_a_i_bits_opcode.value = req["opcode"] |
| dut.io_req_check_a_i_bits_param.value = req["param"] |
| dut.io_req_check_a_i_bits_size.value = req["size"] |
| dut.io_req_check_a_i_bits_source.value = req["source"] |
| dut.io_req_check_a_i_bits_address.value = req["address"] |
| dut.io_req_check_a_i_bits_mask.value = req["mask"] |
| dut.io_req_check_a_i_bits_data.value = req["data"] |
| dut.io_req_check_a_i_bits_user_cmd_intg.value = get_cmd_intg(req) |
| dut.io_req_check_a_i_bits_user_data_intg.value = get_data_intg(req["data"], |
| width=128) |
| dut.io_req_check_a_i_bits_user_rsvd.value = 0 |
| dut.io_req_check_a_i_bits_user_instr_type.value = 0 |
| |
| for _ in range(10): |
| if dut.io_req_check_a_i_ready.value: |
| break |
| await RisingEdge(dut.clock) |
| else: |
| assert False, "Timeout waiting for dut.io_req_check_a_i_ready" |
| |
| await RisingEdge(dut.clock) |
| dut.io_req_check_a_i_valid.value = 0 |
| await RisingEdge(dut.clock) |
| assert not dut.io_req_check_fault.value |
| await ClockCycles(dut.clock, 5) # Delay for clarity in logs/waves |
| |
| # --- Transaction 2: Command integrity fault --- |
| dut.io_req_check_a_i_valid.value = 1 |
| dut.io_req_check_a_i_bits_data.value = req["data"] |
| correct_cmd_intg = get_cmd_intg(req) |
| dut.io_req_check_a_i_bits_user_cmd_intg.value = ~correct_cmd_intg & 0x7F |
| |
| for _ in range(10): |
| if dut.io_req_check_a_i_ready.value: |
| break |
| await RisingEdge(dut.clock) |
| else: |
| assert False, "Timeout waiting for dut.io_req_check_a_i_ready" |
| |
| await RisingEdge(dut.clock) |
| dut.io_req_check_a_i_valid.value = 0 |
| await RisingEdge(dut.clock) |
| assert dut.io_req_check_fault.value |
| await ClockCycles(dut.clock, 5) # Delay for clarity in logs/waves |
| |
| # --- Transaction 3: Data integrity fault --- |
| dut.io_req_check_a_i_valid.value = 1 |
| dut.io_req_check_a_i_bits_data.value = req["data"] |
| dut.io_req_check_a_i_bits_user_cmd_intg.value = get_cmd_intg( |
| req) # Restore cmd_intg |
| correct_data_intg = get_data_intg(req["data"], width=128) |
| dut.io_req_check_a_i_bits_user_data_intg.value = ~correct_data_intg & 0x7F |
| |
| for _ in range(10): |
| if dut.io_req_check_a_i_ready.value: |
| break |
| await RisingEdge(dut.clock) |
| else: |
| assert False, "Timeout waiting for dut.io_req_check_a_i_ready" |
| |
| await RisingEdge(dut.clock) |
| dut.io_req_check_a_i_valid.value = 0 |
| await RisingEdge(dut.clock) |
| assert dut.io_req_check_fault.value |
| await RisingEdge(dut.clock) |
| |
| |
| @cocotb.test() |
| async def test_response_integrity_gen(dut): |
| """Test that the ResponseIntegrityGen module generates correct integrity.""" |
| await setup_dut(dut) |
| |
| # Drive the input D-channel |
| rsp = create_d_channel_rsp(opcode=1, |
| data=0x112233445566778899aabbccddeeff00, |
| size=4, |
| source=1) |
| dut.io_rsp_gen_d_i_valid.value = 1 |
| dut.io_rsp_gen_d_i_bits_opcode.value = rsp["opcode"] |
| dut.io_rsp_gen_d_i_bits_param.value = rsp["param"] |
| dut.io_rsp_gen_d_i_bits_size.value = rsp["size"] |
| dut.io_rsp_gen_d_i_bits_source.value = rsp["source"] |
| dut.io_rsp_gen_d_i_bits_sink.value = rsp["sink"] |
| dut.io_rsp_gen_d_i_bits_data.value = rsp["data"] |
| dut.io_rsp_gen_d_i_bits_error.value = rsp["error"] |
| |
| # Signal that we are ready to accept the output. |
| dut.io_rsp_gen_d_o_ready.value = 1 |
| |
| await RisingEdge(dut.clock) |
| |
| # Check the output D-channel |
| assert dut.io_rsp_gen_d_o_valid.value |
| assert dut.io_rsp_gen_d_o_bits_opcode.value == rsp["opcode"] |
| assert dut.io_rsp_gen_d_o_bits_data.value == rsp["data"] |
| |
| assert dut.io_rsp_gen_d_o_bits_user_rsp_intg.value == get_rsp_intg(rsp) |
| assert dut.io_rsp_gen_d_o_bits_user_data_intg.value == get_data_intg( |
| rsp["data"], width=128) |
| |
| |
| @cocotb.test() |
| async def test_response_integrity_check(dut): |
| """Test that the ResponseIntegrityCheck module correctly identifies faults.""" |
| await setup_dut(dut) |
| rsp = create_d_channel_rsp(opcode=1, |
| data=0x112233445566778899aabbccddeeff00, |
| size=4, |
| source=1) |
| |
| # --- Transaction 1: Correct integrity --- |
| dut.io_rsp_check_d_i_valid.value = 1 |
| dut.io_rsp_check_d_i_bits_opcode.value = rsp["opcode"] |
| dut.io_rsp_check_d_i_bits_param.value = rsp["param"] |
| dut.io_rsp_check_d_i_bits_size.value = rsp["size"] |
| dut.io_rsp_check_d_i_bits_source.value = rsp["source"] |
| dut.io_rsp_check_d_i_bits_sink.value = rsp["sink"] |
| dut.io_rsp_check_d_i_bits_data.value = rsp["data"] |
| dut.io_rsp_check_d_i_bits_error.value = rsp["error"] |
| dut.io_rsp_check_d_i_bits_user_rsp_intg.value = get_rsp_intg(rsp) |
| dut.io_rsp_check_d_i_bits_user_data_intg.value = get_data_intg(rsp["data"], |
| width=128) |
| |
| for _ in range(10): |
| if dut.io_rsp_check_d_i_ready.value: |
| break |
| await RisingEdge(dut.clock) |
| else: |
| assert False, "Timeout waiting for dut.io_rsp_check_d_i_ready" |
| await RisingEdge(dut.clock) |
| dut.io_rsp_check_d_i_valid.value = 0 |
| await RisingEdge(dut.clock) |
| assert not dut.io_rsp_check_fault.value |
| await ClockCycles(dut.clock, 5) # Delay for clarity in logs/waves |
| |
| # --- Transaction 2: Response integrity fault --- |
| dut.io_rsp_check_d_i_valid.value = 1 |
| dut.io_rsp_check_d_i_bits_data.value = rsp["data"] # Keep data same |
| correct_rsp_intg = get_rsp_intg(rsp) |
| dut.io_rsp_check_d_i_bits_user_rsp_intg.value = ~correct_rsp_intg & 0x7F |
| |
| for _ in range(10): |
| if dut.io_rsp_check_d_i_ready.value: |
| break |
| await RisingEdge(dut.clock) |
| else: |
| assert False, "Timeout waiting for dut.io_rsp_check_d_i_ready" |
| await RisingEdge(dut.clock) |
| dut.io_rsp_check_d_i_valid.value = 0 |
| await RisingEdge(dut.clock) |
| assert dut.io_rsp_check_fault.value |
| await ClockCycles(dut.clock, 5) # Delay for clarity in logs/waves |
| |
| # --- Transaction 3: Data integrity fault --- |
| dut.io_rsp_check_d_i_valid.value = 1 |
| dut.io_rsp_check_d_i_bits_data.value = rsp["data"] |
| dut.io_rsp_check_d_i_bits_user_rsp_intg.value = get_rsp_intg( |
| rsp) # Restore rsp_intg |
| correct_data_intg = get_data_intg(rsp["data"], width=128) |
| dut.io_rsp_check_d_i_bits_user_data_intg.value = ~correct_data_intg & 0x7F |
| |
| for _ in range(10): |
| if dut.io_rsp_check_d_i_ready.value: |
| break |
| await RisingEdge(dut.clock) |
| else: |
| assert False, "Timeout waiting for dut.io_rsp_check_d_i_ready" |
| await RisingEdge(dut.clock) |
| dut.io_rsp_check_d_i_valid.value = 0 |
| await RisingEdge(dut.clock) |
| assert dut.io_rsp_check_fault.value |
| await RisingEdge(dut.clock) |