1998-11-04 00:28:49 +01:00
|
|
|
/*
|
2000-02-23 03:56:53 +01:00
|
|
|
* Copyright (c) 1998-2000 Stephen Williams (steve@icarus.com)
|
1998-11-04 00:28:49 +01:00
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2000-02-23 03:56:53 +01:00
|
|
|
#if !defined(WINNT) && !defined(macintosh)
|
2001-10-20 07:21:51 +02:00
|
|
|
#ident "$Id: pform.cc,v 1.79 2001/10/20 05:21:51 steve Exp $"
|
1998-11-04 00:28:49 +01:00
|
|
|
#endif
|
|
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include "config.h"
|
|
|
|
|
|
1999-06-06 22:45:38 +02:00
|
|
|
# include "compiler.h"
|
1998-11-04 00:28:49 +01:00
|
|
|
# include "pform.h"
|
|
|
|
|
# include "parse_misc.h"
|
2000-04-01 21:31:57 +02:00
|
|
|
# include "PEvent.h"
|
1998-11-25 03:35:53 +01:00
|
|
|
# include "PUdp.h"
|
1998-11-04 00:28:49 +01:00
|
|
|
# include <list>
|
1998-11-25 03:35:53 +01:00
|
|
|
# include <map>
|
1998-11-04 00:28:49 +01:00
|
|
|
# include <assert.h>
|
|
|
|
|
# include <typeinfo>
|
1999-01-25 06:45:56 +01:00
|
|
|
# include <strstream>
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1998-12-09 05:02:47 +01:00
|
|
|
/*
|
|
|
|
|
* The lexor accesses the vl_* variables.
|
|
|
|
|
*/
|
|
|
|
|
string vl_file = "";
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
extern int VLparse();
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
static Module*pform_cur_module = 0;
|
2000-07-23 00:09:03 +02:00
|
|
|
static int pform_time_unit = 0;
|
|
|
|
|
static int pform_time_prec = 0;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-06-24 06:24:18 +02:00
|
|
|
/*
|
|
|
|
|
* The scope stack and the following functions handle the processing
|
|
|
|
|
* of scope. As I enter a scope, the push function is called, and as I
|
1999-09-30 03:22:37 +02:00
|
|
|
* leave a scope the pop function is called.
|
|
|
|
|
*
|
|
|
|
|
* The top module is not included in the scope list.
|
1999-06-24 06:24:18 +02:00
|
|
|
*/
|
|
|
|
|
struct scope_name_t {
|
|
|
|
|
string name;
|
|
|
|
|
struct scope_name_t*next;
|
|
|
|
|
};
|
|
|
|
|
static scope_name_t*scope_stack = 0;
|
|
|
|
|
|
2000-07-23 00:09:03 +02:00
|
|
|
void pform_set_timescale(int unit, int prec)
|
|
|
|
|
{
|
|
|
|
|
assert(unit >= prec);
|
|
|
|
|
pform_time_unit = unit;
|
|
|
|
|
pform_time_prec = prec;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 06:24:18 +02:00
|
|
|
void pform_push_scope(const string&name)
|
|
|
|
|
{
|
|
|
|
|
scope_name_t*cur = new scope_name_t;
|
|
|
|
|
cur->name = name;
|
|
|
|
|
cur->next = scope_stack;
|
|
|
|
|
scope_stack = cur;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pform_pop_scope()
|
|
|
|
|
{
|
|
|
|
|
assert(scope_stack);
|
|
|
|
|
scope_name_t*cur = scope_stack;
|
|
|
|
|
scope_stack = cur->next;
|
|
|
|
|
delete cur;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static string scoped_name(string name)
|
|
|
|
|
{
|
|
|
|
|
scope_name_t*cur = scope_stack;
|
|
|
|
|
while (cur) {
|
|
|
|
|
name = cur->name + "." + name;
|
|
|
|
|
cur = cur->next;
|
|
|
|
|
}
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 01:42:13 +01:00
|
|
|
static map<string,Module*> vl_modules;
|
|
|
|
|
static map<string,PUdp*> vl_primitives;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function evaluates delay expressions. The result should be a
|
|
|
|
|
* simple constant that I can interpret as an unsigned number.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned long evaluate_delay(PExpr*delay)
|
|
|
|
|
{
|
|
|
|
|
PENumber*pp = dynamic_cast<PENumber*>(delay);
|
|
|
|
|
if (pp == 0) {
|
|
|
|
|
VLerror("Sorry, delay expression is too complicated.");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pp->value().as_ulong();
|
|
|
|
|
}
|
|
|
|
|
|
2001-10-20 07:21:51 +02:00
|
|
|
void pform_startmodule(const char*name, svector<Module::port_t*>*ports)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-07-03 04:12:51 +02:00
|
|
|
assert( pform_cur_module == 0 );
|
1999-09-17 04:06:25 +02:00
|
|
|
|
|
|
|
|
/* The parser parses ``module foo()'' as having one
|
|
|
|
|
unconnected port, but it is really a module with no
|
|
|
|
|
ports. Fix it up here. */
|
|
|
|
|
if (ports && (ports->count() == 1) && ((*ports)[0] == 0)) {
|
|
|
|
|
delete ports;
|
|
|
|
|
ports = 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-03 06:14:49 +02:00
|
|
|
pform_cur_module = new Module(name, ports);
|
2000-07-23 00:09:03 +02:00
|
|
|
pform_cur_module->time_unit = pform_time_unit;
|
|
|
|
|
pform_cur_module->time_precision = pform_time_prec;
|
1999-08-03 06:14:49 +02:00
|
|
|
delete ports;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2001-10-20 07:21:51 +02:00
|
|
|
void pform_endmodule(const char*name)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-07-03 04:12:51 +02:00
|
|
|
assert(pform_cur_module);
|
2001-10-20 07:21:51 +02:00
|
|
|
assert(strcmp(name, pform_cur_module->mod_name()) == 0);
|
1999-07-03 04:12:51 +02:00
|
|
|
vl_modules[name] = pform_cur_module;
|
|
|
|
|
pform_cur_module = 0;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-05-16 07:08:42 +02:00
|
|
|
bool pform_expression_is_constant(const PExpr*ex)
|
|
|
|
|
{
|
1999-07-03 04:12:51 +02:00
|
|
|
return ex->is_constant(pform_cur_module);
|
1999-05-16 07:08:42 +02:00
|
|
|
}
|
|
|
|
|
|
2000-07-29 19:58:20 +02:00
|
|
|
MIN_TYP_MAX min_typ_max_flag = TYP;
|
|
|
|
|
unsigned min_typ_max_warn = 10;
|
|
|
|
|
|
|
|
|
|
PExpr* pform_select_mtm_expr(PExpr*min, PExpr*typ, PExpr*max)
|
|
|
|
|
{
|
|
|
|
|
PExpr*res = 0;
|
|
|
|
|
|
|
|
|
|
switch (min_typ_max_flag) {
|
|
|
|
|
case MIN:
|
|
|
|
|
res = min;
|
|
|
|
|
delete typ;
|
|
|
|
|
delete max;
|
|
|
|
|
break;
|
|
|
|
|
case TYP:
|
|
|
|
|
res = typ;
|
|
|
|
|
delete min;
|
|
|
|
|
delete max;
|
|
|
|
|
break;
|
|
|
|
|
case MAX:
|
|
|
|
|
res = max;
|
|
|
|
|
delete min;
|
2001-05-20 17:03:25 +02:00
|
|
|
delete typ;
|
2000-07-29 19:58:20 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (min_typ_max_warn > 0) {
|
|
|
|
|
cerr << res->get_line() << ": warning: choosing ";
|
|
|
|
|
switch (min_typ_max_flag) {
|
|
|
|
|
case MIN:
|
|
|
|
|
cerr << "min";
|
|
|
|
|
break;
|
|
|
|
|
case TYP:
|
|
|
|
|
cerr << "typ";
|
|
|
|
|
break;
|
|
|
|
|
case MAX:
|
|
|
|
|
cerr << "max";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cerr << " expression." << endl;
|
|
|
|
|
min_typ_max_warn -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-10 03:03:18 +02:00
|
|
|
void pform_make_udp(const char*name, list<string>*parms,
|
1999-06-12 22:35:27 +02:00
|
|
|
svector<PWire*>*decl, list<string>*table,
|
1998-11-25 03:35:53 +01:00
|
|
|
Statement*init_expr)
|
|
|
|
|
{
|
|
|
|
|
assert(parms->size() > 0);
|
|
|
|
|
|
|
|
|
|
/* Put the declarations into a map, so that I can check them
|
1999-06-17 07:34:42 +02:00
|
|
|
off with the parameters in the list. If the port is already
|
|
|
|
|
in the map, merge the port type. I will rebuild a list
|
1998-11-25 03:35:53 +01:00
|
|
|
of parameters for the PUdp object. */
|
|
|
|
|
map<string,PWire*> defs;
|
1999-06-17 07:34:42 +02:00
|
|
|
for (unsigned idx = 0 ; idx < decl->count() ; idx += 1) {
|
|
|
|
|
|
|
|
|
|
string pname = (*decl)[idx]->name();
|
|
|
|
|
PWire*cur = defs[pname];
|
|
|
|
|
if (PWire*cur = defs[pname]) {
|
1999-06-21 03:02:16 +02:00
|
|
|
bool rc = true;
|
|
|
|
|
assert((*decl)[idx]);
|
|
|
|
|
if ((*decl)[idx]->get_port_type() != NetNet::PIMPLICIT) {
|
|
|
|
|
rc = cur->set_port_type((*decl)[idx]->get_port_type());
|
|
|
|
|
assert(rc);
|
|
|
|
|
}
|
|
|
|
|
if ((*decl)[idx]->get_wire_type() != NetNet::IMPLICIT) {
|
|
|
|
|
rc = cur->set_wire_type((*decl)[idx]->get_wire_type());
|
|
|
|
|
assert(rc);
|
|
|
|
|
}
|
1999-06-17 07:34:42 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
defs[pname] = (*decl)[idx];
|
1998-11-25 03:35:53 +01:00
|
|
|
}
|
1999-06-17 07:34:42 +02:00
|
|
|
}
|
1998-11-25 03:35:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Put the parameters into a vector of wire descriptions. Look
|
|
|
|
|
in the map for the definitions of the name. */
|
1999-06-15 05:44:53 +02:00
|
|
|
svector<PWire*> pins (parms->size());
|
1998-11-25 03:35:53 +01:00
|
|
|
{ list<string>::iterator cur;
|
|
|
|
|
unsigned idx;
|
|
|
|
|
for (cur = parms->begin(), idx = 0
|
|
|
|
|
; cur != parms->end()
|
|
|
|
|
; idx++, cur++) {
|
|
|
|
|
pins[idx] = defs[*cur];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check that the output is an output and the inputs are
|
|
|
|
|
inputs. I can also make sure that only the single output is
|
|
|
|
|
declared a register, if anything. */
|
1999-06-15 05:44:53 +02:00
|
|
|
assert(pins.count() > 0);
|
1998-11-25 03:35:53 +01:00
|
|
|
assert(pins[0]);
|
1999-06-17 07:34:42 +02:00
|
|
|
assert(pins[0]->get_port_type() == NetNet::POUTPUT);
|
1999-06-15 05:44:53 +02:00
|
|
|
for (unsigned idx = 1 ; idx < pins.count() ; idx += 1) {
|
1998-11-25 03:35:53 +01:00
|
|
|
assert(pins[idx]);
|
1999-06-17 07:34:42 +02:00
|
|
|
assert(pins[idx]->get_port_type() == NetNet::PINPUT);
|
|
|
|
|
assert(pins[idx]->get_wire_type() != NetNet::REG);
|
1998-11-25 03:35:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Interpret and check the table entry strings, to make sure
|
|
|
|
|
they correspond to the inputs, output and output type. Make
|
|
|
|
|
up vectors for the fully interpreted result that can be
|
|
|
|
|
placed in the PUdp object. */
|
1999-06-15 05:44:53 +02:00
|
|
|
svector<string> input (table->size());
|
|
|
|
|
svector<char> current (table->size());
|
|
|
|
|
svector<char> output (table->size());
|
1998-11-25 03:35:53 +01:00
|
|
|
{ unsigned idx = 0;
|
|
|
|
|
for (list<string>::iterator cur = table->begin()
|
|
|
|
|
; cur != table->end()
|
|
|
|
|
; cur ++, idx += 1) {
|
|
|
|
|
string tmp = *cur;
|
1999-06-15 05:44:53 +02:00
|
|
|
assert(tmp.find(':') == (pins.count() - 1));
|
1998-11-25 03:35:53 +01:00
|
|
|
|
1999-06-15 05:44:53 +02:00
|
|
|
input[idx] = tmp.substr(0, pins.count()-1);
|
|
|
|
|
tmp = tmp.substr(pins.count()-1);
|
1998-11-25 03:35:53 +01:00
|
|
|
|
1999-06-17 07:34:42 +02:00
|
|
|
if (pins[0]->get_wire_type() == NetNet::REG) {
|
1998-11-25 03:35:53 +01:00
|
|
|
assert(tmp[0] == ':');
|
|
|
|
|
assert(tmp.size() == 4);
|
|
|
|
|
current[idx] = tmp[1];
|
|
|
|
|
tmp = tmp.substr(2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(tmp[0] == ':');
|
|
|
|
|
assert(tmp.size() == 2);
|
|
|
|
|
output[idx] = tmp[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Verify the "initial" statement, if present, to be sure that
|
|
|
|
|
it only assignes to the output and the output is
|
|
|
|
|
registered. Then save the initial value that I get. */
|
|
|
|
|
verinum::V init = verinum::Vx;
|
|
|
|
|
if (init_expr) {
|
|
|
|
|
// XXXX
|
1999-06-17 07:34:42 +02:00
|
|
|
assert(pins[0]->get_wire_type() == NetNet::REG);
|
1998-11-25 03:35:53 +01:00
|
|
|
|
|
|
|
|
PAssign*pa = dynamic_cast<PAssign*>(init_expr);
|
|
|
|
|
assert(pa);
|
|
|
|
|
|
1999-05-10 02:16:57 +02:00
|
|
|
const PEIdent*id = dynamic_cast<const PEIdent*>(pa->lval());
|
|
|
|
|
assert(id);
|
|
|
|
|
|
1998-11-25 03:35:53 +01:00
|
|
|
// XXXX
|
1999-06-17 07:34:42 +02:00
|
|
|
assert(id->name() == pins[0]->name());
|
1998-11-25 03:35:53 +01:00
|
|
|
|
1999-06-14 01:51:16 +02:00
|
|
|
const PENumber*np = dynamic_cast<const PENumber*>(pa->rval());
|
1998-11-25 03:35:53 +01:00
|
|
|
assert(np);
|
|
|
|
|
|
|
|
|
|
init = np->value()[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Put the primitive into the primitives table
|
1999-07-10 03:03:18 +02:00
|
|
|
if (vl_primitives[name]) {
|
1998-11-25 03:35:53 +01:00
|
|
|
VLerror("UDP primitive already exists.");
|
|
|
|
|
|
|
|
|
|
} else {
|
1999-07-10 03:03:18 +02:00
|
|
|
PUdp*udp = new PUdp(name, parms->size());
|
1998-11-25 03:35:53 +01:00
|
|
|
|
|
|
|
|
// Detect sequential udp.
|
1999-06-17 07:34:42 +02:00
|
|
|
if (pins[0]->get_wire_type() == NetNet::REG)
|
1998-11-25 03:35:53 +01:00
|
|
|
udp->sequential = true;
|
|
|
|
|
|
|
|
|
|
// Make the port list for the UDP
|
1999-06-15 05:44:53 +02:00
|
|
|
for (unsigned idx = 0 ; idx < pins.count() ; idx += 1)
|
1999-06-17 07:34:42 +02:00
|
|
|
udp->ports[idx] = pins[idx]->name();
|
1998-11-25 03:35:53 +01:00
|
|
|
|
|
|
|
|
udp->tinput = input;
|
|
|
|
|
udp->tcurrent = current;
|
|
|
|
|
udp->toutput = output;
|
|
|
|
|
udp->initial = init;
|
|
|
|
|
|
1999-07-10 03:03:18 +02:00
|
|
|
vl_primitives[name] = udp;
|
1998-11-25 03:35:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Delete the excess tables and lists from the parser. */
|
|
|
|
|
delete parms;
|
|
|
|
|
delete decl;
|
|
|
|
|
delete table;
|
|
|
|
|
delete init_expr;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
/*
|
|
|
|
|
* This function attaches a range to a given name. The function is
|
|
|
|
|
* only called by the parser within the scope of the net declaration,
|
|
|
|
|
* and the name that I receive only has the tail component.
|
|
|
|
|
*/
|
2000-12-11 01:31:43 +01:00
|
|
|
static void pform_set_net_range(const char*name,
|
|
|
|
|
const svector<PExpr*>*range,
|
|
|
|
|
bool signed_flag)
|
2000-10-31 18:00:04 +01:00
|
|
|
{
|
|
|
|
|
assert(range);
|
|
|
|
|
assert(range->count() == 2);
|
|
|
|
|
|
|
|
|
|
PWire*cur = pform_cur_module->get_wire(scoped_name(name));
|
|
|
|
|
if (cur == 0) {
|
2001-01-10 06:32:44 +01:00
|
|
|
VLerror("error: name is not a valid net.");
|
2000-10-31 18:00:04 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert((*range)[0]);
|
|
|
|
|
assert((*range)[1]);
|
|
|
|
|
cur->set_range((*range)[0], (*range)[1]);
|
2000-12-11 01:31:43 +01:00
|
|
|
cur->set_signed(signed_flag);
|
2000-10-31 18:00:04 +01:00
|
|
|
}
|
|
|
|
|
|
2000-12-11 01:31:43 +01:00
|
|
|
void pform_set_net_range(list<char*>*names,
|
|
|
|
|
svector<PExpr*>*range,
|
|
|
|
|
bool signed_flag)
|
2000-10-31 18:00:04 +01:00
|
|
|
{
|
|
|
|
|
assert(range->count() == 2);
|
|
|
|
|
|
|
|
|
|
for (list<char*>::iterator cur = names->begin()
|
|
|
|
|
; cur != names->end()
|
|
|
|
|
; cur ++ ) {
|
|
|
|
|
char*txt = *cur;
|
2000-12-11 01:31:43 +01:00
|
|
|
pform_set_net_range(txt, range, signed_flag);
|
2000-10-31 18:00:04 +01:00
|
|
|
free(txt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete names;
|
|
|
|
|
delete range;
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-01 21:31:57 +02:00
|
|
|
/*
|
|
|
|
|
* This is invoked to make a named event. This is the declaration of
|
|
|
|
|
* the event, and not necessarily the use of it.
|
|
|
|
|
*/
|
2000-11-30 18:31:42 +01:00
|
|
|
static void pform_make_event(const char*name, const char*fn, unsigned ln)
|
2000-04-01 21:31:57 +02:00
|
|
|
{
|
|
|
|
|
PEvent*event = new PEvent(name);
|
|
|
|
|
event->set_file(fn);
|
|
|
|
|
event->set_lineno(ln);
|
|
|
|
|
pform_cur_module->events[name] = event;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-30 18:31:42 +01:00
|
|
|
void pform_make_events(list<char*>*names, const char*fn, unsigned ln)
|
2000-04-01 21:31:57 +02:00
|
|
|
{
|
2000-10-31 18:00:04 +01:00
|
|
|
list<char*>::iterator cur;
|
|
|
|
|
for (cur = names->begin() ; cur != names->end() ; cur++) {
|
|
|
|
|
char*txt = *cur;
|
|
|
|
|
pform_make_event(txt, fn, ln);
|
|
|
|
|
free(txt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete names;
|
2000-04-01 21:31:57 +02:00
|
|
|
}
|
|
|
|
|
|
1999-02-15 03:06:15 +01:00
|
|
|
/*
|
|
|
|
|
* pform_makegates is called when a list of gates (with the same type)
|
|
|
|
|
* are ready to be instantiated. The function runs through the list of
|
1999-05-29 04:36:17 +02:00
|
|
|
* gates and calls the pform_makegate function to make the individual gate.
|
1999-02-15 03:06:15 +01:00
|
|
|
*/
|
1998-11-04 00:28:49 +01:00
|
|
|
void pform_makegate(PGBuiltin::Type type,
|
2000-05-08 07:30:19 +02:00
|
|
|
struct str_pair_t str,
|
1999-08-01 18:34:50 +02:00
|
|
|
svector<PExpr*>* delay,
|
1999-02-15 03:06:15 +01:00
|
|
|
const lgate&info)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-05-29 04:36:17 +02:00
|
|
|
if (info.parms_by_name) {
|
|
|
|
|
cerr << info.file << ":" << info.lineno << ": Gates do not "
|
|
|
|
|
"have port names." << endl;
|
|
|
|
|
error_count += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-01 18:34:50 +02:00
|
|
|
PGBuiltin*cur = new PGBuiltin(type, info.name, info.parms, delay);
|
1999-02-15 03:06:15 +01:00
|
|
|
if (info.range[0])
|
|
|
|
|
cur->set_range(info.range[0], info.range[1]);
|
|
|
|
|
|
2000-05-08 07:30:19 +02:00
|
|
|
cur->strength0(str.str0);
|
|
|
|
|
cur->strength1(str.str1);
|
1999-02-15 03:06:15 +01:00
|
|
|
cur->set_file(info.file);
|
|
|
|
|
cur->set_lineno(info.lineno);
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->add_gate(cur);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pform_makegates(PGBuiltin::Type type,
|
2000-05-08 07:30:19 +02:00
|
|
|
struct str_pair_t str,
|
|
|
|
|
svector<PExpr*>*delay,
|
|
|
|
|
svector<lgate>*gates)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-05-31 17:45:59 +02:00
|
|
|
for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) {
|
2000-05-08 07:30:19 +02:00
|
|
|
pform_makegate(type, str, delay, (*gates)[idx]);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete gates;
|
|
|
|
|
}
|
|
|
|
|
|
1999-05-29 04:36:17 +02:00
|
|
|
/*
|
|
|
|
|
* A module is different from a gate in that there are different
|
2000-02-18 06:15:02 +01:00
|
|
|
* constraints, and sometimes different syntax. The X_modgate
|
|
|
|
|
* functions handle the instantaions of modules (and UDP objects) by
|
|
|
|
|
* making PGModule objects.
|
1999-05-29 04:36:17 +02:00
|
|
|
*/
|
1999-05-10 02:16:57 +02:00
|
|
|
static void pform_make_modgate(const string&type,
|
|
|
|
|
const string&name,
|
2000-01-09 06:50:48 +01:00
|
|
|
struct parmvalue_t*overrides,
|
1999-05-29 04:36:17 +02:00
|
|
|
svector<PExpr*>*wires,
|
2000-02-18 06:15:02 +01:00
|
|
|
PExpr*msb, PExpr*lsb,
|
2000-11-30 18:31:42 +01:00
|
|
|
const char*fn, unsigned ln)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-01-09 06:50:48 +01:00
|
|
|
PGModule*cur = new PGModule(type, name, wires);
|
1999-01-25 06:45:56 +01:00
|
|
|
cur->set_file(fn);
|
|
|
|
|
cur->set_lineno(ln);
|
2000-02-18 06:15:02 +01:00
|
|
|
cur->set_range(msb,lsb);
|
2000-01-09 06:50:48 +01:00
|
|
|
|
|
|
|
|
if (overrides && overrides->by_name) {
|
|
|
|
|
unsigned cnt = overrides->by_name->count();
|
|
|
|
|
named<PExpr*>*byname = new named<PExpr*>[cnt];
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < cnt ; idx += 1) {
|
|
|
|
|
portname_t*curp = (*overrides->by_name)[idx];
|
|
|
|
|
byname[idx].name = curp->name;
|
|
|
|
|
byname[idx].parm = curp->parm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur->set_parameters(byname, cnt);
|
|
|
|
|
|
|
|
|
|
} else if (overrides && overrides->by_order) {
|
|
|
|
|
cur->set_parameters(overrides->by_order);
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->add_gate(cur);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-05-29 04:36:17 +02:00
|
|
|
static void pform_make_modgate(const string&type,
|
|
|
|
|
const string&name,
|
2000-01-09 06:50:48 +01:00
|
|
|
struct parmvalue_t*overrides,
|
1999-05-29 04:36:17 +02:00
|
|
|
svector<portname_t*>*bind,
|
2000-02-18 06:15:02 +01:00
|
|
|
PExpr*msb, PExpr*lsb,
|
2000-11-30 18:31:42 +01:00
|
|
|
const char*fn, unsigned ln)
|
1999-05-29 04:36:17 +02:00
|
|
|
{
|
|
|
|
|
unsigned npins = bind->count();
|
2000-01-09 06:50:48 +01:00
|
|
|
named<PExpr*>*pins = new named<PExpr*>[npins];
|
1999-05-29 04:36:17 +02:00
|
|
|
for (unsigned idx = 0 ; idx < npins ; idx += 1) {
|
|
|
|
|
portname_t*curp = (*bind)[idx];
|
|
|
|
|
pins[idx].name = curp->name;
|
|
|
|
|
pins[idx].parm = curp->parm;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-09 06:50:48 +01:00
|
|
|
PGModule*cur = new PGModule(type, name, pins, npins);
|
1999-05-29 04:36:17 +02:00
|
|
|
cur->set_file(fn);
|
|
|
|
|
cur->set_lineno(ln);
|
2000-02-18 06:15:02 +01:00
|
|
|
cur->set_range(msb,lsb);
|
2000-01-09 06:50:48 +01:00
|
|
|
|
|
|
|
|
if (overrides && overrides->by_name) {
|
|
|
|
|
unsigned cnt = overrides->by_name->count();
|
|
|
|
|
named<PExpr*>*byname = new named<PExpr*>[cnt];
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < cnt ; idx += 1) {
|
|
|
|
|
portname_t*curp = (*overrides->by_name)[idx];
|
|
|
|
|
byname[idx].name = curp->name;
|
|
|
|
|
byname[idx].parm = curp->parm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur->set_parameters(byname, cnt);
|
|
|
|
|
|
|
|
|
|
} else if (overrides && overrides->by_order) {
|
|
|
|
|
|
|
|
|
|
cur->set_parameters(overrides->by_order);
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->add_gate(cur);
|
1999-05-29 04:36:17 +02:00
|
|
|
}
|
|
|
|
|
|
1999-08-23 18:48:39 +02:00
|
|
|
void pform_make_modgates(const string&type,
|
2000-01-09 06:50:48 +01:00
|
|
|
struct parmvalue_t*overrides,
|
1999-08-23 18:48:39 +02:00
|
|
|
svector<lgate>*gates)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2001-01-15 01:47:01 +01:00
|
|
|
|
1999-05-06 06:37:17 +02:00
|
|
|
for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) {
|
|
|
|
|
lgate cur = (*gates)[idx];
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-05-29 04:36:17 +02:00
|
|
|
if (cur.parms_by_name) {
|
2000-02-18 06:15:02 +01:00
|
|
|
pform_make_modgate(type, cur.name, overrides,
|
|
|
|
|
cur.parms_by_name,
|
|
|
|
|
cur.range[0], cur.range[1],
|
1999-05-29 04:36:17 +02:00
|
|
|
cur.file, cur.lineno);
|
|
|
|
|
|
|
|
|
|
} else if (cur.parms) {
|
2000-05-23 18:03:13 +02:00
|
|
|
|
|
|
|
|
/* If there are no parameters, the parser will be
|
|
|
|
|
tricked into thinking it is one empty
|
|
|
|
|
parameter. This fixes that. */
|
|
|
|
|
if ((cur.parms->count() == 1) && (cur.parms[0][0] == 0)) {
|
|
|
|
|
delete cur.parms;
|
|
|
|
|
cur.parms = new svector<PExpr*>(0);
|
|
|
|
|
}
|
2000-02-18 06:15:02 +01:00
|
|
|
pform_make_modgate(type, cur.name, overrides,
|
|
|
|
|
cur.parms,
|
|
|
|
|
cur.range[0], cur.range[1],
|
|
|
|
|
cur.file, cur.lineno);
|
2000-05-23 18:03:13 +02:00
|
|
|
|
1999-05-10 02:16:57 +02:00
|
|
|
} else {
|
1999-05-29 04:36:17 +02:00
|
|
|
svector<PExpr*>*wires = new svector<PExpr*>(0);
|
2000-02-18 06:15:02 +01:00
|
|
|
pform_make_modgate(type, cur.name, overrides,
|
|
|
|
|
wires,
|
|
|
|
|
cur.range[0], cur.range[1],
|
|
|
|
|
cur.file, cur.lineno);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete gates;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-01 18:34:50 +02:00
|
|
|
PGAssign* pform_make_pgassign(PExpr*lval, PExpr*rval,
|
2000-05-06 17:41:56 +02:00
|
|
|
svector<PExpr*>*del,
|
|
|
|
|
struct str_pair_t str)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-05-29 04:36:17 +02:00
|
|
|
svector<PExpr*>*wires = new svector<PExpr*>(2);
|
|
|
|
|
(*wires)[0] = lval;
|
|
|
|
|
(*wires)[1] = rval;
|
1999-08-01 18:34:50 +02:00
|
|
|
|
|
|
|
|
PGAssign*cur;
|
|
|
|
|
|
|
|
|
|
if (del == 0)
|
|
|
|
|
cur = new PGAssign(wires);
|
|
|
|
|
else
|
|
|
|
|
cur = new PGAssign(wires, del);
|
|
|
|
|
|
2000-05-06 17:41:56 +02:00
|
|
|
cur->strength0(str.str0);
|
|
|
|
|
cur->strength1(str.str1);
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->add_gate(cur);
|
1999-05-20 06:31:45 +02:00
|
|
|
return cur;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-08-27 17:08:37 +02:00
|
|
|
void pform_make_pgassign_list(svector<PExpr*>*alist,
|
|
|
|
|
svector<PExpr*>*del,
|
2000-05-06 17:41:56 +02:00
|
|
|
struct str_pair_t str,
|
2000-11-30 18:31:42 +01:00
|
|
|
const char* fn,
|
1999-08-27 17:08:37 +02:00
|
|
|
unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
PGAssign*tmp;
|
|
|
|
|
for (unsigned idx = 0 ; idx < alist->count()/2 ; idx += 1) {
|
|
|
|
|
tmp = pform_make_pgassign((*alist)[2*idx],
|
2000-05-06 17:41:56 +02:00
|
|
|
(*alist)[2*idx+1],
|
|
|
|
|
del, str);
|
2000-11-30 18:31:42 +01:00
|
|
|
tmp->set_file(fn);
|
1999-08-27 17:08:37 +02:00
|
|
|
tmp->set_lineno(lineno);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-12-30 20:06:14 +01:00
|
|
|
/*
|
|
|
|
|
* this function makes the initial assignment to a register as given
|
|
|
|
|
* in the source. It handles the case where a reg variable is assigned
|
|
|
|
|
* where it it declared:
|
|
|
|
|
*
|
|
|
|
|
* reg foo = <expr>;
|
|
|
|
|
*
|
|
|
|
|
* This is equivilent to the combination of statements:
|
|
|
|
|
*
|
|
|
|
|
* reg foo;
|
|
|
|
|
* initital foo = <expr>;
|
|
|
|
|
*
|
|
|
|
|
* and that is how it is parsed. This syntax is not part of the
|
|
|
|
|
* IEEE1364-1994 standard, but is approved by OVI as enhancement
|
|
|
|
|
* BTF-B14.
|
|
|
|
|
*/
|
|
|
|
|
void pform_make_reginit(const struct vlltype&li,
|
|
|
|
|
const string&name, PExpr*expr)
|
|
|
|
|
{
|
|
|
|
|
const string sname = scoped_name(name);
|
|
|
|
|
PWire*cur = pform_cur_module->get_wire(sname);
|
|
|
|
|
if (cur == 0) {
|
|
|
|
|
VLerror(li, "internal error: reginit to non-register?");
|
|
|
|
|
delete expr;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PEIdent*lval = new PEIdent(sname);
|
|
|
|
|
lval->set_file(li.text);
|
|
|
|
|
lval->set_lineno(li.first_line);
|
|
|
|
|
PAssign*ass = new PAssign(lval, expr);
|
|
|
|
|
ass->set_file(li.text);
|
|
|
|
|
ass->set_lineno(li.first_line);
|
|
|
|
|
PProcess*top = new PProcess(PProcess::PR_INITIAL, ass);
|
|
|
|
|
top->set_file(li.text);
|
|
|
|
|
top->set_lineno(li.first_line);
|
|
|
|
|
|
|
|
|
|
pform_cur_module->add_behavior(top);
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-25 04:21:34 +02:00
|
|
|
/*
|
|
|
|
|
* This function makes a single signal (a wire, a reg, etc) as
|
|
|
|
|
* requested by the parser. The name is unscoped, so I attach the
|
|
|
|
|
* current scope to it (with the scoped_name function) and I try to
|
|
|
|
|
* resolve it with an existing PWire in the scope.
|
|
|
|
|
*
|
|
|
|
|
* The wire might already exist because of an implicit declaration in
|
|
|
|
|
* a module port, i.e.:
|
|
|
|
|
*
|
|
|
|
|
* module foo (bar...
|
|
|
|
|
*
|
|
|
|
|
* reg bar;
|
|
|
|
|
*
|
|
|
|
|
* The output (or other port direction indicator) may or may not have
|
|
|
|
|
* been seen already, so I do not do any cheching with it yet. But I
|
|
|
|
|
* do check to see if the name has already been declared, as this
|
|
|
|
|
* function is called for every declaration.
|
|
|
|
|
*/
|
1999-06-24 06:24:18 +02:00
|
|
|
void pform_makewire(const vlltype&li, const string&nm,
|
1999-06-02 17:38:46 +02:00
|
|
|
NetNet::Type type)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-06-24 06:24:18 +02:00
|
|
|
const string name = scoped_name(nm);
|
1999-07-03 04:12:51 +02:00
|
|
|
PWire*cur = pform_cur_module->get_wire(name);
|
1998-11-04 00:28:49 +01:00
|
|
|
if (cur) {
|
2001-02-17 06:15:33 +01:00
|
|
|
if ((cur->get_wire_type() != NetNet::IMPLICIT)
|
|
|
|
|
&& (cur->get_wire_type() != NetNet::IMPLICIT_REG)) {
|
1999-01-25 06:45:56 +01:00
|
|
|
strstream msg;
|
1999-06-02 17:38:46 +02:00
|
|
|
msg << name << " previously defined at " <<
|
1999-12-30 20:06:14 +01:00
|
|
|
cur->get_line() << "." << ends;
|
1999-01-25 06:45:56 +01:00
|
|
|
VLerror(msg.str());
|
1999-06-17 07:34:42 +02:00
|
|
|
} else {
|
|
|
|
|
bool rc = cur->set_wire_type(type);
|
2001-02-17 06:15:33 +01:00
|
|
|
if (rc == false) {
|
|
|
|
|
strstream msg;
|
|
|
|
|
msg << name << " definition conflicts with "
|
|
|
|
|
<< "definition at " << cur->get_line()
|
|
|
|
|
<< "." << ends;
|
|
|
|
|
VLerror(msg.str());
|
|
|
|
|
}
|
1999-01-25 06:45:56 +01:00
|
|
|
}
|
2001-05-25 04:21:34 +02:00
|
|
|
|
|
|
|
|
cur->set_file(li.text);
|
|
|
|
|
cur->set_lineno(li.first_line);
|
1998-11-04 00:28:49 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-17 07:34:42 +02:00
|
|
|
cur = new PWire(name, type, NetNet::NOT_A_PORT);
|
1999-06-02 17:38:46 +02:00
|
|
|
cur->set_file(li.text);
|
|
|
|
|
cur->set_lineno(li.first_line);
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->add_wire(cur);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
void pform_makewire(const vlltype&li,
|
|
|
|
|
svector<PExpr*>*range,
|
|
|
|
|
list<char*>*names,
|
1999-06-02 17:38:46 +02:00
|
|
|
NetNet::Type type)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-10-31 18:00:04 +01:00
|
|
|
for (list<char*>::iterator cur = names->begin()
|
1998-11-04 00:28:49 +01:00
|
|
|
; cur != names->end()
|
2000-10-31 18:00:04 +01:00
|
|
|
; cur ++ ) {
|
|
|
|
|
char*txt = *cur;
|
|
|
|
|
pform_makewire(li, txt, type);
|
|
|
|
|
if (range)
|
2000-12-11 01:31:43 +01:00
|
|
|
pform_set_net_range(txt, range, false);
|
2000-10-31 18:00:04 +01:00
|
|
|
free(txt);
|
|
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
delete names;
|
|
|
|
|
if (range)
|
|
|
|
|
delete range;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2000-05-16 06:05:15 +02:00
|
|
|
void pform_set_port_type(const string&nm, NetNet::PortType pt)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-05-16 06:05:15 +02:00
|
|
|
const string name = scoped_name(nm);
|
1999-07-03 04:12:51 +02:00
|
|
|
PWire*cur = pform_cur_module->get_wire(name);
|
1998-11-04 00:28:49 +01:00
|
|
|
if (cur == 0) {
|
2000-05-16 06:05:15 +02:00
|
|
|
cur = new PWire(name, NetNet::IMPLICIT, pt);
|
|
|
|
|
pform_cur_module->add_wire(cur);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-06-17 07:34:42 +02:00
|
|
|
if (! cur->set_port_type(pt))
|
1998-11-04 00:28:49 +01:00
|
|
|
VLerror("error setting port direction.");
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-24 04:11:19 +02:00
|
|
|
/*
|
|
|
|
|
* This function is called by the parser to create task ports. The
|
|
|
|
|
* resulting wire (which should be a register) is put into a list to
|
|
|
|
|
* be packed into the task parameter list.
|
|
|
|
|
*
|
|
|
|
|
* It is possible that the wire (er, register) was already created,
|
|
|
|
|
* but we know that if the name matches it is a part of the current
|
|
|
|
|
* task, so in that case I just assign direction to it.
|
|
|
|
|
*
|
1999-07-31 21:14:47 +02:00
|
|
|
* The following example demonstrates some of the issues:
|
1999-07-24 04:11:19 +02:00
|
|
|
*
|
|
|
|
|
* task foo;
|
|
|
|
|
* input a;
|
|
|
|
|
* reg a, b;
|
|
|
|
|
* input b;
|
|
|
|
|
* [...]
|
|
|
|
|
* endtask
|
|
|
|
|
*
|
|
|
|
|
* This function is called when the parser matches the "input a" and
|
|
|
|
|
* the "input b" statements. For ``a'', this function is called before
|
|
|
|
|
* the wire is declared as a register, so I create the foo.a
|
|
|
|
|
* wire. For ``b'', I will find that there is already a foo.b and I
|
|
|
|
|
* just set the port direction. In either case, the ``reg a, b''
|
1999-07-31 21:14:47 +02:00
|
|
|
* statement is caught by the block_item non-terminal and processed
|
|
|
|
|
* there.
|
|
|
|
|
*
|
|
|
|
|
* Ports are implicitly type reg, because it must be possible for the
|
|
|
|
|
* port to act as an l-value in a procedural assignment. It is obvious
|
|
|
|
|
* for output and inout ports that the type is reg, because the task
|
|
|
|
|
* only contains behavior (no structure) to a procedural assignment is
|
1999-07-31 21:15:21 +02:00
|
|
|
* the *only* way to affect the output. It is less obvious for input
|
1999-07-31 21:14:47 +02:00
|
|
|
* ports, but in practice an input port receives its value as if by a
|
|
|
|
|
* procedural assignment from the calling behavior.
|
|
|
|
|
*
|
|
|
|
|
* This function also handles the input ports of function
|
|
|
|
|
* definitions. Input ports to function definitions have the same
|
|
|
|
|
* constraints as those of tasks, so this works fine. Functions have
|
|
|
|
|
* no output or inout ports.
|
1999-07-24 04:11:19 +02:00
|
|
|
*/
|
|
|
|
|
svector<PWire*>*pform_make_task_ports(NetNet::PortType pt,
|
2000-10-31 18:00:04 +01:00
|
|
|
svector<PExpr*>*range,
|
|
|
|
|
list<char*>*names,
|
2000-11-30 18:31:42 +01:00
|
|
|
const char* file,
|
1999-09-10 07:02:09 +02:00
|
|
|
unsigned lineno)
|
1999-07-24 04:11:19 +02:00
|
|
|
{
|
1999-09-10 07:02:09 +02:00
|
|
|
assert(names);
|
1999-07-24 04:11:19 +02:00
|
|
|
svector<PWire*>*res = new svector<PWire*>(0);
|
2000-10-31 18:00:04 +01:00
|
|
|
for (list<char*>::iterator cur = names->begin()
|
1999-07-24 04:11:19 +02:00
|
|
|
; cur != names->end() ; cur ++ ) {
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
char*txt = *cur;
|
|
|
|
|
string name = scoped_name(txt);
|
1999-07-24 04:11:19 +02:00
|
|
|
|
|
|
|
|
/* Look for a preexisting wire. If it exists, set the
|
|
|
|
|
port direction. If not, create it. */
|
|
|
|
|
PWire*curw = pform_cur_module->get_wire(name);
|
|
|
|
|
if (curw) {
|
|
|
|
|
curw->set_port_type(pt);
|
|
|
|
|
} else {
|
|
|
|
|
curw = new PWire(name, NetNet::IMPLICIT_REG, pt);
|
1999-09-10 07:02:09 +02:00
|
|
|
curw->set_file(file);
|
|
|
|
|
curw->set_lineno(lineno);
|
1999-07-24 04:11:19 +02:00
|
|
|
pform_cur_module->add_wire(curw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there is a range involved, it needs to be set. */
|
|
|
|
|
if (range)
|
|
|
|
|
curw->set_range((*range)[0], (*range)[1]);
|
|
|
|
|
|
|
|
|
|
svector<PWire*>*tmp = new svector<PWire*>(*res, curw);
|
2000-10-31 18:00:04 +01:00
|
|
|
|
|
|
|
|
free(txt);
|
1999-07-24 04:11:19 +02:00
|
|
|
delete res;
|
|
|
|
|
res = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
if (range)
|
|
|
|
|
delete range;
|
|
|
|
|
delete names;
|
1999-07-24 04:11:19 +02:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
void pform_set_task(const string&name, PTask*task)
|
|
|
|
|
{
|
|
|
|
|
pform_cur_module->add_task(name, task);
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-01 00:38:29 +02:00
|
|
|
/*
|
|
|
|
|
* This function is called to fill out the definition of the function
|
|
|
|
|
* with the trappings that are discovered after the basic function
|
|
|
|
|
* name is parsed.
|
|
|
|
|
*/
|
1999-08-26 00:22:41 +02:00
|
|
|
void pform_set_function(const string&name, svector<PExpr*>*ra, PFunction *func)
|
1999-07-31 21:14:47 +02:00
|
|
|
{
|
1999-08-26 00:22:41 +02:00
|
|
|
PWire*out = new PWire(name+"."+name, NetNet::REG, NetNet::POUTPUT);
|
|
|
|
|
if (ra) {
|
|
|
|
|
assert(ra->count() == 2);
|
|
|
|
|
out->set_range((*ra)[0], (*ra)[1]);
|
|
|
|
|
delete ra;
|
|
|
|
|
}
|
|
|
|
|
pform_cur_module->add_wire(out);
|
|
|
|
|
func->set_output(out);
|
1999-07-31 21:14:47 +02:00
|
|
|
pform_cur_module->add_function(name, func);
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-23 01:20:22 +01:00
|
|
|
void pform_set_attrib(const string&name, const string&key, const string&value)
|
|
|
|
|
{
|
1999-07-03 04:12:51 +02:00
|
|
|
PWire*cur = pform_cur_module->get_wire(name);
|
1999-12-11 06:45:41 +01:00
|
|
|
if (PWire*cur = pform_cur_module->get_wire(name)) {
|
|
|
|
|
cur->attributes[key] = value;
|
|
|
|
|
|
|
|
|
|
} else if (PGate*cur = pform_cur_module->get_gate(name)) {
|
|
|
|
|
cur->attributes[key] = value;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
VLerror("Unable to match name for setting attribute.");
|
|
|
|
|
|
|
|
|
|
}
|
1998-11-23 01:20:22 +01:00
|
|
|
}
|
|
|
|
|
|
1998-12-01 01:42:13 +01:00
|
|
|
/*
|
|
|
|
|
* Set the attribute of a TYPE. This is different from an object in
|
|
|
|
|
* that this applies to every instantiation of the given type.
|
|
|
|
|
*/
|
|
|
|
|
void pform_set_type_attrib(const string&name, const string&key,
|
|
|
|
|
const string&value)
|
|
|
|
|
{
|
|
|
|
|
map<string,PUdp*>::const_iterator udp = vl_primitives.find(name);
|
|
|
|
|
if (udp == vl_primitives.end()) {
|
|
|
|
|
VLerror("type name is not (yet) defined.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*udp).second ->attributes[key] = value;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-17 07:34:42 +02:00
|
|
|
/*
|
|
|
|
|
* This function attaches a memory index range to an existing
|
|
|
|
|
* register. (The named wire must be a register.
|
|
|
|
|
*/
|
1999-04-19 03:59:36 +02:00
|
|
|
void pform_set_reg_idx(const string&name, PExpr*l, PExpr*r)
|
|
|
|
|
{
|
2001-01-10 06:32:44 +01:00
|
|
|
PWire*cur = pform_cur_module->get_wire(scoped_name(name));
|
1999-04-19 03:59:36 +02:00
|
|
|
if (cur == 0) {
|
2001-01-10 06:32:44 +01:00
|
|
|
VLerror("internal error: name is not a valid memory for index.");
|
1999-04-19 03:59:36 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-17 07:34:42 +02:00
|
|
|
cur->set_memory_idx(l, r);
|
1999-04-19 03:59:36 +02:00
|
|
|
}
|
|
|
|
|
|
1999-02-21 18:01:57 +01:00
|
|
|
void pform_set_parameter(const string&name, PExpr*expr)
|
|
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(expr);
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->parameters[name] = expr;
|
1999-08-23 18:48:39 +02:00
|
|
|
pform_cur_module->param_names.push_back(name);
|
1999-02-21 18:01:57 +01:00
|
|
|
}
|
|
|
|
|
|
2000-03-12 18:09:40 +01:00
|
|
|
void pform_set_localparam(const string&name, PExpr*expr)
|
|
|
|
|
{
|
|
|
|
|
assert(expr);
|
|
|
|
|
pform_cur_module->localparams[name] = expr;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
void pform_set_defparam(const string&name, PExpr*expr)
|
|
|
|
|
{
|
|
|
|
|
assert(expr);
|
|
|
|
|
pform_cur_module->defparms[name] = expr;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
void pform_set_port_type(list<char*>*names,
|
|
|
|
|
svector<PExpr*>*range,
|
|
|
|
|
NetNet::PortType pt)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-10-31 18:00:04 +01:00
|
|
|
for (list<char*>::iterator cur = names->begin()
|
1998-11-04 00:28:49 +01:00
|
|
|
; cur != names->end()
|
|
|
|
|
; cur ++ ) {
|
2000-10-31 18:00:04 +01:00
|
|
|
char*txt = *cur;
|
|
|
|
|
pform_set_port_type(txt, pt);
|
|
|
|
|
if (range)
|
2000-12-11 01:31:43 +01:00
|
|
|
pform_set_net_range(txt, range, false);
|
2000-10-31 18:00:04 +01:00
|
|
|
free(txt);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
2000-10-31 18:00:04 +01:00
|
|
|
|
|
|
|
|
delete names;
|
|
|
|
|
if (range)
|
|
|
|
|
delete range;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
static void pform_set_reg_integer(const char*nm)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-09-10 07:02:09 +02:00
|
|
|
string name = scoped_name(nm);
|
1999-07-03 04:12:51 +02:00
|
|
|
PWire*cur = pform_cur_module->get_wire(name);
|
1999-09-15 03:55:06 +02:00
|
|
|
if (cur == 0) {
|
2001-01-06 03:29:35 +01:00
|
|
|
cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT);
|
|
|
|
|
cur->set_signed(true);
|
1999-09-15 03:55:06 +02:00
|
|
|
pform_cur_module->add_wire(cur);
|
|
|
|
|
} else {
|
2001-01-06 03:29:35 +01:00
|
|
|
bool rc = cur->set_wire_type(NetNet::REG);
|
1999-09-15 03:55:06 +02:00
|
|
|
assert(rc);
|
2001-01-06 03:29:35 +01:00
|
|
|
cur->set_signed(true);
|
1999-09-15 03:55:06 +02:00
|
|
|
}
|
1999-06-06 22:45:38 +02:00
|
|
|
assert(cur);
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-06-17 07:34:42 +02:00
|
|
|
cur->set_range(new PENumber(new verinum(INTEGER_WIDTH-1, INTEGER_WIDTH)),
|
|
|
|
|
new PENumber(new verinum(0UL, INTEGER_WIDTH)));
|
2000-12-11 01:31:43 +01:00
|
|
|
cur->set_signed(true);
|
1999-06-06 22:45:38 +02:00
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
void pform_set_reg_integer(list<char*>*names)
|
1999-06-06 22:45:38 +02:00
|
|
|
{
|
2000-10-31 18:00:04 +01:00
|
|
|
for (list<char*>::iterator cur = names->begin()
|
1998-11-04 00:28:49 +01:00
|
|
|
; cur != names->end()
|
|
|
|
|
; cur ++ ) {
|
2000-10-31 18:00:04 +01:00
|
|
|
char*txt = *cur;
|
|
|
|
|
pform_set_reg_integer(txt);
|
|
|
|
|
free(txt);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
2000-10-31 18:00:04 +01:00
|
|
|
delete names;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:49:02 +01:00
|
|
|
static void pform_set_reg_time(const char*nm)
|
|
|
|
|
{
|
|
|
|
|
string name = scoped_name(nm);
|
|
|
|
|
PWire*cur = pform_cur_module->get_wire(name);
|
|
|
|
|
if (cur == 0) {
|
2001-01-06 07:31:58 +01:00
|
|
|
cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT);
|
2000-10-31 18:49:02 +01:00
|
|
|
pform_cur_module->add_wire(cur);
|
|
|
|
|
} else {
|
2001-01-06 07:31:58 +01:00
|
|
|
bool rc = cur->set_wire_type(NetNet::REG);
|
2000-10-31 18:49:02 +01:00
|
|
|
assert(rc);
|
|
|
|
|
}
|
|
|
|
|
assert(cur);
|
|
|
|
|
|
|
|
|
|
cur->set_range(new PENumber(new verinum(TIME_WIDTH-1, INTEGER_WIDTH)),
|
|
|
|
|
new PENumber(new verinum(0UL, INTEGER_WIDTH)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pform_set_reg_time(list<char*>*names)
|
|
|
|
|
{
|
|
|
|
|
for (list<char*>::iterator cur = names->begin()
|
|
|
|
|
; cur != names->end()
|
|
|
|
|
; cur ++ ) {
|
|
|
|
|
char*txt = *cur;
|
|
|
|
|
pform_set_reg_time(txt);
|
|
|
|
|
free(txt);
|
|
|
|
|
}
|
|
|
|
|
delete names;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-31 18:00:04 +01:00
|
|
|
svector<PWire*>* pform_make_udp_input_ports(list<char*>*names)
|
1998-11-25 03:35:53 +01:00
|
|
|
{
|
1999-06-12 22:35:27 +02:00
|
|
|
svector<PWire*>*out = new svector<PWire*>(names->size());
|
1998-11-25 03:35:53 +01:00
|
|
|
|
1999-06-12 22:35:27 +02:00
|
|
|
unsigned idx = 0;
|
2000-10-31 18:00:04 +01:00
|
|
|
for (list<char*>::iterator cur = names->begin()
|
1998-11-25 03:35:53 +01:00
|
|
|
; cur != names->end()
|
|
|
|
|
; cur ++ ) {
|
2000-10-31 18:00:04 +01:00
|
|
|
char*txt = *cur;
|
|
|
|
|
PWire*pp = new PWire(txt, NetNet::IMPLICIT, NetNet::PINPUT);
|
1999-06-12 22:35:27 +02:00
|
|
|
(*out)[idx] = pp;
|
1999-06-21 03:02:16 +02:00
|
|
|
idx += 1;
|
2000-10-31 18:00:04 +01:00
|
|
|
free(txt);
|
1998-11-25 03:35:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete names;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-25 06:45:56 +01:00
|
|
|
PProcess* pform_make_behavior(PProcess::Type type, Statement*st)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
|
|
|
|
PProcess*pp = new PProcess(type, st);
|
1999-07-03 04:12:51 +02:00
|
|
|
pform_cur_module->add_behavior(pp);
|
1999-01-25 06:45:56 +01:00
|
|
|
return pp;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FILE*vl_input = 0;
|
1998-12-09 05:02:47 +01:00
|
|
|
int pform_parse(const char*path, map<string,Module*>&modules,
|
1998-12-01 01:42:13 +01:00
|
|
|
map<string,PUdp*>&prim)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1998-12-09 05:02:47 +01:00
|
|
|
vl_file = path;
|
1999-11-23 02:04:57 +01:00
|
|
|
if (strcmp(path, "-") == 0)
|
|
|
|
|
vl_input = stdin;
|
|
|
|
|
else
|
|
|
|
|
vl_input = fopen(path, "r");
|
1998-12-09 05:02:47 +01:00
|
|
|
if (vl_input == 0) {
|
|
|
|
|
cerr << "Unable to open " <<vl_file << "." << endl;
|
|
|
|
|
return 11;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-07 18:05:05 +01:00
|
|
|
error_count = 0;
|
|
|
|
|
warn_count = 0;
|
1998-11-04 00:28:49 +01:00
|
|
|
int rc = VLparse();
|
1998-11-07 18:05:05 +01:00
|
|
|
if (rc) {
|
|
|
|
|
cerr << "I give up." << endl;
|
2000-09-13 18:32:26 +02:00
|
|
|
error_count += 1;
|
1998-11-07 18:05:05 +01:00
|
|
|
}
|
1998-11-25 03:35:53 +01:00
|
|
|
|
1998-12-01 01:42:13 +01:00
|
|
|
modules = vl_modules;
|
1998-11-25 03:35:53 +01:00
|
|
|
prim = vl_primitives;
|
1998-11-07 18:05:05 +01:00
|
|
|
return error_count;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* $Log: pform.cc,v $
|
2001-10-20 07:21:51 +02:00
|
|
|
* Revision 1.79 2001/10/20 05:21:51 steve
|
|
|
|
|
* Scope/module names are char* instead of string.
|
|
|
|
|
*
|
2001-07-25 05:10:48 +02:00
|
|
|
* Revision 1.78 2001/07/25 03:10:49 steve
|
|
|
|
|
* Create a config.h.in file to hold all the config
|
|
|
|
|
* junk, and support gcc 3.0. (Stephan Boettcher)
|
|
|
|
|
*
|
2001-05-25 04:21:34 +02:00
|
|
|
* Revision 1.77 2001/05/25 02:21:34 steve
|
|
|
|
|
* Detect input and input ports declared as reg.
|
|
|
|
|
*
|
2001-05-20 17:03:25 +02:00
|
|
|
* Revision 1.76 2001/05/20 15:03:25 steve
|
|
|
|
|
* Deleted wrong time when -Tmax is selected.
|
|
|
|
|
*
|
2001-04-29 01:18:08 +02:00
|
|
|
* Revision 1.75 2001/04/28 23:18:08 steve
|
|
|
|
|
* UDP instances need not have user supplied names.
|
|
|
|
|
*
|
2001-02-17 06:15:33 +01:00
|
|
|
* Revision 1.74 2001/02/17 05:15:33 steve
|
|
|
|
|
* Allow task ports to be given real types.
|
|
|
|
|
*
|
2001-01-15 01:47:01 +01:00
|
|
|
* Revision 1.73 2001/01/15 00:47:01 steve
|
|
|
|
|
* Pass scope type information to the target module.
|
|
|
|
|
*
|
2001-01-15 00:04:55 +01:00
|
|
|
* Revision 1.72 2001/01/14 23:04:56 steve
|
|
|
|
|
* Generalize the evaluation of floating point delays, and
|
|
|
|
|
* get it working with delay assignment statements.
|
|
|
|
|
*
|
|
|
|
|
* Allow parameters to be referenced by hierarchical name.
|
|
|
|
|
*
|
2001-01-10 06:32:44 +01:00
|
|
|
* Revision 1.71 2001/01/10 05:32:44 steve
|
|
|
|
|
* Match memories within task scopes. (PR#101)
|
|
|
|
|
*
|
2001-01-06 07:31:58 +01:00
|
|
|
* Revision 1.70 2001/01/06 06:31:59 steve
|
|
|
|
|
* declaration initialization for time variables.
|
|
|
|
|
*
|
2001-01-06 03:29:35 +01:00
|
|
|
* Revision 1.69 2001/01/06 02:29:36 steve
|
|
|
|
|
* Support arrays of integers.
|
|
|
|
|
*
|
2000-12-11 01:31:43 +01:00
|
|
|
* Revision 1.68 2000/12/11 00:31:43 steve
|
|
|
|
|
* Add support for signed reg variables,
|
|
|
|
|
* simulate in t-vvm signed comparisons.
|
|
|
|
|
*
|
2000-11-30 18:31:42 +01:00
|
|
|
* Revision 1.67 2000/11/30 17:31:42 steve
|
|
|
|
|
* Change LineInfo to store const C strings.
|
|
|
|
|
*
|
2000-10-31 18:49:02 +01:00
|
|
|
* Revision 1.66 2000/10/31 17:49:02 steve
|
|
|
|
|
* Support time variables.
|
|
|
|
|
*
|
2000-10-31 18:00:04 +01:00
|
|
|
* Revision 1.65 2000/10/31 17:00:04 steve
|
|
|
|
|
* Remove C++ string from variable lists.
|
|
|
|
|
*
|
2000-09-13 18:32:26 +02:00
|
|
|
* Revision 1.64 2000/09/13 16:32:26 steve
|
|
|
|
|
* Error message for invalid variable list.
|
|
|
|
|
*
|
2000-07-29 19:58:20 +02:00
|
|
|
* Revision 1.63 2000/07/29 17:58:21 steve
|
|
|
|
|
* Introduce min:typ:max support.
|
|
|
|
|
*
|
2000-07-23 00:09:03 +02:00
|
|
|
* Revision 1.62 2000/07/22 22:09:04 steve
|
|
|
|
|
* Parse and elaborate timescale to scopes.
|
|
|
|
|
*
|
2000-05-23 18:03:13 +02:00
|
|
|
* Revision 1.61 2000/05/23 16:03:13 steve
|
|
|
|
|
* Better parsing of expressions lists will empty expressoins.
|
|
|
|
|
*
|
2000-05-16 06:05:15 +02:00
|
|
|
* Revision 1.60 2000/05/16 04:05:16 steve
|
|
|
|
|
* Module ports are really special PEIdent
|
|
|
|
|
* expressions, because a name can be used
|
|
|
|
|
* many places in the port list.
|
|
|
|
|
*
|
2000-05-08 07:30:19 +02:00
|
|
|
* Revision 1.59 2000/05/08 05:30:20 steve
|
|
|
|
|
* Deliver gate output strengths to the netlist.
|
|
|
|
|
*
|
2000-05-06 17:41:56 +02:00
|
|
|
* Revision 1.58 2000/05/06 15:41:57 steve
|
|
|
|
|
* Carry assignment strength to pform.
|
|
|
|
|
*
|
2000-04-01 21:31:57 +02:00
|
|
|
* Revision 1.57 2000/04/01 19:31:57 steve
|
|
|
|
|
* Named events as far as the pform.
|
|
|
|
|
*
|
2000-03-12 18:09:40 +01:00
|
|
|
* Revision 1.56 2000/03/12 17:09:41 steve
|
|
|
|
|
* Support localparam.
|
|
|
|
|
*
|
2000-03-08 05:36:53 +01:00
|
|
|
* Revision 1.55 2000/03/08 04:36:54 steve
|
|
|
|
|
* Redesign the implementation of scopes and parameters.
|
|
|
|
|
* I now generate the scopes and notice the parameters
|
|
|
|
|
* in a separate pass over the pform. Once the scopes
|
|
|
|
|
* are generated, I can process overrides and evalutate
|
|
|
|
|
* paremeters before elaboration begins.
|
|
|
|
|
*
|
2000-02-23 03:56:53 +01:00
|
|
|
* Revision 1.54 2000/02/23 02:56:55 steve
|
|
|
|
|
* Macintosh compilers do not support ident.
|
|
|
|
|
*
|
2000-02-18 06:15:02 +01:00
|
|
|
* Revision 1.53 2000/02/18 05:15:03 steve
|
|
|
|
|
* Catch module instantiation arrays.
|
|
|
|
|
*
|
2000-01-09 06:50:48 +01:00
|
|
|
* Revision 1.52 2000/01/09 05:50:49 steve
|
|
|
|
|
* Support named parameter override lists.
|
|
|
|
|
*
|
2000-01-02 02:59:28 +01:00
|
|
|
* Revision 1.51 2000/01/02 01:59:28 steve
|
|
|
|
|
* Forgot to handle no overrides at all.
|
|
|
|
|
*
|
2000-01-02 00:47:58 +01:00
|
|
|
* Revision 1.50 2000/01/01 23:47:58 steve
|
|
|
|
|
* Fix module parameter override syntax.
|
|
|
|
|
*
|
1999-12-30 20:06:14 +01:00
|
|
|
* Revision 1.49 1999/12/30 19:06:14 steve
|
|
|
|
|
* Support reg initial assignment syntax.
|
|
|
|
|
*
|
1999-12-11 06:45:41 +01:00
|
|
|
* Revision 1.48 1999/12/11 05:45:41 steve
|
|
|
|
|
* Fix support for attaching attributes to primitive gates.
|
|
|
|
|
*
|
1999-11-23 02:04:57 +01:00
|
|
|
* Revision 1.47 1999/11/23 01:04:57 steve
|
|
|
|
|
* A file name of - means standard input.
|
|
|
|
|
*
|
1999-09-30 03:22:37 +02:00
|
|
|
* Revision 1.46 1999/09/30 01:22:37 steve
|
|
|
|
|
* Handle declaration of integers (including scope) in functions.
|
|
|
|
|
*
|
1999-09-21 02:58:33 +02:00
|
|
|
* Revision 1.45 1999/09/21 00:58:33 steve
|
|
|
|
|
* Get scope right when setting the net range.
|
|
|
|
|
*
|
1999-09-17 04:06:25 +02:00
|
|
|
* Revision 1.44 1999/09/17 02:06:26 steve
|
|
|
|
|
* Handle unconnected module ports.
|
|
|
|
|
*
|
1999-09-15 03:55:06 +02:00
|
|
|
* Revision 1.43 1999/09/15 01:55:06 steve
|
|
|
|
|
* Elaborate non-blocking assignment to memories.
|
|
|
|
|
*
|
1999-09-10 07:02:09 +02:00
|
|
|
* Revision 1.42 1999/09/10 05:02:09 steve
|
|
|
|
|
* Handle integers at task parameters.
|
|
|
|
|
*
|
1999-09-01 00:38:29 +02:00
|
|
|
* Revision 1.41 1999/08/31 22:38:29 steve
|
|
|
|
|
* Elaborate and emit to vvm procedural functions.
|
|
|
|
|
*
|
1999-08-27 17:08:37 +02:00
|
|
|
* Revision 1.40 1999/08/27 15:08:37 steve
|
|
|
|
|
* continuous assignment lists.
|
|
|
|
|
*
|
1999-08-26 00:22:41 +02:00
|
|
|
* Revision 1.39 1999/08/25 22:22:41 steve
|
|
|
|
|
* elaborate some aspects of functions.
|
|
|
|
|
*
|
1999-08-23 18:48:39 +02:00
|
|
|
* Revision 1.38 1999/08/23 16:48:39 steve
|
|
|
|
|
* Parameter overrides support from Peter Monta
|
|
|
|
|
* AND and XOR support wide expressions.
|
|
|
|
|
*
|
1999-08-03 06:14:49 +02:00
|
|
|
* Revision 1.37 1999/08/03 04:14:49 steve
|
|
|
|
|
* Parse into pform arbitrarily complex module
|
|
|
|
|
* port declarations.
|
1998-11-04 00:28:49 +01:00
|
|
|
*/
|
|
|
|
|
|