parent
b42d7c2d9e
commit
925a6640d1
|
|
@ -252,14 +252,23 @@ void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAF
|
||||||
}
|
}
|
||||||
|
|
||||||
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
||||||
|
// Classify now, so a queued request is pending from the moment it is posted
|
||||||
|
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||||
|
const bool stop = contextp->stopRequestReserve(maybe);
|
||||||
|
if (stop) contextp->finishPendingInc();
|
||||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||||
vl_stop_maybe(filename, linenum, hier, maybe);
|
vl_stop_maybe(filename, linenum, hier, maybe);
|
||||||
|
contextp->stopRequestRelease();
|
||||||
|
if (stop) contextp->finishPendingDec();
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||||
|
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||||
|
contextp->finishPendingInc();
|
||||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||||
vl_fatal(filename, linenum, hier, msg);
|
vl_fatal(filename, linenum, hier, msg);
|
||||||
|
contextp->finishPendingDec();
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3282,6 +3291,15 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
m_s.m_gotFinish = flag;
|
m_s.m_gotFinish = flag;
|
||||||
}
|
}
|
||||||
|
bool VerilatedContext::stopRequestReserve(bool maybe) VL_MT_SAFE {
|
||||||
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
|
const int reserved = ++m_ns.m_stopReserved;
|
||||||
|
return !maybe || m_s.m_errorCount + reserved >= m_s.m_errorLimit;
|
||||||
|
}
|
||||||
|
void VerilatedContext::stopRequestRelease() VL_MT_SAFE {
|
||||||
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
|
--m_ns.m_stopReserved;
|
||||||
|
}
|
||||||
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
|
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
return m_ns.m_executingFinal;
|
return m_ns.m_executingFinal;
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,8 @@ private:
|
||||||
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||||
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
||||||
|
// No termination request has stamped m_finishPendingTime yet
|
||||||
|
static constexpr uint64_t TIME_UNSET = ~0ULL;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
@ -447,6 +449,8 @@ protected:
|
||||||
// Fast path
|
// Fast path
|
||||||
// A worker queues $finish before the main thread callback can set m_gotFinish.
|
// A worker queues $finish before the main thread callback can set m_gotFinish.
|
||||||
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
|
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
|
||||||
|
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
|
||||||
|
int m_stopReserved = 0; // Posted $stop requests not yet executed
|
||||||
bool m_executingFinal = false; // Running generated final() code
|
bool m_executingFinal = false; // Running generated final() code
|
||||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||||
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
||||||
|
|
@ -686,13 +690,26 @@ public:
|
||||||
|
|
||||||
// METHODS - public but for internal use only
|
// METHODS - public but for internal use only
|
||||||
|
|
||||||
// Internal: Track $finish callbacks queued by worker threads
|
// Internal: Track $finish/$stop callbacks queued by worker threads
|
||||||
bool finishPending() const VL_MT_SAFE { return m_ns.m_finishPending.load() != 0; }
|
bool finishPending() const VL_MT_SAFE { return m_ns.m_finishPending.load() != 0; }
|
||||||
void finishPendingInc() VL_MT_SAFE { ++m_ns.m_finishPending; }
|
void finishPendingInc() VL_MT_SAFE {
|
||||||
|
++m_ns.m_finishPending;
|
||||||
|
uint64_t unset = TIME_UNSET;
|
||||||
|
m_ns.m_finishPendingTime.compare_exchange_strong(unset, time());
|
||||||
|
}
|
||||||
void finishPendingDec() VL_MT_SAFE {
|
void finishPendingDec() VL_MT_SAFE {
|
||||||
const uint32_t previous = m_ns.m_finishPending.fetch_sub(1);
|
const uint32_t previous = m_ns.m_finishPending.fetch_sub(1);
|
||||||
assert(previous > 0);
|
assert(previous > 0);
|
||||||
|
if (previous == 1 && !gotFinish()) m_ns.m_finishPendingTime = TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
// Internal: Time of the first termination request, else the current time
|
||||||
|
uint64_t finishPendingTime() const VL_MT_SAFE {
|
||||||
|
const uint64_t stamped = m_ns.m_finishPendingTime.load();
|
||||||
|
return stamped == TIME_UNSET ? time() : stamped;
|
||||||
|
}
|
||||||
|
// Internal: Reserve a posted $stop, returning true if it reaches the termination limit
|
||||||
|
bool stopRequestReserve(bool maybe) VL_MT_SAFE;
|
||||||
|
void stopRequestRelease() VL_MT_SAFE;
|
||||||
|
|
||||||
// Internal: access to implementation class
|
// Internal: access to implementation class
|
||||||
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
|
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
// DESCRIPTION: Verilator: VerilatedContext pending termination state test
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// 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 PlanV GmbH
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
#include VM_PREFIX_INCLUDE
|
||||||
|
|
||||||
|
// Workaround to include verilated_imp.h, needed to drive the eval message queue
|
||||||
|
#define VERILATOR_VERILATED_CPP_
|
||||||
|
#include "verilated_imp.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// These require the above. Comment prevents clang-format moving them
|
||||||
|
#include "TestCheck.h"
|
||||||
|
|
||||||
|
int errors = 0;
|
||||||
|
|
||||||
|
int stopCalls = 0;
|
||||||
|
int fatalCalls = 0;
|
||||||
|
|
||||||
|
// Non-exiting overrides so a request can be observed after it ran
|
||||||
|
void vl_stop(const char* filename, int linenum, const char* hier) { ++stopCalls; }
|
||||||
|
|
||||||
|
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) {
|
||||||
|
++fatalCalls;
|
||||||
|
TEST_CHECK_EQ(Verilated::threadContextp()->finishPending(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
VerilatedContext context;
|
||||||
|
Verilated::threadContextp(&context);
|
||||||
|
context.commandArgs(argc, argv);
|
||||||
|
std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{&context}};
|
||||||
|
|
||||||
|
// A maybe-stop under the error limit is ignored and marks nothing pending
|
||||||
|
context.errorLimit(3);
|
||||||
|
context.errorCount(0);
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 1);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 0);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 2);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 0);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
|
||||||
|
// Reaching the limit stops, and a definite stop always stops
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 3);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 1);
|
||||||
|
context.errorCount(0);
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t", false);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 1);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 2);
|
||||||
|
|
||||||
|
// A worker-queued stop is pending from the moment it is posted, and the
|
||||||
|
// error is counted only when the queued message runs
|
||||||
|
context.errorCount(0);
|
||||||
|
context.time(10);
|
||||||
|
{
|
||||||
|
VerilatedEvalMsgQueue evalMsgQ;
|
||||||
|
Verilated::mtaskId(1);
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t", false);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 0);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 2);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), true);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 10);
|
||||||
|
context.time(20);
|
||||||
|
Verilated::endOfThreadMTask(&evalMsgQ);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 2);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), true);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 10);
|
||||||
|
Verilated::endOfEval(&evalMsgQ);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 1);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 3);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ignored worker-queued maybe-stop must not gate same-slot work
|
||||||
|
context.errorLimit(3);
|
||||||
|
context.errorCount(0);
|
||||||
|
{
|
||||||
|
VerilatedEvalMsgQueue evalMsgQ;
|
||||||
|
Verilated::mtaskId(1);
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
Verilated::endOfThreadMTask(&evalMsgQ);
|
||||||
|
Verilated::endOfEval(&evalMsgQ);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 1);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 3);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Several queued maybe-stops still stop exactly once, on the one that crosses
|
||||||
|
context.errorLimit(3);
|
||||||
|
context.errorCount(0);
|
||||||
|
{
|
||||||
|
VerilatedEvalMsgQueue evalMsgQ;
|
||||||
|
Verilated::mtaskId(1);
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
VL_STOP_MT(__FILE__, __LINE__, "TOP.t");
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), true);
|
||||||
|
Verilated::endOfThreadMTask(&evalMsgQ);
|
||||||
|
Verilated::endOfEval(&evalMsgQ);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 3);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 4);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A worker-queued fatal stays pending until end-of-eval runs its handler
|
||||||
|
context.time(30);
|
||||||
|
{
|
||||||
|
VerilatedEvalMsgQueue evalMsgQ;
|
||||||
|
Verilated::mtaskId(1);
|
||||||
|
VL_FATAL_MT(__FILE__, __LINE__, "TOP.t", "queued fatal");
|
||||||
|
TEST_CHECK_EQ(fatalCalls, 0);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), true);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 30);
|
||||||
|
Verilated::endOfThreadMTask(&evalMsgQ);
|
||||||
|
Verilated::endOfEval(&evalMsgQ);
|
||||||
|
TEST_CHECK_EQ(fatalCalls, 1);
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first pending request owns the timestamp until all requests drain
|
||||||
|
context.time(40);
|
||||||
|
context.finishPendingInc();
|
||||||
|
context.time(50);
|
||||||
|
context.finishPendingInc();
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 40);
|
||||||
|
context.finishPendingDec();
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), true);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 40);
|
||||||
|
context.finishPendingDec();
|
||||||
|
TEST_CHECK_EQ(context.finishPending(), false);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 50);
|
||||||
|
|
||||||
|
// A latched termination keeps the timestamp after the request drains
|
||||||
|
context.time(60);
|
||||||
|
context.finishPendingInc();
|
||||||
|
context.gotFinish(true);
|
||||||
|
context.finishPendingDec();
|
||||||
|
context.time(70);
|
||||||
|
TEST_CHECK_EQ(context.finishPendingTime(), 60);
|
||||||
|
|
||||||
|
topp->final();
|
||||||
|
return errors ? 10 : 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/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('vlt_all')
|
||||||
|
|
||||||
|
test.compile(
|
||||||
|
make_top_shell=False,
|
||||||
|
make_main=False,
|
||||||
|
verilator_flags2=['--exe', test.pli_filename, "-CFLAGS '-DVL_USER_STOP -DVL_USER_FATAL'"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t;
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
// DESCRIPTION: Verilator: User replaced vl_finish/vl_stop/vl_fatal/vl_warn test
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// 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 PlanV GmbH
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
#include VM_PREFIX_INCLUDE
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// This requires the above. Comment prevents clang-format moving it
|
||||||
|
#include "TestCheck.h"
|
||||||
|
|
||||||
|
int errors = 0;
|
||||||
|
|
||||||
|
int finishCalls = 0;
|
||||||
|
int stopCalls = 0;
|
||||||
|
int fatalCalls = 0;
|
||||||
|
int warnCalls = 0;
|
||||||
|
|
||||||
|
// An embedder replaces these four and keeps control of the process
|
||||||
|
void vl_finish(const char* filename, int linenum, const char* hier) { ++finishCalls; }
|
||||||
|
|
||||||
|
void vl_stop(const char* filename, int linenum, const char* hier) {
|
||||||
|
++stopCalls;
|
||||||
|
if (Verilated::threadContextp()->fatalOnError())
|
||||||
|
vl_fatal(filename, linenum, hier, "Verilog $stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) {
|
||||||
|
++fatalCalls;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vl_warn(const char* filename, int linenum, const char* hier, const char* msg) { ++warnCalls; }
|
||||||
|
|
||||||
|
static void tick(VM_PREFIX* topp, int step) {
|
||||||
|
topp->step = step;
|
||||||
|
topp->clk = 0;
|
||||||
|
topp->eval();
|
||||||
|
topp->clk = 1;
|
||||||
|
topp->eval();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
VerilatedContext context;
|
||||||
|
Verilated::threadContextp(&context);
|
||||||
|
context.commandArgs(argc, argv);
|
||||||
|
std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{&context}};
|
||||||
|
|
||||||
|
tick(topp.get(), 1);
|
||||||
|
TEST_CHECK_EQ(warnCalls, 1);
|
||||||
|
|
||||||
|
tick(topp.get(), 2);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 1);
|
||||||
|
TEST_CHECK_EQ(fatalCalls, 1);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 1);
|
||||||
|
|
||||||
|
tick(topp.get(), 3);
|
||||||
|
TEST_CHECK_EQ(stopCalls, 2);
|
||||||
|
TEST_CHECK_EQ(fatalCalls, 2);
|
||||||
|
TEST_CHECK_EQ(context.errorCount(), 2);
|
||||||
|
|
||||||
|
tick(topp.get(), 4);
|
||||||
|
TEST_CHECK_EQ(finishCalls, 1);
|
||||||
|
|
||||||
|
TEST_CHECK_EQ(warnCalls, 1);
|
||||||
|
topp->final();
|
||||||
|
return errors ? 10 : 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/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('vlt_all')
|
||||||
|
|
||||||
|
test.compile(make_top_shell=False,
|
||||||
|
make_main=False,
|
||||||
|
verilator_flags2=[
|
||||||
|
'--exe', test.pli_filename,
|
||||||
|
"-CFLAGS '-DVL_USER_FINISH -DVL_USER_STOP -DVL_USER_FATAL -DVL_USER_WARN'"
|
||||||
|
])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t (
|
||||||
|
input logic clk,
|
||||||
|
input int step
|
||||||
|
);
|
||||||
|
|
||||||
|
logic [7:0] mem[0:1];
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
case (step)
|
||||||
|
1: $readmemh("t_verilated_user_hooks_no_such_file.mem", mem);
|
||||||
|
2: $stop;
|
||||||
|
3: $fatal(0, "user hook fatal");
|
||||||
|
4: $finish;
|
||||||
|
default: ;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue