diff --git a/design_dump.cc b/design_dump.cc index 3c77aa1e9..53b625f3d 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -302,6 +302,14 @@ void NetArrayDq::dump_node(ostream&o, unsigned ind) const dump_obj_attr(o, ind+4); } +void NetCastReal::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Cast to real (NetCastReal): " << + name() << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetCLShift::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " << diff --git a/elab_net.cc b/elab_net.cc index 8cbab1fdf..4b5370e34 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -716,6 +716,16 @@ NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope, unsigned rwidth = lwidth; + // If either operand is IVL_VT_REAL, then cast the other to + // IVL_VT_REAL so that the division can become IVL_VT_REAL. + + if (lsig->data_type()==IVL_VT_REAL || rsig->data_type()==IVL_VT_REAL) { + if (lsig->data_type() != IVL_VT_REAL) + lsig = cast_to_real(des, scope, lsig); + if (rsig->data_type() != IVL_VT_REAL) + rsig = cast_to_real(des, scope, rsig); + } + if (rwidth == 0) { rwidth = lsig->vector_width(); if (rsig->vector_width() > rwidth) diff --git a/emit.cc b/emit.cc index 606d13beb..84c4d6a25 100644 --- a/emit.cc +++ b/emit.cc @@ -72,6 +72,11 @@ bool NetCaseCmp::emit_node(struct target_t*tgt) const return true; } +bool NetCastReal::emit_node(struct target_t*tgt) const +{ + return tgt->lpm_cast_real(this); +} + bool NetCLShift::emit_node(struct target_t*tgt) const { tgt->lpm_clshift(this); diff --git a/ivl_target.h b/ivl_target.h index 6ab07a9ea..61069a6eb 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -254,6 +254,7 @@ typedef enum ivl_lpm_type_e { IVL_LPM_ABS = 32, IVL_LPM_ADD = 0, IVL_LPM_ARRAY = 30, + IVL_LPM_CAST_REAL = 33, IVL_LPM_CONCAT = 16, IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ IVL_LPM_CMP_EQ = 10, diff --git a/netlist.cc b/netlist.cc index fefd23581..db630eb6b 100644 --- a/netlist.cc +++ b/netlist.cc @@ -849,6 +849,15 @@ const NetScope* NetProcTop::scope() const return scope_; } +NetCastReal::NetCastReal(NetScope*scope, perm_string n, bool signed_flag) +: NetNode(scope, n, 2), signed_flag_(signed_flag) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("O"), 0); + pin(1).set_dir(Link::INPUT); + pin(1).set_name(perm_string::literal("I"), 0); +} + NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt) : NetNode(scope, n, cnt+1), width_(wid) { diff --git a/netlist.h b/netlist.h index e23fcb3fd..7c9186060 100644 --- a/netlist.h +++ b/netlist.h @@ -916,6 +916,26 @@ class NetArrayDq : public NetNode { }; +/* + * Convert an input to IVL_VT_REAL. The input is pin(1), which can be + * any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0), + * which is IVL_VT_REAL. The conversion interprets the input as an + * unsigned value unless the signed_flag is true. + */ +class NetCastReal : public NetNode { + + public: + NetCastReal(NetScope*s, perm_string n, bool signed_flag); + + bool signed_flag() const { return signed_flag_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + + private: + bool signed_flag_; +}; + /* * This type represents the LPM_CLSHIFT device. */ diff --git a/netmisc.cc b/netmisc.cc index 39787d621..eea23d1e2 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -76,6 +76,25 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val) #endif } +NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) +{ + if (src->data_type() == IVL_VT_REAL) + return src; + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE); + tmp->data_type(IVL_VT_REAL); + tmp->set_line(*src); + + NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed()); + cast->set_line(*src); + des->add_node(cast); + + connect(cast->pin(0), tmp->pin(0)); + connect(cast->pin(1), src->pin(0)); + + return tmp; +} + /* * Add a signed constant to an existing expression. Generate a new * NetEBAdd node that has the input expression and an expression made diff --git a/netmisc.h b/netmisc.h index e82c3ef20..3c8ce1ca9 100644 --- a/netmisc.h +++ b/netmisc.h @@ -64,6 +64,12 @@ extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w); extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w); +/* + * Generate the nodes necessary to cast an expression (a net) to a + * real value. + */ +extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src); + /* * Take the input expression and return a variation that assures that * the expression is 1-bit wide and logical. This reflects the needs diff --git a/t-dll-api.cc b/t-dll-api.cc index a9c4c561e..e5e275aad 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -886,6 +886,7 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) assert(net); switch (net->type) { case IVL_LPM_ABS: + case IVL_LPM_CAST_REAL: assert(idx == 0); return net->u_.arith.a; @@ -1028,20 +1029,18 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) switch (net->type) { case IVL_LPM_ABS: case IVL_LPM_ADD: - case IVL_LPM_DIVIDE: - case IVL_LPM_MOD: - case IVL_LPM_MULT: - case IVL_LPM_POW: - case IVL_LPM_SUB: - assert(idx == 0); - return net->u_.arith.q; - + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_NEE: + case IVL_LPM_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_MULT: + case IVL_LPM_POW: + case IVL_LPM_SUB: assert(idx == 0); return net->u_.arith.q; @@ -1144,6 +1143,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) return 0; case IVL_LPM_ABS: case IVL_LPM_ADD: + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_GE: diff --git a/t-dll.cc b/t-dll.cc index 0261a8346..edeb58a8c 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1550,6 +1550,39 @@ void dll_target::lpm_clshift(const NetCLShift*net) scope_add_lpm(obj->scope, obj); } +bool dll_target::lpm_cast_real(const NetCastReal*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_CAST_REAL; + obj->name = net->name(); // NetCastReal names are permallocated + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->width = 0; + obj->u_.arith.signed_flag = net->signed_flag()? 1 : 0; + + const Nexus*nex; + + nex = net->pin(0).nexus(); + assert(nex->t_cookie()); + + obj->u_.arith.q = nex->t_cookie(); + + nex = net->pin(1).nexus(); + assert(nex->t_cookie()); + obj->u_.arith.a = nex->t_cookie(); + + nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + 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); + + return true; +} + /* * Make out of the NetCompare object an ivl_lpm_s object. The * comparators in ivl_target do not support < or <=, but they can be diff --git a/t-dll.h b/t-dll.h index 63f0a1bb2..3b017bf83 100644 --- a/t-dll.h +++ b/t-dll.h @@ -76,6 +76,7 @@ struct dll_target : public target_t, public expr_scan_t { void lpm_abs(const NetAbs*); void lpm_add_sub(const NetAddSub*); bool lpm_array_dq(const NetArrayDq*); + bool lpm_cast_real(const NetCastReal*); void lpm_clshift(const NetCLShift*); void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); diff --git a/target.cc b/target.cc index e474c7cf7..0d71e0e26 100644 --- a/target.cc +++ b/target.cc @@ -107,6 +107,13 @@ bool target_t::lpm_array_dq(const NetArrayDq*) return false; } +bool target_t::lpm_cast_real(const NetCastReal*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetCastReal." << endl; + return false; +} + void target_t::lpm_clshift(const NetCLShift*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index d002a9797..89c78c037 100644 --- a/target.h +++ b/target.h @@ -72,6 +72,7 @@ struct target_t { virtual void lpm_add_sub(const NetAddSub*); virtual bool lpm_array_dq(const NetArrayDq*); virtual void lpm_clshift(const NetCLShift*); + virtual bool lpm_cast_real(const NetCastReal*); virtual void lpm_compare(const NetCompare*); virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index ecec77782..4238a4820 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -240,6 +240,31 @@ static void show_lpm_array(ivl_lpm_t net) } } +static void show_lpm_cast_real(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_CAST_REAL %s: \n", + ivl_lpm_basename(net), width); + + ivl_nexus_t q = ivl_lpm_q(net,0); + ivl_nexus_t a = ivl_lpm_data(net,0); + fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0))); + fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0))); + + if (type_of_nexus(q) != IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of Q is %s, expecting real\n", + data_type_string(type_of_nexus(q))); + stub_errors += 1; + } + + if (type_of_nexus(a) == IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of A is %s, expecting !real\n", + data_type_string(type_of_nexus(a))); + stub_errors += 1; + } +} + static void show_lpm_divide(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -802,6 +827,10 @@ static void show_lpm(ivl_lpm_t net) show_lpm_array(net); break; + case IVL_LPM_CAST_REAL: + show_lpm_cast_real(net); + break; + case IVL_LPM_DIVIDE: show_lpm_divide(net); break; diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 9a9d51d6d..f7616ade0 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -387,6 +387,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: + case IVL_LPM_CAST_REAL: case IVL_LPM_CONCAT: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index ed1aa48d8..1f2cdeaec 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1079,6 +1079,17 @@ static void draw_lpm_abs(ivl_lpm_t net) net, dly, src_table[0]); } +static void draw_lpm_cast_real(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 .cast/real %s;\n", + net, dly, src_table[0]); +} + static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; @@ -1644,6 +1655,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_abs(net); return; + case IVL_LPM_CAST_REAL: + draw_lpm_cast_real(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 c7b827bbc..d263981aa 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -90,6 +90,22 @@ void vvp_arith_abs::recv_real(vvp_net_ptr_t ptr, double bit) vvp_send_real(ptr.ptr()->out, out); } +vvp_arith_cast_real::vvp_arith_cast_real(bool signed_flag) +: signed_(signed_flag) +{ +} + +vvp_arith_cast_real::~vvp_arith_cast_real() +{ +} + +void vvp_arith_cast_real::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) +{ + double val; + vector4_to_value(bit, val, signed_); + vvp_send_real(ptr.ptr()->out, val); +} + // Division vvp_arith_div::vvp_arith_div(unsigned wid, bool signed_flag) diff --git a/vvp/arith.h b/vvp/arith.h index 41ccf5371..31a479dd3 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -60,6 +60,17 @@ class vvp_arith_abs : public vvp_net_fun_t { private: }; +class vvp_arith_cast_real : public vvp_net_fun_t { + public: + explicit vvp_arith_cast_real(bool signed_flag); + ~vvp_arith_cast_real(); + + void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit); + + private: + bool signed_; +}; + class vvp_arith_div : public vvp_arith_ { public: diff --git a/vvp/compile.cc b/vvp/compile.cc index 18174c937..9cdc5d514 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -889,6 +889,21 @@ template void make_arith(T_ *arith, char*label, free(argv); } +void compile_arith_cast_real(char*label, unsigned argc, struct symb_s*argv) +{ + vvp_arith_cast_real*arith = new vvp_arith_cast_real(false); + + 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_abs(char*label, unsigned argc, struct symb_s*argv) { vvp_arith_abs*arith = new vvp_arith_abs; diff --git a/vvp/compile.h b/vvp/compile.h index 93daf0f30..0d57b3bf5 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -151,6 +151,8 @@ 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_cast_real(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 0e21eb087..2f8b381c3 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -107,6 +107,7 @@ ".array/real" { return K_ARRAY_R; } ".array/s" { return K_ARRAY_S; } ".array/port" { return K_ARRAY_PORT; } +".cast/real" { return K_CAST_REAL; } ".cmp/eeq" { return K_CMP_EEQ; } ".cmp/eq" { return K_CMP_EQ; } ".cmp/eq.r" { return K_CMP_EQ_R; } diff --git a/vvp/parse.y b/vvp/parse.y index 6947f8996..c7678f0ae 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -71,6 +71,7 @@ static struct __vpiModPath*modpath_dst = 0; %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 +%token K_CAST_REAL %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 %token K_CONCAT K_DEBUG K_DELAY K_DFF @@ -252,6 +253,11 @@ statement compile_arith_abs($1, obj.cnt, obj.vect); } + | T_LABEL K_CAST_REAL symbols ';' + { struct symbv_s obj = $3; + compile_arith_cast_real($1, obj.cnt, obj.vect); + } + /* Arithmetic statements generate functor arrays of a given width that take like size input vectors. */