Merge branch 'master' into verilog-ams
This commit is contained in:
commit
1ca8241b88
24
PExpr.cc
24
PExpr.cc
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1998-2007 Stephen Williams <steve@icarus.com>
|
||||
* Copyright (c) 1998-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
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
# include "config.h"
|
||||
|
||||
# include <iostream>
|
||||
# include <cstring>
|
||||
|
||||
# include "PExpr.h"
|
||||
# include "Module.h"
|
||||
|
|
@ -128,6 +129,26 @@ PECallFunction::~PECallFunction()
|
|||
{
|
||||
}
|
||||
|
||||
bool PECallFunction::is_constant(Module*mod) const
|
||||
{
|
||||
/* Only $clog2 can be a constant system function. */
|
||||
if (peek_tail_name(path_)[0] == '$') {
|
||||
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: $clog2 takes a "
|
||||
"single argument." << endl;
|
||||
return false;
|
||||
}
|
||||
/* If the argument is constant $clog2 is constant. */
|
||||
return parms_[0]->is_constant(mod);
|
||||
}
|
||||
return false; /* Most system functions are not constant. */
|
||||
}
|
||||
|
||||
/* Checking for constant user functions goes here. */
|
||||
return false;
|
||||
}
|
||||
|
||||
PEConcat::PEConcat(const svector<PExpr*>&p, PExpr*r)
|
||||
: parms_(p), repeat_(r)
|
||||
{
|
||||
|
|
@ -310,4 +331,3 @@ bool PEUnary::is_constant(Module*m) const
|
|||
{
|
||||
return expr_->is_constant(m);
|
||||
}
|
||||
|
||||
|
|
|
|||
3
PExpr.h
3
PExpr.h
|
|
@ -708,6 +708,8 @@ class PECallFunction : public PExpr {
|
|||
|
||||
~PECallFunction();
|
||||
|
||||
virtual bool is_constant(Module*) const;
|
||||
|
||||
virtual void dump(ostream &) const;
|
||||
|
||||
virtual NetNet* elaborate_net(Design*des, NetScope*scope,
|
||||
|
|
@ -719,6 +721,7 @@ class PECallFunction : public PExpr {
|
|||
Link::strength_t drive1) const;
|
||||
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
|
||||
int expr_wid, bool sys_task_arg) const;
|
||||
virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const;
|
||||
|
||||
virtual unsigned test_width(Design*des, NetScope*scope,
|
||||
unsigned min, unsigned lval,
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@
|
|||
|
||||
#include "PTask.h"
|
||||
|
||||
PFunction::PFunction(perm_string name, PScope*parent)
|
||||
PFunction::PFunction(perm_string name, PScope*parent, bool is_auto)
|
||||
: PScope(name, parent), ports_(0), statement_(0)
|
||||
{
|
||||
is_auto_ = is_auto;
|
||||
return_type_.type = PTF_NONE;
|
||||
}
|
||||
|
||||
|
|
|
|||
31
PTask.cc
31
PTask.cc
|
|
@ -21,9 +21,10 @@
|
|||
|
||||
# include "PTask.h"
|
||||
|
||||
PTask::PTask(perm_string name, PScope*parent)
|
||||
PTask::PTask(perm_string name, PScope*parent, bool is_auto)
|
||||
: PScope(name, parent), ports_(0), statement_(0)
|
||||
{
|
||||
is_auto_ = is_auto;
|
||||
}
|
||||
|
||||
PTask::~PTask()
|
||||
|
|
@ -41,31 +42,3 @@ void PTask::set_statement(Statement*s)
|
|||
assert(statement_ == 0);
|
||||
statement_ = s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* $Log: PTask.cc,v $
|
||||
* Revision 1.7 2002/08/12 01:34:58 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.6 2001/07/25 03:10:48 steve
|
||||
* Create a config.h.in file to hold all the config
|
||||
* junk, and support gcc 3.0. (Stephan Boettcher)
|
||||
*
|
||||
* Revision 1.5 2001/04/19 03:04:47 steve
|
||||
* Spurious assert of empty statemnt.
|
||||
*
|
||||
* Revision 1.4 2001/01/13 22:20:08 steve
|
||||
* Parse parameters within nested scopes.
|
||||
*
|
||||
* Revision 1.3 2000/02/23 02:56:53 steve
|
||||
* Macintosh compilers do not support ident.
|
||||
*
|
||||
* Revision 1.2 1999/07/24 02:11:19 steve
|
||||
* Elaborate task input ports.
|
||||
*
|
||||
* Revision 1.1 1999/07/03 02:12:51 steve
|
||||
* Elaborate user defined tasks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
|||
10
PTask.h
10
PTask.h
|
|
@ -51,7 +51,7 @@ struct PTaskFuncArg {
|
|||
class PTask : public PScope, public LineInfo {
|
||||
|
||||
public:
|
||||
explicit PTask(perm_string name, PScope*parent);
|
||||
explicit PTask(perm_string name, PScope*parent, bool is_auto);
|
||||
~PTask();
|
||||
|
||||
void set_ports(svector<PWire *>*p);
|
||||
|
|
@ -69,11 +69,14 @@ class PTask : public PScope, public LineInfo {
|
|||
// Elaborate the statement to finish off the task definition.
|
||||
void elaborate(Design*des, NetScope*scope) const;
|
||||
|
||||
bool is_auto() const { return is_auto_; };
|
||||
|
||||
void dump(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
svector<PWire*>*ports_;
|
||||
Statement*statement_;
|
||||
bool is_auto_;
|
||||
|
||||
private: // Not implemented
|
||||
PTask(const PTask&);
|
||||
|
|
@ -90,7 +93,7 @@ class PTask : public PScope, public LineInfo {
|
|||
class PFunction : public PScope, public LineInfo {
|
||||
|
||||
public:
|
||||
explicit PFunction(perm_string name, PScope*parent);
|
||||
explicit PFunction(perm_string name, PScope*parent, bool is_auto);
|
||||
~PFunction();
|
||||
|
||||
void set_ports(svector<PWire *>*p);
|
||||
|
|
@ -105,12 +108,15 @@ class PFunction : public PScope, public LineInfo {
|
|||
/* Elaborate the behavioral statement. */
|
||||
void elaborate(Design *des, NetScope*) const;
|
||||
|
||||
bool is_auto() const { return is_auto_; };
|
||||
|
||||
void dump(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
PTaskFuncArg return_type_;
|
||||
svector<PWire *> *ports_;
|
||||
Statement *statement_;
|
||||
bool is_auto_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
echo "Autoconf in root..."
|
||||
autoconf -f
|
||||
|
||||
for dir in vpip vpi vvp tgt-vvp tgt-fpga tgt-stub libveriuser cadpli
|
||||
for dir in vpip vpi vvp tgt-vvp tgt-fpga tgt-stub tgt-vhdl libveriuser cadpli
|
||||
do
|
||||
echo "Autoconf in $dir..."
|
||||
( cd ./$dir ; autoconf -f --include=.. )
|
||||
|
|
|
|||
|
|
@ -122,6 +122,6 @@ AX_C_UNDERSCORES_TRAILING
|
|||
AX_CPP_IDENT
|
||||
|
||||
# XXX disable tgt-fpga for the moment
|
||||
AC_CONFIG_SUBDIRS(vvp vpi tgt-stub tgt-null tgt-vvp libveriuser cadpli)
|
||||
AC_CONFIG_SUBDIRS(vvp vpi tgt-stub tgt-null tgt-vvp tgt-vhdl libveriuser cadpli)
|
||||
|
||||
AC_OUTPUT(Makefile ivlpp/Makefile driver/Makefile driver-vpi/Makefile tgt-null/Makefile tgt-verilog/Makefile tgt-pal/Makefile)
|
||||
AC_OUTPUT(Makefile ivlpp/Makefile driver/Makefile driver-vpi/Makefile tgt-null/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vhdl/Makefile)
|
||||
|
|
|
|||
|
|
@ -1003,6 +1003,7 @@ void NetScope::dump(ostream&o) const
|
|||
o << " generate block";
|
||||
break;
|
||||
}
|
||||
if (is_auto()) o << " (automatic)";
|
||||
o << endl;
|
||||
|
||||
for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1)
|
||||
|
|
@ -1019,6 +1020,8 @@ void NetScope::dump(ostream&o) const
|
|||
; pp != parameters.end() ; pp ++) {
|
||||
o << " parameter ";
|
||||
|
||||
o << pp->second.type << " ";
|
||||
|
||||
if ((*pp).second.signed_flag)
|
||||
o << "signed ";
|
||||
|
||||
|
|
|
|||
|
|
@ -223,6 +223,12 @@ mostly by EDIF format output. The Icarus Verilog fpga code generator
|
|||
can generate complete designs or EDIF macros that can in turn be
|
||||
imported into larger designs by other tools. The \fBfpga\fP target
|
||||
implies the synthesis \fB-S\fP flag.
|
||||
.TP 8
|
||||
.B vhdl
|
||||
This target produces a VHDL translation of the Verilog netlist. The
|
||||
output is a single file containing VHDL entities corresponding to
|
||||
the modules in the Verilog source code. Note that only a subset of
|
||||
the Verilog language is supported. See the wiki for more information.
|
||||
|
||||
.SH "WARNING TYPES"
|
||||
These are the types of warnings that can be selected by the \fB-W\fP
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
delete tmp;
|
||||
}
|
||||
|
||||
verinum val (sub_expr_width, 8*sizeof(unsigned));
|
||||
verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned));
|
||||
NetEConst*sub = new NetEConst(val);
|
||||
sub->set_line(*this);
|
||||
|
||||
|
|
@ -1550,7 +1550,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope,
|
|||
// Recalculate the constant address with the adjusted base.
|
||||
unsigned use_addr = net->array_index_to_address(addr);
|
||||
if (addr < 0 || use_addr != (unsigned long)addr) {
|
||||
verinum val (use_addr, 8*sizeof(use_addr));
|
||||
verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr));
|
||||
NetEConst*tmp = new NetEConst(val);
|
||||
tmp->set_line(*this);
|
||||
delete word_index;
|
||||
|
|
|
|||
|
|
@ -2327,7 +2327,7 @@ NetNet* PEIdent::elaborate_net_array_(Design*des, NetScope*scope,
|
|||
} while (0);
|
||||
#else
|
||||
if (name_tail.index.size() > sig->array_dimensions())
|
||||
tmp = process_select_(des, scope, sig);
|
||||
tmp = process_select_(des, scope, tmp);
|
||||
|
||||
#endif
|
||||
return tmp;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
# include "netmisc.h"
|
||||
|
||||
# include <cstdlib>
|
||||
# include <cstring>
|
||||
# include <iostream>
|
||||
# include "ivl_assert.h"
|
||||
|
||||
|
|
@ -267,3 +268,40 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const
|
|||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* Reuse the routine from eval_tree.cc. */
|
||||
NetExpr* evaluate_clog2(NetExpr*arg);
|
||||
|
||||
NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
|
||||
{
|
||||
/* For now only $clog2 can be a constant system function. */
|
||||
if (peek_tail_name(path_)[0] == '$') {
|
||||
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: $clog2 takes a "
|
||||
"single argument." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
|
||||
eval_expr(arg);
|
||||
NetExpr*rtn = evaluate_clog2(arg);
|
||||
delete arg;
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
||||
cerr << get_fileline() << ": error: this is not a constant "
|
||||
"system function (" << *this << ")." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Constant user function code goes here. */
|
||||
cerr << get_fileline() << ": sorry: constant user functions are not "
|
||||
"currently supported." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
|||
assert(ex);
|
||||
|
||||
NetExpr*val = ex->elaborate_pexpr(des, scope);
|
||||
if (val == 0) return;
|
||||
|
||||
NetExpr*msb = 0;
|
||||
NetExpr*lsb = 0;
|
||||
bool signed_flag = cur.signed_flag;
|
||||
|
|
@ -71,19 +71,6 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
|||
assert(lsb);
|
||||
}
|
||||
|
||||
if (signed_flag) {
|
||||
/* If explicitly signed, then say so. */
|
||||
val->cast_signed(true);
|
||||
} else if (cur.msb) {
|
||||
/* If there is a range, then the signedness comes
|
||||
from the type and not the expression. */
|
||||
val->cast_signed(signed_flag);
|
||||
} else {
|
||||
/* otherwise, let the expression describe
|
||||
itself. */
|
||||
signed_flag = val->has_sign();
|
||||
}
|
||||
|
||||
NetScope::range_t*range_list = 0;
|
||||
for (Module::range_t*range = cur.range ; range ; range = range->next) {
|
||||
NetScope::range_t*tmp = new NetScope::range_t;
|
||||
|
|
@ -118,8 +105,29 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
|||
range_list = tmp;
|
||||
}
|
||||
|
||||
val = scope->set_parameter(name, val, cur.type, msb, lsb, signed_flag, range_list, cur);
|
||||
assert(val);
|
||||
/* Set the parameter expression to 0 if the evaluation failed. */
|
||||
if (val == 0) {
|
||||
val = scope->set_parameter(name, val, cur.type, msb, lsb,
|
||||
signed_flag, range_list, cur);
|
||||
delete val;
|
||||
return;
|
||||
}
|
||||
|
||||
if (signed_flag) {
|
||||
/* If explicitly signed, then say so. */
|
||||
val->cast_signed(true);
|
||||
} else if (cur.msb) {
|
||||
/* If there is a range, then the signedness comes
|
||||
from the type and not the expression. */
|
||||
val->cast_signed(signed_flag);
|
||||
} else {
|
||||
/* otherwise, let the expression describe
|
||||
itself. */
|
||||
signed_flag = val->has_sign();
|
||||
}
|
||||
|
||||
val = scope->set_parameter(name, val, cur.type, msb, lsb, signed_flag,
|
||||
range_list, cur);
|
||||
delete val;
|
||||
}
|
||||
|
||||
|
|
@ -142,6 +150,7 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope,
|
|||
}
|
||||
NetScope*task_scope = new NetScope(scope, use_name,
|
||||
NetScope::TASK);
|
||||
task_scope->is_auto((*cur).second->is_auto());
|
||||
task_scope->set_line((*cur).second);
|
||||
|
||||
if (debug_scopes)
|
||||
|
|
@ -171,6 +180,7 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope,
|
|||
}
|
||||
NetScope*func_scope = new NetScope(scope, use_name,
|
||||
NetScope::FUNC);
|
||||
func_scope->is_auto((*cur).second->is_auto());
|
||||
func_scope->set_line((*cur).second);
|
||||
|
||||
if (debug_scopes)
|
||||
|
|
@ -316,17 +326,17 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
|
|||
// here because the parameter receiving the assignment may be
|
||||
// in a scope not discovered by this pass.
|
||||
|
||||
while (! defparms.empty()) {
|
||||
Module::named_expr_t cur = defparms.front();
|
||||
defparms.pop_front();
|
||||
typedef list<Module::named_expr_t>::const_iterator defparms_iter_t;
|
||||
for (defparms_iter_t cur = defparms.begin()
|
||||
; cur != defparms.end() ; cur ++) {
|
||||
|
||||
PExpr*ex = cur.second;
|
||||
PExpr*ex = cur->second;
|
||||
assert(ex);
|
||||
|
||||
NetExpr*val = ex->elaborate_pexpr(des, scope);
|
||||
delete ex;
|
||||
if (val == 0) continue;
|
||||
scope->defparams.push_back(make_pair(cur.first, val));
|
||||
|
||||
scope->defparams.push_back(make_pair(cur->first, val));
|
||||
}
|
||||
|
||||
// Evaluate the attributes. Evaluate them in the scope of the
|
||||
|
|
@ -475,7 +485,12 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
|
|||
container->genvar_tmp_val = genvar;
|
||||
NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1);
|
||||
NetEConst*test = dynamic_cast<NetEConst*>(test_ex);
|
||||
assert(test);
|
||||
if (test == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar"
|
||||
<< " conditional expression: " << *loop_test << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
while (test->value().as_long()) {
|
||||
|
||||
// The actual name of the scope includes the genvar so
|
||||
|
|
@ -522,7 +537,12 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
|
|||
// Calculate the step for the loop variable.
|
||||
NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1);
|
||||
NetEConst*step = dynamic_cast<NetEConst*>(step_ex);
|
||||
assert(step);
|
||||
if (step == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar"
|
||||
<< " step expression: " << *loop_step << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
if (debug_scopes)
|
||||
cerr << get_fileline() << ": debug: genvar step from "
|
||||
<< genvar << " to " << step->value().as_long() << endl;
|
||||
|
|
@ -547,7 +567,12 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else
|
|||
{
|
||||
NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1);
|
||||
NetEConst*test = dynamic_cast<NetEConst*> (test_ex);
|
||||
assert(test);
|
||||
if (test == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar"
|
||||
<< " conditional expression: " << *loop_test << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the condition evaluates as false, then do not create the
|
||||
// scope.
|
||||
|
|
@ -589,7 +614,12 @@ 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<NetEConst*>(case_value_ex);
|
||||
assert(case_value_co);
|
||||
if (case_value_co == 0) {
|
||||
cerr << get_fileline() << ": error: Cannot evaluate genvar case"
|
||||
<< " expression: " << *loop_test << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The name of the scope to generate, whatever that item is.
|
||||
hname_t use_name (scope_name);
|
||||
|
|
|
|||
21
elab_sig.cc
21
elab_sig.cc
|
|
@ -589,9 +589,17 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const
|
|||
break;
|
||||
|
||||
default:
|
||||
cerr << get_fileline() << ": internal error: I don't know how "
|
||||
<< "to deal with return type of function "
|
||||
<< scope->basename() << "." << endl;
|
||||
if (ports_) {
|
||||
cerr << get_fileline() << ": internal error: I don't know "
|
||||
<< "how to deal with return type of function "
|
||||
<< scope->basename() << "." << endl;
|
||||
} else {
|
||||
/* If we do not have any ports or a return type this
|
||||
* is probably a bad function definition. */
|
||||
cerr << get_fileline() << ": error: Bad definition for "
|
||||
<< "function " << scope->basename() << "?" << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
svector<NetNet*>ports (ports_? ports_->count() : 0);
|
||||
|
|
@ -1003,8 +1011,11 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
|
|||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: Create signal "
|
||||
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_
|
||||
<< " in scope " << scope_path(scope) << endl;
|
||||
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_;
|
||||
if (array_dimensions > 0) {
|
||||
cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl;
|
||||
}
|
||||
cerr << " in scope " << scope_path(scope) << endl;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
98
elaborate.cc
98
elaborate.cc
|
|
@ -944,6 +944,17 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
|
|||
return tmp;
|
||||
}
|
||||
|
||||
static bool need_bufz_for_input_port(const svector<NetNet*>&prts)
|
||||
{
|
||||
if (prts[0]->port_type() != NetNet::PINPUT)
|
||||
return false;
|
||||
|
||||
if (prts[0]->pin(0).nexus()->drivers_present())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a module by recursively elaborating it. Set the path of
|
||||
* the recursive elaboration so that signal names get properly
|
||||
|
|
@ -1162,7 +1173,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
|||
width. We use that, then, to decide how to hook
|
||||
it up.
|
||||
|
||||
NOTE that this also handles the case that the
|
||||
v NOTE that this also handles the case that the
|
||||
port is actually empty on the inside. We assume
|
||||
in that case that the port is input. */
|
||||
|
||||
|
|
@ -1176,6 +1187,21 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
|||
continue;
|
||||
}
|
||||
|
||||
if (need_bufz_for_input_port(prts)) {
|
||||
NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(),
|
||||
sig->vector_width());
|
||||
des->add_node(tmp);
|
||||
connect(tmp->pin(1), sig->pin(0));
|
||||
|
||||
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::WIRE, sig->vector_width());
|
||||
tmp2->local_flag(true);
|
||||
tmp2->set_line(*this);
|
||||
tmp2->data_type(sig->data_type());
|
||||
connect(tmp->pin(0), tmp2->pin(0));
|
||||
sig = tmp2;
|
||||
}
|
||||
|
||||
} else if (prts[0]->port_type() == NetNet::PINOUT) {
|
||||
|
||||
/* Inout to/from module. This is a more
|
||||
|
|
@ -2623,8 +2649,8 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
|
|||
}
|
||||
NexusSet*nset = enet->nex_input(rem_out);
|
||||
if (nset == 0) {
|
||||
cerr << get_fileline() << ": internal error: No NexusSet"
|
||||
<< " from statement." << endl;
|
||||
cerr << get_fileline() << ": error: Unable to elaborate:"
|
||||
<< endl;
|
||||
enet->dump(cerr, 6);
|
||||
des->errors += 1;
|
||||
return enet;
|
||||
|
|
@ -3355,29 +3381,6 @@ bool PProcess::elaborate(Design*des, NetScope*scope) const
|
|||
verinum(1));
|
||||
} while (0);
|
||||
|
||||
/* If this is an always block and we have no or zero delay then
|
||||
* a runtime infinite loop will happen. If we possible have some
|
||||
* delay then print a warning that an infinite loop is possible.
|
||||
*/
|
||||
if (type() == PProcess::PR_ALWAYS) {
|
||||
DelayType dly_type = top->statement()->delay_type();
|
||||
|
||||
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
|
||||
cerr << get_fileline() << ": error: always statement"
|
||||
<< " does not have any delay." << endl;
|
||||
cerr << get_fileline() << ": : A runtime infinite"
|
||||
<< " loop will occur." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
|
||||
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
|
||||
cerr << get_fileline() << ": warning: always statement"
|
||||
<< " may not have any delay." << endl;
|
||||
cerr << get_fileline() << ": : A runtime infinite"
|
||||
<< " loop may be possible." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3845,6 +3848,37 @@ class later_defparams : public elaborator_work_item_t {
|
|||
}
|
||||
};
|
||||
|
||||
bool Design::check_always_delay() const
|
||||
{
|
||||
bool result_flag = true;
|
||||
|
||||
for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) {
|
||||
/* If this is an always block and we have no or zero delay then
|
||||
* a runtime infinite loop will happen. If we possible have some
|
||||
* delay then print a warning that an infinite loop is possible.
|
||||
*/
|
||||
if (pr->type() == NetProcTop::KALWAYS) {
|
||||
DelayType dly_type = pr->statement()->delay_type();
|
||||
|
||||
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
|
||||
cerr << pr->get_fileline() << ": error: always"
|
||||
<< " statement does not have any delay." << endl;
|
||||
cerr << pr->get_fileline() << ": : A runtime"
|
||||
<< " infinite loop will occur." << endl;
|
||||
result_flag = false;
|
||||
|
||||
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
|
||||
cerr << pr->get_fileline() << ": warning: always"
|
||||
<< " statement may not have any delay." << endl;
|
||||
cerr << pr->get_fileline() << ": : A runtime"
|
||||
<< " infinite loop may be possible." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result_flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the root of all elaboration. The input is the list
|
||||
* of root module names. The function locates the Module definitions
|
||||
|
|
@ -3880,7 +3914,7 @@ Design* elaborate(list<perm_string>roots)
|
|||
// Get the module definition for this root instance.
|
||||
Module *rmod = (*mod).second;
|
||||
|
||||
// Make the root scope. This makes a NetScoep object and
|
||||
// Make the root scope. This makes a NetScope object and
|
||||
// pushes it into the list of root scopes in the Design.
|
||||
NetScope*scope = des->make_root_scope(*root);
|
||||
|
||||
|
|
@ -3893,7 +3927,7 @@ Design* elaborate(list<perm_string>roots)
|
|||
des->set_precision(rmod->time_precision);
|
||||
|
||||
|
||||
// Save this scope, along with its defintion, in the
|
||||
// Save this scope, along with its definition, in the
|
||||
// "root_elems" list for later passes.
|
||||
struct root_elem *r = new struct root_elem;
|
||||
r->mod = rmod;
|
||||
|
|
@ -3971,8 +4005,14 @@ Design* elaborate(list<perm_string>roots)
|
|||
rc &= rmod->elaborate(des, scope);
|
||||
}
|
||||
|
||||
|
||||
if (rc == false) {
|
||||
delete des;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now that everything is fully elaborated verify that we do
|
||||
// not have an always block with no delay (an infinite loop).
|
||||
if (des->check_always_delay() == false) {
|
||||
delete des;
|
||||
des = 0;
|
||||
}
|
||||
|
|
|
|||
75
eval_tree.cc
75
eval_tree.cc
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
# include <iostream>
|
||||
# include <cstdlib>
|
||||
# include <cstring>
|
||||
# include <math.h>
|
||||
|
||||
# include "netlist.h"
|
||||
# include "ivl_assert.h"
|
||||
|
|
@ -1607,3 +1609,76 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width)
|
|||
|
||||
return new NetEConst(verinum(res, 1));
|
||||
}
|
||||
|
||||
NetExpr* evaluate_clog2(NetExpr*arg)
|
||||
{
|
||||
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
|
||||
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
|
||||
if (tmpi || tmpr) {
|
||||
verinum arg;
|
||||
if (tmpi) {
|
||||
arg = tmpi->value();
|
||||
} else {
|
||||
arg = verinum(tmpr->value().as_double(), true);
|
||||
}
|
||||
|
||||
/* If we have an x in the verinum we return 32'bx. */
|
||||
if (!arg.is_defined()) {
|
||||
verinum tmp (verinum::Vx, 32);
|
||||
tmp.has_sign(true);
|
||||
NetEConst*rtn = new NetEConst(tmp);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
bool is_neg = false;
|
||||
uint64_t res = 0;
|
||||
if (arg.is_negative()) {
|
||||
is_neg = true;
|
||||
// If the length is not defined, then work with
|
||||
// the trimmed version of the number.
|
||||
if (! arg.has_len())
|
||||
arg = trim_vnum(arg);
|
||||
}
|
||||
arg.has_sign(false); // $unsigned()
|
||||
|
||||
if (!arg.is_zero()) {
|
||||
arg = arg - verinum((uint64_t)1, 1);
|
||||
while (!arg.is_zero()) {
|
||||
res += 1;
|
||||
arg = arg >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_neg && res < integer_width)
|
||||
res = integer_width;
|
||||
|
||||
verinum tmp (res, 32);
|
||||
NetEConst*rtn = new NetEConst(tmp);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NetExpr* NetESFunc::eval_tree(int prune_to_width)
|
||||
{
|
||||
/* For now only $clog2 can be a constant system function. */
|
||||
if (strcmp(name(), "$clog2") == 0) {
|
||||
if (nparms() != 1 || parm(0) == 0) {
|
||||
cerr << get_fileline() << ": error: $clog2 takes a single "
|
||||
"argument." << endl;
|
||||
return 0;
|
||||
}
|
||||
NetExpr*rtn = evaluate_clog2(parm(0));
|
||||
if (rtn != 0) {
|
||||
rtn->set_line(*this);
|
||||
if (debug_eval_tree) {
|
||||
cerr << get_fileline() << ": debug: Evaluate "
|
||||
"constant $clog2()." << endl;
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
1
ivl.def
1
ivl.def
|
|
@ -143,6 +143,7 @@ ivl_scope_def_lineno
|
|||
ivl_scope_event
|
||||
ivl_scope_events
|
||||
ivl_scope_file
|
||||
ivl_scope_is_auto
|
||||
ivl_scope_lineno
|
||||
ivl_scope_logs
|
||||
ivl_scope_log
|
||||
|
|
|
|||
|
|
@ -1448,6 +1448,9 @@ extern unsigned ivl_parameter_lineno(ivl_parameter_t net);
|
|||
* ivl_scope_lineno
|
||||
* Returns the instantiation file and line for this scope.
|
||||
*
|
||||
* ivl_scope_is_auto
|
||||
* Is the task or function declared to be automatic?
|
||||
*
|
||||
* ivl_scope_var
|
||||
* ivl_scope_vars
|
||||
* REMOVED
|
||||
|
|
@ -1524,6 +1527,7 @@ extern unsigned ivl_scope_def_lineno(ivl_scope_t net);
|
|||
extern unsigned ivl_scope_events(ivl_scope_t net);
|
||||
extern ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx);
|
||||
extern const char* ivl_scope_file(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_is_auto(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_lineno(ivl_scope_t net);
|
||||
extern unsigned ivl_scope_logs(ivl_scope_t net);
|
||||
extern ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx);
|
||||
|
|
|
|||
|
|
@ -1588,6 +1588,12 @@ static int load_next_input()
|
|||
if(depend_file)
|
||||
fprintf(depend_file, "%s\n", istack->path);
|
||||
|
||||
/* This works around an issue in flex yyrestart() where it
|
||||
* uses yyin to create a new buffer when one does not exist.
|
||||
* I would have assumed that it would use the file argument.
|
||||
* The problem is that we have deleted the buffer and freed
|
||||
* yyin (isp->file) above. */
|
||||
yyin = 0;
|
||||
yyrestart(istack->file);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ assign, GN_KEYWORDS_1364_1995, K_assign
|
|||
atan, GN_KEYWORDS_VAMS_2_3, K_atan
|
||||
atan2, GN_KEYWORDS_VAMS_2_3, K_atan2
|
||||
atanh, GN_KEYWORDS_VAMS_2_3, K_atanh
|
||||
automatic, GN_KEYWORDS_1364_2001, K_automatic
|
||||
begin, GN_KEYWORDS_1364_1995, K_begin
|
||||
bool, GN_KEYWORDS_ICARUS, K_bool
|
||||
buf, GN_KEYWORDS_1364_1995, K_buf
|
||||
|
|
|
|||
|
|
@ -357,11 +357,11 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
|
|||
|
||||
/* Evaluate the parameter expression, if necessary. */
|
||||
NetExpr*expr = (*cur).second.expr;
|
||||
assert(expr);
|
||||
if (expr == NULL) return; // This is an invalid parameter so return.
|
||||
|
||||
eval_expr(expr);
|
||||
|
||||
/* The eval_expr may delete any replace the expr pointer, so the
|
||||
/* The eval_expr may delete and replace the expr pointer, so the
|
||||
second.expr value cannot be relied on. Might as well replace
|
||||
it now with the expression that we evaluated. */
|
||||
(*cur).second.expr = expr;
|
||||
|
|
|
|||
35
net_link.cc
35
net_link.cc
|
|
@ -247,6 +247,41 @@ verinum::V Nexus::get_init() const
|
|||
return verinum::Vz;
|
||||
}
|
||||
|
||||
bool Nexus::drivers_present() const
|
||||
{
|
||||
assert(list_);
|
||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
||||
if (cur->get_dir() == Link::OUTPUT)
|
||||
return true;
|
||||
|
||||
if (cur->get_dir() == Link::INPUT)
|
||||
continue;
|
||||
|
||||
// Must be PASSIVE, so if it is some kind of net, see if
|
||||
// it is the sort that might drive the nexus.
|
||||
const NetPins*obj;
|
||||
unsigned pin;
|
||||
cur->cur_link(obj, pin);
|
||||
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
|
||||
switch (net->type()) {
|
||||
case NetNet::SUPPLY0:
|
||||
case NetNet::SUPPLY1:
|
||||
case NetNet::TRI0:
|
||||
case NetNet::TRI1:
|
||||
case NetNet::WAND:
|
||||
case NetNet::WOR:
|
||||
case NetNet::TRIAND:
|
||||
case NetNet::TRIOR:
|
||||
case NetNet::REG:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
||||
{
|
||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -53,8 +53,13 @@ NexusSet* NetEBinary::nex_input(bool rem_out)
|
|||
|
||||
NexusSet* NetEConcat::nex_input(bool rem_out)
|
||||
{
|
||||
if (parms_[0] == NULL) return NULL;
|
||||
NexusSet*result = parms_[0]->nex_input(rem_out);
|
||||
for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) {
|
||||
if (parms_[idx] == NULL) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
NexusSet*tmp = parms_[idx]->nex_input(rem_out);
|
||||
result->add(*tmp);
|
||||
delete tmp;
|
||||
|
|
@ -103,6 +108,10 @@ NexusSet* NetESelect::nex_input(bool rem_out)
|
|||
{
|
||||
NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet();
|
||||
NexusSet*tmp = expr_->nex_input(rem_out);
|
||||
if (tmp == NULL) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
result->add(*tmp);
|
||||
delete tmp;
|
||||
return result;
|
||||
|
|
@ -386,4 +395,3 @@ NexusSet* NetWhile::nex_input(bool rem_out)
|
|||
delete tmp;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
|
|||
signals_ = 0;
|
||||
events_ = 0;
|
||||
lcounter_ = 0;
|
||||
is_auto_ = false;
|
||||
|
||||
if (up) {
|
||||
default_nettype_ = up->default_nettype();
|
||||
|
|
@ -261,6 +262,7 @@ NetFuncDef* NetScope::func_def()
|
|||
assert( type_ == FUNC );
|
||||
return func_;
|
||||
}
|
||||
|
||||
bool NetScope::in_func()
|
||||
{
|
||||
return (type_ == FUNC) ? true : false;
|
||||
|
|
|
|||
|
|
@ -2608,6 +2608,7 @@ DelayType NetBlock::delay_type() const
|
|||
for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) {
|
||||
DelayType dt = cur->delay_type();
|
||||
if (dt > result) result = dt;
|
||||
if (dt == DEFINITE_DELAY) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -2642,7 +2643,9 @@ DelayType NetCondit::delay_type() const
|
|||
if (else_) {
|
||||
result = combine_delays(if_->delay_type(), else_->delay_type());
|
||||
} else {
|
||||
result = if_->delay_type();
|
||||
/* Because of the indeterminate conditional value the
|
||||
* best we can have for this case is a possible delay. */
|
||||
result = combine_delays(if_->delay_type(), NO_DELAY);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
11
netlist.h
11
netlist.h
|
|
@ -322,6 +322,10 @@ class Nexus {
|
|||
|
||||
NetNet* pick_any_net();
|
||||
|
||||
/* This method returns true if there are any drivers
|
||||
(including variables) attached to this nexus. */
|
||||
bool drivers_present() const;
|
||||
|
||||
/* This method returns true if all the possible drivers of
|
||||
this nexus are constant. It will also return true if there
|
||||
are no drivers at all. */
|
||||
|
|
@ -725,6 +729,9 @@ class NetScope : public Attrib {
|
|||
unsigned get_def_lineno() const { return def_lineno_; };
|
||||
|
||||
bool in_func();
|
||||
/* Is the task or function automatic. */
|
||||
void is_auto(bool is_auto) { is_auto_ = is_auto; };
|
||||
bool is_auto() const { return is_auto_; };
|
||||
|
||||
const NetTaskDef* task_def() const;
|
||||
const NetFuncDef* func_def() const;
|
||||
|
|
@ -868,6 +875,7 @@ class NetScope : public Attrib {
|
|||
NetScope*sub_;
|
||||
|
||||
unsigned lcounter_;
|
||||
bool is_auto_;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -3496,6 +3504,8 @@ class NetESFunc : public NetExpr {
|
|||
NetExpr* parm(unsigned idx);
|
||||
const NetExpr* parm(unsigned idx) const;
|
||||
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
|
||||
virtual ivl_variable_type_t expr_type() const;
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
virtual bool set_width(unsigned, bool last_chance);
|
||||
|
|
@ -3763,6 +3773,7 @@ class Design {
|
|||
// PROCESSES
|
||||
void add_process(NetProcTop*);
|
||||
void delete_process(NetProcTop*);
|
||||
bool check_always_delay() const;
|
||||
|
||||
// Iterate over the design...
|
||||
void dump(ostream&) const;
|
||||
|
|
|
|||
|
|
@ -245,16 +245,16 @@ bool eval_as_double(double&value, NetExpr*expr)
|
|||
}
|
||||
|
||||
/*
|
||||
* At the parser level, a name component it a name with a collection
|
||||
* At the parser level, a name component is a name with a collection
|
||||
* of expressions. For example foo[N] is the name "foo" and the index
|
||||
* expression "N". This function takes as input the name component and
|
||||
* returns the path component name. It will evaulate the index
|
||||
* returns the path component name. It will evaluate the index
|
||||
* expression if it is present.
|
||||
*/
|
||||
hname_t eval_path_component(Design*des, NetScope*scope,
|
||||
const name_component_t&comp)
|
||||
{
|
||||
// No index exression, so the path component is an undecorated
|
||||
// No index expression, so the path component is an undecorated
|
||||
// name, for example "foo".
|
||||
if (comp.index.empty())
|
||||
return hname_t(comp.name);
|
||||
|
|
@ -284,7 +284,7 @@ hname_t eval_path_component(Design*des, NetScope*scope,
|
|||
}
|
||||
|
||||
// Darn, the expression doesn't evaluate to a constant. That's
|
||||
// and error to be reported. And make up a fake index value to
|
||||
// an error to be reported. And make up a fake index value to
|
||||
// return to the caller.
|
||||
cerr << index.msb->get_fileline() << ": error: "
|
||||
<< "Scope index expression is not constant: "
|
||||
|
|
|
|||
317
parse.y
317
parse.y
|
|
@ -206,7 +206,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
|||
%token K_PSTAR K_STARP
|
||||
%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER
|
||||
%token K_abs K_abstol K_access K_acos K_acosh K_asin K_analog K_asinh
|
||||
%token K_atan K_atanh K_atan2
|
||||
%token K_atan K_atanh K_atan2 K_automatic
|
||||
%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_continuous K_cos K_cosh
|
||||
%token K_ddt_nature K_deassign K_default K_defparam K_disable K_discrete
|
||||
|
|
@ -240,7 +240,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
|||
|
||||
%type <flag> from_exclude
|
||||
%type <number> number
|
||||
%type <flag> signed_opt udp_reg_opt edge_operator
|
||||
%type <flag> signed_opt udp_reg_opt edge_operator automatic_opt
|
||||
%type <drive> drive_strength drive_strength_opt dr_strength0 dr_strength1
|
||||
%type <letter> udp_input_sym udp_output_sym
|
||||
%type <text> udp_input_list udp_sequ_entry udp_comb_entry
|
||||
|
|
@ -355,6 +355,13 @@ number : BASED_NUMBER
|
|||
based_size = 0; }
|
||||
;
|
||||
|
||||
/* real and realtime are exactly the same so save some code
|
||||
* with a common matching rule. */
|
||||
real_or_realtime
|
||||
: K_real
|
||||
| K_realtime
|
||||
;
|
||||
|
||||
/* Verilog-2001 supports attribute lists, which can be attached to a
|
||||
variety of different objects. The syntax inside the (* *) is a
|
||||
comma separated list of names or names with assigned values. */
|
||||
|
|
@ -1501,6 +1508,32 @@ gate_instance
|
|||
delete rng;
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| IDENTIFIER '(' error ')'
|
||||
{ lgate*tmp = new lgate;
|
||||
tmp->name = $1;
|
||||
tmp->parms = 0;
|
||||
tmp->parms_by_name = 0;
|
||||
tmp->file = @1.text;
|
||||
tmp->lineno = @1.first_line;
|
||||
yyerror(@2, "error: Syntax error in instance port "
|
||||
"expression(s).");
|
||||
delete[]$1;
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| IDENTIFIER range '(' error ')'
|
||||
{ lgate*tmp = new lgate;
|
||||
tmp->name = $1;
|
||||
tmp->parms = 0;
|
||||
tmp->parms_by_name = 0;
|
||||
tmp->file = @1.text;
|
||||
tmp->lineno = @1.first_line;
|
||||
yyerror(@3, "error: Syntax error in instance port "
|
||||
"expression(s).");
|
||||
delete[]$1;
|
||||
$$ = tmp;
|
||||
}
|
||||
;
|
||||
|
||||
gate_instance_list
|
||||
|
|
@ -2094,41 +2127,41 @@ module_item
|
|||
statements in the task body. But we continue to accept it as an
|
||||
extension. */
|
||||
|
||||
| K_task IDENTIFIER ';'
|
||||
| K_task automatic_opt IDENTIFIER ';'
|
||||
{ assert(current_task == 0);
|
||||
current_task = pform_push_task_scope($2);
|
||||
current_task = pform_push_task_scope($3, $2);
|
||||
FILE_NAME(current_task, @1);
|
||||
}
|
||||
task_item_list_opt
|
||||
statement_or_null
|
||||
K_endtask
|
||||
{ current_task->set_ports($5);
|
||||
current_task->set_statement($6);
|
||||
{ current_task->set_ports($6);
|
||||
current_task->set_statement($7);
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$2;
|
||||
delete[]$3;
|
||||
}
|
||||
|
||||
| K_task IDENTIFIER
|
||||
| K_task automatic_opt IDENTIFIER
|
||||
{ assert(current_task == 0);
|
||||
current_task = pform_push_task_scope($2);
|
||||
current_task = pform_push_task_scope($3, $2);
|
||||
FILE_NAME(current_task, @1);
|
||||
}
|
||||
'(' task_port_decl_list ')' ';'
|
||||
block_item_decls_opt
|
||||
statement_or_null
|
||||
K_endtask
|
||||
{ current_task->set_ports($5);
|
||||
current_task->set_statement($9);
|
||||
{ current_task->set_ports($6);
|
||||
current_task->set_statement($10);
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$2;
|
||||
delete[]$3;
|
||||
}
|
||||
| K_task IDENTIFIER error K_endtask
|
||||
| K_task automatic_opt IDENTIFIER error K_endtask
|
||||
{
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$2;
|
||||
delete[]$3;
|
||||
}
|
||||
|
||||
/* The function declaration rule matches the function declaration
|
||||
|
|
@ -2136,42 +2169,42 @@ module_item
|
|||
definitions in the func_body to take on the scope of the function
|
||||
instead of the module. */
|
||||
|
||||
| K_function function_range_or_type_opt IDENTIFIER ';'
|
||||
| K_function automatic_opt function_range_or_type_opt IDENTIFIER ';'
|
||||
{ assert(current_function == 0);
|
||||
current_function = pform_push_function_scope($3);
|
||||
current_function = pform_push_function_scope($4, $2);
|
||||
FILE_NAME(current_function, @1);
|
||||
}
|
||||
function_item_list statement
|
||||
K_endfunction
|
||||
{ current_function->set_ports($6);
|
||||
current_function->set_statement($7);
|
||||
current_function->set_return($2);
|
||||
{ current_function->set_ports($7);
|
||||
current_function->set_statement($8);
|
||||
current_function->set_return($3);
|
||||
pform_pop_scope();
|
||||
current_function = 0;
|
||||
delete[]$3;
|
||||
delete[]$4;
|
||||
}
|
||||
|
||||
| K_function function_range_or_type_opt IDENTIFIER
|
||||
| K_function automatic_opt function_range_or_type_opt IDENTIFIER
|
||||
{ assert(current_function == 0);
|
||||
current_function = pform_push_function_scope($3);
|
||||
current_function = pform_push_function_scope($4, $2);
|
||||
FILE_NAME(current_function, @1);
|
||||
}
|
||||
'(' task_port_decl_list ')' ';'
|
||||
block_item_decls_opt
|
||||
statement
|
||||
K_endfunction
|
||||
{ current_function->set_ports($6);
|
||||
current_function->set_statement($10);
|
||||
current_function->set_return($2);
|
||||
{ current_function->set_ports($7);
|
||||
current_function->set_statement($11);
|
||||
current_function->set_return($3);
|
||||
pform_pop_scope();
|
||||
current_function = 0;
|
||||
delete[]$3;
|
||||
delete[]$4;
|
||||
}
|
||||
| K_function function_range_or_type_opt IDENTIFIER error K_endfunction
|
||||
| K_function automatic_opt function_range_or_type_opt IDENTIFIER error K_endfunction
|
||||
{
|
||||
pform_pop_scope();
|
||||
current_task = 0;
|
||||
delete[]$3;
|
||||
delete[]$4;
|
||||
}
|
||||
|
||||
/* A generate region can contain further module items. Actually, it
|
||||
|
|
@ -2278,6 +2311,11 @@ module_item
|
|||
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
|
||||
;
|
||||
|
||||
automatic_opt
|
||||
: K_automatic { $$ = true; }
|
||||
| { $$ = false;}
|
||||
;
|
||||
|
||||
generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); }
|
||||
|
||||
generate_case_items
|
||||
|
|
@ -2433,17 +2471,7 @@ parameter_assign_decl
|
|||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_real
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_REAL;
|
||||
}
|
||||
parameter_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_realtime
|
||||
| real_or_realtime
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_REAL;
|
||||
|
|
@ -2572,17 +2600,7 @@ localparam_assign_decl
|
|||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_real
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_REAL;
|
||||
}
|
||||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_realtime
|
||||
| real_or_realtime
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_REAL;
|
||||
|
|
@ -3741,39 +3759,43 @@ task_item
|
|||
| task_port_item { $$ = $1; }
|
||||
;
|
||||
|
||||
reg_opt
|
||||
: K_reg
|
||||
|
|
||||
;
|
||||
|
||||
task_port_item
|
||||
|
||||
: K_input signed_opt range_opt list_of_identifiers ';'
|
||||
: K_input reg_opt signed_opt range_opt list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_NO_TYPE, $2,
|
||||
$3, $4,
|
||||
IVL_VT_NO_TYPE, $3,
|
||||
$4, $5,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output signed_opt range_opt list_of_identifiers ';'
|
||||
| K_output reg_opt signed_opt range_opt list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, $4,
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, $5,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout signed_opt range_opt list_of_identifiers ';'
|
||||
| K_inout reg_opt signed_opt range_opt list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, $4,
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, $5,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* When the port is an integer, infer a signed vector of the integer
|
||||
shape. Generate a range to make it work. */
|
||||
shape. Generate a range ([31:0]) to make it work. */
|
||||
|
||||
| K_input K_integer list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
|
|
@ -3788,8 +3810,7 @@ task_port_item
|
|||
$$ = tmp;
|
||||
}
|
||||
| K_output K_integer list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
|
|
@ -3804,8 +3825,7 @@ task_port_item
|
|||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_integer list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
|
|
@ -3820,9 +3840,54 @@ task_port_item
|
|||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be real. */
|
||||
/* Ports can be time with a width of [63:0] (unsigned). */
|
||||
|
||||
| K_input K_real list_of_identifiers ';'
|
||||
| K_input K_time list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub, $3,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_time list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub, $3,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_time list_of_identifiers ';'
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub, $3,
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be real or realtime. */
|
||||
|
||||
| K_input real_or_realtime list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_REAL, false,
|
||||
|
|
@ -3830,7 +3895,7 @@ task_port_item
|
|||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_real list_of_identifiers ';'
|
||||
| K_output real_or_realtime list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_REAL, true,
|
||||
|
|
@ -3838,7 +3903,7 @@ task_port_item
|
|||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_real list_of_identifiers ';'
|
||||
| K_inout real_or_realtime list_of_identifiers ';'
|
||||
{ svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_REAL, true,
|
||||
|
|
@ -3868,50 +3933,51 @@ task_item_list_opt
|
|||
|
||||
task_port_decl
|
||||
|
||||
: K_input signed_opt range_opt IDENTIFIER
|
||||
: K_input reg_opt signed_opt range_opt IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = $2;
|
||||
port_declaration_context.sign_flag = $3;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range($3);
|
||||
port_declaration_context.range = copy_range($4);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, list_from_identifier($4),
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, list_from_identifier($5),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| K_output signed_opt range_opt IDENTIFIER
|
||||
| K_output reg_opt signed_opt range_opt IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::POUTPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = $2;
|
||||
port_declaration_context.sign_flag = $3;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range($3);
|
||||
port_declaration_context.range = copy_range($4);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, list_from_identifier($4),
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, list_from_identifier($5),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout signed_opt range_opt IDENTIFIER
|
||||
| K_inout reg_opt signed_opt range_opt IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINOUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = $2;
|
||||
port_declaration_context.sign_flag = $3;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range($3);
|
||||
port_declaration_context.range = copy_range($4);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, $2,
|
||||
$3, list_from_identifier($4),
|
||||
IVL_VT_LOGIC, $3,
|
||||
$4, list_from_identifier($5),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be integer with a width of [31:0]. */
|
||||
|
||||
| K_input K_integer IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
|
|
@ -3932,8 +3998,7 @@ task_port_decl
|
|||
$$ = tmp;
|
||||
}
|
||||
| K_output K_integer IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
|
|
@ -3954,8 +4019,7 @@ task_port_decl
|
|||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_integer IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub
|
||||
= new svector<PExpr*>(2);
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1,
|
||||
integer_width));
|
||||
|
|
@ -3976,9 +4040,72 @@ task_port_decl
|
|||
$$ = tmp;
|
||||
}
|
||||
|
||||
/* Ports can be real. */
|
||||
/* Ports can be time with a width of [63:0] (unsigned). */
|
||||
|
||||
| K_input K_time IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
port_declaration_context.port_type = NetNet::PINPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = false;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range(range_stub);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub,
|
||||
list_from_identifier($3),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_time IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
port_declaration_context.port_type = NetNet::POUTPUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = false;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range(range_stub);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::POUTPUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub,
|
||||
list_from_identifier($3),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_time IDENTIFIER
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum((uint64_t)63, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
port_declaration_context.port_type = NetNet::PINOUT;
|
||||
port_declaration_context.var_type = IVL_VT_LOGIC;
|
||||
port_declaration_context.sign_flag = false;
|
||||
delete port_declaration_context.range;
|
||||
port_declaration_context.range = copy_range(range_stub);
|
||||
svector<PWire*>*tmp
|
||||
= pform_make_task_ports(NetNet::PINOUT,
|
||||
IVL_VT_LOGIC, false,
|
||||
range_stub,
|
||||
list_from_identifier($3),
|
||||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
|
||||
| K_input K_real IDENTIFIER
|
||||
/* Ports can be real or realtime. */
|
||||
|
||||
| K_input real_or_realtime IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINPUT;
|
||||
port_declaration_context.var_type = IVL_VT_REAL;
|
||||
port_declaration_context.sign_flag = false;
|
||||
|
|
@ -3991,7 +4118,7 @@ task_port_decl
|
|||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_output K_real IDENTIFIER
|
||||
| K_output real_or_realtime IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::POUTPUT;
|
||||
port_declaration_context.var_type = IVL_VT_REAL;
|
||||
port_declaration_context.sign_flag = false;
|
||||
|
|
@ -4004,7 +4131,7 @@ task_port_decl
|
|||
@1.text, @1.first_line);
|
||||
$$ = tmp;
|
||||
}
|
||||
| K_inout K_real IDENTIFIER
|
||||
| K_inout real_or_realtime IDENTIFIER
|
||||
{ port_declaration_context.port_type = NetNet::PINOUT;
|
||||
port_declaration_context.var_type = IVL_VT_REAL;
|
||||
port_declaration_context.sign_flag = false;
|
||||
|
|
|
|||
14
pform.cc
14
pform.cc
|
|
@ -103,17 +103,18 @@ void pform_pop_scope()
|
|||
}
|
||||
}
|
||||
|
||||
PTask* pform_push_task_scope(char*name)
|
||||
PTask* pform_push_task_scope(char*name, bool is_auto)
|
||||
{
|
||||
perm_string task_name = lex_strings.make(name);
|
||||
|
||||
PTask*task;
|
||||
if (pform_cur_generate) {
|
||||
task = new PTask(task_name, pform_cur_generate->lexical_scope);
|
||||
task = new PTask(task_name, pform_cur_generate->lexical_scope,
|
||||
is_auto);
|
||||
pform_cur_generate->tasks[task->pscope_name()] = task;
|
||||
pform_cur_generate->lexical_scope = task;
|
||||
} else {
|
||||
task = new PTask(task_name, lexical_scope);
|
||||
task = new PTask(task_name, lexical_scope, is_auto);
|
||||
pform_cur_module->tasks[task->pscope_name()] = task;
|
||||
lexical_scope = task;
|
||||
}
|
||||
|
|
@ -121,17 +122,18 @@ PTask* pform_push_task_scope(char*name)
|
|||
return task;
|
||||
}
|
||||
|
||||
PFunction* pform_push_function_scope(char*name)
|
||||
PFunction* pform_push_function_scope(char*name, bool is_auto)
|
||||
{
|
||||
perm_string func_name = lex_strings.make(name);
|
||||
|
||||
PFunction*func;
|
||||
if (pform_cur_generate) {
|
||||
func = new PFunction(func_name, pform_cur_generate->lexical_scope);
|
||||
func = new PFunction(func_name, pform_cur_generate->lexical_scope,
|
||||
is_auto);
|
||||
pform_cur_generate->funcs[func->pscope_name()] = func;
|
||||
pform_cur_generate->lexical_scope = func;
|
||||
} else {
|
||||
func = new PFunction(func_name, lexical_scope);
|
||||
func = new PFunction(func_name, lexical_scope, is_auto);
|
||||
pform_cur_module->funcs[func->pscope_name()] = func;
|
||||
lexical_scope = func;
|
||||
}
|
||||
|
|
|
|||
4
pform.h
4
pform.h
|
|
@ -176,8 +176,8 @@ extern void pform_make_udp(perm_string name,
|
|||
*/
|
||||
extern void pform_pop_scope();
|
||||
|
||||
extern PTask*pform_push_task_scope(char*name);
|
||||
extern PFunction*pform_push_function_scope(char*name);
|
||||
extern PTask*pform_push_task_scope(char*name, bool is_auto);
|
||||
extern PFunction*pform_push_function_scope(char*name, bool is_auto);
|
||||
extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt);
|
||||
|
||||
extern void pform_put_behavior_in_scope(AProcess*proc);
|
||||
|
|
|
|||
|
|
@ -280,6 +280,9 @@ void PEBinary::dump(ostream&out) const
|
|||
case 'N':
|
||||
out << "!==";
|
||||
break;
|
||||
case 'p':
|
||||
out << "**";
|
||||
break;
|
||||
case 'R':
|
||||
out << ">>>";
|
||||
break;
|
||||
|
|
@ -733,6 +736,7 @@ void PForStatement::dump(ostream&out, unsigned ind) const
|
|||
void PFunction::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "" << "function ";
|
||||
if (is_auto_) cout << "automatic ";
|
||||
switch (return_type_.type) {
|
||||
case PTF_NONE:
|
||||
out << "?none? ";
|
||||
|
|
@ -793,6 +797,9 @@ void PRepeat::dump(ostream&out, unsigned ind) const
|
|||
|
||||
void PTask::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "" << "task ";
|
||||
if (is_auto_) cout << "automatic ";
|
||||
out << pscope_name() << ";" << endl;
|
||||
if (ports_)
|
||||
for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) {
|
||||
out << setw(ind) << "";
|
||||
|
|
|
|||
|
|
@ -1548,6 +1548,12 @@ extern "C" const char*ivl_scope_file(ivl_scope_t net)
|
|||
return net->file.str();
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net)
|
||||
{
|
||||
assert(net);
|
||||
return net->is_auto;
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_scope_lineno(ivl_scope_t net)
|
||||
{
|
||||
assert(net);
|
||||
|
|
|
|||
7
t-dll.cc
7
t-dll.cc
|
|
@ -575,6 +575,7 @@ void dll_target::add_root(ivl_design_s &des_, const NetScope *s)
|
|||
root_->time_units = s->time_unit();
|
||||
root_->nattr = s->attr_cnt();
|
||||
root_->attr = fill_in_attributes(s);
|
||||
root_->is_auto = 0;
|
||||
|
||||
des_.nroots_++;
|
||||
if (des_.roots_)
|
||||
|
|
@ -2319,7 +2320,8 @@ void dll_target::scope(const NetScope*net)
|
|||
scope->time_precision = net->time_precision();
|
||||
scope->time_units = net->time_unit();
|
||||
scope->nattr = net->attr_cnt();
|
||||
scope->attr = fill_in_attributes(net);
|
||||
scope->attr = fill_in_attributes(net);
|
||||
scope->is_auto = net->is_auto();
|
||||
|
||||
switch (net->type()) {
|
||||
case NetScope::MODULE:
|
||||
|
|
@ -2374,8 +2376,7 @@ void dll_target::signal(const NetNet*net)
|
|||
object, or creating the sigs_ array if this is the first
|
||||
signal. */
|
||||
obj->scope_ = find_scope(des_, net->scope());
|
||||
obj->file = perm_string::literal("N/A");
|
||||
obj->lineno = 0;
|
||||
FILE_NAME(obj, net);
|
||||
assert(obj->scope_);
|
||||
|
||||
if (obj->scope_->nsigs_ == 0) {
|
||||
|
|
|
|||
7
t-dll.h
7
t-dll.h
|
|
@ -591,6 +591,7 @@ struct ivl_scope_s {
|
|||
|
||||
/* Scopes that are tasks/functions have a definition. */
|
||||
ivl_statement_t def;
|
||||
unsigned is_auto;
|
||||
|
||||
unsigned ports;
|
||||
ivl_signal_t*port;
|
||||
|
|
@ -772,4 +773,10 @@ static inline void FILE_NAME(ivl_process_t net, const LineInfo*info)
|
|||
net->lineno = info->get_lineno();
|
||||
}
|
||||
|
||||
static inline void FILE_NAME(ivl_signal_t net, const LineInfo*info)
|
||||
{
|
||||
net->file = info->get_file();
|
||||
net->lineno = info->get_lineno();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1448,12 +1448,13 @@ static int show_scope(ivl_scope_t net, void*x)
|
|||
ivl_scope_name(net), ivl_scope_params(net),
|
||||
ivl_scope_sigs(net), ivl_scope_logs(net));
|
||||
|
||||
char *is_auto = ivl_scope_is_auto(net) ? "automatic " : "";
|
||||
switch (ivl_scope_type(net)) {
|
||||
case IVL_SCT_MODULE:
|
||||
fprintf(out, " module %s", ivl_scope_tname(net));
|
||||
break;
|
||||
case IVL_SCT_FUNCTION:
|
||||
fprintf(out, " function %s", ivl_scope_tname(net));
|
||||
fprintf(out, " function %s%s", is_auto, ivl_scope_tname(net));
|
||||
break;
|
||||
case IVL_SCT_BEGIN:
|
||||
fprintf(out, " begin : %s", ivl_scope_tname(net));
|
||||
|
|
@ -1462,7 +1463,7 @@ static int show_scope(ivl_scope_t net, void*x)
|
|||
fprintf(out, " fork : %s", ivl_scope_tname(net));
|
||||
break;
|
||||
case IVL_SCT_TASK:
|
||||
fprintf(out, " task %s", ivl_scope_tname(net));
|
||||
fprintf(out, " task %s%s", is_auto, ivl_scope_tname(net));
|
||||
break;
|
||||
default:
|
||||
fprintf(out, " type(%u) %s", ivl_scope_type(net),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
#
|
||||
# This source code is free software; you can redistribute it
|
||||
# and/or modify it in source code form under the terms of the GNU
|
||||
# Library General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Library General Public
|
||||
# License along with this program; if not, write to the Free
|
||||
# Software Foundation, Inc.,
|
||||
# 59 Temple Place - Suite 330
|
||||
# Boston, MA 02111-1307, USA
|
||||
#
|
||||
SHELL = /bin/sh
|
||||
|
||||
VERSION = 0.0
|
||||
|
||||
prefix = @prefix@
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
srcdir = @srcdir@
|
||||
|
||||
VPATH = $(srcdir)
|
||||
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
includedir = $(prefix)/include
|
||||
|
||||
CXX = @CXX@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
|
||||
CPPFLAGS = @ident_support@ -I. -I$(srcdir)/.. @CPPFLAGS@ @DEFS@ @PICFLAG@
|
||||
CXXFLAGS = -Wall @CXXFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
all: dep vhdl.tgt vhdl.conf
|
||||
|
||||
dep:
|
||||
mkdir dep
|
||||
|
||||
%.o: %.cc
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -MD -c $< -o $*.o
|
||||
mv $*.d dep
|
||||
|
||||
O = vhdl.o vhdl_element.o vhdl_type.o vhdl_syntax.o scope.o process.o \
|
||||
stmt.o expr.o lpm.o display.o support.o cast.o logic.o
|
||||
|
||||
ifeq (@WIN32@,yes)
|
||||
TGTLDFLAGS=-L.. -livl
|
||||
TGTDEPLIBS=../libivl.a
|
||||
else
|
||||
TGTLDFLAGS=
|
||||
TGTDEPLIBS=
|
||||
endif
|
||||
|
||||
vhdl.tgt: $O $(TGTDEPLIBS)
|
||||
$(CXX) @shared@ -o $@ $O $(TGTLDFLAGS)
|
||||
|
||||
Makefile: Makefile.in config.status
|
||||
./config.status
|
||||
|
||||
clean:
|
||||
rm -rf $(O) dep vhdl.tgt
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile config.status config.log config.cache vhdl_config.h
|
||||
|
||||
check: all
|
||||
|
||||
install: all installdirs $(libdir)/ivl/vhdl.tgt $(libdir)/ivl/vhdl.conf
|
||||
|
||||
$(libdir)/ivl/vhdl.tgt: ./vhdl.tgt
|
||||
$(INSTALL_PROGRAM) ./vhdl.tgt $(libdir)/ivl/vhdl.tgt
|
||||
|
||||
$(libdir)/ivl/vhdl.conf: vhdl.conf
|
||||
$(INSTALL_DATA) $< $(libdir)/ivl/vhdl.conf
|
||||
|
||||
installdirs: ../mkinstalldirs
|
||||
$(srcdir)/../mkinstalldirs $(libdir)/ivl
|
||||
|
||||
uninstall:
|
||||
rm -f $(libdir)/ivl/vhdl.tgt $(libdir)/ivl/vhdl.conf
|
||||
|
||||
|
||||
-include $(patsubst %.o, dep/%.d, $O)
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* Generate code to convert between VHDL types.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "support.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
|
||||
{
|
||||
//std::cout << "Cast: from=" << type_->get_string()
|
||||
// << " (" << type_->get_width() << ") "
|
||||
// << " to=" << to->get_string() << " ("
|
||||
// << to->get_width() << ")" << std::endl;
|
||||
|
||||
if (to->get_name() == type_->get_name()) {
|
||||
if (to->get_width() == type_->get_width())
|
||||
return this; // Identical
|
||||
else
|
||||
return resize(to->get_width());
|
||||
}
|
||||
else {
|
||||
switch (to->get_name()) {
|
||||
case VHDL_TYPE_BOOLEAN:
|
||||
return to_boolean();
|
||||
case VHDL_TYPE_INTEGER:
|
||||
return to_integer();
|
||||
case VHDL_TYPE_UNSIGNED:
|
||||
case VHDL_TYPE_SIGNED:
|
||||
case VHDL_TYPE_STD_LOGIC_VECTOR:
|
||||
return to_vector(to->get_name(), to->get_width());
|
||||
case VHDL_TYPE_STD_LOGIC:
|
||||
return to_std_logic();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code to cast an expression to a vector type (std_logic_vector,
|
||||
* signed, unsigned).
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_vector(vhdl_type_name_t name, int w)
|
||||
{
|
||||
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
vhdl_expr *others = w == 1 ? NULL : new vhdl_const_bit('0');
|
||||
vhdl_bit_spec_expr *bs =
|
||||
new vhdl_bit_spec_expr(new vhdl_type(name, w - 1, 0), others);
|
||||
bs->add_bit(0, this);
|
||||
|
||||
return bs;
|
||||
}
|
||||
else {
|
||||
// We have to cast the expression before resizing or the
|
||||
// wrong sign bit may be extended (i.e. when casting between
|
||||
// signed/unsigned *and* resizing)
|
||||
vhdl_type *t = new vhdl_type(name, w - 1, 0);
|
||||
vhdl_fcall *conv = new vhdl_fcall(t->get_string().c_str(), t);
|
||||
conv->add_expr(this);
|
||||
|
||||
if (w != type_->get_width())
|
||||
return conv->resize(w);
|
||||
else
|
||||
return conv;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a generic expression to an Integer.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_integer()
|
||||
{
|
||||
vhdl_fcall *conv;
|
||||
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
require_support_function(SF_LOGIC_TO_INTEGER);
|
||||
conv = new vhdl_fcall(support_function::function_name(SF_LOGIC_TO_INTEGER),
|
||||
vhdl_type::integer());
|
||||
}
|
||||
else
|
||||
conv = new vhdl_fcall("To_Integer", vhdl_type::integer());
|
||||
|
||||
conv->add_expr(this);
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a generic expression to a Boolean.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_boolean()
|
||||
{
|
||||
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
|
||||
// '1' is true all else are false
|
||||
vhdl_const_bit *one = new vhdl_const_bit('1');
|
||||
return new vhdl_binop_expr
|
||||
(this, VHDL_BINOP_EQ, one, vhdl_type::boolean());
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
|
||||
// Need to use a support function for this conversion
|
||||
require_support_function(SF_UNSIGNED_TO_BOOLEAN);
|
||||
|
||||
vhdl_fcall *conv =
|
||||
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_BOOLEAN),
|
||||
vhdl_type::boolean());
|
||||
conv->add_expr(this);
|
||||
return conv;
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_SIGNED) {
|
||||
require_support_function(SF_SIGNED_TO_BOOLEAN);
|
||||
|
||||
vhdl_fcall *conv =
|
||||
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_BOOLEAN),
|
||||
vhdl_type::boolean());
|
||||
conv->add_expr(this);
|
||||
return conv;
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code to convert and expression to std_logic.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::to_std_logic()
|
||||
{
|
||||
if (type_->get_name() == VHDL_TYPE_BOOLEAN) {
|
||||
require_support_function(SF_BOOLEAN_TO_LOGIC);
|
||||
|
||||
vhdl_fcall *ah =
|
||||
new vhdl_fcall(support_function::function_name(SF_BOOLEAN_TO_LOGIC),
|
||||
vhdl_type::std_logic());
|
||||
ah->add_expr(this);
|
||||
|
||||
return ah;
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_SIGNED) {
|
||||
require_support_function(SF_SIGNED_TO_LOGIC);
|
||||
|
||||
vhdl_fcall *ah =
|
||||
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_LOGIC),
|
||||
vhdl_type::std_logic());
|
||||
ah->add_expr(this);
|
||||
|
||||
return ah;
|
||||
}
|
||||
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
|
||||
require_support_function(SF_UNSIGNED_TO_LOGIC);
|
||||
|
||||
vhdl_fcall *ah =
|
||||
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_LOGIC),
|
||||
vhdl_type::std_logic());
|
||||
ah->add_expr(this);
|
||||
|
||||
return ah;
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the width of a signed/unsigned type.
|
||||
*/
|
||||
vhdl_expr *vhdl_expr::resize(int newwidth)
|
||||
{
|
||||
vhdl_type *rtype;
|
||||
assert(type_);
|
||||
if (type_->get_name() == VHDL_TYPE_SIGNED)
|
||||
rtype = vhdl_type::nsigned(newwidth);
|
||||
else if (type_->get_name() == VHDL_TYPE_UNSIGNED)
|
||||
rtype = vhdl_type::nunsigned(newwidth);
|
||||
else
|
||||
return this; // Doesn't make sense to resize non-vector type
|
||||
|
||||
vhdl_fcall *resize = new vhdl_fcall("Resize", rtype);
|
||||
resize->add_expr(this);
|
||||
resize->add_expr(new vhdl_const_int(newwidth));
|
||||
|
||||
return resize;
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_int::to_vector(vhdl_type_name_t name, int w)
|
||||
{
|
||||
if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
|
||||
|
||||
const char *fname = name == VHDL_TYPE_SIGNED
|
||||
? "To_Signed" : "To_Unsigned";
|
||||
vhdl_fcall *conv = new vhdl_fcall(fname, new vhdl_type(name, w - 1, 0));
|
||||
conv->add_expr(this);
|
||||
conv->add_expr(new vhdl_const_int(w));
|
||||
|
||||
return conv;
|
||||
}
|
||||
else
|
||||
return vhdl_expr::to_vector(name, w);
|
||||
}
|
||||
|
||||
int vhdl_const_bits::bits_to_int() const
|
||||
{
|
||||
char msb = value_[value_.size() - 1];
|
||||
int result = 0, bit;
|
||||
for (int i = sizeof(int)*8 - 1; i >= 0; i--) {
|
||||
if (i > (int)value_.size() - 1)
|
||||
bit = msb == '1' ? 1 : 0;
|
||||
else
|
||||
bit = value_[i] == '1' ? 1 : 0;
|
||||
result = (result << 1) | bit;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bits::to_std_logic()
|
||||
{
|
||||
// VHDL won't let us cast directly between a vector and
|
||||
// a scalar type
|
||||
// But we don't need to here as we have the bits available
|
||||
|
||||
// Take the least significant bit
|
||||
char lsb = value_[0];
|
||||
|
||||
return new vhdl_const_bit(lsb);
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bits::to_vector(vhdl_type_name_t name, int w)
|
||||
{
|
||||
if (name == VHDL_TYPE_STD_LOGIC_VECTOR) {
|
||||
// Don't need to do anything
|
||||
return this;
|
||||
}
|
||||
else if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
|
||||
// Extend with sign bit
|
||||
value_.resize(w, value_[0]);
|
||||
return this;
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bits::to_integer()
|
||||
{
|
||||
return new vhdl_const_int(bits_to_int());
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bit::to_integer()
|
||||
{
|
||||
return new vhdl_const_int(bit_ == '1' ? 1 : 0);
|
||||
}
|
||||
|
||||
vhdl_expr *vhdl_const_bit::to_boolean()
|
||||
{
|
||||
return new vhdl_const_bool(bit_ == '1');
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(vhdl.cc)
|
||||
AC_CONFIG_HEADER(vhdl_config.h)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
AC_PROG_INSTALL
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
# $host
|
||||
|
||||
# Combined check for Microsoft-related bogosities; sets WIN32 if found
|
||||
AX_WIN32
|
||||
|
||||
AC_CHECK_HEADERS(malloc.h stdint.h inttypes.h)
|
||||
|
||||
# may modify CPPFLAGS and CFLAGS
|
||||
AX_CPP_PRECOMP
|
||||
|
||||
# Compiler option for position independent code, needed when making shared objects.
|
||||
AX_C_PICFLAG
|
||||
|
||||
# linker options when building a shared library
|
||||
AX_LD_SHAREDLIB_OPTS
|
||||
|
||||
AX_CPP_IDENT
|
||||
|
||||
AC_OUTPUT(Makefile)
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* VHDL implementation of $display.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
static const char *DISPLAY_LINE = "Verilog_Display_Line";
|
||||
|
||||
/*
|
||||
* Write a VHDL expression into the current display line.
|
||||
*/
|
||||
static void display_write(stmt_container *container, vhdl_expr *expr)
|
||||
{
|
||||
vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write");
|
||||
vhdl_var_ref *ref =
|
||||
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
|
||||
write->add_expr(ref);
|
||||
|
||||
vhdl_type_name_t type = expr->get_type()->get_name();
|
||||
if (type == VHDL_TYPE_SIGNED || type == VHDL_TYPE_UNSIGNED) {
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
write->add_expr(expr->cast(&integer));
|
||||
}
|
||||
else if (type != VHDL_TYPE_STRING) {
|
||||
// Need to add a call to Type'Image for types not
|
||||
// supported by std.textio
|
||||
std::string name(expr->get_type()->get_string());
|
||||
name += "'Image";
|
||||
|
||||
vhdl_fcall *cast
|
||||
= new vhdl_fcall(name.c_str(), vhdl_type::string());
|
||||
cast->add_expr(expr);
|
||||
|
||||
write->add_expr(cast);
|
||||
}
|
||||
else
|
||||
write->add_expr(expr);
|
||||
|
||||
container->add_stmt(write);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the value of DISPLAY_LINE to the output.
|
||||
*/
|
||||
static void display_line(stmt_container *container)
|
||||
{
|
||||
vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine");
|
||||
vhdl_var_ref *output_ref =
|
||||
new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE));
|
||||
write_line->add_expr(output_ref);
|
||||
vhdl_var_ref *ref =
|
||||
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
|
||||
write_line->add_expr(ref);
|
||||
container->add_stmt(write_line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an octal escape sequence.
|
||||
*/
|
||||
static char parse_octal(const char *p)
|
||||
{
|
||||
assert(*p && *(p+1) && *(p+2));
|
||||
assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+1)));
|
||||
|
||||
return (*p - '0') * 64
|
||||
+ (*(p+1) - '0') * 8
|
||||
+ (*(p+2) - '0') * 1;
|
||||
}
|
||||
|
||||
static void flush_string(std::ostringstream &ss, stmt_container *container)
|
||||
{
|
||||
display_write(container, new vhdl_const_string(ss.str().c_str()));
|
||||
|
||||
// Clear the stream
|
||||
ss.str("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for the $display system task.
|
||||
* This is implemented using the functions in std.textio. Each
|
||||
* parameter is written to a line variable in the process and
|
||||
* then the line is written to the special variable `Output'
|
||||
* (which represents the console). Subsequent $displays will
|
||||
* use the same line variable.
|
||||
*
|
||||
* It's possible, although quite unlikely, that there will be
|
||||
* name collision with an existing variable called
|
||||
* `Verilog_Display_Line' -- do something about this?
|
||||
*/
|
||||
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool newline)
|
||||
{
|
||||
if (!proc->get_scope()->have_declared(DISPLAY_LINE)) {
|
||||
vhdl_var_decl *line_var =
|
||||
new vhdl_var_decl(DISPLAY_LINE, vhdl_type::line());
|
||||
line_var->set_comment("For generating $display output");
|
||||
proc->get_scope()->add_decl(line_var);
|
||||
}
|
||||
|
||||
// Write the data into the line
|
||||
int count = ivl_stmt_parm_count(stmt), i = 0;
|
||||
while (i < count) {
|
||||
// $display may have an empty parameter, in which case
|
||||
// the expression will be null
|
||||
// The behaviour here seems to be to output a space
|
||||
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
|
||||
if (net == NULL) {
|
||||
display_write(container, new vhdl_const_string(" "));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ivl_expr_type(net) == IVL_EX_STRING) {
|
||||
ostringstream ss;
|
||||
for (const char *p = ivl_expr_string(net); *p; p++) {
|
||||
if (*p == '\\') {
|
||||
// Octal escape
|
||||
char ch = parse_octal(p+1);
|
||||
if (ch == '\n') {
|
||||
flush_string(ss, container);
|
||||
display_line(container);
|
||||
}
|
||||
else
|
||||
ss << ch;
|
||||
p += 3;
|
||||
}
|
||||
else if (*p == '%' && *(++p) != '%') {
|
||||
flush_string(ss, container);
|
||||
|
||||
// Skip over width for now
|
||||
while (isdigit(*p)) ++p;
|
||||
|
||||
// TODO: This needs to be re-written
|
||||
// ...it does not handle format codes at all!
|
||||
// Unfortunately, there is no printf-like
|
||||
// function in VHDL
|
||||
|
||||
assert(i < count);
|
||||
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
|
||||
assert(net);
|
||||
|
||||
vhdl_expr *base = translate_expr(net);
|
||||
if (NULL == base)
|
||||
return 1;
|
||||
|
||||
display_write(container, base);
|
||||
}
|
||||
else
|
||||
ss << *p;
|
||||
}
|
||||
|
||||
display_write(container, new vhdl_const_string(ss.str().c_str()));
|
||||
}
|
||||
else {
|
||||
vhdl_expr *base = translate_expr(net);
|
||||
if (NULL == base)
|
||||
return 1;
|
||||
|
||||
display_write(container, base);
|
||||
}
|
||||
}
|
||||
|
||||
if (newline)
|
||||
display_line(container);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,577 @@
|
|||
/*
|
||||
* VHDL code generation for expressions.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "support.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
/*
|
||||
* Change the signedness of a vector.
|
||||
*/
|
||||
static vhdl_expr *change_signedness(vhdl_expr *e, bool issigned)
|
||||
{
|
||||
int msb = e->get_type()->get_msb();
|
||||
int lsb = e->get_type()->get_lsb();
|
||||
vhdl_type u(issigned ? VHDL_TYPE_SIGNED : VHDL_TYPE_UNSIGNED, msb, lsb);
|
||||
|
||||
return e->cast(&u);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate code to ensure that the VHDL expression vhd_e has the
|
||||
* same signedness as the Verilog expression vl_e.
|
||||
*/
|
||||
static vhdl_expr *correct_signedness(vhdl_expr *vhd_e, ivl_expr_t vl_e)
|
||||
{
|
||||
bool should_be_signed = ivl_expr_signed(vl_e) != 0;
|
||||
|
||||
if (vhd_e->get_type()->get_name() == VHDL_TYPE_UNSIGNED
|
||||
&& should_be_signed) {
|
||||
//operand->print();
|
||||
//std::cout << "^ should be signed but is not" << std::endl;
|
||||
|
||||
return change_signedness(vhd_e, true);
|
||||
}
|
||||
else if (vhd_e->get_type()->get_name() == VHDL_TYPE_SIGNED
|
||||
&& !should_be_signed) {
|
||||
//operand->print();
|
||||
//std::cout << "^ should be unsigned but is not" << std::endl;
|
||||
|
||||
return change_signedness(vhd_e, false);
|
||||
}
|
||||
else
|
||||
return vhd_e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a constant Verilog string to a constant VHDL string.
|
||||
*/
|
||||
static vhdl_expr *translate_string(ivl_expr_t e)
|
||||
{
|
||||
// TODO: May need to inspect or escape parts of this
|
||||
const char *str = ivl_expr_string(e);
|
||||
return new vhdl_const_string(str);
|
||||
}
|
||||
|
||||
/*
|
||||
* A reference to a signal in an expression. It's assumed that the
|
||||
* signal has already been defined elsewhere.
|
||||
*/
|
||||
static vhdl_var_ref *translate_signal(ivl_expr_t e)
|
||||
{
|
||||
ivl_signal_t sig = ivl_expr_signal(e);
|
||||
|
||||
const vhdl_scope *scope = find_scope_for_signal(sig);
|
||||
assert(scope);
|
||||
|
||||
const char *renamed = get_renamed_signal(sig).c_str();
|
||||
|
||||
vhdl_decl *decl = scope->get_decl(renamed);
|
||||
assert(decl);
|
||||
|
||||
// Can't generate a constant initialiser for this signal
|
||||
// later as it has already been read
|
||||
if (scope->initializing())
|
||||
decl->set_initial(NULL);
|
||||
|
||||
vhdl_var_ref *ref =
|
||||
new vhdl_var_ref(renamed, new vhdl_type(*decl->get_type()));
|
||||
|
||||
ivl_expr_t off;
|
||||
if (ivl_signal_array_count(sig) > 0 && (off = ivl_expr_oper1(e))) {
|
||||
// Select from an array
|
||||
vhdl_expr *vhd_off = translate_expr(off);
|
||||
if (NULL == vhd_off)
|
||||
return NULL;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
ref->set_slice(vhd_off->cast(&integer));
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* A numeric literal ends up as std_logic bit string.
|
||||
*/
|
||||
static vhdl_expr *translate_number(ivl_expr_t e)
|
||||
{
|
||||
if (ivl_expr_width(e) == 1)
|
||||
return new vhdl_const_bit(ivl_expr_bits(e)[0]);
|
||||
else
|
||||
return new vhdl_const_bits(ivl_expr_bits(e), ivl_expr_width(e),
|
||||
ivl_expr_signed(e) != 0);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_ulong(ivl_expr_t e)
|
||||
{
|
||||
return new vhdl_const_int(ivl_expr_uvalue(e));
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_reduction(support_function_t f, bool neg,
|
||||
vhdl_expr *operand)
|
||||
{
|
||||
vhdl_expr *result;
|
||||
if (operand->get_type()->get_name() == VHDL_TYPE_STD_LOGIC)
|
||||
result = operand;
|
||||
else {
|
||||
require_support_function(f);
|
||||
vhdl_fcall *fcall =
|
||||
new vhdl_fcall(support_function::function_name(f),
|
||||
vhdl_type::std_logic());
|
||||
|
||||
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR);
|
||||
fcall->add_expr(operand->cast(&std_logic_vector));
|
||||
|
||||
result = fcall;
|
||||
}
|
||||
|
||||
if (neg)
|
||||
return new vhdl_unaryop_expr(VHDL_UNARYOP_NOT, result,
|
||||
vhdl_type::std_logic());
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_unary(ivl_expr_t e)
|
||||
{
|
||||
vhdl_expr *operand = translate_expr(ivl_expr_oper1(e));
|
||||
if (NULL == operand)
|
||||
return NULL;
|
||||
|
||||
operand = correct_signedness(operand, e);
|
||||
|
||||
char opcode = ivl_expr_opcode(e);
|
||||
switch (opcode) {
|
||||
case '!':
|
||||
case '~':
|
||||
return new vhdl_unaryop_expr
|
||||
(VHDL_UNARYOP_NOT, operand, new vhdl_type(*operand->get_type()));
|
||||
case '-':
|
||||
operand = change_signedness(operand, true);
|
||||
return new vhdl_unaryop_expr
|
||||
(VHDL_UNARYOP_NEG, operand, new vhdl_type(*operand->get_type()));
|
||||
case 'N': // NOR
|
||||
return translate_reduction(SF_REDUCE_OR, true, operand);
|
||||
case '|':
|
||||
return translate_reduction(SF_REDUCE_OR, false, operand);
|
||||
case 'A': // NAND
|
||||
return translate_reduction(SF_REDUCE_AND, true, operand);
|
||||
case '&':
|
||||
return translate_reduction(SF_REDUCE_AND, false, operand);
|
||||
default:
|
||||
error("No translation for unary opcode '%c'\n",
|
||||
ivl_expr_opcode(e));
|
||||
delete operand;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a numeric binary operator (+, -, etc.) to
|
||||
* a VHDL equivalent using the numeric_std package.
|
||||
*/
|
||||
static vhdl_expr *translate_numeric(vhdl_expr *lhs, vhdl_expr *rhs,
|
||||
vhdl_binop_t op)
|
||||
{
|
||||
// May need to make either side Boolean for operators
|
||||
// to work
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
if (lhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN)
|
||||
rhs = rhs->cast(&boolean);
|
||||
else if (rhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN)
|
||||
lhs = lhs->cast(&boolean);
|
||||
|
||||
vhdl_type *rtype;
|
||||
if (op == VHDL_BINOP_MULT)
|
||||
rtype = new vhdl_type(lhs->get_type()->get_name(),
|
||||
(lhs->get_type()->get_width()*2) - 1, 0);
|
||||
else
|
||||
rtype = new vhdl_type(*lhs->get_type());
|
||||
return new vhdl_binop_expr(lhs, op, rhs, rtype);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_relation(vhdl_expr *lhs, vhdl_expr *rhs,
|
||||
vhdl_binop_t op)
|
||||
{
|
||||
// Generate any necessary casts
|
||||
// Arbitrarily, the RHS is casted to the type of the LHS
|
||||
vhdl_expr *r_cast = rhs->cast(lhs->get_type());
|
||||
|
||||
return new vhdl_binop_expr(lhs, op, r_cast, vhdl_type::boolean());
|
||||
}
|
||||
|
||||
/*
|
||||
* Like translate_relation but both operands must be Boolean.
|
||||
*/
|
||||
static vhdl_expr *translate_logical(vhdl_expr *lhs, vhdl_expr *rhs,
|
||||
vhdl_binop_t op)
|
||||
{
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
|
||||
return translate_relation(lhs->cast(&boolean), rhs->cast(&boolean), op);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_shift(vhdl_expr *lhs, vhdl_expr *rhs,
|
||||
vhdl_binop_t op)
|
||||
{
|
||||
// The RHS must be an integer
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
vhdl_expr *r_cast = rhs->cast(&integer);
|
||||
|
||||
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
|
||||
return new vhdl_binop_expr(lhs, op, r_cast, rtype);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_binary(ivl_expr_t e)
|
||||
{
|
||||
vhdl_expr *lhs = translate_expr(ivl_expr_oper1(e));
|
||||
if (NULL == lhs)
|
||||
return NULL;
|
||||
|
||||
vhdl_expr *rhs = translate_expr(ivl_expr_oper2(e));
|
||||
if (NULL == rhs)
|
||||
return NULL;
|
||||
|
||||
int lwidth = lhs->get_type()->get_width();
|
||||
int rwidth = rhs->get_type()->get_width();
|
||||
int result_width = ivl_expr_width(e);
|
||||
|
||||
// For === and !== we need to compare std_logic_vectors
|
||||
// rather than signeds
|
||||
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR, result_width-1, 0);
|
||||
vhdl_type_name_t ltype = lhs->get_type()->get_name();
|
||||
vhdl_type_name_t rtype = rhs->get_type()->get_name();
|
||||
bool vectorop =
|
||||
(ltype == VHDL_TYPE_SIGNED || ltype == VHDL_TYPE_UNSIGNED) &&
|
||||
(rtype == VHDL_TYPE_SIGNED || rtype == VHDL_TYPE_UNSIGNED);
|
||||
|
||||
// May need to resize the left or right hand side or change the
|
||||
// signedness
|
||||
if (vectorop) {
|
||||
if (lwidth < rwidth)
|
||||
lhs = lhs->resize(rwidth);
|
||||
else if (rwidth < lwidth)
|
||||
rhs = rhs->resize(lwidth);
|
||||
|
||||
lhs = correct_signedness(lhs, ivl_expr_oper1(e));
|
||||
rhs = correct_signedness(rhs, ivl_expr_oper2(e));
|
||||
}
|
||||
|
||||
vhdl_expr *result;
|
||||
switch (ivl_expr_opcode(e)) {
|
||||
case '+':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_ADD);
|
||||
break;
|
||||
case '-':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_SUB);
|
||||
break;
|
||||
case '*':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_MULT);
|
||||
break;
|
||||
case '/':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_DIV);
|
||||
break;
|
||||
case '%':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_MOD);
|
||||
break;
|
||||
case 'e':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
|
||||
break;
|
||||
case 'E':
|
||||
if (vectorop)
|
||||
result = translate_relation(lhs->cast(&std_logic_vector),
|
||||
rhs->cast(&std_logic_vector), VHDL_BINOP_EQ);
|
||||
else
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
|
||||
break;
|
||||
case 'n':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
|
||||
break;
|
||||
case 'N':
|
||||
if (vectorop)
|
||||
result = translate_relation(lhs->cast(&std_logic_vector),
|
||||
rhs->cast(&std_logic_vector), VHDL_BINOP_NEQ);
|
||||
else
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
|
||||
break;
|
||||
case '&': // Bitwise AND
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_AND);
|
||||
break;
|
||||
case 'a': // Logical AND
|
||||
result = translate_logical(lhs, rhs, VHDL_BINOP_AND);
|
||||
break;
|
||||
case 'A': // Bitwise NAND
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_NAND);
|
||||
break;
|
||||
case 'O': // Bitwise NOR
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_NOR);
|
||||
break;
|
||||
case 'X': // Bitwise XNOR
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_XNOR);
|
||||
break;
|
||||
case '|': // Bitwise OR
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_OR);
|
||||
break;
|
||||
case 'o': // Logical OR
|
||||
result = translate_logical(lhs, rhs, VHDL_BINOP_OR);
|
||||
break;
|
||||
case '<':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_LT);
|
||||
break;
|
||||
case 'L':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_LEQ);
|
||||
break;
|
||||
case '>':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_GT);
|
||||
break;
|
||||
case 'G':
|
||||
result = translate_relation(lhs, rhs, VHDL_BINOP_GEQ);
|
||||
break;
|
||||
case 'l':
|
||||
result = translate_shift(lhs, rhs, VHDL_BINOP_SL);
|
||||
break;
|
||||
case 'r':
|
||||
result = translate_shift(lhs, rhs, VHDL_BINOP_SR);
|
||||
break;
|
||||
case '^':
|
||||
result = translate_numeric(lhs, rhs, VHDL_BINOP_XOR);
|
||||
break;
|
||||
default:
|
||||
error("No translation for binary opcode '%c'\n",
|
||||
ivl_expr_opcode(e));
|
||||
delete lhs;
|
||||
delete rhs;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (NULL == result)
|
||||
return NULL;
|
||||
|
||||
if (vectorop) {
|
||||
result = correct_signedness(result, e);
|
||||
|
||||
int actual_width = result->get_type()->get_width();
|
||||
if (actual_width != result_width) {
|
||||
//result->print();
|
||||
//std::cout << "^ should be " << result_width << " but is " << actual_width << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_select(ivl_expr_t e)
|
||||
{
|
||||
vhdl_var_ref *from =
|
||||
dynamic_cast<vhdl_var_ref*>(translate_expr(ivl_expr_oper1(e)));
|
||||
if (NULL == from) {
|
||||
error("Can only select from variable reference");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ivl_expr_t o2 = ivl_expr_oper2(e);
|
||||
if (o2) {
|
||||
vhdl_expr *base = translate_expr(ivl_expr_oper2(e));
|
||||
if (NULL == base)
|
||||
return NULL;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
from->set_slice(base->cast(&integer), ivl_expr_width(e) - 1);
|
||||
return from;
|
||||
}
|
||||
else
|
||||
return from->resize(ivl_expr_width(e));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T *translate_parms(T *t, ivl_expr_t e)
|
||||
{
|
||||
int nparams = ivl_expr_parms(e);
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
vhdl_expr *param = translate_expr(ivl_expr_parm(e, i));
|
||||
if (NULL == param)
|
||||
return NULL;
|
||||
|
||||
t->add_expr(param);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_ufunc(ivl_expr_t e)
|
||||
{
|
||||
ivl_scope_t defscope = ivl_expr_def(e);
|
||||
ivl_scope_t parentscope = ivl_scope_parent(defscope);
|
||||
assert(ivl_scope_type(parentscope) == IVL_SCT_MODULE);
|
||||
|
||||
// A function is always declared in a module, which should have
|
||||
// a corresponding entity by this point: so we can get type
|
||||
// information, etc. from the declaration
|
||||
vhdl_entity *parent_ent = find_entity(ivl_scope_name(parentscope));
|
||||
assert(parent_ent);
|
||||
|
||||
const char *funcname = ivl_scope_tname(defscope);
|
||||
|
||||
vhdl_type *rettype =
|
||||
vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0);
|
||||
vhdl_fcall *fcall = new vhdl_fcall(funcname, rettype);
|
||||
|
||||
int nparams = ivl_expr_parms(e);
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
vhdl_expr *param = translate_expr(ivl_expr_parm(e, i));
|
||||
if (NULL == param)
|
||||
return NULL;
|
||||
|
||||
// Ensure the parameter has the correct VHDL type
|
||||
ivl_signal_t param_sig = ivl_scope_sig(defscope, i);
|
||||
vhdl_type *param_type =
|
||||
vhdl_type::type_for(ivl_signal_width(param_sig),
|
||||
ivl_signal_signed(param_sig) != 0);
|
||||
|
||||
fcall->add_expr(param->cast(param_type));
|
||||
delete param_type;
|
||||
}
|
||||
|
||||
return fcall;
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_ternary(ivl_expr_t e)
|
||||
{
|
||||
support_function_t sf;
|
||||
int width = ivl_expr_width(e);
|
||||
bool issigned = ivl_expr_signed(e) != 0;
|
||||
if (width == 1)
|
||||
sf = SF_TERNARY_LOGIC;
|
||||
else if (issigned)
|
||||
sf = SF_TERNARY_SIGNED;
|
||||
else
|
||||
sf = SF_TERNARY_UNSIGNED;
|
||||
|
||||
require_support_function(sf);
|
||||
|
||||
vhdl_expr *test = translate_expr(ivl_expr_oper1(e));
|
||||
vhdl_expr *true_part = translate_expr(ivl_expr_oper2(e));
|
||||
vhdl_expr *false_part = translate_expr(ivl_expr_oper3(e));
|
||||
if (!test || !true_part || !false_part)
|
||||
return NULL;
|
||||
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
test = test->cast(&boolean);
|
||||
|
||||
vhdl_fcall *fcall =
|
||||
new vhdl_fcall(support_function::function_name(sf),
|
||||
vhdl_type::type_for(width, issigned));
|
||||
fcall->add_expr(test);
|
||||
fcall->add_expr(true_part);
|
||||
fcall->add_expr(false_part);
|
||||
|
||||
return fcall;
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_concat(ivl_expr_t e)
|
||||
{
|
||||
vhdl_type *rtype =
|
||||
vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0);
|
||||
vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, rtype);
|
||||
|
||||
int nrepeat = ivl_expr_repeat(e);
|
||||
while (nrepeat--)
|
||||
translate_parms<vhdl_binop_expr>(concat, e);
|
||||
|
||||
return concat;
|
||||
}
|
||||
|
||||
vhdl_expr *translate_sfunc_time(ivl_expr_t e)
|
||||
{
|
||||
cerr << "warning: no translation for time (returning 0)" << endl;
|
||||
return new vhdl_const_int(0);
|
||||
}
|
||||
|
||||
vhdl_expr *translate_sfunc(ivl_expr_t e)
|
||||
{
|
||||
const char *name = ivl_expr_name(e);
|
||||
if (strcmp(name, "$time") == 0)
|
||||
return translate_sfunc_time(e);
|
||||
else {
|
||||
error("No translation for system function %s", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a VHDL expression from a Verilog expression.
|
||||
*/
|
||||
vhdl_expr *translate_expr(ivl_expr_t e)
|
||||
{
|
||||
assert(e);
|
||||
ivl_expr_type_t type = ivl_expr_type(e);
|
||||
|
||||
switch (type) {
|
||||
case IVL_EX_STRING:
|
||||
return translate_string(e);
|
||||
case IVL_EX_SIGNAL:
|
||||
return translate_signal(e);
|
||||
case IVL_EX_NUMBER:
|
||||
return translate_number(e);
|
||||
case IVL_EX_ULONG:
|
||||
return translate_ulong(e);
|
||||
case IVL_EX_UNARY:
|
||||
return translate_unary(e);
|
||||
case IVL_EX_BINARY:
|
||||
return translate_binary(e);
|
||||
case IVL_EX_SELECT:
|
||||
return translate_select(e);
|
||||
case IVL_EX_UFUNC:
|
||||
return translate_ufunc(e);
|
||||
case IVL_EX_TERNARY:
|
||||
return translate_ternary(e);
|
||||
case IVL_EX_CONCAT:
|
||||
return translate_concat(e);
|
||||
case IVL_EX_SFUNC:
|
||||
return translate_sfunc(e);
|
||||
default:
|
||||
error("No VHDL translation for expression at %s:%d (type = %d)",
|
||||
ivl_expr_file(e), ivl_expr_lineno(e), type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate an expression into a time. This is achieved simply
|
||||
* by multiplying the expression by 1ns.
|
||||
*/
|
||||
vhdl_expr *translate_time_expr(ivl_expr_t e)
|
||||
{
|
||||
vhdl_expr *time = translate_expr(e);
|
||||
if (NULL == time)
|
||||
return NULL;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
time = time->cast(&integer);
|
||||
|
||||
vhdl_expr *ns1 = new vhdl_const_time(1, TIME_UNIT_NS);
|
||||
return new vhdl_binop_expr(time, VHDL_BINOP_MULT, ns1,
|
||||
vhdl_type::time());
|
||||
}
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* VHDL code generation for logic devices.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/*
|
||||
* Convert the inputs of a logic gate to a binary expression.
|
||||
*/
|
||||
static vhdl_expr *inputs_to_expr(vhdl_scope *scope, vhdl_binop_t op,
|
||||
ivl_net_logic_t log)
|
||||
{
|
||||
// Not always std_logic but this is probably OK since
|
||||
// the program has already been type checked
|
||||
vhdl_binop_expr *gate =
|
||||
new vhdl_binop_expr(op, vhdl_type::std_logic());
|
||||
|
||||
int npins = ivl_logic_pins(log);
|
||||
for (int i = 1; i < npins; i++) {
|
||||
ivl_nexus_t input = ivl_logic_pin(log, i);
|
||||
gate->add_expr(nexus_to_var_ref(scope, input));
|
||||
}
|
||||
|
||||
return gate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a gate intput to an unary expression.
|
||||
*/
|
||||
static vhdl_expr *input_to_expr(vhdl_scope *scope, vhdl_unaryop_t op,
|
||||
ivl_net_logic_t log)
|
||||
{
|
||||
ivl_nexus_t input = ivl_logic_pin(log, 1);
|
||||
assert(input);
|
||||
|
||||
vhdl_expr *operand = nexus_to_var_ref(scope, input);
|
||||
return new vhdl_unaryop_expr(op, operand, vhdl_type::std_logic());
|
||||
}
|
||||
|
||||
static void bufif_logic(vhdl_arch *arch, ivl_net_logic_t log, bool if0)
|
||||
{
|
||||
ivl_nexus_t output = ivl_logic_pin(log, 0);
|
||||
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
|
||||
assert(lhs);
|
||||
|
||||
vhdl_expr *val = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
|
||||
assert(val);
|
||||
|
||||
vhdl_expr *sel = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 2));
|
||||
assert(val);
|
||||
|
||||
vhdl_expr *cmp;
|
||||
if (ivl_logic_width(log) == 1) {
|
||||
vhdl_expr *on = new vhdl_const_bit(if0 ? '0' : '1');
|
||||
cmp = new vhdl_binop_expr(sel, VHDL_BINOP_EQ, on, NULL);
|
||||
}
|
||||
else {
|
||||
vhdl_expr *zero = (new vhdl_const_int(0))->cast(sel->get_type());
|
||||
vhdl_binop_t op = if0 ? VHDL_BINOP_EQ : VHDL_BINOP_NEQ;
|
||||
cmp = new vhdl_binop_expr(sel, op, zero, NULL);
|
||||
}
|
||||
|
||||
|
||||
ivl_signal_t sig = find_signal_named(lhs->get_name(), arch->get_scope());
|
||||
char zbit;
|
||||
switch (ivl_signal_type(sig)) {
|
||||
case IVL_SIT_TRI0:
|
||||
zbit = '0';
|
||||
break;
|
||||
case IVL_SIT_TRI1:
|
||||
zbit = '1';
|
||||
break;
|
||||
case IVL_SIT_TRI:
|
||||
default:
|
||||
zbit = 'Z';
|
||||
}
|
||||
|
||||
vhdl_expr *z = new vhdl_const_bit(zbit);
|
||||
if (ivl_logic_width(log) > 1)
|
||||
z = new vhdl_bit_spec_expr(NULL, z);
|
||||
vhdl_cassign_stmt *cass = new vhdl_cassign_stmt(lhs, z);
|
||||
cass->add_condition(val, cmp);
|
||||
|
||||
arch->add_stmt(cass);
|
||||
}
|
||||
|
||||
static void comb_udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
ivl_udp_t udp = ivl_logic_udp(log);
|
||||
|
||||
// As with regular case statements, the expression in a
|
||||
// `with .. select' statement must be "locally static".
|
||||
// This is achieved by first combining the inputs into
|
||||
// a temporary
|
||||
|
||||
ostringstream ss;
|
||||
ss << ivl_logic_basename(log) << "_Tmp";
|
||||
int msb = ivl_udp_nin(udp) - 1;
|
||||
vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0);
|
||||
vhdl_signal_decl *tmp_decl =
|
||||
new vhdl_signal_decl(ss.str().c_str(), tmp_type);
|
||||
arch->get_scope()->add_decl(tmp_decl);
|
||||
|
||||
int nin = ivl_udp_nin(udp);
|
||||
vhdl_expr *tmp_rhs;
|
||||
if (nin == 1) {
|
||||
tmp_rhs = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
|
||||
tmp_rhs = tmp_rhs->cast(tmp_type);
|
||||
}
|
||||
else
|
||||
tmp_rhs = inputs_to_expr(arch->get_scope(), VHDL_BINOP_CONCAT, log);
|
||||
|
||||
ss.str("");
|
||||
ss << "Input to " << ivl_logic_basename(log) << " "
|
||||
<< ivl_udp_name(udp) << " UDP";
|
||||
tmp_decl->set_comment(ss.str());
|
||||
|
||||
vhdl_var_ref *tmp_ref =
|
||||
new vhdl_var_ref(tmp_decl->get_name().c_str(), NULL);
|
||||
arch->add_stmt(new vhdl_cassign_stmt(tmp_ref, tmp_rhs));
|
||||
|
||||
// Now we can implement the UDP as a `with .. select' statement
|
||||
// by reading values out of the table
|
||||
ivl_nexus_t output_nex = ivl_logic_pin(log, 0);
|
||||
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), output_nex);
|
||||
vhdl_with_select_stmt *ws =
|
||||
new vhdl_with_select_stmt(new vhdl_var_ref(*tmp_ref), out);
|
||||
|
||||
int nrows = ivl_udp_rows(udp);
|
||||
for (int i = 0; i < nrows; i++) {
|
||||
const char *row = ivl_udp_row(udp, i);
|
||||
|
||||
vhdl_expr *value = new vhdl_const_bit(row[nin]);
|
||||
vhdl_expr *cond = new vhdl_const_bits(row, nin, false);
|
||||
|
||||
ivl_expr_t delay_ex = ivl_logic_delay(log, 1);
|
||||
vhdl_expr *delay = NULL;
|
||||
if (delay_ex)
|
||||
delay = translate_time_expr(delay_ex);
|
||||
|
||||
ws->add_condition(value, cond, delay);
|
||||
}
|
||||
|
||||
ss.str("");
|
||||
ss << "UDP " << ivl_udp_name(udp);
|
||||
ws->set_comment(ss.str());
|
||||
|
||||
arch->add_stmt(ws);
|
||||
}
|
||||
|
||||
static void seq_udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
ivl_udp_t udp = ivl_logic_udp(log);
|
||||
|
||||
// These will be translated to a process with a single
|
||||
// case statement
|
||||
|
||||
vhdl_process *proc = new vhdl_process(ivl_logic_basename(log));
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Generated from UDP " << ivl_udp_name(udp);
|
||||
proc->set_comment(ss.str().c_str());
|
||||
|
||||
// Create a variable to hold the concatenation of the inputs
|
||||
int msb = ivl_udp_nin(udp) - 1;
|
||||
vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0);
|
||||
proc->get_scope()->add_decl(new vhdl_var_decl("UDP_Inputs", tmp_type));
|
||||
|
||||
// Concatenate the inputs into a single expression that can be
|
||||
// used as the test in a case statement (this can't be inserted
|
||||
// directly into the case statement due to the requirement that
|
||||
// the test expression be "locally static")
|
||||
int nin = ivl_udp_nin(udp);
|
||||
vhdl_expr *tmp_rhs = NULL;
|
||||
if (nin == 1) {
|
||||
vhdl_var_ref *ref =
|
||||
nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
|
||||
tmp_rhs = ref->cast(tmp_type);
|
||||
proc->add_sensitivity(ref->get_name());
|
||||
}
|
||||
else {
|
||||
vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, NULL);
|
||||
|
||||
for (int i = 1; i < nin; i++) {
|
||||
vhdl_var_ref *ref =
|
||||
nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, i));
|
||||
concat->add_expr(ref);
|
||||
proc->add_sensitivity(ref->get_name());
|
||||
}
|
||||
|
||||
tmp_rhs = concat;
|
||||
}
|
||||
|
||||
proc->get_container()->add_stmt
|
||||
(new vhdl_assign_stmt(new vhdl_var_ref("UDP_Inputs", NULL), tmp_rhs));
|
||||
|
||||
arch->add_stmt(proc);
|
||||
}
|
||||
|
||||
static void udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
if (ivl_udp_sequ(ivl_logic_udp(log)))
|
||||
seq_udp_logic(arch, log);
|
||||
else
|
||||
comb_udp_logic(arch, log);
|
||||
}
|
||||
|
||||
static vhdl_expr *translate_logic_inputs(vhdl_scope *scope, ivl_net_logic_t log)
|
||||
{
|
||||
switch (ivl_logic_type(log)) {
|
||||
case IVL_LO_NOT:
|
||||
return input_to_expr(scope, VHDL_UNARYOP_NOT, log);
|
||||
case IVL_LO_AND:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_AND, log);
|
||||
case IVL_LO_OR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_OR, log);
|
||||
case IVL_LO_NAND:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_NAND, log);
|
||||
case IVL_LO_NOR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_NOR, log);
|
||||
case IVL_LO_XOR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_XOR, log);
|
||||
case IVL_LO_XNOR:
|
||||
return inputs_to_expr(scope, VHDL_BINOP_XNOR, log);
|
||||
case IVL_LO_BUF:
|
||||
case IVL_LO_BUFZ:
|
||||
return nexus_to_var_ref(scope, ivl_logic_pin(log, 1));
|
||||
case IVL_LO_PULLUP:
|
||||
return new vhdl_const_bit('1');
|
||||
case IVL_LO_PULLDOWN:
|
||||
return new vhdl_const_bit('0');
|
||||
default:
|
||||
error("Don't know how to translate logic type = %d to expression",
|
||||
ivl_logic_type(log));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_logic(vhdl_arch *arch, ivl_net_logic_t log)
|
||||
{
|
||||
switch (ivl_logic_type(log)) {
|
||||
case IVL_LO_BUFIF0:
|
||||
bufif_logic(arch, log, true);
|
||||
break;
|
||||
case IVL_LO_BUFIF1:
|
||||
bufif_logic(arch, log, false);
|
||||
break;
|
||||
case IVL_LO_UDP:
|
||||
udp_logic(arch, log);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// The output is always pin zero
|
||||
ivl_nexus_t output = ivl_logic_pin(log, 0);
|
||||
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
|
||||
|
||||
vhdl_expr *rhs = translate_logic_inputs(arch->get_scope(), log);
|
||||
vhdl_cassign_stmt *ass = new vhdl_cassign_stmt(lhs, rhs);
|
||||
|
||||
ivl_expr_t delay = ivl_logic_delay(log, 1);
|
||||
if (delay)
|
||||
ass->set_after(translate_time_expr(delay));
|
||||
|
||||
arch->add_stmt(ass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* VHDL code generation for LPM devices.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
/*
|
||||
* Return the base of a part select.
|
||||
*/
|
||||
static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_expr *off;
|
||||
ivl_nexus_t base = ivl_lpm_data(lpm, 1);
|
||||
if (base != NULL)
|
||||
off = nexus_to_var_ref(scope, base);
|
||||
else
|
||||
off = new vhdl_const_int(ivl_lpm_base(lpm));
|
||||
|
||||
// Array indexes must be integers
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
return off->cast(&integer);
|
||||
}
|
||||
|
||||
static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_type *result_type =
|
||||
vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
|
||||
vhdl_binop_expr *expr =
|
||||
new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type);
|
||||
|
||||
for (int i = ivl_lpm_selects(lpm) - 1; i >= 0; i--) {
|
||||
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
|
||||
if (NULL == e)
|
||||
return NULL;
|
||||
|
||||
expr->add_expr(e);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
|
||||
{
|
||||
unsigned out_width = ivl_lpm_width(lpm);
|
||||
vhdl_type *result_type =
|
||||
vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0);
|
||||
vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
|
||||
if (NULL == e)
|
||||
return NULL;
|
||||
|
||||
expr->add_expr(e->cast(result_type));
|
||||
}
|
||||
|
||||
if (op == VHDL_BINOP_MULT) {
|
||||
// Need to resize the output to the desired size,
|
||||
// as this does not happen automatically in VHDL
|
||||
|
||||
vhdl_fcall *resize =
|
||||
new vhdl_fcall("Resize", vhdl_type::nsigned(out_width));
|
||||
resize->add_expr(expr);
|
||||
resize->add_expr(new vhdl_const_int(out_width));
|
||||
|
||||
return resize;
|
||||
}
|
||||
else
|
||||
return expr;
|
||||
}
|
||||
|
||||
static vhdl_expr *rel_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
|
||||
{
|
||||
vhdl_binop_expr *expr = new vhdl_binop_expr(op, vhdl_type::boolean());
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
|
||||
if (NULL == e)
|
||||
return NULL;
|
||||
|
||||
expr->add_expr(e);
|
||||
}
|
||||
|
||||
// Need to make sure output is std_logic rather than Boolean
|
||||
vhdl_type std_logic(VHDL_TYPE_STD_LOGIC);
|
||||
return expr->cast(&std_logic);
|
||||
}
|
||||
|
||||
static vhdl_expr *part_select_vp_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_var_ref *selfrom = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
if (NULL == selfrom)
|
||||
return NULL;
|
||||
|
||||
vhdl_expr *off = part_select_base(scope, lpm);;
|
||||
if (NULL == off)
|
||||
return NULL;
|
||||
|
||||
selfrom->set_slice(off, ivl_lpm_width(lpm) - 1);
|
||||
return selfrom;
|
||||
}
|
||||
|
||||
|
||||
static vhdl_expr *part_select_pv_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
return nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
}
|
||||
|
||||
static vhdl_expr *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
ivl_scope_t f_scope = ivl_lpm_define(lpm);
|
||||
vhdl_fcall *fcall = new vhdl_fcall(ivl_scope_basename(f_scope), NULL);
|
||||
|
||||
for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) {
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
|
||||
if (NULL == ref)
|
||||
return NULL;
|
||||
|
||||
fcall->add_expr(ref);
|
||||
}
|
||||
|
||||
return fcall;
|
||||
}
|
||||
|
||||
static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
|
||||
support_function_t f, bool invert)
|
||||
{
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
if (NULL == ref)
|
||||
return NULL;
|
||||
|
||||
vhdl_expr *result;
|
||||
if (ref->get_type()->get_name() == VHDL_TYPE_STD_LOGIC)
|
||||
result = ref;
|
||||
else {
|
||||
require_support_function(f);
|
||||
vhdl_fcall *fcall = new vhdl_fcall(support_function::function_name(f),
|
||||
vhdl_type::std_logic());
|
||||
|
||||
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR);
|
||||
fcall->add_expr(ref->cast(&std_logic_vector));
|
||||
|
||||
result = fcall;
|
||||
}
|
||||
|
||||
if (invert)
|
||||
return new vhdl_unaryop_expr
|
||||
(VHDL_UNARYOP_NOT, result, vhdl_type::std_logic());
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
static vhdl_expr *sign_extend_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_expr *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
if (ref)
|
||||
return ref->resize(ivl_lpm_width(lpm));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static vhdl_expr *array_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
ivl_signal_t array = ivl_lpm_array(lpm);
|
||||
if (!seen_signal_before(array))
|
||||
return NULL;
|
||||
|
||||
const char *renamed = get_renamed_signal(array).c_str();
|
||||
|
||||
vhdl_decl *adecl = scope->get_decl(renamed);
|
||||
assert(adecl);
|
||||
|
||||
vhdl_type *atype = new vhdl_type(*adecl->get_type());
|
||||
|
||||
vhdl_expr *select = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
|
||||
if (NULL == select)
|
||||
return NULL;
|
||||
|
||||
vhdl_var_ref *ref = new vhdl_var_ref(renamed, atype);
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
ref->set_slice(select->cast(&integer));
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
static vhdl_expr *shift_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
|
||||
vhdl_binop_t shift_op)
|
||||
{
|
||||
vhdl_expr *lhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
vhdl_expr *rhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
|
||||
if (!lhs || !rhs)
|
||||
return NULL;
|
||||
|
||||
// The RHS must be an integer
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
vhdl_expr *r_cast = rhs->cast(&integer);
|
||||
|
||||
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
|
||||
return new vhdl_binop_expr(lhs, shift_op, r_cast, rtype);
|
||||
}
|
||||
|
||||
static vhdl_expr *repeat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
vhdl_expr *in = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
return new vhdl_bit_spec_expr(NULL, in);
|
||||
}
|
||||
|
||||
static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
|
||||
{
|
||||
switch (ivl_lpm_type(lpm)) {
|
||||
case IVL_LPM_ADD:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD);
|
||||
case IVL_LPM_SUB:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB);
|
||||
case IVL_LPM_MULT:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT);
|
||||
case IVL_LPM_DIVIDE:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_DIV);
|
||||
case IVL_LPM_MOD:
|
||||
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MOD);
|
||||
case IVL_LPM_CONCAT:
|
||||
return concat_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_CMP_GE:
|
||||
return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ);
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_EQ);
|
||||
case IVL_LPM_PART_VP:
|
||||
return part_select_vp_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_PART_PV:
|
||||
return part_select_pv_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_UFUNC:
|
||||
return ufunc_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_RE_AND:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, false);
|
||||
case IVL_LPM_RE_NAND:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, true);
|
||||
case IVL_LPM_RE_NOR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, true);
|
||||
case IVL_LPM_RE_OR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, false);
|
||||
case IVL_LPM_RE_XOR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false);
|
||||
case IVL_LPM_RE_XNOR:
|
||||
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, true);
|
||||
case IVL_LPM_SIGN_EXT:
|
||||
return sign_extend_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_ARRAY:
|
||||
return array_lpm_to_expr(scope, lpm);
|
||||
case IVL_LPM_SHIFTL:
|
||||
return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SL);
|
||||
case IVL_LPM_SHIFTR:
|
||||
return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SR);
|
||||
case IVL_LPM_REPEAT:
|
||||
return repeat_lpm_to_expr(scope, lpm);
|
||||
default:
|
||||
error("Unsupported LPM type: %d", ivl_lpm_type(lpm));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int draw_mux_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
|
||||
{
|
||||
int nselects = ivl_lpm_selects(lpm);
|
||||
|
||||
if (nselects > 1) {
|
||||
error("Only 1 LPM select bit supported at the moment");
|
||||
return 1;
|
||||
}
|
||||
|
||||
vhdl_scope *scope = arch->get_scope();
|
||||
|
||||
vhdl_expr *s0 = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
|
||||
vhdl_expr *s1 = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
|
||||
|
||||
vhdl_expr *sel = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
|
||||
vhdl_expr *b1 = new vhdl_const_bit('1');
|
||||
vhdl_expr *t1 =
|
||||
new vhdl_binop_expr(sel, VHDL_BINOP_EQ, b1, vhdl_type::boolean());
|
||||
|
||||
vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm, 0));
|
||||
|
||||
vhdl_cassign_stmt *s = new vhdl_cassign_stmt(out, s0);
|
||||
s->add_condition(s1, t1);
|
||||
|
||||
arch->add_stmt(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
|
||||
{
|
||||
if (ivl_lpm_type(lpm) == IVL_LPM_MUX)
|
||||
return draw_mux_lpm(arch, lpm);
|
||||
|
||||
vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm);
|
||||
if (NULL == f)
|
||||
return 1;
|
||||
|
||||
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), ivl_lpm_q(lpm, 0));
|
||||
if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) {
|
||||
vhdl_expr *off = part_select_base(arch->get_scope(), lpm);
|
||||
assert(off);
|
||||
|
||||
out->set_slice(off, ivl_lpm_width(lpm) - 1);
|
||||
}
|
||||
|
||||
arch->add_stmt(new vhdl_cassign_stmt(out, f));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* VHDL code generation for processes.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
/*
|
||||
* Convert a Verilog process to VHDL and add it to the architecture
|
||||
* of the given entity.
|
||||
*/
|
||||
static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc)
|
||||
{
|
||||
set_active_entity(ent);
|
||||
|
||||
// Create a new process and store it in the entity's
|
||||
// architecture. This needs to be done first or the
|
||||
// parent link won't be valid (and draw_stmt needs this
|
||||
// to add information to the architecture)
|
||||
vhdl_process *vhdl_proc = new vhdl_process();
|
||||
ent->get_arch()->add_stmt(vhdl_proc);
|
||||
|
||||
// If this is an initial process, push signal initialisation
|
||||
// into the declarations
|
||||
vhdl_proc->get_scope()->set_initializing
|
||||
(ivl_process_type(proc) == IVL_PR_INITIAL);
|
||||
|
||||
ivl_statement_t stmt = ivl_process_stmt(proc);
|
||||
int rc = draw_stmt(vhdl_proc, vhdl_proc->get_container(), stmt);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
// Initial processes are translated to VHDL processes with
|
||||
// no sensitivity list and and indefinite wait statement at
|
||||
// the end
|
||||
// However, if no statements were added to the container
|
||||
// by draw_stmt, don't bother adding a wait as `emit'
|
||||
// will optimise the process out of the output
|
||||
if (ivl_process_type(proc) == IVL_PR_INITIAL
|
||||
&& !vhdl_proc->get_container()->empty()) {
|
||||
vhdl_wait_stmt *wait = new vhdl_wait_stmt();
|
||||
vhdl_proc->get_container()->add_stmt(wait);
|
||||
}
|
||||
|
||||
// Add a comment indicating where it came from
|
||||
ivl_scope_t scope = ivl_process_scope(proc);
|
||||
const char *type = ivl_process_type(proc) == IVL_PR_INITIAL
|
||||
? "initial" : "always";
|
||||
std::ostringstream ss;
|
||||
ss << "Generated from " << type << " process in "
|
||||
<< ivl_scope_tname(scope) << " ("
|
||||
<< ivl_process_file(proc) << ":"
|
||||
<< ivl_process_lineno(proc) << ")";
|
||||
vhdl_proc->set_comment(ss.str());
|
||||
|
||||
set_active_entity(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_process(ivl_process_t proc, void *cd)
|
||||
{
|
||||
ivl_scope_t scope = ivl_process_scope(proc);
|
||||
|
||||
// A process should occur in a module scope, therefore it
|
||||
// should have already been assigned a VHDL entity
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_MODULE);
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent != NULL);
|
||||
|
||||
return generate_vhdl_process(ent, proc);
|
||||
}
|
||||
|
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
* VHDL code generation for scopes.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
static string make_safe_name(ivl_signal_t sig);
|
||||
|
||||
static vhdl_entity *g_active_entity = NULL;
|
||||
|
||||
vhdl_entity *get_active_entity()
|
||||
{
|
||||
return g_active_entity;
|
||||
}
|
||||
|
||||
void set_active_entity(vhdl_entity *ent)
|
||||
{
|
||||
g_active_entity = ent;
|
||||
}
|
||||
|
||||
/*
|
||||
* This represents the portion of a nexus that is visible within
|
||||
* a VHDL scope. If that nexus portion does not contain a signal,
|
||||
* then `tmpname' gives the name of the temporary that will be
|
||||
* used when this nexus is used in `scope' (e.g. for LPMs that
|
||||
* appear in instantiations). The list `connect' lists all the
|
||||
* signals that should be joined together to re-create the net.
|
||||
*/
|
||||
struct scope_nexus_t {
|
||||
vhdl_scope *scope;
|
||||
ivl_signal_t sig; // A real signal
|
||||
unsigned pin; // The pin this signal is connected to
|
||||
string tmpname; // A new temporary signal
|
||||
list<ivl_signal_t> connect; // Other signals to wire together
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is stored in the private part of each nexus.
|
||||
* It stores a scope_nexus_t for each VHDL scope which is
|
||||
* connected to that nexus. It's stored as a list so we can use
|
||||
* contained_within to allow several nested scopes to reference
|
||||
* the same signal.
|
||||
*/
|
||||
struct nexus_private_t {
|
||||
list<scope_nexus_t> signals;
|
||||
vhdl_expr *const_driver;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the scope_nexus_t of this nexus visible within scope.
|
||||
*/
|
||||
static scope_nexus_t *visible_nexus(nexus_private_t *priv, vhdl_scope *scope)
|
||||
{
|
||||
list<scope_nexus_t>::iterator it;
|
||||
for (it = priv->signals.begin(); it != priv->signals.end(); ++it) {
|
||||
if (scope->contained_within((*it).scope))
|
||||
return &*it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remember that a signal in `scope' is part of this nexus. The
|
||||
* first signal passed to this function for a scope will be used
|
||||
* as the canonical representation of this nexus when we need to
|
||||
* convert it to a variable reference (e.g. in a LPM input/output).
|
||||
*/
|
||||
static void link_scope_to_nexus_signal(nexus_private_t *priv, vhdl_scope *scope,
|
||||
ivl_signal_t sig, unsigned pin)
|
||||
{
|
||||
scope_nexus_t *sn;
|
||||
if ((sn = visible_nexus(priv, scope))) {
|
||||
assert(sn->tmpname == "");
|
||||
|
||||
sn->connect.push_back(sig);
|
||||
}
|
||||
else {
|
||||
scope_nexus_t new_sn = { scope, sig, pin, "" };
|
||||
priv->signals.push_back(new_sn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a temporary the representative of this nexus in scope.
|
||||
*/
|
||||
static void link_scope_to_nexus_tmp(nexus_private_t *priv, vhdl_scope *scope,
|
||||
const string &name)
|
||||
{
|
||||
scope_nexus_t new_sn = { scope, NULL, 0, name };
|
||||
priv->signals.push_back(new_sn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the name of the nexus signal within this scope.
|
||||
*/
|
||||
static string visible_nexus_signal_name(nexus_private_t *priv, vhdl_scope *scope,
|
||||
unsigned *pin)
|
||||
{
|
||||
scope_nexus_t *sn = visible_nexus(priv, scope);
|
||||
assert(sn);
|
||||
|
||||
*pin = sn->pin;
|
||||
return sn->sig ? get_renamed_signal(sn->sig) : sn->tmpname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates VHDL code to fully represent a nexus.
|
||||
*/
|
||||
void draw_nexus(ivl_nexus_t nexus)
|
||||
{
|
||||
nexus_private_t *priv = new nexus_private_t;
|
||||
priv->const_driver = NULL;
|
||||
|
||||
int nptrs = ivl_nexus_ptrs(nexus);
|
||||
|
||||
// First pass through connect all the signals up
|
||||
for (int i = 0; i < nptrs; i++) {
|
||||
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
|
||||
|
||||
ivl_signal_t sig;
|
||||
if ((sig = ivl_nexus_ptr_sig(nexus_ptr))) {
|
||||
vhdl_scope *scope = find_scope_for_signal(sig);
|
||||
unsigned pin = ivl_nexus_ptr_pin(nexus_ptr);
|
||||
link_scope_to_nexus_signal(priv, scope, sig, pin);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass through make sure logic/LPMs have signal
|
||||
// inputs and outputs
|
||||
for (int i = 0; i < nptrs; i++) {
|
||||
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
|
||||
|
||||
ivl_net_logic_t log;
|
||||
ivl_lpm_t lpm;
|
||||
ivl_net_const_t con;
|
||||
if ((log = ivl_nexus_ptr_log(nexus_ptr))) {
|
||||
ivl_scope_t log_scope = ivl_logic_scope(log);
|
||||
vhdl_scope *vhdl_scope =
|
||||
find_entity(ivl_scope_name(log_scope))->get_arch()->get_scope();
|
||||
|
||||
if (visible_nexus(priv, vhdl_scope)) {
|
||||
// Already seen this signal in vhdl_scope
|
||||
}
|
||||
else {
|
||||
// Create a temporary signal to connect it to the nexus
|
||||
vhdl_type *type =
|
||||
vhdl_type::type_for(ivl_logic_width(log), false);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "LO" << ivl_logic_basename(log);
|
||||
vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type));
|
||||
|
||||
link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str());
|
||||
}
|
||||
}
|
||||
else if ((lpm = ivl_nexus_ptr_lpm(nexus_ptr))) {
|
||||
ivl_scope_t lpm_scope = ivl_lpm_scope(lpm);
|
||||
vhdl_scope *vhdl_scope =
|
||||
find_entity(ivl_scope_name(lpm_scope))->get_arch()->get_scope();
|
||||
|
||||
if (visible_nexus(priv, vhdl_scope)) {
|
||||
// Already seen this signal in vhdl_scope
|
||||
}
|
||||
else {
|
||||
// Create a temporary signal to connect the nexus
|
||||
// TODO: we could avoid this for IVL_LPM_PART_PV
|
||||
vhdl_type *type = vhdl_type::type_for(ivl_lpm_width(lpm),
|
||||
ivl_lpm_signed(lpm) != 0);
|
||||
ostringstream ss;
|
||||
ss << "LPM" << ivl_lpm_basename(lpm);
|
||||
vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type));
|
||||
|
||||
link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str());
|
||||
}
|
||||
}
|
||||
else if ((con = ivl_nexus_ptr_con(nexus_ptr))) {
|
||||
if (ivl_const_width(con) == 1)
|
||||
priv->const_driver = new vhdl_const_bit(ivl_const_bits(con)[0]);
|
||||
else
|
||||
priv->const_driver =
|
||||
new vhdl_const_bits(ivl_const_bits(con), ivl_const_width(con),
|
||||
ivl_const_signed(con) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the private data in the nexus
|
||||
ivl_nexus_set_private(nexus, priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that a nexus has been initialised. I.e. all the necessary
|
||||
* statements, declarations, etc. have been generated.
|
||||
*/
|
||||
static void seen_nexus(ivl_nexus_t nexus)
|
||||
{
|
||||
if (ivl_nexus_get_private(nexus) == NULL)
|
||||
draw_nexus(nexus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a nexus to a variable reference. Given a nexus and a
|
||||
* scope, this function returns a reference to a signal that is
|
||||
* connected to the nexus and within the given scope. This signal
|
||||
* might not exist in the original Verilog source (even as a
|
||||
* compiler-generated temporary). If this nexus hasn't been
|
||||
* encountered before, the necessary code to connect up the nexus
|
||||
* will be generated.
|
||||
*/
|
||||
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *scope, ivl_nexus_t nexus)
|
||||
{
|
||||
seen_nexus(nexus);
|
||||
|
||||
nexus_private_t *priv =
|
||||
static_cast<nexus_private_t*>(ivl_nexus_get_private(nexus));
|
||||
unsigned pin;
|
||||
string renamed(visible_nexus_signal_name(priv, scope, &pin));
|
||||
|
||||
vhdl_decl *decl = scope->get_decl(renamed);
|
||||
assert(decl);
|
||||
|
||||
vhdl_type *type = new vhdl_type(*(decl->get_type()));
|
||||
vhdl_var_ref *ref = new vhdl_var_ref(renamed.c_str(), type);
|
||||
|
||||
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
|
||||
ref->set_slice(new vhdl_const_int(pin), 0);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate all the primitive logic gates into concurrent
|
||||
* signal assignments.
|
||||
*/
|
||||
static void declare_logic(vhdl_arch *arch, ivl_scope_t scope)
|
||||
{
|
||||
int nlogs = ivl_scope_logs(scope);
|
||||
for (int i = 0; i < nlogs; i++)
|
||||
draw_logic(arch, ivl_scope_log(scope, i));
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure a signal name conforms to VHDL naming rules.
|
||||
*/
|
||||
static string make_safe_name(ivl_signal_t sig)
|
||||
{
|
||||
const char *base = ivl_signal_basename(sig);
|
||||
if (base[0] == '_')
|
||||
return string("VL") + base;
|
||||
|
||||
const char *vhdl_reserved[] = {
|
||||
"in", "out", "entity", "architecture", "inout", "array",
|
||||
"is", "not", "and", "or", "bus", "bit", "line", // Etc...
|
||||
NULL
|
||||
};
|
||||
for (const char **p = vhdl_reserved; *p != NULL; p++) {
|
||||
if (strcasecmp(*p, base) == 0) {
|
||||
return string("VL_") + base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string(base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare all signals and ports for a scope.
|
||||
*/
|
||||
static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
|
||||
{
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
remember_signal(sig, ent->get_arch()->get_scope());
|
||||
|
||||
string name(make_safe_name(sig));
|
||||
rename_signal(sig, name);
|
||||
|
||||
vhdl_type *sig_type;
|
||||
unsigned dimensions = ivl_signal_dimensions(sig);
|
||||
if (dimensions > 0) {
|
||||
// Arrays are implemented by generating a separate type
|
||||
// declaration for each array, and then declaring a
|
||||
// signal of that type
|
||||
|
||||
if (dimensions > 1) {
|
||||
error("> 1 dimension arrays not implemented yet");
|
||||
return;
|
||||
}
|
||||
|
||||
string type_name = name + "_Type";
|
||||
vhdl_type *base_type =
|
||||
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
|
||||
|
||||
int lsb = ivl_signal_array_base(sig);
|
||||
int msb = lsb + ivl_signal_array_count(sig) - 1;
|
||||
|
||||
vhdl_type *array_type =
|
||||
vhdl_type::array_of(base_type, type_name, msb, lsb);
|
||||
vhdl_decl *array_decl = new vhdl_type_decl(type_name.c_str(), array_type);
|
||||
ent->get_arch()->get_scope()->add_decl(array_decl);
|
||||
|
||||
sig_type = new vhdl_type(*array_type);
|
||||
}
|
||||
else
|
||||
sig_type =
|
||||
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
|
||||
|
||||
ivl_signal_port_t mode = ivl_signal_port(sig);
|
||||
switch (mode) {
|
||||
case IVL_SIP_NONE:
|
||||
{
|
||||
vhdl_decl *decl = new vhdl_signal_decl(name.c_str(), sig_type);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Declared at " << ivl_signal_file(sig) << ":"
|
||||
<< ivl_signal_lineno(sig);
|
||||
decl->set_comment(ss.str().c_str());
|
||||
|
||||
ent->get_arch()->get_scope()->add_decl(decl);
|
||||
}
|
||||
break;
|
||||
case IVL_SIP_INPUT:
|
||||
ent->get_scope()->add_decl
|
||||
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_IN));
|
||||
break;
|
||||
case IVL_SIP_OUTPUT:
|
||||
{
|
||||
vhdl_port_decl *decl =
|
||||
new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_OUT);
|
||||
ent->get_scope()->add_decl(decl);
|
||||
}
|
||||
|
||||
if (ivl_signal_type(sig) == IVL_SIT_REG) {
|
||||
// A registered output
|
||||
// In Verilog the output and reg can have the
|
||||
// same name: this is not valid in VHDL
|
||||
// Instead a new signal foo_Reg is created
|
||||
// which represents the register
|
||||
std::string newname(name);
|
||||
newname += "_Reg";
|
||||
rename_signal(sig, newname.c_str());
|
||||
|
||||
vhdl_type *reg_type = new vhdl_type(*sig_type);
|
||||
ent->get_arch()->get_scope()->add_decl
|
||||
(new vhdl_signal_decl(newname.c_str(), reg_type));
|
||||
|
||||
// Create a concurrent assignment statement to
|
||||
// connect the register to the output
|
||||
ent->get_arch()->add_stmt
|
||||
(new vhdl_cassign_stmt
|
||||
(new vhdl_var_ref(name.c_str(), NULL),
|
||||
new vhdl_var_ref(newname.c_str(), NULL)));
|
||||
}
|
||||
break;
|
||||
case IVL_SIP_INOUT:
|
||||
ent->get_scope()->add_decl
|
||||
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_INOUT));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for LPM instances in a module.
|
||||
*/
|
||||
static void declare_lpm(vhdl_arch *arch, ivl_scope_t scope)
|
||||
{
|
||||
int nlpms = ivl_scope_lpms(scope);
|
||||
for (int i = 0; i < nlpms; i++) {
|
||||
ivl_lpm_t lpm = ivl_scope_lpm(scope, i);
|
||||
if (draw_lpm(arch, lpm) != 0)
|
||||
error("Failed to translate LPM %s", ivl_lpm_name(lpm));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Map two signals together in an instantiation.
|
||||
* The signals are joined by a nexus.
|
||||
*/
|
||||
static void map_signal(ivl_signal_t to, vhdl_entity *parent,
|
||||
vhdl_comp_inst *inst)
|
||||
{
|
||||
// TODO: Work for multiple words
|
||||
ivl_nexus_t nexus = ivl_signal_nex(to, 0);
|
||||
seen_nexus(nexus);
|
||||
|
||||
vhdl_scope *arch_scope = parent->get_arch()->get_scope();
|
||||
|
||||
nexus_private_t *priv =
|
||||
static_cast<nexus_private_t*>(ivl_nexus_get_private(nexus));
|
||||
assert(priv);
|
||||
if (!visible_nexus(priv, arch_scope)) {
|
||||
// This nexus isn't attached to anything in the parent
|
||||
return;
|
||||
}
|
||||
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(parent->get_arch()->get_scope(), nexus);
|
||||
|
||||
string name = make_safe_name(to);
|
||||
|
||||
// If we're mapping an output of this entity to an output of
|
||||
// the child entity, then VHDL will not let us read the value
|
||||
// of the signal (i.e. it must pass straight through).
|
||||
// However, Verilog allows the signal to be read in the parent.
|
||||
// The VHDL equivalent of this is to make *both* output ports
|
||||
// a `buffer'.
|
||||
vhdl_decl *decl =
|
||||
parent->get_arch()->get_scope()->get_decl(ref->get_name());
|
||||
vhdl_port_decl *pdecl;
|
||||
if ((pdecl = dynamic_cast<vhdl_port_decl*>(decl))
|
||||
&& pdecl->get_mode() == VHDL_PORT_OUT) {
|
||||
|
||||
// First change the mode in the parent entity
|
||||
pdecl->set_mode(VHDL_PORT_BUFFER);
|
||||
|
||||
// Now change the mode in the child entity
|
||||
vhdl_port_decl *to_pdecl =
|
||||
dynamic_cast<vhdl_port_decl*>(find_scope_for_signal(to)->get_decl(name));
|
||||
assert(to_pdecl);
|
||||
to_pdecl->set_mode(VHDL_PORT_BUFFER);
|
||||
}
|
||||
|
||||
inst->map_port(name.c_str(), ref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all the port mappings of a module instantiation.
|
||||
*/
|
||||
static void port_map(ivl_scope_t scope, vhdl_entity *parent,
|
||||
vhdl_comp_inst *inst)
|
||||
{
|
||||
// Find all the port mappings
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
|
||||
ivl_signal_port_t mode = ivl_signal_port(sig);
|
||||
switch (mode) {
|
||||
case IVL_SIP_NONE:
|
||||
// Internal signals don't appear in the port map
|
||||
break;
|
||||
case IVL_SIP_INPUT:
|
||||
case IVL_SIP_OUTPUT:
|
||||
case IVL_SIP_INOUT:
|
||||
map_signal(sig, parent, inst);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a VHDL function from a Verilog function definition.
|
||||
*/
|
||||
static int draw_function(ivl_scope_t scope, ivl_scope_t parent)
|
||||
{
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_FUNCTION);
|
||||
|
||||
// Find the containing entity
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(parent));
|
||||
assert(ent);
|
||||
|
||||
const char *funcname = ivl_scope_tname(scope);
|
||||
|
||||
assert(!ent->get_arch()->get_scope()->have_declared(funcname));
|
||||
|
||||
// The return type is worked out from the output port
|
||||
vhdl_function *func = new vhdl_function(funcname, NULL);
|
||||
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
vhdl_type *sigtype =
|
||||
vhdl_type::type_for(ivl_signal_width(sig),
|
||||
ivl_signal_signed(sig) != 0);
|
||||
|
||||
string signame(make_safe_name(sig));
|
||||
|
||||
switch (ivl_signal_port(sig)) {
|
||||
case IVL_SIP_INPUT:
|
||||
func->add_param(new vhdl_param_decl(signame.c_str(), sigtype));
|
||||
break;
|
||||
case IVL_SIP_OUTPUT:
|
||||
// The magic variable <funcname>_Result holds the return value
|
||||
signame = funcname;
|
||||
signame += "_Result";
|
||||
func->set_type(new vhdl_type(*sigtype));
|
||||
default:
|
||||
func->get_scope()->add_decl
|
||||
(new vhdl_var_decl(signame.c_str(), sigtype));
|
||||
}
|
||||
|
||||
remember_signal(sig, func->get_scope());
|
||||
rename_signal(sig, signame);
|
||||
}
|
||||
|
||||
// Non-blocking assignment not allowed in functions
|
||||
func->get_scope()->set_allow_signal_assignment(false);
|
||||
|
||||
set_active_entity(ent);
|
||||
{
|
||||
draw_stmt(func, func->get_container(), ivl_scope_def(scope));
|
||||
}
|
||||
set_active_entity(NULL);
|
||||
|
||||
// Add a forward declaration too in case it is called by
|
||||
// another function that has already been added
|
||||
ent->get_arch()->get_scope()->add_forward_decl
|
||||
(new vhdl_forward_fdecl(func));
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Generated from function " << funcname << " at "
|
||||
<< ivl_scope_def_file(scope) << ":" << ivl_scope_def_lineno(scope);
|
||||
func->set_comment(ss.str().c_str());
|
||||
|
||||
ent->get_arch()->get_scope()->add_decl(func);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create the signals necessary to expand this task later.
|
||||
*/
|
||||
static int draw_task(ivl_scope_t scope, ivl_scope_t parent)
|
||||
{
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_TASK);
|
||||
|
||||
// Find the containing entity
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(parent));
|
||||
assert(ent);
|
||||
|
||||
const char *taskname = ivl_scope_tname(scope);
|
||||
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
vhdl_type *sigtype =
|
||||
vhdl_type::type_for(ivl_signal_width(sig),
|
||||
ivl_signal_signed(sig) != 0);
|
||||
|
||||
string signame(make_safe_name(sig));
|
||||
|
||||
// Check this signal isn't declared in the outer scope
|
||||
if (ent->get_arch()->get_scope()->have_declared(signame)) {
|
||||
signame += "_";
|
||||
signame += taskname;
|
||||
}
|
||||
|
||||
vhdl_signal_decl *decl = new vhdl_signal_decl(signame.c_str(), sigtype);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Declared at " << ivl_signal_file(sig) << ":"
|
||||
<< ivl_signal_lineno(sig) << " (in task " << taskname << ")";
|
||||
decl->set_comment(ss.str().c_str());
|
||||
|
||||
ent->get_arch()->get_scope()->add_decl(decl);
|
||||
|
||||
remember_signal(sig, ent->get_arch()->get_scope());
|
||||
rename_signal(sig, signame);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an empty VHDL entity for a Verilog module.
|
||||
*/
|
||||
static void create_skeleton_entity_for(ivl_scope_t scope)
|
||||
{
|
||||
assert(ivl_scope_type(scope) == IVL_SCT_MODULE);
|
||||
|
||||
// The type name will become the entity name
|
||||
const char *tname = ivl_scope_tname(scope);
|
||||
|
||||
// Remember the scope name this entity was derived from so
|
||||
// the correct processes can be added later
|
||||
const char *derived_from = ivl_scope_name(scope);
|
||||
|
||||
// Verilog does not have the entity/architecture distinction
|
||||
// so we always create a pair and associate the architecture
|
||||
// with the entity for convenience (this also means that we
|
||||
// retain a 1-to-1 mapping of scope to VHDL element)
|
||||
vhdl_arch *arch = new vhdl_arch(tname, "FromVerilog");
|
||||
vhdl_entity *ent = new vhdl_entity(tname, derived_from, arch);
|
||||
|
||||
// Build a comment to add to the entity/architecture
|
||||
ostringstream ss;
|
||||
ss << "Generated from Verilog module " << ivl_scope_tname(scope)
|
||||
<< " (" << ivl_scope_def_file(scope) << ":"
|
||||
<< ivl_scope_def_lineno(scope) << ")";
|
||||
|
||||
arch->set_comment(ss.str());
|
||||
ent->set_comment(ss.str());
|
||||
|
||||
remember_entity(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
* A first pass through the hierarchy: create VHDL entities for
|
||||
* each unique Verilog module type.
|
||||
*/
|
||||
static int draw_skeleton_scope(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE)
|
||||
create_skeleton_entity_for(scope);
|
||||
|
||||
return ivl_scope_children(scope, draw_skeleton_scope, scope);
|
||||
}
|
||||
|
||||
static int draw_all_signals(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
declare_signals(ent, scope);
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_all_signals, scope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw all tasks and functions in the hierarchy.
|
||||
*/
|
||||
static int draw_functions(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
ivl_scope_t parent = static_cast<ivl_scope_t>(_parent);
|
||||
if (ivl_scope_type(scope) == IVL_SCT_FUNCTION) {
|
||||
if (draw_function(scope, parent) != 0)
|
||||
return 1;
|
||||
}
|
||||
else if (ivl_scope_type(scope) == IVL_SCT_TASK) {
|
||||
if (draw_task(scope, parent) != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_functions, scope);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make concurrent assignments for constants in nets. This works
|
||||
* bottom-up so that the driver is in the lowest instance it can.
|
||||
* This also has the side effect of generating all the necessary
|
||||
* nexus code.
|
||||
*/
|
||||
static int draw_constant_drivers(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
ivl_scope_children(scope, draw_constant_drivers, scope);
|
||||
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
int nsigs = ivl_scope_sigs(scope);
|
||||
for (int i = 0; i < nsigs; i++) {
|
||||
ivl_signal_t sig = ivl_scope_sig(scope, i);
|
||||
|
||||
for (unsigned i = ivl_signal_array_base(sig);
|
||||
i < ivl_signal_array_count(sig);
|
||||
i++) {
|
||||
// Make sure the nexus code is generated
|
||||
ivl_nexus_t nex = ivl_signal_nex(sig, i);
|
||||
seen_nexus(nex);
|
||||
|
||||
nexus_private_t *priv =
|
||||
static_cast<nexus_private_t*>(ivl_nexus_get_private(nex));
|
||||
assert(priv);
|
||||
|
||||
vhdl_scope *arch_scope = ent->get_arch()->get_scope();
|
||||
|
||||
if (priv->const_driver) {
|
||||
assert(i == 0); // TODO: Make work for more words
|
||||
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(arch_scope, nex);
|
||||
|
||||
ent->get_arch()->add_stmt
|
||||
(new vhdl_cassign_stmt(ref, priv->const_driver));
|
||||
priv->const_driver = NULL;
|
||||
}
|
||||
|
||||
scope_nexus_t *sn = visible_nexus(priv, arch_scope);
|
||||
for (list<ivl_signal_t>::const_iterator it = sn->connect.begin();
|
||||
it != sn->connect.end();
|
||||
++it) {
|
||||
vhdl_var_ref *rref =
|
||||
new vhdl_var_ref(get_renamed_signal(sn->sig).c_str(), NULL);
|
||||
vhdl_var_ref *lref =
|
||||
new vhdl_var_ref(get_renamed_signal(*it).c_str(), NULL);
|
||||
ent->get_arch()->add_stmt(new vhdl_cassign_stmt(lref, rref));
|
||||
}
|
||||
sn->connect.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_all_logic_and_lpm(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
set_active_entity(ent);
|
||||
{
|
||||
declare_logic(ent->get_arch(), scope);
|
||||
declare_lpm(ent->get_arch(), scope);
|
||||
}
|
||||
set_active_entity(NULL);
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_all_logic_and_lpm, scope);
|
||||
}
|
||||
|
||||
static int draw_hierarchy(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
if (ivl_scope_type(scope) == IVL_SCT_MODULE && _parent) {
|
||||
ivl_scope_t parent = static_cast<ivl_scope_t>(_parent);
|
||||
|
||||
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
|
||||
assert(ent);
|
||||
|
||||
vhdl_entity *parent_ent = find_entity(ivl_scope_name(parent));
|
||||
assert(parent_ent);
|
||||
|
||||
vhdl_arch *parent_arch = parent_ent->get_arch();
|
||||
assert(parent_arch != NULL);
|
||||
|
||||
// Create a forward declaration for it
|
||||
vhdl_scope *parent_scope = parent_arch->get_scope();
|
||||
if (!parent_scope->have_declared(ent->get_name())) {
|
||||
vhdl_decl *comp_decl = vhdl_component_decl::component_decl_for(ent);
|
||||
parent_arch->get_scope()->add_decl(comp_decl);
|
||||
}
|
||||
|
||||
// And an instantiation statement
|
||||
string inst_name(ivl_scope_basename(scope));
|
||||
if (inst_name == ent->get_name() || parent_scope->have_declared(inst_name)) {
|
||||
// Cannot have instance name the same as type in VHDL
|
||||
inst_name += "_Inst";
|
||||
}
|
||||
|
||||
// Need to replace any [ and ] characters that result
|
||||
// from generate statements
|
||||
string::size_type loc = inst_name.find('[', 0);
|
||||
if (loc != string::npos)
|
||||
inst_name.erase(loc, 1);
|
||||
|
||||
loc = inst_name.find(']', 0);
|
||||
if (loc != string::npos)
|
||||
inst_name.erase(loc, 1);
|
||||
|
||||
vhdl_comp_inst *inst =
|
||||
new vhdl_comp_inst(inst_name.c_str(), ent->get_name().c_str());
|
||||
port_map(scope, parent_ent, inst);
|
||||
|
||||
ostringstream ss;
|
||||
ss << "Generated from instantiation at "
|
||||
<< ivl_scope_file(scope) << ":" << ivl_scope_lineno(scope);
|
||||
inst->set_comment(ss.str().c_str());
|
||||
|
||||
parent_arch->add_stmt(inst);
|
||||
}
|
||||
|
||||
return ivl_scope_children(scope, draw_hierarchy, scope);
|
||||
}
|
||||
|
||||
int draw_scope(ivl_scope_t scope, void *_parent)
|
||||
{
|
||||
int rc = draw_skeleton_scope(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_all_signals(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_all_logic_and_lpm(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_hierarchy(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_functions(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = draw_constant_drivers(scope, _parent);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
* VHDL code generation for statements.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
|
||||
/*
|
||||
* VHDL has no real equivalent of Verilog's $finish task. The
|
||||
* current solution is to use `assert false ...' to terminate
|
||||
* the simulator. This isn't great, as the simulator will
|
||||
* return a failure exit code when in fact it completed
|
||||
* successfully.
|
||||
*
|
||||
* An alternative is to use the VHPI interface supported by
|
||||
* some VHDL simulators and implement the $finish funcitonality
|
||||
* in C. This function can be enabled with the flag
|
||||
* -puse-vhpi-finish=1.
|
||||
*/
|
||||
static int draw_stask_finish(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
const char *use_vhpi = ivl_design_flag(get_vhdl_design(), "use-vhpi-finish");
|
||||
if (strcmp(use_vhpi, "1") == 0) {
|
||||
//get_active_entity()->requires_package("work.Verilog_Support");
|
||||
container->add_stmt(new vhdl_pcall_stmt("work.Verilog_Support.Finish"));
|
||||
}
|
||||
else {
|
||||
container->add_stmt(new vhdl_assert_stmt("SIMULATION FINISHED"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for system tasks (like $display). Not all of
|
||||
* these are supported.
|
||||
*/
|
||||
static int draw_stask(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
const char *name = ivl_stmt_name(stmt);
|
||||
|
||||
if (strcmp(name, "$display") == 0)
|
||||
return draw_stask_display(proc, container, stmt, true);
|
||||
else if (strcmp(name, "$write") == 0)
|
||||
return draw_stask_display(proc, container, stmt, false);
|
||||
else if (strcmp(name, "$finish") == 0)
|
||||
return draw_stask_finish(proc, container, stmt);
|
||||
else {
|
||||
error("No VHDL translation for system task %s", name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL for a block of Verilog statements. This doesn't
|
||||
* actually do anything, other than recursively translate the
|
||||
* block's statements and add them to the process. This is OK as
|
||||
* the stmt_container class behaves like a Verilog block.
|
||||
*/
|
||||
static int draw_block(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
int count = ivl_stmt_block_count(stmt);
|
||||
for (int i = 0; i < count; i++) {
|
||||
ivl_statement_t stmt_i = ivl_stmt_block_stmt(stmt, i);
|
||||
if (draw_stmt(proc, container, stmt_i, is_last && i == count - 1) != 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A no-op statement. This corresponds to a `null' statement in VHDL.
|
||||
*/
|
||||
static int draw_noop(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
container->add_stmt(new vhdl_null_stmt());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope)
|
||||
{
|
||||
ivl_signal_t sig = ivl_lval_sig(lval);
|
||||
if (!sig) {
|
||||
error("Only signals as lvals supported at the moment");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vhdl_expr *base = NULL;
|
||||
ivl_expr_t e_off = ivl_lval_part_off(lval);
|
||||
if (NULL == e_off)
|
||||
e_off = ivl_lval_idx(lval);
|
||||
if (e_off) {
|
||||
if ((base = translate_expr(e_off)) == NULL)
|
||||
return NULL;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
base = base->cast(&integer);
|
||||
}
|
||||
|
||||
unsigned lval_width = ivl_lval_width(lval);
|
||||
|
||||
string signame(get_renamed_signal(sig));
|
||||
vhdl_decl *decl = scope->get_decl(signame);
|
||||
assert(decl);
|
||||
|
||||
vhdl_type *ltype = new vhdl_type(*decl->get_type());
|
||||
vhdl_var_ref *lval_ref = new vhdl_var_ref(signame.c_str(), ltype);
|
||||
if (base) {
|
||||
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
|
||||
lval_ref->set_slice(base, 0);
|
||||
else
|
||||
lval_ref->set_slice(base, lval_width - 1);
|
||||
}
|
||||
|
||||
return lval_ref;
|
||||
}
|
||||
|
||||
static bool assignment_lvals(ivl_statement_t stmt, vhdl_procedural *proc,
|
||||
list<vhdl_var_ref*> &lvals)
|
||||
{
|
||||
int nlvals = ivl_stmt_lvals(stmt);
|
||||
for (int i = 0; i < nlvals; i++) {
|
||||
ivl_lval_t lval = ivl_stmt_lval(stmt, i);
|
||||
vhdl_var_ref *lhs = make_assign_lhs(lval, proc->get_scope());
|
||||
if (NULL == lhs)
|
||||
return false;
|
||||
|
||||
lvals.push_back(lhs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an assignment of type T for the Verilog statement stmt.
|
||||
*/
|
||||
template <class T>
|
||||
void make_assignment(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool blocking)
|
||||
{
|
||||
list<vhdl_var_ref*> lvals;
|
||||
if (!assignment_lvals(stmt, proc, lvals))
|
||||
return;
|
||||
|
||||
vhdl_expr *rhs, *rhs2 = NULL;
|
||||
ivl_expr_t rval = ivl_stmt_rval(stmt);
|
||||
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
|
||||
rhs = translate_expr(ivl_expr_oper2(rval));
|
||||
rhs2 = translate_expr(ivl_expr_oper3(rval));
|
||||
}
|
||||
else
|
||||
rhs = translate_expr(rval);
|
||||
if (rhs == NULL)
|
||||
return;
|
||||
|
||||
if (lvals.size() == 1) {
|
||||
vhdl_var_ref *lhs = lvals.front();
|
||||
rhs = rhs->cast(lhs->get_type());
|
||||
|
||||
ivl_expr_t i_delay;
|
||||
vhdl_expr *after = NULL;
|
||||
if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL)
|
||||
after = translate_time_expr(i_delay);
|
||||
|
||||
// A small optimisation is to expand ternary RHSs into an
|
||||
// if statement (eliminates a function call and produces
|
||||
// more idiomatic code)
|
||||
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
|
||||
rhs2 = rhs2->cast(lhs->get_type());
|
||||
vhdl_var_ref *lhs2 =
|
||||
make_assign_lhs(ivl_stmt_lval(stmt, 0), proc->get_scope());
|
||||
|
||||
vhdl_expr *test = translate_expr(ivl_expr_oper1(rval));
|
||||
if (NULL == test)
|
||||
return;
|
||||
|
||||
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
|
||||
|
||||
// True part
|
||||
{
|
||||
T *a = new T(lhs, rhs);
|
||||
if (after)
|
||||
a->set_after(after);
|
||||
vhdif->get_then_container()->add_stmt(a);
|
||||
}
|
||||
|
||||
// False part
|
||||
{
|
||||
T *a = new T(lhs2, rhs2);
|
||||
if (after)
|
||||
a->set_after(translate_time_expr(i_delay));
|
||||
vhdif->get_else_container()->add_stmt(a);
|
||||
}
|
||||
|
||||
container->add_stmt(vhdif);
|
||||
return;
|
||||
}
|
||||
|
||||
// Where possible, move constant assignments into the
|
||||
// declaration as initializers. This optimisation is only
|
||||
// performed on assignments of constant values to prevent
|
||||
// ordering problems.
|
||||
|
||||
// This also has another application: If this is an `inital'
|
||||
// process and we haven't yet generated a `wait' statement then
|
||||
// moving the assignment to the initialization preserves the
|
||||
// expected Verilog behaviour: VHDL does not distinguish
|
||||
// `initial' and `always' processes so an `always' process might
|
||||
// be activatated before an `initial' process at time 0. The
|
||||
// `always' process may then use the uninitialized signal value.
|
||||
// The second test ensures that we only try to initialise
|
||||
// internal signals not ports
|
||||
ivl_lval_t lval = ivl_stmt_lval(stmt, 0);
|
||||
vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name());
|
||||
if (proc->get_scope()->initializing()
|
||||
&& ivl_signal_port(ivl_lval_sig(lval)) == IVL_SIP_NONE
|
||||
&& !decl->has_initial()
|
||||
&& rhs->constant()
|
||||
&& decl->get_type()->get_name() != VHDL_TYPE_ARRAY) {
|
||||
|
||||
// If this assignment is not in the top-level container
|
||||
// it will not be made on all paths through the code
|
||||
// This precludes any future extraction of an initialiser
|
||||
if (container != proc->get_container())
|
||||
decl->set_initial(NULL); // Default initial value
|
||||
else {
|
||||
decl->set_initial(rhs);
|
||||
delete lhs;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
T *a = new T(lhs, rhs);
|
||||
container->add_stmt(a);
|
||||
|
||||
if (after != NULL)
|
||||
a->set_after(after);
|
||||
}
|
||||
else {
|
||||
// Multiple lvals are implemented by first assigning the complete
|
||||
// RHS to a temporary, and then assigning each lval in turn as
|
||||
// bit-selects of the temporary
|
||||
|
||||
static int tmp_count = 0;
|
||||
ostringstream ss;
|
||||
ss << "Verilog_Assign_Tmp_" << tmp_count++;
|
||||
string tmpname = ss.str();
|
||||
|
||||
proc->get_scope()->add_decl
|
||||
(new vhdl_var_decl(tmpname.c_str(), new vhdl_type(*rhs->get_type())));
|
||||
|
||||
vhdl_var_ref *tmp_ref =
|
||||
new vhdl_var_ref(tmpname.c_str(), new vhdl_type(*rhs->get_type()));
|
||||
container->add_stmt(new vhdl_assign_stmt(tmp_ref, rhs));
|
||||
|
||||
list<vhdl_var_ref*>::iterator it;
|
||||
int width_so_far = 0;
|
||||
for (it = lvals.begin(); it != lvals.end(); ++it) {
|
||||
vhdl_var_ref *tmp_rhs =
|
||||
new vhdl_var_ref(tmpname.c_str(), new vhdl_type(*rhs->get_type()));
|
||||
|
||||
int lval_width = (*it)->get_type()->get_width();
|
||||
vhdl_expr *slice_base = new vhdl_const_int(width_so_far);
|
||||
tmp_rhs->set_slice(slice_base, lval_width - 1);
|
||||
|
||||
ivl_expr_t i_delay;
|
||||
vhdl_expr *after = NULL;
|
||||
if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL)
|
||||
after = translate_time_expr(i_delay);
|
||||
|
||||
T *a = new T(*it, tmp_rhs);
|
||||
if (after)
|
||||
a->set_after(after);
|
||||
|
||||
container->add_stmt(a);
|
||||
|
||||
width_so_far += lval_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-blocking assignment inside a process. The semantics for
|
||||
* this are essentially the same as VHDL's non-blocking signal
|
||||
* assignment.
|
||||
*/
|
||||
static int draw_nbassign(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
assert(proc->get_scope()->allow_signal_assignment());
|
||||
|
||||
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_assign(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
if (proc->get_scope()->allow_signal_assignment()) {
|
||||
// Blocking assignment is implemented as non-blocking assignment
|
||||
// followed by a zero-time wait
|
||||
// This follows the Verilog semantics fairly closely.
|
||||
|
||||
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
|
||||
|
||||
// Don't generate a zero-wait if this is the last statement in
|
||||
// the process
|
||||
if (!is_last)
|
||||
container->add_stmt
|
||||
(new vhdl_wait_stmt(VHDL_WAIT_FOR, new vhdl_const_time(0)));
|
||||
}
|
||||
else
|
||||
make_assignment<vhdl_assign_stmt>(proc, container, stmt, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay statements are equivalent to the `wait for' form of the
|
||||
* VHDL wait statement.
|
||||
*/
|
||||
static int draw_delay(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
// This currently ignores the time units and precision
|
||||
// of the enclosing scope
|
||||
// A neat way to do this would be to make these values
|
||||
// constants in the scope (type is Time), and have the
|
||||
// VHDL wait statement compute the value from that.
|
||||
// The other solution is to add them as parameters to
|
||||
// the vhdl_process class
|
||||
vhdl_expr *time;
|
||||
if (ivl_statement_type(stmt) == IVL_ST_DELAY) {
|
||||
uint64_t value = ivl_stmt_delay_val(stmt);
|
||||
time = new vhdl_const_time(value, TIME_UNIT_NS);
|
||||
}
|
||||
else {
|
||||
time = translate_time_expr(ivl_stmt_delay_expr(stmt));
|
||||
if (NULL == time)
|
||||
return 1;
|
||||
}
|
||||
|
||||
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
|
||||
vhdl_wait_stmt *wait =
|
||||
new vhdl_wait_stmt(VHDL_WAIT_FOR, time);
|
||||
container->add_stmt(wait);
|
||||
|
||||
// Expand the sub-statement as well
|
||||
// Often this would result in a useless `null' statement which
|
||||
// is caught here instead
|
||||
if (ivl_statement_type(sub_stmt) != IVL_ST_NOOP)
|
||||
draw_stmt(proc, container, sub_stmt);
|
||||
|
||||
// Any further assignments occur after simulation time 0
|
||||
// so they cannot be used to initialize signal declarations
|
||||
// (if this scope is an initial process)
|
||||
proc->get_scope()->set_initializing(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A wait statement waits for a level change on a @(..) list of
|
||||
* signals. Purely combinatorial processes (i.e. no posedge/negedge
|
||||
* events) produce a `wait on' statement at the end of the process.
|
||||
* Sequential processes produce a `wait until' statement at the
|
||||
* start of the process.
|
||||
*/
|
||||
static int draw_wait(vhdl_procedural *_proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
// Wait statements only occur in processes
|
||||
vhdl_process *proc = dynamic_cast<vhdl_process*>(_proc);
|
||||
assert(proc); // Catch not process
|
||||
|
||||
vhdl_binop_expr *test =
|
||||
new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean());
|
||||
|
||||
int nevents = ivl_stmt_nevent(stmt);
|
||||
|
||||
bool combinatorial = true; // True if no negedge/posedge events
|
||||
for (int i = 0; i < nevents; i++) {
|
||||
ivl_event_t event = ivl_stmt_events(stmt, i);
|
||||
if (ivl_event_npos(event) > 0 || ivl_event_nneg(event) > 0)
|
||||
combinatorial = false;
|
||||
}
|
||||
|
||||
if (combinatorial) {
|
||||
vhdl_wait_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_ON);
|
||||
|
||||
for (int i = 0; i < nevents; i++) {
|
||||
ivl_event_t event = ivl_stmt_events(stmt, i);
|
||||
|
||||
int nany = ivl_event_nany(event);
|
||||
for (int i = 0; i < nany; i++) {
|
||||
ivl_nexus_t nexus = ivl_event_any(event, i);
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
|
||||
|
||||
wait->add_sensitivity(ref->get_name());
|
||||
delete ref;
|
||||
}
|
||||
}
|
||||
|
||||
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true);
|
||||
container->add_stmt(wait);
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < nevents; i++) {
|
||||
ivl_event_t event = ivl_stmt_events(stmt, i);
|
||||
|
||||
int nany = ivl_event_nany(event);
|
||||
for (int i = 0; i < nany; i++) {
|
||||
ivl_nexus_t nexus = ivl_event_any(event, i);
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
|
||||
|
||||
ref->set_name(ref->get_name() + "'Event");
|
||||
test->add_expr(ref);
|
||||
}
|
||||
|
||||
int nneg = ivl_event_nneg(event);
|
||||
for (int i = 0; i < nneg; i++) {
|
||||
ivl_nexus_t nexus = ivl_event_neg(event, i);
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
|
||||
vhdl_fcall *detect =
|
||||
new vhdl_fcall("falling_edge", vhdl_type::boolean());
|
||||
detect->add_expr(ref);
|
||||
|
||||
test->add_expr(detect);
|
||||
}
|
||||
|
||||
int npos = ivl_event_npos(event);
|
||||
for (int i = 0; i < npos; i++) {
|
||||
ivl_nexus_t nexus = ivl_event_pos(event, i);
|
||||
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
|
||||
vhdl_fcall *detect =
|
||||
new vhdl_fcall("rising_edge", vhdl_type::boolean());
|
||||
detect->add_expr(ref);
|
||||
|
||||
test->add_expr(detect);
|
||||
}
|
||||
}
|
||||
|
||||
container->add_stmt(new vhdl_wait_stmt(VHDL_WAIT_UNTIL, test));
|
||||
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_if(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == test)
|
||||
return 1;
|
||||
|
||||
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
|
||||
|
||||
ivl_statement_t cond_true_stmt = ivl_stmt_cond_true(stmt);
|
||||
if (cond_true_stmt)
|
||||
draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last);
|
||||
|
||||
ivl_statement_t cond_false_stmt = ivl_stmt_cond_false(stmt);
|
||||
if (cond_false_stmt)
|
||||
draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last);
|
||||
|
||||
container->add_stmt(vhdif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_case(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == test)
|
||||
return 1;
|
||||
|
||||
// VHDL case expressions are required to be quite simple: variable
|
||||
// references or slices. So we may need to create a temporary
|
||||
// variable to hold the result of the expression evaluation
|
||||
if (typeid(*test) != typeid(vhdl_var_ref)) {
|
||||
const char *tmp_name = "Verilog_Case_Ex";
|
||||
vhdl_type *test_type = new vhdl_type(*test->get_type());
|
||||
|
||||
if (!proc->get_scope()->have_declared(tmp_name)) {
|
||||
proc->get_scope()->add_decl
|
||||
(new vhdl_var_decl(tmp_name, new vhdl_type(*test_type)));
|
||||
}
|
||||
|
||||
vhdl_var_ref *tmp_ref = new vhdl_var_ref(tmp_name, NULL);
|
||||
container->add_stmt(new vhdl_assign_stmt(tmp_ref, test));
|
||||
|
||||
test = new vhdl_var_ref(tmp_name, test_type);
|
||||
}
|
||||
|
||||
vhdl_case_stmt *vhdlcase = new vhdl_case_stmt(test);
|
||||
container->add_stmt(vhdlcase);
|
||||
|
||||
// VHDL is more strict than Verilog about covering every
|
||||
// possible case. So make sure we add an 'others' branch
|
||||
// if there isn't a default one.
|
||||
bool have_others = false;
|
||||
|
||||
int nbranches = ivl_stmt_case_count(stmt);
|
||||
for (int i = 0; i < nbranches; i++) {
|
||||
vhdl_expr *when;
|
||||
ivl_expr_t net = ivl_stmt_case_expr(stmt, i);
|
||||
if (net) {
|
||||
when = translate_expr(net)->cast(test->get_type());
|
||||
if (NULL == when)
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
when = new vhdl_var_ref("others", NULL);
|
||||
have_others = true;
|
||||
}
|
||||
|
||||
vhdl_case_branch *branch = new vhdl_case_branch(when);
|
||||
vhdlcase->add_branch(branch);
|
||||
|
||||
ivl_statement_t stmt_i = ivl_stmt_case_stmt(stmt, i);
|
||||
draw_stmt(proc, branch->get_container(), stmt_i, is_last);
|
||||
}
|
||||
|
||||
if (!have_others) {
|
||||
vhdl_case_branch *others =
|
||||
new vhdl_case_branch(new vhdl_var_ref("others", NULL));
|
||||
others->get_container()->add_stmt(new vhdl_null_stmt());
|
||||
vhdlcase->add_branch(others);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_while(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == test)
|
||||
return 1;
|
||||
|
||||
// The test must be a Boolean (and std_logic and (un)signed types
|
||||
// must be explicitly cast unlike in Verilog)
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
test = test->cast(&boolean);
|
||||
|
||||
vhdl_while_stmt *loop = new vhdl_while_stmt(test);
|
||||
container->add_stmt(loop);
|
||||
|
||||
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_forever(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
vhdl_loop_stmt *loop = new vhdl_loop_stmt;
|
||||
container->add_stmt(loop);
|
||||
|
||||
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int draw_repeat(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
vhdl_expr *times = translate_expr(ivl_stmt_cond_expr(stmt));
|
||||
if (NULL == times)
|
||||
return 1;
|
||||
|
||||
vhdl_type integer(VHDL_TYPE_INTEGER);
|
||||
times = times->cast(&integer);
|
||||
|
||||
const char *it_name = "Verilog_Repeat";
|
||||
vhdl_for_stmt *loop =
|
||||
new vhdl_for_stmt(it_name, new vhdl_const_int(1), times);
|
||||
container->add_stmt(loop);
|
||||
|
||||
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tasks are difficult to translate to VHDL since they allow things
|
||||
* not allowed by VHDL's corresponding procedures (e.g. updating
|
||||
* global variables. The solution here is to expand tasks in-line.
|
||||
*/
|
||||
int draw_utask(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt)
|
||||
{
|
||||
ivl_scope_t tscope = ivl_stmt_call(stmt);
|
||||
|
||||
// TODO: adding some comments to the output would be helpful
|
||||
|
||||
// TOOD: this completely ignores paremeters!
|
||||
draw_stmt(proc, container, ivl_scope_def(tscope), false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VHDL statements for the given Verilog statement and
|
||||
* add them to the given VHDL process. The container is the
|
||||
* location to add statements: e.g. the process body, a branch
|
||||
* of an if statement, etc.
|
||||
*
|
||||
* The flag is_last should be set if this is the final statement
|
||||
* in a block or process. It avoids generating useless `wait for 0ns'
|
||||
* statements if the next statement would be a wait anyway.
|
||||
*/
|
||||
int draw_stmt(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last)
|
||||
{
|
||||
assert(stmt);
|
||||
|
||||
switch (ivl_statement_type(stmt)) {
|
||||
case IVL_ST_STASK:
|
||||
return draw_stask(proc, container, stmt);
|
||||
case IVL_ST_BLOCK:
|
||||
return draw_block(proc, container, stmt, is_last);
|
||||
case IVL_ST_NOOP:
|
||||
return draw_noop(proc, container, stmt);
|
||||
case IVL_ST_ASSIGN:
|
||||
return draw_assign(proc, container, stmt, is_last);
|
||||
case IVL_ST_ASSIGN_NB:
|
||||
return draw_nbassign(proc, container, stmt);
|
||||
case IVL_ST_DELAY:
|
||||
case IVL_ST_DELAYX:
|
||||
return draw_delay(proc, container, stmt);
|
||||
case IVL_ST_WAIT:
|
||||
return draw_wait(proc, container, stmt);
|
||||
case IVL_ST_CONDIT:
|
||||
return draw_if(proc, container, stmt, is_last);
|
||||
case IVL_ST_CASE:
|
||||
return draw_case(proc, container, stmt, is_last);
|
||||
case IVL_ST_WHILE:
|
||||
return draw_while(proc, container, stmt);
|
||||
case IVL_ST_FOREVER:
|
||||
return draw_forever(proc, container, stmt);
|
||||
case IVL_ST_REPEAT:
|
||||
return draw_repeat(proc, container, stmt);
|
||||
case IVL_ST_UTASK:
|
||||
return draw_utask(proc, container, stmt);
|
||||
case IVL_ST_FORCE:
|
||||
case IVL_ST_RELEASE:
|
||||
error("force/release statements cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_DISABLE:
|
||||
error("disable statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_CASEX:
|
||||
error("casex statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_CASEZ:
|
||||
error("casez statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_FORK:
|
||||
error("fork statement cannot be translated to VHDL");
|
||||
return 1;
|
||||
case IVL_ST_CASSIGN:
|
||||
case IVL_ST_DEASSIGN:
|
||||
error("continuous procedural assignment cannot be translated to VHDL");
|
||||
return 1;
|
||||
default:
|
||||
error("No VHDL translation for statement at %s:%d (type = %d)",
|
||||
ivl_stmt_file(stmt), ivl_stmt_lineno(stmt),
|
||||
ivl_statement_type(stmt));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Support functions for VHDL output.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "support.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
void require_support_function(support_function_t f)
|
||||
{
|
||||
vhdl_scope *scope = get_active_entity()->get_arch()->get_scope();
|
||||
if (!scope->have_declared(support_function::function_name(f)))
|
||||
scope->add_decl(new support_function(f));
|
||||
}
|
||||
|
||||
const char *support_function::function_name(support_function_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case SF_UNSIGNED_TO_BOOLEAN: return "Unsigned_To_Boolean";
|
||||
case SF_SIGNED_TO_BOOLEAN: return "Signed_To_Boolean";
|
||||
case SF_BOOLEAN_TO_LOGIC: return "Boolean_To_Logic";
|
||||
case SF_REDUCE_OR: return "Reduce_OR";
|
||||
case SF_REDUCE_AND: return "Reduce_AND";
|
||||
case SF_REDUCE_XOR: return "Reduce_XOR";
|
||||
case SF_TERNARY_LOGIC: return "Ternary_Logic";
|
||||
case SF_TERNARY_UNSIGNED: return "Ternary_Unsigned";
|
||||
case SF_TERNARY_SIGNED: return "Ternary_Signed";
|
||||
case SF_LOGIC_TO_INTEGER: return "Logic_To_Integer";
|
||||
case SF_SIGNED_TO_LOGIC: return "Signed_To_Logic";
|
||||
case SF_UNSIGNED_TO_LOGIC: return "Unsigned_To_Logic";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
vhdl_type *support_function::function_type(support_function_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case SF_UNSIGNED_TO_BOOLEAN:
|
||||
case SF_SIGNED_TO_BOOLEAN:
|
||||
return vhdl_type::boolean();
|
||||
case SF_BOOLEAN_TO_LOGIC:
|
||||
case SF_REDUCE_OR:
|
||||
case SF_REDUCE_AND:
|
||||
case SF_REDUCE_XOR:
|
||||
case SF_TERNARY_LOGIC:
|
||||
case SF_SIGNED_TO_LOGIC:
|
||||
case SF_UNSIGNED_TO_LOGIC:
|
||||
return vhdl_type::std_logic();
|
||||
case SF_TERNARY_SIGNED:
|
||||
return new vhdl_type(VHDL_TYPE_SIGNED);
|
||||
case SF_TERNARY_UNSIGNED:
|
||||
return new vhdl_type(VHDL_TYPE_UNSIGNED);
|
||||
case SF_LOGIC_TO_INTEGER:
|
||||
return vhdl_type::integer();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void support_function::emit_ternary(std::ostream &of, int level) const
|
||||
{
|
||||
of << nl_string(level) << "begin" << nl_string(indent(level))
|
||||
<< "if T then return X; else return Y; end if;";
|
||||
}
|
||||
|
||||
void support_function::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << nl_string(level) << "function " << function_name(type_);
|
||||
|
||||
switch (type_) {
|
||||
case SF_UNSIGNED_TO_BOOLEAN:
|
||||
of << "(X : unsigned) return Boolean is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "return X /= To_Unsigned(0, X'Length);";
|
||||
break;
|
||||
case SF_SIGNED_TO_BOOLEAN:
|
||||
of << "(X : signed) return Boolean is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "return X /= To_Signed(0, X'Length);";
|
||||
break;
|
||||
case SF_BOOLEAN_TO_LOGIC:
|
||||
of << "(B : Boolean) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "if B then" << nl_string(indent(indent(level)))
|
||||
<< "return '1';" << nl_string(indent(level))
|
||||
<< "else" << nl_string(indent(indent(level)))
|
||||
<< "return '0';" << nl_string(indent(level))
|
||||
<< "end if;";
|
||||
break;
|
||||
case SF_UNSIGNED_TO_LOGIC:
|
||||
of << "(X : unsigned) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "return X(0);";
|
||||
break;
|
||||
case SF_SIGNED_TO_LOGIC:
|
||||
of << "(X : signed) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "return X(0);";
|
||||
break;
|
||||
case SF_REDUCE_OR:
|
||||
of << "(X : std_logic_vector) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
|
||||
<< "if X(I) = '1' then" << nl_string(indent(indent(indent(level))))
|
||||
<< "return '1';" << nl_string(indent(indent(level)))
|
||||
<< "end if;" << nl_string(indent(level))
|
||||
<< "end loop;" << nl_string(indent(level))
|
||||
<< "return '0';";
|
||||
break;
|
||||
case SF_REDUCE_AND:
|
||||
of << "(X : std_logic_vector) return std_logic is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
|
||||
<< "if X(I) = '0' then" << nl_string(indent(indent(indent(level))))
|
||||
<< "return '0';" << nl_string(indent(indent(level)))
|
||||
<< "end if;" << nl_string(indent(level))
|
||||
<< "end loop;" << nl_string(indent(level))
|
||||
<< "return '1';";
|
||||
break;
|
||||
case SF_REDUCE_XOR:
|
||||
of << "(X : std_logic_vector) return std_logic is"
|
||||
<< nl_string(indent(level))
|
||||
<< "variable R : std_logic := '0';" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
|
||||
<< "R := X(I) xor R;" << nl_string(indent(level))
|
||||
<< "end loop;" << nl_string(indent(level))
|
||||
<< "return R;";
|
||||
break;
|
||||
case SF_TERNARY_LOGIC:
|
||||
of << "(T : Boolean; X, Y : std_logic) return std_logic is";
|
||||
emit_ternary(of, level);
|
||||
break;
|
||||
case SF_TERNARY_SIGNED:
|
||||
of << "(T : Boolean; X, Y : signed) return signed is";
|
||||
emit_ternary(of, level);
|
||||
break;
|
||||
case SF_TERNARY_UNSIGNED:
|
||||
of << "(T : Boolean; X, Y : unsigned) return unsigned is";
|
||||
emit_ternary(of, level);
|
||||
break;
|
||||
case SF_LOGIC_TO_INTEGER:
|
||||
of << "(X : std_logic) return integer is" << nl_string(level)
|
||||
<< "begin" << nl_string(indent(level))
|
||||
<< "if X = '1' then return 1; else return 0; end if;";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
of << nl_string(level) << "end function;";
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Support functions for VHDL output.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef INC_SUPPORT_HH
|
||||
#define INC_SUPPORT_HH
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
|
||||
enum support_function_t {
|
||||
SF_UNSIGNED_TO_BOOLEAN = 0,
|
||||
SF_SIGNED_TO_BOOLEAN,
|
||||
SF_BOOLEAN_TO_LOGIC,
|
||||
SF_REDUCE_OR,
|
||||
SF_REDUCE_AND,
|
||||
SF_REDUCE_XOR,
|
||||
SF_TERNARY_LOGIC,
|
||||
SF_TERNARY_UNSIGNED,
|
||||
SF_TERNARY_SIGNED,
|
||||
SF_LOGIC_TO_INTEGER,
|
||||
SF_SIGNED_TO_LOGIC,
|
||||
SF_UNSIGNED_TO_LOGIC,
|
||||
};
|
||||
|
||||
class support_function : public vhdl_function {
|
||||
public:
|
||||
support_function(support_function_t type)
|
||||
: vhdl_function(function_name(type), function_type(type)),
|
||||
type_(type) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
static const char *function_name(support_function_t type);
|
||||
static vhdl_type *function_type(support_function_t type);
|
||||
|
||||
private:
|
||||
void emit_ternary(std::ostream &of, int level) const;
|
||||
|
||||
support_function_t type_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* VHDL code generator for Icarus Verilog.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_target.h"
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
/*
|
||||
* Maps a signal to the scope it is defined within. Also
|
||||
* provides a mechanism for renaming signals -- i.e. when
|
||||
* an output has the same name as register: valid in Verilog
|
||||
* but not in VHDL, so two separate signals need to be
|
||||
* defined.
|
||||
*/
|
||||
struct signal_defn_t {
|
||||
std::string renamed; // The name of the VHDL signal
|
||||
vhdl_scope *scope; // The scope where it is defined
|
||||
};
|
||||
|
||||
typedef std::map<ivl_signal_t, signal_defn_t> signal_defn_map_t;
|
||||
|
||||
|
||||
static int g_errors = 0; // Total number of errors encountered
|
||||
static entity_list_t g_entities; // All entities to emit
|
||||
static signal_defn_map_t g_known_signals;
|
||||
static ivl_design_t g_design;
|
||||
|
||||
|
||||
/*
|
||||
* Called when an unrecoverable problem is encountered.
|
||||
*/
|
||||
void error(const char *fmt, ...)
|
||||
{
|
||||
std::va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
std::printf("VHDL conversion error: "); // Source/line number?
|
||||
std::vprintf(fmt, args);
|
||||
std::putchar('\n');
|
||||
va_end(args);
|
||||
|
||||
g_errors++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an entity given a scope name.
|
||||
*/
|
||||
vhdl_entity *find_entity(const std::string &sname)
|
||||
{
|
||||
entity_list_t::const_iterator it;
|
||||
for (it = g_entities.begin(); it != g_entities.end(); ++it) {
|
||||
if ((*it)->get_derived_from() == sname)
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entity/architecture pair to the list of entities
|
||||
* to emit.
|
||||
*/
|
||||
void remember_entity(vhdl_entity* ent)
|
||||
{
|
||||
assert(find_entity(ent->get_derived_from()) == NULL);
|
||||
g_entities.push_back(ent);
|
||||
}
|
||||
|
||||
bool seen_signal_before(ivl_signal_t sig)
|
||||
{
|
||||
return g_known_signals.find(sig) != g_known_signals.end();
|
||||
}
|
||||
|
||||
/*
|
||||
* Remeber the association of signal to entity.
|
||||
*/
|
||||
void remember_signal(ivl_signal_t sig, vhdl_scope *scope)
|
||||
{
|
||||
assert(!seen_signal_before(sig));
|
||||
|
||||
signal_defn_t defn = { ivl_signal_basename(sig), scope };
|
||||
g_known_signals[sig] = defn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the VHDL name of a Verilog signal.
|
||||
*/
|
||||
void rename_signal(ivl_signal_t sig, const std::string &renamed)
|
||||
{
|
||||
assert(seen_signal_before(sig));
|
||||
|
||||
g_known_signals[sig].renamed = renamed;
|
||||
}
|
||||
|
||||
vhdl_scope *find_scope_for_signal(ivl_signal_t sig)
|
||||
{
|
||||
assert(seen_signal_before(sig));
|
||||
|
||||
return g_known_signals[sig].scope;
|
||||
}
|
||||
|
||||
const std::string &get_renamed_signal(ivl_signal_t sig)
|
||||
{
|
||||
assert(seen_signal_before(sig));
|
||||
|
||||
return g_known_signals[sig].renamed;
|
||||
}
|
||||
|
||||
ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope)
|
||||
{
|
||||
signal_defn_map_t::const_iterator it;
|
||||
for (it = g_known_signals.begin(); it != g_known_signals.end(); ++it) {
|
||||
if (((*it).second.scope == scope
|
||||
|| (*it).second.scope == scope->get_parent())
|
||||
&& (*it).second.renamed == name)
|
||||
return (*it).first;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
ivl_design_t get_vhdl_design()
|
||||
{
|
||||
return g_design;
|
||||
}
|
||||
|
||||
extern "C" int target_design(ivl_design_t des)
|
||||
{
|
||||
ivl_scope_t *roots;
|
||||
unsigned int nroots;
|
||||
ivl_design_roots(des, &roots, &nroots);
|
||||
|
||||
g_design = des;
|
||||
|
||||
for (unsigned int i = 0; i < nroots; i++)
|
||||
draw_scope(roots[i], NULL);
|
||||
|
||||
ivl_design_process(des, draw_process, NULL);
|
||||
|
||||
// Write the generated elements to the output file
|
||||
// only if there are no errors
|
||||
if (0 == g_errors) {
|
||||
const char *ofname = ivl_design_flag(des, "-o");
|
||||
ofstream outfile(ofname);
|
||||
|
||||
// Make sure we only emit one example of each type of entity
|
||||
set<string> seen_entities;
|
||||
|
||||
for (entity_list_t::iterator it = g_entities.begin();
|
||||
it != g_entities.end();
|
||||
++it) {
|
||||
if (seen_entities.find((*it)->get_name()) == seen_entities.end()) {
|
||||
(*it)->emit(outfile);
|
||||
seen_entities.insert((*it)->get_name());
|
||||
}
|
||||
}
|
||||
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
// Clean up
|
||||
for (entity_list_t::iterator it = g_entities.begin();
|
||||
it != g_entities.end();
|
||||
++it)
|
||||
delete (*it);
|
||||
g_entities.clear();
|
||||
|
||||
return g_errors;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
functor:cprop
|
||||
functor:nodangle
|
||||
-t:dll
|
||||
flag:DLL=vhdl.tgt
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/* vhdl_config.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#undef HAVE_MALLOC_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
static const int VHDL_INDENT = 2; // Spaces to indent
|
||||
|
||||
int indent(int level)
|
||||
{
|
||||
return level + VHDL_INDENT;
|
||||
}
|
||||
|
||||
std::string nl_string(int level)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
newline(ss, level);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit a newline and indent to the correct level.
|
||||
*/
|
||||
void newline(std::ostream &of, int level)
|
||||
{
|
||||
of << std::endl;
|
||||
while (level--)
|
||||
of << ' ';
|
||||
}
|
||||
|
||||
void blank_line(std::ostream &of, int level)
|
||||
{
|
||||
of << std::endl;
|
||||
newline(of, level);
|
||||
}
|
||||
|
||||
void vhdl_element::set_comment(std::string comment)
|
||||
{
|
||||
comment_ = comment;
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the comment for any element. The comment is either on
|
||||
* a line before the element (end_of_line is false) or at the
|
||||
* end of the line containing the element (end_of_line is true).
|
||||
*/
|
||||
void vhdl_element::emit_comment(std::ostream &of, int level,
|
||||
bool end_of_line) const
|
||||
{
|
||||
if (comment_.size() > 0) {
|
||||
if (end_of_line)
|
||||
of << " ";
|
||||
of << "-- " << comment_;
|
||||
if (!end_of_line)
|
||||
newline(of, level);
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_element::print() const
|
||||
{
|
||||
emit(std::cout, 0);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef INC_VHDL_ELEMENT_HH
|
||||
#define INC_VHDL_ELEMENT_HH
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
typedef std::list<std::string> string_list_t;
|
||||
|
||||
/*
|
||||
* Any VHDL syntax element. Each element can also contain a comment.
|
||||
*/
|
||||
class vhdl_element {
|
||||
public:
|
||||
virtual ~vhdl_element() {}
|
||||
|
||||
virtual void emit(std::ostream &of, int level=0) const = 0;
|
||||
void print() const;
|
||||
|
||||
void set_comment(std::string comment);
|
||||
protected:
|
||||
void emit_comment(std::ostream &of, int level,
|
||||
bool end_of_line=false) const;
|
||||
private:
|
||||
std::string comment_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_element*> element_list_t;
|
||||
|
||||
int indent(int level);
|
||||
void newline(std::ostream &of, int level);
|
||||
std::string nl_string(int level);
|
||||
void blank_line(std::ostream &of, int level);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Helper functions for VHDL syntax elements.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef INC_VHDL_HELPER_HH
|
||||
#define INC_VHDL_HELPER_HH
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <cassert>
|
||||
|
||||
template <class T>
|
||||
void emit_children(std::ostream &of,
|
||||
const std::list<T*> &children,
|
||||
int level, const char *delim = "",
|
||||
bool trailing_newline = true)
|
||||
{
|
||||
// Don't indent if there are no children
|
||||
if (children.size() == 0)
|
||||
newline(of, level);
|
||||
else {
|
||||
typename std::list<T*>::const_iterator it;
|
||||
int sz = children.size();
|
||||
for (it = children.begin(); it != children.end(); ++it) {
|
||||
newline(of, indent(level));
|
||||
(*it)->emit(of, indent(level));
|
||||
if (--sz > 0)
|
||||
of << delim;
|
||||
}
|
||||
if (trailing_newline)
|
||||
newline(of, level);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void delete_children(std::list<T*> &children)
|
||||
{
|
||||
typename std::list<T*>::iterator it;
|
||||
for (it = children.begin(); it != children.end(); ++it)
|
||||
delete *it;
|
||||
children.clear();
|
||||
}
|
||||
|
||||
static inline char vl_to_vhdl_bit(char bit)
|
||||
{
|
||||
switch (bit) {
|
||||
case '0':
|
||||
case 'Z':
|
||||
case '1':
|
||||
return bit;
|
||||
case 'z':
|
||||
return 'Z';
|
||||
case 'x':
|
||||
case 'X':
|
||||
return 'U';
|
||||
case '?':
|
||||
return '-';
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,945 @@
|
|||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
#include "vhdl_helper.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
|
||||
vhdl_scope::vhdl_scope()
|
||||
: parent_(NULL), init_(false), sig_assign_(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
vhdl_scope::~vhdl_scope()
|
||||
{
|
||||
delete_children<vhdl_decl>(decls_);
|
||||
}
|
||||
|
||||
void vhdl_scope::set_initializing(bool i)
|
||||
{
|
||||
init_ = i;
|
||||
if (parent_)
|
||||
parent_->set_initializing(i);
|
||||
}
|
||||
|
||||
void vhdl_scope::add_decl(vhdl_decl *decl)
|
||||
{
|
||||
decls_.push_back(decl);
|
||||
}
|
||||
|
||||
void vhdl_scope::add_forward_decl(vhdl_decl *decl)
|
||||
{
|
||||
decls_.push_front(decl);
|
||||
}
|
||||
|
||||
vhdl_decl *vhdl_scope::get_decl(const std::string &name) const
|
||||
{
|
||||
decl_list_t::const_iterator it;
|
||||
for (it = decls_.begin(); it != decls_.end(); ++it) {
|
||||
if (strcasecmp((*it)->get_name().c_str(), name.c_str()) == 0)
|
||||
return *it;
|
||||
}
|
||||
|
||||
return parent_ ? parent_->get_decl(name) : NULL;
|
||||
}
|
||||
|
||||
bool vhdl_scope::have_declared(const std::string &name) const
|
||||
{
|
||||
return get_decl(name) != NULL;
|
||||
}
|
||||
|
||||
bool vhdl_scope::contained_within(const vhdl_scope *other) const
|
||||
{
|
||||
if (this == other)
|
||||
return true;
|
||||
else if (NULL == parent_)
|
||||
return false;
|
||||
else
|
||||
return parent_->contained_within(other);
|
||||
}
|
||||
|
||||
vhdl_scope *vhdl_scope::get_parent() const
|
||||
{
|
||||
assert(parent_);
|
||||
return parent_;
|
||||
}
|
||||
|
||||
vhdl_entity::vhdl_entity(const char *name, const char *derived_from,
|
||||
vhdl_arch *arch)
|
||||
: name_(name), arch_(arch), derived_from_(derived_from)
|
||||
{
|
||||
arch->get_scope()->set_parent(&ports_);
|
||||
}
|
||||
|
||||
vhdl_entity::~vhdl_entity()
|
||||
{
|
||||
delete arch_;
|
||||
}
|
||||
|
||||
void vhdl_entity::add_port(vhdl_port_decl *decl)
|
||||
{
|
||||
ports_.add_decl(decl);
|
||||
}
|
||||
|
||||
void vhdl_entity::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// Pretty much every design will use std_logic so we
|
||||
// might as well include it by default
|
||||
of << "library ieee;" << std::endl;
|
||||
of << "use ieee.std_logic_1164.all;" << std::endl;
|
||||
of << "use ieee.numeric_std.all;" << std::endl;
|
||||
of << "use std.textio.all;" << std::endl;
|
||||
of << std::endl;
|
||||
|
||||
emit_comment(of, level);
|
||||
of << "entity " << name_ << " is";
|
||||
|
||||
if (!ports_.empty()) {
|
||||
newline(of, indent(level));
|
||||
of << "port (";
|
||||
emit_children<vhdl_decl>(of, ports_.get_decls(), indent(level), ";");
|
||||
of << ");";
|
||||
}
|
||||
|
||||
newline(of, level);
|
||||
of << "end entity; ";
|
||||
blank_line(of, level); // Extra blank line after entities
|
||||
arch_->emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_arch::~vhdl_arch()
|
||||
{
|
||||
delete_children<vhdl_conc_stmt>(stmts_);
|
||||
}
|
||||
|
||||
void vhdl_arch::add_stmt(vhdl_process *proc)
|
||||
{
|
||||
proc->get_scope()->set_parent(&scope_);
|
||||
stmts_.push_back(proc);
|
||||
}
|
||||
|
||||
void vhdl_arch::add_stmt(vhdl_conc_stmt *stmt)
|
||||
{
|
||||
stmts_.push_back(stmt);
|
||||
}
|
||||
|
||||
void vhdl_arch::emit(std::ostream &of, int level) const
|
||||
{
|
||||
emit_comment(of, level);
|
||||
of << "architecture " << name_ << " of " << entity_;
|
||||
of << " is";
|
||||
emit_children<vhdl_decl>(of, scope_.get_decls(), level);
|
||||
of << "begin";
|
||||
emit_children<vhdl_conc_stmt>(of, stmts_, level);
|
||||
of << "end architecture;";
|
||||
blank_line(of, level); // Extra blank line after architectures;
|
||||
}
|
||||
|
||||
void vhdl_process::add_sensitivity(const std::string &name)
|
||||
{
|
||||
sens_.push_back(name);
|
||||
}
|
||||
|
||||
void vhdl_process::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// If there are no statements in the body, this process
|
||||
// can't possibly do anything, so don't bother to emit it
|
||||
if (stmts_.empty()) {
|
||||
of << "-- Removed one empty process";
|
||||
newline(of, level);
|
||||
return;
|
||||
}
|
||||
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
if (name_.size() > 0)
|
||||
of << name_ << ": ";
|
||||
of << "process ";
|
||||
|
||||
int num_sens = sens_.size();
|
||||
if (num_sens > 0) {
|
||||
of << "(";
|
||||
string_list_t::const_iterator it;
|
||||
for (it = sens_.begin(); it != sens_.end(); ++it) {
|
||||
of << *it;
|
||||
if (--num_sens > 0)
|
||||
of << ", ";
|
||||
}
|
||||
of << ") ";
|
||||
}
|
||||
|
||||
of << "is";
|
||||
emit_children<vhdl_decl>(of, scope_.get_decls(), level);
|
||||
of << "begin";
|
||||
stmts_.emit(of, level);
|
||||
of << "end process;";
|
||||
}
|
||||
|
||||
stmt_container::~stmt_container()
|
||||
{
|
||||
delete_children<vhdl_seq_stmt>(stmts_);
|
||||
}
|
||||
|
||||
void stmt_container::add_stmt(vhdl_seq_stmt *stmt)
|
||||
{
|
||||
stmts_.push_back(stmt);
|
||||
}
|
||||
|
||||
void stmt_container::emit(std::ostream &of, int level, bool newline) const
|
||||
{
|
||||
emit_children<vhdl_seq_stmt>(of, stmts_, level, "", newline);
|
||||
}
|
||||
|
||||
vhdl_comp_inst::vhdl_comp_inst(const char *inst_name, const char *comp_name)
|
||||
: comp_name_(comp_name), inst_name_(inst_name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
vhdl_comp_inst::~vhdl_comp_inst()
|
||||
{
|
||||
port_map_list_t::iterator it;
|
||||
for (it = mapping_.begin(); it != mapping_.end(); ++it) {
|
||||
delete (*it).expr;
|
||||
}
|
||||
mapping_.clear();
|
||||
}
|
||||
|
||||
void vhdl_comp_inst::map_port(const char *name, vhdl_expr *expr)
|
||||
{
|
||||
port_map_t pmap = { name, expr };
|
||||
mapping_.push_back(pmap);
|
||||
}
|
||||
|
||||
void vhdl_comp_inst::emit(std::ostream &of, int level) const
|
||||
{
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
of << inst_name_ << ": " << comp_name_;
|
||||
|
||||
// If there are no ports or generics we don't need to mention them...
|
||||
if (mapping_.size() > 0) {
|
||||
newline(of, indent(level));
|
||||
of << "port map (";
|
||||
|
||||
int sz = mapping_.size();
|
||||
port_map_list_t::const_iterator it;
|
||||
for (it = mapping_.begin(); it != mapping_.end(); ++it) {
|
||||
newline(of, indent(indent(level)));
|
||||
of << (*it).name << " => ";
|
||||
(*it).expr->emit(of, level);
|
||||
if (--sz > 0)
|
||||
of << ",";
|
||||
}
|
||||
newline(of, indent(level));
|
||||
of << ")";
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_component_decl::vhdl_component_decl(const char *name)
|
||||
: vhdl_decl(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a component declaration for the given entity.
|
||||
*/
|
||||
vhdl_component_decl *vhdl_component_decl::component_decl_for(vhdl_entity *ent)
|
||||
{
|
||||
assert(ent != NULL);
|
||||
|
||||
vhdl_component_decl *decl = new vhdl_component_decl
|
||||
(ent->get_name().c_str());
|
||||
|
||||
decl->ports_ = ent->get_scope()->get_decls();
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
void vhdl_component_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
of << "component " << name_ << " is";
|
||||
|
||||
if (ports_.size() > 0) {
|
||||
newline(of, indent(level));
|
||||
of << "port (";
|
||||
emit_children<vhdl_decl>(of, ports_, indent(level), ";");
|
||||
of << ");";
|
||||
}
|
||||
|
||||
newline(of, level);
|
||||
of << "end component;";
|
||||
}
|
||||
|
||||
vhdl_wait_stmt::~vhdl_wait_stmt()
|
||||
{
|
||||
if (expr_ != NULL)
|
||||
delete expr_;
|
||||
}
|
||||
|
||||
void vhdl_wait_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "wait";
|
||||
|
||||
switch (type_) {
|
||||
case VHDL_WAIT_INDEF:
|
||||
break;
|
||||
case VHDL_WAIT_FOR:
|
||||
assert(expr_);
|
||||
of << " for ";
|
||||
expr_->emit(of, level);
|
||||
break;
|
||||
case VHDL_WAIT_UNTIL:
|
||||
assert(expr_);
|
||||
of << " until ";
|
||||
expr_->emit(of, level);
|
||||
break;
|
||||
case VHDL_WAIT_ON:
|
||||
{
|
||||
of << " on ";
|
||||
string_list_t::const_iterator it = sensitivity_.begin();
|
||||
while (it != sensitivity_.end()) {
|
||||
of << *it;
|
||||
if (++it != sensitivity_.end())
|
||||
of << ", ";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_decl::~vhdl_decl()
|
||||
{
|
||||
if (type_ != NULL)
|
||||
delete type_;
|
||||
if (initial_ != NULL)
|
||||
delete initial_;
|
||||
}
|
||||
|
||||
const vhdl_type *vhdl_decl::get_type() const
|
||||
{
|
||||
assert(type_);
|
||||
return type_;
|
||||
}
|
||||
|
||||
void vhdl_decl::set_initial(vhdl_expr *initial)
|
||||
{
|
||||
if (!has_initial_) {
|
||||
assert(initial_ == NULL);
|
||||
initial_ = initial;
|
||||
has_initial_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_port_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_ << " : ";
|
||||
|
||||
switch (mode_) {
|
||||
case VHDL_PORT_IN:
|
||||
of << "in ";
|
||||
break;
|
||||
case VHDL_PORT_OUT:
|
||||
of << "out ";
|
||||
break;
|
||||
case VHDL_PORT_INOUT:
|
||||
of << "inout ";
|
||||
break;
|
||||
case VHDL_PORT_BUFFER:
|
||||
of << "buffer ";
|
||||
break;
|
||||
}
|
||||
|
||||
type_->emit(of, level);
|
||||
}
|
||||
|
||||
void vhdl_var_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "variable " << name_ << " : ";
|
||||
type_->emit(of, level);
|
||||
|
||||
if (initial_) {
|
||||
of << " := ";
|
||||
initial_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
emit_comment(of, level, true);
|
||||
}
|
||||
|
||||
void vhdl_signal_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "signal " << name_ << " : ";
|
||||
type_->emit(of, level);
|
||||
|
||||
if (initial_) {
|
||||
of << " := ";
|
||||
initial_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
emit_comment(of, level, true);
|
||||
}
|
||||
|
||||
void vhdl_type_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "type " << name_ << " is ";
|
||||
of << type_->get_type_decl_string() << ";";
|
||||
emit_comment(of, level, true);
|
||||
}
|
||||
|
||||
vhdl_expr::~vhdl_expr()
|
||||
{
|
||||
if (type_ != NULL)
|
||||
delete type_;
|
||||
}
|
||||
|
||||
void vhdl_expr_list::add_expr(vhdl_expr *e)
|
||||
{
|
||||
exprs_.push_back(e);
|
||||
}
|
||||
|
||||
vhdl_expr_list::~vhdl_expr_list()
|
||||
{
|
||||
delete_children<vhdl_expr>(exprs_);
|
||||
}
|
||||
|
||||
void vhdl_expr_list::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "(";
|
||||
|
||||
int size = exprs_.size();
|
||||
std::list<vhdl_expr*>::const_iterator it;
|
||||
for (it = exprs_.begin(); it != exprs_.end(); ++it) {
|
||||
(*it)->emit(of, level);
|
||||
if (--size > 0)
|
||||
of << ", ";
|
||||
}
|
||||
|
||||
of << ")";
|
||||
}
|
||||
|
||||
void vhdl_pcall_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_;
|
||||
if (!exprs_.empty())
|
||||
exprs_.emit(of, level);
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_var_ref::~vhdl_var_ref()
|
||||
{
|
||||
if (slice_)
|
||||
delete slice_;
|
||||
}
|
||||
|
||||
void vhdl_var_ref::set_slice(vhdl_expr *s, int w)
|
||||
{
|
||||
assert(type_);
|
||||
|
||||
slice_ = s;
|
||||
slice_width_ = w;
|
||||
|
||||
vhdl_type_name_t tname = type_->get_name();
|
||||
if (tname == VHDL_TYPE_ARRAY) {
|
||||
type_ = new vhdl_type(*type_->get_base());
|
||||
}
|
||||
else {
|
||||
assert(tname == VHDL_TYPE_UNSIGNED || tname == VHDL_TYPE_SIGNED);
|
||||
|
||||
if (type_)
|
||||
delete type_;
|
||||
|
||||
if (w > 0)
|
||||
type_ = new vhdl_type(tname, w);
|
||||
else
|
||||
type_ = vhdl_type::std_logic();
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_var_ref::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_;
|
||||
if (slice_) {
|
||||
of << "(";
|
||||
if (slice_width_ > 0) {
|
||||
slice_->emit(of, level);
|
||||
of << " + " << slice_width_ << " downto ";
|
||||
}
|
||||
slice_->emit(of, level);
|
||||
of << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_const_string::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// In some instances a string literal can be ambiguous between
|
||||
// a String type and some other types (e.g. std_logic_vector)
|
||||
// The explicit cast to String removes this ambiguity (although
|
||||
// isn't always strictly necessary)
|
||||
of << "String'(\"" << value_ << "\")";
|
||||
}
|
||||
|
||||
void vhdl_null_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "null;";
|
||||
}
|
||||
|
||||
void vhdl_fcall::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_;
|
||||
exprs_.emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_abstract_assign_stmt::~vhdl_abstract_assign_stmt()
|
||||
{
|
||||
delete lhs_;
|
||||
delete rhs_;
|
||||
if (after_)
|
||||
delete after_;
|
||||
}
|
||||
|
||||
void vhdl_nbassign_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
lhs_->emit(of, level);
|
||||
of << " <= ";
|
||||
rhs_->emit(of, level);
|
||||
|
||||
if (after_) {
|
||||
of << " after ";
|
||||
after_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
void vhdl_assign_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
lhs_->emit(of, level);
|
||||
of << " := ";
|
||||
rhs_->emit(of, level);
|
||||
of << ";";
|
||||
}
|
||||
|
||||
vhdl_const_bits::vhdl_const_bits(const char *value, int width, bool issigned)
|
||||
: vhdl_expr(issigned ? vhdl_type::nsigned(width)
|
||||
: vhdl_type::nunsigned(width), true),
|
||||
qualified_(false),
|
||||
signed_(issigned)
|
||||
{
|
||||
// Can't rely on value being NULL-terminated
|
||||
while (width--)
|
||||
value_.push_back(*value++);
|
||||
}
|
||||
|
||||
void vhdl_const_bits::emit(std::ostream &of, int level) const
|
||||
{
|
||||
if (qualified_)
|
||||
of << (signed_ ? "signed" : "unsigned") << "'(\"";
|
||||
else
|
||||
of << "\"";
|
||||
|
||||
// The bits appear to be in reverse order
|
||||
std::string::const_reverse_iterator it;
|
||||
for (it = value_.rbegin(); it != value_.rend(); ++it)
|
||||
of << vl_to_vhdl_bit(*it);
|
||||
|
||||
of << (qualified_ ? "\")" : "\"");
|
||||
}
|
||||
|
||||
void vhdl_const_bit::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "'" << vl_to_vhdl_bit(bit_) << "'";
|
||||
}
|
||||
|
||||
void vhdl_const_int::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << value_;
|
||||
}
|
||||
|
||||
void vhdl_const_bool::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << (value_ ? "True" : "False");
|
||||
}
|
||||
|
||||
void vhdl_const_time::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << value_;
|
||||
switch (units_) {
|
||||
case TIME_UNIT_NS:
|
||||
of << " ns";
|
||||
}
|
||||
}
|
||||
|
||||
vhdl_cassign_stmt::~vhdl_cassign_stmt()
|
||||
{
|
||||
delete lhs_;
|
||||
delete rhs_;
|
||||
|
||||
for (std::list<when_part_t>::const_iterator it = whens_.begin();
|
||||
it != whens_.end();
|
||||
++it) {
|
||||
delete (*it).value;
|
||||
delete (*it).cond;
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_cassign_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond)
|
||||
{
|
||||
when_part_t when = { value, cond, NULL };
|
||||
whens_.push_back(when);
|
||||
}
|
||||
|
||||
void vhdl_cassign_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
lhs_->emit(of, level);
|
||||
of << " <= ";
|
||||
if (!whens_.empty()) {
|
||||
for (std::list<when_part_t>::const_iterator it = whens_.begin();
|
||||
it != whens_.end();
|
||||
++it) {
|
||||
(*it).value->emit(of, level);
|
||||
of << " when ";
|
||||
(*it).cond->emit(of, level);
|
||||
of << " ";
|
||||
}
|
||||
of << "else ";
|
||||
}
|
||||
rhs_->emit(of, level);
|
||||
|
||||
if (after_) {
|
||||
of << " after ";
|
||||
after_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ";";
|
||||
}
|
||||
|
||||
void vhdl_assert_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "assert false"; // TODO: Allow arbitrary expression
|
||||
of << " report \"" << reason_ << "\" severity failure;";
|
||||
}
|
||||
|
||||
vhdl_if_stmt::vhdl_if_stmt(vhdl_expr *test)
|
||||
{
|
||||
// Need to ensure that the expression is Boolean
|
||||
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
|
||||
test_ = test->cast(&boolean);
|
||||
}
|
||||
|
||||
vhdl_if_stmt::~vhdl_if_stmt()
|
||||
{
|
||||
delete test_;
|
||||
}
|
||||
|
||||
void vhdl_if_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "if ";
|
||||
test_->emit(of, level);
|
||||
of << " then";
|
||||
then_part_.emit(of, level);
|
||||
if (!else_part_.empty()) {
|
||||
of << "else";
|
||||
else_part_.emit(of, level);
|
||||
}
|
||||
of << "end if;";
|
||||
}
|
||||
|
||||
vhdl_unaryop_expr::~vhdl_unaryop_expr()
|
||||
{
|
||||
delete operand_;
|
||||
}
|
||||
|
||||
void vhdl_unaryop_expr::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "(";
|
||||
switch (op_) {
|
||||
case VHDL_UNARYOP_NOT:
|
||||
of << "not ";
|
||||
break;
|
||||
case VHDL_UNARYOP_NEG:
|
||||
of << "-";
|
||||
break;
|
||||
}
|
||||
operand_->emit(of, level);
|
||||
of << ")";
|
||||
}
|
||||
|
||||
vhdl_binop_expr::vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op,
|
||||
vhdl_expr *right, vhdl_type *type)
|
||||
: vhdl_expr(type), op_(op)
|
||||
{
|
||||
add_expr(left);
|
||||
add_expr(right);
|
||||
}
|
||||
|
||||
vhdl_binop_expr::~vhdl_binop_expr()
|
||||
{
|
||||
delete_children<vhdl_expr>(operands_);
|
||||
}
|
||||
|
||||
void vhdl_binop_expr::add_expr(vhdl_expr *e)
|
||||
{
|
||||
operands_.push_back(e);
|
||||
}
|
||||
|
||||
void vhdl_binop_expr::emit(std::ostream &of, int level) const
|
||||
{
|
||||
// Expressions are fully parenthesized to remove any
|
||||
// ambiguity in the output
|
||||
|
||||
of << "(";
|
||||
|
||||
assert(operands_.size() > 0);
|
||||
std::list<vhdl_expr*>::const_iterator it = operands_.begin();
|
||||
|
||||
(*it)->emit(of, level);
|
||||
while (++it != operands_.end()) {
|
||||
const char* ops[] = {
|
||||
"and", "or", "=", "/=", "+", "-", "*", "<",
|
||||
">", "<=", ">=", "sll", "srl", "xor", "&",
|
||||
"nand", "nor", "xnor", "/", "mod", NULL
|
||||
};
|
||||
|
||||
of << " " << ops[op_] << " ";
|
||||
|
||||
(*it)->emit(of, level);
|
||||
}
|
||||
|
||||
of << ")";
|
||||
}
|
||||
|
||||
vhdl_bit_spec_expr::~vhdl_bit_spec_expr()
|
||||
{
|
||||
if (others_)
|
||||
delete others_;
|
||||
|
||||
std::list<bit_map>::iterator it;
|
||||
for (it = bits_.begin(); it != bits_.end(); ++it)
|
||||
delete (*it).e;
|
||||
}
|
||||
|
||||
void vhdl_bit_spec_expr::add_bit(int bit, vhdl_expr *e)
|
||||
{
|
||||
bit_map bm = { bit, e };
|
||||
bits_.push_back(bm);
|
||||
}
|
||||
|
||||
void vhdl_bit_spec_expr::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "(";
|
||||
|
||||
std::list<bit_map>::const_iterator it;
|
||||
it = bits_.begin();
|
||||
while (it != bits_.end()) {
|
||||
of << (*it).bit << " => ";
|
||||
(*it).e->emit(of, level);
|
||||
if (++it != bits_.end())
|
||||
of << ", ";
|
||||
}
|
||||
|
||||
if (others_) {
|
||||
of << (bits_.empty() ? "" : ", ") << "others => ";
|
||||
others_->emit(of, level);
|
||||
}
|
||||
|
||||
of << ")";
|
||||
}
|
||||
|
||||
vhdl_case_branch::~vhdl_case_branch()
|
||||
{
|
||||
delete when_;
|
||||
}
|
||||
|
||||
void vhdl_case_branch::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "when ";
|
||||
when_->emit(of, level);
|
||||
of << " =>";
|
||||
stmts_.emit(of, indent(level), false);
|
||||
}
|
||||
|
||||
vhdl_case_stmt::~vhdl_case_stmt()
|
||||
{
|
||||
delete test_;
|
||||
}
|
||||
|
||||
void vhdl_case_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "case ";
|
||||
test_->emit(of, level);
|
||||
of << " is";
|
||||
newline(of, indent(level));
|
||||
|
||||
case_branch_list_t::const_iterator it;
|
||||
int n = branches_.size();
|
||||
for (it = branches_.begin(); it != branches_.end(); ++it) {
|
||||
(*it)->emit(of, level);
|
||||
if (--n > 0)
|
||||
newline(of, indent(level));
|
||||
else
|
||||
newline(of, level);
|
||||
}
|
||||
|
||||
of << "end case;";
|
||||
}
|
||||
|
||||
vhdl_while_stmt::~vhdl_while_stmt()
|
||||
{
|
||||
delete test_;
|
||||
}
|
||||
|
||||
void vhdl_while_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "while ";
|
||||
test_->emit(of, level);
|
||||
of << " ";
|
||||
vhdl_loop_stmt::emit(of, level);
|
||||
}
|
||||
|
||||
void vhdl_loop_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "loop";
|
||||
stmts_.emit(of, level);
|
||||
of << "end loop;";
|
||||
}
|
||||
|
||||
vhdl_for_stmt::~vhdl_for_stmt()
|
||||
{
|
||||
delete from_;
|
||||
delete to_;
|
||||
}
|
||||
|
||||
void vhdl_for_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "for " << lname_ << " in ";
|
||||
from_->emit(of, level);
|
||||
of << " to ";
|
||||
to_->emit(of, level);
|
||||
of << " ";
|
||||
vhdl_loop_stmt::emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_function::vhdl_function(const char *name, vhdl_type *ret_type)
|
||||
: vhdl_decl(name, ret_type)
|
||||
{
|
||||
// A function contains two scopes:
|
||||
// scope_ = The paramters
|
||||
// variables_ = Local variables
|
||||
// A call to get_scope returns variables_ whose parent is scope_
|
||||
variables_.set_parent(&scope_);
|
||||
}
|
||||
|
||||
void vhdl_function::emit(std::ostream &of, int level) const
|
||||
{
|
||||
newline(of, level);
|
||||
emit_comment(of, level);
|
||||
of << "function " << name_ << " (";
|
||||
emit_children<vhdl_decl>(of, scope_.get_decls(), level, ";");
|
||||
of << ") ";
|
||||
newline(of, level);
|
||||
of << "return " << type_->get_string() << " is";
|
||||
emit_children<vhdl_decl>(of, variables_.get_decls(), level);
|
||||
of << "begin";
|
||||
stmts_.emit(of, level);
|
||||
of << " return " << name_ << "_Result;";
|
||||
newline(of, level);
|
||||
of << "end function;";
|
||||
}
|
||||
|
||||
void vhdl_forward_fdecl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "function " << f_->get_name() << " (";
|
||||
emit_children<vhdl_decl>(of, f_->scope_.get_decls(), level, ";");
|
||||
of << ") ";
|
||||
newline(of, level);
|
||||
of << "return " << f_->type_->get_string() << ";";
|
||||
newline(of, level);
|
||||
}
|
||||
|
||||
void vhdl_param_decl::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << name_ << " : ";
|
||||
type_->emit(of, level);
|
||||
}
|
||||
|
||||
vhdl_with_select_stmt::~vhdl_with_select_stmt()
|
||||
{
|
||||
delete test_;
|
||||
delete out_;
|
||||
|
||||
for (when_list_t::const_iterator it = whens_.begin();
|
||||
it != whens_.end();
|
||||
++it) {
|
||||
delete (*it).value;
|
||||
delete (*it).cond;
|
||||
if ((*it).delay)
|
||||
delete (*it).delay;
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_with_select_stmt::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << "with ";
|
||||
test_->emit(of, level);
|
||||
of << " select";
|
||||
emit_comment(of, level, true);
|
||||
newline(of, indent(level));
|
||||
|
||||
out_->emit(of, level);
|
||||
of << " <= ";
|
||||
|
||||
when_list_t::const_iterator it = whens_.begin();
|
||||
while (it != whens_.end()) {
|
||||
(*it).value->emit(of, level);
|
||||
if ((*it).delay) {
|
||||
of << " after ";
|
||||
(*it).delay->emit(of, level);
|
||||
}
|
||||
of << " when ";
|
||||
(*it).cond->emit(of, level);
|
||||
|
||||
if (++it != whens_.end()) {
|
||||
of << ",";
|
||||
newline(of, indent(level));
|
||||
}
|
||||
else
|
||||
of << ";";
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_with_select_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay)
|
||||
{
|
||||
when_part_t when = { value, cond, delay };
|
||||
whens_.push_back(when);
|
||||
}
|
||||
|
|
@ -0,0 +1,790 @@
|
|||
/*
|
||||
* VHDL abstract syntax elements.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef INC_VHDL_SYNTAX_HH
|
||||
#define INC_VHDL_SYNTAX_HH
|
||||
|
||||
#include "vhdl_element.hh"
|
||||
#include "vhdl_type.hh"
|
||||
|
||||
class vhdl_scope;
|
||||
class vhdl_entity;
|
||||
class vhdl_arch;
|
||||
|
||||
class vhdl_expr : public vhdl_element {
|
||||
public:
|
||||
vhdl_expr(vhdl_type* type, bool isconst=false)
|
||||
: type_(type), isconst_(isconst) {}
|
||||
virtual ~vhdl_expr();
|
||||
|
||||
const vhdl_type *get_type() const { return type_; }
|
||||
bool constant() const { return isconst_; }
|
||||
|
||||
vhdl_expr *cast(const vhdl_type *to);
|
||||
virtual vhdl_expr *resize(int newwidth);
|
||||
virtual vhdl_expr *to_boolean();
|
||||
virtual vhdl_expr *to_integer();
|
||||
virtual vhdl_expr *to_std_logic();
|
||||
virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w);
|
||||
protected:
|
||||
vhdl_type *type_;
|
||||
bool isconst_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A scalar or array variable reference.
|
||||
*/
|
||||
class vhdl_var_ref : public vhdl_expr {
|
||||
public:
|
||||
vhdl_var_ref(const char *name, vhdl_type *type,
|
||||
vhdl_expr *slice = NULL)
|
||||
: vhdl_expr(type), name_(name), slice_(slice) {}
|
||||
~vhdl_var_ref();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
const std::string &get_name() const { return name_; }
|
||||
void set_name(const std::string &name) { name_ = name; }
|
||||
void set_slice(vhdl_expr *s, int w=0);
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_expr *slice_;
|
||||
unsigned slice_width_;
|
||||
};
|
||||
|
||||
|
||||
enum vhdl_binop_t {
|
||||
VHDL_BINOP_AND = 0,
|
||||
VHDL_BINOP_OR,
|
||||
VHDL_BINOP_EQ,
|
||||
VHDL_BINOP_NEQ,
|
||||
VHDL_BINOP_ADD,
|
||||
VHDL_BINOP_SUB,
|
||||
VHDL_BINOP_MULT,
|
||||
VHDL_BINOP_LT,
|
||||
VHDL_BINOP_GT,
|
||||
VHDL_BINOP_LEQ,
|
||||
VHDL_BINOP_GEQ,
|
||||
VHDL_BINOP_SL,
|
||||
VHDL_BINOP_SR,
|
||||
VHDL_BINOP_XOR,
|
||||
VHDL_BINOP_CONCAT,
|
||||
VHDL_BINOP_NAND,
|
||||
VHDL_BINOP_NOR,
|
||||
VHDL_BINOP_XNOR,
|
||||
VHDL_BINOP_DIV,
|
||||
VHDL_BINOP_MOD,
|
||||
};
|
||||
|
||||
/*
|
||||
* A binary expression contains a list of operands rather
|
||||
* than just two: this is to model n-input gates and the
|
||||
* like. A second constructor is provided to handle the
|
||||
* common case of a true binary expression.
|
||||
*/
|
||||
class vhdl_binop_expr : public vhdl_expr {
|
||||
public:
|
||||
vhdl_binop_expr(vhdl_binop_t op, vhdl_type *type)
|
||||
: vhdl_expr(type), op_(op) {}
|
||||
vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op,
|
||||
vhdl_expr *right, vhdl_type *type);
|
||||
~vhdl_binop_expr();
|
||||
|
||||
void add_expr(vhdl_expr *e);
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::list<vhdl_expr*> operands_;
|
||||
vhdl_binop_t op_;
|
||||
};
|
||||
|
||||
|
||||
enum vhdl_unaryop_t {
|
||||
VHDL_UNARYOP_NOT,
|
||||
VHDL_UNARYOP_NEG,
|
||||
};
|
||||
|
||||
class vhdl_unaryop_expr : public vhdl_expr {
|
||||
public:
|
||||
vhdl_unaryop_expr(vhdl_unaryop_t op, vhdl_expr *operand,
|
||||
vhdl_type *type)
|
||||
: vhdl_expr(type), op_(op), operand_(operand) {}
|
||||
~vhdl_unaryop_expr();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_unaryop_t op_;
|
||||
vhdl_expr *operand_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An expression like (0 => '1', 2 => '0', others => 'Z')
|
||||
*/
|
||||
class vhdl_bit_spec_expr : public vhdl_expr {
|
||||
public:
|
||||
vhdl_bit_spec_expr(vhdl_type *type, vhdl_expr *others)
|
||||
: vhdl_expr(type), others_(others) {}
|
||||
~vhdl_bit_spec_expr();
|
||||
|
||||
void add_bit(int bit, vhdl_expr *e);
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *others_;
|
||||
struct bit_map {
|
||||
int bit;
|
||||
vhdl_expr *e;
|
||||
};
|
||||
std::list<bit_map> bits_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_const_string : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_string(const char *value)
|
||||
: vhdl_expr(vhdl_type::string(), true), value_(value) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
class vhdl_const_bits : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_bits(const char *value, int width, bool issigned);
|
||||
void emit(std::ostream &of, int level) const;
|
||||
const std::string &get_value() const { return value_; }
|
||||
vhdl_expr *to_integer();
|
||||
vhdl_expr *to_std_logic();
|
||||
vhdl_expr *to_vector(vhdl_type_name_t name, int w);
|
||||
private:
|
||||
int bits_to_int() const;
|
||||
|
||||
std::string value_;
|
||||
bool qualified_, signed_;
|
||||
};
|
||||
|
||||
class vhdl_const_bit : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_bit(char bit)
|
||||
: vhdl_expr(vhdl_type::std_logic(), true), bit_(bit) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
vhdl_expr *to_boolean();
|
||||
vhdl_expr *to_integer();
|
||||
private:
|
||||
char bit_;
|
||||
};
|
||||
|
||||
enum time_unit_t {
|
||||
TIME_UNIT_NS,
|
||||
};
|
||||
|
||||
class vhdl_const_time : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_time(int64_t value, time_unit_t units = TIME_UNIT_NS)
|
||||
: vhdl_expr(vhdl_type::time(), true), value_(value), units_(units) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
int64_t value_;
|
||||
time_unit_t units_;
|
||||
};
|
||||
|
||||
class vhdl_const_int : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_int(int64_t value)
|
||||
: vhdl_expr(vhdl_type::integer(), true), value_(value) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
vhdl_expr *to_vector(vhdl_type_name_t name, int w);
|
||||
private:
|
||||
int64_t value_;
|
||||
};
|
||||
|
||||
class vhdl_const_bool : public vhdl_expr {
|
||||
public:
|
||||
vhdl_const_bool(bool value)
|
||||
: vhdl_expr(vhdl_type::boolean(), true), value_(value) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
bool value_;
|
||||
};
|
||||
|
||||
class vhdl_expr_list : public vhdl_element {
|
||||
public:
|
||||
~vhdl_expr_list();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
bool empty() const { return exprs_.empty(); }
|
||||
void add_expr(vhdl_expr *e);
|
||||
private:
|
||||
std::list<vhdl_expr*> exprs_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A function call within an expression.
|
||||
*/
|
||||
class vhdl_fcall : public vhdl_expr {
|
||||
public:
|
||||
vhdl_fcall(const char *name, vhdl_type *rtype)
|
||||
: vhdl_expr(rtype), name_(name) {};
|
||||
~vhdl_fcall() {}
|
||||
|
||||
void add_expr(vhdl_expr *e) { exprs_.add_expr(e); }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_expr_list exprs_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A concurrent statement appears in architecture bodies/
|
||||
*/
|
||||
class vhdl_conc_stmt : public vhdl_element {
|
||||
public:
|
||||
virtual ~vhdl_conc_stmt() {}
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_conc_stmt*> conc_stmt_list_t;
|
||||
|
||||
/*
|
||||
* A '<value> when <cond>' clause that appears in several
|
||||
* statement types.
|
||||
*/
|
||||
struct when_part_t {
|
||||
vhdl_expr *value, *cond, *delay;
|
||||
};
|
||||
typedef std::list<when_part_t> when_list_t;
|
||||
|
||||
|
||||
/*
|
||||
* A concurrent signal assignment (i.e. not part of a process).
|
||||
* Can have any number of `when' clauses, in which case the original
|
||||
* rhs becomes the `else' part.
|
||||
*/
|
||||
class vhdl_cassign_stmt : public vhdl_conc_stmt {
|
||||
public:
|
||||
vhdl_cassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: lhs_(lhs), rhs_(rhs), after_(NULL) {}
|
||||
~vhdl_cassign_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_condition(vhdl_expr *value, vhdl_expr *cond);
|
||||
void set_after(vhdl_expr *a) { after_ = a; }
|
||||
private:
|
||||
vhdl_var_ref *lhs_;
|
||||
vhdl_expr *rhs_;
|
||||
vhdl_expr *after_;
|
||||
when_list_t whens_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_with_select_stmt : public vhdl_conc_stmt {
|
||||
public:
|
||||
vhdl_with_select_stmt(vhdl_expr *test, vhdl_var_ref *out)
|
||||
: test_(test), out_(out) {}
|
||||
~vhdl_with_select_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay=NULL);
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
vhdl_var_ref *out_;
|
||||
when_list_t whens_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Any sequential statement in a process.
|
||||
*/
|
||||
class vhdl_seq_stmt : public vhdl_element {
|
||||
public:
|
||||
virtual ~vhdl_seq_stmt() {}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A list of sequential statements. For example inside a
|
||||
* process, loop, or if statement.
|
||||
*/
|
||||
class stmt_container {
|
||||
public:
|
||||
~stmt_container();
|
||||
|
||||
void add_stmt(vhdl_seq_stmt *stmt);
|
||||
void emit(std::ostream &of, int level, bool newline=true) const;
|
||||
bool empty() const { return stmts_.empty(); }
|
||||
private:
|
||||
std::list<vhdl_seq_stmt*> stmts_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Shared between blocking and non-blocking assignment.
|
||||
*/
|
||||
class vhdl_abstract_assign_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_abstract_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: lhs_(lhs), rhs_(rhs), after_(NULL) {}
|
||||
virtual ~vhdl_abstract_assign_stmt();
|
||||
|
||||
void set_after(vhdl_expr *after) { after_ = after; }
|
||||
protected:
|
||||
vhdl_var_ref *lhs_;
|
||||
vhdl_expr *rhs_, *after_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Similar to Verilog non-blocking assignment, except the LHS
|
||||
* must be a signal not a variable.
|
||||
*/
|
||||
class vhdl_nbassign_stmt : public vhdl_abstract_assign_stmt {
|
||||
public:
|
||||
vhdl_nbassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: vhdl_abstract_assign_stmt(lhs, rhs) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_assign_stmt : public vhdl_abstract_assign_stmt {
|
||||
public:
|
||||
vhdl_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
|
||||
: vhdl_abstract_assign_stmt(lhs, rhs) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
enum vhdl_wait_type_t {
|
||||
VHDL_WAIT_INDEF, // Suspend indefinitely
|
||||
VHDL_WAIT_FOR, // Wait for a constant amount of time
|
||||
VHDL_WAIT_UNTIL, // Wait on an expression
|
||||
VHDL_WAIT_ON, // Wait on a sensitivity list
|
||||
};
|
||||
|
||||
/*
|
||||
* Delay simulation indefinitely, until an event, or for a
|
||||
* specified time.
|
||||
*/
|
||||
class vhdl_wait_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_wait_stmt(vhdl_wait_type_t type = VHDL_WAIT_INDEF,
|
||||
vhdl_expr *expr = NULL)
|
||||
: type_(type), expr_(expr) {}
|
||||
~vhdl_wait_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_sensitivity(const std::string &s) { sensitivity_.push_back(s); }
|
||||
private:
|
||||
vhdl_wait_type_t type_;
|
||||
vhdl_expr *expr_;
|
||||
string_list_t sensitivity_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_null_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_assert_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_assert_stmt(const char *reason)
|
||||
: reason_(reason) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_if_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_if_stmt(vhdl_expr *test);
|
||||
~vhdl_if_stmt();
|
||||
|
||||
stmt_container *get_then_container() { return &then_part_; }
|
||||
stmt_container *get_else_container() { return &else_part_; }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
stmt_container then_part_, else_part_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A single branch in a case statement consisting of an
|
||||
* expression part and a statement container.
|
||||
*/
|
||||
class vhdl_case_branch : public vhdl_element {
|
||||
public:
|
||||
vhdl_case_branch(vhdl_expr *when) : when_(when) {}
|
||||
~vhdl_case_branch();
|
||||
|
||||
stmt_container *get_container() { return &stmts_; }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *when_;
|
||||
stmt_container stmts_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_case_branch*> case_branch_list_t;
|
||||
|
||||
class vhdl_case_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_case_stmt(vhdl_expr *test) : test_(test) {}
|
||||
~vhdl_case_stmt();
|
||||
|
||||
void add_branch(vhdl_case_branch *b) { branches_.push_back(b); }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
case_branch_list_t branches_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_loop_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
virtual ~vhdl_loop_stmt() {}
|
||||
|
||||
stmt_container *get_container() { return &stmts_; }
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
stmt_container stmts_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_while_stmt : public vhdl_loop_stmt {
|
||||
public:
|
||||
vhdl_while_stmt(vhdl_expr *test) : test_(test) {}
|
||||
~vhdl_while_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_expr *test_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_for_stmt : public vhdl_loop_stmt {
|
||||
public:
|
||||
vhdl_for_stmt(const char *lname, vhdl_expr *from, vhdl_expr *to)
|
||||
: lname_(lname), from_(from), to_(to) {}
|
||||
~vhdl_for_stmt();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
const char *lname_;
|
||||
vhdl_expr *from_, *to_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A procedure call. Which is a statement, unlike a function
|
||||
* call which is an expression.
|
||||
*/
|
||||
class vhdl_pcall_stmt : public vhdl_seq_stmt {
|
||||
public:
|
||||
vhdl_pcall_stmt(const char *name) : name_(name) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_expr(vhdl_expr *e) { exprs_.add_expr(e); }
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_expr_list exprs_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A declaration of some sort (variable, component, etc.).
|
||||
* Declarations have names, which is the identifier of the variable,
|
||||
* constant, etc. not the type.
|
||||
*/
|
||||
class vhdl_decl : public vhdl_element {
|
||||
public:
|
||||
vhdl_decl(const char *name, vhdl_type *type = NULL,
|
||||
vhdl_expr *initial = NULL)
|
||||
: name_(name), type_(type), initial_(initial),
|
||||
has_initial_(initial != NULL) {}
|
||||
virtual ~vhdl_decl();
|
||||
|
||||
const std::string &get_name() const { return name_; }
|
||||
const vhdl_type *get_type() const;
|
||||
void set_type(vhdl_type *t) { type_ = t; }
|
||||
void set_initial(vhdl_expr *initial);
|
||||
bool has_initial() const { return has_initial_; }
|
||||
protected:
|
||||
std::string name_;
|
||||
vhdl_type *type_;
|
||||
vhdl_expr *initial_;
|
||||
bool has_initial_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_decl*> decl_list_t;
|
||||
|
||||
|
||||
/*
|
||||
* A forward declaration of a component. At the moment it is assumed
|
||||
* that components declarations will only ever be for entities
|
||||
* generated by this code generator. This is enforced by making the
|
||||
* constructor private (use component_decl_for instead).
|
||||
*/
|
||||
class vhdl_component_decl : public vhdl_decl {
|
||||
public:
|
||||
static vhdl_component_decl *component_decl_for(vhdl_entity *ent);
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
vhdl_component_decl(const char *name);
|
||||
|
||||
decl_list_t ports_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_type_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_type_decl(const char *name, vhdl_type *base)
|
||||
: vhdl_decl(name, base) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A variable declaration inside a process (although this isn't
|
||||
* enforced here).
|
||||
*/
|
||||
class vhdl_var_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_var_decl(const char *name, vhdl_type *type)
|
||||
: vhdl_decl(name, type) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A signal declaration in architecture.
|
||||
*/
|
||||
class vhdl_signal_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_signal_decl(const char *name, vhdl_type *type)
|
||||
: vhdl_decl(name, type) {}
|
||||
virtual void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A parameter to a function.
|
||||
*/
|
||||
class vhdl_param_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_param_decl(const char *name, vhdl_type *type)
|
||||
: vhdl_decl(name, type) {}
|
||||
void emit(std::ostream &of, int level) const;
|
||||
};
|
||||
|
||||
enum vhdl_port_mode_t {
|
||||
VHDL_PORT_IN,
|
||||
VHDL_PORT_OUT,
|
||||
VHDL_PORT_INOUT,
|
||||
VHDL_PORT_BUFFER,
|
||||
};
|
||||
|
||||
/*
|
||||
* A port declaration is like a signal declaration except
|
||||
* it has a direction and appears in the entity rather than
|
||||
* the architecture.
|
||||
*/
|
||||
class vhdl_port_decl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_port_decl(const char *name, vhdl_type *type,
|
||||
vhdl_port_mode_t mode)
|
||||
: vhdl_decl(name, type), mode_(mode) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
vhdl_port_mode_t get_mode() const { return mode_; }
|
||||
void set_mode(vhdl_port_mode_t m) { mode_ = m; }
|
||||
private:
|
||||
vhdl_port_mode_t mode_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A mapping from port name to an expression.
|
||||
*/
|
||||
struct port_map_t {
|
||||
std::string name;
|
||||
vhdl_expr *expr;
|
||||
};
|
||||
|
||||
typedef std::list<port_map_t> port_map_list_t;
|
||||
|
||||
/*
|
||||
* Instantiation of component. This is really only a placeholder
|
||||
* at the moment until the port mappings are worked out.
|
||||
*/
|
||||
class vhdl_comp_inst : public vhdl_conc_stmt {
|
||||
public:
|
||||
vhdl_comp_inst(const char *inst_name, const char *comp_name);
|
||||
~vhdl_comp_inst();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void map_port(const char *name, vhdl_expr *expr);
|
||||
|
||||
const std::string &get_comp_name() const { return comp_name_; }
|
||||
const std::string &get_inst_name() const { return inst_name_; }
|
||||
private:
|
||||
std::string comp_name_, inst_name_;
|
||||
port_map_list_t mapping_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Contains a list of declarations in a hierarchy.
|
||||
* A scope can be `initializing' where assignments automatically
|
||||
* create initial values for declarations.
|
||||
*/
|
||||
class vhdl_scope {
|
||||
public:
|
||||
vhdl_scope();
|
||||
~vhdl_scope();
|
||||
|
||||
void add_decl(vhdl_decl *decl);
|
||||
void add_forward_decl(vhdl_decl *decl);
|
||||
vhdl_decl *get_decl(const std::string &name) const;
|
||||
bool have_declared(const std::string &name) const;
|
||||
bool contained_within(const vhdl_scope *other) const;
|
||||
vhdl_scope *get_parent() const;
|
||||
|
||||
bool empty() const { return decls_.empty(); }
|
||||
const decl_list_t &get_decls() const { return decls_; }
|
||||
void set_parent(vhdl_scope *p) { parent_ = p; }
|
||||
|
||||
bool initializing() const { return init_; }
|
||||
void set_initializing(bool i);
|
||||
|
||||
void set_allow_signal_assignment(bool b) { sig_assign_ = b; }
|
||||
bool allow_signal_assignment() const { return sig_assign_; }
|
||||
private:
|
||||
decl_list_t decls_;
|
||||
vhdl_scope *parent_;
|
||||
bool init_, sig_assign_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Any sort of procedural element: process, function, or
|
||||
* procedure. Roughly these map onto Verilog's processes,
|
||||
* functions, and tasks.
|
||||
*/
|
||||
class vhdl_procedural {
|
||||
public:
|
||||
virtual ~vhdl_procedural() {}
|
||||
|
||||
virtual stmt_container *get_container() { return &stmts_; }
|
||||
virtual vhdl_scope *get_scope() { return &scope_; }
|
||||
protected:
|
||||
stmt_container stmts_;
|
||||
vhdl_scope scope_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_function : public vhdl_decl, public vhdl_procedural {
|
||||
friend class vhdl_forward_fdecl;
|
||||
public:
|
||||
vhdl_function(const char *name, vhdl_type *ret_type);
|
||||
|
||||
virtual void emit(std::ostream &of, int level) const;
|
||||
vhdl_scope *get_scope() { return &variables_; }
|
||||
void add_param(vhdl_param_decl *p) { scope_.add_decl(p); }
|
||||
private:
|
||||
vhdl_scope variables_;
|
||||
};
|
||||
|
||||
class vhdl_forward_fdecl : public vhdl_decl {
|
||||
public:
|
||||
vhdl_forward_fdecl(const vhdl_function *f)
|
||||
: vhdl_decl((f->get_name() + "_Forward").c_str()), f_(f) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
private:
|
||||
const vhdl_function *f_;
|
||||
};
|
||||
|
||||
|
||||
class vhdl_process : public vhdl_conc_stmt, public vhdl_procedural {
|
||||
public:
|
||||
vhdl_process(const char *name = "") : name_(name) {}
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
void add_sensitivity(const std::string &name);
|
||||
private:
|
||||
std::string name_;
|
||||
string_list_t sens_;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* An architecture which implements an entity.
|
||||
*/
|
||||
class vhdl_arch : public vhdl_element {
|
||||
public:
|
||||
vhdl_arch(const char *entity, const char *name)
|
||||
: name_(name), entity_(entity) {}
|
||||
virtual ~vhdl_arch();
|
||||
|
||||
void emit(std::ostream &of, int level=0) const;
|
||||
void add_stmt(vhdl_process *proc);
|
||||
void add_stmt(vhdl_conc_stmt *stmt);
|
||||
vhdl_scope *get_scope() { return &scope_; }
|
||||
private:
|
||||
conc_stmt_list_t stmts_;
|
||||
vhdl_scope scope_;
|
||||
std::string name_, entity_;
|
||||
};
|
||||
|
||||
/*
|
||||
* An entity defines the ports, parameters, etc. of a module. Each
|
||||
* entity is associated with a single architecture (although
|
||||
* technically this need not be the case). Entities are `derived'
|
||||
* from instantiations of Verilog module scopes in the hierarchy.
|
||||
*/
|
||||
class vhdl_entity : public vhdl_element {
|
||||
public:
|
||||
vhdl_entity(const char *name, const char *derived_from,
|
||||
vhdl_arch *arch);
|
||||
virtual ~vhdl_entity();
|
||||
|
||||
void emit(std::ostream &of, int level=0) const;
|
||||
void add_port(vhdl_port_decl *decl);
|
||||
vhdl_arch *get_arch() const { return arch_; }
|
||||
const std::string &get_name() const { return name_; }
|
||||
const std::string &get_derived_from() const { return derived_from_; }
|
||||
|
||||
vhdl_scope *get_scope() { return &ports_; }
|
||||
private:
|
||||
std::string name_;
|
||||
vhdl_arch *arch_; // Entity may only have a single architecture
|
||||
std::string derived_from_;
|
||||
vhdl_scope ports_;
|
||||
};
|
||||
|
||||
typedef std::list<vhdl_entity*> entity_list_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef INC_VHDL_TARGET_H
|
||||
#define INC_VHDL_TARGET_H
|
||||
|
||||
#include "vhdl_config.h"
|
||||
#include "ivl_target.h"
|
||||
|
||||
#include "vhdl_syntax.hh"
|
||||
#include "vhdl_type.hh"
|
||||
|
||||
#include "support.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void error(const char *fmt, ...);
|
||||
|
||||
int draw_scope(ivl_scope_t scope, void *_parent);
|
||||
int draw_process(ivl_process_t net, void *cd);
|
||||
int draw_stmt(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool is_last = false);
|
||||
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm);
|
||||
void draw_logic(vhdl_arch *arch, ivl_net_logic_t log);
|
||||
|
||||
vhdl_expr *translate_expr(ivl_expr_t e);
|
||||
vhdl_expr *translate_time_expr(ivl_expr_t e);
|
||||
|
||||
void remember_entity(vhdl_entity *ent);
|
||||
vhdl_entity *find_entity(const string &sname);
|
||||
|
||||
ivl_design_t get_vhdl_design();
|
||||
vhdl_entity *get_active_entity();
|
||||
void set_active_entity(vhdl_entity *ent);
|
||||
|
||||
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus);
|
||||
|
||||
bool seen_signal_before(ivl_signal_t sig);
|
||||
void remember_signal(ivl_signal_t sig, vhdl_scope *scope);
|
||||
void rename_signal(ivl_signal_t sig, const string &renamed);
|
||||
vhdl_scope *find_scope_for_signal(ivl_signal_t sig);
|
||||
const string &get_renamed_signal(ivl_signal_t sig);
|
||||
ivl_signal_t find_signal_named(const string &name, const vhdl_scope *scope);
|
||||
|
||||
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
|
||||
ivl_statement_t stmt, bool newline = true);
|
||||
|
||||
void require_support_function(support_function_t f);
|
||||
|
||||
#endif /* #ifndef INC_VHDL_TARGET_H */
|
||||
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* VHDL variable and signal types.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vhdl_type.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
vhdl_type *vhdl_type::std_logic()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_STD_LOGIC);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::string()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_STRING);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::line()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_LINE);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::boolean()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::integer()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_INTEGER);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::nunsigned(int width, int lsb)
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_UNSIGNED, width-1+lsb, lsb);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::nsigned(int width, int lsb)
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_SIGNED, width-1+lsb, lsb);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::time()
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_TIME);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::get_base() const
|
||||
{
|
||||
assert(name_ == VHDL_TYPE_ARRAY);
|
||||
return base_;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is just the name of the type, without any parameters.
|
||||
*/
|
||||
std::string vhdl_type::get_string() const
|
||||
{
|
||||
switch (name_) {
|
||||
case VHDL_TYPE_STD_LOGIC:
|
||||
return std::string("std_logic");
|
||||
case VHDL_TYPE_STD_LOGIC_VECTOR:
|
||||
return std::string("std_logic_vector");
|
||||
case VHDL_TYPE_STRING:
|
||||
return std::string("String");
|
||||
case VHDL_TYPE_LINE:
|
||||
return std::string("Line");
|
||||
case VHDL_TYPE_FILE:
|
||||
return std::string("File");
|
||||
case VHDL_TYPE_INTEGER:
|
||||
return std::string("Integer");
|
||||
case VHDL_TYPE_BOOLEAN:
|
||||
return std::string("Boolean");
|
||||
case VHDL_TYPE_SIGNED:
|
||||
return std::string("signed");
|
||||
case VHDL_TYPE_UNSIGNED:
|
||||
return std::string("unsigned");
|
||||
case VHDL_TYPE_ARRAY:
|
||||
// Each array has its own type declaration
|
||||
return array_name_;
|
||||
default:
|
||||
return std::string("BadType");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The is the qualified name of the type.
|
||||
*/
|
||||
std::string vhdl_type::get_decl_string() const
|
||||
{
|
||||
switch (name_) {
|
||||
case VHDL_TYPE_STD_LOGIC_VECTOR:
|
||||
case VHDL_TYPE_UNSIGNED:
|
||||
case VHDL_TYPE_SIGNED:
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << get_string() << "(" << msb_;
|
||||
ss << " downto " << lsb_ << ")";
|
||||
return ss.str();
|
||||
}
|
||||
default:
|
||||
return get_string();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Like get_decl_string but completely expands array declarations.
|
||||
*/
|
||||
std::string vhdl_type::get_type_decl_string() const
|
||||
{
|
||||
switch (name_) {
|
||||
case VHDL_TYPE_ARRAY:
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "array (" << msb_ << " downto "
|
||||
<< lsb_ << ") of "
|
||||
<< base_->get_decl_string();
|
||||
return ss.str();
|
||||
}
|
||||
default:
|
||||
return get_decl_string();
|
||||
}
|
||||
}
|
||||
|
||||
void vhdl_type::emit(std::ostream &of, int level) const
|
||||
{
|
||||
of << get_decl_string();
|
||||
}
|
||||
|
||||
vhdl_type::vhdl_type(const vhdl_type &other)
|
||||
: name_(other.name_), msb_(other.msb_), lsb_(other.lsb_),
|
||||
array_name_(other.array_name_)
|
||||
{
|
||||
if (other.base_ != NULL)
|
||||
base_ = new vhdl_type(*other.base_);
|
||||
else
|
||||
base_ = NULL;
|
||||
}
|
||||
|
||||
vhdl_type::~vhdl_type()
|
||||
{
|
||||
if (base_ != NULL)
|
||||
delete base_;
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::std_logic_vector(int msb, int lsb)
|
||||
{
|
||||
return new vhdl_type(VHDL_TYPE_STD_LOGIC_VECTOR, msb, lsb);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::type_for(int width, bool issigned, int lsb)
|
||||
{
|
||||
if (width == 1)
|
||||
return vhdl_type::std_logic();
|
||||
else if (issigned)
|
||||
return vhdl_type::nsigned(width, lsb);
|
||||
else
|
||||
return vhdl_type::nunsigned(width, lsb);
|
||||
}
|
||||
|
||||
vhdl_type *vhdl_type::array_of(vhdl_type *b, std::string &n, int m, int l)
|
||||
{
|
||||
return new vhdl_type(b, n, m, l);
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* VHDL variable and signal types.
|
||||
*
|
||||
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef INC_VHDL_TYPE_HH
|
||||
#define INC_VHDL_TYPE_HH
|
||||
|
||||
#include "vhdl_element.hh"
|
||||
|
||||
enum vhdl_type_name_t {
|
||||
VHDL_TYPE_STD_LOGIC,
|
||||
VHDL_TYPE_STD_LOGIC_VECTOR,
|
||||
VHDL_TYPE_STRING,
|
||||
VHDL_TYPE_LINE,
|
||||
VHDL_TYPE_FILE,
|
||||
VHDL_TYPE_INTEGER,
|
||||
VHDL_TYPE_BOOLEAN,
|
||||
VHDL_TYPE_SIGNED,
|
||||
VHDL_TYPE_UNSIGNED,
|
||||
VHDL_TYPE_TIME,
|
||||
VHDL_TYPE_ARRAY,
|
||||
};
|
||||
|
||||
/*
|
||||
* A type at the moment is just a name. It shouldn't get
|
||||
* too much more complex, as Verilog's type system is much
|
||||
* simpler than VHDL's.
|
||||
*/
|
||||
class vhdl_type : public vhdl_element {
|
||||
public:
|
||||
// Scalar constructor
|
||||
vhdl_type(vhdl_type_name_t name, int msb = 0, int lsb = 0)
|
||||
: name_(name), msb_(msb), lsb_(lsb), base_(NULL) {}
|
||||
|
||||
// Array constructor
|
||||
vhdl_type(vhdl_type *base, const std::string &array_name,
|
||||
int msb, int lsb)
|
||||
: name_(VHDL_TYPE_ARRAY), msb_(msb), lsb_(lsb), base_(base),
|
||||
array_name_(array_name) {}
|
||||
|
||||
// Copy constructor
|
||||
vhdl_type(const vhdl_type &other);
|
||||
|
||||
virtual ~vhdl_type();
|
||||
|
||||
void emit(std::ostream &of, int level) const;
|
||||
vhdl_type_name_t get_name() const { return name_; }
|
||||
std::string get_string() const;
|
||||
std::string get_decl_string() const;
|
||||
std::string get_type_decl_string() const;
|
||||
vhdl_type *get_base() const;
|
||||
int get_width() const { return msb_ - lsb_ + 1; }
|
||||
int get_msb() const { return msb_; }
|
||||
int get_lsb() const { return lsb_; }
|
||||
|
||||
// Common types
|
||||
static vhdl_type *std_logic();
|
||||
static vhdl_type *string();
|
||||
static vhdl_type *line();
|
||||
static vhdl_type *std_logic_vector(int msb, int lsb);
|
||||
static vhdl_type *nunsigned(int width, int lsb=0);
|
||||
static vhdl_type *nsigned(int width, int lsb=0);
|
||||
static vhdl_type *integer();
|
||||
static vhdl_type *boolean();
|
||||
static vhdl_type *time();
|
||||
|
||||
static vhdl_type *type_for(int width, bool issigned, int lsb=0);
|
||||
static vhdl_type *array_of(vhdl_type *b, std::string &n, int m, int l);
|
||||
protected:
|
||||
vhdl_type_name_t name_;
|
||||
int msb_, lsb_;
|
||||
vhdl_type *base_; // Array base type for VHDL_TYPE_ARRAY
|
||||
std::string array_name_; // Type name for the array `type array_name_ is ...'
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
void finish(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -29,6 +29,13 @@
|
|||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
struct args_info {
|
||||
char*text;
|
||||
int vec_flag; /* True if the vec must be released. */
|
||||
struct vector_info vec;
|
||||
struct args_info *child; /* Arguments can be nested. */
|
||||
};
|
||||
|
||||
static const char* magic_sfuncs[] = {
|
||||
"$time",
|
||||
"$stime",
|
||||
|
|
@ -68,6 +75,153 @@ static int is_fixed_memory_word(ivl_expr_t net)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_vpi_taskfunc_signal_arg(struct args_info *result,
|
||||
ivl_expr_t expr)
|
||||
{
|
||||
char buffer[4096];
|
||||
|
||||
switch (ivl_expr_type(expr)) {
|
||||
case IVL_EX_SIGNAL:
|
||||
/* If the signal node is narrower then the signal itself,
|
||||
then this is a part select so I'm going to need to
|
||||
evaluate the expression.
|
||||
|
||||
Also, if the signedness of the expression is different
|
||||
from the signedness of the signal. This could be
|
||||
caused by a $signed or $unsigned system function.
|
||||
|
||||
If I don't need to do any evaluating, then skip it as
|
||||
I'll be passing the handle to the signal itself. */
|
||||
if (ivl_expr_width(expr) !=
|
||||
ivl_signal_width(ivl_expr_signal(expr))) {
|
||||
/* This should never happen since we have IVL_EX_SELECT. */
|
||||
return 0;
|
||||
|
||||
} else if (ivl_expr_signed(expr) !=
|
||||
ivl_signal_signed(ivl_expr_signal(expr))) {
|
||||
return 0;
|
||||
} else if (is_fixed_memory_word(expr)) {
|
||||
/* This is a word of a non-array, or a word of a net
|
||||
array, so we can address the word directly. */
|
||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||
unsigned use_word = 0;
|
||||
ivl_expr_t word_ex = ivl_expr_oper1(expr);
|
||||
if (word_ex) {
|
||||
/* Some array select have been evaluated. */
|
||||
if (number_is_immediate(word_ex,
|
||||
8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
}
|
||||
if (word_ex) return 0;
|
||||
|
||||
assert(word_ex == 0);
|
||||
snprintf(buffer, sizeof buffer, "v%p_%u", sig, use_word);
|
||||
result->text = strdup(buffer);
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
/* What's left, this is the work of a var array.
|
||||
Create the right code to handle it. */
|
||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||
unsigned use_word = 0;
|
||||
ivl_expr_t word_ex = ivl_expr_oper1(expr);
|
||||
if (word_ex) {
|
||||
/* Some array select have been evaluated. */
|
||||
if (number_is_immediate(word_ex,
|
||||
8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
}
|
||||
if (word_ex && (ivl_expr_type(word_ex)==IVL_EX_SIGNAL ||
|
||||
ivl_expr_type(word_ex)==IVL_EX_SELECT)) {
|
||||
/* Special case: the index is a signal/select. */
|
||||
result->child = calloc(1, sizeof(struct args_info));
|
||||
if (get_vpi_taskfunc_signal_arg(result->child,
|
||||
word_ex)) {
|
||||
snprintf(buffer, sizeof buffer, "&A<v%p, %s >",
|
||||
sig, result->child->text);
|
||||
free(result->child->text);
|
||||
} else {
|
||||
free(result->child);
|
||||
result->child = NULL;
|
||||
return 0;
|
||||
}
|
||||
} else if (word_ex) {
|
||||
/* Fallback case: evaluate expression. */
|
||||
struct vector_info av;
|
||||
av = draw_eval_expr(word_ex, STUFF_OK_XZ);
|
||||
snprintf(buffer, sizeof buffer, "&A<v%p, %u %u>",
|
||||
sig, av.base, av.wid);
|
||||
result->vec = av;
|
||||
result->vec_flag = 1;
|
||||
} else {
|
||||
snprintf(buffer, sizeof buffer, "&A<v%p, %u>",
|
||||
sig, use_word);
|
||||
}
|
||||
result->text = strdup(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case IVL_EX_SELECT: {
|
||||
ivl_expr_t vexpr = ivl_expr_oper1(expr);
|
||||
assert(vexpr);
|
||||
|
||||
/* This code is only for signals or selects. */
|
||||
if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL &&
|
||||
ivl_expr_type(vexpr) != IVL_EX_SELECT) return 0;
|
||||
|
||||
/* The signal is part of an array. */
|
||||
/* Add &APV<> code here when it is finished. */
|
||||
if (ivl_expr_oper1(vexpr)) return 0;
|
||||
|
||||
ivl_expr_t bexpr = ivl_expr_oper2(expr);
|
||||
assert(bexpr);
|
||||
|
||||
/* This is a constant bit/part select. */
|
||||
if (number_is_immediate(bexpr, 64, 1)) {
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %ld, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
get_number_immediate(bexpr),
|
||||
ivl_expr_width(expr));
|
||||
/* This is an indexed bit/part select. */
|
||||
} else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL ||
|
||||
ivl_expr_type(bexpr) == IVL_EX_SELECT) {
|
||||
/* Special case: the base is a signal/select. */
|
||||
result->child = calloc(1, sizeof(struct args_info));
|
||||
if (get_vpi_taskfunc_signal_arg(result->child, bexpr)) {
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %s, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
result->child->text,
|
||||
ivl_expr_width(expr));
|
||||
free(result->child->text);
|
||||
} else {
|
||||
free(result->child);
|
||||
result->child = NULL;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Fallback case: evaluate the expression. */
|
||||
struct vector_info rv;
|
||||
rv = draw_eval_expr(bexpr, STUFF_OK_XZ);
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %u %u, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
rv.base, rv.wid,
|
||||
ivl_expr_width(expr));
|
||||
result->vec = rv;
|
||||
result->vec_flag = 1;
|
||||
}
|
||||
result->text = strdup(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_vpi_taskfunc_args(const char*call_string,
|
||||
ivl_statement_t tnet,
|
||||
ivl_expr_t fnet)
|
||||
|
|
@ -77,11 +231,7 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
|||
? ivl_stmt_parm_count(tnet)
|
||||
: ivl_expr_parms(fnet);
|
||||
|
||||
struct args_info {
|
||||
char*text;
|
||||
int vec_flag; /* True if the vec must be released. */
|
||||
struct vector_info vec;
|
||||
} *args = calloc(parm_count, sizeof(struct args_info));
|
||||
struct args_info *args = calloc(parm_count, sizeof(struct args_info));
|
||||
|
||||
char buffer[4096];
|
||||
|
||||
|
|
@ -154,121 +304,9 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
|||
break;
|
||||
|
||||
case IVL_EX_SIGNAL:
|
||||
/* If the signal node is narrower then the signal
|
||||
itself, then this is a part select so I'm going
|
||||
to need to evaluate the expression.
|
||||
|
||||
Also, if the signedness of the expression is
|
||||
different from the signedness of the
|
||||
signal. This could be caused by a $signed or
|
||||
$unsigned system function.
|
||||
|
||||
If I don't need to do any evaluating, then skip
|
||||
it as I'll be passing the handle to the signal
|
||||
itself. */
|
||||
if (ivl_expr_width(expr) !=
|
||||
ivl_signal_width(ivl_expr_signal(expr))) {
|
||||
break;
|
||||
|
||||
} else if (ivl_expr_signed(expr) !=
|
||||
ivl_signal_signed(ivl_expr_signal(expr))) {
|
||||
break;
|
||||
} else if (is_fixed_memory_word(expr)) {
|
||||
/* This is a word of a non-array, or a word
|
||||
of a net array, so we can address the
|
||||
word directly. */
|
||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||
unsigned use_word = 0;
|
||||
ivl_expr_t word_ex = ivl_expr_oper1(expr);
|
||||
if (word_ex) {
|
||||
/* Some array select have been evaluated. */
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
}
|
||||
if (word_ex)
|
||||
break;
|
||||
|
||||
assert(word_ex == 0);
|
||||
snprintf(buffer, sizeof buffer, "v%p_%u", sig, use_word);
|
||||
args[idx].text = strdup(buffer);
|
||||
continue;
|
||||
|
||||
} else {
|
||||
/* What's left, this is the work of a var
|
||||
array. Create the right code to handle
|
||||
it. */
|
||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||
unsigned use_word = 0;
|
||||
ivl_expr_t word_ex = ivl_expr_oper1(expr);
|
||||
if (word_ex) {
|
||||
/* Some array select have been evaluated. */
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
}
|
||||
if (word_ex && ivl_expr_type(word_ex)==IVL_EX_SIGNAL) {
|
||||
/* Special case: the index is a signal. */
|
||||
snprintf(buffer, sizeof buffer,
|
||||
"&A<v%p, v%p_0 >", sig,
|
||||
ivl_expr_signal(word_ex));
|
||||
} else if (word_ex) {
|
||||
/* Fallback case: evaluate expression. */
|
||||
struct vector_info av;
|
||||
av = draw_eval_expr(word_ex, STUFF_OK_XZ);
|
||||
snprintf(buffer, sizeof buffer,
|
||||
"&A<v%p, %u %u>", sig, av.base, av.wid);
|
||||
args[idx].vec = av;
|
||||
args[idx].vec_flag = 1;
|
||||
} else {
|
||||
snprintf(buffer, sizeof buffer,
|
||||
"&A<v%p, %u>", sig, use_word);
|
||||
}
|
||||
args[idx].text = strdup(buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
case IVL_EX_SELECT: {
|
||||
ivl_expr_t vexpr = ivl_expr_oper1(expr);
|
||||
assert(vexpr);
|
||||
|
||||
/* This code is only for signals. */
|
||||
if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL) break;
|
||||
|
||||
/* The signal is part of an array. */
|
||||
/* Add &APV<> code here when it is finished. */
|
||||
if (ivl_expr_oper1(vexpr)) break;
|
||||
|
||||
ivl_expr_t bexpr = ivl_expr_oper2(expr);
|
||||
assert(bexpr);
|
||||
|
||||
/* This is a constant bit/part select. */
|
||||
if (number_is_immediate(bexpr, 64, 1)) {
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %ld, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
get_number_immediate(bexpr),
|
||||
ivl_expr_width(expr));
|
||||
/* This is an indexed bit/part select. */
|
||||
} else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL) {
|
||||
/* Sepcial case: the base is a signal. */
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, v%p_0, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
ivl_expr_signal(bexpr),
|
||||
ivl_expr_width(expr));
|
||||
} else {
|
||||
/* Fallback case: evaluate the expression. */
|
||||
struct vector_info rv;
|
||||
rv = draw_eval_expr(bexpr, STUFF_OK_XZ);
|
||||
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %u %u, %u>",
|
||||
ivl_expr_signal(vexpr),
|
||||
rv.base, rv.wid,
|
||||
ivl_expr_width(expr));
|
||||
}
|
||||
args[idx].text = strdup(buffer);
|
||||
continue;
|
||||
}
|
||||
case IVL_EX_SELECT:
|
||||
if (get_vpi_taskfunc_signal_arg(&args[idx], expr)) continue;
|
||||
else break;
|
||||
|
||||
/* Everything else will need to be evaluated and
|
||||
passed as a constant to the vpi task. */
|
||||
|
|
@ -301,14 +339,22 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
|||
fprintf(vvp_out, "%s", call_string);
|
||||
|
||||
for (idx = 0 ; idx < parm_count ; idx += 1) {
|
||||
|
||||
fprintf(vvp_out, ", %s", args[idx].text);
|
||||
free(args[idx].text);
|
||||
if (args[idx].vec_flag) {
|
||||
if (args[idx].vec.wid > 0)
|
||||
clr_vector(args[idx].vec);
|
||||
else
|
||||
clr_word(args[idx].vec.base);
|
||||
struct args_info*ptr;
|
||||
/* Clear the nested children vectors. */
|
||||
for (ptr = &args[idx]; ptr != NULL; ptr = ptr->child) {
|
||||
if (ptr->vec_flag) {
|
||||
if (ptr->vec.wid > 0) clr_vector(ptr->vec);
|
||||
else clr_word(ptr->vec.base);
|
||||
}
|
||||
}
|
||||
/* Free the nested children. */
|
||||
ptr = args[idx].child;
|
||||
while (ptr != NULL) {
|
||||
struct args_info*tptr = ptr;
|
||||
ptr = ptr->child;
|
||||
free(tptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1670,22 +1670,31 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid)
|
|||
vvp_errors += 1;
|
||||
}
|
||||
|
||||
/* Detect the special case that the entire number fits in an
|
||||
immediate. In this case we generate a single %movi
|
||||
instruction. */
|
||||
if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID,0)) {
|
||||
unsigned long val = get_number_immediate(exp);
|
||||
fprintf(vvp_out, " %%movi %u, %lu, %u;\n", res.base, val, wid);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Use %movi as much as possible to build the value into the
|
||||
destination. Use the %mov to handle the remaining general
|
||||
bits. */
|
||||
idx = 0;
|
||||
unsigned long val = 0;
|
||||
unsigned val_bits = 0;
|
||||
unsigned val_addr = res.base;
|
||||
while (idx < nwid) {
|
||||
unsigned cnt;
|
||||
char src = '?';
|
||||
char src = 0;
|
||||
switch (bits[idx]) {
|
||||
case '0':
|
||||
src = '0';
|
||||
val_bits += 1;
|
||||
break;
|
||||
case '1':
|
||||
src = '1';
|
||||
val |= 1UL << val_bits;
|
||||
val_bits += 1;
|
||||
break;
|
||||
case 'x':
|
||||
src = '2';
|
||||
|
|
@ -1695,14 +1704,29 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid)
|
|||
break;
|
||||
}
|
||||
|
||||
for (cnt = 1 ; idx+cnt < nwid ; cnt += 1)
|
||||
if (bits[idx+cnt] != bits[idx])
|
||||
break;
|
||||
if (val_bits >= IMM_WID
|
||||
|| (val_bits>0 && src != 0)
|
||||
|| (val_bits>0 && idx+1==nwid)) {
|
||||
fprintf(vvp_out, " %%movi %u, %lu, %u;\n",
|
||||
val_addr, val, val_bits);
|
||||
val_addr += val_bits;
|
||||
val_bits = 0;
|
||||
val = 0;
|
||||
}
|
||||
|
||||
fprintf(vvp_out, " %%mov %u, %c, %u;\n",
|
||||
res.base+idx, src, cnt);
|
||||
if (src != 0) {
|
||||
assert(val_bits == 0);
|
||||
unsigned cnt;
|
||||
for (cnt = 1 ; idx+cnt < nwid ; cnt += 1)
|
||||
if (bits[idx+cnt] != bits[idx])
|
||||
break;
|
||||
|
||||
idx += cnt;
|
||||
fprintf(vvp_out, " %%mov %u, %c, %u;\n", val_addr, src, cnt);
|
||||
val_addr += cnt;
|
||||
idx += cnt-1;
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
/* Pad the number up to the expression width. */
|
||||
|
|
|
|||
|
|
@ -895,6 +895,10 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
|
|||
|
||||
static void draw_event_in_scope(ivl_event_t obj)
|
||||
{
|
||||
char tmp[4][32];
|
||||
|
||||
const unsigned ntmp = sizeof(tmp) / sizeof(tmp[0]);
|
||||
|
||||
unsigned nany = ivl_event_nany(obj);
|
||||
unsigned nneg = ivl_event_nneg(obj);
|
||||
unsigned npos = ivl_event_npos(obj);
|
||||
|
|
@ -903,13 +907,13 @@ static void draw_event_in_scope(ivl_event_t obj)
|
|||
|
||||
/* Figure out how many probe functors are needed. */
|
||||
if (nany > 0)
|
||||
cnt += (nany+3) / 4;
|
||||
cnt += (nany+ntmp-1) / ntmp;
|
||||
|
||||
if (nneg > 0)
|
||||
cnt += (nneg+3) / 4;
|
||||
cnt += (nneg+ntmp-1) / ntmp;
|
||||
|
||||
if (npos > 0)
|
||||
cnt += (npos+3) / 4;
|
||||
cnt += (npos+ntmp-1) / ntmp;
|
||||
|
||||
if (cnt == 0) {
|
||||
/* If none are needed, then this is a named event. The
|
||||
|
|
@ -923,48 +927,57 @@ static void draw_event_in_scope(ivl_event_t obj)
|
|||
unsigned idx;
|
||||
unsigned ecnt = 0;
|
||||
|
||||
for (idx = 0 ; idx < nany ; idx += 4, ecnt += 1) {
|
||||
for (idx = 0 ; idx < nany ; idx += ntmp, ecnt += 1) {
|
||||
unsigned sub, top;
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
|
||||
|
||||
top = idx + 4;
|
||||
top = idx + ntmp;
|
||||
if (nany < top)
|
||||
top = nany;
|
||||
for (sub = idx ; sub < top ; sub += 1) {
|
||||
ivl_nexus_t nex = ivl_event_any(obj, sub);
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
|
||||
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
|
||||
for (sub = idx ; sub < top ; sub += 1)
|
||||
fprintf(vvp_out, ", %s", tmp[sub-idx]);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
||||
for (idx = 0 ; idx < nneg ; idx += 4, ecnt += 1) {
|
||||
for (idx = 0 ; idx < nneg ; idx += ntmp, ecnt += 1) {
|
||||
unsigned sub, top;
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
|
||||
|
||||
top = idx + 4;
|
||||
top = idx + ntmp;
|
||||
if (nneg < top)
|
||||
top = nneg;
|
||||
for (sub = idx ; sub < top ; sub += 1) {
|
||||
ivl_nexus_t nex = ivl_event_neg(obj, sub);
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
|
||||
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
|
||||
for (sub = idx ; sub < top ; sub += 1)
|
||||
fprintf(vvp_out, ", %s", tmp[sub-idx]);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
||||
for (idx = 0 ; idx < npos ; idx += 4, ecnt += 1) {
|
||||
for (idx = 0 ; idx < npos ; idx += ntmp, ecnt += 1) {
|
||||
unsigned sub, top;
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
|
||||
|
||||
top = idx + 4;
|
||||
top = idx + ntmp;
|
||||
if (npos < top)
|
||||
top = npos;
|
||||
for (sub = idx ; sub < top ; sub += 1) {
|
||||
ivl_nexus_t nex = ivl_event_pos(obj, sub);
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
|
||||
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
|
||||
for (sub = idx ; sub < top ; sub += 1)
|
||||
fprintf(vvp_out, ", %s", tmp[sub-idx]);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
||||
|
|
@ -981,20 +994,17 @@ static void draw_event_in_scope(ivl_event_t obj)
|
|||
} else {
|
||||
unsigned num_input_strings = nany + nneg + npos;
|
||||
unsigned idx;
|
||||
ivl_nexus_t input_nexa[4];
|
||||
const char*edge = 0;
|
||||
|
||||
assert(num_input_strings <= 4);
|
||||
assert(num_input_strings <= ntmp);
|
||||
|
||||
if (nany > 0) {
|
||||
assert((nneg + npos) == 0);
|
||||
assert(nany <= 4);
|
||||
|
||||
edge = "edge";
|
||||
|
||||
for (idx = 0 ; idx < nany ; idx += 1) {
|
||||
ivl_nexus_t nex = ivl_event_any(obj, idx);
|
||||
input_nexa[idx] = nex;
|
||||
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
} else if (nneg > 0) {
|
||||
|
|
@ -1003,7 +1013,7 @@ static void draw_event_in_scope(ivl_event_t obj)
|
|||
|
||||
for (idx = 0 ; idx < nneg ; idx += 1) {
|
||||
ivl_nexus_t nex = ivl_event_neg(obj, idx);
|
||||
input_nexa[idx] = nex;
|
||||
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -1012,14 +1022,14 @@ static void draw_event_in_scope(ivl_event_t obj)
|
|||
|
||||
for (idx = 0 ; idx < npos ; idx += 1) {
|
||||
ivl_nexus_t nex = ivl_event_pos(obj, idx);
|
||||
input_nexa[idx] = nex;
|
||||
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "E_%p .event %s", obj, edge);
|
||||
for (idx = 0 ; idx < num_input_strings ; idx += 1)
|
||||
fprintf(vvp_out, ", %s", draw_input_from_net(input_nexa[idx]));
|
||||
|
||||
for (idx = 0 ; idx < num_input_strings ; idx += 1) {
|
||||
fprintf(vvp_out, ", %s", tmp[idx]);
|
||||
}
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -1759,6 +1769,13 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent)
|
|||
{
|
||||
unsigned idx;
|
||||
const char *type;
|
||||
/* For now we do not support automatic tasks or functions. */
|
||||
if (ivl_scope_is_auto(net)) {
|
||||
fprintf(stderr, "%s:%u: vvp-tgt sorry: automatic tasks/functions "
|
||||
"are not supported!\n",
|
||||
ivl_scope_def_file(net), ivl_scope_def_lineno(net));
|
||||
exit(1);
|
||||
}
|
||||
switch (ivl_scope_type(net)) {
|
||||
case IVL_SCT_MODULE: type = "module"; break;
|
||||
case IVL_SCT_FUNCTION: type = "function"; break;
|
||||
|
|
|
|||
128
verinum.cc
128
verinum.cc
|
|
@ -152,6 +152,112 @@ verinum::verinum(uint64_t val, unsigned n)
|
|||
}
|
||||
}
|
||||
|
||||
/* The second argument is not used! It is there to make this
|
||||
* constructor unique. */
|
||||
verinum::verinum(double val, bool dummy)
|
||||
: has_len_(false), has_sign_(true), string_flag_(false)
|
||||
{
|
||||
bool is_neg = false;
|
||||
double fraction;
|
||||
int exponent;
|
||||
const unsigned BITS_IN_LONG = 8*sizeof(long);
|
||||
|
||||
/* We return `bx for a NaN or +/- infinity. */
|
||||
if (val != val || (val && (val == 0.5*val))) {
|
||||
nbits_ = 1;
|
||||
bits_ = new V[nbits_];
|
||||
bits_[0] = Vx;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert to a positive result. */
|
||||
if (val < 0.0) {
|
||||
is_neg = true;
|
||||
val = -val;
|
||||
}
|
||||
|
||||
/* Get the exponent and fractional part of the number. */
|
||||
fraction = frexp(val, &exponent);
|
||||
nbits_ = exponent+1;
|
||||
bits_ = new V[nbits_];
|
||||
const verinum const_one(1);
|
||||
|
||||
/* If the value is small enough just use lround(). */
|
||||
if (nbits_ <= BITS_IN_LONG) {
|
||||
long sval = lround(val);
|
||||
if (is_neg) sval = -sval;
|
||||
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||
bits_[idx] = (sval&1) ? V1 : V0;
|
||||
sval >>= 1;
|
||||
}
|
||||
/* Trim the result. */
|
||||
signed_trim();
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned nwords = (exponent-1)/BITS_IN_LONG;
|
||||
|
||||
fraction = ldexp(fraction, (exponent-1) % BITS_IN_LONG + 1);
|
||||
|
||||
if (nwords == 0) {
|
||||
unsigned long bits = (unsigned long) fraction;
|
||||
fraction = fraction - (double) bits;
|
||||
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||
bits_[idx] = (bits&1) ? V1 : V0;
|
||||
bits >>= 1;
|
||||
}
|
||||
if (fraction >= 0.5) *this = *this + const_one;
|
||||
} else {
|
||||
for (int wd = nwords; wd >= 0; wd -= 1) {
|
||||
unsigned long bits = (unsigned long) fraction;
|
||||
fraction = fraction - (double) bits;
|
||||
unsigned max = (wd+1)*BITS_IN_LONG;
|
||||
if (max > nbits_) max = nbits_;
|
||||
for (unsigned idx = wd*BITS_IN_LONG; idx < max; idx += 1) {
|
||||
bits_[idx] = (bits&1) ? V1 : V0;
|
||||
bits >>= 1;
|
||||
}
|
||||
fraction = ldexp(fraction, BITS_IN_LONG);
|
||||
}
|
||||
if (fraction >= ldexp(0.5, BITS_IN_LONG)) *this = *this + const_one;
|
||||
}
|
||||
|
||||
/* Convert a negative number if needed. */
|
||||
if (is_neg) {
|
||||
*this = v_not(*this) + const_one;
|
||||
}
|
||||
|
||||
/* Trim the result. */
|
||||
signed_trim();
|
||||
}
|
||||
|
||||
|
||||
/* This is used by the double constructor above. It is needed to remove
|
||||
* extra sign bits that can occur when calculating a negative value. */
|
||||
void verinum::signed_trim()
|
||||
{
|
||||
/* Do we have any extra digits? */
|
||||
unsigned tlen = nbits_-1;
|
||||
verinum::V sign = bits_[tlen];
|
||||
while ((tlen > 0) && (bits_[tlen] == sign)) tlen -= 1;
|
||||
|
||||
/* tlen now points to the first digit that is not the sign.
|
||||
* or bit 0. Set the length to include this bit and one proper
|
||||
* sign bit if needed. */
|
||||
if (bits_[tlen] != sign) tlen += 1;
|
||||
tlen += 1;
|
||||
|
||||
/* Trim the bits if needed. */
|
||||
if (tlen < nbits_) {
|
||||
V* tbits = new V[tlen];
|
||||
for (unsigned idx = 0; idx < tlen; idx += 1)
|
||||
tbits[idx] = bits_[idx];
|
||||
delete[] bits_;
|
||||
bits_ = tbits;
|
||||
nbits_ = tlen;
|
||||
}
|
||||
}
|
||||
|
||||
verinum::verinum(const verinum&that)
|
||||
{
|
||||
string_flag_ = that.string_flag_;
|
||||
|
|
@ -336,14 +442,9 @@ double verinum::as_double() const
|
|||
{
|
||||
if (nbits_ == 0) return 0.0;
|
||||
|
||||
/* Do we have a signed value? */
|
||||
bool signed_flag = false;
|
||||
if (bits_[nbits_-1] == V1) {
|
||||
signed_flag = true;
|
||||
}
|
||||
|
||||
double val = 0.0;
|
||||
if (signed_flag) {
|
||||
/* Do we have/want a signed value? */
|
||||
if (has_sign_ && bits_[nbits_-1] == V1) {
|
||||
V carry = V1;
|
||||
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||
V sum = add_with_carry(~bits_[idx], V0, carry);
|
||||
|
|
@ -351,7 +452,6 @@ double verinum::as_double() const
|
|||
val += pow(2.0, (double)idx);
|
||||
}
|
||||
val *= -1.0;
|
||||
// val = (double) as_long();
|
||||
} else {
|
||||
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||
if (bits_[idx] == V1)
|
||||
|
|
@ -591,11 +691,15 @@ ostream& operator<< (ostream&o, const verinum&v)
|
|||
verinum::V trim_left = v.get(v.len()-1);
|
||||
unsigned idx;
|
||||
|
||||
for (idx = v.len()-1; idx > 0; idx -= 1)
|
||||
if (trim_left != v.get(idx-1))
|
||||
break;
|
||||
if (v.has_sign()) {
|
||||
for (idx = v.len()-1; idx > 0; idx -= 1)
|
||||
if (trim_left != v.get(idx-1))
|
||||
break;
|
||||
|
||||
o << trim_left;
|
||||
o << trim_left;
|
||||
} else {
|
||||
idx = v.len();
|
||||
}
|
||||
|
||||
while (idx > 0) {
|
||||
o << v.get(idx-1);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class verinum {
|
|||
verinum(const V*v, unsigned nbits, bool has_len =true);
|
||||
verinum(V, unsigned nbits =1, bool has_len =true);
|
||||
verinum(uint64_t val, unsigned bits);
|
||||
verinum(double val, bool dummy);
|
||||
verinum(const verinum&);
|
||||
|
||||
// Create a signed number, with an unspecified number of bits.
|
||||
|
|
@ -93,6 +94,8 @@ class verinum {
|
|||
signed long as_long() const;
|
||||
double as_double() const;
|
||||
string as_string() const;
|
||||
private:
|
||||
void signed_trim();
|
||||
|
||||
private:
|
||||
V* bits_;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \
|
|||
sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \
|
||||
sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \
|
||||
mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \
|
||||
vams_simparam.o
|
||||
sys_clog2.o vams_simparam.o
|
||||
|
||||
ifeq (@HAVE_LIBZ@,yes)
|
||||
ifeq (@HAVE_LIBBZ2@,yes)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Cary R. (cygcary@yahoo.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <vpi_user.h>
|
||||
#include "sys_priv.h"
|
||||
|
||||
/*
|
||||
* Check that the function is called with the correct argument.
|
||||
*/
|
||||
static PLI_INT32 sys_clog2_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
assert(callh != 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
(void) name; // Not used!
|
||||
|
||||
/* We must have an argument. */
|
||||
if (argv == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("$clog2 requires one numeric argument.\n");
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The argument must be numeric. */
|
||||
arg = vpi_scan(argv);
|
||||
if (! is_numeric_obj(arg)) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("The first argument to $clog2 must be numeric.\n");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* We can have a maximum of one argument. */
|
||||
if (vpi_scan(argv) != 0) {
|
||||
char msg [64];
|
||||
snprintf(msg, 64, "ERROR: %s line %d:",
|
||||
vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
|
||||
unsigned argc = 1;
|
||||
while (vpi_scan(argv)) argc += 1;
|
||||
|
||||
vpi_printf("%s $clog2 takes at most one argument.\n", msg);
|
||||
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 sys_clog2_calltf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
s_vpi_value val;
|
||||
s_vpi_vecval vec;
|
||||
(void) name; // Not used!/
|
||||
|
||||
/* Get the argument. */
|
||||
arg = vpi_scan(argv);
|
||||
vpi_free_object(argv);
|
||||
|
||||
vec = vpip_calc_clog2(arg);
|
||||
|
||||
val.format = vpiVectorVal;
|
||||
val.value.vector = &vec;
|
||||
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the function with Verilog.
|
||||
*/
|
||||
void sys_clog2_register(void)
|
||||
{
|
||||
s_vpi_systf_data tf_data;
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.sysfunctype = vpiIntFunc;
|
||||
tf_data.calltf = sys_clog2_calltf;
|
||||
tf_data.compiletf = sys_clog2_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.tfname = "$clog2";
|
||||
tf_data.user_data = 0;
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
157
vpi/sys_icarus.c
157
vpi/sys_icarus.c
|
|
@ -42,18 +42,159 @@ static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 task_not_implemented_compiletf(PLI_BYTE8* name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
|
||||
vpi_printf("%s:%d: SORRY: task %s() is not currently implemented.\n",
|
||||
vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh),
|
||||
name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 function_not_implemented_compiletf(PLI_BYTE8* name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
|
||||
vpi_printf("%s:%d: SORRY: function %s() is not currently implemented.\n",
|
||||
vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh),
|
||||
name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the function with Verilog.
|
||||
*/
|
||||
void sys_special_register(void)
|
||||
{
|
||||
s_vpi_systf_data tf_data;
|
||||
s_vpi_systf_data tf_data;
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.calltf = finish_and_return_calltf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.tfname = "$finish_and_return";
|
||||
tf_data.user_data = "$finish_and_return";
|
||||
vpi_register_systf(&tf_data);
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.calltf = finish_and_return_calltf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.tfname = "$finish_and_return";
|
||||
tf_data.user_data = "$finish_and_return";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
/* These tasks are not currently implemented. */
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.calltf = 0;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.compiletf = task_not_implemented_compiletf;
|
||||
|
||||
tf_data.tfname = "$fmonitor";
|
||||
tf_data.user_data = "$fmonitor";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$fmonitorb";
|
||||
tf_data.user_data = "$fmonitorb";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$fmonitoro";
|
||||
tf_data.user_data = "$fmonitoro";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$fmonitorh";
|
||||
tf_data.user_data = "$fmonitorh";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$and$array";
|
||||
tf_data.user_data = "$async$and$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$nand$array";
|
||||
tf_data.user_data = "$async$nand$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$or$array";
|
||||
tf_data.user_data = "$async$or$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$nor$array";
|
||||
tf_data.user_data = "$async$nor$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$and$plane";
|
||||
tf_data.user_data = "$async$and$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$nand$plane";
|
||||
tf_data.user_data = "$async$nand$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$or$plane";
|
||||
tf_data.user_data = "$async$or$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$async$nor$plane";
|
||||
tf_data.user_data = "$async$nor$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$and$array";
|
||||
tf_data.user_data = "$sync$and$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$nand$array";
|
||||
tf_data.user_data = "$sync$nand$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$or$array";
|
||||
tf_data.user_data = "$sync$or$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$nor$array";
|
||||
tf_data.user_data = "$sync$nor$array";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$and$plane";
|
||||
tf_data.user_data = "$sync$and$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$nand$plane";
|
||||
tf_data.user_data = "$sync$nand$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$or$plane";
|
||||
tf_data.user_data = "$sync$or$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$sync$nor$plane";
|
||||
tf_data.user_data = "$sync$nor$plane";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$q_initialize";
|
||||
tf_data.user_data = "$q_initialize";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$q_add";
|
||||
tf_data.user_data = "$q_add";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$q_remove";
|
||||
tf_data.user_data = "$q_remove";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$q_full";
|
||||
tf_data.user_data = "$q_full";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$q_exam";
|
||||
tf_data.user_data = "$q_exam";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
/* These functions are not currently implemented. */
|
||||
tf_data.compiletf = function_not_implemented_compiletf;
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.sysfunctype = vpiIntFunc;
|
||||
|
||||
tf_data.tfname = "$ferror";
|
||||
tf_data.user_data = "$ferror";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.tfname = "$fread";
|
||||
tf_data.user_data = "$fread";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,7 +281,6 @@ static int scan_format(vpiHandle sys, struct byte_source*src, vpiHandle argv)
|
|||
int suppress_flag = 0;
|
||||
int length_field = -1;
|
||||
int code = 0;
|
||||
int sign_flag = 1;
|
||||
PLI_INT32 value;
|
||||
|
||||
char*tmp;
|
||||
|
|
@ -373,30 +372,38 @@ static int scan_format(vpiHandle sys, struct byte_source*src, vpiHandle argv)
|
|||
break;
|
||||
|
||||
case 'd':
|
||||
match_fail = 1;
|
||||
/* Decimal integer */
|
||||
ch = byte_getc(src);
|
||||
if (ch == '-') {
|
||||
sign_flag = -1;
|
||||
ch = byte_getc(src);
|
||||
}
|
||||
/* decimal integer */
|
||||
tmp = malloc(2);
|
||||
value = 0;
|
||||
while ( isdigit(ch) ) {
|
||||
tmp[0] = 0;
|
||||
match_fail = 1;
|
||||
|
||||
ch = byte_getc(src);
|
||||
while (isdigit(ch) || ch == '_' || (value == 0 && ch == '-')) {
|
||||
match_fail = 0;
|
||||
value *= 10;
|
||||
value += ch - '0';
|
||||
ch = byte_getc(src);
|
||||
if (ch != '_') {
|
||||
tmp[value++] = ch;
|
||||
tmp = realloc(tmp, value+1);
|
||||
tmp[value] = 0;
|
||||
ch = byte_getc(src);
|
||||
}
|
||||
}
|
||||
byte_ungetc(src, ch);
|
||||
|
||||
if (match_fail) {
|
||||
free(tmp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (match_fail)
|
||||
break;
|
||||
|
||||
/* Matched a decimal value, put it to an argument. */
|
||||
item = vpi_scan(argv);
|
||||
assert(item);
|
||||
|
||||
val.format = vpiIntVal;
|
||||
val.value.integer = value * sign_flag;
|
||||
val.format = vpiDecStrVal;
|
||||
val.value.str = tmp;
|
||||
vpi_put_value(item, &val, 0, vpiNoDelay);
|
||||
|
||||
free(tmp);
|
||||
rc += 1;
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ extern void sys_time_register();
|
|||
extern void sys_vcd_register();
|
||||
extern void sys_vcdoff_register();
|
||||
extern void sys_special_register();
|
||||
extern void sys_clog2_register();
|
||||
extern void vams_simparam_register();
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
|
|
@ -181,6 +182,7 @@ void (*vlog_startup_routines[])() = {
|
|||
sys_lxt_or_vcd_register,
|
||||
sys_sdf_register,
|
||||
sys_special_register,
|
||||
sys_clog2_register,
|
||||
vams_simparam_register,
|
||||
0
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ $dist_poisson vpiSysFuncInt
|
|||
$dist_chi_square vpiSysFuncInt
|
||||
$dist_t vpiSysFuncInt
|
||||
$dist_erlang vpiSysFuncInt
|
||||
$clog2 vpiSysFuncInt
|
||||
|
||||
$simparam vpiSysFuncReal
|
||||
$simparam$str vpiSysFuncSized 1024 unsigned
|
||||
|
|
|
|||
|
|
@ -566,6 +566,7 @@ extern DLLEXPORT void (*vlog_startup_routines[])();
|
|||
buffer. The value must be a vpiStrengthVal. */
|
||||
extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit);
|
||||
extern void vpip_set_return_value(int value);
|
||||
extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
|
|
|
|||
38
vvp/array.cc
38
vvp/array.cc
|
|
@ -75,6 +75,7 @@ struct __vpiArray {
|
|||
|
||||
class vvp_fun_arrayport*ports_;
|
||||
struct __vpiCallback *vpi_callbacks;
|
||||
bool signed_flag;
|
||||
};
|
||||
|
||||
struct __vpiArrayIterator {
|
||||
|
|
@ -452,7 +453,8 @@ static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value)
|
|||
unsigned index = decode_array_word_pointer(obj, parent);
|
||||
unsigned width = parent->vals->width();
|
||||
|
||||
vpip_vec4_get_value(parent->vals->get_word(index), width, false, value);
|
||||
vpip_vec4_get_value(parent->vals->get_word(index), width,
|
||||
parent->signed_flag, value);
|
||||
}
|
||||
|
||||
static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int flags)
|
||||
|
|
@ -635,7 +637,7 @@ static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value)
|
|||
|
||||
unsigned index = obj->get_address();
|
||||
vvp_vector4_t tmp = array_get_word(parent, index);
|
||||
vpip_vec4_get_value(tmp, parent->vals_width, false, value);
|
||||
vpip_vec4_get_value(tmp, parent->vals_width, parent->signed_flag, value);
|
||||
}
|
||||
|
||||
static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int)
|
||||
|
|
@ -756,11 +758,14 @@ vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address)
|
|||
}
|
||||
|
||||
static vpiHandle vpip_make_array(char*label, const char*name,
|
||||
int first_addr, int last_addr)
|
||||
int first_addr, int last_addr,
|
||||
bool signed_flag)
|
||||
{
|
||||
struct __vpiArray*obj = (struct __vpiArray*)
|
||||
malloc(sizeof(struct __vpiArray));
|
||||
|
||||
obj->signed_flag = signed_flag;
|
||||
|
||||
// Assume increasing addresses.
|
||||
assert(last_addr >= first_addr);
|
||||
unsigned array_count = last_addr+1-first_addr;
|
||||
|
|
@ -833,7 +838,8 @@ void array_attach_word(vvp_array_t array, unsigned long addr, vpiHandle word)
|
|||
void compile_var_array(char*label, char*name, int last, int first,
|
||||
int msb, int lsb, char signed_flag)
|
||||
{
|
||||
vpiHandle obj = vpip_make_array(label, name, first, last);
|
||||
vpiHandle obj = vpip_make_array(label, name, first, last,
|
||||
signed_flag != 0);
|
||||
|
||||
struct __vpiArray*arr = ARRAY_HANDLE(obj);
|
||||
|
||||
|
|
@ -853,7 +859,7 @@ void compile_var_array(char*label, char*name, int last, int first,
|
|||
void compile_real_array(char*label, char*name, int last, int first,
|
||||
int msb, int lsb)
|
||||
{
|
||||
vpiHandle obj = vpip_make_array(label, name, first, last);
|
||||
vpiHandle obj = vpip_make_array(label, name, first, last, true);
|
||||
|
||||
struct __vpiArray*arr = ARRAY_HANDLE(obj);
|
||||
vvp_array_t array = array_find(label);
|
||||
|
|
@ -874,7 +880,7 @@ void compile_real_array(char*label, char*name, int last, int first,
|
|||
|
||||
void compile_net_array(char*label, char*name, int last, int first)
|
||||
{
|
||||
vpiHandle obj = vpip_make_array(label, name, first, last);
|
||||
vpiHandle obj = vpip_make_array(label, name, first, last, false);
|
||||
|
||||
struct __vpiArray*arr = ARRAY_HANDLE(obj);
|
||||
arr->nets = (vpiHandle*)calloc(arr->array_count, sizeof(vpiHandle));
|
||||
|
|
@ -981,7 +987,8 @@ void array_word_change(vvp_array_t array, unsigned long addr)
|
|||
if (cur->cb_data.value)
|
||||
vpip_vec4_get_value(array->vals->get_word(addr),
|
||||
array->vals_width,
|
||||
false, cur->cb_data.value);
|
||||
array->signed_flag,
|
||||
cur->cb_data.value);
|
||||
|
||||
callback_execute(cur);
|
||||
prev = cur;
|
||||
|
|
@ -1170,6 +1177,23 @@ vpiHandle vpip_make_vthr_A(char*label, char*symbol)
|
|||
|
||||
return &(obj->base);
|
||||
}
|
||||
vpiHandle vpip_make_vthr_A(char*label, vpiHandle handle)
|
||||
{
|
||||
struct __vpiArrayVthrA*obj = (struct __vpiArrayVthrA*)
|
||||
malloc(sizeof (struct __vpiArrayVthrA));
|
||||
|
||||
obj->base.vpi_type = &vpip_array_vthr_A_rt;
|
||||
|
||||
obj->array = array_find(label);
|
||||
assert(obj->array);
|
||||
free(label);
|
||||
|
||||
obj->address_handle = handle;
|
||||
obj->address = 0;
|
||||
obj->wid = 0;
|
||||
|
||||
return &(obj->base);
|
||||
}
|
||||
|
||||
void compile_array_cleanup(void)
|
||||
{
|
||||
|
|
|
|||
32
vvp/logic.cc
32
vvp/logic.cc
|
|
@ -109,35 +109,6 @@ void vvp_fun_and::run_run()
|
|||
vvp_send_vec4(ptr->out, result);
|
||||
}
|
||||
|
||||
vvp_fun_eeq::vvp_fun_eeq(unsigned wid, bool invert)
|
||||
: vvp_fun_boolean_(wid), invert_(invert)
|
||||
{
|
||||
count_functors_logic += 1;
|
||||
}
|
||||
|
||||
vvp_fun_eeq::~vvp_fun_eeq()
|
||||
{
|
||||
}
|
||||
|
||||
void vvp_fun_eeq::run_run()
|
||||
{
|
||||
vvp_net_t*ptr = net_;
|
||||
net_ = 0;
|
||||
|
||||
vvp_vector4_t result (input_[0]);
|
||||
|
||||
for (unsigned idx = 0 ; idx < result.size() ; idx += 1) {
|
||||
vvp_bit4_t bitbit = result.value(idx);
|
||||
bitbit = (bitbit == input_[1].value(idx))? BIT4_1 : BIT4_0;
|
||||
if (invert_)
|
||||
bitbit = ~bitbit;
|
||||
|
||||
result.set_bit(idx, bitbit);
|
||||
}
|
||||
|
||||
vvp_send_vec4(ptr->out, result);
|
||||
}
|
||||
|
||||
vvp_fun_buf::vvp_fun_buf()
|
||||
{
|
||||
net_ = 0;
|
||||
|
|
@ -579,9 +550,6 @@ void compile_functor(char*label, char*type, unsigned width,
|
|||
} else if (strcmp(type, "RPMOS") == 0) {
|
||||
obj = new vvp_fun_rpmos(false);
|
||||
|
||||
} else if (strcmp(type, "EEQ") == 0) {
|
||||
obj = new vvp_fun_eeq(width, false);
|
||||
|
||||
} else if (strcmp(type, "NOT") == 0) {
|
||||
obj = new vvp_fun_not();
|
||||
|
||||
|
|
|
|||
11
vvp/logic.h
11
vvp/logic.h
|
|
@ -52,17 +52,6 @@ class vvp_fun_and : public vvp_fun_boolean_ {
|
|||
bool invert_;
|
||||
};
|
||||
|
||||
class vvp_fun_eeq : public vvp_fun_boolean_ {
|
||||
|
||||
public:
|
||||
explicit vvp_fun_eeq(unsigned wid, bool invert);
|
||||
~vvp_fun_eeq();
|
||||
|
||||
private:
|
||||
void run_run();
|
||||
bool invert_;
|
||||
};
|
||||
|
||||
/*
|
||||
* The buffer functor is a very primitive functor that takes the input
|
||||
* from port-0 (and only port-0) and retransmits it as a vvp_vector4_t.
|
||||
|
|
|
|||
|
|
@ -414,8 +414,9 @@ ended, then the %join does not block or yield the thread.
|
|||
* %load/av <bit>, <array-label>, <wid>
|
||||
|
||||
This instruction loads a word from the specified array. The word
|
||||
address is in index register 3. The width should match the width of
|
||||
the array word.
|
||||
address is in index register 3. Like %load/v below the width does
|
||||
not have to match the width of the array word. See the %load/v
|
||||
description for more information.
|
||||
|
||||
* %load/avp0 <bit>, <array-label>, <wid>
|
||||
* %load/avp0/s <bit>, <array-label>, <wid>
|
||||
|
|
|
|||
14
vvp/parse.y
14
vvp/parse.y
|
|
@ -105,7 +105,7 @@ static struct __vpiModPath*modpath_dst = 0;
|
|||
%type <table> udp_table
|
||||
|
||||
%type <argv> argument_opt argument_list
|
||||
%type <vpi> argument
|
||||
%type <vpi> argument symbol_access
|
||||
%type <cdelay> delay delay_opt
|
||||
|
||||
%%
|
||||
|
|
@ -831,21 +831,29 @@ argument
|
|||
{ $$ = vpip_make_binary_const($1.idx, $1.text);
|
||||
free($1.text);
|
||||
}
|
||||
| K_A '<' T_SYMBOL ',' T_NUMBER '>'
|
||||
| symbol_access
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
symbol_access
|
||||
: K_A '<' T_SYMBOL ',' T_NUMBER '>'
|
||||
{ $$ = vpip_make_vthr_A($3, $5); }
|
||||
| K_A '<' T_SYMBOL ',' T_NUMBER T_NUMBER '>'
|
||||
{ $$ = vpip_make_vthr_A($3, $5, $6); }
|
||||
| K_A '<' T_SYMBOL ',' T_SYMBOL '>'
|
||||
{ $$ = vpip_make_vthr_A($3, $5); }
|
||||
| K_A '<' T_SYMBOL ',' symbol_access '>'
|
||||
{ $$ = vpip_make_vthr_A($3, $5); }
|
||||
| K_PV '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER '>'
|
||||
{ $$ = vpip_make_PV($3, $5, $7); }
|
||||
| K_PV '<' T_SYMBOL ',' '-' T_NUMBER ',' T_NUMBER '>'
|
||||
{ $$ = vpip_make_PV($3, -$6, $8); }
|
||||
| K_PV '<' T_SYMBOL ',' T_SYMBOL ',' T_NUMBER '>'
|
||||
{ $$ = vpip_make_PV($3, $5, $7); }
|
||||
| K_PV '<' T_SYMBOL ',' symbol_access ',' T_NUMBER '>'
|
||||
{ $$ = vpip_make_PV($3, $5, $7); }
|
||||
| K_PV '<' T_SYMBOL ',' T_NUMBER T_NUMBER ',' T_NUMBER '>'
|
||||
{ $$ = vpip_make_PV($3, $5, $6, $8); }
|
||||
;
|
||||
|
||||
/* functor operands can only be a list of symbols. */
|
||||
symbols
|
||||
|
|
|
|||
|
|
@ -127,6 +127,12 @@ static struct __vpiCallback* make_value_change(p_cb_data data)
|
|||
obj->cb_time.type = vpiSuppressTime;
|
||||
}
|
||||
obj->cb_data.time = &obj->cb_time;
|
||||
if (data->value) {
|
||||
obj->cb_value = *(data->value);
|
||||
} else {
|
||||
obj->cb_value.format = vpiSuppressVal;
|
||||
}
|
||||
obj->cb_data.value = &obj->cb_value;
|
||||
|
||||
assert(data->obj);
|
||||
assert(data->obj->vpi_type);
|
||||
|
|
@ -457,8 +463,25 @@ void callback_execute(struct __vpiCallback*cur)
|
|||
vpi_mode_flag = VPI_MODE_RWSYNC;
|
||||
|
||||
assert(cur->cb_data.cb_rtn);
|
||||
cur->cb_data.time->type = vpiSimTime;
|
||||
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
|
||||
switch (cur->cb_data.time->type) {
|
||||
case vpiSimTime:
|
||||
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
|
||||
break;
|
||||
case vpiScaledRealTime: {
|
||||
cur->cb_data.time->real =
|
||||
vpip_time_to_scaled_real(schedule_simtime(),
|
||||
(struct __vpiScope *) vpi_handle(vpiScope,
|
||||
cur->cb_data.obj));
|
||||
break;
|
||||
}
|
||||
case vpiSuppressTime:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported time format %d.\n",
|
||||
cur->cb_data.time->type);
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
(cur->cb_data.cb_rtn)(&cur->cb_data);
|
||||
|
||||
vpi_mode_flag = save_mode;
|
||||
|
|
@ -546,10 +569,32 @@ void vvp_fun_signal::get_value(struct t_vpi_value*vp)
|
|||
{
|
||||
switch (vp->format) {
|
||||
case vpiScalarVal:
|
||||
// This works because vvp_bit4_t has the same encoding
|
||||
// as a scalar value! See vpip_vec4_get_value() for a
|
||||
// more robust method.
|
||||
vp->value.scalar = value(0);
|
||||
break;
|
||||
|
||||
case vpiBinStrVal:
|
||||
case vpiOctStrVal:
|
||||
case vpiDecStrVal:
|
||||
case vpiHexStrVal:
|
||||
case vpiIntVal:
|
||||
case vpiVectorVal:
|
||||
case vpiStringVal:
|
||||
case vpiRealVal: {
|
||||
unsigned wid = size();
|
||||
vvp_vector4_t vec4(wid);
|
||||
for (unsigned idx = 0; idx < wid; idx += 1) {
|
||||
vec4.set_bit(idx, value(idx));
|
||||
}
|
||||
vpip_vec4_get_value(vec4, wid, false, vp);
|
||||
break;
|
||||
}
|
||||
|
||||
case vpiSuppressVal:
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "vpi_callback: value "
|
||||
"format %d not supported (fun_signal)\n",
|
||||
|
|
@ -622,6 +667,9 @@ void vvp_fun_signal_real::get_value(struct t_vpi_value*vp)
|
|||
break;
|
||||
}
|
||||
|
||||
case vpiSuppressVal:
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "vpi_callback: value "
|
||||
"format %d not supported (fun_signal_real)\n",
|
||||
|
|
|
|||
|
|
@ -456,6 +456,13 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width,
|
|||
vp->format);
|
||||
assert(0 && "format not implemented");
|
||||
|
||||
case vpiObjTypeVal:
|
||||
vp->format = vpiVectorVal;
|
||||
break;
|
||||
|
||||
case vpiSuppressVal:
|
||||
break;
|
||||
|
||||
case vpiBinStrVal:
|
||||
rbuf = need_result_buf(width+1, RBUF_VAL);
|
||||
for (unsigned idx = 0 ; idx < width ; idx += 1) {
|
||||
|
|
@ -504,6 +511,7 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width,
|
|||
break;
|
||||
case BIT4_X:
|
||||
vp->value.scalar = vpiX;
|
||||
break;
|
||||
case BIT4_Z:
|
||||
vp->value.scalar = vpiZ;
|
||||
break;
|
||||
|
|
@ -1029,3 +1037,63 @@ extern "C" void vpi_control(PLI_INT32 operation, ...)
|
|||
vpi_sim_vcontrol(operation, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine calculated the return value for $clog2.
|
||||
* It is easier to do it here vs trying to to use the VPI interface.
|
||||
*/
|
||||
extern "C" s_vpi_vecval vpip_calc_clog2(vpiHandle arg)
|
||||
{
|
||||
s_vpi_vecval rtn;
|
||||
s_vpi_value val;
|
||||
vvp_vector4_t vec4;
|
||||
bool is_neg = false; // At this point only a real can be negative.
|
||||
|
||||
/* Get the value as a vvp_vector4_t. */
|
||||
val.format = vpiObjTypeVal;
|
||||
vpi_get_value(arg, &val);
|
||||
if (val.format == vpiRealVal) {
|
||||
vpi_get_value(arg, &val);
|
||||
/* All double values can be represented in 1024 bits. */
|
||||
vec4 = vvp_vector4_t(1024, val.value.real);
|
||||
if (val.value.real < 0) is_neg = true;
|
||||
} else {
|
||||
val.format = vpiVectorVal;
|
||||
vpi_get_value(arg, &val);
|
||||
unsigned wid = vpi_get(vpiSize, arg);
|
||||
vec4 = vvp_vector4_t(wid, BIT4_0);
|
||||
for (unsigned idx=0; idx < wid; idx += 1) {
|
||||
PLI_INT32 aval = val.value.vector[idx/32].aval;
|
||||
PLI_INT32 bval = val.value.vector[idx/32].bval;
|
||||
aval >>= idx % 32;
|
||||
bval >>= idx % 32;
|
||||
int bitmask = (aval&1) | ((bval<<1)&2);
|
||||
vvp_bit4_t bit = scalar_to_bit4(bitmask);
|
||||
vec4.set_bit(idx, bit);
|
||||
}
|
||||
}
|
||||
|
||||
if (vec4.has_xz()) {
|
||||
rtn.aval = rtn.bval = 0xFFFFFFFFU; /* Set to 'bx. */
|
||||
return rtn;
|
||||
}
|
||||
|
||||
vvp_vector2_t vec2(vec4);
|
||||
|
||||
if (is_neg) vec2.trim_neg(); /* This is a special trim! */
|
||||
else vec2.trim(); /* This makes less work shifting. */
|
||||
|
||||
/* Calculate the clog2 result. */
|
||||
PLI_INT32 res = 0;
|
||||
if (!vec2.is_zero()) {
|
||||
vec2 -= vvp_vector2_t(1, vec2.size());
|
||||
while(!vec2.is_zero()) {
|
||||
res += 1;
|
||||
vec2 >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
rtn.aval = res;
|
||||
rtn.bval = 0;
|
||||
return rtn;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ struct __vpiCallback {
|
|||
// user supplied callback data
|
||||
struct t_cb_data cb_data;
|
||||
struct t_vpi_time cb_time;
|
||||
struct t_vpi_value cb_value;
|
||||
|
||||
// scheduled event
|
||||
struct sync_cb* cb_sync;
|
||||
|
|
@ -238,6 +239,7 @@ struct __vpiPV {
|
|||
};
|
||||
extern vpiHandle vpip_make_PV(char*name, int base, int width);
|
||||
extern vpiHandle vpip_make_PV(char*name, char*symbol, int width);
|
||||
extern vpiHandle vpip_make_PV(char*name, vpiHandle handle, int width);
|
||||
extern vpiHandle vpip_make_PV(char*name, int tbase, int twid, int width);
|
||||
|
||||
extern struct __vpiPV* vpip_PV_from_handle(vpiHandle obj);
|
||||
|
|
@ -455,9 +457,10 @@ vpiHandle vpip_make_vthr_vector(unsigned base, unsigned wid, bool signed_flag);
|
|||
|
||||
vpiHandle vpip_make_vthr_word(unsigned base, const char*type);
|
||||
|
||||
vpiHandle vpip_make_vthr_A(char*symbol, unsigned index);
|
||||
vpiHandle vpip_make_vthr_A(char*label, unsigned index);
|
||||
vpiHandle vpip_make_vthr_A(char*label, char*symbol);
|
||||
vpiHandle vpip_make_vthr_A(char*symbol, unsigned tbase, unsigned twid);
|
||||
vpiHandle vpip_make_vthr_A(char*label, unsigned tbase, unsigned twid);
|
||||
vpiHandle vpip_make_vthr_A(char*label, vpiHandle handle);
|
||||
|
||||
/*
|
||||
* This function is called before any compilation to load VPI
|
||||
|
|
|
|||
|
|
@ -728,23 +728,6 @@ static vvp_vector4_t from_stringval(const char*str, unsigned wid)
|
|||
return val;
|
||||
}
|
||||
|
||||
static vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar)
|
||||
{
|
||||
switch(scalar) {
|
||||
case vpi0:
|
||||
return BIT4_0;
|
||||
case vpi1:
|
||||
return BIT4_1;
|
||||
case vpiX:
|
||||
return BIT4_X;
|
||||
case vpiZ:
|
||||
return BIT4_Z;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported scalar value %d.\n", scalar);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static vpiHandle signal_put_value(vpiHandle ref, s_vpi_value*vp, int flags)
|
||||
{
|
||||
unsigned wid;
|
||||
|
|
@ -1188,6 +1171,21 @@ vpiHandle vpip_make_PV(char*var, char*symbol, int width)
|
|||
return &obj->base;
|
||||
}
|
||||
|
||||
vpiHandle vpip_make_PV(char*var, vpiHandle handle, int width)
|
||||
{
|
||||
struct __vpiPV*obj = (struct __vpiPV*) malloc(sizeof(struct __vpiPV));
|
||||
obj->base.vpi_type = &vpip_PV_rt;
|
||||
obj->parent = vvp_lookup_handle(var);
|
||||
obj->sbase = handle;
|
||||
obj->tbase = 0;
|
||||
obj->twid = 0;
|
||||
obj->width = (unsigned) width;
|
||||
obj->net = 0;
|
||||
functor_ref_lookup(&obj->net, var);
|
||||
|
||||
return &obj->base;
|
||||
}
|
||||
|
||||
vpiHandle vpip_make_PV(char*var, int tbase, int twid, int width)
|
||||
{
|
||||
struct __vpiPV*obj = (struct __vpiPV*) malloc(sizeof(struct __vpiPV));
|
||||
|
|
|
|||
|
|
@ -229,7 +229,13 @@ void vpip_dec_str_to_vec4(vvp_vector4_t&vec,
|
|||
input is "1234", str gets "4321". */
|
||||
unsigned slen = strlen(buf);
|
||||
char*str = new char[slen + 1];
|
||||
int is_negative = 0;
|
||||
for (unsigned idx = 0 ; idx < slen ; idx += 1) {
|
||||
if (idx == slen-1 && buf[slen-idx-1] == '-') {
|
||||
is_negative = 1;
|
||||
slen--;
|
||||
continue;
|
||||
}
|
||||
if (isdigit(buf[slen-idx-1]))
|
||||
str[idx] = buf[slen-idx-1];
|
||||
else
|
||||
|
|
@ -266,5 +272,10 @@ void vpip_dec_str_to_vec4(vvp_vector4_t&vec,
|
|||
|
||||
}
|
||||
|
||||
if (is_negative) {
|
||||
vec.invert();
|
||||
vec += 1;
|
||||
}
|
||||
|
||||
delete[]str;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1626,7 +1626,10 @@ static unsigned long* divide_bits(unsigned long*ap, unsigned long*bp, unsigned w
|
|||
unsigned cur_ptr = cur-1;
|
||||
unsigned long cur_res;
|
||||
if (ap[cur_ptr+btop] >= bp[btop]) {
|
||||
cur_res = ap[cur_ptr+btop] / bp[btop];
|
||||
unsigned long high = 0;
|
||||
if (cur_ptr+btop+1 < words)
|
||||
high = ap[cur_ptr+btop+1];
|
||||
cur_res = divide2words(ap[cur_ptr+btop], bp[btop], high);
|
||||
|
||||
} else if (cur_ptr+btop+1 >= words) {
|
||||
continue;
|
||||
|
|
@ -1749,6 +1752,9 @@ bool of_DIV_S(vthread_t thr, vvp_code_t cp)
|
|||
|
||||
assert(adra >= 4);
|
||||
|
||||
// Get the values, left in right, in binary form. If there is
|
||||
// a problem with either (caused by an X or Z bit) then we
|
||||
// know right away that the entire result is X.
|
||||
unsigned long*ap = vector_to_array(thr, adra, wid);
|
||||
if (ap == 0) {
|
||||
vvp_vector4_t tmp(wid, BIT4_X);
|
||||
|
|
@ -1764,6 +1770,7 @@ bool of_DIV_S(vthread_t thr, vvp_code_t cp)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Sign extend the bits in the array to fill out the array.
|
||||
unsigned long sign_mask = 0;
|
||||
if (unsigned long sign_bits = (words*CPU_WORD_BITS) - wid) {
|
||||
sign_mask = -1UL << (CPU_WORD_BITS-sign_bits);
|
||||
|
|
@ -1773,6 +1780,7 @@ bool of_DIV_S(vthread_t thr, vvp_code_t cp)
|
|||
bp[words-1] |= sign_mask;
|
||||
}
|
||||
|
||||
// If the value fits in a single word, then use the native divide.
|
||||
if (wid <= CPU_WORD_BITS) {
|
||||
if (bp[0] == 0) {
|
||||
vvp_vector4_t tmp(wid, BIT4_X);
|
||||
|
|
@ -2076,7 +2084,14 @@ bool of_FORK(vthread_t thr, vvp_code_t cp)
|
|||
|
||||
thr->fork_count += 1;
|
||||
|
||||
schedule_vthread(child, 0, true);
|
||||
/* If the new child was created to evaluate a function,
|
||||
run it immediately, then return to this thread. */
|
||||
if (cp->scope->base.vpi_type->type_code == vpiFunction) {
|
||||
child->is_scheduled = 1;
|
||||
vthread_run(child);
|
||||
} else {
|
||||
schedule_vthread(child, 0, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2363,19 +2378,21 @@ bool of_LOAD_AV(vthread_t thr, vvp_code_t cp)
|
|||
|
||||
vvp_vector4_t word = array_get_word(cp->array, adr);
|
||||
|
||||
if (word.size() != wid) {
|
||||
fprintf(stderr, "internal error: array width=%u, word.size()=%u, wid=%u\n",
|
||||
0, word.size(), wid);
|
||||
assert(word.size() == wid);
|
||||
}
|
||||
|
||||
/* Check the address once, before we scan the vector. */
|
||||
thr_check_addr(thr, bit+wid-1);
|
||||
|
||||
if (word.size() > wid)
|
||||
word.resize(wid);
|
||||
|
||||
/* Copy the vector bits into the bits4 vector. Do the copy
|
||||
directly to skip the excess calls to thr_check_addr. */
|
||||
thr->bits4.set_vec(bit, word);
|
||||
|
||||
/* If the source is shorter then the desired width, then pad
|
||||
with BIT4_X values. */
|
||||
for (unsigned idx = word.size() ; idx < wid ; idx += 1)
|
||||
thr->bits4.set_bit(bit+idx, BIT4_X);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,3 +37,4 @@ vpi_vprintf
|
|||
|
||||
vpip_format_strength
|
||||
vpip_set_return_value
|
||||
vpip_calc_clog2
|
||||
|
|
|
|||
|
|
@ -130,6 +130,23 @@ vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c)
|
|||
}
|
||||
}
|
||||
|
||||
vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar)
|
||||
{
|
||||
switch(scalar) {
|
||||
case vpi0:
|
||||
return BIT4_0;
|
||||
case vpi1:
|
||||
return BIT4_1;
|
||||
case vpiX:
|
||||
return BIT4_X;
|
||||
case vpiZ:
|
||||
return BIT4_Z;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported scalar value %d.\n", scalar);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
vvp_bit4_t operator ^ (vvp_bit4_t a, vvp_bit4_t b)
|
||||
{
|
||||
if (bit4_is_xz(a))
|
||||
|
|
@ -372,19 +389,12 @@ vvp_vector4_t::vvp_vector4_t(unsigned size, double val)
|
|||
double fraction;
|
||||
int exponent;
|
||||
|
||||
/* We return 'bx for a NaN. */
|
||||
if (val != val) {
|
||||
/* We return 'bx for a NaN or +/- infinity. */
|
||||
if (val != val || (val && (val == 0.5*val))) {
|
||||
allocate_words_(size, WORD_X_ABITS, WORD_X_BBITS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We return 'b1 for + infinity or 'b0 for - infinity. */
|
||||
if (val && (val == 0.5*val)) {
|
||||
if (val > 0) allocate_words_(size, WORD_1_ABITS, WORD_1_BBITS);
|
||||
else allocate_words_(size, WORD_0_ABITS, WORD_0_BBITS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert to a positive result. */
|
||||
if (val < 0.0) {
|
||||
is_neg = true;
|
||||
|
|
@ -1618,6 +1628,15 @@ void vvp_vector2_t::trim()
|
|||
while (value(wid_-1) == 0 && wid_ > 1) wid_ -= 1;
|
||||
}
|
||||
|
||||
/* This is a special trim that is used on numbers we know represent a
|
||||
* negative signed value (they came from a negative real value). */
|
||||
void vvp_vector2_t::trim_neg()
|
||||
{
|
||||
if (value(wid_-1) == 1 && wid_ > 32) {
|
||||
while (value(wid_-2) == 1 && wid_ > 32) wid_ -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
int vvp_vector2_t::value(unsigned idx) const
|
||||
{
|
||||
if (idx >= wid_)
|
||||
|
|
@ -2125,22 +2144,19 @@ void vvp_net_fun_t::recv_vec4(vvp_net_ptr_t, const vvp_vector4_t&)
|
|||
assert(0);
|
||||
}
|
||||
|
||||
void vvp_net_fun_t::recv_vec4_pv(vvp_net_ptr_t, const vvp_vector4_t&bits,
|
||||
void vvp_net_fun_t::recv_vec4_pv(vvp_net_ptr_t, const vvp_vector4_t&bit,
|
||||
unsigned base, unsigned wid, unsigned vwid)
|
||||
{
|
||||
cerr << "internal error: " << typeid(*this).name() << ": "
|
||||
<< "recv_vec4_pv(" << bits << ", " << base
|
||||
<< "recv_vec4_pv(" << bit << ", " << base
|
||||
<< ", " << wid << ", " << vwid << ") not implemented" << endl;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void vvp_net_fun_t::recv_vec8_pv(vvp_net_ptr_t, const vvp_vector8_t&bits,
|
||||
void vvp_net_fun_t::recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
|
||||
unsigned base, unsigned wid, unsigned vwid)
|
||||
{
|
||||
cerr << "internal error: " << typeid(*this).name() << ": "
|
||||
<< "recv_vec8_pv(" << bits << ", " << base
|
||||
<< ", " << wid << ", " << vwid << ") not implemented" << endl;
|
||||
assert(0);
|
||||
recv_vec4_pv(port, reduce4(bit), base, wid, vwid);
|
||||
}
|
||||
|
||||
void vvp_net_fun_t::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
# include "config.h"
|
||||
# include "vpi_user.h"
|
||||
# include <stddef.h>
|
||||
# include <string.h>
|
||||
# include <new>
|
||||
|
|
@ -67,6 +68,8 @@ inline char vvp_bit4_to_ascii(vvp_bit4_t a) { return "01zx"[a]; }
|
|||
|
||||
extern vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c);
|
||||
|
||||
extern vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar);
|
||||
|
||||
/* Return TRUE if the bit is BIT4_X or BIT4_Z. The fast
|
||||
implementation here relies on the encoding of vvp_bit4_t values. */
|
||||
inline bool bit4_is_xz(vvp_bit4_t a) { return a >= 2; }
|
||||
|
|
@ -470,6 +473,9 @@ class vvp_vector2_t {
|
|||
void set_bit(unsigned idx, int bit);
|
||||
// Make the size just big enough to hold the first 1 bit.
|
||||
void trim();
|
||||
// Trim off extra 1 bit since this is representing a negative value.
|
||||
// Always keep at least 32 bits.
|
||||
void trim_neg();
|
||||
|
||||
private:
|
||||
enum { BITS_PER_WORD = 8 * sizeof(unsigned long) };
|
||||
|
|
@ -853,8 +859,9 @@ struct vvp_net_t {
|
|||
* port. The value is a vvp_vector4_t.
|
||||
*
|
||||
* Most nodes do not care about the specific strengths of bits, so the
|
||||
* default behavior for recv_vec8 is to reduce the operand to a
|
||||
* vvp_vector4_t and pass it on to the recv_vec4 method.
|
||||
* default behavior for recv_vec8 and recv_vec8_pv is to reduce the
|
||||
* operand to a vvp_vector4_t and pass it on to the recv_vec4 or
|
||||
* recv_vec4_pv method.
|
||||
*/
|
||||
class vvp_net_fun_t {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue