diff --git a/design_dump.cc b/design_dump.cc index 12c4a6e5a..82eb6919f 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -247,6 +247,15 @@ void NetObj::dump_obj_attr(ostream&o, unsigned ind) const } } +void NetAbs::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Absolute value (NetAbs): " << name() + << " width=" << width() << " pin_count=" << pin_count() + << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetAddSub::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Adder (NetAddSub): " << name() diff --git a/elab_net.cc b/elab_net.cc index c1b628e5c..1f0abdfba 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -3212,6 +3212,32 @@ NetNet* PEUnary::elaborate_net(Design*des, NetScope*scope, connect(gate->pin(0), sig->pin(0)); break; + case 'm': // abs(sub_sig) + // If this expression is self determined, get its width + // from the sub_expression. + if (owidth == 0) + owidth = sub_sig->vector_width(); + + if (sub_sig->vector_width() < owidth) + sub_sig = pad_to_width(des, sub_sig, owidth); + + sig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, owidth); + sig->set_line(*this); + sig->data_type(sub_sig->data_type()); + sig->local_flag(true); + + NetAbs*tmp = new NetAbs(scope, scope->local_symbol(), sub_sig->vector_width()); + tmp->set_line(*this); + des->add_node(tmp); + tmp->rise_time(rise); + tmp->fall_time(fall); + tmp->decay_time(decay); + + connect(tmp->pin(1), sub_sig->pin(0)); + connect(tmp->pin(0), sig->pin(0)); + break; + case 'N': // Reduction NOR case '!': // Reduction NOT reduction=true; rtype = NetUReduce::NOR; break; @@ -3290,7 +3316,7 @@ NetNet* PEUnary::elaborate_net(Design*des, NetScope*scope, break; default: - cerr << "internal error: Unhandled UNARY '" << op_ << "'" << endl; + cerr << get_fileline() << ": internal error: Unhandled UNARY '" << op_ << "'" << endl; sig = 0; } @@ -3465,6 +3491,18 @@ NetNet* PEUnary::elab_net_unary_real_(Design*des, NetScope*scope, des->errors += 1; break; + case 'm': // abs() + NetAbs*tmp = new NetAbs(scope, scope->local_symbol(), 1); + tmp->set_line(*this); + tmp->rise_time(rise); + tmp->fall_time(fall); + tmp->decay_time(decay); + des->add_node(tmp); + + connect(tmp->pin(0), sig->pin(0)); + connect(tmp->pin(1), sub_sig->pin(0)); + break; + case '-': NetAddSub*sub = new NetAddSub(scope, scope->local_symbol(), 1); sub->attribute(perm_string::literal("LPM_Direction"), diff --git a/emit.cc b/emit.cc index f94d0403b..c52190684 100644 --- a/emit.cc +++ b/emit.cc @@ -49,6 +49,12 @@ bool NetUDP::emit_node(struct target_t*tgt) const return true; } +bool NetAbs::emit_node(struct target_t*tgt) const +{ + tgt->lpm_abs(this); + return true; +} + bool NetAddSub::emit_node(struct target_t*tgt) const { tgt->lpm_add_sub(this); diff --git a/functor.cc b/functor.cc index f318ef80d..406d0ff9f 100644 --- a/functor.cc +++ b/functor.cc @@ -40,6 +40,10 @@ void functor_t::process(class Design*, class NetProcTop*) { } +void functor_t::lpm_abs(class Design*, class NetAbs*) +{ +} + void functor_t::lpm_add_sub(class Design*, class NetAddSub*) { } @@ -174,6 +178,11 @@ void NetNode::functor_node(Design*, functor_t*) { } +void NetAbs::functor_node(Design*des, functor_t*fun) +{ + fun->lpm_abs(des, this); +} + void NetAddSub::functor_node(Design*des, functor_t*fun) { fun->lpm_add_sub(des, this); diff --git a/functor.h b/functor.h index 31e204261..d3b9b5088 100644 --- a/functor.h +++ b/functor.h @@ -48,6 +48,9 @@ struct functor_t { /* This method is called for each process in the design. */ virtual void process(class Design*des, class NetProcTop*); + /* This method is called for each structural abs(). */ + virtual void lpm_abs(class Design*des, class NetAbs*); + /* This method is called for each structural adder. */ virtual void lpm_add_sub(class Design*des, class NetAddSub*); diff --git a/ivl_target.h b/ivl_target.h index 827901241..785fd0dcb 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -228,6 +228,7 @@ typedef enum ivl_logic_e { /* This is the type of an LPM object. */ typedef enum ivl_lpm_type_e { + IVL_LPM_ABS = 32, IVL_LPM_ADD = 0, IVL_LPM_ARRAY = 30, IVL_LPM_CONCAT = 16, diff --git a/netlist.cc b/netlist.cc index 4d7e8a9c8..45fee8f34 100644 --- a/netlist.cc +++ b/netlist.cc @@ -1046,6 +1046,24 @@ const verinum& NetFF::sset_value() const } +NetAbs::NetAbs(NetScope*s, perm_string n, unsigned w) +: NetNode(s, n, 2), width_(w) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("Result"), 0); + pin(1).set_dir(Link::INPUT); + pin(1).set_name(perm_string::literal("DataA"), 0); +} + +NetAbs::~NetAbs() +{ +} + +unsigned NetAbs::width() const +{ + return width_; +} + /* * The NetAddSub class represents an LPM_ADD_SUB device. The pinout is * assigned like so: diff --git a/netlist.h b/netlist.h index b473050a4..38859ccf8 100644 --- a/netlist.h +++ b/netlist.h @@ -578,6 +578,27 @@ class NetNet : public NetObj { vector delay_paths_; }; +/* + * This class implements the LPM_ABS component. The node has a single + * input, a signe expression, that it converts to the absolute + * value. The gate is simple: pin(0) is the output and pin(1) is the input. + */ +class NetAbs : public NetNode { + + public: + NetAbs(NetScope*s, perm_string n, unsigned width); + ~NetAbs(); + + unsigned width() const; + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + virtual void functor_node(Design*des, functor_t*fun); + + private: + unsigned width_; +}; + /* * This class implements the LPM_ADD_SUB component as described in the * EDIF LPM Version 2 1 0 standard. It is used as a structural diff --git a/t-dll-api.cc b/t-dll-api.cc index 4d3a90fef..b7eb79e26 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -864,6 +864,10 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) { assert(net); switch (net->type) { + case IVL_LPM_ABS: + assert(idx == 0); + return net->u_.arith.a; + case IVL_LPM_ADD: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: @@ -1002,6 +1006,7 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) assert(net); switch (net->type) { + case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: @@ -1118,6 +1123,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) case IVL_LPM_FF: case IVL_LPM_MUX: return 0; + case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: diff --git a/t-dll.cc b/t-dll.cc index fd7ba497e..c7232e721 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1307,6 +1307,38 @@ void dll_target::udp(const NetUDP*net) scope_add_logic(scope, obj); } +void dll_target::lpm_abs(const NetAbs*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_ABS; + obj->name = net->name(); // NetAddSub names are permallocated. + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->u_.arith.signed_flag = 0; + obj->width = net->width(); + + const Nexus*nex; + /* the output is pin(0) */ + nex = net->pin(0).nexus(); + assert(nex->t_cookie()); + + obj->u_.arith.q = nex->t_cookie(); + nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + + nex = net->pin(1).nexus(); + assert(nex->t_cookie()); + + /* pin(1) is the input data. */ + obj->u_.arith.a = nex->t_cookie(); + nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + make_lpm_delays_(obj, net); + + scope_add_lpm(obj->scope, obj); +} + void dll_target::lpm_add_sub(const NetAddSub*net) { ivl_lpm_t obj = new struct ivl_lpm_s; diff --git a/t-dll.h b/t-dll.h index d1e68eaad..76b2443f5 100644 --- a/t-dll.h +++ b/t-dll.h @@ -70,6 +70,7 @@ struct dll_target : public target_t, public expr_scan_t { bool ureduce(const NetUReduce*); void net_case_cmp(const NetCaseCmp*); void udp(const NetUDP*); + void lpm_abs(const NetAbs*); void lpm_add_sub(const NetAddSub*); bool lpm_array_dq(const NetArrayDq*); void lpm_clshift(const NetCLShift*); diff --git a/target.cc b/target.cc index 9c1f88b95..379a91754 100644 --- a/target.cc +++ b/target.cc @@ -81,6 +81,12 @@ bool target_t::ureduce(const NetUReduce*) return false; } +void target_t::lpm_abs(const NetAbs*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetAbs." << endl; +} + void target_t::lpm_add_sub(const NetAddSub*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index e6e1b8546..c68ffc2f0 100644 --- a/target.h +++ b/target.h @@ -68,6 +68,7 @@ struct target_t { virtual bool func_def(const NetScope*); /* LPM style components are handled here. */ + virtual void lpm_abs(const NetAbs*); virtual void lpm_add_sub(const NetAddSub*); virtual bool lpm_array_dq(const NetArrayDq*); virtual void lpm_clshift(const NetCLShift*); diff --git a/tgt-stub/expression.c b/tgt-stub/expression.c index 2de7b787e..fe197c30a 100644 --- a/tgt-stub/expression.c +++ b/tgt-stub/expression.c @@ -160,6 +160,28 @@ static void show_ternary_expression(ivl_expr_t net, unsigned ind) } } +void show_unary_expression(ivl_expr_t net, unsigned ind) +{ + unsigned width = ivl_expr_width(net); + const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; + const char*vt = vt_type_string(net); + + char name[8]; + switch (ivl_expr_opcode(net)) { + default: + snprintf(name, sizeof name, "%c", ivl_expr_opcode(net)); + break; + + case 'm': + snprintf(name, sizeof name, "abs()"); + break; + } + + fprintf(out, "%*s\n", ind, "", + name, width, sign, vt); + show_expression(ivl_expr_oper1(net), ind+4); +} + void show_expression(ivl_expr_t net, unsigned ind) { unsigned idx; @@ -252,9 +274,7 @@ void show_expression(ivl_expr_t net, unsigned ind) break; case IVL_EX_UNARY: - fprintf(out, "%*s\n", ind, "", - ivl_expr_opcode(net), width, sign, vt); - show_expression(ivl_expr_oper1(net), ind+4); + show_unary_expression(net, ind); break; case IVL_EX_UFUNC: diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 917b4b43d..7b455d171 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -175,6 +175,32 @@ static void show_lpm_arithmetic_pins(ivl_lpm_t net) fprintf(out, " DataB: %s\n", nex? ivl_nexus_name(nex) : ""); } +static void show_lpm_abs(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_ABS %s: \n", + ivl_lpm_basename(net), width); + + ivl_nexus_t nex; + nex = ivl_lpm_q(net, 0); + fprintf(out, " Q: %s\n", ivl_nexus_name(ivl_lpm_q(net, 0))); + + nex = ivl_lpm_data(net, 0); + fprintf(out, " D: %s\n", nex? ivl_nexus_name(nex) : ""); + if (nex == 0) { + fprintf(out, " ERROR: missing input\n"); + stub_errors += 1; + return; + } + + if (width_of_nexus(nex) != width) { + fprintf(out, " ERROR: D width (%d) is wrong\n", + width_of_nexus(nex)); + stub_errors += 1; + } +} + static void show_lpm_add(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -796,6 +822,10 @@ static void show_lpm(ivl_lpm_t net) switch (ivl_lpm_type(net)) { + case IVL_LPM_ABS: + show_lpm_abs(net); + break; + case IVL_LPM_ADD: show_lpm_add(net); break; diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 04848e572..bca49cc0a 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -2456,6 +2456,32 @@ static struct vector_info draw_unary_expr(ivl_expr_t exp, unsigned wid) } break; + case 'm': /* abs() */ + res = draw_eval_expr_wid(sub, wid, 0); + + if (!ivl_expr_signed(sub)) + break; + + if (res.base == 0 || res.base == 2 || res.base == 3) + break; + + /* Handle the special case of a -1 constant. Make the + result a 1. */ + if (res.base == 1) { + res.base = allocate_vector(wid); + fprintf(vvp_out, " %%movi %d, 1, %u;\n", + res.base, res.wid); + break; + } + + fprintf(vvp_out, " %%cmpi/s %d, 0, %u;\n", res.base, res.wid); + fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, local_count); + fprintf(vvp_out, " %%inv %d, %u;\n", res.base, res.wid); + fprintf(vvp_out, " %%addi %d, 1, %u;\n", res.base, res.wid); + fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_count); + local_count += 1; + break; + default: fprintf(stderr, "vvp error: unhandled unary: %c\n", ivl_expr_opcode(exp)); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 2d1b6d184..05944f639 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -679,6 +679,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) if (lpm) switch (ivl_lpm_type(lpm)) { case IVL_LPM_FF: + case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: case IVL_LPM_CONCAT: @@ -1693,6 +1694,17 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net) return dly; } +static void draw_lpm_abs(ivl_lpm_t net) +{ + const char*src_table[1]; + draw_lpm_data_inputs(net, 0, 1, src_table); + + const char*dly = draw_lpm_output_delay(net); + + fprintf(vvp_out, "L_%p%s .abs %s;\n", + net, dly, src_table[0]); +} + static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; @@ -2338,6 +2350,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) { switch (ivl_lpm_type(net)) { + case IVL_LPM_ABS: + draw_lpm_abs(net); + return; + case IVL_LPM_ADD: case IVL_LPM_SUB: case IVL_LPM_MULT: diff --git a/vvp/arith.cc b/vvp/arith.cc index 0fd3041e4..c7b827bbc 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -55,6 +55,41 @@ void vvp_arith_::dispatch_operand_(vvp_net_ptr_t ptr, vvp_vector4_t bit) } +vvp_arith_abs::vvp_arith_abs() +{ +} + +vvp_arith_abs::~vvp_arith_abs() +{ +} + +void vvp_arith_abs::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) +{ + vvp_vector4_t out (bit.size(), BIT4_0);; + + vvp_bit4_t cmp = compare_gtge_signed(bit, out, BIT4_1); + switch (cmp) { + case BIT4_1: // bit >= 0 + out = bit; + break; + case BIT4_0: // bit < 0 + out = ~bit; + out += 1; + break; + default: // There's an X. + out = vvp_vector4_t(bit.size(), BIT4_X); + break; + } + + vvp_send_vec4(ptr.ptr()->out, out); +} + +void vvp_arith_abs::recv_real(vvp_net_ptr_t ptr, double bit) +{ + double out = fabs(bit); + vvp_send_real(ptr.ptr()->out, out); +} + // Division vvp_arith_div::vvp_arith_div(unsigned wid, bool signed_flag) diff --git a/vvp/arith.h b/vvp/arith.h index acc85b263..41ccf5371 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -49,6 +49,17 @@ class vvp_arith_ : public vvp_net_fun_t { vvp_vector4_t x_val_; }; +class vvp_arith_abs : public vvp_net_fun_t { + public: + explicit vvp_arith_abs(); + ~vvp_arith_abs(); + + void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit); + void recv_real(vvp_net_ptr_t ptr, double bit); + + private: +}; + class vvp_arith_div : public vvp_arith_ { public: diff --git a/vvp/compile.cc b/vvp/compile.cc index e152ff7e0..a8f2f7069 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -941,6 +941,21 @@ template void make_arith(T_ *arith, char*label, free(argv); } +void compile_arith_abs(char*label, unsigned argc, struct symb_s*argv) +{ + vvp_arith_abs*arith = new vvp_arith_abs; + + vvp_net_t* ptr = new vvp_net_t; + ptr->fun = arith; + + define_functor_symbol(label, ptr); + free(label); + + assert(argc == 1); + inputs_connect(ptr, argc, argv); + free(argv); +} + void compile_arith_div(char*label, long wid, bool signed_flag, unsigned argc, struct symb_s*argv) { diff --git a/vvp/compile.h b/vvp/compile.h index 74c723f9d..89151af86 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -149,6 +149,8 @@ extern void compile_part_select_var(char*label, char*src, */ extern void compile_arith_pow(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); +extern void compile_arith_abs(char*label, + unsigned argc, struct symb_s*argv); extern void compile_arith_div(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_mod(char*label, long width, diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 3c7222313..46032abc5 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -87,6 +87,7 @@ ".alias" { return K_ALIAS; } ".alias/real" { return K_ALIAS_R; } ".alias/s" { return K_ALIAS_S; } +".abs" { return K_ARITH_ABS; } ".arith/div" { return K_ARITH_DIV; } ".arith/div.r" { return K_ARITH_DIV_R; } ".arith/div.s" { return K_ARITH_DIV_S; } diff --git a/vvp/parse.y b/vvp/parse.y index c07dc451b..60eb9103e 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -66,7 +66,8 @@ 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_ABS K_ARITH_DIV K_ARITH_DIV_R K_ARITH_DIV_S K_ARITH_MOD +%token 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 K_ARITH_POW_S %token K_ARRAY K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_PORT @@ -248,6 +249,14 @@ statement symbols ';' { compile_concat($1, $4, $5, $6, $7, $10.cnt, $10.vect); } + /* The ABS statement is a special arithmetic node that takes 1 + input. Re-use the symbols rule. */ + + | T_LABEL K_ARITH_ABS symbols ';' + { struct symbv_s obj = $3; + compile_arith_abs($1, obj.cnt, obj.vect); + } + /* Arithmetic statements generate functor arrays of a given width that take like size input vectors. */