diff --git a/design_dump.cc b/design_dump.cc index 074bbe25d..12c4a6e5a 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1082,6 +1082,20 @@ void NetExpr::dump(ostream&o) const void NetEBinary::dump(ostream&o) const { + if (op_ == 'm' || op_ == 'M') { + if (op_ == 'm') + o << "min"; + else + o << "max"; + + o << "("; + left_->dump(o); + o << ", "; + right_->dump(o); + o << ")"; + return; + } + o << "("; left_->dump(o); o << ")"; @@ -1267,6 +1281,9 @@ void NetEUFunc::dump(ostream&o) const void NetEUnary::dump(ostream&o) const { switch (op_) { + case 'm': + o << "abs"; + break; case 'N': o << "~|"; break; diff --git a/elab_expr.cc b/elab_expr.cc index 564ace5fd..4a024df34 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -311,6 +311,12 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, des->errors += 1; } break; + + case 'm': // min(l,r) + case 'M': // max(l,r) + tmp = new NetEBMinMax(op_, lp, rp); + tmp->set_line(*this); + break; } return tmp; diff --git a/lexor_keyword.gperf b/lexor_keyword.gperf index 7c641a1c9..554089607 100644 --- a/lexor_keyword.gperf +++ b/lexor_keyword.gperf @@ -11,6 +11,7 @@ %} struct lexor_keyword { const char*name; int mask; int tokenType; }; %% +abs, GN_KEYWORDS_VAMS_2_3, K_abs acos, GN_KEYWORDS_VAMS_2_3, K_acos acosh, GN_KEYWORDS_VAMS_2_3, K_acosh always, GN_KEYWORDS_1364_1995, K_always @@ -74,7 +75,9 @@ localparam, GN_KEYWORDS_1364_2001, K_localparam log, GN_KEYWORDS_VAMS_2_3, K_log logic, GN_KEYWORDS_ICARUS, K_logic macromodule, GN_KEYWORDS_1364_1995, K_macromodule +max, GN_KEYWORDS_VAMS_2_3, K_max medium, GN_KEYWORDS_1364_1995, K_medium +min, GN_KEYWORDS_VAMS_2_3, K_min module, GN_KEYWORDS_1364_1995, K_module nand, GN_KEYWORDS_1364_1995, K_nand negedge, GN_KEYWORDS_1364_1995, K_negedge diff --git a/net_expr.cc b/net_expr.cc index 8130c4cd6..2efb91f8f 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -219,6 +219,27 @@ ivl_variable_type_t NetEBDiv::expr_type() const return IVL_VT_LOGIC; } +NetEBMinMax::NetEBMinMax(char op, NetExpr*l, NetExpr*r) +: NetEBinary(op, l, r) +{ + expr_width( max(l->expr_width(), r->expr_width()) ); + cast_signed(l->has_sign() || r->has_sign()); +} + +NetEBMinMax::~NetEBMinMax() +{ +} + +ivl_variable_type_t NetEBMinMax::expr_type() const +{ + if (left_->expr_type() == IVL_VT_REAL) + return IVL_VT_REAL; + if (right_->expr_type() == IVL_VT_REAL) + return IVL_VT_REAL; + + return IVL_VT_LOGIC; +} + NetEBMult::NetEBMult(char op, NetExpr*l, NetExpr*r) : NetEBinary(op, l, r) { diff --git a/netlist.cc b/netlist.cc index 03b6be397..4d7e8a9c8 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2294,6 +2294,7 @@ NetEUnary::NetEUnary(char op, NetExpr*ex) switch (op_) { case '-': case '+': + case 'm': // abs() cast_signed(ex->has_sign()); break; default: diff --git a/netlist.h b/netlist.h index 191c2b4b0..891864b84 100644 --- a/netlist.h +++ b/netlist.h @@ -2664,6 +2664,8 @@ class NetProcTop : public LineInfo, public Attrib { * r -- Right shift (>>) * R -- signed right shift (>>>) * X -- Bitwise exclusive NOR (~^) + * m -- min(a,b) + * M -- max(a,b) */ class NetEBinary : public NetExpr { @@ -2831,6 +2833,24 @@ class NetEBLogic : public NetEBinary { private: }; +/* + * Support the binary min(l,r) and max(l,r) operators. The opcodes + * supported are: + * + * m -- min + * M -- max + */ +class NetEBMinMax : public NetEBinary { + + public: + NetEBMinMax(char op, NetExpr*l, NetExpr*r); + ~NetEBMinMax(); + + virtual ivl_variable_type_t expr_type() const; + + private: + +}; /* * Support the binary multiplication (*) operator. @@ -3143,6 +3163,7 @@ class NetETernary : public NetExpr { * A -- Reduction NAND (~&) * N -- Reduction NOR (~|) * X -- Reduction NXOR (~^ or ^~) + * m -- abs(x) (i.e. "magnitude") */ class NetEUnary : public NetExpr { diff --git a/parse.y b/parse.y index b596dfbea..35a139fca 100644 --- a/parse.y +++ b/parse.y @@ -203,7 +203,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %token K_PO_POS K_PO_NEG K_POW %token K_PSTAR K_STARP %token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER -%token K_acos K_acosh K_asin K_asinh K_atan K_atanh K_atan2 +%token K_abs K_acos K_acosh K_asin K_asinh K_atan K_atanh K_atan2 %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_cos K_cosh %token K_deassign K_default K_defparam K_disable @@ -213,8 +213,8 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %token K_for K_force K_forever K_fork K_function K_generate K_genvar %token K_highz0 K_highz1 K_hypot K_if K_ifnone %token K_initial K_inout K_input K_integer K_join K_large K_ln K_localparam -%token K_log K_logic K_macromodule -%token K_medium K_module K_nand K_negedge K_nmos K_nor K_not K_notif0 +%token K_log K_logic K_macromodule K_max +%token K_medium K_min K_module K_nand K_negedge K_nmos K_nor K_not K_notif0 %token K_notif1 K_or K_output K_parameter K_pmos K_posedge K_pow K_primitive %token K_pull0 K_pull1 K_pulldown K_pullup K_rcmos K_real K_realtime %token K_reg K_release K_repeat @@ -1254,6 +1254,28 @@ expr_primary $$ = tmp; } + /* These mathematical functions are conveniently expressed as unary + and binary expressions. They behave much like unary/binary + operators, even though they are parsed as functions. */ + + | K_abs '(' expression ')' + { PEUnary*tmp = new PEUnary('m', $3); + FILE_NAME(tmp,@1); + $$ = tmp; + } + + | K_max '(' expression ',' expression ')' + { PEBinary*tmp = new PEBinary('M', $3, $5); + FILE_NAME(tmp,@1); + $$ = tmp; + } + + | K_min '(' expression ',' expression ')' + { PEBinary*tmp = new PEBinary('m', $3, $5); + FILE_NAME(tmp,@1); + $$ = tmp; + } + /* Parenthesized expressions are primaries. */ | '(' expr_mintypmax ')' diff --git a/pform_dump.cc b/pform_dump.cc index fec687c26..d8efeab24 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -215,11 +215,30 @@ void PETernary::dump(ostream&out) const void PEUnary::dump(ostream&out) const { - out << op_ << "(" << *expr_ << ")"; + switch (op_) { + case 'm': + out << "abs"; + break; + default: + out << op_; + break; + } + out << "(" << *expr_ << ")"; } void PEBinary::dump(ostream&out) const { + /* Handle some special cases, where the operators are written + in function notation. */ + if (op_ == 'm') { + out << "min(" << *left_ << "," << *right_ << ")"; + return; + } + if (op_ == 'M') { + out << "min(" << *left_ << "," << *right_ << ")"; + return; + } + out << "(" << *left_ << ")"; switch (op_) { case 'a': diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index 768d60d0c..cb3fda4f6 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -112,6 +112,24 @@ static int draw_binary_real(ivl_expr_t exp) case 'p': fprintf(vvp_out, " %%pow/wr %d, %d;\n", l, r); break; + + case 'm': { // min(l,r) + int lab_out = local_count++; + fprintf(vvp_out, " %%cmp/wr %d, %d;\n", r, l); + fprintf(vvp_out, " %%jmp/0xz T_%d.%d, 5;\n", thread_count, lab_out); + fprintf(vvp_out, " %%mov/wr %d, %d;\n", l, r); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_out); + break; + } + + case 'M': { // max(l,r) + int lab_out = local_count++; + fprintf(vvp_out, " %%cmp/wr %d, %d;\n", l, r); + fprintf(vvp_out, " %%jmp/0xz T_%d.%d, 5;\n", thread_count, lab_out); + fprintf(vvp_out, " %%mov/wr %d, %d;\n", l, r); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_out); + break; + } default: fprintf(stderr, "XXXX draw_binary_real(%c)\n", ivl_expr_opcode(exp)); @@ -420,6 +438,25 @@ static int draw_unary_real(ivl_expr_t exp) return res; } + if (ivl_expr_opcode(exp) == 'm') { /* abs(sube) */ + unsigned lab_positive = local_count++; + unsigned lab_out = local_count++; + int res = allocate_word(); + fprintf(vvp_out, " %%loadi/wr %d, 0, 0; load 0.0 -- %d = abs(%d)\n", + res, res, sub); + fprintf(vvp_out, " %%cmp/wr %d, %d;\n", sub, res); + fprintf(vvp_out, " %%jmp/0xz T_%d.%d, 5;\n", + thread_count, lab_positive); + fprintf(vvp_out, " %%sub/wr %d, %d;\n", res, sub); + fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_out); + fprintf(vvp_out, "T_%d.%d %%mov/wr %d, %d;\n", + thread_count, lab_positive, res, sub); + fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_out); + + clr_word(sub); + return res; + } + fprintf(vvp_out, "; XXXX unary (%c)\n", ivl_expr_opcode(exp)); fprintf(stderr, "XXXX evaluate unary (%c)\n", ivl_expr_opcode(exp)); return 0;