SV: add mailbox/semaphore builtins for UVM IPC
Implement native SystemVerilog mailbox and semaphore with blocking and nonblocking put/get, so Accellera-style code can use the standard API without depending on the legacy IVL_UVM poor-man's classes.
This commit is contained in:
parent
8504b349f8
commit
7379ff6530
|
|
@ -8,7 +8,7 @@ Each item should be a dedicated `feat/<name>` branch, with tests/examples and a
|
|||
2. **Associative arrays** — `int aa[string];` — **in progress / partial** (string keys only; see [assoc-array.md](assoc-array.md))
|
||||
3. **Virtual interfaces** + eventing on `vif.clk` — **partial** (see [virtual-interface.md](virtual-interface.md))
|
||||
4. **Clocking blocks** — enough for `@(vif.cb)` — **partial** (interface-local `@(bif.cb)`; see [clocking.md](clocking.md))
|
||||
5. **`mailbox` / `semaphore` builtins** (or solid class equivalents with blocking put/get)
|
||||
5. **`mailbox` / `semaphore` builtins** (or solid class equivalents with blocking put/get) — **partial** (int mailbox + semaphore; see [mailbox-semaphore.md](mailbox-semaphore.md))
|
||||
6. **Constraints + `randomize()` / `randomize() with`** — start unconstrained `rand`, then solver
|
||||
7. **`$cast` / `$typename` hardening** for factory patterns
|
||||
8. **Covergroups** — functional coverage
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ Last updated: 2026-07-21
|
|||
| Area | Status |
|
||||
|------|--------|
|
||||
| Icarus Verilog tree | Fork of `steveicarus/iverilog` (`master` @ merge of SV array ordering work) |
|
||||
| [`uvm/`](../uvm/) | Seeded from IVL_UVM (VerifWorks) — messaging, CLP, stub phases, poor-man’s mailbox/semaphore; **not** Accellera-compatible |
|
||||
| [`uvm/`](../uvm/) | Seeded from IVL_UVM (VerifWorks) — messaging, CLP, stub phases, legacy poor-man’s mailbox/semaphore classes; **not** Accellera-compatible. Prefer compiler builtins for `mailbox`/`semaphore` (see STATUS row below). |
|
||||
| [`examples/hello_uvm`](../examples/hello_uvm) | Smoke TB for the seeded library (`Makefile` included) |
|
||||
| Parameterized classes | **Partial** on `feat/param-classes` (merged): ANSI `class C #(type T = int, parameter int W = 8);` parses into the class scope and elaborates with **defaults**. Explicit specializations `C#(byte)` / overrides not done yet. See [`examples/param_classes`](../examples/param_classes). |
|
||||
| Associative arrays | **Partial** on `feat/assoc-array`: string-keyed `int aa[string]` / `int aa[*]` with size/num/exists/delete/foreach/copy. See [`docs/assoc-array.md`](assoc-array.md) and [`examples/assoc_array`](../examples/assoc_array). |
|
||||
| Virtual interfaces | **Partial** on `feat/virtual-interface`: `virtual interface T` as class property / TF arg; assign interface instance; member R/W; `@(posedge vif.clk)`. See [`docs/virtual-interface.md`](virtual-interface.md) and [`examples/virtual_interface`](../examples/virtual_interface). |
|
||||
| Clocking blocks | **Partial** on `feat/clocking-blocks`: interface-local `clocking`; `@(bif.cb)`; `cb.sig` R/W with `#0` skew. See [`docs/clocking.md`](clocking.md) and [`examples/clocking`](../examples/clocking). |
|
||||
| Mailbox / semaphore | **Partial** on `feat/mailbox-semaphore`: compiler builtins; `mailbox #(int)` + blocking/nonblocking put/get/num; `semaphore` get/put/try_get. See [`docs/mailbox-semaphore.md`](mailbox-semaphore.md) and [`examples/mailbox_sem`](../examples/mailbox_sem). |
|
||||
| Constraints / randomize | Missing |
|
||||
| Covergroups / DPI | Missing |
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# Mailbox / semaphore (Tier A #5)
|
||||
|
||||
Status: **partial** — compiler builtins for Accellera-style IPC (int messages).
|
||||
|
||||
## Choice: compiler builtins (not library classes)
|
||||
|
||||
IEEE `mailbox` / `semaphore` are built-in classes. A pure SV library cannot implement the required API here because:
|
||||
|
||||
- Functions cannot take `output`/`ref` ports (`try_get(v)` needs a side-effecting write).
|
||||
- Class `event` properties are not supported yet (needed for efficient wait/wakeup).
|
||||
|
||||
This fork therefore implements **native builtins**, similar in shape to `string` / queue method sfuncs: empty `netclass_t` types named `mailbox` / `semaphore`, methods lowered to `$ivl_mailbox$*` / `$ivl_semaphore$*`, and vvp opcodes (`%mbx/*`, `%sem/*`) with real thread suspend/resume.
|
||||
|
||||
The older IVL_UVM “poor-man’s” classes (`ivl_uvm_mbx`, `ivl_uvm_semaphore`) remain in `uvm/` for that library’s own tests; new Accellera-shaped code should use the builtins.
|
||||
|
||||
## Supported in this slice
|
||||
|
||||
```systemverilog
|
||||
mailbox #(int) mb = new(); // #(T) accepted; element type is int/vec for v1
|
||||
mb.put(7);
|
||||
mb.get(v); // blocking
|
||||
ok = mb.try_put(8);
|
||||
ok = mb.try_get(v); // nonblocking; writes v on success
|
||||
n = mb.num();
|
||||
|
||||
semaphore sem = new(1);
|
||||
sem.get(1); // blocking
|
||||
sem.put(1);
|
||||
ok = sem.try_get(1);
|
||||
```
|
||||
|
||||
Also: unbounded `new()` / `new(0)`, bounded mailbox `new(N)`, blocking put when full, `peek` / `try_peek`.
|
||||
|
||||
## Encoding
|
||||
|
||||
| Layer | Role |
|
||||
|-------|------|
|
||||
| Parse / pform | Builtin typedefs `mailbox` / `semaphore`; `mailbox #(T) x` as var decl (T ignored for now) |
|
||||
| Netlist | Empty `netclass_t` “mailbox” / “semaphore” |
|
||||
| Elab | `$ivl_mailbox$new/put/get/...`, `$ivl_semaphore$new/get/put/try_get` |
|
||||
| Codegen | `%mbx/*`, `%sem/*`, `%box/vec4` / `%unbox/vec4` for int payloads |
|
||||
| Runtime | `vvp_mailbox` / `vvp_semaphore` (`vvp/vvp_mailbox.{h,cc}`) |
|
||||
|
||||
## Deferred (do not claim)
|
||||
|
||||
- True parameterized element types (`mailbox #(string)`, class handles) beyond boxing ints
|
||||
- Explicit specialization of user param-classes interacting with mailbox
|
||||
- `peek` stress / priority / typed message checking
|
||||
- Replacing IVL_UVM’s `ivl_uvm_mbx` usages with the builtin
|
||||
|
||||
## Example
|
||||
|
||||
[`examples/mailbox_sem/mbx_sem_basic.sv`](../examples/mailbox_sem/mbx_sem_basic.sv) — prints `PASSED`.
|
||||
|
||||
```bash
|
||||
./install/bin/iverilog -g2012 -o /tmp/mbx_sem.vvp examples/mailbox_sem/mbx_sem_basic.sv
|
||||
./install/bin/vvp /tmp/mbx_sem.vvp
|
||||
```
|
||||
|
||||
See also [STATUS.md](STATUS.md) and [ROADMAP.md](ROADMAP.md).
|
||||
99
elab_expr.cc
99
elab_expr.cc
|
|
@ -2017,6 +2017,25 @@ unsigned PECallFunction::test_width_method_(Design*, NetScope*,
|
|||
if (search_results.net && search_results.net->data_type()==IVL_VT_CLASS) {
|
||||
const netclass_t *class_type = dynamic_cast<const netclass_t*>(search_results.type);
|
||||
ivl_assert(*this, class_type);
|
||||
perm_string cname = class_type->get_name();
|
||||
if (cname == perm_string::literal("mailbox")) {
|
||||
if (method_name == "num" || method_name == "try_get"
|
||||
|| method_name == "try_peek" || method_name == "try_put") {
|
||||
expr_type_ = IVL_VT_BOOL;
|
||||
expr_width_ = (method_name == "num") ? 32 : 1;
|
||||
min_width_ = expr_width_;
|
||||
signed_flag_ = false;
|
||||
return expr_width_;
|
||||
}
|
||||
}
|
||||
if (cname == perm_string::literal("semaphore")
|
||||
&& method_name == "try_get") {
|
||||
expr_type_ = IVL_VT_BOOL;
|
||||
expr_width_ = 1;
|
||||
min_width_ = 1;
|
||||
signed_flag_ = false;
|
||||
return expr_width_;
|
||||
}
|
||||
NetScope*method = class_type->method_from_name(method_name);
|
||||
|
||||
if (method == 0) {
|
||||
|
|
@ -4126,6 +4145,59 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
|
|||
NetNet*net = search_results.net;
|
||||
const netclass_t*class_type = dynamic_cast<const netclass_t*>(search_results.type);
|
||||
ivl_assert(*this, class_type);
|
||||
|
||||
perm_string cname = class_type->get_name();
|
||||
if (cname == perm_string::literal("mailbox")) {
|
||||
if (method_name == perm_string::literal("num")) {
|
||||
NetESFunc*sys = new NetESFunc("$ivl_mailbox$num",
|
||||
&netvector_t::atom2u32, 1);
|
||||
sys->set_line(*this);
|
||||
sys->parm(0, sub_expr);
|
||||
return sys;
|
||||
}
|
||||
if (method_name == perm_string::literal("try_get")
|
||||
|| method_name == perm_string::literal("try_peek")
|
||||
|| method_name == perm_string::literal("try_put")) {
|
||||
const char*sname =
|
||||
method_name==perm_string::literal("try_get")
|
||||
? "$ivl_mailbox$try_get"
|
||||
: (method_name==perm_string::literal("try_peek")
|
||||
? "$ivl_mailbox$try_peek"
|
||||
: "$ivl_mailbox$try_put");
|
||||
unsigned nargs = parms_.empty() ? 0 : 1;
|
||||
NetESFunc*sys = new NetESFunc(sname,
|
||||
&netvector_t::atom2u32,
|
||||
1 + nargs);
|
||||
sys->set_line(*this);
|
||||
sys->parm(0, sub_expr);
|
||||
if (nargs > 0 && parms_[0].parm) {
|
||||
NetExpr*a = elab_and_eval(des, scope,
|
||||
parms_[0].parm,
|
||||
-1, false, false);
|
||||
if (a) sys->parm(1, a);
|
||||
}
|
||||
return sys;
|
||||
}
|
||||
}
|
||||
if (cname == perm_string::literal("semaphore")
|
||||
&& method_name == perm_string::literal("try_get")) {
|
||||
unsigned nargs = parms_.empty() ? 0 : 1;
|
||||
NetESFunc*sys = new NetESFunc("$ivl_semaphore$try_get",
|
||||
&netvector_t::atom2u32,
|
||||
1 + nargs);
|
||||
sys->set_line(*this);
|
||||
sys->parm(0, sub_expr);
|
||||
if (nargs > 0 && parms_[0].parm) {
|
||||
NetExpr*narg = elab_and_eval(des, scope,
|
||||
parms_[0].parm,
|
||||
32, false, false,
|
||||
IVL_VT_LOGIC);
|
||||
sys->parm(1, narg ? narg
|
||||
: new NetEConst(verinum((uint64_t)1, 32)));
|
||||
}
|
||||
return sys;
|
||||
}
|
||||
|
||||
return elaborate_class_method_net_(des, scope, net, class_type,
|
||||
method_name, nullptr);
|
||||
}
|
||||
|
|
@ -7426,6 +7498,33 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
|
|||
|
||||
NetScope *new_scope = ctype->get_constructor();
|
||||
if (new_scope == 0) {
|
||||
// Built-in mailbox/semaphore constructors map to runtime sfuncs.
|
||||
if (gn_system_verilog()
|
||||
&& ctype->get_name() == perm_string::literal("mailbox")) {
|
||||
unsigned nargs = parms_.empty() ? 0 : 1;
|
||||
NetESFunc*mbx = new NetESFunc("$ivl_mailbox$new", ctype, nargs);
|
||||
mbx->set_line(*this);
|
||||
if (!parms_.empty() && parms_[0].parm) {
|
||||
NetExpr*barg = elab_and_eval(des, scope, parms_[0].parm, 32,
|
||||
false, false, IVL_VT_LOGIC);
|
||||
mbx->parm(0, barg ? barg : new NetEConst(verinum((uint64_t)0, 32)));
|
||||
}
|
||||
delete obj;
|
||||
return mbx;
|
||||
}
|
||||
if (gn_system_verilog()
|
||||
&& ctype->get_name() == perm_string::literal("semaphore")) {
|
||||
unsigned nargs = parms_.empty() ? 0 : 1;
|
||||
NetESFunc*sem = new NetESFunc("$ivl_semaphore$new", ctype, nargs);
|
||||
sem->set_line(*this);
|
||||
if (!parms_.empty() && parms_[0].parm) {
|
||||
NetExpr*carg = elab_and_eval(des, scope, parms_[0].parm, 32,
|
||||
false, false, IVL_VT_LOGIC);
|
||||
sem->parm(0, carg ? carg : new NetEConst(verinum((uint64_t)0, 32)));
|
||||
}
|
||||
delete obj;
|
||||
return sem;
|
||||
}
|
||||
// No constructor.
|
||||
if (parms_.size() > 0) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
|
|
|
|||
22
elab_type.cc
22
elab_type.cc
|
|
@ -40,6 +40,23 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static netclass_t* make_builtin_semaphore_type_()
|
||||
{
|
||||
static netclass_t*builtin_semaphore_type = 0;
|
||||
if (!builtin_semaphore_type)
|
||||
builtin_semaphore_type = new netclass_t(perm_string::literal("semaphore"), 0);
|
||||
return builtin_semaphore_type;
|
||||
}
|
||||
|
||||
static netclass_t* make_builtin_mailbox_type_()
|
||||
{
|
||||
static netclass_t*builtin_mailbox_type = 0;
|
||||
if (!builtin_mailbox_type)
|
||||
builtin_mailbox_type = new netclass_t(perm_string::literal("mailbox"), 0);
|
||||
return builtin_mailbox_type;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Elaborations of types may vary depending on the scope that it is
|
||||
* done in, so keep a per-scope cache of the results.
|
||||
|
|
@ -506,6 +523,11 @@ NetScope *typeref_t::find_scope(Design *des, NetScope *s) const
|
|||
|
||||
ivl_type_t typedef_t::elaborate_type(Design *des, NetScope *scope)
|
||||
{
|
||||
if (name == perm_string::literal("mailbox"))
|
||||
return make_builtin_mailbox_type_();
|
||||
if (name == perm_string::literal("semaphore"))
|
||||
return make_builtin_semaphore_type_();
|
||||
|
||||
if (!data_type.get()) {
|
||||
cerr << get_fileline() << ": error: Undefined type `" << name << "`."
|
||||
<< endl;
|
||||
|
|
|
|||
60
elaborate.cc
60
elaborate.cc
|
|
@ -4582,6 +4582,66 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
|
|||
}
|
||||
|
||||
if (const netclass_t*class_type = dynamic_cast<const netclass_t*>(sr.type)) {
|
||||
perm_string cname = class_type->get_name();
|
||||
if (cname == perm_string::literal("mailbox")) {
|
||||
if (method_name == perm_string::literal("put")) {
|
||||
static const std::vector<perm_string> parm_names = {
|
||||
perm_string::literal("message")
|
||||
};
|
||||
return elaborate_sys_task_method_(des, scope, net, method_name,
|
||||
"$ivl_mailbox$put", parm_names);
|
||||
}
|
||||
if (method_name == perm_string::literal("get")
|
||||
|| method_name == perm_string::literal("peek")) {
|
||||
static const std::vector<perm_string> parm_names = {
|
||||
perm_string::literal("message")
|
||||
};
|
||||
const char*sname = method_name == perm_string::literal("get")
|
||||
? "$ivl_mailbox$get" : "$ivl_mailbox$peek";
|
||||
return elaborate_sys_task_method_(des, scope, net, method_name,
|
||||
sname, parm_names);
|
||||
}
|
||||
if (method_name == perm_string::literal("try_put")
|
||||
|| method_name == perm_string::literal("try_get")
|
||||
|| method_name == perm_string::literal("try_peek")) {
|
||||
static const std::vector<perm_string> parm_names = {
|
||||
perm_string::literal("message")
|
||||
};
|
||||
const char*sname =
|
||||
method_name == perm_string::literal("try_put")
|
||||
? "$ivl_mailbox$try_put"
|
||||
: (method_name == perm_string::literal("try_get")
|
||||
? "$ivl_mailbox$try_get"
|
||||
: "$ivl_mailbox$try_peek");
|
||||
return elaborate_sys_task_method_(des, scope, net, method_name,
|
||||
sname, parm_names);
|
||||
}
|
||||
if (method_name == perm_string::literal("num")) {
|
||||
static const std::vector<perm_string> no_parm_names;
|
||||
return elaborate_sys_task_method_(des, scope, net, method_name,
|
||||
"$ivl_mailbox$num", no_parm_names);
|
||||
}
|
||||
}
|
||||
if (cname == perm_string::literal("semaphore")) {
|
||||
static const std::vector<perm_string> sem_no_parms;
|
||||
static const std::vector<perm_string> sem_one_parm = {
|
||||
perm_string::literal("keycount")
|
||||
};
|
||||
if (method_name == perm_string::literal("get")
|
||||
|| method_name == perm_string::literal("put")
|
||||
|| method_name == perm_string::literal("try_get")) {
|
||||
const char*sname =
|
||||
method_name == perm_string::literal("get")
|
||||
? "$ivl_semaphore$get"
|
||||
: (method_name == perm_string::literal("put")
|
||||
? "$ivl_semaphore$put"
|
||||
: "$ivl_semaphore$try_get");
|
||||
return elaborate_sys_task_method_(des, scope, net, method_name,
|
||||
sname,
|
||||
parms_.empty() ? sem_no_parms
|
||||
: sem_one_parm);
|
||||
}
|
||||
}
|
||||
NetScope*task = class_type->method_from_name(method_name);
|
||||
if (task == 0) {
|
||||
// If an implicit this was added it is not an error if we
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
// Tier A #5 smoke: built-in mailbox #(int) + semaphore API.
|
||||
module mbx_sem_basic;
|
||||
mailbox #(int) mb = new();
|
||||
semaphore sem = new(1);
|
||||
int v, n, ok;
|
||||
int pass;
|
||||
|
||||
initial begin
|
||||
pass = 1;
|
||||
|
||||
mb.put(7);
|
||||
mb.get(v);
|
||||
if (v !== 7) begin
|
||||
$display("FAIL: mb.get got %0d want 7", v);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
ok = mb.try_put(8);
|
||||
if (ok !== 1) begin
|
||||
$display("FAIL: try_put expected 1 got %0d", ok);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
ok = mb.try_get(v);
|
||||
if (ok !== 1 || v !== 8) begin
|
||||
$display("FAIL: try_get ok=%0d v=%0d", ok, v);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
n = mb.num();
|
||||
if (n !== 0) begin
|
||||
$display("FAIL: num expected 0 got %0d", n);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
ok = mb.try_get(v);
|
||||
if (ok !== 0) begin
|
||||
$display("FAIL: empty try_get expected 0 got %0d", ok);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
sem.get(1);
|
||||
sem.put(1);
|
||||
ok = sem.try_get(1);
|
||||
if (ok !== 1) begin
|
||||
$display("FAIL: sem.try_get expected 1 got %0d", ok);
|
||||
pass = 0;
|
||||
end
|
||||
ok = sem.try_get(1);
|
||||
if (ok !== 0) begin
|
||||
$display("FAIL: sem.try_get empty expected 0 got %0d", ok);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
fork
|
||||
begin
|
||||
#5;
|
||||
mb.put(42);
|
||||
end
|
||||
begin
|
||||
mb.get(v);
|
||||
if (v !== 42) begin
|
||||
$display("FAIL: blocking get got %0d", v);
|
||||
pass = 0;
|
||||
end
|
||||
end
|
||||
join
|
||||
|
||||
fork
|
||||
begin
|
||||
#5;
|
||||
sem.put(1);
|
||||
end
|
||||
begin
|
||||
sem.get(1);
|
||||
end
|
||||
join
|
||||
|
||||
if (pass) $display("PASSED");
|
||||
else $display("FAILED");
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -165,7 +165,9 @@ bool netclass_t::test_for_missing_initializers() const
|
|||
|
||||
NetScope*netclass_t::method_from_name(perm_string name) const
|
||||
{
|
||||
NetScope*task = class_scope_->child( hname_t(name) );
|
||||
NetScope*task = 0;
|
||||
if (class_scope_)
|
||||
task = class_scope_->child( hname_t(name) );
|
||||
if ((task == 0) && super_)
|
||||
task = super_->method_from_name(name);
|
||||
return task;
|
||||
|
|
@ -174,6 +176,9 @@ NetScope*netclass_t::method_from_name(perm_string name) const
|
|||
|
||||
NetScope* netclass_t::get_constructor() const
|
||||
{
|
||||
if (class_scope_ == 0)
|
||||
return nullptr;
|
||||
|
||||
auto task = class_scope_->child(hname_t(perm_string::literal("new")));
|
||||
if (task)
|
||||
return task;
|
||||
|
|
|
|||
66
parse.y
66
parse.y
|
|
@ -1102,7 +1102,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
|
|||
%type <porttype> port_direction port_direction_opt clocking_direction
|
||||
%type <expr> clocking_skew_opt
|
||||
%type <vartype> integer_vector_type integer_vector_type_no_reg
|
||||
%type <parmvalue> parameter_value_opt
|
||||
%type <parmvalue> parameter_value_opt type_parameter_value
|
||||
|
||||
%type <event_exprs> event_expression_list
|
||||
%type <event_expr> event_expression
|
||||
|
|
@ -1609,6 +1609,29 @@ data_declaration /* IEEE1800-2005: A.2.1.3 */
|
|||
$1, $2);
|
||||
var_lifetime = LexicalScope::INHERITED;
|
||||
}
|
||||
/* Built-in / parameterized class types: `mailbox #(int) mb = new();` */
|
||||
| attribute_list_opt K_const_opt variable_lifetime_opt TYPE_IDENTIFIER type_parameter_value list_of_variable_decl_assignments ';'
|
||||
{ typeref_t*tmp = new typeref_t($4.type);
|
||||
FILE_NAME(tmp, @4);
|
||||
pform_make_var(@4, $6, tmp, $1, $2);
|
||||
if ($5) {
|
||||
if ($5->by_order) {
|
||||
for (std::list<PExpr*>::iterator i = $5->by_order->begin()
|
||||
; i != $5->by_order->end() ; ++i)
|
||||
delete *i;
|
||||
delete $5->by_order;
|
||||
}
|
||||
if ($5->by_name) {
|
||||
for (std::list<named_pexpr_t>::iterator i = $5->by_name->begin()
|
||||
; i != $5->by_name->end() ; ++i)
|
||||
delete i->parm;
|
||||
delete $5->by_name;
|
||||
}
|
||||
delete $5;
|
||||
}
|
||||
delete[]$4.text;
|
||||
var_lifetime = LexicalScope::INHERITED;
|
||||
}
|
||||
| attribute_list_opt K_const_opt K_var variable_lifetime_opt list_of_variable_decl_assignments_with_type ';'
|
||||
{ data_type_t*data_type = $5.type;
|
||||
if (!data_type) {
|
||||
|
|
@ -2361,6 +2384,30 @@ type_identifier_variable_decl_assignments_with_type
|
|||
$$.decl_assignments = $3;
|
||||
$$.type = pform_make_parray_type(@2, tmp, $2);
|
||||
}
|
||||
/* Built-in parameterized classes: `mailbox #(int) mb = new();` */
|
||||
| TYPE_IDENTIFIER type_parameter_value list_of_variable_decl_assignments
|
||||
{ pform_set_type_referenced(@1, $1.text);
|
||||
auto tmp = new typeref_t($1.type);
|
||||
FILE_NAME(tmp, @1);
|
||||
delete[]$1.text;
|
||||
if ($2) {
|
||||
if ($2->by_order) {
|
||||
for (std::list<PExpr*>::iterator i = $2->by_order->begin()
|
||||
; i != $2->by_order->end() ; ++i)
|
||||
delete *i;
|
||||
delete $2->by_order;
|
||||
}
|
||||
if ($2->by_name) {
|
||||
for (std::list<named_pexpr_t>::iterator i = $2->by_name->begin()
|
||||
; i != $2->by_name->end() ; ++i)
|
||||
delete i->parm;
|
||||
delete $2->by_name;
|
||||
}
|
||||
delete $2;
|
||||
}
|
||||
$$.decl_assignments = $3;
|
||||
$$.type = tmp;
|
||||
}
|
||||
| package_scope TYPE_IDENTIFIER dimensions_opt list_of_variable_decl_assignments
|
||||
{ lex_in_package_scope(nullptr);
|
||||
auto tmp = new typeref_t($2.type, $1);
|
||||
|
|
@ -5866,6 +5913,7 @@ module_item
|
|||
delete[]$2;
|
||||
}
|
||||
|
||||
|
||||
| attribute_list_opt
|
||||
IDENTIFIER parameter_value_opt error ';'
|
||||
{ yyerror(@2, "error: Invalid module instantiation");
|
||||
|
|
@ -6400,6 +6448,22 @@ from_exclude : K_from { $$ = false; } | K_exclude { $$ = true; } ;
|
|||
The parameter value by name syntax is OVI enhancement BTF-B06 as
|
||||
approved by WG1364 on 6/28/1998. */
|
||||
|
||||
/* Required `#(...)` type/parameter override list (not optional). */
|
||||
type_parameter_value
|
||||
: '#' '(' expression_list_with_nuls ')'
|
||||
{ struct parmvalue_t*tmp = new struct parmvalue_t;
|
||||
tmp->by_order = $3;
|
||||
tmp->by_name = 0;
|
||||
$$ = tmp;
|
||||
}
|
||||
| '#' '(' parameter_value_byname_list ')'
|
||||
{ struct parmvalue_t*tmp = new struct parmvalue_t;
|
||||
tmp->by_order = 0;
|
||||
tmp->by_name = $3;
|
||||
$$ = tmp;
|
||||
}
|
||||
;
|
||||
|
||||
parameter_value_opt
|
||||
: '#' '(' expression_list_with_nuls ')'
|
||||
{ struct parmvalue_t*tmp = new struct parmvalue_t;
|
||||
|
|
|
|||
75
pform.cc
75
pform.cc
|
|
@ -922,6 +922,28 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt)
|
|||
{
|
||||
perm_string name = lex_strings.make(txt);
|
||||
|
||||
// Built-in SystemVerilog classes (IEEE 1800 mailbox/semaphore).
|
||||
if (gn_system_verilog()) {
|
||||
if (name == lex_strings.make("semaphore")) {
|
||||
static typedef_t*semaphore_type = 0;
|
||||
if (!semaphore_type) {
|
||||
semaphore_type = new typedef_t(lex_strings.make("semaphore"));
|
||||
semaphore_type->set_data_type(new type_parameter_t(semaphore_type->name));
|
||||
semaphore_type->set_basic_type(type_restrict_t(type_restrict_t::CLASS));
|
||||
}
|
||||
return semaphore_type;
|
||||
}
|
||||
if (name == lex_strings.make("mailbox")) {
|
||||
static typedef_t*mailbox_type = 0;
|
||||
if (!mailbox_type) {
|
||||
mailbox_type = new typedef_t(lex_strings.make("mailbox"));
|
||||
mailbox_type->set_data_type(new type_parameter_t(mailbox_type->name));
|
||||
mailbox_type->set_basic_type(type_restrict_t(type_restrict_t::CLASS));
|
||||
}
|
||||
return mailbox_type;
|
||||
}
|
||||
}
|
||||
|
||||
LexicalScope*cur_scope = lexical_scope;
|
||||
do {
|
||||
LexicalScope::typedef_map_t::iterator cur;
|
||||
|
|
@ -2558,6 +2580,59 @@ void pform_make_modgates(const struct vlltype&loc,
|
|||
std::vector<lgate>*gates,
|
||||
std::list<named_pexpr_t>*attr)
|
||||
{
|
||||
// Reinterpret `mailbox #(int) mb;` style no-port instantiations of a
|
||||
// known type identifier as variable declarations (built-in classes).
|
||||
if (gn_system_verilog()) {
|
||||
typedef_t*decl_type = pform_test_type_identifier(loc, type);
|
||||
if (decl_type) {
|
||||
bool declaration_like = true;
|
||||
std::list<decl_assignment_t*>*decls = new std::list<decl_assignment_t*>;
|
||||
for (unsigned idx = 0 ; idx < gates->size() ; idx += 1) {
|
||||
lgate&cur = (*gates)[idx];
|
||||
if (cur.parms || cur.parms_by_name) {
|
||||
declaration_like = false;
|
||||
break;
|
||||
}
|
||||
decl_assignment_t*decl = new decl_assignment_t;
|
||||
decl->name = { lex_strings.make(cur.name), loc.lexical_pos };
|
||||
if (cur.ranges) {
|
||||
decl->index.swap(*cur.ranges);
|
||||
delete cur.ranges;
|
||||
cur.ranges = 0;
|
||||
}
|
||||
decls->push_back(decl);
|
||||
}
|
||||
if (declaration_like) {
|
||||
pform_set_type_referenced(loc, type);
|
||||
typeref_t*dtype = new typeref_t(decl_type);
|
||||
FILE_NAME(dtype, loc);
|
||||
pform_make_var(loc, decls, dtype, attr, false);
|
||||
if (overrides) {
|
||||
// Built-in mailbox/semaphore ignore specialization args for now.
|
||||
if (overrides->by_order) {
|
||||
for (list<PExpr*>::iterator i = overrides->by_order->begin()
|
||||
; i != overrides->by_order->end() ; ++i)
|
||||
delete *i;
|
||||
delete overrides->by_order;
|
||||
}
|
||||
if (overrides->by_name) {
|
||||
for (list<named_pexpr_t>::iterator i = overrides->by_name->begin()
|
||||
; i != overrides->by_name->end() ; ++i)
|
||||
delete i->parm;
|
||||
delete overrides->by_name;
|
||||
}
|
||||
delete overrides;
|
||||
}
|
||||
delete gates;
|
||||
return;
|
||||
}
|
||||
for (list<decl_assignment_t*>::iterator cur = decls->begin()
|
||||
; cur != decls->end() ; ++cur)
|
||||
delete *cur;
|
||||
delete decls;
|
||||
}
|
||||
}
|
||||
|
||||
// The grammer should not allow module gates to happen outside
|
||||
// an active module. But if really bad input errors combine in
|
||||
// an ugly way with error recovery, then catch this
|
||||
|
|
|
|||
|
|
@ -794,6 +794,30 @@ int draw_eval_object(ivl_expr_t ex)
|
|||
strstr(ivl_expr_name(ex), "_with") != 0) {
|
||||
if (draw_queue_method_find_sfunc(ex) == 0) return 0;
|
||||
}
|
||||
/* Mailbox constructor: $ivl_mailbox$new([bound]) */
|
||||
if (strcmp(ivl_expr_name(ex), "$ivl_mailbox$new") == 0) {
|
||||
long bound = 0;
|
||||
unsigned parm_count = ivl_expr_parms(ex);
|
||||
if (parm_count > 0) {
|
||||
ivl_expr_t bexpr = ivl_expr_parm(ex, 0);
|
||||
if (bexpr && ivl_expr_type(bexpr) == IVL_EX_NUMBER)
|
||||
bound = (long)ivl_expr_uvalue(bexpr);
|
||||
}
|
||||
fprintf(vvp_out, " %%mbx/new %ld;\n", bound);
|
||||
return 0;
|
||||
}
|
||||
/* Semaphore constructor: $ivl_semaphore$new([initial_count]) */
|
||||
if (strcmp(ivl_expr_name(ex), "$ivl_semaphore$new") == 0) {
|
||||
long cnt = 0;
|
||||
unsigned parm_count = ivl_expr_parms(ex);
|
||||
if (parm_count > 0) {
|
||||
ivl_expr_t cexpr = ivl_expr_parm(ex, 0);
|
||||
if (cexpr && ivl_expr_type(cexpr) == IVL_EX_NUMBER)
|
||||
cnt = (long)ivl_expr_uvalue(cexpr);
|
||||
}
|
||||
fprintf(vvp_out, " %%sem/new %ld;\n", cnt);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(ivl_expr_name(ex), "$ivl_vif_new") == 0) {
|
||||
unsigned n = ivl_expr_parms(ex);
|
||||
fprintf(vvp_out, " %%new/vif %u", n);
|
||||
|
|
|
|||
|
|
@ -1194,6 +1194,82 @@ static void draw_sfunc_vec4(ivl_expr_t expr)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Mailbox num: $ivl_mailbox$num(mbx) -> int on vec4 */
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_mailbox$num") == 0) {
|
||||
ivl_expr_t mbx_e = (parm_count > 0) ? ivl_expr_parm(expr, 0) : 0;
|
||||
if (mbx_e) draw_eval_object(mbx_e);
|
||||
fprintf(vvp_out, " %%mbx/num;\n");
|
||||
if (ivl_expr_width(expr) > 1)
|
||||
fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mailbox try_put: $ivl_mailbox$try_put(mbx, item) -> bit on vec4 */
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_mailbox$try_put") == 0) {
|
||||
ivl_expr_t mbx_e = (parm_count > 0) ? ivl_expr_parm(expr, 0) : 0;
|
||||
ivl_expr_t item_e = (parm_count > 1) ? ivl_expr_parm(expr, 1) : 0;
|
||||
if (mbx_e) draw_eval_object(mbx_e);
|
||||
if (item_e) {
|
||||
if (ivl_expr_value(item_e) == IVL_VT_CLASS) {
|
||||
draw_eval_object(item_e);
|
||||
} else {
|
||||
unsigned wid = ivl_expr_width(item_e);
|
||||
if (!wid) wid = 32;
|
||||
draw_eval_vec4(item_e);
|
||||
fprintf(vvp_out, " %%box/vec4 %u;\n", wid);
|
||||
}
|
||||
} else {
|
||||
fprintf(vvp_out, " %%null;\n");
|
||||
}
|
||||
fprintf(vvp_out, " %%mbx/try_put;\n");
|
||||
if (ivl_expr_width(expr) > 1)
|
||||
fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mailbox try_get/try_peek */
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_mailbox$try_get") == 0 ||
|
||||
strcmp(ivl_expr_name(expr), "$ivl_mailbox$try_peek") == 0) {
|
||||
int is_peek = (strcmp(ivl_expr_name(expr), "$ivl_mailbox$try_peek") == 0);
|
||||
ivl_expr_t mbx_e = (parm_count > 0) ? ivl_expr_parm(expr, 0) : 0;
|
||||
ivl_expr_t item_e = (parm_count > 1) ? ivl_expr_parm(expr, 1) : 0;
|
||||
if (mbx_e) draw_eval_object(mbx_e);
|
||||
fprintf(vvp_out, " %s;\n", is_peek ? "%mbx/try_peek" : "%mbx/try_get");
|
||||
if (item_e && ivl_expr_type(item_e) == IVL_EX_SIGNAL
|
||||
&& ivl_expr_signal(item_e)) {
|
||||
ivl_signal_t sig = ivl_expr_signal(item_e);
|
||||
if (ivl_signal_data_type(sig) == IVL_VT_CLASS) {
|
||||
fprintf(vvp_out, " %%store/obj v%p_0;\n", sig);
|
||||
} else {
|
||||
unsigned wid = ivl_signal_width(sig);
|
||||
if (!wid) wid = 32;
|
||||
fprintf(vvp_out, " %%unbox/vec4 %u;\n", wid);
|
||||
fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", sig, wid);
|
||||
}
|
||||
} else {
|
||||
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
|
||||
}
|
||||
if (ivl_expr_width(expr) > 1)
|
||||
fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Semaphore try_get */
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_semaphore$try_get") == 0) {
|
||||
ivl_expr_t sem_e = (parm_count > 0) ? ivl_expr_parm(expr, 0) : 0;
|
||||
if (sem_e) draw_eval_object(sem_e);
|
||||
if (parm_count >= 2 && ivl_expr_parm(expr, 1)) {
|
||||
draw_eval_vec4(ivl_expr_parm(expr, 1));
|
||||
} else {
|
||||
fprintf(vvp_out, " %%pushi/vec4 1, 0, 32;\n");
|
||||
}
|
||||
fprintf(vvp_out, " %%sem/try_get;\n");
|
||||
if (ivl_expr_width(expr) > 1)
|
||||
fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
draw_vpi_func_call(expr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1945,6 +1945,146 @@ static int show_system_task_call(ivl_statement_t net)
|
|||
{
|
||||
const char*stmt_name = ivl_stmt_name(net);
|
||||
|
||||
|
||||
#define EMIT_MBX_PUSH_ITEM(item_expr) \
|
||||
do { \
|
||||
ivl_expr_t _item = (item_expr); \
|
||||
if (_item) { \
|
||||
if (ivl_expr_value(_item) == IVL_VT_CLASS) { \
|
||||
draw_eval_object(_item); \
|
||||
} else { \
|
||||
unsigned _wid = ivl_expr_width(_item); \
|
||||
if (!_wid) _wid = 32; \
|
||||
draw_eval_vec4(_item); \
|
||||
fprintf(vvp_out, " %%box/vec4 %u;\n", _wid); \
|
||||
} \
|
||||
} else { \
|
||||
fprintf(vvp_out, " %%null;\n"); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define EMIT_MBX_STORE_ITEM(net_arg_idx) \
|
||||
do { \
|
||||
if (ivl_stmt_parm_count(net) > (net_arg_idx)) { \
|
||||
ivl_expr_t _item = ivl_stmt_parm(net, net_arg_idx); \
|
||||
if (_item && ivl_expr_type(_item) == IVL_EX_SIGNAL) { \
|
||||
ivl_signal_t _sig = ivl_expr_signal(_item); \
|
||||
if (_sig && ivl_signal_data_type(_sig) == IVL_VT_CLASS) { \
|
||||
fprintf(vvp_out, " %%store/obj v%p_0;\n", _sig); \
|
||||
} else if (_sig) { \
|
||||
unsigned _wid = ivl_signal_width(_sig); \
|
||||
if (!_wid) _wid = 32; \
|
||||
fprintf(vvp_out, " %%unbox/vec4 %u;\n", _wid); \
|
||||
fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", _sig, _wid); \
|
||||
} else { \
|
||||
fprintf(vvp_out, " %%pop/obj 1, 0;\n"); \
|
||||
} \
|
||||
} else { \
|
||||
fprintf(vvp_out, " %%pop/obj 1, 0;\n"); \
|
||||
} \
|
||||
} else { \
|
||||
fprintf(vvp_out, " %%pop/obj 1, 0;\n"); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$put") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
ivl_expr_t item = ivl_stmt_parm_count(net) >= 2 ? ivl_stmt_parm(net, 1) : 0;
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
EMIT_MBX_PUSH_ITEM(item);
|
||||
fprintf(vvp_out, " %%mbx/put;\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$get") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
fprintf(vvp_out, " %%mbx/get;\n");
|
||||
EMIT_MBX_STORE_ITEM(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$peek") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
fprintf(vvp_out, " %%mbx/peek;\n");
|
||||
EMIT_MBX_STORE_ITEM(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$try_put") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
ivl_expr_t item = ivl_stmt_parm_count(net) >= 2 ? ivl_stmt_parm(net, 1) : 0;
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
EMIT_MBX_PUSH_ITEM(item);
|
||||
fprintf(vvp_out, " %%mbx/try_put;\n");
|
||||
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$try_get") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
fprintf(vvp_out, " %%mbx/try_get;\n");
|
||||
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
||||
EMIT_MBX_STORE_ITEM(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$try_peek") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
fprintf(vvp_out, " %%mbx/try_peek;\n");
|
||||
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
||||
EMIT_MBX_STORE_ITEM(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_mailbox$num") == 0) {
|
||||
ivl_expr_t mbx = ivl_stmt_parm(net, 0);
|
||||
if (mbx) draw_eval_object(mbx);
|
||||
fprintf(vvp_out, " %%mbx/num;\n");
|
||||
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_semaphore$get") == 0) {
|
||||
ivl_expr_t sem = ivl_stmt_parm(net, 0);
|
||||
if (sem) draw_eval_object(sem);
|
||||
if (ivl_stmt_parm_count(net) >= 2 && ivl_stmt_parm(net, 1)) {
|
||||
draw_eval_vec4(ivl_stmt_parm(net, 1));
|
||||
} else {
|
||||
fprintf(vvp_out, " %%pushi/vec4 1, 0, 32;\n");
|
||||
}
|
||||
fprintf(vvp_out, " %%sem/get;\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_semaphore$put") == 0) {
|
||||
ivl_expr_t sem = ivl_stmt_parm(net, 0);
|
||||
if (sem) draw_eval_object(sem);
|
||||
if (ivl_stmt_parm_count(net) >= 2 && ivl_stmt_parm(net, 1)) {
|
||||
draw_eval_vec4(ivl_stmt_parm(net, 1));
|
||||
} else {
|
||||
fprintf(vvp_out, " %%pushi/vec4 1, 0, 32;\n");
|
||||
}
|
||||
fprintf(vvp_out, " %%sem/put;\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_semaphore$try_get") == 0) {
|
||||
ivl_expr_t sem = ivl_stmt_parm(net, 0);
|
||||
if (sem) draw_eval_object(sem);
|
||||
if (ivl_stmt_parm_count(net) >= 2 && ivl_stmt_parm(net, 1)) {
|
||||
draw_eval_vec4(ivl_stmt_parm(net, 1));
|
||||
} else {
|
||||
fprintf(vvp_out, " %%pushi/vec4 1, 0, 32;\n");
|
||||
}
|
||||
fprintf(vvp_out, " %%sem/try_get;\n");
|
||||
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name,"$ivl_vif_wait") == 0) {
|
||||
ivl_expr_t vif = ivl_stmt_parm(net, 0);
|
||||
ivl_expr_t edge_ex = ivl_stmt_parm(net, 1);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
`define IVL_UVM_MBX_T int
|
||||
`endif
|
||||
|
||||
// Poor man's mailbox
|
||||
// Poor man's mailbox (legacy IVL_UVM helper).
|
||||
// Prefer the compiler builtin `mailbox #(int)` when using -g2012 on this fork
|
||||
// (see docs/mailbox-semaphore.md). This class remains for IVL_UVM tests that
|
||||
// still instantiate ivl_uvm_mbx.
|
||||
// Default size = 1 in SV
|
||||
// Here, only size-1 is supported as of now
|
||||
// Also no parameterized classes in Icarus (yet), so using a `define
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ CORE_OBJ = lib_main.o \
|
|||
substitute.o \
|
||||
symbols.o ufunc.o codes.o vthread.o schedule.o \
|
||||
statistics.o tables.o udp.o vvp_island.o vvp_net.o vvp_net_sig.o \
|
||||
vvp_object.o vvp_cobject.o vvp_darray.o vvp_aarray.o vvp_vif.o event.o logic.o delay.o \
|
||||
vvp_object.o vvp_cobject.o vvp_darray.o vvp_aarray.o vvp_vif.o vvp_mailbox.o event.o logic.o delay.o \
|
||||
words.o island_tran.o
|
||||
|
||||
VPI_OBJ = vpi_modules.o vpi_bit.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \
|
||||
|
|
|
|||
14
vvp/codes.h
14
vvp/codes.h
|
|
@ -172,6 +172,20 @@ extern bool of_NANDR(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_NEW_COBJ(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_NEW_DARRAY(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_NEW_VIF(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_NEW(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_PUT(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_GET(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_PEEK(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_TRY_PUT(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_TRY_GET(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_TRY_PEEK(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MBX_NUM(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_SEM_NEW(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_SEM_GET(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_SEM_PUT(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_SEM_TRY_GET(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_BOX_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_UNBOX_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_NOOP(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_NOR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_NORR(vthread_t thr, vvp_code_t code);
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%assign/wr/e",of_ASSIGN_WRE,1,{OA_VPI_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%blend", of_BLEND, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%blend/wr", of_BLEND_WR,0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%box/vec4", of_BOX_VEC4, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%breakpoint", of_BREAKPOINT, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%callf/obj", of_CALLF_OBJ, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} },
|
||||
{ "%callf/real", of_CALLF_REAL, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} },
|
||||
|
|
@ -225,6 +226,14 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%load/vec4", of_LOAD_VEC4, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%load/vec4a", of_LOAD_VEC4A,2,{OA_ARR_PTR, OA_BIT1, OA_NONE} },
|
||||
{ "%max/wr", of_MAX_WR, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/get", of_MBX_GET, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/new", of_MBX_NEW, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/num", of_MBX_NUM, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/peek", of_MBX_PEEK, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/put", of_MBX_PUT, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/try_get", of_MBX_TRY_GET, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/try_peek", of_MBX_TRY_PEEK, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mbx/try_put", of_MBX_TRY_PUT, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%min/wr", of_MIN_WR, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mod", of_MOD, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%mod/s", of_MOD_S, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
|
|
@ -335,6 +344,10 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%rsort/obj", of_RSORT_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%rsort/prop/obj", of_RSORT_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%sem/get", of_SEM_GET, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%sem/new", of_SEM_NEW, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%sem/put", of_SEM_PUT, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%sem/try_get", of_SEM_TRY_GET, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
||||
{ "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
||||
{ "%set/dar/obj/vec4",of_SET_DAR_OBJ_VEC4,1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
||||
|
|
@ -389,6 +402,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%test_nul/a", of_TEST_NUL_A, 2,{OA_ARR_PTR, OA_BIT1, OA_NONE} },
|
||||
{ "%test_nul/obj", of_TEST_NUL_OBJ, 0,{OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%test_nul/prop",of_TEST_NUL_PROP,2,{OA_NUMBER, OA_BIT1, OA_NONE} },
|
||||
{ "%unbox/vec4",of_UNBOX_VEC4,1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%vif/load/vec4", of_VIF_LOAD_VEC4, 2,{OA_NUMBER, OA_BIT1, OA_NONE} },
|
||||
{ "%vif/store/vec4",of_VIF_STORE_VEC4,2,{OA_NUMBER, OA_BIT1, OA_NONE} },
|
||||
{ "%vif/wait", of_VIF_WAIT, 2,{OA_NUMBER, OA_BIT1, OA_NONE} },
|
||||
|
|
|
|||
233
vvp/vthread.cc
233
vvp/vthread.cc
|
|
@ -29,6 +29,7 @@
|
|||
# include "vvp_darray.h"
|
||||
# include "vvp_aarray.h"
|
||||
# include "vvp_vif.h"
|
||||
# include "vvp_mailbox.h"
|
||||
# include "class_type.h"
|
||||
#ifdef CHECK_WITH_VALGRIND
|
||||
# include "vvp_cleanup.h"
|
||||
|
|
@ -8399,3 +8400,235 @@ bool of_REAP_UFUNC(vthread_t thr, vvp_code_t cp)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Push an object onto a thread's object stack (used when waking mailbox/semaphore waiters).
|
||||
*/
|
||||
void vthread_push_obj_item(vthread_t thr, const vvp_object_t& obj)
|
||||
{
|
||||
thr->push_object(obj);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Mailbox opcodes
|
||||
* ================================================================ */
|
||||
|
||||
bool of_MBX_NEW(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
size_t bound = cp->number;
|
||||
vvp_object_t mbx(new vvp_mailbox(bound));
|
||||
thr->push_object(mbx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_PUT(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t item_obj;
|
||||
thr->pop_object(item_obj);
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
if (!mbx) return true;
|
||||
|
||||
bool done = mbx->put(thr, item_obj);
|
||||
if (!done) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_GET(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
if (!mbx) {
|
||||
thr->push_object(vvp_object_t());
|
||||
return true;
|
||||
}
|
||||
|
||||
vvp_object_t item;
|
||||
bool done = mbx->get(thr, item);
|
||||
if (!done) return false;
|
||||
thr->push_object(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_PEEK(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
if (!mbx) {
|
||||
thr->push_object(vvp_object_t());
|
||||
return true;
|
||||
}
|
||||
|
||||
vvp_object_t item;
|
||||
bool done = mbx->peek(thr, item);
|
||||
if (!done) return false;
|
||||
thr->push_object(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_TRY_PUT(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t item_obj;
|
||||
thr->pop_object(item_obj);
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
bool ok = mbx ? mbx->try_put(item_obj) : false;
|
||||
vvp_vector4_t res(1, ok ? BIT4_1 : BIT4_0);
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_TRY_GET(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
vvp_object_t item;
|
||||
bool ok = mbx ? mbx->try_get(item) : false;
|
||||
thr->push_object(ok ? item : vvp_object_t());
|
||||
vvp_vector4_t res(1, ok ? BIT4_1 : BIT4_0);
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_TRY_PEEK(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
vvp_object_t item;
|
||||
bool ok = mbx ? mbx->try_peek(item) : false;
|
||||
thr->push_object(ok ? item : vvp_object_t());
|
||||
vvp_vector4_t res(1, ok ? BIT4_1 : BIT4_0);
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_MBX_NUM(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
vvp_object_t mbx_obj;
|
||||
thr->pop_object(mbx_obj);
|
||||
|
||||
vvp_mailbox*mbx = mbx_obj.peek<vvp_mailbox>();
|
||||
unsigned long cnt = mbx ? (unsigned long)mbx->num() : 0UL;
|
||||
vvp_vector4_t res(32);
|
||||
for (unsigned idx = 0 ; idx < 32 ; ++idx)
|
||||
res.set_bit(idx, (cnt >> idx) & 1 ? BIT4_1 : BIT4_0);
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Semaphore opcodes
|
||||
* ================================================================ */
|
||||
|
||||
bool of_SEM_NEW(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
size_t cnt = cp->number;
|
||||
vvp_object_t sem(new vvp_semaphore(cnt));
|
||||
thr->push_object(sem);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_SEM_GET(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
unsigned long nval = 0;
|
||||
{
|
||||
const vvp_vector4_t& nv = thr->peek_vec4(0);
|
||||
unsigned nbits = nv.size() < 32 ? nv.size() : 32;
|
||||
for (unsigned i = 0 ; i < nbits ; ++i)
|
||||
if (nv.value(i) == BIT4_1) nval |= (1UL << i);
|
||||
}
|
||||
thr->pop_vec4(1);
|
||||
|
||||
vvp_object_t sem_obj;
|
||||
thr->pop_object(sem_obj);
|
||||
|
||||
vvp_semaphore*sem = sem_obj.peek<vvp_semaphore>();
|
||||
if (!sem) return true;
|
||||
|
||||
bool done = sem->get(thr, nval ? nval : 1);
|
||||
if (!done) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_SEM_PUT(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
unsigned long nval = 0;
|
||||
{
|
||||
const vvp_vector4_t& nv = thr->peek_vec4(0);
|
||||
unsigned nbits = nv.size() < 32 ? nv.size() : 32;
|
||||
for (unsigned i = 0 ; i < nbits ; ++i)
|
||||
if (nv.value(i) == BIT4_1) nval |= (1UL << i);
|
||||
}
|
||||
thr->pop_vec4(1);
|
||||
|
||||
vvp_object_t sem_obj;
|
||||
thr->pop_object(sem_obj);
|
||||
|
||||
vvp_semaphore*sem = sem_obj.peek<vvp_semaphore>();
|
||||
if (sem) sem->put(nval ? nval : 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_SEM_TRY_GET(vthread_t thr, vvp_code_t)
|
||||
{
|
||||
unsigned long nval = 0;
|
||||
{
|
||||
const vvp_vector4_t& nv = thr->peek_vec4(0);
|
||||
unsigned nbits = nv.size() < 32 ? nv.size() : 32;
|
||||
for (unsigned i = 0 ; i < nbits ; ++i)
|
||||
if (nv.value(i) == BIT4_1) nval |= (1UL << i);
|
||||
}
|
||||
thr->pop_vec4(1);
|
||||
|
||||
vvp_object_t sem_obj;
|
||||
thr->pop_object(sem_obj);
|
||||
|
||||
vvp_semaphore*sem = sem_obj.peek<vvp_semaphore>();
|
||||
bool ok = sem ? sem->try_get(nval ? nval : 1) : false;
|
||||
vvp_vector4_t res(1, ok ? BIT4_1 : BIT4_0);
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_BOX_VEC4(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
unsigned wid = cp->number;
|
||||
vvp_vector4_t v(wid);
|
||||
const vvp_vector4_t& top = thr->peek_vec4(0);
|
||||
unsigned bits = top.size() < wid ? top.size() : wid;
|
||||
for (unsigned i = 0 ; i < bits ; ++i)
|
||||
v.set_bit(i, top.value(i));
|
||||
thr->pop_vec4(1);
|
||||
vvp_object_t box(new vvp_boxed_vec4(v));
|
||||
thr->push_object(box);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_UNBOX_VEC4(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
unsigned wid = cp->number;
|
||||
vvp_object_t obj;
|
||||
thr->pop_object(obj);
|
||||
vvp_vector4_t res(wid, BIT4_0);
|
||||
if (vvp_boxed_vec4*box = obj.peek<vvp_boxed_vec4>()) {
|
||||
const vvp_vector4_t& v = box->get_value();
|
||||
unsigned bits = v.size() < wid ? v.size() : wid;
|
||||
for (unsigned i = 0 ; i < bits ; ++i)
|
||||
res.set_bit(i, v.value(i));
|
||||
}
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,4 +136,8 @@ extern const vvp_vector4_t& vthread_get_vec4_stack(struct vthread_s*thr, unsigne
|
|||
/* This is used to actually delete a thread once we are done with it. */
|
||||
extern void vthread_delete(vthread_t thr);
|
||||
|
||||
/* Push an object onto a (possibly suspended) thread object stack. */
|
||||
class vvp_object_t;
|
||||
extern void vthread_push_obj_item(vthread_t thr, const vvp_object_t& obj);
|
||||
|
||||
#endif /* IVL_vthread_H */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* 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 implementation of SystemVerilog built-in mailbox and semaphore.
|
||||
*/
|
||||
|
||||
#include "vvp_mailbox.h"
|
||||
#include "vthread.h"
|
||||
#include "schedule.h"
|
||||
#include <cassert>
|
||||
|
||||
/* ================================================================
|
||||
* vvp_mailbox
|
||||
* ================================================================ */
|
||||
|
||||
vvp_mailbox::vvp_mailbox(size_t bound)
|
||||
: bound_(bound)
|
||||
{
|
||||
}
|
||||
|
||||
vvp_mailbox::~vvp_mailbox()
|
||||
{
|
||||
}
|
||||
|
||||
vvp_object* vvp_mailbox::duplicate() const
|
||||
{
|
||||
vvp_mailbox* copy = new vvp_mailbox(bound_);
|
||||
copy->items_ = items_;
|
||||
/* Note: waiters are NOT copied – a duplicate mailbox has no
|
||||
* pending waiters. */
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool vvp_mailbox::try_put(const vvp_object_t& item)
|
||||
{
|
||||
if (full())
|
||||
return false;
|
||||
items_.push_back(item);
|
||||
resume_get_waiters_();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vvp_mailbox::try_get(vvp_object_t& item)
|
||||
{
|
||||
if (empty())
|
||||
return false;
|
||||
item = items_.front();
|
||||
items_.pop_front();
|
||||
resume_put_waiters_();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vvp_mailbox::try_peek(vvp_object_t& item)
|
||||
{
|
||||
if (empty())
|
||||
return false;
|
||||
item = items_.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vvp_mailbox::put(vthread_t thr, const vvp_object_t& item)
|
||||
{
|
||||
if (!full()) {
|
||||
items_.push_back(item);
|
||||
resume_get_waiters_();
|
||||
return true; /* completed immediately */
|
||||
}
|
||||
/* Mailbox is full – suspend the thread. Store the item in the
|
||||
* waiter record so it is available when space opens up. */
|
||||
put_waiter_t w;
|
||||
w.thr = thr;
|
||||
w.item = item;
|
||||
put_waiters_.push_back(w);
|
||||
return false; /* thread suspended */
|
||||
}
|
||||
|
||||
bool vvp_mailbox::get(vthread_t thr, vvp_object_t& item_out)
|
||||
{
|
||||
if (!empty()) {
|
||||
item_out = items_.front();
|
||||
items_.pop_front();
|
||||
resume_put_waiters_();
|
||||
return true;
|
||||
}
|
||||
get_waiter_t w;
|
||||
w.thr = thr;
|
||||
w.is_peek = false;
|
||||
get_waiters_.push_back(w);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vvp_mailbox::peek(vthread_t thr, vvp_object_t& item_out)
|
||||
{
|
||||
if (!empty()) {
|
||||
item_out = items_.front();
|
||||
return true;
|
||||
}
|
||||
get_waiter_t w;
|
||||
w.thr = thr;
|
||||
w.is_peek = true;
|
||||
get_waiters_.push_back(w);
|
||||
return false;
|
||||
}
|
||||
|
||||
void vvp_mailbox::resume_get_waiters_()
|
||||
{
|
||||
while (!get_waiters_.empty() && !empty()) {
|
||||
get_waiter_t waiter = get_waiters_.front();
|
||||
get_waiters_.erase(get_waiters_.begin());
|
||||
/* Push the retrieved item onto the waiting thread's object
|
||||
* stack so it is available when the thread resumes at the
|
||||
* instruction following %mbx/get or %mbx/peek. */
|
||||
vthread_push_obj_item(waiter.thr, items_.front());
|
||||
if (!waiter.is_peek) {
|
||||
items_.pop_front();
|
||||
resume_put_waiters_();
|
||||
}
|
||||
schedule_vthread(waiter.thr, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
void vvp_mailbox::resume_put_waiters_()
|
||||
{
|
||||
while (!put_waiters_.empty() && !full()) {
|
||||
put_waiter_t w = put_waiters_.front();
|
||||
put_waiters_.erase(put_waiters_.begin());
|
||||
items_.push_back(w.item);
|
||||
schedule_vthread(w.thr, 0, true);
|
||||
resume_get_waiters_();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* vvp_semaphore
|
||||
* ================================================================ */
|
||||
|
||||
vvp_semaphore::vvp_semaphore(size_t initial_count)
|
||||
: count_(initial_count)
|
||||
{
|
||||
}
|
||||
|
||||
vvp_semaphore::~vvp_semaphore()
|
||||
{
|
||||
}
|
||||
|
||||
vvp_object* vvp_semaphore::duplicate() const
|
||||
{
|
||||
return new vvp_semaphore(count_);
|
||||
}
|
||||
|
||||
bool vvp_semaphore::try_get(size_t n)
|
||||
{
|
||||
if (count_ >= n) {
|
||||
count_ -= n;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void vvp_semaphore::put(size_t n)
|
||||
{
|
||||
count_ += n;
|
||||
resume_waiters_();
|
||||
}
|
||||
|
||||
bool vvp_semaphore::get(vthread_t thr, size_t n)
|
||||
{
|
||||
if (count_ >= n) {
|
||||
count_ -= n;
|
||||
return true;
|
||||
}
|
||||
waiter_t w;
|
||||
w.thr = thr;
|
||||
w.n = n;
|
||||
waiters_.push_back(w);
|
||||
return false;
|
||||
}
|
||||
|
||||
void vvp_semaphore::resume_waiters_()
|
||||
{
|
||||
bool progress = true;
|
||||
while (progress) {
|
||||
progress = false;
|
||||
for (size_t idx = 0; idx < waiters_.size(); ++idx) {
|
||||
if (count_ >= waiters_[idx].n) {
|
||||
count_ -= waiters_[idx].n;
|
||||
vthread_t w = waiters_[idx].thr;
|
||||
waiters_.erase(waiters_.begin() + (long)idx);
|
||||
schedule_vthread(w, 0, true);
|
||||
progress = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* 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 */
|
||||
Loading…
Reference in New Issue