Compare commits

...
17 Commits
Author SHA1 Message Date
Stephen Williams 3c2fb6a601 Fix dynamic array assignment to make a copy of the rvalue.
IEEE Std 1800-2017 Section 7.6 Array assignments

Assignment of a dynamic array creates a duplicate of the source,
so that assignments to the copy don't impact the original. Handle
all sorts of dynamic array base types.
2020-11-26 12:52:06 -08:00
Stephen Williams 7277f4e807 Merge pull request #394 from steveicarus/super-new-handling
Fixes for issues #387 and #390
2020-11-22 16:48:45 -08:00
Stephen Williams 919fd22a79 Handle the special case that constructor only chains.
Fix the case that a constructure is only a chaining constructor,
with no other content.
2020-11-22 16:13:01 -08:00
Stephen Williams 156644d91e Detect and complain about some constructor chain errors
This.new is not allowed.

super.new beyond the first statement is not allowed.

And while I'm at it, clean up the use of "@" and "#" in
the code as tokens for this and super.
2020-11-22 15:31:40 -08:00
Cary R 292d174cad Add support for an empty ';' in the description text 2020-11-20 21:42:39 -08:00
Cary R b14a623eef Update module items to include just a ';' 2020-11-20 20:48:55 -08:00
Martin Whitaker 1b3f0dd689 Add enumeration assignment compatibility check for continuous assignments. 2020-11-20 17:34:55 +00:00
Martin Whitaker 0fada92389 Fix expression type for packed struct member access (GitHub issue #386)
A NetESelect is used for accessing packed struct members and also for
accessing dynamic array elements. In these cases the expr_type() and
enumeration() methods should reflect the member/element type.
2020-11-20 16:50:11 +00:00
Cary R ad862020bb Move GNU lesser to tgt-vvp since that is the only place where LGPL code is located 2020-11-14 19:03:27 -08:00
Cary R d209e7533a Update some queue code since deques support random access 2020-11-14 17:09:51 -08:00
Martin Whitaker d6e01d0c55 Fix assertion failure when no value supplied with -P option (GitHub issue #377) 2020-10-24 22:48:00 +01:00
Martin Whitaker 359b2b65c2 Support escaped identifiers as macro names. 2020-10-09 11:38:16 +01:00
Martin Whitaker 6566072741 Fix GitHub issue #374 - ordering of `` and embedded macro expansion.
The IEEE standard does not clearly state whether the `` directive is applied
before or after embedded macros are expanded. Other simulators vary in their
behaviour. For maximum compatibility, this fix adopts the behaviour found in
Verilator, where `prefix``suffix expands to <prefix>suffix if prefix is a
defined macro, otherwise it expands to <prefixsuffix> (where <...> is the
expanded macro text). Other simulators show this behaviour in at least some
circumstances.
2020-10-09 08:09:04 +01:00
Martin Whitaker 8f8737198c Bump version to 12.0 in vvp/examples/*.vvp. 2020-10-03 10:57:25 +01:00
Martin Whitaker 4e79c1c861 Bump major version to 12. 2020-10-03 10:13:45 +01:00
Martin Whitaker 6880b39770 Refactor task declaration parsing and fix warning for empty port list.
1364-2005 and later allow a task declaration with an empty port list.
2020-10-03 09:30:51 +01:00
Martin Whitaker c4883da334 Fix width of localparam created from genvar when using -gstrict-expr-width. 2020-10-01 11:33:24 +01:00
37 changed files with 333 additions and 249 deletions
+3 -2
View File
@@ -43,7 +43,7 @@ void PFunction::set_statement(Statement*s)
void PFunction::push_statement_front(Statement*stmt)
{
// This can only happen after the statement is initially set.
// This should not be possible.
ivl_assert(*this, statement_);
// Get the PBlock of the statement. If it is not a PBlock,
@@ -75,7 +75,8 @@ PChainConstructor* PFunction::extract_chain_constructor()
PChainConstructor*res = 0;
if ((res = dynamic_cast<PChainConstructor*> (statement_))) {
statement_ = 0;
statement_ = new PBlock(PBlock::BL_SEQ);
statement_->set_line(*this);
} else if (PBlock*blk = dynamic_cast<PBlock*>(statement_)) {
res = blk->extract_chain_constructor();
+20 -16
View File
@@ -1958,6 +1958,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
unsigned long use_width = struct_type->packed_width();
pform_name_t completed_path;
ivl_type_t member_type = 0;
do {
const name_component_t member_comp = member_path.front();
const perm_string&member_name = member_comp.name;
@@ -1985,28 +1986,29 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
des->errors += 1;
return 0;
}
member_type = member->net_type;
if (debug_elaborate) {
cerr << li->get_fileline() << ": check_for_struct_members: "
<< "Member type: " << *(member->net_type)
<< " (" << typeid(*(member->net_type)).name() << ")"
<< "Member type: " << *member_type
<< " (" << typeid(*member_type).name() << ")"
<< endl;
}
off += tmp_off;
ivl_assert(*li, use_width >= (unsigned long)member->net_type->packed_width());
use_width = member->net_type->packed_width();
ivl_assert(*li, use_width >= (unsigned long)member_type->packed_width());
use_width = member_type->packed_width();
// At this point, off and use_width are the part select
// expressed by the member_comp, which is a member of the
// struct. We can further refine the part select with any
// indices that might be present.
if (const netstruct_t*tmp_struct = dynamic_cast<const netstruct_t*>(member->net_type)) {
if (const netstruct_t*tmp_struct = dynamic_cast<const netstruct_t*>(member_type)) {
// If the member is itself a struct, then get
// ready to go on to the next iteration.
struct_type = tmp_struct;
} else if (const netenum_t*tmp_enum = dynamic_cast<const netenum_t*> (member->net_type)) {
} else if (const netenum_t*tmp_enum = dynamic_cast<const netenum_t*> (member_type)) {
// If the element is an enum, then we don't have
// anything special to do.
@@ -2017,7 +2019,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
}
struct_type = 0;
} else if (const netvector_t*mem_vec = dynamic_cast<const netvector_t*>(member->net_type)) {
} else if (const netvector_t*mem_vec = dynamic_cast<const netvector_t*>(member_type)) {
if (debug_elaborate) {
cerr << li->get_fileline() << ": check_for_struct_members: "
@@ -2104,7 +2106,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
// there is no next struct type.
struct_type = 0;
} else if (const netparray_t*array = dynamic_cast<const netparray_t*>(member->net_type)) {
} else if (const netparray_t*array = dynamic_cast<const netparray_t*>(member_type)) {
// If the member is a parray, then the elements
// are themselves packed object, including
@@ -2178,7 +2180,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
} else {
// Unknown type?
cerr << li->get_fileline() << ": internal error: "
<< "Unexpected member type? " << *(member->net_type)
<< "Unexpected member type? " << *member_type
<< endl;
des->errors += 1;
struct_type = 0;
@@ -2224,7 +2226,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
NetESignal*sig = new NetESignal(net);
NetExpr *base = packed_base? packed_base : make_const_val(off);
NetESelect*sel = new NetESelect(sig, base, use_width);
NetESelect*sel = new NetESelect(sig, base, use_width, member_type);
if (debug_elaborate) {
cerr << li->get_fileline() << ": check_for_struct_member: "
@@ -2599,7 +2601,7 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope,
/* Add the implicit this reference when requested. */
if (add_this_flag) {
assert(use_path.empty());
use_path.push_front(name_component_t(perm_string::literal("@")));
use_path.push_front(name_component_t(perm_string::literal(THIS_TOKEN)));
}
// If there is no object to the left of the method name, then
@@ -3904,7 +3906,7 @@ NetExpr* PEIdent::elaborate_expr_class_member_(Design*des, NetScope*scope,
NetScope*scope_method = find_method_containing_scope(*this, scope);
ivl_assert(*this, scope_method);
NetNet*this_net = scope_method->find_signal(perm_string::literal("@"));
NetNet*this_net = scope_method->find_signal(perm_string::literal(THIS_TOKEN));
if (this_net == 0) {
cerr << get_fileline() << ": internal error: "
<< "Unable to find 'this' port of " << scope_path(scope_method)
@@ -5395,7 +5397,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope,
cerr << get_fileline() << ": debug: "
<< "Bit select of a dynamic array becomes NetESelect." << endl;
}
NetESelect*res = new NetESelect(net, mux, darray->element_width());
NetESelect*res = new NetESelect(net, mux, darray->element_width(), darray->element_type());
res->set_line(*net);
return res;
}
@@ -5597,13 +5599,15 @@ NetExpr* PEIdent::elaborate_expr_net_bit_last_(Design*, NetScope*,
}
unsigned use_width = 1;
ivl_type_t use_type = 0;
if (const netdarray_t*darray = net->sig()->darray_type()) {
use_width = darray->element_width();
use_type = darray->element_type();
}
NetELast*mux = new NetELast(net->sig());
mux->set_line(*this);
NetESelect*ss = new NetESelect(net, mux, use_width);
NetESelect*ss = new NetESelect(net, mux, use_width, use_type);
ss->set_line(*this);
return ss;
}
@@ -5762,7 +5766,7 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
// The return value of the initializer is the "this"
// variable, instead of the "new&" scope name.
NetNet*res1 = new1_scope->find_signal(perm_string::literal("@"));
NetNet*res1 = new1_scope->find_signal(perm_string::literal(THIS_TOKEN));
ivl_assert(*this, res1);
NetESignal*eres = new NetESignal(res1);
@@ -5849,7 +5853,7 @@ NetExpr* PENewClass::elaborate_expr_constructor_(Design*des, NetScope*scope,
// The return value for the constructor is actually the "this"
// variable, instead of the "new" scope name.
NetNet*res = new_scope->find_signal(perm_string::literal("@"));
NetNet*res = new_scope->find_signal(perm_string::literal(THIS_TOKEN));
ivl_assert(*this, res);
NetESignal*eres = new NetESignal(res);
+1 -1
View File
@@ -378,7 +378,7 @@ NetAssign_* PEIdent::elaborate_lval_method_class_member_(Design*des,
NetScope*scope_method = find_method_containing_scope(*this, scope);
ivl_assert(*this, scope_method);
NetNet*this_net = scope_method->find_signal(perm_string::literal("@"));
NetNet*this_net = scope_method->find_signal(perm_string::literal(THIS_TOKEN));
if (this_net == 0) {
cerr << get_fileline() << ": internal error: "
<< "Unable to find 'this' port of " << scope_path(scope_method)
+5 -1
View File
@@ -966,7 +966,11 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
// block. Code within this scope thus has access to the
// genvar as a constant.
{
verinum genvar_verinum(genvar);
verinum genvar_verinum;
if (gn_strict_expr_width_flag)
genvar_verinum = verinum(genvar, integer_width);
else
genvar_verinum = verinum(genvar);
genvar_verinum.has_sign(true);
NetEConstParam*gp = new NetEConstParam(scope,
loop_index,
+4 -4
View File
@@ -578,10 +578,10 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const
// source code for the definition may be:
// function new(...);
// endfunction
// In this case, the "@" port is the synthetic "this"
// argument and we also use it as a return value at the
// same time.
ret_sig = scope->find_signal(perm_string::literal("@"));
// In this case, the "@" port (THIS_TOKEN) is the synthetic
// "this" argument and we also use it as a return value at
// the same time.
ret_sig = scope->find_signal(perm_string::literal(THIS_TOKEN));
ivl_assert(*this, ret_sig);
if (debug_elaborate)
+33 -2
View File
@@ -110,6 +110,18 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
return;
}
if (lval->enumeration()) {
if (! rval_expr->enumeration()) {
cerr << get_fileline() << ": error: "
"This assignment requires an explicit cast." << endl;
des->errors += 1;
} else if (! lval->enumeration()->matches(rval_expr->enumeration())) {
cerr << get_fileline() << ": error: "
"Enumeration type mismatch in assignment." << endl;
des->errors += 1;
}
}
NetNet*rval = rval_expr->synthesize(des, scope, rval_expr);
if (rval == 0) {
@@ -2990,6 +3002,25 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) {
assert(list_[idx]);
// Detect the error that a super.new() statement is in the
// midst of a block. Report the error. Continue on with the
// elaboration so that other errors might be found.
if (PChainConstructor*supernew = dynamic_cast<PChainConstructor*> (list_[idx])) {
if (debug_elaborate) {
cerr << get_fileline() << ": PBlock::elaborate: "
<< "Found super.new statement, idx=" << idx << ", "
<< " at " << supernew->get_fileline() << "."
<< endl;
}
if (idx > 0) {
des->errors += 1;
cerr << supernew->get_fileline() << ": error: "
<< "super.new(...) must be the first statement in a block."
<< endl;
}
}
NetProc*tmp = list_[idx]->elaborate(des, nscope);
// If the statement fails to elaborate, then simply
// ignore it. Presumably, the elaborate for the
@@ -3254,7 +3285,7 @@ NetProc* PChainConstructor::elaborate(Design*des, NetScope*scope) const
// Need the "this" variable for the current constructor. We're
// going to pass this to the chained constructor.
NetNet*var_this = scope->find_signal(perm_string::literal("@"));
NetNet*var_this = scope->find_signal(perm_string::literal(THIS_TOKEN));
// If super.new is an implicit constructor, then there are no
// arguments (other than "this" to worry about, so make a
@@ -3716,7 +3747,7 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
/* Add the implicit this reference when requested. */
if (add_this_flag) {
assert(use_path.empty());
use_path.push_front(name_component_t(perm_string::literal("@")));
use_path.push_front(name_component_t(perm_string::literal(THIS_TOKEN)));
}
// There is no signal to search for so this cannot be a method.
+35 -5
View File
@@ -154,9 +154,9 @@ static void ifdef_leave(void)
result = (rc == 0) ? YY_NULL : rc; \
} else { \
/* We are expanding a macro. Handle the SV `` delimiter. \
If the delimiter terminates a compiler directive, leave \
If the delimiter terminates a defined macro usage, leave \
it in place, otherwise remove it now. */ \
if (yytext[0] != '`') { \
if (!(yytext[0] == '`' && is_defined(yytext+1))) { \
while ((istack->str[0] == '`') && \
(istack->str[1] == '`')) { \
istack->str += 2; \
@@ -186,9 +186,11 @@ static int ma_parenthesis_level = 0;
%x PPINCLUDE
%x DEF_NAME
%x DEF_ESC
%x DEF_ARG
%x DEF_SEP
%x DEF_TXT
%x MN_ESC
%x MA_START
%x MA_ADD
%x CCOMMENT
@@ -338,6 +340,11 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif)
<DEF_NAME>[a-zA-Z_][a-zA-Z0-9_$]*"("{W}? { BEGIN(DEF_ARG); def_start(); }
<DEF_NAME>[a-zA-Z_][a-zA-Z0-9_$]*{W}? { BEGIN(DEF_TXT); def_start(); }
<DEF_NAME>\\ { BEGIN(DEF_ESC); }
<DEF_ESC>[^ \t\b\f\n\r]+{W}"("{W}? { BEGIN(DEF_ARG); def_start(); }
<DEF_ESC>[^ \t\b\f\n\r]+{W} { BEGIN(DEF_TXT); def_start(); }
/* define arg: <name> = <text> */
<DEF_ARG>[a-zA-Z_][a-zA-Z0-9_$]*{W}*"="[^,\)]*{W}? { BEGIN(DEF_SEP); def_add_arg(); }
/* define arg: <name> */
@@ -352,7 +359,7 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif)
<DEF_ARG,DEF_SEP>(\n|"\r\n"|"\n\r"|\r){W}? { istack->lineno += 1; fputc('\n', yyout); }
<DEF_NAME,DEF_ARG,DEF_SEP>. {
<DEF_NAME,DEF_ESC,DEF_ARG,DEF_SEP>. {
emit_pathline(istack);
fprintf(stderr, "error: malformed `define directive.\n");
error_count += 1;
@@ -535,6 +542,24 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif)
do_expand(0);
}
`\\ { yy_push_state(MN_ESC); }
<MN_ESC>[^ \t\b\f\n\r]+ {
yy_pop_state();
if (macro_needs_args(yytext))
yy_push_state(MA_START);
else
do_expand(0);
}
<MN_ESC>. {
yy_pop_state();
unput(yytext[0]);
emit_pathline(istack);
fprintf(stderr, "error: malformed macro name.\n");
error_count += 1;
}
/* Stringified version of macro expansion. This is an Icarus extension.
When expanding macro text, the SV usage of `` takes precedence. */
``[a-zA-Z_][a-zA-Z0-9_$]* {
@@ -825,8 +850,13 @@ static void def_add_arg(void)
/* This can happen because we are also processing "argv[0]", the
macro name, as a pseudo-argument. The lexor will match that
as name(, so chop off the ( here. */
if (yytext[length - 1] == '(') length--;
as name(, so chop off the ( here. If we have an escaped name,
we also need to strip off the white space that terminates the
name. */
if (yytext[length - 1] == '(') {
length--;
while (isspace((int)yytext[length - 1])) length--;
}
yytext[length] = 0;
+17 -15
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2019 Stephen Williams (steve@icarus.com)
* Copyright (c) 2002-2020 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@@ -403,7 +403,14 @@ ivl_variable_type_t NetEProperty::expr_type() const
NetESelect::NetESelect(NetExpr*exp, NetExpr*base, unsigned wid,
ivl_select_type_t sel_type)
: expr_(exp), base_(base), sel_type_(sel_type)
: expr_(exp), base_(base), use_type_(0), sel_type_(sel_type)
{
expr_width(wid);
}
NetESelect::NetESelect(NetExpr*exp, NetExpr*base, unsigned wid,
ivl_type_t use_type)
: expr_(exp), base_(base), use_type_(use_type), sel_type_(IVL_SEL_OTHER)
{
expr_width(wid);
}
@@ -431,6 +438,9 @@ ivl_select_type_t NetESelect::select_type() const
ivl_variable_type_t NetESelect::expr_type() const
{
if (use_type_)
return use_type_->base_type();
ivl_variable_type_t type = expr_->expr_type();
// Special case: If the sub-expression is an IVL_VT_STRING,
@@ -439,20 +449,12 @@ ivl_variable_type_t NetESelect::expr_type() const
if (type == IVL_VT_STRING && expr_width()==8)
return IVL_VT_BOOL;
if (type != IVL_VT_DARRAY)
return type;
return type;
}
ivl_assert(*this, type == IVL_VT_DARRAY);
// Special case: If the expression is a DARRAY, then the
// sub-expression must be a NetESignal and the type of the
// NetESelect expression is the element type of the arrayed signal.
NetESignal*sig = dynamic_cast<NetESignal*>(expr_);
ivl_assert(*this, sig);
const netarray_t*array_type = dynamic_cast<const netarray_t*> (sig->sig()->net_type());
ivl_assert(*this, array_type);
return array_type->element_type()->base_type();
const netenum_t* NetESelect::enumeration() const
{
return dynamic_cast<const netenum_t*> (use_type_);
}
bool NetESelect::has_width() const
+11 -2
View File
@@ -4401,21 +4401,29 @@ class NetEConcat : public NetExpr {
* If the base expression is null, then this expression node can be
* used to express width expansion, signed or unsigned depending on
* the has_sign() flag.
*
* An alternative form of this expression node is used for dynamic
* array word selects and for packed struct member selects. In this
* case use_type indicates the type of the selected element/member.
*/
class NetESelect : public NetExpr {
public:
NetESelect(NetExpr*exp, NetExpr*base, unsigned wid,
ivl_select_type_t sel_type = IVL_SEL_OTHER);
NetESelect(NetExpr*exp, NetExpr*base, unsigned wid,
ivl_type_t use_type);
~NetESelect();
const NetExpr*sub_expr() const;
const NetExpr*select() const;
ivl_select_type_t select_type() const;
// The type of a NetESelect is the base type of the
// sub-expression.
// The type of a bit/part select is the base type of the
// sub-expression. The type of an array/member select is
// the base type of the element/member.
virtual ivl_variable_type_t expr_type() const;
virtual const netenum_t* enumeration() const;
virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false,
bool nested_func = false) const;
@@ -4431,6 +4439,7 @@ class NetESelect : public NetExpr {
private:
NetExpr*expr_;
NetExpr*base_;
ivl_type_t use_type_;
ivl_select_type_t sel_type_;
};
+53 -86
View File
@@ -68,7 +68,7 @@ static LexicalScope::lifetime_t var_lifetime;
static pform_name_t* pform_create_this(void)
{
name_component_t name (perm_string::literal("@"));
name_component_t name (perm_string::literal(THIS_TOKEN));
pform_name_t*res = new pform_name_t;
res->push_back(name);
return res;
@@ -76,7 +76,7 @@ static pform_name_t* pform_create_this(void)
static pform_name_t* pform_create_super(void)
{
name_component_t name (perm_string::literal("#"));
name_component_t name (perm_string::literal(SUPER_TOKEN));
pform_name_t*res = new pform_name_t;
res->push_back(name);
return res;
@@ -1364,6 +1364,8 @@ description /* IEEE1800-2005: A.1.2 */
delete[] $3;
delete[] $5;
}
| ';'
{ }
;
description_list
@@ -2321,7 +2323,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */
{ assert(current_task == 0);
current_task = pform_push_task_scope(@1, $3, $2);
}
tf_port_list ')' ';'
tf_port_list_opt ')' ';'
block_item_decls_opt
statement_or_null_list_opt
K_endtask
@@ -2329,6 +2331,10 @@ task_declaration /* IEEE1800-2005: A.2.7 */
current_task_set_statement(@3, $10);
pform_set_this_class(@3, current_task);
pform_pop_scope();
if (generation_flag < GN_VER2005 && $6 == 0) {
cerr << @3 << ": warning: task definition for \"" << $3
<< "\" has an empty port declaration list!" << endl;
}
current_task = 0;
if ($10) delete $10;
}
@@ -2351,46 +2357,6 @@ task_declaration /* IEEE1800-2005: A.2.7 */
delete[]$3;
}
| K_task lifetime_opt IDENTIFIER '(' ')' ';'
{ assert(current_task == 0);
current_task = pform_push_task_scope(@1, $3, $2);
}
block_item_decls_opt
statement_or_null_list
K_endtask
{ current_task->set_ports(0);
current_task_set_statement(@3, $9);
pform_set_this_class(@3, current_task);
if (! current_task->method_of()) {
cerr << @3 << ": warning: task definition for \"" << $3
<< "\" has an empty port declaration list!" << endl;
}
pform_pop_scope();
current_task = 0;
if ($9->size() > 1 && !gn_system_verilog()) {
yyerror(@9, "error: Task body with multiple statements requires SystemVerilog.");
}
delete $9;
}
endlabel_opt
{ // Last step: check any closing name. This is done late so
// that the parser can look ahead to detect the present
// endlabel_opt but still have the pform_endmodule() called
// early enough that the lexor can know we are outside the
// module.
if ($12) {
if (strcmp($3,$12) != 0) {
yyerror(@12, "error: End label doesn't match task name");
}
if (! gn_system_verilog()) {
yyerror(@12, "error: Task end labels require "
"SystemVerilog.");
}
delete[]$12;
}
delete[]$3;
}
| K_task lifetime_opt IDENTIFIER error K_endtask
{
if (current_task) {
@@ -5315,14 +5281,14 @@ module_item
{ pform_endgenerate(false); }
| generate_if
generate_block_opt
generate_block
K_else
{ pform_start_generate_else(@1); }
generate_block
{ pform_endgenerate(true); }
| generate_if
generate_block_opt %prec less_than_K_else
generate_block %prec less_than_K_else
{ pform_endgenerate(true); }
| K_case '(' expression ')'
@@ -5360,50 +5326,51 @@ module_item
module items. These rules try to catch them at a point where a
reasonable error message can be produced. */
| error ';'
{ yyerror(@2, "error: invalid module item.");
yyerrok;
}
| error ';'
{ yyerror(@2, "error: invalid module item.");
yyerrok;
}
| K_assign error '=' expression ';'
{ yyerror(@1, "error: syntax error in left side "
"of continuous assignment.");
yyerrok;
}
| K_assign error '=' expression ';'
{ yyerror(@1, "error: syntax error in left side of "
"continuous assignment.");
yyerrok;
}
| K_assign error ';'
{ yyerror(@1, "error: syntax error in "
"continuous assignment");
yyerrok;
}
| K_assign error ';'
{ yyerror(@1, "error: syntax error in continuous assignment");
yyerrok;
}
| K_function error K_endfunction endlabel_opt
{ yyerror(@1, "error: I give up on this "
"function definition.");
if ($4) {
if (!gn_system_verilog()) {
yyerror(@4, "error: Function end names require "
"SystemVerilog.");
}
delete[]$4;
}
yyerrok;
}
| K_function error K_endfunction endlabel_opt
{ yyerror(@1, "error: I give up on this function definition.");
if ($4) {
if (!gn_system_verilog()) {
yyerror(@4, "error: Function end names require "
"SystemVerilog.");
}
delete[]$4;
}
yyerrok;
}
/* These rules are for the Icarus Verilog specific $attribute
extensions. Then catch the parameters of the $attribute keyword. */
| KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' ';'
{ perm_string tmp3 = lex_strings.make($3);
perm_string tmp5 = lex_strings.make($5);
pform_set_attrib(tmp3, tmp5, $7);
delete[] $3;
delete[] $5;
}
| KK_attribute '(' error ')' ';'
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
| KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' ';'
{ perm_string tmp3 = lex_strings.make($3);
perm_string tmp5 = lex_strings.make($5);
pform_set_attrib(tmp3, tmp5, $7);
delete[] $3;
delete[] $5;
}
| KK_attribute '(' error ')' ';'
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
;
| ';'
{ }
;
module_item_list
: module_item_list module_item
@@ -5423,9 +5390,9 @@ generate_case_items
;
generate_case_item
: expression_list_proper ':' { pform_generate_case_item(@1, $1); } generate_block_opt
: expression_list_proper ':' { pform_generate_case_item(@1, $1); } generate_block
{ pform_endgenerate(false); }
| K_default ':' { pform_generate_case_item(@1, 0); } generate_block_opt
| K_default ':' { pform_generate_case_item(@1, 0); } generate_block
{ pform_endgenerate(false); }
;
@@ -5487,9 +5454,6 @@ generate_block
}
;
generate_block_opt : generate_block | ';' ;
/* A net declaration assignment allows the programmer to combine the
net declaration and the continuous assignment into a single
statement.
@@ -6933,6 +6897,9 @@ statement_item /* This is roughly statement_item in the LRM */
| implicit_class_handle '.' K_new '(' expression_list_with_nuls ')' ';'
{ PChainConstructor*tmp = new PChainConstructor(*$5);
FILE_NAME(tmp, @3);
if (peek_head_name(*$1) == THIS_TOKEN) {
yyerror(@1, "error: this.new is invalid syntax. Did you mean super.new?");
}
delete $1;
$$ = tmp;
}
+1 -1
View File
@@ -297,7 +297,7 @@ void parm_to_defparam_list(const string&param)
// Is it a decimal number?
num = (value[0] == '-') ? value + 1 : value;
if (is_dec_digit_str(num)) {
if (num[0] != '\0' && is_dec_digit_str(num)) {
verinum *val = make_unsized_dec(num);
if (value[0] == '-') *val = -(*val);
PExpr* ndec = new PENumber(val);
+1 -1
View File
@@ -109,7 +109,7 @@ void pform_set_this_class(const struct vlltype&loc, PTaskFunc*net)
return;
list<perm_string>*this_name = new list<perm_string>;
this_name->push_back(perm_string::literal("@"));
this_name->push_back(perm_string::literal(THIS_TOKEN));
vector<pform_tf_port_t>*this_port = pform_make_task_ports(loc,
NetNet::PINPUT,
pform_cur_class->type,
+8
View File
@@ -381,6 +381,14 @@ inline perm_string peek_tail_name(const pform_name_t&that)
return that.back().name;
}
/*
* In pform names, the "super" and "this" keywords are converted to
* These tokens so that they don't interfere with the namespace and
* are handled specially.
*/
# define SUPER_TOKEN "#"
# define THIS_TOKEN "@"
extern std::ostream& operator<< (std::ostream&out, const pform_name_t&);
extern std::ostream& operator<< (std::ostream&out, const name_component_t&that);
extern std::ostream& operator<< (std::ostream&out, const index_component_t&that);
+30 -6
View File
@@ -954,7 +954,7 @@ static int show_stmt_assign_darray_pattern(ivl_statement_t net)
// FIXME: At the moment we reallocate the array space.
// This probably should be a resize to avoid values glitching
/* Allocate at least enough space for the array patter. */
/* Allocate at least enough space for the array pattern. */
fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", size_reg, ivl_expr_parms(rval));
/* This can not have have a X/Z value so clear flag 4. */
fprintf(vvp_out, " %%flag_set/imm 4, 0;\n");
@@ -1038,12 +1038,36 @@ static int show_stmt_assign_sig_darray(ivl_statement_t net)
elements of the l-value. */
errors += show_stmt_assign_darray_pattern(net);
} else {
/* There is no l-value mux, so this must be an
assignment to the array as a whole. Evaluate the
"object", and store the evaluated result. */
} else if (ivl_expr_type(rval) == IVL_EX_NEW) {
// There is no l-value mux, and the r-value expression is
// a "new" expression. Handle this by simply storing the
// new object to the lval.
errors += draw_eval_object(rval);
fprintf(vvp_out, " %%store/obj v%p_0;\n", var);
fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: %s = new ...\n",
var, ivl_stmt_file(net), ivl_stmt_lineno(net),
ivl_signal_basename(var));
} else if (ivl_expr_type(rval) == IVL_EX_SIGNAL) {
// There is no l-value mux, and the r-value expression is
// a "signal" expression. Store a duplicate into the lvalue
// By using the %dup/obj. Remember to pop the rvalue that
// is no longer needed.
errors += draw_eval_object(rval);
fprintf(vvp_out, " %%dup/obj;\n");
fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: %s = <signal>\n",
var, ivl_stmt_file(net), ivl_stmt_lineno(net),
ivl_signal_basename(var));
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
} else {
// There is no l-value mux, so this must be an
// assignment to the array as a whole. Evaluate the
// "object", and store the evaluated result.
errors += draw_eval_object(rval);
fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: %s = <expr type %d>\n",
var, ivl_stmt_file(net), ivl_stmt_lineno(net),
ivl_signal_basename(var), ivl_expr_type(rval));
}
return errors;
+2 -2
View File
@@ -1,6 +1,6 @@
#norootforbuild
#
%define rev_date 20150815
%define rev_date 20201003
# Normally, the suff-ix is %nil, meaning the suffix is to not be used.
# But if the builder wants to make a suffixed package, he may set this
# to a value (i.e. -test) to cause suffixes to be put in all the right
@@ -10,7 +10,7 @@
#
Summary: Icarus Verilog
Name: verilog%{suff}
Version: 11.0.%{rev_date}
Version: 12.0.%{rev_date}
Release: 0
License: GPL
Group: Productivity/Scientific/Electronics
+1 -1
View File
@@ -3,7 +3,7 @@
* Edit this definition in version_base.in to define the base version
* number for the compiled result.
*/
# define VERSION_MAJOR 11
# define VERSION_MAJOR 12
# define VERSION_MINOR 0
/*
+1
View File
@@ -107,6 +107,7 @@ extern bool of_DISABLE_FORK(vthread_t thr, vvp_code_t code);
extern bool of_DIV(vthread_t thr, vvp_code_t code);
extern bool of_DIV_S(vthread_t thr, vvp_code_t code);
extern bool of_DIV_WR(vthread_t thr, vvp_code_t code);
extern bool of_DUP_OBJ(vthread_t thr, vvp_code_t code);
extern bool of_DUP_REAL(vthread_t thr, vvp_code_t code);
extern bool of_DUP_VEC4(vthread_t thr, vvp_code_t code);
extern bool of_END(vthread_t thr, vvp_code_t code);
+1
View File
@@ -159,6 +159,7 @@ static const struct opcode_table_s opcode_table[] = {
{ "%div", of_DIV, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%div/s", of_DIV_S, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%div/wr", of_DIV_WR, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%dup/obj", of_DUP_OBJ, 0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%dup/real", of_DUP_REAL,0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%dup/vec4", of_DUP_VEC4,0, {OA_NONE, OA_NONE, OA_NONE} },
{ "%end", of_END, 0, {OA_NONE, OA_NONE, OA_NONE} },
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; Copyright (c) 2001-2015 Stephen Williams ([email protected])
+1 -1
View File
@@ -1,4 +1,4 @@
:ivl_version "11.0" "vec4-stack";
:ivl_version "12.0" "vec4-stack";
:vpi_module "system";
; This program is free software; you can redistribute it and/or modify
+3 -2
View File
@@ -491,8 +491,9 @@ This opcode divides the left operand by the right operand. If the
right operand is 0, then the result is NaN.
* dup/real
* dup/vec4
* %dup/real
* %dup/vec4
* %dup/obj
These opcodes duplicate the value on the top of the stack for the
corresponding type.
+24
View File
@@ -3002,6 +3002,19 @@ bool of_DIV_WR(vthread_t thr, vvp_code_t)
return true;
}
/*
* %dup/obj
* %dup/real
* %dup/vec4
*
* Push a duplicate of the object on the appropriate stack.
*/
bool of_DUP_OBJ(vthread_t thr, vvp_code_t)
{
thr->push_object(thr->peek_object().duplicate());
return true;
}
bool of_DUP_REAL(vthread_t thr, vvp_code_t)
{
thr->push_real(thr->peek_real(0));
@@ -5596,6 +5609,17 @@ bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t cp)
return retload<vvp_vector4_t>(thr, cp);
}
/*
* %scopy
*
* Pop the top item from the object stack, and shallow_copy() that item into
* the new top of the object stack. This will copy at many items as needed
* from the source object to fill the target object. If the target object is
* larger then the source object, then some items will be left unchanged.
*
* The object may be any kind of object that supports shallow_copy(),
* including dynamic arrays and class objects.
*/
bool of_SCOPY(vthread_t thr, vvp_code_t)
{
vvp_object_t tmp;
+48 -86
View File
@@ -67,11 +67,6 @@ void vvp_darray::get_word(unsigned, vvp_object_t&)
cerr << "XXXX get_word(vvp_object_t) not implemented for " << typeid(*this).name() << endl;
}
void vvp_darray::shallow_copy(const vvp_object*)
{
cerr << "XXXX shallow_copy(vvp_object_t) not implemented for " << typeid(*this).name() << endl;
}
vvp_vector4_t vvp_darray::get_bitstream(bool)
{
cerr << "XXXX get_bitstream() not implemented for " << typeid(*this).name() << endl;
@@ -122,6 +117,15 @@ template <class TYPE> void vvp_darray_atom<TYPE>::shallow_copy(const vvp_object*
array_[idx] = that->array_[idx];
}
template <class TYPE> vvp_object* vvp_darray_atom<TYPE>::duplicate(void) const
{
vvp_darray_atom<TYPE>*that = new vvp_darray_atom<TYPE>(array_.size());
for (size_t idx = 0 ; idx < array_.size() ; idx += 1)
that->array_[idx] = array_[idx];
return that;
}
template <class TYPE> vvp_vector4_t vvp_darray_atom<TYPE>::get_bitstream(bool)
{
const unsigned word_wid = sizeof(TYPE) * 8;
@@ -192,6 +196,16 @@ void vvp_darray_vec4::shallow_copy(const vvp_object*obj)
array_[idx] = that->array_[idx];
}
vvp_object* vvp_darray_vec4::duplicate(void) const
{
vvp_darray_vec4*that = new vvp_darray_vec4(array_.size(), word_wid_);
for (size_t idx = 0 ; idx < array_.size() ; idx += 1)
that->array_[idx] = array_[idx];
return that;
}
vvp_vector4_t vvp_darray_vec4::get_bitstream(bool as_vec4)
{
vvp_vector4_t vec(array_.size() * word_wid_, BIT4_0);
@@ -344,6 +358,16 @@ void vvp_darray_real::shallow_copy(const vvp_object*obj)
array_[idx] = that->array_[idx];
}
vvp_object* vvp_darray_real::duplicate(void) const
{
vvp_darray_real*that = new vvp_darray_real(array_.size());
for (size_t idx = 0 ; idx < array_.size() ; idx += 1)
that->array_[idx] = array_[idx];
return that;
}
vvp_vector4_t vvp_darray_real::get_bitstream(bool)
{
const unsigned word_wid = sizeof(double) * 8;
@@ -406,6 +430,16 @@ void vvp_darray_string::shallow_copy(const vvp_object*obj)
array_[idx] = that->array_[idx];
}
vvp_object* vvp_darray_string::duplicate(void) const
{
vvp_darray_string*that = new vvp_darray_string(array_.size());
for (size_t idx = 0 ; idx < array_.size() ; idx += 1)
that->array_[idx] = array_[idx];
return that;
}
vvp_queue::~vvp_queue()
{
}
@@ -591,22 +625,7 @@ void vvp_queue_real::insert(unsigned idx, double value, unsigned max_size)
<< max_size << "]." << endl;
queue.pop_back();
}
// Inserting at the beginning
if (idx == 0)
queue.push_front(value);
// Inserting in the middle
else {
std::deque<double>::iterator pos;
unsigned middle = queue.size()/2;
if (idx < middle) {
pos = queue.begin();
for (unsigned count = 0; count < idx; ++count) ++pos;
} else {
pos = queue.end();
for (unsigned count = queue.size(); count > idx; --count) --pos;
}
queue.insert(pos, value);
}
queue.insert(queue.begin()+idx, value);
}
}
@@ -635,17 +654,8 @@ void vvp_queue_real::push_front(double value, unsigned max_size)
void vvp_queue_real::erase(unsigned idx)
{
std::deque<double>::iterator pos;
unsigned middle = queue.size()/2;
if (idx < middle) {
pos = queue.begin();
for (unsigned count = 0; count < idx; ++count) ++pos;
} else {
pos = queue.end();
for (unsigned count = queue.size(); count > idx; --count) --pos;
}
queue.erase(pos);
assert(queue.size() > idx);
queue.erase(queue.begin()+idx);
}
void vvp_queue_real::erase_tail(unsigned idx)
@@ -727,22 +737,7 @@ void vvp_queue_string::insert(unsigned idx, const string&value, unsigned max_siz
<< max_size << "]." << endl;
queue.pop_back();
}
// Inserting at the beginning
if (idx == 0)
queue.push_front(value);
// Inserting in the middle
else {
std::deque<string>::iterator pos;
unsigned middle = queue.size()/2;
if (idx < middle) {
pos = queue.begin();
for (unsigned count = 0; count < idx; ++count) ++pos;
} else {
pos = queue.end();
for (unsigned count = queue.size(); count > idx; --count) --pos;
}
queue.insert(pos, value);
}
queue.insert(queue.begin()+idx, value);
}
}
@@ -771,17 +766,8 @@ void vvp_queue_string::push_front(const string&value, unsigned max_size)
void vvp_queue_string::erase(unsigned idx)
{
std::deque<std::string>::iterator pos;
unsigned middle = queue.size()/2;
if (idx < middle) {
pos = queue.begin();
for (unsigned count = 0; count < idx; ++count) ++pos;
} else {
pos = queue.end();
for (unsigned count = queue.size(); count > idx; --count) --pos;
}
queue.erase(pos);
assert(queue.size() > idx);
queue.erase(queue.begin()+idx);
}
void vvp_queue_string::erase_tail(unsigned idx)
@@ -863,22 +849,7 @@ void vvp_queue_vec4::insert(unsigned idx, const vvp_vector4_t&value, unsigned ma
<< value.size() << "]> [" << max_size << "]." << endl;
queue.pop_back();
}
// Inserting at the beginning
if (idx == 0)
queue.push_front(value);
// Inserting in the middle
else {
std::deque<vvp_vector4_t>::iterator pos;
unsigned middle = queue.size()/2;
if (idx < middle) {
pos = queue.begin();
for (unsigned count = 0; count < idx; ++count) ++pos;
} else {
pos = queue.end();
for (unsigned count = queue.size(); count > idx; --count) --pos;
}
queue.insert(pos, value);
}
queue.insert(queue.begin()+idx, value);
}
}
@@ -907,17 +878,8 @@ void vvp_queue_vec4::push_front(const vvp_vector4_t&value, unsigned max_size)
void vvp_queue_vec4::erase(unsigned idx)
{
std::deque<vvp_vector4_t>::iterator pos;
unsigned middle = queue.size()/2;
if (idx < middle) {
pos = queue.begin();
for (unsigned count = 0; count < idx; ++count) ++pos;
} else {
pos = queue.end();
for (unsigned count = queue.size(); count > idx; --count) --pos;
}
queue.erase(pos);
assert(queue.size() > idx);
queue.erase(queue.begin()+idx);
}
void vvp_queue_vec4::erase_tail(unsigned idx)
+5 -2
View File
@@ -45,8 +45,6 @@ class vvp_darray : public vvp_object {
virtual void set_word(unsigned adr, const vvp_object_t&value);
virtual void get_word(unsigned adr, vvp_object_t&value);
virtual void shallow_copy(const vvp_object*obj);
virtual vvp_vector4_t get_bitstream(bool as_vec4);
};
@@ -60,6 +58,7 @@ template <class TYPE> class vvp_darray_atom : public vvp_darray {
void set_word(unsigned adr, const vvp_vector4_t&value);
void get_word(unsigned adr, vvp_vector4_t&value);
void shallow_copy(const vvp_object*obj);
vvp_object* duplicate(void) const;
vvp_vector4_t get_bitstream(bool as_vec4);
private:
@@ -77,6 +76,7 @@ class vvp_darray_vec4 : public vvp_darray {
void set_word(unsigned adr, const vvp_vector4_t&value);
void get_word(unsigned adr, vvp_vector4_t&value);
void shallow_copy(const vvp_object*obj);
vvp_object* duplicate(void) const;
vvp_vector4_t get_bitstream(bool as_vec4);
private:
@@ -112,6 +112,7 @@ class vvp_darray_real : public vvp_darray {
void set_word(unsigned adr, double value);
void get_word(unsigned adr, double&value);
void shallow_copy(const vvp_object*obj);
vvp_object* duplicate(void) const;
vvp_vector4_t get_bitstream(bool as_vec4);
private:
@@ -128,6 +129,7 @@ class vvp_darray_string : public vvp_darray {
void set_word(unsigned adr, const std::string&value);
void get_word(unsigned adr, std::string&value);
void shallow_copy(const vvp_object*obj);
vvp_object* duplicate(void) const;
private:
std::vector<std::string> array_;
@@ -143,6 +145,7 @@ class vvp_darray_object : public vvp_darray {
void set_word(unsigned adr, const vvp_object_t&value);
void get_word(unsigned adr, vvp_object_t&value);
void shallow_copy(const vvp_object*obj);
//virtual vvp_object* duplicate(void) const;
private:
std::vector<vvp_object_t> array_;
+9 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 2012-2020 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@@ -37,5 +37,13 @@ vvp_object::~vvp_object()
void vvp_object::shallow_copy(const vvp_object*)
{
cerr << "XXXX shallow_copy(vvp_object_t) not implemented for " << typeid(*this).name() << endl;
assert(0);
}
vvp_object* vvp_object::duplicate(void) const
{
cerr << "XXXX duplicate() not implemented for " << typeid(*this).name() << endl;
assert(0);
return 0;
}
+5 -1
View File
@@ -1,7 +1,7 @@
#ifndef IVL_vvp_object_H
#define IVL_vvp_object_H
/*
* Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2012-2020 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@@ -33,6 +33,7 @@ class vvp_object {
virtual ~vvp_object() =0;
virtual void shallow_copy(const vvp_object*that);
virtual vvp_object* duplicate(void) const;
static void cleanup(void);
@@ -63,6 +64,9 @@ class vvp_object_t {
inline void shallow_copy(const vvp_object_t&that)
{ ref_->shallow_copy(that.ref_); }
inline vvp_object_t duplicate(void) const
{ return vvp_object_t(ref_->duplicate()); }
template <class T> T*peek(void) const;
private: