From fb63bf7dbaa62b1baaaca1b92cc0d8dd4c381b8e Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Tue, 5 Feb 2008 20:36:57 -0800 Subject: [PATCH 1/5] Compile portability issues. --- design_dump.cc | 19 +++++++++++++------ driver/cfparse_misc.h | 3 --- vvp/vpi_priv.h | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index 427a9a8da..301c9301e 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -465,9 +465,14 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const break; } o << setw(ind) << "" << "NetPartSelect(" << pt << "): " - << name() << " #(" << rise_time() - << "," << fall_time() << "," << decay_time() << ") " - << " off=" << off_ << " wid=" << wid_ < struct __vpiHandle*vpi_handle(T obj) +template vpiHandle vpi_handle(T obj) { return &obj->base; } /* From 3a300725de75ed111f3bf9c844d72e254217d4b1 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Wed, 6 Feb 2008 18:37:42 -0800 Subject: [PATCH 2/5] Output delays for part select nets. Implement net delays for part select devices. --- tgt-vvp/vvp_scope.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index b2f73350e..4ff861162 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -2143,17 +2143,19 @@ static void draw_lpm_part(ivl_lpm_t net) unsigned width, base; ivl_nexus_t sel; + const char*dly = draw_lpm_output_delay(net); + width = ivl_lpm_width(net); base = ivl_lpm_base(net); sel = ivl_lpm_data(net,1); if (sel == 0) { - fprintf(vvp_out, "L_%p .part %s", - net, draw_net_input(ivl_lpm_data(net, 0))); + fprintf(vvp_out, "L_%p%s .part %s", + net, dly, draw_net_input(ivl_lpm_data(net, 0))); fprintf(vvp_out, ", %u, %u;\n", base, width); } else { - fprintf(vvp_out, "L_%p .part/v %s", - net, draw_net_input(ivl_lpm_data(net,0))); + fprintf(vvp_out, "L_%p%s .part/v %s", + net, dly, draw_net_input(ivl_lpm_data(net,0))); fprintf(vvp_out, ", %s", draw_net_input(sel)); fprintf(vvp_out, ", %u;\n", width); } From bc1d3eb7cd009053f00d16bd33547bf390564b5e Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 9 Feb 2008 22:19:42 -0800 Subject: [PATCH 3/5] Add support for generate case Generate case is a complex generate scheme where the items are sub-schemes of the case generate itself. The parser handles them something like nested generate statements, but storing the case guards as the test expression. Then the elaborator notes the case scheme and reaches into the case item schemes inside to make up tests, select the generate item, and elaborate. --- PGenerate.h | 16 ++++++++-- elab_scope.cc | 86 +++++++++++++++++++++++++++++++++++++++++++++++---- elaborate.cc | 21 +++++++++++++ parse.y | 18 +++++++++++ pform.cc | 53 +++++++++++++++++++++++++++++-- pform.h | 2 ++ pform_dump.cc | 11 ++++++- 7 files changed, 196 insertions(+), 11 deletions(-) diff --git a/PGenerate.h b/PGenerate.h index 39be19f47..b29c60b25 100644 --- a/PGenerate.h +++ b/PGenerate.h @@ -37,7 +37,17 @@ class PGate; class PWire; /* - * This represents a generate scheme. + * This represents a generate scheme. The interpretation of the + * members depends on the scheme_type. + * + * GS_LOOP + * + * GS_CASE + * loop_test is the expression to be compared. + * generates contains only GS_CASE_ITEM schemes. + * GS_CASE_ITEM + * The parent points to the GS_CASE that contains this item. + * the loop_test is compared with the parent->loop_test expression. */ class PGenerate : public LineInfo { @@ -50,7 +60,8 @@ class PGenerate : public LineInfo { const unsigned id_number; perm_string scope_name; - enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE}; + enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE, + GS_CASE, GS_CASE_ITEM}; scheme_t scheme_type; // generate loops have an index variable and three @@ -88,6 +99,7 @@ class PGenerate : public LineInfo { private: bool generate_scope_loop_(Design*des, NetScope*container); bool generate_scope_condit_(Design*des, NetScope*container, bool else_flag); + bool generate_scope_case_(Design*des, NetScope*container); // Elaborate_scope within a generated scope. void elaborate_subscope_(Design*des, NetScope*scope); diff --git a/elab_scope.cc b/elab_scope.cc index 7cc2eeddd..c954da025 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -315,6 +315,15 @@ bool PGenerate::generate_scope(Design*des, NetScope*container) case GS_ELSE: return generate_scope_condit_(des, container, true); + case GS_CASE: + return generate_scope_case_(des, container); + return true; + + case GS_CASE_ITEM: + cerr << get_fileline() << ": internal error: " + << "Case item outside of a case generate scheme?" << endl; + return false; + default: cerr << get_fileline() << ": sorry: Generate of this sort" << " is not supported yet!" << endl; @@ -345,7 +354,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) genvar = init->value().as_long(); delete init_ex; - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; @@ -367,7 +376,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) des->errors += 1; return false; } - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create generated scope " << use_name << endl; @@ -386,7 +395,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) genvar_verinum); scope->set_localparam(loop_index, gp); - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create implicit localparam " << loop_index << " = " << genvar_verinum << endl; @@ -398,7 +407,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); NetEConst*step = dynamic_cast(step_ex); assert(step); - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: genvar step from " << genvar << " to " << step->value().as_long() << endl; @@ -428,7 +437,7 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else // scope. if ( (test->value().as_long() == 0 && !else_flag) || (test->value().as_long() != 0 && else_flag) ) { - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " value=" << test->value() << ": skip generation" @@ -445,7 +454,7 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else des->errors += 1; return false; } - if (debug_elaborate) + if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") << " value=" << test->value() << ": Generate scope=" @@ -459,6 +468,71 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else return true; } +bool PGenerate::generate_scope_case_(Design*des, NetScope*container) +{ + NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); + NetEConst*case_value_co = dynamic_cast(case_value_ex); + assert(case_value_co); + + // The name of the scope to generate, whatever that item is. + hname_t use_name (scope_name); + + if (debug_scopes) + cerr << get_fileline() << ": debug: Generate case " + << "switch value=" << case_value_co->value() << endl; + + PGenerate*default_item = 0; + + typedef list::const_iterator generator_it_t; + generator_it_t cur = generates.begin(); + while (cur != generates.end()) { + PGenerate*item = *cur; + assert( item->scheme_type == PGenerate::GS_CASE_ITEM ); + + // Detect that the item is a default. + if (item->loop_test == 0) { + default_item = item; + cur ++; + continue; + } + + NetExpr*item_value_ex = elab_and_eval(des, container, item->loop_test, -1); + NetEConst*item_value_co = dynamic_cast(item_value_ex); + assert(item_value_co); + + // If we stumble on the item that matches, then break + // out now. + if (case_value_co->value() == item_value_co->value()) { + delete item_value_co; + break; + } + + delete item_value_co; + cur ++; + } + + delete case_value_co; + case_value_co = 0; + + PGenerate*item = (cur == generates.end())? default_item : *cur; + if (item == 0) { + cerr << get_fileline() << ": debug: " + << "No generate items found" << endl; + return true; + } + + if (debug_scopes) + cerr << get_fileline() << ": debug: " + << "Generate case matches item at " + << item->get_fileline() << endl; + + NetScope*scope = new NetScope(container, use_name, + NetScope::GENBLOCK); + item->elaborate_subscope_(des, scope); + + return true; +} + void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) { // Scan the generated scope for nested generate schemes, diff --git a/elaborate.cc b/elaborate.cc index 0845329e5..50481b044 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3554,6 +3554,27 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const { bool flag = true; + // Handle the special case that this is a CASE scheme. In this + // case the PGenerate itself does not have the generated + // item. Look instead for the case ITEM that has a scope + // generated for it. + if (scheme_type == PGenerate::GS_CASE) { + if (debug_elaborate) + cerr << get_fileline() << ": debug: generate case" + << " elaborating in scope " + << scope_path(container) << "." << endl; + + typedef list::const_iterator generate_it_t; + for (generate_it_t cur = generates.begin() + ; cur != generates.end() ; cur ++) { + PGenerate*item = *cur; + if (! item->scope_list_.empty()) { + flag &= item->elaborate(des, container); + } + } + return flag; + } + typedef list::const_iterator scope_list_it_t; for (scope_list_it_t cur = scope_list_.begin() ; cur != scope_list_.end() ; cur ++ ) { diff --git a/parse.y b/parse.y index a5d0679fc..818372c7c 100644 --- a/parse.y +++ b/parse.y @@ -1920,6 +1920,12 @@ module_item generate_block_opt %prec less_than_K_else { pform_endgenerate(); } + | K_case '(' expression ')' + { pform_start_generate_case(@1, $3); } + generate_case_items + K_endcase + { pform_endgenerate(); } + /* specify blocks are parsed but ignored. */ | K_specify K_endspecify @@ -1984,6 +1990,18 @@ module_item generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } +generate_case_items + : generate_case_items generate_case_item + | generate_case_item + ; + +generate_case_item + : expression ':' { pform_generate_case_item(@1, $1); } generate_block + { pform_endgenerate(); } + | K_default ':' { pform_generate_case_item(@1, 0); } generate_block + { pform_endgenerate(); } + ; + module_item_list : module_item_list module_item | module_item diff --git a/pform.cc b/pform.cc index 3da43e8e4..425a2162e 100644 --- a/pform.cc +++ b/pform.cc @@ -412,6 +412,51 @@ void pform_start_generate_else(const struct vlltype&li) pform_cur_generate->loop_step = 0; } +/* + * The GS_CASE version of the PGenerate contains only case items. The + * items in turn contain the generated items themselves. + */ +void pform_start_generate_case(const struct vlltype&li, PExpr*expr) +{ + PGenerate*gen = new PGenerate(scope_generate_counter++); + + FILE_NAME(gen, li); + + gen->parent = pform_cur_generate; + pform_cur_generate = gen; + + pform_cur_generate->scheme_type = PGenerate::GS_CASE; + + pform_cur_generate->loop_init = 0; + pform_cur_generate->loop_test = expr; + pform_cur_generate->loop_step = 0; +} + +/* + * The generate case item is a special case schema that takes its id + * from the case schema that it is a part of. The idea is that the + * case schema can only instantiate exactly one item, so the items + * need not have a unique number. + */ +void pform_generate_case_item(const struct vlltype&li, PExpr*expr) +{ + assert(pform_cur_generate); + assert(pform_cur_generate->scheme_type == PGenerate::GS_CASE); + + PGenerate*gen = new PGenerate(pform_cur_generate->id_number); + + FILE_NAME(gen, li); + + gen->parent = pform_cur_generate; + pform_cur_generate = gen; + + pform_cur_generate->scheme_type = PGenerate::GS_CASE_ITEM; + + pform_cur_generate->loop_init = 0; + pform_cur_generate->loop_test = expr; + pform_cur_generate->loop_step = 0; +} + void pform_generate_block_name(char*name) { assert(pform_cur_generate != 0); @@ -439,10 +484,14 @@ void pform_endgenerate() PGenerate*cur = pform_cur_generate; pform_cur_generate = cur->parent; - if (pform_cur_generate != 0) + if (pform_cur_generate != 0) { + assert(cur->scheme_type == PGenerate::GS_CASE_ITEM + || pform_cur_generate->scheme_type != PGenerate::GS_CASE); pform_cur_generate->generates.push_back(cur); - else + } else { + assert(cur->scheme_type != PGenerate::GS_CASE_ITEM); pform_cur_module->generate_schemes.push_back(cur); + } } bool pform_expression_is_constant(const PExpr*ex) diff --git a/pform.h b/pform.h index 2ae8d0c57..5dc556f30 100644 --- a/pform.h +++ b/pform.h @@ -188,6 +188,8 @@ extern void pform_start_generate_for(const struct vlltype&li, PExpr*next); extern void pform_start_generate_if(const struct vlltype&li, PExpr*test); extern void pform_start_generate_else(const struct vlltype&li); +extern void pform_start_generate_case(const struct vlltype&lp, PExpr*test); +extern void pform_generate_case_item(const struct vlltype&lp, PExpr*test); extern void pform_generate_block_name(char*name); extern void pform_endgenerate(); diff --git a/pform_dump.cc b/pform_dump.cc index 6db7ccce1..addc8932c 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -854,6 +854,15 @@ void PGenerate::dump(ostream&out, unsigned indent) const case GS_ELSE: out << " else !(" << *loop_test << ")"; break; + case GS_CASE: + out << " case (" << *loop_test << ")"; + break; + case GS_CASE_ITEM: + if (loop_test) + out << " (" << *loop_test << ") == (" << *parent->loop_test << ")"; + else + out << " default:"; + break; } if (scope_name) @@ -882,7 +891,7 @@ void PGenerate::dump(ostream&out, unsigned indent) const (*idx)->dump(out, indent+2); } - out << " endgenerate" << endl; + out << setw(indent) << "" << "endgenerate" << endl; } void Module::dump(ostream&out) const From f2ff25bfefd2b1073f13120c3810d1893ea8fffc Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 9 Feb 2008 22:20:01 -0800 Subject: [PATCH 4/5] Dump delays of constants. --- design_dump.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/design_dump.cc b/design_dump.cc index 301c9301e..03821c044 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -348,7 +348,14 @@ void NetConst::dump_node(ostream&o, unsigned ind) const o << setw(ind) << "" << "constant " << width_ << "'b"; for (unsigned idx = width_ ; idx > 0 ; idx -= 1) o << value_[idx-1]; - o << ": " << name() << endl; + o << ": " << name(); + if (rise_time()) + o << " #(" << *rise_time() + << "," << *fall_time() + << "," << *decay_time() << ")"; + else + o << " #(.,.,.)"; + o << endl; dump_node_pins(o, ind+4); } From 331faa22173d8a3ede93a8117b35b9723155eb51 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 8 Feb 2008 17:32:57 -0800 Subject: [PATCH 5/5] Add signed bit based power to continuous assignments. This patch adds the power operator for signed bit based values in a continuous assignment. It also fixes a few other power expression width problems. The expression width is still not calculated correctly, since the correct method can produce huge possible bit widths. The result is currently limited to the width of the native long. This is because lround() is used to convert from a double to an integer. A check in the code generator protects the runtime from this limitation. --- elab_net.cc | 12 +++--------- net_expr.cc | 5 +++-- set_width.cc | 6 ++---- t-dll.cc | 1 + tgt-vvp/vvp_scope.c | 13 ++++++++++--- vvp/arith.cc | 34 ++++++++++++++++++++++++---------- vvp/arith.h | 4 +++- vvp/compile.cc | 19 +++++++++++++++---- vvp/compile.h | 2 +- vvp/lexor.lex | 1 + vvp/parse.y | 9 +++++++-- vvp/vvp_net.cc | 26 ++++++++++++++++++++++++++ vvp/vvp_net.h | 2 ++ 13 files changed, 98 insertions(+), 36 deletions(-) diff --git a/elab_net.cc b/elab_net.cc index 6ee8add8a..a2ff432cd 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -1142,14 +1142,6 @@ NetNet* PEBinary::elaborate_net_pow_(Design*des, NetScope*scope, // The power is signed if either its operands are signed. bool arith_is_signed = lsig->get_signed() || rsig->get_signed(); - /* For now we only support real values. */ - if (lsig->data_type() != IVL_VT_REAL && arith_is_signed) { - cerr << get_fileline() << ": sorry: Signed bit based power (**) is " - << "currently unsupported in continuous assignments." << endl; - des->errors += 1; - return 0; - } - unsigned rwidth = lwidth; if (rwidth == 0) { /* Reals are always 1 wide and lsig/rsig types match here. */ @@ -1157,7 +1149,9 @@ NetNet* PEBinary::elaborate_net_pow_(Design*des, NetScope*scope, rwidth = 1; lwidth = 1; } else { - /* Nothing for now. Need integer value.*/ + /* This is incorrect! a * (2^b - 1) is close. */ + rwidth = lsig->vector_width() + rsig->vector_width(); + lwidth = rwidth; } } diff --git a/net_expr.cc b/net_expr.cc index 0bd86acff..73dd924f6 100644 --- a/net_expr.cc +++ b/net_expr.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 @@ -258,7 +258,8 @@ NetEBPow::NetEBPow(char op, NetExpr*l, NetExpr*r) : NetEBinary(op, l, r) { assert(op == 'p'); - expr_width(l->expr_width()); + /* This is incorrect! a * (2^b - 1) is close. */ + expr_width(l->expr_width()+r->expr_width()); cast_signed(l->has_sign() || r->has_sign()); } diff --git a/set_width.cc b/set_width.cc index 4dcc72e44..8b3257340 100644 --- a/set_width.cc +++ b/set_width.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-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 @@ -185,8 +185,7 @@ bool NetEBMult::set_width(unsigned w, bool) bool NetEBPow::set_width(unsigned w, bool last_chance) { - bool flag = left_->set_width(w, last_chance); - return flag; + return w == expr_width(); } /* @@ -445,4 +444,3 @@ bool NetEUReduce::set_width(unsigned w, bool) { return w == 1; } - diff --git a/t-dll.cc b/t-dll.cc index 958cbdc46..2e2e06316 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1777,6 +1777,7 @@ void dll_target::lpm_pow(const NetPow*net) { ivl_lpm_t obj = new struct ivl_lpm_s; obj->type = IVL_LPM_POW; + FILE_NAME(obj, net); obj->name = net->name(); assert(net->scope()); obj->scope = find_scope(des_, net->scope()); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index b90ac15d8..aae63e41c 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1708,9 +1708,16 @@ static void draw_lpm_add(ivl_lpm_t net) case IVL_LPM_POW: if (dto == IVL_VT_REAL) type = "pow.r"; - else if (ivl_lpm_signed(net)) - assert(0); /* No support for signed bit based signals. */ - else + else if (ivl_lpm_signed(net)) { + type = "pow.s"; + if (width > 8*sizeof(long)) { + fprintf(stderr, "%s:%u: sorry (vvp-tgt): Signed power " + "result must be no more than %d bits.\n", + ivl_lpm_file(net), ivl_lpm_lineno(net), + 8*sizeof(long)); + exit(1); + } + } else type = "pow"; break; default: diff --git a/vvp/arith.cc b/vvp/arith.cc index a984d68d6..7ee51a451 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -400,8 +400,8 @@ void vvp_arith_mult::wide(vvp_ipoint_t base, bool push) // Power -vvp_arith_pow::vvp_arith_pow(unsigned wid) -: vvp_arith_(wid) +vvp_arith_pow::vvp_arith_pow(unsigned wid, bool signed_flag) +: vvp_arith_(wid), signed_flag_(signed_flag) { } @@ -413,17 +413,31 @@ void vvp_arith_pow::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) { dispatch_operand_(ptr, bit); - vvp_vector2_t a2 (op_a_); - vvp_vector2_t b2 (op_b_); + vvp_vector4_t res4; + if (signed_flag_) { + if (op_a_.has_xz() || op_b_.has_xz()) { + vvp_send_vec4(ptr.ptr()->out, x_val_); + return; + } - if (a2.is_NaN() || b2.is_NaN()) { - vvp_send_vec4(ptr.ptr()->out, x_val_); - return; + double ad, bd; + vector4_to_value(op_a_, ad, true); + vector4_to_value(op_b_, bd, true); + + res4 = double_to_vector4(pow(ad, bd), wid_); + } else { + vvp_vector2_t a2 (op_a_); + vvp_vector2_t b2 (op_b_); + + if (a2.is_NaN() || b2.is_NaN()) { + vvp_send_vec4(ptr.ptr()->out, x_val_); + return; + } + + vvp_vector2_t result = pow(a2, b2); + res4 = vector2_to_vector4(result, wid_); } - vvp_vector2_t result = pow(a2, b2); - - vvp_vector4_t res4 = vector2_to_vector4(result, wid_); vvp_send_vec4(ptr.ptr()->out, res4); } diff --git a/vvp/arith.h b/vvp/arith.h index bd238edc3..acc85b263 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -162,9 +162,11 @@ class vvp_arith_mult : public vvp_arith_ { class vvp_arith_pow : public vvp_arith_ { public: - explicit vvp_arith_pow(unsigned wid); + explicit vvp_arith_pow(unsigned wid, bool signed_flag); ~vvp_arith_pow(); void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit); + private: + bool signed_flag_; }; class vvp_arith_sub : public vvp_arith_ { diff --git a/vvp/compile.cc b/vvp/compile.cc index e7337d8bc..c826f5910 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -921,7 +921,10 @@ void compile_arith_div(char*label, long wid, bool signed_flag, assert( wid > 0 ); if (argc != 2) { - fprintf(stderr, "%s; .arith/div has wrong number of symbols\n", label); + char *suffix = ""; + if (signed_flag) suffix = ".s"; + fprintf(stderr, "%s; .arith/div%s has wrong number of " + "symbols\n", label, suffix); compile_errors += 1; return; } @@ -998,18 +1001,26 @@ void compile_arith_mult_r(char*label, unsigned argc, struct symb_s*argv) } -void compile_arith_pow(char*label, long wid, +void compile_arith_pow(char*label, long wid, bool signed_flag, unsigned argc, struct symb_s*argv) { assert( wid > 0 ); + /* For now we need to do a double to long cast, so the number + of bits is limited. This should be caught in the compiler. */ + if (signed_flag) { + assert( wid <= (long)(8*sizeof(long)) ); + } if (argc != 2) { - fprintf(stderr, "%s .arith/pow has wrong number of symbols\n", label); + char *suffix = ""; + if (signed_flag) suffix = ".s"; + fprintf(stderr, "%s .arith/pow%s has wrong number of " + "symbols\n", label, suffix); compile_errors += 1; return; } - vvp_arith_ *arith = new vvp_arith_pow(wid); + vvp_arith_ *arith = new vvp_arith_pow(wid, signed_flag); make_arith(arith, label, argc, argv); } diff --git a/vvp/compile.h b/vvp/compile.h index 92270a794..49da17e13 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -146,7 +146,7 @@ extern void compile_part_select_var(char*label, char*src, * This is called by the parser to make the various arithmetic and * comparison functors. */ -extern void compile_arith_pow(char*label, long width, +extern void compile_arith_pow(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_div(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 93641eb9d..c0513c65a 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -96,6 +96,7 @@ ".arith/mult.r" { return K_ARITH_MULT_R; } ".arith/pow" { return K_ARITH_POW; } ".arith/pow.r" { return K_ARITH_POW_R; } +".arith/pow.s" { return K_ARITH_POW_S; } ".arith/sub" { return K_ARITH_SUB; } ".arith/sub.r" { return K_ARITH_SUB_R; } ".arith/sum" { return K_ARITH_SUM; } diff --git a/vvp/parse.y b/vvp/parse.y index 7fda09d4d..9cd696016 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -68,7 +68,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_ALIAS K_ALIAS_S K_ALIAS_R %token K_ARITH_DIV K_ARITH_DIV_R K_ARITH_DIV_S K_ARITH_MOD K_ARITH_MOD_R %token K_ARITH_MULT K_ARITH_MULT_R K_ARITH_SUB K_ARITH_SUB_R -%token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R +%token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R K_ARITH_POW_S %token K_ARRAY K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_PORT %token K_CMP_EEQ K_CMP_EQ K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R %token K_CMP_GE K_CMP_GE_R K_CMP_GE_S K_CMP_GT K_CMP_GT_R K_CMP_GT_S @@ -288,7 +288,7 @@ statement | T_LABEL K_ARITH_POW T_NUMBER ',' symbols ';' { struct symbv_s obj = $5; - compile_arith_pow($1, $3, obj.cnt, obj.vect); + compile_arith_pow($1, $3, false, obj.cnt, obj.vect); } | T_LABEL K_ARITH_POW_R T_NUMBER ',' symbols ';' @@ -296,6 +296,11 @@ statement compile_arith_pow_r($1, obj.cnt, obj.vect); } + | T_LABEL K_ARITH_POW_S T_NUMBER ',' symbols ';' + { struct symbv_s obj = $5; + compile_arith_pow($1, $3, true, obj.cnt, obj.vect); + } + | T_LABEL K_ARITH_SUB T_NUMBER ',' symbols ';' { struct symbv_s obj = $5; compile_arith_sub($1, $3, obj.cnt, obj.vect); diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 2da64524a..8f54bf05f 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -697,6 +697,32 @@ ostream& operator<< (ostream&out, const vvp_vector4_t&that) return out; } +/* The width is guaranteed to not be larger than a long. + * If the double is outside the integer range (+/-) the + * largest/smallest integer value is returned. */ +vvp_vector4_t double_to_vector4(double val, unsigned wid) +{ + long span = 1l << (wid-1); + double dmin = -1l * span; + double dmax = span - 1l; + + if (val > dmax) val = dmax; + if (val < dmin) val = dmin; + + vvp_vector4_t res (wid); + long bits = lround(val); + for (unsigned idx = 0 ; idx < wid ; idx += 1) { + vvp_bit4_t bit = BIT4_0; + + if (bits & 1L) bit = BIT4_1; + + res.set_bit(idx, bit); + bits >>= 1; + } + + return res; +} + bool vector4_to_value(const vvp_vector4_t&vec, unsigned long&val) { unsigned long res = 0; diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 7f32ade93..8b163f803 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -258,6 +258,8 @@ extern vvp_bit4_t compare_gtge_signed(const vvp_vector4_t&a, vvp_bit4_t val_if_equal); template extern T coerce_to_width(const T&that, unsigned width); +extern vvp_vector4_t double_to_vector4(double val, unsigned wid); + /* * These functions extract the value of the vector as a native type, * if possible, and return true to indicate success. If the vector has