Merge branch 'master' into elaborate-net-rework
This commit is contained in:
commit
c9efe87146
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 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
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include "config.h"
|
||||
|
||||
# include "AStatement.h"
|
||||
|
||||
AStatement::~AStatement()
|
||||
{
|
||||
}
|
||||
|
||||
AContrib::AContrib(PExpr*lv, PExpr*rv)
|
||||
: lval_(lv), rval_(rv)
|
||||
{
|
||||
}
|
||||
|
||||
AContrib::~AContrib()
|
||||
{
|
||||
delete lval_;
|
||||
delete rval_;
|
||||
}
|
||||
|
||||
AProcess::~AProcess()
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
#ifndef __AStatement_H
|
||||
#define __AStatement_H
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include <map>
|
||||
# include "StringHeap.h"
|
||||
# include "LineInfo.h"
|
||||
|
||||
class PExpr;
|
||||
|
||||
class AStatement : public LineInfo {
|
||||
|
||||
public:
|
||||
AStatement() { }
|
||||
virtual ~AStatement() =0;
|
||||
|
||||
virtual void dump(ostream&out, unsigned ind) const;
|
||||
|
||||
private: // not implemented
|
||||
AStatement(const AStatement&);
|
||||
AStatement& operator= (const AStatement&);
|
||||
};
|
||||
|
||||
/*
|
||||
* A contribution statement is like an assignment: there is an l-value
|
||||
* expression and an r-value expression. The l-value is a branch probe
|
||||
* expression.
|
||||
*/
|
||||
class AContrib : public AStatement {
|
||||
|
||||
public:
|
||||
AContrib(PExpr*lval, PExpr*rval);
|
||||
~AContrib();
|
||||
|
||||
virtual void dump(ostream&out, unsigned ind) const;
|
||||
|
||||
private:
|
||||
PExpr*lval_;
|
||||
PExpr*rval_;
|
||||
};
|
||||
|
||||
/*
|
||||
* An analog process is not a statement, but contains an analog
|
||||
* statement. The process is where we attach process characteristics
|
||||
* such as initial vs. always, attributes....
|
||||
*/
|
||||
class AProcess : public LineInfo {
|
||||
|
||||
public:
|
||||
enum Type { PR_INITIAL, PR_ALWAYS };
|
||||
|
||||
AProcess(Type t, AStatement*st)
|
||||
: type_(t), statement_(st) { }
|
||||
|
||||
~AProcess();
|
||||
|
||||
map<perm_string,PExpr*> attributes;
|
||||
|
||||
// Dump the analog process
|
||||
void dump(ostream&out, unsigned ind) const;
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
AStatement*statement_;
|
||||
|
||||
private: // not implemented
|
||||
AProcess(const AProcess&);
|
||||
AProcess& operator= (const AProcess&);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -107,12 +107,13 @@ load_module.o netlist.o netmisc.o net_assign.o \
|
|||
net_design.o net_event.o net_expr.o net_force.o net_func.o \
|
||||
net_link.o net_modulo.o net_nex_input.o net_nex_output.o \
|
||||
net_proc.o net_scope.o net_tran.o net_udp.o pad_to_width.o \
|
||||
parse.o parse_misc.o pform.o pform_disciplines.o pform_dump.o pform_types.o \
|
||||
parse.o parse_misc.o pform.o pform_analog.o pform_disciplines.o \
|
||||
pform_dump.o pform_types.o \
|
||||
set_width.o symbol_search.o sync.o sys_funcs.o \
|
||||
verinum.o verireal.o target.o targets.o \
|
||||
Attrib.o HName.o LineInfo.o Module.o PDelays.o PEvent.o \
|
||||
PExpr.o PGate.o PGenerate.o PScope.o PSpec.o \
|
||||
PTask.o PUdp.o PFunction.o PWire.o Statement.o StringHeap.o \
|
||||
PTask.o PUdp.o PFunction.o PWire.o Statement.o AStatement.o StringHeap.o \
|
||||
$(FF) $(TT)
|
||||
|
||||
Makefile: Makefile.in config.h.in config.status
|
||||
|
|
|
|||
17
PExpr.cc
17
PExpr.cc
|
|
@ -91,7 +91,7 @@ PEBShift::~PEBShift()
|
|||
{
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms)
|
||||
PECallFunction::PECallFunction(const pform_name_t&n, const vector<PExpr *> &parms)
|
||||
: path_(n), parms_(parms)
|
||||
{
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ static pform_name_t pn_from_ps(perm_string n)
|
|||
return tmp;
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(perm_string n, const svector<PExpr*>&parms)
|
||||
PECallFunction::PECallFunction(perm_string n, const vector<PExpr*>&parms)
|
||||
: path_(pn_from_ps(n)), parms_(parms)
|
||||
{
|
||||
}
|
||||
|
|
@ -114,6 +114,17 @@ PECallFunction::PECallFunction(perm_string n)
|
|||
{
|
||||
}
|
||||
|
||||
// NOTE: Anachronism. Try to work all use of svector out.
|
||||
PECallFunction::PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms)
|
||||
: path_(n), parms_(vector_from_svector(parms))
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::PECallFunction(perm_string n, const svector<PExpr*>&parms)
|
||||
: path_(pn_from_ps(n)), parms_(vector_from_svector(parms))
|
||||
{
|
||||
}
|
||||
|
||||
PECallFunction::~PECallFunction()
|
||||
{
|
||||
}
|
||||
|
|
@ -123,7 +134,7 @@ 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_.count() != 1 || parms_[0] == 0) {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: $clog2 takes a "
|
||||
"single argument." << endl;
|
||||
return false;
|
||||
|
|
|
|||
12
PExpr.h
12
PExpr.h
|
|
@ -705,10 +705,15 @@ class PETernary : public PExpr {
|
|||
*/
|
||||
class PECallFunction : public PExpr {
|
||||
public:
|
||||
explicit PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms);
|
||||
explicit PECallFunction(const pform_name_t&n, const vector<PExpr *> &parms);
|
||||
// Call of system function (name is not hierarchical)
|
||||
explicit PECallFunction(perm_string n, const svector<PExpr *> &parms);
|
||||
explicit PECallFunction(perm_string n, const vector<PExpr *> &parms);
|
||||
explicit PECallFunction(perm_string n);
|
||||
|
||||
// svector versions. Should be removed!
|
||||
explicit PECallFunction(const pform_name_t&n, const svector<PExpr *> &parms);
|
||||
explicit PECallFunction(perm_string n, const svector<PExpr *> &parms);
|
||||
|
||||
~PECallFunction();
|
||||
|
||||
virtual bool is_constant(Module*) const;
|
||||
|
|
@ -732,11 +737,12 @@ class PECallFunction : public PExpr {
|
|||
|
||||
private:
|
||||
pform_name_t path_;
|
||||
svector<PExpr *> parms_;
|
||||
vector<PExpr *> parms_;
|
||||
|
||||
bool check_call_matches_definition_(Design*des, NetScope*dscope) const;
|
||||
|
||||
NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const;
|
||||
NetExpr* elaborate_access_func_(Design*des, NetScope*scope, int expr_wid) const;
|
||||
NetNet* elaborate_net_sfunc_(Design*des, NetScope*scope,
|
||||
unsigned width,
|
||||
const NetExpr* rise,
|
||||
|
|
|
|||
8
PScope.h
8
PScope.h
|
|
@ -24,6 +24,7 @@
|
|||
# include <map>
|
||||
|
||||
class PEvent;
|
||||
class AProcess;
|
||||
class PProcess;
|
||||
class PWire;
|
||||
|
||||
|
|
@ -50,6 +51,10 @@ class LexicalScope {
|
|||
map<perm_string,PWire*>wires;
|
||||
PWire* wires_find(perm_string name);
|
||||
|
||||
// Behaviors (processes) in this scope
|
||||
list<PProcess*> behaviors;
|
||||
list<AProcess*> analog_behaviors;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
|
@ -74,9 +79,6 @@ class PScope : public LexicalScope {
|
|||
// Named events in the scope.
|
||||
map<perm_string,PEvent*>events;
|
||||
|
||||
// Behaviors (processes) in this scope
|
||||
list<PProcess*> behaviors;
|
||||
|
||||
protected:
|
||||
void dump_wires_(ostream&out, unsigned indent) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
4
cprop.cc
4
cprop.cc
|
|
@ -897,7 +897,7 @@ void cprop_dc_functor::lpm_const(Design*des, NetConst*obj)
|
|||
for (Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
|
||||
NetObj*cur;
|
||||
NetPins*cur;
|
||||
unsigned pin;
|
||||
clnk->cur_link(cur, pin);
|
||||
|
||||
|
|
@ -923,7 +923,7 @@ void cprop_dc_functor::lpm_const(Design*des, NetConst*obj)
|
|||
for (Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
|
||||
NetObj*cur;
|
||||
NetPins*cur;
|
||||
unsigned pin;
|
||||
clnk->cur_link(cur, pin);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
# include <iomanip>
|
||||
# include "netlist.h"
|
||||
# include "compiler.h"
|
||||
# include "discipline.h"
|
||||
# include "ivl_assert.h"
|
||||
|
||||
static ostream& operator<< (ostream&o, NetBlock::Type t)
|
||||
|
|
@ -187,6 +188,10 @@ void NetNet::dump_net(ostream&o, unsigned ind) const
|
|||
o << " inout";
|
||||
break;
|
||||
}
|
||||
|
||||
if (discipline_t*dis = get_discipline())
|
||||
o << " discipline=" << dis->name();
|
||||
|
||||
o << " (eref=" << peek_eref() << ", lref=" << peek_lref() << ")";
|
||||
if (scope())
|
||||
o << " scope=" << scope_path(scope());
|
||||
|
|
@ -236,7 +241,7 @@ void NetNode::dump_node(ostream&o, unsigned ind) const
|
|||
/* This is the generic dumping of all the signals connected to each
|
||||
pin of the object. The "this" object is not printed, only the
|
||||
signals connected to this. */
|
||||
void NetObj::dump_node_pins(ostream&o, unsigned ind) const
|
||||
void NetPins::dump_node_pins(ostream&o, unsigned ind) const
|
||||
{
|
||||
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
||||
o << setw(ind) << "" << idx << " " << pin(idx).get_name()
|
||||
|
|
@ -1185,6 +1190,18 @@ void NetExpr::dump(ostream&o) const
|
|||
o << "(?" << typeid(*this).name() << "?)";
|
||||
}
|
||||
|
||||
void NetEAccess::dump(ostream&o) const
|
||||
{
|
||||
o << nature_->name() << "." << nature_->access() << "(";
|
||||
assert(branch_);
|
||||
if (branch_->pin(0).is_linked())
|
||||
o << branch_->pin(0).nexus()->name();
|
||||
o << ", ";
|
||||
if (branch_->pin(1).is_linked())
|
||||
o << branch_->pin(1).nexus()->name();
|
||||
o << ")";
|
||||
}
|
||||
|
||||
void NetEBinary::dump(ostream&o) const
|
||||
{
|
||||
if (op_ == 'm' || op_ == 'M') {
|
||||
|
|
|
|||
|
|
@ -71,5 +71,7 @@ class discipline_t : public LineInfo {
|
|||
|
||||
extern map<perm_string,nature_t*> natures;
|
||||
extern map<perm_string,discipline_t*> disciplines;
|
||||
// Map access function name to the nature that it accesses.
|
||||
extern map<perm_string,nature_t*> access_function_nature;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@
|
|||
|
||||
# include "netlist.h"
|
||||
# include <cassert>
|
||||
# include "ivl_assert.h"
|
||||
|
||||
NetEAccess* NetEAccess::dup_expr() const
|
||||
{
|
||||
NetEAccess*tmp = new NetEAccess(branch_, nature_);
|
||||
ivl_assert(*this, tmp);
|
||||
tmp->set_line(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetEBComp* NetEBComp::dup_expr() const
|
||||
{
|
||||
|
|
|
|||
72
elab_expr.cc
72
elab_expr.cc
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
# include "pform.h"
|
||||
# include "netlist.h"
|
||||
# include "discipline.h"
|
||||
# include "netmisc.h"
|
||||
# include "util.h"
|
||||
# include "ivl_assert.h"
|
||||
|
|
@ -627,7 +628,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
and makes it into a signed expression. No bits are changed,
|
||||
it just changes the interpretation. */
|
||||
if (strcmp(peek_tail_name(path_), "$signed") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $signed() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
@ -641,7 +642,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
}
|
||||
/* add $unsigned to match $signed */
|
||||
if (strcmp(peek_tail_name(path_), "$unsigned") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $unsigned() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
@ -664,7 +665,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
deleted. */
|
||||
if ((strcmp(peek_tail_name(path_), "$sizeof") == 0)
|
||||
|| (strcmp(peek_tail_name(path_), "$bits") == 0)) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $bits() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
@ -700,7 +701,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
otherwise. The subexpression is elaborated but not
|
||||
evaluated. */
|
||||
if (strcmp(peek_tail_name(path_), "$is_signed") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $is_signed() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
@ -736,7 +737,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
Functions cannot really take empty parameters, but the
|
||||
case ``func()'' is the same as no parameters at all. So
|
||||
catch that special case here. */
|
||||
unsigned nparms = parms_.count();
|
||||
unsigned nparms = parms_.size();
|
||||
if ((nparms == 1) && (parms_[0] == 0))
|
||||
nparms = 0;
|
||||
|
||||
|
|
@ -779,6 +780,55 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
|
|||
return fun;
|
||||
}
|
||||
|
||||
NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope,
|
||||
int expr_wid) const
|
||||
{
|
||||
// Hierarchical names cannot be access functions.
|
||||
if (path_.size() != 1)
|
||||
return 0;
|
||||
|
||||
perm_string access_name = peek_tail_name(path_);
|
||||
nature_t*nature = access_function_nature[access_name];
|
||||
|
||||
// If the name doesn't match any access functions, then give up.
|
||||
if (nature == 0)
|
||||
return 0;
|
||||
|
||||
// An access function must have 1 or 2 arguments.
|
||||
ivl_assert(*this, parms_.size()==2 || parms_.size()==1);
|
||||
|
||||
NetBranch*branch = 0;
|
||||
|
||||
if (parms_.size() == 1) {
|
||||
PExpr*arg1 = parms_[0];
|
||||
PEIdent*arg_ident = dynamic_cast<PEIdent*> (arg1);
|
||||
ivl_assert(*this, arg_ident);
|
||||
|
||||
const pform_name_t&path = arg_ident->path();
|
||||
ivl_assert(*this, path.size()==1);
|
||||
perm_string name = peek_tail_name(path);
|
||||
|
||||
NetNet*sig = scope->find_signal(name);
|
||||
ivl_assert(*this, sig);
|
||||
|
||||
discipline_t*dis = sig->get_discipline();
|
||||
ivl_assert(*this, dis);
|
||||
ivl_assert(*this, nature == dis->potential() || nature == dis->flow());
|
||||
|
||||
branch = new NetBranch(dis);
|
||||
branch->set_line(*this);
|
||||
connect(branch->pin(0), sig->pin(0));
|
||||
|
||||
} else {
|
||||
ivl_assert(*this, 0);
|
||||
}
|
||||
|
||||
NetEAccess*tmp = new NetEAccess(branch, nature);
|
||||
tmp->set_line(*this);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
||||
int expr_wid, bool) const
|
||||
{
|
||||
|
|
@ -787,20 +837,26 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|||
|
||||
NetFuncDef*def = des->find_function(scope, path_);
|
||||
if (def == 0) {
|
||||
// Not a user defined function. Maybe it is an access
|
||||
// function for a nature? If so then elaborate it that way.
|
||||
NetExpr*tmp = elaborate_access_func_(des, scope, expr_wid);
|
||||
if (tmp != 0)
|
||||
return tmp;
|
||||
|
||||
cerr << get_fileline() << ": error: No function " << path_ <<
|
||||
" in this context (" << scope_path(scope) << ")." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
assert(def);
|
||||
ivl_assert(*this, def);
|
||||
|
||||
NetScope*dscope = def->scope();
|
||||
assert(dscope);
|
||||
ivl_assert(*this, dscope);
|
||||
|
||||
if (! check_call_matches_definition_(des, dscope))
|
||||
return 0;
|
||||
|
||||
unsigned parms_count = parms_.count();
|
||||
unsigned parms_count = parms_.size();
|
||||
if ((parms_count == 1) && (parms_[0] == 0))
|
||||
parms_count = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1466,7 +1466,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
|||
forces it to be a signed result. Otherwise, it is as if the
|
||||
$signed did not exist. */
|
||||
if (strcmp(name, "$signed") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $signed() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
@ -1482,7 +1482,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
|||
|
||||
/* handle $unsigned like $signed */
|
||||
if (strcmp(name, "$unsigned") == 0) {
|
||||
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
||||
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
||||
cerr << get_fileline() << ": error: The $unsigned() function "
|
||||
<< "takes exactly one(1) argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
@ -1514,7 +1514,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
|||
}
|
||||
|
||||
NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(),
|
||||
def, 1+parms_.count());
|
||||
def, 1+parms_.size());
|
||||
net->set_line(*this);
|
||||
net->rise_time(rise);
|
||||
net->fall_time(fall);
|
||||
|
|
@ -1531,7 +1531,7 @@ NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
|||
connect(net->pin(0), osig->pin(0));
|
||||
|
||||
unsigned errors = 0;
|
||||
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
||||
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
|
||||
NetNet*tmp = parms_[idx]->elaborate_net(des, scope, 0,
|
||||
0, 0, 0,
|
||||
Link::STRONG, Link::STRONG);
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ 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_.count() != 1 || parms_[0] == 0) {
|
||||
if (parms_.size() != 1 || parms_[0] == 0) {
|
||||
cerr << get_fileline() << ": error: $clog2 takes a "
|
||||
"single argument." << endl;
|
||||
des->errors += 1;
|
||||
|
|
|
|||
|
|
@ -1040,6 +1040,10 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
|
|||
sig->set_signed(get_signed());
|
||||
sig->set_isint(get_isint());
|
||||
|
||||
if (discipline_t*dis = get_discipline()) {
|
||||
sig->set_discipline(dis);
|
||||
}
|
||||
|
||||
if (pull)
|
||||
connect(sig->pin(0), pull->pin(0));
|
||||
|
||||
|
|
|
|||
8
emit.cc
8
emit.cc
|
|
@ -198,8 +198,7 @@ bool NetProc::emit_proc(struct target_t*tgt) const
|
|||
|
||||
bool NetAssign::emit_proc(struct target_t*tgt) const
|
||||
{
|
||||
tgt->proc_assign(this);
|
||||
return true;
|
||||
return tgt->proc_assign(this);
|
||||
}
|
||||
|
||||
bool NetAssignNB::emit_proc(struct target_t*tgt) const
|
||||
|
|
@ -461,6 +460,11 @@ int Design::emit(struct target_t*tgt) const
|
|||
return rc;
|
||||
}
|
||||
|
||||
void NetEAccess::expr_scan(struct expr_scan_t*tgt) const
|
||||
{
|
||||
tgt->expr_access_func(this);
|
||||
}
|
||||
|
||||
void NetEBinary::expr_scan(struct expr_scan_t*tgt) const
|
||||
{
|
||||
tgt->expr_binary(this);
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ typedef enum ivl_drive_e {
|
|||
typedef enum ivl_expr_type_e {
|
||||
IVL_EX_NONE = 0,
|
||||
IVL_EX_ARRAY = 18,
|
||||
IVL_EX_BACCESS= 19,
|
||||
IVL_EX_BINARY = 2,
|
||||
IVL_EX_CONCAT = 3,
|
||||
IVL_EX_EVENT = 17,
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ bool Nexus::drivers_constant() const
|
|||
|
||||
if (cur_dir == Link::PASSIVE) {
|
||||
|
||||
const NetObj*obj = cur->get_obj();
|
||||
if (obj->scope()->parent() != 0)
|
||||
const NetPins*obj = cur->get_obj();
|
||||
const NetObj*as_obj = dynamic_cast<const NetObj*>(obj);
|
||||
if (as_obj == 0 || as_obj->scope()->parent() != 0)
|
||||
continue;
|
||||
|
||||
sig = dynamic_cast<const NetNet*>(cur->get_obj());
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ void NetEvProbe::find_similar_probes(list<NetEvProbe*>&plist)
|
|||
Nexus*nex = pin(0).nexus();
|
||||
|
||||
for (Link*lcur = nex->first_nlink(); lcur; lcur = lcur->next_nlink()) {
|
||||
NetObj*obj = lcur->get_obj();
|
||||
NetPins*obj = lcur->get_obj();
|
||||
if (obj->pin_count() != pin_count())
|
||||
continue;
|
||||
|
||||
|
|
|
|||
14
net_expr.cc
14
net_expr.cc
|
|
@ -584,3 +584,17 @@ ivl_variable_type_t NetESFunc::expr_type() const
|
|||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
NetEAccess::NetEAccess(NetBranch*br, nature_t*nat)
|
||||
: branch_(br), nature_(nat)
|
||||
{
|
||||
}
|
||||
|
||||
NetEAccess::~NetEAccess()
|
||||
{
|
||||
}
|
||||
|
||||
ivl_variable_type_t NetEAccess::expr_type() const
|
||||
{
|
||||
return IVL_VT_REAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ bool PECallFunction::check_call_matches_definition_(Design*des, NetScope*dscope)
|
|||
1 nil pointer. This is how the parser tells me of no
|
||||
parameter. In other words, ``func()'' is 1 nil parameter. */
|
||||
|
||||
unsigned parms_count = parms_.count();
|
||||
unsigned parms_count = parms_.size();
|
||||
if ((parms_count == 1) && (parms_[0] == 0))
|
||||
parms_count = 0;
|
||||
|
||||
|
|
|
|||
17
net_link.cc
17
net_link.cc
|
|
@ -139,13 +139,13 @@ verinum::V Link::get_init() const
|
|||
}
|
||||
|
||||
|
||||
void Link::cur_link(NetObj*&net, unsigned &pin)
|
||||
void Link::cur_link(NetPins*&net, unsigned &pin)
|
||||
{
|
||||
net = node_;
|
||||
pin = pin_;
|
||||
}
|
||||
|
||||
void Link::cur_link(const NetObj*&net, unsigned &pin) const
|
||||
void Link::cur_link(const NetPins*&net, unsigned &pin) const
|
||||
{
|
||||
net = node_;
|
||||
pin = pin_;
|
||||
|
|
@ -191,12 +191,12 @@ const Link* Link::next_nlink() const
|
|||
return next_;
|
||||
}
|
||||
|
||||
const NetObj*Link::get_obj() const
|
||||
const NetPins*Link::get_obj() const
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
NetObj*Link::get_obj()
|
||||
NetPins*Link::get_obj()
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
|
@ -264,7 +264,7 @@ bool Nexus::drivers_present() const
|
|||
|
||||
// Must be PASSIVE, so if it is some kind of net, see if
|
||||
// it is the sort that might drive the nexus.
|
||||
const NetObj*obj;
|
||||
const NetPins*obj;
|
||||
unsigned pin;
|
||||
cur->cur_link(obj, pin);
|
||||
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
|
||||
|
|
@ -293,7 +293,10 @@ void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
|||
if (cur->get_dir() != Link::OUTPUT)
|
||||
continue;
|
||||
|
||||
NetObj*obj = cur->get_obj();
|
||||
NetObj*obj = dynamic_cast<NetObj*>(cur->get_obj());
|
||||
if (obj == 0)
|
||||
continue;
|
||||
|
||||
obj->rise_time(rise);
|
||||
obj->fall_time(fall);
|
||||
obj->decay_time(decay);
|
||||
|
|
@ -448,7 +451,7 @@ const char* Nexus::name() const
|
|||
|
||||
if (sig == 0) {
|
||||
const Link*lnk = first_nlink();
|
||||
const NetObj*obj = lnk->get_obj();
|
||||
const NetObj*obj = dynamic_cast<const NetObj*>(lnk->get_obj());
|
||||
pin = lnk->get_pin();
|
||||
cerr << "internal error: No signal for nexus of " <<
|
||||
obj->name() << " pin " << pin << "(" <<
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ NexusSet* NetEConcat::nex_input(bool rem_out)
|
|||
return result;
|
||||
}
|
||||
|
||||
NexusSet* NetEAccess::nex_input(bool rem_out)
|
||||
{
|
||||
return new NexusSet;
|
||||
}
|
||||
|
||||
/*
|
||||
* A constant has not inputs, so always return an empty set.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -100,8 +100,12 @@ void join_island(NetObj*obj)
|
|||
Nexus*nex = obj->pin(idx).nexus();
|
||||
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||
unsigned pin;
|
||||
NetObj*tmp;
|
||||
cur->cur_link(tmp, pin);
|
||||
NetPins*tmp_pins;
|
||||
cur->cur_link(tmp_pins, pin);
|
||||
|
||||
NetObj*tmp = dynamic_cast<NetObj*> (tmp_pins);
|
||||
if (tmp == 0)
|
||||
continue;
|
||||
|
||||
// Skip self.
|
||||
if (tmp == obj)
|
||||
|
|
|
|||
71
netlist.cc
71
netlist.cc
|
|
@ -91,7 +91,7 @@ unsigned count_inputs(const Link&pin)
|
|||
const Nexus*nex = pin.nexus();
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
if (cur->pin(cpin).get_dir() == Link::INPUT)
|
||||
|
|
@ -108,7 +108,7 @@ unsigned count_outputs(const Link&pin)
|
|||
const Nexus*nex = pin.nexus();
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
if (cur->pin(cpin).get_dir() == Link::OUTPUT)
|
||||
|
|
@ -125,7 +125,7 @@ unsigned count_signals(const Link&pin)
|
|||
const Nexus*nex = pin.nexus();
|
||||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
if (dynamic_cast<const NetNet*>(cur))
|
||||
|
|
@ -142,7 +142,7 @@ const NetNet* find_link_signal(const NetObj*net, unsigned pin, unsigned&bidx)
|
|||
for (const Link*clnk = nex->first_nlink()
|
||||
; clnk ; clnk = clnk->next_nlink()) {
|
||||
|
||||
const NetObj*cur;
|
||||
const NetPins*cur;
|
||||
unsigned cpin;
|
||||
clnk->cur_link(cur, cpin);
|
||||
|
||||
|
|
@ -171,8 +171,8 @@ Link* find_next_output(Link*lnk)
|
|||
return 0;
|
||||
}
|
||||
|
||||
NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
|
||||
: scope_(s), name_(n), npins_(np), delay1_(0), delay2_(0), delay3_(0)
|
||||
NetPins::NetPins(unsigned npins)
|
||||
: npins_(npins)
|
||||
{
|
||||
pins_ = new Link[npins_];
|
||||
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
||||
|
|
@ -181,22 +181,12 @@ NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
|
|||
}
|
||||
}
|
||||
|
||||
NetObj::~NetObj()
|
||||
NetPins::~NetPins()
|
||||
{
|
||||
delete[]pins_;
|
||||
}
|
||||
|
||||
NetScope* NetObj::scope()
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
const NetScope* NetObj::scope() const
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
Link& NetObj::pin(unsigned idx)
|
||||
Link& NetPins::pin(unsigned idx)
|
||||
{
|
||||
if (idx >= npins_) {
|
||||
cerr << get_fileline() << ": internal error: pin("<<idx<<")"
|
||||
|
|
@ -209,12 +199,31 @@ Link& NetObj::pin(unsigned idx)
|
|||
return pins_[idx];
|
||||
}
|
||||
|
||||
const Link& NetObj::pin(unsigned idx) const
|
||||
const Link& NetPins::pin(unsigned idx) const
|
||||
{
|
||||
assert(idx < npins_);
|
||||
return pins_[idx];
|
||||
}
|
||||
|
||||
NetObj::NetObj(NetScope*s, perm_string n, unsigned np)
|
||||
: NetPins(np), scope_(s), name_(n), delay1_(0), delay2_(0), delay3_(0)
|
||||
{
|
||||
}
|
||||
|
||||
NetObj::~NetObj()
|
||||
{
|
||||
}
|
||||
|
||||
NetScope* NetObj::scope()
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
const NetScope* NetObj::scope() const
|
||||
{
|
||||
return scope_;
|
||||
}
|
||||
|
||||
NetNode::NetNode(NetScope*s, perm_string n, unsigned npins)
|
||||
: NetObj(s, n, npins), node_next_(0), node_prev_(0), design_(0)
|
||||
{
|
||||
|
|
@ -226,6 +235,19 @@ NetNode::~NetNode()
|
|||
design_->del_node(this);
|
||||
}
|
||||
|
||||
NetBranch::NetBranch(discipline_t*dis)
|
||||
: NetPins(2), discipline_(dis)
|
||||
{
|
||||
pin(0).set_name(perm_string::literal("A"), 0);
|
||||
pin(0).set_dir(Link::PASSIVE);
|
||||
pin(1).set_name(perm_string::literal("B"), 0);
|
||||
pin(1).set_dir(Link::PASSIVE);
|
||||
}
|
||||
|
||||
NetBranch::~NetBranch()
|
||||
{
|
||||
}
|
||||
|
||||
NetBus::NetBus(NetScope*s, unsigned pin_count)
|
||||
: NetObj(s, perm_string::literal(""), pin_count)
|
||||
{
|
||||
|
|
@ -625,6 +647,17 @@ void NetNet::set_isint(bool flag)
|
|||
isint_ = flag;
|
||||
}
|
||||
|
||||
discipline_t* NetNet::get_discipline() const
|
||||
{
|
||||
return discipline_;
|
||||
}
|
||||
|
||||
void NetNet::set_discipline(discipline_t*dis)
|
||||
{
|
||||
ivl_assert(*this, discipline_ == 0);
|
||||
discipline_ = dis;
|
||||
}
|
||||
|
||||
long NetNet::lsb() const
|
||||
{
|
||||
return lsb_;
|
||||
|
|
|
|||
88
netlist.h
88
netlist.h
|
|
@ -68,6 +68,8 @@ class NetTaskDef;
|
|||
class NetEvTrig;
|
||||
class NetEvWait;
|
||||
|
||||
class nature_t;
|
||||
class discipline_t;
|
||||
|
||||
struct target;
|
||||
struct functor_t;
|
||||
|
|
@ -76,6 +78,24 @@ ostream& operator << (ostream&o, ivl_variable_type_t val);
|
|||
|
||||
extern void join_island(NetObj*obj);
|
||||
|
||||
class NetPins : public LineInfo {
|
||||
|
||||
public:
|
||||
explicit NetPins(unsigned npins);
|
||||
virtual ~NetPins();
|
||||
|
||||
unsigned pin_count() const { return npins_; }
|
||||
|
||||
Link&pin(unsigned idx);
|
||||
const Link&pin(unsigned idx) const;
|
||||
|
||||
void dump_node_pins(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
Link*pins_;
|
||||
const unsigned npins_;
|
||||
};
|
||||
|
||||
/* =========
|
||||
* A NetObj is anything that has any kind of behavior in the
|
||||
* netlist. Nodes can be gates, registers, etc. and are linked
|
||||
|
|
@ -96,7 +116,7 @@ extern void join_island(NetObj*obj);
|
|||
* interpretation of the rise/fall/decay times is typically left to
|
||||
* the target to properly interpret.
|
||||
*/
|
||||
class NetObj : public Attrib, public virtual LineInfo {
|
||||
class NetObj : public NetPins, public Attrib {
|
||||
|
||||
public:
|
||||
public:
|
||||
|
|
@ -110,8 +130,6 @@ class NetObj : public Attrib, public virtual LineInfo {
|
|||
|
||||
perm_string name() const { return name_; }
|
||||
|
||||
unsigned pin_count() const { return npins_; }
|
||||
|
||||
const NetExpr* rise_time() const { return delay1_; }
|
||||
const NetExpr* fall_time() const { return delay2_; }
|
||||
const NetExpr* decay_time() const { return delay3_; }
|
||||
|
|
@ -120,17 +138,11 @@ class NetObj : public Attrib, public virtual LineInfo {
|
|||
void fall_time(const NetExpr* d) { delay2_ = d; }
|
||||
void decay_time(const NetExpr* d) { delay3_ = d; }
|
||||
|
||||
Link&pin(unsigned idx);
|
||||
const Link&pin(unsigned idx) const;
|
||||
|
||||
void dump_node_pins(ostream&, unsigned) const;
|
||||
void dump_obj_attr(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
NetScope*scope_;
|
||||
perm_string name_;
|
||||
Link*pins_;
|
||||
const unsigned npins_;
|
||||
const NetExpr* delay1_;
|
||||
const NetExpr* delay2_;
|
||||
const NetExpr* delay3_;
|
||||
|
|
@ -150,11 +162,31 @@ class IslandBranch {
|
|||
struct ivl_island_s* island;
|
||||
};
|
||||
|
||||
/*
|
||||
* A NetBranch is a construct of Verilog-A that is a branch between
|
||||
* two nodes. The branch has exactly 2 pins and a discipline.
|
||||
*
|
||||
* pin(0) is the source of flow through a branch and the plus side of
|
||||
* potential. Pin(1) is the sink of flow and the minus (or ground) of
|
||||
* potential.
|
||||
*/
|
||||
class NetBranch : public NetPins {
|
||||
|
||||
public:
|
||||
explicit NetBranch(discipline_t*dis);
|
||||
explicit NetBranch(discipline_t*dis, perm_string name);
|
||||
~NetBranch();
|
||||
|
||||
private:
|
||||
discipline_t*discipline_;
|
||||
perm_string name_;
|
||||
};
|
||||
|
||||
class Link {
|
||||
|
||||
friend void connect(Link&, Link&);
|
||||
friend void connect(Nexus*, Link&);
|
||||
friend class NetObj;
|
||||
friend class NetPins;
|
||||
friend class Nexus;
|
||||
|
||||
public:
|
||||
|
|
@ -192,8 +224,8 @@ class Link {
|
|||
void set_init(verinum::V val);
|
||||
verinum::V get_init() const;
|
||||
|
||||
void cur_link(NetObj*&net, unsigned &pin);
|
||||
void cur_link(const NetObj*&net, unsigned &pin) const;
|
||||
void cur_link(NetPins*&net, unsigned &pin);
|
||||
void cur_link(const NetPins*&net, unsigned &pin) const;
|
||||
|
||||
// Get a pointer to the nexus that represents all the links
|
||||
// connected to me.
|
||||
|
|
@ -220,8 +252,8 @@ class Link {
|
|||
|
||||
// Return information about the object that this link is
|
||||
// a part of.
|
||||
const NetObj*get_obj() const;
|
||||
NetObj*get_obj();
|
||||
const NetPins*get_obj() const;
|
||||
NetPins*get_obj();
|
||||
unsigned get_pin() const;
|
||||
|
||||
// A link of an object (sometimes called a "pin") has a
|
||||
|
|
@ -234,7 +266,7 @@ class Link {
|
|||
private:
|
||||
// The NetNode manages these. They point back to the
|
||||
// NetNode so that following the links can get me here.
|
||||
NetObj *node_;
|
||||
NetPins *node_;
|
||||
unsigned pin_;
|
||||
|
||||
DIR dir_;
|
||||
|
|
@ -529,6 +561,10 @@ class NetNet : public NetObj {
|
|||
bool get_isint() const;
|
||||
void set_isint(bool);
|
||||
|
||||
/* Attach a discipline to the net. */
|
||||
discipline_t* get_discipline() const;
|
||||
void set_discipline(discipline_t*dis);
|
||||
|
||||
/* These methods return the msb and lsb indices for the most
|
||||
significant and least significant bits. These are signed
|
||||
longs, and may be different from pin numbers. For example,
|
||||
|
|
@ -596,6 +632,7 @@ class NetNet : public NetObj {
|
|||
ivl_variable_type_t data_type_;
|
||||
bool signed_;
|
||||
bool isint_; // original type of integer
|
||||
discipline_t*discipline_;
|
||||
|
||||
long msb_, lsb_;
|
||||
const unsigned dimensions_;
|
||||
|
|
@ -2894,6 +2931,27 @@ class NetEUFunc : public NetExpr {
|
|||
NetEUFunc& operator= (const NetEUFunc&);
|
||||
};
|
||||
|
||||
/*
|
||||
* A call to a nature access function for a branch.
|
||||
*/
|
||||
class NetEAccess : public NetExpr {
|
||||
|
||||
public:
|
||||
explicit NetEAccess(NetBranch*br, nature_t*nat);
|
||||
~NetEAccess();
|
||||
|
||||
virtual ivl_variable_type_t expr_type() const;
|
||||
virtual void dump(ostream&) const;
|
||||
|
||||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual NetEAccess*dup_expr() const;
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
|
||||
private:
|
||||
NetBranch*branch_;
|
||||
nature_t*nature_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A call to a user defined task is elaborated into this object. This
|
||||
* contains a pointer to the elaborated task definition, but is a
|
||||
|
|
|
|||
9
parse.y
9
parse.y
|
|
@ -180,6 +180,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
|||
PEventStatement*event_statement;
|
||||
Statement*statement;
|
||||
svector<Statement*>*statement_list;
|
||||
AStatement*astatement;
|
||||
|
||||
PTaskFuncArg function_type;
|
||||
|
||||
|
|
@ -282,6 +283,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
|||
%type <pform_name> hierarchy_identifier
|
||||
%type <expr> expression expr_primary expr_mintypmax
|
||||
%type <expr> lpvalue
|
||||
%type <expr> branch_probe_expression
|
||||
%type <expr> delay_value delay_value_simple
|
||||
%type <exprs> delay1 delay3 delay3_opt delay_value_list
|
||||
%type <exprs> expression_list_with_nuls expression_list_proper
|
||||
|
|
@ -302,6 +304,8 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
|||
%type <statement> statement statement_or_null
|
||||
%type <statement_list> statement_list
|
||||
|
||||
%type <astatement> analog_statement
|
||||
|
||||
%type <letter> spec_polarity
|
||||
%type <perm_strings> specify_path_identifiers
|
||||
|
||||
|
|
@ -858,7 +862,9 @@ event_expression
|
|||
function name really is a nature attribute identifier. */
|
||||
branch_probe_expression
|
||||
: IDENTIFIER '(' IDENTIFIER ',' IDENTIFIER ')'
|
||||
{ $$ = pform_make_branch_probe_expression(@1, $1, $3, $5); }
|
||||
| IDENTIFIER '(' IDENTIFIER ')'
|
||||
{ $$ = pform_make_branch_probe_expression(@1, $1, $3); }
|
||||
;
|
||||
|
||||
expression
|
||||
|
|
@ -2111,6 +2117,7 @@ module_item
|
|||
}
|
||||
|
||||
| attribute_list_opt K_analog analog_statement
|
||||
{ pform_make_analog_behavior(@2, AProcess::PR_ALWAYS, $3); }
|
||||
|
||||
/* The task declaration rule matches the task declaration
|
||||
header, then pushes the function scope. This causes the
|
||||
|
|
@ -3742,7 +3749,7 @@ statement_or_null
|
|||
|
||||
analog_statement
|
||||
: branch_probe_expression K_CONTRIBUTE expression ';'
|
||||
{ yyerror(@1, "sorry: Analog contribution statements not supported."); }
|
||||
{ $$ = pform_contribution_statement(@2, $1, $3); }
|
||||
;
|
||||
|
||||
/* Task items are, other than the statement, task port items and
|
||||
|
|
|
|||
11
pform.cc
11
pform.cc
|
|
@ -194,6 +194,17 @@ static void pform_put_behavior_in_scope(PProcess*pp)
|
|||
lexical_scope->behaviors.push_back(pp);
|
||||
}
|
||||
|
||||
void pform_put_behavior_in_scope(AProcess*pp)
|
||||
{
|
||||
if (pform_cur_generate)
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
pform_cur_generate->lexical_scope->analog_behaviors.push_back(pp);
|
||||
else
|
||||
pform_cur_generate->analog_behaviors.push_back(pp);
|
||||
else
|
||||
lexical_scope->analog_behaviors.push_back(pp);
|
||||
}
|
||||
|
||||
void pform_set_default_nettype(NetNet::Type type,
|
||||
const char*file, unsigned lineno)
|
||||
{
|
||||
|
|
|
|||
15
pform.h
15
pform.h
|
|
@ -24,6 +24,7 @@
|
|||
# include "named.h"
|
||||
# include "Module.h"
|
||||
# include "Statement.h"
|
||||
# include "AStatement.h"
|
||||
# include "PGate.h"
|
||||
# include "PExpr.h"
|
||||
# include "PTask.h"
|
||||
|
|
@ -179,6 +180,7 @@ 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);
|
||||
|
||||
extern verinum* pform_verinum_with_size(verinum*s, verinum*val,
|
||||
const char*file, unsigned lineno);
|
||||
|
|
@ -389,4 +391,17 @@ extern void pform_attach_discipline(const struct vlltype&loc,
|
|||
extern void pform_dump(ostream&out, const nature_t*);
|
||||
extern void pform_dump(ostream&out, const discipline_t*);
|
||||
|
||||
/* ** pform_analog.cc
|
||||
*/
|
||||
extern void pform_make_analog_behavior(const struct vlltype&loc,
|
||||
AProcess::Type type, AStatement*st);
|
||||
|
||||
extern AStatement*pform_contribution_statement(const struct vlltype&loc,
|
||||
PExpr*lval, PExpr*rval);
|
||||
|
||||
extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*n1, char*n2);
|
||||
|
||||
extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*branch);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 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
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include "config.h"
|
||||
# include "compiler.h"
|
||||
# include "pform.h"
|
||||
# include "parse_misc.h"
|
||||
# include "AStatement.h"
|
||||
|
||||
AStatement* pform_contribution_statement(const struct vlltype&loc,
|
||||
PExpr*lval, PExpr*rval)
|
||||
{
|
||||
AContrib*tmp = new AContrib(lval, rval);
|
||||
FILE_NAME(tmp, loc);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void pform_make_analog_behavior(const struct vlltype&loc, AProcess::Type pt,
|
||||
AStatement*statement)
|
||||
{
|
||||
AProcess*proc = new AProcess(pt, statement);
|
||||
FILE_NAME(proc, loc);
|
||||
|
||||
pform_put_behavior_in_scope(proc);
|
||||
}
|
||||
|
||||
PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*n1, char*n2)
|
||||
{
|
||||
vector<PExpr*> parms (2);
|
||||
parms[0] = new PEIdent(lex_strings.make(n1));
|
||||
FILE_NAME(parms[0], loc);
|
||||
|
||||
parms[1] = new PEIdent(lex_strings.make(n2));
|
||||
FILE_NAME(parms[1], loc);
|
||||
|
||||
PECallFunction*res = new PECallFunction(lex_strings.make(name), parms);
|
||||
FILE_NAME(res, loc);
|
||||
return res;
|
||||
}
|
||||
|
||||
PExpr* pform_make_branch_probe_expression(const struct vlltype&loc,
|
||||
char*name, char*branch_name)
|
||||
{
|
||||
vector<PExpr*> parms (1);
|
||||
parms[0] = new PEIdent(lex_strings.make(branch_name));
|
||||
FILE_NAME(parms[0], loc);
|
||||
|
||||
PECallFunction*res = new PECallFunction(lex_strings.make(name), parms);
|
||||
FILE_NAME(res, loc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
map<perm_string,nature_t*> natures;
|
||||
map<perm_string,discipline_t*> disciplines;
|
||||
map<perm_string,nature_t*> access_function_nature;
|
||||
|
||||
static perm_string nature_name = perm_string::perm_string();
|
||||
static perm_string nature_access = perm_string::perm_string();
|
||||
|
|
@ -62,9 +63,24 @@ void pform_end_nature(const struct vlltype&loc)
|
|||
}
|
||||
|
||||
nature_t*tmp = new nature_t(nature_name, nature_access);
|
||||
FILE_NAME(tmp, loc);
|
||||
|
||||
natures[nature_name] = tmp;
|
||||
|
||||
FILE_NAME(tmp, loc);
|
||||
// Make sure the access function is not used by multiple
|
||||
// different natures.
|
||||
if (nature_t*dup_access_nat = access_function_nature[nature_access]) {
|
||||
cerr << tmp->get_fileline() << ": error: "
|
||||
<< "Access function name " << nature_access
|
||||
<< " is already used by nature " << dup_access_nat->name()
|
||||
<< " declared at " << dup_access_nat->get_fileline()
|
||||
<< "." << endl;
|
||||
error_count += 1;
|
||||
}
|
||||
|
||||
// Map the access functio back to the nature so that
|
||||
// expressions that use the access function can find it.
|
||||
access_function_nature[nature_access] = tmp;
|
||||
|
||||
nature_name = perm_string::perm_string();
|
||||
nature_access = perm_string::perm_string();
|
||||
|
|
@ -189,6 +205,7 @@ void pform_attach_discipline(const struct vlltype&loc,
|
|||
error_count += 1;
|
||||
|
||||
} else {
|
||||
cur_net->set_data_type(IVL_VT_REAL);
|
||||
cur_net->set_discipline(discipline);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,9 +180,9 @@ void PECallFunction::dump(ostream &out) const
|
|||
{
|
||||
out << path_ << "(";
|
||||
|
||||
if (parms_.count() > 0) {
|
||||
if (parms_.size() > 0) {
|
||||
if (parms_[0]) parms_[0]->dump(out);
|
||||
for (unsigned idx = 1; idx < parms_.count(); ++idx) {
|
||||
for (unsigned idx = 1; idx < parms_.size(); ++idx) {
|
||||
out << ", ";
|
||||
if (parms_[idx]) parms_[idx]->dump(out);
|
||||
}
|
||||
|
|
@ -537,6 +537,24 @@ void Statement::dump(ostream&out, unsigned ind) const
|
|||
<< " */ ;" << endl;
|
||||
}
|
||||
|
||||
void AStatement::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
/* I give up. I don't know what type this statement is,
|
||||
so just print the C++ typeid and let the user figure
|
||||
it out. */
|
||||
out << setw(ind) << "";
|
||||
out << "/* " << get_fileline() << ": " << typeid(*this).name()
|
||||
<< " */ ;" << endl;
|
||||
}
|
||||
|
||||
void AContrib::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "";
|
||||
out << *lval_ << " <+ " << *rval_
|
||||
<< "; /* " << get_fileline() << " */"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
void PAssign::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "";
|
||||
|
|
@ -850,6 +868,32 @@ void PProcess::dump(ostream&out, unsigned ind) const
|
|||
statement_->dump(out, ind+2);
|
||||
}
|
||||
|
||||
void AProcess::dump(ostream&out, unsigned ind) const
|
||||
{
|
||||
switch (type_) {
|
||||
case AProcess::PR_INITIAL:
|
||||
out << setw(ind) << "" << "analog initial";
|
||||
break;
|
||||
case AProcess::PR_ALWAYS:
|
||||
out << setw(ind) << "" << "analog";
|
||||
break;
|
||||
}
|
||||
|
||||
out << " /* " << get_fileline() << " */" << endl;
|
||||
|
||||
for (map<perm_string,PExpr*>::const_iterator idx = attributes.begin()
|
||||
; idx != attributes.end() ; idx++ ) {
|
||||
|
||||
out << setw(ind+2) << "" << "(* " << (*idx).first;
|
||||
if ((*idx).second) {
|
||||
out << " = " << *(*idx).second;
|
||||
}
|
||||
out << " *)" << endl;
|
||||
}
|
||||
|
||||
statement_->dump(out, ind+2);
|
||||
}
|
||||
|
||||
void PSpecPath::dump(std::ostream&out, unsigned ind) const
|
||||
{
|
||||
out << setw(ind) << "" << "specify path ";
|
||||
|
|
@ -947,6 +991,11 @@ void PGenerate::dump(ostream&out, unsigned indent) const
|
|||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
for (list<AProcess*>::const_iterator idx = analog_behaviors.begin()
|
||||
; idx != analog_behaviors.end() ; idx++) {
|
||||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
for (list<PGenerate*>::const_iterator idx = generate_schemes.begin()
|
||||
; idx != generate_schemes.end() ; idx++) {
|
||||
(*idx)->dump(out, indent+2);
|
||||
|
|
@ -1132,6 +1181,11 @@ void Module::dump(ostream&out) const
|
|||
(*behav)->dump(out, 4);
|
||||
}
|
||||
|
||||
for (list<AProcess*>::const_iterator idx = analog_behaviors.begin()
|
||||
; idx != analog_behaviors.end() ; idx++) {
|
||||
(*idx)->dump(out, 4);
|
||||
}
|
||||
|
||||
for (list<PSpecPath*>::const_iterator spec = specify_paths.begin()
|
||||
; spec != specify_paths.end()
|
||||
; spec ++ ) {
|
||||
|
|
|
|||
55
svector.h
55
svector.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __svector_H
|
||||
#define __svector_H
|
||||
/*
|
||||
* Copyright (c) 1999 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1999-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,12 +20,10 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: svector.h,v 1.11 2007/03/22 16:08:17 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "config.h"
|
||||
# include <string>
|
||||
# include <vector>
|
||||
# include <assert.h>
|
||||
|
||||
/*
|
||||
|
|
@ -106,41 +104,18 @@ template <> inline svector<std::string>::svector(unsigned size)
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* $Log: svector.h,v $
|
||||
* Revision 1.11 2007/03/22 16:08:17 steve
|
||||
* Spelling fixes from Larry
|
||||
*
|
||||
* Revision 1.10 2005/06/14 19:13:43 steve
|
||||
* gcc3/4 compile errors.
|
||||
*
|
||||
* Revision 1.9 2003/07/23 02:35:44 steve
|
||||
* Inline the svector<string> constructor.
|
||||
*
|
||||
* Revision 1.8 2003/07/16 00:54:07 steve
|
||||
* Needs the config.h header.
|
||||
*
|
||||
* Revision 1.7 2003/07/15 05:07:13 steve
|
||||
* Move PUdp constructor into compiled file.
|
||||
*
|
||||
* Revision 1.6 2002/08/12 01:35:00 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.5 2000/02/23 02:56:55 steve
|
||||
* Macintosh compilers do not support ident.
|
||||
*
|
||||
* Revision 1.4 1999/06/15 03:44:53 steve
|
||||
* Get rid of the STL vector template.
|
||||
*
|
||||
* Revision 1.3 1999/05/06 04:37:17 steve
|
||||
* Get rid of list<lgate> types.
|
||||
*
|
||||
* Revision 1.2 1999/05/01 02:57:53 steve
|
||||
* Handle much more complex event expressions.
|
||||
*
|
||||
* Revision 1.1 1999/04/29 02:16:26 steve
|
||||
* Parse OR of event expressions.
|
||||
*
|
||||
*/
|
||||
* This is a convenience function that converts an svector to a
|
||||
* vector. This is to ease the transition from svector to vector so
|
||||
* that the svector class can be gradually removed.
|
||||
*/
|
||||
template <class T> inline std::vector<T> vector_from_svector(const svector<T>&that)
|
||||
{
|
||||
std::vector<T> res (that.count());
|
||||
for (unsigned idx = 0 ; idx < that.count() ; idx += 1)
|
||||
res[idx] = that[idx];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ extern "C" ivl_expr_t ivl_expr_oper3(ivl_expr_t net)
|
|||
|
||||
extern "C" ivl_parameter_t ivl_expr_parameter(ivl_expr_t net)
|
||||
{
|
||||
assert(net);
|
||||
switch (net->type_) {
|
||||
case IVL_EX_NUMBER:
|
||||
return net->u_.number_.parameter;
|
||||
|
|
|
|||
|
|
@ -149,6 +149,19 @@ ivl_expr_t dll_target::expr_from_value_(const verinum&val)
|
|||
return expr;
|
||||
}
|
||||
|
||||
void dll_target::expr_access_func(const NetEAccess*net)
|
||||
{
|
||||
assert(expr_ == 0);
|
||||
// Make a stub Branch Access Function expression node.
|
||||
expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s));
|
||||
expr_->type_ = IVL_EX_BACCESS;
|
||||
expr_->value_ = IVL_VT_REAL;
|
||||
expr_->file = net->get_file();
|
||||
expr_->lineno = net->get_lineno();
|
||||
expr_->width_ = 1;
|
||||
expr_->signed_= 1;
|
||||
}
|
||||
|
||||
void dll_target::expr_binary(const NetEBinary*net)
|
||||
{
|
||||
assert(expr_ == 0);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
bool dll_target::process(const NetProcTop*net)
|
||||
{
|
||||
bool rc_flag = true;
|
||||
|
||||
ivl_process_t obj = (struct ivl_process_s*)
|
||||
calloc(1, sizeof(struct ivl_process_s));
|
||||
|
||||
|
|
@ -68,7 +70,7 @@ bool dll_target::process(const NetProcTop*net)
|
|||
assert(stmt_cur_ == 0);
|
||||
stmt_cur_ = (struct ivl_statement_s*)calloc(1, sizeof*stmt_cur_);
|
||||
assert(stmt_cur_);
|
||||
net->statement()->emit_proc(this);
|
||||
rc_flag = net->statement()->emit_proc(this) && rc_flag;
|
||||
|
||||
assert(stmt_cur_);
|
||||
obj->stmt_ = stmt_cur_;
|
||||
|
|
@ -78,7 +80,7 @@ bool dll_target::process(const NetProcTop*net)
|
|||
obj->next_ = des_.threads_;
|
||||
des_.threads_ = obj;
|
||||
|
||||
return true;
|
||||
return rc_flag;
|
||||
}
|
||||
|
||||
void dll_target::task_def(const NetScope*net)
|
||||
|
|
@ -188,7 +190,7 @@ void dll_target::make_assign_lvals_(const NetAssignBase*net)
|
|||
|
||||
/*
|
||||
*/
|
||||
void dll_target::proc_assign(const NetAssign*net)
|
||||
bool dll_target::proc_assign(const NetAssign*net)
|
||||
{
|
||||
assert(stmt_cur_);
|
||||
assert(stmt_cur_->type_ == IVL_ST_NONE);
|
||||
|
|
@ -212,6 +214,8 @@ void dll_target::proc_assign(const NetAssign*net)
|
|||
stmt_cur_->u_.assign_.delay = expr_;
|
||||
expr_ = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -397,6 +401,8 @@ bool dll_target::proc_cassign(const NetCAssign*net)
|
|||
|
||||
bool dll_target::proc_condit(const NetCondit*net)
|
||||
{
|
||||
bool rc_flag = true;
|
||||
|
||||
assert(stmt_cur_);
|
||||
assert(stmt_cur_->type_ == IVL_ST_NONE);
|
||||
FILE_NAME(stmt_cur_, net);
|
||||
|
|
@ -408,18 +414,20 @@ bool dll_target::proc_condit(const NetCondit*net)
|
|||
assert(expr_ == 0);
|
||||
net->expr()->expr_scan(this);
|
||||
stmt_cur_->u_.condit_.cond_ = expr_;
|
||||
if (expr_ == 0)
|
||||
rc_flag = false;
|
||||
expr_ = 0;
|
||||
|
||||
ivl_statement_t save_cur_ = stmt_cur_;
|
||||
|
||||
stmt_cur_ = save_cur_->u_.condit_.stmt_+0;
|
||||
bool flag = net->emit_recurse_if(this);
|
||||
rc_flag = net->emit_recurse_if(this) && rc_flag;
|
||||
|
||||
stmt_cur_ = save_cur_->u_.condit_.stmt_+1;
|
||||
flag = flag && net->emit_recurse_else(this);
|
||||
rc_flag = net->emit_recurse_else(this) && rc_flag;
|
||||
|
||||
stmt_cur_ = save_cur_;
|
||||
return flag;
|
||||
return rc_flag;
|
||||
}
|
||||
|
||||
bool dll_target::proc_deassign(const NetDeassign*net)
|
||||
|
|
|
|||
3
t-dll.h
3
t-dll.h
|
|
@ -111,7 +111,7 @@ struct dll_target : public target_t, public expr_scan_t {
|
|||
/* These methods and members are used for forming the
|
||||
statements of a thread. */
|
||||
struct ivl_statement_s*stmt_cur_;
|
||||
void proc_assign(const NetAssign*);
|
||||
bool proc_assign(const NetAssign*);
|
||||
void proc_assign_nb(const NetAssignNB*);
|
||||
bool proc_block(const NetBlock*);
|
||||
void proc_case(const NetCase*);
|
||||
|
|
@ -134,6 +134,7 @@ struct dll_target : public target_t, public expr_scan_t {
|
|||
void task_def(const NetScope*);
|
||||
|
||||
struct ivl_expr_s*expr_;
|
||||
void expr_access_func(const NetEAccess*);
|
||||
void expr_binary(const NetEBinary*);
|
||||
void expr_concat(const NetEConcat*);
|
||||
void expr_const(const NetEConst*);
|
||||
|
|
|
|||
|
|
@ -243,10 +243,11 @@ bool target_t::process(const NetProcTop*top)
|
|||
return top->statement()->emit_proc(this);
|
||||
}
|
||||
|
||||
void target_t::proc_assign(const NetAssign*)
|
||||
bool target_t::proc_assign(const NetAssign*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
"Unhandled procedural assignment." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void target_t::proc_assign_nb(const NetAssignNB*)
|
||||
|
|
@ -377,6 +378,12 @@ expr_scan_t::~expr_scan_t()
|
|||
{
|
||||
}
|
||||
|
||||
void expr_scan_t::expr_access_func(const NetEAccess*)
|
||||
{
|
||||
cerr << "expr_scan_t (" << typeid(*this).name() << "): "
|
||||
"unhandled expr_access_func." << endl;
|
||||
}
|
||||
|
||||
void expr_scan_t::expr_const(const NetEConst*)
|
||||
{
|
||||
cerr << "expr_scan_t (" << typeid(*this).name() << "): "
|
||||
|
|
|
|||
3
target.h
3
target.h
|
|
@ -105,7 +105,7 @@ struct target_t {
|
|||
virtual bool process(const NetProcTop*);
|
||||
|
||||
/* Various kinds of process nodes are dispatched through these. */
|
||||
virtual void proc_assign(const NetAssign*);
|
||||
virtual bool proc_assign(const NetAssign*);
|
||||
virtual void proc_assign_nb(const NetAssignNB*);
|
||||
virtual bool proc_block(const NetBlock*);
|
||||
virtual void proc_case(const NetCase*);
|
||||
|
|
@ -133,6 +133,7 @@ struct target_t {
|
|||
of expressions. */
|
||||
struct expr_scan_t {
|
||||
virtual ~expr_scan_t();
|
||||
virtual void expr_access_func(const NetEAccess*);
|
||||
virtual void expr_const(const NetEConst*);
|
||||
virtual void expr_param(const NetEConstParam*);
|
||||
virtual void expr_rparam(const NetECRealParam*);
|
||||
|
|
|
|||
|
|
@ -43,16 +43,40 @@ static void show_array_expression(ivl_expr_t net, unsigned ind)
|
|||
ivl_signal_dimensions(sig), width, vt);
|
||||
}
|
||||
|
||||
static void show_branch_access_expression(ivl_expr_t net, unsigned ind)
|
||||
{
|
||||
fprintf(out, "%*s<Branch Access>\n", ind, "");
|
||||
|
||||
if (ivl_expr_value(net) != IVL_VT_REAL) {
|
||||
fprintf(out, "%*sERROR: Expecting type IVL_VT_REAL, got %s\n",
|
||||
ind, "", vt_type_string(net));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_binary_expression(ivl_expr_t net, unsigned ind)
|
||||
{
|
||||
unsigned width = ivl_expr_width(net);
|
||||
const char*sign = ivl_expr_signed(net)? "signed" : "unsigned";
|
||||
const char*vt = vt_type_string(net);
|
||||
|
||||
ivl_expr_t oper1 = ivl_expr_oper1(net);
|
||||
ivl_expr_t oper2 = ivl_expr_oper2(net);
|
||||
|
||||
fprintf(out, "%*s<\"%c\" width=%u, %s, type=%s>\n", ind, "",
|
||||
ivl_expr_opcode(net), width, sign, vt);
|
||||
show_expression(ivl_expr_oper1(net), ind+3);
|
||||
show_expression(ivl_expr_oper2(net), ind+3);
|
||||
if (oper1) {
|
||||
show_expression(oper1, ind+3);
|
||||
} else {
|
||||
fprintf(out, "%*sERROR: Missing operand 1\n", ind+3, "");
|
||||
stub_errors += 1;
|
||||
}
|
||||
if (oper2) {
|
||||
show_expression(oper2, ind+3);
|
||||
} else {
|
||||
fprintf(out, "%*sERROR: Missing operand 2\n", ind+3, "");
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
switch (ivl_expr_opcode(net)) {
|
||||
|
||||
|
|
@ -203,6 +227,10 @@ void show_expression(ivl_expr_t net, unsigned ind)
|
|||
show_array_expression(net, ind);
|
||||
break;
|
||||
|
||||
case IVL_EX_BACCESS:
|
||||
show_branch_access_expression(net, ind);
|
||||
break;
|
||||
|
||||
case IVL_EX_BINARY:
|
||||
show_binary_expression(net, ind);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -264,7 +264,12 @@ void show_statement(ivl_statement_t net, unsigned ind)
|
|||
ivl_statement_t f = ivl_stmt_cond_false(net);
|
||||
|
||||
fprintf(out, "%*sif (...)\n", ind, "");
|
||||
show_expression(ex, ind+4);
|
||||
if (ex) {
|
||||
show_expression(ex, ind+4);
|
||||
} else {
|
||||
fprintf(out, "%*sERROR: Condition expression is NIL;\n", ind+4, "");
|
||||
stub_errors += 1;
|
||||
}
|
||||
if (t)
|
||||
show_statement(t, ind+4);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue