SV: add string-keyed associative array vertical slice
Support int aa[string]/int aa[*] with assign, size/num, exists, delete, foreach, and whole-array copy for UVM Tier A table patterns. Runtime uses vvp_aarray_vec4 (std::map); int keys and richer element types deferred.
This commit is contained in:
parent
39e757e975
commit
bcc026c902
|
|
@ -116,7 +116,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \
|
|||
emit.o eval_attrib.o \
|
||||
eval_tree.o expr_synth.o functor.o lexor.o lexor_keyword.o link_const.o \
|
||||
load_module.o netlist.o netmisc.o nettypes.o net_analog.o net_assign.o \
|
||||
net_design.o netclass.o netdarray.o \
|
||||
net_design.o netclass.o netdarray.o netaarray.o \
|
||||
netenum.o netparray.o netqueue.o netscalar.o netstruct.o netvector.o \
|
||||
net_event.o net_expr.o net_func.o \
|
||||
net_func_eval.o net_link.o net_modulo.o \
|
||||
|
|
|
|||
9
PExpr.cc
9
PExpr.cc
|
|
@ -391,6 +391,15 @@ PENull::~PENull()
|
|||
{
|
||||
}
|
||||
|
||||
PEAArrayKey::PEAArrayKey(key_kind_t kind)
|
||||
: kind_(kind)
|
||||
{
|
||||
}
|
||||
|
||||
PEAArrayKey::~PEAArrayKey()
|
||||
{
|
||||
}
|
||||
|
||||
PEFNumber::PEFNumber(verireal*v)
|
||||
: value_(v)
|
||||
{
|
||||
|
|
|
|||
27
PExpr.h
27
PExpr.h
|
|
@ -637,6 +637,33 @@ class PENull : public PExpr {
|
|||
unsigned flags) const override;
|
||||
};
|
||||
|
||||
/*
|
||||
* Sentinel used in pform_range_t for associative-array dimensions.
|
||||
* WILDCARD encodes `[*]` (string-keyed in the first vertical slice).
|
||||
* STRING encodes `[string]`.
|
||||
*/
|
||||
class PEAArrayKey : public PExpr {
|
||||
public:
|
||||
enum key_kind_t { STRING, WILDCARD };
|
||||
|
||||
explicit PEAArrayKey(key_kind_t kind);
|
||||
~PEAArrayKey() override;
|
||||
|
||||
inline key_kind_t key_kind() const { return kind_; }
|
||||
|
||||
virtual void dump(std::ostream&) const override;
|
||||
virtual unsigned test_width(Design*des, NetScope*scope,
|
||||
width_mode_t&mode) override;
|
||||
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
|
||||
ivl_type_t type, unsigned flags) const override;
|
||||
virtual NetExpr*elaborate_expr(Design*des, NetScope*,
|
||||
unsigned expr_wid,
|
||||
unsigned flags) const override;
|
||||
|
||||
private:
|
||||
key_kind_t kind_;
|
||||
};
|
||||
|
||||
class PENumber : public PExpr {
|
||||
|
||||
public:
|
||||
|
|
|
|||
1
PWire.h
1
PWire.h
|
|
@ -80,6 +80,7 @@ class PWire : public PNamedItem {
|
|||
void set_range(const std::list<pform_range_t>&ranges, PWSRType type);
|
||||
|
||||
void set_unpacked_idx(const std::list<pform_range_t>&ranges);
|
||||
const std::list<pform_range_t>& get_unpacked_idx() const { return unpacked_; }
|
||||
|
||||
void set_data_type(data_type_t*type);
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ ostream& operator << (ostream&o, ivl_variable_type_t val)
|
|||
case IVL_VT_QUEUE:
|
||||
o << "queue";
|
||||
break;
|
||||
case IVL_VT_AARRAY:
|
||||
o << "aarray";
|
||||
break;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ Each item should be a dedicated `feat/<name>` branch, with tests/examples and a
|
|||
|
||||
## Tier A — Compiler SV foundations (block Accellera UVM)
|
||||
|
||||
1. **Parameterized classes** — `class C #(type T = int);` / `C#(byte)` — needed for `uvm_*#(T)`, `config_db`
|
||||
2. **Associative arrays** — `int aa[string];`
|
||||
1. **Parameterized classes** — `class C #(type T = int);` / `C#(byte)` — needed for `uvm_*#(T)`, `config_db` — **partial** (defaults; see STATUS)
|
||||
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`
|
||||
4. **Clocking blocks** — enough for `@(vif.cb)`
|
||||
5. **`mailbox` / `semaphore` builtins** (or solid class equivalents with blocking put/get)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ Last updated: 2026-07-21
|
|||
| 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 |
|
||||
| [`examples/hello_uvm`](../examples/hello_uvm) | Smoke TB for the seeded library (`Makefile` included) |
|
||||
| Parameterized classes | **Partial** on `feat/param-classes`: 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 | Missing |
|
||||
| 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 | Missing |
|
||||
| Constraints / randomize | Missing |
|
||||
| Covergroups / DPI | Missing |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# Associative arrays (Tier A #2)
|
||||
|
||||
Status: **partial** — string-keyed vertical slice for UVM-like tables.
|
||||
|
||||
## Supported in this slice
|
||||
|
||||
```systemverilog
|
||||
int aa[string];
|
||||
int aa[*]; // accepted as string-keyed (not IEEE int-key wildcard yet)
|
||||
aa["x"] = 1;
|
||||
v = aa["x"];
|
||||
n = aa.size(); // also aa.num()
|
||||
aa.exists("x");
|
||||
aa.delete("x");
|
||||
aa.delete(); // clear all
|
||||
foreach (aa[k]) ... // k is string
|
||||
bb = aa; // whole-array copy
|
||||
```
|
||||
|
||||
Element types: packed integral (`int` / `logic` vectors) only for now.
|
||||
|
||||
## Encoding
|
||||
|
||||
| Syntax | `pform_range_t` sentinel |
|
||||
|--------|--------------------------|
|
||||
| `[*]` | `(PENull, PENull)` — distinct from queue `(PENull, 0)` and darray `(0, 0)` |
|
||||
| `[string]` | `(PETypename(string), 0)` via `'[' expression ']'` (avoids bison conflict with typename-as-expr) |
|
||||
|
||||
Elaborates to `netaarray_t` with `IVL_VT_AARRAY`. Runtime: `vvp_aarray_vec4` (`std::map<std::string, vvp_vector4_t>`).
|
||||
|
||||
## Deferred (do not claim)
|
||||
|
||||
- Integer / class / enum keys (true IEEE `[*]` integer keys)
|
||||
- Class / string / real / nested AA elements
|
||||
- `first` / `last` / `next` / `prev` methods (foreach uses internal `key_at`)
|
||||
- Associative arrays as class properties
|
||||
|
||||
See also [STATUS.md](STATUS.md) and [ROADMAP.md](ROADMAP.md).
|
||||
100
elab_expr.cc
100
elab_expr.cc
|
|
@ -35,6 +35,7 @@
|
|||
# include "discipline.h"
|
||||
# include "netmisc.h"
|
||||
# include "netdarray.h"
|
||||
# include "netaarray.h"
|
||||
# include "netqueue.h"
|
||||
# include "netstruct.h"
|
||||
# include "netscalar.h"
|
||||
|
|
@ -414,6 +415,10 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type,
|
|||
// all the types to this new form.
|
||||
typed_elab = true;
|
||||
break;
|
||||
case IVL_VT_AARRAY:
|
||||
// Whole-array assigns are handled as objects; indexed
|
||||
// assigns use the element type via context_wid below.
|
||||
break;
|
||||
case IVL_VT_REAL:
|
||||
case IVL_VT_STRING:
|
||||
break;
|
||||
|
|
@ -3939,6 +3944,65 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
|
|||
|
||||
ivl_assert(*this, sub_expr);
|
||||
|
||||
// Associative array methods (string-keyed vertical slice).
|
||||
if (search_results.net &&
|
||||
search_results.net->data_type()==IVL_VT_AARRAY &&
|
||||
search_results.path_head.back().index.size()==0) {
|
||||
|
||||
perm_string method_name = search_results.path_tail.back().name;
|
||||
|
||||
if (method_name == "size" || method_name == "num") {
|
||||
if (parms_.size() != 0) {
|
||||
cerr << get_fileline() << ": error: " << method_name
|
||||
<< "() method takes no arguments" << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1);
|
||||
sys_expr->set_line(*this);
|
||||
sys_expr->parm(0, sub_expr);
|
||||
return sys_expr;
|
||||
}
|
||||
|
||||
if (method_name == "exists") {
|
||||
if (parms_.size() != 1) {
|
||||
cerr << get_fileline() << ": error: exists() method "
|
||||
<< "takes one argument" << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc("$ivl_aarray_method$exists",
|
||||
&netvector_t::atom2u32, 2);
|
||||
sys_expr->set_line(*this);
|
||||
sys_expr->parm(0, sub_expr);
|
||||
if (parms_.size() >= 1 && parms_[0].parm) {
|
||||
NetExpr*key = elab_and_eval(des, scope, parms_[0].parm, -1);
|
||||
sys_expr->parm(1, key);
|
||||
} else {
|
||||
sys_expr->parm(1, 0);
|
||||
}
|
||||
return sys_expr;
|
||||
}
|
||||
|
||||
if (method_name == "key_at") {
|
||||
/* Internal helper for foreach lowering. */
|
||||
if (parms_.size() != 1) {
|
||||
cerr << get_fileline() << ": error: key_at() takes one argument"
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
NetESFunc*sys_expr = new NetESFunc("$ivl_aarray_method$key_at",
|
||||
&netstring_t::type_string, 2);
|
||||
sys_expr->set_line(*this);
|
||||
sys_expr->parm(0, sub_expr);
|
||||
if (parms_.size() >= 1 && parms_[0].parm) {
|
||||
NetExpr*idx = elab_and_eval(des, scope, parms_[0].parm, -1);
|
||||
sys_expr->parm(1, idx);
|
||||
} else {
|
||||
sys_expr->parm(1, 0);
|
||||
}
|
||||
return sys_expr;
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic array or queue methods when there is no index.
|
||||
if (search_results.net &&
|
||||
(search_results.net->data_type()==IVL_VT_DARRAY ||
|
||||
|
|
@ -4669,6 +4733,7 @@ bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, const NetNet
|
|||
case IVL_VT_STRING:
|
||||
case IVL_VT_DARRAY:
|
||||
case IVL_VT_QUEUE:
|
||||
case IVL_VT_AARRAY:
|
||||
dimensions += 1;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -6914,6 +6979,16 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope,
|
|||
return res;
|
||||
}
|
||||
|
||||
if (const netaarray_t*aarray = net->sig()->aarray_type()) {
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: "
|
||||
<< "Bit select of an associative array becomes NetESelect." << endl;
|
||||
}
|
||||
NetESelect*res = new NetESelect(net, mux, aarray->element_width(), aarray->element_type());
|
||||
res->set_line(*net);
|
||||
return res;
|
||||
}
|
||||
|
||||
// If the bit select is constant, then treat it similar
|
||||
// to the part select, so that I save the effort of
|
||||
// making a mux part in the netlist.
|
||||
|
|
@ -7455,6 +7530,31 @@ NetExpr* PENull::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const
|
|||
return tmp;
|
||||
}
|
||||
|
||||
unsigned PEAArrayKey::test_width(Design*, NetScope*, width_mode_t&)
|
||||
{
|
||||
expr_type_ = IVL_VT_NO_TYPE;
|
||||
expr_width_ = 1;
|
||||
min_width_ = 1;
|
||||
signed_flag_ = false;
|
||||
return expr_width_;
|
||||
}
|
||||
|
||||
NetExpr* PEAArrayKey::elaborate_expr(Design*des, NetScope*, ivl_type_t, unsigned) const
|
||||
{
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "PEAArrayKey used as an expression." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* PEAArrayKey::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const
|
||||
{
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "PEAArrayKey used as an expression." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode)
|
||||
{
|
||||
expr_type_ = IVL_VT_LOGIC;
|
||||
|
|
|
|||
10
elab_lval.cc
10
elab_lval.cc
|
|
@ -27,6 +27,7 @@
|
|||
# include "netstruct.h"
|
||||
# include "netclass.h"
|
||||
# include "netdarray.h"
|
||||
# include "netaarray.h"
|
||||
# include "netparray.h"
|
||||
# include "netvector.h"
|
||||
# include "netenum.h"
|
||||
|
|
@ -340,7 +341,7 @@ NetAssign_*PEIdent::elaborate_lval_var_(Design *des, NetScope *scope,
|
|||
|
||||
|
||||
if (use_sel == index_component_t::SEL_BIT) {
|
||||
if (reg->darray_type()) {
|
||||
if (reg->darray_type() || reg->aarray_type()) {
|
||||
NetAssign_*lv = new NetAssign_(reg);
|
||||
elaborate_lval_darray_bit_(des, scope, lv, is_force);
|
||||
return lv;
|
||||
|
|
@ -729,12 +730,13 @@ bool PEIdent::elaborate_lval_darray_bit_(Design*des,
|
|||
const name_component_t&name_tail = path_.back();
|
||||
ivl_assert(*this, !name_tail.index.empty());
|
||||
|
||||
// For now, only support single-dimension dynamic arrays.
|
||||
// For now, only support single-dimension dynamic/associative arrays.
|
||||
ivl_assert(*this, name_tail.index.size() == 1);
|
||||
|
||||
if ((lv->sig()->type()==NetNet::UNRESOLVED_WIRE) && !is_force) {
|
||||
ivl_assert(*this, lv->sig()->coerced_to_uwire());
|
||||
report_mixed_assignment_conflict_("darray word");
|
||||
report_mixed_assignment_conflict_(lv->sig()->aarray_type()
|
||||
? "aarray word" : "darray word");
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -743,7 +745,7 @@ bool PEIdent::elaborate_lval_darray_bit_(Design*des,
|
|||
ivl_assert(*this, index_tail.msb != 0);
|
||||
ivl_assert(*this, index_tail.lsb == 0);
|
||||
|
||||
// Evaluate the select expression...
|
||||
// Evaluate the select expression (integer for darray, string for aarray).
|
||||
NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
|
||||
lv->set_word(mux);
|
||||
|
|
|
|||
38
elab_type.cc
38
elab_type.cc
|
|
@ -23,6 +23,7 @@
|
|||
# include "netlist.h"
|
||||
# include "netclass.h"
|
||||
# include "netdarray.h"
|
||||
# include "netaarray.h"
|
||||
# include "netenum.h"
|
||||
# include "netqueue.h"
|
||||
# include "netparray.h"
|
||||
|
|
@ -343,6 +344,12 @@ static ivl_type_t elaborate_static_array_type(Design *des, const LineInfo &li,
|
|||
des->errors++;
|
||||
// Recover
|
||||
base_type = new netvector_t(IVL_VT_LOGIC);
|
||||
} else if (dynamic_cast<const netaarray_t*>(base_type)) {
|
||||
cerr << li.get_fileline() << ": sorry: "
|
||||
<< "array of associative array type is not yet supported."
|
||||
<< endl;
|
||||
des->errors++;
|
||||
base_type = new netvector_t(IVL_VT_LOGIC);
|
||||
}
|
||||
|
||||
ivl_type_t type = new netuarray_t(dims, base_type);
|
||||
|
|
@ -374,6 +381,37 @@ ivl_type_t elaborate_array_type(Design *des, NetScope *scope,
|
|||
type = elaborate_darray_check_type(des, li, type, "Dynamic array");
|
||||
type = new netdarray_t(type);
|
||||
continue;
|
||||
} else if (dynamic_cast<PENull*>(lidx) && dynamic_cast<PENull*>(ridx)) {
|
||||
// Associative array wildcard `[*]` — string-keyed in this slice.
|
||||
type = elaborate_static_array_type(des, li, type, dimensions);
|
||||
type = elaborate_darray_check_type(des, li, type, "Associative array");
|
||||
type = new netaarray_t(type);
|
||||
continue;
|
||||
} else if (dynamic_cast<PEAArrayKey*>(lidx)) {
|
||||
// Associative array via PEAArrayKey sentinel.
|
||||
type = elaborate_static_array_type(des, li, type, dimensions);
|
||||
type = elaborate_darray_check_type(des, li, type, "Associative array");
|
||||
type = new netaarray_t(type);
|
||||
continue;
|
||||
} else if (PETypename*tn = dynamic_cast<PETypename*>(lidx)) {
|
||||
// `[string]` arrives as PETypename via `'[' expression ']'`
|
||||
// (dedicated K_string rule conflicts with typename-as-expr).
|
||||
if (dynamic_cast<string_type_t*>(tn->get_type()) &&
|
||||
(ridx == 0 || dynamic_cast<PENull*>(ridx))) {
|
||||
type = elaborate_static_array_type(des, li, type, dimensions);
|
||||
type = elaborate_darray_check_type(des, li, type, "Associative array");
|
||||
type = new netaarray_t(type);
|
||||
continue;
|
||||
}
|
||||
cerr << li.get_fileline() << ": sorry: "
|
||||
<< "associative array key type is not yet supported "
|
||||
<< "(string keys only in this slice)."
|
||||
<< endl;
|
||||
des->errors++;
|
||||
type = elaborate_static_array_type(des, li, type, dimensions);
|
||||
type = elaborate_darray_check_type(des, li, type, "Associative array");
|
||||
type = new netaarray_t(type);
|
||||
continue;
|
||||
} else if (dynamic_cast<PENull*>(lidx)) {
|
||||
// Special case: Detect the mark for a QUEUE declaration,
|
||||
// which is the dimensions [null:max_idx].
|
||||
|
|
|
|||
98
elaborate.cc
98
elaborate.cc
|
|
@ -47,6 +47,7 @@
|
|||
# include "netenum.h"
|
||||
# include "netvector.h"
|
||||
# include "netdarray.h"
|
||||
# include "netaarray.h"
|
||||
# include "netqueue.h"
|
||||
# include "netparray.h"
|
||||
# include "netscalar.h"
|
||||
|
|
@ -3047,6 +3048,13 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
|
|||
|
||||
rv = elaborate_rval_(des, scope, use_lv_type);
|
||||
|
||||
} else if (const netaarray_t*atype = dynamic_cast<const netaarray_t*> (lv_net_type)) {
|
||||
ivl_assert(*this, lv->more==0);
|
||||
ivl_type_t use_lv_type = lv_net_type;
|
||||
if (lv->word())
|
||||
use_lv_type = atype->element_type();
|
||||
rv = elaborate_rval_(des, scope, use_lv_type);
|
||||
|
||||
} else if (const netuarray_t*utype = dynamic_cast<const netuarray_t*>(lv_net_type)) {
|
||||
ivl_assert(*this, lv->more==0);
|
||||
if (debug_elaborate) {
|
||||
|
|
@ -4042,6 +4050,12 @@ NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope,
|
|||
<< "method takes zero or one argument." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
} else if (net->aarray_type()) {
|
||||
if (nparms > 1) {
|
||||
cerr << get_fileline() << ": error: associative array delete() "
|
||||
<< "method takes zero or one argument." << endl;
|
||||
des->errors += 1;
|
||||
}
|
||||
} else if (nparms > 0) {
|
||||
cerr << get_fileline() << ": error: darray delete() "
|
||||
<< "method takes no arguments." << endl;
|
||||
|
|
@ -4507,6 +4521,22 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
|
|||
}
|
||||
}
|
||||
|
||||
// Associative array methods (string-keyed vertical slice).
|
||||
if (net->aarray_type()) {
|
||||
if (method_name == "delete") {
|
||||
static const std::vector<perm_string> parm_names = {
|
||||
perm_string::literal("index")
|
||||
};
|
||||
return elaborate_sys_task_method_(des, scope, net, method_name,
|
||||
"$ivl_aarray_method$delete",
|
||||
parm_names);
|
||||
} else if (method_name == "size" || method_name == "num") {
|
||||
return elaborate_method_func_(scope, net,
|
||||
&netvector_t::atom2s32,
|
||||
method_name, "$size");
|
||||
}
|
||||
}
|
||||
|
||||
if (net->queue_type()) {
|
||||
if (method_name == "push_back") {
|
||||
static const std::vector<perm_string> parm_names = {
|
||||
|
|
@ -6048,6 +6078,74 @@ NetProc* PForeach::elaborate(Design*des, NetScope*scope) const
|
|||
if (array_sig->unpacked_dimensions() >= index_vars_.size())
|
||||
return elaborate_static_array_(des, scope, dims);
|
||||
|
||||
// Associative array foreach: for (i=0; i<size; i++) { k = key_at(i); body }
|
||||
if (array_sig->aarray_type()) {
|
||||
if (index_vars_.size() != 1) {
|
||||
cerr << get_fileline() << ": sorry: "
|
||||
<< "Multi-index foreach loops not supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pform_name_t index_name;
|
||||
index_name.push_back(name_component_t(index_vars_[0]));
|
||||
NetNet*idx_sig = des->find_signal(scope, index_name);
|
||||
ivl_assert(*this, idx_sig);
|
||||
|
||||
// Hidden integer loop index
|
||||
netvector_t*idx_vec = new netvector_t(IVL_VT_BOOL, 31, 0);
|
||||
idx_vec->set_signed(true);
|
||||
NetNet*loop_idx = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::REG, idx_vec);
|
||||
loop_idx->set_line(*this);
|
||||
loop_idx->local_flag(true);
|
||||
|
||||
NetESignal*array_exp = new NetESignal(array_sig);
|
||||
array_exp->set_line(*this);
|
||||
NetESignal*loop_exp = new NetESignal(loop_idx);
|
||||
loop_exp->set_line(*this);
|
||||
|
||||
NetEConst*zero = make_const_val(0);
|
||||
zero->set_line(*this);
|
||||
|
||||
NetESFunc*size_expr = new NetESFunc("$size", &netvector_t::atom2u32, 1);
|
||||
size_expr->set_line(*this);
|
||||
size_expr->parm(0, array_exp);
|
||||
|
||||
NetEBComp*cond_expr = new NetEBComp('<', loop_exp, size_expr);
|
||||
cond_expr->set_line(*this);
|
||||
|
||||
NetESFunc*key_at = new NetESFunc("$ivl_aarray_method$key_at",
|
||||
&netstring_t::type_string, 2);
|
||||
key_at->set_line(*this);
|
||||
key_at->parm(0, new NetESignal(array_sig));
|
||||
key_at->parm(1, new NetESignal(loop_idx));
|
||||
|
||||
NetAssign_*key_lv = new NetAssign_(idx_sig);
|
||||
NetAssign*key_asgn = new NetAssign(key_lv, key_at);
|
||||
key_asgn->set_line(*this);
|
||||
|
||||
NetProc*sub;
|
||||
if (statement_)
|
||||
sub = statement_->elaborate(des, scope);
|
||||
else
|
||||
sub = new NetBlock(NetBlock::SEQU, 0);
|
||||
|
||||
NetBlock*body = new NetBlock(NetBlock::SEQU, 0);
|
||||
body->set_line(*this);
|
||||
body->append(key_asgn);
|
||||
body->append(sub);
|
||||
|
||||
NetAssign_*step_lv = new NetAssign_(loop_idx);
|
||||
NetEConst*step_val = make_const_val(1);
|
||||
NetAssign*step = new NetAssign(step_lv, '+', step_val);
|
||||
step->set_line(*this);
|
||||
|
||||
NetForLoop*stmt = new NetForLoop(loop_idx, zero, cond_expr, body, step);
|
||||
stmt->set_line(*this);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
// At this point, we know that the array is dynamic so we
|
||||
// handle that slightly differently, using run-time tests.
|
||||
|
||||
|
|
|
|||
|
|
@ -2195,6 +2195,7 @@ static bool get_array_info(const NetExpr*arg, long dim,
|
|||
switch (ptype->base_type()) {
|
||||
case IVL_VT_DARRAY:
|
||||
case IVL_VT_QUEUE:
|
||||
case IVL_VT_AARRAY:
|
||||
case IVL_VT_STRING:
|
||||
defer = true;
|
||||
return true;
|
||||
|
|
@ -2211,6 +2212,7 @@ static bool get_array_info(const NetExpr*arg, long dim,
|
|||
switch (sig->data_type()) {
|
||||
case IVL_VT_DARRAY:
|
||||
case IVL_VT_QUEUE:
|
||||
case IVL_VT_AARRAY:
|
||||
case IVL_VT_STRING:
|
||||
defer = true;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
// Associative-array vertical slice smoke test (string keys).
|
||||
// Also accepts int aa[*] as string-keyed AA in this first slice.
|
||||
module aa_string;
|
||||
int aa[string];
|
||||
int bb[string];
|
||||
int aa_star[*]; // documented: [*] is string-keyed in this slice
|
||||
int v, n, e;
|
||||
int pass;
|
||||
string k;
|
||||
int sum;
|
||||
|
||||
initial begin
|
||||
pass = 1;
|
||||
|
||||
aa["x"] = 1;
|
||||
aa["y"] = 2;
|
||||
aa["z"] = 3;
|
||||
v = aa["x"];
|
||||
if (v !== 1) begin
|
||||
$display("FAIL: aa[\"x\"] read got %0d", v);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
n = aa.size();
|
||||
if (n !== 3) begin
|
||||
$display("FAIL: size() expected 3 got %0d", n);
|
||||
pass = 0;
|
||||
end
|
||||
n = aa.num();
|
||||
if (n !== 3) begin
|
||||
$display("FAIL: num() expected 3 got %0d", n);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
e = aa.exists("x");
|
||||
if (e !== 1) begin
|
||||
$display("FAIL: exists(\"x\") expected 1 got %0d", e);
|
||||
pass = 0;
|
||||
end
|
||||
e = aa.exists("missing");
|
||||
if (e !== 0) begin
|
||||
$display("FAIL: exists(\"missing\") expected 0 got %0d", e);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
sum = 0;
|
||||
foreach (aa[k]) begin
|
||||
sum = sum + aa[k];
|
||||
end
|
||||
if (sum !== 6) begin
|
||||
$display("FAIL: foreach sum expected 6 got %0d", sum);
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
bb = aa;
|
||||
if (bb.size() !== 3 || bb["y"] !== 2) begin
|
||||
$display("FAIL: whole-array copy");
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
aa.delete("x");
|
||||
if (aa.exists("x") !== 0 || aa.size() !== 2) begin
|
||||
$display("FAIL: delete(\"x\")");
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
aa.delete();
|
||||
if (aa.size() !== 0) begin
|
||||
$display("FAIL: delete() clear all, size=%0d", aa.size());
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
aa_star["hello"] = 42;
|
||||
if (aa_star["hello"] !== 42 || aa_star.size() !== 1) begin
|
||||
$display("FAIL: [*] string-keyed AA");
|
||||
pass = 0;
|
||||
end
|
||||
|
||||
if (pass) $display("PASSED");
|
||||
else $display("FAILED");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -467,6 +467,7 @@ typedef enum ivl_variable_type_e ENUM_UNSIGNED_INT {
|
|||
IVL_VT_DARRAY = 6, /* Array (esp. dynamic array) */
|
||||
IVL_VT_CLASS = 7, /* SystemVerilog class instances */
|
||||
IVL_VT_QUEUE = 8, /* SystemVerilog queue instances */
|
||||
IVL_VT_AARRAY = 9, /* SystemVerilog associative array */
|
||||
IVL_VT_VECTOR = IVL_VT_LOGIC /* For compatibility */
|
||||
} ivl_variable_type_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Icarus UVM track
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
# include "netaarray.h"
|
||||
# include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
netaarray_t::netaarray_t(ivl_type_t vec)
|
||||
: netarray_t(vec)
|
||||
{
|
||||
}
|
||||
|
||||
netaarray_t::~netaarray_t()
|
||||
{
|
||||
}
|
||||
|
||||
ivl_variable_type_t netaarray_t::base_type(void) const
|
||||
{
|
||||
return IVL_VT_AARRAY;
|
||||
}
|
||||
|
||||
bool netaarray_t::test_equivalence(ivl_type_t that) const
|
||||
{
|
||||
const netaarray_t* that_aa = dynamic_cast<const netaarray_t*>(that);
|
||||
if (!that_aa) return false;
|
||||
return test_compatibility(that);
|
||||
}
|
||||
|
||||
bool netaarray_t::test_compatibility(ivl_type_t that) const
|
||||
{
|
||||
const netaarray_t *that_aa = dynamic_cast<const netaarray_t*>(that);
|
||||
if (!that_aa)
|
||||
return false;
|
||||
|
||||
return element_type()->type_equivalent(that_aa->element_type());
|
||||
}
|
||||
|
||||
ostream& netaarray_t::debug_dump(ostream&out) const
|
||||
{
|
||||
out << "aarray<" << *element_type() << ">[string]";
|
||||
return out;
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef IVL_netaarray_H
|
||||
#define IVL_netaarray_H
|
||||
/*
|
||||
* Copyright (c) 2026 Icarus UVM track
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
# include "nettypes.h"
|
||||
# include "ivl_target.h"
|
||||
|
||||
/*
|
||||
* Associative array type.
|
||||
*
|
||||
* First vertical slice (UVM Tier A #2): string-keyed arrays only.
|
||||
* Both `int aa[string];` and `int aa[*];` elaborate to this type with
|
||||
* string keys. Integer keys, class elements, and nested AAs are deferred.
|
||||
*/
|
||||
class netaarray_t : public netarray_t {
|
||||
|
||||
public:
|
||||
explicit netaarray_t(ivl_type_t vec);
|
||||
~netaarray_t() override;
|
||||
|
||||
ivl_variable_type_t base_type() const override;
|
||||
|
||||
inline bool get_signed() const override
|
||||
{ return element_type()->get_signed(); }
|
||||
|
||||
inline ivl_variable_type_t element_base_type() const
|
||||
{ return element_type()->base_type(); }
|
||||
|
||||
inline unsigned long element_width(void) const
|
||||
{ return element_type()->packed_width(); }
|
||||
|
||||
std::ostream& debug_dump(std::ostream&) const override;
|
||||
|
||||
private:
|
||||
bool test_compatibility(ivl_type_t that) const override;
|
||||
bool test_equivalence(ivl_type_t that) const override;
|
||||
};
|
||||
|
||||
#endif /* IVL_netaarray_H */
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
# include "netmisc.h"
|
||||
# include "netclass.h"
|
||||
# include "netdarray.h"
|
||||
# include "netaarray.h"
|
||||
# include "netenum.h"
|
||||
# include "netparray.h"
|
||||
# include "netscalar.h"
|
||||
|
|
@ -711,6 +712,11 @@ const netdarray_t* NetNet::darray_type(void) const
|
|||
return dynamic_cast<const netdarray_t*> (net_type_);
|
||||
}
|
||||
|
||||
const netaarray_t* NetNet::aarray_type(void) const
|
||||
{
|
||||
return dynamic_cast<const netaarray_t*> (net_type_);
|
||||
}
|
||||
|
||||
const netqueue_t* NetNet::queue_type(void) const
|
||||
{
|
||||
return dynamic_cast<const netqueue_t*> (net_type_);
|
||||
|
|
@ -2498,6 +2504,8 @@ ivl_variable_type_t NetESignal::expr_type() const
|
|||
{
|
||||
if (net_->darray_type())
|
||||
return IVL_VT_DARRAY;
|
||||
if (net_->aarray_type())
|
||||
return IVL_VT_AARRAY;
|
||||
else
|
||||
return net_->data_type();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class data_type_t;
|
|||
struct enum_type_t;
|
||||
class netclass_t;
|
||||
class netdarray_t;
|
||||
class netaarray_t;
|
||||
class netparray_t;
|
||||
class netuarray_t;
|
||||
class netqueue_t;
|
||||
|
|
@ -756,6 +757,7 @@ class NetNet : public NetObj, public PortType {
|
|||
const netenum_t*enumeration(void) const;
|
||||
const netstruct_t*struct_type(void) const;
|
||||
const netdarray_t*darray_type(void) const;
|
||||
const netaarray_t*aarray_type(void) const;
|
||||
const netqueue_t*queue_type(void) const;
|
||||
const netclass_t*class_type(void) const;
|
||||
const netarray_t*array_type(void) const;
|
||||
|
|
|
|||
|
|
@ -1005,6 +1005,10 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
|
|||
if (dynamic_cast<PEAssignPattern*>(pe))
|
||||
return tmp;
|
||||
// fall through
|
||||
case IVL_VT_AARRAY:
|
||||
if (expr_type == IVL_VT_AARRAY)
|
||||
return tmp;
|
||||
break;
|
||||
case IVL_VT_STRING:
|
||||
if (dynamic_cast<PEConcat*>(pe))
|
||||
return tmp;
|
||||
|
|
|
|||
10
parse.y
10
parse.y
|
|
@ -2252,7 +2252,7 @@ loop_statement /* IEEE1800-2005: A.6.8 */
|
|||
PBlock*tmp = pform_push_block_scope(@1, for_block_name, PBlock::BL_SEQ);
|
||||
current_block_stack.push(tmp);
|
||||
|
||||
pform_make_foreach_declarations(@1, $5);
|
||||
pform_make_foreach_declarations(@1, $3, $5);
|
||||
}
|
||||
statement_or_null
|
||||
{ PForeach*tmp_for = pform_make_foreach(@1, $3, $5, $9);
|
||||
|
|
@ -3203,6 +3203,14 @@ variable_dimension /* IEEE1800-2005: A.2.5 */
|
|||
tmp->push_back(index);
|
||||
$$ = tmp;
|
||||
}
|
||||
| '[' '*' ']'
|
||||
{ // Associative array wildcard key. First slice: string-keyed.
|
||||
std::list<pform_range_t> *tmp = new std::list<pform_range_t>;
|
||||
pform_range_t index (new PENull, new PENull);
|
||||
pform_requires_sv(@$, "Associative array declaration");
|
||||
tmp->push_back(index);
|
||||
$$ = tmp;
|
||||
}
|
||||
| '[' '$' ']'
|
||||
{ // SystemVerilog queue
|
||||
list<pform_range_t> *tmp = new std::list<pform_range_t>;
|
||||
|
|
|
|||
23
pform.cc
23
pform.cc
|
|
@ -1032,6 +1032,7 @@ void pform_make_var(const struct vlltype&loc,
|
|||
}
|
||||
|
||||
void pform_make_foreach_declarations(const struct vlltype&loc,
|
||||
char*array_name,
|
||||
std::list<perm_string>*loop_vars)
|
||||
{
|
||||
list<decl_assignment_t*>assign_list;
|
||||
|
|
@ -1044,7 +1045,27 @@ void pform_make_foreach_declarations(const struct vlltype&loc,
|
|||
assign_list.push_back(tmp_assign);
|
||||
}
|
||||
|
||||
pform_make_var(loc, &assign_list, &size_type);
|
||||
/* Associative arrays (string / [*] keys in this slice) need string
|
||||
foreach index variables. Look up the array in enclosing scopes. */
|
||||
data_type_t*idx_type = &size_type;
|
||||
perm_string aname = lex_strings.make(array_name);
|
||||
for (LexicalScope*scp = lexical_scope ? lexical_scope->parent_scope() : 0;
|
||||
scp; scp = scp->parent_scope()) {
|
||||
PWire*wire = scp->wires_find(aname);
|
||||
if (!wire) continue;
|
||||
const list<pform_range_t>&udims = wire->get_unpacked_idx();
|
||||
if (udims.empty()) break;
|
||||
const pform_range_t&r = udims.front();
|
||||
if ((dynamic_cast<PENull*>(r.first) && dynamic_cast<PENull*>(r.second)) ||
|
||||
dynamic_cast<PEAArrayKey*>(r.first) ||
|
||||
dynamic_cast<PETypename*>(r.first)) {
|
||||
static string_type_t aa_string_idx;
|
||||
idx_type = &aa_string_idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pform_make_var(loc, &assign_list, idx_type);
|
||||
}
|
||||
|
||||
PForeach* pform_make_foreach(const struct vlltype&loc,
|
||||
|
|
|
|||
1
pform.h
1
pform.h
|
|
@ -346,6 +346,7 @@ extern PCallTask* pform_make_call_task(const struct vlltype&loc,
|
|||
const std::list<named_pexpr_t> &parms);
|
||||
|
||||
extern void pform_make_foreach_declarations(const struct vlltype&loc,
|
||||
char*array_name,
|
||||
std::list<perm_string>*loop_vars);
|
||||
extern PForeach* pform_make_foreach(const struct vlltype&loc,
|
||||
char*ident,
|
||||
|
|
|
|||
|
|
@ -506,6 +506,14 @@ void PENull::dump(ostream&out) const
|
|||
out << "null";
|
||||
}
|
||||
|
||||
void PEAArrayKey::dump(ostream&out) const
|
||||
{
|
||||
if (kind_ == STRING)
|
||||
out << "string";
|
||||
else
|
||||
out << "*";
|
||||
}
|
||||
|
||||
void PENumber::dump(ostream&out) const
|
||||
{
|
||||
out << value();
|
||||
|
|
|
|||
|
|
@ -93,6 +93,16 @@ typedef std::pair<perm_string, unsigned> pform_ident_t;
|
|||
* [ $ ] -- Queue type
|
||||
* first = PENull
|
||||
* second = 0
|
||||
*
|
||||
* [ * ] -- Associative array (wildcard key; this slice treats as string)
|
||||
* first = PENull
|
||||
* second = PENull
|
||||
*
|
||||
* [ string ] -- Associative array with string keys
|
||||
* first = PETypename(string_type_t) (via '[' expression ']')
|
||||
* second = 0
|
||||
*
|
||||
* PEAArrayKey may also appear as first for AA sentinels.
|
||||
*/
|
||||
typedef std::pair<PExpr*,PExpr*> pform_range_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,9 @@ const char*data_type_string(ivl_variable_type_t vtype)
|
|||
case IVL_VT_QUEUE:
|
||||
vt = "queue";
|
||||
break;
|
||||
case IVL_VT_AARRAY:
|
||||
vt = "aarray";
|
||||
break;
|
||||
}
|
||||
|
||||
return vt;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ void show_net_type(ivl_type_t net_type)
|
|||
case IVL_VT_QUEUE:
|
||||
show_net_type_queue(net_type);
|
||||
break;
|
||||
case IVL_VT_AARRAY:
|
||||
fprintf(out, "aarray");
|
||||
break;
|
||||
case IVL_VT_VOID:
|
||||
fprintf(out, "void");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -218,7 +218,14 @@ void draw_eval_string(ivl_expr_t expr)
|
|||
string_ex_pop(expr);
|
||||
else if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_front")==0)
|
||||
string_ex_pop(expr);
|
||||
else
|
||||
else if (strcmp(ivl_expr_name(expr), "$ivl_aarray_method$key_at")==0) {
|
||||
ivl_expr_t arr = ivl_expr_parm(expr, 0);
|
||||
ivl_expr_t idx = ivl_expr_parm(expr, 1);
|
||||
assert(ivl_expr_type(arr) == IVL_EX_SIGNAL);
|
||||
draw_eval_expr_into_integer(idx, 3);
|
||||
fprintf(vvp_out, " %%aar/key_at v%p_0;\n",
|
||||
ivl_expr_signal(arr));
|
||||
} else
|
||||
draw_sfunc_string(expr);
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -1013,6 +1013,19 @@ static void draw_select_vec4(ivl_expr_t expr)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ivl_expr_value(subexpr) == IVL_VT_AARRAY) {
|
||||
ivl_signal_t sig = ivl_expr_signal(subexpr);
|
||||
assert(sig);
|
||||
assert(ivl_signal_data_type(sig)==IVL_VT_AARRAY);
|
||||
assert(base);
|
||||
draw_eval_string(base);
|
||||
fprintf(vvp_out, " %%load/aar/vec4 v%p_0;\n", sig);
|
||||
if (ivl_expr_value(expr) == IVL_VT_BOOL) {
|
||||
fprintf(vvp_out, " %%cast2;\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_immediate_vec4_ok(base)) {
|
||||
unsigned long val0, valx;
|
||||
unsigned base_wid;
|
||||
|
|
@ -1110,6 +1123,16 @@ static void draw_sfunc_vec4(ivl_expr_t expr)
|
|||
return;
|
||||
}
|
||||
|
||||
if (strcmp(ivl_expr_name(expr), "$ivl_aarray_method$exists")==0 &&
|
||||
parm_count == 2) {
|
||||
ivl_expr_t arr = ivl_expr_parm(expr, 0);
|
||||
ivl_expr_t key = ivl_expr_parm(expr, 1);
|
||||
assert(ivl_expr_type(arr) == IVL_EX_SIGNAL);
|
||||
draw_eval_string(key);
|
||||
fprintf(vvp_out, " %%aar/exists v%p_0;\n", ivl_expr_signal(arr));
|
||||
return;
|
||||
}
|
||||
|
||||
/* find*_with has four parameters and is lowered in eval_object.c */
|
||||
if (parm_count == 4 &&
|
||||
strncmp(ivl_expr_name(expr), "$ivl_queue_method$",
|
||||
|
|
@ -1128,6 +1151,11 @@ static void draw_sfunc_vec4(ivl_expr_t expr)
|
|||
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
|
||||
return;
|
||||
}
|
||||
if (ivl_expr_type(arg) == IVL_EX_SIGNAL &&
|
||||
ivl_signal_data_type(ivl_expr_signal(arg)) == IVL_VT_AARRAY) {
|
||||
fprintf(vvp_out, " %%aar/size v%p_0;\n", ivl_expr_signal(arg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((strcmp(ivl_expr_name(expr), "$ivl_queue_method$sum") == 0 ||
|
||||
|
|
|
|||
|
|
@ -1162,6 +1162,59 @@ static int show_stmt_assign_sig_darray(ivl_statement_t net)
|
|||
return errors;
|
||||
}
|
||||
|
||||
/*
|
||||
* Associative array assignment (string-keyed vertical slice).
|
||||
* Indexed: evaluate value then string key, %store/aar/vec4.
|
||||
* Whole: duplicate object like darray.
|
||||
*/
|
||||
static int show_stmt_assign_sig_aarray(ivl_statement_t net)
|
||||
{
|
||||
int errors = 0;
|
||||
ivl_lval_t lval = ivl_stmt_lval(net, 0);
|
||||
ivl_expr_t rval = ivl_stmt_rval(net);
|
||||
ivl_signal_t var= ivl_lval_sig(lval);
|
||||
ivl_type_t var_type= ivl_signal_net_type(var);
|
||||
assert(ivl_type_base(var_type) == IVL_VT_AARRAY);
|
||||
ivl_type_t element_type = ivl_type_element(var_type);
|
||||
|
||||
assert(ivl_stmt_lvals(net) == 1);
|
||||
assert(ivl_lval_part_off(lval) == 0);
|
||||
|
||||
if (ivl_lval_idx(lval)) {
|
||||
ivl_expr_t mux = ivl_lval_idx(lval);
|
||||
assert(ivl_stmt_opcode(net) == 0);
|
||||
switch (ivl_type_base(element_type)) {
|
||||
case IVL_VT_BOOL:
|
||||
case IVL_VT_LOGIC:
|
||||
draw_eval_vec4(rval);
|
||||
resize_vec4_wid(rval, ivl_type_packed_width(element_type));
|
||||
draw_eval_string(mux);
|
||||
fprintf(vvp_out, " %%store/aar/vec4 v%p_0;\n", var);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s:%u: sorry: associative array element "
|
||||
"type not supported in this slice.\n",
|
||||
ivl_stmt_file(net), ivl_stmt_lineno(net));
|
||||
errors += 1;
|
||||
break;
|
||||
}
|
||||
} else if (ivl_expr_type(rval) == IVL_EX_SIGNAL) {
|
||||
assert(ivl_stmt_opcode(net) == 0);
|
||||
errors += draw_eval_object(rval);
|
||||
fprintf(vvp_out, " %%dup/obj;\n");
|
||||
fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: aarray copy\n",
|
||||
var, ivl_stmt_file(net), ivl_stmt_lineno(net));
|
||||
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
|
||||
} else {
|
||||
assert(ivl_stmt_opcode(net) == 0);
|
||||
errors += draw_eval_object(rval);
|
||||
fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: aarray assign\n",
|
||||
var, ivl_stmt_file(net), ivl_stmt_lineno(net));
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles the special case that we assign an array
|
||||
* pattern to a queue. Handle this by assigning each element.
|
||||
|
|
@ -1447,6 +1500,10 @@ int show_stmt_assign(ivl_statement_t net)
|
|||
return show_stmt_assign_sig_darray(net);
|
||||
}
|
||||
|
||||
if (sig && (ivl_signal_data_type(sig) == IVL_VT_AARRAY)) {
|
||||
return show_stmt_assign_sig_aarray(net);
|
||||
}
|
||||
|
||||
if (sig && (ivl_signal_data_type(sig) == IVL_VT_QUEUE)) {
|
||||
return show_stmt_assign_sig_queue(net);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1684,6 +1684,16 @@ static int show_delete_method(ivl_statement_t net)
|
|||
assert(ivl_expr_type(parm) == IVL_EX_SIGNAL);
|
||||
ivl_signal_t var = ivl_expr_signal(parm);
|
||||
|
||||
if (ivl_signal_data_type(var) == IVL_VT_AARRAY) {
|
||||
if (parm_count == 2) {
|
||||
draw_eval_string(ivl_stmt_parm(net, 1));
|
||||
fprintf(vvp_out, " %%delete/aar/str v%p_0;\n", var);
|
||||
} else {
|
||||
fprintf(vvp_out, " %%delete/aar v%p_0;\n", var);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If this is a queue then it can have an element to delete. */
|
||||
if (parm_count == 2) {
|
||||
if (ivl_type_base(ivl_signal_net_type(var)) != IVL_VT_QUEUE)
|
||||
|
|
@ -1938,6 +1948,9 @@ static int show_system_task_call(ivl_statement_t net)
|
|||
if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0)
|
||||
return show_delete_method(net);
|
||||
|
||||
if (strcmp(stmt_name,"$ivl_aarray_method$delete") == 0)
|
||||
return show_delete_method(net);
|
||||
|
||||
if (strcmp(stmt_name,"$ivl_darray_method$reverse") == 0)
|
||||
return show_reverse_method(net);
|
||||
|
||||
|
|
|
|||
|
|
@ -538,6 +538,15 @@ static void draw_reg_in_scope(ivl_signal_t sig)
|
|||
ivl_type_packed_width(element_type),
|
||||
ivl_signal_local(sig)? " Local signal" : "");
|
||||
|
||||
} else if (ivl_signal_data_type(sig) == IVL_VT_AARRAY) {
|
||||
ivl_type_t var_type = ivl_signal_net_type(sig);
|
||||
ivl_type_t element_type = ivl_type_element(var_type);
|
||||
|
||||
fprintf(vvp_out, "v%p_0 .var/aarray \"%s\", %u;%s\n", sig,
|
||||
vvp_mangle_name(ivl_signal_basename(sig)),
|
||||
ivl_type_packed_width(element_type),
|
||||
ivl_signal_local(sig)? " Local signal" : "");
|
||||
|
||||
} else if (ivl_signal_data_type(sig) == IVL_VT_QUEUE) {
|
||||
ivl_type_t var_type = ivl_signal_net_type(sig);
|
||||
ivl_type_t element_type = ivl_type_element(var_type);
|
||||
|
|
|
|||
|
|
@ -50,12 +50,13 @@ static PLI_INT32 dobject_size_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
|
|||
switch(vpi_get(vpiArrayType, arg)) {
|
||||
case vpiDynamicArray:
|
||||
case vpiQueueArray:
|
||||
case vpiAssocArray:
|
||||
break;
|
||||
default:
|
||||
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s argument must be a dynamic array, queue or "
|
||||
"string.\n", name);
|
||||
vpi_printf("%s argument must be a dynamic array, queue, "
|
||||
"associative array or string.\n", name);
|
||||
vpip_set_return_value(1);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 event.o logic.o delay.o \
|
||||
vvp_object.o vvp_cobject.o vvp_darray.o vvp_aarray.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 \
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ extern bool of_LOAD_REAL(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_LOAD_DAR_R(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_DAR_STR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_DAR_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AAR_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_PROP_DAR_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_OBJ(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_OBJA(vthread_t thr, vvp_code_t code);
|
||||
|
|
@ -236,6 +237,12 @@ extern bool of_SPLIT_VEC4(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_STORE_DAR_R(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_STORE_DAR_STR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_STORE_DAR_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_STORE_AAR_VEC4(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DELETE_AAR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DELETE_AAR_STR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_AAR_EXISTS(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_AAR_KEY_AT(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_AAR_SIZE(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_STORE_OBJ(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_STORE_OBJA(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_STORE_PROP_OBJ(vthread_t thr, vvp_code_t code);
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ struct opcode_table_s {
|
|||
};
|
||||
|
||||
static const struct opcode_table_s opcode_table[] = {
|
||||
{ "%aar/exists", of_AAR_EXISTS, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%aar/key_at", of_AAR_KEY_AT, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%aar/size", of_AAR_SIZE, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%abs/wr", of_ABS_WR, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%add", of_ADD, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%add/wr", of_ADD_WR, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
|
|
@ -155,6 +158,8 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%debug/thr", of_DEBUG_THR, 1,{OA_STRING, OA_NONE, OA_NONE} },
|
||||
{ "%delay", of_DELAY, 2, {OA_BIT1, OA_BIT2, OA_NONE} },
|
||||
{ "%delayx", of_DELAYX, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%delete/aar", of_DELETE_AAR, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%delete/aar/str",of_DELETE_AAR_STR,1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%delete/elem",of_DELETE_ELEM,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} },
|
||||
{ "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%delete/prop/elem", of_DELETE_PROP_ELEM, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
|
|
@ -206,6 +211,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%jmp/1xz",of_JMP1XZ, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} },
|
||||
{ "%join", of_JOIN, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%join/detach",of_JOIN_DETACH,1,{OA_NUMBER,OA_NONE, OA_NONE} },
|
||||
{ "%load/aar/vec4",of_LOAD_AAR_VEC4,1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%load/ar",of_LOAD_AR,2, {OA_ARR_PTR, OA_BIT1, OA_NONE} },
|
||||
{ "%load/dar/r", of_LOAD_DAR_R, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE}},
|
||||
{ "%load/dar/str",of_LOAD_DAR_STR, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
|
|
@ -340,6 +346,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%sort/obj", of_SORT_OBJ, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%sort/prop/obj", of_SORT_PROP_OBJ, 1, {OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%split/vec4", of_SPLIT_VEC4, 1,{OA_NUMBER, OA_NONE, OA_NONE} },
|
||||
{ "%store/aar/vec4",of_STORE_AAR_VEC4,1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%store/dar/r", of_STORE_DAR_R, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%store/dar/str", of_STORE_DAR_STR, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
{ "%store/dar/vec4",of_STORE_DAR_VEC4,1,{OA_FUNC_PTR, OA_NONE, OA_NONE} },
|
||||
|
|
|
|||
|
|
@ -511,6 +511,7 @@ extern void compile_variable(char*label, char*name,
|
|||
extern void compile_var_real(char*label, char*name, bool local_flag);
|
||||
extern void compile_var_string(char*label, char*name);
|
||||
extern void compile_var_darray(char*label, char*name, unsigned size);
|
||||
extern void compile_var_aarray(char*label, char*name, unsigned size);
|
||||
extern void compile_var_cobject(char*label, char*name);
|
||||
extern void compile_var_queue(char*label, char*name, unsigned size);
|
||||
|
||||
|
|
|
|||
|
|
@ -244,6 +244,7 @@ inline uint64_t strtouint64(const char*str, char**endptr, int base)
|
|||
".var" { return K_VAR; }
|
||||
".var/cobj" { return K_VAR_COBJECT; }
|
||||
".var/darray" { return K_VAR_DARRAY; }
|
||||
".var/aarray" { return K_VAR_AARRAY; }
|
||||
".var/queue" { return K_VAR_QUEUE; }
|
||||
".var/real" { return K_VAR_R; }
|
||||
".var/s" { return K_VAR_S; }
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ static struct __vpiModPath*modpath_dst = 0;
|
|||
%token K_SUBSTITUTE
|
||||
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP
|
||||
%token K_UFUNC_REAL K_UFUNC_VEC4 K_UFUNC_E K_UDP K_UDP_C K_UDP_S
|
||||
%token K_VAR K_VAR_COBJECT K_VAR_DARRAY
|
||||
%token K_VAR K_VAR_COBJECT K_VAR_DARRAY K_VAR_AARRAY
|
||||
%token K_VAR_QUEUE
|
||||
%token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U
|
||||
%token K_vpi_call K_vpi_call_w K_vpi_call_i
|
||||
|
|
@ -772,6 +772,9 @@ statement
|
|||
| T_LABEL K_VAR_DARRAY T_STRING ',' T_NUMBER ';'
|
||||
{ compile_var_darray($1, $3, $5); }
|
||||
|
||||
| T_LABEL K_VAR_AARRAY T_STRING ',' T_NUMBER ';'
|
||||
{ compile_var_aarray($1, $3, $5); }
|
||||
|
||||
| T_LABEL K_VAR_QUEUE T_STRING ',' T_NUMBER';'
|
||||
{ compile_var_queue($1, $3, $5); }
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
# include "vpi_priv.h"
|
||||
# include "vvp_net_sig.h"
|
||||
# include "vvp_darray.h"
|
||||
# include "vvp_aarray.h"
|
||||
# include "vvp_cobject.h"
|
||||
# include "array_common.h"
|
||||
# include "schedule.h"
|
||||
|
|
@ -294,6 +295,49 @@ vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net)
|
|||
return obj;
|
||||
}
|
||||
|
||||
class __vpiAarrayVar : public __vpiBaseVar {
|
||||
|
||||
public:
|
||||
__vpiAarrayVar(__vpiScope*sc, const char*na, vvp_net_t*ne)
|
||||
: __vpiBaseVar(sc, na, ne) { }
|
||||
|
||||
int get_type_code() const override { return vpiArrayVar; }
|
||||
|
||||
int vpi_get(int code) override {
|
||||
switch (code) {
|
||||
case vpiArrayType:
|
||||
return vpiAssocArray;
|
||||
case vpiSize: {
|
||||
const vvp_fun_signal_object*fun =
|
||||
dynamic_cast<const vvp_fun_signal_object*>(get_net()->fun);
|
||||
if (!fun) return 0;
|
||||
vvp_object_t val = fun->get_object();
|
||||
const vvp_aarray*aval = val.peek<vvp_aarray>();
|
||||
return aval ? (int)aval->get_size() : 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char* vpi_get_str(int code) override {
|
||||
if (code == vpiFile)
|
||||
return simple_set_rbuf_str(file_names[0]);
|
||||
return generic_get_str(code, scope_, name_, NULL);
|
||||
}
|
||||
|
||||
void vpi_get_value(p_vpi_value val) override {
|
||||
val->format = vpiSuppressVal;
|
||||
}
|
||||
};
|
||||
|
||||
vpiHandle vpip_make_aarray_var(const char*name, vvp_net_t*net)
|
||||
{
|
||||
__vpiScope*scope = vpip_peek_current_scope();
|
||||
const char*use_name = name ? vpip_name_string(name) : NULL;
|
||||
return new __vpiAarrayVar(scope, use_name, net);
|
||||
}
|
||||
|
||||
__vpiQueueVar::__vpiQueueVar(__vpiScope*sc, const char*na, vvp_net_t*ne)
|
||||
: __vpiDarrayVar(sc, na, ne)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -869,6 +869,7 @@ class __vpiDarrayVar : public __vpiBaseVar, public __vpiArrayBase {
|
|||
};
|
||||
|
||||
extern vpiHandle vpip_make_darray_var(const char*name, vvp_net_t*net);
|
||||
extern vpiHandle vpip_make_aarray_var(const char*name, vvp_net_t*net);
|
||||
|
||||
class __vpiQueueVar : public __vpiDarrayVar {
|
||||
|
||||
|
|
|
|||
121
vvp/vthread.cc
121
vvp/vthread.cc
|
|
@ -27,6 +27,7 @@
|
|||
# include "vvp_net_sig.h"
|
||||
# include "vvp_cobject.h"
|
||||
# include "vvp_darray.h"
|
||||
# include "vvp_aarray.h"
|
||||
# include "class_type.h"
|
||||
#ifdef CHECK_WITH_VALGRIND
|
||||
# include "vvp_cleanup.h"
|
||||
|
|
@ -6037,6 +6038,126 @@ bool of_STORE_DAR_VEC4(vthread_t thr, vvp_code_t cp)
|
|||
return store_dar<vvp_vector4_t>(thr, cp);
|
||||
}
|
||||
|
||||
static vvp_aarray* get_aarray(vvp_code_t cp)
|
||||
{
|
||||
vvp_net_t*net = cp->net;
|
||||
assert(net);
|
||||
vvp_fun_signal_object*obj = dynamic_cast<vvp_fun_signal_object*>(net->fun);
|
||||
assert(obj);
|
||||
return obj->get_object().peek<vvp_aarray>();
|
||||
}
|
||||
|
||||
/*
|
||||
* %store/aar/vec4 <var>
|
||||
* string key on string stack, vec4 value on vec4 stack.
|
||||
*/
|
||||
bool of_STORE_AAR_VEC4(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
string key = thr->pop_str();
|
||||
vvp_vector4_t value = thr->pop_vec4();
|
||||
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
if (!aa) {
|
||||
/* Lazily create if somehow missing. */
|
||||
vvp_net_t*net = cp->net;
|
||||
vvp_fun_signal_object*obj = dynamic_cast<vvp_fun_signal_object*>(net->fun);
|
||||
unsigned wid = obj ? obj->size() : value.size();
|
||||
vvp_object_t empty(new vvp_aarray_vec4(wid));
|
||||
vvp_net_ptr_t ptr(net, 0);
|
||||
vvp_send_object(ptr, empty, thr->wt_context);
|
||||
aa = empty.peek<vvp_aarray>();
|
||||
}
|
||||
aa->set_word(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %load/aar/vec4 <var>
|
||||
* string key on string stack; push vec4 value.
|
||||
*/
|
||||
bool of_LOAD_AAR_VEC4(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
string key = thr->pop_str();
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
vvp_vector4_t word;
|
||||
if (aa)
|
||||
aa->get_word(key, word);
|
||||
else {
|
||||
vvp_fun_signal_object*obj = dynamic_cast<vvp_fun_signal_object*>(cp->net->fun);
|
||||
word = vvp_vector4_t(obj ? obj->size() : 1, BIT4_X);
|
||||
}
|
||||
thr->push_vec4(word);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %delete/aar <var> — clear all entries
|
||||
*/
|
||||
bool of_DELETE_AAR(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
(void) thr;
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
if (aa) aa->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %delete/aar/str <var> — erase one key (string stack)
|
||||
*/
|
||||
bool of_DELETE_AAR_STR(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
string key = thr->pop_str();
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
if (aa) aa->erase(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %aar/exists <var> — pop string key, push 32-bit 0/1
|
||||
*/
|
||||
bool of_AAR_EXISTS(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
string key = thr->pop_str();
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
int present = (aa && aa->exists(key)) ? 1 : 0;
|
||||
vvp_vector4_t res(32, BIT4_0);
|
||||
if (present)
|
||||
res.set_bit(0, BIT4_1);
|
||||
thr->push_vec4(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %aar/key_at <var> — index in words[3], push string key
|
||||
*/
|
||||
bool of_AAR_KEY_AT(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
int64_t adr = thr->words[3].w_int;
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
string key;
|
||||
if (aa && adr >= 0)
|
||||
key = aa->key_at((size_t)adr);
|
||||
thr->push_str(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %aar/size <var> — push 32-bit entry count
|
||||
*/
|
||||
bool of_AAR_SIZE(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
vvp_aarray*aa = get_aarray(cp);
|
||||
size_t sz = aa ? aa->get_size() : 0;
|
||||
vvp_vector4_t val(32, BIT4_0);
|
||||
unsigned long ul = sz;
|
||||
for (unsigned idx = 0; idx < 32; idx++) {
|
||||
val.set_bit(idx, (ul & 1UL) ? BIT4_1 : BIT4_0);
|
||||
ul >>= 1;
|
||||
}
|
||||
thr->push_vec4(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool of_STORE_OBJ(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
/* set the value into port 0 of the destination. */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Icarus UVM track
|
||||
*/
|
||||
|
||||
# include "vvp_aarray.h"
|
||||
# include <cassert>
|
||||
|
||||
using namespace std;
|
||||
|
||||
vvp_aarray::~vvp_aarray()
|
||||
{
|
||||
}
|
||||
|
||||
void vvp_aarray::set_word(const string&, const vvp_vector4_t&)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void vvp_aarray::get_word(const string&, vvp_vector4_t&) const
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
vvp_aarray_vec4::vvp_aarray_vec4(unsigned word_wid)
|
||||
: word_wid_(word_wid)
|
||||
{
|
||||
}
|
||||
|
||||
vvp_aarray_vec4::~vvp_aarray_vec4()
|
||||
{
|
||||
}
|
||||
|
||||
size_t vvp_aarray_vec4::get_size() const
|
||||
{
|
||||
return map_.size();
|
||||
}
|
||||
|
||||
void vvp_aarray_vec4::clear()
|
||||
{
|
||||
map_.clear();
|
||||
}
|
||||
|
||||
bool vvp_aarray_vec4::exists(const string&key) const
|
||||
{
|
||||
return map_.find(key) != map_.end();
|
||||
}
|
||||
|
||||
void vvp_aarray_vec4::erase(const string&key)
|
||||
{
|
||||
map_.erase(key);
|
||||
}
|
||||
|
||||
void vvp_aarray_vec4::set_word(const string&key, const vvp_vector4_t&value)
|
||||
{
|
||||
vvp_vector4_t tmp = value;
|
||||
if (tmp.size() != word_wid_)
|
||||
tmp.resize(word_wid_);
|
||||
map_[key] = tmp;
|
||||
}
|
||||
|
||||
void vvp_aarray_vec4::get_word(const string&key, vvp_vector4_t&value) const
|
||||
{
|
||||
map<string,vvp_vector4_t>::const_iterator cur = map_.find(key);
|
||||
if (cur == map_.end()) {
|
||||
value = vvp_vector4_t(word_wid_, BIT4_X);
|
||||
return;
|
||||
}
|
||||
value = cur->second;
|
||||
}
|
||||
|
||||
string vvp_aarray_vec4::key_at(size_t idx) const
|
||||
{
|
||||
if (idx >= map_.size())
|
||||
return string();
|
||||
map<string,vvp_vector4_t>::const_iterator cur = map_.begin();
|
||||
for (size_t i = 0; i < idx; i += 1)
|
||||
++cur;
|
||||
return cur->first;
|
||||
}
|
||||
|
||||
void vvp_aarray_vec4::shallow_copy(const vvp_object*obj)
|
||||
{
|
||||
const vvp_aarray_vec4*that = dynamic_cast<const vvp_aarray_vec4*>(obj);
|
||||
assert(that);
|
||||
map_ = that->map_;
|
||||
word_wid_ = that->word_wid_;
|
||||
}
|
||||
|
||||
vvp_object* vvp_aarray_vec4::duplicate() const
|
||||
{
|
||||
vvp_aarray_vec4*obj = new vvp_aarray_vec4(word_wid_);
|
||||
obj->map_ = map_;
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef IVL_vvp_aarray_H
|
||||
#define IVL_vvp_aarray_H
|
||||
/*
|
||||
* Copyright (c) 2026 Icarus UVM track
|
||||
*
|
||||
* Associative-array runtime (string-keyed vertical slice).
|
||||
* Deferred: int keys, class elements, nested AAs.
|
||||
*/
|
||||
|
||||
# include "vvp_object.h"
|
||||
# include "vvp_net.h"
|
||||
# include <map>
|
||||
# include <string>
|
||||
# include <vector>
|
||||
|
||||
class vvp_aarray : public vvp_object {
|
||||
|
||||
public:
|
||||
inline vvp_aarray() { }
|
||||
virtual ~vvp_aarray() override;
|
||||
|
||||
virtual size_t get_size(void) const =0;
|
||||
virtual void clear(void) =0;
|
||||
|
||||
virtual bool exists(const std::string&key) const =0;
|
||||
virtual void erase(const std::string&key) =0;
|
||||
|
||||
virtual void set_word(const std::string&key, const vvp_vector4_t&value);
|
||||
virtual void get_word(const std::string&key, vvp_vector4_t&value) const;
|
||||
|
||||
virtual std::string key_at(size_t idx) const =0;
|
||||
};
|
||||
|
||||
class vvp_aarray_vec4 : public vvp_aarray {
|
||||
|
||||
public:
|
||||
explicit vvp_aarray_vec4(unsigned word_wid);
|
||||
~vvp_aarray_vec4() override;
|
||||
|
||||
size_t get_size(void) const override;
|
||||
void clear(void) override;
|
||||
bool exists(const std::string&key) const override;
|
||||
void erase(const std::string&key) override;
|
||||
|
||||
void set_word(const std::string&key, const vvp_vector4_t&value) override;
|
||||
void get_word(const std::string&key, vvp_vector4_t&value) const override;
|
||||
|
||||
std::string key_at(size_t idx) const override;
|
||||
|
||||
void shallow_copy(const vvp_object*obj) override;
|
||||
vvp_object* duplicate(void) const override;
|
||||
|
||||
unsigned word_wid() const { return word_wid_; }
|
||||
|
||||
private:
|
||||
std::map<std::string, vvp_vector4_t> map_;
|
||||
unsigned word_wid_;
|
||||
};
|
||||
|
||||
#endif /* IVL_vvp_aarray_H */
|
||||
31
vvp/words.cc
31
vvp/words.cc
|
|
@ -21,6 +21,7 @@
|
|||
# include "vpi_priv.h"
|
||||
# include "array.h"
|
||||
# include "vvp_net_sig.h"
|
||||
# include "vvp_aarray.h"
|
||||
# include "logic.h"
|
||||
# include "schedule.h"
|
||||
#ifdef CHECK_WITH_VALGRIND
|
||||
|
|
@ -143,6 +144,36 @@ void compile_var_queue(char*label, char*name, unsigned size)
|
|||
delete[] name;
|
||||
}
|
||||
|
||||
void compile_var_aarray(char*label, char*name, unsigned size)
|
||||
{
|
||||
vvp_net_t*net = new vvp_net_t;
|
||||
|
||||
if (vpip_peek_current_scope()->is_automatic()) {
|
||||
vvp_fun_signal_object_aa*tmp = new vvp_fun_signal_object_aa(size);
|
||||
net->fil = tmp;
|
||||
net->fun = tmp;
|
||||
} else {
|
||||
net->fil = 0;
|
||||
net->fun = new vvp_fun_signal_object_sa(size);
|
||||
}
|
||||
|
||||
define_functor_symbol(label, net);
|
||||
|
||||
/* Immediately bind an empty aarray so methods work without `new`. */
|
||||
{
|
||||
vvp_object_t empty(new vvp_aarray_vec4(size));
|
||||
vvp_net_ptr_t ptr(net, 0);
|
||||
vvp_send_object(ptr, empty, 0);
|
||||
}
|
||||
|
||||
vpiHandle obj = vpip_make_aarray_var(name, net);
|
||||
compile_vpi_symbol(label, obj);
|
||||
|
||||
vpip_attach_to_current_scope(obj);
|
||||
free(label);
|
||||
delete[] name;
|
||||
}
|
||||
|
||||
void compile_var_cobject(char*label, char*name)
|
||||
{
|
||||
vvp_net_t*net = new vvp_net_t;
|
||||
|
|
|
|||
Loading…
Reference in New Issue