Merge branch 'master' into verilog-ams

This commit is contained in:
Stephen Williams 2008-08-29 19:03:34 -07:00
commit 1ca8241b88
85 changed files with 7716 additions and 521 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2007 Stephen Williams <steve@icarus.com>
* Copyright (c) 1998-2008 Stephen Williams <steve@icarus.com>
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -20,6 +20,7 @@
# include "config.h"
# include <iostream>
# include <cstring>
# include "PExpr.h"
# include "Module.h"
@ -128,6 +129,26 @@ PECallFunction::~PECallFunction()
{
}
bool PECallFunction::is_constant(Module*mod) const
{
/* Only $clog2 can be a constant system function. */
if (peek_tail_name(path_)[0] == '$') {
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: $clog2 takes a "
"single argument." << endl;
return false;
}
/* If the argument is constant $clog2 is constant. */
return parms_[0]->is_constant(mod);
}
return false; /* Most system functions are not constant. */
}
/* Checking for constant user functions goes here. */
return false;
}
PEConcat::PEConcat(const svector<PExpr*>&p, PExpr*r)
: parms_(p), repeat_(r)
{
@ -310,4 +331,3 @@ bool PEUnary::is_constant(Module*m) const
{
return expr_->is_constant(m);
}

View File

@ -708,6 +708,8 @@ class PECallFunction : public PExpr {
~PECallFunction();
virtual bool is_constant(Module*) const;
virtual void dump(ostream &) const;
virtual NetNet* elaborate_net(Design*des, NetScope*scope,
@ -719,6 +721,7 @@ class PECallFunction : public PExpr {
Link::strength_t drive1) const;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
int expr_wid, bool sys_task_arg) const;
virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const;
virtual unsigned test_width(Design*des, NetScope*scope,
unsigned min, unsigned lval,

View File

@ -21,9 +21,10 @@
#include "PTask.h"
PFunction::PFunction(perm_string name, PScope*parent)
PFunction::PFunction(perm_string name, PScope*parent, bool is_auto)
: PScope(name, parent), ports_(0), statement_(0)
{
is_auto_ = is_auto;
return_type_.type = PTF_NONE;
}

View File

@ -21,9 +21,10 @@
# include "PTask.h"
PTask::PTask(perm_string name, PScope*parent)
PTask::PTask(perm_string name, PScope*parent, bool is_auto)
: PScope(name, parent), ports_(0), statement_(0)
{
is_auto_ = is_auto;
}
PTask::~PTask()
@ -41,31 +42,3 @@ void PTask::set_statement(Statement*s)
assert(statement_ == 0);
statement_ = s;
}
/*
* $Log: PTask.cc,v $
* Revision 1.7 2002/08/12 01:34:58 steve
* conditional ident string using autoconfig.
*
* Revision 1.6 2001/07/25 03:10:48 steve
* Create a config.h.in file to hold all the config
* junk, and support gcc 3.0. (Stephan Boettcher)
*
* Revision 1.5 2001/04/19 03:04:47 steve
* Spurious assert of empty statemnt.
*
* Revision 1.4 2001/01/13 22:20:08 steve
* Parse parameters within nested scopes.
*
* Revision 1.3 2000/02/23 02:56:53 steve
* Macintosh compilers do not support ident.
*
* Revision 1.2 1999/07/24 02:11:19 steve
* Elaborate task input ports.
*
* Revision 1.1 1999/07/03 02:12:51 steve
* Elaborate user defined tasks.
*
*/

10
PTask.h
View File

@ -51,7 +51,7 @@ struct PTaskFuncArg {
class PTask : public PScope, public LineInfo {
public:
explicit PTask(perm_string name, PScope*parent);
explicit PTask(perm_string name, PScope*parent, bool is_auto);
~PTask();
void set_ports(svector<PWire *>*p);
@ -69,11 +69,14 @@ class PTask : public PScope, public LineInfo {
// Elaborate the statement to finish off the task definition.
void elaborate(Design*des, NetScope*scope) const;
bool is_auto() const { return is_auto_; };
void dump(ostream&, unsigned) const;
private:
svector<PWire*>*ports_;
Statement*statement_;
bool is_auto_;
private: // Not implemented
PTask(const PTask&);
@ -90,7 +93,7 @@ class PTask : public PScope, public LineInfo {
class PFunction : public PScope, public LineInfo {
public:
explicit PFunction(perm_string name, PScope*parent);
explicit PFunction(perm_string name, PScope*parent, bool is_auto);
~PFunction();
void set_ports(svector<PWire *>*p);
@ -105,12 +108,15 @@ class PFunction : public PScope, public LineInfo {
/* Elaborate the behavioral statement. */
void elaborate(Design *des, NetScope*) const;
bool is_auto() const { return is_auto_; };
void dump(ostream&, unsigned) const;
private:
PTaskFuncArg return_type_;
svector<PWire *> *ports_;
Statement *statement_;
bool is_auto_;
};
#endif

View File

@ -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=.. )

View File

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

View File

@ -1003,6 +1003,7 @@ void NetScope::dump(ostream&o) const
o << " generate block";
break;
}
if (is_auto()) o << " (automatic)";
o << endl;
for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1)
@ -1019,6 +1020,8 @@ void NetScope::dump(ostream&o) const
; pp != parameters.end() ; pp ++) {
o << " parameter ";
o << pp->second.type << " ";
if ((*pp).second.signed_flag)
o << "signed ";

View File

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

View File

@ -511,7 +511,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w
delete tmp;
}
verinum val (sub_expr_width, 8*sizeof(unsigned));
verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned));
NetEConst*sub = new NetEConst(val);
sub->set_line(*this);
@ -1550,7 +1550,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope,
// Recalculate the constant address with the adjusted base.
unsigned use_addr = net->array_index_to_address(addr);
if (addr < 0 || use_addr != (unsigned long)addr) {
verinum val (use_addr, 8*sizeof(use_addr));
verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr));
NetEConst*tmp = new NetEConst(val);
tmp->set_line(*this);
delete word_index;

View File

@ -2327,7 +2327,7 @@ NetNet* PEIdent::elaborate_net_array_(Design*des, NetScope*scope,
} while (0);
#else
if (name_tail.index.size() > sig->array_dimensions())
tmp = process_select_(des, scope, sig);
tmp = process_select_(des, scope, tmp);
#endif
return tmp;

View File

@ -25,6 +25,7 @@
# include "netmisc.h"
# include <cstdlib>
# include <cstring>
# include <iostream>
# include "ivl_assert.h"
@ -267,3 +268,40 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const
return tmp;
}
/* Reuse the routine from eval_tree.cc. */
NetExpr* evaluate_clog2(NetExpr*arg);
NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
{
/* For now only $clog2 can be a constant system function. */
if (peek_tail_name(path_)[0] == '$') {
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
if (parms_.size() != 1 || parms_[0] == 0) {
cerr << get_fileline() << ": error: $clog2 takes a "
"single argument." << endl;
des->errors += 1;
return 0;
}
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
eval_expr(arg);
NetExpr*rtn = evaluate_clog2(arg);
delete arg;
if (rtn != 0) {
rtn->set_line(*this);
return rtn;
}
}
cerr << get_fileline() << ": error: this is not a constant "
"system function (" << *this << ")." << endl;
des->errors += 1;
return 0;
}
/* Constant user function code goes here. */
cerr << get_fileline() << ": sorry: constant user functions are not "
"currently supported." << endl;
des->errors += 1;
return 0;
}

View File

@ -53,7 +53,7 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
assert(ex);
NetExpr*val = ex->elaborate_pexpr(des, scope);
if (val == 0) return;
NetExpr*msb = 0;
NetExpr*lsb = 0;
bool signed_flag = cur.signed_flag;
@ -71,19 +71,6 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
assert(lsb);
}
if (signed_flag) {
/* If explicitly signed, then say so. */
val->cast_signed(true);
} else if (cur.msb) {
/* If there is a range, then the signedness comes
from the type and not the expression. */
val->cast_signed(signed_flag);
} else {
/* otherwise, let the expression describe
itself. */
signed_flag = val->has_sign();
}
NetScope::range_t*range_list = 0;
for (Module::range_t*range = cur.range ; range ; range = range->next) {
NetScope::range_t*tmp = new NetScope::range_t;
@ -118,8 +105,29 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
range_list = tmp;
}
val = scope->set_parameter(name, val, cur.type, msb, lsb, signed_flag, range_list, cur);
assert(val);
/* Set the parameter expression to 0 if the evaluation failed. */
if (val == 0) {
val = scope->set_parameter(name, val, cur.type, msb, lsb,
signed_flag, range_list, cur);
delete val;
return;
}
if (signed_flag) {
/* If explicitly signed, then say so. */
val->cast_signed(true);
} else if (cur.msb) {
/* If there is a range, then the signedness comes
from the type and not the expression. */
val->cast_signed(signed_flag);
} else {
/* otherwise, let the expression describe
itself. */
signed_flag = val->has_sign();
}
val = scope->set_parameter(name, val, cur.type, msb, lsb, signed_flag,
range_list, cur);
delete val;
}
@ -142,6 +150,7 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope,
}
NetScope*task_scope = new NetScope(scope, use_name,
NetScope::TASK);
task_scope->is_auto((*cur).second->is_auto());
task_scope->set_line((*cur).second);
if (debug_scopes)
@ -171,6 +180,7 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope,
}
NetScope*func_scope = new NetScope(scope, use_name,
NetScope::FUNC);
func_scope->is_auto((*cur).second->is_auto());
func_scope->set_line((*cur).second);
if (debug_scopes)
@ -316,17 +326,17 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
// here because the parameter receiving the assignment may be
// in a scope not discovered by this pass.
while (! defparms.empty()) {
Module::named_expr_t cur = defparms.front();
defparms.pop_front();
typedef list<Module::named_expr_t>::const_iterator defparms_iter_t;
for (defparms_iter_t cur = defparms.begin()
; cur != defparms.end() ; cur ++) {
PExpr*ex = cur.second;
PExpr*ex = cur->second;
assert(ex);
NetExpr*val = ex->elaborate_pexpr(des, scope);
delete ex;
if (val == 0) continue;
scope->defparams.push_back(make_pair(cur.first, val));
scope->defparams.push_back(make_pair(cur->first, val));
}
// Evaluate the attributes. Evaluate them in the scope of the
@ -475,7 +485,12 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
container->genvar_tmp_val = genvar;
NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1);
NetEConst*test = dynamic_cast<NetEConst*>(test_ex);
assert(test);
if (test == 0) {
cerr << get_fileline() << ": error: Cannot evaluate genvar"
<< " conditional expression: " << *loop_test << endl;
des->errors += 1;
return false;
}
while (test->value().as_long()) {
// The actual name of the scope includes the genvar so
@ -522,7 +537,12 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container)
// Calculate the step for the loop variable.
NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1);
NetEConst*step = dynamic_cast<NetEConst*>(step_ex);
assert(step);
if (step == 0) {
cerr << get_fileline() << ": error: Cannot evaluate genvar"
<< " step expression: " << *loop_step << endl;
des->errors += 1;
return false;
}
if (debug_scopes)
cerr << get_fileline() << ": debug: genvar step from "
<< genvar << " to " << step->value().as_long() << endl;
@ -547,7 +567,12 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else
{
NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1);
NetEConst*test = dynamic_cast<NetEConst*> (test_ex);
assert(test);
if (test == 0) {
cerr << get_fileline() << ": error: Cannot evaluate genvar"
<< " conditional expression: " << *loop_test << endl;
des->errors += 1;
return false;
}
// If the condition evaluates as false, then do not create the
// scope.
@ -589,7 +614,12 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container)
{
NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1);
NetEConst*case_value_co = dynamic_cast<NetEConst*>(case_value_ex);
assert(case_value_co);
if (case_value_co == 0) {
cerr << get_fileline() << ": error: Cannot evaluate genvar case"
<< " expression: " << *loop_test << endl;
des->errors += 1;
return false;
}
// The name of the scope to generate, whatever that item is.
hname_t use_name (scope_name);

View File

@ -589,9 +589,17 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const
break;
default:
cerr << get_fileline() << ": internal error: I don't know how "
<< "to deal with return type of function "
<< scope->basename() << "." << endl;
if (ports_) {
cerr << get_fileline() << ": internal error: I don't know "
<< "how to deal with return type of function "
<< scope->basename() << "." << endl;
} else {
/* If we do not have any ports or a return type this
* is probably a bad function definition. */
cerr << get_fileline() << ": error: Bad definition for "
<< "function " << scope->basename() << "?" << endl;
return;
}
}
svector<NetNet*>ports (ports_? ports_->count() : 0);
@ -1003,8 +1011,11 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Create signal "
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_
<< " in scope " << scope_path(scope) << endl;
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_;
if (array_dimensions > 0) {
cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl;
}
cerr << " in scope " << scope_path(scope) << endl;
}

View File

@ -944,6 +944,17 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
return tmp;
}
static bool need_bufz_for_input_port(const svector<NetNet*>&prts)
{
if (prts[0]->port_type() != NetNet::PINPUT)
return false;
if (prts[0]->pin(0).nexus()->drivers_present())
return true;
return false;
}
/*
* Instantiate a module by recursively elaborating it. Set the path of
* the recursive elaboration so that signal names get properly
@ -1162,7 +1173,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
width. We use that, then, to decide how to hook
it up.
NOTE that this also handles the case that the
v NOTE that this also handles the case that the
port is actually empty on the inside. We assume
in that case that the port is input. */
@ -1176,6 +1187,21 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
continue;
}
if (need_bufz_for_input_port(prts)) {
NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(),
sig->vector_width());
des->add_node(tmp);
connect(tmp->pin(1), sig->pin(0));
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, sig->vector_width());
tmp2->local_flag(true);
tmp2->set_line(*this);
tmp2->data_type(sig->data_type());
connect(tmp->pin(0), tmp2->pin(0));
sig = tmp2;
}
} else if (prts[0]->port_type() == NetNet::PINOUT) {
/* Inout to/from module. This is a more
@ -2623,8 +2649,8 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
}
NexusSet*nset = enet->nex_input(rem_out);
if (nset == 0) {
cerr << get_fileline() << ": internal error: No NexusSet"
<< " from statement." << endl;
cerr << get_fileline() << ": error: Unable to elaborate:"
<< endl;
enet->dump(cerr, 6);
des->errors += 1;
return enet;
@ -3355,29 +3381,6 @@ bool PProcess::elaborate(Design*des, NetScope*scope) const
verinum(1));
} while (0);
/* If this is an always block and we have no or zero delay then
* a runtime infinite loop will happen. If we possible have some
* delay then print a warning that an infinite loop is possible.
*/
if (type() == PProcess::PR_ALWAYS) {
DelayType dly_type = top->statement()->delay_type();
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
cerr << get_fileline() << ": error: always statement"
<< " does not have any delay." << endl;
cerr << get_fileline() << ": : A runtime infinite"
<< " loop will occur." << endl;
des->errors += 1;
return false;
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
cerr << get_fileline() << ": warning: always statement"
<< " may not have any delay." << endl;
cerr << get_fileline() << ": : A runtime infinite"
<< " loop may be possible." << endl;
}
}
return true;
}
@ -3845,6 +3848,37 @@ class later_defparams : public elaborator_work_item_t {
}
};
bool Design::check_always_delay() const
{
bool result_flag = true;
for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) {
/* If this is an always block and we have no or zero delay then
* a runtime infinite loop will happen. If we possible have some
* delay then print a warning that an infinite loop is possible.
*/
if (pr->type() == NetProcTop::KALWAYS) {
DelayType dly_type = pr->statement()->delay_type();
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
cerr << pr->get_fileline() << ": error: always"
<< " statement does not have any delay." << endl;
cerr << pr->get_fileline() << ": : A runtime"
<< " infinite loop will occur." << endl;
result_flag = false;
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
cerr << pr->get_fileline() << ": warning: always"
<< " statement may not have any delay." << endl;
cerr << pr->get_fileline() << ": : A runtime"
<< " infinite loop may be possible." << endl;
}
}
}
return result_flag;
}
/*
* This function is the root of all elaboration. The input is the list
* of root module names. The function locates the Module definitions
@ -3880,7 +3914,7 @@ Design* elaborate(list<perm_string>roots)
// Get the module definition for this root instance.
Module *rmod = (*mod).second;
// Make the root scope. This makes a NetScoep object and
// Make the root scope. This makes a NetScope object and
// pushes it into the list of root scopes in the Design.
NetScope*scope = des->make_root_scope(*root);
@ -3893,7 +3927,7 @@ Design* elaborate(list<perm_string>roots)
des->set_precision(rmod->time_precision);
// Save this scope, along with its defintion, in the
// Save this scope, along with its definition, in the
// "root_elems" list for later passes.
struct root_elem *r = new struct root_elem;
r->mod = rmod;
@ -3971,8 +4005,14 @@ Design* elaborate(list<perm_string>roots)
rc &= rmod->elaborate(des, scope);
}
if (rc == false) {
delete des;
return 0;
}
// Now that everything is fully elaborated verify that we do
// not have an always block with no delay (an infinite loop).
if (des->check_always_delay() == false) {
delete des;
des = 0;
}

View File

@ -22,6 +22,8 @@
# include <iostream>
# include <cstdlib>
# include <cstring>
# include <math.h>
# include "netlist.h"
# include "ivl_assert.h"
@ -1607,3 +1609,76 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width)
return new NetEConst(verinum(res, 1));
}
NetExpr* evaluate_clog2(NetExpr*arg)
{
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
if (tmpi || tmpr) {
verinum arg;
if (tmpi) {
arg = tmpi->value();
} else {
arg = verinum(tmpr->value().as_double(), true);
}
/* If we have an x in the verinum we return 32'bx. */
if (!arg.is_defined()) {
verinum tmp (verinum::Vx, 32);
tmp.has_sign(true);
NetEConst*rtn = new NetEConst(tmp);
return rtn;
}
bool is_neg = false;
uint64_t res = 0;
if (arg.is_negative()) {
is_neg = true;
// If the length is not defined, then work with
// the trimmed version of the number.
if (! arg.has_len())
arg = trim_vnum(arg);
}
arg.has_sign(false); // $unsigned()
if (!arg.is_zero()) {
arg = arg - verinum((uint64_t)1, 1);
while (!arg.is_zero()) {
res += 1;
arg = arg >> 1;
}
}
if (is_neg && res < integer_width)
res = integer_width;
verinum tmp (res, 32);
NetEConst*rtn = new NetEConst(tmp);
return rtn;
}
return 0;
}
NetExpr* NetESFunc::eval_tree(int prune_to_width)
{
/* For now only $clog2 can be a constant system function. */
if (strcmp(name(), "$clog2") == 0) {
if (nparms() != 1 || parm(0) == 0) {
cerr << get_fileline() << ": error: $clog2 takes a single "
"argument." << endl;
return 0;
}
NetExpr*rtn = evaluate_clog2(parm(0));
if (rtn != 0) {
rtn->set_line(*this);
if (debug_eval_tree) {
cerr << get_fileline() << ": debug: Evaluate "
"constant $clog2()." << endl;
}
return rtn;
}
}
return 0;
}

View File

@ -143,6 +143,7 @@ ivl_scope_def_lineno
ivl_scope_event
ivl_scope_events
ivl_scope_file
ivl_scope_is_auto
ivl_scope_lineno
ivl_scope_logs
ivl_scope_log

View File

@ -1448,6 +1448,9 @@ extern unsigned ivl_parameter_lineno(ivl_parameter_t net);
* ivl_scope_lineno
* Returns the instantiation file and line for this scope.
*
* ivl_scope_is_auto
* Is the task or function declared to be automatic?
*
* ivl_scope_var
* ivl_scope_vars
* REMOVED
@ -1524,6 +1527,7 @@ extern unsigned ivl_scope_def_lineno(ivl_scope_t net);
extern unsigned ivl_scope_events(ivl_scope_t net);
extern ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx);
extern const char* ivl_scope_file(ivl_scope_t net);
extern unsigned ivl_scope_is_auto(ivl_scope_t net);
extern unsigned ivl_scope_lineno(ivl_scope_t net);
extern unsigned ivl_scope_logs(ivl_scope_t net);
extern ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx);

View File

@ -1588,6 +1588,12 @@ static int load_next_input()
if(depend_file)
fprintf(depend_file, "%s\n", istack->path);
/* This works around an issue in flex yyrestart() where it
* uses yyin to create a new buffer when one does not exist.
* I would have assumed that it would use the file argument.
* The problem is that we have deleted the buffer and freed
* yyin (isp->file) above. */
yyin = 0;
yyrestart(istack->file);
return 1;
}

View File

@ -25,6 +25,7 @@ assign, GN_KEYWORDS_1364_1995, K_assign
atan, GN_KEYWORDS_VAMS_2_3, K_atan
atan2, GN_KEYWORDS_VAMS_2_3, K_atan2
atanh, GN_KEYWORDS_VAMS_2_3, K_atanh
automatic, GN_KEYWORDS_1364_2001, K_automatic
begin, GN_KEYWORDS_1364_1995, K_begin
bool, GN_KEYWORDS_ICARUS, K_bool
buf, GN_KEYWORDS_1364_1995, K_buf

View File

@ -357,11 +357,11 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
/* Evaluate the parameter expression, if necessary. */
NetExpr*expr = (*cur).second.expr;
assert(expr);
if (expr == NULL) return; // This is an invalid parameter so return.
eval_expr(expr);
/* The eval_expr may delete any replace the expr pointer, so the
/* The eval_expr may delete and replace the expr pointer, so the
second.expr value cannot be relied on. Might as well replace
it now with the expression that we evaluated. */
(*cur).second.expr = expr;

View File

