Skip to content
Merged
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
4 changes: 1 addition & 3 deletions verification/cocotb/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,6 @@ def i2c_target_fsm_verify(session, test_group, test_name, coverage, simulator):
"test_ibi_multi_queue",
"test_te_errors",
"test_tsco_violation",
"test_bus_idle",
],
)
@nox.parametrize("coverage", coverage_types)
Expand Down Expand Up @@ -342,7 +341,6 @@ def i3c_ahb_verify(session, test_group, test_name, coverage, simulator):
"test_ibi_multi_queue",
"test_te_errors",
"test_tsco_violation",
"test_bus_idle",
],
)
@nox.parametrize("coverage", coverage_types)
Expand Down Expand Up @@ -534,7 +532,7 @@ def recovery_pec_verify(session, test_group, test_name, coverage, simulator):
"test_ibi_multi_queue",
"test_te_errors",
"test_tsco_violation",
"test_bus_idle",
"test_bus_timers",
],
)
@nox.parametrize("coverage", coverage_types)
Expand Down
105 changes: 0 additions & 105 deletions verification/cocotb/top/lib_i3c_top/test_bus_idle.py

This file was deleted.

130 changes: 129 additions & 1 deletion verification/cocotb/top/lib_i3c_top/test_bus_timers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from interface import I3CTopTestInterface

import cocotb
from cocotb.triggers import ClockCycles
from cocotb.triggers import ClockCycles, FallingEdge, ReadOnly, RisingEdge, Timer

from common import VALID_I3C_ADDRESSES, log_seed

Expand Down Expand Up @@ -71,8 +71,12 @@ async def test_setup(dut, dynamic_addr=None):
speed=12.5e6,
)

i3c_controller.monitor_enable.clear()
await i3c_controller.monitor_idle.wait()

dut.sda_sim_target_i.setimmediatevalue(1)
dut.scl_sim_target_i.setimmediatevalue(1)
dut.peripheral_reset_done_i.value = 0

tb = I3CTopTestInterface(dut)
await tb.setup()
Expand Down Expand Up @@ -255,3 +259,127 @@ async def test_bus_timers_reset_on_hdr_entry(dut):
f"Expected bus_free=1 after STOP following HDR exit, got {state['free']}"

await tb.teardown()


@cocotb.test()
async def test_bus_idle(dut):
"""
Ensures target enters and leaves bus idle state after certain delays.
"""
i3c_controller, tb = await test_setup(dut)
bus_idle_sig = getattr(dut, BUS_TIMERS_PATH + ".bus_idle_o")

# 1. Generate a manual STOP condition to start the bus timers
# (The timer requires a STOP detection edge to restart its internal counters)
dut._log.info("Generating STOP condition (SDA 0->1 while SCL=1) to start bus timers")
i3c_controller.scl = 1
i3c_controller.sda = 0
await Timer(2, "us")
i3c_controller.sda = 1 # STOP edge
await Timer(2, "us")

# 2. Wait > 200us for T_IDLE. This forces bus_idle_o to toggle 0 -> 1.
dut._log.info("Waiting 210us for bus_idle_o to assert")
await Timer(210, "us")
assert bus_idle_sig.value == 1, "Target should be in bus idle state"

# 3. Generate a manual START condition to break the idle state (1 -> 0 toggle)
dut._log.info("Generating START condition (SDA 1->0 while SCL=1) to deassert bus_idle_o")
i3c_controller.sda = 0
await Timer(2, "us")
assert bus_idle_sig.value == 0, "Target should not be in bus idle state"

await tb.teardown()


@cocotb.test
async def test_exotic_idle_timings(dut):
"""
The bus conditions in the bus_timers module operate independently from each other
with each one configured by its own CSR. This introduces the unlikely possibility
of T_AVAL < T_FREE and T_IDLE < T_AVAL. This test exists to cover these conditions.
"""

i3c_controller, tb = await test_setup(dut)
T_FREE = tb.reg_map.I3C_EC.SOCMGMTIF.T_FREE_REG.base_addr
T_AVAL = tb.reg_map.I3C_EC.SOCMGMTIF.T_AVAL_REG.base_addr
T_IDLE = tb.reg_map.I3C_EC.SOCMGMTIF.T_IDLE_REG.base_addr
SIG_FREE = getattr(dut, BUS_TIMERS_PATH + ".bus_free_o")
SIG_AVAL = getattr(dut, BUS_TIMERS_PATH + ".bus_available_o")
SIG_IDLE = getattr(dut, BUS_TIMERS_PATH + ".bus_idle_o")
SIG_BUSY = getattr(dut, BUS_TIMERS_PATH + ".bus_busy_o")

# COND 1: BUS AVAILABLE, BUT NOT FREE NOR IDLE
await tb.write_csr(T_AVAL, int2dword(0x100), 4)
await tb.write_csr(T_FREE, int2dword(0x1000), 4)
await tb.write_csr(T_IDLE, int2dword(0x1000), 4)
await i3c_controller.send_start()
await i3c_controller.send_stop()
await FallingEdge(SIG_BUSY)
assert ~SIG_FREE.value and ~SIG_IDLE.value and SIG_AVAL.value

