1070 lines
30 KiB
C++
1070 lines
30 KiB
C++
/*
|
|
* Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com)
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
# include "config.h"
|
|
|
|
# include <cstdlib>
|
|
# include <iostream>
|
|
|
|
# include "Module.h"
|
|
# include "PExpr.h"
|
|
# include "PGate.h"
|
|
# include "PGenerate.h"
|
|
# include "PTask.h"
|
|
# include "PWire.h"
|
|
# include "Statement.h"
|
|
# include "compiler.h"
|
|
# include "netlist.h"
|
|
# include "netmisc.h"
|
|
# include "util.h"
|
|
# include "ivl_assert.h"
|
|
|
|
static bool get_const_argument(NetExpr*exp, verinum&res)
|
|
{
|
|
switch (exp->expr_type()) {
|
|
case IVL_VT_REAL: {
|
|
NetECReal*cv = dynamic_cast<NetECReal*>(exp);
|
|
if (cv == 0) return false;
|
|
verireal tmp = cv->value();
|
|
res = verinum(tmp.as_long());
|
|
break;
|
|
}
|
|
|
|
case IVL_VT_BOOL:
|
|
case IVL_VT_LOGIC: {
|
|
NetEConst*cv = dynamic_cast<NetEConst*>(exp);
|
|
if (cv == 0) return false;
|
|
res = cv->value();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
assert(0);;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Statement::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
}
|
|
|
|
bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const
|
|
{
|
|
bool flag = true;
|
|
|
|
for (map<perm_string,PWire*>::const_iterator wt = wires.begin()
|
|
; wt != wires.end() ; wt ++ ) {
|
|
|
|
PWire*cur = (*wt).second;
|
|
NetNet*sig = cur->elaborate_sig(des, scope);
|
|
|
|
|
|
/* If the signal is an input and is also declared as a
|
|
reg, then report an error. */
|
|
|
|
if (sig && (sig->scope() == scope)
|
|
&& (scope->type() == NetScope::MODULE)
|
|
&& (sig->port_type() == NetNet::PINPUT)
|
|
&& (sig->type() == NetNet::REG)) {
|
|
|
|
cerr << cur->get_fileline() << ": error: "
|
|
<< cur->basename() << " in "
|
|
<< scope->module_name()
|
|
<< " declared as input and as a reg type." << endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
if (sig && (sig->scope() == scope)
|
|
&& (scope->type() == NetScope::MODULE)
|
|
&& (sig->port_type() == NetNet::PINOUT)
|
|
&& (sig->type() == NetNet::REG)) {
|
|
|
|
cerr << cur->get_fileline() << ": error: "
|
|
<< cur->basename() << " in "
|
|
<< scope->module_name()
|
|
<< " declared as inout and as a reg type." << endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
static void elaborate_sig_funcs(Design*des, NetScope*scope,
|
|
const map<perm_string,PFunction*>&funcs)
|
|
{
|
|
typedef map<perm_string,PFunction*>::const_iterator mfunc_it_t;
|
|
|
|
for (mfunc_it_t cur = funcs.begin()
|
|
; cur != funcs.end() ; cur ++) {
|
|
|
|
hname_t use_name ( (*cur).first );
|
|
NetScope*fscope = scope->child(use_name);
|
|
if (scope == 0) {
|
|
cerr << (*cur).second->get_fileline() << ": internal error: "
|
|
<< "Child scope for function " << (*cur).first
|
|
<< " missing in " << scope_path(scope) << "." << endl;
|
|
des->errors += 1;
|
|
continue;
|
|
}
|
|
|
|
(*cur).second->elaborate_sig(des, fscope);
|
|
}
|
|
}
|
|
|
|
static void elaborate_sig_tasks(Design*des, NetScope*scope,
|
|
const map<perm_string,PTask*>&tasks)
|
|
{
|
|
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
|
|
|
for (mtask_it_t cur = tasks.begin()
|
|
; cur != tasks.end() ; cur ++) {
|
|
NetScope*tscope = scope->child( hname_t((*cur).first) );
|
|
assert(tscope);
|
|
(*cur).second->elaborate_sig(des, tscope);
|
|
}
|
|
}
|
|
|
|
|
|
bool Module::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
bool flag = true;
|
|
|
|
// Scan all the ports of the module, and make sure that each
|
|
// is connected to wires that have port declarations.
|
|
for (unsigned idx = 0 ; idx < ports.size() ; idx += 1) {
|
|
Module::port_t*pp = ports[idx];
|
|
if (pp == 0)
|
|
continue;
|
|
|
|
// The port has a name and an array of expressions. The
|
|
// expression are all identifiers that should reference
|
|
// wires within the scope.
|
|
map<perm_string,PWire*>::const_iterator wt;
|
|
for (unsigned cc = 0 ; cc < pp->expr.size() ; cc += 1) {
|
|
pform_name_t port_path (pp->expr[cc]->path());
|
|
// A concatenated wire of a port really should not
|
|
// have any hierarchy.
|
|
if (port_path.size() != 1) {
|
|
cerr << get_fileline() << ": internal error: "
|
|
<< "Port " << port_path << " has a funny name?"
|
|
<< endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
wt = wires.find(peek_tail_name(port_path));
|
|
|
|
if (wt == wires.end()) {
|
|
cerr << get_fileline() << ": error: "
|
|
<< "Port " << port_path << " ("
|
|
<< (idx+1) << ") of module " << mod_name()
|
|
<< " is not declared within module." << endl;
|
|
des->errors += 1;
|
|
continue;
|
|
}
|
|
|
|
if ((*wt).second->get_port_type() == NetNet::NOT_A_PORT) {
|
|
cerr << get_fileline() << ": error: "
|
|
<< "Port " << pp->expr[cc]->path() << " ("
|
|
<< (idx+1) << ") of module " << mod_name()
|
|
<< " has no direction declaration."
|
|
<< endl;
|
|
des->errors += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
flag = elaborate_sig_wires_(des, scope) && flag;
|
|
|
|
// Run through all the generate schemes to elaborate the
|
|
// signals that they hold. Note that the generate schemes hold
|
|
// the scopes that they instantiated, so we don't pass any
|
|
// scope in.
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
|
for (generate_it_t cur = generate_schemes.begin()
|
|
; cur != generate_schemes.end() ; cur ++ ) {
|
|
(*cur) -> elaborate_sig(des, scope);
|
|
}
|
|
|
|
// Get all the gates of the module and elaborate them by
|
|
// connecting them to the signals. The gate may be simple or
|
|
// complex. What we are looking for is gates that are modules
|
|
// that can create scopes and signals.
|
|
|
|
const list<PGate*>&gl = get_gates();
|
|
|
|
for (list<PGate*>::const_iterator gt = gl.begin()
|
|
; gt != gl.end()
|
|
; gt ++ ) {
|
|
|
|
flag &= (*gt)->elaborate_sig(des, scope);
|
|
}
|
|
|
|
// After all the wires are elaborated, we are free to
|
|
// elaborate the ports of the tasks defined within this
|
|
// module. Run through them now.
|
|
|
|
elaborate_sig_funcs(des, scope, funcs);
|
|
elaborate_sig_tasks(des, scope, tasks);
|
|
|
|
// initial and always blocks may contain begin-end and
|
|
// fork-join blocks that can introduce scopes. Therefore, I
|
|
// get to scan processes here.
|
|
|
|
typedef list<PProcess*>::const_iterator proc_it_t;
|
|
|
|
for (proc_it_t cur = behaviors.begin()
|
|
; cur != behaviors.end() ; cur ++ ) {
|
|
|
|
(*cur) -> statement() -> elaborate_sig(des, scope);
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
bool PExpr::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool PEConcat::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
bool flag = true;
|
|
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1)
|
|
flag = parms_[idx]->elaborate_sig(des, scope) && flag;
|
|
|
|
return flag;
|
|
}
|
|
|
|
bool PEIdent::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
NetNet* sig = 0;
|
|
const NetExpr*par = 0;
|
|
NetEvent* eve = 0;
|
|
|
|
// If implicit net creation is turned off, then stop now.
|
|
if (scope->default_nettype() == NetNet::NONE)
|
|
return true;
|
|
if (error_implicit)
|
|
return true;
|
|
|
|
symbol_search(this, des, scope, path_, sig, par, eve);
|
|
|
|
if (eve != 0)
|
|
return false;
|
|
|
|
if (par != 0)
|
|
return true;
|
|
|
|
if (sig == 0)
|
|
sig = make_implicit_net_(des, scope);
|
|
|
|
return sig != 0;
|
|
}
|
|
|
|
bool PEBinary::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
bool flag = true;
|
|
|
|
flag = left_->elaborate_sig(des, scope) && flag;
|
|
flag = right_->elaborate_sig(des, scope) && flag;
|
|
return flag;
|
|
}
|
|
|
|
bool PETernary::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
bool flag = true;
|
|
flag = tru_->elaborate_sig(des, scope) && flag;
|
|
flag = fal_->elaborate_sig(des, scope) && flag;
|
|
return flag;
|
|
}
|
|
|
|
bool PEUnary::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
return expr_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
bool PGate::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool PGBuiltin::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
bool flag = true;
|
|
|
|
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
|
const PExpr* pin_expr = pin(idx);
|
|
if (pin_expr == 0) {
|
|
// If there is no pin expression for this port,
|
|
// then skip it. Do not bother generating an error
|
|
// message here, that will be done during
|
|
// elaboration where these semantic details are tested.
|
|
continue;
|
|
}
|
|
ivl_assert(*this, pin_expr);
|
|
flag = pin_expr->elaborate_sig(des, scope) && flag;
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
bool PGAssign::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
/* Normally, l-values to continuous assignments are NOT allowed
|
|
to implicitly declare nets. However, so many tools do allow
|
|
it that Icarus Verilog will allow it, at least if extensions
|
|
are enabled. */
|
|
if (gn_icarus_misc_flag)
|
|
return pin(0)->elaborate_sig(des, scope);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PGModule::elaborate_sig_mod_(Design*des, NetScope*scope,
|
|
Module*rmod) const
|
|
{
|
|
bool flag = true;
|
|
|
|
// First, elaborate the signals that may be created implicitly
|
|
// by ports to this module instantiation. Handle the case that
|
|
// the ports are passed by name (pins_ != 0) or position.
|
|
if (pins_)
|
|
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
|
const PExpr*tmp = pins_[idx].parm;
|
|
if (tmp == 0)
|
|
continue;
|
|
flag = tmp->elaborate_sig(des, scope) && flag;
|
|
}
|
|
else
|
|
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
|
const PExpr*tmp = pin(idx);
|
|
if (tmp == 0)
|
|
continue;
|
|
flag = tmp->elaborate_sig(des, scope) && flag;
|
|
}
|
|
|
|
|
|
NetScope::scope_vec_t instance = scope->instance_arrays[get_name()];
|
|
|
|
for (unsigned idx = 0 ; idx < instance.size() ; idx += 1) {
|
|
// I know a priori that the elaborate_scope created the scope
|
|
// already, so just look it up as a child of the current scope.
|
|
NetScope*my_scope = instance[idx];
|
|
assert(my_scope);
|
|
|
|
if (my_scope->parent() != scope) {
|
|
cerr << get_fileline() << ": internal error: "
|
|
<< "Instance " << scope_path(my_scope)
|
|
<< " is in parent " << scope_path(my_scope->parent())
|
|
<< " instead of " << scope_path(scope)
|
|
<< endl;
|
|
}
|
|
assert(my_scope->parent() == scope);
|
|
|
|
if (! rmod->elaborate_sig(des, my_scope))
|
|
flag = false;
|
|
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
bool PGModule::elaborate_sig_udp_(Design*des, NetScope*scope, PUdp*udp) const
|
|
{
|
|
bool flag = true;
|
|
|
|
if (pins_)
|
|
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
|
const PExpr*tmp = pins_[idx].parm;
|
|
if (tmp == 0)
|
|
continue;
|
|
flag = tmp->elaborate_sig(des, scope) && flag;
|
|
}
|
|
else
|
|
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
|
const PExpr*tmp = pin(idx);
|
|
if (tmp == 0)
|
|
continue;
|
|
flag = tmp->elaborate_sig(des, scope) && flag;
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
bool PGenerate::elaborate_sig(Design*des, NetScope*container) const
|
|
{
|
|
bool flag = true;
|
|
|
|
// Handle the special case that this is a CASE scheme. In this
|
|
// case the PGenerate itself does not have the generated
|
|
// item. Look instead for the case ITEM that has a scope
|
|
// generated for it.
|
|
if (scheme_type == PGenerate::GS_CASE) {
|
|
if (debug_elaborate)
|
|
cerr << get_fileline() << ": debug: generate case"
|
|
<< " elaborate_sig in scope "
|
|
<< scope_path(container) << "." << endl;
|
|
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
|
for (generate_it_t cur = generate_schemes.begin()
|
|
; cur != generate_schemes.end() ; cur ++) {
|
|
PGenerate*item = *cur;
|
|
if (! item->scope_list_.empty()) {
|
|
flag &= item->elaborate_sig(des, container);
|
|
}
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
typedef list<NetScope*>::const_iterator scope_list_it_t;
|
|
for (scope_list_it_t cur = scope_list_.begin()
|
|
; cur != scope_list_.end() ; cur ++ ) {
|
|
|
|
NetScope*scope = *cur;
|
|
|
|
if (scope->parent() != container)
|
|
continue;
|
|
|
|
if (debug_elaborate)
|
|
cerr << get_fileline() << ": debug: Elaborate nets in "
|
|
<< "scope " << scope_path(*cur)
|
|
<< " in generate " << id_number << endl;
|
|
flag = elaborate_sig_(des, *cur) & flag;
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const
|
|
{
|
|
// Scan the declared PWires to elaborate the obvious signals
|
|
// in the current scope.
|
|
typedef map<perm_string,PWire*>::const_iterator wires_it_t;
|
|
for (wires_it_t wt = wires.begin()
|
|
; wt != wires.end() ; wt ++ ) {
|
|
|
|
PWire*cur = (*wt).second;
|
|
|
|
if (debug_elaborate)
|
|
cerr << get_fileline() << ": debug: Elaborate PWire "
|
|
<< cur->basename() << " in scope " << scope_path(scope) << endl;
|
|
|
|
cur->elaborate_sig(des, scope);
|
|
}
|
|
|
|
elaborate_sig_funcs(des, scope, funcs);
|
|
elaborate_sig_tasks(des, scope, tasks);
|
|
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
|
for (generate_it_t cur = generate_schemes.begin()
|
|
; cur != generate_schemes.end() ; cur ++ ) {
|
|
(*cur) -> elaborate_sig(des, scope);
|
|
}
|
|
|
|
typedef list<PGate*>::const_iterator pgate_list_it_t;
|
|
for (pgate_list_it_t cur = gates.begin()
|
|
; cur != gates.end() ; cur ++) {
|
|
(*cur) ->elaborate_sig(des, scope);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* A function definition exists within an elaborated module. This
|
|
* matters when elaborating signals, as the ports of the function are
|
|
* created as signals/variables for each instance of the
|
|
* function. That is why PFunction has an elaborate_sig method.
|
|
*/
|
|
void PFunction::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
perm_string fname = scope->basename();
|
|
assert(scope->type() == NetScope::FUNC);
|
|
|
|
elaborate_sig_wires_(des, scope);
|
|
|
|
/* Make sure the function has at least one input port. If it
|
|
fails this test, print an error message. Keep going so we
|
|
can find more errors. */
|
|
if (ports_ == 0) {
|
|
cerr << get_fileline() << ": error: Function " << fname
|
|
<< " has no ports." << endl;
|
|
cerr << get_fileline() << ": : Functions must have"
|
|
<< " at least one input port." << endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
NetNet*ret_sig = 0;
|
|
|
|
/* Create the signals/variables of the return value and write
|
|
them into the function scope. */
|
|
switch (return_type_.type) {
|
|
|
|
case PTF_REG:
|
|
case PTF_REG_S:
|
|
if (return_type_.range) {
|
|
NetExpr*me = elab_and_eval(des, scope,
|
|
(*return_type_.range)[0], -1);
|
|
assert(me);
|
|
NetExpr*le = elab_and_eval(des, scope,
|
|
(*return_type_.range)[1], -1);
|
|
assert(le);
|
|
|
|
long mnum = 0, lnum = 0;
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(me)) {
|
|
mnum = tmp->value().as_long();
|
|
} else {
|
|
cerr << me->get_fileline() << ": error: "
|
|
"Unable to evaluate constant expression "
|
|
<< *me << "." << endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(le)) {
|
|
lnum = tmp->value().as_long();
|
|
} else {
|
|
cerr << le->get_fileline() << ": error: "
|
|
"Unable to evaluate constant expression "
|
|
<< *le << "." << endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
ret_sig = new NetNet(scope, fname, NetNet::REG, mnum, lnum);
|
|
|
|
} else {
|
|
ret_sig = new NetNet(scope, fname, NetNet::REG);
|
|
}
|
|
ret_sig->set_line(*this);
|
|
ret_sig->set_signed(return_type_.type == PTF_REG_S);
|
|
ret_sig->port_type(NetNet::POUTPUT);
|
|
ret_sig->data_type(IVL_VT_LOGIC);
|
|
break;
|
|
|
|
case PTF_INTEGER:
|
|
ret_sig = new NetNet(scope, fname, NetNet::REG, integer_width);
|
|
ret_sig->set_line(*this);
|
|
ret_sig->set_signed(true);
|
|
ret_sig->set_isint(true);
|
|
ret_sig->port_type(NetNet::POUTPUT);
|
|
ret_sig->data_type(IVL_VT_LOGIC);
|
|
break;
|
|
|
|
case PTF_TIME:
|
|
ret_sig = new NetNet(scope, fname, NetNet::REG, 64);
|
|
ret_sig->set_line(*this);
|
|
ret_sig->set_signed(false);
|
|
ret_sig->set_isint(false);
|
|
ret_sig->port_type(NetNet::POUTPUT);
|
|
ret_sig->data_type(IVL_VT_LOGIC);
|
|
break;
|
|
|
|
case PTF_REAL:
|
|
case PTF_REALTIME:
|
|
ret_sig = new NetNet(scope, fname, NetNet::REG, 1);
|
|
ret_sig->set_line(*this);
|
|
ret_sig->set_signed(true);
|
|
ret_sig->set_isint(false);
|
|
ret_sig->port_type(NetNet::POUTPUT);
|
|
ret_sig->data_type(IVL_VT_REAL);
|
|
break;
|
|
|
|
default:
|
|
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);
|
|
|
|
if (ports_)
|
|
for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) {
|
|
|
|
/* Parse the port name into the task name and the reg
|
|
name. We know by design that the port name is given
|
|
as two components: <func>.<port>. */
|
|
|
|
perm_string pname = (*ports_)[idx]->basename();
|
|
|
|
NetNet*tmp = scope->find_signal(pname);
|
|
ports[idx] = 0;
|
|
|
|
if (tmp == 0) {
|
|
cerr << get_fileline() << ": internal error: function "
|
|
<< scope_path(scope) << " is missing port "
|
|
<< pname << "." << endl;
|
|
scope->dump(cerr);
|
|
cerr << get_fileline() << ": Continuing..." << endl;
|
|
des->errors += 1;
|
|
continue;
|
|
}
|
|
|
|
if (tmp->port_type() == NetNet::NOT_A_PORT) {
|
|
cerr << get_fileline() << ": internal error: function "
|
|
<< scope_path(scope) << " port " << pname
|
|
<< " is a port but is not a port?" << endl;
|
|
des->errors += 1;
|
|
scope->dump(cerr);
|
|
continue;
|
|
}
|
|
|
|
ports[idx] = tmp;
|
|
if (tmp->port_type() != NetNet::PINPUT) {
|
|
cerr << tmp->get_fileline() << ": error: function "
|
|
<< scope_path(scope) << " port " << pname
|
|
<< " is not an input port." << endl;
|
|
cerr << tmp->get_fileline() << ": : Function arguments "
|
|
<< "must be input ports." << endl;
|
|
des->errors += 1;
|
|
}
|
|
}
|
|
|
|
|
|
NetFuncDef*def = 0;
|
|
if (ret_sig) def = new NetFuncDef(scope, ret_sig, ports);
|
|
|
|
assert(def);
|
|
if (debug_elaborate)
|
|
cerr << get_fileline() << ": debug: "
|
|
<< "Attach function definition to scope "
|
|
<< scope_path(scope) << "." << endl;
|
|
|
|
scope->set_func_def(def);
|
|
|
|
// Look for further signals in the sub-statement
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
/*
|
|
* A task definition is a scope within an elaborated module. When we
|
|
* are elaborating signals, the scopes have already been created, as
|
|
* have the reg objects that are the parameters of this task. The
|
|
* elaborate_sig method of PTask is therefore left to connect the
|
|
* signals to the ports of the NetTaskDef definition. We know for
|
|
* certain that signals exist (They are in my scope!) so the port
|
|
* binding is sure to work.
|
|
*/
|
|
void PTask::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
assert(scope->type() == NetScope::TASK);
|
|
|
|
elaborate_sig_wires_(des, scope);
|
|
|
|
svector<NetNet*>ports (ports_? ports_->count() : 0);
|
|
for (unsigned idx = 0 ; idx < ports.count() ; idx += 1) {
|
|
|
|
perm_string port_name = (*ports_)[idx]->basename();
|
|
|
|
/* Find the signal for the port. We know by definition
|
|
that it is in the scope of the task, so look only in
|
|
the scope. */
|
|
NetNet*tmp = scope->find_signal(port_name);
|
|
|
|
if (tmp == 0) {
|
|
cerr << get_fileline() << ": internal error: "
|
|
<< "Could not find port " << port_name
|
|
<< " in scope " << scope_path(scope) << endl;
|
|
scope->dump(cerr);
|
|
des->errors += 1;
|
|
}
|
|
|
|
ports[idx] = tmp;
|
|
}
|
|
|
|
NetTaskDef*def = new NetTaskDef(scope, ports);
|
|
scope->set_task_def(def);
|
|
|
|
// Look for further signals in the sub-statement
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PBlock::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
NetScope*my_scope = scope;
|
|
|
|
if (pscope_name() != 0) {
|
|
hname_t use_name (pscope_name());
|
|
my_scope = scope->child(use_name);
|
|
if (my_scope == 0) {
|
|
cerr << get_fileline() << ": internal error: "
|
|
<< "Unable to find child scope " << pscope_name()
|
|
<< " in this context?" << endl;
|
|
des->errors += 1;
|
|
my_scope = scope;
|
|
} else {
|
|
if (debug_elaborate)
|
|
cerr << get_fileline() << ": debug: "
|
|
<< "elaborate_sig descending into "
|
|
<< scope_path(my_scope) << "." << endl;
|
|
|
|
elaborate_sig_wires_(des, my_scope);
|
|
}
|
|
}
|
|
|
|
// elaborate_sig in the statements included in the
|
|
// block. There may be named blocks in there.
|
|
for (unsigned idx = 0 ; idx < list_.count() ; idx += 1)
|
|
list_[idx] -> elaborate_sig(des, my_scope);
|
|
}
|
|
|
|
void PCase::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (items_ == 0)
|
|
return;
|
|
|
|
for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) {
|
|
if ( (*items_)[idx]->stat )
|
|
(*items_)[idx]->stat ->elaborate_sig(des,scope);
|
|
}
|
|
}
|
|
|
|
void PCondit::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (if_)
|
|
if_->elaborate_sig(des, scope);
|
|
if (else_)
|
|
else_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PDelayStatement::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PEventStatement::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PForever::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PForStatement::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PRepeat::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
void PWhile::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
if (statement_)
|
|
statement_->elaborate_sig(des, scope);
|
|
}
|
|
|
|
/*
|
|
* Elaborate a source wire. The "wire" is the declaration of wires,
|
|
* registers, ports and memories. The parser has already merged the
|
|
* multiple properties of a wire (i.e., "input wire") so come the
|
|
* elaboration this creates an object in the design that represent the
|
|
* defined item.
|
|
*/
|
|
NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
|
|
{
|
|
NetNet::Type wtype = type_;
|
|
if (wtype == NetNet::IMPLICIT)
|
|
wtype = NetNet::WIRE;
|
|
if (wtype == NetNet::IMPLICIT_REG)
|
|
wtype = NetNet::REG;
|
|
|
|
unsigned wid = 1;
|
|
long lsb = 0, msb = 0;
|
|
|
|
des->errors += error_cnt_;
|
|
|
|
if (port_set_ || net_set_) {
|
|
long pmsb = 0, plsb = 0, nmsb = 0, nlsb = 0;
|
|
bool bad_lsb = false, bad_msb = false;
|
|
/* If they exist get the port definition MSB and LSB */
|
|
if (port_set_ && port_msb_ != 0) {
|
|
NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1);
|
|
|
|
if (! eval_as_long(pmsb, texpr)) {
|
|
cerr << port_msb_->get_fileline() << ": error: "
|
|
"Range expressions must be constant." << endl;
|
|
cerr << port_msb_->get_fileline() << " : "
|
|
"This MSB expression violates the rule: "
|
|
<< *port_msb_ << endl;
|
|
des->errors += 1;
|
|
bad_msb = true;
|
|
}
|
|
|
|
delete texpr;
|
|
|
|
texpr = elab_and_eval(des, scope, port_lsb_, -1);
|
|
|
|
if (! eval_as_long(plsb, texpr)) {
|
|
cerr << port_lsb_->get_fileline() << ": error: "
|
|
"Range expressions must be constant." << endl;
|
|
cerr << port_lsb_->get_fileline() << " : "
|
|
"This LSB expression violates the rule: "
|
|
<< *port_lsb_ << endl;
|
|
des->errors += 1;
|
|
bad_lsb = true;
|
|
}
|
|
|
|
delete texpr;
|
|
nmsb = pmsb;
|
|
nlsb = plsb;
|
|
}
|
|
if (!port_set_) assert(port_msb_ == 0 && port_lsb_ == 0);
|
|
if (port_msb_ == 0) assert(port_lsb_ == 0);
|
|
if (port_lsb_ == 0) assert(port_msb_ == 0);
|
|
|
|
/* If they exist get the net/etc. definition MSB and LSB */
|
|
if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) {
|
|
NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1);
|
|
|
|
if (! eval_as_long(nmsb, texpr)) {
|
|
cerr << net_msb_->get_fileline() << ": error: "
|
|
"Range expressions must be constant." << endl;
|
|
cerr << net_msb_->get_fileline() << " : "
|
|
"This MSB expression violates the rule: "
|
|
<< *net_msb_ << endl;
|
|
des->errors += 1;
|
|
bad_msb = true;
|
|
}
|
|
|
|
delete texpr;
|
|
|
|
texpr = elab_and_eval(des, scope, net_lsb_, -1);
|
|
|
|
if (! eval_as_long(nlsb, texpr)) {
|
|
cerr << net_lsb_->get_fileline() << ": error: "
|
|
"Range expressions must be constant." << endl;
|
|
cerr << net_lsb_->get_fileline() << " : "
|
|
"This LSB expression violates the rule: "
|
|
<< *net_lsb_ << endl;
|
|
des->errors += 1;
|
|
bad_lsb = true;
|
|
}
|
|
|
|
delete texpr;
|
|
}
|
|
if (!net_set_) assert(net_msb_ == 0 && net_lsb_ == 0);
|
|
if (net_msb_ == 0) assert(net_lsb_ == 0);
|
|
if (net_lsb_ == 0) assert(net_msb_ == 0);
|
|
|
|
/* We have a port size error */
|
|
if (port_set_ && net_set_ && (pmsb != nmsb || plsb != nlsb)) {
|
|
|
|
/* Scalar port with a vector net/etc. definition */
|
|
if (port_msb_ == 0) {
|
|
if (!gn_io_range_error_flag) {
|
|
cerr << get_fileline()
|
|
<< ": warning: Scalar port ``" << name_
|
|
<< "'' has a vectored net declaration ["
|
|
<< nmsb << ":" << nlsb << "]." << endl;
|
|
} else {
|
|
cerr << get_fileline()
|
|
<< ": error: Scalar port ``" << name_
|
|
<< "'' has a vectored net declaration ["
|
|
<< nmsb << ":" << nlsb << "]." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Vectored port with a scalar net/etc. definition */
|
|
if (net_msb_ == 0) {
|
|
cerr << port_msb_->get_fileline()
|
|
<< ": error: Vectored port ``"
|
|
<< name_ << "'' [" << pmsb << ":" << plsb
|
|
<< "] has a scalar net declaration at "
|
|
<< get_fileline() << "." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Both vectored, but they have different ranges. */
|
|
if (port_msb_ != 0 && net_msb_ != 0) {
|
|
cerr << port_msb_->get_fileline()
|
|
<< ": error: Vectored port ``"
|
|
<< name_ << "'' [" << pmsb << ":" << plsb
|
|
<< "] has a net declaration [" << nmsb << ":"
|
|
<< nlsb << "] at " << net_msb_->get_fileline()
|
|
<< " that does not match." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Attempt to recover from errors. */
|
|
if (bad_lsb) nlsb = 0;
|
|
if (bad_msb) nmsb = nlsb;
|
|
|
|
lsb = nlsb;
|
|
msb = nmsb;
|
|
if (nmsb > nlsb)
|
|
wid = nmsb - nlsb + 1;
|
|
else
|
|
wid = nlsb - nmsb + 1;
|
|
|
|
|
|
}
|
|
|
|
unsigned nattrib = 0;
|
|
attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib,
|
|
des, scope);
|
|
|
|
long array_s0 = 0;
|
|
long array_e0 = 0;
|
|
unsigned array_dimensions = 0;
|
|
|
|
/* If the ident has idx expressions, then this is a
|
|
memory. It can only have the idx registers after the msb
|
|
and lsb expressions are filled. And, if it has one index,
|
|
it has both. */
|
|
if (lidx_ || ridx_) {
|
|
assert(lidx_ && ridx_);
|
|
|
|
NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1);
|
|
NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1);
|
|
|
|
if ((lexp == 0) || (rexp == 0)) {
|
|
cerr << get_fileline() << ": internal error: There is "
|
|
<< "a problem evaluating indices for ``"
|
|
<< name_ << "''." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
|
|
bool const_flag = true;
|
|
verinum lval, rval;
|
|
const_flag &= get_const_argument(lexp, lval);
|
|
const_flag &= get_const_argument(rexp, rval);
|
|
delete rexp;
|
|
delete lexp;
|
|
|
|
if (!const_flag) {
|
|
cerr << get_fileline() << ": error: The indices "
|
|
<< "are not constant for array ``"
|
|
<< name_ << "''." << endl;
|
|
des->errors += 1;
|
|
/* Attempt to recover from error, */
|
|
array_s0 = 0;
|
|
array_e0 = 0;
|
|
} else {
|
|
array_s0 = lval.as_long();
|
|
array_e0 = rval.as_long();
|
|
}
|
|
array_dimensions = 1;
|
|
}
|
|
|
|
/* If the net type is supply0 or supply1, replace it
|
|
with a simple wire with a pulldown/pullup with supply
|
|
strength. In other words, transform:
|
|
|
|
supply0 foo;
|
|
|
|
to:
|
|
|
|
wire foo;
|
|
pulldown #(supply0) (foo);
|
|
|
|
This reduces the backend burden, and behaves exactly
|
|
the same. */
|
|
|
|
NetLogic*pull = 0;
|
|
if (wtype == NetNet::SUPPLY0 || wtype == NetNet::SUPPLY1) {
|
|
NetLogic::TYPE pull_type = (wtype==NetNet::SUPPLY1)
|
|
? NetLogic::PULLUP
|
|
: NetLogic::PULLDOWN;
|
|
pull = new NetLogic(scope, scope->local_symbol(),
|
|
1, pull_type, wid);
|
|
pull->set_line(*this);
|
|
pull->pin(0).drive0(Link::SUPPLY);
|
|
pull->pin(0).drive1(Link::SUPPLY);
|
|
des->add_node(pull);
|
|
wtype = NetNet::WIRE;
|
|
|
|
if (debug_elaborate) {
|
|
cerr << get_fileline() << ": debug: "
|
|
<< "Generate a SUPPLY pulldown for the "
|
|
<< "supply0 net." << endl;
|
|
}
|
|
}
|
|
|
|
if (debug_elaborate) {
|
|
cerr << get_fileline() << ": debug: Create signal "
|
|
<< wtype << " ["<<msb<<":"<<lsb<<"] " << name_;
|
|
if (array_dimensions > 0) {
|
|
cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl;
|
|
}
|
|
cerr << " in scope " << scope_path(scope) << endl;
|
|
}
|
|
|
|
|
|
NetNet*sig = array_dimensions > 0
|
|
? new NetNet(scope, name_, wtype, msb, lsb, array_s0, array_e0)
|
|
: new NetNet(scope, name_, wtype, msb, lsb);
|
|
|
|
ivl_variable_type_t use_data_type = data_type_;
|
|
if (use_data_type == IVL_VT_NO_TYPE) {
|
|
use_data_type = IVL_VT_LOGIC;
|
|
if (debug_elaborate) {
|
|
cerr << get_fileline() << ": debug: "
|
|
<< "Signal " << name_
|
|
<< " in scope " << scope_path(scope)
|
|
<< " defaults to data type " << use_data_type << endl;
|
|
}
|
|
}
|
|
|
|
sig->data_type(use_data_type);
|
|
sig->set_line(*this);
|
|
sig->port_type(port_type_);
|
|
sig->set_signed(get_signed());
|
|
sig->set_isint(get_isint());
|
|
|
|
if (ivl_discipline_t dis = get_discipline()) {
|
|
sig->set_discipline(dis);
|
|
}
|
|
|
|
if (pull)
|
|
connect(sig->pin(0), pull->pin(0));
|
|
|
|
for (unsigned idx = 0 ; idx < nattrib ; idx += 1)
|
|
sig->attribute(attrib_list[idx].key, attrib_list[idx].val);
|
|
|
|
return sig;
|
|
}
|