@ -247,6 +247,41 @@ verinum::V Nexus::get_init() const
return verinum::Vz;
}
bool Nexus::drivers_present() const
{
assert(list_);
for (Link*cur = list_ ; cur ; cur = cur->next_) {
if (cur->get_dir() == Link::OUTPUT)
return true;
if (cur->get_dir() == Link::INPUT)
continue;
// Must be PASSIVE, so if it is some kind of net, see if
// it is the sort that might drive the nexus.
const NetPins*obj;
unsigned pin;
cur->cur_link(obj, pin);
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
switch (net->type()) {
case NetNet::SUPPLY0:
case NetNet::SUPPLY1:
case NetNet::TRI0:
case NetNet::TRI1:
case NetNet::WAND:
case NetNet::WOR:
case NetNet::TRIAND:
case NetNet::TRIOR:
case NetNet::REG:
return true;
default:
break;
}
}
return false;
}
void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
{
for (Link*cur = list_ ; cur ; cur = cur->next_) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2007 Stephen Williams (steve@icarus.com)
* Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -53,8 +53,13 @@ NexusSet* NetEBinary::nex_input(bool rem_out)
NexusSet* NetEConcat::nex_input(bool rem_out)
{
if (parms_[0] == NULL) return NULL;
NexusSet*result = parms_[0]->nex_input(rem_out);
for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) {
if (parms_[idx] == NULL) {
delete result;
return NULL;
}
NexusSet*tmp = parms_[idx]->nex_input(rem_out);
result->add(*tmp);
delete tmp;
@ -103,6 +108,10 @@ NexusSet* NetESelect::nex_input(bool rem_out)
{
NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet();
NexusSet*tmp = expr_->nex_input(rem_out);
if (tmp == NULL) {
delete result;
return NULL;
}
result->add(*tmp);
delete tmp;
return result;
@ -386,4 +395,3 @@ NexusSet* NetWhile::nex_input(bool rem_out)
delete tmp;
return result;
}

View File

@ -41,6 +41,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t)
signals_ = 0;
events_ = 0;
lcounter_ = 0;
is_auto_ = false;
if (up) {
default_nettype_ = up->default_nettype();
@ -261,6 +262,7 @@ NetFuncDef* NetScope::func_def()
assert( type_ == FUNC );
return func_;
}
bool NetScope::in_func()
{
return (type_ == FUNC) ? true : false;

View File

@ -2608,6 +2608,7 @@ DelayType NetBlock::delay_type() const
for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) {
DelayType dt = cur->delay_type();
if (dt > result) result = dt;
if (dt == DEFINITE_DELAY) break;
}
return result;
@ -2642,7 +2643,9 @@ DelayType NetCondit::delay_type() const
if (else_) {
result = combine_delays(if_->delay_type(), else_->delay_type());
} else {
result = if_->delay_type();
/* Because of the indeterminate conditional value the
* best we can have for this case is a possible delay. */
result = combine_delays(if_->delay_type(), NO_DELAY);
}
return result;

View File

@ -322,6 +322,10 @@ class Nexus {
NetNet* pick_any_net();
/* This method returns true if there are any drivers
(including variables) attached to this nexus. */
bool drivers_present() const;
/* This method returns true if all the possible drivers of
this nexus are constant. It will also return true if there
are no drivers at all. */
@ -725,6 +729,9 @@ class NetScope : public Attrib {
unsigned get_def_lineno() const { return def_lineno_; };
bool in_func();
/* Is the task or function automatic. */
void is_auto(bool is_auto) { is_auto_ = is_auto; };
bool is_auto() const { return is_auto_; };
const NetTaskDef* task_def() const;
const NetFuncDef* func_def() const;
@ -868,6 +875,7 @@ class NetScope : public Attrib {
NetScope*sub_;
unsigned lcounter_;
bool is_auto_;
};
/*
@ -3496,6 +3504,8 @@ class NetESFunc : public NetExpr {
NetExpr* parm(unsigned idx);
const NetExpr* parm(unsigned idx) const;
virtual NetExpr* eval_tree(int prune_to_width = -1);
virtual ivl_variable_type_t expr_type() const;
virtual NexusSet* nex_input(bool rem_out = true);
virtual bool set_width(unsigned, bool last_chance);
@ -3763,6 +3773,7 @@ class Design {
// PROCESSES
void add_process(NetProcTop*);
void delete_process(NetProcTop*);
bool check_always_delay() const;
// Iterate over the design...
void dump(ostream&) const;

View File

@ -245,16 +245,16 @@ bool eval_as_double(double&value, NetExpr*expr)
}
/*
* At the parser level, a name component it a name with a collection
* At the parser level, a name component is a name with a collection
* of expressions. For example foo[N] is the name "foo" and the index
* expression "N". This function takes as input the name component and
* returns the path component name. It will evaulate the index
* returns the path component name. It will evaluate the index
* expression if it is present.
*/
hname_t eval_path_component(Design*des, NetScope*scope,
const name_component_t&comp)
{
// No index exression, so the path component is an undecorated
// No index expression, so the path component is an undecorated
// name, for example "foo".
if (comp.index.empty())
return hname_t(comp.name);
@ -284,7 +284,7 @@ hname_t eval_path_component(Design*des, NetScope*scope,
}
// Darn, the expression doesn't evaluate to a constant. That's
// and error to be reported. And make up a fake index value to
// an error to be reported. And make up a fake index value to
// return to the caller.
cerr << index.msb->get_fileline() << ": error: "
<< "Scope index expression is not constant: "

317
parse.y
View File

@ -206,7 +206,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
%token K_PSTAR K_STARP
%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER
%token K_abs K_abstol K_access K_acos K_acosh K_asin K_analog K_asinh
%token K_atan K_atanh K_atan2
%token K_atan K_atanh K_atan2 K_automatic
%token K_always K_and K_assign K_begin K_bool K_buf K_bufif0 K_bufif1 K_case
%token K_casex K_casez K_ceil K_cmos K_continuous K_cos K_cosh
%token K_ddt_nature K_deassign K_default K_defparam K_disable K_discrete
@ -240,7 +240,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
%type <flag> from_exclude
%type <number> number
%type <flag> signed_opt udp_reg_opt edge_operator
%type <flag> signed_opt udp_reg_opt edge_operator automatic_opt
%type <drive> drive_strength drive_strength_opt dr_strength0 dr_strength1
%type <letter> udp_input_sym udp_output_sym
%type <text> udp_input_list udp_sequ_entry udp_comb_entry
@ -355,6 +355,13 @@ number : BASED_NUMBER
based_size = 0; }
;
/* real and realtime are exactly the same so save some code
* with a common matching rule. */
real_or_realtime
: K_real
| K_realtime
;
/* Verilog-2001 supports attribute lists, which can be attached to a
variety of different objects. The syntax inside the (* *) is a
comma separated list of names or names with assigned values. */
@ -1501,6 +1508,32 @@ gate_instance
delete rng;
$$ = tmp;
}
| IDENTIFIER '(' error ')'
{ lgate*tmp = new lgate;
tmp->name = $1;
tmp->parms = 0;
tmp->parms_by_name = 0;
tmp->file = @1.text;
tmp->lineno = @1.first_line;
yyerror(@2, "error: Syntax error in instance port "
"expression(s).");
delete[]$1;
$$ = tmp;
}
| IDENTIFIER range '(' error ')'
{ lgate*tmp = new lgate;
tmp->name = $1;
tmp->parms = 0;
tmp->parms_by_name = 0;
tmp->file = @1.text;
tmp->lineno = @1.first_line;
yyerror(@3, "error: Syntax error in instance port "
"expression(s).");
delete[]$1;
$$ = tmp;
}
;
gate_instance_list
@ -2094,41 +2127,41 @@ module_item
statements in the task body. But we continue to accept it as an
extension. */
| K_task IDENTIFIER ';'
| K_task automatic_opt IDENTIFIER ';'
{ assert(current_task == 0);
current_task = pform_push_task_scope($2);
current_task = pform_push_task_scope($3, $2);
FILE_NAME(current_task, @1);
}
task_item_list_opt
statement_or_null
K_endtask
{ current_task->set_ports($5);
current_task->set_statement($6);
{ current_task->set_ports($6);
current_task->set_statement($7);
pform_pop_scope();
current_task = 0;
delete[]$2;
delete[]$3;
}
| K_task IDENTIFIER
| K_task automatic_opt IDENTIFIER
{ assert(current_task == 0);
current_task = pform_push_task_scope($2);
current_task = pform_push_task_scope($3, $2);
FILE_NAME(current_task, @1);
}
'(' task_port_decl_list ')' ';'
block_item_decls_opt
statement_or_null
K_endtask
{ current_task->set_ports($5);
current_task->set_statement($9);
{ current_task->set_ports($6);
current_task->set_statement($10);
pform_pop_scope();
current_task = 0;
delete[]$2;
delete[]$3;
}
| K_task IDENTIFIER error K_endtask
| K_task automatic_opt IDENTIFIER error K_endtask
{
pform_pop_scope();
current_task = 0;
delete[]$2;
delete[]$3;
}
/* The function declaration rule matches the function declaration
@ -2136,42 +2169,42 @@ module_item
definitions in the func_body to take on the scope of the function
instead of the module. */
| K_function function_range_or_type_opt IDENTIFIER ';'
| K_function automatic_opt function_range_or_type_opt IDENTIFIER ';'
{ assert(current_function == 0);
current_function = pform_push_function_scope($3);
current_function = pform_push_function_scope($4, $2);
FILE_NAME(current_function, @1);
}
function_item_list statement
K_endfunction
{ current_function->set_ports($6);
current_function->set_statement($7);
current_function->set_return($2);
{ current_function->set_ports($7);
current_function->set_statement($8);
current_function->set_return($3);
pform_pop_scope();
current_function = 0;
delete[]$3;
delete[]$4;
}
| K_function function_range_or_type_opt IDENTIFIER
| K_function automatic_opt function_range_or_type_opt IDENTIFIER
{ assert(current_function == 0);
current_function = pform_push_function_scope($3);
current_function = pform_push_function_scope($4, $2);
FILE_NAME(current_function, @1);
}
'(' task_port_decl_list ')' ';'
block_item_decls_opt
statement
K_endfunction
{ current_function->set_ports($6);
current_function->set_statement($10);
current_function->set_return($2);
{ current_function->set_ports($7);
current_function->set_statement($11);
current_function->set_return($3);
pform_pop_scope();
current_function = 0;
delete[]$3;
delete[]$4;
}
| K_function function_range_or_type_opt IDENTIFIER error K_endfunction
| K_function automatic_opt function_range_or_type_opt IDENTIFIER error K_endfunction
{
pform_pop_scope();
current_task = 0;
delete[]$3;
delete[]$4;
}
/* A generate region can contain further module items. Actually, it
@ -2278,6 +2311,11 @@ module_item
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
;
automatic_opt
: K_automatic { $$ = true; }
| { $$ = false;}
;
generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); }
generate_case_items
@ -2433,17 +2471,7 @@ parameter_assign_decl
param_active_signed = false;
param_active_type = IVL_VT_LOGIC;
}
| K_real
{ param_active_range = 0;
param_active_signed = true;
param_active_type = IVL_VT_REAL;
}
parameter_assign_list
{ param_active_range = 0;
param_active_signed = false;
param_active_type = IVL_VT_LOGIC;
}
| K_realtime
| real_or_realtime
{ param_active_range = 0;
param_active_signed = true;
param_active_type = IVL_VT_REAL;
@ -2572,17 +2600,7 @@ localparam_assign_decl
param_active_signed = false;
param_active_type = IVL_VT_LOGIC;
}
| K_real
{ param_active_range = 0;
param_active_signed = true;
param_active_type = IVL_VT_REAL;
}
localparam_assign_list
{ param_active_range = 0;
param_active_signed = false;
param_active_type = IVL_VT_LOGIC;
}
| K_realtime
| real_or_realtime
{ param_active_range = 0;
param_active_signed = true;
param_active_type = IVL_VT_REAL;
@ -3741,39 +3759,43 @@ task_item
| task_port_item { $$ = $1; }
;
reg_opt
: K_reg
|
;
task_port_item
: K_input signed_opt range_opt list_of_identifiers ';'
: K_input reg_opt signed_opt range_opt list_of_identifiers ';'
{ svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINPUT,
IVL_VT_NO_TYPE, $2,
$3, $4,
IVL_VT_NO_TYPE, $3,
$4, $5,
@1.text, @1.first_line);
$$ = tmp;
}
| K_output signed_opt range_opt list_of_identifiers ';'
| K_output reg_opt signed_opt range_opt list_of_identifiers ';'
{ svector<PWire*>*tmp
= pform_make_task_ports(NetNet::POUTPUT,
IVL_VT_LOGIC, $2,
$3, $4,
IVL_VT_LOGIC, $3,
$4, $5,
@1.text, @1.first_line);
$$ = tmp;
}
| K_inout signed_opt range_opt list_of_identifiers ';'
| K_inout reg_opt signed_opt range_opt list_of_identifiers ';'
{ svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINOUT,
IVL_VT_LOGIC, $2,
$3, $4,
IVL_VT_LOGIC, $3,
$4, $5,
@1.text, @1.first_line);
$$ = tmp;
}
/* When the port is an integer, infer a signed vector of the integer
shape. Generate a range to make it work. */
shape. Generate a range ([31:0]) to make it work. */
| K_input K_integer list_of_identifiers ';'
{ svector<PExpr*>*range_stub
= new svector<PExpr*>(2);
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum(integer_width-1,
integer_width));
@ -3788,8 +3810,7 @@ task_port_item
$$ = tmp;
}
| K_output K_integer list_of_identifiers ';'
{ svector<PExpr*>*range_stub
= new svector<PExpr*>(2);
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum(integer_width-1,
integer_width));
@ -3804,8 +3825,7 @@ task_port_item
$$ = tmp;
}
| K_inout K_integer list_of_identifiers ';'
{ svector<PExpr*>*range_stub
= new svector<PExpr*>(2);
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum(integer_width-1,
integer_width));
@ -3820,9 +3840,54 @@ task_port_item
$$ = tmp;
}
/* Ports can be real. */
/* Ports can be time with a width of [63:0] (unsigned). */
| K_input K_real list_of_identifiers ';'
| K_input K_time list_of_identifiers ';'
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum((uint64_t)63, integer_width));
(*range_stub)[0] = re;
re = new PENumber(new verinum((uint64_t)0, integer_width));
(*range_stub)[1] = re;
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINPUT,
IVL_VT_LOGIC, false,
range_stub, $3,
@1.text, @1.first_line);
$$ = tmp;
}
| K_output K_time list_of_identifiers ';'
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum((uint64_t)63, integer_width));
(*range_stub)[0] = re;
re = new PENumber(new verinum((uint64_t)0, integer_width));
(*range_stub)[1] = re;
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::POUTPUT,
IVL_VT_LOGIC, false,
range_stub, $3,
@1.text, @1.first_line);
$$ = tmp;
}
| K_inout K_time list_of_identifiers ';'
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum((uint64_t)63, integer_width));
(*range_stub)[0] = re;
re = new PENumber(new verinum((uint64_t)0, integer_width));
(*range_stub)[1] = re;
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINOUT,
IVL_VT_LOGIC, false,
range_stub, $3,
@1.text, @1.first_line);
$$ = tmp;
}
/* Ports can be real or realtime. */
| K_input real_or_realtime list_of_identifiers ';'
{ svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINPUT,
IVL_VT_REAL, false,
@ -3830,7 +3895,7 @@ task_port_item
@1.text, @1.first_line);
$$ = tmp;
}
| K_output K_real list_of_identifiers ';'
| K_output real_or_realtime list_of_identifiers ';'
{ svector<PWire*>*tmp
= pform_make_task_ports(NetNet::POUTPUT,
IVL_VT_REAL, true,
@ -3838,7 +3903,7 @@ task_port_item
@1.text, @1.first_line);
$$ = tmp;
}
| K_inout K_real list_of_identifiers ';'
| K_inout real_or_realtime list_of_identifiers ';'
{ svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINOUT,
IVL_VT_REAL, true,
@ -3868,50 +3933,51 @@ task_item_list_opt
task_port_decl
: K_input signed_opt range_opt IDENTIFIER
: K_input reg_opt signed_opt range_opt IDENTIFIER
{ port_declaration_context.port_type = NetNet::PINPUT;
port_declaration_context.var_type = IVL_VT_LOGIC;
port_declaration_context.sign_flag = $2;
port_declaration_context.sign_flag = $3;
delete port_declaration_context.range;
port_declaration_context.range = copy_range($3);
port_declaration_context.range = copy_range($4);
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINPUT,
IVL_VT_LOGIC, $2,
$3, list_from_identifier($4),
IVL_VT_LOGIC, $3,
$4, list_from_identifier($5),
@1.text, @1.first_line);
$$ = tmp;
}
| K_output signed_opt range_opt IDENTIFIER
| K_output reg_opt signed_opt range_opt IDENTIFIER
{ port_declaration_context.port_type = NetNet::POUTPUT;
port_declaration_context.var_type = IVL_VT_LOGIC;
port_declaration_context.sign_flag = $2;
port_declaration_context.sign_flag = $3;
delete port_declaration_context.range;
port_declaration_context.range = copy_range($3);
port_declaration_context.range = copy_range($4);
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::POUTPUT,
IVL_VT_LOGIC, $2,
$3, list_from_identifier($4),
IVL_VT_LOGIC, $3,
$4, list_from_identifier($5),
@1.text, @1.first_line);
$$ = tmp;
}
| K_inout signed_opt range_opt IDENTIFIER
| K_inout reg_opt signed_opt range_opt IDENTIFIER
{ port_declaration_context.port_type = NetNet::PINOUT;
port_declaration_context.var_type = IVL_VT_LOGIC;
port_declaration_context.sign_flag = $2;
port_declaration_context.sign_flag = $3;
delete port_declaration_context.range;
port_declaration_context.range = copy_range($3);
port_declaration_context.range = copy_range($4);
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINOUT,
IVL_VT_LOGIC, $2,
$3, list_from_identifier($4),
IVL_VT_LOGIC, $3,
$4, list_from_identifier($5),
@1.text, @1.first_line);
$$ = tmp;
}
/* Ports can be integer with a width of [31:0]. */
| K_input K_integer IDENTIFIER
{ svector<PExpr*>*range_stub
= new svector<PExpr*>(2);
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum(integer_width-1,
integer_width));
@ -3932,8 +3998,7 @@ task_port_decl
$$ = tmp;
}
| K_output K_integer IDENTIFIER
{ svector<PExpr*>*range_stub
= new svector<PExpr*>(2);
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum(integer_width-1,
integer_width));
@ -3954,8 +4019,7 @@ task_port_decl
$$ = tmp;
}
| K_inout K_integer IDENTIFIER
{ svector<PExpr*>*range_stub
= new svector<PExpr*>(2);
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum(integer_width-1,
integer_width));
@ -3976,9 +4040,72 @@ task_port_decl
$$ = tmp;
}
/* Ports can be real. */
/* Ports can be time with a width of [63:0] (unsigned). */
| K_input K_time IDENTIFIER
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum((uint64_t)63, integer_width));
(*range_stub)[0] = re;
re = new PENumber(new verinum((uint64_t)0, integer_width));
(*range_stub)[1] = re;
port_declaration_context.port_type = NetNet::PINPUT;
port_declaration_context.var_type = IVL_VT_LOGIC;
port_declaration_context.sign_flag = false;
delete port_declaration_context.range;
port_declaration_context.range = copy_range(range_stub);
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINPUT,
IVL_VT_LOGIC, false,
range_stub,
list_from_identifier($3),
@1.text, @1.first_line);
$$ = tmp;
}
| K_output K_time IDENTIFIER
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum((uint64_t)63, integer_width));
(*range_stub)[0] = re;
re = new PENumber(new verinum((uint64_t)0, integer_width));
(*range_stub)[1] = re;
port_declaration_context.port_type = NetNet::POUTPUT;
port_declaration_context.var_type = IVL_VT_LOGIC;
port_declaration_context.sign_flag = false;
delete port_declaration_context.range;
port_declaration_context.range = copy_range(range_stub);
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::POUTPUT,
IVL_VT_LOGIC, false,
range_stub,
list_from_identifier($3),
@1.text, @1.first_line);
$$ = tmp;
}
| K_inout K_time IDENTIFIER
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
PExpr*re;
re = new PENumber(new verinum((uint64_t)63, integer_width));
(*range_stub)[0] = re;
re = new PENumber(new verinum((uint64_t)0, integer_width));
(*range_stub)[1] = re;
port_declaration_context.port_type = NetNet::PINOUT;
port_declaration_context.var_type = IVL_VT_LOGIC;
port_declaration_context.sign_flag = false;
delete port_declaration_context.range;
port_declaration_context.range = copy_range(range_stub);
svector<PWire*>*tmp
= pform_make_task_ports(NetNet::PINOUT,
IVL_VT_LOGIC, false,
range_stub,
list_from_identifier($3),
@1.text, @1.first_line);
$$ = tmp;
}
| K_input K_real IDENTIFIER
/* Ports can be real or realtime. */
| K_input real_or_realtime IDENTIFIER
{ port_declaration_context.port_type = NetNet::PINPUT;
port_declaration_context.var_type = IVL_VT_REAL;
port_declaration_context.sign_flag = false;
@ -3991,7 +4118,7 @@ task_port_decl
@1.text, @1.first_line);
$$ = tmp;
}
| K_output K_real IDENTIFIER
| K_output real_or_realtime IDENTIFIER
{ port_declaration_context.port_type = NetNet::POUTPUT;
port_declaration_context.var_type = IVL_VT_REAL;
port_declaration_context.sign_flag = false;
@ -4004,7 +4131,7 @@ task_port_decl
@1.text, @1.first_line);
$$ = tmp;
}
| K_inout K_real IDENTIFIER
| K_inout real_or_realtime IDENTIFIER
{ port_declaration_context.port_type = NetNet::PINOUT;
port_declaration_context.var_type = IVL_VT_REAL;
port_declaration_context.sign_flag = false;

View File

@ -103,17 +103,18 @@ void pform_pop_scope()
}
}
PTask* pform_push_task_scope(char*name)
PTask* pform_push_task_scope(char*name, bool is_auto)
{
perm_string task_name = lex_strings.make(name);
PTask*task;
if (pform_cur_generate) {
task = new PTask(task_name, pform_cur_generate->lexical_scope);
task = new PTask(task_name, pform_cur_generate->lexical_scope,
is_auto);
pform_cur_generate->tasks[task->pscope_name()] = task;
pform_cur_generate->lexical_scope = task;
} else {
task = new PTask(task_name, lexical_scope);
task = new PTask(task_name, lexical_scope, is_auto);
pform_cur_module->tasks[task->pscope_name()] = task;
lexical_scope = task;
}
@ -121,17 +122,18 @@ PTask* pform_push_task_scope(char*name)
return task;
}
PFunction* pform_push_function_scope(char*name)
PFunction* pform_push_function_scope(char*name, bool is_auto)
{
perm_string func_name = lex_strings.make(name);
PFunction*func;
if (pform_cur_generate) {
func = new PFunction(func_name, pform_cur_generate->lexical_scope);
func = new PFunction(func_name, pform_cur_generate->lexical_scope,
is_auto);
pform_cur_generate->funcs[func->pscope_name()] = func;
pform_cur_generate->lexical_scope = func;
} else {
func = new PFunction(func_name, lexical_scope);
func = new PFunction(func_name, lexical_scope, is_auto);
pform_cur_module->funcs[func->pscope_name()] = func;
lexical_scope = func;
}

View File

@ -176,8 +176,8 @@ extern void pform_make_udp(perm_string name,
*/
extern void pform_pop_scope();
extern PTask*pform_push_task_scope(char*name);
extern PFunction*pform_push_function_scope(char*name);
extern PTask*pform_push_task_scope(char*name, bool is_auto);
extern PFunction*pform_push_function_scope(char*name, bool is_auto);
extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt);
extern void pform_put_behavior_in_scope(AProcess*proc);

View File

@ -280,6 +280,9 @@ void PEBinary::dump(ostream&out) const
case 'N':
out << "!==";
break;
case 'p':
out << "**";
break;
case 'R':
out << ">>>";
break;
@ -733,6 +736,7 @@ void PForStatement::dump(ostream&out, unsigned ind) const
void PFunction::dump(ostream&out, unsigned ind) const
{
out << setw(ind) << "" << "function ";
if (is_auto_) cout << "automatic ";
switch (return_type_.type) {
case PTF_NONE:
out << "?none? ";
@ -793,6 +797,9 @@ void PRepeat::dump(ostream&out, unsigned ind) const
void PTask::dump(ostream&out, unsigned ind) const
{
out << setw(ind) << "" << "task ";
if (is_auto_) cout << "automatic ";
out << pscope_name() << ";" << endl;
if (ports_)
for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) {
out << setw(ind) << "";

View File

@ -1548,6 +1548,12 @@ extern "C" const char*ivl_scope_file(ivl_scope_t net)
return net->file.str();
}
extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net)
{
assert(net);
return net->is_auto;
}
extern "C" unsigned ivl_scope_lineno(ivl_scope_t net)
{
assert(net);

View File

@ -575,6 +575,7 @@ void dll_target::add_root(ivl_design_s &des_, const NetScope *s)
root_->time_units = s->time_unit();
root_->nattr = s->attr_cnt();
root_->attr = fill_in_attributes(s);
root_->is_auto = 0;
des_.nroots_++;
if (des_.roots_)
@ -2319,7 +2320,8 @@ void dll_target::scope(const NetScope*net)
scope->time_precision = net->time_precision();
scope->time_units = net->time_unit();
scope->nattr = net->attr_cnt();
scope->attr = fill_in_attributes(net);
scope->attr = fill_in_attributes(net);
scope->is_auto = net->is_auto();
switch (net->type()) {
case NetScope::MODULE:
@ -2374,8 +2376,7 @@ void dll_target::signal(const NetNet*net)
object, or creating the sigs_ array if this is the first
signal. */
obj->scope_ = find_scope(des_, net->scope());
obj->file = perm_string::literal("N/A");
obj->lineno = 0;
FILE_NAME(obj, net);
assert(obj->scope_);
if (obj->scope_->nsigs_ == 0) {

View File

@ -591,6 +591,7 @@ struct ivl_scope_s {
/* Scopes that are tasks/functions have a definition. */
ivl_statement_t def;
unsigned is_auto;
unsigned ports;
ivl_signal_t*port;
@ -772,4 +773,10 @@ static inline void FILE_NAME(ivl_process_t net, const LineInfo*info)
net->lineno = info->get_lineno();
}
static inline void FILE_NAME(ivl_signal_t net, const LineInfo*info)
{
net->file = info->get_file();
net->lineno = info->get_lineno();
}
#endif

View File

@ -1448,12 +1448,13 @@ static int show_scope(ivl_scope_t net, void*x)
ivl_scope_name(net), ivl_scope_params(net),
ivl_scope_sigs(net), ivl_scope_logs(net));
char *is_auto = ivl_scope_is_auto(net) ? "automatic " : "";
switch (ivl_scope_type(net)) {
case IVL_SCT_MODULE:
fprintf(out, " module %s", ivl_scope_tname(net));
break;
case IVL_SCT_FUNCTION:
fprintf(out, " function %s", ivl_scope_tname(net));
fprintf(out, " function %s%s", is_auto, ivl_scope_tname(net));
break;
case IVL_SCT_BEGIN:
fprintf(out, " begin : %s", ivl_scope_tname(net));
@ -1462,7 +1463,7 @@ static int show_scope(ivl_scope_t net, void*x)
fprintf(out, " fork : %s", ivl_scope_tname(net));
break;
case IVL_SCT_TASK:
fprintf(out, " task %s", ivl_scope_tname(net));
fprintf(out, " task %s%s", is_auto, ivl_scope_tname(net));
break;
default:
fprintf(out, " type(%u) %s", ivl_scope_type(net),

92
tgt-vhdl/Makefile.in Normal file
View File

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

274
tgt-vhdl/cast.cc Normal file
View File

@ -0,0 +1,274 @@
/*
* Generate code to convert between VHDL types.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_syntax.hh"
#include "vhdl_target.h"
#include "support.hh"
#include <cassert>
#include <iostream>
vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
{
//std::cout << "Cast: from=" << type_->get_string()
// << " (" << type_->get_width() << ") "
// << " to=" << to->get_string() << " ("
// << to->get_width() << ")" << std::endl;
if (to->get_name() == type_->get_name()) {
if (to->get_width() == type_->get_width())
return this; // Identical
else
return resize(to->get_width());
}
else {
switch (to->get_name()) {
case VHDL_TYPE_BOOLEAN:
return to_boolean();
case VHDL_TYPE_INTEGER:
return to_integer();
case VHDL_TYPE_UNSIGNED:
case VHDL_TYPE_SIGNED:
case VHDL_TYPE_STD_LOGIC_VECTOR:
return to_vector(to->get_name(), to->get_width());
case VHDL_TYPE_STD_LOGIC:
return to_std_logic();
default:
assert(false);
}
}
}
/*
* Generate code to cast an expression to a vector type (std_logic_vector,
* signed, unsigned).
*/
vhdl_expr *vhdl_expr::to_vector(vhdl_type_name_t name, int w)
{
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
vhdl_expr *others = w == 1 ? NULL : new vhdl_const_bit('0');
vhdl_bit_spec_expr *bs =
new vhdl_bit_spec_expr(new vhdl_type(name, w - 1, 0), others);
bs->add_bit(0, this);
return bs;
}
else {
// We have to cast the expression before resizing or the
// wrong sign bit may be extended (i.e. when casting between
// signed/unsigned *and* resizing)
vhdl_type *t = new vhdl_type(name, w - 1, 0);
vhdl_fcall *conv = new vhdl_fcall(t->get_string().c_str(), t);
conv->add_expr(this);
if (w != type_->get_width())
return conv->resize(w);
else
return conv;
}
}
/*
* Convert a generic expression to an Integer.
*/
vhdl_expr *vhdl_expr::to_integer()
{
vhdl_fcall *conv;
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
require_support_function(SF_LOGIC_TO_INTEGER);
conv = new vhdl_fcall(support_function::function_name(SF_LOGIC_TO_INTEGER),
vhdl_type::integer());
}
else
conv = new vhdl_fcall("To_Integer", vhdl_type::integer());
conv->add_expr(this);
return conv;
}
/*
* Convert a generic expression to a Boolean.
*/
vhdl_expr *vhdl_expr::to_boolean()
{
if (type_->get_name() == VHDL_TYPE_STD_LOGIC) {
// '1' is true all else are false
vhdl_const_bit *one = new vhdl_const_bit('1');
return new vhdl_binop_expr
(this, VHDL_BINOP_EQ, one, vhdl_type::boolean());
}
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
// Need to use a support function for this conversion
require_support_function(SF_UNSIGNED_TO_BOOLEAN);
vhdl_fcall *conv =
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_BOOLEAN),
vhdl_type::boolean());
conv->add_expr(this);
return conv;
}
else if (type_->get_name() == VHDL_TYPE_SIGNED) {
require_support_function(SF_SIGNED_TO_BOOLEAN);
vhdl_fcall *conv =
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_BOOLEAN),
vhdl_type::boolean());
conv->add_expr(this);
return conv;
}
else {
assert(false);
}
}
/*
* Generate code to convert and expression to std_logic.
*/
vhdl_expr *vhdl_expr::to_std_logic()
{
if (type_->get_name() == VHDL_TYPE_BOOLEAN) {
require_support_function(SF_BOOLEAN_TO_LOGIC);
vhdl_fcall *ah =
new vhdl_fcall(support_function::function_name(SF_BOOLEAN_TO_LOGIC),
vhdl_type::std_logic());
ah->add_expr(this);
return ah;
}
else if (type_->get_name() == VHDL_TYPE_SIGNED) {
require_support_function(SF_SIGNED_TO_LOGIC);
vhdl_fcall *ah =
new vhdl_fcall(support_function::function_name(SF_SIGNED_TO_LOGIC),
vhdl_type::std_logic());
ah->add_expr(this);
return ah;
}
else if (type_->get_name() == VHDL_TYPE_UNSIGNED) {
require_support_function(SF_UNSIGNED_TO_LOGIC);
vhdl_fcall *ah =
new vhdl_fcall(support_function::function_name(SF_UNSIGNED_TO_LOGIC),
vhdl_type::std_logic());
ah->add_expr(this);
return ah;
}
else
assert(false);
}
/*
* Change the width of a signed/unsigned type.
*/
vhdl_expr *vhdl_expr::resize(int newwidth)
{
vhdl_type *rtype;
assert(type_);
if (type_->get_name() == VHDL_TYPE_SIGNED)
rtype = vhdl_type::nsigned(newwidth);
else if (type_->get_name() == VHDL_TYPE_UNSIGNED)
rtype = vhdl_type::nunsigned(newwidth);
else
return this; // Doesn't make sense to resize non-vector type
vhdl_fcall *resize = new vhdl_fcall("Resize", rtype);
resize->add_expr(this);
resize->add_expr(new vhdl_const_int(newwidth));
return resize;
}
vhdl_expr *vhdl_const_int::to_vector(vhdl_type_name_t name, int w)
{
if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
const char *fname = name == VHDL_TYPE_SIGNED
? "To_Signed" : "To_Unsigned";
vhdl_fcall *conv = new vhdl_fcall(fname, new vhdl_type(name, w - 1, 0));
conv->add_expr(this);
conv->add_expr(new vhdl_const_int(w));
return conv;
}
else
return vhdl_expr::to_vector(name, w);
}
int vhdl_const_bits::bits_to_int() const
{
char msb = value_[value_.size() - 1];
int result = 0, bit;
for (int i = sizeof(int)*8 - 1; i >= 0; i--) {
if (i > (int)value_.size() - 1)
bit = msb == '1' ? 1 : 0;
else
bit = value_[i] == '1' ? 1 : 0;
result = (result << 1) | bit;
}
return result;
}
vhdl_expr *vhdl_const_bits::to_std_logic()
{
// VHDL won't let us cast directly between a vector and
// a scalar type
// But we don't need to here as we have the bits available
// Take the least significant bit
char lsb = value_[0];
return new vhdl_const_bit(lsb);
}
vhdl_expr *vhdl_const_bits::to_vector(vhdl_type_name_t name, int w)
{
if (name == VHDL_TYPE_STD_LOGIC_VECTOR) {
// Don't need to do anything
return this;
}
else if (name == VHDL_TYPE_SIGNED || name == VHDL_TYPE_UNSIGNED) {
// Extend with sign bit
value_.resize(w, value_[0]);
return this;
}
else
assert(false);
}
vhdl_expr *vhdl_const_bits::to_integer()
{
return new vhdl_const_int(bits_to_int());
}
vhdl_expr *vhdl_const_bit::to_integer()
{
return new vhdl_const_int(bit_ == '1' ? 1 : 0);
}
vhdl_expr *vhdl_const_bit::to_boolean()
{
return new vhdl_const_bool(bit_ == '1');
}

30
tgt-vhdl/configure.in Normal file
View File

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

188
tgt-vhdl/display.cc Normal file
View File

@ -0,0 +1,188 @@
/*
* VHDL implementation of $display.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include <iostream>
#include <cstring>
#include <cassert>
#include <cctype>
#include <sstream>
static const char *DISPLAY_LINE = "Verilog_Display_Line";
/*
* Write a VHDL expression into the current display line.
*/
static void display_write(stmt_container *container, vhdl_expr *expr)
{
vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write");
vhdl_var_ref *ref =
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
write->add_expr(ref);
vhdl_type_name_t type = expr->get_type()->get_name();
if (type == VHDL_TYPE_SIGNED || type == VHDL_TYPE_UNSIGNED) {
vhdl_type integer(VHDL_TYPE_INTEGER);
write->add_expr(expr->cast(&integer));
}
else if (type != VHDL_TYPE_STRING) {
// Need to add a call to Type'Image for types not
// supported by std.textio
std::string name(expr->get_type()->get_string());
name += "'Image";
vhdl_fcall *cast
= new vhdl_fcall(name.c_str(), vhdl_type::string());
cast->add_expr(expr);
write->add_expr(cast);
}
else
write->add_expr(expr);
container->add_stmt(write);
}
/*
* Write the value of DISPLAY_LINE to the output.
*/
static void display_line(stmt_container *container)
{
vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine");
vhdl_var_ref *output_ref =
new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE));
write_line->add_expr(output_ref);
vhdl_var_ref *ref =
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
write_line->add_expr(ref);
container->add_stmt(write_line);
}
/*
* Parse an octal escape sequence.
*/
static char parse_octal(const char *p)
{
assert(*p && *(p+1) && *(p+2));
assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+1)));
return (*p - '0') * 64
+ (*(p+1) - '0') * 8
+ (*(p+2) - '0') * 1;
}
static void flush_string(std::ostringstream &ss, stmt_container *container)
{
display_write(container, new vhdl_const_string(ss.str().c_str()));
// Clear the stream
ss.str("");
}
/*
* Generate VHDL for the $display system task.
* This is implemented using the functions in std.textio. Each
* parameter is written to a line variable in the process and
* then the line is written to the special variable `Output'
* (which represents the console). Subsequent $displays will
* use the same line variable.
*
* It's possible, although quite unlikely, that there will be
* name collision with an existing variable called
* `Verilog_Display_Line' -- do something about this?
*/
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool newline)
{
if (!proc->get_scope()->have_declared(DISPLAY_LINE)) {
vhdl_var_decl *line_var =
new vhdl_var_decl(DISPLAY_LINE, vhdl_type::line());
line_var->set_comment("For generating $display output");
proc->get_scope()->add_decl(line_var);
}
// Write the data into the line
int count = ivl_stmt_parm_count(stmt), i = 0;
while (i < count) {
// $display may have an empty parameter, in which case
// the expression will be null
// The behaviour here seems to be to output a space
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
if (net == NULL) {
display_write(container, new vhdl_const_string(" "));
continue;
}
if (ivl_expr_type(net) == IVL_EX_STRING) {
ostringstream ss;
for (const char *p = ivl_expr_string(net); *p; p++) {
if (*p == '\\') {
// Octal escape
char ch = parse_octal(p+1);
if (ch == '\n') {
flush_string(ss, container);
display_line(container);
}
else
ss << ch;
p += 3;
}
else if (*p == '%' && *(++p) != '%') {
flush_string(ss, container);
// Skip over width for now
while (isdigit(*p)) ++p;
// TODO: This needs to be re-written
// ...it does not handle format codes at all!
// Unfortunately, there is no printf-like
// function in VHDL
assert(i < count);
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
assert(net);
vhdl_expr *base = translate_expr(net);
if (NULL == base)
return 1;
display_write(container, base);
}
else
ss << *p;
}
display_write(container, new vhdl_const_string(ss.str().c_str()));
}
else {
vhdl_expr *base = translate_expr(net);
if (NULL == base)
return 1;
display_write(container, base);
}
}
if (newline)
display_line(container);
return 0;
}

577
tgt-vhdl/expr.cc Normal file
View File

