SV: add class-handle $cast and static $typename
Enable UVM factory-style downcasts via runtime is-a checks on the class inheritance chain, and fold $typename to documented static type strings (e.g. "class derived").
This commit is contained in:
parent
b27828d5a6
commit
23f2989056
|
|
@ -10,7 +10,7 @@ Each item should be a dedicated `feat/<name>` branch, with tests/examples and a
|
|||
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) — **partial** (int mailbox + semaphore; see [mailbox-semaphore.md](mailbox-semaphore.md))
|
||||
6. **Constraints + `randomize()` / `randomize() with`** — **partial** (unconstrained `rand`/`randc` only; see [randomize.md](randomize.md)); solver / `with` still missing
|
||||
7. **`$cast` / `$typename` hardening** for factory patterns
|
||||
7. **`$cast` / `$typename` hardening** for factory patterns — **partial** (class-handle `$cast` + static `$typename`; see [cast-typename.md](cast-typename.md))
|
||||
8. **Covergroups** — functional coverage
|
||||
9. **DPI-C** — optional but common in real flows
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ Last updated: 2026-07-21
|
|||
| 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 | **Partial** on `feat/randomize-unconstrained`: unconstrained `rand`/`randc` integral props via `obj.randomize()` (returns 1). No constraint solver / `with` yet. See [`docs/randomize.md`](randomize.md) and [`examples/randomize`](../examples/randomize). |
|
||||
| `$cast` / `$typename` | **Partial** on `feat/cast-typename`: class-handle `$cast` (dynamic is-a + assign); static `$typename` → `"class <name>"` etc. See [`docs/cast-typename.md`](cast-typename.md) and [`examples/cast_typename`](../examples/cast_typename). |
|
||||
| Covergroups / DPI | Missing |
|
||||
|
||||
## Remotes
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
# `$cast` / `$typename` (Tier A #7, first slice)
|
||||
|
||||
Status: **partial** — class-handle `$cast` and static `$typename` for common UVM factory-style patterns.
|
||||
|
||||
## Supported in this slice
|
||||
|
||||
```systemverilog
|
||||
class base; endclass
|
||||
class derived extends base;
|
||||
int x;
|
||||
function new(); x = 7; endfunction
|
||||
endclass
|
||||
|
||||
base b;
|
||||
derived d, d2;
|
||||
bit ok;
|
||||
string tn;
|
||||
|
||||
d = new;
|
||||
b = d;
|
||||
ok = $cast(d2, b); // 1 if b's dynamic type is-a derived
|
||||
tn = $typename(d); // "class derived"
|
||||
```
|
||||
|
||||
### `$cast(dest, src)`
|
||||
|
||||
| Form | Behavior |
|
||||
|------|----------|
|
||||
| Function `ok = $cast(dest, src)` | Returns `1` on success, `0` on failure; on success assigns `src` into `dest` |
|
||||
| Task `$cast(dest, src);` | Same assign/check; return value discarded (no runtime fatal on failure yet) |
|
||||
|
||||
- **Class handles only** (destination must be a simple class variable)
|
||||
- Success when `src` is non-null and its **runtime** class is `dest`'s declared type or a subclass (inheritance walk)
|
||||
- Failure leaves `dest` unchanged and returns `0`
|
||||
- Null `src` → failure
|
||||
|
||||
### `$typename(expr)` / `$typename(type)`
|
||||
|
||||
Returns a **compile-time** string for the **static** type of the argument (optional second length argument is accepted and ignored, for Accellera `uvm_typename` compatibility).
|
||||
|
||||
| Argument | String format |
|
||||
|----------|----------------|
|
||||
| Class variable / type | `class <name>` (e.g. `class derived`) |
|
||||
| `string` | `string` |
|
||||
| `real` | `real` |
|
||||
| `bit` / `logic` (and other integral) | `bit` / `logic` (width not included yet) |
|
||||
| Anything else | `unknown` |
|
||||
|
||||
**Limitation:** `$typename` does **not** report the dynamic (runtime) type of a class handle. `$typename(b)` is `"class base"` even when `b` holds a `derived` object. Use `$cast` / virtual `get_type_name()`-style methods for dynamic discrimination.
|
||||
|
||||
## Encoding
|
||||
|
||||
| Layer | Role |
|
||||
|-------|------|
|
||||
| Elab | `$cast` → `$ivl_cast(dest, src)`; `$typename` → `NetECString` |
|
||||
| Codegen | `%cast/cobj C<dest_type>, v<dest>` after evaluating `src` |
|
||||
| Runtime | Walk `class_type` super chain (`is_a`); store handle on success |
|
||||
| Class emit | `.class "name" [n], C<sup>` records inheritance for the walk |
|
||||
|
||||
## Deferred (do not claim)
|
||||
|
||||
- Non-class `$cast` (integrals, enums, etc.)
|
||||
- Destinations that are class properties / selects (`obj.m`, `arr[i]`)
|
||||
- Task-form fatal on failed cast (IEEE optional behavior)
|
||||
- Dynamic `$typename` / full IEEE type-string formatting (packed ranges, enums, packages)
|
||||
- `$typename` second-argument truncation
|
||||
|
||||
## Example
|
||||
|
||||
[`examples/cast_typename/cast_typename_basic.sv`](../examples/cast_typename/cast_typename_basic.sv) — prints `PASSED`.
|
||||
|
||||
```bash
|
||||
./install/bin/iverilog -g2012 -o /tmp/cast.vvp examples/cast_typename/cast_typename_basic.sv
|
||||
./install/bin/vvp /tmp/cast.vvp
|
||||
```
|
||||
|
||||
See also [STATUS.md](STATUS.md) and [ROADMAP.md](ROADMAP.md).
|
||||
148
elab_expr.cc
148
elab_expr.cc
|
|
@ -1775,6 +1775,34 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope,
|
|||
return expr_width_;
|
||||
}
|
||||
|
||||
if (name=="$cast") {
|
||||
/* Function form returns bit success; args are self-determined. */
|
||||
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
|
||||
if (parms_[idx].parm) {
|
||||
width_mode_t arg_mode = SIZED;
|
||||
parms_[idx].parm->test_width(des, scope, arg_mode);
|
||||
}
|
||||
}
|
||||
expr_type_ = IVL_VT_BOOL;
|
||||
expr_width_ = 1;
|
||||
min_width_ = 1;
|
||||
signed_flag_ = false;
|
||||
return expr_width_;
|
||||
}
|
||||
|
||||
if (name=="$typename") {
|
||||
if (parms_.size() >= 1 && parms_[0].parm) {
|
||||
width_mode_t arg_mode = SIZED;
|
||||
if (! dynamic_cast<PETypename*>(parms_[0].parm))
|
||||
parms_[0].parm->test_width(des, scope, arg_mode);
|
||||
}
|
||||
expr_type_ = IVL_VT_STRING;
|
||||
expr_width_ = 1;
|
||||
min_width_ = 1;
|
||||
signed_flag_ = false;
|
||||
return expr_width_;
|
||||
}
|
||||
|
||||
/* Get the return type of the system function by looking it up
|
||||
in the sfunc_table. */
|
||||
const struct sfunc_return_type*sfunc_info = lookup_sys_func(name);
|
||||
|
|
@ -2411,6 +2439,126 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope,
|
|||
return cast_to_width_(sub, expr_wid);
|
||||
}
|
||||
|
||||
/* $cast(dest, src): class-handle dynamic cast. Rewritten to
|
||||
$ivl_cast so codegen can assign dest and return success. */
|
||||
if (name=="$cast") {
|
||||
if (parms_.size() != 2 || !parms_[0].parm || !parms_[1].parm) {
|
||||
cerr << get_fileline() << ": error: $cast takes exactly "
|
||||
<< "two arguments: $cast(dest, src)." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr*dest_ex = elab_sys_task_arg(des, scope, name, 0, parms_[0].parm);
|
||||
NetExpr*src_ex = elab_sys_task_arg(des, scope, name, 1, parms_[1].parm);
|
||||
if (!dest_ex || !src_ex) {
|
||||
delete dest_ex;
|
||||
delete src_ex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetESignal*dest_sig = dynamic_cast<NetESignal*>(dest_ex);
|
||||
const netclass_t*dest_cls = 0;
|
||||
if (dest_sig && dest_sig->sig())
|
||||
dest_cls = dynamic_cast<const netclass_t*>(dest_sig->sig()->net_type());
|
||||
|
||||
if (!dest_sig || !dest_cls) {
|
||||
cerr << get_fileline() << ": sorry: $cast currently supports "
|
||||
<< "class-handle destinations only." << endl;
|
||||
des->errors += 1;
|
||||
delete dest_ex;
|
||||
delete src_ex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (src_ex->expr_type() != IVL_VT_CLASS
|
||||
&& ! dynamic_cast<NetENull*>(src_ex)) {
|
||||
cerr << get_fileline() << ": sorry: $cast currently supports "
|
||||
<< "class-handle sources only." << endl;
|
||||
des->errors += 1;
|
||||
delete dest_ex;
|
||||
delete src_ex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetESFunc*sys = new NetESFunc("$ivl_cast",
|
||||
&netvector_t::scalar_logic, 2);
|
||||
sys->set_line(*this);
|
||||
sys->parm(0, dest_ex);
|
||||
sys->parm(1, src_ex);
|
||||
return pad_to_width(sys, expr_wid, signed_flag_, *this);
|
||||
}
|
||||
|
||||
/* $typename(expr): fold to a string describing the static type.
|
||||
Format for classes is "class <name>" (see docs/cast-typename.md). */
|
||||
if (name=="$typename") {
|
||||
if (parms_.empty() || !parms_[0].parm) {
|
||||
cerr << get_fileline() << ": error: $typename takes at least "
|
||||
<< "one argument." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
if (parms_.size() > 2) {
|
||||
cerr << get_fileline() << ": error: $typename takes one or "
|
||||
<< "two arguments." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PExpr*texpr = parms_[0].parm;
|
||||
string tname;
|
||||
|
||||
if (const PETypename*type_expr = dynamic_cast<PETypename*>(texpr)) {
|
||||
ivl_type_t data_type = type_expr->get_type()->elaborate_type(des, scope);
|
||||
if (const netclass_t*cls = dynamic_cast<const netclass_t*>(data_type)) {
|
||||
tname = string("class ") + cls->get_name().str();
|
||||
} else if (data_type && data_type->base_type() == IVL_VT_STRING) {
|
||||
tname = "string";
|
||||
} else if (data_type && data_type->base_type() == IVL_VT_REAL) {
|
||||
tname = "real";
|
||||
} else if (data_type && data_type->base_type() == IVL_VT_BOOL) {
|
||||
tname = "bit";
|
||||
} else if (data_type && data_type->base_type() == IVL_VT_LOGIC) {
|
||||
tname = "logic";
|
||||
} else {
|
||||
tname = "unknown";
|
||||
}
|
||||
} else {
|
||||
NetExpr*ne = elab_sys_task_arg(des, scope, name, 0, texpr);
|
||||
if (!ne)
|
||||
return 0;
|
||||
|
||||
if (ne->expr_type() == IVL_VT_CLASS) {
|
||||
const netclass_t*cls = dynamic_cast<const netclass_t*>(ne->net_type());
|
||||
if (!cls) {
|
||||
if (NetESignal*sig = dynamic_cast<NetESignal*>(ne)) {
|
||||
if (sig->sig())
|
||||
cls = dynamic_cast<const netclass_t*>(sig->sig()->net_type());
|
||||
}
|
||||
}
|
||||
if (cls)
|
||||
tname = string("class ") + cls->get_name().str();
|
||||
else
|
||||
tname = "class";
|
||||
} else if (ne->expr_type() == IVL_VT_STRING) {
|
||||
tname = "string";
|
||||
} else if (ne->expr_type() == IVL_VT_REAL) {
|
||||
tname = "real";
|
||||
} else if (ne->expr_type() == IVL_VT_BOOL) {
|
||||
tname = "bit";
|
||||
} else if (ne->expr_type() == IVL_VT_LOGIC) {
|
||||
tname = "logic";
|
||||
} else {
|
||||
tname = "unknown";
|
||||
}
|
||||
delete ne;
|
||||
}
|
||||
|
||||
NetECString*str = new NetECString(tname);
|
||||
str->set_line(*this);
|
||||
return str;
|
||||
}
|
||||
|
||||
unsigned nparms = parms_.size();
|
||||
|
||||
NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms, is_overridden_);
|
||||
|
|
|
|||
48
elaborate.cc
48
elaborate.cc
|
|
@ -3922,6 +3922,54 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const
|
|||
|
||||
scope->calls_sys_task(true);
|
||||
|
||||
/* Task form of $cast(dest, src): same runtime as the function. */
|
||||
if (name == "$cast") {
|
||||
if (parm_count != 2 || !parms_[0].parm || !parms_[1].parm) {
|
||||
cerr << get_fileline() << ": error: $cast takes exactly "
|
||||
<< "two arguments: $cast(dest, src)." << endl;
|
||||
des->errors += 1;
|
||||
for (unsigned idx = 0 ; idx < parm_count ; idx += 1)
|
||||
delete eparms[idx];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Re-elaborate dest/src with class-handle checks via a
|
||||
temporary function-style call path is awkward here; do
|
||||
lightweight validation and emit $ivl_cast. */
|
||||
NetExpr*dest_ex = eparms[0];
|
||||
NetExpr*src_ex = eparms[1];
|
||||
NetESignal*dest_sig = dynamic_cast<NetESignal*>(dest_ex);
|
||||
const netclass_t*dest_cls = 0;
|
||||
if (dest_sig && dest_sig->sig())
|
||||
dest_cls = dynamic_cast<const netclass_t*>(dest_sig->sig()->net_type());
|
||||
|
||||
if (!dest_sig || !dest_cls) {
|
||||
cerr << get_fileline() << ": sorry: $cast currently supports "
|
||||
<< "class-handle destinations only." << endl;
|
||||
des->errors += 1;
|
||||
for (unsigned idx = 0 ; idx < parm_count ; idx += 1)
|
||||
delete eparms[idx];
|
||||
return 0;
|
||||
}
|
||||
if (src_ex && src_ex->expr_type() != IVL_VT_CLASS
|
||||
&& ! dynamic_cast<NetENull*>(src_ex)) {
|
||||
cerr << get_fileline() << ": sorry: $cast currently supports "
|
||||
<< "class-handle sources only." << endl;
|
||||
des->errors += 1;
|
||||
for (unsigned idx = 0 ; idx < parm_count ; idx += 1)
|
||||
delete eparms[idx];
|
||||
return 0;
|
||||
}
|
||||
|
||||
vector<NetExpr*> cparms(2);
|
||||
cparms[0] = dest_ex;
|
||||
cparms[1] = src_ex;
|
||||
NetSTask*cur = new NetSTask("$ivl_cast",
|
||||
def_sfunc_as_task, cparms);
|
||||
cur->set_line(*this);
|
||||
return cur;
|
||||
}
|
||||
|
||||
NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms);
|
||||
cur->set_line(*this);
|
||||
return cur;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
// Tier A #7 smoke: $cast (class handles) + $typename (static type string).
|
||||
class base; endclass
|
||||
class derived extends base;
|
||||
int x;
|
||||
function new(); x = 7; endfunction
|
||||
endclass
|
||||
|
||||
module top;
|
||||
base b, b2;
|
||||
derived d, d2;
|
||||
string tn;
|
||||
bit ok;
|
||||
int pass;
|
||||
|
||||
initial begin
|
||||
pass = 1;
|
||||
|
||||
d = new;
|
||||
b = d;
|
||||
|
||||
ok = $cast(d2, b);
|
||||
if (!ok || d2 == null || d2.x !== 7) begin
|
||||
$display("FAIL: downcast ok=%0b d2.x=%0d", ok, d2 == null ? -1 : d2.x);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
// Upcast always succeeds for a live derived object.
|
||||
ok = $cast(b2, d);
|
||||
if (!ok || b2 == null) begin
|
||||
$display("FAIL: upcast");
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
// Task form: same dynamic check / assign.
|
||||
d2 = null;
|
||||
$cast(d2, b);
|
||||
if (d2 == null || d2.x !== 7) begin
|
||||
$display("FAIL: task-form $cast");
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
// Incompatible: base-only object into derived.
|
||||
b2 = new;
|
||||
ok = $cast(d2, b2);
|
||||
if (ok) begin
|
||||
$display("FAIL: expected incompatible cast to fail");
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
tn = $typename(d);
|
||||
if (tn != "class derived") begin
|
||||
$display("FAIL: $typename(d)=\"%s\" want \"class derived\"", tn);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
tn = $typename(b);
|
||||
if (tn != "class base") begin
|
||||
$display("FAIL: $typename(b)=\"%s\" want \"class base\"", tn);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
$display("typename=%s", $typename(d));
|
||||
if (pass) $display("PASSED");
|
||||
else $fatal(1, "cast/typename smoke failed");
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -2398,6 +2398,8 @@ extern int ivl_type_packed_msb(ivl_type_t net, unsigned dim);
|
|||
extern unsigned ivl_type_packed_width(ivl_type_t net);
|
||||
extern int ivl_type_signed(ivl_type_t net);
|
||||
extern const char* ivl_type_name(ivl_type_t net);
|
||||
/* Superclass for a class type, or 0 if none / not a class. */
|
||||
extern ivl_type_t ivl_type_super(ivl_type_t net);
|
||||
extern int ivl_type_properties(ivl_type_t net);
|
||||
extern const char* ivl_type_prop_name(ivl_type_t net, int idx);
|
||||
extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx);
|
||||
|
|
|
|||
|
|
@ -3290,6 +3290,14 @@ extern "C" const char* ivl_type_name(ivl_type_t net)
|
|||
return 0;
|
||||
}
|
||||
|
||||
extern "C" ivl_type_t ivl_type_super(ivl_type_t net)
|
||||
{
|
||||
if (const netclass_t*class_type = dynamic_cast<const netclass_t*>(net))
|
||||
return class_type->get_super();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int ivl_type_properties(ivl_type_t net)
|
||||
{
|
||||
const netclass_t*class_type = dynamic_cast<const netclass_t*>(net);
|
||||
|
|
|
|||
|
|
@ -81,8 +81,16 @@ static void show_prop_type(ivl_type_t ptype)
|
|||
void draw_class_in_scope(ivl_type_t classtype)
|
||||
{
|
||||
int idx;
|
||||
fprintf(vvp_out, "C%p .class \"%s\" [%d]\n",
|
||||
classtype, ivl_type_name(classtype), ivl_type_properties(classtype));
|
||||
ivl_type_t super = ivl_type_super(classtype);
|
||||
|
||||
if (super)
|
||||
fprintf(vvp_out, "C%p .class \"%s\" [%d], C%p\n",
|
||||
classtype, ivl_type_name(classtype),
|
||||
ivl_type_properties(classtype), super);
|
||||
else
|
||||
fprintf(vvp_out, "C%p .class \"%s\" [%d]\n",
|
||||
classtype, ivl_type_name(classtype),
|
||||
ivl_type_properties(classtype));
|
||||
|
||||
for (idx = 0 ; idx < ivl_type_properties(classtype) ; idx += 1) {
|
||||
fprintf(vvp_out, " %3d: \"%s\", ", idx, ivl_type_prop_name(classtype,idx));
|
||||
|
|
|
|||
|
|
@ -94,6 +94,45 @@ void draw_ivl_randomize(ivl_expr_t obj, int leave_result)
|
|||
fprintf(vvp_out, " %%pushi/vec4 1, 0, 1;\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Class-handle $cast: evaluate src object, then %cast/cobj into dest.
|
||||
* Leaves a 1-bit success flag when leave_result is set.
|
||||
*/
|
||||
void draw_ivl_cast(ivl_expr_t dest, ivl_expr_t src, int leave_result)
|
||||
{
|
||||
ivl_signal_t dest_sig = 0;
|
||||
ivl_type_t dest_type = 0;
|
||||
|
||||
if (dest && ivl_expr_type(dest) == IVL_EX_SIGNAL) {
|
||||
dest_sig = ivl_expr_signal(dest);
|
||||
if (dest_sig)
|
||||
dest_type = ivl_signal_net_type(dest_sig);
|
||||
}
|
||||
|
||||
if (src)
|
||||
draw_eval_object(src);
|
||||
|
||||
if (dest_sig == 0 || dest_type == 0
|
||||
|| ivl_type_base(dest_type) != IVL_VT_CLASS) {
|
||||
if (src)
|
||||
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
|
||||
if (leave_result)
|
||||
fprintf(vvp_out, " %%pushi/vec4 0, 0, 1;\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! src) {
|
||||
/* No source: fail without touching dest. */
|
||||
if (leave_result)
|
||||
fprintf(vvp_out, " %%pushi/vec4 0, 0, 1;\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(vvp_out, " %%cast/cobj C%p, v%p_0;\n", dest_type, dest_sig);
|
||||
if (! leave_result)
|
||||
fprintf(vvp_out, " %%pop/vec4 1;\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if the draw_immediate_vec4 instruction can be used.
|
||||
*/
|
||||
|
|
@ -1338,6 +1377,16 @@ static void draw_sfunc_vec4(ivl_expr_t expr)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Class-handle $cast(dest, src) -> $ivl_cast */
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_cast") == 0) {
|
||||
ivl_expr_t dest = (parm_count > 0) ? ivl_expr_parm(expr, 0) : 0;
|
||||
ivl_expr_t src = (parm_count > 1) ? ivl_expr_parm(expr, 1) : 0;
|
||||
draw_ivl_cast(dest, src, 1);
|
||||
if (ivl_expr_width(expr) > 1)
|
||||
fprintf(vvp_out, " %%pad/u %u;\n", ivl_expr_width(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
draw_vpi_func_call(expr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -213,6 +213,10 @@ extern void draw_eval_vec4(ivl_expr_t ex);
|
|||
* Leaves a 1-bit success flag on the vec4 stack when leave_result!=0;
|
||||
* otherwise pops the result (task form). */
|
||||
extern void draw_ivl_randomize(ivl_expr_t obj, int leave_result);
|
||||
|
||||
/* Emit class-handle $cast as $ivl_cast(dest, src). Stores src into dest
|
||||
* when the dynamic type is compatible; leaves 1-bit ok when leave_result. */
|
||||
extern void draw_ivl_cast(ivl_expr_t dest, ivl_expr_t src, int leave_result);
|
||||
extern void resize_vec4_wid(ivl_expr_t expr, unsigned wid);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2055,6 +2055,15 @@ static int show_system_task_call(ivl_statement_t net)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(stmt_name, "$ivl_cast") == 0) {
|
||||
ivl_expr_t dest = ivl_stmt_parm_count(net) > 0
|
||||
? ivl_stmt_parm(net, 0) : 0;
|
||||
ivl_expr_t src = ivl_stmt_parm_count(net) > 1
|
||||
? ivl_stmt_parm(net, 1) : 0;
|
||||
draw_ivl_cast(dest, src, 0);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ void property_object::copy(char*dst, char*src)
|
|||
/* **** */
|
||||
|
||||
class_type::class_type(const string&nam, size_t nprop)
|
||||
: class_name_(nam), properties_(nprop)
|
||||
: class_name_(nam), super_(0), properties_(nprop)
|
||||
{
|
||||
instance_size_ = 0;
|
||||
}
|
||||
|
|
@ -408,6 +408,17 @@ class_type::~class_type()
|
|||
delete properties_[idx].type;
|
||||
}
|
||||
|
||||
bool class_type::is_a(const class_type*target) const
|
||||
{
|
||||
if (target == 0)
|
||||
return false;
|
||||
for (const class_type*cur = this; cur; cur = cur->get_super()) {
|
||||
if (cur == target)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void class_type::set_property(size_t idx, const string&name, const string&type, uint64_t array_size)
|
||||
{
|
||||
assert(idx < properties_.size());
|
||||
|
|
@ -596,6 +607,14 @@ void compile_class_property(unsigned idx, char*nam, char*typ, uint64_t array_siz
|
|||
delete[]typ;
|
||||
}
|
||||
|
||||
void compile_class_set_super(char*super_lab)
|
||||
{
|
||||
assert(compile_class);
|
||||
if (super_lab == 0)
|
||||
return;
|
||||
compile_vpi_lookup(compile_class->super_handle_ptr(), super_lab);
|
||||
}
|
||||
|
||||
void compile_class_done(void)
|
||||
{
|
||||
__vpiScope*scope = vpip_peek_current_scope();
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ class class_type : public __vpiHandle {
|
|||
|
||||
// This is the name of the class type.
|
||||
inline const std::string&class_name(void) const { return class_name_; }
|
||||
// Superclass (may be filled later via compile_vpi_lookup).
|
||||
inline const class_type* get_super(void) const
|
||||
{ return dynamic_cast<const class_type*>(super_); }
|
||||
inline vpiHandle* super_handle_ptr(void) { return &super_; }
|
||||
/* True if this type is target or derived from target. */
|
||||
bool is_a(const class_type*target) const;
|
||||
// Number of properties in the class definition.
|
||||
inline size_t property_count(void) const { return properties_.size(); }
|
||||
|
||||
|
|
@ -76,6 +82,7 @@ class class_type : public __vpiHandle {
|
|||
|
||||
private:
|
||||
std::string class_name_;
|
||||
vpiHandle super_;
|
||||
|
||||
struct prop_t {
|
||||
std::string name;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ extern bool of_CASSIGN_VEC4(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_CASSIGN_VEC4_OFF(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_CASSIGN_WR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_CAST2(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_CAST_COBJ(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_CAST_VEC2_DAR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_CAST_VEC4_DAR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_CAST_VEC4_STR(vthread_t thr, vvp_code_t code);
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%cassign/vec4", of_CASSIGN_VEC4, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%cassign/vec4/off",of_CASSIGN_VEC4_OFF,2,{OA_FUNC_PTR,OA_BIT1, OA_NONE} },
|
||||
{ "%cassign/wr", of_CASSIGN_WR, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%cast/cobj", of_CAST_COBJ, 2, {OA_VPI_PTR, OA_FUNC_PTR2, OA_NONE} },
|
||||
{ "%cast/vec2/dar", of_CAST_VEC2_DAR, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%cast/vec4/dar", of_CAST_VEC4_DAR, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%cast/vec4/str", of_CAST_VEC4_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
|
|
|
|||
|
|
@ -585,6 +585,7 @@ extern void delete_udp_symbols(void);
|
|||
|
||||
extern void compile_class_start(char*lab, char*nam, unsigned nprop);
|
||||
extern void compile_class_property(unsigned idx, char*nam, char*typ, uint64_t array_size);
|
||||
extern void compile_class_set_super(char*super_lab);
|
||||
extern void compile_class_done(void);
|
||||
|
||||
#endif /* IVL_compile_H */
|
||||
|
|
|
|||
|
|
@ -916,6 +916,11 @@ statement
|
|||
class_properties_opt ';'
|
||||
{ compile_class_done(); }
|
||||
|
||||
| T_LABEL K_CLASS T_STRING '[' T_NUMBER ']' ',' T_SYMBOL
|
||||
{ compile_class_start($1, $3, $5); compile_class_set_super($8); }
|
||||
class_properties_opt ';'
|
||||
{ compile_class_done(); }
|
||||
|
||||
| enum_type
|
||||
{ ; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1750,6 +1750,37 @@ bool of_CAST2(vthread_t thr, vvp_code_t)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %cast/cobj <class_defn>, <dest_net>
|
||||
*
|
||||
* Pop a class object from the object stack. If its dynamic type is an
|
||||
* instance of (or derived from) <class_defn>, store it into <dest_net>
|
||||
* and push 1 onto the vec4 stack; otherwise leave dest unchanged and
|
||||
* push 0. A null source fails the cast.
|
||||
*/
|
||||
bool of_CAST_COBJ(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
const class_type*dest_type = dynamic_cast<const class_type*>(cp->handle);
|
||||
assert(dest_type);
|
||||
assert(cp->net2);
|
||||
|
||||
vvp_object_t obj;
|
||||
thr->pop_object(obj);
|
||||
|
||||
int ok = 0;
|
||||
if (! obj.test_nil()) {
|
||||
vvp_cobject*cobj = obj.peek<vvp_cobject>();
|
||||
if (cobj && cobj->get_defn() && cobj->get_defn()->is_a(dest_type)) {
|
||||
vvp_net_ptr_t ptr(cp->net2, 0);
|
||||
vvp_send_object(ptr, obj, thr->wt_context);
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
thr->push_vec4(vvp_vector4_t(1, ok ? BIT4_1 : BIT4_0));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_cast_vec_dar(vthread_t thr, vvp_code_t cp, bool as_vec4)
|
||||
{
|
||||
unsigned wid = cp->number;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ class vvp_cobject : public vvp_object {
|
|||
|
||||
void shallow_copy(const vvp_object*that) override;
|
||||
|
||||
inline const class_type* get_defn(void) const { return defn_; }
|
||||
|
||||
private:
|
||||
const class_type* defn_;
|
||||
// For now, only support 32bit bool signed properties.
|
||||
|
|
|
|||
Loading…
Reference in New Issue