mirror of
https://github.com/verilator/verilator.git
synced 2025-04-12 07:56:53 +00:00
Add std::process class (#4212)
This commit is contained in:
parent
9cc218db3e
commit
db7935faf3
@ -26,10 +26,6 @@
|
|||||||
// verilator lint_off TIMESCALEMOD
|
// verilator lint_off TIMESCALEMOD
|
||||||
// verilator lint_off UNUSEDSIGNAL
|
// verilator lint_off UNUSEDSIGNAL
|
||||||
package std;
|
package std;
|
||||||
// The process class is not implemented, but it's predeclared here,
|
|
||||||
// so the linter accepts references to it.
|
|
||||||
typedef class process;
|
|
||||||
|
|
||||||
class mailbox #(type T);
|
class mailbox #(type T);
|
||||||
protected int m_bound;
|
protected int m_bound;
|
||||||
protected T m_queue[$];
|
protected T m_queue[$];
|
||||||
@ -45,7 +41,7 @@ package std;
|
|||||||
task put(T message);
|
task put(T message);
|
||||||
`ifdef VERILATOR_TIMING
|
`ifdef VERILATOR_TIMING
|
||||||
if (m_bound != 0)
|
if (m_bound != 0)
|
||||||
wait (m_queue.size() < m_bound);
|
wait (m_queue.size() < m_bound);
|
||||||
m_queue.push_back(message);
|
m_queue.push_back(message);
|
||||||
`endif
|
`endif
|
||||||
endtask
|
endtask
|
||||||
@ -117,43 +113,80 @@ package std;
|
|||||||
endclass
|
endclass
|
||||||
|
|
||||||
class process;
|
class process;
|
||||||
typedef enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state;
|
typedef enum {
|
||||||
static process _s_global_process;
|
FINISHED = 0,
|
||||||
|
RUNNING = 1,
|
||||||
|
WAITING = 2,
|
||||||
|
SUSPENDED = 3,
|
||||||
|
KILLED = 4
|
||||||
|
} state;
|
||||||
|
|
||||||
|
`ifdef VERILATOR_TIMING
|
||||||
|
// Width visitor changes it to VlProcessRef
|
||||||
|
protected chandle m_process;
|
||||||
|
`endif
|
||||||
|
|
||||||
static function process self();
|
static function process self();
|
||||||
// Unsupported, emulating with single process' state
|
process p = new;
|
||||||
if (!_s_global_process) _s_global_process = new;
|
`ifdef VERILATOR_TIMING
|
||||||
return _s_global_process;
|
$c(p.m_process, " = vlProcess;");
|
||||||
|
`endif
|
||||||
|
return p;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
protected function void set_status(state s);
|
||||||
|
`ifdef VERILATOR_TIMING
|
||||||
|
$c(m_process, "->state(", s, ");");
|
||||||
|
`endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
function state status();
|
function state status();
|
||||||
// Unsupported, emulating with single process' state
|
`ifdef VERILATOR_TIMING
|
||||||
|
return state'($c(m_process, "->state()"));
|
||||||
|
`else
|
||||||
return RUNNING;
|
return RUNNING;
|
||||||
|
`endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function void kill();
|
function void kill();
|
||||||
$error("std::process::kill() not supported");
|
set_status(KILLED);
|
||||||
endfunction
|
endfunction
|
||||||
task await();
|
|
||||||
$error("std::process::await() not supported");
|
|
||||||
endtask
|
|
||||||
function void suspend();
|
function void suspend();
|
||||||
$error("std::process::suspend() not supported");
|
$error("std::process::suspend() not supported");
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function void resume();
|
function void resume();
|
||||||
$error("std::process::resume() not supported");
|
set_status(RUNNING);
|
||||||
endfunction
|
endfunction
|
||||||
// When really implemented, srandom must operates on the process, but for
|
|
||||||
|
task await();
|
||||||
|
`ifdef VERILATOR_TIMING
|
||||||
|
wait (status() == FINISHED || status() == KILLED);
|
||||||
|
`endif
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// When really implemented, srandom must operate on the process, but for
|
||||||
// now rely on the srandom() that is automatically generated for all
|
// now rely on the srandom() that is automatically generated for all
|
||||||
// classes.
|
// classes.
|
||||||
|
//
|
||||||
// function void srandom(int seed);
|
// function void srandom(int seed);
|
||||||
// endfunction
|
// endfunction
|
||||||
|
|
||||||
|
// The methods below work only if set_randstate is never applied to
|
||||||
|
// a state string created before another such string. Full support
|
||||||
|
// could use VlRNG class to store the state per process in VlProcess
|
||||||
|
// objects.
|
||||||
function string get_randstate();
|
function string get_randstate();
|
||||||
// Could operate on all proceses for now
|
string s;
|
||||||
// No error, as harmless until set_randstate is called
|
|
||||||
return "NOT_SUPPORTED";
|
s.itoa($random); // Get a random number
|
||||||
|
set_randstate(s); // Pretend it's the state of RNG
|
||||||
|
return s;
|
||||||
endfunction
|
endfunction
|
||||||
function void set_randstate(string randstate);
|
|
||||||
$error("std::process::set_randstate() not supported");
|
function void set_randstate(string s);
|
||||||
// Could operate on all proceses for now
|
$urandom(s.atoi()); // Set the seed using a string
|
||||||
endfunction
|
endfunction
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
@ -30,7 +30,16 @@ void VlCoroutineHandle::resume() {
|
|||||||
// main process
|
// main process
|
||||||
if (VL_LIKELY(m_coro)) {
|
if (VL_LIKELY(m_coro)) {
|
||||||
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
|
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
|
||||||
m_coro();
|
if (m_process) { // If process state is managed with std::process
|
||||||
|
if (m_process->state() == VlProcess::KILLED) {
|
||||||
|
m_coro.destroy();
|
||||||
|
} else {
|
||||||
|
m_process->state(VlProcess::RUNNING);
|
||||||
|
m_coro();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_coro();
|
||||||
|
}
|
||||||
m_coro = nullptr;
|
m_coro = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,36 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// VlProcess stores metadata of running processes
|
||||||
|
|
||||||
|
class VlProcess final {
|
||||||
|
// MEMBERS
|
||||||
|
int m_state; // Current state of the process
|
||||||
|
|
||||||
|
public:
|
||||||
|
// TYPES
|
||||||
|
enum : int { // Type int for compatibility with $c
|
||||||
|
FINISHED = 0,
|
||||||
|
RUNNING = 1,
|
||||||
|
WAITING = 2,
|
||||||
|
SUSPENDED = 3,
|
||||||
|
KILLED = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// CONSTRUCTORS
|
||||||
|
VlProcess()
|
||||||
|
: m_state{RUNNING} {}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
int state() { return m_state; }
|
||||||
|
void state(int s) { m_state = s; }
|
||||||
|
};
|
||||||
|
|
||||||
|
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||||
|
|
||||||
|
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
|
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
|
||||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||||
@ -96,25 +126,38 @@ class VlCoroutineHandle final {
|
|||||||
|
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
|
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
|
||||||
|
VlProcessRef m_process; // Data of the suspended process, null if not needed
|
||||||
VlFileLineDebug m_fileline;
|
VlFileLineDebug m_fileline;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
// Construct
|
// Construct
|
||||||
VlCoroutineHandle()
|
VlCoroutineHandle(VlProcessRef process)
|
||||||
: m_coro{nullptr} {}
|
: m_coro{nullptr}
|
||||||
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
|
, m_process{process} {
|
||||||
|
if (m_process) m_process->state(VlProcess::WAITING);
|
||||||
|
}
|
||||||
|
VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
|
||||||
: m_coro{coro}
|
: m_coro{coro}
|
||||||
, m_fileline{fileline} {}
|
, m_process{process}
|
||||||
|
, m_fileline{fileline} {
|
||||||
|
if (m_process) m_process->state(VlProcess::WAITING);
|
||||||
|
}
|
||||||
// Move the handle, leaving a nullptr
|
// Move the handle, leaving a nullptr
|
||||||
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
||||||
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
||||||
|
, m_process{moved.m_process}
|
||||||
, m_fileline{moved.m_fileline} {}
|
, m_fileline{moved.m_fileline} {}
|
||||||
// Destroy if the handle isn't null
|
// Destroy if the handle isn't null
|
||||||
~VlCoroutineHandle() {
|
~VlCoroutineHandle() {
|
||||||
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
|
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
|
||||||
// model with some coroutines suspended
|
// model with some coroutines suspended
|
||||||
if (VL_UNLIKELY(m_coro)) m_coro.destroy();
|
if (VL_UNLIKELY(m_coro)) {
|
||||||
|
m_coro.destroy();
|
||||||
|
if (m_process && m_process->state() != VlProcess::KILLED) {
|
||||||
|
m_process->state(VlProcess::FINISHED);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// METHODS
|
// METHODS
|
||||||
// Move the handle, leaving a null handle
|
// Move the handle, leaving a null handle
|
||||||
@ -122,7 +165,7 @@ public:
|
|||||||
m_coro = std::exchange(moved.m_coro, nullptr);
|
m_coro = std::exchange(moved.m_coro, nullptr);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
// Resume the coroutine if the handle isn't null
|
// Resume the coroutine if the handle isn't null and the process isn't killed
|
||||||
void resume();
|
void resume();
|
||||||
#ifdef VL_DEBUG
|
#ifdef VL_DEBUG
|
||||||
void dump() const;
|
void dump() const;
|
||||||
@ -173,21 +216,24 @@ public:
|
|||||||
void dump() const;
|
void dump() const;
|
||||||
#endif
|
#endif
|
||||||
// Used by coroutines for co_awaiting a certain simulation time
|
// Used by coroutines for co_awaiting a certain simulation time
|
||||||
auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN,
|
||||||
|
int lineno = 0) {
|
||||||
struct Awaitable {
|
struct Awaitable {
|
||||||
|
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||||
VlDelayedCoroutineQueue& queue;
|
VlDelayedCoroutineQueue& queue;
|
||||||
uint64_t delay;
|
uint64_t delay;
|
||||||
VlFileLineDebug fileline;
|
VlFileLineDebug fileline;
|
||||||
|
|
||||||
bool await_ready() const { return false; } // Always suspend
|
bool await_ready() const { return false; } // Always suspend
|
||||||
void await_suspend(std::coroutine_handle<> coro) {
|
void await_suspend(std::coroutine_handle<> coro) {
|
||||||
queue.push_back({delay, VlCoroutineHandle{coro, fileline}});
|
queue.push_back({delay, VlCoroutineHandle{coro, process, fileline}});
|
||||||
// Move last element to the proper place in the max-heap
|
// Move last element to the proper place in the max-heap
|
||||||
std::push_heap(queue.begin(), queue.end());
|
std::push_heap(queue.begin(), queue.end());
|
||||||
}
|
}
|
||||||
void await_resume() const {}
|
void await_resume() const {}
|
||||||
};
|
};
|
||||||
return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}};
|
return Awaitable{process, m_queue, m_context.time() + delay,
|
||||||
|
VlFileLineDebug{filename, lineno}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -224,21 +270,23 @@ public:
|
|||||||
void dump(const char* eventDescription) const;
|
void dump(const char* eventDescription) const;
|
||||||
#endif
|
#endif
|
||||||
// Used by coroutines for co_awaiting a certain trigger
|
// Used by coroutines for co_awaiting a certain trigger
|
||||||
auto trigger(bool commit, const char* eventDescription = VL_UNKNOWN,
|
auto trigger(bool commit, VlProcessRef process, const char* eventDescription = VL_UNKNOWN,
|
||||||
const char* filename = VL_UNKNOWN, int lineno = 0) {
|
const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||||
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
||||||
eventDescription, filename, lineno););
|
eventDescription, filename, lineno););
|
||||||
struct Awaitable {
|
struct Awaitable {
|
||||||
VlCoroutineVec& suspended; // Coros waiting on trigger
|
VlCoroutineVec& suspended; // Coros waiting on trigger
|
||||||
|
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||||
VlFileLineDebug fileline;
|
VlFileLineDebug fileline;
|
||||||
|
|
||||||
bool await_ready() const { return false; } // Always suspend
|
bool await_ready() const { return false; } // Always suspend
|
||||||
void await_suspend(std::coroutine_handle<> coro) {
|
void await_suspend(std::coroutine_handle<> coro) {
|
||||||
suspended.emplace_back(coro, fileline);
|
suspended.emplace_back(coro, process, fileline);
|
||||||
}
|
}
|
||||||
void await_resume() const {}
|
void await_resume() const {}
|
||||||
};
|
};
|
||||||
return Awaitable{commit ? m_ready : m_uncommitted, VlFileLineDebug{filename, lineno}};
|
return Awaitable{commit ? m_ready : m_uncommitted, process,
|
||||||
|
VlFileLineDebug{filename, lineno}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -271,18 +319,19 @@ class VlDynamicTriggerScheduler final {
|
|||||||
// with destructive post updates, e.g. named events)
|
// with destructive post updates, e.g. named events)
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
|
auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) {
|
||||||
struct Awaitable {
|
struct Awaitable {
|
||||||
|
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||||
VlCoroutineVec& suspended; // Coros waiting on trigger
|
VlCoroutineVec& suspended; // Coros waiting on trigger
|
||||||
VlFileLineDebug fileline;
|
VlFileLineDebug fileline;
|
||||||
|
|
||||||
bool await_ready() const { return false; } // Always suspend
|
bool await_ready() const { return false; } // Always suspend
|
||||||
void await_suspend(std::coroutine_handle<> coro) {
|
void await_suspend(std::coroutine_handle<> coro) {
|
||||||
suspended.emplace_back(coro, fileline);
|
suspended.emplace_back(coro, process, fileline);
|
||||||
}
|
}
|
||||||
void await_resume() const {}
|
void await_resume() const {}
|
||||||
};
|
};
|
||||||
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
|
return Awaitable{process, queue, VlFileLineDebug{filename, lineno}};
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -296,23 +345,26 @@ public:
|
|||||||
void dump() const;
|
void dump() const;
|
||||||
#endif
|
#endif
|
||||||
// Used by coroutines for co_awaiting trigger evaluation
|
// Used by coroutines for co_awaiting trigger evaluation
|
||||||
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
|
auto evaluation(VlProcessRef process, const char* eventDescription, const char* filename,
|
||||||
|
int lineno) {
|
||||||
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
||||||
eventDescription, filename, lineno););
|
eventDescription, filename, lineno););
|
||||||
return awaitable(m_suspended, filename, lineno);
|
return awaitable(process, m_suspended, filename, lineno);
|
||||||
}
|
}
|
||||||
// Used by coroutines for co_awaiting the trigger post update step
|
// Used by coroutines for co_awaiting the trigger post update step
|
||||||
auto postUpdate(const char* eventDescription, const char* filename, int lineno) {
|
auto postUpdate(VlProcessRef process, const char* eventDescription, const char* filename,
|
||||||
|
int lineno) {
|
||||||
VL_DEBUG_IF(
|
VL_DEBUG_IF(
|
||||||
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
|
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
|
||||||
eventDescription, filename, lineno););
|
eventDescription, filename, lineno););
|
||||||
return awaitable(m_post, filename, lineno);
|
return awaitable(process, m_post, filename, lineno);
|
||||||
}
|
}
|
||||||
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
|
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
|
||||||
auto resumption(const char* eventDescription, const char* filename, int lineno) {
|
auto resumption(VlProcessRef process, const char* eventDescription, const char* filename,
|
||||||
|
int lineno) {
|
||||||
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
|
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
|
||||||
eventDescription, filename, lineno););
|
eventDescription, filename, lineno););
|
||||||
return awaitable(m_triggered, filename, lineno);
|
return awaitable(process, m_triggered, filename, lineno);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -342,24 +394,27 @@ class VlForkSync final {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Create the join object and set the counter to the specified number
|
// Create the join object and set the counter to the specified number
|
||||||
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
|
void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
|
||||||
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
|
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
|
||||||
// main process gets resumed
|
// main process gets resumed
|
||||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||||
// Used by coroutines for co_awaiting a join
|
// Used by coroutines for co_awaiting a join
|
||||||
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
|
auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||||
assert(m_join);
|
assert(m_join);
|
||||||
VL_DEBUG_IF(
|
VL_DEBUG_IF(
|
||||||
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
||||||
struct Awaitable {
|
struct Awaitable {
|
||||||
|
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||||
const std::shared_ptr<VlJoin> join; // Join to await on
|
const std::shared_ptr<VlJoin> join; // Join to await on
|
||||||
VlFileLineDebug fileline;
|
VlFileLineDebug fileline;
|
||||||
|
|
||||||
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
|
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
|
||||||
void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; }
|
void await_suspend(std::coroutine_handle<> coro) {
|
||||||
|
join->m_susp = {coro, process, fileline};
|
||||||
|
}
|
||||||
void await_resume() const {}
|
void await_resume() const {}
|
||||||
};
|
};
|
||||||
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
|
return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
26
src/V3Ast.h
26
src/V3Ast.h
@ -461,6 +461,7 @@ public:
|
|||||||
TRIGGER_SCHEDULER,
|
TRIGGER_SCHEDULER,
|
||||||
DYNAMIC_TRIGGER_SCHEDULER,
|
DYNAMIC_TRIGGER_SCHEDULER,
|
||||||
FORK_SYNC,
|
FORK_SYNC,
|
||||||
|
PROCESS_REFERENCE,
|
||||||
// Unsigned and two state; fundamental types
|
// Unsigned and two state; fundamental types
|
||||||
UINT32,
|
UINT32,
|
||||||
UINT64,
|
UINT64,
|
||||||
@ -493,6 +494,7 @@ public:
|
|||||||
"VlTriggerScheduler",
|
"VlTriggerScheduler",
|
||||||
"VlDynamicTriggerScheduler",
|
"VlDynamicTriggerScheduler",
|
||||||
"VlFork",
|
"VlFork",
|
||||||
|
"VlProcessRef",
|
||||||
"IData",
|
"IData",
|
||||||
"QData",
|
"QData",
|
||||||
"LOGIC_IMPLICIT",
|
"LOGIC_IMPLICIT",
|
||||||
@ -500,13 +502,20 @@ public:
|
|||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
const char* dpiType() const {
|
const char* dpiType() const {
|
||||||
static const char* const names[]
|
static const char* const names[] = {"%E-unk", "svBit",
|
||||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
"char", "void*",
|
||||||
"int", "%E-integer", "svLogic", "long long", "double",
|
"char", "int",
|
||||||
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
"%E-integer", "svLogic",
|
||||||
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
|
"long long", "double",
|
||||||
"%E-dyn-sched", "%E-fork", "IData", "QData", "%E-logic-implct",
|
"short", "%E-time",
|
||||||
" MAX"};
|
"const char*", "%E-untyped",
|
||||||
|
"dpiScope", "const char*",
|
||||||
|
"%E-mtaskstate", "%E-triggervec",
|
||||||
|
"%E-dly-sched", "%E-trig-sched",
|
||||||
|
"%E-dyn-sched", "%E-fork",
|
||||||
|
"%E-proc-ref", "IData",
|
||||||
|
"QData", "%E-logic-implct",
|
||||||
|
" MAX"};
|
||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
static void selfTest() {
|
static void selfTest() {
|
||||||
@ -545,6 +554,7 @@ public:
|
|||||||
case TRIGGER_SCHEDULER: return 0; // opaque
|
case TRIGGER_SCHEDULER: return 0; // opaque
|
||||||
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
||||||
case FORK_SYNC: return 0; // opaque
|
case FORK_SYNC: return 0; // opaque
|
||||||
|
case PROCESS_REFERENCE: return 0; // opaque
|
||||||
case UINT32: return 32;
|
case UINT32: return 32;
|
||||||
case UINT64: return 64;
|
case UINT64: return 64;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
@ -584,7 +594,7 @@ public:
|
|||||||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
||||||
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
||||||
|| m_e == DOUBLE || m_e == UNTYPED);
|
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
|
||||||
}
|
}
|
||||||
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||||
bool isEvent() const { return m_e == EVENT; }
|
bool isEvent() const { return m_e == EVENT; }
|
||||||
|
@ -438,6 +438,7 @@ public:
|
|||||||
bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; }
|
bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; }
|
||||||
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
|
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
|
||||||
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
|
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
|
||||||
|
bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; }
|
||||||
bool isDelayScheduler() const VL_MT_SAFE {
|
bool isDelayScheduler() const VL_MT_SAFE {
|
||||||
return keyword() == VBasicDTypeKwd::DELAY_SCHEDULER;
|
return keyword() == VBasicDTypeKwd::DELAY_SCHEDULER;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ private:
|
|||||||
bool m_recursive : 1; // Recursive or part of recursion
|
bool m_recursive : 1; // Recursive or part of recursion
|
||||||
bool m_underGenerate : 1; // Under generate (for warning)
|
bool m_underGenerate : 1; // Under generate (for warning)
|
||||||
bool m_virtual : 1; // Virtual method in class
|
bool m_virtual : 1; // Virtual method in class
|
||||||
|
bool m_fromStd : 1; // Part of std
|
||||||
VLifetime m_lifetime; // Lifetime
|
VLifetime m_lifetime; // Lifetime
|
||||||
protected:
|
protected:
|
||||||
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||||
@ -110,7 +111,8 @@ protected:
|
|||||||
, m_pureVirtual{false}
|
, m_pureVirtual{false}
|
||||||
, m_recursive{false}
|
, m_recursive{false}
|
||||||
, m_underGenerate{false}
|
, m_underGenerate{false}
|
||||||
, m_virtual{false} {
|
, m_virtual{false}
|
||||||
|
, m_fromStd{false} {
|
||||||
addStmtsp(stmtsp);
|
addStmtsp(stmtsp);
|
||||||
cname(name); // Might be overridden by dpi import/export
|
cname(name); // Might be overridden by dpi import/export
|
||||||
}
|
}
|
||||||
@ -170,6 +172,8 @@ public:
|
|||||||
bool underGenerate() const { return m_underGenerate; }
|
bool underGenerate() const { return m_underGenerate; }
|
||||||
void isVirtual(bool flag) { m_virtual = flag; }
|
void isVirtual(bool flag) { m_virtual = flag; }
|
||||||
bool isVirtual() const { return m_virtual; }
|
bool isVirtual() const { return m_virtual; }
|
||||||
|
void isFromStd(bool flag) { m_fromStd = flag; }
|
||||||
|
bool isFromStd() const { return m_fromStd; }
|
||||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||||
VLifetime lifetime() const { return m_lifetime; }
|
VLifetime lifetime() const { return m_lifetime; }
|
||||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||||
@ -272,10 +276,13 @@ public:
|
|||||||
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||||
// IEEE procedure: initial, final, always
|
// IEEE procedure: initial, final, always
|
||||||
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
|
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
|
||||||
bool m_suspendable = false; // Is suspendable by a Delay, EventControl, etc.
|
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
|
||||||
|
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||||
protected:
|
protected:
|
||||||
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
||||||
: AstNode{t, fl} {
|
: AstNode{t, fl} {
|
||||||
|
m_needProcess = false;
|
||||||
|
m_suspendable = false;
|
||||||
addStmtsp(stmtsp);
|
addStmtsp(stmtsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +293,8 @@ public:
|
|||||||
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
||||||
bool isSuspendable() const { return m_suspendable; }
|
bool isSuspendable() const { return m_suspendable; }
|
||||||
void setSuspendable() { m_suspendable = true; }
|
void setSuspendable() { m_suspendable = true; }
|
||||||
|
bool needProcess() const { return m_needProcess; }
|
||||||
|
void setNeedProcess() { m_needProcess = true; }
|
||||||
};
|
};
|
||||||
class AstNodeRange VL_NOT_FINAL : public AstNode {
|
class AstNodeRange VL_NOT_FINAL : public AstNode {
|
||||||
// A range, sized or unsized
|
// A range, sized or unsized
|
||||||
@ -576,6 +585,7 @@ private:
|
|||||||
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
||||||
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||||
|
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||||
public:
|
public:
|
||||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||||
: ASTGEN_SUPER_CFunc(fl) {
|
: ASTGEN_SUPER_CFunc(fl) {
|
||||||
@ -595,6 +605,7 @@ public:
|
|||||||
m_isLoose = false;
|
m_isLoose = false;
|
||||||
m_isInline = false;
|
m_isInline = false;
|
||||||
m_isVirtual = false;
|
m_isVirtual = false;
|
||||||
|
m_needProcess = false;
|
||||||
m_entryPoint = false;
|
m_entryPoint = false;
|
||||||
m_pure = false;
|
m_pure = false;
|
||||||
m_dpiContext = false;
|
m_dpiContext = false;
|
||||||
@ -663,6 +674,8 @@ public:
|
|||||||
void isInline(bool flag) { m_isInline = flag; }
|
void isInline(bool flag) { m_isInline = flag; }
|
||||||
bool isVirtual() const { return m_isVirtual; }
|
bool isVirtual() const { return m_isVirtual; }
|
||||||
void isVirtual(bool flag) { m_isVirtual = flag; }
|
void isVirtual(bool flag) { m_isVirtual = flag; }
|
||||||
|
bool needProcess() const { return m_needProcess; }
|
||||||
|
void setNeedProcess() { m_needProcess = true; }
|
||||||
bool entryPoint() const { return m_entryPoint; }
|
bool entryPoint() const { return m_entryPoint; }
|
||||||
void entryPoint(bool flag) { m_entryPoint = flag; }
|
void entryPoint(bool flag) { m_entryPoint = flag; }
|
||||||
bool pure() const { return m_pure; }
|
bool pure() const { return m_pure; }
|
||||||
|
@ -784,6 +784,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const VL_M
|
|||||||
info.m_type = "VlDynamicTriggerScheduler";
|
info.m_type = "VlDynamicTriggerScheduler";
|
||||||
} else if (bdtypep->isForkSync()) {
|
} else if (bdtypep->isForkSync()) {
|
||||||
info.m_type = "VlForkSync";
|
info.m_type = "VlForkSync";
|
||||||
|
} else if (bdtypep->isProcessRef()) {
|
||||||
|
info.m_type = "VlProcessRef";
|
||||||
} else if (bdtypep->isEvent()) {
|
} else if (bdtypep->isEvent()) {
|
||||||
info.m_type = "VlEvent";
|
info.m_type = "VlEvent";
|
||||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||||
@ -1352,6 +1354,7 @@ void AstNode::dump(std::ostream& str) const {
|
|||||||
void AstNodeProcedure::dump(std::ostream& str) const {
|
void AstNodeProcedure::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (isSuspendable()) str << " [SUSP]";
|
if (isSuspendable()) str << " [SUSP]";
|
||||||
|
if (needProcess()) str << " [NPRC]";
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstAlways::dump(std::ostream& str) const {
|
void AstAlways::dump(std::ostream& str) const {
|
||||||
@ -2294,6 +2297,7 @@ void AstCFunc::dump(std::ostream& str) const {
|
|||||||
if (isDestructor()) str << " [DTOR]";
|
if (isDestructor()) str << " [DTOR]";
|
||||||
if (isVirtual()) str << " [VIRT]";
|
if (isVirtual()) str << " [VIRT]";
|
||||||
if (isCoroutine()) str << " [CORO]";
|
if (isCoroutine()) str << " [CORO]";
|
||||||
|
if (needProcess()) str << " [NPRC]";
|
||||||
}
|
}
|
||||||
const char* AstCAwait::broken() const {
|
const char* AstCAwait::broken() const {
|
||||||
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
|
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
|
||||||
|
@ -186,7 +186,8 @@ private:
|
|||||||
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
|
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
|
||||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
|
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
|
||||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
||||||
&& !nodep->varp()->basicp()->isForkSync())
|
&& !nodep->varp()->basicp()->isForkSync()
|
||||||
|
&& !nodep->varp()->basicp()->isProcessRef())
|
||||||
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
|
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||||
// Cast vars to IData first, else below has upper bits wrongly set
|
// Cast vars to IData first, else below has upper bits wrongly set
|
||||||
// CData x=3; out = (QData)(x<<30);
|
// CData x=3; out = (QData)(x<<30);
|
||||||
|
@ -79,6 +79,10 @@ string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) {
|
|||||||
args += prefixNameProtect(EmitCParentModule::get(nodep));
|
args += prefixNameProtect(EmitCParentModule::get(nodep));
|
||||||
args += "* vlSelf";
|
args += "* vlSelf";
|
||||||
}
|
}
|
||||||
|
if (nodep->needProcess()) {
|
||||||
|
if (!args.empty()) args += ", ";
|
||||||
|
args += "VlProcessRef vlProcess";
|
||||||
|
}
|
||||||
if (!nodep->argTypes().empty()) {
|
if (!nodep->argTypes().empty()) {
|
||||||
if (!args.empty()) args += ", ";
|
if (!args.empty()) args += ", ";
|
||||||
args += nodep->argTypes();
|
args += nodep->argTypes();
|
||||||
|
@ -417,6 +417,15 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
|
|||||||
puts(selfPointer);
|
puts(selfPointer);
|
||||||
comma = true;
|
comma = true;
|
||||||
}
|
}
|
||||||
|
if (nodep->funcp()->needProcess()) {
|
||||||
|
if (comma) puts(", ");
|
||||||
|
if (VN_IS(nodep->backp(), CAwait)) {
|
||||||
|
puts("vlProcess");
|
||||||
|
} else {
|
||||||
|
puts("std::make_shared<VlProcess>()");
|
||||||
|
}
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
if (!nodep->argTypes().empty()) {
|
if (!nodep->argTypes().empty()) {
|
||||||
if (comma) puts(", ");
|
if (comma) puts(", ");
|
||||||
puts(nodep->argTypes());
|
puts(nodep->argTypes());
|
||||||
@ -695,6 +704,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
|||||||
return "";
|
return "";
|
||||||
} else if (basicp && basicp->isForkSync()) {
|
} else if (basicp && basicp->isForkSync()) {
|
||||||
return "";
|
return "";
|
||||||
|
} else if (basicp && basicp->isProcessRef()) {
|
||||||
|
return "";
|
||||||
} else if (basicp && basicp->isDelayScheduler()) {
|
} else if (basicp && basicp->isDelayScheduler()) {
|
||||||
return "";
|
return "";
|
||||||
} else if (basicp && basicp->isTriggerScheduler()) {
|
} else if (basicp && basicp->isTriggerScheduler()) {
|
||||||
|
@ -1214,9 +1214,11 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp,
|
|||||||
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
|
// Process procedures per statement (unless profCFuncs), so we can split CFuncs within
|
||||||
// procedures. Everything else is handled in one go
|
// procedures. Everything else is handled in one go
|
||||||
bool suspendable = false;
|
bool suspendable = false;
|
||||||
|
bool needProcess = false;
|
||||||
bool slow = m_slow;
|
bool slow = m_slow;
|
||||||
if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) {
|
if (AstNodeProcedure* const procp = VN_CAST(nodep, NodeProcedure)) {
|
||||||
suspendable = procp->isSuspendable();
|
suspendable = procp->isSuspendable();
|
||||||
|
needProcess = procp->needProcess();
|
||||||
if (suspendable) slow = slow && !VN_IS(procp, Always);
|
if (suspendable) slow = slow && !VN_IS(procp, Always);
|
||||||
nodep = procp->stmtsp();
|
nodep = procp->stmtsp();
|
||||||
pushDeletep(procp);
|
pushDeletep(procp);
|
||||||
@ -1241,6 +1243,7 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp,
|
|||||||
const string name = cfuncName(modp, domainp, scopep, nodep);
|
const string name = cfuncName(modp, domainp, scopep, nodep);
|
||||||
newFuncpr
|
newFuncpr
|
||||||
= new AstCFunc{nodep->fileline(), name, scopep, suspendable ? "VlCoroutine" : ""};
|
= new AstCFunc{nodep->fileline(), name, scopep, suspendable ? "VlCoroutine" : ""};
|
||||||
|
if (needProcess) newFuncpr->setNeedProcess();
|
||||||
newFuncpr->isStatic(false);
|
newFuncpr->isStatic(false);
|
||||||
newFuncpr->isLoose(true);
|
newFuncpr->isLoose(true);
|
||||||
newFuncpr->slow(slow);
|
newFuncpr->slow(slow);
|
||||||
|
@ -247,6 +247,7 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
|
|||||||
subFuncp = createNewSubFuncp(scopep);
|
subFuncp = createNewSubFuncp(scopep);
|
||||||
subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc()));
|
subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc()));
|
||||||
subFuncp->rtnType("VlCoroutine");
|
subFuncp->rtnType("VlCoroutine");
|
||||||
|
if (procp->needProcess()) subFuncp->setNeedProcess();
|
||||||
if (VN_IS(procp, Always)) {
|
if (VN_IS(procp, Always)) {
|
||||||
subFuncp->slow(false);
|
subFuncp->slow(false);
|
||||||
FileLine* const flp = procp->fileline();
|
FileLine* const flp = procp->fileline();
|
||||||
|
@ -155,6 +155,19 @@ TimingKit prepareTiming(AstNetlist* const netlistp) {
|
|||||||
std::vector<AstVarScope*> m_writtenBySuspendable;
|
std::vector<AstVarScope*> m_writtenBySuspendable;
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
// Add arguments to a resume() call based on arguments in the suspending call
|
||||||
|
void addResumePins(AstCMethodHard* const resumep, AstNodeExpr* pinsp) {
|
||||||
|
AstCExpr* const exprp = VN_CAST(pinsp, CExpr);
|
||||||
|
AstText* const textp = VN_CAST(exprp->exprsp(), Text);
|
||||||
|
if (textp) {
|
||||||
|
// The first argument, vlProcess, isn't used by any of resume() methods, skip it
|
||||||
|
if ((pinsp = VN_CAST(pinsp->nextp(), NodeExpr))) {
|
||||||
|
resumep->addPinsp(pinsp->cloneTree(false));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resumep->addPinsp(pinsp->cloneTree(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
// Create an active with a timing scheduler resume() call
|
// Create an active with a timing scheduler resume() call
|
||||||
void createResumeActive(AstCAwait* const awaitp) {
|
void createResumeActive(AstCAwait* const awaitp) {
|
||||||
auto* const methodp = VN_AS(awaitp->exprp(), CMethodHard);
|
auto* const methodp = VN_AS(awaitp->exprp(), CMethodHard);
|
||||||
@ -171,7 +184,8 @@ TimingKit prepareTiming(AstNetlist* const netlistp) {
|
|||||||
// The first pin is the commit boolean, the rest (if any) should be debug info
|
// The first pin is the commit boolean, the rest (if any) should be debug info
|
||||||
// See V3Timing for details
|
// See V3Timing for details
|
||||||
if (AstNode* const dbginfop = methodp->pinsp()->nextp()) {
|
if (AstNode* const dbginfop = methodp->pinsp()->nextp()) {
|
||||||
resumep->addPinsp(static_cast<AstNodeExpr*>(dbginfop)->cloneTree(false));
|
if (methodp->pinsp())
|
||||||
|
addResumePins(resumep, static_cast<AstNodeExpr*>(dbginfop));
|
||||||
}
|
}
|
||||||
} else if (schedulerp->dtypep()->basicp()->isDynamicTriggerScheduler()) {
|
} else if (schedulerp->dtypep()->basicp()->isDynamicTriggerScheduler()) {
|
||||||
auto* const postp = resumep->cloneTree(false);
|
auto* const postp = resumep->cloneTree(false);
|
||||||
@ -262,6 +276,7 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
// STATE
|
// STATE
|
||||||
bool m_inClass = false; // Are we in a class?
|
bool m_inClass = false; // Are we in a class?
|
||||||
bool m_beginHasAwaits = false; // Does the current begin have awaits?
|
bool m_beginHasAwaits = false; // Does the current begin have awaits?
|
||||||
|
bool m_beginNeedProcess = false; // Does the current begin have process::self dependency?
|
||||||
AstFork* m_forkp = nullptr; // Current fork
|
AstFork* m_forkp = nullptr; // Current fork
|
||||||
AstCFunc* m_funcp = nullptr; // Current function
|
AstCFunc* m_funcp = nullptr; // Current function
|
||||||
|
|
||||||
@ -348,8 +363,9 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
UASSERT_OBJ(m_forkp, nodep, "Begin outside of a fork");
|
UASSERT_OBJ(m_forkp, nodep, "Begin outside of a fork");
|
||||||
// Start with children, so later we only find awaits that are actually in this begin
|
// Start with children, so later we only find awaits that are actually in this begin
|
||||||
m_beginHasAwaits = false;
|
m_beginHasAwaits = false;
|
||||||
|
m_beginNeedProcess = false;
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
if (m_beginHasAwaits) {
|
if (m_beginHasAwaits || m_beginNeedProcess) {
|
||||||
UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name");
|
UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name");
|
||||||
// Create a function to put this begin's statements in
|
// Create a function to put this begin's statements in
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
@ -372,9 +388,19 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
}
|
}
|
||||||
// Put the begin's statements in the function, delete the begin
|
// Put the begin's statements in the function, delete the begin
|
||||||
newfuncp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
newfuncp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||||
|
if (m_beginNeedProcess) {
|
||||||
|
newfuncp->setNeedProcess();
|
||||||
|
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(),
|
||||||
|
"vlProcess->state(VlProcess::FINISHED);\n"});
|
||||||
|
}
|
||||||
|
if (!m_beginHasAwaits) {
|
||||||
|
// co_return at the end (either that or a co_await is required in a coroutine
|
||||||
|
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
|
||||||
|
}
|
||||||
remapLocals(newfuncp, callp);
|
remapLocals(newfuncp, callp);
|
||||||
} else {
|
} else {
|
||||||
// No awaits, just inline the forked process
|
// The begin has neither awaits nor a process::self call, just inline the
|
||||||
|
// statements
|
||||||
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||||
}
|
}
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
@ -383,6 +409,10 @@ void transformForks(AstNetlist* const netlistp) {
|
|||||||
m_beginHasAwaits = true;
|
m_beginHasAwaits = true;
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
}
|
}
|
||||||
|
void visit(AstCCall* nodep) override {
|
||||||
|
if (nodep->funcp()->needProcess()) m_beginNeedProcess = true;
|
||||||
|
iterateChildrenConst(nodep);
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------
|
//--------------------
|
||||||
void visit(AstNodeExpr*) override {} // Accelerate
|
void visit(AstNodeExpr*) override {} // Accelerate
|
||||||
|
@ -1316,6 +1316,9 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark the fact that this function allocates std::process
|
||||||
|
if (nodep->isFromStd() && nodep->name() == "self") cfuncp->setNeedProcess();
|
||||||
|
|
||||||
// Delete rest of cloned task and return new func
|
// Delete rest of cloned task and return new func
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
if (debug() >= 9) cfuncp->dumpTree("- userFunc: ");
|
if (debug() >= 9) cfuncp->dumpTree("- userFunc: ");
|
||||||
|
@ -61,6 +61,16 @@
|
|||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
// ######################################################################
|
||||||
|
|
||||||
|
enum TimingFlag : uint8_t {
|
||||||
|
// Properties of flags with higher numbers include properties of flags with
|
||||||
|
// lower numbers
|
||||||
|
T_NORM = 0, // Normal non-suspendable process
|
||||||
|
T_SUSP = 1, // Suspendable
|
||||||
|
T_PROC = 2 // Suspendable with process metadata
|
||||||
|
};
|
||||||
|
|
||||||
// ######################################################################
|
// ######################################################################
|
||||||
// Detect nodes affected by timing
|
// Detect nodes affected by timing
|
||||||
|
|
||||||
@ -94,8 +104,10 @@ private:
|
|||||||
// AstClass::user1() -> bool. Set true if the class
|
// AstClass::user1() -> bool. Set true if the class
|
||||||
// member cache has been
|
// member cache has been
|
||||||
// refreshed.
|
// refreshed.
|
||||||
// Ast{NodeProcedure,CFunc,Begin}::user2() -> bool. Set true if process/task is
|
// Ast{NodeProcedure,CFunc,Begin}::user2() -> int. Set to >= T_SUSP if
|
||||||
// suspendable
|
// process/task suspendable
|
||||||
|
// and to T_PROC if it
|
||||||
|
// needs process metadata.
|
||||||
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph
|
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph
|
||||||
const VNUser1InUse m_user1InUse;
|
const VNUser1InUse m_user1InUse;
|
||||||
const VNUser2InUse m_user2InUse;
|
const VNUser2InUse m_user2InUse;
|
||||||
@ -113,15 +125,23 @@ private:
|
|||||||
if (!nodep->user3p()) nodep->user3p(new TimingDependencyVertex{&m_depGraph, nodep});
|
if (!nodep->user3p()) nodep->user3p(new TimingDependencyVertex{&m_depGraph, nodep});
|
||||||
return nodep->user3u().to<TimingDependencyVertex*>();
|
return nodep->user3u().to<TimingDependencyVertex*>();
|
||||||
}
|
}
|
||||||
// Propagate suspendable flag to all nodes that depend on the given one
|
// Set timing flag of a node
|
||||||
void propagateSuspendable(TimingDependencyVertex* const vxp) {
|
bool setTimingFlag(AstNode* nodep, int flag) {
|
||||||
|
// Properties of flags with higher numbers include properties of flags with lower
|
||||||
|
// numbers, so modify nodep->user2() only if it will increase.
|
||||||
|
if (nodep->user2() < flag) {
|
||||||
|
nodep->user2(flag);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Propagate suspendable/needProcess flag to all nodes that depend on the given one
|
||||||
|
void propagateTimingFlags(TimingDependencyVertex* const vxp) {
|
||||||
|
auto* const parentp = vxp->nodep();
|
||||||
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||||
auto* const depVxp = static_cast<TimingDependencyVertex*>(edgep->fromp());
|
auto* const depVxp = static_cast<TimingDependencyVertex*>(edgep->fromp());
|
||||||
AstNode* const depp = depVxp->nodep();
|
AstNode* const depp = depVxp->nodep();
|
||||||
if (!depp->user2()) {
|
if (setTimingFlag(depp, parentp->user2())) propagateTimingFlags(depVxp);
|
||||||
depp->user2(true);
|
|
||||||
propagateSuspendable(depVxp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +162,7 @@ private:
|
|||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
TimingDependencyVertex* const vxp = getDependencyVertex(nodep);
|
TimingDependencyVertex* const vxp = getDependencyVertex(nodep);
|
||||||
|
if (nodep->needProcess()) nodep->user2(T_PROC);
|
||||||
if (!m_classp) return;
|
if (!m_classp) return;
|
||||||
// If class method (possibly overrides another method)
|
// If class method (possibly overrides another method)
|
||||||
if (!m_classp->user1SetOnce()) m_classp->repairCache();
|
if (!m_classp->user1SetOnce()) m_classp->repairCache();
|
||||||
@ -162,15 +183,13 @@ private:
|
|||||||
// the root of the inheritance hierarchy and check if the original method is
|
// the root of the inheritance hierarchy and check if the original method is
|
||||||
// virtual or not.
|
// virtual or not.
|
||||||
if (!cextp->classp()->user1SetOnce()) cextp->classp()->repairCache();
|
if (!cextp->classp()->user1SetOnce()) cextp->classp()->repairCache();
|
||||||
if (AstCFunc* const overriddenp
|
if (auto* const overriddenp
|
||||||
= VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) {
|
= VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) {
|
||||||
if (overriddenp->user2()) { // If it's suspendable
|
setTimingFlag(nodep, overriddenp->user2());
|
||||||
nodep->user2(true); // Then we are also suspendable
|
if (nodep->user2()
|
||||||
// As both are suspendable already, there is no need to add it as our
|
< T_PROC) { // Add a vertex only if the flag can still change
|
||||||
// dependency or self to its dependencies
|
// Make a dependency cycle, as being suspendable should propagate both up
|
||||||
} else {
|
// and down the inheritance tree
|
||||||
// Make a dependency cycle, as being suspendable should propagate both
|
|
||||||
// up and down the inheritance tree
|
|
||||||
TimingDependencyVertex* const overriddenVxp
|
TimingDependencyVertex* const overriddenVxp
|
||||||
= getDependencyVertex(overriddenp);
|
= getDependencyVertex(overriddenp);
|
||||||
new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1};
|
new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1};
|
||||||
@ -184,11 +203,8 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstNodeCCall* nodep) override {
|
void visit(AstNodeCCall* nodep) override {
|
||||||
if (nodep->funcp()->user2()) {
|
setTimingFlag(m_procp, nodep->funcp()->user2());
|
||||||
m_procp->user2(true);
|
if (m_procp->user2() < T_PROC) { // Add a vertex only if the flag can still change
|
||||||
// Both the caller and the callee are suspendable, no need to make dependency edges
|
|
||||||
// between them
|
|
||||||
} else {
|
|
||||||
TimingDependencyVertex* const procVxp = getDependencyVertex(m_procp);
|
TimingDependencyVertex* const procVxp = getDependencyVertex(m_procp);
|
||||||
TimingDependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp());
|
TimingDependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp());
|
||||||
new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1};
|
new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1};
|
||||||
@ -203,7 +219,7 @@ private:
|
|||||||
void visit(AstNode* nodep) override {
|
void visit(AstNode* nodep) override {
|
||||||
if (nodep->isTimingControl()) {
|
if (nodep->isTimingControl()) {
|
||||||
v3Global.setUsesTiming();
|
v3Global.setUsesTiming();
|
||||||
if (m_procp) m_procp->user2(true);
|
if (m_procp) m_procp->user2(T_SUSP);
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
@ -218,7 +234,7 @@ public:
|
|||||||
m_depGraph.removeTransitiveEdges();
|
m_depGraph.removeTransitiveEdges();
|
||||||
for (V3GraphVertex* vxp = m_depGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
for (V3GraphVertex* vxp = m_depGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
TimingDependencyVertex* const depVxp = static_cast<TimingDependencyVertex*>(vxp);
|
TimingDependencyVertex* const depVxp = static_cast<TimingDependencyVertex*>(vxp);
|
||||||
if (depVxp->nodep()->user2()) propagateSuspendable(depVxp);
|
if (depVxp->nodep()->user2()) propagateTimingFlags(depVxp);
|
||||||
}
|
}
|
||||||
if (dumpGraphLevel() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps");
|
if (dumpGraphLevel() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps");
|
||||||
}
|
}
|
||||||
@ -252,6 +268,7 @@ private:
|
|||||||
AstClass* m_classp = nullptr; // Current class
|
AstClass* m_classp = nullptr; // Current class
|
||||||
AstScope* m_scopep = nullptr; // Current scope
|
AstScope* m_scopep = nullptr; // Current scope
|
||||||
AstActive* m_activep = nullptr; // Current active
|
AstActive* m_activep = nullptr; // Current active
|
||||||
|
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
|
||||||
double m_timescaleFactor = 1.0; // Factor to scale delays by
|
double m_timescaleFactor = 1.0; // Factor to scale delays by
|
||||||
|
|
||||||
// Unique names
|
// Unique names
|
||||||
@ -460,6 +477,14 @@ private:
|
|||||||
methodp->addPinsp(createEventDescription(sensesp));
|
methodp->addPinsp(createEventDescription(sensesp));
|
||||||
addDebugInfo(methodp);
|
addDebugInfo(methodp);
|
||||||
}
|
}
|
||||||
|
// Adds process pointer to a hardcoded method call
|
||||||
|
void addProcessInfo(AstCMethodHard* const methodp) const {
|
||||||
|
FileLine* const flp = methodp->fileline();
|
||||||
|
AstCExpr* const ap = new AstCExpr{
|
||||||
|
flp, m_procp && m_procp->user2() == T_PROC ? "vlProcess" : "nullptr", 0};
|
||||||
|
ap->dtypeSetVoid();
|
||||||
|
methodp->addPinsp(ap);
|
||||||
|
}
|
||||||
// Creates the fork handle type and returns it
|
// Creates the fork handle type and returns it
|
||||||
AstBasicDType* getCreateForkSyncDTypep() {
|
AstBasicDType* getCreateForkSyncDTypep() {
|
||||||
if (m_forkDtp) return m_forkDtp;
|
if (m_forkDtp) return m_forkDtp;
|
||||||
@ -512,11 +537,13 @@ private:
|
|||||||
auto* const initp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE},
|
auto* const initp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE},
|
||||||
"init", new AstConst{flp, joinCount}};
|
"init", new AstConst{flp, joinCount}};
|
||||||
initp->dtypeSetVoid();
|
initp->dtypeSetVoid();
|
||||||
|
addProcessInfo(initp);
|
||||||
forkp->addHereThisAsNext(initp->makeStmt());
|
forkp->addHereThisAsNext(initp->makeStmt());
|
||||||
// Await the join at the end
|
// Await the join at the end
|
||||||
auto* const joinp
|
auto* const joinp
|
||||||
= new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, "join"};
|
= new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, "join"};
|
||||||
joinp->dtypeSetVoid();
|
joinp->dtypeSetVoid();
|
||||||
|
addProcessInfo(joinp);
|
||||||
addDebugInfo(joinp);
|
addDebugInfo(joinp);
|
||||||
AstCAwait* const awaitp = new AstCAwait{flp, joinp};
|
AstCAwait* const awaitp = new AstCAwait{flp, joinp};
|
||||||
awaitp->dtypeSetVoid();
|
awaitp->dtypeSetVoid();
|
||||||
@ -543,13 +570,24 @@ private:
|
|||||||
m_activep = nullptr;
|
m_activep = nullptr;
|
||||||
}
|
}
|
||||||
void visit(AstNodeProcedure* nodep) override {
|
void visit(AstNodeProcedure* nodep) override {
|
||||||
|
VL_RESTORER(m_procp);
|
||||||
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (nodep->user2()) nodep->setSuspendable();
|
if (nodep->user2() >= T_SUSP) nodep->setSuspendable();
|
||||||
|
if (nodep->user2() >= T_PROC) nodep->setNeedProcess();
|
||||||
|
}
|
||||||
|
void visit(AstInitial* nodep) override {
|
||||||
|
visit(static_cast<AstNodeProcedure*>(nodep));
|
||||||
|
if (nodep->needProcess() && !nodep->user1SetOnce()) {
|
||||||
|
nodep->addStmtsp(
|
||||||
|
new AstCStmt{nodep->fileline(), "vlProcess->state(VlProcess::FINISHED);\n"});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void visit(AstAlways* nodep) override {
|
void visit(AstAlways* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (!nodep->user2()) return;
|
if (!nodep->user2()) return;
|
||||||
|
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
|
||||||
nodep->setSuspendable();
|
nodep->setSuspendable();
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstSenTree* const sensesp = m_activep->sensesp();
|
AstSenTree* const sensesp = m_activep->sensesp();
|
||||||
@ -567,6 +605,8 @@ private:
|
|||||||
m_activep->addNextHere(activep);
|
m_activep->addNextHere(activep);
|
||||||
}
|
}
|
||||||
void visit(AstCFunc* nodep) override {
|
void visit(AstCFunc* nodep) override {
|
||||||
|
VL_RESTORER(m_procp);
|
||||||
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (!nodep->user2()) return;
|
if (!nodep->user2()) return;
|
||||||
nodep->rtnType("VlCoroutine");
|
nodep->rtnType("VlCoroutine");
|
||||||
@ -588,6 +628,7 @@ private:
|
|||||||
firstCoStmtp->v3warn(E_UNSUPPORTED,
|
firstCoStmtp->v3warn(E_UNSUPPORTED,
|
||||||
"Unsupported: Timing controls inside DPI-exported tasks");
|
"Unsupported: Timing controls inside DPI-exported tasks");
|
||||||
}
|
}
|
||||||
|
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
|
||||||
}
|
}
|
||||||
void visit(AstNodeCCall* nodep) override {
|
void visit(AstNodeCCall* nodep) override {
|
||||||
if (nodep->funcp()->user2() && !nodep->user1SetOnce()) { // If suspendable
|
if (nodep->funcp()->user2() && !nodep->user1SetOnce()) { // If suspendable
|
||||||
@ -627,6 +668,7 @@ private:
|
|||||||
auto* const delayMethodp = new AstCMethodHard{
|
auto* const delayMethodp = new AstCMethodHard{
|
||||||
flp, new AstVarRef{flp, getCreateDelayScheduler(), VAccess::WRITE}, "delay", valuep};
|
flp, new AstVarRef{flp, getCreateDelayScheduler(), VAccess::WRITE}, "delay", valuep};
|
||||||
delayMethodp->dtypeSetVoid();
|
delayMethodp->dtypeSetVoid();
|
||||||
|
addProcessInfo(delayMethodp);
|
||||||
addDebugInfo(delayMethodp);
|
addDebugInfo(delayMethodp);
|
||||||
// Create the co_await
|
// Create the co_await
|
||||||
AstCAwait* const awaitp = new AstCAwait{flp, delayMethodp, getCreateDelaySenTree()};
|
AstCAwait* const awaitp = new AstCAwait{flp, delayMethodp, getCreateDelaySenTree()};
|
||||||
@ -666,6 +708,7 @@ private:
|
|||||||
flp, new AstVarRef{flp, getCreateDynamicTriggerScheduler(), VAccess::WRITE},
|
flp, new AstVarRef{flp, getCreateDynamicTriggerScheduler(), VAccess::WRITE},
|
||||||
"evaluation"};
|
"evaluation"};
|
||||||
evalMethodp->dtypeSetVoid();
|
evalMethodp->dtypeSetVoid();
|
||||||
|
addProcessInfo(evalMethodp);
|
||||||
auto* const sensesp = nodep->sensesp();
|
auto* const sensesp = nodep->sensesp();
|
||||||
addEventDebugInfo(evalMethodp, sensesp);
|
addEventDebugInfo(evalMethodp, sensesp);
|
||||||
// Create the co_await
|
// Create the co_await
|
||||||
@ -723,6 +766,7 @@ private:
|
|||||||
// If it should be committed immediately, pass true, otherwise false
|
// If it should be committed immediately, pass true, otherwise false
|
||||||
triggerMethodp->addPinsp(nodep->user2() ? new AstConst{flp, AstConst::BitTrue{}}
|
triggerMethodp->addPinsp(nodep->user2() ? new AstConst{flp, AstConst::BitTrue{}}
|
||||||
: new AstConst{flp, AstConst::BitFalse{}});
|
: new AstConst{flp, AstConst::BitFalse{}});
|
||||||
|
addProcessInfo(triggerMethodp);
|
||||||
addEventDebugInfo(triggerMethodp, sensesp);
|
addEventDebugInfo(triggerMethodp, sensesp);
|
||||||
// Create the co_await
|
// Create the co_await
|
||||||
AstCAwait* const awaitp = new AstCAwait{flp, triggerMethodp, sensesp};
|
AstCAwait* const awaitp = new AstCAwait{flp, triggerMethodp, sensesp};
|
||||||
@ -858,6 +902,11 @@ private:
|
|||||||
}
|
}
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
}
|
}
|
||||||
|
void visit(AstBegin* nodep) override {
|
||||||
|
VL_RESTORER(m_procp);
|
||||||
|
m_procp = nodep;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
void visit(AstFork* nodep) override {
|
void visit(AstFork* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
v3Global.setUsesTiming();
|
v3Global.setUsesTiming();
|
||||||
|
@ -232,7 +232,7 @@ private:
|
|||||||
const AstWith* m_withp = nullptr; // Current 'with' statement
|
const AstWith* m_withp = nullptr; // Current 'with' statement
|
||||||
const AstFunc* m_funcp = nullptr; // Current function
|
const AstFunc* m_funcp = nullptr; // Current function
|
||||||
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
const AstAttrOf* m_attrp = nullptr; // Current attribute
|
||||||
AstNodeModule* m_modp = nullptr; // Current module
|
AstPackage* m_pkgp = nullptr; // Current package
|
||||||
const bool m_paramsOnly; // Computing parameter value; limit operation
|
const bool m_paramsOnly; // Computing parameter value; limit operation
|
||||||
const bool m_doGenerate; // Do errors later inside generate statement
|
const bool m_doGenerate; // Do errors later inside generate statement
|
||||||
int m_dtTables = 0; // Number of created data type tables
|
int m_dtTables = 0; // Number of created data type tables
|
||||||
@ -2628,6 +2628,16 @@ private:
|
|||||||
}
|
}
|
||||||
void visit(AstClass* nodep) override {
|
void visit(AstClass* nodep) override {
|
||||||
if (nodep->didWidthAndSet()) return;
|
if (nodep->didWidthAndSet()) return;
|
||||||
|
// If the package is std::process, set m_process type to VlProcessRef
|
||||||
|
if (m_pkgp && m_pkgp->name() == "std" && nodep->name() == "process") {
|
||||||
|
if (AstVar* const varp = VN_CAST(nodep->findMember("m_process"), Var)) {
|
||||||
|
AstBasicDType* const dtypep = new AstBasicDType{
|
||||||
|
nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED};
|
||||||
|
v3Global.rootp()->typeTablep()->addTypesp(dtypep);
|
||||||
|
varp->getChildDTypep()->unlinkFrBack();
|
||||||
|
varp->dtypep(dtypep);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Must do extends first, as we may in functions under this class
|
// Must do extends first, as we may in functions under this class
|
||||||
// start following a tree of extends that takes us to other classes
|
// start following a tree of extends that takes us to other classes
|
||||||
VL_RESTORER(m_classp);
|
VL_RESTORER(m_classp);
|
||||||
@ -2636,6 +2646,11 @@ private:
|
|||||||
userIterateChildren(nodep, nullptr); // First size all members
|
userIterateChildren(nodep, nullptr); // First size all members
|
||||||
nodep->repairCache();
|
nodep->repairCache();
|
||||||
}
|
}
|
||||||
|
void visit(AstPackage* nodep) override {
|
||||||
|
VL_RESTORER(m_pkgp);
|
||||||
|
m_pkgp = nodep;
|
||||||
|
userIterateChildren(nodep, nullptr);
|
||||||
|
}
|
||||||
void visit(AstThisRef* nodep) override {
|
void visit(AstThisRef* nodep) override {
|
||||||
if (nodep->didWidthAndSet()) return;
|
if (nodep->didWidthAndSet()) return;
|
||||||
nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
|
nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
|
||||||
@ -2646,12 +2661,6 @@ private:
|
|||||||
// though causes problems with t_class_forward.v, so for now avoided
|
// though causes problems with t_class_forward.v, so for now avoided
|
||||||
// userIterateChildren(nodep->classp(), nullptr);
|
// userIterateChildren(nodep->classp(), nullptr);
|
||||||
}
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
|
||||||
// Visitor does not include AstClass - specialized visitor above
|
|
||||||
VL_RESTORER(m_modp);
|
|
||||||
m_modp = nodep;
|
|
||||||
userIterateChildren(nodep, nullptr);
|
|
||||||
}
|
|
||||||
void visit(AstClassOrPackageRef* nodep) override {
|
void visit(AstClassOrPackageRef* nodep) override {
|
||||||
if (nodep->didWidthAndSet()) return;
|
if (nodep->didWidthAndSet()) return;
|
||||||
userIterateChildren(nodep, nullptr);
|
userIterateChildren(nodep, nullptr);
|
||||||
@ -3491,7 +3500,7 @@ private:
|
|||||||
VL_DANGLING(index_exprp); // May have been edited
|
VL_DANGLING(index_exprp); // May have been edited
|
||||||
return VN_AS(nodep->pinsp(), Arg)->exprp();
|
return VN_AS(nodep->pinsp(), Arg)->exprp();
|
||||||
}
|
}
|
||||||
void methodCallWarnTiming(AstMethodCall* const nodep, const std::string& className) {
|
void methodCallWarnTiming(AstNodeFTaskRef* const nodep, const std::string& className) {
|
||||||
if (v3Global.opt.timing().isSetFalse()) {
|
if (v3Global.opt.timing().isSetFalse()) {
|
||||||
nodep->v3warn(E_NOTIMING,
|
nodep->v3warn(E_NOTIMING,
|
||||||
className << "::" << nodep->name() << "() requires --timing");
|
className << "::" << nodep->name() << "() requires --timing");
|
||||||
@ -3515,14 +3524,12 @@ private:
|
|||||||
if (classp->name() == "semaphore" || classp->name() == "process"
|
if (classp->name() == "semaphore" || classp->name() == "process"
|
||||||
|| VString::startsWith(classp->name(), "mailbox")) {
|
|| VString::startsWith(classp->name(), "mailbox")) {
|
||||||
// Find the package the class is in
|
// Find the package the class is in
|
||||||
AstNode* pkgItemp = classp;
|
AstPackage* const packagep = getItemPackage(classp);
|
||||||
while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) {
|
|
||||||
pkgItemp = pkgItemp->backp();
|
|
||||||
}
|
|
||||||
AstPackage* const packagep = VN_CAST(pkgItemp->backp(), Package);
|
|
||||||
// Check if it's std
|
// Check if it's std
|
||||||
if (packagep && packagep->name() == "std") {
|
if (packagep && packagep->name() == "std") {
|
||||||
if (classp->name() == "semaphore" && nodep->name() == "get") {
|
if (classp->name() == "process") {
|
||||||
|
methodCallWarnTiming(nodep, "process");
|
||||||
|
} else if (classp->name() == "semaphore" && nodep->name() == "get") {
|
||||||
methodCallWarnTiming(nodep, "semaphore");
|
methodCallWarnTiming(nodep, "semaphore");
|
||||||
} else if (nodep->name() == "put" || nodep->name() == "get"
|
} else if (nodep->name() == "put" || nodep->name() == "get"
|
||||||
|| nodep->name() == "peek") {
|
|| nodep->name() == "peek") {
|
||||||
@ -5323,6 +5330,7 @@ private:
|
|||||||
nodep->didWidth(true);
|
nodep->didWidth(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (m_pkgp && m_pkgp->name() == "std") nodep->isFromStd(true);
|
||||||
if (nodep->classMethod() && nodep->name() == "rand_mode") {
|
if (nodep->classMethod() && nodep->name() == "rand_mode") {
|
||||||
nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
|
nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
|
||||||
" (IEEE 1800-2017 18.8)");
|
" (IEEE 1800-2017 18.8)");
|
||||||
@ -5405,9 +5413,25 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstPackage* getItemPackage(AstNode* pkgItemp) {
|
||||||
|
while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) {
|
||||||
|
pkgItemp = pkgItemp->backp();
|
||||||
|
}
|
||||||
|
return VN_CAST(pkgItemp->backp(), Package);
|
||||||
|
}
|
||||||
void visit(AstFuncRef* nodep) override {
|
void visit(AstFuncRef* nodep) override {
|
||||||
visit(static_cast<AstNodeFTaskRef*>(nodep));
|
visit(static_cast<AstNodeFTaskRef*>(nodep));
|
||||||
nodep->dtypeFrom(nodep->taskp());
|
nodep->dtypeFrom(nodep->taskp());
|
||||||
|
if (nodep->fileline()->timingOn()) {
|
||||||
|
AstNodeModule* const classp = nodep->classOrPackagep();
|
||||||
|
if (nodep->name() == "self" && classp->name() == "process") {
|
||||||
|
// Find if package the class is in is std::
|
||||||
|
AstPackage* const packagep = getItemPackage(classp);
|
||||||
|
if (packagep && packagep->name() == "std") {
|
||||||
|
methodCallWarnTiming(nodep, "process");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// if (debug()) nodep->dumpTree("- FuncOut: ");
|
// if (debug()) nodep->dumpTree("- FuncOut: ");
|
||||||
}
|
}
|
||||||
// Returns true if dtypep0 and dtypep1 have same dimensions
|
// Returns true if dtypep0 and dtypep1 have same dimensions
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
[0] %Error: verilated_std.sv:154: Assertion failed in top.std.process.set_randstate: std::process::set_randstate() not supported
|
'{m_process:process}
|
||||||
%Error: verilated_std.sv:154: Verilog $stop
|
*-* All Finished *-*
|
||||||
Aborting...
|
|
||||||
|
@ -10,14 +10,19 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
|
|
||||||
scenarios(simulator => 1);
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
compile(
|
if (!$Self->have_coroutines) {
|
||||||
);
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compile(
|
||||||
|
v_flags2 => ["--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
fails => $Self->{vlt_all},
|
check_finished => 1,
|
||||||
expect_filename => $Self->{golden_filename},
|
expect_filename => $Self->{golden_filename},
|
||||||
check_finished => !$Self->{vlt_all},
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
@ -38,6 +38,8 @@ module t(/*AUTOARG*/);
|
|||||||
p.srandom(0);
|
p.srandom(0);
|
||||||
p.set_randstate(p.get_randstate());
|
p.set_randstate(p.get_randstate());
|
||||||
|
|
||||||
|
$display("%p", p);
|
||||||
|
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
scenarios(vlt => 1);
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
lint(
|
lint(
|
||||||
verilator_flags2 => ["--xml-only"],
|
verilator_flags2 => ["--xml-only", "--timing"],
|
||||||
fails => 1,
|
fails => 1,
|
||||||
expect_filename => $Self->{golden_filename},
|
expect_filename => $Self->{golden_filename},
|
||||||
);
|
);
|
||||||
|
27
test_regress/t/t_process_finished.pl
Executable file
27
test_regress/t/t_process_finished.pl
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2023 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
24
test_regress/t/t_process_finished.v
Normal file
24
test_regress/t/t_process_finished.v
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
process p;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
p = process::self();
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (p.status() != process::FINISHED)
|
||||||
|
$stop;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
11
test_regress/t/t_process_fork.out
Normal file
11
test_regress/t/t_process_fork.out
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
job started
|
||||||
|
job started
|
||||||
|
job started
|
||||||
|
job started
|
||||||
|
job started
|
||||||
|
job started
|
||||||
|
job started
|
||||||
|
job started
|
||||||
|
all jobs started
|
||||||
|
all jobs finished
|
||||||
|
*-* All Finished *-*
|
28
test_regress/t/t_process_fork.pl
Executable file
28
test_regress/t/t_process_fork.pl
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2023 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
30
test_regress/t/t_process_fork.v
Normal file
30
test_regress/t/t_process_fork.v
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t;
|
||||||
|
process job[] = new [8];
|
||||||
|
bit is_alloc = 0;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
foreach (job[j]) fork
|
||||||
|
begin
|
||||||
|
$write("job started\n");
|
||||||
|
job[j] = process::self();
|
||||||
|
end
|
||||||
|
join_none
|
||||||
|
foreach (job[j]) begin
|
||||||
|
is_alloc = !!job[j];
|
||||||
|
wait (is_alloc);
|
||||||
|
end
|
||||||
|
$write("all jobs started\n");
|
||||||
|
foreach (job[j]) begin
|
||||||
|
job[j].await();
|
||||||
|
end
|
||||||
|
$write("all jobs finished\n");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
27
test_regress/t/t_process_kill.pl
Executable file
27
test_regress/t/t_process_kill.pl
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2023 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
31
test_regress/t/t_process_kill.v
Normal file
31
test_regress/t/t_process_kill.v
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
process p;
|
||||||
|
bit s = 0;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
wait (s);
|
||||||
|
p.kill();
|
||||||
|
p.await();
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (!p) begin
|
||||||
|
p = process::self();
|
||||||
|
s = 1;
|
||||||
|
end else begin
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
54
test_regress/t/t_process_notiming.out
Normal file
54
test_regress/t/t_process_notiming.out
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
%Error-NOTIMING: t/t_process.v:26:20: process::self() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
26 | p = process::self();
|
||||||
|
| ^~~~
|
||||||
|
... For error description see https://verilator.org/warn/NOTIMING?v=latest
|
||||||
|
%Error-NOTIMING: t/t_process.v:27:13: process::status() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
27 | if (p.status() != process::RUNNING) $stop;
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:28:13: process::status() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
28 | if (p.status() == process::WAITING) $stop;
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:29:13: process::status() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
29 | if (p.status() == process::SUSPENDED) $stop;
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:30:13: process::status() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
30 | if (p.status() == process::KILLED) $stop;
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:31:13: process::status() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
31 | if (p.status() == process::FINISHED) $stop;
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:33:16: process::kill() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
33 | if (0) p.kill();
|
||||||
|
| ^~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:34:16: process::await() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
34 | if (0) p.await();
|
||||||
|
| ^~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:35:16: process::suspend() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
35 | if (0) p.suspend();
|
||||||
|
| ^~~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:36:16: process::resume() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
36 | if (0) p.resume();
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:38:9: process::srandom() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
38 | p.srandom(0);
|
||||||
|
| ^~~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:39:25: process::get_randstate() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
39 | p.set_randstate(p.get_randstate());
|
||||||
|
| ^~~~~~~~~~~~~
|
||||||
|
%Error-NOTIMING: t/t_process.v:39:9: process::set_randstate() requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
39 | p.set_randstate(p.get_randstate());
|
||||||
|
| ^~~~~~~~~~~~~
|
||||||
|
%Error: Exiting due to
|
22
test_regress/t/t_process_notiming.pl
Executable file
22
test_regress/t/t_process_notiming.pl
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2020 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
top_filename("t/t_process.v");
|
||||||
|
|
||||||
|
compile(
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
v_flags2 => ["+define+T_PROCESS+std::process", "--no-timing"],
|
||||||
|
fails => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
27
test_regress/t/t_process_rand.pl
Executable file
27
test_regress/t/t_process_rand.pl
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compile(
|
||||||
|
v_flags2 => ["--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
52
test_regress/t/t_process_rand.v
Normal file
52
test_regress/t/t_process_rand.v
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t;
|
||||||
|
process p;
|
||||||
|
|
||||||
|
integer seed;
|
||||||
|
string state;
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
p = process::self();
|
||||||
|
|
||||||
|
// Test setting RNG state with state string
|
||||||
|
state = p.get_randstate();
|
||||||
|
p.set_randstate(state);
|
||||||
|
a = $random;
|
||||||
|
p.set_randstate(state);
|
||||||
|
b = $random;
|
||||||
|
$display("a=%d, b=%d", a, b);
|
||||||
|
if (a != b) $stop;
|
||||||
|
|
||||||
|
// Test the same with $urandom
|
||||||
|
state = p.get_randstate();
|
||||||
|
p.set_randstate(state);
|
||||||
|
a = $urandom;
|
||||||
|
p.set_randstate(state);
|
||||||
|
b = $urandom;
|
||||||
|
$display("a=%d, b=%d", a, b);
|
||||||
|
if (a != b) $stop;
|
||||||
|
|
||||||
|
// Test if the results repeat after the state is reset
|
||||||
|
state = p.get_randstate();
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
$random;
|
||||||
|
a = $random;
|
||||||
|
// Now reset the state and take 11th result again
|
||||||
|
p.set_randstate(state);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
$random;
|
||||||
|
b = $random;
|
||||||
|
$display("a=%d, b=%d", a, b);
|
||||||
|
if (a != b) $stop;
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
@ -12,15 +12,18 @@ scenarios(simulator => 1);
|
|||||||
|
|
||||||
top_filename("t/t_process.v");
|
top_filename("t/t_process.v");
|
||||||
|
|
||||||
compile(
|
if (!$Self->have_coroutines) {
|
||||||
v_flags2 => ["+define+T_PROCESS+std::process"],
|
skip("No coroutine support");
|
||||||
);
|
}
|
||||||
|
else {
|
||||||
|
compile(
|
||||||
|
v_flags2 => ["+define+T_PROCESS+std::process", "--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
check_finished => !$Self->{vlt_all},
|
check_finished => 1,
|
||||||
fails => $Self->{vlt_all},
|
) if !$Self->{vlt_all};
|
||||||
expect_filename => $Self->{golden_filename},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
Loading…
Reference in New Issue
Block a user