@ -0,0 +1,577 @@
/*
* VHDL code generation for expressions.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "support.hh"
#include <iostream>
#include <cassert>
#include <cstring>
/*
* Change the signedness of a vector.
*/
static vhdl_expr *change_signedness(vhdl_expr *e, bool issigned)
{
int msb = e->get_type()->get_msb();
int lsb = e->get_type()->get_lsb();
vhdl_type u(issigned ? VHDL_TYPE_SIGNED : VHDL_TYPE_UNSIGNED, msb, lsb);
return e->cast(&u);
}
/*
* Generate code to ensure that the VHDL expression vhd_e has the
* same signedness as the Verilog expression vl_e.
*/
static vhdl_expr *correct_signedness(vhdl_expr *vhd_e, ivl_expr_t vl_e)
{
bool should_be_signed = ivl_expr_signed(vl_e) != 0;
if (vhd_e->get_type()->get_name() == VHDL_TYPE_UNSIGNED
&& should_be_signed) {
//operand->print();
//std::cout << "^ should be signed but is not" << std::endl;
return change_signedness(vhd_e, true);
}
else if (vhd_e->get_type()->get_name() == VHDL_TYPE_SIGNED
&& !should_be_signed) {
//operand->print();
//std::cout << "^ should be unsigned but is not" << std::endl;
return change_signedness(vhd_e, false);
}
else
return vhd_e;
}
/*
* Convert a constant Verilog string to a constant VHDL string.
*/
static vhdl_expr *translate_string(ivl_expr_t e)
{
// TODO: May need to inspect or escape parts of this
const char *str = ivl_expr_string(e);
return new vhdl_const_string(str);
}
/*
* A reference to a signal in an expression. It's assumed that the
* signal has already been defined elsewhere.
*/
static vhdl_var_ref *translate_signal(ivl_expr_t e)
{
ivl_signal_t sig = ivl_expr_signal(e);
const vhdl_scope *scope = find_scope_for_signal(sig);
assert(scope);
const char *renamed = get_renamed_signal(sig).c_str();
vhdl_decl *decl = scope->get_decl(renamed);
assert(decl);
// Can't generate a constant initialiser for this signal
// later as it has already been read
if (scope->initializing())
decl->set_initial(NULL);
vhdl_var_ref *ref =
new vhdl_var_ref(renamed, new vhdl_type(*decl->get_type()));
ivl_expr_t off;
if (ivl_signal_array_count(sig) > 0 && (off = ivl_expr_oper1(e))) {
// Select from an array
vhdl_expr *vhd_off = translate_expr(off);
if (NULL == vhd_off)
return NULL;
vhdl_type integer(VHDL_TYPE_INTEGER);
ref->set_slice(vhd_off->cast(&integer));
}
return ref;
}
/*
* A numeric literal ends up as std_logic bit string.
*/
static vhdl_expr *translate_number(ivl_expr_t e)
{
if (ivl_expr_width(e) == 1)
return new vhdl_const_bit(ivl_expr_bits(e)[0]);
else
return new vhdl_const_bits(ivl_expr_bits(e), ivl_expr_width(e),
ivl_expr_signed(e) != 0);
}
static vhdl_expr *translate_ulong(ivl_expr_t e)
{
return new vhdl_const_int(ivl_expr_uvalue(e));
}
static vhdl_expr *translate_reduction(support_function_t f, bool neg,
vhdl_expr *operand)
{
vhdl_expr *result;
if (operand->get_type()->get_name() == VHDL_TYPE_STD_LOGIC)
result = operand;
else {
require_support_function(f);
vhdl_fcall *fcall =
new vhdl_fcall(support_function::function_name(f),
vhdl_type::std_logic());
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR);
fcall->add_expr(operand->cast(&std_logic_vector));
result = fcall;
}
if (neg)
return new vhdl_unaryop_expr(VHDL_UNARYOP_NOT, result,
vhdl_type::std_logic());
else
return result;
}
static vhdl_expr *translate_unary(ivl_expr_t e)
{
vhdl_expr *operand = translate_expr(ivl_expr_oper1(e));
if (NULL == operand)
return NULL;
operand = correct_signedness(operand, e);
char opcode = ivl_expr_opcode(e);
switch (opcode) {
case '!':
case '~':
return new vhdl_unaryop_expr
(VHDL_UNARYOP_NOT, operand, new vhdl_type(*operand->get_type()));
case '-':
operand = change_signedness(operand, true);
return new vhdl_unaryop_expr
(VHDL_UNARYOP_NEG, operand, new vhdl_type(*operand->get_type()));
case 'N': // NOR
return translate_reduction(SF_REDUCE_OR, true, operand);
case '|':
return translate_reduction(SF_REDUCE_OR, false, operand);
case 'A': // NAND
return translate_reduction(SF_REDUCE_AND, true, operand);
case '&':
return translate_reduction(SF_REDUCE_AND, false, operand);
default:
error("No translation for unary opcode '%c'\n",
ivl_expr_opcode(e));
delete operand;
return NULL;
}
}
/*
* Translate a numeric binary operator (+, -, etc.) to
* a VHDL equivalent using the numeric_std package.
*/
static vhdl_expr *translate_numeric(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
// May need to make either side Boolean for operators
// to work
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
if (lhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN)
rhs = rhs->cast(&boolean);
else if (rhs->get_type()->get_name() == VHDL_TYPE_BOOLEAN)
lhs = lhs->cast(&boolean);
vhdl_type *rtype;
if (op == VHDL_BINOP_MULT)
rtype = new vhdl_type(lhs->get_type()->get_name(),
(lhs->get_type()->get_width()*2) - 1, 0);
else
rtype = new vhdl_type(*lhs->get_type());
return new vhdl_binop_expr(lhs, op, rhs, rtype);
}
static vhdl_expr *translate_relation(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
// Generate any necessary casts
// Arbitrarily, the RHS is casted to the type of the LHS
vhdl_expr *r_cast = rhs->cast(lhs->get_type());
return new vhdl_binop_expr(lhs, op, r_cast, vhdl_type::boolean());
}
/*
* Like translate_relation but both operands must be Boolean.
*/
static vhdl_expr *translate_logical(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
return translate_relation(lhs->cast(&boolean), rhs->cast(&boolean), op);
}
static vhdl_expr *translate_shift(vhdl_expr *lhs, vhdl_expr *rhs,
vhdl_binop_t op)
{
// The RHS must be an integer
vhdl_type integer(VHDL_TYPE_INTEGER);
vhdl_expr *r_cast = rhs->cast(&integer);
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
return new vhdl_binop_expr(lhs, op, r_cast, rtype);
}
static vhdl_expr *translate_binary(ivl_expr_t e)
{
vhdl_expr *lhs = translate_expr(ivl_expr_oper1(e));
if (NULL == lhs)
return NULL;
vhdl_expr *rhs = translate_expr(ivl_expr_oper2(e));
if (NULL == rhs)
return NULL;
int lwidth = lhs->get_type()->get_width();
int rwidth = rhs->get_type()->get_width();
int result_width = ivl_expr_width(e);
// For === and !== we need to compare std_logic_vectors
// rather than signeds
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR, result_width-1, 0);
vhdl_type_name_t ltype = lhs->get_type()->get_name();
vhdl_type_name_t rtype = rhs->get_type()->get_name();
bool vectorop =
(ltype == VHDL_TYPE_SIGNED || ltype == VHDL_TYPE_UNSIGNED) &&
(rtype == VHDL_TYPE_SIGNED || rtype == VHDL_TYPE_UNSIGNED);
// May need to resize the left or right hand side or change the
// signedness
if (vectorop) {
if (lwidth < rwidth)
lhs = lhs->resize(rwidth);
else if (rwidth < lwidth)
rhs = rhs->resize(lwidth);
lhs = correct_signedness(lhs, ivl_expr_oper1(e));
rhs = correct_signedness(rhs, ivl_expr_oper2(e));
}
vhdl_expr *result;
switch (ivl_expr_opcode(e)) {
case '+':
result = translate_numeric(lhs, rhs, VHDL_BINOP_ADD);
break;
case '-':
result = translate_numeric(lhs, rhs, VHDL_BINOP_SUB);
break;
case '*':
result = translate_numeric(lhs, rhs, VHDL_BINOP_MULT);
break;
case '/':
result = translate_numeric(lhs, rhs, VHDL_BINOP_DIV);
break;
case '%':
result = translate_numeric(lhs, rhs, VHDL_BINOP_MOD);
break;
case 'e':
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
break;
case 'E':
if (vectorop)
result = translate_relation(lhs->cast(&std_logic_vector),
rhs->cast(&std_logic_vector), VHDL_BINOP_EQ);
else
result = translate_relation(lhs, rhs, VHDL_BINOP_EQ);
break;
case 'n':
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
break;
case 'N':
if (vectorop)
result = translate_relation(lhs->cast(&std_logic_vector),
rhs->cast(&std_logic_vector), VHDL_BINOP_NEQ);
else
result = translate_relation(lhs, rhs, VHDL_BINOP_NEQ);
break;
case '&': // Bitwise AND
result = translate_numeric(lhs, rhs, VHDL_BINOP_AND);
break;
case 'a': // Logical AND
result = translate_logical(lhs, rhs, VHDL_BINOP_AND);
break;
case 'A': // Bitwise NAND
result = translate_numeric(lhs, rhs, VHDL_BINOP_NAND);
break;
case 'O': // Bitwise NOR
result = translate_numeric(lhs, rhs, VHDL_BINOP_NOR);
break;
case 'X': // Bitwise XNOR
result = translate_numeric(lhs, rhs, VHDL_BINOP_XNOR);
break;
case '|': // Bitwise OR
result = translate_numeric(lhs, rhs, VHDL_BINOP_OR);
break;
case 'o': // Logical OR
result = translate_logical(lhs, rhs, VHDL_BINOP_OR);
break;
case '<':
result = translate_relation(lhs, rhs, VHDL_BINOP_LT);
break;
case 'L':
result = translate_relation(lhs, rhs, VHDL_BINOP_LEQ);
break;
case '>':
result = translate_relation(lhs, rhs, VHDL_BINOP_GT);
break;
case 'G':
result = translate_relation(lhs, rhs, VHDL_BINOP_GEQ);
break;
case 'l':
result = translate_shift(lhs, rhs, VHDL_BINOP_SL);
break;
case 'r':
result = translate_shift(lhs, rhs, VHDL_BINOP_SR);
break;
case '^':
result = translate_numeric(lhs, rhs, VHDL_BINOP_XOR);
break;
default:
error("No translation for binary opcode '%c'\n",
ivl_expr_opcode(e));
delete lhs;
delete rhs;
return NULL;
}
if (NULL == result)
return NULL;
if (vectorop) {
result = correct_signedness(result, e);
int actual_width = result->get_type()->get_width();
if (actual_width != result_width) {
//result->print();
//std::cout << "^ should be " << result_width << " but is " << actual_width << std::endl;
}
}
return result;
}
static vhdl_expr *translate_select(ivl_expr_t e)
{
vhdl_var_ref *from =
dynamic_cast<vhdl_var_ref*>(translate_expr(ivl_expr_oper1(e)));
if (NULL == from) {
error("Can only select from variable reference");
return NULL;
}
ivl_expr_t o2 = ivl_expr_oper2(e);
if (o2) {
vhdl_expr *base = translate_expr(ivl_expr_oper2(e));
if (NULL == base)
return NULL;
vhdl_type integer(VHDL_TYPE_INTEGER);
from->set_slice(base->cast(&integer), ivl_expr_width(e) - 1);
return from;
}
else
return from->resize(ivl_expr_width(e));
}
template <class T>
static T *translate_parms(T *t, ivl_expr_t e)
{
int nparams = ivl_expr_parms(e);
for (int i = 0; i < nparams; i++) {
vhdl_expr *param = translate_expr(ivl_expr_parm(e, i));
if (NULL == param)
return NULL;
t->add_expr(param);
}
return t;
}
static vhdl_expr *translate_ufunc(ivl_expr_t e)
{
ivl_scope_t defscope = ivl_expr_def(e);
ivl_scope_t parentscope = ivl_scope_parent(defscope);
assert(ivl_scope_type(parentscope) == IVL_SCT_MODULE);
// A function is always declared in a module, which should have
// a corresponding entity by this point: so we can get type
// information, etc. from the declaration
vhdl_entity *parent_ent = find_entity(ivl_scope_name(parentscope));
assert(parent_ent);
const char *funcname = ivl_scope_tname(defscope);
vhdl_type *rettype =
vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0);
vhdl_fcall *fcall = new vhdl_fcall(funcname, rettype);
int nparams = ivl_expr_parms(e);
for (int i = 0; i < nparams; i++) {
vhdl_expr *param = translate_expr(ivl_expr_parm(e, i));
if (NULL == param)
return NULL;
// Ensure the parameter has the correct VHDL type
ivl_signal_t param_sig = ivl_scope_sig(defscope, i);
vhdl_type *param_type =
vhdl_type::type_for(ivl_signal_width(param_sig),
ivl_signal_signed(param_sig) != 0);
fcall->add_expr(param->cast(param_type));
delete param_type;
}
return fcall;
}
static vhdl_expr *translate_ternary(ivl_expr_t e)
{
support_function_t sf;
int width = ivl_expr_width(e);
bool issigned = ivl_expr_signed(e) != 0;
if (width == 1)
sf = SF_TERNARY_LOGIC;
else if (issigned)
sf = SF_TERNARY_SIGNED;
else
sf = SF_TERNARY_UNSIGNED;
require_support_function(sf);
vhdl_expr *test = translate_expr(ivl_expr_oper1(e));
vhdl_expr *true_part = translate_expr(ivl_expr_oper2(e));
vhdl_expr *false_part = translate_expr(ivl_expr_oper3(e));
if (!test || !true_part || !false_part)
return NULL;
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
test = test->cast(&boolean);
vhdl_fcall *fcall =
new vhdl_fcall(support_function::function_name(sf),
vhdl_type::type_for(width, issigned));
fcall->add_expr(test);
fcall->add_expr(true_part);
fcall->add_expr(false_part);
return fcall;
}
static vhdl_expr *translate_concat(ivl_expr_t e)
{
vhdl_type *rtype =
vhdl_type::type_for(ivl_expr_width(e), ivl_expr_signed(e) != 0);
vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, rtype);
int nrepeat = ivl_expr_repeat(e);
while (nrepeat--)
translate_parms<vhdl_binop_expr>(concat, e);
return concat;
}
vhdl_expr *translate_sfunc_time(ivl_expr_t e)
{
cerr << "warning: no translation for time (returning 0)" << endl;
return new vhdl_const_int(0);
}
vhdl_expr *translate_sfunc(ivl_expr_t e)
{
const char *name = ivl_expr_name(e);
if (strcmp(name, "$time") == 0)
return translate_sfunc_time(e);
else {
error("No translation for system function %s", name);
return NULL;
}
}
/*
* Generate a VHDL expression from a Verilog expression.
*/
vhdl_expr *translate_expr(ivl_expr_t e)
{
assert(e);
ivl_expr_type_t type = ivl_expr_type(e);
switch (type) {
case IVL_EX_STRING:
return translate_string(e);
case IVL_EX_SIGNAL:
return translate_signal(e);
case IVL_EX_NUMBER:
return translate_number(e);
case IVL_EX_ULONG:
return translate_ulong(e);
case IVL_EX_UNARY:
return translate_unary(e);
case IVL_EX_BINARY:
return translate_binary(e);
case IVL_EX_SELECT:
return translate_select(e);
case IVL_EX_UFUNC:
return translate_ufunc(e);
case IVL_EX_TERNARY:
return translate_ternary(e);
case IVL_EX_CONCAT:
return translate_concat(e);
case IVL_EX_SFUNC:
return translate_sfunc(e);
default:
error("No VHDL translation for expression at %s:%d (type = %d)",
ivl_expr_file(e), ivl_expr_lineno(e), type);
return NULL;
}
}
/*
* Translate an expression into a time. This is achieved simply
* by multiplying the expression by 1ns.
*/
vhdl_expr *translate_time_expr(ivl_expr_t e)
{
vhdl_expr *time = translate_expr(e);
if (NULL == time)
return NULL;
vhdl_type integer(VHDL_TYPE_INTEGER);
time = time->cast(&integer);
vhdl_expr *ns1 = new vhdl_const_time(1, TIME_UNIT_NS);
return new vhdl_binop_expr(time, VHDL_BINOP_MULT, ns1,
vhdl_type::time());
}

289
tgt-vhdl/logic.cc Normal file
View File

@ -0,0 +1,289 @@
/*
* VHDL code generation for logic devices.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "vhdl_element.hh"
#include <cassert>
#include <sstream>
#include <iostream>
/*
* Convert the inputs of a logic gate to a binary expression.
*/
static vhdl_expr *inputs_to_expr(vhdl_scope *scope, vhdl_binop_t op,
ivl_net_logic_t log)
{
// Not always std_logic but this is probably OK since
// the program has already been type checked
vhdl_binop_expr *gate =
new vhdl_binop_expr(op, vhdl_type::std_logic());
int npins = ivl_logic_pins(log);
for (int i = 1; i < npins; i++) {
ivl_nexus_t input = ivl_logic_pin(log, i);
gate->add_expr(nexus_to_var_ref(scope, input));
}
return gate;
}
/*
* Convert a gate intput to an unary expression.
*/
static vhdl_expr *input_to_expr(vhdl_scope *scope, vhdl_unaryop_t op,
ivl_net_logic_t log)
{
ivl_nexus_t input = ivl_logic_pin(log, 1);
assert(input);
vhdl_expr *operand = nexus_to_var_ref(scope, input);
return new vhdl_unaryop_expr(op, operand, vhdl_type::std_logic());
}
static void bufif_logic(vhdl_arch *arch, ivl_net_logic_t log, bool if0)
{
ivl_nexus_t output = ivl_logic_pin(log, 0);
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
assert(lhs);
vhdl_expr *val = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
assert(val);
vhdl_expr *sel = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 2));
assert(val);
vhdl_expr *cmp;
if (ivl_logic_width(log) == 1) {
vhdl_expr *on = new vhdl_const_bit(if0 ? '0' : '1');
cmp = new vhdl_binop_expr(sel, VHDL_BINOP_EQ, on, NULL);
}
else {
vhdl_expr *zero = (new vhdl_const_int(0))->cast(sel->get_type());
vhdl_binop_t op = if0 ? VHDL_BINOP_EQ : VHDL_BINOP_NEQ;
cmp = new vhdl_binop_expr(sel, op, zero, NULL);
}
ivl_signal_t sig = find_signal_named(lhs->get_name(), arch->get_scope());
char zbit;
switch (ivl_signal_type(sig)) {
case IVL_SIT_TRI0:
zbit = '0';
break;
case IVL_SIT_TRI1:
zbit = '1';
break;
case IVL_SIT_TRI:
default:
zbit = 'Z';
}
vhdl_expr *z = new vhdl_const_bit(zbit);
if (ivl_logic_width(log) > 1)
z = new vhdl_bit_spec_expr(NULL, z);
vhdl_cassign_stmt *cass = new vhdl_cassign_stmt(lhs, z);
cass->add_condition(val, cmp);
arch->add_stmt(cass);
}
static void comb_udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
{
ivl_udp_t udp = ivl_logic_udp(log);
// As with regular case statements, the expression in a
// `with .. select' statement must be "locally static".
// This is achieved by first combining the inputs into
// a temporary
ostringstream ss;
ss << ivl_logic_basename(log) << "_Tmp";
int msb = ivl_udp_nin(udp) - 1;
vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0);
vhdl_signal_decl *tmp_decl =
new vhdl_signal_decl(ss.str().c_str(), tmp_type);
arch->get_scope()->add_decl(tmp_decl);
int nin = ivl_udp_nin(udp);
vhdl_expr *tmp_rhs;
if (nin == 1) {
tmp_rhs = nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
tmp_rhs = tmp_rhs->cast(tmp_type);
}
else
tmp_rhs = inputs_to_expr(arch->get_scope(), VHDL_BINOP_CONCAT, log);
ss.str("");
ss << "Input to " << ivl_logic_basename(log) << " "
<< ivl_udp_name(udp) << " UDP";
tmp_decl->set_comment(ss.str());
vhdl_var_ref *tmp_ref =
new vhdl_var_ref(tmp_decl->get_name().c_str(), NULL);
arch->add_stmt(new vhdl_cassign_stmt(tmp_ref, tmp_rhs));
// Now we can implement the UDP as a `with .. select' statement
// by reading values out of the table
ivl_nexus_t output_nex = ivl_logic_pin(log, 0);
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), output_nex);
vhdl_with_select_stmt *ws =
new vhdl_with_select_stmt(new vhdl_var_ref(*tmp_ref), out);
int nrows = ivl_udp_rows(udp);
for (int i = 0; i < nrows; i++) {
const char *row = ivl_udp_row(udp, i);
vhdl_expr *value = new vhdl_const_bit(row[nin]);
vhdl_expr *cond = new vhdl_const_bits(row, nin, false);
ivl_expr_t delay_ex = ivl_logic_delay(log, 1);
vhdl_expr *delay = NULL;
if (delay_ex)
delay = translate_time_expr(delay_ex);
ws->add_condition(value, cond, delay);
}
ss.str("");
ss << "UDP " << ivl_udp_name(udp);
ws->set_comment(ss.str());
arch->add_stmt(ws);
}
static void seq_udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
{
ivl_udp_t udp = ivl_logic_udp(log);
// These will be translated to a process with a single
// case statement
vhdl_process *proc = new vhdl_process(ivl_logic_basename(log));
ostringstream ss;
ss << "Generated from UDP " << ivl_udp_name(udp);
proc->set_comment(ss.str().c_str());
// Create a variable to hold the concatenation of the inputs
int msb = ivl_udp_nin(udp) - 1;
vhdl_type *tmp_type = vhdl_type::std_logic_vector(msb, 0);
proc->get_scope()->add_decl(new vhdl_var_decl("UDP_Inputs", tmp_type));
// Concatenate the inputs into a single expression that can be
// used as the test in a case statement (this can't be inserted
// directly into the case statement due to the requirement that
// the test expression be "locally static")
int nin = ivl_udp_nin(udp);
vhdl_expr *tmp_rhs = NULL;
if (nin == 1) {
vhdl_var_ref *ref =
nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, 1));
tmp_rhs = ref->cast(tmp_type);
proc->add_sensitivity(ref->get_name());
}
else {
vhdl_binop_expr *concat = new vhdl_binop_expr(VHDL_BINOP_CONCAT, NULL);
for (int i = 1; i < nin; i++) {
vhdl_var_ref *ref =
nexus_to_var_ref(arch->get_scope(), ivl_logic_pin(log, i));
concat->add_expr(ref);
proc->add_sensitivity(ref->get_name());
}
tmp_rhs = concat;
}
proc->get_container()->add_stmt
(new vhdl_assign_stmt(new vhdl_var_ref("UDP_Inputs", NULL), tmp_rhs));
arch->add_stmt(proc);
}
static void udp_logic(vhdl_arch *arch, ivl_net_logic_t log)
{
if (ivl_udp_sequ(ivl_logic_udp(log)))
seq_udp_logic(arch, log);
else
comb_udp_logic(arch, log);
}
static vhdl_expr *translate_logic_inputs(vhdl_scope *scope, ivl_net_logic_t log)
{
switch (ivl_logic_type(log)) {
case IVL_LO_NOT:
return input_to_expr(scope, VHDL_UNARYOP_NOT, log);
case IVL_LO_AND:
return inputs_to_expr(scope, VHDL_BINOP_AND, log);
case IVL_LO_OR:
return inputs_to_expr(scope, VHDL_BINOP_OR, log);
case IVL_LO_NAND:
return inputs_to_expr(scope, VHDL_BINOP_NAND, log);
case IVL_LO_NOR:
return inputs_to_expr(scope, VHDL_BINOP_NOR, log);
case IVL_LO_XOR:
return inputs_to_expr(scope, VHDL_BINOP_XOR, log);
case IVL_LO_XNOR:
return inputs_to_expr(scope, VHDL_BINOP_XNOR, log);
case IVL_LO_BUF:
case IVL_LO_BUFZ:
return nexus_to_var_ref(scope, ivl_logic_pin(log, 1));
case IVL_LO_PULLUP:
return new vhdl_const_bit('1');
case IVL_LO_PULLDOWN:
return new vhdl_const_bit('0');
default:
error("Don't know how to translate logic type = %d to expression",
ivl_logic_type(log));
return NULL;
}
}
void draw_logic(vhdl_arch *arch, ivl_net_logic_t log)
{
switch (ivl_logic_type(log)) {
case IVL_LO_BUFIF0:
bufif_logic(arch, log, true);
break;
case IVL_LO_BUFIF1:
bufif_logic(arch, log, false);
break;
case IVL_LO_UDP:
udp_logic(arch, log);
break;
default:
{
// The output is always pin zero
ivl_nexus_t output = ivl_logic_pin(log, 0);
vhdl_var_ref *lhs = nexus_to_var_ref(arch->get_scope(), output);
vhdl_expr *rhs = translate_logic_inputs(arch->get_scope(), log);
vhdl_cassign_stmt *ass = new vhdl_cassign_stmt(lhs, rhs);
ivl_expr_t delay = ivl_logic_delay(log, 1);
if (delay)
ass->set_after(translate_time_expr(delay));
arch->add_stmt(ass);
}
}
}

330
tgt-vhdl/lpm.cc Normal file
View File

@ -0,0 +1,330 @@
/*
* VHDL code generation for LPM devices.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include <iostream>
#include <cassert>
/*
* Return the base of a part select.
*/
static vhdl_expr *part_select_base(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_expr *off;
ivl_nexus_t base = ivl_lpm_data(lpm, 1);
if (base != NULL)
off = nexus_to_var_ref(scope, base);
else
off = new vhdl_const_int(ivl_lpm_base(lpm));
// Array indexes must be integers
vhdl_type integer(VHDL_TYPE_INTEGER);
return off->cast(&integer);
}
static vhdl_expr *concat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_type *result_type =
vhdl_type::type_for(ivl_lpm_width(lpm), ivl_lpm_signed(lpm) != 0);
vhdl_binop_expr *expr =
new vhdl_binop_expr(VHDL_BINOP_CONCAT, result_type);
for (int i = ivl_lpm_selects(lpm) - 1; i >= 0; i--) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e);
}
return expr;
}
static vhdl_expr *binop_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
unsigned out_width = ivl_lpm_width(lpm);
vhdl_type *result_type =
vhdl_type::type_for(out_width, ivl_lpm_signed(lpm) != 0);
vhdl_binop_expr *expr = new vhdl_binop_expr(op, result_type);
for (int i = 0; i < 2; i++) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e->cast(result_type));
}
if (op == VHDL_BINOP_MULT) {
// Need to resize the output to the desired size,
// as this does not happen automatically in VHDL
vhdl_fcall *resize =
new vhdl_fcall("Resize", vhdl_type::nsigned(out_width));
resize->add_expr(expr);
resize->add_expr(new vhdl_const_int(out_width));
return resize;
}
else
return expr;
}
static vhdl_expr *rel_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm, vhdl_binop_t op)
{
vhdl_binop_expr *expr = new vhdl_binop_expr(op, vhdl_type::boolean());
for (int i = 0; i < 2; i++) {
vhdl_expr *e = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == e)
return NULL;
expr->add_expr(e);
}
// Need to make sure output is std_logic rather than Boolean
vhdl_type std_logic(VHDL_TYPE_STD_LOGIC);
return expr->cast(&std_logic);
}
static vhdl_expr *part_select_vp_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_var_ref *selfrom = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
if (NULL == selfrom)
return NULL;
vhdl_expr *off = part_select_base(scope, lpm);;
if (NULL == off)
return NULL;
selfrom->set_slice(off, ivl_lpm_width(lpm) - 1);
return selfrom;
}
static vhdl_expr *part_select_pv_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
return nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
}
static vhdl_expr *ufunc_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
ivl_scope_t f_scope = ivl_lpm_define(lpm);
vhdl_fcall *fcall = new vhdl_fcall(ivl_scope_basename(f_scope), NULL);
for (unsigned i = 0; i < ivl_lpm_size(lpm); i++) {
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, i));
if (NULL == ref)
return NULL;
fcall->add_expr(ref);
}
return fcall;
}
static vhdl_expr *reduction_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
support_function_t f, bool invert)
{
vhdl_var_ref *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
if (NULL == ref)
return NULL;
vhdl_expr *result;
if (ref->get_type()->get_name() == VHDL_TYPE_STD_LOGIC)
result = ref;
else {
require_support_function(f);
vhdl_fcall *fcall = new vhdl_fcall(support_function::function_name(f),
vhdl_type::std_logic());
vhdl_type std_logic_vector(VHDL_TYPE_STD_LOGIC_VECTOR);
fcall->add_expr(ref->cast(&std_logic_vector));
result = fcall;
}
if (invert)
return new vhdl_unaryop_expr
(VHDL_UNARYOP_NOT, result, vhdl_type::std_logic());
else
return result;
}
static vhdl_expr *sign_extend_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_expr *ref = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
if (ref)
return ref->resize(ivl_lpm_width(lpm));
else
return NULL;
}
static vhdl_expr *array_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
ivl_signal_t array = ivl_lpm_array(lpm);
if (!seen_signal_before(array))
return NULL;
const char *renamed = get_renamed_signal(array).c_str();
vhdl_decl *adecl = scope->get_decl(renamed);
assert(adecl);
vhdl_type *atype = new vhdl_type(*adecl->get_type());
vhdl_expr *select = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
if (NULL == select)
return NULL;
vhdl_var_ref *ref = new vhdl_var_ref(renamed, atype);
vhdl_type integer(VHDL_TYPE_INTEGER);
ref->set_slice(select->cast(&integer));
return ref;
}
static vhdl_expr *shift_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm,
vhdl_binop_t shift_op)
{
vhdl_expr *lhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
vhdl_expr *rhs = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
if (!lhs || !rhs)
return NULL;
// The RHS must be an integer
vhdl_type integer(VHDL_TYPE_INTEGER);
vhdl_expr *r_cast = rhs->cast(&integer);
vhdl_type *rtype = new vhdl_type(*lhs->get_type());
return new vhdl_binop_expr(lhs, shift_op, r_cast, rtype);
}
static vhdl_expr *repeat_lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
vhdl_expr *in = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
return new vhdl_bit_spec_expr(NULL, in);
}
static vhdl_expr *lpm_to_expr(vhdl_scope *scope, ivl_lpm_t lpm)
{
switch (ivl_lpm_type(lpm)) {
case IVL_LPM_ADD:
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_ADD);
case IVL_LPM_SUB:
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_SUB);
case IVL_LPM_MULT:
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MULT);
case IVL_LPM_DIVIDE:
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_DIV);
case IVL_LPM_MOD:
return binop_lpm_to_expr(scope, lpm, VHDL_BINOP_MOD);
case IVL_LPM_CONCAT:
return concat_lpm_to_expr(scope, lpm);
case IVL_LPM_CMP_GE:
return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_GEQ);
case IVL_LPM_CMP_EQ:
case IVL_LPM_CMP_EEQ:
return rel_lpm_to_expr(scope, lpm, VHDL_BINOP_EQ);
case IVL_LPM_PART_VP:
return part_select_vp_lpm_to_expr(scope, lpm);
case IVL_LPM_PART_PV:
return part_select_pv_lpm_to_expr(scope, lpm);
case IVL_LPM_UFUNC:
return ufunc_lpm_to_expr(scope, lpm);
case IVL_LPM_RE_AND:
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, false);
case IVL_LPM_RE_NAND:
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_AND, true);
case IVL_LPM_RE_NOR:
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, true);
case IVL_LPM_RE_OR:
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_OR, false);
case IVL_LPM_RE_XOR:
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, false);
case IVL_LPM_RE_XNOR:
return reduction_lpm_to_expr(scope, lpm, SF_REDUCE_XOR, true);
case IVL_LPM_SIGN_EXT:
return sign_extend_lpm_to_expr(scope, lpm);
case IVL_LPM_ARRAY:
return array_lpm_to_expr(scope, lpm);
case IVL_LPM_SHIFTL:
return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SL);
case IVL_LPM_SHIFTR:
return shift_lpm_to_expr(scope, lpm, VHDL_BINOP_SR);
case IVL_LPM_REPEAT:
return repeat_lpm_to_expr(scope, lpm);
default:
error("Unsupported LPM type: %d", ivl_lpm_type(lpm));
return NULL;
}
}
static int draw_mux_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
{
int nselects = ivl_lpm_selects(lpm);
if (nselects > 1) {
error("Only 1 LPM select bit supported at the moment");
return 1;
}
vhdl_scope *scope = arch->get_scope();
vhdl_expr *s0 = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 0));
vhdl_expr *s1 = nexus_to_var_ref(scope, ivl_lpm_data(lpm, 1));
vhdl_expr *sel = nexus_to_var_ref(scope, ivl_lpm_select(lpm));
vhdl_expr *b1 = new vhdl_const_bit('1');
vhdl_expr *t1 =
new vhdl_binop_expr(sel, VHDL_BINOP_EQ, b1, vhdl_type::boolean());
vhdl_var_ref *out = nexus_to_var_ref(scope, ivl_lpm_q(lpm, 0));
vhdl_cassign_stmt *s = new vhdl_cassign_stmt(out, s0);
s->add_condition(s1, t1);
arch->add_stmt(s);
return 0;
}
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm)
{
if (ivl_lpm_type(lpm) == IVL_LPM_MUX)
return draw_mux_lpm(arch, lpm);
vhdl_expr *f = lpm_to_expr(arch->get_scope(), lpm);
if (NULL == f)
return 1;
vhdl_var_ref *out = nexus_to_var_ref(arch->get_scope(), ivl_lpm_q(lpm, 0));
if (ivl_lpm_type(lpm) == IVL_LPM_PART_PV) {
vhdl_expr *off = part_select_base(arch->get_scope(), lpm);
assert(off);
out->set_slice(off, ivl_lpm_width(lpm) - 1);
}
arch->add_stmt(new vhdl_cassign_stmt(out, f));
return 0;
}

91
tgt-vhdl/process.cc Normal file
View File

@ -0,0 +1,91 @@
/*
* VHDL code generation for processes.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "vhdl_element.hh"
#include <iostream>
#include <cassert>
#include <sstream>
/*
* Convert a Verilog process to VHDL and add it to the architecture
* of the given entity.
*/
static int generate_vhdl_process(vhdl_entity *ent, ivl_process_t proc)
{
set_active_entity(ent);
// Create a new process and store it in the entity's
// architecture. This needs to be done first or the
// parent link won't be valid (and draw_stmt needs this
// to add information to the architecture)
vhdl_process *vhdl_proc = new vhdl_process();
ent->get_arch()->add_stmt(vhdl_proc);
// If this is an initial process, push signal initialisation
// into the declarations
vhdl_proc->get_scope()->set_initializing
(ivl_process_type(proc) == IVL_PR_INITIAL);
ivl_statement_t stmt = ivl_process_stmt(proc);
int rc = draw_stmt(vhdl_proc, vhdl_proc->get_container(), stmt);
if (rc != 0)
return rc;
// Initial processes are translated to VHDL processes with
// no sensitivity list and and indefinite wait statement at
// the end
// However, if no statements were added to the container
// by draw_stmt, don't bother adding a wait as `emit'
// will optimise the process out of the output
if (ivl_process_type(proc) == IVL_PR_INITIAL
&& !vhdl_proc->get_container()->empty()) {
vhdl_wait_stmt *wait = new vhdl_wait_stmt();
vhdl_proc->get_container()->add_stmt(wait);
}
// Add a comment indicating where it came from
ivl_scope_t scope = ivl_process_scope(proc);
const char *type = ivl_process_type(proc) == IVL_PR_INITIAL
? "initial" : "always";
std::ostringstream ss;
ss << "Generated from " << type << " process in "
<< ivl_scope_tname(scope) << " ("
<< ivl_process_file(proc) << ":"
<< ivl_process_lineno(proc) << ")";
vhdl_proc->set_comment(ss.str());
set_active_entity(NULL);
return 0;
}
int draw_process(ivl_process_t proc, void *cd)
{
ivl_scope_t scope = ivl_process_scope(proc);
// A process should occur in a module scope, therefore it
// should have already been assigned a VHDL entity
assert(ivl_scope_type(scope) == IVL_SCT_MODULE);
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
assert(ent != NULL);
return generate_vhdl_process(ent, proc);
}

