2018-07-23 00:54:28 +00:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
//=============================================================================
|
|
|
|
//
|
2021-03-20 21:46:00 +00:00
|
|
|
// Code available from: https://verilator.org
|
|
|
|
//
|
2024-01-01 08:19:59 +00:00
|
|
|
// Copyright 2012-2024 by Wilson Snyder. This program is free software; you can
|
2020-03-21 15:24:24 +00:00
|
|
|
// 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
|
2018-07-23 00:54:28 +00:00
|
|
|
//
|
|
|
|
//=============================================================================
|
|
|
|
///
|
|
|
|
/// \file
|
2021-03-20 21:46:00 +00:00
|
|
|
/// \brief Verilated thread pool implementation code
|
|
|
|
///
|
|
|
|
/// This file must be compiled and linked against all Verilated objects
|
|
|
|
/// that use --threads.
|
|
|
|
///
|
|
|
|
/// Use "verilator --threads" to add this to the Makefile for the linker.
|
2018-07-23 00:54:28 +00:00
|
|
|
///
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#include "verilatedos.h"
|
2022-08-05 09:56:57 +00:00
|
|
|
|
2018-07-23 00:54:28 +00:00
|
|
|
#include "verilated_threads.h"
|
2018-10-14 17:43:24 +00:00
|
|
|
|
2018-07-23 00:54:28 +00:00
|
|
|
#include <cstdio>
|
2022-01-09 21:49:38 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2018-07-23 00:54:28 +00:00
|
|
|
|
2021-03-05 00:23:40 +00:00
|
|
|
//=============================================================================
|
|
|
|
// Globals
|
|
|
|
|
|
|
|
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
|
|
|
|
2022-03-27 19:27:40 +00:00
|
|
|
std::atomic<uint64_t> VlMTaskVertex::s_yields;
|
2018-07-23 00:54:28 +00:00
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
// VlMTaskVertex
|
|
|
|
|
2022-03-27 19:27:40 +00:00
|
|
|
VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
|
2020-08-16 13:55:36 +00:00
|
|
|
: m_upstreamDepsDone{0}
|
|
|
|
, m_upstreamDepCount{upstreamDepCount} {
|
2018-07-23 00:54:28 +00:00
|
|
|
assert(atomic_is_lock_free(&m_upstreamDepsDone));
|
|
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
// VlWorkerThread
|
|
|
|
|
2022-07-12 10:41:15 +00:00
|
|
|
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
|
2021-07-25 17:38:27 +00:00
|
|
|
: m_ready_size{0}
|
2022-07-12 10:41:15 +00:00
|
|
|
, m_cthread{startWorker, this, contextp} {}
|
2018-07-23 00:54:28 +00:00
|
|
|
|
|
|
|
VlWorkerThread::~VlWorkerThread() {
|
2022-06-27 13:16:20 +00:00
|
|
|
shutdown();
|
2018-07-23 00:54:28 +00:00
|
|
|
// The thread should exit; join it.
|
|
|
|
m_cthread.join();
|
|
|
|
}
|
|
|
|
|
2022-10-03 14:57:37 +00:00
|
|
|
static void shutdownTask(void*, bool) { // LCOV_EXCL_LINE
|
2022-06-27 13:16:20 +00:00
|
|
|
// Deliberately empty, we use the address of this function as a magic number
|
|
|
|
}
|
|
|
|
|
2022-07-12 10:41:15 +00:00
|
|
|
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
|
|
|
|
|
|
|
|
void VlWorkerThread::wait() {
|
|
|
|
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
|
|
|
|
std::atomic<bool> flag{false};
|
|
|
|
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
|
|
|
|
// Spin wait
|
|
|
|
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
|
|
|
|
if (flag.load()) return;
|
|
|
|
VL_CPU_RELAX();
|
|
|
|
}
|
|
|
|
// Yield wait
|
|
|
|
while (!flag.load()) std::this_thread::yield();
|
|
|
|
}
|
|
|
|
|
2018-07-23 00:54:28 +00:00
|
|
|
void VlWorkerThread::workerLoop() {
|
|
|
|
ExecRec work;
|
|
|
|
|
2022-07-12 10:41:15 +00:00
|
|
|
// Wait for the first task without spinning, in case the thread is never actually used.
|
|
|
|
dequeWork</* SpinWait: */ false>(&work);
|
|
|
|
|
2020-04-04 02:31:54 +00:00
|
|
|
while (true) {
|
2022-06-27 13:16:20 +00:00
|
|
|
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
|
|
|
|
work.m_fnp(work.m_selfp, work.m_evenCycle);
|
2022-07-12 10:41:15 +00:00
|
|
|
// Wait for next task with spinning.
|
|
|
|
dequeWork</* SpinWait: */ true>(&work);
|
2018-07-23 00:54:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 10:41:15 +00:00
|
|
|
void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
|
|
|
|
Verilated::threadContextp(contextp);
|
2021-03-07 16:01:54 +00:00
|
|
|
workerp->workerLoop();
|
|
|
|
}
|
2018-07-23 00:54:28 +00:00
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
// VlThreadPool
|
|
|
|
|
2022-07-12 10:41:15 +00:00
|
|
|
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
|
|
|
|
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
|
2018-07-23 00:54:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VlThreadPool::~VlThreadPool() {
|
2020-11-11 02:40:14 +00:00
|
|
|
// Each ~WorkerThread will wait for its thread to exit.
|
|
|
|
for (auto& i : m_workers) delete i;
|
2018-07-23 00:54:28 +00:00
|
|
|
}
|