158 lines
4.9 KiB
C++
158 lines
4.9 KiB
C++
/*
|
|
* Copyright (c) 2026 Icarus UVM fork contributors
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* Runtime objects for SystemVerilog built-in mailbox and semaphore.
|
|
*/
|
|
#ifndef VVP_MAILBOX_H
|
|
#define VVP_MAILBOX_H
|
|
|
|
#include "vvp_object.h"
|
|
#include "vvp_net.h"
|
|
#include <deque>
|
|
#include <vector>
|
|
|
|
struct vthread_s;
|
|
typedef vthread_s* vthread_t;
|
|
|
|
/*
|
|
* vvp_mailbox -- runtime representation of SystemVerilog's
|
|
* built-in parameterized mailbox.
|
|
*
|
|
* A mailbox is an unbounded (or bounded) typed FIFO. When the FIFO
|
|
* is empty, get() and peek() suspend the calling thread. When the
|
|
* FIFO is at capacity, put() suspends the calling thread.
|
|
*
|
|
* We store contents as vvp_object_t (like queue_object). String and
|
|
* vec4 mailboxes would need separate classes; for UVM the mailbox
|
|
* element type is always an object.
|
|
*/
|
|
class vvp_mailbox : public vvp_object {
|
|
public:
|
|
explicit vvp_mailbox(size_t bound = 0); /* 0 = unbounded */
|
|
~vvp_mailbox() override;
|
|
|
|
vvp_object* duplicate() const override;
|
|
void shallow_copy(const vvp_object*) override {}
|
|
|
|
size_t bound() const { return bound_; }
|
|
size_t num() const { return items_.size(); }
|
|
bool full() const { return bound_ && items_.size() >= bound_; }
|
|
bool empty() const { return items_.empty(); }
|
|
|
|
/* Non-blocking operations. Return true on success. */
|
|
bool try_put(const vvp_object_t& item);
|
|
bool try_get(vvp_object_t& item);
|
|
bool try_peek(vvp_object_t& item);
|
|
|
|
/*
|
|
* Blocking operations: return true if the operation completed
|
|
* immediately, or false if thr was suspended and will be
|
|
* rescheduled when the condition changes.
|
|
*
|
|
* For get/peek: when returning false the thread is added to
|
|
* get_waiters_. On resumption (via resume_get_waiters_) the
|
|
* retrieved item has already been pushed onto the thread's
|
|
* object stack by vthread_push_obj_item().
|
|
*
|
|
* For put: when returning false the item is stored in the
|
|
* put_waiter_t record; it is NOT pushed to the thread stack.
|
|
*/
|
|
bool put(vthread_t thr, const vvp_object_t& item);
|
|
bool get(vthread_t thr, vvp_object_t& item_out);
|
|
bool peek(vthread_t thr, vvp_object_t& item_out);
|
|
|
|
private:
|
|
void resume_get_waiters_();
|
|
void resume_put_waiters_();
|
|
|
|
size_t bound_; /* 0 = unbounded */
|
|
std::deque<vvp_object_t> items_;
|
|
|
|
/* Threads suspended waiting for items (get/peek). */
|
|
struct get_waiter_t {
|
|
vthread_t thr;
|
|
bool is_peek;
|
|
};
|
|
std::vector<get_waiter_t> get_waiters_;
|
|
|
|
/* Threads suspended waiting for space (put).
|
|
* The item to be put is stored here until space is available. */
|
|
struct put_waiter_t {
|
|
vthread_t thr;
|
|
vvp_object_t item;
|
|
};
|
|
std::vector<put_waiter_t> put_waiters_;
|
|
};
|
|
|
|
/*
|
|
* vvp_semaphore -- runtime representation of SystemVerilog's
|
|
* built-in semaphore.
|
|
*
|
|
* A semaphore has an integer key count. get(N) blocks until at least
|
|
* N keys are available, then decrements by N. put(N) adds N keys
|
|
* and wakes any waiters. try_get(N) is non-blocking.
|
|
*/
|
|
class vvp_semaphore : public vvp_object {
|
|
public:
|
|
explicit vvp_semaphore(size_t initial_count = 0);
|
|
~vvp_semaphore() override;
|
|
|
|
vvp_object* duplicate() const override;
|
|
void shallow_copy(const vvp_object*) override {}
|
|
|
|
size_t count() const { return count_; }
|
|
|
|
/* try_get: if count >= n, decrement and return true; else false. */
|
|
bool try_get(size_t n = 1);
|
|
|
|
/* put: add n keys and wake suspended threads. */
|
|
void put(size_t n = 1);
|
|
|
|
/* get: block thr until count >= n, then decrement.
|
|
* Returns true if immediately satisfied, false if thr is suspended. */
|
|
bool get(vthread_t thr, size_t n = 1);
|
|
|
|
private:
|
|
void resume_waiters_();
|
|
|
|
size_t count_;
|
|
|
|
struct waiter_t {
|
|
vthread_t thr;
|
|
size_t n;
|
|
};
|
|
std::vector<waiter_t> waiters_;
|
|
};
|
|
|
|
/*
|
|
* vvp_boxed_vec4 -- wraps a vec4 (integer/bit) value as a vvp_object_t so
|
|
* that it can be stored in a mailbox alongside class objects. Used when
|
|
* mailbox #(T) is instantiated with a non-class element type (e.g. int).
|
|
*/
|
|
class vvp_boxed_vec4 : public vvp_object {
|
|
public:
|
|
explicit vvp_boxed_vec4(const vvp_vector4_t& v) : value_(v) {}
|
|
~vvp_boxed_vec4() override {}
|
|
|
|
vvp_object* duplicate() const override {
|
|
return new vvp_boxed_vec4(value_);
|
|
}
|
|
void shallow_copy(const vvp_object* src) override {
|
|
if (const vvp_boxed_vec4* s = dynamic_cast<const vvp_boxed_vec4*>(src))
|
|
value_ = s->value_;
|
|
}
|
|
|
|
const vvp_vector4_t& get_value() const { return value_; }
|
|
|
|
private:
|
|
vvp_vector4_t value_;
|
|
};
|
|
|
|
#endif /* VVP_MAILBOX_H */
|