821
tgt-vhdl/scope.cc Normal file
View File

@ -0,0 +1,821 @@
/*
* VHDL code generation for scopes.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "vhdl_element.hh"
#include <iostream>
#include <sstream>
#include <cassert>
#include <cstring>
static string make_safe_name(ivl_signal_t sig);
static vhdl_entity *g_active_entity = NULL;
vhdl_entity *get_active_entity()
{
return g_active_entity;
}
void set_active_entity(vhdl_entity *ent)
{
g_active_entity = ent;
}
/*
* This represents the portion of a nexus that is visible within
* a VHDL scope. If that nexus portion does not contain a signal,
* then `tmpname' gives the name of the temporary that will be
* used when this nexus is used in `scope' (e.g. for LPMs that
* appear in instantiations). The list `connect' lists all the
* signals that should be joined together to re-create the net.
*/
struct scope_nexus_t {
vhdl_scope *scope;
ivl_signal_t sig; // A real signal
unsigned pin; // The pin this signal is connected to
string tmpname; // A new temporary signal
list<ivl_signal_t> connect; // Other signals to wire together
};
/*
* This structure is stored in the private part of each nexus.
* It stores a scope_nexus_t for each VHDL scope which is
* connected to that nexus. It's stored as a list so we can use
* contained_within to allow several nested scopes to reference
* the same signal.
*/
struct nexus_private_t {
list<scope_nexus_t> signals;
vhdl_expr *const_driver;
};
/*
* Returns the scope_nexus_t of this nexus visible within scope.
*/
static scope_nexus_t *visible_nexus(nexus_private_t *priv, vhdl_scope *scope)
{
list<scope_nexus_t>::iterator it;
for (it = priv->signals.begin(); it != priv->signals.end(); ++it) {
if (scope->contained_within((*it).scope))
return &*it;
}
return NULL;
}
/*
* Remember that a signal in `scope' is part of this nexus. The
* first signal passed to this function for a scope will be used
* as the canonical representation of this nexus when we need to
* convert it to a variable reference (e.g. in a LPM input/output).
*/
static void link_scope_to_nexus_signal(nexus_private_t *priv, vhdl_scope *scope,
ivl_signal_t sig, unsigned pin)
{
scope_nexus_t *sn;
if ((sn = visible_nexus(priv, scope))) {
assert(sn->tmpname == "");
sn->connect.push_back(sig);
}
else {
scope_nexus_t new_sn = { scope, sig, pin, "" };
priv->signals.push_back(new_sn);
}
}
/*
* Make a temporary the representative of this nexus in scope.
*/
static void link_scope_to_nexus_tmp(nexus_private_t *priv, vhdl_scope *scope,
const string &name)
{
scope_nexus_t new_sn = { scope, NULL, 0, name };
priv->signals.push_back(new_sn);
}
/*
* Finds the name of the nexus signal within this scope.
*/
static string visible_nexus_signal_name(nexus_private_t *priv, vhdl_scope *scope,
unsigned *pin)
{
scope_nexus_t *sn = visible_nexus(priv, scope);
assert(sn);
*pin = sn->pin;
return sn->sig ? get_renamed_signal(sn->sig) : sn->tmpname;
}
/*
* Generates VHDL code to fully represent a nexus.
*/
void draw_nexus(ivl_nexus_t nexus)
{
nexus_private_t *priv = new nexus_private_t;
priv->const_driver = NULL;
int nptrs = ivl_nexus_ptrs(nexus);
// First pass through connect all the signals up
for (int i = 0; i < nptrs; i++) {
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
ivl_signal_t sig;
if ((sig = ivl_nexus_ptr_sig(nexus_ptr))) {
vhdl_scope *scope = find_scope_for_signal(sig);
unsigned pin = ivl_nexus_ptr_pin(nexus_ptr);
link_scope_to_nexus_signal(priv, scope, sig, pin);
}
}
// Second pass through make sure logic/LPMs have signal
// inputs and outputs
for (int i = 0; i < nptrs; i++) {
ivl_nexus_ptr_t nexus_ptr = ivl_nexus_ptr(nexus, i);
ivl_net_logic_t log;
ivl_lpm_t lpm;
ivl_net_const_t con;
if ((log = ivl_nexus_ptr_log(nexus_ptr))) {
ivl_scope_t log_scope = ivl_logic_scope(log);
vhdl_scope *vhdl_scope =
find_entity(ivl_scope_name(log_scope))->get_arch()->get_scope();
if (visible_nexus(priv, vhdl_scope)) {
// Already seen this signal in vhdl_scope
}
else {
// Create a temporary signal to connect it to the nexus
vhdl_type *type =
vhdl_type::type_for(ivl_logic_width(log), false);
ostringstream ss;
ss << "LO" << ivl_logic_basename(log);
vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type));
link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str());
}
}
else if ((lpm = ivl_nexus_ptr_lpm(nexus_ptr))) {
ivl_scope_t lpm_scope = ivl_lpm_scope(lpm);
vhdl_scope *vhdl_scope =
find_entity(ivl_scope_name(lpm_scope))->get_arch()->get_scope();
if (visible_nexus(priv, vhdl_scope)) {
// Already seen this signal in vhdl_scope
}
else {
// Create a temporary signal to connect the nexus
// TODO: we could avoid this for IVL_LPM_PART_PV
vhdl_type *type = vhdl_type::type_for(ivl_lpm_width(lpm),
ivl_lpm_signed(lpm) != 0);
ostringstream ss;
ss << "LPM" << ivl_lpm_basename(lpm);
vhdl_scope->add_decl(new vhdl_signal_decl(ss.str().c_str(), type));
link_scope_to_nexus_tmp(priv, vhdl_scope, ss.str());
}
}
else if ((con = ivl_nexus_ptr_con(nexus_ptr))) {
if (ivl_const_width(con) == 1)
priv->const_driver = new vhdl_const_bit(ivl_const_bits(con)[0]);
else
priv->const_driver =
new vhdl_const_bits(ivl_const_bits(con), ivl_const_width(con),
ivl_const_signed(con) != 0);
}
}
// Save the private data in the nexus
ivl_nexus_set_private(nexus, priv);
}
/*
* Ensure that a nexus has been initialised. I.e. all the necessary
* statements, declarations, etc. have been generated.
*/
static void seen_nexus(ivl_nexus_t nexus)
{
if (ivl_nexus_get_private(nexus) == NULL)
draw_nexus(nexus);
}
/*
* Translate a nexus to a variable reference. Given a nexus and a
* scope, this function returns a reference to a signal that is
* connected to the nexus and within the given scope. This signal
* might not exist in the original Verilog source (even as a
* compiler-generated temporary). If this nexus hasn't been
* encountered before, the necessary code to connect up the nexus
* will be generated.
*/
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *scope, ivl_nexus_t nexus)
{
seen_nexus(nexus);
nexus_private_t *priv =
static_cast<nexus_private_t*>(ivl_nexus_get_private(nexus));
unsigned pin;
string renamed(visible_nexus_signal_name(priv, scope, &pin));
vhdl_decl *decl = scope->get_decl(renamed);
assert(decl);
vhdl_type *type = new vhdl_type(*(decl->get_type()));
vhdl_var_ref *ref = new vhdl_var_ref(renamed.c_str(), type);
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
ref->set_slice(new vhdl_const_int(pin), 0);
return ref;
}
/*
* Translate all the primitive logic gates into concurrent
* signal assignments.
*/
static void declare_logic(vhdl_arch *arch, ivl_scope_t scope)
{
int nlogs = ivl_scope_logs(scope);
for (int i = 0; i < nlogs; i++)
draw_logic(arch, ivl_scope_log(scope, i));
}
/*
* Make sure a signal name conforms to VHDL naming rules.
*/
static string make_safe_name(ivl_signal_t sig)
{
const char *base = ivl_signal_basename(sig);
if (base[0] == '_')
return string("VL") + base;
const char *vhdl_reserved[] = {
"in", "out", "entity", "architecture", "inout", "array",
"is", "not", "and", "or", "bus", "bit", "line", // Etc...
NULL
};
for (const char **p = vhdl_reserved; *p != NULL; p++) {
if (strcasecmp(*p, base) == 0) {
return string("VL_") + base;
break;
}
}
return string(base);
}
/*
* Declare all signals and ports for a scope.
*/
static void declare_signals(vhdl_entity *ent, ivl_scope_t scope)
{
int nsigs = ivl_scope_sigs(scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(scope, i);
remember_signal(sig, ent->get_arch()->get_scope());
string name(make_safe_name(sig));
rename_signal(sig, name);
vhdl_type *sig_type;
unsigned dimensions = ivl_signal_dimensions(sig);
if (dimensions > 0) {
// Arrays are implemented by generating a separate type
// declaration for each array, and then declaring a
// signal of that type
if (dimensions > 1) {
error("> 1 dimension arrays not implemented yet");
return;
}
string type_name = name + "_Type";
vhdl_type *base_type =
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
int lsb = ivl_signal_array_base(sig);
int msb = lsb + ivl_signal_array_count(sig) - 1;
vhdl_type *array_type =
vhdl_type::array_of(base_type, type_name, msb, lsb);
vhdl_decl *array_decl = new vhdl_type_decl(type_name.c_str(), array_type);
ent->get_arch()->get_scope()->add_decl(array_decl);
sig_type = new vhdl_type(*array_type);
}
else
sig_type =
vhdl_type::type_for(ivl_signal_width(sig), ivl_signal_signed(sig) != 0);
ivl_signal_port_t mode = ivl_signal_port(sig);
switch (mode) {
case IVL_SIP_NONE:
{
vhdl_decl *decl = new vhdl_signal_decl(name.c_str(), sig_type);
ostringstream ss;
ss << "Declared at " << ivl_signal_file(sig) << ":"
<< ivl_signal_lineno(sig);
decl->set_comment(ss.str().c_str());
ent->get_arch()->get_scope()->add_decl(decl);
}
break;
case IVL_SIP_INPUT:
ent->get_scope()->add_decl
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_IN));
break;
case IVL_SIP_OUTPUT:
{
vhdl_port_decl *decl =
new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_OUT);
ent->get_scope()->add_decl(decl);
}
if (ivl_signal_type(sig) == IVL_SIT_REG) {
// A registered output
// In Verilog the output and reg can have the
// same name: this is not valid in VHDL
// Instead a new signal foo_Reg is created
// which represents the register
std::string newname(name);
newname += "_Reg";
rename_signal(sig, newname.c_str());
vhdl_type *reg_type = new vhdl_type(*sig_type);
ent->get_arch()->get_scope()->add_decl
(new vhdl_signal_decl(newname.c_str(), reg_type));
// Create a concurrent assignment statement to
// connect the register to the output
ent->get_arch()->add_stmt
(new vhdl_cassign_stmt
(new vhdl_var_ref(name.c_str(), NULL),
new vhdl_var_ref(newname.c_str(), NULL)));
}
break;
case IVL_SIP_INOUT:
ent->get_scope()->add_decl
(new vhdl_port_decl(name.c_str(), sig_type, VHDL_PORT_INOUT));
break;
default:
assert(false);
}
}
}
/*
* Generate VHDL for LPM instances in a module.
*/
static void declare_lpm(vhdl_arch *arch, ivl_scope_t scope)
{
int nlpms = ivl_scope_lpms(scope);
for (int i = 0; i < nlpms; i++) {
ivl_lpm_t lpm = ivl_scope_lpm(scope, i);
if (draw_lpm(arch, lpm) != 0)
error("Failed to translate LPM %s", ivl_lpm_name(lpm));
}
}
/*
* Map two signals together in an instantiation.
* The signals are joined by a nexus.
*/
static void map_signal(ivl_signal_t to, vhdl_entity *parent,
vhdl_comp_inst *inst)
{
// TODO: Work for multiple words
ivl_nexus_t nexus = ivl_signal_nex(to, 0);
seen_nexus(nexus);
vhdl_scope *arch_scope = parent->get_arch()->get_scope();
nexus_private_t *priv =
static_cast<nexus_private_t*>(ivl_nexus_get_private(nexus));
assert(priv);
if (!visible_nexus(priv, arch_scope)) {
// This nexus isn't attached to anything in the parent
return;
}
vhdl_var_ref *ref = nexus_to_var_ref(parent->get_arch()->get_scope(), nexus);
string name = make_safe_name(to);
// If we're mapping an output of this entity to an output of
// the child entity, then VHDL will not let us read the value
// of the signal (i.e. it must pass straight through).
// However, Verilog allows the signal to be read in the parent.
// The VHDL equivalent of this is to make *both* output ports
// a `buffer'.
vhdl_decl *decl =
parent->get_arch()->get_scope()->get_decl(ref->get_name());
vhdl_port_decl *pdecl;
if ((pdecl = dynamic_cast<vhdl_port_decl*>(decl))
&& pdecl->get_mode() == VHDL_PORT_OUT) {
// First change the mode in the parent entity
pdecl->set_mode(VHDL_PORT_BUFFER);
// Now change the mode in the child entity
vhdl_port_decl *to_pdecl =
dynamic_cast<vhdl_port_decl*>(find_scope_for_signal(to)->get_decl(name));
assert(to_pdecl);
to_pdecl->set_mode(VHDL_PORT_BUFFER);
}
inst->map_port(name.c_str(), ref);
}
/*
* Find all the port mappings of a module instantiation.
*/
static void port_map(ivl_scope_t scope, vhdl_entity *parent,
vhdl_comp_inst *inst)
{
// Find all the port mappings
int nsigs = ivl_scope_sigs(scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(scope, i);
ivl_signal_port_t mode = ivl_signal_port(sig);
switch (mode) {
case IVL_SIP_NONE:
// Internal signals don't appear in the port map
break;
case IVL_SIP_INPUT:
case IVL_SIP_OUTPUT:
case IVL_SIP_INOUT:
map_signal(sig, parent, inst);
break;
default:
assert(false);
}
}
}
/*
* Create a VHDL function from a Verilog function definition.
*/
static int draw_function(ivl_scope_t scope, ivl_scope_t parent)
{
assert(ivl_scope_type(scope) == IVL_SCT_FUNCTION);
// Find the containing entity
vhdl_entity *ent = find_entity(ivl_scope_name(parent));
assert(ent);
const char *funcname = ivl_scope_tname(scope);
assert(!ent->get_arch()->get_scope()->have_declared(funcname));
// The return type is worked out from the output port
vhdl_function *func = new vhdl_function(funcname, NULL);
int nsigs = ivl_scope_sigs(scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(scope, i);
vhdl_type *sigtype =
vhdl_type::type_for(ivl_signal_width(sig),
ivl_signal_signed(sig) != 0);
string signame(make_safe_name(sig));
switch (ivl_signal_port(sig)) {
case IVL_SIP_INPUT:
func->add_param(new vhdl_param_decl(signame.c_str(), sigtype));
break;
case IVL_SIP_OUTPUT:
// The magic variable <funcname>_Result holds the return value
signame = funcname;
signame += "_Result";
func->set_type(new vhdl_type(*sigtype));
default:
func->get_scope()->add_decl
(new vhdl_var_decl(signame.c_str(), sigtype));
}
remember_signal(sig, func->get_scope());
rename_signal(sig, signame);
}
// Non-blocking assignment not allowed in functions
func->get_scope()->set_allow_signal_assignment(false);
set_active_entity(ent);
{
draw_stmt(func, func->get_container(), ivl_scope_def(scope));
}
set_active_entity(NULL);
// Add a forward declaration too in case it is called by
// another function that has already been added
ent->get_arch()->get_scope()->add_forward_decl
(new vhdl_forward_fdecl(func));
ostringstream ss;
ss << "Generated from function " << funcname << " at "
<< ivl_scope_def_file(scope) << ":" << ivl_scope_def_lineno(scope);
func->set_comment(ss.str().c_str());
ent->get_arch()->get_scope()->add_decl(func);
return 0;
}
/*
* Create the signals necessary to expand this task later.
*/
static int draw_task(ivl_scope_t scope, ivl_scope_t parent)
{
assert(ivl_scope_type(scope) == IVL_SCT_TASK);
// Find the containing entity
vhdl_entity *ent = find_entity(ivl_scope_name(parent));
assert(ent);
const char *taskname = ivl_scope_tname(scope);
int nsigs = ivl_scope_sigs(scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(scope, i);
vhdl_type *sigtype =
vhdl_type::type_for(ivl_signal_width(sig),
ivl_signal_signed(sig) != 0);
string signame(make_safe_name(sig));
// Check this signal isn't declared in the outer scope
if (ent->get_arch()->get_scope()->have_declared(signame)) {
signame += "_";
signame += taskname;
}
vhdl_signal_decl *decl = new vhdl_signal_decl(signame.c_str(), sigtype);
ostringstream ss;
ss << "Declared at " << ivl_signal_file(sig) << ":"
<< ivl_signal_lineno(sig) << " (in task " << taskname << ")";
decl->set_comment(ss.str().c_str());
ent->get_arch()->get_scope()->add_decl(decl);
remember_signal(sig, ent->get_arch()->get_scope());
rename_signal(sig, signame);
}
return 0;
}
/*
* Create an empty VHDL entity for a Verilog module.
*/
static void create_skeleton_entity_for(ivl_scope_t scope)
{
assert(ivl_scope_type(scope) == IVL_SCT_MODULE);
// The type name will become the entity name
const char *tname = ivl_scope_tname(scope);
// Remember the scope name this entity was derived from so
// the correct processes can be added later
const char *derived_from = ivl_scope_name(scope);
// Verilog does not have the entity/architecture distinction
// so we always create a pair and associate the architecture
// with the entity for convenience (this also means that we
// retain a 1-to-1 mapping of scope to VHDL element)
vhdl_arch *arch = new vhdl_arch(tname, "FromVerilog");
vhdl_entity *ent = new vhdl_entity(tname, derived_from, arch);
// Build a comment to add to the entity/architecture
ostringstream ss;
ss << "Generated from Verilog module " << ivl_scope_tname(scope)
<< " (" << ivl_scope_def_file(scope) << ":"
<< ivl_scope_def_lineno(scope) << ")";
arch->set_comment(ss.str());
ent->set_comment(ss.str());
remember_entity(ent);
}
/*
* A first pass through the hierarchy: create VHDL entities for
* each unique Verilog module type.
*/
static int draw_skeleton_scope(ivl_scope_t scope, void *_parent)
{
if (ivl_scope_type(scope) == IVL_SCT_MODULE)
create_skeleton_entity_for(scope);
return ivl_scope_children(scope, draw_skeleton_scope, scope);
}
static int draw_all_signals(ivl_scope_t scope, void *_parent)
{
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
assert(ent);
declare_signals(ent, scope);
}
return ivl_scope_children(scope, draw_all_signals, scope);
}
/*
* Draw all tasks and functions in the hierarchy.
*/
static int draw_functions(ivl_scope_t scope, void *_parent)
{
ivl_scope_t parent = static_cast<ivl_scope_t>(_parent);
if (ivl_scope_type(scope) == IVL_SCT_FUNCTION) {
if (draw_function(scope, parent) != 0)
return 1;
}
else if (ivl_scope_type(scope) == IVL_SCT_TASK) {
if (draw_task(scope, parent) != 0)
return 1;
}
return ivl_scope_children(scope, draw_functions, scope);
}
/*
* Make concurrent assignments for constants in nets. This works
* bottom-up so that the driver is in the lowest instance it can.
* This also has the side effect of generating all the necessary
* nexus code.
*/
static int draw_constant_drivers(ivl_scope_t scope, void *_parent)
{
ivl_scope_children(scope, draw_constant_drivers, scope);
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
assert(ent);
int nsigs = ivl_scope_sigs(scope);
for (int i = 0; i < nsigs; i++) {
ivl_signal_t sig = ivl_scope_sig(scope, i);
for (unsigned i = ivl_signal_array_base(sig);
i < ivl_signal_array_count(sig);
i++) {
// Make sure the nexus code is generated
ivl_nexus_t nex = ivl_signal_nex(sig, i);
seen_nexus(nex);
nexus_private_t *priv =
static_cast<nexus_private_t*>(ivl_nexus_get_private(nex));
assert(priv);
vhdl_scope *arch_scope = ent->get_arch()->get_scope();
if (priv->const_driver) {
assert(i == 0); // TODO: Make work for more words
vhdl_var_ref *ref = nexus_to_var_ref(arch_scope, nex);
ent->get_arch()->add_stmt
(new vhdl_cassign_stmt(ref, priv->const_driver));
priv->const_driver = NULL;
}
scope_nexus_t *sn = visible_nexus(priv, arch_scope);
for (list<ivl_signal_t>::const_iterator it = sn->connect.begin();
it != sn->connect.end();
++it) {
vhdl_var_ref *rref =
new vhdl_var_ref(get_renamed_signal(sn->sig).c_str(), NULL);
vhdl_var_ref *lref =
new vhdl_var_ref(get_renamed_signal(*it).c_str(), NULL);
ent->get_arch()->add_stmt(new vhdl_cassign_stmt(lref, rref));
}
sn->connect.clear();
}
}
}
return 0;
}
static int draw_all_logic_and_lpm(ivl_scope_t scope, void *_parent)
{
if (ivl_scope_type(scope) == IVL_SCT_MODULE) {
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
assert(ent);
set_active_entity(ent);
{
declare_logic(ent->get_arch(), scope);
declare_lpm(ent->get_arch(), scope);
}
set_active_entity(NULL);
}
return ivl_scope_children(scope, draw_all_logic_and_lpm, scope);
}
static int draw_hierarchy(ivl_scope_t scope, void *_parent)
{
if (ivl_scope_type(scope) == IVL_SCT_MODULE && _parent) {
ivl_scope_t parent = static_cast<ivl_scope_t>(_parent);
vhdl_entity *ent = find_entity(ivl_scope_name(scope));
assert(ent);
vhdl_entity *parent_ent = find_entity(ivl_scope_name(parent));
assert(parent_ent);
vhdl_arch *parent_arch = parent_ent->get_arch();
assert(parent_arch != NULL);
// Create a forward declaration for it
vhdl_scope *parent_scope = parent_arch->get_scope();
if (!parent_scope->have_declared(ent->get_name())) {
vhdl_decl *comp_decl = vhdl_component_decl::component_decl_for(ent);
parent_arch->get_scope()->add_decl(comp_decl);
}
// And an instantiation statement
string inst_name(ivl_scope_basename(scope));
if (inst_name == ent->get_name() || parent_scope->have_declared(inst_name)) {
// Cannot have instance name the same as type in VHDL
inst_name += "_Inst";
}
// Need to replace any [ and ] characters that result
// from generate statements
string::size_type loc = inst_name.find('[', 0);
if (loc != string::npos)
inst_name.erase(loc, 1);
loc = inst_name.find(']', 0);
if (loc != string::npos)
inst_name.erase(loc, 1);
vhdl_comp_inst *inst =
new vhdl_comp_inst(inst_name.c_str(), ent->get_name().c_str());
port_map(scope, parent_ent, inst);
ostringstream ss;
ss << "Generated from instantiation at "
<< ivl_scope_file(scope) << ":" << ivl_scope_lineno(scope);
inst->set_comment(ss.str().c_str());
parent_arch->add_stmt(inst);
}
return ivl_scope_children(scope, draw_hierarchy, scope);
}
int draw_scope(ivl_scope_t scope, void *_parent)
{
int rc = draw_skeleton_scope(scope, _parent);
if (rc != 0)
return rc;
rc = draw_all_signals(scope, _parent);
if (rc != 0)
return rc;
rc = draw_all_logic_and_lpm(scope, _parent);
if (rc != 0)
return rc;
rc = draw_hierarchy(scope, _parent);
if (rc != 0)
return rc;
rc = draw_functions(scope, _parent);
if (rc != 0)
return rc;
rc = draw_constant_drivers(scope, _parent);
if (rc != 0)
return rc;
return 0;
}

700
tgt-vhdl/stmt.cc Normal file
View File

@ -0,0 +1,700 @@
/*
* VHDL code generation for statements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include <iostream>
#include <cstring>
#include <cassert>
#include <sstream>
#include <typeinfo>
/*
* VHDL has no real equivalent of Verilog's $finish task. The
* current solution is to use `assert false ...' to terminate
* the simulator. This isn't great, as the simulator will
* return a failure exit code when in fact it completed
* successfully.
*
* An alternative is to use the VHPI interface supported by
* some VHDL simulators and implement the $finish funcitonality
* in C. This function can be enabled with the flag
* -puse-vhpi-finish=1.
*/
static int draw_stask_finish(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
const char *use_vhpi = ivl_design_flag(get_vhdl_design(), "use-vhpi-finish");
if (strcmp(use_vhpi, "1") == 0) {
//get_active_entity()->requires_package("work.Verilog_Support");
container->add_stmt(new vhdl_pcall_stmt("work.Verilog_Support.Finish"));
}
else {
container->add_stmt(new vhdl_assert_stmt("SIMULATION FINISHED"));
}
return 0;
}
/*
* Generate VHDL for system tasks (like $display). Not all of
* these are supported.
*/
static int draw_stask(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
const char *name = ivl_stmt_name(stmt);
if (strcmp(name, "$display") == 0)
return draw_stask_display(proc, container, stmt, true);
else if (strcmp(name, "$write") == 0)
return draw_stask_display(proc, container, stmt, false);
else if (strcmp(name, "$finish") == 0)
return draw_stask_finish(proc, container, stmt);
else {
error("No VHDL translation for system task %s", name);
return 0;
}
}
/*
* Generate VHDL for a block of Verilog statements. This doesn't
* actually do anything, other than recursively translate the
* block's statements and add them to the process. This is OK as
* the stmt_container class behaves like a Verilog block.
*/
static int draw_block(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
int count = ivl_stmt_block_count(stmt);
for (int i = 0; i < count; i++) {
ivl_statement_t stmt_i = ivl_stmt_block_stmt(stmt, i);
if (draw_stmt(proc, container, stmt_i, is_last && i == count - 1) != 0)
return 1;
}
return 0;
}
/*
* A no-op statement. This corresponds to a `null' statement in VHDL.
*/
static int draw_noop(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
container->add_stmt(new vhdl_null_stmt());
return 0;
}
static vhdl_var_ref *make_assign_lhs(ivl_lval_t lval, vhdl_scope *scope)
{
ivl_signal_t sig = ivl_lval_sig(lval);
if (!sig) {
error("Only signals as lvals supported at the moment");
return NULL;
}
vhdl_expr *base = NULL;
ivl_expr_t e_off = ivl_lval_part_off(lval);
if (NULL == e_off)
e_off = ivl_lval_idx(lval);
if (e_off) {
if ((base = translate_expr(e_off)) == NULL)
return NULL;
vhdl_type integer(VHDL_TYPE_INTEGER);
base = base->cast(&integer);
}
unsigned lval_width = ivl_lval_width(lval);
string signame(get_renamed_signal(sig));
vhdl_decl *decl = scope->get_decl(signame);
assert(decl);
vhdl_type *ltype = new vhdl_type(*decl->get_type());
vhdl_var_ref *lval_ref = new vhdl_var_ref(signame.c_str(), ltype);
if (base) {
if (decl->get_type()->get_name() == VHDL_TYPE_ARRAY)
lval_ref->set_slice(base, 0);
else
lval_ref->set_slice(base, lval_width - 1);
}
return lval_ref;
}
static bool assignment_lvals(ivl_statement_t stmt, vhdl_procedural *proc,
list<vhdl_var_ref*> &lvals)
{
int nlvals = ivl_stmt_lvals(stmt);
for (int i = 0; i < nlvals; i++) {
ivl_lval_t lval = ivl_stmt_lval(stmt, i);
vhdl_var_ref *lhs = make_assign_lhs(lval, proc->get_scope());
if (NULL == lhs)
return false;
lvals.push_back(lhs);
}
return true;
}
/*
* Generate an assignment of type T for the Verilog statement stmt.
*/
template <class T>
void make_assignment(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool blocking)
{
list<vhdl_var_ref*> lvals;
if (!assignment_lvals(stmt, proc, lvals))
return;
vhdl_expr *rhs, *rhs2 = NULL;
ivl_expr_t rval = ivl_stmt_rval(stmt);
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
rhs = translate_expr(ivl_expr_oper2(rval));
rhs2 = translate_expr(ivl_expr_oper3(rval));
}
else
rhs = translate_expr(rval);
if (rhs == NULL)
return;
if (lvals.size() == 1) {
vhdl_var_ref *lhs = lvals.front();
rhs = rhs->cast(lhs->get_type());
ivl_expr_t i_delay;
vhdl_expr *after = NULL;
if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL)
after = translate_time_expr(i_delay);
// A small optimisation is to expand ternary RHSs into an
// if statement (eliminates a function call and produces
// more idiomatic code)
if (ivl_expr_type(rval) == IVL_EX_TERNARY) {
rhs2 = rhs2->cast(lhs->get_type());
vhdl_var_ref *lhs2 =
make_assign_lhs(ivl_stmt_lval(stmt, 0), proc->get_scope());
vhdl_expr *test = translate_expr(ivl_expr_oper1(rval));
if (NULL == test)
return;
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
// True part
{
T *a = new T(lhs, rhs);
if (after)
a->set_after(after);
vhdif->get_then_container()->add_stmt(a);
}
// False part
{
T *a = new T(lhs2, rhs2);
if (after)
a->set_after(translate_time_expr(i_delay));
vhdif->get_else_container()->add_stmt(a);
}
container->add_stmt(vhdif);
return;
}
// Where possible, move constant assignments into the
// declaration as initializers. This optimisation is only
// performed on assignments of constant values to prevent
// ordering problems.
// This also has another application: If this is an `inital'
// process and we haven't yet generated a `wait' statement then
// moving the assignment to the initialization preserves the
// expected Verilog behaviour: VHDL does not distinguish
// `initial' and `always' processes so an `always' process might
// be activatated before an `initial' process at time 0. The
// `always' process may then use the uninitialized signal value.
// The second test ensures that we only try to initialise
// internal signals not ports
ivl_lval_t lval = ivl_stmt_lval(stmt, 0);
vhdl_decl *decl = proc->get_scope()->get_decl(lhs->get_name());
if (proc->get_scope()->initializing()
&& ivl_signal_port(ivl_lval_sig(lval)) == IVL_SIP_NONE
&& !decl->has_initial()
&& rhs->constant()
&& decl->get_type()->get_name() != VHDL_TYPE_ARRAY) {
// If this assignment is not in the top-level container
// it will not be made on all paths through the code
// This precludes any future extraction of an initialiser
if (container != proc->get_container())
decl->set_initial(NULL); // Default initial value
else {
decl->set_initial(rhs);
delete lhs;
return;
}
}
T *a = new T(lhs, rhs);
container->add_stmt(a);
if (after != NULL)
a->set_after(after);
}
else {
// Multiple lvals are implemented by first assigning the complete
// RHS to a temporary, and then assigning each lval in turn as
// bit-selects of the temporary
static int tmp_count = 0;
ostringstream ss;
ss << "Verilog_Assign_Tmp_" << tmp_count++;
string tmpname = ss.str();
proc->get_scope()->add_decl
(new vhdl_var_decl(tmpname.c_str(), new vhdl_type(*rhs->get_type())));
vhdl_var_ref *tmp_ref =
new vhdl_var_ref(tmpname.c_str(), new vhdl_type(*rhs->get_type()));
container->add_stmt(new vhdl_assign_stmt(tmp_ref, rhs));
list<vhdl_var_ref*>::iterator it;
int width_so_far = 0;
for (it = lvals.begin(); it != lvals.end(); ++it) {
vhdl_var_ref *tmp_rhs =
new vhdl_var_ref(tmpname.c_str(), new vhdl_type(*rhs->get_type()));
int lval_width = (*it)->get_type()->get_width();
vhdl_expr *slice_base = new vhdl_const_int(width_so_far);
tmp_rhs->set_slice(slice_base, lval_width - 1);
ivl_expr_t i_delay;
vhdl_expr *after = NULL;
if ((i_delay = ivl_stmt_delay_expr(stmt)) != NULL)
after = translate_time_expr(i_delay);
T *a = new T(*it, tmp_rhs);
if (after)
a->set_after(after);
container->add_stmt(a);
width_so_far += lval_width;
}
}
}
/*
* A non-blocking assignment inside a process. The semantics for
* this are essentially the same as VHDL's non-blocking signal
* assignment.
*/
static int draw_nbassign(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
assert(proc->get_scope()->allow_signal_assignment());
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
return 0;
}
static int draw_assign(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
if (proc->get_scope()->allow_signal_assignment()) {
// Blocking assignment is implemented as non-blocking assignment
// followed by a zero-time wait
// This follows the Verilog semantics fairly closely.
make_assignment<vhdl_nbassign_stmt>(proc, container, stmt, false);
// Don't generate a zero-wait if this is the last statement in
// the process
if (!is_last)
container->add_stmt
(new vhdl_wait_stmt(VHDL_WAIT_FOR, new vhdl_const_time(0)));
}
else
make_assignment<vhdl_assign_stmt>(proc, container, stmt, true);
return 0;
}
/*
* Delay statements are equivalent to the `wait for' form of the
* VHDL wait statement.
*/
static int draw_delay(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
// This currently ignores the time units and precision
// of the enclosing scope
// A neat way to do this would be to make these values
// constants in the scope (type is Time), and have the
// VHDL wait statement compute the value from that.
// The other solution is to add them as parameters to
// the vhdl_process class
vhdl_expr *time;
if (ivl_statement_type(stmt) == IVL_ST_DELAY) {
uint64_t value = ivl_stmt_delay_val(stmt);
time = new vhdl_const_time(value, TIME_UNIT_NS);
}
else {
time = translate_time_expr(ivl_stmt_delay_expr(stmt));
if (NULL == time)
return 1;
}
ivl_statement_t sub_stmt = ivl_stmt_sub_stmt(stmt);
vhdl_wait_stmt *wait =
new vhdl_wait_stmt(VHDL_WAIT_FOR, time);
container->add_stmt(wait);
// Expand the sub-statement as well
// Often this would result in a useless `null' statement which
// is caught here instead
if (ivl_statement_type(sub_stmt) != IVL_ST_NOOP)
draw_stmt(proc, container, sub_stmt);
// Any further assignments occur after simulation time 0
// so they cannot be used to initialize signal declarations
// (if this scope is an initial process)
proc->get_scope()->set_initializing(false);
return 0;
}
/*
* A wait statement waits for a level change on a @(..) list of
* signals. Purely combinatorial processes (i.e. no posedge/negedge
* events) produce a `wait on' statement at the end of the process.
* Sequential processes produce a `wait until' statement at the
* start of the process.
*/
static int draw_wait(vhdl_procedural *_proc, stmt_container *container,
ivl_statement_t stmt)
{
// Wait statements only occur in processes
vhdl_process *proc = dynamic_cast<vhdl_process*>(_proc);
assert(proc); // Catch not process
vhdl_binop_expr *test =
new vhdl_binop_expr(VHDL_BINOP_OR, vhdl_type::boolean());
int nevents = ivl_stmt_nevent(stmt);
bool combinatorial = true; // True if no negedge/posedge events
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
if (ivl_event_npos(event) > 0 || ivl_event_nneg(event) > 0)
combinatorial = false;
}
if (combinatorial) {
vhdl_wait_stmt *wait = new vhdl_wait_stmt(VHDL_WAIT_ON);
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
int nany = ivl_event_nany(event);
for (int i = 0; i < nany; i++) {
ivl_nexus_t nexus = ivl_event_any(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
wait->add_sensitivity(ref->get_name());
delete ref;
}
}
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true);
container->add_stmt(wait);
}
else {
for (int i = 0; i < nevents; i++) {
ivl_event_t event = ivl_stmt_events(stmt, i);
int nany = ivl_event_nany(event);
for (int i = 0; i < nany; i++) {
ivl_nexus_t nexus = ivl_event_any(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
ref->set_name(ref->get_name() + "'Event");
test->add_expr(ref);
}
int nneg = ivl_event_nneg(event);
for (int i = 0; i < nneg; i++) {
ivl_nexus_t nexus = ivl_event_neg(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
vhdl_fcall *detect =
new vhdl_fcall("falling_edge", vhdl_type::boolean());
detect->add_expr(ref);
test->add_expr(detect);
}
int npos = ivl_event_npos(event);
for (int i = 0; i < npos; i++) {
ivl_nexus_t nexus = ivl_event_pos(event, i);
vhdl_var_ref *ref = nexus_to_var_ref(proc->get_scope(), nexus);
vhdl_fcall *detect =
new vhdl_fcall("rising_edge", vhdl_type::boolean());
detect->add_expr(ref);
test->add_expr(detect);
}
}
container->add_stmt(new vhdl_wait_stmt(VHDL_WAIT_UNTIL, test));
draw_stmt(proc, container, ivl_stmt_sub_stmt(stmt), true);
}
return 0;
}
static int draw_if(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
if (NULL == test)
return 1;
vhdl_if_stmt *vhdif = new vhdl_if_stmt(test);
ivl_statement_t cond_true_stmt = ivl_stmt_cond_true(stmt);
if (cond_true_stmt)
draw_stmt(proc, vhdif->get_then_container(), cond_true_stmt, is_last);
ivl_statement_t cond_false_stmt = ivl_stmt_cond_false(stmt);
if (cond_false_stmt)
draw_stmt(proc, vhdif->get_else_container(), cond_false_stmt, is_last);
container->add_stmt(vhdif);
return 0;
}
static int draw_case(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
if (NULL == test)
return 1;
// VHDL case expressions are required to be quite simple: variable
// references or slices. So we may need to create a temporary
// variable to hold the result of the expression evaluation
if (typeid(*test) != typeid(vhdl_var_ref)) {
const char *tmp_name = "Verilog_Case_Ex";
vhdl_type *test_type = new vhdl_type(*test->get_type());
if (!proc->get_scope()->have_declared(tmp_name)) {
proc->get_scope()->add_decl
(new vhdl_var_decl(tmp_name, new vhdl_type(*test_type)));
}
vhdl_var_ref *tmp_ref = new vhdl_var_ref(tmp_name, NULL);
container->add_stmt(new vhdl_assign_stmt(tmp_ref, test));
test = new vhdl_var_ref(tmp_name, test_type);
}
vhdl_case_stmt *vhdlcase = new vhdl_case_stmt(test);
container->add_stmt(vhdlcase);
// VHDL is more strict than Verilog about covering every
// possible case. So make sure we add an 'others' branch
// if there isn't a default one.
bool have_others = false;
int nbranches = ivl_stmt_case_count(stmt);
for (int i = 0; i < nbranches; i++) {
vhdl_expr *when;
ivl_expr_t net = ivl_stmt_case_expr(stmt, i);
if (net) {
when = translate_expr(net)->cast(test->get_type());
if (NULL == when)
return 1;
}
else {
when = new vhdl_var_ref("others", NULL);
have_others = true;
}
vhdl_case_branch *branch = new vhdl_case_branch(when);
vhdlcase->add_branch(branch);
ivl_statement_t stmt_i = ivl_stmt_case_stmt(stmt, i);
draw_stmt(proc, branch->get_container(), stmt_i, is_last);
}
if (!have_others) {
vhdl_case_branch *others =
new vhdl_case_branch(new vhdl_var_ref("others", NULL));
others->get_container()->add_stmt(new vhdl_null_stmt());
vhdlcase->add_branch(others);
}
return 0;
}
int draw_while(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
vhdl_expr *test = translate_expr(ivl_stmt_cond_expr(stmt));
if (NULL == test)
return 1;
// The test must be a Boolean (and std_logic and (un)signed types
// must be explicitly cast unlike in Verilog)
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
test = test->cast(&boolean);
vhdl_while_stmt *loop = new vhdl_while_stmt(test);
container->add_stmt(loop);
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
return 0;
}
int draw_forever(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
vhdl_loop_stmt *loop = new vhdl_loop_stmt;
container->add_stmt(loop);
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
return 0;
}
int draw_repeat(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
vhdl_expr *times = translate_expr(ivl_stmt_cond_expr(stmt));
if (NULL == times)
return 1;
vhdl_type integer(VHDL_TYPE_INTEGER);
times = times->cast(&integer);
const char *it_name = "Verilog_Repeat";
vhdl_for_stmt *loop =
new vhdl_for_stmt(it_name, new vhdl_const_int(1), times);
container->add_stmt(loop);
draw_stmt(proc, loop->get_container(), ivl_stmt_sub_stmt(stmt));
return 0;
}
/*
* Tasks are difficult to translate to VHDL since they allow things
* not allowed by VHDL's corresponding procedures (e.g. updating
* global variables. The solution here is to expand tasks in-line.
*/
int draw_utask(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt)
{
ivl_scope_t tscope = ivl_stmt_call(stmt);
// TODO: adding some comments to the output would be helpful
// TOOD: this completely ignores paremeters!
draw_stmt(proc, container, ivl_scope_def(tscope), false);
return 0;
}
/*
* Generate VHDL statements for the given Verilog statement and
* add them to the given VHDL process. The container is the
* location to add statements: e.g. the process body, a branch
* of an if statement, etc.
*
* The flag is_last should be set if this is the final statement
* in a block or process. It avoids generating useless `wait for 0ns'
* statements if the next statement would be a wait anyway.
*/
int draw_stmt(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last)
{
assert(stmt);
switch (ivl_statement_type(stmt)) {
case IVL_ST_STASK:
return draw_stask(proc, container, stmt);
case IVL_ST_BLOCK:
return draw_block(proc, container, stmt, is_last);
case IVL_ST_NOOP:
return draw_noop(proc, container, stmt);
case IVL_ST_ASSIGN:
return draw_assign(proc, container, stmt, is_last);
case IVL_ST_ASSIGN_NB:
return draw_nbassign(proc, container, stmt);
case IVL_ST_DELAY:
case IVL_ST_DELAYX:
return draw_delay(proc, container, stmt);
case IVL_ST_WAIT:
return draw_wait(proc, container, stmt);
case IVL_ST_CONDIT:
return draw_if(proc, container, stmt, is_last);
case IVL_ST_CASE:
return draw_case(proc, container, stmt, is_last);
case IVL_ST_WHILE:
return draw_while(proc, container, stmt);
case IVL_ST_FOREVER:
return draw_forever(proc, container, stmt);
case IVL_ST_REPEAT:
return draw_repeat(proc, container, stmt);
case IVL_ST_UTASK:
return draw_utask(proc, container, stmt);
case IVL_ST_FORCE:
case IVL_ST_RELEASE:
error("force/release statements cannot be translated to VHDL");
return 1;
case IVL_ST_DISABLE:
error("disable statement cannot be translated to VHDL");
return 1;
case IVL_ST_CASEX:
error("casex statement cannot be translated to VHDL");
return 1;
case IVL_ST_CASEZ:
error("casez statement cannot be translated to VHDL");
return 1;
case IVL_ST_FORK:
error("fork statement cannot be translated to VHDL");
return 1;
case IVL_ST_CASSIGN:
case IVL_ST_DEASSIGN:
error("continuous procedural assignment cannot be translated to VHDL");
return 1;
default:
error("No VHDL translation for statement at %s:%d (type = %d)",
ivl_stmt_file(stmt), ivl_stmt_lineno(stmt),
ivl_statement_type(stmt));
return 1;
}
}

171
tgt-vhdl/support.cc Normal file
View File

@ -0,0 +1,171 @@
/*
* Support functions for VHDL output.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "support.hh"
#include <cassert>
#include <iostream>
void require_support_function(support_function_t f)
{
vhdl_scope *scope = get_active_entity()->get_arch()->get_scope();
if (!scope->have_declared(support_function::function_name(f)))
scope->add_decl(new support_function(f));
}
const char *support_function::function_name(support_function_t type)
{
switch (type) {
case SF_UNSIGNED_TO_BOOLEAN: return "Unsigned_To_Boolean";
case SF_SIGNED_TO_BOOLEAN: return "Signed_To_Boolean";
case SF_BOOLEAN_TO_LOGIC: return "Boolean_To_Logic";
case SF_REDUCE_OR: return "Reduce_OR";
case SF_REDUCE_AND: return "Reduce_AND";
case SF_REDUCE_XOR: return "Reduce_XOR";
case SF_TERNARY_LOGIC: return "Ternary_Logic";
case SF_TERNARY_UNSIGNED: return "Ternary_Unsigned";
case SF_TERNARY_SIGNED: return "Ternary_Signed";
case SF_LOGIC_TO_INTEGER: return "Logic_To_Integer";
case SF_SIGNED_TO_LOGIC: return "Signed_To_Logic";
case SF_UNSIGNED_TO_LOGIC: return "Unsigned_To_Logic";
default:
assert(false);
}
}
vhdl_type *support_function::function_type(support_function_t type)
{
switch (type) {
case SF_UNSIGNED_TO_BOOLEAN:
case SF_SIGNED_TO_BOOLEAN:
return vhdl_type::boolean();
case SF_BOOLEAN_TO_LOGIC:
case SF_REDUCE_OR:
case SF_REDUCE_AND:
case SF_REDUCE_XOR:
case SF_TERNARY_LOGIC:
case SF_SIGNED_TO_LOGIC:
case SF_UNSIGNED_TO_LOGIC:
return vhdl_type::std_logic();
case SF_TERNARY_SIGNED:
return new vhdl_type(VHDL_TYPE_SIGNED);
case SF_TERNARY_UNSIGNED:
return new vhdl_type(VHDL_TYPE_UNSIGNED);
case SF_LOGIC_TO_INTEGER:
return vhdl_type::integer();
default:
assert(false);
}
}
void support_function::emit_ternary(std::ostream &of, int level) const
{
of << nl_string(level) << "begin" << nl_string(indent(level))
<< "if T then return X; else return Y; end if;";
}
void support_function::emit(std::ostream &of, int level) const
{
of << nl_string(level) << "function " << function_name(type_);
switch (type_) {
case SF_UNSIGNED_TO_BOOLEAN:
of << "(X : unsigned) return Boolean is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "return X /= To_Unsigned(0, X'Length);";
break;
case SF_SIGNED_TO_BOOLEAN:
of << "(X : signed) return Boolean is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "return X /= To_Signed(0, X'Length);";
break;
case SF_BOOLEAN_TO_LOGIC:
of << "(B : Boolean) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "if B then" << nl_string(indent(indent(level)))
<< "return '1';" << nl_string(indent(level))
<< "else" << nl_string(indent(indent(level)))
<< "return '0';" << nl_string(indent(level))
<< "end if;";
break;
case SF_UNSIGNED_TO_LOGIC:
of << "(X : unsigned) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "return X(0);";
break;
case SF_SIGNED_TO_LOGIC:
of << "(X : signed) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "return X(0);";
break;
case SF_REDUCE_OR:
of << "(X : std_logic_vector) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
<< "if X(I) = '1' then" << nl_string(indent(indent(indent(level))))
<< "return '1';" << nl_string(indent(indent(level)))
<< "end if;" << nl_string(indent(level))
<< "end loop;" << nl_string(indent(level))
<< "return '0';";
break;
case SF_REDUCE_AND:
of << "(X : std_logic_vector) return std_logic is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
<< "if X(I) = '0' then" << nl_string(indent(indent(indent(level))))
<< "return '0';" << nl_string(indent(indent(level)))
<< "end if;" << nl_string(indent(level))
<< "end loop;" << nl_string(indent(level))
<< "return '1';";
break;
case SF_REDUCE_XOR:
of << "(X : std_logic_vector) return std_logic is"
<< nl_string(indent(level))
<< "variable R : std_logic := '0';" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "for I in X'Range loop" << nl_string(indent(indent(level)))
<< "R := X(I) xor R;" << nl_string(indent(level))
<< "end loop;" << nl_string(indent(level))
<< "return R;";
break;
case SF_TERNARY_LOGIC:
of << "(T : Boolean; X, Y : std_logic) return std_logic is";
emit_ternary(of, level);
break;
case SF_TERNARY_SIGNED:
of << "(T : Boolean; X, Y : signed) return signed is";
emit_ternary(of, level);
break;
case SF_TERNARY_UNSIGNED:
of << "(T : Boolean; X, Y : unsigned) return unsigned is";
emit_ternary(of, level);
break;
case SF_LOGIC_TO_INTEGER:
of << "(X : std_logic) return integer is" << nl_string(level)
<< "begin" << nl_string(indent(level))
<< "if X = '1' then return 1; else return 0; end if;";
break;
default:
assert(false);
}
of << nl_string(level) << "end function;";
}

56
tgt-vhdl/support.hh Normal file
View File

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

193
tgt-vhdl/vhdl.cc Normal file
View File

@ -0,0 +1,193 @@
/*
* VHDL code generator for Icarus Verilog.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include "vhdl_element.hh"
#include <iostream>
#include <fstream>
#include <cstdarg>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <list>
#include <map>
#include <set>
/*
* Maps a signal to the scope it is defined within. Also
* provides a mechanism for renaming signals -- i.e. when
* an output has the same name as register: valid in Verilog
* but not in VHDL, so two separate signals need to be
* defined.
*/
struct signal_defn_t {
std::string renamed; // The name of the VHDL signal
vhdl_scope *scope; // The scope where it is defined
};
typedef std::map<ivl_signal_t, signal_defn_t> signal_defn_map_t;
static int g_errors = 0; // Total number of errors encountered
static entity_list_t g_entities; // All entities to emit
static signal_defn_map_t g_known_signals;
static ivl_design_t g_design;
/*
* Called when an unrecoverable problem is encountered.
*/
void error(const char *fmt, ...)
{
std::va_list args;
va_start(args, fmt);
std::printf("VHDL conversion error: "); // Source/line number?
std::vprintf(fmt, args);
std::putchar('\n');
va_end(args);
g_errors++;
}
/*
* Find an entity given a scope name.
*/
vhdl_entity *find_entity(const std::string &sname)
{
entity_list_t::const_iterator it;
for (it = g_entities.begin(); it != g_entities.end(); ++it) {
if ((*it)->get_derived_from() == sname)
return *it;
}
return NULL;
}
/*
* Add an entity/architecture pair to the list of entities
* to emit.
*/
void remember_entity(vhdl_entity* ent)
{
assert(find_entity(ent->get_derived_from()) == NULL);
g_entities.push_back(ent);
}
bool seen_signal_before(ivl_signal_t sig)
{
return g_known_signals.find(sig) != g_known_signals.end();
}
/*
* Remeber the association of signal to entity.
*/
void remember_signal(ivl_signal_t sig, vhdl_scope *scope)
{
assert(!seen_signal_before(sig));
signal_defn_t defn = { ivl_signal_basename(sig), scope };
g_known_signals[sig] = defn;
}
/*
* Change the VHDL name of a Verilog signal.
*/
void rename_signal(ivl_signal_t sig, const std::string &renamed)
{
assert(seen_signal_before(sig));
g_known_signals[sig].renamed = renamed;
}
vhdl_scope *find_scope_for_signal(ivl_signal_t sig)
{
assert(seen_signal_before(sig));
return g_known_signals[sig].scope;
}
const std::string &get_renamed_signal(ivl_signal_t sig)
{
assert(seen_signal_before(sig));
return g_known_signals[sig].renamed;
}
ivl_signal_t find_signal_named(const std::string &name, const vhdl_scope *scope)
{
signal_defn_map_t::const_iterator it;
for (it = g_known_signals.begin(); it != g_known_signals.end(); ++it) {
if (((*it).second.scope == scope
|| (*it).second.scope == scope->get_parent())
&& (*it).second.renamed == name)
return (*it).first;
}
assert(false);
}
ivl_design_t get_vhdl_design()
{
return g_design;
}
extern "C" int target_design(ivl_design_t des)
{
ivl_scope_t *roots;
unsigned int nroots;
ivl_design_roots(des, &roots, &nroots);
g_design = des;
for (unsigned int i = 0; i < nroots; i++)
draw_scope(roots[i], NULL);
ivl_design_process(des, draw_process, NULL);
// Write the generated elements to the output file
// only if there are no errors
if (0 == g_errors) {
const char *ofname = ivl_design_flag(des, "-o");
ofstream outfile(ofname);
// Make sure we only emit one example of each type of entity
set<string> seen_entities;
for (entity_list_t::iterator it = g_entities.begin();
it != g_entities.end();
++it) {
if (seen_entities.find((*it)->get_name()) == seen_entities.end()) {
(*it)->emit(outfile);
seen_entities.insert((*it)->get_name());
}
}
outfile.close();
}
// Clean up
for (entity_list_t::iterator it = g_entities.begin();
it != g_entities.end();
++it)
delete (*it);
g_entities.clear();
return g_errors;
}

4
tgt-vhdl/vhdl.conf Normal file
View File

@ -0,0 +1,4 @@
functor:cprop
functor:nodangle
-t:dll
flag:DLL=vhdl.tgt

49
tgt-vhdl/vhdl_config.h.in Normal file
View File

@ -0,0 +1,49 @@
/* vhdl_config.h.in. Generated from configure.in by autoheader. */
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS

87
tgt-vhdl/vhdl_element.cc Normal file
View File

@ -0,0 +1,87 @@
/*
* VHDL abstract syntax elements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_element.hh"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <typeinfo>
#include <iostream>
#include <sstream>
static const int VHDL_INDENT = 2; // Spaces to indent
int indent(int level)
{
return level + VHDL_INDENT;
}
std::string nl_string(int level)
{
std::ostringstream ss;
newline(ss, level);
return ss.str();
}
/*
* Emit a newline and indent to the correct level.
*/
void newline(std::ostream &of, int level)
{
of << std::endl;
while (level--)
of << ' ';
}
void blank_line(std::ostream &of, int level)
{
of << std::endl;
newline(of, level);
}
void vhdl_element::set_comment(std::string comment)
{
comment_ = comment;
}
/*
* Draw the comment for any element. The comment is either on
* a line before the element (end_of_line is false) or at the
* end of the line containing the element (end_of_line is true).
*/
void vhdl_element::emit_comment(std::ostream &of, int level,
bool end_of_line) const
{
if (comment_.size() > 0) {
if (end_of_line)
of << " ";
of << "-- " << comment_;
if (!end_of_line)
newline(of, level);
}
}
void vhdl_element::print() const
{
emit(std::cout, 0);
std::cout << std::endl;
}

56
tgt-vhdl/vhdl_element.hh Normal file
View File

@ -0,0 +1,56 @@
/*
* VHDL abstract syntax elements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef INC_VHDL_ELEMENT_HH
#define INC_VHDL_ELEMENT_HH
#include <fstream>
#include <list>
#include <string>
typedef std::list<std::string> string_list_t;
/*
* Any VHDL syntax element. Each element can also contain a comment.
*/
class vhdl_element {
public:
virtual ~vhdl_element() {}
virtual void emit(std::ostream &of, int level=0) const = 0;
void print() const;
void set_comment(std::string comment);
protected:
void emit_comment(std::ostream &of, int level,
bool end_of_line=false) const;
private:
std::string comment_;
};
typedef std::list<vhdl_element*> element_list_t;
int indent(int level);
void newline(std::ostream &of, int level);
std::string nl_string(int level);
void blank_line(std::ostream &of, int level);
#endif

78
tgt-vhdl/vhdl_helper.hh Normal file
View File

@ -0,0 +1,78 @@
/*
* Helper functions for VHDL syntax elements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef INC_VHDL_HELPER_HH
#define INC_VHDL_HELPER_HH
#include <fstream>
#include <list>
#include <cassert>
template <class T>
void emit_children(std::ostream &of,
const std::list<T*> &children,
int level, const char *delim = "",
bool trailing_newline = true)
{
// Don't indent if there are no children
if (children.size() == 0)
newline(of, level);
else {
typename std::list<T*>::const_iterator it;
int sz = children.size();
for (it = children.begin(); it != children.end(); ++it) {
newline(of, indent(level));
(*it)->emit(of, indent(level));
if (--sz > 0)
of << delim;
}
if (trailing_newline)
newline(of, level);
}
}
template <class T>
void delete_children(std::list<T*> &children)
{
typename std::list<T*>::iterator it;
for (it = children.begin(); it != children.end(); ++it)
delete *it;
children.clear();
}
static inline char vl_to_vhdl_bit(char bit)
{
switch (bit) {
case '0':
case 'Z':
case '1':
return bit;
case 'z':
return 'Z';
case 'x':
case 'X':
return 'U';
case '?':
return '-';
}
assert(false);
}
#endif

945
tgt-vhdl/vhdl_syntax.cc Normal file
View File

@ -0,0 +1,945 @@
/*
* VHDL abstract syntax elements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_syntax.hh"
#include "vhdl_helper.hh"
#include <cassert>
#include <cstring>
#include <iostream>
#include <typeinfo>
vhdl_scope::vhdl_scope()
: parent_(NULL), init_(false), sig_assign_(true)
{
}
vhdl_scope::~vhdl_scope()
{
delete_children<vhdl_decl>(decls_);
}
void vhdl_scope::set_initializing(bool i)
{
init_ = i;
if (parent_)
parent_->set_initializing(i);
}
void vhdl_scope::add_decl(vhdl_decl *decl)
{
decls_.push_back(decl);
}
void vhdl_scope::add_forward_decl(vhdl_decl *decl)
{
decls_.push_front(decl);
}
vhdl_decl *vhdl_scope::get_decl(const std::string &name) const
{
decl_list_t::const_iterator it;
for (it = decls_.begin(); it != decls_.end(); ++it) {
if (strcasecmp((*it)->get_name().c_str(), name.c_str()) == 0)
return *it;
}
return parent_ ? parent_->get_decl(name) : NULL;
}
bool vhdl_scope::have_declared(const std::string &name) const
{
return get_decl(name) != NULL;
}
bool vhdl_scope::contained_within(const vhdl_scope *other) const
{
if (this == other)
return true;
else if (NULL == parent_)
return false;
else
return parent_->contained_within(other);
}
vhdl_scope *vhdl_scope::get_parent() const
{
assert(parent_);
return parent_;
}
vhdl_entity::vhdl_entity(const char *name, const char *derived_from,
vhdl_arch *arch)
: name_(name), arch_(arch), derived_from_(derived_from)
{
arch->get_scope()->set_parent(&ports_);
}
vhdl_entity::~vhdl_entity()
{
delete arch_;
}
void vhdl_entity::add_port(vhdl_port_decl *decl)
{
ports_.add_decl(decl);
}
void vhdl_entity::emit(std::ostream &of, int level) const
{
// Pretty much every design will use std_logic so we
// might as well include it by default
of << "library ieee;" << std::endl;
of << "use ieee.std_logic_1164.all;" << std::endl;
of << "use ieee.numeric_std.all;" << std::endl;
of << "use std.textio.all;" << std::endl;
of << std::endl;
emit_comment(of, level);
of << "entity " << name_ << " is";
if (!ports_.empty()) {
newline(of, indent(level));
of << "port (";
emit_children<vhdl_decl>(of, ports_.get_decls(), indent(level), ";");
of << ");";
}
newline(of, level);
of << "end entity; ";
blank_line(of, level); // Extra blank line after entities
arch_->emit(of, level);
}
vhdl_arch::~vhdl_arch()
{
delete_children<vhdl_conc_stmt>(stmts_);
}
void vhdl_arch::add_stmt(vhdl_process *proc)
{
proc->get_scope()->set_parent(&scope_);
stmts_.push_back(proc);
}
void vhdl_arch::add_stmt(vhdl_conc_stmt *stmt)
{
stmts_.push_back(stmt);
}
void vhdl_arch::emit(std::ostream &of, int level) const
{
emit_comment(of, level);
of << "architecture " << name_ << " of " << entity_;
of << " is";
emit_children<vhdl_decl>(of, scope_.get_decls(), level);
of << "begin";
emit_children<vhdl_conc_stmt>(of, stmts_, level);
of << "end architecture;";
blank_line(of, level); // Extra blank line after architectures;
}
void vhdl_process::add_sensitivity(const std::string &name)
{
sens_.push_back(name);
}
void vhdl_process::emit(std::ostream &of, int level) const
{
// If there are no statements in the body, this process
// can't possibly do anything, so don't bother to emit it
if (stmts_.empty()) {
of << "-- Removed one empty process";
newline(of, level);
return;
}
newline(of, level);
emit_comment(of, level);
if (name_.size() > 0)
of << name_ << ": ";
of << "process ";
int num_sens = sens_.size();
if (num_sens > 0) {
of << "(";
string_list_t::const_iterator it;
for (it = sens_.begin(); it != sens_.end(); ++it) {
of << *it;
if (--num_sens > 0)
of << ", ";
}
of << ") ";
}
of << "is";
emit_children<vhdl_decl>(of, scope_.get_decls(), level);
of << "begin";
stmts_.emit(of, level);
of << "end process;";
}
stmt_container::~stmt_container()
{
delete_children<vhdl_seq_stmt>(stmts_);
}
void stmt_container::add_stmt(vhdl_seq_stmt *stmt)
{
stmts_.push_back(stmt);
}
void stmt_container::emit(std::ostream &of, int level, bool newline) const
{
emit_children<vhdl_seq_stmt>(of, stmts_, level, "", newline);
}
vhdl_comp_inst::vhdl_comp_inst(const char *inst_name, const char *comp_name)
: comp_name_(comp_name), inst_name_(inst_name)
{
}
vhdl_comp_inst::~vhdl_comp_inst()
{
port_map_list_t::iterator it;
for (it = mapping_.begin(); it != mapping_.end(); ++it) {
delete (*it).expr;
}
mapping_.clear();
}
void vhdl_comp_inst::map_port(const char *name, vhdl_expr *expr)
{
port_map_t pmap = { name, expr };
mapping_.push_back(pmap);
}
void vhdl_comp_inst::emit(std::ostream &of, int level) const
{
newline(of, level);
emit_comment(of, level);
of << inst_name_ << ": " << comp_name_;
// If there are no ports or generics we don't need to mention them...
if (mapping_.size() > 0) {
newline(of, indent(level));
of << "port map (";
int sz = mapping_.size();
port_map_list_t::const_iterator it;
for (it = mapping_.begin(); it != mapping_.end(); ++it) {
newline(of, indent(indent(level)));
of << (*it).name << " => ";
(*it).expr->emit(of, level);
if (--sz > 0)
of << ",";
}
newline(of, indent(level));
of << ")";
}
of << ";";
}
vhdl_component_decl::vhdl_component_decl(const char *name)
: vhdl_decl(name)
{
}
/*
* Create a component declaration for the given entity.
*/
vhdl_component_decl *vhdl_component_decl::component_decl_for(vhdl_entity *ent)
{
assert(ent != NULL);
vhdl_component_decl *decl = new vhdl_component_decl
(ent->get_name().c_str());
decl->ports_ = ent->get_scope()->get_decls();
return decl;
}
void vhdl_component_decl::emit(std::ostream &of, int level) const
{
newline(of, level);
emit_comment(of, level);
of << "component " << name_ << " is";
if (ports_.size() > 0) {
newline(of, indent(level));
of << "port (";
emit_children<vhdl_decl>(of, ports_, indent(level), ";");
of << ");";
}
newline(of, level);
of << "end component;";
}
vhdl_wait_stmt::~vhdl_wait_stmt()
{
if (expr_ != NULL)
delete expr_;
}
void vhdl_wait_stmt::emit(std::ostream &of, int level) const
{
of << "wait";
switch (type_) {
case VHDL_WAIT_INDEF:
break;
case VHDL_WAIT_FOR:
assert(expr_);
of << " for ";
expr_->emit(of, level);
break;
case VHDL_WAIT_UNTIL:
assert(expr_);
of << " until ";
expr_->emit(of, level);
break;
case VHDL_WAIT_ON:
{
of << " on ";
string_list_t::const_iterator it = sensitivity_.begin();
while (it != sensitivity_.end()) {
of << *it;
if (++it != sensitivity_.end())
of << ", ";
}
}
break;
}
of << ";";
}
vhdl_decl::~vhdl_decl()
{
if (type_ != NULL)
delete type_;
if (initial_ != NULL)
delete initial_;
}
const vhdl_type *vhdl_decl::get_type() const
{
assert(type_);
return type_;
}
void vhdl_decl::set_initial(vhdl_expr *initial)
{
if (!has_initial_) {
assert(initial_ == NULL);
initial_ = initial;
has_initial_ = true;
}
}
void vhdl_port_decl::emit(std::ostream &of, int level) const
{
of << name_ << " : ";
switch (mode_) {
case VHDL_PORT_IN:
of << "in ";
break;
case VHDL_PORT_OUT:
of << "out ";
break;
case VHDL_PORT_INOUT:
of << "inout ";
break;
case VHDL_PORT_BUFFER:
of << "buffer ";
break;
}
type_->emit(of, level);
}
void vhdl_var_decl::emit(std::ostream &of, int level) const
{
of << "variable " << name_ << " : ";
type_->emit(of, level);
if (initial_) {
of << " := ";
initial_->emit(of, level);
}
of << ";";
emit_comment(of, level, true);
}
void vhdl_signal_decl::emit(std::ostream &of, int level) const
{
of << "signal " << name_ << " : ";
type_->emit(of, level);
if (initial_) {
of << " := ";
initial_->emit(of, level);
}
of << ";";
emit_comment(of, level, true);
}
void vhdl_type_decl::emit(std::ostream &of, int level) const
{
of << "type " << name_ << " is ";
of << type_->get_type_decl_string() << ";";
emit_comment(of, level, true);
}
vhdl_expr::~vhdl_expr()
{
if (type_ != NULL)
delete type_;
}
void vhdl_expr_list::add_expr(vhdl_expr *e)
{
exprs_.push_back(e);
}
vhdl_expr_list::~vhdl_expr_list()
{
delete_children<vhdl_expr>(exprs_);
}
void vhdl_expr_list::emit(std::ostream &of, int level) const
{
of << "(";
int size = exprs_.size();
std::list<vhdl_expr*>::const_iterator it;
for (it = exprs_.begin(); it != exprs_.end(); ++it) {
(*it)->emit(of, level);
if (--size > 0)
of << ", ";
}
of << ")";
}
void vhdl_pcall_stmt::emit(std::ostream &of, int level) const
{
of << name_;
if (!exprs_.empty())
exprs_.emit(of, level);
of << ";";
}
vhdl_var_ref::~vhdl_var_ref()
{
if (slice_)
delete slice_;
}
void vhdl_var_ref::set_slice(vhdl_expr *s, int w)
{
assert(type_);
slice_ = s;
slice_width_ = w;
vhdl_type_name_t tname = type_->get_name();
if (tname == VHDL_TYPE_ARRAY) {
type_ = new vhdl_type(*type_->get_base());
}
else {
assert(tname == VHDL_TYPE_UNSIGNED || tname == VHDL_TYPE_SIGNED);
if (type_)
delete type_;
if (w > 0)
type_ = new vhdl_type(tname, w);
else
type_ = vhdl_type::std_logic();
}
}
void vhdl_var_ref::emit(std::ostream &of, int level) const
{
of << name_;
if (slice_) {
of << "(";
if (slice_width_ > 0) {
slice_->emit(of, level);
of << " + " << slice_width_ << " downto ";
}
slice_->emit(of, level);
of << ")";
}
}
void vhdl_const_string::emit(std::ostream &of, int level) const
{
// In some instances a string literal can be ambiguous between
// a String type and some other types (e.g. std_logic_vector)
// The explicit cast to String removes this ambiguity (although
// isn't always strictly necessary)
of << "String'(\"" << value_ << "\")";
}
void vhdl_null_stmt::emit(std::ostream &of, int level) const
{
of << "null;";
}
void vhdl_fcall::emit(std::ostream &of, int level) const
{
of << name_;
exprs_.emit(of, level);
}
vhdl_abstract_assign_stmt::~vhdl_abstract_assign_stmt()
{
delete lhs_;
delete rhs_;
if (after_)
delete after_;
}
void vhdl_nbassign_stmt::emit(std::ostream &of, int level) const
{
lhs_->emit(of, level);
of << " <= ";
rhs_->emit(of, level);
if (after_) {
of << " after ";
after_->emit(of, level);
}
of << ";";
}
void vhdl_assign_stmt::emit(std::ostream &of, int level) const
{
lhs_->emit(of, level);
of << " := ";
rhs_->emit(of, level);
of << ";";
}
vhdl_const_bits::vhdl_const_bits(const char *value, int width, bool issigned)
: vhdl_expr(issigned ? vhdl_type::nsigned(width)
: vhdl_type::nunsigned(width), true),
qualified_(false),
signed_(issigned)
{
// Can't rely on value being NULL-terminated
while (width--)
value_.push_back(*value++);
}
void vhdl_const_bits::emit(std::ostream &of, int level) const
{
if (qualified_)
of << (signed_ ? "signed" : "unsigned") << "'(\"";
else
of << "\"";
// The bits appear to be in reverse order
std::string::const_reverse_iterator it;
for (it = value_.rbegin(); it != value_.rend(); ++it)
of << vl_to_vhdl_bit(*it);
of << (qualified_ ? "\")" : "\"");
}
void vhdl_const_bit::emit(std::ostream &of, int level) const
{
of << "'" << vl_to_vhdl_bit(bit_) << "'";
}
void vhdl_const_int::emit(std::ostream &of, int level) const
{
of << value_;
}
void vhdl_const_bool::emit(std::ostream &of, int level) const
{
of << (value_ ? "True" : "False");
}
void vhdl_const_time::emit(std::ostream &of, int level) const
{
of << value_;
switch (units_) {
case TIME_UNIT_NS:
of << " ns";
}
}
vhdl_cassign_stmt::~vhdl_cassign_stmt()
{
delete lhs_;
delete rhs_;
for (std::list<when_part_t>::const_iterator it = whens_.begin();
it != whens_.end();
++it) {
delete (*it).value;
delete (*it).cond;
}
}
void vhdl_cassign_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond)
{
when_part_t when = { value, cond, NULL };
whens_.push_back(when);
}
void vhdl_cassign_stmt::emit(std::ostream &of, int level) const
{
lhs_->emit(of, level);
of << " <= ";
if (!whens_.empty()) {
for (std::list<when_part_t>::const_iterator it = whens_.begin();
it != whens_.end();
++it) {
(*it).value->emit(of, level);
of << " when ";
(*it).cond->emit(of, level);
of << " ";
}
of << "else ";
}
rhs_->emit(of, level);
if (after_) {
of << " after ";
after_->emit(of, level);
}
of << ";";
}
void vhdl_assert_stmt::emit(std::ostream &of, int level) const
{
of << "assert false"; // TODO: Allow arbitrary expression
of << " report \"" << reason_ << "\" severity failure;";
}
vhdl_if_stmt::vhdl_if_stmt(vhdl_expr *test)
{
// Need to ensure that the expression is Boolean
vhdl_type boolean(VHDL_TYPE_BOOLEAN);
test_ = test->cast(&boolean);
}
vhdl_if_stmt::~vhdl_if_stmt()
{
delete test_;
}
void vhdl_if_stmt::emit(std::ostream &of, int level) const
{
of << "if ";
test_->emit(of, level);
of << " then";
then_part_.emit(of, level);
if (!else_part_.empty()) {
of << "else";
else_part_.emit(of, level);
}
of << "end if;";
}
vhdl_unaryop_expr::~vhdl_unaryop_expr()
{
delete operand_;
}
void vhdl_unaryop_expr::emit(std::ostream &of, int level) const
{
of << "(";
switch (op_) {
case VHDL_UNARYOP_NOT:
of << "not ";
break;
case VHDL_UNARYOP_NEG:
of << "-";
break;
}
operand_->emit(of, level);
of << ")";
}
vhdl_binop_expr::vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op,
vhdl_expr *right, vhdl_type *type)
: vhdl_expr(type), op_(op)
{
add_expr(left);
add_expr(right);
}
vhdl_binop_expr::~vhdl_binop_expr()
{
delete_children<vhdl_expr>(operands_);
}
void vhdl_binop_expr::add_expr(vhdl_expr *e)
{
operands_.push_back(e);
}
void vhdl_binop_expr::emit(std::ostream &of, int level) const
{
// Expressions are fully parenthesized to remove any
// ambiguity in the output
of << "(";
assert(operands_.size() > 0);
std::list<vhdl_expr*>::const_iterator it = operands_.begin();
(*it)->emit(of, level);
while (++it != operands_.end()) {
const char* ops[] = {
"and", "or", "=", "/=", "+", "-", "*", "<",
">", "<=", ">=", "sll", "srl", "xor", "&",
"nand", "nor", "xnor", "/", "mod", NULL
};
of << " " << ops[op_] << " ";
(*it)->emit(of, level);
}
of << ")";
}
vhdl_bit_spec_expr::~vhdl_bit_spec_expr()
{
if (others_)
delete others_;
std::list<bit_map>::iterator it;
for (it = bits_.begin(); it != bits_.end(); ++it)
delete (*it).e;
}
void vhdl_bit_spec_expr::add_bit(int bit, vhdl_expr *e)
{
bit_map bm = { bit, e };
bits_.push_back(bm);
}
void vhdl_bit_spec_expr::emit(std::ostream &of, int level) const
{
of << "(";
std::list<bit_map>::const_iterator it;
it = bits_.begin();
while (it != bits_.end()) {
of << (*it).bit << " => ";
(*it).e->emit(of, level);
if (++it != bits_.end())
of << ", ";
}
if (others_) {
of << (bits_.empty() ? "" : ", ") << "others => ";
others_->emit(of, level);
}
of << ")";
}
vhdl_case_branch::~vhdl_case_branch()
{
delete when_;
}
void vhdl_case_branch::emit(std::ostream &of, int level) const
{
of << "when ";
when_->emit(of, level);
of << " =>";
stmts_.emit(of, indent(level), false);
}
vhdl_case_stmt::~vhdl_case_stmt()
{
delete test_;
}
void vhdl_case_stmt::emit(std::ostream &of, int level) const
{
of << "case ";
test_->emit(of, level);
of << " is";
newline(of, indent(level));
case_branch_list_t::const_iterator it;
int n = branches_.size();
for (it = branches_.begin(); it != branches_.end(); ++it) {
(*it)->emit(of, level);
if (--n > 0)
newline(of, indent(level));
else
newline(of, level);
}
of << "end case;";
}
vhdl_while_stmt::~vhdl_while_stmt()
{
delete test_;
}
void vhdl_while_stmt::emit(std::ostream &of, int level) const
{
of << "while ";
test_->emit(of, level);
of << " ";
vhdl_loop_stmt::emit(of, level);
}
void vhdl_loop_stmt::emit(std::ostream &of, int level) const
{
of << "loop";
stmts_.emit(of, level);
of << "end loop;";
}
vhdl_for_stmt::~vhdl_for_stmt()
{
delete from_;
delete to_;
}
void vhdl_for_stmt::emit(std::ostream &of, int level) const
{
of << "for " << lname_ << " in ";
from_->emit(of, level);
of << " to ";
to_->emit(of, level);
of << " ";
vhdl_loop_stmt::emit(of, level);
}
vhdl_function::vhdl_function(const char *name, vhdl_type *ret_type)
: vhdl_decl(name, ret_type)
{
// A function contains two scopes:
// scope_ = The paramters
// variables_ = Local variables
// A call to get_scope returns variables_ whose parent is scope_
variables_.set_parent(&scope_);
}
void vhdl_function::emit(std::ostream &of, int level) const
{
newline(of, level);
emit_comment(of, level);
of << "function " << name_ << " (";
emit_children<vhdl_decl>(of, scope_.get_decls(), level, ";");
of << ") ";
newline(of, level);
of << "return " << type_->get_string() << " is";
emit_children<vhdl_decl>(of, variables_.get_decls(), level);
of << "begin";
stmts_.emit(of, level);
of << " return " << name_ << "_Result;";
newline(of, level);
of << "end function;";
}
void vhdl_forward_fdecl::emit(std::ostream &of, int level) const
{
of << "function " << f_->get_name() << " (";
emit_children<vhdl_decl>(of, f_->scope_.get_decls(), level, ";");
of << ") ";
newline(of, level);
of << "return " << f_->type_->get_string() << ";";
newline(of, level);
}
void vhdl_param_decl::emit(std::ostream &of, int level) const
{
of << name_ << " : ";
type_->emit(of, level);
}
vhdl_with_select_stmt::~vhdl_with_select_stmt()
{
delete test_;
delete out_;
for (when_list_t::const_iterator it = whens_.begin();
it != whens_.end();
++it) {
delete (*it).value;
delete (*it).cond;
if ((*it).delay)
delete (*it).delay;
}
}
void vhdl_with_select_stmt::emit(std::ostream &of, int level) const
{
of << "with ";
test_->emit(of, level);
of << " select";
emit_comment(of, level, true);
newline(of, indent(level));
out_->emit(of, level);
of << " <= ";
when_list_t::const_iterator it = whens_.begin();
while (it != whens_.end()) {
(*it).value->emit(of, level);
if ((*it).delay) {
of << " after ";
(*it).delay->emit(of, level);
}
of << " when ";
(*it).cond->emit(of, level);
if (++it != whens_.end()) {
of << ",";
newline(of, indent(level));
}
else
of << ";";
}
}
void vhdl_with_select_stmt::add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay)
{
when_part_t when = { value, cond, delay };
whens_.push_back(when);
}

