diff --git a/AStatement.cc b/AStatement.cc new file mode 100644 index 000000000..a3617d6bf --- /dev/null +++ b/AStatement.cc @@ -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() +{ +} diff --git a/AStatement.h b/AStatement.h new file mode 100644 index 000000000..a1b2085ef --- /dev/null +++ b/AStatement.h @@ -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 +# 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 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 diff --git a/Makefile.in b/Makefile.in index a5be31f9b..a45841892 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/PExpr.cc b/PExpr.cc index 751d2be5f..bdb955c41 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -91,7 +91,7 @@ PEBShift::~PEBShift() { } -PECallFunction::PECallFunction(const pform_name_t&n, const svector &parms) +PECallFunction::PECallFunction(const pform_name_t&n, const vector &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&parms) +PECallFunction::PECallFunction(perm_string n, const vector&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 &parms) +: path_(n), parms_(vector_from_svector(parms)) +{ +} + +PECallFunction::PECallFunction(perm_string n, const svector&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; diff --git a/PExpr.h b/PExpr.h index e424355f2..3ef8f331f 100644 --- a/PExpr.h +++ b/PExpr.h @@ -705,10 +705,15 @@ class PETernary : public PExpr { */ class PECallFunction : public PExpr { public: - explicit PECallFunction(const pform_name_t&n, const svector &parms); + explicit PECallFunction(const pform_name_t&n, const vector &parms); // Call of system function (name is not hierarchical) - explicit PECallFunction(perm_string n, const svector &parms); + explicit PECallFunction(perm_string n, const vector &parms); explicit PECallFunction(perm_string n); + + // svector versions. Should be removed! + explicit PECallFunction(const pform_name_t&n, const svector &parms); + explicit PECallFunction(perm_string n, const svector &parms); + ~PECallFunction(); virtual bool is_constant(Module*) const; @@ -732,11 +737,12 @@ class PECallFunction : public PExpr { private: pform_name_t path_; - svector parms_; + vector 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, diff --git a/PScope.h b/PScope.h index d65244a03..545b502f3 100644 --- a/PScope.h +++ b/PScope.h @@ -24,6 +24,7 @@ # include class PEvent; +class AProcess; class PProcess; class PWire; @@ -50,6 +51,10 @@ class LexicalScope { mapwires; PWire* wires_find(perm_string name); + // Behaviors (processes) in this scope + list behaviors; + list analog_behaviors; + private: }; @@ -74,9 +79,6 @@ class PScope : public LexicalScope { // Named events in the scope. mapevents; - // Behaviors (processes) in this scope - list behaviors; - protected: void dump_wires_(ostream&out, unsigned indent) const; diff --git a/autoconf.sh b/autoconf.sh index 1ace2dc34..a92a6fa65 100644 --- a/autoconf.sh +++ b/autoconf.sh @@ -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=.. ) diff --git a/configure.in b/configure.in index 46529fbe9..1a975b173 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/cprop.cc b/cprop.cc index 9345b9ea8..56e3a7bf7 100644 --- a/cprop.cc +++ b/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); diff --git a/design_dump.cc b/design_dump.cc index 82f1f35bf..25e61d04e 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -27,6 +27,7 @@ # include # 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') { diff --git a/discipline.h b/discipline.h index 30932d49d..a79e538d2 100644 --- a/discipline.h +++ b/discipline.h @@ -71,5 +71,7 @@ class discipline_t : public LineInfo { extern map natures; extern map disciplines; + // Map access function name to the nature that it accesses. +extern map access_function_nature; #endif diff --git a/driver/iverilog.man b/driver/iverilog.man index 05d936a1d..3c74e9feb 100644 --- a/driver/iverilog.man +++ b/driver/iverilog.man @@ -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 diff --git a/dup_expr.cc b/dup_expr.cc index 7a8ec8a76..ad4bb7b0a 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -21,6 +21,15 @@ # include "netlist.h" # include +# 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 { diff --git a/elab_expr.cc b/elab_expr.cc index 73d0a3a27..e2ac32b21 100644 --- a/elab_expr.cc +++ b/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 (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; diff --git a/elab_net.cc b/elab_net.cc index deffb5404..0a37364dc 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -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); diff --git a/elab_pexpr.cc b/elab_pexpr.cc index e4cdbe087..588fb3d8d 100644 --- a/elab_pexpr.cc +++ b/elab_pexpr.cc @@ -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; diff --git a/elab_sig.cc b/elab_sig.cc index ae2221d1c..bb5c897e4 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -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)); diff --git a/emit.cc b/emit.cc index 4e2b652b6..785520a12 100644 --- a/emit.cc +++ b/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); diff --git a/ivl_target.h b/ivl_target.h index f04d90f8a..f198fe55d 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -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, diff --git a/link_const.cc b/link_const.cc index 60b68afa6..212fbf9e7 100644 --- a/link_const.cc +++ b/link_const.cc @@ -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(obj); + if (as_obj == 0 || as_obj->scope()->parent() != 0) continue; sig = dynamic_cast(cur->get_obj()); diff --git a/net_event.cc b/net_event.cc index a214cdd48..3535e7ea9 100644 --- a/net_event.cc +++ b/net_event.cc @@ -299,7 +299,7 @@ void NetEvProbe::find_similar_probes(list&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; diff --git a/net_expr.cc b/net_expr.cc index 4c15ddd7d..90ea8cbf8 100644 --- a/net_expr.cc +++ b/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; +} diff --git a/net_func.cc b/net_func.cc index 5a4d4e5bb..564d16258 100644 --- a/net_func.cc +++ b/net_func.cc @@ -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; diff --git a/net_link.cc b/net_link.cc index f9ee2c19c..54271616b 100644 --- a/net_link.cc +++ b/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(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(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(lnk->get_obj()); pin = lnk->get_pin(); cerr << "internal error: No signal for nexus of " << obj->name() << " pin " << pin << "(" << diff --git a/net_nex_input.cc b/net_nex_input.cc index 0f97c2a44..f61393a17 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -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. */ diff --git a/net_tran.cc b/net_tran.cc index b28a28753..42b726307 100644 --- a/net_tran.cc +++ b/net_tran.cc @@ -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 (tmp_pins); + if (tmp == 0) + continue; // Skip self. if (tmp == obj) diff --git a/netlist.cc b/netlist.cc index 3d7eae4f1..9e10d52db 100644 --- a/netlist.cc +++ b/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(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("<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_; diff --git a/netlist.h b/netlist.h index 8967f9f03..80cb49b37 100644 --- a/netlist.h +++ b/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 diff --git a/parse.y b/parse.y index a26cdc1b8..015034559 100644 --- a/parse.y +++ b/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_list; + AStatement*astatement; PTaskFuncArg function_type; @@ -282,6 +283,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %type hierarchy_identifier %type expression expr_primary expr_mintypmax %type lpvalue +%type branch_probe_expression %type delay_value delay_value_simple %type delay1 delay3 delay3_opt delay_value_list %type 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_or_null %type statement_list +%type analog_statement + %type spec_polarity %type 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 diff --git a/pform.cc b/pform.cc index 7057376cc..51d80236b 100644 --- a/pform.cc +++ b/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) { diff --git a/pform.h b/pform.h index ae410c8e7..68d439396 100644 --- a/pform.h +++ b/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 diff --git a/pform_analog.cc b/pform_analog.cc new file mode 100644 index 000000000..55408452b --- /dev/null +++ b/pform_analog.cc @@ -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 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 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; +} diff --git a/pform_disciplines.cc b/pform_disciplines.cc index 82f78708b..2ce7aeedd 100644 --- a/pform_disciplines.cc +++ b/pform_disciplines.cc @@ -25,6 +25,7 @@ map natures; map disciplines; +map 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); } } diff --git a/pform_dump.cc b/pform_dump.cc index 729a30819..e9d65044f 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -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::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::const_iterator idx = analog_behaviors.begin() + ; idx != analog_behaviors.end() ; idx++) { + (*idx)->dump(out, indent+2); + } + for (list::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::const_iterator idx = analog_behaviors.begin() + ; idx != analog_behaviors.end() ; idx++) { + (*idx)->dump(out, 4); + } + for (list::const_iterator spec = specify_paths.begin() ; spec != specify_paths.end() ; spec ++ ) { diff --git a/svector.h b/svector.h index 09181966d..c0f262e4c 100644 --- a/svector.h +++ b/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 +# include # include /* @@ -106,41 +104,18 @@ template <> inline svector::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 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 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 inline std::vector vector_from_svector(const svector&that) +{ + std::vector res (that.count()); + for (unsigned idx = 0 ; idx < that.count() ; idx += 1) + res[idx] = that[idx]; + + return res; +} + #endif diff --git a/t-dll-api.cc b/t-dll-api.cc index 379053b45..f230dfa9f 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -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; diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 958e5d0f2..cbba20ded 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -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); diff --git a/t-dll-proc.cc b/t-dll-proc.cc index e3c693748..8e26f6d3d 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -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) diff --git a/t-dll.h b/t-dll.h index f5d6fd4b7..e3646417b 100644 --- a/t-dll.h +++ b/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*); diff --git a/target.cc b/target.cc index 10e900027..fef82cedb 100644 --- a/target.cc +++ b/target.cc @@ -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() << "): " diff --git a/target.h b/target.h index 03fba0886..bed4815e6 100644 --- a/target.h +++ b/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*); diff --git a/tgt-stub/expression.c b/tgt-stub/expression.c index 823d359a6..98092d127 100644 --- a/tgt-stub/expression.c +++ b/tgt-stub/expression.c @@ -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\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; diff --git a/tgt-stub/statement.c b/tgt-stub/statement.c index efdb7094d..bf560cf5f 100644 --- a/tgt-stub/statement.c +++ b/tgt-stub/statement.c @@ -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 diff --git a/tgt-vhdl/Makefile.in b/tgt-vhdl/Makefile.in new file mode 100644 index 000000000..bbd0cfeaf --- /dev/null +++ b/tgt-vhdl/Makefile.in @@ -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) diff --git a/tgt-vhdl/cast.cc b/tgt-vhdl/cast.cc new file mode 100644 index 000000000..2499e0c6f --- /dev/null +++ b/tgt-vhdl/cast.cc @@ -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 +#include + +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'); +} diff --git a/tgt-vhdl/configure.in b/tgt-vhdl/configure.in new file mode 100644 index 000000000..3223b50e8 --- /dev/null +++ b/tgt-vhdl/configure.in @@ -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) diff --git a/tgt-vhdl/display.cc b/tgt-vhdl/display.cc new file mode 100644 index 000000000..b230002cc --- /dev/null +++ b/tgt-vhdl/display.cc @@ -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 +#include +#include +#include +#include + +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; +} diff --git a/tgt-vhdl/expr.cc b/tgt-vhdl/expr.cc new file mode 100644 index 000000000..5e49f1857 --- /dev/null +++ b/tgt-vhdl/expr.cc @@ -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 +#include +#include + + +/* + * 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(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 +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(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()); +} diff --git a/tgt-vhdl/logic.cc b/tgt-vhdl/logic.cc new file mode 100644 index 000000000..80e5fe28e --- /dev/null +++ b/tgt-vhdl/logic.cc @@ -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 +#include +#include + + +/* + * 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); + } + } +} diff --git a/tgt-vhdl/lpm.cc b/tgt-vhdl/lpm.cc new file mode 100644 index 000000000..3c2af5ead --- /dev/null +++ b/tgt-vhdl/lpm.cc @@ -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 +#include + +/* + * 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; +} + diff --git a/tgt-vhdl/process.cc b/tgt-vhdl/process.cc new file mode 100644 index 000000000..cc2d9b649 --- /dev/null +++ b/tgt-vhdl/process.cc @@ -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 +#include +#include + +/* + * 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); +} diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc new file mode 100644 index 000000000..5768f3b7b --- /dev/null +++ b/tgt-vhdl/scope.cc @@ -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 +#include +#include +#include + +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 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 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::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(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(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(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(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 _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(_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(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::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(_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; +} + diff --git a/tgt-vhdl/stmt.cc b/tgt-vhdl/stmt.cc new file mode 100644 index 000000000..f1a5eb9c7 --- /dev/null +++ b/tgt-vhdl/stmt.cc @@ -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 +#include +#include +#include +#include + +/* + * 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 &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 +void make_assignment(vhdl_procedural *proc, stmt_container *container, + ivl_statement_t stmt, bool blocking) +{ + list 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::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(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(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(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(_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; + } +} diff --git a/tgt-vhdl/support.cc b/tgt-vhdl/support.cc new file mode 100644 index 000000000..9ad74a3ca --- /dev/null +++ b/tgt-vhdl/support.cc @@ -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 +#include + +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;"; +} diff --git a/tgt-vhdl/support.hh b/tgt-vhdl/support.hh new file mode 100644 index 000000000..234831c24 --- /dev/null +++ b/tgt-vhdl/support.hh @@ -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 diff --git a/tgt-vhdl/vhdl.cc b/tgt-vhdl/vhdl.cc new file mode 100644 index 000000000..a82f583d3 --- /dev/null +++ b/tgt-vhdl/vhdl.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 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 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; +} diff --git a/tgt-vhdl/vhdl.conf b/tgt-vhdl/vhdl.conf new file mode 100644 index 000000000..b96c4c05e --- /dev/null +++ b/tgt-vhdl/vhdl.conf @@ -0,0 +1,4 @@ +functor:cprop +functor:nodangle +-t:dll +flag:DLL=vhdl.tgt diff --git a/tgt-vhdl/vhdl_config.h.in b/tgt-vhdl/vhdl_config.h.in new file mode 100644 index 000000000..23193ebc2 --- /dev/null +++ b/tgt-vhdl/vhdl_config.h.in @@ -0,0 +1,49 @@ +/* vhdl_config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the 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 diff --git a/tgt-vhdl/vhdl_element.cc b/tgt-vhdl/vhdl_element.cc new file mode 100644 index 000000000..fc09fe158 --- /dev/null +++ b/tgt-vhdl/vhdl_element.cc @@ -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 +#include +#include +#include +#include +#include + + +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; +} diff --git a/tgt-vhdl/vhdl_element.hh b/tgt-vhdl/vhdl_element.hh new file mode 100644 index 000000000..f4248500b --- /dev/null +++ b/tgt-vhdl/vhdl_element.hh @@ -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 +#include +#include + +typedef std::list 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 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 + diff --git a/tgt-vhdl/vhdl_helper.hh b/tgt-vhdl/vhdl_helper.hh new file mode 100644 index 000000000..a9c4b7717 --- /dev/null +++ b/tgt-vhdl/vhdl_helper.hh @@ -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 +#include +#include + +template +void emit_children(std::ostream &of, + const std::list &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::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 +void delete_children(std::list &children) +{ + typename std::list::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 diff --git a/tgt-vhdl/vhdl_syntax.cc b/tgt-vhdl/vhdl_syntax.cc new file mode 100644 index 000000000..2de81f1c6 --- /dev/null +++ b/tgt-vhdl/vhdl_syntax.cc @@ -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 +#include +#include +#include + +vhdl_scope::vhdl_scope() + : parent_(NULL), init_(false), sig_assign_(true) +{ + +} + +vhdl_scope::~vhdl_scope() +{ + delete_children(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(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(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(of, scope_.get_decls(), level); + of << "begin"; + emit_children(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(of, scope_.get_decls(), level); + of << "begin"; + stmts_.emit(of, level); + of << "end process;"; +} + +stmt_container::~stmt_container() +{ + delete_children(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(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(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(exprs_); +} + +void vhdl_expr_list::emit(std::ostream &of, int level) const +{ + of << "("; + + int size = exprs_.size(); + std::list::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::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::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(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::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::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::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(of, scope_.get_decls(), level, ";"); + of << ") "; + newline(of, level); + of << "return " << type_->get_string() << " is"; + emit_children(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(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); +} diff --git a/tgt-vhdl/vhdl_syntax.hh b/tgt-vhdl/vhdl_syntax.hh new file mode 100644 index 000000000..4d5141e98 --- /dev/null +++ b/tgt-vhdl/vhdl_syntax.hh @@ -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 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 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 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 conc_stmt_list_t; + +/* + * A ' when ' clause that appears in several + * statement types. + */ +struct when_part_t { + vhdl_expr *value, *cond, *delay; +}; +typedef std::list 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 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 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 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_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 entity_list_t; + +#endif + diff --git a/tgt-vhdl/vhdl_target.h b/tgt-vhdl/vhdl_target.h new file mode 100644 index 000000000..5d5753379 --- /dev/null +++ b/tgt-vhdl/vhdl_target.h @@ -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 + +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 */ + diff --git a/tgt-vhdl/vhdl_type.cc b/tgt-vhdl/vhdl_type.cc new file mode 100644 index 000000000..e14c3fbe9 --- /dev/null +++ b/tgt-vhdl/vhdl_type.cc @@ -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 +#include +#include + + +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); +} diff --git a/tgt-vhdl/vhdl_type.hh b/tgt-vhdl/vhdl_type.hh new file mode 100644 index 000000000..f3bfb2211 --- /dev/null +++ b/tgt-vhdl/vhdl_type.hh @@ -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 diff --git a/tgt-vhdl/vhpi/finish.c b/tgt-vhdl/vhpi/finish.c new file mode 100644 index 000000000..7a544da0b --- /dev/null +++ b/tgt-vhdl/vhpi/finish.c @@ -0,0 +1,6 @@ +#include + +void finish(void) +{ + exit(0); +}