From d3caa547babcd923fbdb72366f0b974c0c0cc62c Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 13 Aug 2008 13:58:49 -0700 Subject: [PATCH 01/11] Print an error for automatic tasks or functions. This patch adds code to recognize and report that automatic task or functions are not currently supported. --- lexor_keyword.gperf | 1 + parse.y | 63 ++++++++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/lexor_keyword.gperf b/lexor_keyword.gperf index 980b33c89..9cb72b281 100644 --- a/lexor_keyword.gperf +++ b/lexor_keyword.gperf @@ -25,6 +25,7 @@ assign, GN_KEYWORDS_1364_1995, K_assign atan, GN_KEYWORDS_VAMS_2_3, K_atan atan2, GN_KEYWORDS_VAMS_2_3, K_atan2 atanh, GN_KEYWORDS_VAMS_2_3, K_atanh +automatic, GN_KEYWORDS_1364_2001, K_automatic begin, GN_KEYWORDS_1364_1995, K_begin bool, GN_KEYWORDS_ICARUS, K_bool buf, GN_KEYWORDS_1364_1995, K_buf diff --git a/parse.y b/parse.y index 7382eae6b..8f180090d 100644 --- a/parse.y +++ b/parse.y @@ -205,7 +205,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %token K_PSTAR K_STARP %token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER %token K_abs K_abstol K_access K_acos K_acosh K_asin K_analog K_asinh -%token K_atan K_atanh K_atan2 +%token K_atan K_atanh K_atan2 K_automatic %token K_always K_and K_assign K_begin K_bool K_buf K_bufif0 K_bufif1 K_case %token K_casex K_casez K_ceil K_cmos K_continuous K_cos K_cosh %token K_ddt_nature K_deassign K_default K_defparam K_disable K_discrete @@ -2094,41 +2094,41 @@ module_item statements in the task body. But we continue to accept it as an extension. */ - | K_task IDENTIFIER ';' + | K_task automatic_opt IDENTIFIER ';' { assert(current_task == 0); - current_task = pform_push_task_scope($2); + current_task = pform_push_task_scope($3); FILE_NAME(current_task, @1); } task_item_list_opt statement_or_null K_endtask - { current_task->set_ports($5); - current_task->set_statement($6); + { current_task->set_ports($6); + current_task->set_statement($7); pform_pop_scope(); current_task = 0; - delete[]$2; + delete[]$3; } - | K_task IDENTIFIER + | K_task automatic_opt IDENTIFIER { assert(current_task == 0); - current_task = pform_push_task_scope($2); + current_task = pform_push_task_scope($3); FILE_NAME(current_task, @1); } '(' task_port_decl_list ')' ';' block_item_decls_opt statement_or_null K_endtask - { current_task->set_ports($5); - current_task->set_statement($9); + { current_task->set_ports($6); + current_task->set_statement($10); pform_pop_scope(); current_task = 0; - delete[]$2; + delete[]$3; } - | K_task IDENTIFIER error K_endtask + | K_task automatic_opt IDENTIFIER error K_endtask { pform_pop_scope(); current_task = 0; - delete[]$2; + delete[]$3; } /* The function declaration rule matches the function declaration @@ -2136,42 +2136,42 @@ module_item definitions in the func_body to take on the scope of the function instead of the module. */ - | K_function function_range_or_type_opt IDENTIFIER ';' + | K_function automatic_opt function_range_or_type_opt IDENTIFIER ';' { assert(current_function == 0); - current_function = pform_push_function_scope($3); + current_function = pform_push_function_scope($4); FILE_NAME(current_function, @1); } function_item_list statement K_endfunction - { current_function->set_ports($6); - current_function->set_statement($7); - current_function->set_return($2); + { current_function->set_ports($7); + current_function->set_statement($8); + current_function->set_return($3); pform_pop_scope(); current_function = 0; - delete[]$3; + delete[]$4; } - | K_function function_range_or_type_opt IDENTIFIER + | K_function automatic_opt function_range_or_type_opt IDENTIFIER { assert(current_function == 0); - current_function = pform_push_function_scope($3); + current_function = pform_push_function_scope($4); FILE_NAME(current_function, @1); } '(' task_port_decl_list ')' ';' block_item_decls_opt statement K_endfunction - { current_function->set_ports($6); - current_function->set_statement($10); - current_function->set_return($2); + { current_function->set_ports($7); + current_function->set_statement($11); + current_function->set_return($3); pform_pop_scope(); current_function = 0; - delete[]$3; + delete[]$4; } - | K_function function_range_or_type_opt IDENTIFIER error K_endfunction + | K_function automatic_opt function_range_or_type_opt IDENTIFIER error K_endfunction { pform_pop_scope(); current_task = 0; - delete[]$3; + delete[]$4; } /* A generate region can contain further module items. Actually, it @@ -2278,6 +2278,15 @@ module_item { yyerror(@1, "error: Malformed $attribute parameter list."); } ; +automatic_opt + : K_automatic + { yyerror(@1, "sorry: automatic tasks/functions are not " + "currently supported."); + yyerrok; + } + | {} + ; + generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } generate_case_items From 1f5b11246e7bd6befc2e3110bbe7dc81b5a13c06 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 14 Aug 2008 12:40:53 -0700 Subject: [PATCH 02/11] Correctly pass a concatenation elaboration error. Because Icarus tries to elaborate as much as it can even after an error has occurred we need to check for these errors during elaboration. This patch prevent an undefined identifier from crashing the compiler. --- elaborate.cc | 4 ++-- net_nex_input.cc | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index ad7c273ab..332c976eb 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2649,8 +2649,8 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, } NexusSet*nset = enet->nex_input(rem_out); if (nset == 0) { - cerr << get_fileline() << ": internal error: No NexusSet" - << " from statement." << endl; + cerr << get_fileline() << ": error: Unable to elaborate:" + << endl; enet->dump(cerr, 6); des->errors += 1; return enet; diff --git a/net_nex_input.cc b/net_nex_input.cc index 2a68c2a8b..0f97c2a44 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 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 @@ -53,8 +53,13 @@ NexusSet* NetEBinary::nex_input(bool rem_out) NexusSet* NetEConcat::nex_input(bool rem_out) { + if (parms_[0] == NULL) return NULL; NexusSet*result = parms_[0]->nex_input(rem_out); for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) { + if (parms_[idx] == NULL) { + delete result; + return NULL; + } NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; @@ -98,6 +103,10 @@ NexusSet* NetESelect::nex_input(bool rem_out) { NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet(); NexusSet*tmp = expr_->nex_input(rem_out); + if (tmp == NULL) { + delete result; + return NULL; + } result->add(*tmp); delete tmp; return result; @@ -381,4 +390,3 @@ NexusSet* NetWhile::nex_input(bool rem_out) delete tmp; return result; } - From 2c1426a44dae3b5e18bdcdb4bd17415033d9b0e5 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Fri, 15 Aug 2008 23:58:59 +0100 Subject: [PATCH 03/11] Patch to ensure functions are evaluated immediately. This patch causes a thread that is created to evaluate a function to be executed immediately. The parent thread is resumed when the function thread terminates. --- vvp/vthread.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vvp/vthread.cc b/vvp/vthread.cc index b83cfc397..86d8ed5dd 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -2076,7 +2076,14 @@ bool of_FORK(vthread_t thr, vvp_code_t cp) thr->fork_count += 1; - schedule_vthread(child, 0, true); + /* If the new child was created to evaluate a function, + run it immediately, then return to this thread. */ + if (cp->scope->base.vpi_type->type_code == vpiFunction) { + child->is_scheduled = 1; + vthread_run(child); + } else { + schedule_vthread(child, 0, true); + } return true; } From e501bbdd2756e882ec87b992009c21da45d82c5a Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 13 Aug 2008 10:29:44 -0700 Subject: [PATCH 04/11] Fix problems in VPI callback time and value formats. This patch adds support for vpiScaledRealTime and vpiSuppressTime to VPI callbacks. It also fixes a bug where the callback data object was not being copied correctly and adds support for vpiSuppressVal. This requires adding vpiSuppressVal to a few other routines. It adds the ability for a callback to return more than vpiScalarVal (all values supported by the vpip_vec4_get_value() procedure). It also fixes a bug where vpip_vec4_get_value() would incorrectly return vpiZ for a BIT4_X scalar value. It also comments the potentially dangerous vpiScalarVal calculation in the vvp_fun_signal::get_value() procedure. --- vvp/vpi_callback.cc | 52 +++++++++++++++++++++++++++++++++++++++++++-- vvp/vpi_priv.cc | 4 ++++ vvp/vpi_priv.h | 1 + 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/vvp/vpi_callback.cc b/vvp/vpi_callback.cc index 931d92d39..db8cf64b1 100644 --- a/vvp/vpi_callback.cc +++ b/vvp/vpi_callback.cc @@ -127,6 +127,12 @@ static struct __vpiCallback* make_value_change(p_cb_data data) obj->cb_time.type = vpiSuppressTime; } obj->cb_data.time = &obj->cb_time; + if (data->value) { + obj->cb_value = *(data->value); + } else { + obj->cb_value.format = vpiSuppressVal; + } + obj->cb_data.value = &obj->cb_value; assert(data->obj); assert(data->obj->vpi_type); @@ -457,8 +463,25 @@ void callback_execute(struct __vpiCallback*cur) vpi_mode_flag = VPI_MODE_RWSYNC; assert(cur->cb_data.cb_rtn); - cur->cb_data.time->type = vpiSimTime; - vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime()); + switch (cur->cb_data.time->type) { + case vpiSimTime: + vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime()); + break; + case vpiScaledRealTime: { + cur->cb_data.time->real = + vpip_time_to_scaled_real(schedule_simtime(), + (struct __vpiScope *) vpi_handle(vpiScope, + cur->cb_data.obj)); + break; + } + case vpiSuppressTime: + break; + default: + fprintf(stderr, "Unsupported time format %d.\n", + cur->cb_data.time->type); + assert(0); + break; + } (cur->cb_data.cb_rtn)(&cur->cb_data); vpi_mode_flag = save_mode; @@ -546,10 +569,32 @@ void vvp_fun_signal::get_value(struct t_vpi_value*vp) { switch (vp->format) { case vpiScalarVal: + // This works because vvp_bit4_t has the same encoding + // as a scalar value! See vpip_vec4_get_value() for a + // more robust method. vp->value.scalar = value(0); break; + + case vpiBinStrVal: + case vpiOctStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiIntVal: + case vpiVectorVal: + case vpiStringVal: + case vpiRealVal: { + unsigned wid = size(); + vvp_vector4_t vec4(wid); + for (unsigned idx = 0; idx < wid; idx += 1) { + vec4.set_bit(idx, value(idx)); + } + vpip_vec4_get_value(vec4, wid, false, vp); + break; + } + case vpiSuppressVal: break; + default: fprintf(stderr, "vpi_callback: value " "format %d not supported (fun_signal)\n", @@ -622,6 +667,9 @@ void vvp_fun_signal_real::get_value(struct t_vpi_value*vp) break; } + case vpiSuppressVal: + break; + default: fprintf(stderr, "vpi_callback: value " "format %d not supported (fun_signal_real)\n", diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index 0c004d2a9..8e0a63fe0 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -456,6 +456,9 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width, vp->format); assert(0 && "format not implemented"); + case vpiSuppressVal: + break; + case vpiBinStrVal: rbuf = need_result_buf(width+1, RBUF_VAL); for (unsigned idx = 0 ; idx < width ; idx += 1) { @@ -504,6 +507,7 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width, break; case BIT4_X: vp->value.scalar = vpiX; + break; case BIT4_Z: vp->value.scalar = vpiZ; break; diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 25a5282b8..49d38ed5d 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -137,6 +137,7 @@ struct __vpiCallback { // user supplied callback data struct t_cb_data cb_data; struct t_vpi_time cb_time; + struct t_vpi_value cb_value; // scheduled event struct sync_cb* cb_sync; From 768633e4645bc9712959177be900ac4a0e62826b Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 31 Jul 2008 14:05:27 -0700 Subject: [PATCH 05/11] Add $clog2 function. This patch adds the $clog2 system function. It also makes this function work as a constant function. The runtime version still needs to be updated to use an integer based version instead of the current double based method. The double method suffers from rounding errors. --- PExpr.cc | 23 ++++++++- PExpr.h | 3 ++ elab_pexpr.cc | 37 ++++++++++++++ eval_tree.cc | 54 +++++++++++++++++++++ netlist.h | 2 + verinum.cc | 125 ++++++++++++++++++++++++++++++++++++++++++++---- verinum.h | 3 ++ vpi/Makefile.in | 2 +- vpi/sys_clog2.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++ vpi/sys_table.c | 2 + vpi/system.sft | 1 + 11 files changed, 363 insertions(+), 11 deletions(-) create mode 100644 vpi/sys_clog2.c diff --git a/PExpr.cc b/PExpr.cc index 96428204f..880dcc8c2 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2007 Stephen Williams + * Copyright (c) 1998-2008 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -117,6 +117,26 @@ PECallFunction::~PECallFunction() { } +bool PECallFunction::is_constant(Module*mod) const +{ + /* Only $clog2 can be a constant system function. */ + if (peek_tail_name(path_)[0] == '$') { + if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) { + if (parms_.count() != 1 || parms_[0] == 0) { + cerr << get_fileline() << ": error: $clog2 takes a " + "single argument." << endl; + return false; + } + /* If the argument is constant $clog2 is constant. */ + return parms_[0]->is_constant(mod); + } + return false; /* Most system functions are not constant. */ + } + + /* Checking for constant user functions goes here. */ + return false; +} + PEConcat::PEConcat(const svector&p, PExpr*r) : parms_(p), repeat_(r) { @@ -299,4 +319,3 @@ bool PEUnary::is_constant(Module*m) const { return expr_->is_constant(m); } - diff --git a/PExpr.h b/PExpr.h index 08dccdd29..8101bdc05 100644 --- a/PExpr.h +++ b/PExpr.h @@ -703,6 +703,8 @@ class PECallFunction : public PExpr { explicit PECallFunction(perm_string n); ~PECallFunction(); + virtual bool is_constant(Module*) const; + virtual void dump(ostream &) const; virtual NetNet* elaborate_net(Design*des, NetScope*scope, @@ -714,6 +716,7 @@ class PECallFunction : public PExpr { Link::strength_t drive1) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, int expr_wid, bool sys_task_arg) const; + virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const; virtual unsigned test_width(Design*des, NetScope*scope, unsigned min, unsigned lval, diff --git a/elab_pexpr.cc b/elab_pexpr.cc index a3d398a47..59f80dd80 100644 --- a/elab_pexpr.cc +++ b/elab_pexpr.cc @@ -267,3 +267,40 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const return tmp; } + +/* Reuse the routine from eval_tree.cc. */ +NetExpr* evaluate_clog2(NetExpr*arg); + +NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const +{ + /* For now only $clog2 can be a constant system function. */ + if (peek_tail_name(path_)[0] == '$') { + if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) { + if (parms_.count() != 1 || parms_[0] == 0) { + cerr << get_fileline() << ": error: $clog2 takes a " + "single argument." << endl; + des->errors += 1; + return 0; + } + NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope); + eval_expr(arg); + NetExpr*rtn = evaluate_clog2(arg); + delete arg; + if (rtn != 0) { + rtn->set_line(*this); + return rtn; + } + } + + cerr << get_fileline() << ": error: this is not a constant " + "system function (" << *this << ")." << endl; + des->errors += 1; + return 0; + } + + /* Constant user function code goes here. */ + cerr << get_fileline() << ": sorry: constant user functions are not " + "currently supported." << endl; + des->errors += 1; + return 0; +} diff --git a/eval_tree.cc b/eval_tree.cc index 7cc7b9ac8..fd1acc47d 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -22,6 +22,7 @@ # include # include +# include # include "netlist.h" # include "ivl_assert.h" @@ -1607,3 +1608,56 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width) return new NetEConst(verinum(res, 1)); } + +NetExpr* evaluate_clog2(NetExpr*arg) +{ + NetEConst*tmpi = dynamic_cast(arg); + NetECReal*tmpr = dynamic_cast(arg); + bool is_neg = false; + if (tmpi || tmpr) { + verinum arg; + if (tmpi) { + arg = tmpi->value(); + } else { + arg = verinum(tmpr->value().as_double(), true); + } + uint64_t res = 0; + if (arg.is_negative()) is_neg = true; + arg.has_sign(false); // $unsigned() + if (!arg.is_zero()) { + arg = arg - verinum((uint64_t)1, 1); + while (!arg.is_zero()) { + res += 1; + arg = arg >> 1; + } + } + if (is_neg && res < 32) res = 32; + NetEConst*rtn = new NetEConst(verinum(res, 32)); + return rtn; + } + + return 0; +} + +NetExpr* NetESFunc::eval_tree(int prune_to_width) +{ + /* For now only $clog2 can be a constant system function. */ + if (strcmp(name(), "$clog2") == 0) { + if (nparms() != 1 || parm(0) == 0) { + cerr << get_fileline() << ": error: $clog2 takes a single " + "argument." << endl; + return 0; + } + NetExpr*rtn = evaluate_clog2(parm(0)); + if (rtn != 0) { + rtn->set_line(*this); + if (debug_eval_tree) { + cerr << get_fileline() << ": debug: Evaluate " + "constant $clog2()." << endl; + } + return rtn; + } + } + + return 0; +} diff --git a/netlist.h b/netlist.h index 7818938e8..05f151d10 100644 --- a/netlist.h +++ b/netlist.h @@ -3442,6 +3442,8 @@ class NetESFunc : public NetExpr { NetExpr* parm(unsigned idx); const NetExpr* parm(unsigned idx) const; + virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool set_width(unsigned, bool last_chance); diff --git a/verinum.cc b/verinum.cc index 87f28de04..5bce5831a 100644 --- a/verinum.cc +++ b/verinum.cc @@ -152,6 +152,121 @@ verinum::verinum(uint64_t val, unsigned n) } } +/* The second argument is not used! It is there to make this + * constructor unique. */ +verinum::verinum(double val, bool dummy) +: has_len_(false), has_sign_(true), string_flag_(false) +{ + bool is_neg = false; + double fraction; + int exponent; + const unsigned BITS_IN_LONG = 8*sizeof(long); + + /* We return `bx for a NaN. */ + if (val != val) { + nbits_ = 1; + bits_ = new V[nbits_]; + bits_[0] = Vx; + return; + } + + /* We return 'b1 for + infinity or 'b0 for - infinity. */ + if (val && (val == 0.5*val)) { + nbits_ = 1; + bits_ = new V[nbits_]; + if (val > 0) bits_[0] = V1; + else bits_[0] = V0; + return; + } + + /* Convert to a positive result. */ + if (val < 0.0) { + is_neg = true; + val = -val; + } + + /* Get the exponent and fractional part of the number. */ + fraction = frexp(val, &exponent); + nbits_ = exponent+1; + bits_ = new V[nbits_]; + const verinum const_one(1); + + /* If the value is small enough just use lround(). */ + if (nbits_ <= BITS_IN_LONG) { + long sval = lround(val); + if (is_neg) sval = -sval; + for (unsigned idx = 0; idx < nbits_; idx += 1) { + bits_[idx] = (sval&1) ? V1 : V0; + sval >>= 1; + } + /* Trim the result. */ + signed_trim(); + return; + } + + unsigned nwords = (exponent-1)/BITS_IN_LONG; + + fraction = ldexp(fraction, (exponent-1) % BITS_IN_LONG + 1); + + if (nwords == 0) { + unsigned long bits = (unsigned long) fraction; + fraction = fraction - (double) bits; + for (unsigned idx = 0; idx < nbits_; idx += 1) { + bits_[idx] = (bits&1) ? V1 : V0; + bits >>= 1; + } + if (fraction >= 0.5) *this = *this + const_one; + } else { + for (int wd = nwords; wd >= 0; wd -= 1) { + unsigned long bits = (unsigned long) fraction; + fraction = fraction - (double) bits; + unsigned max = (wd+1)*BITS_IN_LONG; + if (max > nbits_) max = nbits_; + for (unsigned idx = wd*BITS_IN_LONG; idx < max; idx += 1) { + bits_[idx] = (bits&1) ? V1 : V0; + bits >>= 1; + } + fraction = ldexp(fraction, BITS_IN_LONG); + } + if (fraction >= ldexp(0.5, BITS_IN_LONG)) *this = *this + const_one; + } + + /* Convert a negative number if needed. */ + if (is_neg) { + *this = v_not(*this) + const_one; + } + + /* Trim the result. */ + signed_trim(); +} + + +/* This is used by the double constructor above. It is needed to remove + * extra sign bits that can occur when calculating a negative value. */ +void verinum::signed_trim() +{ + /* Do we have any extra digits? */ + unsigned tlen = nbits_-1; + verinum::V sign = bits_[tlen]; + while ((tlen > 0) && (bits_[tlen] == sign)) tlen -= 1; + + /* tlen now points to the first digit that is not the sign. + * or bit 0. Set the length to include this bit and one proper + * sign bit if needed. */ + if (bits_[tlen] != sign) tlen += 1; + tlen += 1; + + /* Trim the bits if needed. */ + if (tlen < nbits_) { + V* tbits = new V[tlen]; + for (unsigned idx = 0; idx < tlen; idx += 1) + tbits[idx] = bits_[idx]; + delete[] bits_; + bits_ = tbits; + nbits_ = tlen; + } +} + verinum::verinum(const verinum&that) { string_flag_ = that.string_flag_; @@ -336,14 +451,9 @@ double verinum::as_double() const { if (nbits_ == 0) return 0.0; - /* Do we have a signed value? */ - bool signed_flag = false; - if (bits_[nbits_-1] == V1) { - signed_flag = true; - } - double val = 0.0; - if (signed_flag) { + /* Do we have/want a signed value? */ + if (has_sign_ && bits_[nbits_-1] == V1) { V carry = V1; for (unsigned idx = 0; idx < nbits_; idx += 1) { V sum = add_with_carry(~bits_[idx], V0, carry); @@ -351,7 +461,6 @@ double verinum::as_double() const val += pow(2.0, (double)idx); } val *= -1.0; -// val = (double) as_long(); } else { for (unsigned idx = 0; idx < nbits_; idx += 1) { if (bits_[idx] == V1) diff --git a/verinum.h b/verinum.h index b184c3c8c..5142883ee 100644 --- a/verinum.h +++ b/verinum.h @@ -46,6 +46,7 @@ class verinum { verinum(const V*v, unsigned nbits, bool has_len =true); verinum(V, unsigned nbits =1, bool has_len =true); verinum(uint64_t val, unsigned bits); + verinum(double val, bool dummy); verinum(const verinum&); // Create a signed number, with an unspecified number of bits. @@ -93,6 +94,8 @@ class verinum { signed long as_long() const; double as_double() const; string as_string() const; + private: + void signed_trim(); private: V* bits_; diff --git a/vpi/Makefile.in b/vpi/Makefile.in index f9b28a57d..a2b8bea94 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -60,7 +60,7 @@ sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \ sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \ sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \ mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \ -vams_simparam.o +sys_clog2.o vams_simparam.o ifeq (@HAVE_LIBZ@,yes) ifeq (@HAVE_LIBBZ2@,yes) diff --git a/vpi/sys_clog2.c b/vpi/sys_clog2.c new file mode 100644 index 000000000..ae3714815 --- /dev/null +++ b/vpi/sys_clog2.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008 Cary R. (cygcary@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include "sys_priv.h" + +/* + * Check that the function is called with the correct argument. + */ +static PLI_INT32 sys_clog2_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + assert(callh != 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + (void) name; // Not used! + + /* We must have an argument. */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$clog2 requires one numeric argument.\n"); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The argument must be numeric. */ + arg = vpi_scan(argv); + if (! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("The first argument to $clog2 must be numeric.\n"); + vpi_control(vpiFinish, 1); + } + + /* We can have a maximum of one argument. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s $clog2 takes at most one argument.\n", msg); + vpi_printf("%*s Found %u extra argument%s.\n", + strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +static PLI_INT32 sys_clog2_calltf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + s_vpi_value val; + (void) name; // Not used!/ + + /* Get the argument. */ + arg = vpi_scan(argv); + val.format = vpiRealVal; + vpi_get_value(arg, &val); + vpi_free_object(argv); + + /* For now we don't support a negative value! */ + if (val.value.real < 0.0) { + vpi_printf("SORRY: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$clog2 does not currently support negative values.\n"); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Return the value to the system. */ + val.format = vpiIntVal; + if (val.value.real == 0.0) + val.value.integer = 0; + else + val.value.integer = ceil(log(floor(val.value.real+0.5))/M_LN2); + vpi_put_value(callh, &val, 0, vpiNoDelay); + + return 0; +} + +/* + * Register the function with Verilog. + */ +void sys_clog2_register(void) +{ + s_vpi_systf_data tf_data; + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.calltf = sys_clog2_calltf; + tf_data.compiletf = sys_clog2_compiletf; + tf_data.sizetf = 0; + tf_data.tfname = "$clog2"; + tf_data.user_data = 0; + vpi_register_systf(&tf_data); +} diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 4560744e7..56d480217 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -38,6 +38,7 @@ extern void sys_time_register(); extern void sys_vcd_register(); extern void sys_vcdoff_register(); extern void sys_special_register(); +extern void sys_clog2_register(); extern void vams_simparam_register(); #ifdef HAVE_LIBZ @@ -181,6 +182,7 @@ void (*vlog_startup_routines[])() = { sys_lxt_or_vcd_register, sys_sdf_register, sys_special_register, + sys_clog2_register, vams_simparam_register, 0 }; diff --git a/vpi/system.sft b/vpi/system.sft index d6ff0a311..75abe1bdc 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -14,6 +14,7 @@ $dist_poisson vpiSysFuncInt $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt +$clog2 vpiSysFuncInt $simparam vpiSysFuncReal $simparam$str vpiSysFuncSized 1024 unsigned From c032d28aaa3beba5e188aadf7dd538dc5d376997 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 15 Aug 2008 11:31:36 -0700 Subject: [PATCH 06/11] Convert the infinities to 'bx This patch modifies the double to vector conversions to return 'bx for either +/- infinity. --- verinum.cc | 13 ++----------- vvp/vvp_net.cc | 11 ++--------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/verinum.cc b/verinum.cc index 5bce5831a..b4f20f32e 100644 --- a/verinum.cc +++ b/verinum.cc @@ -162,23 +162,14 @@ verinum::verinum(double val, bool dummy) int exponent; const unsigned BITS_IN_LONG = 8*sizeof(long); - /* We return `bx for a NaN. */ - if (val != val) { + /* We return `bx for a NaN or +/- infinity. */ + if (val != val || (val && (val == 0.5*val))) { nbits_ = 1; bits_ = new V[nbits_]; bits_[0] = Vx; return; } - /* We return 'b1 for + infinity or 'b0 for - infinity. */ - if (val && (val == 0.5*val)) { - nbits_ = 1; - bits_ = new V[nbits_]; - if (val > 0) bits_[0] = V1; - else bits_[0] = V0; - return; - } - /* Convert to a positive result. */ if (val < 0.0) { is_neg = true; diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index a8c669e26..cead9a17b 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -372,19 +372,12 @@ vvp_vector4_t::vvp_vector4_t(unsigned size, double val) double fraction; int exponent; - /* We return 'bx for a NaN. */ - if (val != val) { + /* We return 'bx for a NaN or +/- infinity. */ + if (val != val || (val && (val == 0.5*val))) { allocate_words_(size, WORD_X_ABITS, WORD_X_BBITS); return; } - /* We return 'b1 for + infinity or 'b0 for - infinity. */ - if (val && (val == 0.5*val)) { - if (val > 0) allocate_words_(size, WORD_1_ABITS, WORD_1_BBITS); - else allocate_words_(size, WORD_0_ABITS, WORD_0_BBITS); - return; - } - /* Convert to a positive result. */ if (val < 0.0) { is_neg = true; From 5e512e65704728b52e28448b4c18c70815a5b4b0 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 15 Aug 2008 19:28:11 -0700 Subject: [PATCH 07/11] Finish $clog2 function. This patch fixes problems in the initial $clog2 implementation and adds correct functionality to the runtime. --- eval_tree.cc | 13 +++++++++- vpi/sys_clog2.c | 21 +++------------- vpi_user.h | 1 + vvp/vpi_priv.cc | 64 +++++++++++++++++++++++++++++++++++++++++++++++ vvp/vpi_signal.cc | 17 ------------- vvp/vvp_net.cc | 26 +++++++++++++++++++ vvp/vvp_net.h | 6 +++++ 7 files changed, 113 insertions(+), 35 deletions(-) diff --git a/eval_tree.cc b/eval_tree.cc index fd1acc47d..e50005fdf 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1621,6 +1621,15 @@ NetExpr* evaluate_clog2(NetExpr*arg) } else { arg = verinum(tmpr->value().as_double(), true); } + + /* If we have an x in the verinum we return 32'bx. */ + if (!arg.is_defined()) { + verinum tmp (verinum::Vx, 32); + tmp.has_sign(true); + NetEConst*rtn = new NetEConst(tmp); + return rtn; + } + uint64_t res = 0; if (arg.is_negative()) is_neg = true; arg.has_sign(false); // $unsigned() @@ -1632,7 +1641,9 @@ NetExpr* evaluate_clog2(NetExpr*arg) } } if (is_neg && res < 32) res = 32; - NetEConst*rtn = new NetEConst(verinum(res, 32)); + verinum tmp (res, 32); + tmp.has_sign(true); + NetEConst*rtn = new NetEConst(tmp); return rtn; } diff --git a/vpi/sys_clog2.c b/vpi/sys_clog2.c index ae3714815..043e44fd0 100644 --- a/vpi/sys_clog2.c +++ b/vpi/sys_clog2.c @@ -76,31 +76,18 @@ static PLI_INT32 sys_clog2_calltf(PLI_BYTE8 *name) vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; + s_vpi_vecval vec; (void) name; // Not used!/ /* Get the argument. */ arg = vpi_scan(argv); - val.format = vpiRealVal; - vpi_get_value(arg, &val); vpi_free_object(argv); - /* For now we don't support a negative value! */ - if (val.value.real < 0.0) { - vpi_printf("SORRY: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$clog2 does not currently support negative values.\n"); - vpi_control(vpiFinish, 1); - return 0; - } + vec = vpip_calc_clog2(arg); - /* Return the value to the system. */ - val.format = vpiIntVal; - if (val.value.real == 0.0) - val.value.integer = 0; - else - val.value.integer = ceil(log(floor(val.value.real+0.5))/M_LN2); + val.format = vpiVectorVal; + val.value.vector = &vec; vpi_put_value(callh, &val, 0, vpiNoDelay); - return 0; } diff --git a/vpi_user.h b/vpi_user.h index 72b10c6c6..19d128cf8 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -566,6 +566,7 @@ extern DLLEXPORT void (*vlog_startup_routines[])(); buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); extern void vpip_set_return_value(int value); +extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg); EXTERN_C_END diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index 8e0a63fe0..d7a897604 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -456,6 +456,10 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width, vp->format); assert(0 && "format not implemented"); + case vpiObjTypeVal: + vp->format = vpiVectorVal; + break; + case vpiSuppressVal: break; @@ -1033,3 +1037,63 @@ extern "C" void vpi_control(PLI_INT32 operation, ...) vpi_sim_vcontrol(operation, ap); va_end(ap); } + +/* + * This routine calculated the return value for $clog2. + * It is easier to do it here vs trying to to use the VPI interface. + */ +extern "C" s_vpi_vecval vpip_calc_clog2(vpiHandle arg) +{ + s_vpi_vecval rtn; + s_vpi_value val; + vvp_vector4_t vec4; + bool is_neg = false; // At this point only a real can be negative. + + /* Get the value as a vvp_vector4_t. */ + val.format = vpiObjTypeVal; + vpi_get_value(arg, &val); + if (val.format == vpiRealVal) { + vpi_get_value(arg, &val); + /* All double values can be represented in 1024 bits. */ + vec4 = vvp_vector4_t(1024, val.value.real); + if (val.value.real < 0) is_neg = true; + } else { + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + unsigned wid = vpi_get(vpiSize, arg); + vec4 = vvp_vector4_t(wid, BIT4_0); + for (unsigned idx=0; idx < wid; idx += 1) { + PLI_INT32 aval = val.value.vector[idx/32].aval; + PLI_INT32 bval = val.value.vector[idx/32].bval; + aval >>= idx % 32; + bval >>= idx % 32; + int bitmask = (aval&1) | ((bval<<1)&2); + vvp_bit4_t bit = scalar_to_bit4(bitmask); + vec4.set_bit(idx, bit); + } + } + + if (vec4.has_xz()) { + rtn.aval = rtn.bval = 0xFFFFFFFFU; /* Set to 'bx. */ + return rtn; + } + + vvp_vector2_t vec2(vec4); + + if (is_neg) vec2.trim_neg(); /* This is a special trim! */ + else vec2.trim(); /* This makes less work shifting. */ + + /* Calculate the clog2 result. */ + PLI_INT32 res = 0; + if (!vec2.is_zero()) { + vec2 -= vvp_vector2_t(1, vec2.size()); + while(!vec2.is_zero()) { + res += 1; + vec2 >>= 1; + } + } + + rtn.aval = res; + rtn.bval = 0; + return rtn; +} diff --git a/vvp/vpi_signal.cc b/vvp/vpi_signal.cc index eacbf1732..cea4c9882 100644 --- a/vvp/vpi_signal.cc +++ b/vvp/vpi_signal.cc @@ -728,23 +728,6 @@ static vvp_vector4_t from_stringval(const char*str, unsigned wid) return val; } -static vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar) -{ - switch(scalar) { - case vpi0: - return BIT4_0; - case vpi1: - return BIT4_1; - case vpiX: - return BIT4_X; - case vpiZ: - return BIT4_Z; - default: - fprintf(stderr, "Unsupported scalar value %d.\n", scalar); - assert(0); - } -} - static vpiHandle signal_put_value(vpiHandle ref, s_vpi_value*vp, int flags) { unsigned wid; diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index cead9a17b..a9d9df79c 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -130,6 +130,23 @@ vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c) } } +vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar) +{ + switch(scalar) { + case vpi0: + return BIT4_0; + case vpi1: + return BIT4_1; + case vpiX: + return BIT4_X; + case vpiZ: + return BIT4_Z; + default: + fprintf(stderr, "Unsupported scalar value %d.\n", scalar); + assert(0); + } +} + vvp_bit4_t operator ^ (vvp_bit4_t a, vvp_bit4_t b) { if (bit4_is_xz(a)) @@ -1611,6 +1628,15 @@ void vvp_vector2_t::trim() while (value(wid_-1) == 0 && wid_ > 1) wid_ -= 1; } +/* This is a special trim that is used on numbers we know represent a + * negative signed value (they came from a negative real value). */ +void vvp_vector2_t::trim_neg() +{ + if (value(wid_-1) == 1 && wid_ > 32) { + while (value(wid_-2) == 1 && wid_ > 32) wid_ -= 1; + } +} + int vvp_vector2_t::value(unsigned idx) const { if (idx >= wid_) diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 6e6497179..1297564e8 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -20,6 +20,7 @@ */ # include "config.h" +# include "vpi_user.h" # include # include # include @@ -67,6 +68,8 @@ inline char vvp_bit4_to_ascii(vvp_bit4_t a) { return "01zx"[a]; } extern vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c); +extern vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar); + /* Return TRUE if the bit is BIT4_X or BIT4_Z. The fast implementation here relies on the encoding of vvp_bit4_t values. */ inline bool bit4_is_xz(vvp_bit4_t a) { return a >= 2; } @@ -470,6 +473,9 @@ class vvp_vector2_t { void set_bit(unsigned idx, int bit); // Make the size just big enough to hold the first 1 bit. void trim(); + // Trim off extra 1 bit since this is representing a negative value. + // Always keep at least 32 bits. + void trim_neg(); private: enum { BITS_PER_WORD = 8 * sizeof(unsigned long) }; From 11109f519c15fb10e62faf3c0e020db9274af1eb Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 19 Aug 2008 18:39:49 -0700 Subject: [PATCH 08/11] Push the automatic property for tasks and functions to the code gen. This patch pushes the automatic property for both tasks and functions to the code generators. The vvp back end does not currently support this so it will error out during code generation. The VHDL back end should be able to use this property and tgt-stub prints the property. Having this will also make it easier when we do adding this to the runtime. --- PFunction.cc | 3 ++- PTask.cc | 31 ++----------------------------- PTask.h | 10 ++++++++-- design_dump.cc | 1 + elab_scope.cc | 2 ++ ivl.def | 1 + ivl_target.h | 4 ++++ net_scope.cc | 2 ++ netlist.h | 4 ++++ parse.y | 18 +++++++----------- pform.cc | 14 ++++++++------ pform.h | 4 ++-- pform_dump.cc | 4 ++++ t-dll-api.cc | 6 ++++++ t-dll.cc | 4 +++- t-dll.h | 1 + tgt-stub/stub.c | 5 +++-- tgt-vvp/vvp_scope.c | 7 +++++++ 18 files changed, 67 insertions(+), 54 deletions(-) diff --git a/PFunction.cc b/PFunction.cc index fb26c434b..1a3146a8a 100644 --- a/PFunction.cc +++ b/PFunction.cc @@ -21,9 +21,10 @@ #include "PTask.h" -PFunction::PFunction(perm_string name, PScope*parent) +PFunction::PFunction(perm_string name, PScope*parent, bool is_auto) : PScope(name, parent), ports_(0), statement_(0) { + is_auto_ = is_auto; return_type_.type = PTF_NONE; } diff --git a/PTask.cc b/PTask.cc index b829e03f2..3719c4b3c 100644 --- a/PTask.cc +++ b/PTask.cc @@ -21,9 +21,10 @@ # include "PTask.h" -PTask::PTask(perm_string name, PScope*parent) +PTask::PTask(perm_string name, PScope*parent, bool is_auto) : PScope(name, parent), ports_(0), statement_(0) { + is_auto_ = is_auto; } PTask::~PTask() @@ -41,31 +42,3 @@ void PTask::set_statement(Statement*s) assert(statement_ == 0); statement_ = s; } - - -/* - * $Log: PTask.cc,v $ - * Revision 1.7 2002/08/12 01:34:58 steve - * conditional ident string using autoconfig. - * - * Revision 1.6 2001/07/25 03:10:48 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.5 2001/04/19 03:04:47 steve - * Spurious assert of empty statemnt. - * - * Revision 1.4 2001/01/13 22:20:08 steve - * Parse parameters within nested scopes. - * - * Revision 1.3 2000/02/23 02:56:53 steve - * Macintosh compilers do not support ident. - * - * Revision 1.2 1999/07/24 02:11:19 steve - * Elaborate task input ports. - * - * Revision 1.1 1999/07/03 02:12:51 steve - * Elaborate user defined tasks. - * - */ - diff --git a/PTask.h b/PTask.h index 4d9c24e9c..de05ac053 100644 --- a/PTask.h +++ b/PTask.h @@ -51,7 +51,7 @@ struct PTaskFuncArg { class PTask : public PScope, public LineInfo { public: - explicit PTask(perm_string name, PScope*parent); + explicit PTask(perm_string name, PScope*parent, bool is_auto); ~PTask(); void set_ports(svector*p); @@ -69,11 +69,14 @@ class PTask : public PScope, public LineInfo { // Elaborate the statement to finish off the task definition. void elaborate(Design*des, NetScope*scope) const; + bool is_auto() const { return is_auto_; }; + void dump(ostream&, unsigned) const; private: svector*ports_; Statement*statement_; + bool is_auto_; private: // Not implemented PTask(const PTask&); @@ -90,7 +93,7 @@ class PTask : public PScope, public LineInfo { class PFunction : public PScope, public LineInfo { public: - explicit PFunction(perm_string name, PScope*parent); + explicit PFunction(perm_string name, PScope*parent, bool is_auto); ~PFunction(); void set_ports(svector*p); @@ -105,12 +108,15 @@ class PFunction : public PScope, public LineInfo { /* Elaborate the behavioral statement. */ void elaborate(Design *des, NetScope*) const; + bool is_auto() const { return is_auto_; }; + void dump(ostream&, unsigned) const; private: PTaskFuncArg return_type_; svector *ports_; Statement *statement_; + bool is_auto_; }; #endif diff --git a/design_dump.cc b/design_dump.cc index ca8e71d8e..1af627745 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -998,6 +998,7 @@ void NetScope::dump(ostream&o) const o << " generate block"; break; } + if (is_auto()) o << " (automatic)"; o << endl; for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) diff --git a/elab_scope.cc b/elab_scope.cc index b77292dea..5df05b7bc 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -150,6 +150,7 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope, } NetScope*task_scope = new NetScope(scope, use_name, NetScope::TASK); + task_scope->is_auto((*cur).second->is_auto()); task_scope->set_line((*cur).second); if (debug_scopes) @@ -179,6 +180,7 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope, } NetScope*func_scope = new NetScope(scope, use_name, NetScope::FUNC); + func_scope->is_auto((*cur).second->is_auto()); func_scope->set_line((*cur).second); if (debug_scopes) diff --git a/ivl.def b/ivl.def index dfcf064cf..22260e7e9 100644 --- a/ivl.def +++ b/ivl.def @@ -143,6 +143,7 @@ ivl_scope_def_lineno ivl_scope_event ivl_scope_events ivl_scope_file +ivl_scope_is_auto ivl_scope_lineno ivl_scope_logs ivl_scope_log diff --git a/ivl_target.h b/ivl_target.h index 7535bd0d4..f04d90f8a 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1447,6 +1447,9 @@ extern unsigned ivl_parameter_lineno(ivl_parameter_t net); * ivl_scope_lineno * Returns the instantiation file and line for this scope. * + * ivl_scope_is_auto + * Is the task or function declared to be automatic? + * * ivl_scope_var * ivl_scope_vars * REMOVED @@ -1523,6 +1526,7 @@ extern unsigned ivl_scope_def_lineno(ivl_scope_t net); extern unsigned ivl_scope_events(ivl_scope_t net); extern ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx); extern const char* ivl_scope_file(ivl_scope_t net); +extern unsigned ivl_scope_is_auto(ivl_scope_t net); extern unsigned ivl_scope_lineno(ivl_scope_t net); extern unsigned ivl_scope_logs(ivl_scope_t net); extern ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx); diff --git a/net_scope.cc b/net_scope.cc index 2b8baf0cb..d39152cdc 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -41,6 +41,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) signals_ = 0; events_ = 0; lcounter_ = 0; + is_auto_ = false; if (up) { default_nettype_ = up->default_nettype(); @@ -261,6 +262,7 @@ NetFuncDef* NetScope::func_def() assert( type_ == FUNC ); return func_; } + bool NetScope::in_func() { return (type_ == FUNC) ? true : false; diff --git a/netlist.h b/netlist.h index 05f151d10..141c3d909 100644 --- a/netlist.h +++ b/netlist.h @@ -692,6 +692,9 @@ class NetScope : public Attrib { unsigned get_def_lineno() const { return def_lineno_; }; bool in_func(); + /* Is the task or function automatic. */ + void is_auto(bool is_auto) { is_auto_ = is_auto; }; + bool is_auto() const { return is_auto_; }; const NetTaskDef* task_def() const; const NetFuncDef* func_def() const; @@ -835,6 +838,7 @@ class NetScope : public Attrib { NetScope*sub_; unsigned lcounter_; + bool is_auto_; }; /* diff --git a/parse.y b/parse.y index 8f180090d..4b2dae490 100644 --- a/parse.y +++ b/parse.y @@ -239,7 +239,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %type from_exclude %type number -%type signed_opt udp_reg_opt edge_operator +%type signed_opt udp_reg_opt edge_operator automatic_opt %type drive_strength drive_strength_opt dr_strength0 dr_strength1 %type udp_input_sym udp_output_sym %type udp_input_list udp_sequ_entry udp_comb_entry @@ -2096,7 +2096,7 @@ module_item | K_task automatic_opt IDENTIFIER ';' { assert(current_task == 0); - current_task = pform_push_task_scope($3); + current_task = pform_push_task_scope($3, $2); FILE_NAME(current_task, @1); } task_item_list_opt @@ -2111,7 +2111,7 @@ module_item | K_task automatic_opt IDENTIFIER { assert(current_task == 0); - current_task = pform_push_task_scope($3); + current_task = pform_push_task_scope($3, $2); FILE_NAME(current_task, @1); } '(' task_port_decl_list ')' ';' @@ -2138,7 +2138,7 @@ module_item | K_function automatic_opt function_range_or_type_opt IDENTIFIER ';' { assert(current_function == 0); - current_function = pform_push_function_scope($4); + current_function = pform_push_function_scope($4, $2); FILE_NAME(current_function, @1); } function_item_list statement @@ -2153,7 +2153,7 @@ module_item | K_function automatic_opt function_range_or_type_opt IDENTIFIER { assert(current_function == 0); - current_function = pform_push_function_scope($4); + current_function = pform_push_function_scope($4, $2); FILE_NAME(current_function, @1); } '(' task_port_decl_list ')' ';' @@ -2279,12 +2279,8 @@ module_item ; automatic_opt - : K_automatic - { yyerror(@1, "sorry: automatic tasks/functions are not " - "currently supported."); - yyerrok; - } - | {} + : K_automatic { $$ = true; } + | { $$ = false;} ; generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } diff --git a/pform.cc b/pform.cc index 6b3612a06..7057376cc 100644 --- a/pform.cc +++ b/pform.cc @@ -103,17 +103,18 @@ void pform_pop_scope() } } -PTask* pform_push_task_scope(char*name) +PTask* pform_push_task_scope(char*name, bool is_auto) { perm_string task_name = lex_strings.make(name); PTask*task; if (pform_cur_generate) { - task = new PTask(task_name, pform_cur_generate->lexical_scope); + task = new PTask(task_name, pform_cur_generate->lexical_scope, + is_auto); pform_cur_generate->tasks[task->pscope_name()] = task; pform_cur_generate->lexical_scope = task; } else { - task = new PTask(task_name, lexical_scope); + task = new PTask(task_name, lexical_scope, is_auto); pform_cur_module->tasks[task->pscope_name()] = task; lexical_scope = task; } @@ -121,17 +122,18 @@ PTask* pform_push_task_scope(char*name) return task; } -PFunction* pform_push_function_scope(char*name) +PFunction* pform_push_function_scope(char*name, bool is_auto) { perm_string func_name = lex_strings.make(name); PFunction*func; if (pform_cur_generate) { - func = new PFunction(func_name, pform_cur_generate->lexical_scope); + func = new PFunction(func_name, pform_cur_generate->lexical_scope, + is_auto); pform_cur_generate->funcs[func->pscope_name()] = func; pform_cur_generate->lexical_scope = func; } else { - func = new PFunction(func_name, lexical_scope); + func = new PFunction(func_name, lexical_scope, is_auto); pform_cur_module->funcs[func->pscope_name()] = func; lexical_scope = func; } diff --git a/pform.h b/pform.h index a94be9f96..ae410c8e7 100644 --- a/pform.h +++ b/pform.h @@ -175,8 +175,8 @@ extern void pform_make_udp(perm_string name, */ extern void pform_pop_scope(); -extern PTask*pform_push_task_scope(char*name); -extern PFunction*pform_push_function_scope(char*name); +extern PTask*pform_push_task_scope(char*name, bool is_auto); +extern PFunction*pform_push_function_scope(char*name, bool is_auto); extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt); diff --git a/pform_dump.cc b/pform_dump.cc index ba6e60912..f506bd655 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -715,6 +715,7 @@ void PForStatement::dump(ostream&out, unsigned ind) const void PFunction::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "function "; + if (is_auto_) cout << "automatic "; switch (return_type_.type) { case PTF_NONE: out << "?none? "; @@ -775,6 +776,9 @@ void PRepeat::dump(ostream&out, unsigned ind) const void PTask::dump(ostream&out, unsigned ind) const { + out << setw(ind) << "" << "task "; + if (is_auto_) cout << "automatic "; + out << pscope_name() << ";" << endl; if (ports_) for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) { out << setw(ind) << ""; diff --git a/t-dll-api.cc b/t-dll-api.cc index e9db3bf5e..379053b45 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1547,6 +1547,12 @@ extern "C" const char*ivl_scope_file(ivl_scope_t net) return net->file.str(); } +extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net) +{ + assert(net); + return net->is_auto; +} + extern "C" unsigned ivl_scope_lineno(ivl_scope_t net) { assert(net); diff --git a/t-dll.cc b/t-dll.cc index e11692145..98ade6c15 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -575,6 +575,7 @@ void dll_target::add_root(ivl_design_s &des_, const NetScope *s) root_->time_units = s->time_unit(); root_->nattr = s->attr_cnt(); root_->attr = fill_in_attributes(s); + root_->is_auto = 0; des_.nroots_++; if (des_.roots_) @@ -2319,7 +2320,8 @@ void dll_target::scope(const NetScope*net) scope->time_precision = net->time_precision(); scope->time_units = net->time_unit(); scope->nattr = net->attr_cnt(); - scope->attr = fill_in_attributes(net); + scope->attr = fill_in_attributes(net); + scope->is_auto = net->is_auto(); switch (net->type()) { case NetScope::MODULE: diff --git a/t-dll.h b/t-dll.h index c409a6ce9..f5d6fd4b7 100644 --- a/t-dll.h +++ b/t-dll.h @@ -590,6 +590,7 @@ struct ivl_scope_s { /* Scopes that are tasks/functions have a definition. */ ivl_statement_t def; + unsigned is_auto; unsigned ports; ivl_signal_t*port; diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 927312765..aba418c14 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -1448,12 +1448,13 @@ static int show_scope(ivl_scope_t net, void*x) ivl_scope_name(net), ivl_scope_params(net), ivl_scope_sigs(net), ivl_scope_logs(net)); + char *is_auto = ivl_scope_is_auto(net) ? "automatic " : ""; switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: fprintf(out, " module %s", ivl_scope_tname(net)); break; case IVL_SCT_FUNCTION: - fprintf(out, " function %s", ivl_scope_tname(net)); + fprintf(out, " function %s%s", is_auto, ivl_scope_tname(net)); break; case IVL_SCT_BEGIN: fprintf(out, " begin : %s", ivl_scope_tname(net)); @@ -1462,7 +1463,7 @@ static int show_scope(ivl_scope_t net, void*x) fprintf(out, " fork : %s", ivl_scope_tname(net)); break; case IVL_SCT_TASK: - fprintf(out, " task %s", ivl_scope_tname(net)); + fprintf(out, " task %s%s", is_auto, ivl_scope_tname(net)); break; default: fprintf(out, " type(%u) %s", ivl_scope_type(net), diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 42f378711..433263b24 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1769,6 +1769,13 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) { unsigned idx; const char *type; + /* For now we do not support automatic tasks or functions. */ + if (ivl_scope_is_auto(net)) { + fprintf(stderr, "%s:%u: vvp-tgt sorry: automatic tasks/functions " + "are not supported!\n", + ivl_scope_def_file(net), ivl_scope_def_lineno(net)); + exit(1); + } switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: type = "module"; break; case IVL_SCT_FUNCTION: type = "function"; break; From 28991d30f489363428d55f478266e6933966da98 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Wed, 20 Aug 2008 09:40:16 -0700 Subject: [PATCH 09/11] Fix some ambiguous casts. --- elab_expr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 2939b91d9..7f42ef743 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -510,7 +510,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w delete tmp; } - verinum val (sub_expr_width, 8*sizeof(unsigned)); + verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned)); NetEConst*sub = new NetEConst(val); sub->set_line(*this); @@ -1494,7 +1494,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, // Recalculate the constant address with the adjusted base. unsigned use_addr = net->array_index_to_address(addr); if (addr < 0 || use_addr != (unsigned long)addr) { - verinum val (use_addr, 8*sizeof(use_addr)); + verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr)); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); delete word_index; From 7eb34013dd4abac6ed57cbfd5945f8932f0bba0a Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 20 Aug 2008 10:04:24 -0700 Subject: [PATCH 10/11] Add vpip_calc_clog2 to vvp.def file This is needed to get cygwin to compile correctly. --- vvp/vvp.def | 1 + 1 file changed, 1 insertion(+) diff --git a/vvp/vvp.def b/vvp/vvp.def index ad3990f05..1ec96567d 100644 --- a/vvp/vvp.def +++ b/vvp/vvp.def @@ -37,3 +37,4 @@ vpi_vprintf vpip_format_strength vpip_set_return_value +vpip_calc_clog2 From b92b62a8fb7dfbe51d8cbc635b9bced74fcd9a35 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Wed, 20 Aug 2008 21:42:51 +0100 Subject: [PATCH 11/11] Include cstring required for GCC 4.3 GCC 4.3 has tightened up header dependencies so that `strcmp' is no longer declared if is included (which it was in prior versions). This causes the changes in 5e512e65704728b52e28448b4c18c70815a5b4b0 to fail to build with 4.3. This patch includes in the files that are failing. --- PExpr.cc | 1 + elab_pexpr.cc | 1 + eval_tree.cc | 1 + 3 files changed, 3 insertions(+) diff --git a/PExpr.cc b/PExpr.cc index 880dcc8c2..751d2be5f 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -20,6 +20,7 @@ # include "config.h" # include +# include # include "PExpr.h" # include "Module.h" diff --git a/elab_pexpr.cc b/elab_pexpr.cc index 59f80dd80..e4cdbe087 100644 --- a/elab_pexpr.cc +++ b/elab_pexpr.cc @@ -25,6 +25,7 @@ # include "netmisc.h" # include +# include # include # include "ivl_assert.h" diff --git a/eval_tree.cc b/eval_tree.cc index e50005fdf..7559c36ad 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -22,6 +22,7 @@ # include # include +# include # include # include "netlist.h"