790
tgt-vhdl/vhdl_syntax.hh Normal file
View File

@ -0,0 +1,790 @@
/*
* VHDL abstract syntax elements.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef INC_VHDL_SYNTAX_HH
#define INC_VHDL_SYNTAX_HH
#include "vhdl_element.hh"
#include "vhdl_type.hh"
class vhdl_scope;
class vhdl_entity;
class vhdl_arch;
class vhdl_expr : public vhdl_element {
public:
vhdl_expr(vhdl_type* type, bool isconst=false)
: type_(type), isconst_(isconst) {}
virtual ~vhdl_expr();
const vhdl_type *get_type() const { return type_; }
bool constant() const { return isconst_; }
vhdl_expr *cast(const vhdl_type *to);
virtual vhdl_expr *resize(int newwidth);
virtual vhdl_expr *to_boolean();
virtual vhdl_expr *to_integer();
virtual vhdl_expr *to_std_logic();
virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w);
protected:
vhdl_type *type_;
bool isconst_;
};
/*
* A scalar or array variable reference.
*/
class vhdl_var_ref : public vhdl_expr {
public:
vhdl_var_ref(const char *name, vhdl_type *type,
vhdl_expr *slice = NULL)
: vhdl_expr(type), name_(name), slice_(slice) {}
~vhdl_var_ref();
void emit(std::ostream &of, int level) const;
const std::string &get_name() const { return name_; }
void set_name(const std::string &name) { name_ = name; }
void set_slice(vhdl_expr *s, int w=0);
private:
std::string name_;
vhdl_expr *slice_;
unsigned slice_width_;
};
enum vhdl_binop_t {
VHDL_BINOP_AND = 0,
VHDL_BINOP_OR,
VHDL_BINOP_EQ,
VHDL_BINOP_NEQ,
VHDL_BINOP_ADD,
VHDL_BINOP_SUB,
VHDL_BINOP_MULT,
VHDL_BINOP_LT,
VHDL_BINOP_GT,
VHDL_BINOP_LEQ,
VHDL_BINOP_GEQ,
VHDL_BINOP_SL,
VHDL_BINOP_SR,
VHDL_BINOP_XOR,
VHDL_BINOP_CONCAT,
VHDL_BINOP_NAND,
VHDL_BINOP_NOR,
VHDL_BINOP_XNOR,
VHDL_BINOP_DIV,
VHDL_BINOP_MOD,
};
/*
* A binary expression contains a list of operands rather
* than just two: this is to model n-input gates and the
* like. A second constructor is provided to handle the
* common case of a true binary expression.
*/
class vhdl_binop_expr : public vhdl_expr {
public:
vhdl_binop_expr(vhdl_binop_t op, vhdl_type *type)
: vhdl_expr(type), op_(op) {}
vhdl_binop_expr(vhdl_expr *left, vhdl_binop_t op,
vhdl_expr *right, vhdl_type *type);
~vhdl_binop_expr();
void add_expr(vhdl_expr *e);
void emit(std::ostream &of, int level) const;
private:
std::list<vhdl_expr*> operands_;
vhdl_binop_t op_;
};
enum vhdl_unaryop_t {
VHDL_UNARYOP_NOT,
VHDL_UNARYOP_NEG,
};
class vhdl_unaryop_expr : public vhdl_expr {
public:
vhdl_unaryop_expr(vhdl_unaryop_t op, vhdl_expr *operand,
vhdl_type *type)
: vhdl_expr(type), op_(op), operand_(operand) {}
~vhdl_unaryop_expr();
void emit(std::ostream &of, int level) const;
private:
vhdl_unaryop_t op_;
vhdl_expr *operand_;
};
/*
* An expression like (0 => '1', 2 => '0', others => 'Z')
*/
class vhdl_bit_spec_expr : public vhdl_expr {
public:
vhdl_bit_spec_expr(vhdl_type *type, vhdl_expr *others)
: vhdl_expr(type), others_(others) {}
~vhdl_bit_spec_expr();
void add_bit(int bit, vhdl_expr *e);
void emit(std::ostream &of, int level) const;
private:
vhdl_expr *others_;
struct bit_map {
int bit;
vhdl_expr *e;
};
std::list<bit_map> bits_;
};
class vhdl_const_string : public vhdl_expr {
public:
vhdl_const_string(const char *value)
: vhdl_expr(vhdl_type::string(), true), value_(value) {}
void emit(std::ostream &of, int level) const;
private:
std::string value_;
};
class vhdl_const_bits : public vhdl_expr {
public:
vhdl_const_bits(const char *value, int width, bool issigned);
void emit(std::ostream &of, int level) const;
const std::string &get_value() const { return value_; }
vhdl_expr *to_integer();
vhdl_expr *to_std_logic();
vhdl_expr *to_vector(vhdl_type_name_t name, int w);
private:
int bits_to_int() const;
std::string value_;
bool qualified_, signed_;
};
class vhdl_const_bit : public vhdl_expr {
public:
vhdl_const_bit(char bit)
: vhdl_expr(vhdl_type::std_logic(), true), bit_(bit) {}
void emit(std::ostream &of, int level) const;
vhdl_expr *to_boolean();
vhdl_expr *to_integer();
private:
char bit_;
};
enum time_unit_t {
TIME_UNIT_NS,
};
class vhdl_const_time : public vhdl_expr {
public:
vhdl_const_time(int64_t value, time_unit_t units = TIME_UNIT_NS)
: vhdl_expr(vhdl_type::time(), true), value_(value), units_(units) {}
void emit(std::ostream &of, int level) const;
private:
int64_t value_;
time_unit_t units_;
};
class vhdl_const_int : public vhdl_expr {
public:
vhdl_const_int(int64_t value)
: vhdl_expr(vhdl_type::integer(), true), value_(value) {}
void emit(std::ostream &of, int level) const;
vhdl_expr *to_vector(vhdl_type_name_t name, int w);
private:
int64_t value_;
};
class vhdl_const_bool : public vhdl_expr {
public:
vhdl_const_bool(bool value)
: vhdl_expr(vhdl_type::boolean(), true), value_(value) {}
void emit(std::ostream &of, int level) const;
private:
bool value_;
};
class vhdl_expr_list : public vhdl_element {
public:
~vhdl_expr_list();
void emit(std::ostream &of, int level) const;
bool empty() const { return exprs_.empty(); }
void add_expr(vhdl_expr *e);
private:
std::list<vhdl_expr*> exprs_;
};
/*
* A function call within an expression.
*/
class vhdl_fcall : public vhdl_expr {
public:
vhdl_fcall(const char *name, vhdl_type *rtype)
: vhdl_expr(rtype), name_(name) {};
~vhdl_fcall() {}
void add_expr(vhdl_expr *e) { exprs_.add_expr(e); }
void emit(std::ostream &of, int level) const;
private:
std::string name_;
vhdl_expr_list exprs_;
};
/*
* A concurrent statement appears in architecture bodies/
*/
class vhdl_conc_stmt : public vhdl_element {
public:
virtual ~vhdl_conc_stmt() {}
};
typedef std::list<vhdl_conc_stmt*> conc_stmt_list_t;
/*
* A '<value> when <cond>' clause that appears in several
* statement types.
*/
struct when_part_t {
vhdl_expr *value, *cond, *delay;
};
typedef std::list<when_part_t> when_list_t;
/*
* A concurrent signal assignment (i.e. not part of a process).
* Can have any number of `when' clauses, in which case the original
* rhs becomes the `else' part.
*/
class vhdl_cassign_stmt : public vhdl_conc_stmt {
public:
vhdl_cassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
: lhs_(lhs), rhs_(rhs), after_(NULL) {}
~vhdl_cassign_stmt();
void emit(std::ostream &of, int level) const;
void add_condition(vhdl_expr *value, vhdl_expr *cond);
void set_after(vhdl_expr *a) { after_ = a; }
private:
vhdl_var_ref *lhs_;
vhdl_expr *rhs_;
vhdl_expr *after_;
when_list_t whens_;
};
class vhdl_with_select_stmt : public vhdl_conc_stmt {
public:
vhdl_with_select_stmt(vhdl_expr *test, vhdl_var_ref *out)
: test_(test), out_(out) {}
~vhdl_with_select_stmt();
void emit(std::ostream &of, int level) const;
void add_condition(vhdl_expr *value, vhdl_expr *cond, vhdl_expr *delay=NULL);
private:
vhdl_expr *test_;
vhdl_var_ref *out_;
when_list_t whens_;
};
/*
* Any sequential statement in a process.
*/
class vhdl_seq_stmt : public vhdl_element {
public:
virtual ~vhdl_seq_stmt() {}
};
/*
* A list of sequential statements. For example inside a
* process, loop, or if statement.
*/
class stmt_container {
public:
~stmt_container();
void add_stmt(vhdl_seq_stmt *stmt);
void emit(std::ostream &of, int level, bool newline=true) const;
bool empty() const { return stmts_.empty(); }
private:
std::list<vhdl_seq_stmt*> stmts_;
};
/*
* Shared between blocking and non-blocking assignment.
*/
class vhdl_abstract_assign_stmt : public vhdl_seq_stmt {
public:
vhdl_abstract_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
: lhs_(lhs), rhs_(rhs), after_(NULL) {}
virtual ~vhdl_abstract_assign_stmt();
void set_after(vhdl_expr *after) { after_ = after; }
protected:
vhdl_var_ref *lhs_;
vhdl_expr *rhs_, *after_;
};
/*
* Similar to Verilog non-blocking assignment, except the LHS
* must be a signal not a variable.
*/
class vhdl_nbassign_stmt : public vhdl_abstract_assign_stmt {
public:
vhdl_nbassign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
: vhdl_abstract_assign_stmt(lhs, rhs) {}
void emit(std::ostream &of, int level) const;
};
class vhdl_assign_stmt : public vhdl_abstract_assign_stmt {
public:
vhdl_assign_stmt(vhdl_var_ref *lhs, vhdl_expr *rhs)
: vhdl_abstract_assign_stmt(lhs, rhs) {}
void emit(std::ostream &of, int level) const;
};
enum vhdl_wait_type_t {
VHDL_WAIT_INDEF, // Suspend indefinitely
VHDL_WAIT_FOR, // Wait for a constant amount of time
VHDL_WAIT_UNTIL, // Wait on an expression
VHDL_WAIT_ON, // Wait on a sensitivity list
};
/*
* Delay simulation indefinitely, until an event, or for a
* specified time.
*/
class vhdl_wait_stmt : public vhdl_seq_stmt {
public:
vhdl_wait_stmt(vhdl_wait_type_t type = VHDL_WAIT_INDEF,
vhdl_expr *expr = NULL)
: type_(type), expr_(expr) {}
~vhdl_wait_stmt();
void emit(std::ostream &of, int level) const;
void add_sensitivity(const std::string &s) { sensitivity_.push_back(s); }
private:
vhdl_wait_type_t type_;
vhdl_expr *expr_;
string_list_t sensitivity_;
};
class vhdl_null_stmt : public vhdl_seq_stmt {
public:
void emit(std::ostream &of, int level) const;
};
class vhdl_assert_stmt : public vhdl_seq_stmt {
public:
vhdl_assert_stmt(const char *reason)
: reason_(reason) {}
void emit(std::ostream &of, int level) const;
private:
std::string reason_;
};
class vhdl_if_stmt : public vhdl_seq_stmt {
public:
vhdl_if_stmt(vhdl_expr *test);
~vhdl_if_stmt();
stmt_container *get_then_container() { return &then_part_; }
stmt_container *get_else_container() { return &else_part_; }
void emit(std::ostream &of, int level) const;
private:
vhdl_expr *test_;
stmt_container then_part_, else_part_;
};
/*
* A single branch in a case statement consisting of an
* expression part and a statement container.
*/
class vhdl_case_branch : public vhdl_element {
public:
vhdl_case_branch(vhdl_expr *when) : when_(when) {}
~vhdl_case_branch();
stmt_container *get_container() { return &stmts_; }
void emit(std::ostream &of, int level) const;
private:
vhdl_expr *when_;
stmt_container stmts_;
};
typedef std::list<vhdl_case_branch*> case_branch_list_t;
class vhdl_case_stmt : public vhdl_seq_stmt {
public:
vhdl_case_stmt(vhdl_expr *test) : test_(test) {}
~vhdl_case_stmt();
void add_branch(vhdl_case_branch *b) { branches_.push_back(b); }
void emit(std::ostream &of, int level) const;
private:
vhdl_expr *test_;
case_branch_list_t branches_;
};
class vhdl_loop_stmt : public vhdl_seq_stmt {
public:
virtual ~vhdl_loop_stmt() {}
stmt_container *get_container() { return &stmts_; }
void emit(std::ostream &of, int level) const;
private:
stmt_container stmts_;
};
class vhdl_while_stmt : public vhdl_loop_stmt {
public:
vhdl_while_stmt(vhdl_expr *test) : test_(test) {}
~vhdl_while_stmt();
void emit(std::ostream &of, int level) const;
private:
vhdl_expr *test_;
};
class vhdl_for_stmt : public vhdl_loop_stmt {
public:
vhdl_for_stmt(const char *lname, vhdl_expr *from, vhdl_expr *to)
: lname_(lname), from_(from), to_(to) {}
~vhdl_for_stmt();
void emit(std::ostream &of, int level) const;
private:
const char *lname_;
vhdl_expr *from_, *to_;
};
/*
* A procedure call. Which is a statement, unlike a function
* call which is an expression.
*/
class vhdl_pcall_stmt : public vhdl_seq_stmt {
public:
vhdl_pcall_stmt(const char *name) : name_(name) {}
void emit(std::ostream &of, int level) const;
void add_expr(vhdl_expr *e) { exprs_.add_expr(e); }
private:
std::string name_;
vhdl_expr_list exprs_;
};
/*
* A declaration of some sort (variable, component, etc.).
* Declarations have names, which is the identifier of the variable,
* constant, etc. not the type.
*/
class vhdl_decl : public vhdl_element {
public:
vhdl_decl(const char *name, vhdl_type *type = NULL,
vhdl_expr *initial = NULL)
: name_(name), type_(type), initial_(initial),
has_initial_(initial != NULL) {}
virtual ~vhdl_decl();
const std::string &get_name() const { return name_; }
const vhdl_type *get_type() const;
void set_type(vhdl_type *t) { type_ = t; }
void set_initial(vhdl_expr *initial);
bool has_initial() const { return has_initial_; }
protected:
std::string name_;
vhdl_type *type_;
vhdl_expr *initial_;
bool has_initial_;
};
typedef std::list<vhdl_decl*> decl_list_t;
/*
* A forward declaration of a component. At the moment it is assumed
* that components declarations will only ever be for entities
* generated by this code generator. This is enforced by making the
* constructor private (use component_decl_for instead).
*/
class vhdl_component_decl : public vhdl_decl {
public:
static vhdl_component_decl *component_decl_for(vhdl_entity *ent);
void emit(std::ostream &of, int level) const;
private:
vhdl_component_decl(const char *name);
decl_list_t ports_;
};
class vhdl_type_decl : public vhdl_decl {
public:
vhdl_type_decl(const char *name, vhdl_type *base)
: vhdl_decl(name, base) {}
void emit(std::ostream &of, int level) const;
};
/*
* A variable declaration inside a process (although this isn't
* enforced here).
*/
class vhdl_var_decl : public vhdl_decl {
public:
vhdl_var_decl(const char *name, vhdl_type *type)
: vhdl_decl(name, type) {}
void emit(std::ostream &of, int level) const;
};
/*
* A signal declaration in architecture.
*/
class vhdl_signal_decl : public vhdl_decl {
public:
vhdl_signal_decl(const char *name, vhdl_type *type)
: vhdl_decl(name, type) {}
virtual void emit(std::ostream &of, int level) const;
};
/*
* A parameter to a function.
*/
class vhdl_param_decl : public vhdl_decl {
public:
vhdl_param_decl(const char *name, vhdl_type *type)
: vhdl_decl(name, type) {}
void emit(std::ostream &of, int level) const;
};
enum vhdl_port_mode_t {
VHDL_PORT_IN,
VHDL_PORT_OUT,
VHDL_PORT_INOUT,
VHDL_PORT_BUFFER,
};
/*
* A port declaration is like a signal declaration except
* it has a direction and appears in the entity rather than
* the architecture.
*/
class vhdl_port_decl : public vhdl_decl {
public:
vhdl_port_decl(const char *name, vhdl_type *type,
vhdl_port_mode_t mode)
: vhdl_decl(name, type), mode_(mode) {}
void emit(std::ostream &of, int level) const;
vhdl_port_mode_t get_mode() const { return mode_; }
void set_mode(vhdl_port_mode_t m) { mode_ = m; }
private:
vhdl_port_mode_t mode_;
};
/*
* A mapping from port name to an expression.
*/
struct port_map_t {
std::string name;
vhdl_expr *expr;
};
typedef std::list<port_map_t> port_map_list_t;
/*
* Instantiation of component. This is really only a placeholder
* at the moment until the port mappings are worked out.
*/
class vhdl_comp_inst : public vhdl_conc_stmt {
public:
vhdl_comp_inst(const char *inst_name, const char *comp_name);
~vhdl_comp_inst();
void emit(std::ostream &of, int level) const;
void map_port(const char *name, vhdl_expr *expr);
const std::string &get_comp_name() const { return comp_name_; }
const std::string &get_inst_name() const { return inst_name_; }
private:
std::string comp_name_, inst_name_;
port_map_list_t mapping_;
};
/*
* Contains a list of declarations in a hierarchy.
* A scope can be `initializing' where assignments automatically
* create initial values for declarations.
*/
class vhdl_scope {
public:
vhdl_scope();
~vhdl_scope();
void add_decl(vhdl_decl *decl);
void add_forward_decl(vhdl_decl *decl);
vhdl_decl *get_decl(const std::string &name) const;
bool have_declared(const std::string &name) const;
bool contained_within(const vhdl_scope *other) const;
vhdl_scope *get_parent() const;
bool empty() const { return decls_.empty(); }
const decl_list_t &get_decls() const { return decls_; }
void set_parent(vhdl_scope *p) { parent_ = p; }
bool initializing() const { return init_; }
void set_initializing(bool i);
void set_allow_signal_assignment(bool b) { sig_assign_ = b; }
bool allow_signal_assignment() const { return sig_assign_; }
private:
decl_list_t decls_;
vhdl_scope *parent_;
bool init_, sig_assign_;
};
/*
* Any sort of procedural element: process, function, or
* procedure. Roughly these map onto Verilog's processes,
* functions, and tasks.
*/
class vhdl_procedural {
public:
virtual ~vhdl_procedural() {}
virtual stmt_container *get_container() { return &stmts_; }
virtual vhdl_scope *get_scope() { return &scope_; }
protected:
stmt_container stmts_;
vhdl_scope scope_;
};
class vhdl_function : public vhdl_decl, public vhdl_procedural {
friend class vhdl_forward_fdecl;
public:
vhdl_function(const char *name, vhdl_type *ret_type);
virtual void emit(std::ostream &of, int level) const;
vhdl_scope *get_scope() { return &variables_; }
void add_param(vhdl_param_decl *p) { scope_.add_decl(p); }
private:
vhdl_scope variables_;
};
class vhdl_forward_fdecl : public vhdl_decl {
public:
vhdl_forward_fdecl(const vhdl_function *f)
: vhdl_decl((f->get_name() + "_Forward").c_str()), f_(f) {}
void emit(std::ostream &of, int level) const;
private:
const vhdl_function *f_;
};
class vhdl_process : public vhdl_conc_stmt, public vhdl_procedural {
public:
vhdl_process(const char *name = "") : name_(name) {}
void emit(std::ostream &of, int level) const;
void add_sensitivity(const std::string &name);
private:
std::string name_;
string_list_t sens_;
};
/*
* An architecture which implements an entity.
*/
class vhdl_arch : public vhdl_element {
public:
vhdl_arch(const char *entity, const char *name)
: name_(name), entity_(entity) {}
virtual ~vhdl_arch();
void emit(std::ostream &of, int level=0) const;
void add_stmt(vhdl_process *proc);
void add_stmt(vhdl_conc_stmt *stmt);
vhdl_scope *get_scope() { return &scope_; }
private:
conc_stmt_list_t stmts_;
vhdl_scope scope_;
std::string name_, entity_;
};
/*
* An entity defines the ports, parameters, etc. of a module. Each
* entity is associated with a single architecture (although
* technically this need not be the case). Entities are `derived'
* from instantiations of Verilog module scopes in the hierarchy.
*/
class vhdl_entity : public vhdl_element {
public:
vhdl_entity(const char *name, const char *derived_from,
vhdl_arch *arch);
virtual ~vhdl_entity();
void emit(std::ostream &of, int level=0) const;
void add_port(vhdl_port_decl *decl);
vhdl_arch *get_arch() const { return arch_; }
const std::string &get_name() const { return name_; }
const std::string &get_derived_from() const { return derived_from_; }
vhdl_scope *get_scope() { return &ports_; }
private:
std::string name_;
vhdl_arch *arch_; // Entity may only have a single architecture
std::string derived_from_;
vhdl_scope ports_;
};
typedef std::list<vhdl_entity*> entity_list_t;
#endif

