Support IEEE-compliant force/release handling (#7391)

This commit is contained in:
Artur Bieniek 2026-04-21 17:54:42 +02:00 committed by GitHub
parent 280cff06f3
commit a1a8b9624c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1667 additions and 779 deletions

258
include/verilated_force.h Normal file
View File

@ -0,0 +1,258 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2026-2026 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilator: Runtime support for force/release statements
///
/// This file provides runtime data structures for efficient dynamic
/// resolution of force/release statements. A sorted list of active
/// forces is maintained that can be efficiently queried and modified
/// at runtime.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_FORCE_H_
#define VERILATOR_VERILATED_FORCE_H_
#include "verilatedos.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <vector>
template <typename T>
using VlForceBaseType = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
// VlForceRead - Helper functions to read a forced value
//
// These functions combine original value with forced values based on
// VlForceVec entries.
// This achieves O(k) complexity where k = number of active forces.
template <typename T>
struct VlForceTypeInfo final {
using Type = VlForceBaseType<T>;
static constexpr bool bitwise
= std::is_integral<Type>::value || std::is_enum<Type>::value || VlIsVlWide<Type>::value;
static constexpr bool unpackedArray = false;
};
template <typename T>
struct VlForceArrayIndexer final {
static constexpr std::size_t size = 1;
static T& elem(T& value, std::size_t) { return value; }
};
template <typename T, std::size_t N>
struct VlForceArrayIndexer<VlUnpacked<T, N>> final {
static constexpr std::size_t size = N * VlForceArrayIndexer<T>::size;
static auto& elem(VlUnpacked<T, N>& array, std::size_t index) {
constexpr std::size_t subSize = VlForceArrayIndexer<T>::size;
return VlForceArrayIndexer<T>::elem(array[index / subSize], index % subSize);
}
};
template <typename T, std::size_t N>
struct VlForceTypeInfo<VlUnpacked<T, N>> final {
using Type = VlUnpacked<T, N>;
static constexpr bool bitwise = false;
static constexpr bool unpackedArray = true;
};
template <typename T, bool = std::is_enum<T>::value>
struct VlForceStorageTypeOf final {
using type = typename std::make_unsigned<T>::type;
};
template <typename T>
struct VlForceStorageTypeOf<T, true> final {
using type = typename std::make_unsigned<typename std::underlying_type<T>::type>::type;
};
template <typename T>
using VlForceStorageType = typename VlForceStorageTypeOf<VlForceBaseType<T>>::type;
//=============================================================================
// VlForceVec - Vector of active force entries for a signal
//
// This class maintains a sorted vector of non-overlapping force entries.
// When a new force is added, it removes or trims existing entries that
// overlap with the new range.
//
// The generated code will:
// 1. Use addForce/release to update the active forces
// 2. Call a generated read function that iterates entries and evaluates RHS
class VlForceVec final {
private:
struct Entry final {
int m_lsb; // Inclusive lower bit
int m_msb; // Inclusive upper bit
int m_rhsLsb; // Destination index that maps to RHS index 0
const void* m_rhsDatap; // Pointer to RHS storage
bool operator<(const Entry& other) const { return m_msb < other.m_msb; }
};
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
std::vector<Entry>::iterator trimEntries(int lsb, int msb) {
auto it = std::lower_bound(m_entries.begin(), m_entries.end(), lsb,
[](const Entry& e, int bit) { return e.m_msb < bit; });
while (it != m_entries.end() && it->m_lsb <= msb) {
if (it->m_lsb < lsb && it->m_msb > msb) {
const Entry right{msb + 1, it->m_msb, it->m_rhsLsb, it->m_rhsDatap};
it->m_msb = lsb - 1;
return m_entries.insert(++it, right);
}
if (it->m_lsb < lsb) {
it->m_msb = lsb - 1;
++it;
continue;
}
if (it->m_msb > msb) {
it->m_lsb = msb + 1;
return it;
}
it = m_entries.erase(it);
}
return it;
}
static QData extractRhsChunk(const Entry& entry, int rhsLsb, int width) {
assert(width > 0 && width <= VL_QUADSIZE);
assert(rhsLsb >= 0);
const QData mask = static_cast<QData>(VL_MASK_Q(width));
const int rhsWidth = entry.m_msb - entry.m_rhsLsb + 1;
if (rhsWidth <= VL_QUADSIZE) {
const QData rhsVal = static_cast<QData>(*static_cast<const QData*>(entry.m_rhsDatap));
return (rhsVal >> rhsLsb) & mask;
}
const EData* const rhswp = static_cast<const EData*>(entry.m_rhsDatap);
return VL_SEL_QWII(rhsWidth, rhswp, rhsLsb, width) & mask;
}
template <typename T>
static T applyBits(T cur, const Entry& entry, int lsb, int width, int rhsLsb) {
const T lowMask = static_cast<T>(VL_MASK_Q(width));
const T mask = static_cast<T>(lowMask << lsb);
const T rhsBits = static_cast<T>(
(static_cast<T>(extractRhsChunk(entry, rhsLsb, width)) & lowMask) << lsb);
return static_cast<T>((cur & ~mask) | (rhsBits & mask));
}
template <typename T>
static typename std::enable_if<VlIsVlWide<T>::value, T>::type applyEntry(T result,
const Entry& entry) {
EData* const reswp = result.data();
const int lword = VL_BITWORD_E(entry.m_lsb);
const int hword = VL_BITWORD_E(entry.m_msb);
for (int word = lword; word <= hword; ++word) {
const int wordLsb = word * VL_EDATASIZE;
const int segLsb = std::max(entry.m_lsb, wordLsb);
const int segMsb = std::min(entry.m_msb, wordLsb + VL_EDATASIZE - 1);
const int segWidth = segMsb - segLsb + 1;
const int bitOffset = segLsb - wordLsb;
const int rhsLsb = segLsb - entry.m_rhsLsb;
reswp[word] = applyBits(reswp[word], entry, bitOffset, segWidth, rhsLsb);
}
return result;
}
template <typename T>
static typename std::enable_if<!VlIsVlWide<T>::value && VlForceTypeInfo<T>::bitwise, T>::type
applyEntry(T result, const Entry& entry) {
using U = VlForceStorageType<T>;
const int width = entry.m_msb - entry.m_lsb + 1;
const int bits = static_cast<int>(sizeof(U) * 8);
const int rhsLsb = entry.m_lsb - entry.m_rhsLsb;
const QData rhsChunk = extractRhsChunk(entry, rhsLsb, width);
if (width >= bits) return static_cast<T>(static_cast<U>(rhsChunk));
return static_cast<T>(
applyBits(static_cast<U>(result), entry, entry.m_lsb, width, rhsLsb));
}
template <typename T>
static typename std::enable_if<!VlForceTypeInfo<T>::bitwise, T>::type
applyEntry(T result, const Entry& entry) {
static_cast<void>(result);
return *static_cast<const VlForceBaseType<T>*>(entry.m_rhsDatap);
}
public:
VlForceVec() = default;
template <typename T>
T read(T val) const {
if VL_CONSTEXPR_CXX17 (VlForceTypeInfo<T>::unpackedArray) {
// Handling the case of a nested flattened array using recursion
using ElemRef
= decltype(VlForceArrayIndexer<T>::elem(val, static_cast<std::size_t>(0)));
using Elem = VlForceBaseType<ElemRef>;
const int total = static_cast<int>(VlForceArrayIndexer<T>::size);
for (const auto& entry : m_entries) {
const Elem* const rhsBasep = static_cast<const Elem*>(entry.m_rhsDatap);
const int startIdx = entry.m_lsb;
const int endIdx = entry.m_msb;
for (int idx = startIdx; idx <= endIdx; idx++) {
const int rhsIndex = idx - entry.m_rhsLsb;
const std::size_t uidx = static_cast<std::size_t>(idx);
VlForceArrayIndexer<T>::elem(val, uidx) = rhsBasep[rhsIndex];
}
}
return val;
}
for (const auto& entry : m_entries) { val = applyEntry(val, entry); }
return val;
}
template <typename T>
T readIndex(T origVal, int index) const {
if (m_entries.empty()) return origVal;
const auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index,
[](const Entry& e, int idx) { return e.m_msb < idx; });
if (it != m_entries.end() && it->m_lsb <= index) {
const int rhsIndex = index - it->m_rhsLsb;
const T* const rhsBasep = static_cast<const T*>(it->m_rhsDatap);
return rhsBasep[rhsIndex];
}
return origVal;
}
void addForce(int lsb, int msb, const void* rhsDatap, int rhsLsb) {
assert(lsb <= msb);
assert(rhsDatap);
assert(rhsLsb <= lsb);
auto it = trimEntries(lsb, msb);
m_entries.insert(it, {lsb, msb, rhsLsb, rhsDatap});
}
void release(int lsb, int msb) {
assert(lsb <= msb);
trimEntries(lsb, msb);
}
void touch() {}
};
#endif // guard

View File

@ -809,6 +809,11 @@ public:
EVENT_FIRE,
EVENT_IS_FIRED,
EVENT_IS_TRIGGERED,
FORCE_ADD,
FORCE_READ,
FORCE_READ_INDEX,
FORCE_RELEASE,
FORCE_TOUCH,
FORK_DONE,
FORK_INIT,
FORK_JOIN,
@ -955,6 +960,11 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
{EVENT_FIRE, "fire", false}, \
{EVENT_IS_FIRED, "isFired", true}, \
{EVENT_IS_TRIGGERED, "isTriggered", true}, \
{FORCE_ADD, "addForce", false}, \
{FORCE_READ, "read", true}, \
{FORCE_READ_INDEX, "readIndex", true}, \
{FORCE_RELEASE, "release", false}, \
{FORCE_TOUCH, "touch", false}, \
{FORK_DONE, "done", false}, \
{FORK_INIT, "init", false}, \
{FORK_JOIN, "join", false}, \

View File

@ -663,6 +663,7 @@ class EmitCHeader final : public EmitCConstInit {
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
if (v3Global.usesForce()) puts("#include \"verilated_force.h\"\n");
std::set<string> cuse_set;
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,7 @@ class V3Global final {
bool m_usesProbDist = false; // Uses $dist_*
bool m_usesStdPackage = false; // Design uses the std package
bool m_usesTiming = false; // Design uses timing constructs
bool m_usesForce = false; // Design uses force/release statements
bool m_usesZeroDelay = false; // Design uses #0 delay (or non-constant delay)
bool m_hasForceableSignals = false; // Need to apply V3Force pass
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
@ -205,6 +206,8 @@ public:
void setUsesZeroDelay() { m_usesZeroDelay = true; }
bool hasForceableSignals() const { return m_hasForceableSignals; }
void setHasForceableSignals() { m_hasForceableSignals = true; }
bool usesForce() const { return m_usesForce; }
void setUsesForce() { m_usesForce = true; }
bool hasSystemCSections() const VL_MT_SAFE { return m_hasSystemCSections; }
void setHasSystemCSections() { m_hasSystemCSections = true; }
V3HierGraph* hierGraphp() const { return m_hierGraphp; }

View File

@ -263,6 +263,10 @@ class PremitVisitor final : public VNVisitor {
iterateChildren(nodep);
checkNode(nodep);
}
void visit(AstCMethodHard* nodep) override {
iterateChildren(nodep);
checkNode(nodep);
}
void visit(AstCvtArrayToPacked* nodep) override {
iterateChildren(nodep);
checkNode(nodep);

View File

@ -403,9 +403,9 @@ module t (
wire logic [63:0] volatile_packed_out_of_cycle /* verilator forceable */ = rand_a;
assign VOLATILE_PACKED_OUT_OF_CYCLE = volatile_packed_out_of_cycle ^ 64'(VOLATILE_PACKED_OUT_OF_CYCLE[63:1]);
wire logic [2:0] volatile_packed_in_cycle /* verilator forceable */;
// verilator lint_off UNOPTFLAT
`signal(VOLATILE_PACKED_IN_CYCLE, 3);
wire logic [2:0] volatile_packed_in_cycle /* verilator forceable */;
assign volatile_packed_in_cycle = rand_a[2:0] ^ 3'(volatile_packed_in_cycle[2:1]);
assign VOLATILE_PACKED_IN_CYCLE = volatile_packed_in_cycle;
// verilator lint_on

View File

@ -10,14 +10,29 @@
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t_assert;
logic clk;
logic zeroize;
logic [7:0] key_mem [0:0];
assert property (@(posedge clk) zeroize |=> (key_mem[0] == 0));
initial force zeroize = 0;
endmodule
module t (
input clk
);
t_assert t_assert_i();
integer cyc = 0;
localparam logic [95:0] WIDE_INIT = 96'h12345678_9abcdef0_13579bdf;
localparam logic [94:0] WIDE_FORCE95 = {3'b101, 32'h12345678, 32'h89abcdef, 28'h2468ace};
reg [3:0] in;
tri [3:0] bus = in;
logic [95:0] wide_src;
wire [95:0] wide_bus = wide_src;
logic [7:0] unpacked [0:3];
int never_driven;
int never_forced;
@ -98,6 +113,25 @@ module t (
`checkh(bus, 4'b0101);
end
//
else if (cyc == 35) begin
force bus = 4'b1111;
end
else if (cyc == 36) begin
`checkh(bus, 4'b1111);
force bus[3:1] = 3'b000;
end
else if (cyc == 37) begin
`checkh(bus, 4'b0001);
release bus[2];
end
else if (cyc == 38) begin
`checkh(bus, 4'b0101);
release bus;
end
else if (cyc == 39) begin
`checkh(bus, 4'b0101);
end
//
else if (cyc == 40) begin
r <= 1.25;
end
@ -117,6 +151,66 @@ module t (
`checkr(r, 2.5);
end
//
else if (cyc == 50) begin
wide_src <= WIDE_INIT;
end
else if (cyc == 51) begin
`checkh(wide_bus, WIDE_INIT);
end
else if (cyc == 52) begin
force wide_bus[95:1] = WIDE_FORCE95;
end
else if (cyc == 53) begin
`checkh(wide_bus, {WIDE_FORCE95, WIDE_INIT[0]});
end
else if (cyc == 54) begin
release wide_bus[95:1];
end
else if (cyc == 55) begin
`checkh(wide_bus, WIDE_INIT);
end
//
else if (cyc == 60) begin
unpacked[0] <= 8'h10;
unpacked[1] <= 8'h20;
unpacked[2] <= 8'h30;
unpacked[3] <= 8'h40;
end
else if (cyc == 61) begin
`checkh(unpacked[0], 8'h10);
`checkh(unpacked[1], 8'h20);
`checkh(unpacked[2], 8'h30);
`checkh(unpacked[3], 8'h40);
end
else if (cyc == 62) begin
force unpacked[1] = 8'hb1;
force unpacked[2] = 8'hc2;
end
else if (cyc == 63) begin
`checkh(unpacked[0], 8'h10);
`checkh(unpacked[1], 8'hb1);
`checkh(unpacked[2], 8'hc2);
`checkh(unpacked[3], 8'h40);
end
else if (cyc == 64) begin
release unpacked[1];
release unpacked[2];
end
else if (cyc == 65) begin
`checkh(unpacked[0], 8'h10);
`checkh(unpacked[1], 8'hb1);
`checkh(unpacked[2], 8'hc2);
`checkh(unpacked[3], 8'h40);
unpacked[1] <= 8'h21;
unpacked[2] <= 8'h32;
end
else if (cyc == 66) begin
`checkh(unpacked[0], 8'h10);
`checkh(unpacked[1], 8'h21);
`checkh(unpacked[2], 8'h32);
`checkh(unpacked[3], 8'h40);
end
//
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;

View File

@ -1,4 +0,0 @@
%Error: t/t_force_chained.v:30: got='h0 exp='h00000001
%Error: t/t_force_chained.v:36: got='h0 exp='h00000002
%Error: t/t_force_chained.v:43: got='h0 exp='h00000003
%Error: t/t_force_chained.v:49: got='h0 exp='h00000003

View File

@ -13,6 +13,6 @@ test.scenarios('vlt')
test.compile(verilator_flags2=["--binary"])
test.execute(expect_filename=test.golden_filename)
test.execute()
test.passes()

View File

@ -4,10 +4,10 @@
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop // TODO
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: off
module t;
reg [1:0] a;
@ -26,7 +26,6 @@ module t;
#1;
`checkh(a, 1);
`checkh(b, 1);
// TODO implement inter-dependency resolution between force statements
`checkh(c, 1);
a = 2;

18
test_regress/t/t_force_cond.py Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Antmicro.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
`define IMPURE_ONE |($random | $random)
module t;
reg [1:0] a = 2;
bit [1:0] b = 1;
bit [1:0] c = 0;
initial begin
force a = b;
if (`IMPURE_ONE == 1) force a = c;
if (`IMPURE_ONE == 0) force a = b;
c = 3;
b = 2;
#1;
`checkh(a, 3);
if (`IMPURE_ONE == 1) force a = b;
if (`IMPURE_ONE == 0) force a = c;
c = 2;
b = 1;
#1;
`checkh(a, 1);
#1 $finish;
end
endmodule

View File

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_force_forceable_readwrite_unsup.v:15:14: Unsupported: Signals used via read-write reference cannot be forced
15 | take_ref(value);
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -4,12 +4,12 @@
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2024 Wilson Snyder
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.scenarios('vlt')
test.lint(fails=True, expect_filename=test.golden_filename)

View File

@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t;
logic value /* verilator forceable */;
task take_ref(ref logic i);
// verilator no_inline_task
endtask
initial begin
take_ref(value);
force value = 1'b1;
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,49 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
module t (
input clk
);
integer cyc = 0;
logic src_1 = 0;
logic [7:0] src_8 = 8'h10;
logic dst_1 /* verilator forceable */;
logic [7:0] dst_8 /* verilator forceable */;
always @* dst_1 = src_1;
always @* dst_8 = src_8;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
0: begin
force dst_1 = src_1;
force dst_8 = src_8 ^ 8'hf0;
`checkh(dst_1, 1'b0);
`checkh(dst_8, 8'he0);
end
1: begin
release dst_1;
release dst_8;
src_1 = 1'b1;
src_8 = 8'h23;
end
2: begin
`checkh(dst_1, 1'b1);
`checkh(dst_8, 8'h23);
$write("*-* All Finished *-*\n");
$finish;
end
default: begin
end
endcase
end
endmodule

View File

@ -9,35 +9,35 @@
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
module t(/*AUTOARG*/
// Inputs
input clk
);
module t ( /*AUTOARG*/
// Inputs
input clk
);
integer cyc = 0;
integer cyc = 0;
logic [3:0] busa;
logic [3:0] busb;
logic [3:0] busa;
logic [3:0] busb;
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 0) begin
busa <= 4'b0101;
busb <= 4'b0111;
end
else if (cyc == 1) begin
force {busa, busb} = 8'b1111_1101;
end
else if (cyc == 2) begin
`checkh(busa, 4'b1111);
`checkh(busb, 4'b1101);
end
//
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
// Test loop
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 0) begin
busa <= 4'b0101;
busb <= 4'b0111;
end
else if (cyc == 1) begin
force {busa, busb} = 8'b1111_1101;
end
else if (cyc == 2) begin
`checkh(busa, 4'b1111);
`checkh(busb, 4'b1101);
end
//
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-FileCopyrightText: 2025 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,48 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Antmicro.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
typedef struct {
logic [31:0] val;
logic [31:0] other;
} St1;
typedef struct {
St1 inner;
logic [31:0] tail;
} St2;
module m;
St2 st2;
St2 forced;
St2 snapshot;
initial begin
st2.inner.val = 32'h11;
st2.inner.other = 32'h12;
st2.tail = 32'h13;
forced.inner.val = 32'h21;
forced.inner.other = 32'h22;
forced.tail = 32'h23;
force st2 = forced;
snapshot = st2;
`checkh(snapshot.inner.val, 32'h21);
`checkh(snapshot.inner.val[0], 1'b1);
force st2.inner.val = 32'h30;
release st2.inner.val;
snapshot = st2;
`checkh(snapshot.inner.val, 32'h21);
`checkh(snapshot.inner.val[0], 1'b1);
`checkh(snapshot.inner.other, 32'h22);
`checkh(snapshot.tail, 32'h23);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2026 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026 by Antmicro.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
typedef struct packed {
logic [31:0] value;
} Entry;
typedef struct packed {
Entry [1:0][1:0] entries;
} DataBlock;
module sub;
DataBlock data_block;
endmodule
module t;
sub sub1 ();
logic [31:0] forced_value;
initial begin
forced_value = 32'h00000001;
force sub1.data_block.entries[0][0].value = forced_value[31:0];
`checkh(sub1.data_block.entries[0][0].value[0], 1'b1);
`checkh(sub1.data_block.entries[0][0].value, 32'h00000001);
$finish;
end
endmodule

View File

@ -9,69 +9,75 @@
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
module sub(
input wire [7:0] i,
output wire [7:0] o
module sub (
input wire [7:0] i,
output wire [7:0] o
);
// Must inline this module
// verilator inline_module
// Must inline this module
// verilator inline_module
wire [7:0] m;
assign m = i;
assign o = m;
wire [7:0] m;
assign m = i;
assign o = m;
endmodule
module top;
// Variable input
reg [7:0] i = 8'h01;
reg [7:0] o_v;
sub sub_v(i, o_v);
// Variable input
reg [7:0] i = 8'h01;
reg [7:0] o_v;
sub sub_v (
i,
o_v
);
// Constant input
reg [7:0] o_c;
sub sub_c(8'h10, o_c);
// Constant input
reg [7:0] o_c;
sub sub_c (
8'h10,
o_c
);
logic clk = 1'b0;
always #1 clk = ~clk;
int cyc = 0;
logic clk = 1'b0;
always #1 clk = ~clk;
int cyc = 0;
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) begin
`checkh(i, 8'h01);
`checkh(sub_v.i, 8'h01);
`checkh(sub_v.m, 8'h01);
`checkh(sub_v.o, 8'h01);
`checkh(o_v, 8'h01);
`checkh(sub_c.i, 8'h10);
`checkh(sub_c.m, 8'h10);
`checkh(sub_c.o, 8'h10);
`checkh(o_c, 8'h10);
end
else if (cyc == 2) begin
force sub_v.i = 8'h02;
force sub_v.m = 8'h03;
force sub_v.o = 8'h04;
force sub_c.i = 8'h20;
force sub_c.m = 8'h30;
force sub_c.o = 8'h40;
end
else if (cyc == 3) begin
`checkh(i, 8'h01);
`checkh(sub_v.i, 8'h02);
`checkh(sub_v.m, 8'h03);
`checkh(sub_v.o, 8'h04);
`checkh(o_v, 8'h04);
`checkh(sub_c.i, 8'h20);
`checkh(sub_c.m, 8'h30);
`checkh(sub_c.o, 8'h40);
`checkh(o_c, 8'h40);
end
//
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) begin
`checkh(i, 8'h01);
`checkh(sub_v.i, 8'h01);
`checkh(sub_v.m, 8'h01);
`checkh(sub_v.o, 8'h01);
`checkh(o_v, 8'h01);
`checkh(sub_c.i, 8'h10);
`checkh(sub_c.m, 8'h10);
`checkh(sub_c.o, 8'h10);
`checkh(o_c, 8'h10);
end
else if (cyc == 2) begin
force sub_v.i = 8'h02;
force sub_v.m = 8'h03;
force sub_v.o = 8'h04;
force sub_c.i = 8'h20;
force sub_c.m = 8'h30;
force sub_c.o = 8'h40;
end
else if (cyc == 3) begin
`checkh(i, 8'h01);
`checkh(sub_v.i, 8'h02);
`checkh(sub_v.m, 8'h03);
`checkh(sub_v.o, 8'h04);
`checkh(o_v, 8'h04);
`checkh(sub_c.i, 8'h20);
`checkh(sub_c.m, 8'h30);
`checkh(sub_c.o, 8'h40);
`checkh(o_c, 8'h40);
end
//
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -2,7 +2,4 @@
19 | cls.take_ref(a);
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:20:18: Unsupported: Signals used via read-write reference cannot be forced
20 | cls.take_ref(b);
| ^
%Error: Exiting due to

View File

@ -1,14 +0,0 @@
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:26:7: Unsupported: Force of variable with >= 1000 unpacked elements
26 | bit big_array[40][40][40];
| ^~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:25:12: Unsupported: Force of variable with >= 1000 unpacked elements
25 | struct_t s_array[3000];
| ^~~~~~~
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:27:11: Forcing variable of unsupported type: REFDTYPE 'union_t'
27 | union_t my_union;
| ^~~~~~~~
%Error: Internal Error: t/t_force_unpacked_unsup.v:27:11: ../V3Force.cpp:#: Unsupported: Force of variable of unhandled data type
27 | union_t my_union;
| ^~~~~~~~
... This fatal error may be caused by the earlier error(s); resolve those first.

View File

@ -1,84 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t (
input clk
);
integer cyc = 0;
typedef struct {int x;} struct_t;
typedef union {
int x;
logic y;
} union_t;
struct_t s_array[3000];
bit big_array[40][40][40];
union_t my_union;
// Test loop
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 0) begin
big_array[1][2][3] <= 1;
s_array[1].x <= 1;
my_union.x <= 1;
end
else if (cyc == 1) begin
`checkr(big_array[1][2][3], 1);
`checkh(s_array[1].x, 1);
`checkh(my_union.x, 1);
end
else if (cyc == 2) begin
force big_array[1][2][3] = 0;
force s_array[1].x = 0;
force my_union.x = 0;
end
else if (cyc == 3) begin
`checkr(big_array[1][2][3], 0);
big_array[1][2][3] <= 1;
`checkh(s_array[1].x, 0);
s_array[1].x <= 1;
`checkh(my_union.x, 0);
my_union.x <= 1;
end
else if (cyc == 4) begin
`checkr(big_array[1][2][3], 0);
`checkh(s_array[1].x, 0);
`checkh(my_union.x, 0);
end
else if (cyc == 5) begin
release big_array[1][2][3];
release s_array[1].x;
release my_union.x;
end
else if (cyc == 6) begin
`checkr(big_array[1][2][3], 0);
big_array[1][2][3] <= 1;
`checkh(s_array[1].x, 0);
s_array[1].x <= 1;
`checkh(my_union.x, 0);
my_union.x <= 1;
end
else if (cyc == 7) begin
`checkr(big_array[1][2][3], 1);
`checkh(s_array[1].x, 1);
`checkh(my_union.x, 1);
end
else if (cyc == 8) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -14,7 +14,6 @@ test.scenarios('vlt')
test.compile()
files = test.glob_some(test.obj_dir + "/" + test.vm_prefix + "*.h")
test.file_grep_any(files, r' u_sub__DOT__a__VforceRd')
test.file_grep_any(files, r' u_sub__DOT__a__VforceEn')
test.file_grep_any(files, r' u_sub__DOT__a__VforceVal')

View File

@ -25,7 +25,7 @@ module t (
endmodule
module sub (
input a /* verilator forceable */ /* verilator public_flat */,
input a /* verilator forceable */ /* verilator public_flat */,
input b,
output c
);

View File

@ -2,8 +2,4 @@
8 | string str /*verilator forceable*/;
| ^~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error-UNSUPPORTED: t/t_forceable_string_bad.v:8:10: Forcing variable of unsupported type: BASICDTYPE 'string'
8 | string str /*verilator forceable*/;
| ^~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -17,7 +17,7 @@ module t (
int cyc;
integer rand_result;
integer seed = 123;
integer frc;
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc != 0) begin
@ -27,6 +27,7 @@ module t (
c = new;
rand_result = c.randomize();
$display("rand: %x x: %x ", rand_result, c.x); // Get verilated_random.cpp
force frc=42; // Get verilated_force.h
$write("*-* All Finished *-*\n");
$finish;
end