# COND 2: BUS IDLE, BUT NOT AVAILABLE NOR FREE
await tb.write_csr(T_AVAL, int2dword(0x1000), 4)
await tb.write_csr(T_FREE, int2dword(0x1000), 4)
await tb.write_csr(T_IDLE, int2dword(0x100), 4)
await i3c_controller.send_start()
await i3c_controller.send_stop()
await FallingEdge(SIG_BUSY)
assert ~SIG_FREE.value and SIG_IDLE.value and ~SIG_AVAL.value

await tb.teardown()


@cocotb.test
async def test_bus_edge_detectors(dut):
"""Setup different bus timing values and check if edge detectors react for changes as expected"""

i3c_controller, tb = await test_setup(dut)

SIG_STATE = dut.xi3c_wrapper.i3c.xcontroller.xbus_monitor
await i3c_controller.take_bus_control()

async def check_edge_occurred(edge, timing):
# Always +2 from input FFs and if timing > 0, the logic adds
# another 2 cycle delay. When timing == 0 it passthroughs instead
for _ in range(timing + (4 if timing > 0 else 2)):
assert ~edge.value
await RisingEdge(dut.clk_i)
await ReadOnly()
assert edge.value
await RisingEdge(dut.clk_i)
assert ~edge.value

async def check_edge_did_not_occur(edge, timing):
for _ in range(timing+10):
assert ~edge.value
await RisingEdge(dut.clk_i)

for timing in (0, 1, *random.sample(range(10, 500), k=5)):
# Redo boot_init with different timings
timings = {
"T_R": timing,
"T_F": timing,
"T_HD_DAT": timing,
"T_SU_DAT": timing,
}
await boot_init(tb, timings)

for line, edge, pos in [
(i3c_controller.scl_o, SIG_STATE.scl_negedge, False),
(i3c_controller.scl_o, SIG_STATE.scl_posedge, True),
(i3c_controller.sda_o, SIG_STATE.sda_negedge, False),
(i3c_controller.sda_o, SIG_STATE.sda_posedge, True),
]:
for test in (check_edge_occurred, check_edge_did_not_occur):
# Setup line
line.value = not pos
await ClockCycles(dut.clk_i, timing + 10) # make sure this setup edge is not the one detected

coro = await cocotb.start(test(edge, timing))
line.value = pos
await ClockCycles(dut.clk_i, timing+1 if test is check_edge_occurred else timing)
line.value = not pos
await coro

await tb.teardown()
3 changes: 1 addition & 2 deletions verification/testplan/source-maps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ testplans:
testpoints:
- name: "^(.*)$"
source: ".*verification/cocotb/top/lib_i3c_top/test_i3c_target.py"
# test_bus_idle here is for bus_idle_coverage
cocotb_xml: ".*verification/cocotb/top/i3c_axi/test_(i3c_target|bus_idle)(_[0-9]+)?.xml"
cocotb_xml: ".*verification/cocotb/top/i3c_axi/test_i3c_target(_[0-9]+)?.xml"
- name: 'Recovery mode tests'
testpoints:
- name: "^(.*)$"
Expand Down
21 changes: 0 additions & 21 deletions verification/testplan/top/target.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -328,26 +328,5 @@
tests: ["priv_write_tight_timing_sr"]
tags: ["top"]
}
{
name: bus_idle
desc:
'''
Generate STOP condition, wait for over 200us and ensure the target entered idle state. Then
generate START condition and ensure target left idle state.
'''
tests: ["bus_idle"]
tags: ["top"]
}
{
name: exotic_idle_timings
desc:
'''
The bus conditions in the bus_timers module operate independently from each other
with each one configured by its own CSR. This introduces the unlikely possibility
of T_AVAL < T_FREE and T_IDLE < T_AVAL. This test exists to cover these conditions.
'''
tests: ["exotic_idle_timings"]
tags: ["top"]
}
]
}
33 changes: 33 additions & 0 deletions verification/testplan/top/target_bus_timers.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,38 @@
tests: ["bus_timers_reset_on_hdr_entry"]
tags: ["top"]
}
{
name: bus_idle
desc:
'''
Generate STOP condition, wait for over 200us and ensure the target entered idle state. Then
generate START condition and ensure target left idle state.
'''
tests: ["bus_idle"]
tags: ["top"]
}
{
name: exotic_idle_timings
desc:
'''
The bus conditions in the bus_timers module operate independently from each other
with each one configured by its own CSR. This introduces the unlikely possibility
of T_AVAL < T_FREE and T_IDLE < T_AVAL. This test exists to cover these conditions.
'''
tests: ["exotic_idle_timings"]
tags: ["top"]
}
{
name: bus_edge_detectors
desc:
'''
Sets up different values of T_R and T_F timings and for each detector (SDA posedge,
SDA negedge, SCL posedge, SCL negedge) verifies both whether the edge is correctly
detected when held for appropriate duration and whether it doesn't get detected when
deasserted before the timing duration.
'''
tests: ["bus_edge_detectors"]
tags: ["top"]
}
]
}
Loading