50
tgt-vhdl/vhdl_target.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef INC_VHDL_TARGET_H
#define INC_VHDL_TARGET_H
#include "vhdl_config.h"
#include "ivl_target.h"
#include "vhdl_syntax.hh"
#include "vhdl_type.hh"
#include "support.hh"
#include <string>
using namespace std;
void error(const char *fmt, ...);
int draw_scope(ivl_scope_t scope, void *_parent);
int draw_process(ivl_process_t net, void *cd);
int draw_stmt(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool is_last = false);
int draw_lpm(vhdl_arch *arch, ivl_lpm_t lpm);
void draw_logic(vhdl_arch *arch, ivl_net_logic_t log);
vhdl_expr *translate_expr(ivl_expr_t e);
vhdl_expr *translate_time_expr(ivl_expr_t e);
void remember_entity(vhdl_entity *ent);
vhdl_entity *find_entity(const string &sname);
ivl_design_t get_vhdl_design();
vhdl_entity *get_active_entity();
void set_active_entity(vhdl_entity *ent);
vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus);
bool seen_signal_before(ivl_signal_t sig);
void remember_signal(ivl_signal_t sig, vhdl_scope *scope);
void rename_signal(ivl_signal_t sig, const string &renamed);
vhdl_scope *find_scope_for_signal(ivl_signal_t sig);
const string &get_renamed_signal(ivl_signal_t sig);
ivl_signal_t find_signal_named(const string &name, const vhdl_scope *scope);
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool newline = true);
void require_support_function(support_function_t f);
#endif /* #ifndef INC_VHDL_TARGET_H */

184
tgt-vhdl/vhdl_type.cc Normal file
View File

@ -0,0 +1,184 @@
/*
* VHDL variable and signal types.
*
* Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_type.hh"
#include <cassert>
#include <sstream>
#include <iostream>
vhdl_type *vhdl_type::std_logic()
{
return new vhdl_type(VHDL_TYPE_STD_LOGIC);
}
vhdl_type *vhdl_type::string()
{
return new vhdl_type(VHDL_TYPE_STRING);
}
vhdl_type *vhdl_type::line()
{
return new vhdl_type(VHDL_TYPE_LINE);
}
vhdl_type *vhdl_type::boolean()
{
return new vhdl_type(VHDL_TYPE_BOOLEAN);
}
vhdl_type *vhdl_type::integer()
{
return new vhdl_type(VHDL_TYPE_INTEGER);
}
vhdl_type *vhdl_type::nunsigned(int width, int lsb)
{
return new vhdl_type(VHDL_TYPE_UNSIGNED, width-1+lsb, lsb);
}
vhdl_type *vhdl_type::nsigned(int width, int lsb)
{
return new vhdl_type(VHDL_TYPE_SIGNED, width-1+lsb, lsb);
}
vhdl_type *vhdl_type::time()
{
return new vhdl_type(VHDL_TYPE_TIME);
}
vhdl_type *vhdl_type::get_base() const
{
assert(name_ == VHDL_TYPE_ARRAY);
return base_;
}
/*
* This is just the name of the type, without any parameters.
*/
std::string vhdl_type::get_string() const
{
switch (name_) {
case VHDL_TYPE_STD_LOGIC:
return std::string("std_logic");
case VHDL_TYPE_STD_LOGIC_VECTOR:
return std::string("std_logic_vector");
case VHDL_TYPE_STRING:
return std::string("String");
case VHDL_TYPE_LINE:
return std::string("Line");
case VHDL_TYPE_FILE:
return std::string("File");
case VHDL_TYPE_INTEGER:
return std::string("Integer");
case VHDL_TYPE_BOOLEAN:
return std::string("Boolean");
case VHDL_TYPE_SIGNED:
return std::string("signed");
case VHDL_TYPE_UNSIGNED:
return std::string("unsigned");
case VHDL_TYPE_ARRAY:
// Each array has its own type declaration
return array_name_;
default:
return std::string("BadType");
}
}
/*
* The is the qualified name of the type.
*/
std::string vhdl_type::get_decl_string() const
{
switch (name_) {
case VHDL_TYPE_STD_LOGIC_VECTOR:
case VHDL_TYPE_UNSIGNED:
case VHDL_TYPE_SIGNED:
{
std::ostringstream ss;
ss << get_string() << "(" << msb_;
ss << " downto " << lsb_ << ")";
return ss.str();
}
default:
return get_string();
}
}
/*
* Like get_decl_string but completely expands array declarations.
*/
std::string vhdl_type::get_type_decl_string() const
{
switch (name_) {
case VHDL_TYPE_ARRAY:
{
std::ostringstream ss;
ss << "array (" << msb_ << " downto "
<< lsb_ << ") of "
<< base_->get_decl_string();
return ss.str();
}
default:
return get_decl_string();
}
}
void vhdl_type::emit(std::ostream &of, int level) const
{
of << get_decl_string();
}
vhdl_type::vhdl_type(const vhdl_type &other)
: name_(other.name_), msb_(other.msb_), lsb_(other.lsb_),
array_name_(other.array_name_)
{
if (other.base_ != NULL)
base_ = new vhdl_type(*other.base_);
else
base_ = NULL;
}
vhdl_type::~vhdl_type()
{
if (base_ != NULL)
delete base_;
}
vhdl_type *vhdl_type::std_logic_vector(int msb, int lsb)
{
return new vhdl_type(VHDL_TYPE_STD_LOGIC_VECTOR, msb, lsb);
}
vhdl_type *vhdl_type::type_for(int width, bool issigned, int lsb)
{
if (width == 1)
return vhdl_type::std_logic();
else if (issigned)
return vhdl_type::nsigned(width, lsb);
else
return vhdl_type::nunsigned(width, lsb);
}
vhdl_type *vhdl_type::array_of(vhdl_type *b, std::string &n, int m, int l)
{
return new vhdl_type(b, n, m, l);
}

92
tgt-vhdl/vhdl_type.hh Normal file
View File

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

6
tgt-vhdl/vhpi/finish.c Normal file
View File

@ -0,0 +1,6 @@
#include <stdlib.h>
void finish(void)
{
exit(0);
}

View File

@ -29,6 +29,13 @@
#define snprintf _snprintf
#endif
struct args_info {
char*text;
int vec_flag; /* True if the vec must be released. */
struct vector_info vec;
struct args_info *child; /* Arguments can be nested. */
};
static const char* magic_sfuncs[] = {
"$time",
"$stime",
@ -68,6 +75,153 @@ static int is_fixed_memory_word(ivl_expr_t net)
return 0;
}
static int get_vpi_taskfunc_signal_arg(struct args_info *result,
ivl_expr_t expr)
{
char buffer[4096];
switch (ivl_expr_type(expr)) {
case IVL_EX_SIGNAL:
/* If the signal node is narrower then the signal itself,
then this is a part select so I'm going to need to
evaluate the expression.
Also, if the signedness of the expression is different
from the signedness of the signal. This could be
caused by a $signed or $unsigned system function.
If I don't need to do any evaluating, then skip it as
I'll be passing the handle to the signal itself. */
if (ivl_expr_width(expr) !=
ivl_signal_width(ivl_expr_signal(expr))) {
/* This should never happen since we have IVL_EX_SELECT. */
return 0;
} else if (ivl_expr_signed(expr) !=
ivl_signal_signed(ivl_expr_signal(expr))) {
return 0;
} else if (is_fixed_memory_word(expr)) {
/* This is a word of a non-array, or a word of a net
array, so we can address the word directly. */
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned use_word = 0;
ivl_expr_t word_ex = ivl_expr_oper1(expr);
if (word_ex) {
/* Some array select have been evaluated. */
if (number_is_immediate(word_ex,
8*sizeof(unsigned), 0)) {
use_word = get_number_immediate(word_ex);
word_ex = 0;
}
}
if (word_ex) return 0;
assert(word_ex == 0);
snprintf(buffer, sizeof buffer, "v%p_%u", sig, use_word);
result->text = strdup(buffer);
return 1;
} else {
/* What's left, this is the work of a var array.
Create the right code to handle it. */
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned use_word = 0;
ivl_expr_t word_ex = ivl_expr_oper1(expr);
if (word_ex) {
/* Some array select have been evaluated. */
if (number_is_immediate(word_ex,
8*sizeof(unsigned), 0)) {
use_word = get_number_immediate(word_ex);
word_ex = 0;
}
}
if (word_ex && (ivl_expr_type(word_ex)==IVL_EX_SIGNAL ||
ivl_expr_type(word_ex)==IVL_EX_SELECT)) {
/* Special case: the index is a signal/select. */
result->child = calloc(1, sizeof(struct args_info));
if (get_vpi_taskfunc_signal_arg(result->child,
word_ex)) {
snprintf(buffer, sizeof buffer, "&A<v%p, %s >",
sig, result->child->text);
free(result->child->text);
} else {
free(result->child);
result->child = NULL;
return 0;
}
} else if (word_ex) {
/* Fallback case: evaluate expression. */
struct vector_info av;
av = draw_eval_expr(word_ex, STUFF_OK_XZ);
snprintf(buffer, sizeof buffer, "&A<v%p, %u %u>",
sig, av.base, av.wid);
result->vec = av;
result->vec_flag = 1;
} else {
snprintf(buffer, sizeof buffer, "&A<v%p, %u>",
sig, use_word);
}
result->text = strdup(buffer);
return 1;
}
case IVL_EX_SELECT: {
ivl_expr_t vexpr = ivl_expr_oper1(expr);
assert(vexpr);
/* This code is only for signals or selects. */
if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL &&
ivl_expr_type(vexpr) != IVL_EX_SELECT) return 0;
/* The signal is part of an array. */
/* Add &APV<> code here when it is finished. */
if (ivl_expr_oper1(vexpr)) return 0;
ivl_expr_t bexpr = ivl_expr_oper2(expr);
assert(bexpr);
/* This is a constant bit/part select. */
if (number_is_immediate(bexpr, 64, 1)) {
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %ld, %u>",
ivl_expr_signal(vexpr),
get_number_immediate(bexpr),
ivl_expr_width(expr));
/* This is an indexed bit/part select. */
} else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL ||
ivl_expr_type(bexpr) == IVL_EX_SELECT) {
/* Special case: the base is a signal/select. */
result->child = calloc(1, sizeof(struct args_info));
if (get_vpi_taskfunc_signal_arg(result->child, bexpr)) {
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %s, %u>",
ivl_expr_signal(vexpr),
result->child->text,
ivl_expr_width(expr));
free(result->child->text);
} else {
free(result->child);
result->child = NULL;
return 0;
}
} else {
/* Fallback case: evaluate the expression. */
struct vector_info rv;
rv = draw_eval_expr(bexpr, STUFF_OK_XZ);
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %u %u, %u>",
ivl_expr_signal(vexpr),
rv.base, rv.wid,
ivl_expr_width(expr));
result->vec = rv;
result->vec_flag = 1;
}
result->text = strdup(buffer);
return 1;
}
default:
return 0;
}
}
static void draw_vpi_taskfunc_args(const char*call_string,
ivl_statement_t tnet,
ivl_expr_t fnet)
@ -77,11 +231,7 @@ static void draw_vpi_taskfunc_args(const char*call_string,
? ivl_stmt_parm_count(tnet)
: ivl_expr_parms(fnet);
struct args_info {
char*text;
int vec_flag; /* True if the vec must be released. */
struct vector_info vec;
} *args = calloc(parm_count, sizeof(struct args_info));
struct args_info *args = calloc(parm_count, sizeof(struct args_info));
char buffer[4096];
@ -154,121 +304,9 @@ static void draw_vpi_taskfunc_args(const char*call_string,
break;
case IVL_EX_SIGNAL:
/* If the signal node is narrower then the signal
itself, then this is a part select so I'm going
to need to evaluate the expression.
Also, if the signedness of the expression is
different from the signedness of the
signal. This could be caused by a $signed or
$unsigned system function.
If I don't need to do any evaluating, then skip
it as I'll be passing the handle to the signal
itself. */
if (ivl_expr_width(expr) !=
ivl_signal_width(ivl_expr_signal(expr))) {
break;
} else if (ivl_expr_signed(expr) !=
ivl_signal_signed(ivl_expr_signal(expr))) {
break;
} else if (is_fixed_memory_word(expr)) {
/* This is a word of a non-array, or a word
of a net array, so we can address the
word directly. */
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned use_word = 0;
ivl_expr_t word_ex = ivl_expr_oper1(expr);
if (word_ex) {
/* Some array select have been evaluated. */
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
use_word = get_number_immediate(word_ex);
word_ex = 0;
}
}
if (word_ex)
break;
assert(word_ex == 0);
snprintf(buffer, sizeof buffer, "v%p_%u", sig, use_word);
args[idx].text = strdup(buffer);
continue;
} else {
/* What's left, this is the work of a var
array. Create the right code to handle
it. */
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned use_word = 0;
ivl_expr_t word_ex = ivl_expr_oper1(expr);
if (word_ex) {
/* Some array select have been evaluated. */
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
use_word = get_number_immediate(word_ex);
word_ex = 0;
}
}
if (word_ex && ivl_expr_type(word_ex)==IVL_EX_SIGNAL) {
/* Special case: the index is a signal. */
snprintf(buffer, sizeof buffer,
"&A<v%p, v%p_0 >", sig,
ivl_expr_signal(word_ex));
} else if (word_ex) {
/* Fallback case: evaluate expression. */
struct vector_info av;
av = draw_eval_expr(word_ex, STUFF_OK_XZ);
snprintf(buffer, sizeof buffer,
"&A<v%p, %u %u>", sig, av.base, av.wid);
args[idx].vec = av;
args[idx].vec_flag = 1;
} else {
snprintf(buffer, sizeof buffer,
"&A<v%p, %u>", sig, use_word);
}
args[idx].text = strdup(buffer);
continue;
}
case IVL_EX_SELECT: {
ivl_expr_t vexpr = ivl_expr_oper1(expr);
assert(vexpr);
/* This code is only for signals. */
if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL) break;
/* The signal is part of an array. */
/* Add &APV<> code here when it is finished. */
if (ivl_expr_oper1(vexpr)) break;
ivl_expr_t bexpr = ivl_expr_oper2(expr);
assert(bexpr);
/* This is a constant bit/part select. */
if (number_is_immediate(bexpr, 64, 1)) {
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %ld, %u>",
ivl_expr_signal(vexpr),
get_number_immediate(bexpr),
ivl_expr_width(expr));
/* This is an indexed bit/part select. */
} else if (ivl_expr_type(bexpr) == IVL_EX_SIGNAL) {
/* Sepcial case: the base is a signal. */
snprintf(buffer, sizeof buffer, "&PV<v%p_0, v%p_0, %u>",
ivl_expr_signal(vexpr),
ivl_expr_signal(bexpr),
ivl_expr_width(expr));
} else {
/* Fallback case: evaluate the expression. */
struct vector_info rv;
rv = draw_eval_expr(bexpr, STUFF_OK_XZ);
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %u %u, %u>",
ivl_expr_signal(vexpr),
rv.base, rv.wid,
ivl_expr_width(expr));
}
args[idx].text = strdup(buffer);
continue;
}
case IVL_EX_SELECT:
if (get_vpi_taskfunc_signal_arg(&args[idx], expr)) continue;
else break;
/* Everything else will need to be evaluated and
passed as a constant to the vpi task. */
@ -301,14 +339,22 @@ static void draw_vpi_taskfunc_args(const char*call_string,
fprintf(vvp_out, "%s", call_string);
for (idx = 0 ; idx < parm_count ; idx += 1) {
fprintf(vvp_out, ", %s", args[idx].text);
free(args[idx].text);
if (args[idx].vec_flag) {
if (args[idx].vec.wid > 0)
clr_vector(args[idx].vec);
else
clr_word(args[idx].vec.base);
struct args_info*ptr;
/* Clear the nested children vectors. */
for (ptr = &args[idx]; ptr != NULL; ptr = ptr->child) {
if (ptr->vec_flag) {
if (ptr->vec.wid > 0) clr_vector(ptr->vec);
else clr_word(ptr->vec.base);
}
}
/* Free the nested children. */
ptr = args[idx].child;
while (ptr != NULL) {
struct args_info*tptr = ptr;
ptr = ptr->child;
free(tptr);
}
}

View File

@ -1670,22 +1670,31 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid)
vvp_errors += 1;
}
/* Detect the special case that the entire number fits in an
immediate. In this case we generate a single %movi
instruction. */
if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID,0)) {
unsigned long val = get_number_immediate(exp);
fprintf(vvp_out, " %%movi %u, %lu, %u;\n", res.base, val, wid);
return res;
}
/* Use %movi as much as possible to build the value into the
destination. Use the %mov to handle the remaining general
bits. */
idx = 0;
unsigned long val = 0;
unsigned val_bits = 0;
unsigned val_addr = res.base;
while (idx < nwid) {
unsigned cnt;
char src = '?';
char src = 0;
switch (bits[idx]) {
case '0':
src = '0';
val_bits += 1;
break;
case '1':
src = '1';
val |= 1UL << val_bits;
val_bits += 1;
break;
case 'x':
src = '2';
@ -1695,14 +1704,29 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid)
break;
}
for (cnt = 1 ; idx+cnt < nwid ; cnt += 1)
if (bits[idx+cnt] != bits[idx])
break;
if (val_bits >= IMM_WID
|| (val_bits>0 && src != 0)
|| (val_bits>0 && idx+1==nwid)) {
fprintf(vvp_out, " %%movi %u, %lu, %u;\n",
val_addr, val, val_bits);
val_addr += val_bits;
val_bits = 0;
val = 0;
}
fprintf(vvp_out, " %%mov %u, %c, %u;\n",
res.base+idx, src, cnt);
if (src != 0) {
assert(val_bits == 0);
unsigned cnt;
for (cnt = 1 ; idx+cnt < nwid ; cnt += 1)
if (bits[idx+cnt] != bits[idx])
break;
idx += cnt;
fprintf(vvp_out, " %%mov %u, %c, %u;\n", val_addr, src, cnt);
val_addr += cnt;
idx += cnt-1;
}
idx += 1;
}
/* Pad the number up to the expression width. */

View File

@ -895,6 +895,10 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
static void draw_event_in_scope(ivl_event_t obj)
{
char tmp[4][32];
const unsigned ntmp = sizeof(tmp) / sizeof(tmp[0]);
unsigned nany = ivl_event_nany(obj);
unsigned nneg = ivl_event_nneg(obj);
unsigned npos = ivl_event_npos(obj);
@ -903,13 +907,13 @@ static void draw_event_in_scope(ivl_event_t obj)
/* Figure out how many probe functors are needed. */
if (nany > 0)
cnt += (nany+3) / 4;
cnt += (nany+ntmp-1) / ntmp;
if (nneg > 0)
cnt += (nneg+3) / 4;
cnt += (nneg+ntmp-1) / ntmp;
if (npos > 0)
cnt += (npos+3) / 4;
cnt += (npos+ntmp-1) / ntmp;
if (cnt == 0) {
/* If none are needed, then this is a named event. The
@ -923,48 +927,57 @@ static void draw_event_in_scope(ivl_event_t obj)
unsigned idx;
unsigned ecnt = 0;
for (idx = 0 ; idx < nany ; idx += 4, ecnt += 1) {
for (idx = 0 ; idx < nany ; idx += ntmp, ecnt += 1) {
unsigned sub, top;
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
top = idx + 4;
top = idx + ntmp;
if (nany < top)
top = nany;
for (sub = idx ; sub < top ; sub += 1) {
ivl_nexus_t nex = ivl_event_any(obj, sub);
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
}
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
for (sub = idx ; sub < top ; sub += 1)
fprintf(vvp_out, ", %s", tmp[sub-idx]);
fprintf(vvp_out, ";\n");
}
for (idx = 0 ; idx < nneg ; idx += 4, ecnt += 1) {
for (idx = 0 ; idx < nneg ; idx += ntmp, ecnt += 1) {
unsigned sub, top;
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
top = idx + 4;
top = idx + ntmp;
if (nneg < top)
top = nneg;
for (sub = idx ; sub < top ; sub += 1) {
ivl_nexus_t nex = ivl_event_neg(obj, sub);
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
}
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
for (sub = idx ; sub < top ; sub += 1)
fprintf(vvp_out, ", %s", tmp[sub-idx]);
fprintf(vvp_out, ";\n");
}
for (idx = 0 ; idx < npos ; idx += 4, ecnt += 1) {
for (idx = 0 ; idx < npos ; idx += ntmp, ecnt += 1) {
unsigned sub, top;
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
top = idx + 4;
top = idx + ntmp;
if (npos < top)
top = npos;
for (sub = idx ; sub < top ; sub += 1) {
ivl_nexus_t nex = ivl_event_pos(obj, sub);
fprintf(vvp_out, ", %s", draw_input_from_net(nex));
strncpy(tmp[sub-idx], draw_input_from_net(nex), sizeof(tmp[0]));
}
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
for (sub = idx ; sub < top ; sub += 1)
fprintf(vvp_out, ", %s", tmp[sub-idx]);
fprintf(vvp_out, ";\n");
}
@ -981,20 +994,17 @@ static void draw_event_in_scope(ivl_event_t obj)
} else {
unsigned num_input_strings = nany + nneg + npos;
unsigned idx;
ivl_nexus_t input_nexa[4];
const char*edge = 0;
assert(num_input_strings <= 4);
assert(num_input_strings <= ntmp);
if (nany > 0) {
assert((nneg + npos) == 0);
assert(nany <= 4);
edge = "edge";
for (idx = 0 ; idx < nany ; idx += 1) {
ivl_nexus_t nex = ivl_event_any(obj, idx);
input_nexa[idx] = nex;
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
}
} else if (nneg > 0) {
@ -1003,7 +1013,7 @@ static void draw_event_in_scope(ivl_event_t obj)
for (idx = 0 ; idx < nneg ; idx += 1) {
ivl_nexus_t nex = ivl_event_neg(obj, idx);
input_nexa[idx] = nex;
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
}
} else {
@ -1012,14 +1022,14 @@ static void draw_event_in_scope(ivl_event_t obj)
for (idx = 0 ; idx < npos ; idx += 1) {
ivl_nexus_t nex = ivl_event_pos(obj, idx);
input_nexa[idx] = nex;
strncpy(tmp[idx], draw_input_from_net(nex), sizeof(tmp[0]));
}
}
fprintf(vvp_out, "E_%p .event %s", obj, edge);
for (idx = 0 ; idx < num_input_strings ; idx += 1)
fprintf(vvp_out, ", %s", draw_input_from_net(input_nexa[idx]));
for (idx = 0 ; idx < num_input_strings ; idx += 1) {
fprintf(vvp_out, ", %s", tmp[idx]);
}
fprintf(vvp_out, ";\n");
}
}
@ -1759,6 +1769,13 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent)
{
unsigned idx;
const char *type;
/* For now we do not support automatic tasks or functions. */
if (ivl_scope_is_auto(net)) {
fprintf(stderr, "%s:%u: vvp-tgt sorry: automatic tasks/functions "
"are not supported!\n",
ivl_scope_def_file(net), ivl_scope_def_lineno(net));
exit(1);
}
switch (ivl_scope_type(net)) {
case IVL_SCT_MODULE: type = "module"; break;
case IVL_SCT_FUNCTION: type = "function"; break;

View File

@ -152,6 +152,112 @@ verinum::verinum(uint64_t val, unsigned n)
}
}
/* The second argument is not used! It is there to make this
* constructor unique. */
verinum::verinum(double val, bool dummy)
: has_len_(false), has_sign_(true), string_flag_(false)
{
bool is_neg = false;
double fraction;
int exponent;
const unsigned BITS_IN_LONG = 8*sizeof(long);
/* We return `bx for a NaN or +/- infinity. */
if (val != val || (val && (val == 0.5*val))) {
nbits_ = 1;
bits_ = new V[nbits_];
bits_[0] = Vx;
return;
}
/* Convert to a positive result. */
if (val < 0.0) {
is_neg = true;
val = -val;
}
/* Get the exponent and fractional part of the number. */
fraction = frexp(val, &exponent);
nbits_ = exponent+1;
bits_ = new V[nbits_];
const verinum const_one(1);
/* If the value is small enough just use lround(). */
if (nbits_ <= BITS_IN_LONG) {
long sval = lround(val);
if (is_neg) sval = -sval;
for (unsigned idx = 0; idx < nbits_; idx += 1) {
bits_[idx] = (sval&1) ? V1 : V0;
sval >>= 1;
}
/* Trim the result. */
signed_trim();
return;
}
unsigned nwords = (exponent-1)/BITS_IN_LONG;
fraction = ldexp(fraction, (exponent-1) % BITS_IN_LONG + 1);
if (nwords == 0) {
unsigned long bits = (unsigned long) fraction;
fraction = fraction - (double) bits;
for (unsigned idx = 0; idx < nbits_; idx += 1) {
bits_[idx] = (bits&1) ? V1 : V0;
bits >>= 1;
}
if (fraction >= 0.5) *this = *this + const_one;
} else {
for (int wd = nwords; wd >= 0; wd -= 1) {
unsigned long bits = (unsigned long) fraction;
fraction = fraction - (double) bits;
unsigned max = (wd+1)*BITS_IN_LONG;
if (max > nbits_) max = nbits_;
for (unsigned idx = wd*BITS_IN_LONG; idx < max; idx += 1) {
bits_[idx] = (bits&1) ? V1 : V0;
bits >>= 1;
}
fraction = ldexp(fraction, BITS_IN_LONG);
}
if (fraction >= ldexp(0.5, BITS_IN_LONG)) *this = *this + const_one;
}
/* Convert a negative number if needed. */
if (is_neg) {
*this = v_not(*this) + const_one;
}
/* Trim the result. */
signed_trim();
}
/* This is used by the double constructor above. It is needed to remove
* extra sign bits that can occur when calculating a negative value. */
void verinum::signed_trim()
{
/* Do we have any extra digits? */
unsigned tlen = nbits_-1;
verinum::V sign = bits_[tlen];
while ((tlen > 0) && (bits_[tlen] == sign)) tlen -= 1;
/* tlen now points to the first digit that is not the sign.
* or bit 0. Set the length to include this bit and one proper
* sign bit if needed. */
if (bits_[tlen] != sign) tlen += 1;
tlen += 1;
/* Trim the bits if needed. */
if (tlen < nbits_) {
V* tbits = new V[tlen];
for (unsigned idx = 0; idx < tlen; idx += 1)
tbits[idx] = bits_[idx];
delete[] bits_;
bits_ = tbits;
nbits_ = tlen;
}
}
verinum::verinum(const verinum&that)
{
string_flag_ = that.string_flag_;
@ -336,14 +442,9 @@ double verinum::as_double() const
{
if (nbits_ == 0) return 0.0;
/* Do we have a signed value? */
bool signed_flag = false;
if (bits_[nbits_-1] == V1) {
signed_flag = true;
}
double val = 0.0;
if (signed_flag) {
/* Do we have/want a signed value? */
if (has_sign_ && bits_[nbits_-1] == V1) {
V carry = V1;
for (unsigned idx = 0; idx < nbits_; idx += 1) {
V sum = add_with_carry(~bits_[idx], V0, carry);
@ -351,7 +452,6 @@ double verinum::as_double() const
val += pow(2.0, (double)idx);
}
val *= -1.0;
// val = (double) as_long();
} else {
for (unsigned idx = 0; idx < nbits_; idx += 1) {
if (bits_[idx] == V1)
@ -591,11 +691,15 @@ ostream& operator<< (ostream&o, const verinum&v)
verinum::V trim_left = v.get(v.len()-1);
unsigned idx;
for (idx = v.len()-1; idx > 0; idx -= 1)
if (trim_left != v.get(idx-1))
break;
if (v.has_sign()) {
for (idx = v.len()-1; idx > 0; idx -= 1)
if (trim_left != v.get(idx-1))
break;
o << trim_left;
o << trim_left;
} else {
idx = v.len();
}
while (idx > 0) {
o << v.get(idx-1);

View File

@ -46,6 +46,7 @@ class verinum {
verinum(const V*v, unsigned nbits, bool has_len =true);
verinum(V, unsigned nbits =1, bool has_len =true);
verinum(uint64_t val, unsigned bits);
verinum(double val, bool dummy);
verinum(const verinum&);
// Create a signed number, with an unspecified number of bits.
@ -93,6 +94,8 @@ class verinum {
signed long as_long() const;
double as_double() const;
string as_string() const;
private:
void signed_trim();
private:
V* bits_;

View File

@ -60,7 +60,7 @@ sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \
sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \
sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \
mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \
vams_simparam.o
sys_clog2.o vams_simparam.o
ifeq (@HAVE_LIBZ@,yes)
ifeq (@HAVE_LIBBZ2@,yes)

109
vpi/sys_clog2.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2008 Cary R. (cygcary@yahoo.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <assert.h>
#include <math.h>
#include <string.h>
#include <vpi_user.h>
#include "sys_priv.h"
/*
* Check that the function is called with the correct argument.
*/
static PLI_INT32 sys_clog2_compiletf(PLI_BYTE8 *name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
assert(callh != 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
(void) name; // Not used!
/* We must have an argument. */
if (argv == 0) {
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
vpi_printf("$clog2 requires one numeric argument.\n");
vpi_control(vpiFinish, 1);
return 0;
}
/* The argument must be numeric. */
arg = vpi_scan(argv);
if (! is_numeric_obj(arg)) {
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
vpi_printf("The first argument to $clog2 must be numeric.\n");
vpi_control(vpiFinish, 1);
}
/* We can have a maximum of one argument. */
if (vpi_scan(argv) != 0) {
char msg [64];
snprintf(msg, 64, "ERROR: %s line %d:",
vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
unsigned argc = 1;
while (vpi_scan(argv)) argc += 1;
vpi_printf("%s $clog2 takes at most one argument.\n", msg);
vpi_printf("%*s Found %u extra argument%s.\n",
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
vpi_control(vpiFinish, 1);
}
return 0;
}
static PLI_INT32 sys_clog2_calltf(PLI_BYTE8 *name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
s_vpi_value val;
s_vpi_vecval vec;
(void) name; // Not used!/
/* Get the argument. */
arg = vpi_scan(argv);
vpi_free_object(argv);
vec = vpip_calc_clog2(arg);
val.format = vpiVectorVal;
val.value.vector = &vec;
vpi_put_value(callh, &val, 0, vpiNoDelay);
return 0;
}
/*
* Register the function with Verilog.
*/
void sys_clog2_register(void)
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiIntFunc;
tf_data.calltf = sys_clog2_calltf;
tf_data.compiletf = sys_clog2_compiletf;
tf_data.sizetf = 0;
tf_data.tfname = "$clog2";
tf_data.user_data = 0;
vpi_register_systf(&tf_data);
}

View File

@ -42,18 +42,159 @@ static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* name)
return 0;
}
static PLI_INT32 task_not_implemented_compiletf(PLI_BYTE8* name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpi_printf("%s:%d: SORRY: task %s() is not currently implemented.\n",
vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh),
name);
vpi_control(vpiFinish, 1);
return 0;
}
static PLI_INT32 function_not_implemented_compiletf(PLI_BYTE8* name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpi_printf("%s:%d: SORRY: function %s() is not currently implemented.\n",
vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh),
name);
vpi_control(vpiFinish, 1);
return 0;
}
/*
* Register the function with Verilog.
*/
void sys_special_register(void)
{
s_vpi_systf_data tf_data;
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.calltf = finish_and_return_calltf;
tf_data.compiletf = sys_one_numeric_arg_compiletf;
tf_data.sizetf = 0;
tf_data.tfname = "$finish_and_return";
tf_data.user_data = "$finish_and_return";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.calltf = finish_and_return_calltf;
tf_data.compiletf = sys_one_numeric_arg_compiletf;
tf_data.sizetf = 0;
tf_data.tfname = "$finish_and_return";
tf_data.user_data = "$finish_and_return";
vpi_register_systf(&tf_data);
/* These tasks are not currently implemented. */
tf_data.type = vpiSysTask;
tf_data.calltf = 0;
tf_data.sizetf = 0;
tf_data.compiletf = task_not_implemented_compiletf;
tf_data.tfname = "$fmonitor";
tf_data.user_data = "$fmonitor";
vpi_register_systf(&tf_data);
tf_data.tfname = "$fmonitorb";
tf_data.user_data = "$fmonitorb";
vpi_register_systf(&tf_data);
tf_data.tfname = "$fmonitoro";
tf_data.user_data = "$fmonitoro";
vpi_register_systf(&tf_data);
tf_data.tfname = "$fmonitorh";
tf_data.user_data = "$fmonitorh";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$and$array";
tf_data.user_data = "$async$and$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$nand$array";
tf_data.user_data = "$async$nand$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$or$array";
tf_data.user_data = "$async$or$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$nor$array";
tf_data.user_data = "$async$nor$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$and$plane";
tf_data.user_data = "$async$and$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$nand$plane";
tf_data.user_data = "$async$nand$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$or$plane";
tf_data.user_data = "$async$or$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$async$nor$plane";
tf_data.user_data = "$async$nor$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$and$array";
tf_data.user_data = "$sync$and$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$nand$array";
tf_data.user_data = "$sync$nand$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$or$array";
tf_data.user_data = "$sync$or$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$nor$array";
tf_data.user_data = "$sync$nor$array";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$and$plane";
tf_data.user_data = "$sync$and$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$nand$plane";
tf_data.user_data = "$sync$nand$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$or$plane";
tf_data.user_data = "$sync$or$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$sync$nor$plane";
tf_data.user_data = "$sync$nor$plane";
vpi_register_systf(&tf_data);
tf_data.tfname = "$q_initialize";
tf_data.user_data = "$q_initialize";
vpi_register_systf(&tf_data);
tf_data.tfname = "$q_add";
tf_data.user_data = "$q_add";
vpi_register_systf(&tf_data);
tf_data.tfname = "$q_remove";
tf_data.user_data = "$q_remove";
vpi_register_systf(&tf_data);
tf_data.tfname = "$q_full";
tf_data.user_data = "$q_full";
vpi_register_systf(&tf_data);
tf_data.tfname = "$q_exam";
tf_data.user_data = "$q_exam";
vpi_register_systf(&tf_data);
/* These functions are not currently implemented. */
tf_data.compiletf = function_not_implemented_compiletf;
tf_data.type = vpiSysFunc;
tf_data.sysfunctype = vpiIntFunc;
tf_data.tfname = "$ferror";
tf_data.user_data = "$ferror";
vpi_register_systf(&tf_data);
tf_data.tfname = "$fread";
tf_data.user_data = "$fread";
vpi_register_systf(&tf_data);
}

View File

@ -281,7 +281,6 @@ static int scan_format(vpiHandle sys, struct byte_source*src, vpiHandle argv)
int suppress_flag = 0;
int length_field = -1;
int code = 0;
int sign_flag = 1;
PLI_INT32 value;
char*tmp;
@ -373,30 +372,38 @@ static int scan_format(vpiHandle sys, struct byte_source*src, vpiHandle argv)
break;
case 'd':
match_fail = 1;
/* Decimal integer */
ch = byte_getc(src);
if (ch == '-') {
sign_flag = -1;
ch = byte_getc(src);
}
/* decimal integer */
tmp = malloc(2);
value = 0;
while ( isdigit(ch) ) {
tmp[0] = 0;
match_fail = 1;
ch = byte_getc(src);
while (isdigit(ch) || ch == '_' || (value == 0 && ch == '-')) {
match_fail = 0;
value *= 10;
value += ch - '0';
ch = byte_getc(src);
if (ch != '_') {
tmp[value++] = ch;
tmp = realloc(tmp, value+1);
tmp[value] = 0;
ch = byte_getc(src);
}
}
byte_ungetc(src, ch);
if (match_fail) {
free(tmp);
break;
}
if (match_fail)
break;
/* Matched a decimal value, put it to an argument. */
item = vpi_scan(argv);
assert(item);
val.format = vpiIntVal;
val.value.integer = value * sign_flag;
val.format = vpiDecStrVal;
val.value.str = tmp;
vpi_put_value(item, &val, 0, vpiNoDelay);
free(tmp);
rc += 1;
break;

View File

@ -38,6 +38,7 @@ extern void sys_time_register();
extern void sys_vcd_register();
extern void sys_vcdoff_register();
extern void sys_special_register();
extern void sys_clog2_register();
extern void vams_simparam_register();
#ifdef HAVE_LIBZ
@ -181,6 +182,7 @@ void (*vlog_startup_routines[])() = {
sys_lxt_or_vcd_register,
sys_sdf_register,
sys_special_register,
sys_clog2_register,
vams_simparam_register,
0
};

View File

@ -14,6 +14,7 @@ $dist_poisson vpiSysFuncInt
$dist_chi_square vpiSysFuncInt
$dist_t vpiSysFuncInt
$dist_erlang vpiSysFuncInt
$clog2 vpiSysFuncInt
$simparam vpiSysFuncReal
$simparam$str vpiSysFuncSized 1024 unsigned

View File

@ -566,6 +566,7 @@ extern DLLEXPORT void (*vlog_startup_routines[])();
buffer. The value must be a vpiStrengthVal. */
extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit);
extern void vpip_set_return_value(int value);
extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg);
EXTERN_C_END

View File

@ -75,6 +75,7 @@ struct __vpiArray {
class vvp_fun_arrayport*ports_;
struct __vpiCallback *vpi_callbacks;
bool signed_flag;
};
struct __vpiArrayIterator {
@ -452,7 +453,8 @@ static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value)
unsigned index = decode_array_word_pointer(obj, parent);
unsigned width = parent->vals->width();
vpip_vec4_get_value(parent->vals->get_word(index), width, false, value);
vpip_vec4_get_value(parent->vals->get_word(index), width,
parent->signed_flag, value);
}
static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int flags)
@ -635,7 +637,7 @@ static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value)
unsigned index = obj->get_address();
vvp_vector4_t tmp = array_get_word(parent, index);
vpip_vec4_get_value(tmp, parent->vals_width, false, value);
vpip_vec4_get_value(tmp, parent->vals_width, parent->signed_flag, value);
}
static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int)
@ -756,11 +758,14 @@ vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address)
}
static vpiHandle vpip_make_array(char*label, const char*name,
int first_addr, int last_addr)
int first_addr, int last_addr,
bool signed_flag)
{
struct __vpiArray*obj = (struct __vpiArray*)
malloc(sizeof(struct __vpiArray));
obj->signed_flag = signed_flag;
// Assume increasing addresses.
assert(last_addr >= first_addr);
unsigned array_count = last_addr+1-first_addr;
@ -833,7 +838,8 @@ void array_attach_word(vvp_array_t array, unsigned long addr, vpiHandle word)
void compile_var_array(char*label, char*name, int last, int first,
int msb, int lsb, char signed_flag)
{
vpiHandle obj = vpip_make_array(label, name, first, last);
vpiHandle obj = vpip_make_array(label, name, first, last,
signed_flag != 0);
struct __vpiArray*arr = ARRAY_HANDLE(obj);
@ -853,7 +859,7 @@ void compile_var_array(char*label, char*name, int last, int first,
void compile_real_array(char*label, char*name, int last, int first,
int msb, int lsb)
{
vpiHandle obj = vpip_make_array(label, name, first, last);
vpiHandle obj = vpip_make_array(label, name, first, last, true);
struct __vpiArray*arr = ARRAY_HANDLE(obj);
vvp_array_t array = array_find(label);
@ -874,7 +880,7 @@ void compile_real_array(char*label, char*name, int last, int first,
void compile_net_array(char*label, char*name, int last, int first)
{
vpiHandle obj = vpip_make_array(label, name, first, last);
vpiHandle obj = vpip_make_array(label, name, first, last, false);
struct __vpiArray*arr = ARRAY_HANDLE(obj);
arr->nets = (vpiHandle*)calloc(arr->array_count, sizeof(vpiHandle));
@ -981,7 +987,8 @@ void array_word_change(vvp_array_t array, unsigned long addr)
if (cur->cb_data.value)
vpip_vec4_get_value(array->vals->get_word(addr),
array->vals_width,
false, cur->cb_data.value);
array->signed_flag,
cur->cb_data.value);
callback_execute(cur);
prev = cur;
@ -1170,6 +1177,23 @@ vpiHandle vpip_make_vthr_A(char*label, char*symbol)
return &(obj->base);
}
vpiHandle vpip_make_vthr_A(char*label, vpiHandle handle)
{
struct __vpiArrayVthrA*obj = (struct __vpiArrayVthrA*)
malloc(sizeof (struct __vpiArrayVthrA));
obj->base.vpi_type = &vpip_array_vthr_A_rt;
obj->array = array_find(label);
assert(obj->array);
free(label);
obj->address_handle = handle;
obj->address = 0;
obj->wid = 0;
return &(obj->base);
}
void compile_array_cleanup(void)
{

View File

@ -109,35 +109,6 @@ void vvp_fun_and::run_run()
vvp_send_vec4(ptr->out, result);
}
vvp_fun_eeq::vvp_fun_eeq(unsigned wid, bool invert)
: vvp_fun_boolean_(wid), invert_(invert)
{
count_functors_logic += 1;
}
vvp_fun_eeq::~vvp_fun_eeq()
{
}
void vvp_fun_eeq::run_run()
{
vvp_net_t*ptr = net_;
net_ = 0;
vvp_vector4_t result (input_[0]);
for (unsigned idx = 0 ; idx < result.size() ; idx += 1) {
vvp_bit4_t bitbit = result.value(idx);
bitbit = (bitbit == input_[1].value(idx))? BIT4_1 : BIT4_0;
if (invert_)
bitbit = ~bitbit;
result.set_bit(idx, bitbit);
}
vvp_send_vec4(ptr->out, result);
}
vvp_fun_buf::vvp_fun_buf()
{
net_ = 0;
@ -579,9 +550,6 @@ void compile_functor(char*label, char*type, unsigned width,
} else if (strcmp(type, "RPMOS") == 0) {
obj = new vvp_fun_rpmos(false);
} else if (strcmp(type, "EEQ") == 0) {
obj = new vvp_fun_eeq(width, false);
} else if (strcmp(type, "NOT") == 0) {
obj = new vvp_fun_not();

View File

@ -52,17 +52,6 @@ class vvp_fun_and : public vvp_fun_boolean_ {
bool invert_;
};
class vvp_fun_eeq : public vvp_fun_boolean_ {
public:
explicit vvp_fun_eeq(unsigned wid, bool invert);
~vvp_fun_eeq();
private:
void run_run();
bool invert_;
};
/*
* The buffer functor is a very primitive functor that takes the input
* from port-0 (and only port-0) and retransmits it as a vvp_vector4_t.

View File

@ -414,8 +414,9 @@ ended, then the %join does not block or yield the thread.
* %load/av <bit>, <array-label>, <wid>
This instruction loads a word from the specified array. The word
address is in index register 3. The width should match the width of
the array word.
address is in index register 3. Like %load/v below the width does
not have to match the width of the array word. See the %load/v
description for more information.
* %load/avp0 <bit>, <array-label>, <wid>
* %load/avp0/s <bit>, <array-label>, <wid>

View File

@ -105,7 +105,7 @@ static struct __vpiModPath*modpath_dst = 0;
%type <table> udp_table
%type <argv> argument_opt argument_list
%type <vpi> argument
%type <vpi> argument symbol_access
%type <cdelay> delay delay_opt
%%
@ -831,21 +831,29 @@ argument
{ $$ = vpip_make_binary_const($1.idx, $1.text);
free($1.text);
}
| K_A '<' T_SYMBOL ',' T_NUMBER '>'
| symbol_access
{ $$ = $1; }
;
symbol_access
: K_A '<' T_SYMBOL ',' T_NUMBER '>'
{ $$ = vpip_make_vthr_A($3, $5); }
| K_A '<' T_SYMBOL ',' T_NUMBER T_NUMBER '>'
{ $$ = vpip_make_vthr_A($3, $5, $6); }
| K_A '<' T_SYMBOL ',' T_SYMBOL '>'
{ $$ = vpip_make_vthr_A($3, $5); }
| K_A '<' T_SYMBOL ',' symbol_access '>'
{ $$ = vpip_make_vthr_A($3, $5); }
| K_PV '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER '>'
{ $$ = vpip_make_PV($3, $5, $7); }
| K_PV '<' T_SYMBOL ',' '-' T_NUMBER ',' T_NUMBER '>'
{ $$ = vpip_make_PV($3, -$6, $8); }
| K_PV '<' T_SYMBOL ',' T_SYMBOL ',' T_NUMBER '>'
{ $$ = vpip_make_PV($3, $5, $7); }
| K_PV '<' T_SYMBOL ',' symbol_access ',' T_NUMBER '>'
{ $$ = vpip_make_PV($3, $5, $7); }
| K_PV '<' T_SYMBOL ',' T_NUMBER T_NUMBER ',' T_NUMBER '>'
{ $$ = vpip_make_PV($3, $5, $6, $8); }
;
/* functor operands can only be a list of symbols. */
symbols

View File

@ -127,6 +127,12 @@ static struct __vpiCallback* make_value_change(p_cb_data data)
obj->cb_time.type = vpiSuppressTime;
}
obj->cb_data.time = &obj->cb_time;
if (data->value) {
obj->cb_value = *(data->value);
} else {
obj->cb_value.format = vpiSuppressVal;
}
obj->cb_data.value = &obj->cb_value;
assert(data->obj);
assert(data->obj->vpi_type);
@ -457,8 +463,25 @@ void callback_execute(struct __vpiCallback*cur)
vpi_mode_flag = VPI_MODE_RWSYNC;
assert(cur->cb_data.cb_rtn);
cur->cb_data.time->type = vpiSimTime;
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
switch (cur->cb_data.time->type) {
case vpiSimTime:
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
break;
case vpiScaledRealTime: {
cur->cb_data.time->real =
vpip_time_to_scaled_real(schedule_simtime(),
(struct __vpiScope *) vpi_handle(vpiScope,
cur->cb_data.obj));
break;
}
case vpiSuppressTime:
break;
default:
fprintf(stderr, "Unsupported time format %d.\n",
cur->cb_data.time->type);
assert(0);
break;
}
(cur->cb_data.cb_rtn)(&cur->cb_data);
vpi_mode_flag = save_mode;
@ -546,10 +569,32 @@ void vvp_fun_signal::get_value(struct t_vpi_value*vp)
{
switch (vp->format) {
case vpiScalarVal:
// This works because vvp_bit4_t has the same encoding
// as a scalar value! See vpip_vec4_get_value() for a
// more robust method.
vp->value.scalar = value(0);
break;
case vpiBinStrVal:
case vpiOctStrVal:
case vpiDecStrVal:
case vpiHexStrVal:
case vpiIntVal:
case vpiVectorVal:
case vpiStringVal:
case vpiRealVal: {
unsigned wid = size();
vvp_vector4_t vec4(wid);
for (unsigned idx = 0; idx < wid; idx += 1) {
vec4.set_bit(idx, value(idx));
}
vpip_vec4_get_value(vec4, wid, false, vp);
break;
}
case vpiSuppressVal:
break;
default:
fprintf(stderr, "vpi_callback: value "
"format %d not supported (fun_signal)\n",
@ -622,6 +667,9 @@ void vvp_fun_signal_real::get_value(struct t_vpi_value*vp)
break;
}
case vpiSuppressVal:
break;
default:
fprintf(stderr, "vpi_callback: value "
"format %d not supported (fun_signal_real)\n",

View File

@ -456,6 +456,13 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width,
vp->format);
assert(0 && "format not implemented");
case vpiObjTypeVal:
vp->format = vpiVectorVal;
break;
case vpiSuppressVal:
break;
case vpiBinStrVal:
rbuf = need_result_buf(width+1, RBUF_VAL);
for (unsigned idx = 0 ; idx < width ; idx += 1) {
@ -504,6 +511,7 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width,
break;
case BIT4_X:
vp->value.scalar = vpiX;
break;
case BIT4_Z:
vp->value.scalar = vpiZ;
break;
@ -1029,3 +1037,63 @@ extern "C" void vpi_control(PLI_INT32 operation, ...)
vpi_sim_vcontrol(operation, ap);
va_end(ap);
}
/*
* This routine calculated the return value for $clog2.
* It is easier to do it here vs trying to to use the VPI interface.
*/
extern "C" s_vpi_vecval vpip_calc_clog2(vpiHandle arg)
{
s_vpi_vecval rtn;
s_vpi_value val;
vvp_vector4_t vec4;
bool is_neg = false; // At this point only a real can be negative.
/* Get the value as a vvp_vector4_t. */
val.format = vpiObjTypeVal;
vpi_get_value(arg, &val);
if (val.format == vpiRealVal) {
vpi_get_value(arg, &val);
/* All double values can be represented in 1024 bits. */
vec4 = vvp_vector4_t(1024, val.value.real);
if (val.value.real < 0) is_neg = true;
} else {
val.format = vpiVectorVal;
vpi_get_value(arg, &val);
unsigned wid = vpi_get(vpiSize, arg);
vec4 = vvp_vector4_t(wid, BIT4_0);
for (unsigned idx=0; idx < wid; idx += 1) {
PLI_INT32 aval = val.value.vector[idx/32].aval;
PLI_INT32 bval = val.value.vector[idx/32].bval;
aval >>= idx % 32;
bval >>= idx % 32;
int bitmask = (aval&1) | ((bval<<1)&2);
vvp_bit4_t bit = scalar_to_bit4(bitmask);
vec4.set_bit(idx, bit);
}
}
if (vec4.has_xz()) {
rtn.aval = rtn.bval = 0xFFFFFFFFU; /* Set to 'bx. */
return rtn;
}
vvp_vector2_t vec2(vec4);
if (is_neg) vec2.trim_neg(); /* This is a special trim! */
else vec2.trim(); /* This makes less work shifting. */
/* Calculate the clog2 result. */
PLI_INT32 res = 0;
if (!vec2.is_zero()) {
vec2 -= vvp_vector2_t(1, vec2.size());
while(!vec2.is_zero()) {
res += 1;
vec2 >>= 1;
}
}
rtn.aval = res;
rtn.bval = 0;
return rtn;
}

View File

@ -137,6 +137,7 @@ struct __vpiCallback {
// user supplied callback data
struct t_cb_data cb_data;
struct t_vpi_time cb_time;
struct t_vpi_value cb_value;
// scheduled event
struct sync_cb* cb_sync;
@ -238,6 +239,7 @@ struct __vpiPV {
};
extern vpiHandle vpip_make_PV(char*name, int base, int width);
extern vpiHandle vpip_make_PV(char*name, char*symbol, int width);
extern vpiHandle vpip_make_PV(char*name, vpiHandle handle, int width);
extern vpiHandle vpip_make_PV(char*name, int tbase, int twid, int width);
extern struct __vpiPV* vpip_PV_from_handle(vpiHandle obj);
@ -455,9 +457,10 @@ vpiHandle vpip_make_vthr_vector(unsigned base, unsigned wid, bool signed_flag);
vpiHandle vpip_make_vthr_word(unsigned base, const char*type);
vpiHandle vpip_make_vthr_A(char*symbol, unsigned index);
vpiHandle vpip_make_vthr_A(char*label, unsigned index);
vpiHandle vpip_make_vthr_A(char*label, char*symbol);
vpiHandle vpip_make_vthr_A(char*symbol, unsigned tbase, unsigned twid);
vpiHandle vpip_make_vthr_A(char*label, unsigned tbase, unsigned twid);
vpiHandle vpip_make_vthr_A(char*label, vpiHandle handle);
/*
* This function is called before any compilation to load VPI

View File

@ -728,23 +728,6 @@ static vvp_vector4_t from_stringval(const char*str, unsigned wid)
return val;
}
static vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar)
{
switch(scalar) {
case vpi0:
return BIT4_0;
case vpi1:
return BIT4_1;
case vpiX:
return BIT4_X;
case vpiZ:
return BIT4_Z;
default:
fprintf(stderr, "Unsupported scalar value %d.\n", scalar);
assert(0);
}
}
static vpiHandle signal_put_value(vpiHandle ref, s_vpi_value*vp, int flags)
{
unsigned wid;
@ -1188,6 +1171,21 @@ vpiHandle vpip_make_PV(char*var, char*symbol, int width)
return &obj->base;
}
vpiHandle vpip_make_PV(char*var, vpiHandle handle, int width)
{
struct __vpiPV*obj = (struct __vpiPV*) malloc(sizeof(struct __vpiPV));
obj->base.vpi_type = &vpip_PV_rt;
obj->parent = vvp_lookup_handle(var);
obj->sbase = handle;
obj->tbase = 0;
obj->twid = 0;
obj->width = (unsigned) width;
obj->net = 0;
functor_ref_lookup(&obj->net, var);
return &obj->base;
}
vpiHandle vpip_make_PV(char*var, int tbase, int twid, int width)
{
struct __vpiPV*obj = (struct __vpiPV*) malloc(sizeof(struct __vpiPV));

View File

@ -229,7 +229,13 @@ void vpip_dec_str_to_vec4(vvp_vector4_t&vec,
input is "1234", str gets "4321". */
unsigned slen = strlen(buf);
char*str = new char[slen + 1];
int is_negative = 0;
for (unsigned idx = 0 ; idx < slen ; idx += 1) {
if (idx == slen-1 && buf[slen-idx-1] == '-') {
is_negative = 1;
slen--;
continue;
}
if (isdigit(buf[slen-idx-1]))
str[idx] = buf[slen-idx-1];
else
@ -266,5 +272,10 @@ void vpip_dec_str_to_vec4(vvp_vector4_t&vec,
}
if (is_negative) {
vec.invert();
vec += 1;
}
delete[]str;
}

View File

@ -1626,7 +1626,10 @@ static unsigned long* divide_bits(unsigned long*ap, unsigned long*bp, unsigned w
unsigned cur_ptr = cur-1;
unsigned long cur_res;
if (ap[cur_ptr+btop] >= bp[btop]) {
cur_res = ap[cur_ptr+btop] / bp[btop];
unsigned long high = 0;
if (cur_ptr+btop+1 < words)
high = ap[cur_ptr+btop+1];
cur_res = divide2words(ap[cur_ptr+btop], bp[btop], high);
} else if (cur_ptr+btop+1 >= words) {
continue;
@ -1749,6 +1752,9 @@ bool of_DIV_S(vthread_t thr, vvp_code_t cp)
assert(adra >= 4);
// Get the values, left in right, in binary form. If there is
// a problem with either (caused by an X or Z bit) then we
// know right away that the entire result is X.
unsigned long*ap = vector_to_array(thr, adra, wid);
if (ap == 0) {
vvp_vector4_t tmp(wid, BIT4_X);
@ -1764,6 +1770,7 @@ bool of_DIV_S(vthread_t thr, vvp_code_t cp)
return true;
}
// Sign extend the bits in the array to fill out the array.
unsigned long sign_mask = 0;
if (unsigned long sign_bits = (words*CPU_WORD_BITS) - wid) {
sign_mask = -1UL << (CPU_WORD_BITS-sign_bits);
@ -1773,6 +1780,7 @@ bool of_DIV_S(vthread_t thr, vvp_code_t cp)
bp[words-1] |= sign_mask;
}
// If the value fits in a single word, then use the native divide.
if (wid <= CPU_WORD_BITS) {
if (bp[0] == 0) {
vvp_vector4_t tmp(wid, BIT4_X);
@ -2076,7 +2084,14 @@ bool of_FORK(vthread_t thr, vvp_code_t cp)
thr->fork_count += 1;
schedule_vthread(child, 0, true);
/* If the new child was created to evaluate a function,
run it immediately, then return to this thread. */
if (cp->scope->base.vpi_type->type_code == vpiFunction) {
child->is_scheduled = 1;
vthread_run(child);
} else {
schedule_vthread(child, 0, true);
}
return true;
}
@ -2363,19 +2378,21 @@ bool of_LOAD_AV(vthread_t thr, vvp_code_t cp)
vvp_vector4_t word = array_get_word(cp->array, adr);
if (word.size() != wid) {
fprintf(stderr, "internal error: array width=%u, word.size()=%u, wid=%u\n",
0, word.size(), wid);
assert(word.size() == wid);
}
/* Check the address once, before we scan the vector. */
thr_check_addr(thr, bit+wid-1);
if (word.size() > wid)
word.resize(wid);
/* Copy the vector bits into the bits4 vector. Do the copy
directly to skip the excess calls to thr_check_addr. */
thr->bits4.set_vec(bit, word);
/* If the source is shorter then the desired width, then pad
with BIT4_X values. */
for (unsigned idx = word.size() ; idx < wid ; idx += 1)
thr->bits4.set_bit(bit+idx, BIT4_X);
return true;
}

View File

@ -37,3 +37,4 @@ vpi_vprintf
vpip_format_strength
vpip_set_return_value
vpip_calc_clog2

View File

@ -130,6 +130,23 @@ vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c)
}
}
vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar)
{
switch(scalar) {
case vpi0:
return BIT4_0;
case vpi1:
return BIT4_1;
case vpiX:
return BIT4_X;
case vpiZ:
return BIT4_Z;
default:
fprintf(stderr, "Unsupported scalar value %d.\n", scalar);
assert(0);
}
}
vvp_bit4_t operator ^ (vvp_bit4_t a, vvp_bit4_t b)
{
if (bit4_is_xz(a))
@ -372,19 +389,12 @@ vvp_vector4_t::vvp_vector4_t(unsigned size, double val)
double fraction;
int exponent;
/* We return 'bx for a NaN. */
if (val != val) {
/* We return 'bx for a NaN or +/- infinity. */
if (val != val || (val && (val == 0.5*val))) {
allocate_words_(size, WORD_X_ABITS, WORD_X_BBITS);
return;
}
/* We return 'b1 for + infinity or 'b0 for - infinity. */
if (val && (val == 0.5*val)) {
if (val > 0) allocate_words_(size, WORD_1_ABITS, WORD_1_BBITS);
else allocate_words_(size, WORD_0_ABITS, WORD_0_BBITS);
return;
}
/* Convert to a positive result. */
if (val < 0.0) {
is_neg = true;
@ -1618,6 +1628,15 @@ void vvp_vector2_t::trim()
while (value(wid_-1) == 0 && wid_ > 1) wid_ -= 1;
}
/* This is a special trim that is used on numbers we know represent a
* negative signed value (they came from a negative real value). */
void vvp_vector2_t::trim_neg()
{
if (value(wid_-1) == 1 && wid_ > 32) {
while (value(wid_-2) == 1 && wid_ > 32) wid_ -= 1;
}
}
int vvp_vector2_t::value(unsigned idx) const
{
if (idx >= wid_)
@ -2125,22 +2144,19 @@ void vvp_net_fun_t::recv_vec4(vvp_net_ptr_t, const vvp_vector4_t&)
assert(0);
}
void vvp_net_fun_t::recv_vec4_pv(vvp_net_ptr_t, const vvp_vector4_t&bits,
void vvp_net_fun_t::recv_vec4_pv(vvp_net_ptr_t, const vvp_vector4_t&bit,
unsigned base, unsigned wid, unsigned vwid)
{
cerr << "internal error: " << typeid(*this).name() << ": "
<< "recv_vec4_pv(" << bits << ", " << base
<< "recv_vec4_pv(" << bit << ", " << base
<< ", " << wid << ", " << vwid << ") not implemented" << endl;
assert(0);
}
void vvp_net_fun_t::recv_vec8_pv(vvp_net_ptr_t, const vvp_vector8_t&bits,
void vvp_net_fun_t::recv_vec8_pv(vvp_net_ptr_t port, const vvp_vector8_t&bit,
unsigned base, unsigned wid, unsigned vwid)
{
cerr << "internal error: " << typeid(*this).name() << ": "
<< "recv_vec8_pv(" << bits << ", " << base
<< ", " << wid << ", " << vwid << ") not implemented" << endl;
assert(0);
recv_vec4_pv(port, reduce4(bit), base, wid, vwid);
}
void vvp_net_fun_t::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit)

View File

@ -20,6 +20,7 @@
*/
# include "config.h"
# include "vpi_user.h"
# include <stddef.h>
# include <string.h>
# include <new>
@ -67,6 +68,8 @@ inline char vvp_bit4_to_ascii(vvp_bit4_t a) { return "01zx"[a]; }
extern vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c);
extern vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar);
/* Return TRUE if the bit is BIT4_X or BIT4_Z. The fast
implementation here relies on the encoding of vvp_bit4_t values. */
inline bool bit4_is_xz(vvp_bit4_t a) { return a >= 2; }
@ -470,6 +473,9 @@ class vvp_vector2_t {
void set_bit(unsigned idx, int bit);
// Make the size just big enough to hold the first 1 bit.
void trim();
// Trim off extra 1 bit since this is representing a negative value.
// Always keep at least 32 bits.
void trim_neg();
private:
enum { BITS_PER_WORD = 8 * sizeof(unsigned long) };
@ -853,8 +859,9 @@ struct vvp_net_t {
* port. The value is a vvp_vector4_t.
*
* Most nodes do not care about the specific strengths of bits, so the
* default behavior for recv_vec8 is to reduce the operand to a
* vvp_vector4_t and pass it on to the recv_vec4 method.
* default behavior for recv_vec8 and recv_vec8_pv is to reduce the
* operand to a vvp_vector4_t and pass it on to the recv_vec4 or
* recv_vec4_pv method.
*/
class vvp_net_fun_t {