Implement abs/min/max operators for real values.

Implement in behavioral the abs/min/max operators for real values.
The parser treats these builtin functions as operators, unary or
binary, and elaborates them appropriately.

Also add enough code generator support to handle real valued expressions
in thread context.
This commit is contained in:
Stephen Williams 2008-05-03 21:54:42 -07:00
parent 6d433364ac
commit d60df2d75b
9 changed files with 151 additions and 4 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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)
{

View File

@ -2294,6 +2294,7 @@ NetEUnary::NetEUnary(char op, NetExpr*ex)
switch (op_) {
case '-':
case '+':
case 'm': // abs()
cast_signed(ex->has_sign());
break;
default:

View File

@ -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 {

28
parse.y
View File

@ -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 ')'

View File

@ -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':

View File

@ -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;