-
Notifications
You must be signed in to change notification settings - Fork 488
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce COCOTB_TRUST_INERTIAL_WRITES
Behavior and performance optimization by trusting inertial writes in the VPI/VHPI/FLI rather than grossing mimicing them by applying writes in ReadWrite callback.
- Loading branch information
Showing
8 changed files
with
188 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Introduced :envvar:`COCOTB_TRUST_INERTIAL_WRITES` to enable a mode where VPI/VHPI/FLI inertial writes are trusted to behave properly. Enabling this feature can lead to behavioral and noticable performance improvements. Some simulators do not handle writes properly, so use this option with caution. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Copyright cocotb contributors | ||
# Licensed under the Revised BSD License, see LICENSE for details. | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
import os | ||
|
||
_trust_inertial = "COCOTB_TRUST_INERTIAL_WRITES" in os.environ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Copyright cocotb contributors | ||
# Licensed under the Revised BSD License, see LICENSE for details. | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
|
||
.PHONY: override_tests | ||
override_tests: | ||
$(MAKE) sim | ||
$(MAKE) COCOTB_TRUST_INERTIAL_WRITES=1 sim | ||
|
||
MODULE := inertial_writes_tests | ||
|
||
include ../../designs/sample_module/Makefile |
124 changes: 124 additions & 0 deletions
124
tests/test_cases/test_inertial_writes/inertial_writes_tests.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Copyright cocotb contributors | ||
# Licensed under the Revised BSD License, see LICENSE for details. | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
import os | ||
|
||
import cocotb | ||
from cocotb._conf import _trust_inertial | ||
from cocotb.clock import Clock | ||
from cocotb.result import SimTimeoutError | ||
from cocotb.triggers import First, ReadOnly, ReadWrite, RisingEdge, Timer, with_timeout | ||
|
||
SIM_NAME = cocotb.SIM_NAME.lower() | ||
vhdl = os.environ.get("TOPLEVEL_LANG", "verilog").lower() == "vhdl" | ||
verilog = os.environ.get("TOPLEVEL_LANG", "verilog").lower() == "verilog" | ||
|
||
|
||
# Riviera's VHPI is skipped in all tests when COCOTB_TRUST_INERTIAL_WRITES mode is enable | ||
# because it behaves extremely erratically. Their scheduler has some serious issues. | ||
|
||
|
||
# Verilator currently only does vpiNoDelay writes. | ||
@cocotb.test( | ||
expect_error=SimTimeoutError | ||
if (SIM_NAME.startswith("verilator") and _trust_inertial) | ||
else (), | ||
skip=SIM_NAME.startswith("riviera") and vhdl and _trust_inertial, | ||
) | ||
async def test_writes_on_timer_seen_on_edge(dut): | ||
# steady state | ||
dut.clk.value = 0 | ||
await Timer(10, "ns") | ||
|
||
# inertial write on a signal | ||
dut.clk.value = 1 | ||
|
||
# check we can register an edge trigger on the signal we just changed because it hasn't taken effect yet | ||
await with_timeout(RisingEdge(dut.clk), 10, "ns") | ||
|
||
|
||
if _trust_inertial: | ||
expect_fail = False | ||
elif SIM_NAME.startswith(("riviera", "modelsim")) and vhdl: | ||
expect_fail = False | ||
elif SIM_NAME.startswith("verilator"): | ||
expect_fail = False | ||
else: | ||
expect_fail = True | ||
|
||
|
||
# Verilator currently only does vpiNoDelay writes, so this works. | ||
# Riviera and Questa on VHDL designs seem to apply inertial writes in this state immediately, | ||
# presumably because it's the NBA application region. | ||
# This test will fail because the ReadWrite write applicator task does inertial writes of it's own. | ||
@cocotb.test( | ||
expect_fail=expect_fail, | ||
skip=SIM_NAME.startswith("riviera") and vhdl and _trust_inertial, | ||
) | ||
async def test_read_back_in_readwrite(dut): | ||
# steady state | ||
dut.clk.value = 0 | ||
await Timer(10, "ns") | ||
|
||
# write in the "normal" phase | ||
dut.clk.value = 1 | ||
|
||
# assert we can read back what we wrote in the ReadWrite phase | ||
await ReadWrite() | ||
assert dut.clk.value == 1 | ||
|
||
|
||
if not _trust_inertial: | ||
expect_fail = False | ||
elif SIM_NAME.startswith(("icarus", "verilator")): | ||
expect_fail = True | ||
elif SIM_NAME.startswith("modelsim") and verilog: | ||
expect_fail = True | ||
elif SIM_NAME.startswith("xmsim") and vhdl: | ||
expect_fail = True | ||
|
||
|
||
# Verilator currently only does vpiNoDelay writes. | ||
# Icarus, Questa VPI, and Xcelium VHPI inertial writes aren't actually inertial. | ||
@cocotb.test( | ||
expect_fail=expect_fail, | ||
skip=SIM_NAME.startswith("riviera") and vhdl and _trust_inertial, | ||
) | ||
async def test_writes_dont_update_hdl_this_delta(dut): | ||
cocotb.start_soon(Clock(dut.clk, 10, "ns").start()) | ||
|
||
# assert steady state | ||
dut.stream_in_data.value = 0 | ||
await RisingEdge(dut.clk) | ||
await ReadOnly() | ||
assert dut.stream_out_data_registered.value == 0 | ||
|
||
# write on the clock edge | ||
await RisingEdge(dut.clk) | ||
dut.stream_in_data.value = 1 | ||
|
||
# ensure that the write data wasn't used on this clock cycle | ||
await ReadOnly() | ||
assert dut.stream_out_data_registered.value == 0 | ||
|
||
# ensure that the write data made it on the result of the next clock cycle | ||
await RisingEdge(dut.clk) | ||
await ReadOnly() | ||
assert dut.stream_out_data_registered.value == 1 | ||
|
||
|
||
# Verilator currently only does vpiNoDelay writes. | ||
# This test will fail because writes scheduled in Timer won't be applied until ReadWrite. | ||
@cocotb.test( | ||
expect_fail=not _trust_inertial | ||
or (SIM_NAME.startswith("verilator") and _trust_inertial), | ||
skip=SIM_NAME.startswith("riviera") and vhdl and _trust_inertial, | ||
) | ||
async def test_write_in_timer_seen_before_readwrite(dut): | ||
dut.clk.value = 0 | ||
await Timer(1, "ns") | ||
dut.clk.value = 1 | ||
t1 = RisingEdge(dut.clk) | ||
t2 = ReadWrite() | ||
t_res = await First(t1, t2) | ||
assert t_res is t1 |