Merge branch 'master' of git://github.com/steveicarus/iverilog into vhdl
This commit is contained in:
commit
181a53d3ed
2
HName.h
2
HName.h
|
|
@ -57,7 +57,7 @@ class hname_t {
|
|||
|
||||
private:
|
||||
perm_string name_;
|
||||
// If the number is anything other then INT_MIN, then this is
|
||||
// If the number is anything other than INT_MIN, then this is
|
||||
// the numeric part of the name. Otherwise, it is not part of
|
||||
// the name at all.
|
||||
int number_;
|
||||
|
|
|
|||
10
Makefile.in
10
Makefile.in
|
|
@ -186,11 +186,14 @@ iverilog-vpi.pdf: iverilog-vpi.ps
|
|||
# version.h file in the source tree (included in snapshots and releases), and
|
||||
# finally use nothing.
|
||||
.PHONY: version.h
|
||||
version.h:
|
||||
# "true" and "false" in the next few lines are Unix shell command names
|
||||
ifeq ($(GIT),none)
|
||||
@echo '#define VERSION_TAG ""' > $@;
|
||||
GIT_PRESENT = false
|
||||
else
|
||||
@if test -d $(srcdir)/.git; then \
|
||||
GIT_PRESENT = true
|
||||
endif
|
||||
version.h:
|
||||
@if $(GIT_PRESENT) && test -d $(srcdir)/.git; then \
|
||||
echo "Using git-describe for VERSION_TAG"; \
|
||||
tmp=`$(GIT) --git-dir $(srcdir)/.git describe \
|
||||
| sed -e 's;\(.*\);#define VERSION_TAG "\1";'`; \
|
||||
|
|
@ -202,7 +205,6 @@ else
|
|||
echo "Using empty VERSION_TAG"; \
|
||||
echo '#define VERSION_TAG ""' > $@; \
|
||||
fi
|
||||
endif
|
||||
|
||||
ifeq (@MINGW32@,yes)
|
||||
ifeq ($(MAN),none)
|
||||
|
|
|
|||
12
Module.cc
12
Module.cc
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
/* n is a permallocated string. */
|
||||
Module::Module(perm_string n)
|
||||
: PScope(n, 0)
|
||||
: PScope(n)
|
||||
{
|
||||
library_flag = false;
|
||||
default_nettype = NetNet::NONE;
|
||||
|
|
@ -41,16 +41,6 @@ void Module::add_gate(PGate*gate)
|
|||
gates_.push_back(gate);
|
||||
}
|
||||
|
||||
void Module::add_task(perm_string name, PTask*task)
|
||||
{
|
||||
tasks_[name] = task;
|
||||
}
|
||||
|
||||
void Module::add_function(perm_string name, PFunction *func)
|
||||
{
|
||||
funcs_[name] = func;
|
||||
}
|
||||
|
||||
unsigned Module::port_count() const
|
||||
{
|
||||
return ports.count();
|
||||
|
|
|
|||
12
Module.h
12
Module.h
|
|
@ -94,7 +94,7 @@ class Module : public PScope, public LineInfo {
|
|||
module is elaborated. During parsing, I put the parameters
|
||||
into this map. */
|
||||
struct param_expr_t : public LineInfo {
|
||||
param_expr_t() : range(0) { }
|
||||
param_expr_t() : type(IVL_VT_NO_TYPE), msb(0), lsb(0), signed_flag(false), expr(0), range(0) { }
|
||||
// Type information
|
||||
ivl_variable_type_t type;
|
||||
PExpr*msb;
|
||||
|
|
@ -102,7 +102,7 @@ class Module : public PScope, public LineInfo {
|
|||
bool signed_flag;
|
||||
// Value expression
|
||||
PExpr*expr;
|
||||
// If there are range constrants, list them here
|
||||
// If there are range constraints, list them here
|
||||
range_t*range;
|
||||
};
|
||||
map<perm_string,param_expr_t>parameters;
|
||||
|
|
@ -138,6 +138,10 @@ class Module : public PScope, public LineInfo {
|
|||
set by the `timescale directive. */
|
||||
int time_unit, time_precision;
|
||||
|
||||
/* Task definitions within this module */
|
||||
map<perm_string,PTask*> tasks;
|
||||
map<perm_string,PFunction*> funcs;
|
||||
|
||||
/* The module has a list of genvars that may be used in
|
||||
various generate schemes. */
|
||||
map<perm_string,LineInfo*> genvars;
|
||||
|
|
@ -152,8 +156,6 @@ class Module : public PScope, public LineInfo {
|
|||
perm_string mod_name() const { return pscope_name(); }
|
||||
|
||||
void add_gate(PGate*gate);
|
||||
void add_task(perm_string name, PTask*def);
|
||||
void add_function(perm_string name, PFunction*def);
|
||||
|
||||
unsigned port_count() const;
|
||||
const svector<PEIdent*>& get_port(unsigned idx) const;
|
||||
|
|
@ -173,8 +175,6 @@ class Module : public PScope, public LineInfo {
|
|||
|
||||
private:
|
||||
list<PGate*> gates_;
|
||||
map<perm_string,PTask*> tasks_;
|
||||
map<perm_string,PFunction*> funcs_;
|
||||
|
||||
static void elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
||||
Design*des, NetScope*scope);
|
||||
|
|
|
|||
18
PExpr.h
18
PExpr.h
|
|
@ -306,10 +306,16 @@ class PEIdent : public PExpr {
|
|||
private:
|
||||
// Common functions to calculate parts of part/bit selects.
|
||||
bool calculate_parts_(Design*, NetScope*, long&msb, long&lsb) const;
|
||||
NetExpr* calculate_up_do_base_(Design*, NetScope*) const;
|
||||
bool calculate_param_range_(Design*, NetScope*,
|
||||
const NetExpr*msb_ex, long&msb,
|
||||
const NetExpr*lsb_ex, long&lsb) const;
|
||||
|
||||
bool calculate_up_do_width_(Design*, NetScope*, unsigned long&wid) const;
|
||||
|
||||
private:
|
||||
NetAssign_*elaborate_lval_net_word_(Design*, NetScope*, NetNet*) const;
|
||||
bool elaborate_lval_net_bit_(Design*, NetScope*, NetAssign_*) const;
|
||||
bool elaborate_lval_net_part_(Design*, NetScope*, NetAssign_*) const;
|
||||
bool elaborate_lval_net_idx_(Design*, NetScope*, NetAssign_*,
|
||||
index_component_t::ctype_t) const;
|
||||
|
|
@ -321,6 +327,18 @@ class PEIdent : public PExpr {
|
|||
NetScope*found,
|
||||
const NetExpr*par_msb,
|
||||
const NetExpr*par_lsb) const;
|
||||
NetExpr*elaborate_expr_param_part_(Design*des,
|
||||
NetScope*scope,
|
||||
const NetExpr*par,
|
||||
NetScope*found,
|
||||
const NetExpr*par_msb,
|
||||
const NetExpr*par_lsb) const;
|
||||
NetExpr*elaborate_expr_param_idx_up_(Design*des,
|
||||
NetScope*scope,
|
||||
const NetExpr*par,
|
||||
NetScope*found,
|
||||
const NetExpr*par_msb,
|
||||
const NetExpr*par_lsb) const;
|
||||
NetExpr*elaborate_expr_net(Design*des,
|
||||
NetScope*scope,
|
||||
NetNet*net,
|
||||
|
|
|
|||
15
PGenerate.cc
15
PGenerate.cc
|
|
@ -27,27 +27,14 @@ PGenerate::PGenerate(unsigned id)
|
|||
: id_number(id)
|
||||
{
|
||||
parent = 0;
|
||||
lexical_scope = 0;
|
||||
}
|
||||
|
||||
PGenerate::~PGenerate()
|
||||
{
|
||||
}
|
||||
|
||||
PWire* PGenerate::get_wire(perm_string name) const
|
||||
{
|
||||
map<perm_string,PWire*>::const_iterator obj = wires.find(name);
|
||||
if (obj == wires.end())
|
||||
return 0;
|
||||
else
|
||||
return (*obj).second;
|
||||
}
|
||||
|
||||
void PGenerate::add_gate(PGate*gate)
|
||||
{
|
||||
gates.push_back(gate);
|
||||
}
|
||||
|
||||
void PGenerate::add_behavior(PProcess*proc)
|
||||
{
|
||||
behaviors.push_back(proc);
|
||||
}
|
||||
|
|
|
|||
27
PGenerate.h
27
PGenerate.h
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __PGenerate_H
|
||||
#define __PGenerate_H
|
||||
/*
|
||||
* Copyright (c) 2006 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2006-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
|
||||
|
|
@ -18,13 +18,11 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: PGenerate.h,v 1.4 2007/06/02 03:42:12 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "LineInfo.h"
|
||||
# include "StringHeap.h"
|
||||
# include "HName.h"
|
||||
# include "PScope.h"
|
||||
# include <list>
|
||||
# include <map>
|
||||
# include "pform_types.h"
|
||||
|
|
@ -32,7 +30,9 @@
|
|||
class Design;
|
||||
class NetScope;
|
||||
class PExpr;
|
||||
class PFunction;
|
||||
class PProcess;
|
||||
class PTask;
|
||||
class PGate;
|
||||
class PWire;
|
||||
|
||||
|
|
@ -49,10 +49,10 @@ class PWire;
|
|||
* The parent points to the GS_CASE that contains this item.
|
||||
* the loop_test is compared with the parent->loop_test expression.
|
||||
*/
|
||||
class PGenerate : public LineInfo {
|
||||
class PGenerate : public LineInfo, public LexicalScope {
|
||||
|
||||
public:
|
||||
PGenerate(unsigned id_number);
|
||||
explicit PGenerate(unsigned id_number);
|
||||
~PGenerate();
|
||||
|
||||
// Generate schemes have an ID number, for when the scope is
|
||||
|
|
@ -60,6 +60,10 @@ class PGenerate : public LineInfo {
|
|||
const unsigned id_number;
|
||||
perm_string scope_name;
|
||||
|
||||
// This is used during parsing to stack lexical scopes within
|
||||
// this generate scheme.
|
||||
PScope*lexical_scope;
|
||||
|
||||
enum scheme_t {GS_NONE, GS_LOOP, GS_CONDIT, GS_ELSE,
|
||||
GS_CASE, GS_CASE_ITEM};
|
||||
scheme_t scheme_type;
|
||||
|
|
@ -71,16 +75,17 @@ class PGenerate : public LineInfo {
|
|||
PExpr*loop_test;
|
||||
PExpr*loop_step;
|
||||
|
||||
map<perm_string,PWire*>wires;
|
||||
PWire* get_wire(perm_string name) const;
|
||||
|
||||
list<PGate*> gates;
|
||||
void add_gate(PGate*);
|
||||
|
||||
list<PProcess*> behaviors;
|
||||
void add_behavior(PProcess*behave);
|
||||
|
||||
list<PGenerate*> generates;
|
||||
// Tasks instantiated within this scheme.
|
||||
map<perm_string,PTask*> tasks;
|
||||
map<perm_string,PFunction*>funcs;
|
||||
|
||||
// Generate schemes can contain further generate schemes.
|
||||
list<PGenerate*> generate_schemes;
|
||||
PGenerate*parent;
|
||||
|
||||
// This method is called by the elaboration of a module to
|
||||
|
|
|
|||
11
PScope.cc
11
PScope.cc
|
|
@ -19,8 +19,13 @@
|
|||
|
||||
# include "PScope.h"
|
||||
|
||||
PScope::PScope(perm_string n, PScope*p)
|
||||
: name_(n), parent_(p)
|
||||
PScope::PScope(perm_string n, PScope*parent)
|
||||
: name_(n), parent_(parent)
|
||||
{
|
||||
}
|
||||
|
||||
PScope::PScope(perm_string n)
|
||||
: name_(n), parent_(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +33,7 @@ PScope::~PScope()
|
|||
{
|
||||
}
|
||||
|
||||
PWire* PScope::wires_find(perm_string name)
|
||||
PWire* LexicalScope::wires_find(perm_string name)
|
||||
{
|
||||
map<perm_string,PWire*>::const_iterator cur = wires.find(name);
|
||||
if (cur == wires.end())
|
||||
|
|
|
|||
22
PScope.h
22
PScope.h
|
|
@ -38,7 +38,22 @@ class NetScope;
|
|||
* NOTE: This is note the same concept as the "scope" of an elaborated
|
||||
* hierarchy. That is represented by NetScope objects after elaboration.
|
||||
*/
|
||||
class PScope {
|
||||
|
||||
class LexicalScope {
|
||||
|
||||
public:
|
||||
explicit LexicalScope() { }
|
||||
// A virtual destructor is so that dynamic_cast can work.
|
||||
virtual ~LexicalScope() { }
|
||||
|
||||
// Nets an variables (wires) in the scope
|
||||
map<perm_string,PWire*>wires;
|
||||
PWire* wires_find(perm_string name);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class PScope : public LexicalScope {
|
||||
|
||||
public:
|
||||
// When created, a scope has a name and a parent. The name is
|
||||
|
|
@ -50,15 +65,12 @@ class PScope {
|
|||
// modules. Scopes for tasks and functions point to their
|
||||
// containing module.
|
||||
PScope(perm_string name, PScope*parent);
|
||||
PScope(perm_string name);
|
||||
virtual ~PScope();
|
||||
|
||||
perm_string pscope_name() const { return name_; }
|
||||
PScope* pscope_parent() { return parent_; }
|
||||
|
||||
// Nets an variables (wires) in the scope
|
||||
map<perm_string,PWire*>wires;
|
||||
PWire* wires_find(perm_string name);
|
||||
|
||||
// Named events in the scope.
|
||||
map<perm_string,PEvent*>events;
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ PBlock::PBlock(perm_string n, PScope*parent, BL_TYPE t)
|
|||
}
|
||||
|
||||
PBlock::PBlock(BL_TYPE t)
|
||||
: PScope(perm_string(),0), bl_type_(t)
|
||||
: PScope(perm_string()), bl_type_(t)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ ostream& operator << (ostream&o, ivl_switch_type_t val)
|
|||
case IVL_SW_RTRANIF1:
|
||||
o << "rtranif1";
|
||||
break;
|
||||
case IVL_SW_TRAN_VP:
|
||||
o << "tran(VP)";
|
||||
break;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
|
@ -299,6 +302,22 @@ void NetArrayDq::dump_node(ostream&o, unsigned ind) const
|
|||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
||||
void NetCastInt::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Cast to int. (NetCastInt): " <<
|
||||
name() << " width=" << width() << endl;
|
||||
dump_node_pins(o, ind+4);
|
||||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
||||
void NetCastReal::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Cast to real (NetCastReal): " <<
|
||||
name() << endl;
|
||||
dump_node_pins(o, ind+4);
|
||||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
||||
void NetCLShift::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " <<
|
||||
|
|
@ -518,9 +537,6 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const
|
|||
case PV:
|
||||
pt = "PV";
|
||||
break;
|
||||
case BI:
|
||||
pt = "BI";
|
||||
break;
|
||||
}
|
||||
o << setw(ind) << "" << "NetPartSelect(" << pt << "): "
|
||||
<< name();
|
||||
|
|
@ -638,7 +654,14 @@ void NetTaskDef::dump(ostream&o, unsigned ind) const
|
|||
|
||||
void NetTran::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << type_ << " " << name() << endl;
|
||||
o << setw(ind) << "" << type_ << " " << name()
|
||||
<< " island " << island;
|
||||
if (type_ == IVL_SW_TRAN_VP) {
|
||||
o << " width=" << vector_width()
|
||||
<< " part=" << part_width()
|
||||
<< " offset=" << part_offset();
|
||||
}
|
||||
o << endl;
|
||||
dump_node_pins(o, ind+4);
|
||||
dump_obj_attr(o, ind+4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,23 +328,24 @@ static int t_default(char*cmd, unsigned ncmd)
|
|||
remove(compiled_defines_path);
|
||||
}
|
||||
#ifdef __MINGW32__ /* MinGW just returns the exit status, so return it! */
|
||||
free(cmd);
|
||||
return rc;
|
||||
#else
|
||||
|
||||
int rtn = 0;
|
||||
if (rc != 0) {
|
||||
if (rc == 127) {
|
||||
fprintf(stderr, "Failed to execute: %s\n", cmd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WIFEXITED(rc))
|
||||
return WEXITSTATUS(rc);
|
||||
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
return -1;
|
||||
rtn = 1;
|
||||
} else if (WIFEXITED(rc)) {
|
||||
rtn = WEXITSTATUS(rc);
|
||||
} else {
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
rtn = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
free(cmd);
|
||||
return rtn;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +506,7 @@ int process_generation(const char*name)
|
|||
" 2005 -- IEEE1364-2005\n"
|
||||
"Other generation flags:\n"
|
||||
" specify | no-specify\n"
|
||||
" verilog-ams | no-verinlog-ams\n"
|
||||
" verilog-ams | no-verilog-ams\n"
|
||||
" std-include | no-std-include\n"
|
||||
" xtypes | no-xtypes\n"
|
||||
" icarus-misc | no-icarus-misc\n"
|
||||
|
|
@ -871,6 +872,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
fprintf(stderr, "Command signaled: %s\n", cmd);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -889,6 +891,4 @@ int main(int argc, char **argv)
|
|||
fclose(iconfig_file);
|
||||
|
||||
return t_default(cmd, ncmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
319
elab_expr.cc
319
elab_expr.cc
|
|
@ -716,6 +716,15 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope,
|
|||
des->errors += 1;
|
||||
}
|
||||
|
||||
if (!rep->value().is_defined()) {
|
||||
cerr << get_fileline() << ": error: Concatenation repeat "
|
||||
<< "may not be undefined (" << rep->value()
|
||||
<< ")." << endl;
|
||||
des->errors += 1;
|
||||
concat_depth -= 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rep->value().is_negative()) {
|
||||
cerr << get_fileline() << ": error: Concatenation repeat "
|
||||
<< "may not be negative (" << rep->value().as_long()
|
||||
|
|
@ -873,6 +882,51 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope,
|
|||
return flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we know that this is an indexed part select (up or down) this
|
||||
* method calculates the up/down base, as far at it can be calculated.
|
||||
*/
|
||||
NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const
|
||||
{
|
||||
const name_component_t&name_tail = path_.back();
|
||||
ivl_assert(*this, !name_tail.index.empty());
|
||||
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.lsb != 0);
|
||||
ivl_assert(*this, index_tail.msb != 0);
|
||||
|
||||
NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool PEIdent::calculate_param_range_(Design*des, NetScope*scope,
|
||||
const NetExpr*par_msb, long&par_msv,
|
||||
const NetExpr*par_lsb, long&par_lsv) const
|
||||
{
|
||||
if (par_msb == 0) {
|
||||
// If the parameter doesn't have an explicit range, then
|
||||
// just return range values of 0:0. The par_msv==0 is
|
||||
// correct. The par_msv is not necessarily correct, but
|
||||
// clients of this function don't need a correct value.
|
||||
ivl_assert(*this, par_lsb == 0);
|
||||
par_msv = 0;
|
||||
par_lsv = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const NetEConst*tmp = dynamic_cast<const NetEConst*> (par_msb);
|
||||
ivl_assert(*this, tmp);
|
||||
|
||||
par_msv = tmp->value().as_long();
|
||||
|
||||
tmp = dynamic_cast<const NetEConst*> (par_lsb);
|
||||
ivl_assert(*this, tmp);
|
||||
|
||||
par_lsv = tmp->value().as_long();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned PEIdent::test_width(Design*des, NetScope*scope,
|
||||
unsigned min, unsigned lval,
|
||||
bool&unsized_flag) const
|
||||
|
|
@ -1085,6 +1139,131 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static verinum param_part_select_bits(const verinum&par_val, long wid,
|
||||
long lsv, long par_lsv)
|
||||
{
|
||||
verinum result (verinum::Vx, wid, true);
|
||||
|
||||
for (long idx = 0 ; idx < wid ; idx += 1) {
|
||||
long off = idx + lsv - par_lsv;
|
||||
if (off < 0)
|
||||
result.set(idx, verinum::Vx);
|
||||
else if (off < (long)par_val.len())
|
||||
result.set(idx, par_val.get(off));
|
||||
else if (par_val.is_string()) // Pad strings with nulls.
|
||||
result.set(idx, verinum::V0);
|
||||
else if (par_val.has_len()) // Pad sized parameters with X
|
||||
result.set(idx, verinum::Vx);
|
||||
else // Unsized parameters are "infinite" width.
|
||||
result.set(idx, sign_bit(par_val));
|
||||
}
|
||||
|
||||
// If the input is a string, and the part select is working on
|
||||
// byte boundaries, then make the result into a string.
|
||||
if (par_val.is_string() && (labs(lsv-par_lsv)%8 == 0) && (wid%8 == 0))
|
||||
return result.as_string();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope,
|
||||
const NetExpr*par,
|
||||
NetScope*found_in,
|
||||
const NetExpr*par_msb,
|
||||
const NetExpr*par_lsb) const
|
||||
{
|
||||
long msv, lsv;
|
||||
bool flag = calculate_parts_(des, scope, msv, lsv);
|
||||
if (!flag)
|
||||
return 0;
|
||||
|
||||
long par_msv, par_lsv;
|
||||
flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv);
|
||||
if (!flag)
|
||||
return 0;
|
||||
|
||||
// Notice that the par_msv is not used in this function other
|
||||
// than for this test. It is used to tell the direction that
|
||||
// the bits are numbers, so that we can make sure the
|
||||
// direction matches the part select direction. After that,
|
||||
// we only need the par_lsv.
|
||||
if ((msv>lsv && par_msv<par_lsv) || (msv<lsv && par_msv>=par_lsv)) {
|
||||
cerr << get_fileline() << ": error: Part select "
|
||||
<< "[" << msv << ":" << lsv << "] is out of order." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long wid = 1 + labs(msv-lsv);
|
||||
|
||||
if (debug_elaborate)
|
||||
cerr << get_fileline() << ": debug: Calculate part select "
|
||||
<< "[" << msv << ":" << lsv << "] from range "
|
||||
<< "[" << par_msv << ":" << par_lsv << "]." << endl;
|
||||
|
||||
const NetEConst*par_ex = dynamic_cast<const NetEConst*> (par);
|
||||
ivl_assert(*this, par_ex);
|
||||
|
||||
verinum result = param_part_select_bits(par_ex->value(), wid, lsv, par_lsv);
|
||||
NetEConst*result_ex = new NetEConst(result);
|
||||
result_ex->set_line(*this);
|
||||
|
||||
return result_ex;
|
||||
}
|
||||
|
||||
NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope,
|
||||
const NetExpr*par,
|
||||
NetScope*found_in,
|
||||
const NetExpr*par_msb,
|
||||
const NetExpr*par_lsb) const
|
||||
{
|
||||
|
||||
long par_msv, par_lsv;
|
||||
bool flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv);
|
||||
if (!flag)
|
||||
return 0;
|
||||
|
||||
NetExpr*base = calculate_up_do_base_(des, scope);
|
||||
if (base == 0)
|
||||
return 0;
|
||||
|
||||
unsigned long wid = 0;
|
||||
calculate_up_do_width_(des, scope, wid);
|
||||
|
||||
const NetEConst*par_ex = dynamic_cast<const NetEConst*> (par);
|
||||
ivl_assert(*this, par_ex);
|
||||
|
||||
if (debug_elaborate)
|
||||
cerr << get_fileline() << ": debug: Calculate part select "
|
||||
<< "[" << *base << "+:" << wid << "] from range "
|
||||
<< "[" << par_msv << ":" << par_lsv << "]." << endl;
|
||||
|
||||
// Handle the special case that the base is constant. In this
|
||||
// case, just precalculate the entire constant result.
|
||||
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
|
||||
long lsv = base_c->value().as_long();
|
||||
|
||||
// Watch out for reversed bit numbering. We're making
|
||||
// the part select from LSB to MSB.
|
||||
if (par_msv < par_lsv)
|
||||
lsv = lsv - wid + 1;
|
||||
|
||||
verinum result = param_part_select_bits(par_ex->value(), wid,
|
||||
lsv, par_lsv);
|
||||
NetEConst*result_ex = new NetEConst(result);
|
||||
result_ex->set_line(*this);
|
||||
return result_ex;
|
||||
}
|
||||
|
||||
if ((par_msb < par_lsb) && (wid>1))
|
||||
base = make_add_expr(base, 1-(long)wid);
|
||||
|
||||
NetExpr*tmp = par->dup_expr();
|
||||
tmp = new NetESelect(tmp, base, wid);
|
||||
tmp->set_line(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the case that the identifier is a parameter reference. The
|
||||
* parameter expression has already been located for us (as the par
|
||||
|
|
@ -1097,84 +1276,29 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des,
|
|||
const NetExpr*par_msb,
|
||||
const NetExpr*par_lsb) const
|
||||
{
|
||||
NetExpr*tmp = par->dup_expr();
|
||||
|
||||
const name_component_t&name_tail = path_.back();
|
||||
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
||||
if (!name_tail.index.empty())
|
||||
use_sel = name_tail.index.back().sel;
|
||||
|
||||
if (use_sel == index_component_t::SEL_PART) {
|
||||
ivl_assert(*this, !name_tail.index.empty());
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.msb);
|
||||
ivl_assert(*this, index_tail.lsb);
|
||||
// NOTE TO SELF: This is the way I want to see this code
|
||||
// structured. This closely follows the structure of the
|
||||
// elaborate_expr_net_ code, which splits all the various
|
||||
// selects to different methods.
|
||||
if (use_sel == index_component_t::SEL_PART)
|
||||
return elaborate_expr_param_part_(des, scope, par, found_in,
|
||||
par_msb, par_lsb);
|
||||
|
||||
/* If the identifier has a part select, we support
|
||||
it by pulling the right bits out and making a
|
||||
sized unsigned constant. This code assumes the
|
||||
lsb of a parameter is 0 and the msb is the
|
||||
width of the parameter. */
|
||||
if (use_sel == index_component_t::SEL_IDX_UP)
|
||||
return elaborate_expr_param_idx_up_(des, scope, par, found_in,
|
||||
par_msb, par_lsb);
|
||||
|
||||
verinum*lsn = index_tail.lsb->eval_const(des, scope);
|
||||
verinum*msn = index_tail.msb->eval_const(des, scope);
|
||||
if ((lsn == 0) || (msn == 0)) {
|
||||
cerr << get_fileline() << ": error: "
|
||||
"Part select expressions must be "
|
||||
"constant expressions." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
// NOTE TO SELF (continued): The code below should be
|
||||
// rewritten in the above format, as I get to it.
|
||||
|
||||
long lsb = lsn->as_long();
|
||||
long msb = msn->as_long();
|
||||
if ((lsb < 0) || (msb < lsb)) {
|
||||
cerr << get_fileline() << ": error: invalid part "
|
||||
<< "select: " << path_
|
||||
<< "["<<msb<<":"<<lsb<<"]" << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
unsigned long ulsb=lsb;
|
||||
unsigned long umsb=msb;
|
||||
NetExpr*tmp = par->dup_expr();
|
||||
|
||||
NetEConst*le = dynamic_cast<NetEConst*>(tmp);
|
||||
assert(le);
|
||||
|
||||
verinum result (verinum::V0, msb-lsb+1, true);
|
||||
verinum exl = le->value();
|
||||
|
||||
/* Pull the bits from the parameter, one at a
|
||||
time. If the bit is within the range, simply
|
||||
copy it to the result. If the bit is outside
|
||||
the range, we sign extend signed unsized
|
||||
numbers, zero extend unsigned unsigned numbers,
|
||||
and X extend sized numbers. */
|
||||
for (unsigned long idx = ulsb ; idx <= umsb ; idx += 1) {
|
||||
if (idx < exl.len())
|
||||
result.set(idx-lsb, exl.get(idx));
|
||||
else if (exl.is_string())
|
||||
result.set(idx-lsb, verinum::V0);
|
||||
else if (exl.has_len())
|
||||
result.set(idx-lsb, verinum::Vx);
|
||||
else if (exl.has_sign())
|
||||
result.set(idx-lsb, exl.get(exl.len()-1));
|
||||
else
|
||||
result.set(idx-lsb, verinum::V0);
|
||||
}
|
||||
|
||||
/* If the input is a string, and the part select
|
||||
is working on byte boundaries, then the result
|
||||
can be made into a string. */
|
||||
if (exl.is_string()
|
||||
&& (lsb%8 == 0)
|
||||
&& (result.len()%8 == 0))
|
||||
result = verinum(result.as_string());
|
||||
|
||||
delete tmp;
|
||||
tmp = new NetEConst(result);
|
||||
|
||||
} else if (use_sel == index_component_t::SEL_IDX_UP || use_sel == index_component_t::SEL_IDX_DO) {
|
||||
if (use_sel == index_component_t::SEL_IDX_DO) {
|
||||
|
||||
ivl_assert(*this, !name_tail.index.empty());
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
|
|
@ -1249,7 +1373,7 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des,
|
|||
par_mv = par_me->value().as_long();
|
||||
par_lv = par_le->value().as_long();
|
||||
}
|
||||
/* Convert the index to cannonical bit address. */
|
||||
/* Convert the index to canonical bit address. */
|
||||
long ridx = rv.as_long();
|
||||
if (par_mv >= par_lv) {
|
||||
ridx -= par_lv;
|
||||
|
|
@ -1361,8 +1485,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope,
|
|||
// Special case: The index is out of range, so the value
|
||||
// of this expression is a 'bx vector the width of a word.
|
||||
if (!net->array_index_is_valid(addr)) {
|
||||
verinum xxx (verinum::Vx, net->vector_width());
|
||||
NetEConst*resx = new NetEConst(xxx);
|
||||
NetEConst*resx = make_const_x(net->vector_width());
|
||||
resx->set_line(*this);
|
||||
delete word_index;
|
||||
return resx;
|
||||
|
|
@ -1428,16 +1551,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
|||
negative values. However, the width that they represent is
|
||||
unsigned. Remember that any order is possible,
|
||||
i.e., [1:0], [-4:6], etc. */
|
||||
unsigned long wid = 1 + ((msv>lsv)? (msv-lsv) : (lsv-msv));
|
||||
if (wid > net->vector_width()) {
|
||||
cerr << get_fileline() << ": error: part select ["
|
||||
<< msv << ":" << lsv << "] out of range." << endl;
|
||||
des->errors += 1;
|
||||
//delete lsn;
|
||||
//delete msn;
|
||||
return net;
|
||||
}
|
||||
ivl_assert(*this, wid <= net->vector_width());
|
||||
unsigned long wid = 1 + labs(msv-lsv);
|
||||
|
||||
if (net->sig()->sb_to_idx(msv) < net->sig()->sb_to_idx(lsv)) {
|
||||
cerr << get_fileline() << ": error: part select ["
|
||||
|
|
@ -1448,26 +1562,29 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
|||
return net;
|
||||
}
|
||||
|
||||
|
||||
if (net->sig()->sb_to_idx(msv) >= (signed) net->vector_width()) {
|
||||
cerr << get_fileline() << ": error: part select ["
|
||||
<< msv << ":" << lsv << "] out of range." << endl;
|
||||
des->errors += 1;
|
||||
//delete lsn;
|
||||
//delete msn;
|
||||
return net;
|
||||
}
|
||||
long sb_lsb = net->sig()->sb_to_idx(lsv);
|
||||
long sb_msb = net->sig()->sb_to_idx(msv);
|
||||
|
||||
// If the part select covers exactly the entire
|
||||
// vector, then do not bother with it. Return the
|
||||
// signal itself.
|
||||
if (net->sig()->sb_to_idx(lsv) == 0 && wid == net->vector_width())
|
||||
// signal itself, casting to unsigned if necessary.
|
||||
if (sb_lsb == 0 && wid == net->vector_width()) {
|
||||
net->cast_signed(false);
|
||||
return net;
|
||||
}
|
||||
|
||||
NetExpr*ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv)));
|
||||
// If the part select covers NONE of the vector, then return a
|
||||
// constant X.
|
||||
|
||||
if ((sb_lsb >= (signed) net->vector_width()) || (sb_msb < 0)) {
|
||||
NetEConst*tmp = make_const_x(wid);
|
||||
tmp->set_line(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetExpr*ex = new NetEConst(verinum(sb_lsb));
|
||||
NetESelect*ss = new NetESelect(net, ex, wid);
|
||||
ss->set_line(*this);
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
|
|
@ -1477,14 +1594,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
|||
NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope,
|
||||
NetESignal*net, NetScope*found_in) const
|
||||
{
|
||||
const name_component_t&name_tail = path_.back();
|
||||
ivl_assert(*this, !name_tail.index.empty());
|
||||
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.lsb != 0);
|
||||
ivl_assert(*this, index_tail.msb != 0);
|
||||
|
||||
NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
NetExpr*base = calculate_up_do_base_(des, scope);
|
||||
|
||||
unsigned long wid = 0;
|
||||
calculate_up_do_width_(des, scope, wid);
|
||||
|
|
@ -1609,8 +1719,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope,
|
|||
/* The bit select is out of range of the
|
||||
vector. This is legal, but returns a
|
||||
constant 1'bx value. */
|
||||
verinum x (verinum::Vx);
|
||||
NetEConst*tmp = new NetEConst(x);
|
||||
NetEConst*tmp = make_const_x(1);
|
||||
tmp->set_line(*this);
|
||||
|
||||
cerr << get_fileline() << ": warning: Bit select ["
|
||||
|
|
|
|||
173
elab_lval.cc
173
elab_lval.cc
|
|
@ -208,101 +208,18 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
return 0;
|
||||
}
|
||||
|
||||
long msb, lsb;
|
||||
NetExpr*mux;
|
||||
|
||||
if (use_sel == index_component_t::SEL_BIT) {
|
||||
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.msb != 0);
|
||||
ivl_assert(*this, index_tail.lsb == 0);
|
||||
|
||||
/* If there is only a single select expression, it is a
|
||||
bit select. Evaluate the constant value and treat it
|
||||
as a part select with a bit width of 1. If the
|
||||
expression it not constant, then return the
|
||||
expression as a mux. */
|
||||
|
||||
NetExpr*index_expr = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
|
||||
if (NetEConst*index_con = dynamic_cast<NetEConst*> (index_expr)) {
|
||||
msb = index_con->value().as_long();
|
||||
lsb = index_con->value().as_long();
|
||||
mux = 0;
|
||||
|
||||
} else {
|
||||
msb = 0;
|
||||
lsb = 0;
|
||||
mux = index_expr;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* No select expressions, so presume a part select the
|
||||
width of the register. */
|
||||
|
||||
msb = reg->msb();
|
||||
lsb = reg->lsb();
|
||||
mux = 0;
|
||||
NetAssign_*lv = new NetAssign_(reg);
|
||||
elaborate_lval_net_bit_(des, scope, lv);
|
||||
return lv;
|
||||
}
|
||||
|
||||
ivl_assert(*this, use_sel == index_component_t::SEL_NONE);
|
||||
|
||||
NetAssign_*lv;
|
||||
if (mux) {
|
||||
|
||||
/* If there is a non-constant bit select, make a
|
||||
NetAssign_ to the target reg and attach a
|
||||
bmux to select the target bit. */
|
||||
lv = new NetAssign_(reg);
|
||||
|
||||
/* Correct the mux for the range of the vector. */
|
||||
if (reg->msb() < reg->lsb())
|
||||
mux = make_sub_expr(reg->lsb(), mux);
|
||||
else if (reg->lsb() != 0)
|
||||
mux = make_add_expr(mux, - reg->lsb());
|
||||
|
||||
lv->set_part(mux, 1);
|
||||
|
||||
} else if (msb == reg->msb() && lsb == reg->lsb()) {
|
||||
|
||||
/* No bit select, and part select covers the entire
|
||||
vector. Simplest case. */
|
||||
lv = new NetAssign_(reg);
|
||||
|
||||
} else {
|
||||
|
||||
/* If the bit/part select is constant, then make the
|
||||
NetAssign_ only as wide as it needs to be and connect
|
||||
only to the selected bits of the reg. */
|
||||
unsigned loff = reg->sb_to_idx(lsb);
|
||||
unsigned moff = reg->sb_to_idx(msb);
|
||||
unsigned wid = moff - loff + 1;
|
||||
|
||||
if (moff < loff) {
|
||||
cerr << get_fileline() << ": error: part select "
|
||||
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
|
||||
<< " is reversed." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the part select extends beyond the extreme of the
|
||||
variable, then report an error. Note that loff is
|
||||
converted to normalized form so is relative the
|
||||
variable pins. */
|
||||
|
||||
if ((wid + loff) > reg->vector_width()) {
|
||||
cerr << get_fileline() << ": error: bit/part select "
|
||||
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
|
||||
<< " is out of range." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv = new NetAssign_(reg);
|
||||
lv->set_part(new NetEConst(verinum(loff)), wid);
|
||||
}
|
||||
/* No select expressions. */
|
||||
|
||||
NetAssign_*lv = new NetAssign_(reg);
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
|
@ -374,10 +291,67 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des,
|
|||
return lv;
|
||||
}
|
||||
|
||||
bool PEIdent::elaborate_lval_net_bit_(Design*des,
|
||||
NetScope*scope,
|
||||
NetAssign_*lv) const
|
||||
{
|
||||
const name_component_t&name_tail = path_.back();
|
||||
const index_component_t&index_tail = name_tail.index.back();
|
||||
ivl_assert(*this, index_tail.msb != 0);
|
||||
ivl_assert(*this, index_tail.lsb == 0);
|
||||
|
||||
NetNet*reg = lv->sig();
|
||||
|
||||
// Bit selects have a single select expression. Evaluate the
|
||||
// constant value and treat it as a part select with a bit
|
||||
// width of 1.
|
||||
NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1);
|
||||
long lsb = 0;
|
||||
|
||||
if (NetEConst*index_con = dynamic_cast<NetEConst*> (mux)) {
|
||||
lsb = index_con->value().as_long();
|
||||
mux = 0;
|
||||
}
|
||||
|
||||
if (mux) {
|
||||
// Non-constant bit mux. Correct the mux for the range
|
||||
// of the vector, then set the l-value part select expression.
|
||||
if (reg->msb() < reg->lsb())
|
||||
mux = make_sub_expr(reg->lsb(), mux);
|
||||
else if (reg->lsb() != 0)
|
||||
mux = make_add_expr(mux, - reg->lsb());
|
||||
|
||||
lv->set_part(mux, 1);
|
||||
|
||||
} else if (lsb == reg->msb() && lsb == reg->lsb()) {
|
||||
// Constant bit mux that happens to select the only bit
|
||||
// of the l-value. Don't bother with any select at all.
|
||||
|
||||
} else {
|
||||
// Constant bit select that does something useful.
|
||||
long loff = reg->sb_to_idx(lsb);
|
||||
|
||||
if (loff < 0 || loff >= (long)reg->vector_width()) {
|
||||
cerr << get_fileline() << ": error: bit select "
|
||||
<< reg->name() << "[" <<lsb<<"]"
|
||||
<< " is out of range." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv->set_part(new NetEConst(verinum(loff)), 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PEIdent::elaborate_lval_net_part_(Design*des,
|
||||
NetScope*scope,
|
||||
NetAssign_*lv) const
|
||||
{
|
||||
// The range expressions of a part select must be
|
||||
// constant. The calculate_parts_ function calculates the
|
||||
// values into msb and lsb.
|
||||
long msb, lsb;
|
||||
bool flag = calculate_parts_(des, scope, msb, lsb);
|
||||
if (!flag)
|
||||
|
|
@ -388,17 +362,14 @@ bool PEIdent::elaborate_lval_net_part_(Design*des,
|
|||
|
||||
if (msb == reg->msb() && lsb == reg->lsb()) {
|
||||
|
||||
/* No bit select, and part select covers the entire
|
||||
vector. Simplest case. */
|
||||
/* Part select covers the entire vector. Simplest case. */
|
||||
|
||||
} else {
|
||||
|
||||
/* If the bit/part select is constant, then make the
|
||||
NetAssign_ only as wide as it needs to be and connect
|
||||
only to the selected bits of the reg. */
|
||||
unsigned loff = reg->sb_to_idx(lsb);
|
||||
unsigned moff = reg->sb_to_idx(msb);
|
||||
unsigned wid = moff - loff + 1;
|
||||
/* Get the canonical offsets into the vector. */
|
||||
long loff = reg->sb_to_idx(lsb);
|
||||
long moff = reg->sb_to_idx(msb);
|
||||
long wid = moff - loff + 1;
|
||||
|
||||
if (moff < loff) {
|
||||
cerr << get_fileline() << ": error: part select "
|
||||
|
|
@ -408,17 +379,15 @@ bool PEIdent::elaborate_lval_net_part_(Design*des,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* If the part select extends beyond the extreme of the
|
||||
/* If the part select extends beyond the extremes of the
|
||||
variable, then report an error. Note that loff is
|
||||
converted to normalized form so is relative the
|
||||
variable pins. */
|
||||
|
||||
if ((wid + loff) > reg->vector_width()) {
|
||||
cerr << get_fileline() << ": error: bit/part select "
|
||||
if (loff < 0 || moff >= (signed)reg->vector_width()) {
|
||||
cerr << get_fileline() << ": warning: Part select "
|
||||
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
|
||||
<< " is out of range." << endl;
|
||||
des->errors += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
lv->set_part(new NetEConst(verinum(loff)), wid);
|
||||
|
|
|
|||
161
elab_net.cc
161
elab_net.cc
|
|
@ -137,7 +137,7 @@ NetNet* PEBinary::elaborate_net_add_(Design*des, NetScope*scope,
|
|||
const NetExpr* decay) const
|
||||
{
|
||||
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0),
|
||||
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
||||
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
||||
|
||||
if (lsig == 0 || rsig == 0) return 0;
|
||||
|
||||
|
|
@ -449,26 +449,21 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope,
|
|||
const NetExpr* fall,
|
||||
const NetExpr* decay) const
|
||||
{
|
||||
|
||||
/* Elaborate the operands of the compare first as expressions
|
||||
(so that the eval_tree method can reduce constant
|
||||
expressions, including parameters) then turn those results
|
||||
into synthesized nets. */
|
||||
NetExpr*lexp = elab_and_eval(des, scope, left_, lwidth),
|
||||
*rexp = elab_and_eval(des, scope, right_, lwidth);
|
||||
NetExpr*lexp = elab_and_eval(des, scope, left_, -1),
|
||||
*rexp = elab_and_eval(des, scope, right_, -1);
|
||||
|
||||
if (lexp == 0 || rexp == 0) return 0;
|
||||
|
||||
unsigned operand_width;
|
||||
bool real_arg = false;
|
||||
if (lexp->expr_type() == IVL_VT_REAL ||
|
||||
rexp->expr_type() == IVL_VT_REAL) {
|
||||
operand_width = 1;
|
||||
real_arg = true;
|
||||
} else {
|
||||
bool real_arg = true;
|
||||
if (lexp->expr_type() != IVL_VT_REAL &&
|
||||
rexp->expr_type() != IVL_VT_REAL) {
|
||||
/* Choose the operand width to be the width of the widest
|
||||
self-determined operand. */
|
||||
operand_width = lexp->expr_width();
|
||||
unsigned operand_width = lexp->expr_width();
|
||||
if (rexp->expr_width() > operand_width)
|
||||
operand_width = rexp->expr_width();
|
||||
|
||||
|
|
@ -476,6 +471,8 @@ NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope,
|
|||
lexp = pad_to_width(lexp, operand_width);
|
||||
rexp->set_width(operand_width);
|
||||
rexp = pad_to_width(rexp, operand_width);
|
||||
|
||||
real_arg = false;
|
||||
}
|
||||
|
||||
NetNet*lsig = 0;
|
||||
|
|
@ -719,6 +716,16 @@ NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope,
|
|||
|
||||
unsigned rwidth = lwidth;
|
||||
|
||||
// If either operand is IVL_VT_REAL, then cast the other to
|
||||
// IVL_VT_REAL so that the division can become IVL_VT_REAL.
|
||||
|
||||
if (lsig->data_type()==IVL_VT_REAL || rsig->data_type()==IVL_VT_REAL) {
|
||||
if (lsig->data_type() != IVL_VT_REAL)
|
||||
lsig = cast_to_real(des, scope, lsig);
|
||||
if (rsig->data_type() != IVL_VT_REAL)
|
||||
rsig = cast_to_real(des, scope, rsig);
|
||||
}
|
||||
|
||||
if (rwidth == 0) {
|
||||
rwidth = lsig->vector_width();
|
||||
if (rsig->vector_width() > rwidth)
|
||||
|
|
@ -790,8 +797,8 @@ NetNet* PEBinary::elaborate_net_mod_(Design*des, NetScope*scope,
|
|||
const NetExpr* fall,
|
||||
const NetExpr* decay) const
|
||||
{
|
||||
NetNet*lsig = left_->elaborate_net(des, scope, 0, 0, 0, 0),
|
||||
*rsig = right_->elaborate_net(des, scope, 0, 0, 0, 0);
|
||||
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0),
|
||||
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
||||
|
||||
if (lsig == 0 || rsig == 0) return 0;
|
||||
|
||||
|
|
@ -1578,6 +1585,14 @@ NetNet* PEConcat::elaborate_net(Design*des, NetScope*scope,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!erep->value().is_defined()) {
|
||||
cerr << get_fileline() << ": error: Concatenation repeat "
|
||||
<< "may not be undefined (" << erep->value()
|
||||
<< ")." << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (erep->value().is_negative()) {
|
||||
cerr << get_fileline() << ": error: Concatenation repeat "
|
||||
<< "may not be negative (" << erep->value().as_long()
|
||||
|
|
@ -1779,8 +1794,32 @@ NetNet* PEIdent::elaborate_net(Design*des, NetScope*scope,
|
|||
that connects to a signal with the correct name. */
|
||||
if (par != 0) {
|
||||
|
||||
// Detect and handle the special case that we have a
|
||||
// real valued parameter. Return a NetLiteral and a
|
||||
// properly typed net.
|
||||
if (const NetECReal*pc = dynamic_cast<const NetECReal*>(par)) {
|
||||
NetLiteral*tmp = new NetLiteral(scope, scope->local_symbol(),
|
||||
pc->value());
|
||||
des->add_node(tmp);
|
||||
tmp->set_line(*par);
|
||||
sig = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::IMPLICIT);
|
||||
sig->set_line(*tmp);
|
||||
sig->data_type(tmp->data_type());
|
||||
sig->local_flag(true);
|
||||
|
||||
connect(tmp->pin(0), sig->pin(0));
|
||||
return sig;
|
||||
}
|
||||
|
||||
const NetEConst*pc = dynamic_cast<const NetEConst*>(par);
|
||||
assert(pc);
|
||||
if (pc == 0) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "Non-consant parameter value?: " << *par << endl;
|
||||
cerr << get_fileline() << ": : "
|
||||
<< "Expression type is " << par->expr_type() << endl;
|
||||
}
|
||||
ivl_assert(*this, pc);
|
||||
verinum pvalue = pc->value();
|
||||
|
||||
/* If the parameter has declared dimensions, then apply
|
||||
|
|
@ -2367,30 +2406,53 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope,
|
|||
osig->local_flag(true);
|
||||
osig->set_line(*this);
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: Generating part selects "
|
||||
<< "to connect input l-value to subexpressions."
|
||||
<< endl;
|
||||
if (bidirectional_flag) {
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: Generating tran(VP) "
|
||||
<< "to connect input l-value to subexpressions."
|
||||
<< endl;
|
||||
}
|
||||
|
||||
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
||||
unsigned wid = nets[idx]->vector_width();
|
||||
unsigned off = width - wid;
|
||||
NetTran*ps = new NetTran(scope, scope->local_symbol(),
|
||||
osig->vector_width(), wid, off);
|
||||
des->add_node(ps);
|
||||
ps->set_line(*this);
|
||||
|
||||
connect(ps->pin(0), osig->pin(0));
|
||||
connect(ps->pin(1), nets[idx]->pin(0));
|
||||
|
||||
ivl_assert(*this, wid <= width);
|
||||
width -= wid;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: Generating part selects "
|
||||
<< "to connect input l-value to subexpressions."
|
||||
<< endl;
|
||||
}
|
||||
|
||||
NetPartSelect::dir_t part_dir = NetPartSelect::VP;
|
||||
|
||||
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
||||
unsigned wid = nets[idx]->vector_width();
|
||||
unsigned off = width - wid;
|
||||
NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir);
|
||||
des->add_node(ps);
|
||||
ps->set_line(*this);
|
||||
|
||||
connect(ps->pin(1), osig->pin(0));
|
||||
connect(ps->pin(0), nets[idx]->pin(0));
|
||||
|
||||
assert(wid <= width);
|
||||
width -= wid;
|
||||
}
|
||||
assert(width == 0);
|
||||
}
|
||||
|
||||
NetPartSelect::dir_t part_dir = bidirectional_flag
|
||||
? NetPartSelect::BI
|
||||
: NetPartSelect::VP;
|
||||
|
||||
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
||||
unsigned wid = nets[idx]->vector_width();
|
||||
unsigned off = width - wid;
|
||||
NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir);
|
||||
des->add_node(ps);
|
||||
|
||||
connect(ps->pin(1), osig->pin(0));
|
||||
connect(ps->pin(0), nets[idx]->pin(0));
|
||||
|
||||
assert(wid <= width);
|
||||
width -= wid;
|
||||
}
|
||||
assert(width == 0);
|
||||
|
||||
osig->data_type(nets[0]->data_type());
|
||||
osig->local_flag(true);
|
||||
return osig;
|
||||
|
|
@ -2743,11 +2805,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
|||
/* If we are processing a tran or inout, then the
|
||||
partselect is bi-directional. Otherwise, it is a
|
||||
Part-to-Vector select. */
|
||||
NetPartSelect::dir_t part_dir;
|
||||
if (bidirectional_flag)
|
||||
part_dir = NetPartSelect::BI;
|
||||
else
|
||||
part_dir = NetPartSelect::PV;
|
||||
|
||||
if (debug_elaborate)
|
||||
cerr << get_fileline() << ": debug: "
|
||||
|
|
@ -2764,11 +2821,23 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
|||
subsig->local_flag(true);
|
||||
subsig->set_line(*this);
|
||||
|
||||
NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid,
|
||||
part_dir);
|
||||
des->add_node(sub);
|
||||
connect(sub->pin(0), subsig->pin(0));
|
||||
if (bidirectional_flag) {
|
||||
// Make a tran(VP)
|
||||
NetTran*sub = new NetTran(scope, scope->local_symbol(),
|
||||
sig->vector_width(),
|
||||
subnet_wid, lidx);
|
||||
sub->set_line(*this);
|
||||
des->add_node(sub);
|
||||
connect(sub->pin(0), sig->pin(0));
|
||||
connect(sub->pin(1), subsig->pin(0));
|
||||
|
||||
} else {
|
||||
NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid,
|
||||
NetPartSelect::PV);
|
||||
des->add_node(sub);
|
||||
sub->set_line(*this);
|
||||
connect(sub->pin(0), subsig->pin(0));
|
||||
}
|
||||
sig = subsig;
|
||||
}
|
||||
|
||||
|
|
|
|||
127
elab_scope.cc
127
elab_scope.cc
|
|
@ -98,7 +98,16 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
|||
tmp->low_expr = 0;
|
||||
}
|
||||
|
||||
if (range->high_expr) {
|
||||
if (range->high_expr && range->high_expr==range->low_expr) {
|
||||
// Detect the special case of a "point"
|
||||
// range. These are called out by setting the high
|
||||
// and low expression ranges to the same
|
||||
// expression. The exclude_flags should be false
|
||||
// in this case
|
||||
ivl_assert(*range->high_expr, tmp->low_open_flag==false && tmp->high_open_flag==false);
|
||||
tmp->high_expr = tmp->low_expr;
|
||||
|
||||
} else if (range->high_expr) {
|
||||
tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1);
|
||||
ivl_assert(*range->high_expr, tmp->high_expr);
|
||||
} else {
|
||||
|
|
@ -114,6 +123,64 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur,
|
|||
delete val;
|
||||
}
|
||||
|
||||
static void elaborate_scope_tasks(Design*des, NetScope*scope,
|
||||
const LineInfo&loc,
|
||||
const map<perm_string,PTask*>&tasks)
|
||||
{
|
||||
typedef map<perm_string,PTask*>::const_iterator tasks_it_t;
|
||||
|
||||
for (tasks_it_t cur = tasks.begin()
|
||||
; cur != tasks.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << loc.get_fileline() << ": error: task/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*task_scope = new NetScope(scope, use_name,
|
||||
NetScope::TASK);
|
||||
task_scope->set_line((*cur).second);
|
||||
|
||||
if (debug_scopes)
|
||||
cerr << cur->second->get_fileline() << ": debug: "
|
||||
<< "Elaborate task scope " << scope_path(task_scope) << endl;
|
||||
(*cur).second->elaborate_scope(des, task_scope);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void elaborate_scope_funcs(Design*des, NetScope*scope,
|
||||
const LineInfo&loc,
|
||||
const map<perm_string,PFunction*>&funcs)
|
||||
{
|
||||
typedef map<perm_string,PFunction*>::const_iterator funcs_it_t;
|
||||
|
||||
for (funcs_it_t cur = funcs.begin()
|
||||
; cur != funcs.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << loc.get_fileline() << ": error: function/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*func_scope = new NetScope(scope, use_name,
|
||||
NetScope::FUNC);
|
||||
func_scope->set_line((*cur).second);
|
||||
|
||||
if (debug_scopes)
|
||||
cerr << cur->second->get_fileline() << ": debug: "
|
||||
<< "Elaborate function scope " << scope_path(func_scope) << endl;
|
||||
(*cur).second->elaborate_scope(des, func_scope);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Module::elaborate_scope(Design*des, NetScope*scope,
|
||||
const replace_t&replacements) const
|
||||
{
|
||||
|
|
@ -257,49 +324,14 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
|
|||
// elaborate_scope method of the PTask for detailed
|
||||
// processing.
|
||||
|
||||
typedef map<perm_string,PTask*>::const_iterator tasks_it_t;
|
||||
|
||||
for (tasks_it_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << get_fileline() << ": error: task/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*task_scope = new NetScope(scope, use_name,
|
||||
NetScope::TASK);
|
||||
task_scope->set_line((*cur).second);
|
||||
(*cur).second->elaborate_scope(des, task_scope);
|
||||
}
|
||||
elaborate_scope_tasks(des, scope, *this, tasks);
|
||||
|
||||
|
||||
// Functions are very similar to tasks, at least from the
|
||||
// perspective of scopes. So handle them exactly the same
|
||||
// way.
|
||||
|
||||
typedef map<perm_string,PFunction*>::const_iterator funcs_it_t;
|
||||
|
||||
for (funcs_it_t cur = funcs_.begin()
|
||||
; cur != funcs_.end() ; cur ++ ) {
|
||||
|
||||
hname_t use_name( (*cur).first );
|
||||
if (scope->child(use_name)) {
|
||||
cerr << get_fileline() << ": error: function/scope name "
|
||||
<< use_name << " already used in this context."
|
||||
<< endl;
|
||||
des->errors += 1;
|
||||
continue;
|
||||
}
|
||||
NetScope*func_scope = new NetScope(scope, use_name,
|
||||
NetScope::FUNC);
|
||||
func_scope->set_line((*cur).second);
|
||||
(*cur).second->elaborate_scope(des, func_scope);
|
||||
}
|
||||
|
||||
elaborate_scope_funcs(des, scope, *this, funcs);
|
||||
|
||||
// Gates include modules, which might introduce new scopes, so
|
||||
// scan all of them to create those scopes.
|
||||
|
|
@ -539,8 +571,8 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container)
|
|||
PGenerate*default_item = 0;
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generator_it_t;
|
||||
generator_it_t cur = generates.begin();
|
||||
while (cur != generates.end()) {
|
||||
generator_it_t cur = generate_schemes.begin();
|
||||
while (cur != generate_schemes.end()) {
|
||||
PGenerate*item = *cur;
|
||||
assert( item->scheme_type == PGenerate::GS_CASE_ITEM );
|
||||
|
||||
|
|
@ -569,7 +601,7 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container)
|
|||
delete case_value_co;
|
||||
case_value_co = 0;
|
||||
|
||||
PGenerate*item = (cur == generates.end())? default_item : *cur;
|
||||
PGenerate*item = (cur == generate_schemes.end())? default_item : *cur;
|
||||
if (item == 0) {
|
||||
cerr << get_fileline() << ": debug: "
|
||||
<< "No generate items found" << endl;
|
||||
|
|
@ -596,11 +628,14 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope)
|
|||
// from simple elaboration.
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++ ) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur) -> generate_scope(des, scope);
|
||||
}
|
||||
|
||||
elaborate_scope_tasks(des, scope, *this, tasks);
|
||||
elaborate_scope_funcs(des, scope, *this, funcs);
|
||||
|
||||
// Scan the generated scope for gates that may create
|
||||
// their own scopes.
|
||||
typedef list<PGate*>::const_iterator pgate_list_it_t;
|
||||
|
|
@ -609,6 +644,12 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope)
|
|||
(*cur) ->elaborate_scope(des, scope);
|
||||
}
|
||||
|
||||
typedef list<PProcess*>::const_iterator proc_it_t;
|
||||
for (proc_it_t cur = behaviors.begin()
|
||||
; cur != behaviors.end() ; cur ++ ) {
|
||||
(*cur) -> statement() -> elaborate_scope(des, scope);
|
||||
}
|
||||
|
||||
// Save the scope that we created, for future use.
|
||||
scope_list_.push_back(scope);
|
||||
}
|
||||
|
|
|
|||
77
elab_sig.cc
77
elab_sig.cc
|
|
@ -108,6 +108,42 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const
|
|||
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;
|
||||
|
|
@ -182,38 +218,12 @@ bool Module::elaborate_sig(Design*des, NetScope*scope) const
|
|||
flag &= (*gt)->elaborate_sig(des, scope);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// After all the wires are elaborated, we are free to
|
||||
// elaborate the ports of the tasks defined within this
|
||||
// module. Run through them now.
|
||||
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
|
@ -415,8 +425,8 @@ bool PGenerate::elaborate_sig(Design*des, NetScope*container) const
|
|||
<< scope_path(container) << "." << endl;
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++) {
|
||||
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);
|
||||
|
|
@ -461,9 +471,12 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const
|
|||
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 = generates.begin()
|
||||
; cur != generates.end() ; cur ++ ) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur) -> elaborate_sig(des, scope);
|
||||
}
|
||||
|
||||
|
|
|
|||
139
elaborate.cc
139
elaborate.cc
|
|
@ -94,7 +94,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
|||
assert(lval->pin_count() == 1);
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: PGassign: elaborated l-value"
|
||||
cerr << get_fileline() << ": debug: PGAssign: elaborated l-value"
|
||||
<< " width=" << lval->vector_width()
|
||||
<< ", type=" << lval->data_type() << endl;
|
||||
}
|
||||
|
|
@ -113,14 +113,13 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
|||
return;
|
||||
}
|
||||
|
||||
/* If either lval or rid are real then both must be real. */
|
||||
if ((lval->data_type() == IVL_VT_REAL ||
|
||||
rid->data_type() == IVL_VT_REAL) &&
|
||||
lval->data_type() != rid->data_type()) {
|
||||
cerr << get_fileline() << ": sorry: Both the r-value and "
|
||||
"the l-value must be real in this context." << endl;
|
||||
des->errors += 1;
|
||||
return;
|
||||
/* Cast the right side when needed. */
|
||||
if ((lval->data_type() == IVL_VT_REAL &&
|
||||
rid->data_type() != IVL_VT_REAL)) {
|
||||
rid = cast_to_real(des, scope, rid);
|
||||
} else if ((lval->data_type() != IVL_VT_REAL &&
|
||||
rid->data_type() == IVL_VT_REAL)) {
|
||||
rid = cast_to_int(des, scope, rid, lval->vector_width());
|
||||
}
|
||||
|
||||
ivl_assert(*this, rid);
|
||||
|
|
@ -287,14 +286,13 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
|||
assert(lval && rval);
|
||||
assert(rval->pin_count() == 1);
|
||||
|
||||
/* If either lval or rval are real then both must be real. */
|
||||
if ((lval->data_type() == IVL_VT_REAL ||
|
||||
rval->data_type() == IVL_VT_REAL) &&
|
||||
lval->data_type() != rval->data_type()) {
|
||||
cerr << get_fileline() << ": sorry: Both the r-value and "
|
||||
"the l-value must be real in this context." << endl;
|
||||
des->errors += 1;
|
||||
return;
|
||||
/* Cast the right side when needed. */
|
||||
if ((lval->data_type() == IVL_VT_REAL &&
|
||||
rval->data_type() != IVL_VT_REAL)) {
|
||||
rval = cast_to_real(des, scope, rval);
|
||||
} else if ((lval->data_type() != IVL_VT_REAL &&
|
||||
rval->data_type() == IVL_VT_REAL)) {
|
||||
rval = cast_to_int(des, scope, rval, lval->vector_width());
|
||||
}
|
||||
|
||||
/* If the r-value insists on being smaller then the l-value
|
||||
|
|
@ -386,7 +384,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
|
|||
high = msb.as_long();
|
||||
|
||||
if (debug_elaborate) {
|
||||
cerr << get_fileline() << ": debug: PGBuiltin: Make arrray "
|
||||
cerr << get_fileline() << ": debug: PGBuiltin: Make array "
|
||||
<< "[" << high << ":" << low << "]"
|
||||
<< " of " << count << " gates for " << name << endl;
|
||||
}
|
||||
|
|
@ -864,6 +862,9 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// "cur" is an array of pointers, and we don't need it any more.
|
||||
delete[]cur;
|
||||
}
|
||||
|
||||
NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
|
||||
|
|
@ -878,6 +879,31 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
|
|||
tmp->local_flag(true);
|
||||
tmp->set_line(*sig);
|
||||
|
||||
// Handle the special case of a bi-directional part
|
||||
// select. Create a NetTran(VP) instead of a uni-directional
|
||||
// NetPartSelect node.
|
||||
if (dir == NetNet::PINOUT) {
|
||||
unsigned wida = sig->vector_width();
|
||||
unsigned widb = tmp->vector_width();
|
||||
bool part_b = widb < wida;
|
||||
NetTran*node = new NetTran(scope, scope->local_symbol(),
|
||||
part_b? wida : widb,
|
||||
part_b? widb : wida,
|
||||
0);
|
||||
if (part_b) {
|
||||
connect(node->pin(0), sig->pin(0));
|
||||
connect(node->pin(1), tmp->pin(0));
|
||||
} else {
|
||||
connect(node->pin(0), tmp->pin(0));
|
||||
connect(node->pin(1), sig->pin(0));
|
||||
}
|
||||
|
||||
node->set_line(*this);
|
||||
des->add_node(node);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetPartSelect*node = 0;
|
||||
|
||||
switch (dir) {
|
||||
|
|
@ -906,15 +932,7 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
|
|||
break;
|
||||
|
||||
case NetNet::PINOUT:
|
||||
if (sig->vector_width() > tmp->vector_width()) {
|
||||
node = new NetPartSelect(sig, 0, tmp->vector_width(),
|
||||
NetPartSelect::BI);
|
||||
connect(node->pin(0), tmp->pin(0));
|
||||
} else {
|
||||
node = new NetPartSelect(tmp, 0, sig->vector_width(),
|
||||
NetPartSelect::BI);
|
||||
connect(node->pin(0), sig->pin(0));
|
||||
}
|
||||
ivl_assert(*this, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -1169,8 +1187,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
|||
identifier elaborates to the same NetNet in
|
||||
both cases so the extra elaboration has no
|
||||
effect. But if the expression passed to the
|
||||
inout port is a part select, aspecial part
|
||||
select must be created that can paqss data in
|
||||
inout port is a part select, a special part
|
||||
select must be created that can pass data in
|
||||
both directions.
|
||||
|
||||
Use the elaborate_bi_net method to handle all
|
||||
|
|
@ -1932,7 +1950,7 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
|
|||
if (nscope == 0) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
"unable to find block scope " << scope_path(scope)
|
||||
<< "<" << pscope_name() << ">" << endl;
|
||||
<< "." << pscope_name() << endl;
|
||||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3035,7 +3053,7 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
|
|||
sig = des->find_signal(scope, id2->path());
|
||||
if (sig == 0) {
|
||||
cerr << get_fileline() << ": error: Unable to find variable "
|
||||
<< id2->path() << " in for-loop increment expressin." << endl;
|
||||
<< id2->path() << " in for-loop increment expression." << endl;
|
||||
des->errors += 1;
|
||||
return body;
|
||||
}
|
||||
|
|
@ -3536,6 +3554,34 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const
|
|||
|
||||
}
|
||||
|
||||
static void elaborate_functions(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);
|
||||
assert(fscope);
|
||||
(*cur).second->elaborate(des, fscope);
|
||||
}
|
||||
}
|
||||
|
||||
static void elaborate_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 ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*tscope = scope->child(use_name);
|
||||
assert(tscope);
|
||||
(*cur).second->elaborate(des, tscope);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When a module is instantiated, it creates the scope then uses this
|
||||
* method to elaborate the contents of the module.
|
||||
|
|
@ -3595,28 +3641,12 @@ bool Module::elaborate(Design*des, NetScope*scope) const
|
|||
}
|
||||
|
||||
// Elaborate functions.
|
||||
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);
|
||||
assert(fscope);
|
||||
(*cur).second->elaborate(des, fscope);
|
||||
}
|
||||
elaborate_functions(des, scope, funcs);
|
||||
|
||||
// Elaborate the task definitions. This is done before the
|
||||
// behaviors so that task calls may reference these, and after
|
||||
// the signals so that the tasks can reference them.
|
||||
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
||||
for (mtask_it_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++) {
|
||||
|
||||
hname_t use_name ( (*cur).first );
|
||||
NetScope*tscope = scope->child(use_name);
|
||||
assert(tscope);
|
||||
(*cur).second->elaborate(des, tscope);
|
||||
}
|
||||
elaborate_tasks(des, scope, tasks);
|
||||
|
||||
// Get all the gates of the module and elaborate them by
|
||||
// connecting them to the signals. The gate may be simple or
|
||||
|
|
@ -3661,8 +3691,8 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const
|
|||
<< scope_path(container) << "." << endl;
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++) {
|
||||
PGenerate*item = *cur;
|
||||
if (! item->scope_list_.empty()) {
|
||||
flag &= item->elaborate(des, container);
|
||||
|
|
@ -3704,6 +3734,9 @@ bool PGenerate::elaborate(Design*des, NetScope*container) const
|
|||
|
||||
bool PGenerate::elaborate_(Design*des, NetScope*scope) const
|
||||
{
|
||||
elaborate_functions(des, scope, funcs);
|
||||
elaborate_tasks(des, scope, tasks);
|
||||
|
||||
typedef list<PGate*>::const_iterator gates_it_t;
|
||||
for (gates_it_t cur = gates.begin() ; cur != gates.end() ; cur ++ )
|
||||
(*cur)->elaborate(des, scope);
|
||||
|
|
@ -3713,8 +3746,8 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const
|
|||
(*cur)->elaborate(des, scope);
|
||||
|
||||
typedef list<PGenerate*>::const_iterator generate_it_t;
|
||||
for (generate_it_t cur = generates.begin()
|
||||
; cur != generates.end() ; cur ++ ) {
|
||||
for (generate_it_t cur = generate_schemes.begin()
|
||||
; cur != generate_schemes.end() ; cur ++ ) {
|
||||
(*cur)->elaborate(des, scope);
|
||||
}
|
||||
|
||||
|
|
|
|||
12
emit.cc
12
emit.cc
|
|
@ -72,6 +72,16 @@ bool NetCaseCmp::emit_node(struct target_t*tgt) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NetCastInt::emit_node(struct target_t*tgt) const
|
||||
{
|
||||
return tgt->lpm_cast_int(this);
|
||||
}
|
||||
|
||||
bool NetCastReal::emit_node(struct target_t*tgt) const
|
||||
{
|
||||
return tgt->lpm_cast_real(this);
|
||||
}
|
||||
|
||||
bool NetCLShift::emit_node(struct target_t*tgt) const
|
||||
{
|
||||
tgt->lpm_clshift(this);
|
||||
|
|
@ -362,7 +372,7 @@ void NetScope::emit_scope(struct target_t*tgt) const
|
|||
/* Run the signals again, but this time to connect the
|
||||
delay paths. This is done as a second pass because
|
||||
the paths reference other signals that may be later
|
||||
in the list. We can do it here becase delay paths are
|
||||
in the list. We can do it here because delay paths are
|
||||
always connected within the scope. */
|
||||
cur = signals_->sig_next_;
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@
|
|||
*
|
||||
* The POST_MAP compiler directive causes the GSR manipulations
|
||||
* included in the test bench to be compiled in, to simulate the chip
|
||||
* startup. Other then that, the test bench runs the post-map design
|
||||
* startup. Other than that, the test bench runs the post-map design
|
||||
* the same way the pre-synthesis design works.
|
||||
*
|
||||
* Run this design with the command:
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ NetNet* NetEBDiv::synthesize(Design*des)
|
|||
|
||||
default: {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
<< "NetEBDiv has unexpeced op() code: "
|
||||
<< "NetEBDiv has unexpected op() code: "
|
||||
<< op() << endl;
|
||||
des->errors += 1;
|
||||
|
||||
|
|
|
|||
12
ivl.def
12
ivl.def
|
|
@ -53,6 +53,9 @@ ivl_file_table_index
|
|||
ivl_file_table_item
|
||||
ivl_file_table_size
|
||||
|
||||
ivl_island_flag_set
|
||||
ivl_island_flag_test
|
||||
|
||||
ivl_logic_attr
|
||||
ivl_logic_attr_cnt
|
||||
ivl_logic_attr_val
|
||||
|
|
@ -115,6 +118,7 @@ ivl_nexus_ptr_pin
|
|||
ivl_nexus_ptr_lpm
|
||||
ivl_nexus_ptr_log
|
||||
ivl_nexus_ptr_sig
|
||||
ivl_nexus_ptr_switch
|
||||
|
||||
ivl_parameter_basename
|
||||
ivl_parameter_expr
|
||||
|
|
@ -222,10 +226,14 @@ ivl_switch_a
|
|||
ivl_switch_b
|
||||
ivl_switch_basename
|
||||
ivl_switch_enable
|
||||
ivl_switch_file
|
||||
ivl_switch_island
|
||||
ivl_switch_lineno
|
||||
ivl_switch_offset
|
||||
ivl_switch_part
|
||||
ivl_switch_scope
|
||||
ivl_switch_type
|
||||
ivl_switch_attr_cnt;
|
||||
ivl_switch_attr_val;
|
||||
ivl_switch_width
|
||||
|
||||
ivl_udp_init
|
||||
ivl_udp_name
|
||||
|
|
|
|||
47
ivl_target.h
47
ivl_target.h
|
|
@ -80,6 +80,13 @@ _BEGIN_DECL
|
|||
* processes. Structural expressions are instead treated as logic
|
||||
* gates.
|
||||
*
|
||||
* ivl_island_t
|
||||
* Certain types of objects may belong to islands. The island that
|
||||
* they belong to is represented by the ivl_island_t cookie. To
|
||||
* know if object belong to the same island, it is sufficient to
|
||||
* compare island cookies. If a==b, then island a is the same as
|
||||
* island b.
|
||||
*
|
||||
* ivl_lpm_t
|
||||
* This object is the base class for all the various LPM type
|
||||
* device nodes. This object carries a few base properties
|
||||
|
|
@ -146,6 +153,7 @@ typedef struct ivl_delaypath_s*ivl_delaypath_t;
|
|||
typedef struct ivl_design_s *ivl_design_t;
|
||||
typedef struct ivl_event_s *ivl_event_t;
|
||||
typedef struct ivl_expr_s *ivl_expr_t;
|
||||
typedef struct ivl_island_s *ivl_island_t;
|
||||
typedef struct ivl_lpm_s *ivl_lpm_t;
|
||||
typedef struct ivl_lval_s *ivl_lval_t;
|
||||
typedef struct ivl_net_const_s*ivl_net_const_t;
|
||||
|
|
@ -237,7 +245,8 @@ typedef enum ivl_switch_type_e {
|
|||
IVL_SW_TRANIF1 = 2,
|
||||
IVL_SW_RTRAN = 3,
|
||||
IVL_SW_RTRANIF0 = 4,
|
||||
IVL_SW_RTRANIF1 = 5
|
||||
IVL_SW_RTRANIF1 = 5,
|
||||
IVL_SW_TRAN_VP = 6
|
||||
} ivl_switch_type_t;
|
||||
|
||||
/* This is the type of an LPM object. */
|
||||
|
|
@ -245,6 +254,8 @@ typedef enum ivl_lpm_type_e {
|
|||
IVL_LPM_ABS = 32,
|
||||
IVL_LPM_ADD = 0,
|
||||
IVL_LPM_ARRAY = 30,
|
||||
IVL_LPM_CAST_INT = 34,
|
||||
IVL_LPM_CAST_REAL = 33,
|
||||
IVL_LPM_CONCAT = 16,
|
||||
IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */
|
||||
IVL_LPM_CMP_EQ = 10,
|
||||
|
|
@ -257,7 +268,7 @@ typedef enum ivl_lpm_type_e {
|
|||
IVL_LPM_MOD = 13,
|
||||
IVL_LPM_MULT = 4,
|
||||
IVL_LPM_MUX = 5,
|
||||
IVL_LPM_PART_BI= 28, /* part select: bi-directional (part on 0) */
|
||||
/* IVL_LPM_PART_BI= 28, / obsolete */
|
||||
IVL_LPM_PART_VP= 15, /* part select: vector to part */
|
||||
IVL_LPM_PART_PV= 17, /* part select: part written to vector */
|
||||
IVL_LPM_POW = 31,
|
||||
|
|
@ -713,6 +724,16 @@ extern unsigned ivl_file_table_index(const char *);
|
|||
extern unsigned ivl_file_table_size(void);
|
||||
|
||||
|
||||
/* ISLAND
|
||||
*
|
||||
* ivl_island_flag_set
|
||||
* ivl_island_flag_test
|
||||
* Allow the user to test or set a boolean flag associated with the
|
||||
* island.
|
||||
*/
|
||||
extern int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value);
|
||||
extern int ivl_island_flag_test(ivl_island_t net, unsigned flag);
|
||||
|
||||
/* LOGIC
|
||||
* These types and functions support manipulation of logic gates. The
|
||||
* ivl_logic_t enumeration identifies the various kinds of gates that
|
||||
|
|
@ -1327,6 +1348,7 @@ extern unsigned ivl_nexus_ptr_pin(ivl_nexus_ptr_t net);
|
|||
extern ivl_net_const_t ivl_nexus_ptr_con(ivl_nexus_ptr_t net);
|
||||
extern ivl_net_logic_t ivl_nexus_ptr_log(ivl_nexus_ptr_t net);
|
||||
extern ivl_lpm_t ivl_nexus_ptr_lpm(ivl_nexus_ptr_t net);
|
||||
extern ivl_switch_t ivl_nexus_ptr_switch(ivl_nexus_ptr_t net);
|
||||
extern ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net);
|
||||
|
||||
/* PARAMETER
|
||||
|
|
@ -1854,9 +1876,6 @@ extern ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net);
|
|||
* ivl_switch_basename
|
||||
* This is the name given to the device in the source code.
|
||||
*
|
||||
* ivl_switch_scope
|
||||
* The scope where the switch device appears.
|
||||
*
|
||||
* ivl_switch_a
|
||||
* ivl_switch_b
|
||||
* The a and b ports are the two ports of the switch.
|
||||
|
|
@ -1867,17 +1886,31 @@ extern ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net);
|
|||
*
|
||||
* SEMANTIC NOTES
|
||||
* The a/b ports can be any type, but the types must exactly
|
||||
* match. The enable must be a scalar.
|
||||
* match, including vector widths. The enable must be a scalar.
|
||||
*
|
||||
* The IVL_SW_TRAN_VP is an exception to the above. In this case,
|
||||
* the B side may be a different size, and the a side will have a
|
||||
* a fixed width. The unused bits are padded to Z on the A side.
|
||||
*/
|
||||
extern ivl_switch_type_t ivl_switch_type(ivl_switch_t net);
|
||||
extern const char*ivl_switch_basename(ivl_switch_t net);
|
||||
extern ivl_scope_t ivl_switch_scope(ivl_switch_t net);
|
||||
extern const char*ivl_switch_basename(ivl_switch_t net);
|
||||
extern ivl_nexus_t ivl_switch_a(ivl_switch_t net);
|
||||
extern ivl_nexus_t ivl_switch_b(ivl_switch_t net);
|
||||
extern ivl_nexus_t ivl_switch_enable(ivl_switch_t net);
|
||||
extern ivl_island_t ivl_switch_island(ivl_switch_t net);
|
||||
|
||||
/* These are only support for IVL_SW_TRAN_VP switches. */
|
||||
extern unsigned ivl_switch_width(ivl_switch_t net);
|
||||
extern unsigned ivl_switch_part(ivl_switch_t net);
|
||||
extern unsigned ivl_switch_offset(ivl_switch_t net);
|
||||
|
||||
/* Not implemented yet
|
||||
extern unsigned ivl_switch_attr_cnt(ivl_switch_t net);
|
||||
extern ivl_attribute_t ivl_switch_attr_val(ivl_switch_t net, unsigned idx);
|
||||
*** */
|
||||
extern const char* ivl_switch_file(ivl_switch_t net);
|
||||
extern unsigned ivl_switch_lineno(ivl_switch_t net);
|
||||
|
||||
#if defined(__MINGW32__) || defined (__CYGWIN32__)
|
||||
# define DLLEXPORT __declspec(dllexport)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef __ivl_target_priv_H
|
||||
#define __ivl_target_H
|
||||
/*
|
||||
* Copyright (c) 2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
* General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include <inttypes.h>
|
||||
|
||||
/*
|
||||
* This deader has declarations related to the ivl_target.h API that
|
||||
* are not to be exported outside of the core via the ivl_target.h
|
||||
* interface.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Information about islands. Connected branches within a net are
|
||||
* collected into islands. Branches that are purely ddiscrete do not
|
||||
* have disciplines and do not belong to islands.
|
||||
*/
|
||||
|
||||
class discipline_t;
|
||||
|
||||
struct ivl_island_s {
|
||||
discipline_t*discipline;
|
||||
// user accessible flags. They are initially false, always.
|
||||
vector<bool> flags;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -941,7 +941,7 @@ static void do_define()
|
|||
}
|
||||
|
||||
/* Detect the continuation sequence. If I find it, remove it
|
||||
* and the white space that preceeds it, then replace all that
|
||||
* and the white space that precedes it, then replace all that
|
||||
* with a single newline.
|
||||
*/
|
||||
if ((cp > yytext) && (cp[-1] == '\\'))
|
||||
|
|
|
|||
|
|
@ -843,7 +843,7 @@ static verinum*make_unsized_dec(const char*ptr)
|
|||
if (ptr[0] == '\'') {
|
||||
/* The number has decorations of the form 'sd<digits>,
|
||||
possibly with space between the d and the <digits>.
|
||||
Also, the 's' is optional, and markes the number as
|
||||
Also, the 's' is optional, and marks the number as
|
||||
signed. */
|
||||
ptr += 1;
|
||||
|
||||
|
|
|
|||
5
main.cc
5
main.cc
|
|
@ -825,6 +825,11 @@ int main(int argc, char*argv[])
|
|||
func(des);
|
||||
}
|
||||
|
||||
if (verbose_flag) {
|
||||
cout << "CALCULATING ISLANDS" << endl;
|
||||
}
|
||||
des->join_islands();
|
||||
|
||||
if (net_path) {
|
||||
if (verbose_flag)
|
||||
cerr<<" dumping netlist to " <<net_path<< "..." <<endl;
|
||||
|
|
|
|||
|
|
@ -227,6 +227,10 @@ void NetScope::run_defparams(Design*des)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Once placed in the parameter map, the expression may
|
||||
// be deleted when evaluated, so give it a copy of this
|
||||
// expression, not the original.
|
||||
val = val->dup_expr();
|
||||
bool flag = targ_scope->replace_parameter(perm_name, val);
|
||||
if (! flag) {
|
||||
cerr << val->get_fileline() << ": warning: parameter "
|
||||
|
|
@ -335,6 +339,14 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
|
|||
unsigned long wid = (msb >= lsb)? msb - lsb : lsb - msb;
|
||||
wid += 1;
|
||||
|
||||
/* If we have a real value convert it to an integer. */
|
||||
if(NetECReal*tmp = dynamic_cast<NetECReal*>(expr)) {
|
||||
verinum nval(tmp->value().as_long64());
|
||||
expr = new NetEConst(nval);
|
||||
expr->set_line(*((*cur).second.expr));
|
||||
(*cur).second.expr = expr;
|
||||
}
|
||||
|
||||
NetEConst*val = dynamic_cast<NetEConst*>(expr);
|
||||
assert(val);
|
||||
|
||||
|
|
@ -348,7 +360,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
|
|||
tmp.has_sign ( (*cur).second.signed_flag );
|
||||
delete val;
|
||||
val = new NetEConst(tmp);
|
||||
expr = val;
|
||||
(*cur).second.expr = expr = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -529,6 +541,10 @@ void NetScope::evaluate_parameters(Design*des)
|
|||
cerr << (*cur).second.get_fileline() << ": internal error: "
|
||||
<< "Unexpected expression type " << (*cur).second.type
|
||||
<< "." << endl;
|
||||
cerr << (*cur).second.get_fileline() << ": : "
|
||||
<< "Parameter name: " << (*cur).first << endl;
|
||||
cerr << (*cur).second.get_fileline() << ": : "
|
||||
<< "Expression is: " << *(*cur).second.expr << endl;
|
||||
ivl_assert((*cur).second, 0);
|
||||
break;
|
||||
}
|
||||
|
|
@ -673,3 +689,14 @@ void Design::delete_process(NetProcTop*top)
|
|||
delete top;
|
||||
}
|
||||
|
||||
void Design::join_islands(void)
|
||||
{
|
||||
if (nodes_ == 0)
|
||||
return;
|
||||
|
||||
NetNode*cur = nodes_->node_next_;
|
||||
do {
|
||||
join_island(cur);
|
||||
cur = cur->node_next_;
|
||||
} while (cur != nodes_->node_next_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,9 @@ NetExpr* NetScope::set_parameter(perm_string key, NetExpr*expr,
|
|||
ivl_assert(file_line, ref.range == 0);
|
||||
ref.range = range_list;
|
||||
ref.set_line(file_line);
|
||||
|
||||
ivl_assert(file_line, type != IVL_VT_NO_TYPE);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +166,7 @@ bool NetScope::replace_parameter(perm_string key, NetExpr*expr)
|
|||
|
||||
/*
|
||||
* This is not really complete (msb, lsb, sign). It is currently only
|
||||
* used to add a genver to the local parameter list.
|
||||
* used to add a genvar to the local parameter list.
|
||||
*/
|
||||
NetExpr* NetScope::set_localparam(perm_string key, NetExpr*expr,
|
||||
const LineInfo&file_line)
|
||||
|
|
@ -272,7 +275,7 @@ const NetFuncDef* NetScope::func_def() const
|
|||
void NetScope::set_module_name(perm_string n)
|
||||
{
|
||||
assert(type_ == MODULE);
|
||||
module_name_ = n; /* NOTE: n mus have been permallocated. */
|
||||
module_name_ = n; /* NOTE: n must have been permallocated. */
|
||||
}
|
||||
|
||||
perm_string NetScope::module_name() const
|
||||
|
|
|
|||
105
net_tran.cc
105
net_tran.cc
|
|
@ -26,6 +26,7 @@
|
|||
# include "compiler.h"
|
||||
# include "netlist.h"
|
||||
# include "netmisc.h"
|
||||
# include "ivl_target_priv.h"
|
||||
# include "ivl_assert.h"
|
||||
|
||||
static bool has_enable(ivl_switch_type_t tt)
|
||||
|
|
@ -42,7 +43,7 @@ static bool has_enable(ivl_switch_type_t tt)
|
|||
}
|
||||
|
||||
NetTran::NetTran(NetScope*scope, perm_string n, ivl_switch_type_t tt)
|
||||
: NetNode(scope, n, has_enable(tt)? 3 : 2)
|
||||
: NetNode(scope, n, has_enable(tt)? 3 : 2), type_(tt)
|
||||
{
|
||||
pin(0).set_dir(Link::PASSIVE); pin(0).set_name(perm_string::literal("A"), 0);
|
||||
pin(1).set_dir(Link::PASSIVE); pin(1).set_name(perm_string::literal("B"), 0);
|
||||
|
|
@ -52,6 +53,108 @@ NetTran::NetTran(NetScope*scope, perm_string n, ivl_switch_type_t tt)
|
|||
}
|
||||
}
|
||||
|
||||
NetTran::NetTran(NetScope*scope, perm_string n, unsigned wid, unsigned part, unsigned off)
|
||||
: NetNode(scope, n, 2), type_(IVL_SW_TRAN_VP), wid_(wid), part_(part), off_(off)
|
||||
{
|
||||
pin(0).set_dir(Link::PASSIVE); pin(0).set_name(perm_string::literal("A"), 0);
|
||||
pin(1).set_dir(Link::PASSIVE); pin(1).set_name(perm_string::literal("B"), 0);
|
||||
}
|
||||
|
||||
NetTran::~NetTran()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned NetTran::vector_width() const
|
||||
{
|
||||
return wid_;
|
||||
}
|
||||
|
||||
unsigned NetTran::part_width() const
|
||||
{
|
||||
return part_;
|
||||
}
|
||||
|
||||
unsigned NetTran::part_offset() const
|
||||
{
|
||||
return off_;
|
||||
}
|
||||
|
||||
void join_island(NetObj*obj)
|
||||
{
|
||||
IslandBranch*branch = dynamic_cast<IslandBranch*> (obj);
|
||||
|
||||
// If this is not even a branch, then stop now.
|
||||
if (branch == 0)
|
||||
return;
|
||||
|
||||
// If this is a branch, but already given to an island, then
|
||||
// stop.
|
||||
if (branch->island)
|
||||
return;
|
||||
|
||||
list<NetObj*> uncommitted_neighbors;
|
||||
|
||||
// Look for neighboring objects that might already be in
|
||||
// islands. If we find something, then join that island.
|
||||
for (unsigned idx = 0 ; idx < obj->pin_count() ; idx += 1) {
|
||||
Nexus*nex = obj->pin(idx).nexus();
|
||||
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||
unsigned pin;
|
||||
NetObj*tmp;
|
||||
cur->cur_link(tmp, pin);
|
||||
|
||||
// Skip self.
|
||||
if (tmp == obj)
|
||||
continue;
|
||||
|
||||
// If tmb is not a branch, then skip it.
|
||||
IslandBranch*tmp_branch = dynamic_cast<IslandBranch*> (tmp);
|
||||
if (tmp_branch == 0)
|
||||
continue;
|
||||
|
||||
// If that is an uncommitted branch, then save
|
||||
// it. When I finally choose an island for self,
|
||||
// these branches will be scanned so that they join
|
||||
// this island as well.
|
||||
if (tmp_branch->island == 0) {
|
||||
uncommitted_neighbors.push_back(tmp);
|
||||
continue;
|
||||
}
|
||||
|
||||
ivl_assert(*obj, branch->island==0 || branch->island==tmp_branch->island);
|
||||
|
||||
// We found an existing island to join. Join it
|
||||
// now. Keep scanning in order to find more neighbors.
|
||||
if (branch->island == 0) {
|
||||
if (debug_elaborate)
|
||||
cerr << obj->get_fileline() << ": debug: "
|
||||
<< "Join branch to existing island." << endl;
|
||||
branch->island = tmp_branch->island;
|
||||
|
||||
} else if (branch->island != tmp_branch->island) {
|
||||
cerr << obj->get_fileline() << ": internal error: "
|
||||
<< "Oops, Found 2 neighboring islands." << endl;
|
||||
ivl_assert(*obj, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If after all that we did not find an island to join, then
|
||||
// start the island not and join it.
|
||||
if (branch->island == 0) {
|
||||
branch->island = new ivl_island_s;
|
||||
branch->island->discipline = 0;
|
||||
if (debug_elaborate)
|
||||
cerr << obj->get_fileline() << ": debug: "
|
||||
<< "Create new island for this branch" << endl;
|
||||
}
|
||||
|
||||
// Now scan all the uncommitted neighbors I found. Calling
|
||||
// join_island() on them will cause them to notice me in the
|
||||
// process, and thus they will join my island. This process
|
||||
// will recurse until all the connected branches join this island.
|
||||
for (list<NetObj*>::iterator cur = uncommitted_neighbors.begin()
|
||||
; cur != uncommitted_neighbors.end() ; cur ++ ) {
|
||||
join_island(*cur);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
netlist.cc
31
netlist.cc
|
|
@ -761,10 +761,6 @@ NetPartSelect::NetPartSelect(NetNet*sig, unsigned off, unsigned wid,
|
|||
pin(0).set_dir(Link::INPUT);
|
||||
pin(1).set_dir(Link::OUTPUT);
|
||||
break;
|
||||
case NetPartSelect::BI:
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
pin(1).set_dir(Link::OUTPUT);
|
||||
break;
|
||||
}
|
||||
pin(0).set_name(perm_string::literal("Part"), 0);
|
||||
pin(1).set_name(perm_string::literal("Vect"), 0);
|
||||
|
|
@ -787,10 +783,6 @@ NetPartSelect::NetPartSelect(NetNet*sig, NetNet*sel,
|
|||
pin(0).set_dir(Link::INPUT);
|
||||
pin(1).set_dir(Link::OUTPUT);
|
||||
break;
|
||||
case NetPartSelect::BI:
|
||||
pin(0).set_dir(Link::PASSIVE);
|
||||
pin(1).set_dir(Link::PASSIVE);
|
||||
break;
|
||||
}
|
||||
pin(2).set_dir(Link::INPUT);
|
||||
|
||||
|
|
@ -857,6 +849,24 @@ const NetScope* NetProcTop::scope() const
|
|||
return scope_;
|
||||
}
|
||||
|
||||
NetCastInt::NetCastInt(NetScope*scope, perm_string n, unsigned width)
|
||||
: NetNode(scope, n, 2), width_(width)
|
||||
{
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
pin(0).set_name(perm_string::literal("O"), 0);
|
||||
pin(1).set_dir(Link::INPUT);
|
||||
pin(1).set_name(perm_string::literal("I"), 0);
|
||||
}
|
||||
|
||||
NetCastReal::NetCastReal(NetScope*scope, perm_string n, bool signed_flag)
|
||||
: NetNode(scope, n, 2), signed_flag_(signed_flag)
|
||||
{
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
pin(0).set_name(perm_string::literal("O"), 0);
|
||||
pin(1).set_dir(Link::INPUT);
|
||||
pin(1).set_name(perm_string::literal("I"), 0);
|
||||
}
|
||||
|
||||
NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt)
|
||||
: NetNode(scope, n, cnt+1), width_(wid)
|
||||
{
|
||||
|
|
@ -1504,6 +1514,11 @@ NetLiteral::~NetLiteral()
|
|||
{
|
||||
}
|
||||
|
||||
ivl_variable_type_t NetLiteral::data_type() const
|
||||
{
|
||||
return IVL_VT_REAL;
|
||||
}
|
||||
|
||||
const verireal& NetLiteral::value_real() const
|
||||
{
|
||||
return real_;
|
||||
|
|
|
|||
82
netlist.h
82
netlist.h
|
|
@ -52,6 +52,7 @@ class Nexus;
|
|||
class NetEvent;
|
||||
class NetNet;
|
||||
class NetNode;
|
||||
class NetObj;
|
||||
class NetProc;
|
||||
class NetProcTop;
|
||||
class NetRelease;
|
||||
|
|
@ -71,6 +72,8 @@ struct functor_t;
|
|||
|
||||
ostream& operator << (ostream&o, ivl_variable_type_t val);
|
||||
|
||||
extern void join_island(NetObj*obj);
|
||||
|
||||
/* =========
|
||||
* A NetObj is anything that has any kind of behavior in the
|
||||
* netlist. Nodes can be gates, registers, etc. and are linked
|
||||
|
|
@ -131,6 +134,20 @@ class NetObj : public Attrib, public virtual LineInfo {
|
|||
const NetExpr* delay3_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Objects that can be island branches are derived from this. (It is
|
||||
* possible for an object to be a NetObj and an IslandBranch.) This is
|
||||
* used to collect island information about the node.
|
||||
*/
|
||||
|
||||
class IslandBranch {
|
||||
public:
|
||||
IslandBranch() : island(0) { }
|
||||
|
||||
public:
|
||||
struct ivl_island_s* island;
|
||||
};
|
||||
|
||||
class Link {
|
||||
|
||||
friend void connect(Link&, Link&);
|
||||
|
|
@ -810,7 +827,7 @@ class NetScope : public Attrib {
|
|||
|
||||
/*
|
||||
* This class implements the LPM_ABS component. The node has a single
|
||||
* input, a signe expression, that it converts to the absolute
|
||||
* input, a signed expression, that it converts to the absolute
|
||||
* value. The gate is simple: pin(0) is the output and pin(1) is the input.
|
||||
*/
|
||||
class NetAbs : public NetNode {
|
||||
|
|
@ -899,6 +916,44 @@ class NetArrayDq : public NetNode {
|
|||
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an IVL_VT_REAL input to a logical value with the
|
||||
* given width. The input is pin(1) and the output is pin(0).
|
||||
*/
|
||||
class NetCastInt : public NetNode {
|
||||
|
||||
public:
|
||||
NetCastInt(NetScope*s, perm_string n, unsigned width);
|
||||
|
||||
unsigned width() const { return width_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
unsigned width_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert an input to IVL_VT_REAL. The input is pin(1), which can be
|
||||
* any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0),
|
||||
* which is IVL_VT_REAL. The conversion interprets the input as an
|
||||
* unsigned value unless the signed_flag is true.
|
||||
*/
|
||||
class NetCastReal : public NetNode {
|
||||
|
||||
public:
|
||||
NetCastReal(NetScope*s, perm_string n, bool signed_flag);
|
||||
|
||||
bool signed_flag() const { return signed_flag_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
bool signed_flag_;
|
||||
};
|
||||
|
||||
/*
|
||||
* This type represents the LPM_CLSHIFT device.
|
||||
*/
|
||||
|
|
@ -1359,19 +1414,31 @@ class NetSysFunc : public NetNode {
|
|||
const struct sfunc_return_type*def_;
|
||||
};
|
||||
|
||||
class NetTran : public NetNode {
|
||||
class NetTran : public NetNode, public IslandBranch {
|
||||
|
||||
public:
|
||||
// Tran devices other than TRAN_VP
|
||||
NetTran(NetScope*scope, perm_string n, ivl_switch_type_t type);
|
||||
// Create a TRAN_VP
|
||||
NetTran(NetScope*scope, perm_string n, unsigned wid,
|
||||
unsigned part, unsigned off);
|
||||
~NetTran();
|
||||
|
||||
ivl_switch_type_t type() const { return type_; }
|
||||
|
||||
// These are only used for IVL_SW_TRAN_PV
|
||||
unsigned vector_width() const;
|
||||
unsigned part_width() const;
|
||||
unsigned part_offset() const;
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
ivl_switch_type_t type_;
|
||||
unsigned wid_;
|
||||
unsigned part_;
|
||||
unsigned off_;
|
||||
};
|
||||
|
||||
/* =========
|
||||
|
|
@ -1588,8 +1655,8 @@ class NetECRealParam : public NetECReal {
|
|||
* selector as the input.
|
||||
*
|
||||
* The NetPartSelect can be output from the signal (i.e. reading a
|
||||
* part), input into the signal, or bi-directional. The DIR method
|
||||
* gives the type of the node.
|
||||
* part) or input into the signal. The DIR method gives the type of
|
||||
* the node.
|
||||
*
|
||||
* VP (Vector-to-Part)
|
||||
* Output pin 0 is the part select, and input pin 1 is connected to
|
||||
|
|
@ -1599,10 +1666,6 @@ class NetECRealParam : public NetECReal {
|
|||
* Output pin 1 is connected to the NetNet, and input pin 0 is the
|
||||
* part select. In this case, the node is driving the NetNet.
|
||||
*
|
||||
* BI (BI-directional)
|
||||
* Pin 0 is the part select and pin 1 is connected to the NetNet, but
|
||||
* the ports are intended to be bi-directional.
|
||||
*
|
||||
* Note that whatever the direction that data is intended to flow,
|
||||
* pin-0 is the part select and pin-1 is connected to the NetNet.
|
||||
*/
|
||||
|
|
@ -1610,7 +1673,7 @@ class NetPartSelect : public NetNode {
|
|||
|
||||
public:
|
||||
// enum for the device direction
|
||||
enum dir_t { VP, PV, BI };
|
||||
enum dir_t { VP, PV};
|
||||
|
||||
explicit NetPartSelect(NetNet*sig,
|
||||
unsigned off, unsigned wid, dir_t dir);
|
||||
|
|
@ -3613,6 +3676,7 @@ class Design {
|
|||
// Iterate over the design...
|
||||
void dump(ostream&) const;
|
||||
void functor(struct functor_t*);
|
||||
void join_islands(void);
|
||||
int emit(struct target_t*) const;
|
||||
|
||||
// This is incremented by elaboration when an error is
|
||||
|
|
|
|||
47
netmisc.cc
47
netmisc.cc
|
|
@ -76,6 +76,46 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val)
|
|||
#endif
|
||||
}
|
||||
|
||||
NetNet* cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid)
|
||||
{
|
||||
if (src->data_type() != IVL_VT_REAL)
|
||||
return src;
|
||||
|
||||
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, wid);
|
||||
tmp->data_type(IVL_VT_LOGIC);
|
||||
tmp->set_line(*src);
|
||||
tmp->local_flag(true);
|
||||
|
||||
NetCastInt*cast = new NetCastInt(scope, scope->local_symbol(), wid);
|
||||
cast->set_line(*src);
|
||||
des->add_node(cast);
|
||||
|
||||
connect(cast->pin(0), tmp->pin(0));
|
||||
connect(cast->pin(1), src->pin(0));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src)
|
||||
{
|
||||
if (src->data_type() == IVL_VT_REAL)
|
||||
return src;
|
||||
|
||||
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE);
|
||||
tmp->data_type(IVL_VT_REAL);
|
||||
tmp->set_line(*src);
|
||||
tmp->local_flag(true);
|
||||
|
||||
NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed());
|
||||
cast->set_line(*src);
|
||||
des->add_node(cast);
|
||||
|
||||
connect(cast->pin(0), tmp->pin(0));
|
||||
connect(cast->pin(1), src->pin(0));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a signed constant to an existing expression. Generate a new
|
||||
* NetEBAdd node that has the input expression and an expression made
|
||||
|
|
@ -123,6 +163,13 @@ NetExpr* make_sub_expr(long val, NetExpr*expr)
|
|||
return res;
|
||||
}
|
||||
|
||||
NetEConst* make_const_x(unsigned long wid)
|
||||
{
|
||||
verinum xxx (verinum::Vx, wid);
|
||||
NetEConst*resx = new NetEConst(xxx);
|
||||
return resx;
|
||||
}
|
||||
|
||||
NetExpr* condition_reduce(NetExpr*expr)
|
||||
{
|
||||
if (expr->expr_width() == 1)
|
||||
|
|
|
|||
12
netmisc.h
12
netmisc.h
|
|
@ -64,6 +64,13 @@ extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w);
|
|||
|
||||
extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w);
|
||||
|
||||
/*
|
||||
* Generate the nodes necessary to cast an expression (a net) to a
|
||||
* real value.
|
||||
*/
|
||||
extern NetNet*cast_to_int(Design*des, NetScope*scope, NetNet*src, unsigned wid);
|
||||
extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src);
|
||||
|
||||
/*
|
||||
* Take the input expression and return a variation that assures that
|
||||
* the expression is 1-bit wide and logical. This reflects the needs
|
||||
|
|
@ -102,6 +109,11 @@ extern NetNet*add_to_net(Design*des, NetNet*sig, long val);
|
|||
extern NetExpr*make_add_expr(NetExpr*expr, long val);
|
||||
extern NetExpr*make_sub_expr(long val, NetExpr*expr);
|
||||
|
||||
/*
|
||||
* Make a NetEConst object that contains only X bits.
|
||||
*/
|
||||
extern NetEConst*make_const_x(unsigned long wid);
|
||||
|
||||
/*
|
||||
* In some cases the lval is accessible as a pointer to the head of
|
||||
* a list of NetAssign_ objects. This function returns the width of
|
||||
|
|
|
|||
101
parse.y
101
parse.y
|
|
@ -323,10 +323,15 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
|
|||
%left K_POW
|
||||
%left UNARY_PREC
|
||||
|
||||
|
||||
/* to resolve dangling else ambiguity. */
|
||||
%nonassoc less_than_K_else
|
||||
%nonassoc K_else
|
||||
|
||||
/* to resolve exclude (... ambiguity */
|
||||
%nonassoc '('
|
||||
%nonassoc K_exclude
|
||||
|
||||
%%
|
||||
|
||||
/* A degenerate source file can be completely empty. */
|
||||
|
|
@ -427,7 +432,7 @@ block_item_decl
|
|||
|
||||
/* Integer declarations are simpler in that they do not have all the
|
||||
trappings of a general variable declaration. All of that is
|
||||
implicit in the "integer" of the declaratin. */
|
||||
implicit in the "integer" of the declaration. */
|
||||
|
||||
| attribute_list_opt K_integer register_variable_list ';'
|
||||
{ pform_set_reg_integer($3);
|
||||
|
|
@ -440,7 +445,7 @@ block_item_decl
|
|||
|
||||
/* real declarations are fairly simple as there is no range of
|
||||
signed flag in the declaration. Create the real as a NetNet::REG
|
||||
with real value. Note that real and realtime are interchangable
|
||||
with real value. Note that real and realtime are interchangeable
|
||||
in this context. */
|
||||
|
||||
| attribute_list_opt K_real real_variable_list ';'
|
||||
|
|
@ -1181,7 +1186,7 @@ expr_primary
|
|||
}
|
||||
|
||||
/* Many of the VAMS built-in functions are available as builtin
|
||||
functions with $system_function equivilents. */
|
||||
functions with $system_function equivalents. */
|
||||
|
||||
| K_acos '(' expression ')'
|
||||
{ perm_string tn = perm_string::literal("$acos");
|
||||
|
|
@ -2195,6 +2200,15 @@ module_item
|
|||
K_endcase
|
||||
{ pform_endgenerate(); }
|
||||
|
||||
/* Handle some anachronistic syntax cases. */
|
||||
| K_generate K_begin module_item_list_opt K_end K_endgenerate
|
||||
{ /* Detect and warn about anachronistic begin/end use */
|
||||
if (generation_flag > GN_VER2001) {
|
||||
warn_count += 1;
|
||||
cerr << @2 << ": warning: Anachronistic use of begin/end to surround generate schemes." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* specify blocks are parsed but ignored. */
|
||||
|
||||
| K_specify K_endspecify
|
||||
|
|
@ -2379,7 +2393,14 @@ parameter_assign_decl
|
|||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_integer
|
||||
{ param_active_range = 0;
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
/* The default range is [31:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
|
|
@ -2388,6 +2409,23 @@ parameter_assign_decl
|
|||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_time
|
||||
{ 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;
|
||||
/* The range is [63:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
parameter_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_real
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = true;
|
||||
|
|
@ -2398,6 +2436,16 @@ parameter_assign_decl
|
|||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_realtime
|
||||
{ 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;
|
||||
}
|
||||
;
|
||||
|
||||
parameter_assign_list
|
||||
|
|
@ -2432,7 +2480,8 @@ parameter_value_range
|
|||
{ $$ = pform_parameter_value_range($1, true, $3, false, $5); }
|
||||
| from_exclude '(' value_range_expression ':' value_range_expression ')'
|
||||
{ $$ = pform_parameter_value_range($1, true, $3, true, $5); }
|
||||
/* | K_exclude expression */
|
||||
| K_exclude expression
|
||||
{ $$ = pform_parameter_value_range(true, false, $2, false, $2); }
|
||||
;
|
||||
|
||||
value_range_expression
|
||||
|
|
@ -2480,17 +2529,41 @@ localparam_assign_decl
|
|||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_NO_TYPE;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_integer
|
||||
{ param_active_range = 0;
|
||||
{ svector<PExpr*>*range_stub = new svector<PExpr*>(2);
|
||||
PExpr*re;
|
||||
re = new PENumber(new verinum(integer_width-1, integer_width));
|
||||
(*range_stub)[0] = re;
|
||||
re = new PENumber(new verinum((uint64_t)0, integer_width));
|
||||
(*range_stub)[1] = re;
|
||||
/* The default range is [31:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = true;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_NO_TYPE;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_time
|
||||
{ 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;
|
||||
/* The range is [63:0] */
|
||||
param_active_range = range_stub;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_real
|
||||
{ param_active_range = 0;
|
||||
|
|
@ -2500,7 +2573,17 @@ localparam_assign_decl
|
|||
localparam_assign_list
|
||||
{ param_active_range = 0;
|
||||
param_active_signed = false;
|
||||
param_active_type = IVL_VT_NO_TYPE;
|
||||
param_active_type = IVL_VT_LOGIC;
|
||||
}
|
||||
| K_realtime
|
||||
{ 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;
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@ unsigned error_count = 0;
|
|||
unsigned warn_count = 0;
|
||||
unsigned long based_size = 0;
|
||||
|
||||
std::ostream& operator << (std::ostream&o, const YYLTYPE&loc)
|
||||
{
|
||||
if (loc.text)
|
||||
o << loc.text << ":";
|
||||
o << loc.first_line;
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
void VLerror(const char*msg)
|
||||
{
|
||||
error_count += 1;
|
||||
|
|
@ -39,20 +48,14 @@ void VLerror(const char*msg)
|
|||
void VLerror(const YYLTYPE&loc, const char*msg)
|
||||
{
|
||||
error_count += 1;
|
||||
if (loc.text)
|
||||
cerr << loc.text << ":";
|
||||
|
||||
cerr << loc.first_line << ": " << msg << endl;
|
||||
cerr << loc << ": " << msg << endl;
|
||||
based_size = 0; /* Clear the base information if we have an error. */
|
||||
}
|
||||
|
||||
void yywarn(const YYLTYPE&loc, const char*msg)
|
||||
{
|
||||
warn_count += 1;
|
||||
if (loc.text)
|
||||
cerr << loc.text << ":";
|
||||
|
||||
cerr << loc.first_line << ": warning: " << msg << endl;
|
||||
cerr << loc << ": warning: " << msg << endl;
|
||||
}
|
||||
|
||||
int VLwrap()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#endif
|
||||
|
||||
# include <list>
|
||||
# include <ostream>
|
||||
# include "compiler.h"
|
||||
# include "pform.h"
|
||||
|
||||
|
|
@ -62,6 +63,8 @@ extern void VLerror(const YYLTYPE&loc, const char*msg);
|
|||
#define yywarn VLwarn
|
||||
extern void VLwarn(const YYLTYPE&loc, const char*msg);
|
||||
|
||||
extern ostream& operator << (ostream&, const YYLTYPE&loc);
|
||||
|
||||
extern unsigned error_count, warn_count;
|
||||
extern unsigned long based_size;
|
||||
|
||||
|
|
|
|||
123
pform.cc
123
pform.cc
|
|
@ -93,40 +93,64 @@ static PScope* lexical_scope = 0;
|
|||
|
||||
void pform_pop_scope()
|
||||
{
|
||||
lexical_scope = lexical_scope->pscope_parent();
|
||||
if (pform_cur_generate) {
|
||||
assert(pform_cur_generate->lexical_scope);
|
||||
PScope*cur = pform_cur_generate->lexical_scope;
|
||||
pform_cur_generate->lexical_scope = cur->pscope_parent();
|
||||
} else {
|
||||
assert(lexical_scope);
|
||||
lexical_scope = lexical_scope->pscope_parent();
|
||||
}
|
||||
}
|
||||
|
||||
PTask* pform_push_task_scope(char*name)
|
||||
{
|
||||
perm_string task_name = lex_strings.make(name);
|
||||
PTask*task = new PTask(task_name, pform_cur_module);
|
||||
|
||||
// Add the task to the current module
|
||||
pform_cur_module->add_task(task->pscope_name(), task);
|
||||
// Make this the current lexical scope
|
||||
lexical_scope = task;
|
||||
PTask*task;
|
||||
if (pform_cur_generate) {
|
||||
task = new PTask(task_name, pform_cur_generate->lexical_scope);
|
||||
pform_cur_generate->tasks[task->pscope_name()] = task;
|
||||
pform_cur_generate->lexical_scope = task;
|
||||
} else {
|
||||
task = new PTask(task_name, lexical_scope);
|
||||
pform_cur_module->tasks[task->pscope_name()] = task;
|
||||
lexical_scope = task;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
PFunction* pform_push_function_scope(char*name)
|
||||
{
|
||||
perm_string func_name = lex_strings.make(name);
|
||||
PFunction*func = new PFunction(func_name, lexical_scope);
|
||||
|
||||
// Add the task to the current module
|
||||
pform_cur_module->add_function(func->pscope_name(), func);
|
||||
// Make this the current lexical scope
|
||||
lexical_scope = func;
|
||||
PFunction*func;
|
||||
if (pform_cur_generate) {
|
||||
func = new PFunction(func_name, pform_cur_generate->lexical_scope);
|
||||
pform_cur_generate->funcs[func->pscope_name()] = func;
|
||||
pform_cur_generate->lexical_scope = func;
|
||||
} else {
|
||||
func = new PFunction(func_name, lexical_scope);
|
||||
pform_cur_module->funcs[func->pscope_name()] = func;
|
||||
lexical_scope = func;
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
PBlock* pform_push_block_scope(char*name, PBlock::BL_TYPE bt)
|
||||
{
|
||||
perm_string block_name = lex_strings.make(name);
|
||||
PBlock*block = new PBlock(block_name, lexical_scope, bt);
|
||||
|
||||
// Make this the current lexical scope
|
||||
lexical_scope = block;
|
||||
PBlock*block;
|
||||
if (pform_cur_generate) {
|
||||
block = new PBlock(block_name, pform_cur_generate->lexical_scope, bt);
|
||||
pform_cur_generate->lexical_scope = block;
|
||||
} else {
|
||||
block = new PBlock(block_name, lexical_scope, bt);
|
||||
lexical_scope = block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
|
@ -138,9 +162,34 @@ PWire*pform_get_wire_in_scope(perm_string name)
|
|||
cannot be within sub-scopes. Only directly in
|
||||
modules. */
|
||||
if (pform_cur_generate)
|
||||
return pform_cur_generate->get_wire(name);
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
return pform_cur_generate->lexical_scope->wires_find(name);
|
||||
else
|
||||
return pform_cur_generate->wires_find(name);
|
||||
else
|
||||
return lexical_scope->wires_find(name);
|
||||
}
|
||||
|
||||
return lexical_scope->wires_find(name);
|
||||
static void pform_put_wire_in_scope(perm_string name, PWire*net)
|
||||
{
|
||||
if (pform_cur_generate)
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
pform_cur_generate->lexical_scope->wires[name] = net;
|
||||
else
|
||||
pform_cur_generate->wires[name] = net;
|
||||
else
|
||||
lexical_scope->wires[name] = net;
|
||||
}
|
||||
|
||||
static void pform_put_behavior_in_scope(PProcess*pp)
|
||||
{
|
||||
if (pform_cur_generate)
|
||||
if (pform_cur_generate->lexical_scope)
|
||||
pform_cur_generate->lexical_scope->behaviors.push_back(pp);
|
||||
else
|
||||
pform_cur_generate->behaviors.push_back(pp);
|
||||
else
|
||||
lexical_scope->behaviors.push_back(pp);
|
||||
}
|
||||
|
||||
void pform_set_default_nettype(NetNet::Type type,
|
||||
|
|
@ -508,7 +557,7 @@ void pform_endgenerate()
|
|||
if (pform_cur_generate != 0) {
|
||||
assert(cur->scheme_type == PGenerate::GS_CASE_ITEM
|
||||
|| pform_cur_generate->scheme_type != PGenerate::GS_CASE);
|
||||
pform_cur_generate->generates.push_back(cur);
|
||||
pform_cur_generate->generate_schemes.push_back(cur);
|
||||
} else {
|
||||
assert(cur->scheme_type != PGenerate::GS_CASE_ITEM);
|
||||
pform_cur_module->generate_schemes.push_back(cur);
|
||||
|
|
@ -1236,7 +1285,7 @@ void pform_make_pgassign_list(svector<PExpr*>*alist,
|
|||
void pform_make_reginit(const struct vlltype&li,
|
||||
perm_string name, PExpr*expr)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
VLerror(li, "internal error: reginit to non-register?");
|
||||
delete expr;
|
||||
|
|
@ -1250,7 +1299,7 @@ void pform_make_reginit(const struct vlltype&li,
|
|||
PProcess*top = new PProcess(PProcess::PR_INITIAL, ass);
|
||||
FILE_NAME(top, li);
|
||||
|
||||
lexical_scope->behaviors.push_back(top);
|
||||
pform_put_behavior_in_scope(top);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1271,7 +1320,7 @@ void pform_module_define_port(const struct vlltype&li,
|
|||
svector<PExpr*>*range,
|
||||
svector<named_pexpr_t*>*attr)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur) {
|
||||
ostringstream msg;
|
||||
msg << name << " definition conflicts with "
|
||||
|
|
@ -1305,7 +1354,7 @@ void pform_module_define_port(const struct vlltype&li,
|
|||
cur->attributes[tmp->name] = tmp->parm;
|
||||
}
|
||||
}
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1388,12 +1437,8 @@ void pform_makewire(const vlltype&li, perm_string name,
|
|||
}
|
||||
}
|
||||
|
||||
if (new_wire_flag) {
|
||||
if (pform_cur_generate)
|
||||
pform_cur_generate->wires[name] = cur;
|
||||
else
|
||||
lexical_scope->wires[name] = cur;
|
||||
}
|
||||
if (new_wire_flag)
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1469,11 +1514,11 @@ void pform_makewire(const vlltype&li,
|
|||
void pform_set_port_type(perm_string name, NetNet::PortType pt,
|
||||
const char*file, unsigned lineno)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
cur = new PWire(name, NetNet::IMPLICIT, NetNet::PIMPLICIT, IVL_VT_NO_TYPE);
|
||||
FILE_NAME(cur, file, lineno);
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
}
|
||||
|
||||
switch (cur->get_port_type()) {
|
||||
|
|
@ -1555,13 +1600,13 @@ svector<PWire*>*pform_make_task_ports(NetNet::PortType pt,
|
|||
|
||||
/* Look for a preexisting wire. If it exists, set the
|
||||
port direction. If not, create it. */
|
||||
PWire*curw = lexical_scope->wires_find(name);
|
||||
PWire*curw = pform_get_wire_in_scope(name);
|
||||
if (curw) {
|
||||
curw->set_port_type(pt);
|
||||
} else {
|
||||
curw = new PWire(name, NetNet::IMPLICIT_REG, pt, vtype);
|
||||
FILE_NAME(curw, file, lineno);
|
||||
lexical_scope->wires[name] = curw;
|
||||
pform_put_wire_in_scope(name, curw);
|
||||
}
|
||||
|
||||
curw->set_signed(signed_flag);
|
||||
|
|
@ -1622,7 +1667,7 @@ void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r)
|
|||
{
|
||||
PWire*cur = 0;
|
||||
if (pform_cur_generate) {
|
||||
cur = pform_cur_generate->get_wire(name);
|
||||
cur = pform_cur_generate->wires_find(name);
|
||||
} else {
|
||||
cur = lexical_scope->wires_find(name);
|
||||
}
|
||||
|
|
@ -1805,13 +1850,13 @@ void pform_set_port_type(const struct vlltype&li,
|
|||
|
||||
static void pform_set_reg_integer(perm_string name)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
cur = new PWire(name, NetNet::INTEGER,
|
||||
NetNet::NOT_A_PORT,
|
||||
IVL_VT_LOGIC);
|
||||
cur->set_signed(true);
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
} else {
|
||||
bool rc = cur->set_wire_type(NetNet::INTEGER);
|
||||
assert(rc);
|
||||
|
|
@ -1839,10 +1884,10 @@ void pform_set_reg_integer(list<perm_string>*names)
|
|||
|
||||
static void pform_set_reg_time(perm_string name)
|
||||
{
|
||||
PWire*cur = lexical_scope->wires_find(name);
|
||||
PWire*cur = pform_get_wire_in_scope(name);
|
||||
if (cur == 0) {
|
||||
cur = new PWire(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_LOGIC);
|
||||
lexical_scope->wires[name] = cur;
|
||||
pform_put_wire_in_scope(name, cur);
|
||||
} else {
|
||||
bool rc = cur->set_wire_type(NetNet::REG);
|
||||
assert(rc);
|
||||
|
|
@ -1901,11 +1946,7 @@ PProcess* pform_make_behavior(PProcess::Type type, Statement*st,
|
|||
delete attr;
|
||||
}
|
||||
|
||||
if (pform_cur_generate)
|
||||
pform_cur_generate->add_behavior(pp);
|
||||
else
|
||||
pform_cur_module->behaviors.push_back(pp);
|
||||
|
||||
pform_put_behavior_in_scope(pp);
|
||||
return pp;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -937,8 +937,8 @@ void PGenerate::dump(ostream&out, unsigned indent) const
|
|||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
for (list<PGenerate*>::const_iterator idx = generates.begin()
|
||||
; idx != generates.end() ; idx++) {
|
||||
for (list<PGenerate*>::const_iterator idx = generate_schemes.begin()
|
||||
; idx != generate_schemes.end() ; idx++) {
|
||||
(*idx)->dump(out, indent+2);
|
||||
}
|
||||
|
||||
|
|
@ -1089,8 +1089,8 @@ void Module::dump(ostream&out) const
|
|||
|
||||
// Dump the task definitions.
|
||||
typedef map<perm_string,PTask*>::const_iterator task_iter_t;
|
||||
for (task_iter_t cur = tasks_.begin()
|
||||
; cur != tasks_.end() ; cur ++) {
|
||||
for (task_iter_t cur = tasks.begin()
|
||||
; cur != tasks.end() ; cur ++) {
|
||||
out << " task " << (*cur).first << ";" << endl;
|
||||
(*cur).second->dump(out, 6);
|
||||
out << " endtask;" << endl;
|
||||
|
|
@ -1098,8 +1098,8 @@ void Module::dump(ostream&out) const
|
|||
|
||||
// Dump the function definitions.
|
||||
typedef map<perm_string,PFunction*>::const_iterator func_iter_t;
|
||||
for (func_iter_t cur = funcs_.begin()
|
||||
; cur != funcs_.end() ; cur ++) {
|
||||
for (func_iter_t cur = funcs.begin()
|
||||
; cur != funcs.end() ; cur ++) {
|
||||
out << " function " << (*cur).first << ";" << endl;
|
||||
(*cur).second->dump(out, 6);
|
||||
out << " endfunction;" << endl;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script manually creates a version.h file.
|
||||
#
|
||||
# It is used when creating a MinGW executable from a Cygwin
|
||||
# hosted git repository. It assumes that git is available.
|
||||
#
|
||||
# sh scripts/CREATE_VERSION.sh
|
||||
#
|
||||
|
||||
echo "Building verion.h with git describe"
|
||||
tmp=`git describe | sed -e 's;\(.*\);#define VERSION_TAG "\1";'`
|
||||
echo "$tmp" > version.h
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
# sh scripts/MAKE_SNAPSHOT.sh 20080428 ~/tmp
|
||||
#
|
||||
# The above assumes that there is a tag "s20080428" at the point
|
||||
# to be snaphot. (The tag has the "s", but the argument to this
|
||||
# to be snapshot. (The tag has the "s", but the argument to this
|
||||
# script does not have the "s"). This script extracts based on the
|
||||
# tag, uses the temporary directory to stage intermediate results,
|
||||
# and finally creates a file called verilog-20080428.tar.gz that
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope, NetFF*ff,
|
|||
delete expr_input;
|
||||
|
||||
/* Detect the case that this is a *synchronous* set/reset. It
|
||||
is not asyncronous because we know the condition is not
|
||||
is not asynchronous because we know the condition is not
|
||||
included in the sensitivity list, but if the if_ case is
|
||||
constant (has no inputs) then we can model this as a
|
||||
synchronous set/reset.
|
||||
|
|
|
|||
89
t-dll-api.cc
89
t-dll-api.cc
|
|
@ -555,6 +555,28 @@ extern "C" unsigned ivl_file_table_size()
|
|||
return fn_vector.size();
|
||||
}
|
||||
|
||||
extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value)
|
||||
{
|
||||
if (flag >= net->flags.size()) {
|
||||
if (value == 0)
|
||||
return 0;
|
||||
else
|
||||
net->flags.resize(flag+1, false);
|
||||
}
|
||||
|
||||
int old_flag = net->flags[flag];
|
||||
net->flags[flag] = value != 0;
|
||||
return old_flag;
|
||||
}
|
||||
|
||||
extern "C" int ivl_island_flag_test(ivl_island_t net, unsigned flag)
|
||||
{
|
||||
if (flag >= net->flags.size())
|
||||
return 0;
|
||||
else
|
||||
return net->flags[flag];
|
||||
}
|
||||
|
||||
extern "C" const char* ivl_logic_attr(ivl_net_logic_t net, const char*key)
|
||||
{
|
||||
assert(net);
|
||||
|
|
@ -782,7 +804,6 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net)
|
|||
switch (net->type) {
|
||||
case IVL_LPM_PART_VP:
|
||||
case IVL_LPM_PART_PV:
|
||||
case IVL_LPM_PART_BI:
|
||||
return net->u_.part.base;
|
||||
default:
|
||||
assert(0);
|
||||
|
|
@ -865,6 +886,8 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx)
|
|||
assert(net);
|
||||
switch (net->type) {
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
assert(idx == 0);
|
||||
return net->u_.arith.a;
|
||||
|
||||
|
|
@ -918,7 +941,6 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx)
|
|||
|
||||
case IVL_LPM_PART_VP:
|
||||
case IVL_LPM_PART_PV:
|
||||
case IVL_LPM_PART_BI:
|
||||
assert(idx <= 1);
|
||||
if (idx == 0)
|
||||
return net->u_.part.a;
|
||||
|
|
@ -1008,20 +1030,19 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx)
|
|||
switch (net->type) {
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
case IVL_LPM_MULT:
|
||||
case IVL_LPM_POW:
|
||||
case IVL_LPM_SUB:
|
||||
assert(idx == 0);
|
||||
return net->u_.arith.q;
|
||||
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_NE:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_NEE:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
case IVL_LPM_MULT:
|
||||
case IVL_LPM_POW:
|
||||
case IVL_LPM_SUB:
|
||||
assert(idx == 0);
|
||||
return net->u_.arith.q;
|
||||
|
||||
|
|
@ -1061,7 +1082,6 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx)
|
|||
|
||||
case IVL_LPM_PART_VP:
|
||||
case IVL_LPM_PART_PV:
|
||||
case IVL_LPM_PART_BI:
|
||||
assert(idx == 0);
|
||||
return net->u_.part.q;
|
||||
|
||||
|
|
@ -1125,6 +1145,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
|
|||
return 0;
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
|
|
@ -1147,6 +1168,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
|
|||
case IVL_LPM_SHIFTL:
|
||||
case IVL_LPM_SHIFTR:
|
||||
return net->u_.shift.signed_flag;
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_SIGN_EXT: // Sign extend is always signed.
|
||||
return 1;
|
||||
case IVL_LPM_SFUNC:
|
||||
|
|
@ -1157,7 +1179,6 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
|
|||
return 0;
|
||||
case IVL_LPM_PART_VP:
|
||||
case IVL_LPM_PART_PV:
|
||||
case IVL_LPM_PART_BI:
|
||||
return net->u_.part.signed_flag;
|
||||
case IVL_LPM_REPEAT:
|
||||
return 0;
|
||||
|
|
@ -1338,6 +1359,15 @@ extern "C" ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net)
|
|||
return net->l.sig;
|
||||
}
|
||||
|
||||
extern "C" ivl_switch_t ivl_nexus_ptr_switch(ivl_nexus_ptr_t net)
|
||||
{
|
||||
if (net == 0)
|
||||
return 0;
|
||||
if (net->type_ != __NEXUS_PTR_SWI)
|
||||
return 0;
|
||||
return net->l.swi;
|
||||
}
|
||||
|
||||
extern "C" const char* ivl_parameter_basename(ivl_parameter_t net)
|
||||
{
|
||||
assert(net);
|
||||
|
|
@ -2162,6 +2192,11 @@ extern "C" const char*ivl_switch_basename(ivl_switch_t net)
|
|||
return net->name;
|
||||
}
|
||||
|
||||
extern "C" ivl_scope_t ivl_switch_scope(ivl_switch_t net)
|
||||
{
|
||||
return net->scope;
|
||||
}
|
||||
|
||||
extern "C" ivl_switch_type_t ivl_switch_type(ivl_switch_t net)
|
||||
{
|
||||
return net->type;
|
||||
|
|
@ -2181,3 +2216,33 @@ extern "C" ivl_nexus_t ivl_switch_enable(ivl_switch_t net)
|
|||
{
|
||||
return net->pins[2];
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_switch_width(ivl_switch_t net)
|
||||
{
|
||||
return net->width;
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_switch_part(ivl_switch_t net)
|
||||
{
|
||||
return net->part;
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_switch_offset(ivl_switch_t net)
|
||||
{
|
||||
return net->offset;
|
||||
}
|
||||
|
||||
extern "C" const char* ivl_switch_file(ivl_switch_t net)
|
||||
{
|
||||
return net->file;
|
||||
}
|
||||
|
||||
extern "C" ivl_island_t ivl_switch_island(ivl_switch_t net)
|
||||
{
|
||||
return net->island;
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_switch_lineno(ivl_switch_t net)
|
||||
{
|
||||
return net->lineno;
|
||||
}
|
||||
|
|
|
|||
107
t-dll.cc
107
t-dll.cc
|
|
@ -1037,9 +1037,14 @@ bool dll_target::tran(const NetTran*net)
|
|||
{
|
||||
struct ivl_switch_s*obj = new struct ivl_switch_s;
|
||||
obj->type = net->type();
|
||||
obj->width = 0;
|
||||
obj->part = 0;
|
||||
obj->offset = 0;
|
||||
obj->name = net->name();
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
obj->island = net->island;
|
||||
assert(obj->scope);
|
||||
assert(obj->island);
|
||||
|
||||
const Nexus*nex;
|
||||
|
||||
|
|
@ -1063,6 +1068,15 @@ bool dll_target::tran(const NetTran*net)
|
|||
obj->pins[2] = 0;
|
||||
}
|
||||
|
||||
if (obj->type == IVL_SW_TRAN_VP) {
|
||||
obj->width = net->vector_width();
|
||||
obj->part = net->part_width();
|
||||
obj->offset= net->part_offset();
|
||||
}
|
||||
|
||||
obj->file = net->get_file();
|
||||
obj->lineno = net->get_lineno();
|
||||
|
||||
switch_attributes(obj, net);
|
||||
scope_add_switch(obj->scope, obj);
|
||||
|
||||
|
|
@ -1536,6 +1550,71 @@ void dll_target::lpm_clshift(const NetCLShift*net)
|
|||
scope_add_lpm(obj->scope, obj);
|
||||
}
|
||||
|
||||
bool dll_target::lpm_cast_int(const NetCastInt*net)
|
||||
{
|
||||
ivl_lpm_t obj = new struct ivl_lpm_s;
|
||||
obj->type = IVL_LPM_CAST_INT;
|
||||
obj->name = net->name(); // NetCastInt names are permallocated
|
||||
assert(net->scope());
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
assert(obj->scope);
|
||||
|
||||
obj->width = net->width();
|
||||
|
||||
const Nexus*nex;
|
||||
|
||||
nex = net->pin(0).nexus();
|
||||
assert(nex->t_cookie());
|
||||
|
||||
obj->u_.arith.q = nex->t_cookie();
|
||||
|
||||
nex = net->pin(1).nexus();
|
||||
assert(nex->t_cookie());
|
||||
obj->u_.arith.a = nex->t_cookie();
|
||||
|
||||
nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
|
||||
nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
|
||||
make_lpm_delays_(obj, net);
|
||||
|
||||
scope_add_lpm(obj->scope, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dll_target::lpm_cast_real(const NetCastReal*net)
|
||||
{
|
||||
ivl_lpm_t obj = new struct ivl_lpm_s;
|
||||
obj->type = IVL_LPM_CAST_REAL;
|
||||
obj->name = net->name(); // NetCastReal names are permallocated
|
||||
assert(net->scope());
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
assert(obj->scope);
|
||||
|
||||
obj->width = 0;
|
||||
obj->u_.arith.signed_flag = net->signed_flag()? 1 : 0;
|
||||
|
||||
const Nexus*nex;
|
||||
|
||||
nex = net->pin(0).nexus();
|
||||
assert(nex->t_cookie());
|
||||
|
||||
obj->u_.arith.q = nex->t_cookie();
|
||||
|
||||
nex = net->pin(1).nexus();
|
||||
assert(nex->t_cookie());
|
||||
obj->u_.arith.a = nex->t_cookie();
|
||||
|
||||
nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
|
||||
nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
|
||||
make_lpm_delays_(obj, net);
|
||||
|
||||
scope_add_lpm(obj->scope, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make out of the NetCompare object an ivl_lpm_s object. The
|
||||
* comparators in ivl_target do not support < or <=, but they can be
|
||||
|
|
@ -1991,9 +2070,6 @@ bool dll_target::part_select(const NetPartSelect*net)
|
|||
case NetPartSelect::PV:
|
||||
obj->type = IVL_LPM_PART_PV;
|
||||
break;
|
||||
case NetPartSelect::BI:
|
||||
obj->type = IVL_LPM_PART_BI;
|
||||
break;
|
||||
}
|
||||
obj->name = net->name(); // NetPartSelect names are permallocated.
|
||||
assert(net->scope());
|
||||
|
|
@ -2047,35 +2123,12 @@ bool dll_target::part_select(const NetPartSelect*net)
|
|||
obj->u_.part.a = nex->t_cookie();
|
||||
break;
|
||||
|
||||
case IVL_LPM_PART_BI:
|
||||
/* For now, handle this exactly the same as a PV */
|
||||
|
||||
/* NetPartSelect:pin(0) is the output pin. */
|
||||
nex = net->pin(0).nexus();
|
||||
assert(nex->t_cookie());
|
||||
|
||||
obj->u_.part.q = nex->t_cookie();
|
||||
|
||||
/* NetPartSelect:pin(1) is the input pin. */
|
||||
nex = net->pin(1).nexus();
|
||||
assert(nex->t_cookie());
|
||||
|
||||
obj->u_.part.a = nex->t_cookie();
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
nexus_lpm_add(obj->u_.part.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
|
||||
|
||||
/* If the device is a PART_BI, then the "input" is also a
|
||||
strength aware output, so attach it to the nexus with
|
||||
strong driver. */
|
||||
if (obj->type == IVL_LPM_PART_BI)
|
||||
nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
|
||||
else
|
||||
nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
|
||||
/* The select input is optional. */
|
||||
if (obj->u_.part.s)
|
||||
|
|
|
|||
7
t-dll.h
7
t-dll.h
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
# include "target.h"
|
||||
# include "ivl_target.h"
|
||||
# include "ivl_target_priv.h"
|
||||
# include "StringHeap.h"
|
||||
# include "netlist.h"
|
||||
# include <vector>
|
||||
|
|
@ -75,6 +76,8 @@ struct dll_target : public target_t, public expr_scan_t {
|
|||
void lpm_abs(const NetAbs*);
|
||||
void lpm_add_sub(const NetAddSub*);
|
||||
bool lpm_array_dq(const NetArrayDq*);
|
||||
bool lpm_cast_int(const NetCastInt*);
|
||||
bool lpm_cast_real(const NetCastReal*);
|
||||
void lpm_clshift(const NetCLShift*);
|
||||
void lpm_compare(const NetCompare*);
|
||||
void lpm_divide(const NetDivide*);
|
||||
|
|
@ -450,9 +453,13 @@ struct ivl_net_logic_s {
|
|||
|
||||
struct ivl_switch_s {
|
||||
ivl_switch_type_t type;
|
||||
unsigned width;
|
||||
unsigned part;
|
||||
unsigned offset;
|
||||
|
||||
perm_string name;
|
||||
ivl_scope_t scope;
|
||||
ivl_island_t island;
|
||||
|
||||
struct ivl_attribute_s*attr;
|
||||
unsigned nattr;
|
||||
|
|
|
|||
14
target.cc
14
target.cc
|
|
@ -107,6 +107,20 @@ bool target_t::lpm_array_dq(const NetArrayDq*)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool target_t::lpm_cast_int(const NetCastInt*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
"Unhandled NetCastInt." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool target_t::lpm_cast_real(const NetCastReal*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
"Unhandled NetCastReal." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void target_t::lpm_clshift(const NetCLShift*)
|
||||
{
|
||||
cerr << "target (" << typeid(*this).name() << "): "
|
||||
|
|
|
|||
2
target.h
2
target.h
|
|
@ -72,6 +72,8 @@ struct target_t {
|
|||
virtual void lpm_add_sub(const NetAddSub*);
|
||||
virtual bool lpm_array_dq(const NetArrayDq*);
|
||||
virtual void lpm_clshift(const NetCLShift*);
|
||||
virtual bool lpm_cast_int(const NetCastInt*);
|
||||
virtual bool lpm_cast_real(const NetCastReal*);
|
||||
virtual void lpm_compare(const NetCompare*);
|
||||
virtual void lpm_divide(const NetDivide*);
|
||||
virtual void lpm_modulo(const NetModulo*);
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ void show_unary_expression(ivl_expr_t net, unsigned ind)
|
|||
break;
|
||||
}
|
||||
|
||||
if (ivl_expr_opcode(net) == '!' && ivl_expr_type(net)==IVL_VT_REAL) {
|
||||
fprintf(out, "%*sERROR: Real argument to unary ! !?\n", ind,"");
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
fprintf(out, "%*s<unary \"%s\" width=%u, %s, type=%s>\n", ind, "",
|
||||
name, width, sign, vt);
|
||||
show_expression(ivl_expr_oper1(net), ind+4);
|
||||
|
|
|
|||
101
tgt-stub/stub.c
101
tgt-stub/stub.c
|
|
@ -240,6 +240,56 @@ static void show_lpm_array(ivl_lpm_t net)
|
|||
}
|
||||
}
|
||||
|
||||
static void show_lpm_cast_int(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
|
||||
fprintf(out, " LPM_CAST_INT %s: <width=%u>\n",
|
||||
ivl_lpm_basename(net), width);
|
||||
|
||||
ivl_nexus_t q = ivl_lpm_q(net,0);
|
||||
ivl_nexus_t a = ivl_lpm_data(net,0);
|
||||
fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0)));
|
||||
fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0)));
|
||||
|
||||
if (type_of_nexus(q) == IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of Q is %s, expecting !real\n",
|
||||
data_type_string(type_of_nexus(q)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
if (type_of_nexus(a) != IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of A is %s, expecting real\n",
|
||||
data_type_string(type_of_nexus(a)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_lpm_cast_real(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
|
||||
fprintf(out, " LPM_CAST_REAL %s: <width=%u>\n",
|
||||
ivl_lpm_basename(net), width);
|
||||
|
||||
ivl_nexus_t q = ivl_lpm_q(net,0);
|
||||
ivl_nexus_t a = ivl_lpm_data(net,0);
|
||||
fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0)));
|
||||
fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0)));
|
||||
|
||||
if (type_of_nexus(q) != IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of Q is %s, expecting real\n",
|
||||
data_type_string(type_of_nexus(q)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
if (type_of_nexus(a) == IVL_VT_REAL) {
|
||||
fprintf(out, " ERROR: Data type of A is %s, expecting !real\n",
|
||||
data_type_string(type_of_nexus(a)));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_lpm_divide(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
|
|
@ -555,38 +605,6 @@ static void show_lpm_part(ivl_lpm_t net)
|
|||
}
|
||||
}
|
||||
|
||||
static void show_lpm_part_bi(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
unsigned base = ivl_lpm_base(net);
|
||||
ivl_nexus_t port_p = ivl_lpm_q(net,0);
|
||||
ivl_nexus_t port_v = ivl_lpm_data(net,0);
|
||||
|
||||
fprintf(out, " LPM_PART_BI %s: <width=%u, base=%u, signed=%d>\n",
|
||||
ivl_lpm_basename(net), width, base, ivl_lpm_signed(net));
|
||||
fprintf(out, " P: %s\n", ivl_nexus_name(port_p));
|
||||
fprintf(out, " V: %s <width=%u>\n", ivl_nexus_name(port_v),
|
||||
width_of_nexus(port_v));
|
||||
|
||||
|
||||
/* The data(0) port must be large enough for the part select. */
|
||||
if (width_of_nexus(ivl_lpm_data(net,0)) < (width+base)) {
|
||||
fprintf(out, " ERROR: Part select is out of range."
|
||||
" Data nexus width=%u, width+base=%u\n",
|
||||
width_of_nexus(ivl_lpm_data(net,0)), width+base);
|
||||
stub_errors += 1;
|
||||
}
|
||||
|
||||
/* The Q vector must be exactly the width of the part select. */
|
||||
if (width_of_nexus(ivl_lpm_q(net,0)) != width) {
|
||||
fprintf(out, " ERROR: Part select input mismatch."
|
||||
" Nexus width=%u, expect width=%u\n",
|
||||
width_of_nexus(ivl_lpm_q(net,0)), width);
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The reduction operators have similar characteristics and are
|
||||
* displayed here.
|
||||
|
|
@ -834,6 +852,14 @@ static void show_lpm(ivl_lpm_t net)
|
|||
show_lpm_array(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_CAST_INT:
|
||||
show_lpm_cast_int(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_CAST_REAL:
|
||||
show_lpm_cast_real(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_DIVIDE:
|
||||
show_lpm_divide(net);
|
||||
break;
|
||||
|
|
@ -905,11 +931,6 @@ static void show_lpm(ivl_lpm_t net)
|
|||
show_lpm_part(net);
|
||||
break;
|
||||
|
||||
/* The BI part select is slightly special. */
|
||||
case IVL_LPM_PART_BI:
|
||||
show_lpm_part_bi(net);
|
||||
break;
|
||||
|
||||
case IVL_LPM_REPEAT:
|
||||
show_lpm_repeat(net);
|
||||
break;
|
||||
|
|
@ -1059,6 +1080,7 @@ static void show_nexus_details(ivl_signal_t net, ivl_nexus_t nex)
|
|||
ivl_net_logic_t log;
|
||||
ivl_lpm_t lpm;
|
||||
ivl_signal_t sig;
|
||||
ivl_switch_t swt;
|
||||
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
|
||||
|
||||
const char*dr0 = str_tab[ivl_nexus_ptr_drive0(ptr)];
|
||||
|
|
@ -1092,6 +1114,11 @@ static void show_nexus_details(ivl_signal_t net, ivl_nexus_t nex)
|
|||
ivl_scope_name(ivl_lpm_scope(lpm)),
|
||||
ivl_lpm_basename(lpm), dr0, dr1);
|
||||
|
||||
} else if ((swt = ivl_nexus_ptr_switch(ptr))) {
|
||||
fprintf(out, " SWITCH %s.%s\n",
|
||||
ivl_scope_name(ivl_switch_scope(swt)),
|
||||
ivl_switch_basename(swt));
|
||||
|
||||
} else if ((con = ivl_nexus_ptr_con(ptr))) {
|
||||
signal_nexus_const(net, ptr, con);
|
||||
|
||||
|
|
|
|||
|
|
@ -52,18 +52,23 @@ void show_switch(ivl_switch_t net)
|
|||
fprintf(out, " rtranif1 %s", name);
|
||||
has_enable = 1;
|
||||
break;
|
||||
case IVL_SW_TRAN_VP:
|
||||
fprintf(out, " tran(VP wid=%u, part=%u, off=%u) %s",
|
||||
ivl_switch_width(net), ivl_switch_part(net),
|
||||
ivl_switch_offset(net), name);
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, " island=%p\n", ivl_switch_island(net));
|
||||
|
||||
ivl_nexus_t nex = ivl_switch_a(net);
|
||||
const char*nex_name = nex? ivl_nexus_name(nex) : "";
|
||||
ivl_variable_type_t nex_type_a = nex? type_of_nexus(nex) : IVL_VT_NO_TYPE;
|
||||
ivl_nexus_t nexa = ivl_switch_a(net);
|
||||
const char*nex_name = nexa? ivl_nexus_name(nexa) : "";
|
||||
ivl_variable_type_t nex_type_a = nexa? type_of_nexus(nexa) : IVL_VT_NO_TYPE;
|
||||
fprintf(out, " A: %s <type=%s>\n", nex_name, data_type_string(nex_type_a));
|
||||
|
||||
nex = ivl_switch_b(net);
|
||||
nex_name = nex? ivl_nexus_name(nex) : "";
|
||||
ivl_variable_type_t nex_type_b = nex? type_of_nexus(nex) : IVL_VT_NO_TYPE;
|
||||
ivl_nexus_t nexb = ivl_switch_b(net);
|
||||
nex_name = nexb? ivl_nexus_name(nexb) : "";
|
||||
ivl_variable_type_t nex_type_b = nexb? type_of_nexus(nexb) : IVL_VT_NO_TYPE;
|
||||
fprintf(out, " B: %s <type=%s>\n", nex_name, data_type_string(nex_type_b));
|
||||
|
||||
/* The A/B pins of the switch must be present, and must match. */
|
||||
|
|
@ -80,13 +85,40 @@ void show_switch(ivl_switch_t net)
|
|||
stub_errors += 1;
|
||||
}
|
||||
|
||||
if (ivl_switch_type(net) == IVL_SW_TRAN_VP) {
|
||||
/* The TRAN_VP nodes are special in that the specific
|
||||
width matters for each port and should be exactly
|
||||
right for both. */
|
||||
if (width_of_nexus(nexa) != ivl_switch_width(net)) {
|
||||
fprintf(out, " A: ERROR: part vector nexus "
|
||||
"width=%u, expecting width=%u\n",
|
||||
width_of_nexus(nexa), ivl_switch_width(net));
|
||||
stub_errors += 1;
|
||||
}
|
||||
if (width_of_nexus(nexb) != ivl_switch_part(net)) {
|
||||
fprintf(out, " B: ERROR: part select nexus "
|
||||
"width=%u, expecting width=%u\n",
|
||||
width_of_nexus(nexb), ivl_switch_part(net));
|
||||
stub_errors += 1;
|
||||
}
|
||||
} else {
|
||||
/* All other TRAN nodes will have matching vector
|
||||
widths, but the actual value doesn't matter. */
|
||||
if (width_of_nexus(nexa) != width_of_nexus(nexb)) {
|
||||
fprintf(out, " A/B: ERROR: Width of ports don't match"
|
||||
": A=%u, B=%u\n",
|
||||
width_of_nexus(nexa), width_of_nexus(nexb));
|
||||
stub_errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_enable) {
|
||||
nex = ivl_switch_enable(net);
|
||||
nex_name = nex? ivl_nexus_name(nex) : "";
|
||||
ivl_nexus_t nexe = ivl_switch_enable(net);
|
||||
nex_name = nexe? ivl_nexus_name(nexe) : "";
|
||||
fprintf(out, " E: %s\n", nex_name);
|
||||
if (width_of_nexus(nex) != 1) {
|
||||
if (width_of_nexus(nexe) != 1) {
|
||||
fprintf(out, " E: ERROR: Nexus width is %u\n",
|
||||
width_of_nexus(nex));
|
||||
width_of_nexus(nexe));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ dep:
|
|||
$(CC) $(CPPFLAGS) $(CFLAGS) -MD -c $< -o $*.o
|
||||
mv $*.d dep
|
||||
|
||||
O = vvp.o draw_mux.o draw_ufunc.o draw_vpi.o eval_bool.o eval_expr.o \
|
||||
O = vvp.o draw_mux.o draw_net_input.o draw_switch.o draw_ufunc.o draw_vpi.o \
|
||||
eval_bool.o eval_expr.o \
|
||||
eval_real.o modpath.o vector.o \
|
||||
vvp_process.o vvp_scope.o
|
||||
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ static void draw_lpm_mux_ab(ivl_lpm_t net, const char*muxz)
|
|||
|
||||
const char*dly = "";
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64));
|
||||
assert(number_is_immediate(d_fall, 64));
|
||||
assert(number_is_immediate(d_decay, 64));
|
||||
assert(number_is_immediate(d_rise, 64, 0));
|
||||
assert(number_is_immediate(d_fall, 64, 0));
|
||||
assert(number_is_immediate(d_decay, 64, 0));
|
||||
dly = "/d";
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
net, get_number_immediate(d_rise),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,689 @@
|
|||
/*
|
||||
* Copyright (c) 2001-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 "vvp_priv.h"
|
||||
#ifdef HAVE_MALLOC_H
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
# include <stdlib.h>
|
||||
# include <math.h>
|
||||
# include <string.h>
|
||||
# include <inttypes.h>
|
||||
# include <assert.h>
|
||||
|
||||
#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex)
|
||||
{
|
||||
unsigned idx;
|
||||
ivl_signal_type_t out = IVL_SIT_TRI;
|
||||
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ivl_signal_type_t stype;
|
||||
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
|
||||
ivl_signal_t sig = ivl_nexus_ptr_sig(ptr);
|
||||
if (sig == 0)
|
||||
continue;
|
||||
|
||||
stype = ivl_signal_type(sig);
|
||||
if (stype == IVL_SIT_REG)
|
||||
continue;
|
||||
if (stype == IVL_SIT_TRI)
|
||||
continue;
|
||||
if (stype == IVL_SIT_NONE)
|
||||
continue;
|
||||
out = stype;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void draw_C4_repeated_constant(char bit_char, unsigned width)
|
||||
{
|
||||
unsigned idx;
|
||||
|
||||
fprintf(vvp_out, "C4<");
|
||||
for (idx = 0 ; idx < width ; idx += 1)
|
||||
fprintf(vvp_out, "%c", bit_char);
|
||||
|
||||
fprintf(vvp_out, ">");
|
||||
}
|
||||
|
||||
static char* draw_C4_to_string(ivl_net_const_t cptr)
|
||||
{
|
||||
const char*bits = ivl_const_bits(cptr);
|
||||
unsigned idx;
|
||||
|
||||
size_t result_len = 5 + ivl_const_width(cptr);
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
strcpy(dp, "C4<");
|
||||
dp += strlen(dp);
|
||||
|
||||
for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) {
|
||||
char bitchar = bits[ivl_const_width(cptr)-idx-1];
|
||||
*dp++ = bitchar;
|
||||
assert((dp - result) < result_len);
|
||||
}
|
||||
|
||||
strcpy(dp, ">");
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* draw_C8_to_string(ivl_net_const_t cptr,
|
||||
ivl_drive_t dr0, ivl_drive_t dr1)
|
||||
{
|
||||
size_t nresult = 5 + 3*ivl_const_width(cptr);
|
||||
char*result = malloc(nresult);
|
||||
const char*bits = ivl_const_bits(cptr);
|
||||
unsigned idx;
|
||||
|
||||
char dr0c = "01234567"[dr0];
|
||||
char dr1c = "01234567"[dr1];
|
||||
char*dp = result;
|
||||
|
||||
strcpy(dp, "C8<");
|
||||
dp += strlen(dp);
|
||||
|
||||
for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) {
|
||||
switch (bits[ivl_const_width(cptr)-idx-1]) {
|
||||
case '0':
|
||||
*dp++ = dr0c;
|
||||
*dp++ = dr0c;
|
||||
*dp++ = '0';
|
||||
break;
|
||||
case '1':
|
||||
*dp++ = dr1c;
|
||||
*dp++ = dr1c;
|
||||
*dp++ = '1';
|
||||
break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
*dp++ = dr0c;
|
||||
*dp++ = dr1c;
|
||||
*dp++ = 'x';
|
||||
break;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
*dp++ = '0';
|
||||
*dp++ = '0';
|
||||
*dp++ = 'z';
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
assert(dp - result < nresult);
|
||||
}
|
||||
|
||||
strcpy(dp, ">");
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct vvp_nexus_data*new_nexus_data()
|
||||
{
|
||||
struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data));
|
||||
return data;
|
||||
}
|
||||
|
||||
static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr)
|
||||
{
|
||||
if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG)
|
||||
return 1;
|
||||
if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG)
|
||||
return 1;
|
||||
|
||||
ivl_net_logic_t log = ivl_nexus_ptr_log(nptr);
|
||||
if (log != 0) {
|
||||
/* These logic gates are able to generate unusual
|
||||
strength values and so their outputs are considered
|
||||
strength aware. */
|
||||
if (ivl_logic_type(log) == IVL_LO_BUFIF0)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_BUFIF1)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_PMOS)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_NMOS)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_CMOS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a nexus, look for a signal that has module delay
|
||||
* paths. Return that signal. (There should be no more than 1.) If we
|
||||
* don't find any, then return nil.
|
||||
*/
|
||||
static ivl_signal_t find_modpath(ivl_nexus_t nex)
|
||||
{
|
||||
unsigned idx;
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx);
|
||||
ivl_signal_t sig = ivl_nexus_ptr_sig(ptr);
|
||||
if (sig == 0)
|
||||
continue;
|
||||
if (ivl_signal_npath(sig) == 0)
|
||||
continue;
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void str_repeat(char*buf, const char*str, unsigned rpt)
|
||||
{
|
||||
unsigned idx;
|
||||
size_t len = strlen(str);
|
||||
for (idx = 0 ; idx < rpt ; idx += 1) {
|
||||
strcpy(buf, str);
|
||||
buf += len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function takes a nexus and looks for an input functor. It then
|
||||
* draws to the output a string that represents that functor. What we
|
||||
* are trying to do here is find the input to the net that is attached
|
||||
* to this nexus.
|
||||
*/
|
||||
|
||||
static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
||||
{
|
||||
unsigned nptr_pin = ivl_nexus_ptr_pin(nptr);
|
||||
ivl_net_const_t cptr;
|
||||
ivl_net_logic_t lptr;
|
||||
ivl_signal_t sptr;
|
||||
ivl_lpm_t lpm;
|
||||
|
||||
lptr = ivl_nexus_ptr_log(nptr);
|
||||
if (lptr && (ivl_logic_type(lptr) == IVL_LO_BUFZ) && (nptr_pin == 0))
|
||||
do {
|
||||
if (! can_elide_bufz(lptr, nptr))
|
||||
break;
|
||||
|
||||
return strdup(draw_net_input(ivl_logic_pin(lptr, 1)));
|
||||
} while(0);
|
||||
|
||||
/* If this is a pulldown device, then there is a single pin
|
||||
that drives a constant value to the entire width of the
|
||||
vector. The driver normally drives a pull0 value, so a C8<>
|
||||
constant is appropriate, but if the drive is really strong,
|
||||
then we can draw a C4<> constant instead. */
|
||||
if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) {
|
||||
if (ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) {
|
||||
size_t result_len = ivl_logic_width(lptr) + 5;
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
strcpy(dp, "C4<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, "0", ivl_logic_width(lptr));
|
||||
dp += ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
} else {
|
||||
char val[4];
|
||||
size_t result_len = 3*ivl_logic_width(lptr) + 5;
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
|
||||
val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)];
|
||||
val[1] = val[0];
|
||||
val[2] = '0';
|
||||
val[3] = 0;
|
||||
|
||||
strcpy(dp, "C8<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, val, ivl_logic_width(lptr));
|
||||
dp += 3*ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) {
|
||||
if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) {
|
||||
size_t result_len = 5 + ivl_logic_width(lptr);
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
strcpy(dp, "C4<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, "1", ivl_logic_width(lptr));
|
||||
dp += ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
} else {
|
||||
char val[4];
|
||||
size_t result_len = 5 + 3*ivl_logic_width(lptr);
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
|
||||
val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)];
|
||||
val[1] = val[0];
|
||||
val[2] = '1';
|
||||
val[3] = 0;
|
||||
|
||||
strcpy(dp, "C8<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, val, ivl_logic_width(lptr));
|
||||
dp += 3*ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (lptr && (nptr_pin == 0)) {
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p", lptr);
|
||||
return strdup(tmp);
|
||||
}
|
||||
|
||||
sptr = ivl_nexus_ptr_sig(nptr);
|
||||
if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) {
|
||||
char tmp[128];
|
||||
/* Input is a .var. This device may be a non-zero pin
|
||||
because it may be an array of reg vectors. */
|
||||
snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin);
|
||||
|
||||
if (ivl_signal_array_count(sptr) > 1) {
|
||||
fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n",
|
||||
sptr, nptr_pin, sptr, nptr_pin);
|
||||
}
|
||||
|
||||
return strdup(tmp);
|
||||
}
|
||||
|
||||
cptr = ivl_nexus_ptr_con(nptr);
|
||||
if (cptr) {
|
||||
/* Constants should have exactly 1 pin, with a literal value. */
|
||||
assert(nptr_pin == 0);
|
||||
char *result = 0;
|
||||
|
||||
switch (ivl_const_type(cptr)) {
|
||||
case IVL_VT_LOGIC:
|
||||
case IVL_VT_BOOL:
|
||||
if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG)
|
||||
&& (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) {
|
||||
|
||||
result = draw_C4_to_string(cptr);
|
||||
|
||||
} else {
|
||||
result = draw_C8_to_string(cptr,
|
||||
ivl_nexus_ptr_drive0(nptr),
|
||||
ivl_nexus_ptr_drive1(nptr));
|
||||
}
|
||||
break;
|
||||
|
||||
case IVL_VT_REAL:
|
||||
result = draw_Cr_to_string(ivl_const_real(cptr));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ivl_expr_t d_rise = ivl_const_delay(cptr, 0);
|
||||
ivl_expr_t d_fall = ivl_const_delay(cptr, 1);
|
||||
ivl_expr_t d_decay = ivl_const_delay(cptr, 2);
|
||||
|
||||
/* We have a delayed constant, so we need to build some code. */
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64, 0));
|
||||
assert(number_is_immediate(d_fall, 64, 0));
|
||||
assert(number_is_immediate(d_decay, 64, 0));
|
||||
|
||||
fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, "
|
||||
"C4<0>, C4<0>, C4<0>;\n", cptr, result);
|
||||
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
cptr, get_number_immediate(d_rise),
|
||||
get_number_immediate(d_rise),
|
||||
get_number_immediate(d_rise), cptr);
|
||||
|
||||
free(result);
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p", cptr);
|
||||
result = strdup(tmp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
lpm = ivl_nexus_ptr_lpm(nptr);
|
||||
if (lpm) switch (ivl_lpm_type(lpm)) {
|
||||
|
||||
case IVL_LPM_FF:
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_ARRAY:
|
||||
case IVL_LPM_CAST_INT:
|
||||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CONCAT:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
case IVL_LPM_CMP_NEE:
|
||||
case IVL_LPM_RE_AND:
|
||||
case IVL_LPM_RE_OR:
|
||||
case IVL_LPM_RE_XOR:
|
||||
case IVL_LPM_RE_NAND:
|
||||
case IVL_LPM_RE_NOR:
|
||||
case IVL_LPM_RE_XNOR:
|
||||
case IVL_LPM_SFUNC:
|
||||
case IVL_LPM_SHIFTL:
|
||||
case IVL_LPM_SHIFTR:
|
||||
case IVL_LPM_SIGN_EXT:
|
||||
case IVL_LPM_SUB:
|
||||
case IVL_LPM_MULT:
|
||||
case IVL_LPM_MUX:
|
||||
case IVL_LPM_POW:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
case IVL_LPM_UFUNC:
|
||||
case IVL_LPM_PART_VP:
|
||||
case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */
|
||||
case IVL_LPM_REPEAT:
|
||||
if (ivl_lpm_q(lpm, 0) == nex) {
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p", lpm);
|
||||
return strdup(tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
fprintf(stderr, "internal error: no input to nexus %s\n",
|
||||
ivl_nexus_name(nex));
|
||||
assert(0);
|
||||
return strdup("C<z>");
|
||||
}
|
||||
|
||||
static char* draw_island_port(ivl_island_t island,
|
||||
ivl_nexus_t nex, const char*src)
|
||||
{
|
||||
char result[64];
|
||||
if (ivl_island_flag_test(island,0) == 0) {
|
||||
fprintf(vvp_out, "I%p .island tran;\n", island);
|
||||
ivl_island_flag_set(island,0,1);
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "p%p .port I%p, %s;\n", nex, island, src);
|
||||
snprintf(result, sizeof result, "p%p", nex);
|
||||
|
||||
return strdup(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function draws the input to a net into a string. What that
|
||||
* means is that it returns a static string that can be used to
|
||||
* represent a resolved driver to a nexus. If there are multiple
|
||||
* drivers to the nexus, then it writes out the resolver declarations
|
||||
* needed to perform strength resolution.
|
||||
*
|
||||
* The string that this returns is malloced, and that means that the
|
||||
* caller must free the string or store it permanently. This function
|
||||
* does *not* check for a previously calculated string. Use the
|
||||
* draw_net_input for the general case.
|
||||
*/
|
||||
/* Omit LPMPART_BI device pin-data(0) drivers. */
|
||||
# define OMIT_PART_BI_DATA 0x0001
|
||||
|
||||
char* draw_net_input_x(ivl_nexus_t nex,
|
||||
ivl_nexus_ptr_t omit_ptr, int omit_flags,
|
||||
struct vvp_nexus_data*nex_data)
|
||||
{
|
||||
ivl_island_t island = 0;
|
||||
ivl_signal_type_t res;
|
||||
char result[512];
|
||||
unsigned idx;
|
||||
int level;
|
||||
unsigned ndrivers = 0;
|
||||
static ivl_nexus_ptr_t *drivers = 0x0;
|
||||
static unsigned adrivers = 0;
|
||||
|
||||
const char*resolv_type;
|
||||
|
||||
char*nex_private = 0;
|
||||
|
||||
/* Accumulate nex_data flags. */
|
||||
int nex_flags = 0;
|
||||
|
||||
res = signal_type_of_nexus(nex);
|
||||
switch (res) {
|
||||
case IVL_SIT_TRI:
|
||||
resolv_type = "tri";
|
||||
break;
|
||||
case IVL_SIT_TRI0:
|
||||
resolv_type = "tri0";
|
||||
nex_flags |= VVP_NEXUS_DATA_STR;
|
||||
break;
|
||||
case IVL_SIT_TRI1:
|
||||
resolv_type = "tri1";
|
||||
nex_flags |= VVP_NEXUS_DATA_STR;
|
||||
break;
|
||||
case IVL_SIT_TRIAND:
|
||||
resolv_type = "triand";
|
||||
break;
|
||||
case IVL_SIT_TRIOR:
|
||||
resolv_type = "trior";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res);
|
||||
assert(0);
|
||||
resolv_type = "tri";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ivl_lpm_t lpm_tmp;
|
||||
ivl_switch_t sw = 0;
|
||||
ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx);
|
||||
|
||||
/* If this object is part of an island, then we'll be
|
||||
making a port. Save the island cookie. */
|
||||
if ( (sw = ivl_nexus_ptr_switch(nptr)) ) {
|
||||
island = ivl_switch_island(sw);
|
||||
}
|
||||
|
||||
/* If we are supposed to skip LPM_PART_BI data pins,
|
||||
check that this driver is that. */
|
||||
if ((omit_flags&OMIT_PART_BI_DATA)
|
||||
&& (lpm_tmp = ivl_nexus_ptr_lpm(nptr))
|
||||
&& (nex == ivl_lpm_data(lpm_tmp,0)))
|
||||
continue;
|
||||
|
||||
if (nptr == omit_ptr)
|
||||
continue;
|
||||
|
||||
/* Skip input only pins. */
|
||||
if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ)
|
||||
&& (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ))
|
||||
continue;
|
||||
|
||||
/* Mark the strength-aware flag if the driver can
|
||||
generate values other than the standard "6"
|
||||
strength. */
|
||||
if (nexus_drive_is_strength_aware(nptr))
|
||||
nex_flags |= VVP_NEXUS_DATA_STR;
|
||||
|
||||
/* Save this driver. */
|
||||
if (ndrivers >= adrivers) {
|
||||
adrivers += 4;
|
||||
drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t));
|
||||
assert(drivers);
|
||||
}
|
||||
drivers[ndrivers] = nptr;
|
||||
ndrivers += 1;
|
||||
}
|
||||
|
||||
/* If the caller is collecting nexus information, then save
|
||||
the nexus driver count in the nex_data. */
|
||||
if (nex_data) {
|
||||
nex_data->drivers_count = ndrivers;
|
||||
nex_data->flags |= nex_flags;
|
||||
}
|
||||
|
||||
/* If the nexus has no drivers, then send a constant HiZ into
|
||||
the net. */
|
||||
if (ndrivers == 0) {
|
||||
unsigned idx, wid = width_of_nexus(nex);
|
||||
char*tmp = malloc(wid + 5);
|
||||
nex_private = tmp;
|
||||
strcpy(tmp, "C4<");
|
||||
tmp += strlen(tmp);
|
||||
switch (res) {
|
||||
case IVL_SIT_TRI:
|
||||
for (idx = 0 ; idx < wid ; idx += 1)
|
||||
*tmp++ = 'z';
|
||||
break;
|
||||
case IVL_SIT_TRI0:
|
||||
for (idx = 0 ; idx < wid ; idx += 1)
|
||||
*tmp++ = '0';
|
||||
break;
|
||||
case IVL_SIT_TRI1:
|
||||
for (idx = 0 ; idx < wid ; idx += 1)
|
||||
*tmp++ = '1';
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
*tmp++ = '>';
|
||||
*tmp = 0;
|
||||
if (island) {
|
||||
char*tmp = draw_island_port(island, nex, nex_private);
|
||||
free(nex_private);
|
||||
nex_private = tmp;
|
||||
}
|
||||
return nex_private;
|
||||
}
|
||||
|
||||
|
||||
/* If the nexus has exactly one driver, then simply draw
|
||||
it. Note that this will *not* work if the nexus is not a
|
||||
TRI type nexus. */
|
||||
if (ndrivers == 1 && res == IVL_SIT_TRI) {
|
||||
ivl_signal_t path_sig = find_modpath(nex);
|
||||
if (path_sig) {
|
||||
char*nex_str = draw_net_input_drive(nex, drivers[0]);
|
||||
char modpath_label[64];
|
||||
snprintf(modpath_label, sizeof modpath_label,
|
||||
"V_%p/m", path_sig);
|
||||
nex_private = strdup(modpath_label);
|
||||
draw_modpath(path_sig, nex_str);
|
||||
|
||||
} else {
|
||||
nex_private = draw_net_input_drive(nex, drivers[0]);
|
||||
}
|
||||
if (island) {
|
||||
char*tmp = draw_island_port(island, nex, nex_private);
|
||||
free(nex_private);
|
||||
nex_private = tmp;
|
||||
}
|
||||
return nex_private;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (ndrivers) {
|
||||
unsigned int inst;
|
||||
for (inst = 0; inst < ndrivers; inst += 4) {
|
||||
if (ndrivers > 4)
|
||||
fprintf(vvp_out, "RS_%p/%d/%d .resolv tri",
|
||||
nex, level, inst);
|
||||
else
|
||||
fprintf(vvp_out, "RS_%p .resolv %s",
|
||||
nex, resolv_type);
|
||||
|
||||
for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) {
|
||||
if (level) {
|
||||
fprintf(vvp_out, ", RS_%p/%d/%d",
|
||||
nex, level - 1, idx*4);
|
||||
} else {
|
||||
char*drive = draw_net_input_drive(nex, drivers[idx]);
|
||||
fprintf(vvp_out, ", %s", drive);
|
||||
free(drive);
|
||||
}
|
||||
}
|
||||
for ( ; idx < inst+4 ; idx += 1) {
|
||||
fprintf(vvp_out, ", ");
|
||||
draw_C4_repeated_constant('z',width_of_nexus(nex));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
if (ndrivers > 4)
|
||||
ndrivers = (ndrivers+3) / 4;
|
||||
else
|
||||
ndrivers = 0;
|
||||
level += 1;
|
||||
}
|
||||
|
||||
snprintf(result, sizeof result, "RS_%p", nex);
|
||||
|
||||
if (island)
|
||||
nex_private = draw_island_port(island, nex, result);
|
||||
else
|
||||
nex_private = strdup(result);
|
||||
|
||||
return nex_private;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a cached description of the nexus input, or create one if this
|
||||
* nexus has not been cached yet. This is a wrapper for the common
|
||||
* case call to draw_net_input_x.
|
||||
*/
|
||||
const char*draw_net_input(ivl_nexus_t nex)
|
||||
{
|
||||
struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*)
|
||||
ivl_nexus_get_private(nex);
|
||||
|
||||
/* If this nexus already has a label, then its input is
|
||||
already figured out. Just return the existing label. */
|
||||
if (nex_data && nex_data->net_input)
|
||||
return nex_data->net_input;
|
||||
|
||||
if (nex_data == 0) {
|
||||
nex_data = new_nexus_data();
|
||||
ivl_nexus_set_private(nex, nex_data);
|
||||
}
|
||||
|
||||
assert(nex_data->net_input == 0);
|
||||
nex_data->net_input = draw_net_input_x(nex, 0, 0, nex_data);
|
||||
|
||||
return nex_data->net_input;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
* General Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
# include "vvp_priv.h"
|
||||
# include <assert.h>
|
||||
#ifdef HAVE_MALLOC_H
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
|
||||
static void draw_tran_island(ivl_island_t island)
|
||||
{
|
||||
fprintf(vvp_out, "I%p .island tran;\n", island);
|
||||
ivl_island_flag_set(island, 0, 1);
|
||||
}
|
||||
|
||||
void draw_switch_in_scope(ivl_switch_t sw)
|
||||
{
|
||||
ivl_island_t island = ivl_switch_island(sw);
|
||||
if (ivl_island_flag_test(island, 0) == 0)
|
||||
draw_tran_island(island);
|
||||
|
||||
ivl_nexus_t nex_a = ivl_switch_a(sw);
|
||||
assert(nex_a);
|
||||
const char*str_a = draw_net_input(nex_a);
|
||||
|
||||
ivl_nexus_t nex_b = ivl_switch_b(sw);
|
||||
assert(nex_b);
|
||||
const char*str_b = draw_net_input(nex_b);
|
||||
|
||||
ivl_nexus_t enable = ivl_switch_enable(sw);
|
||||
const char*str_e = 0;
|
||||
if (enable)
|
||||
str_e = draw_net_input(enable);
|
||||
|
||||
|
||||
switch (ivl_switch_type(sw)) {
|
||||
case IVL_SW_TRAN:
|
||||
fprintf(vvp_out, " .tran");
|
||||
break;
|
||||
case IVL_SW_TRANIF0:
|
||||
fprintf(vvp_out, " .tranif0");
|
||||
break;
|
||||
case IVL_SW_TRANIF1:
|
||||
fprintf(vvp_out, " .tranif1");
|
||||
break;
|
||||
case IVL_SW_TRAN_VP:
|
||||
fprintf(vvp_out, " .tranvp %u %u %u,",
|
||||
ivl_switch_width(sw), ivl_switch_part(sw), ivl_switch_offset(sw));
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s:%u: sorry: vvp target does not support switch modeling.\n",
|
||||
ivl_switch_file(sw), ivl_switch_lineno(sw));
|
||||
vvp_errors += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(vvp_out, " I%p, %s %s", island, str_a, str_b);
|
||||
if (enable)
|
||||
fprintf(vvp_out, ", %s", str_e);
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ static int is_fixed_memory_word(ivl_expr_t net)
|
|||
if (ivl_signal_type(sig) == IVL_SIT_REG)
|
||||
return 0;
|
||||
|
||||
if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned)))
|
||||
if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned), 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
|
@ -182,7 +182,7 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
|||
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))) {
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
|
|
@ -204,19 +204,72 @@ static void draw_vpi_taskfunc_args(const char*call_string,
|
|||
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))) {
|
||||
if (number_is_immediate(word_ex, 8*sizeof(unsigned), 0)) {
|
||||
use_word = get_number_immediate(word_ex);
|
||||
word_ex = 0;
|
||||
}
|
||||
}
|
||||
if (word_ex)
|
||||
break;
|
||||
|
||||
snprintf(buffer, sizeof buffer, "&A<v%p, %u>", sig, use_word);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Everything else will need to be evaluated and
|
||||
passed as a constant to the vpi task. */
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
static void draw_eval_expr_dest(ivl_expr_t exp, struct vector_info dest,
|
||||
int ok_flags);
|
||||
static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
||||
int add_index, unsigned long immediate);
|
||||
int add_index, long immediate);
|
||||
|
||||
int number_is_unknown(ivl_expr_t ex)
|
||||
{
|
||||
|
|
@ -54,9 +54,11 @@ int number_is_unknown(ivl_expr_t ex)
|
|||
* above bitX. The maximum size of the immediate may vary, so use
|
||||
* lim_wid at the width limit to use.
|
||||
*/
|
||||
int number_is_immediate(ivl_expr_t ex, unsigned lim_wid)
|
||||
int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_ok_flag)
|
||||
{
|
||||
const char*bits;
|
||||
unsigned nbits = ivl_expr_width(ex);
|
||||
char pad_bit = '0';
|
||||
unsigned idx;
|
||||
|
||||
if (ivl_expr_type(ex) != IVL_EX_NUMBER
|
||||
|
|
@ -64,20 +66,23 @@ int number_is_immediate(ivl_expr_t ex, unsigned lim_wid)
|
|||
return 0;
|
||||
|
||||
bits = ivl_expr_bits(ex);
|
||||
for (idx = lim_wid ; idx < ivl_expr_width(ex) ; idx += 1)
|
||||
if (bits[idx] != '0')
|
||||
return 0;
|
||||
|
||||
/* Negative numbers are not "immediate". */
|
||||
if (ivl_expr_signed(ex) && bits[ivl_expr_width(ex)-1]=='1')
|
||||
if (ivl_expr_signed(ex) && bits[nbits-1]=='1')
|
||||
pad_bit = '1';
|
||||
|
||||
if (pad_bit == '1' && !negative_ok_flag)
|
||||
return 0;
|
||||
|
||||
for (idx = lim_wid ; idx < nbits ; idx += 1)
|
||||
if (bits[idx] != pad_bit)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned long get_number_immediate(ivl_expr_t ex)
|
||||
long get_number_immediate(ivl_expr_t ex)
|
||||
{
|
||||
unsigned long imm = 0;
|
||||
long imm = 0;
|
||||
unsigned idx;
|
||||
|
||||
switch (ivl_expr_type(ex)) {
|
||||
|
|
@ -92,11 +97,14 @@ unsigned long get_number_immediate(ivl_expr_t ex)
|
|||
case '0':
|
||||
break;
|
||||
case '1':
|
||||
imm |= 1UL << idx;
|
||||
assert(idx < 8*sizeof(imm));
|
||||
imm |= 1L << idx;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (ivl_expr_signed(ex) && bits[nbits-1]=='1' && nbits < 8*sizeof(imm))
|
||||
imm |= -1L << nbits;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -111,30 +119,18 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix)
|
|||
{
|
||||
switch (ivl_expr_type(expr)) {
|
||||
|
||||
case IVL_EX_NUMBER: {
|
||||
unsigned value = 0;
|
||||
unsigned idx, nbits = ivl_expr_width(expr);
|
||||
const char*bits = ivl_expr_bits(expr);
|
||||
|
||||
for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]) {
|
||||
|
||||
case '0':
|
||||
break;
|
||||
case '1':
|
||||
assert(idx < (8*sizeof value));
|
||||
value |= 1 << idx;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
fprintf(vvp_out, " %%ix/load %u, %u;\n", ix, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case IVL_EX_NUMBER:
|
||||
case IVL_EX_ULONG:
|
||||
fprintf(vvp_out, " %%ix/load %u, %lu;\n", ix, ivl_expr_uvalue(expr));
|
||||
break;
|
||||
{
|
||||
long imm = get_number_immediate(expr);
|
||||
if (imm >= 0) {
|
||||
fprintf(vvp_out, " %%ix/load %u, %ld;\n", ix, imm);
|
||||
} else {
|
||||
fprintf(vvp_out, " %%ix/load %u, 0; loading %ld\n", ix, imm);
|
||||
fprintf(vvp_out, " %%ix/sub %u, %ld;\n", ix, -imm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IVL_EX_SIGNAL: {
|
||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||
|
|
@ -155,7 +151,7 @@ static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix)
|
|||
}
|
||||
|
||||
ivl_expr_t ixe = ivl_expr_oper1(expr);
|
||||
if (number_is_immediate(ixe, 8*sizeof(unsigned long)))
|
||||
if (number_is_immediate(ixe, 8*sizeof(unsigned long), 0))
|
||||
word = get_number_immediate(ixe);
|
||||
else {
|
||||
struct vector_info rv;
|
||||
|
|
@ -383,7 +379,7 @@ static struct vector_info draw_binary_expr_eq(ivl_expr_t exp,
|
|||
return draw_binary_expr_eq_real(exp);
|
||||
}
|
||||
|
||||
if (number_is_immediate(re,16) && !number_is_unknown(re))
|
||||
if (number_is_immediate(re,16,0) && !number_is_unknown(re))
|
||||
return draw_eq_immediate(exp, ewid, le, re, stuff_ok_flag);
|
||||
|
||||
assert(ivl_expr_value(le) == IVL_VT_LOGIC
|
||||
|
|
@ -814,7 +810,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
|||
switch (ivl_expr_opcode(exp)) {
|
||||
case 'G':
|
||||
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(le,16) && !number_is_unknown(le)) {
|
||||
if (number_is_immediate(le,16,0) && !number_is_unknown(le)) {
|
||||
unsigned imm = get_number_immediate(le);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
|
|
@ -830,7 +826,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
|||
|
||||
case 'L':
|
||||
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(re,16) && !number_is_unknown(re)) {
|
||||
if (number_is_immediate(re,16,0) && !number_is_unknown(re)) {
|
||||
unsigned imm = get_number_immediate(re);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
|
|
@ -846,7 +842,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
|||
|
||||
case '<':
|
||||
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(re,16) && !number_is_unknown(re)) {
|
||||
if (number_is_immediate(re,16,0) && !number_is_unknown(re)) {
|
||||
unsigned imm = get_number_immediate(re);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
|
|
@ -861,7 +857,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t exp,
|
|||
|
||||
case '>':
|
||||
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
|
||||
if (number_is_immediate(le,16) && !number_is_unknown(le)) {
|
||||
if (number_is_immediate(le,16,0) && !number_is_unknown(le)) {
|
||||
unsigned imm = get_number_immediate(le);
|
||||
assert(imm >= 0);
|
||||
fprintf(vvp_out, " %%cmpi/%c %u, %u, %u;\n", s_flag,
|
||||
|
|
@ -940,9 +936,9 @@ static struct vector_info draw_binary_expr_logic(ivl_expr_t exp,
|
|||
ivl_expr_t re = ivl_expr_oper2(exp);
|
||||
|
||||
if (ivl_expr_opcode(exp) == '&') {
|
||||
if (number_is_immediate(re, IMM_WID) && !number_is_unknown(re))
|
||||
if (number_is_immediate(re, IMM_WID, 0) && !number_is_unknown(re))
|
||||
return draw_logic_immediate(exp, le, re, wid);
|
||||
if (number_is_immediate(le, IMM_WID) && !number_is_unknown(le))
|
||||
if (number_is_immediate(le, IMM_WID, 0) && !number_is_unknown(le))
|
||||
return draw_logic_immediate(exp, re, le, wid);
|
||||
}
|
||||
|
||||
|
|
@ -1162,12 +1158,11 @@ static struct vector_info draw_binary_expr_lrs(ivl_expr_t exp, unsigned wid)
|
|||
|
||||
static struct vector_info draw_load_add_immediate(ivl_expr_t le,
|
||||
ivl_expr_t re,
|
||||
unsigned wid)
|
||||
unsigned wid,
|
||||
int signed_flag)
|
||||
{
|
||||
struct vector_info lv;
|
||||
unsigned long imm;
|
||||
|
||||
imm = get_number_immediate(re);
|
||||
long imm = get_number_immediate(re);
|
||||
lv.base = allocate_vector(wid);
|
||||
lv.wid = wid;
|
||||
if (lv.base == 0) {
|
||||
|
|
@ -1180,7 +1175,7 @@ static struct vector_info draw_load_add_immediate(ivl_expr_t le,
|
|||
|
||||
/* Load the signal value with a %load that adds the index
|
||||
register to the value being loaded. */
|
||||
draw_signal_dest(le, lv, 0, imm);
|
||||
draw_signal_dest(le, lv, signed_flag, imm);
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
|
@ -1197,23 +1192,50 @@ static struct vector_info draw_add_immediate(ivl_expr_t le,
|
|||
|
||||
imm = get_number_immediate(re);
|
||||
|
||||
/* Now generate enough %addi instructions to add the entire
|
||||
immediate value to the destination. The adds are done IMM_WID
|
||||
bits at a time, but +1 bits are done to push the carry into
|
||||
the higher bits if needed. */
|
||||
{ unsigned base;
|
||||
for (base = 0 ; base < lv.wid ; base += IMM_WID) {
|
||||
unsigned long tmp = imm & 0xffffffffUL;
|
||||
unsigned add_wid = lv.wid - base;
|
||||
/* This shouldn't generally happen, because the elaborator
|
||||
should take care of simple constant propagation like this,
|
||||
but it doesn't have to and it is easy to catch here. */
|
||||
if (imm == 0)
|
||||
return lv;
|
||||
|
||||
imm >>= IMM_WID;
|
||||
switch (lv.base) {
|
||||
case 0: /* Left expression is 0. */
|
||||
lv.base = allocate_vector(wid);
|
||||
if (lv.base == 0) {
|
||||
fprintf(stderr, "%s:%u: vvp.tgt error: "
|
||||
"Unable to allocate %u thread bits "
|
||||
"for result of addition.\n",
|
||||
ivl_expr_file(re), ivl_expr_lineno(re), wid);
|
||||
vvp_errors += 1;
|
||||
}
|
||||
fprintf(vvp_out, " %%movi %u, %lu %u;\n", lv.base, imm, wid);
|
||||
break;
|
||||
|
||||
fprintf(vvp_out, " %%addi %u, %lu, %u;\n",
|
||||
lv.base+base, tmp, add_wid);
|
||||
case 1: /* Left expression is 1...1 (i.e. -1) */
|
||||
imm -= 1;
|
||||
if (imm == 0) {
|
||||
lv.base = 0;
|
||||
} else {
|
||||
lv.base = allocate_vector(wid);
|
||||
if (lv.base == 0) {
|
||||
fprintf(stderr, "%s:%u: vvp.tgt error: "
|
||||
"Unable to allocate %u thread bits "
|
||||
"for result of addition.\n",
|
||||
ivl_expr_file(re), ivl_expr_lineno(re), wid);
|
||||
vvp_errors += 1;
|
||||
}
|
||||
fprintf(vvp_out, " %%movi %u, %lu %u;\n", lv.base, imm, wid);
|
||||
}
|
||||
break;
|
||||
|
||||
if (imm == 0)
|
||||
break;
|
||||
}
|
||||
case 2: /* Left expression is X or Z */
|
||||
case 3:
|
||||
lv.base = 2;
|
||||
break;
|
||||
|
||||
default: /* The regular case. */
|
||||
fprintf(vvp_out, " %%addi %u, %lu, %u;\n", lv.base, imm, wid);
|
||||
break;
|
||||
}
|
||||
|
||||
return lv;
|
||||
|
|
@ -1234,7 +1256,8 @@ static struct vector_info draw_sub_immediate(ivl_expr_t le,
|
|||
assert(lv.wid == wid);
|
||||
|
||||
imm = get_number_immediate(re);
|
||||
assert( (imm & ~0xffffffffUL) == 0 );
|
||||
if (imm == 0)
|
||||
return lv;
|
||||
|
||||
switch (lv.base) {
|
||||
case 0:
|
||||
|
|
@ -1248,21 +1271,21 @@ static struct vector_info draw_sub_immediate(ivl_expr_t le,
|
|||
vvp_errors += 1;
|
||||
}
|
||||
|
||||
fprintf(vvp_out, " %%mov %u, %u, %u;\n", tmp, lv.base, wid);
|
||||
fprintf(vvp_out, " %%mov %u, %u, %u;\n", tmp, lv.base, wid);
|
||||
lv.base = tmp;
|
||||
fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid);
|
||||
return lv;
|
||||
fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
lv.base = 2;
|
||||
return lv;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid);
|
||||
fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
|
|
@ -1277,8 +1300,10 @@ static struct vector_info draw_mul_immediate(ivl_expr_t le,
|
|||
assert(lv.wid == wid);
|
||||
|
||||
imm = get_number_immediate(re);
|
||||
if (imm == 0)
|
||||
return lv;
|
||||
|
||||
fprintf(vvp_out, " %%muli %u, %lu, %u;\n", lv.base, imm, lv.wid);
|
||||
fprintf(vvp_out, " %%muli %u, %lu, %u;\n", lv.base, imm, lv.wid);
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
|
@ -1293,25 +1318,27 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid)
|
|||
|
||||
const char*sign_string = ivl_expr_signed(le) && ivl_expr_signed(re)? "/s" : "";
|
||||
|
||||
int signed_flag = ivl_expr_signed(exp)? 1 : 0;
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(le) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(re) == IVL_EX_ULONG))
|
||||
return draw_load_add_immediate(le, re, wid);
|
||||
return draw_load_add_immediate(le, re, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(le) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER))
|
||||
return draw_load_add_immediate(le, re, wid);
|
||||
return draw_load_add_immediate(le, re, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(le) == IVL_EX_ULONG))
|
||||
return draw_load_add_immediate(re, le, wid);
|
||||
return draw_load_add_immediate(re, le, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_SIGNAL)
|
||||
&& (ivl_expr_type(le) == IVL_EX_NUMBER))
|
||||
return draw_load_add_immediate(re, le, wid);
|
||||
return draw_load_add_immediate(re, le, wid, signed_flag);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_ULONG))
|
||||
|
|
@ -1320,7 +1347,7 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid)
|
|||
if ((ivl_expr_opcode(exp) == '+')
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(re))
|
||||
&& number_is_immediate(re, 8*sizeof(unsigned long)))
|
||||
&& number_is_immediate(re, 8*sizeof(unsigned long), 0))
|
||||
return draw_add_immediate(le, re, wid);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '-')
|
||||
|
|
@ -1330,13 +1357,13 @@ static struct vector_info draw_binary_expr_arith(ivl_expr_t exp, unsigned wid)
|
|||
if ((ivl_expr_opcode(exp) == '-')
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(re))
|
||||
&& number_is_immediate(re, IMM_WID))
|
||||
&& number_is_immediate(re, IMM_WID, 0))
|
||||
return draw_sub_immediate(le, re, wid);
|
||||
|
||||
if ((ivl_expr_opcode(exp) == '*')
|
||||
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(re))
|
||||
&& number_is_immediate(re, IMM_WID))
|
||||
&& number_is_immediate(re, IMM_WID, 0))
|
||||
return draw_mul_immediate(le, re, wid);
|
||||
|
||||
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
|
||||
|
|
@ -1643,7 +1670,7 @@ static struct vector_info draw_number_expr(ivl_expr_t exp, unsigned wid)
|
|||
vvp_errors += 1;
|
||||
}
|
||||
|
||||
if ((!number_is_unknown(exp)) && number_is_immediate(exp, IMM_WID)) {
|
||||
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;
|
||||
|
|
@ -1937,11 +1964,13 @@ void pad_expr_in_place(ivl_expr_t exp, struct vector_info res, unsigned swid)
|
|||
* offsetting the read from the lsi (least significant index) of the
|
||||
* signal.
|
||||
*
|
||||
* If the add_index is >=0, then generate a %load/vp0 to add the
|
||||
* word0 value to the loaded value before storing it into the destination.
|
||||
* If the add_index is 0, then generate a %load/vp0 to add the
|
||||
* word0 value to the loaded value before storing it into the
|
||||
* destination. If the add_index is 1, then generate a %load/vp0/s to
|
||||
* do a signed load.
|
||||
*/
|
||||
static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
||||
int add_index, unsigned long immediate)
|
||||
int add_index, long immediate)
|
||||
{
|
||||
unsigned swid = ivl_expr_width(exp);
|
||||
ivl_signal_t sig = ivl_expr_signal(exp);
|
||||
|
|
@ -1958,17 +1987,17 @@ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
|||
|
||||
draw_eval_expr_into_integer(ix, 3);
|
||||
if (add_index < 0) {
|
||||
fprintf(vvp_out, " %%load/av %u, v%p, %u;\n",
|
||||
fprintf(vvp_out, " %%load/av %u, v%p, %u;\n",
|
||||
res.base, sig, swid);
|
||||
pad_expr_in_place(exp, res, swid);
|
||||
} else {
|
||||
assert(add_index == 0);
|
||||
const char*sign_flag = (add_index>0)? "/s" : "";
|
||||
|
||||
/* Add an immediate value to an array value. */
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
fprintf(vvp_out, " %%load/avp0 %u, v%p, %u;\n",
|
||||
res.base, sig, swid);
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
fprintf(vvp_out, " %%load/avp0%s %u, v%p, %u;\n",
|
||||
sign_flag, res.base, sig, res.wid);
|
||||
}
|
||||
pad_expr_in_place(exp, res, swid);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1983,13 +2012,17 @@ static void draw_signal_dest(ivl_expr_t exp, struct vector_info res,
|
|||
|
||||
} else if (add_index >= 0) {
|
||||
|
||||
assert(add_index == 0);
|
||||
const char*sign_flag = add_index==1? "/s" : "";
|
||||
|
||||
/* If this is a REG (a variable) then I can do a vector read. */
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
fprintf(vvp_out, " %%ix/load 2, %u;\n", res.wid);
|
||||
fprintf(vvp_out, " %%load/vp0 %u, v%p_%u, %u;\n",
|
||||
res.base, sig, word, swid);
|
||||
if (immediate >= 0) {
|
||||
fprintf(vvp_out, " %%ix/load 0, %lu;\n", immediate);
|
||||
} else {
|
||||
fprintf(vvp_out, " %%ix/load 0, 0; immediate=%ld\n", immediate);
|
||||
fprintf(vvp_out, " %%ix/sub 0, %ld;\n", -immediate);
|
||||
}
|
||||
fprintf(vvp_out, " %%load/vp0%s %u, v%p_%u, %u;\n", sign_flag,
|
||||
res.base, sig,word, res.wid);
|
||||
swid = res.wid;
|
||||
|
||||
} else {
|
||||
|
|
@ -2078,7 +2111,6 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
|||
{
|
||||
ivl_signal_t sig = ivl_expr_signal(sube);
|
||||
struct vector_info res;
|
||||
unsigned idx;
|
||||
|
||||
/* Use this word of the signal. */
|
||||
unsigned use_word = 0;
|
||||
|
|
@ -2093,7 +2125,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
|||
ivl_expr_t ix = ivl_expr_oper1(sube);
|
||||
|
||||
if (ivl_signal_type(sig)==IVL_SIT_REG
|
||||
|| !number_is_immediate(ix, 8*sizeof(unsigned long)))
|
||||
|| !number_is_immediate(ix, 8*sizeof(unsigned long),0))
|
||||
return draw_select_array(sube, bit_idx, bit_wid, wid);
|
||||
|
||||
/* The index is constant, so we can return to direct
|
||||
|
|
@ -2104,7 +2136,7 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
|||
/* Try the special case that the part is at the beginning of
|
||||
the signal (or the entire width). Just load the early bits
|
||||
in one go. */
|
||||
if (number_is_immediate(bit_idx, 32)
|
||||
if (number_is_immediate(bit_idx, 32, 0)
|
||||
&& get_number_immediate(bit_idx) == 0
|
||||
&& (ivl_expr_width(sube) >= bit_wid)) {
|
||||
|
||||
|
|
@ -2122,23 +2154,23 @@ static struct vector_info draw_select_signal(ivl_expr_t sube,
|
|||
return res;
|
||||
}
|
||||
|
||||
draw_eval_expr_into_integer(bit_idx, 0);
|
||||
|
||||
/* Alas, do it the hard way. */
|
||||
|
||||
draw_eval_expr_into_integer(bit_idx, 1);
|
||||
|
||||
res.base = allocate_vector(wid);
|
||||
res.wid = wid;
|
||||
assert(res.base);
|
||||
|
||||
for (idx = 0 ; idx < res.wid ; idx += 1) {
|
||||
if (idx >= bit_wid) {
|
||||
fprintf(vvp_out, " %%movi %u, 0, %u; Pad from %u to %u\n",
|
||||
res.base+idx, res.wid-idx,
|
||||
ivl_expr_width(sube), wid);
|
||||
break;
|
||||
}
|
||||
fprintf(vvp_out, " %%load/x.p %u, v%p_%u, 0;\n",
|
||||
res.base+idx, sig, use_word);
|
||||
}
|
||||
unsigned use_wid = res.wid;
|
||||
if (use_wid > bit_wid)
|
||||
use_wid = bit_wid;
|
||||
|
||||
fprintf(vvp_out, " %%load/x1p %u, v%p_%u, %u;\n",
|
||||
res.base, sig, use_word, use_wid);
|
||||
if (use_wid < res.wid)
|
||||
fprintf(vvp_out, " %%movi %u, 0, %u;\n",
|
||||
res.base + use_wid, res.wid - use_wid);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ static int draw_signal_real_real(ivl_expr_t exp)
|
|||
|
||||
if (ivl_signal_array_count(sig) > 1) {
|
||||
ivl_expr_t ix = ivl_expr_oper1(exp);
|
||||
if (!number_is_immediate(ix, 8*sizeof(word))) {
|
||||
if (!number_is_immediate(ix, 8*sizeof(word), 0)) {
|
||||
/* XXXX Need to generate a %load/ar instruction. */
|
||||
assert(0);
|
||||
return res;
|
||||
|
|
@ -441,7 +441,22 @@ static int draw_unary_real(ivl_expr_t exp)
|
|||
return res;
|
||||
}
|
||||
|
||||
if (ivl_expr_opcode(exp) == '!') {
|
||||
struct vector_info vi;
|
||||
vi = draw_eval_expr(exp, STUFF_OK_XZ);
|
||||
int res = allocate_word();
|
||||
const char*sign_flag = ivl_expr_signed(exp)? "/s" : "";
|
||||
fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n",
|
||||
sign_flag, res, vi.base, vi.wid);
|
||||
|
||||
fprintf(vvp_out, " %%cvt/ri %d, %d;\n", res, res);
|
||||
|
||||
clr_vector(vi);
|
||||
return res;
|
||||
}
|
||||
|
||||
ivl_expr_t sube = ivl_expr_oper1(exp);
|
||||
|
||||
int sub = draw_eval_real(sube);
|
||||
|
||||
if (ivl_expr_opcode(exp) == '+')
|
||||
|
|
@ -461,8 +476,8 @@ static int draw_unary_real(ivl_expr_t exp)
|
|||
return sub;
|
||||
}
|
||||
|
||||
fprintf(vvp_out, "; XXXX unary (%c)\n", ivl_expr_opcode(exp));
|
||||
fprintf(stderr, "XXXX evaluate unary (%c)\n", ivl_expr_opcode(exp));
|
||||
fprintf(vvp_out, "; XXXX unary (%c) on sube in %d\n", ivl_expr_opcode(exp), sub);
|
||||
fprintf(stderr, "XXXX evaluate unary (%c) on sube in %d\n", ivl_expr_opcode(exp), sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __vvp_priv_H
|
||||
#define __vvp_priv_H
|
||||
/*
|
||||
* Copyright (c) 2001-2005 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2001-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
|
||||
|
|
@ -51,6 +51,8 @@ struct vector_info {
|
|||
extern const char *vvp_mangle_id(const char *);
|
||||
extern const char *vvp_mangle_name(const char *);
|
||||
|
||||
extern char* draw_Cr_to_string(double value);
|
||||
|
||||
/*
|
||||
* This generates a string from a signal that uniquely identifies
|
||||
* that signal with letters that can be used in a label.
|
||||
|
|
@ -65,6 +67,8 @@ extern const char* vvp_signal_label(ivl_signal_t sig);
|
|||
extern unsigned width_of_nexus(ivl_nexus_t nex);
|
||||
extern ivl_variable_type_t data_type_of_nexus(ivl_nexus_t nex);
|
||||
|
||||
extern int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr);
|
||||
|
||||
/*
|
||||
* This function draws a process (initial or always) into the output
|
||||
* file. It normally returns 0, but returns !0 of there is some sort
|
||||
|
|
@ -90,7 +94,7 @@ extern void pad_expr_in_place(ivl_expr_t exp, struct vector_info res, unsigned s
|
|||
* draw_modpath arranges for a .modpath record to be written out.
|
||||
*
|
||||
* cleanup_modpath() cleans up any pending .modpath records that may
|
||||
* have been scheduled by draw_modpath() but not yet writte.
|
||||
* have been scheduled by draw_modpath() but not yet written.
|
||||
*
|
||||
* Note: draw_modpath drive_label must be malloc'ed by the
|
||||
* caller. This function will free the string sometime in the future.
|
||||
|
|
@ -110,6 +114,24 @@ extern struct vector_info draw_vpi_func_call(ivl_expr_t exp,
|
|||
unsigned wid);
|
||||
extern int draw_vpi_rfunc_call(ivl_expr_t exp);
|
||||
|
||||
/*
|
||||
* Switches (tran)
|
||||
*/
|
||||
extern void draw_switch_in_scope(ivl_switch_t sw);
|
||||
|
||||
/* Draw_net_input and friends uses this. */
|
||||
struct vvp_nexus_data {
|
||||
/* draw_net_input uses this */
|
||||
const char*net_input;
|
||||
unsigned drivers_count;
|
||||
int flags;
|
||||
/* draw_net_in_scope uses these to identify the controlling word. */
|
||||
ivl_signal_t net;
|
||||
unsigned net_word;
|
||||
};
|
||||
#define VVP_NEXUS_DATA_STR 0x0001
|
||||
|
||||
|
||||
/*
|
||||
* Given a nexus, draw a string that represents the functor output
|
||||
* that feeds the nexus. This function can be used to get the input to
|
||||
|
|
@ -120,6 +142,17 @@ extern int draw_vpi_rfunc_call(ivl_expr_t exp);
|
|||
*/
|
||||
extern const char* draw_net_input(ivl_nexus_t nex);
|
||||
|
||||
/*
|
||||
* See draw_net_input.c for details on draw_net_input_x. (It would be
|
||||
* nice if this can be made private.)
|
||||
*/
|
||||
/* Omit LPMPART_BI device pin-data(0) drivers. */
|
||||
# define OMIT_PART_BI_DATA 0x0001
|
||||
struct vvp_nexus_data;
|
||||
extern char* draw_net_input_x(ivl_nexus_t nex,
|
||||
ivl_nexus_ptr_t omit_ptr, int omit_flags,
|
||||
struct vvp_nexus_data*nex_data);
|
||||
|
||||
/*
|
||||
* This function is different from draw_net_input in that it will
|
||||
* return a reference to a net as its first choice. This reference
|
||||
|
|
@ -236,8 +269,8 @@ extern unsigned allocate_vector_exp(ivl_expr_t exp, unsigned wid,
|
|||
int exclusive_flag);
|
||||
|
||||
extern int number_is_unknown(ivl_expr_t ex);
|
||||
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid);
|
||||
extern unsigned long get_number_immediate(ivl_expr_t ex);
|
||||
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok);
|
||||
extern long get_number_immediate(ivl_expr_t ex);
|
||||
|
||||
/*
|
||||
* draw_eval_real evaluates real value expressions. The return code
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ static void set_to_lvariable(ivl_lval_t lval,
|
|||
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else if (number_is_immediate(part_off_ex, 64)) {
|
||||
} else if (number_is_immediate(part_off_ex, 64, 0)) {
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
part_off_ex = 0;
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ static void set_to_lvariable(ivl_lval_t lval,
|
|||
/* If the word index is a constant expression, then evaluate
|
||||
it to select the word, and pay no further heed to the
|
||||
expression itself. */
|
||||
if (word_ix && number_is_immediate(word_ix, 8*sizeof(use_word))) {
|
||||
if (word_ix && number_is_immediate(word_ix, 8*sizeof(use_word), 0)) {
|
||||
use_word = get_number_immediate(word_ix);
|
||||
word_ix = 0;
|
||||
}
|
||||
|
|
@ -227,14 +227,14 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix,
|
|||
unsigned part_off = 0;
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else if (number_is_immediate(part_off_ex, 64)) {
|
||||
} else if (number_is_immediate(part_off_ex, 64, 0)) {
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
part_off_ex = 0;
|
||||
}
|
||||
|
||||
if (dexp == 0) {
|
||||
/* Constant delay... */
|
||||
if (number_is_immediate(word_ix, 64)) {
|
||||
if (number_is_immediate(word_ix, 64, 0)) {
|
||||
fprintf(vvp_out, " %%ix/load 3, %lu; address\n",
|
||||
get_number_immediate(word_ix));
|
||||
} else {
|
||||
|
|
@ -259,7 +259,7 @@ static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix,
|
|||
/* Calculated delay... */
|
||||
int delay_index = allocate_word();
|
||||
draw_eval_expr_into_integer(dexp, delay_index);
|
||||
if (number_is_immediate(word_ix, 64)) {
|
||||
if (number_is_immediate(word_ix, 64, 0)) {
|
||||
fprintf(vvp_out, " %%ix/load 3, %lu; address\n",
|
||||
get_number_immediate(word_ix));
|
||||
} else {
|
||||
|
|
@ -306,7 +306,7 @@ static void assign_to_lvector(ivl_lval_t lval, unsigned bit,
|
|||
|
||||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else if (number_is_immediate(part_off_ex, 64)) {
|
||||
} else if (number_is_immediate(part_off_ex, 64, 0)) {
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
part_off_ex = 0;
|
||||
}
|
||||
|
|
@ -543,7 +543,7 @@ static int show_stmt_assign_nb_real(ivl_statement_t net)
|
|||
if (ivl_signal_array_count(sig) > 1) {
|
||||
word_ix = ivl_lval_idx(lval);
|
||||
assert(word_ix);
|
||||
assert(number_is_immediate(word_ix, 8*sizeof(use_word)));
|
||||
assert(number_is_immediate(word_ix, 8*sizeof(use_word), 0));
|
||||
use_word = get_number_immediate(word_ix);
|
||||
}
|
||||
|
||||
|
|
@ -700,7 +700,7 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope)
|
|||
if ((ivl_statement_type(net) == IVL_ST_CASE)
|
||||
&& (ivl_expr_type(cex) == IVL_EX_NUMBER)
|
||||
&& (! number_is_unknown(cex))
|
||||
&& number_is_immediate(cex, 16)) {
|
||||
&& number_is_immediate(cex, 16, 0)) {
|
||||
|
||||
unsigned long imm = get_number_immediate(cex);
|
||||
|
||||
|
|
@ -926,12 +926,12 @@ static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec)
|
|||
if (part_off_ex == 0) {
|
||||
part_off = 0;
|
||||
} else {
|
||||
assert(number_is_immediate(part_off_ex, 64));
|
||||
assert(number_is_immediate(part_off_ex, 64, 0));
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
}
|
||||
|
||||
if (word_idx != 0) {
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(unsigned long)));
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(unsigned long), 0));
|
||||
use_word = get_number_immediate(word_idx);
|
||||
}
|
||||
|
||||
|
|
@ -1004,12 +1004,12 @@ static void force_link_rval(ivl_statement_t net, ivl_expr_t rval)
|
|||
|
||||
/* At least for now, only handle force to fixed words of an array. */
|
||||
if ((lword_idx = ivl_lval_idx(lval)) != 0) {
|
||||
assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long)));
|
||||
assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long), 0));
|
||||
use_lword = get_number_immediate(lword_idx);
|
||||
}
|
||||
|
||||
if ((rword_idx = ivl_expr_oper1(rval)) != 0) {
|
||||
assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long)));
|
||||
assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long), 0));
|
||||
use_rword = get_number_immediate(rword_idx);
|
||||
}
|
||||
|
||||
|
|
@ -1087,12 +1087,12 @@ static int show_stmt_deassign(ivl_statement_t net)
|
|||
ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
|
||||
unsigned part_off = 0;
|
||||
if (part_off_ex != 0) {
|
||||
assert(number_is_immediate(part_off_ex, 64));
|
||||
assert(number_is_immediate(part_off_ex, 64, 0));
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
}
|
||||
|
||||
if (word_idx != 0) {
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word)));
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word), 0));
|
||||
use_word = get_number_immediate(word_idx);
|
||||
}
|
||||
|
||||
|
|
@ -1369,7 +1369,7 @@ static int show_stmt_release(ivl_statement_t net)
|
|||
ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
|
||||
unsigned part_off = 0;
|
||||
if (part_off_ex != 0) {
|
||||
assert(number_is_immediate(part_off_ex, 64));
|
||||
assert(number_is_immediate(part_off_ex, 64, 0));
|
||||
part_off = get_number_immediate(part_off_ex);
|
||||
}
|
||||
|
||||
|
|
@ -1383,7 +1383,7 @@ static int show_stmt_release(ivl_statement_t net)
|
|||
}
|
||||
|
||||
if (word_idx != 0) {
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word)));
|
||||
assert(number_is_immediate(word_idx, 8*sizeof(use_word), 0));
|
||||
use_word = get_number_immediate(word_idx);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,23 +31,6 @@
|
|||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
struct vvp_nexus_data {
|
||||
/* draw_net_input uses this */
|
||||
const char*net_input;
|
||||
unsigned drivers_count;
|
||||
int flags;
|
||||
/* draw_net_in_scope uses these to identify the controlling word. */
|
||||
ivl_signal_t net;
|
||||
unsigned net_word;
|
||||
};
|
||||
#define VVP_NEXUS_DATA_STR 0x0001
|
||||
|
||||
|
||||
static struct vvp_nexus_data*new_nexus_data()
|
||||
{
|
||||
struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data));
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Escape non-symbol characters in ids, and quotes in strings.
|
||||
|
|
@ -147,27 +130,6 @@ const char *vvp_mangle_name(const char *id)
|
|||
return out;
|
||||
}
|
||||
|
||||
static void draw_C4_repeated_constant(char bit_char, unsigned width)
|
||||
{
|
||||
unsigned idx;
|
||||
|
||||
fprintf(vvp_out, "C4<");
|
||||
for (idx = 0 ; idx < width ; idx += 1)
|
||||
fprintf(vvp_out, "%c", bit_char);
|
||||
|
||||
fprintf(vvp_out, ">");
|
||||
}
|
||||
|
||||
static void str_repeat(char*buf, const char*str, unsigned rpt)
|
||||
{
|
||||
unsigned idx;
|
||||
size_t len = strlen(str);
|
||||
for (idx = 0 ; idx < rpt ; idx += 1) {
|
||||
strcpy(buf, str);
|
||||
buf += len;
|
||||
}
|
||||
}
|
||||
|
||||
/* REMOVE ME: vvp_signal_label should not be used. DEAD CODE
|
||||
* Given a signal, generate a string name that is suitable for use as
|
||||
* a label. The only rule is that the same signal will always have the
|
||||
|
|
@ -198,31 +160,6 @@ ivl_signal_t signal_of_nexus(ivl_nexus_t nex, unsigned*word)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex)
|
||||
{
|
||||
unsigned idx;
|
||||
ivl_signal_type_t out = IVL_SIT_TRI;
|
||||
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ivl_signal_type_t stype;
|
||||
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
|
||||
ivl_signal_t sig = ivl_nexus_ptr_sig(ptr);
|
||||
if (sig == 0)
|
||||
continue;
|
||||
|
||||
stype = ivl_signal_type(sig);
|
||||
if (stype == IVL_SIT_REG)
|
||||
continue;
|
||||
if (stype == IVL_SIT_TRI)
|
||||
continue;
|
||||
if (stype == IVL_SIT_NONE)
|
||||
continue;
|
||||
out = stype;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
unsigned width_of_nexus(ivl_nexus_t nex)
|
||||
{
|
||||
unsigned idx;
|
||||
|
|
@ -332,7 +269,7 @@ const char*drive_string(ivl_drive_t drive)
|
|||
* gate will be generated for this node. Otherwise, the code generator
|
||||
* will connect its input to its output and skip the gate.
|
||||
*/
|
||||
static int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr)
|
||||
int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr)
|
||||
{
|
||||
ivl_nexus_t in_n;
|
||||
unsigned idx;
|
||||
|
|
@ -380,100 +317,7 @@ static int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a nexus, look for a signal that has module delay
|
||||
* paths. Return that signal. (There should be no more than 1.) If we
|
||||
* don't find any, then return nil.
|
||||
*/
|
||||
static ivl_signal_t find_modpath(ivl_nexus_t nex)
|
||||
{
|
||||
unsigned idx;
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx);
|
||||
ivl_signal_t sig = ivl_nexus_ptr_sig(ptr);
|
||||
if (sig == 0)
|
||||
continue;
|
||||
if (ivl_signal_npath(sig) == 0)
|
||||
continue;
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* draw_C4_to_string(ivl_net_const_t cptr)
|
||||
{
|
||||
const char*bits = ivl_const_bits(cptr);
|
||||
unsigned idx;
|
||||
|
||||
size_t result_len = 5 + ivl_const_width(cptr);
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
strcpy(dp, "C4<");
|
||||
dp += strlen(dp);
|
||||
|
||||
for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) {
|
||||
char bitchar = bits[ivl_const_width(cptr)-idx-1];
|
||||
*dp++ = bitchar;
|
||||
assert((dp - result) < result_len);
|
||||
}
|
||||
|
||||
strcpy(dp, ">");
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* draw_C8_to_string(ivl_net_const_t cptr,
|
||||
ivl_drive_t dr0, ivl_drive_t dr1)
|
||||
{
|
||||
size_t nresult = 5 + 3*ivl_const_width(cptr);
|
||||
char*result = malloc(nresult);
|
||||
const char*bits = ivl_const_bits(cptr);
|
||||
unsigned idx;
|
||||
|
||||
char dr0c = "01234567"[dr0];
|
||||
char dr1c = "01234567"[dr1];
|
||||
char*dp = result;
|
||||
|
||||
strcpy(dp, "C8<");
|
||||
dp += strlen(dp);
|
||||
|
||||
for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) {
|
||||
switch (bits[ivl_const_width(cptr)-idx-1]) {
|
||||
case '0':
|
||||
*dp++ = dr0c;
|
||||
*dp++ = dr0c;
|
||||
*dp++ = '0';
|
||||
break;
|
||||
case '1':
|
||||
*dp++ = dr1c;
|
||||
*dp++ = dr1c;
|
||||
*dp++ = '1';
|
||||
break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
*dp++ = dr0c;
|
||||
*dp++ = dr1c;
|
||||
*dp++ = 'x';
|
||||
break;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
*dp++ = '0';
|
||||
*dp++ = '0';
|
||||
*dp++ = 'z';
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
assert(dp - result < nresult);
|
||||
}
|
||||
|
||||
strcpy(dp, ">");
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* draw_Cr_to_string(double value)
|
||||
char* draw_Cr_to_string(double value)
|
||||
{
|
||||
char tmp[256];
|
||||
|
||||
|
|
@ -508,488 +352,6 @@ static char* draw_Cr_to_string(double value)
|
|||
return strdup(tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function takes a nexus and looks for an input functor. It then
|
||||
* draws to the output a string that represents that functor. What we
|
||||
* are trying to do here is find the input to the net that is attached
|
||||
* to this nexus.
|
||||
*/
|
||||
|
||||
static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
||||
{
|
||||
unsigned nptr_pin = ivl_nexus_ptr_pin(nptr);
|
||||
ivl_net_const_t cptr;
|
||||
ivl_net_logic_t lptr;
|
||||
ivl_signal_t sptr;
|
||||
ivl_lpm_t lpm;
|
||||
|
||||
lptr = ivl_nexus_ptr_log(nptr);
|
||||
if (lptr && (ivl_logic_type(lptr) == IVL_LO_BUFZ) && (nptr_pin == 0))
|
||||
do {
|
||||
if (! can_elide_bufz(lptr, nptr))
|
||||
break;
|
||||
|
||||
return strdup(draw_net_input(ivl_logic_pin(lptr, 1)));
|
||||
} while(0);
|
||||
|
||||
/* If this is a pulldown device, then there is a single pin
|
||||
that drives a constant value to the entire width of the
|
||||
vector. The driver normally drives a pull0 value, so a C8<>
|
||||
constant is appropriate, but if the drive is really strong,
|
||||
then we can draw a C4<> constant instead. */
|
||||
if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) {
|
||||
if (ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) {
|
||||
size_t result_len = ivl_logic_width(lptr) + 5;
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
strcpy(dp, "C4<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, "0", ivl_logic_width(lptr));
|
||||
dp += ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
} else {
|
||||
char val[4];
|
||||
size_t result_len = 3*ivl_logic_width(lptr) + 5;
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
|
||||
val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)];
|
||||
val[1] = val[0];
|
||||
val[2] = '0';
|
||||
val[3] = 0;
|
||||
|
||||
strcpy(dp, "C8<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, val, ivl_logic_width(lptr));
|
||||
dp += 3*ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) {
|
||||
if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) {
|
||||
size_t result_len = 5 + ivl_logic_width(lptr);
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
strcpy(dp, "C4<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, "1", ivl_logic_width(lptr));
|
||||
dp += ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
} else {
|
||||
char val[4];
|
||||
size_t result_len = 5 + 3*ivl_logic_width(lptr);
|
||||
char*result = malloc(result_len);
|
||||
char*dp = result;
|
||||
|
||||
val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)];
|
||||
val[1] = val[0];
|
||||
val[2] = '1';
|
||||
val[3] = 0;
|
||||
|
||||
strcpy(dp, "C8<");
|
||||
dp += strlen(dp);
|
||||
str_repeat(dp, val, ivl_logic_width(lptr));
|
||||
dp += 3*ivl_logic_width(lptr);
|
||||
*dp++ = '>';
|
||||
*dp = 0;
|
||||
assert((dp-result) <= result_len);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (lptr && (nptr_pin == 0)) {
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p", lptr);
|
||||
return strdup(tmp);
|
||||
}
|
||||
|
||||
sptr = ivl_nexus_ptr_sig(nptr);
|
||||
if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) {
|
||||
char tmp[128];
|
||||
/* Input is a .var. This device may be a non-zero pin
|
||||
because it may be an array of reg vectors. */
|
||||
snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin);
|
||||
|
||||
if (ivl_signal_array_count(sptr) > 1) {
|
||||
fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n",
|
||||
sptr, nptr_pin, sptr, nptr_pin);
|
||||
}
|
||||
|
||||
return strdup(tmp);
|
||||
}
|
||||
|
||||
cptr = ivl_nexus_ptr_con(nptr);
|
||||
if (cptr) {
|
||||
/* Constants should have exactly 1 pin, with a literal value. */
|
||||
assert(nptr_pin == 0);
|
||||
char *result = 0;
|
||||
|
||||
switch (ivl_const_type(cptr)) {
|
||||
case IVL_VT_LOGIC:
|
||||
case IVL_VT_BOOL:
|
||||
if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG)
|
||||
&& (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) {
|
||||
|
||||
result = draw_C4_to_string(cptr);
|
||||
|
||||
} else {
|
||||
result = draw_C8_to_string(cptr,
|
||||
ivl_nexus_ptr_drive0(nptr),
|
||||
ivl_nexus_ptr_drive1(nptr));
|
||||
}
|
||||
break;
|
||||
|
||||
case IVL_VT_REAL:
|
||||
result = draw_Cr_to_string(ivl_const_real(cptr));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ivl_expr_t d_rise = ivl_const_delay(cptr, 0);
|
||||
ivl_expr_t d_fall = ivl_const_delay(cptr, 1);
|
||||
ivl_expr_t d_decay = ivl_const_delay(cptr, 2);
|
||||
|
||||
/* We have a delayed constant, so we need to build some code. */
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64));
|
||||
assert(number_is_immediate(d_fall, 64));
|
||||
assert(number_is_immediate(d_decay, 64));
|
||||
|
||||
fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, "
|
||||
"C4<0>, C4<0>, C4<0>;\n", cptr, result);
|
||||
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
cptr, get_number_immediate(d_rise),
|
||||
get_number_immediate(d_rise),
|
||||
get_number_immediate(d_rise), cptr);
|
||||
|
||||
free(result);
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p", cptr);
|
||||
result = strdup(tmp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
lpm = ivl_nexus_ptr_lpm(nptr);
|
||||
if (lpm) switch (ivl_lpm_type(lpm)) {
|
||||
|
||||
case IVL_LPM_FF:
|
||||
case IVL_LPM_ABS:
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_ARRAY:
|
||||
case IVL_LPM_CONCAT:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
case IVL_LPM_CMP_NEE:
|
||||
case IVL_LPM_RE_AND:
|
||||
case IVL_LPM_RE_OR:
|
||||
case IVL_LPM_RE_XOR:
|
||||
case IVL_LPM_RE_NAND:
|
||||
case IVL_LPM_RE_NOR:
|
||||
case IVL_LPM_RE_XNOR:
|
||||
case IVL_LPM_SFUNC:
|
||||
case IVL_LPM_SHIFTL:
|
||||
case IVL_LPM_SHIFTR:
|
||||
case IVL_LPM_SIGN_EXT:
|
||||
case IVL_LPM_SUB:
|
||||
case IVL_LPM_MULT:
|
||||
case IVL_LPM_MUX:
|
||||
case IVL_LPM_POW:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
case IVL_LPM_UFUNC:
|
||||
case IVL_LPM_PART_VP:
|
||||
case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */
|
||||
case IVL_LPM_REPEAT:
|
||||
if (ivl_lpm_q(lpm, 0) == nex) {
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p", lpm);
|
||||
return strdup(tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
case IVL_LPM_PART_BI:
|
||||
if (ivl_lpm_q(lpm, 0) == nex) {
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p/P", lpm);
|
||||
return strdup(tmp);
|
||||
} else if (ivl_lpm_data(lpm,0) == nex) {
|
||||
char tmp[128];
|
||||
snprintf(tmp, sizeof tmp, "L_%p/V", lpm);
|
||||
return strdup(tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "internal error: no input to nexus %s\n",
|
||||
ivl_nexus_name(nex));
|
||||
assert(0);
|
||||
return strdup("C<z>");
|
||||
}
|
||||
|
||||
static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr)
|
||||
{
|
||||
if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG)
|
||||
return 1;
|
||||
if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG)
|
||||
return 1;
|
||||
|
||||
ivl_net_logic_t log = ivl_nexus_ptr_log(nptr);
|
||||
if (log != 0) {
|
||||
/* These logic gates are able to generate unusual
|
||||
strength values and so their outputs are considered
|
||||
strength aware. */
|
||||
if (ivl_logic_type(log) == IVL_LO_BUFIF0)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_BUFIF1)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_PMOS)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_NMOS)
|
||||
return 1;
|
||||
if (ivl_logic_type(log) == IVL_LO_CMOS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function draws the input to a net into a string. What that
|
||||
* means is that it returns a static string that can be used to
|
||||
* represent a resolved driver to a nexus. If there are multiple
|
||||
* drivers to the nexus, then it writes out the resolver declarations
|
||||
* needed to perform strength resolution.
|
||||
*
|
||||
* The string that this returns is malloced, and that means that the
|
||||
* caller must free the string or store it permanently. This function
|
||||
* does *not* check for a previously calculated string. Use the
|
||||
* draw_net_input for the general case.
|
||||
*/
|
||||
/* Omit LPMPART_BI device pin-data(0) drivers. */
|
||||
# define OMIT_PART_BI_DATA 0x0001
|
||||
|
||||
static char* draw_net_input_x(ivl_nexus_t nex,
|
||||
ivl_nexus_ptr_t omit_ptr, int omit_flags,
|
||||
struct vvp_nexus_data*nex_data)
|
||||
{
|
||||
ivl_signal_type_t res;
|
||||
char result[512];
|
||||
unsigned idx;
|
||||
int level;
|
||||
unsigned ndrivers = 0;
|
||||
static ivl_nexus_ptr_t *drivers = 0x0;
|
||||
static unsigned adrivers = 0;
|
||||
|
||||
const char*resolv_type;
|
||||
|
||||
char*nex_private = 0;
|
||||
|
||||
/* Accumulate nex_data flags. */
|
||||
int nex_flags = 0;
|
||||
|
||||
res = signal_type_of_nexus(nex);
|
||||
switch (res) {
|
||||
case IVL_SIT_TRI:
|
||||
resolv_type = "tri";
|
||||
break;
|
||||
case IVL_SIT_TRI0:
|
||||
resolv_type = "tri0";
|
||||
nex_flags |= VVP_NEXUS_DATA_STR;
|
||||
break;
|
||||
case IVL_SIT_TRI1:
|
||||
resolv_type = "tri1";
|
||||
nex_flags |= VVP_NEXUS_DATA_STR;
|
||||
break;
|
||||
case IVL_SIT_TRIAND:
|
||||
resolv_type = "triand";
|
||||
break;
|
||||
case IVL_SIT_TRIOR:
|
||||
resolv_type = "trior";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res);
|
||||
assert(0);
|
||||
resolv_type = "tri";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ivl_lpm_t lpm_tmp;
|
||||
ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx);
|
||||
|
||||
/* If we are supposed to skip LPM_PART_BI data pins,
|
||||
check that this driver is that. */
|
||||
if ((omit_flags&OMIT_PART_BI_DATA)
|
||||
&& (lpm_tmp = ivl_nexus_ptr_lpm(nptr))
|
||||
&& (nex == ivl_lpm_data(lpm_tmp,0)))
|
||||
continue;
|
||||
|
||||
if (nptr == omit_ptr)
|
||||
continue;
|
||||
|
||||
/* Skip input only pins. */
|
||||
if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ)
|
||||
&& (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ))
|
||||
continue;
|
||||
|
||||
/* Mark the strength-aware flag if the driver can
|
||||
generate values other than the standard "6"
|
||||
strength. */
|
||||
if (nexus_drive_is_strength_aware(nptr))
|
||||
nex_flags |= VVP_NEXUS_DATA_STR;
|
||||
|
||||
/* Save this driver. */
|
||||
if (ndrivers >= adrivers) {
|
||||
adrivers += 4;
|
||||
drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t));
|
||||
assert(drivers);
|
||||
}
|
||||
drivers[ndrivers] = nptr;
|
||||
ndrivers += 1;
|
||||
}
|
||||
|
||||
/* If the caller is collecting nexus information, then save
|
||||
the nexus driver count in the nex_data. */
|
||||
if (nex_data) {
|
||||
nex_data->drivers_count = ndrivers;
|
||||
nex_data->flags |= nex_flags;
|
||||
}
|
||||
|
||||
/* If the nexus has no drivers, then send a constant HiZ into
|
||||
the net. */
|
||||
if (ndrivers == 0) {
|
||||
unsigned idx, wid = width_of_nexus(nex);
|
||||
char*tmp = malloc(wid + 5);
|
||||
nex_private = tmp;
|
||||
strcpy(tmp, "C4<");
|
||||
tmp += strlen(tmp);
|
||||
switch (res) {
|
||||
case IVL_SIT_TRI:
|
||||
for (idx = 0 ; idx < wid ; idx += 1)
|
||||
*tmp++ = 'z';
|
||||
break;
|
||||
case IVL_SIT_TRI0:
|
||||
for (idx = 0 ; idx < wid ; idx += 1)
|
||||
*tmp++ = '0';
|
||||
break;
|
||||
case IVL_SIT_TRI1:
|
||||
for (idx = 0 ; idx < wid ; idx += 1)
|
||||
*tmp++ = '1';
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
*tmp++ = '>';
|
||||
*tmp = 0;
|
||||
return nex_private;
|
||||
}
|
||||
|
||||
|
||||
/* If the nexus has exactly one driver, then simply draw
|
||||
it. Note that this will *not* work if the nexus is not a
|
||||
TRI type nexus. */
|
||||
if (ndrivers == 1 && res == IVL_SIT_TRI) {
|
||||
ivl_signal_t path_sig = find_modpath(nex);
|
||||
if (path_sig) {
|
||||
char*nex_str = draw_net_input_drive(nex, drivers[0]);
|
||||
char modpath_label[64];
|
||||
snprintf(modpath_label, sizeof modpath_label,
|
||||
"V_%p/m", path_sig);
|
||||
nex_private = strdup(modpath_label);
|
||||
draw_modpath(path_sig, nex_str);
|
||||
|
||||
} else {
|
||||
nex_private = draw_net_input_drive(nex, drivers[0]);
|
||||
}
|
||||
return nex_private;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (ndrivers) {
|
||||
unsigned int inst;
|
||||
for (inst = 0; inst < ndrivers; inst += 4) {
|
||||
if (ndrivers > 4)
|
||||
fprintf(vvp_out, "RS_%p/%d/%d .resolv tri",
|
||||
nex, level, inst);
|
||||
else
|
||||
fprintf(vvp_out, "RS_%p .resolv %s",
|
||||
nex, resolv_type);
|
||||
|
||||
for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) {
|
||||
if (level) {
|
||||
fprintf(vvp_out, ", RS_%p/%d/%d",
|
||||
nex, level - 1, idx*4);
|
||||
} else {
|
||||
char*drive = draw_net_input_drive(nex, drivers[idx]);
|
||||
fprintf(vvp_out, ", %s", drive);
|
||||
free(drive);
|
||||
}
|
||||
}
|
||||
for ( ; idx < inst+4 ; idx += 1) {
|
||||
fprintf(vvp_out, ", ");
|
||||
draw_C4_repeated_constant('z',width_of_nexus(nex));
|
||||
}
|
||||
|
||||
fprintf(vvp_out, ";\n");
|
||||
}
|
||||
if (ndrivers > 4)
|
||||
ndrivers = (ndrivers+3) / 4;
|
||||
else
|
||||
ndrivers = 0;
|
||||
level += 1;
|
||||
}
|
||||
|
||||
sprintf(result, "RS_%p", nex);
|
||||
nex_private = strdup(result);
|
||||
return nex_private;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a cached description of the nexus input, or create one if this
|
||||
* nexus has not been cached yet. This is a wrapper for the common
|
||||
* case call to draw_net_input_x.
|
||||
*/
|
||||
const char*draw_net_input(ivl_nexus_t nex)
|
||||
{
|
||||
struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*)
|
||||
ivl_nexus_get_private(nex);
|
||||
|
||||
/* If this nexus already has a label, then its input is
|
||||
already figured out. Just return the existing label. */
|
||||
if (nex_data && nex_data->net_input)
|
||||
return nex_data->net_input;
|
||||
|
||||
if (nex_data == 0) {
|
||||
nex_data = new_nexus_data();
|
||||
ivl_nexus_set_private(nex, nex_data);
|
||||
}
|
||||
|
||||
assert(nex_data->net_input == 0);
|
||||
nex_data->net_input = draw_net_input_x(nex, 0, 0, nex_data);
|
||||
|
||||
return nex_data->net_input;
|
||||
}
|
||||
|
||||
const char*draw_input_from_net(ivl_nexus_t nex)
|
||||
{
|
||||
static char result[32];
|
||||
|
|
@ -1184,9 +546,9 @@ static void draw_delay(ivl_net_logic_t lptr)
|
|||
return;
|
||||
|
||||
/* FIXME: Assume that the expression is a constant */
|
||||
assert(number_is_immediate(d0, 64));
|
||||
assert(number_is_immediate(d1, 64));
|
||||
assert(number_is_immediate(d2, 64));
|
||||
assert(number_is_immediate(d0, 64, 0));
|
||||
assert(number_is_immediate(d1, 64, 0));
|
||||
assert(number_is_immediate(d2, 64, 0));
|
||||
|
||||
if (d0 == d1 && d1 == d2)
|
||||
fprintf(vvp_out, " (%lu)", get_number_immediate(d0));
|
||||
|
|
@ -1500,9 +862,9 @@ static void draw_logic_in_scope(ivl_net_logic_t lptr)
|
|||
ivl_expr_t fall_exp = ivl_logic_delay(lptr,1);
|
||||
ivl_expr_t decay_exp = ivl_logic_delay(lptr,2);
|
||||
|
||||
if (number_is_immediate(rise_exp,64)
|
||||
&& number_is_immediate(fall_exp,64)
|
||||
&& number_is_immediate(decay_exp,64)) {
|
||||
if (number_is_immediate(rise_exp,64,0)
|
||||
&& number_is_immediate(fall_exp,64,0)
|
||||
&& number_is_immediate(decay_exp,64,0)) {
|
||||
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
lptr, get_number_immediate(rise_exp),
|
||||
|
|
@ -1693,9 +1055,9 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net)
|
|||
|
||||
const char*dly = "";
|
||||
if (d_rise != 0) {
|
||||
assert(number_is_immediate(d_rise, 64));
|
||||
assert(number_is_immediate(d_fall, 64));
|
||||
assert(number_is_immediate(d_decay, 64));
|
||||
assert(number_is_immediate(d_rise, 64, 0));
|
||||
assert(number_is_immediate(d_fall, 64, 0));
|
||||
assert(number_is_immediate(d_decay, 64, 0));
|
||||
dly = "/d";
|
||||
fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n",
|
||||
net, get_number_immediate(d_rise),
|
||||
|
|
@ -1717,6 +1079,31 @@ static void draw_lpm_abs(ivl_lpm_t net)
|
|||
net, dly, src_table[0]);
|
||||
}
|
||||
|
||||
static void draw_lpm_cast_int(ivl_lpm_t net)
|
||||
{
|
||||
const char*src_table[1];
|
||||
draw_lpm_data_inputs(net, 0, 1, src_table);
|
||||
|
||||
const char*dly = draw_lpm_output_delay(net);
|
||||
|
||||
fprintf(vvp_out, "L_%p%s .cast/int %u, %s;\n",
|
||||
net, dly, ivl_lpm_width(net), src_table[0]);
|
||||
}
|
||||
|
||||
static void draw_lpm_cast_real(ivl_lpm_t net)
|
||||
{
|
||||
const char*src_table[1];
|
||||
draw_lpm_data_inputs(net, 0, 1, src_table);
|
||||
|
||||
const char*dly = draw_lpm_output_delay(net);
|
||||
|
||||
const char*is_signed = "";
|
||||
if (ivl_lpm_signed(net)) is_signed = ".s";
|
||||
|
||||
fprintf(vvp_out, "L_%p%s .cast/real%s %s;\n",
|
||||
net, dly, is_signed, src_table[0]);
|
||||
}
|
||||
|
||||
static void draw_lpm_add(ivl_lpm_t net)
|
||||
{
|
||||
const char*src_table[2];
|
||||
|
|
@ -1767,20 +1154,9 @@ static void draw_lpm_add(ivl_lpm_t net)
|
|||
case IVL_LPM_POW:
|
||||
if (dto == IVL_VT_REAL)
|
||||
type = "pow.r";
|
||||
else if (ivl_lpm_signed(net)) {
|
||||
else if (ivl_lpm_signed(net))
|
||||
type = "pow.s";
|
||||
if (width > 8*sizeof(long)) {
|
||||
fprintf(stderr, "%s:%u: sorry (vvp-tgt): Signed power "
|
||||
#ifdef __MINGW32__ /* MinGW does not know about z. */
|
||||
"result must be no more than %u bits.\n",
|
||||
#else
|
||||
"result must be no more than %zu bits.\n",
|
||||
#endif
|
||||
ivl_lpm_file(net), ivl_lpm_lineno(net),
|
||||
8*sizeof(long));
|
||||
exit(1);
|
||||
}
|
||||
} else
|
||||
else
|
||||
type = "pow";
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2249,94 +1625,6 @@ static void draw_lpm_part_pv(ivl_lpm_t net)
|
|||
fprintf(vvp_out, ", %u, %u, %u;\n", base, width, signal_width);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the drawing of a bi-directional part select. The two ports
|
||||
* are simultaneously input and output. A simple minded connect of the
|
||||
* input to the output causes a functor cycle which will lock into an
|
||||
* X value, so something special is needed.
|
||||
*
|
||||
* NOTE: The inputs of the tran device at this point need to be from
|
||||
* all the drivers of the nexus *except* the tran itself. This
|
||||
* function will draw three labels that can be linked:
|
||||
*
|
||||
* The ivl_lpm_q of a part(bi) may be a smaller vector then the
|
||||
* ivl_lpm_data, the tran acts like a forward part select in that
|
||||
* way.
|
||||
*
|
||||
* The device creates these nodes:
|
||||
*
|
||||
* - L_%p/i
|
||||
* This is the Q port of the tran resolved and padded to the maximum
|
||||
* width of the tran. The tran itself is not included in the
|
||||
* resolution of this port.
|
||||
*
|
||||
* - L_%p/V
|
||||
* This is the Q and D parts resolved together, still without the tran
|
||||
* driving anything.
|
||||
*
|
||||
* - L_%p/P
|
||||
* This is the /V node part-selected back to the dimensions of the Q
|
||||
* side.
|
||||
*/
|
||||
static void draw_lpm_part_bi(ivl_lpm_t net)
|
||||
{
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
unsigned base = ivl_lpm_base(net);
|
||||
unsigned signal_width = width_of_nexus(ivl_lpm_data(net,0));
|
||||
|
||||
unsigned idx;
|
||||
ivl_nexus_t nex;
|
||||
ivl_nexus_ptr_t ptr = 0;
|
||||
|
||||
char*p_str;
|
||||
char*v_str;
|
||||
|
||||
/* It seems implausible that the two inputs of a tran will be
|
||||
connected together. So assert that this is so to simplify
|
||||
the code to look for the nexus_ptr_t objects. */
|
||||
assert(ivl_lpm_q(net,0) != ivl_lpm_data(net,0));
|
||||
|
||||
nex = ivl_lpm_q(net,0);
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ptr = ivl_nexus_ptr(nex, idx);
|
||||
if (ivl_nexus_ptr_lpm(ptr) == net)
|
||||
break;
|
||||
}
|
||||
assert(ptr != 0);
|
||||
p_str = draw_net_input_x(nex, ptr, 0, 0);
|
||||
|
||||
nex = ivl_lpm_data(net,0);
|
||||
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
||||
ptr = ivl_nexus_ptr(nex, idx);
|
||||
if (ivl_nexus_ptr_lpm(ptr) == net)
|
||||
break;
|
||||
}
|
||||
v_str = draw_net_input_x(nex, ptr, OMIT_PART_BI_DATA, 0);
|
||||
|
||||
/* Pad the part-sized input out to a common width...
|
||||
The /i label is the Q side of the tran, resolved except for
|
||||
the tran itself and padded (with z) to the larger width. */
|
||||
fprintf(vvp_out, "L_%p/i .part/pv %s, %u, %u, %u;\n",
|
||||
net, p_str, base, width, signal_width);
|
||||
|
||||
/* Resolve together the two halves of the tran...
|
||||
The /V label is the ports of the tran (now the same width)
|
||||
resolved together. Neither input to this resolver includes
|
||||
the tran itself. */
|
||||
fprintf(vvp_out, "L_%p/V .resolv tri, L_%p/i, %s;\n",
|
||||
net, net, v_str);
|
||||
|
||||
/* The full-width side is created by the tran device, all we
|
||||
have left to to is take a part select of that for the
|
||||
smaller output, and this becomes the part select output of
|
||||
the BI device. */
|
||||
fprintf(vvp_out, "L_%p/P .part L_%p/V, %u, %u;\n", net,
|
||||
net, base, width);
|
||||
|
||||
free(p_str);
|
||||
free(v_str);
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw unary reduction devices.
|
||||
*/
|
||||
|
|
@ -2370,6 +1658,14 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
|
|||
draw_lpm_abs(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_CAST_INT:
|
||||
draw_lpm_cast_int(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_CAST_REAL:
|
||||
draw_lpm_cast_real(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_SUB:
|
||||
case IVL_LPM_MULT:
|
||||
|
|
@ -2383,10 +1679,6 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
|
|||
draw_lpm_array(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_PART_BI:
|
||||
draw_lpm_part_bi(net);
|
||||
return;
|
||||
|
||||
case IVL_LPM_PART_VP:
|
||||
draw_lpm_part(net);
|
||||
return;
|
||||
|
|
@ -2569,6 +1861,11 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent)
|
|||
draw_lpm_in_scope(lpm);
|
||||
}
|
||||
|
||||
for (idx = 0 ; idx < ivl_scope_switches(net) ; idx += 1) {
|
||||
ivl_switch_t sw = ivl_scope_switch(net, idx);
|
||||
draw_switch_in_scope(sw);
|
||||
}
|
||||
|
||||
if (ivl_scope_type(net) == IVL_SCT_TASK)
|
||||
draw_task_definition(net);
|
||||
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ verinum::verinum(int64_t that)
|
|||
bits_ = new V[nbits_];
|
||||
for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) {
|
||||
bits_[idx] = (that & 1)? V1 : V0;
|
||||
that /= 2;
|
||||
that >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -508,7 +508,7 @@ verinum trim_vnum(const verinum&that)
|
|||
|
||||
/* Now top is the index of the highest non-zero bit. If
|
||||
that turns out to the highest bit in the vector, then
|
||||
tere is no trimming possible. */
|
||||
there is no trimming possible. */
|
||||
if (top+1 == that.len())
|
||||
return that;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ O = sys_table.o sys_convert.o sys_deposit.o sys_display.o sys_fileio.o \
|
|||
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 priv.o sdf_lexor.o sdf_parse.o stringheap.o
|
||||
mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \
|
||||
vams_simparam.o
|
||||
|
||||
ifeq (@HAVE_LIBZ@,yes)
|
||||
ifeq (@HAVE_LIBBZ2@,yes)
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ static int lookup_keyword(const char*text)
|
|||
}
|
||||
|
||||
/*
|
||||
* Create a string witout the leading and trailing quotes.
|
||||
* Create a string without the leading and trailing quotes.
|
||||
*/
|
||||
static void process_quoted_string(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,16 +60,6 @@ struct strobe_cb_info {
|
|||
unsigned mcd;
|
||||
};
|
||||
|
||||
int is_constant(vpiHandle obj)
|
||||
{
|
||||
if (vpi_get(vpiType, obj) == vpiConstant)
|
||||
return vpiConstant;
|
||||
if (vpi_get(vpiType, obj) == vpiParameter)
|
||||
return vpiParameter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The number of decimal digits needed to represent a
|
||||
// nr_bits binary number is floor(nr_bits*log_10(2))+1,
|
||||
// where log_10(2) = 0.30102999566398.... and I approximate
|
||||
|
|
@ -277,7 +267,7 @@ static void format_time(unsigned mcd, int fsize,
|
|||
/* Fill the leading characters to make up the desired
|
||||
width. This may require a '0' if the last character
|
||||
written was the decimal point. This may also require a '0'
|
||||
if there are no other characters at all in the ouput. */
|
||||
if there are no other characters at all in the output. */
|
||||
if (fusize > 0) {
|
||||
while (bp > start_address) {
|
||||
if (*bp == '.' || strcmp(bp, timeformat_info.suff) == 0)
|
||||
|
|
@ -665,7 +655,7 @@ static int format_str_char(vpiHandle scope, unsigned int mcd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (is_constant(argv[idx])
|
||||
if (is_constant_obj(argv[idx])
|
||||
&& (vpi_get(vpiConstType, argv[idx]) == vpiRealConst)) {
|
||||
|
||||
value.format = vpiRealVal;
|
||||
|
|
@ -827,6 +817,7 @@ static void do_display(unsigned int mcd, struct strobe_cb_info*info)
|
|||
case vpiReg:
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiPartSelect:
|
||||
do_display_numeric(mcd, info, item);
|
||||
break;
|
||||
|
||||
|
|
@ -1118,11 +1109,17 @@ static PLI_INT32 sys_monitor_calltf(PLI_BYTE8*name)
|
|||
for (idx = 0 ; idx < monitor_info.nitems ; idx += 1) {
|
||||
|
||||
switch (vpi_get(vpiType, monitor_info.items[idx])) {
|
||||
case vpiMemoryWord:
|
||||
/*
|
||||
* We only support constant selections. Make this
|
||||
* better when we add a real compiletf routine.
|
||||
*/
|
||||
assert(vpi_get(vpiConstantSelect, monitor_info.items[idx]));
|
||||
case vpiNet:
|
||||
case vpiReg:
|
||||
case vpiIntegerVar:
|
||||
case vpiRealVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiPartSelect:
|
||||
/* Monitoring reg and net values involves setting
|
||||
a callback for value changes. Pass the storage
|
||||
pointer for the callback itself as user_data so
|
||||
|
|
@ -1418,7 +1415,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus,
|
|||
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
|
||||
fmtb);
|
||||
} else {
|
||||
/* If a width was not giveni, use a width of zero. */
|
||||
/* If a width was not given, use a width of zero. */
|
||||
if (width == -1) width = 0;
|
||||
if (ljust == 0) sprintf(result, "%*c", width,
|
||||
value.value.str[strlen(value.value.str)-1]);
|
||||
|
|
@ -1662,7 +1659,7 @@ static unsigned int get_format_char(char **rtn, int ljust, int plus,
|
|||
/* If a width was not given use a width of zero. */
|
||||
if (width == -1) width = 0;
|
||||
nbits = vpi_get(vpiSize, info->items[*idx]);
|
||||
/* This is 4 chars for all but the last bit (strenght + "_")
|
||||
/* This is 4 chars for all but the last bit (strength + "_")
|
||||
* which only needs three chars (strength), but then you need
|
||||
* space for the EOS '\0', so it is just number of bits * 4. */
|
||||
rsize = nbits*4;
|
||||
|
|
@ -1836,6 +1833,7 @@ static char *get_display(unsigned int *rtnsz, struct strobe_cb_info *info)
|
|||
case vpiReg:
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiPartSelect:
|
||||
width = get_numeric(&result, info, item);
|
||||
rtn = realloc(rtn, (size+width)*sizeof(char));
|
||||
memcpy(rtn+size-1, result, width);
|
||||
|
|
@ -2185,12 +2183,7 @@ static PLI_INT32 sys_printtimescale_calltf(PLI_BYTE8*xx)
|
|||
vpiHandle argv = vpi_iterate(vpiArgument, sys);
|
||||
vpiHandle scope;
|
||||
if (!argv) {
|
||||
vpiHandle parent = vpi_handle(vpiScope, sys);
|
||||
scope = NULL; /* fallback value if parent is NULL */
|
||||
while (parent) {
|
||||
scope = parent;
|
||||
parent = vpi_handle(vpiScope, scope);
|
||||
}
|
||||
scope = sys_func_module(sys);
|
||||
} else {
|
||||
scope = vpi_scan(argv);
|
||||
vpi_free_object(argv);
|
||||
|
|
|
|||
1115
vpi/sys_fileio.c
1115
vpi/sys_fileio.c
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -16,44 +16,11 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_finish.c,v 1.11 2007/04/09 22:49:33 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "vpi_config.h"
|
||||
|
||||
# include "vpi_user.h"
|
||||
# include <string.h>
|
||||
|
||||
static PLI_INT32 sys_finish_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
|
||||
/* The argument is optional. */
|
||||
if (argv == 0) return 0;
|
||||
arg = vpi_scan(argv);
|
||||
|
||||
/* A string diagnostic message level makes no sense. */
|
||||
if (vpi_get(vpiType, arg) == vpiConstant &&
|
||||
vpi_get(vpiConstType, arg) == vpiStringConst) {
|
||||
vpi_printf("Error: %s does not take a string argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* These functions take at most one argument (diagnostic message). */
|
||||
arg = vpi_scan(argv);
|
||||
if (arg != 0) {
|
||||
vpi_printf("Error: %s takes at most one argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vpi_scan returning 0 (NULL) has already freed argv. */
|
||||
return 0;
|
||||
}
|
||||
#include "vpi_config.h"
|
||||
#include "vpi_user.h"
|
||||
#include "sys_priv.h"
|
||||
#include <string.h>
|
||||
|
||||
static PLI_INT32 sys_finish_calltf(PLI_BYTE8 *name)
|
||||
{
|
||||
|
|
@ -88,55 +55,16 @@ void sys_finish_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$finish";
|
||||
tf_data.calltf = sys_finish_calltf;
|
||||
tf_data.compiletf = sys_finish_compiletf;
|
||||
tf_data.compiletf = sys_one_opt_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = (PLI_BYTE8*)"$finish";
|
||||
tf_data.user_data = "$finish";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$stop";
|
||||
tf_data.calltf = sys_finish_calltf;
|
||||
tf_data.compiletf = sys_finish_compiletf;
|
||||
tf_data.compiletf = sys_one_opt_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = (PLI_BYTE8*)"$stop";
|
||||
tf_data.user_data = "$stop";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* $Log: sys_finish.c,v $
|
||||
* Revision 1.11 2007/04/09 22:49:33 steve
|
||||
* More strict use of PLI_BYTE8 type.
|
||||
*
|
||||
* Revision 1.10 2006/10/30 22:45:37 steve
|
||||
* Updates for Cygwin portability (pr1585922)
|
||||
*
|
||||
* Revision 1.9 2004/01/21 01:22:53 steve
|
||||
* Give the vip directory its own configure and vpi_config.h
|
||||
*
|
||||
* Revision 1.8 2003/02/21 03:24:03 steve
|
||||
* Make the $stop system task really vpiStop.
|
||||
*
|
||||
* Revision 1.7 2002/08/12 01:35:04 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.6 2001/07/25 03:10:50 steve
|
||||
* Create a config.h.in file to hold all the config
|
||||
* junk, and support gcc 3.0. (Stephan Boettcher)
|
||||
*
|
||||
* Revision 1.5 2001/01/01 19:33:44 steve
|
||||
* Add $stop that does a finish.
|
||||
*
|
||||
* Revision 1.4 2000/02/23 02:56:56 steve
|
||||
* Macintosh compilers do not support ident.
|
||||
*
|
||||
* Revision 1.3 1999/08/28 02:10:44 steve
|
||||
* Call the right vpiFinish code.
|
||||
*
|
||||
* Revision 1.2 1999/08/19 02:51:03 steve
|
||||
* Add vpi_sim_control
|
||||
*
|
||||
* Revision 1.1 1999/08/15 01:23:56 steve
|
||||
* Convert vvm to implement system tasks with vpi.
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
|||
112
vpi/sys_icarus.c
112
vpi/sys_icarus.c
|
|
@ -16,110 +16,17 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "vpi_config.h"
|
||||
#include <assert.h>
|
||||
#include <vpi_user.h>
|
||||
#include "sys_priv.h"
|
||||
|
||||
|
||||
/*
|
||||
* Routine to return the width in bits of a CPU word (long).
|
||||
*/
|
||||
static PLI_INT32 vvp_cpu_wordsize_calltf(PLI_BYTE8* ud)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
assert(callh != 0);
|
||||
s_vpi_value val;
|
||||
(void) ud; /* Not used! */
|
||||
|
||||
/* Calculate the result */
|
||||
val.format = vpiIntVal;
|
||||
val.value.integer = 8*sizeof(long);
|
||||
|
||||
/* Return the result */
|
||||
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 size_32(PLI_BYTE8* ud)
|
||||
{
|
||||
(void) ud; /* Not used! */
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Routine to finish the simulation and return a value to the
|
||||
* calling environment.
|
||||
*/
|
||||
static PLI_INT32 finish_and_return_compiletf(PLI_BYTE8* ud)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
(void) ud; /* Not used! */
|
||||
|
||||
/* We must have at least one argument. */
|
||||
if (argv == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("$finish_and_return requires an argument.\n");
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This must be a numeric argument. */
|
||||
arg = vpi_scan(argv);
|
||||
switch(vpi_get(vpiType, arg)) {
|
||||
case vpiConstant:
|
||||
case vpiParameter:
|
||||
/* String constants are invalid numeric values. */
|
||||
if (vpi_get(vpiConstType, arg) == vpiStringConst) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("The argument to $finish_and_return must be numeric.\n");
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiNet:
|
||||
case vpiRealVar:
|
||||
case vpiReg:
|
||||
case vpiTimeVar:
|
||||
break;
|
||||
|
||||
default:
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("The argument to $finish_and_return must be numeric.\n");
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We can only have one argument. */
|
||||
if (vpi_scan(argv) != 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("$finish_and_return takes a single argument.\n");
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* ud)
|
||||
static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
s_vpi_value val;
|
||||
(void) ud; /* Not used! */
|
||||
(void) name; /* Not used! */
|
||||
|
||||
/* Get the return value. */
|
||||
arg = vpi_scan(argv);
|
||||
|
|
@ -142,20 +49,11 @@ void sys_special_register(void)
|
|||
{
|
||||
s_vpi_systf_data tf_data;
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.sysfunctype = vpiIntFunc;
|
||||
tf_data.calltf = vvp_cpu_wordsize_calltf;
|
||||
tf_data.compiletf = 0;
|
||||
tf_data.sizetf = size_32;
|
||||
tf_data.tfname = "$vvp_cpu_wordsize";
|
||||
tf_data.user_data = 0;
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.calltf = finish_and_return_calltf;
|
||||
tf_data.compiletf = finish_and_return_compiletf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.tfname = "$finish_and_return";
|
||||
tf_data.user_data = 0;
|
||||
tf_data.user_data = "$finish_and_return";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2002 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
|
||||
|
|
@ -16,9 +16,6 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_lxt.c,v 1.28 2007/03/14 04:05:51 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "sys_priv.h"
|
||||
# include "lxt_write.h"
|
||||
|
|
@ -324,9 +321,9 @@ inline static int install_dumpvars_callback(void)
|
|||
return 0;
|
||||
|
||||
if (dumpvars_status == 2) {
|
||||
vpi_mcd_printf(1, "LXT warning:" " $dumpvars ignored, previously"
|
||||
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||
dumpvars_time);
|
||||
vpi_printf("LXT warning: $dumpvars ignored, previously"
|
||||
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||
dumpvars_time);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -431,22 +428,23 @@ static void *close_dumpfile(void)
|
|||
return (dump_file = NULL);
|
||||
}
|
||||
|
||||
static void open_dumpfile(void)
|
||||
static void open_dumpfile(vpiHandle callh)
|
||||
{
|
||||
if (dump_path == 0) dump_path = strdup("dump.lxt");
|
||||
|
||||
dump_file = lt_init(dump_path);
|
||||
|
||||
if (dump_file == 0) {
|
||||
vpi_mcd_printf(1, "LXT Error: Unable to open %s for output.\n",
|
||||
dump_path);
|
||||
vpi_printf("LXT Error: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("Unable to open %s for output.\n", dump_path);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return;
|
||||
} else {
|
||||
int prec = vpi_get(vpiTimePrecision, 0);
|
||||
|
||||
vpi_mcd_printf(1, "LXT info: dumpfile %s opened for output.\n",
|
||||
dump_path);
|
||||
vpi_printf("LXT info: dumpfile %s opened for output.\n",
|
||||
dump_path);
|
||||
|
||||
assert(prec >= -15);
|
||||
lt_set_timescale(dump_file, prec);
|
||||
|
|
@ -468,9 +466,9 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
|||
|
||||
/* $dumpfile must be called before $dumpvars starts! */
|
||||
if (dumpvars_status != 0) {
|
||||
vpi_mcd_printf(1, "LXT warning: %s called after $dumpvars started"
|
||||
",\n using existing file (%s).\n",
|
||||
name, dump_path);
|
||||
vpi_printf("LXT warning: %s called after $dumpvars started,\n"
|
||||
" using existing file (%s).\n",
|
||||
name, dump_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -480,8 +478,8 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
|||
path = strdup(value.value.str);
|
||||
|
||||
if (dump_path) {
|
||||
vpi_mcd_printf(1, "LXT warning: Overriding dump file %s with"
|
||||
" %s\n", dump_path, path);
|
||||
vpi_printf("LXT warning: Overriding dump file %s with %s\n",
|
||||
dump_path, path);
|
||||
free(dump_path);
|
||||
}
|
||||
dump_path = path;
|
||||
|
|
@ -542,16 +540,9 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
|
||||
switch (vpi_get(vpiType, item)) {
|
||||
|
||||
case vpiMemory:
|
||||
/* don't know how to watch memories. */
|
||||
break;
|
||||
|
||||
case vpiNamedEvent:
|
||||
/* There is nothing in named events to dump. */
|
||||
break;
|
||||
|
||||
case vpiNet: type = "wire"; if(0){
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiTimeVar:
|
||||
case vpiReg: type = "reg"; }
|
||||
|
||||
|
|
@ -644,18 +635,16 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
vpi_get_str(vpiFullName, item);
|
||||
|
||||
#if 0
|
||||
vpi_mcd_printf(1,
|
||||
"LXT info:"
|
||||
" scanning scope %s, %u levels\n",
|
||||
fullname, depth);
|
||||
vpi_printf("LXT info: scanning scope %s, %u levels\n",
|
||||
fullname, depth);
|
||||
#endif
|
||||
nskip = 0 != vcd_names_search(&lxt_tab, fullname);
|
||||
|
||||
if (!nskip)
|
||||
vcd_names_add(&lxt_tab, fullname);
|
||||
else
|
||||
vpi_mcd_printf(1, "LXT warning: ignoring signals in"
|
||||
" previously scanned scope %s\n", fullname);
|
||||
vpi_printf("LXT warning: ignoring signals in "
|
||||
"previously scanned scope %s\n", fullname);
|
||||
|
||||
name = vpi_get_str(vpiName, item);
|
||||
|
||||
|
|
@ -674,8 +663,8 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
break;
|
||||
|
||||
default:
|
||||
vpi_mcd_printf(1, "LXT warning: $dumpvars: Unsupported parameter"
|
||||
" type (%d)\n", vpi_get(vpiType, item));
|
||||
vpi_printf("LXT warning: $dumpvars: Unsupported parameter "
|
||||
"type (%s)\n", vpi_get_str(vpiType, item));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -684,7 +673,6 @@ static int draw_scope(vpiHandle item)
|
|||
{
|
||||
int depth;
|
||||
const char *name;
|
||||
// char *type; // Not needed, see below.
|
||||
|
||||
vpiHandle scope = vpi_handle(vpiScope, item);
|
||||
if (!scope)
|
||||
|
|
@ -693,20 +681,6 @@ static int draw_scope(vpiHandle item)
|
|||
depth = 1 + draw_scope(scope);
|
||||
name = vpi_get_str(vpiName, scope);
|
||||
|
||||
#if 0 /* The type information is not needed by the LXT dumper. */
|
||||
switch (vpi_get(vpiType, scope)) {
|
||||
case vpiNamedBegin: type = "begin"; break;
|
||||
case vpiTask: type = "task"; break;
|
||||
case vpiFunction: type = "function"; break;
|
||||
case vpiNamedFork: type = "fork"; break;
|
||||
case vpiModule: type = "module"; break;
|
||||
default:
|
||||
vpi_mcd_printf(1, "LXT Error: $dumpvars: Unsupported scope "
|
||||
"type (%d)\n", vpi_get(vpiType, item));
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
push_scope(name);
|
||||
|
||||
return depth;
|
||||
|
|
@ -721,7 +695,7 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
|
|||
unsigned depth = 0;
|
||||
|
||||
if (dump_file == 0) {
|
||||
open_dumpfile();
|
||||
open_dumpfile(callh);
|
||||
if (dump_file == 0) return 0;
|
||||
}
|
||||
|
||||
|
|
@ -784,7 +758,7 @@ void sys_lxt_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpall";
|
||||
tf_data.calltf = sys_dumpall_calltf;
|
||||
tf_data.compiletf = sys_dumpall_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpall";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -792,7 +766,7 @@ void sys_lxt_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpfile";
|
||||
tf_data.calltf = sys_dumpfile_calltf;
|
||||
tf_data.compiletf = sys_dumpfile_compiletf;
|
||||
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpfile";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -800,7 +774,7 @@ void sys_lxt_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpflush";
|
||||
tf_data.calltf = sys_dumpflush_calltf;
|
||||
tf_data.compiletf = sys_dumpflush_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpflush";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -808,7 +782,7 @@ void sys_lxt_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumplimit";
|
||||
tf_data.calltf = sys_dumplimit_calltf;
|
||||
tf_data.compiletf = sys_dumplimit_compiletf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumplimit";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -816,7 +790,7 @@ void sys_lxt_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpoff";
|
||||
tf_data.calltf = sys_dumpoff_calltf;
|
||||
tf_data.compiletf = sys_dumpoff_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpoff";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -824,7 +798,7 @@ void sys_lxt_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpon";
|
||||
tf_data.calltf = sys_dumpon_calltf;
|
||||
tf_data.compiletf = sys_dumpon_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpon";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -837,4 +811,3 @@ void sys_lxt_register()
|
|||
tf_data.user_data = "$dumpvars";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2004 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2003-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
|
||||
|
|
@ -16,9 +16,6 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_lxt2.c,v 1.10 2007/03/14 04:05:51 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "sys_priv.h"
|
||||
# include "lxt2_write.h"
|
||||
|
|
@ -322,9 +319,9 @@ inline static int install_dumpvars_callback(void)
|
|||
if (dumpvars_status == 1) return 0;
|
||||
|
||||
if (dumpvars_status == 2) {
|
||||
vpi_mcd_printf(1, "LXT2 warning: $dumpvars ignored, previously"
|
||||
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||
dumpvars_time);
|
||||
vpi_printf("LXT2 warning: $dumpvars ignored, previously"
|
||||
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||
dumpvars_time);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -429,22 +426,23 @@ static void *close_dumpfile(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void open_dumpfile(void)
|
||||
static void open_dumpfile(vpiHandle callh)
|
||||
{
|
||||
if (dump_path == 0) dump_path = strdup("dump.lx2");
|
||||
|
||||
dump_file = lxt2_wr_init(dump_path);
|
||||
|
||||
if (dump_file == 0) {
|
||||
vpi_mcd_printf(1, "LXT2 Error: Unable to open %s for output.\n",
|
||||
dump_path);
|
||||
vpi_printf("LXT2 Error: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("Unable to open %s for output.\n", dump_path);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return;
|
||||
} else {
|
||||
int prec = vpi_get(vpiTimePrecision, 0);
|
||||
|
||||
vpi_mcd_printf(1, "LXT2 info: dumpfile %s opened for output.\n",
|
||||
dump_path);
|
||||
vpi_printf("LXT2 info: dumpfile %s opened for output.\n",
|
||||
dump_path);
|
||||
|
||||
assert(prec >= -15);
|
||||
lxt2_wr_set_timescale(dump_file, prec);
|
||||
|
|
@ -468,9 +466,9 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
|||
|
||||
/* $dumpfile must be called before $dumpvars starts! */
|
||||
if (dumpvars_status != 0) {
|
||||
vpi_mcd_printf(1, "LXT2 warning: %s called after $dumpvars started"
|
||||
",\n using existing file (%s).\n",
|
||||
name, dump_path);
|
||||
vpi_printf("LXT2 warning: %s called after $dumpvars started,\n"
|
||||
" using existing file (%s).\n",
|
||||
name, dump_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -480,8 +478,8 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
|||
path = strdup(value.value.str);
|
||||
|
||||
if (dump_path) {
|
||||
vpi_mcd_printf(1, "LXT2 warning: Overriding dump file %s with"
|
||||
" %s.\n", dump_path, path);
|
||||
vpi_printf("LXT2 warning: Overriding dump file %s with %s.\n",
|
||||
dump_path, path);
|
||||
free(dump_path);
|
||||
}
|
||||
dump_path = path;
|
||||
|
|
@ -548,16 +546,9 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
|
||||
switch (vpi_get(vpiType, item)) {
|
||||
|
||||
case vpiMemory:
|
||||
/* don't know how to watch memories. */
|
||||
break;
|
||||
|
||||
case vpiNamedEvent:
|
||||
/* There is nothing in named events to dump. */
|
||||
break;
|
||||
|
||||
case vpiNet: type = "wire"; if(0){
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiTimeVar:
|
||||
case vpiReg: type = "reg"; }
|
||||
|
||||
|
|
@ -657,18 +648,16 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
vpi_get_str(vpiFullName, item);
|
||||
|
||||
#if 0
|
||||
vpi_mcd_printf(1,
|
||||
"LXT2 info:"
|
||||
" scanning scope %s, %u levels\n",
|
||||
fullname, depth);
|
||||
vpi_printf("LXT2 info: scanning scope %s, %u levels\n",
|
||||
fullname, depth);
|
||||
#endif
|
||||
nskip = 0 != vcd_names_search(&lxt_tab, fullname);
|
||||
|
||||
if (!nskip)
|
||||
vcd_names_add(&lxt_tab, fullname);
|
||||
else
|
||||
vpi_mcd_printf(1, "LXT2 warning: ignoring signals in"
|
||||
" previously scanned scope %s\n", fullname);
|
||||
vpi_printf("LXT2 warning: ignoring signals in "
|
||||
"previously scanned scope %s\n", fullname);
|
||||
|
||||
name = vpi_get_str(vpiName, item);
|
||||
|
||||
|
|
@ -687,8 +676,8 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
break;
|
||||
|
||||
default:
|
||||
vpi_mcd_printf(1, "LXT2 warning: $dumpvars: Unsupported parameter"
|
||||
" type (%d)\n", vpi_get(vpiType, item));
|
||||
vpi_printf("LXT2 warning: $dumpvars: Unsupported parameter "
|
||||
"type (%s)\n", vpi_get_str(vpiType, item));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -697,7 +686,6 @@ static int draw_scope(vpiHandle item)
|
|||
{
|
||||
int depth;
|
||||
const char *name;
|
||||
// char *type; // Not needed, see below.
|
||||
|
||||
vpiHandle scope = vpi_handle(vpiScope, item);
|
||||
if (!scope) return 0;
|
||||
|
|
@ -705,20 +693,6 @@ static int draw_scope(vpiHandle item)
|
|||
depth = 1 + draw_scope(scope);
|
||||
name = vpi_get_str(vpiName, scope);
|
||||
|
||||
#if 0 /* The type information is not needed by the LXT2 dumper. */
|
||||
switch (vpi_get(vpiType, item)) {
|
||||
case vpiNamedBegin: type = "begin"; break;
|
||||
case vpiTask: type = "task"; break;
|
||||
case vpiFunction: type = "function"; break;
|
||||
case vpiNamedFork: type = "fork"; break;
|
||||
case vpiModule: type = "module"; break;
|
||||
default:
|
||||
vpi_mcd_printf(1, "LXT2 Error: $dumpvars: Unsupported scope "
|
||||
"type (%d)\n", vpi_get(vpiType, item));
|
||||
assert(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
push_scope(name);
|
||||
|
||||
return depth;
|
||||
|
|
@ -733,7 +707,7 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
|
|||
unsigned depth = 0;
|
||||
|
||||
if (dump_file == 0) {
|
||||
open_dumpfile();
|
||||
open_dumpfile(callh);
|
||||
if (dump_file == 0) return 0;
|
||||
}
|
||||
|
||||
|
|
@ -799,7 +773,7 @@ void sys_lxt2_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpall";
|
||||
tf_data.calltf = sys_dumpall_calltf;
|
||||
tf_data.compiletf = sys_dumpall_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpall";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -807,7 +781,7 @@ void sys_lxt2_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpfile";
|
||||
tf_data.calltf = sys_dumpfile_calltf;
|
||||
tf_data.compiletf = sys_dumpfile_compiletf;
|
||||
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpfile";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -815,7 +789,7 @@ void sys_lxt2_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpflush";
|
||||
tf_data.calltf = sys_dumpflush_calltf;
|
||||
tf_data.compiletf = sys_dumpflush_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpflush";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -823,7 +797,7 @@ void sys_lxt2_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumplimit";
|
||||
tf_data.calltf = sys_dumplimit_calltf;
|
||||
tf_data.compiletf = sys_dumplimit_compiletf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumplimit";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -831,7 +805,7 @@ void sys_lxt2_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpoff";
|
||||
tf_data.calltf = sys_dumpoff_calltf;
|
||||
tf_data.compiletf = sys_dumpoff_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpoff";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -839,7 +813,7 @@ void sys_lxt2_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpon";
|
||||
tf_data.calltf = sys_dumpon_calltf;
|
||||
tf_data.compiletf = sys_dumpon_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpon";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -852,4 +826,3 @@ void sys_lxt2_register()
|
|||
tf_data.user_data = "$dumpvars";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright (c) 2003-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 <assert.h>
|
||||
#include <string.h>
|
||||
#include "sys_priv.h"
|
||||
|
||||
PLI_UINT64 timerec_to_time64(const struct t_vpi_time*time)
|
||||
{
|
||||
PLI_UINT64 tmp;
|
||||
|
||||
tmp = time->high;
|
||||
tmp <<= 32;
|
||||
tmp |= (PLI_UINT64) time->low;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine returns 1 if the argument is a constant value,
|
||||
* otherwise it returns 0.
|
||||
*/
|
||||
unsigned is_constant_obj(vpiHandle obj)
|
||||
{
|
||||
assert(obj);
|
||||
unsigned rtn = 0;
|
||||
|
||||
switch(vpi_get(vpiType, obj)) {
|
||||
case vpiConstant:
|
||||
case vpiParameter:
|
||||
rtn = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine returns 1 if the argument supports has a numeric value,
|
||||
* otherwise it returns 0.
|
||||
*/
|
||||
unsigned is_numeric_obj(vpiHandle obj)
|
||||
{
|
||||
assert(obj);
|
||||
unsigned rtn = 0;
|
||||
|
||||
switch(vpi_get(vpiType, obj)) {
|
||||
case vpiConstant:
|
||||
case vpiParameter:
|
||||
/* These cannot be a string constant. */
|
||||
if (vpi_get(vpiConstType, obj) != vpiStringConst) rtn = 1;
|
||||
break;
|
||||
|
||||
/* These can have a valid numeric value. */
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiNet:
|
||||
case vpiPartSelect:
|
||||
case vpiRealVar:
|
||||
case vpiReg:
|
||||
case vpiTimeVar:
|
||||
rtn = 1;;
|
||||
break;
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This routine returns 1 if the argument supports a valid string value,
|
||||
* otherwise it returns 0.
|
||||
*/
|
||||
unsigned is_string_obj(vpiHandle obj)
|
||||
{
|
||||
assert(obj);
|
||||
unsigned rtn = 0;
|
||||
|
||||
switch(vpi_get(vpiType, obj)) {
|
||||
case vpiConstant:
|
||||
case vpiParameter: {
|
||||
/* These must be a string or binary constant. */
|
||||
PLI_INT32 ctype = vpi_get(vpiConstType, obj);
|
||||
if (ctype == vpiStringConst || ctype == vpiBinaryConst) rtn = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* These can have a valid string value. */
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiNet:
|
||||
case vpiPartSelect:
|
||||
case vpiReg:
|
||||
case vpiTimeVar:
|
||||
rtn = 1;;
|
||||
break;
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find the enclosing module.
|
||||
*/
|
||||
vpiHandle sys_func_module(vpiHandle obj)
|
||||
{
|
||||
assert(obj);
|
||||
|
||||
while (vpi_get(vpiType, obj) != vpiModule) {
|
||||
obj = vpi_handle(vpiScope, obj);
|
||||
assert(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard compiletf routines.
|
||||
*/
|
||||
|
||||
/* For system tasks/functions that do not take an argument. */
|
||||
PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
/* Make sure there are no arguments. */
|
||||
if (argv != 0) {
|
||||
char msg [64];
|
||||
snprintf(msg, 64, "ERROR: %s line %d:",
|
||||
vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
|
||||
unsigned argc = 0;
|
||||
while (vpi_scan(argv)) argc += 1;
|
||||
|
||||
vpi_printf("%s %s does not take an argument.\n", msg, name);
|
||||
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For system tasks/functions that take a single numeric argument. */
|
||||
PLI_INT32 sys_one_numeric_arg_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
/* Check that there is an argument and that it is numeric. */
|
||||
if (argv == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s requires a single numeric argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (! is_numeric_obj(vpi_scan(argv))) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s's argument must be numeric.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* Make sure there are no extra arguments. */
|
||||
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 %s takes a single numeric argument.\n", msg, name);
|
||||
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For system tasks/functions that take a single optional numeric argument. */
|
||||
PLI_INT32 sys_one_opt_numeric_arg_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
/* The argument is optional so just return if none are found. */
|
||||
if (argv == 0) return 0;
|
||||
|
||||
if (! is_numeric_obj(vpi_scan(argv))) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s's argument must be numeric.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* Make sure there are no extra arguments. */
|
||||
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 %s takes at most one numeric argument.\n",
|
||||
msg, name);
|
||||
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For system tasks/functions that take two numeric arguments. */
|
||||
PLI_INT32 sys_two_numeric_args_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
|
||||
/* Check that there are two argument and that they are numeric. */
|
||||
if (argv == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s requires two numeric arguments.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (! is_numeric_obj(vpi_scan(argv))) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s's first argument must be numeric.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
arg = vpi_scan(argv);
|
||||
if (! arg) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s requires a second (numeric) argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (! is_numeric_obj(arg)) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s's second argument must be numeric.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* Make sure there are no extra arguments. */
|
||||
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 %s takes two numeric arguments.\n", msg, name);
|
||||
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For system tasks/functions that take a single string argument. */
|
||||
PLI_INT32 sys_one_string_arg_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
/* Check that there is an argument and that it is a string. */
|
||||
if (argv == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s requires a single string argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
if (! is_string_obj(vpi_scan(argv))) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s's argument must be a string.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* Make sure there are no extra arguments. */
|
||||
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 %s takes a single string argument.\n", msg, name);
|
||||
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||
(int) strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __vpi_sys_priv_H
|
||||
#define __vpi_sys_priv_H
|
||||
/*
|
||||
* Copyright (c) 2002 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
|
||||
|
|
@ -18,12 +18,9 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_priv.h,v 1.8 2007/03/14 04:05:51 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "vpi_config.h"
|
||||
# include "vpi_user.h"
|
||||
#include "vpi_config.h"
|
||||
#include "vpi_user.h"
|
||||
|
||||
/*
|
||||
* Context structure for PRNG in mt19937int.c
|
||||
|
|
@ -36,9 +33,6 @@ struct context_s {
|
|||
extern void sgenrand(struct context_s *context, unsigned long seed);
|
||||
extern unsigned long genrand(struct context_s *context);
|
||||
|
||||
|
||||
extern int is_constant(vpiHandle obj);
|
||||
|
||||
extern PLI_UINT64 timerec_to_time64(const struct t_vpi_time*time);
|
||||
|
||||
struct timeformat_info_s {
|
||||
|
|
@ -50,4 +44,19 @@ struct timeformat_info_s {
|
|||
|
||||
extern struct timeformat_info_s timeformat_info;
|
||||
|
||||
extern unsigned is_constant_obj(vpiHandle obj);
|
||||
extern unsigned is_numeric_obj(vpiHandle obj);
|
||||
extern unsigned is_string_obj(vpiHandle obj);
|
||||
|
||||
extern vpiHandle sys_func_module(vpiHandle obj);
|
||||
|
||||
/*
|
||||
* The standard compiletf routines.
|
||||
*/
|
||||
extern PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_one_numeric_arg_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_one_opt_numeric_arg_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_two_numeric_args_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_one_string_arg_compiletf(PLI_BYTE8 *name);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ static PLI_INT32 sys_rand_two_args_compiletf(PLI_BYTE8 *name)
|
|||
break;
|
||||
default:
|
||||
vpi_printf("ERROR: %s's seed must be an integer/time"
|
||||
" varible or a register.\n", name);
|
||||
" variable or a register.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -449,7 +449,7 @@ static PLI_INT32 sys_rand_three_args_compiletf(PLI_BYTE8 *name)
|
|||
break;
|
||||
default:
|
||||
vpi_printf("ERROR: %s's seed must be an integer/time"
|
||||
" varible or a register.\n", name);
|
||||
" variable or a register.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -484,7 +484,7 @@ static PLI_INT32 sys_random_compiletf(PLI_BYTE8 *name)
|
|||
break;
|
||||
default:
|
||||
vpi_printf("ERROR: %s's seed must be an integer/time"
|
||||
" varible or a register.\n", name);
|
||||
" variable or a register.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ static PLI_INT32 sys_writemem_calltf(PLI_BYTE8*name)
|
|||
/* Open the data file. */
|
||||
file = fopen(path, "w");
|
||||
if (file == 0) {
|
||||
vpi_printf("%s: Unable to open %s for writeing.\n", name, path);
|
||||
vpi_printf("%s: Unable to open %s for writing.\n", name, path);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ static int scan_format(vpiHandle sys, struct byte_source*src, vpiHandle argv)
|
|||
if (isspace(*fmtp)) {
|
||||
/* White space matches a string of white space in
|
||||
the input. The number of spaces is not
|
||||
relevent, and the match may be 0 or more
|
||||
relevant, and the match may be 0 or more
|
||||
spaces. */
|
||||
while (*fmtp && isspace(*fmtp))
|
||||
fmtp += 1;
|
||||
|
|
|
|||
|
|
@ -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 vams_simparam_register();
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
#ifdef HAVE_LIBBZ2
|
||||
|
|
@ -180,5 +181,6 @@ void (*vlog_startup_routines[])() = {
|
|||
sys_lxt_or_vcd_register,
|
||||
sys_sdf_register,
|
||||
sys_special_register,
|
||||
vams_simparam_register,
|
||||
0
|
||||
};
|
||||
|
|
|
|||
139
vpi/sys_time.c
139
vpi/sys_time.c
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000 Stephen Williams (steve@icarus.com)
|
||||
* 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
|
||||
|
|
@ -16,36 +16,14 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_time.c,v 1.12 2007/03/14 04:05:51 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "vpi_config.h"
|
||||
#include "vpi_config.h"
|
||||
|
||||
# include "vpi_user.h"
|
||||
# include <string.h>
|
||||
# include <math.h>
|
||||
# include <assert.h>
|
||||
|
||||
static vpiHandle module_of_function(vpiHandle obj)
|
||||
{
|
||||
while (vpi_get(vpiType, obj) != vpiModule) {
|
||||
obj = vpi_handle(vpiScope, obj);
|
||||
assert(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static PLI_INT32 sys_time_sizetf(PLI_BYTE8*x)
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
static PLI_INT32 sys_stime_sizetf(PLI_BYTE8*x)
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
#include "vpi_user.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <sys_priv.h>
|
||||
|
||||
static PLI_INT32 sys_time_calltf(PLI_BYTE8*name)
|
||||
{
|
||||
|
|
@ -60,7 +38,7 @@ static PLI_INT32 sys_time_calltf(PLI_BYTE8*name)
|
|||
call_handle = vpi_handle(vpiSysTfCall, 0);
|
||||
assert(call_handle);
|
||||
|
||||
mod = module_of_function(call_handle);
|
||||
mod = sys_func_module(call_handle);
|
||||
|
||||
now.type = vpiSimTime;
|
||||
vpi_get_time(0, &now);
|
||||
|
|
@ -113,7 +91,7 @@ static PLI_INT32 sys_realtime_calltf(PLI_BYTE8*name)
|
|||
call_handle = vpi_handle(vpiSysTfCall, 0);
|
||||
assert(call_handle);
|
||||
|
||||
mod = module_of_function(call_handle);
|
||||
mod = sys_func_module(call_handle);
|
||||
|
||||
now.type = vpiSimTime;
|
||||
vpi_get_time(0, &now);
|
||||
|
|
@ -134,82 +112,39 @@ void sys_time_register()
|
|||
{
|
||||
s_vpi_systf_data tf_data;
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$time";
|
||||
tf_data.calltf = sys_time_calltf;
|
||||
tf_data.compiletf = 0;
|
||||
tf_data.sizetf = sys_time_sizetf;
|
||||
tf_data.user_data = "$time";
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$time";
|
||||
tf_data.sysfunctype = vpiTimeFunc;
|
||||
tf_data.calltf = sys_time_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$time";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$realtime";
|
||||
tf_data.calltf = sys_realtime_calltf;
|
||||
tf_data.compiletf = 0;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$realtime";
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$realtime";
|
||||
tf_data.sysfunctype = vpiRealFunc;
|
||||
tf_data.calltf = sys_realtime_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$realtime";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$stime";
|
||||
tf_data.calltf = sys_time_calltf;
|
||||
tf_data.compiletf = 0;
|
||||
tf_data.sizetf = sys_stime_sizetf;
|
||||
tf_data.user_data = "$stime";
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$stime";
|
||||
tf_data.sysfunctype = vpiIntFunc;
|
||||
tf_data.calltf = sys_time_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$stime";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$simtime";
|
||||
tf_data.calltf = sys_time_calltf;
|
||||
tf_data.compiletf = 0;
|
||||
tf_data.sizetf = sys_time_sizetf;
|
||||
tf_data.user_data = "$simtime";
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.tfname = "$simtime";
|
||||
tf_data.sysfunctype = vpiTimeFunc;
|
||||
tf_data.calltf = sys_time_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$simtime";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* $Log: sys_time.c,v $
|
||||
* Revision 1.12 2007/03/14 04:05:51 steve
|
||||
* VPI tasks take PLI_BYTE* by the standard.
|
||||
*
|
||||
* Revision 1.11 2006/10/30 22:45:37 steve
|
||||
* Updates for Cygwin portability (pr1585922)
|
||||
*
|
||||
* Revision 1.10 2004/01/21 01:22:53 steve
|
||||
* Give the vip directory its own configure and vpi_config.h
|
||||
*
|
||||
* Revision 1.9 2003/06/18 00:54:28 steve
|
||||
* Account for all 64 bits in results of $time.
|
||||
*
|
||||
* Revision 1.8 2003/02/07 02:44:25 steve
|
||||
* Properly round inter time values from $time.
|
||||
*
|
||||
* Revision 1.7 2003/01/28 04:41:55 steve
|
||||
* Use more precise pow function to scale time by units.
|
||||
*
|
||||
* Revision 1.6 2003/01/27 00:14:37 steve
|
||||
* Support in various contexts the $realtime
|
||||
* system task.
|
||||
*
|
||||
* Revision 1.5 2002/12/21 00:55:58 steve
|
||||
* The $time system task returns the integer time
|
||||
* scaled to the local units. Change the internal
|
||||
* implementation of vpiSystemTime the $time functions
|
||||
* to properly account for this. Also add $simtime
|
||||
* to get the simulation time.
|
||||
*
|
||||
* Revision 1.4 2002/08/12 01:35:05 steve
|
||||
* conditional ident string using autoconfig.
|
||||
*
|
||||
* Revision 1.3 2002/01/11 05:20:59 steve
|
||||
* Add the stime system function.
|
||||
*
|
||||
* Revision 1.2 2001/07/25 03:10:50 steve
|
||||
* Create a config.h.in file to hold all the config
|
||||
* junk, and support gcc 3.0. (Stephan Boettcher)
|
||||
*
|
||||
* Revision 1.1 2000/11/01 03:19:36 steve
|
||||
* Add the general $time system function.
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999-2003 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -16,9 +16,6 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_vcd.c,v 1.58 2007/03/14 04:05:51 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "sys_priv.h"
|
||||
|
||||
|
|
@ -269,9 +266,9 @@ inline static int install_dumpvars_callback(void)
|
|||
if (dumpvars_status == 1) return 0;
|
||||
|
||||
if (dumpvars_status == 2) {
|
||||
vpi_mcd_printf(1, "VCD warning: $dumpvars ignored, previously"
|
||||
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||
dumpvars_time);
|
||||
vpi_printf("VCD warning: $dumpvars ignored, previously"
|
||||
" called at simtime %" PLI_UINT64_FMT "\n",
|
||||
dumpvars_time);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -374,15 +371,16 @@ static PLI_INT32 sys_dumpall_calltf(PLI_BYTE8*name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void open_dumpfile(void)
|
||||
static void open_dumpfile(vpiHandle callh)
|
||||
{
|
||||
if (dump_path == 0) dump_path = strdup("dump.vcd");
|
||||
|
||||
dump_file = fopen(dump_path, "w");
|
||||
|
||||
if (dump_file == 0) {
|
||||
vpi_mcd_printf(1, "VCD Error: Unable to open %s for output.\n",
|
||||
dump_path);
|
||||
vpi_printf("VCD Error: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("Unable to open %s for output.\n", dump_path);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return;
|
||||
} else {
|
||||
|
|
@ -391,8 +389,7 @@ static void open_dumpfile(void)
|
|||
unsigned udx = 0;
|
||||
time_t walltime;
|
||||
|
||||
vpi_mcd_printf(1, "VCD info: dumpfile %s opened for output.\n",
|
||||
dump_path);
|
||||
vpi_printf("VCD info: dumpfile %s opened for output.\n", dump_path);
|
||||
|
||||
time(&walltime);
|
||||
|
||||
|
|
@ -428,9 +425,9 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
|||
|
||||
/* $dumpfile must be called before $dumpvars starts! */
|
||||
if (dumpvars_status != 0) {
|
||||
vpi_mcd_printf(1, "VCD warning: %s called after $dumpvars started"
|
||||
",\n using existing file (%s).\n",
|
||||
name, dump_path);
|
||||
vpi_printf("VCD warning: %s called after $dumpvars started,\n"
|
||||
" using existing file (%s).\n",
|
||||
name, dump_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -440,8 +437,8 @@ static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
|
|||
path = strdup(value.value.str);
|
||||
|
||||
if (dump_path) {
|
||||
vpi_mcd_printf(1, "VCD warning: Overriding dump file %s with"
|
||||
" %s\n", dump_path, path);
|
||||
vpi_printf("VCD warning: Overriding dump file %s with %s\n",
|
||||
dump_path, path);
|
||||
free(dump_path);
|
||||
}
|
||||
dump_path = path;
|
||||
|
|
@ -502,16 +499,9 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
|
||||
switch (vpi_get(vpiType, item)) {
|
||||
|
||||
case vpiMemory:
|
||||
/* don't know how to watch memories. */
|
||||
break;
|
||||
|
||||
case vpiNamedEvent:
|
||||
/* There is nothing in named events to dump. */
|
||||
break;
|
||||
|
||||
case vpiNet: type = "wire"; if(0){
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiTimeVar:
|
||||
case vpiReg: type = "reg"; }
|
||||
|
||||
|
|
@ -635,18 +625,16 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
vpi_get_str(vpiFullName, item);
|
||||
|
||||
#if 0
|
||||
vpi_mcd_printf(1,
|
||||
"VCD info:"
|
||||
" scanning scope %s, %u levels\n",
|
||||
fullname, depth);
|
||||
vpi_printf("VCD info: scanning scope %s, %u levels\n",
|
||||
fullname, depth);
|
||||
#endif
|
||||
nskip = 0 != vcd_names_search(&vcd_tab, fullname);
|
||||
|
||||
if (!nskip)
|
||||
vcd_names_add(&vcd_tab, fullname);
|
||||
else
|
||||
vpi_mcd_printf(1, "VCD warning: ignoring signals in"
|
||||
" previously scanned scope %s\n", fullname);
|
||||
vpi_printf("VCD warning: ignoring signals in "
|
||||
"previously scanned scope %s\n", fullname);
|
||||
|
||||
name = vpi_get_str(vpiName, item);
|
||||
|
||||
|
|
@ -665,12 +653,12 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
break;
|
||||
|
||||
default:
|
||||
vpi_mcd_printf(1, "VCD warning: $dumpvars: Unsupported parameter "
|
||||
"type (%d)\n", vpi_get(vpiType, item));
|
||||
vpi_printf("VCD warning: $dumpvars: Unsupported parameter "
|
||||
"type (%s)\n", vpi_get_str(vpiType, item));
|
||||
}
|
||||
}
|
||||
|
||||
static int draw_scope(vpiHandle item)
|
||||
static int draw_scope(vpiHandle item, vpiHandle callh)
|
||||
{
|
||||
int depth;
|
||||
const char *name;
|
||||
|
|
@ -679,7 +667,7 @@ static int draw_scope(vpiHandle item)
|
|||
vpiHandle scope = vpi_handle(vpiScope, item);
|
||||
if (!scope) return 0;
|
||||
|
||||
depth = 1 + draw_scope(scope);
|
||||
depth = 1 + draw_scope(scope, callh);
|
||||
name = vpi_get_str(vpiName, scope);
|
||||
|
||||
switch (vpi_get(vpiType, scope)) {
|
||||
|
|
@ -689,8 +677,9 @@ static int draw_scope(vpiHandle item)
|
|||
case vpiNamedFork: type = "fork"; break;
|
||||
case vpiModule: type = "module"; break;
|
||||
default:
|
||||
vpi_mcd_printf(1, "VCD Error: $dumpvars: Unsupported scope "
|
||||
"type (%d)\n", vpi_get(vpiType, item));
|
||||
vpi_printf("VCD Error: %s line %d: $dumpvars: Unsupported scope "
|
||||
"type (%d)\n", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh), vpi_get(vpiType, item));
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
|
@ -708,7 +697,7 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
|
|||
unsigned depth = 0;
|
||||
|
||||
if (dump_file == 0) {
|
||||
open_dumpfile();
|
||||
open_dumpfile(callh);
|
||||
if (dump_file == 0) return 0;
|
||||
}
|
||||
|
||||
|
|
@ -739,23 +728,23 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
|
|||
* been included. */
|
||||
switch (vpi_get(vpiType, item)) {
|
||||
case vpiIntegerVar:
|
||||
/* What about MemoryWord? */
|
||||
case vpiMemoryWord:
|
||||
case vpiNet:
|
||||
case vpiRealVar:
|
||||
case vpiReg:
|
||||
case vpiTimeVar:
|
||||
scname = vpi_get_str(vpiFullName, vpi_handle(vpiScope, item));
|
||||
if (vcd_names_search(&vcd_tab, scname)) {
|
||||
vpi_mcd_printf(1, "VCD warning: skipping signal %s, "
|
||||
"it was previously included.\n",
|
||||
vpi_get_str(vpiFullName, item));
|
||||
vpi_printf("VCD warning: skipping signal %s, "
|
||||
"it was previously included.\n",
|
||||
vpi_get_str(vpiFullName, item));
|
||||
continue;
|
||||
} else {
|
||||
add_var = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int dep = draw_scope(item);
|
||||
int dep = draw_scope(item, callh);
|
||||
|
||||
scan_item(depth, item, 0);
|
||||
|
||||
|
|
@ -782,7 +771,7 @@ void sys_vcd_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpall";
|
||||
tf_data.calltf = sys_dumpall_calltf;
|
||||
tf_data.compiletf = sys_dumpall_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpall";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -790,7 +779,7 @@ void sys_vcd_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpfile";
|
||||
tf_data.calltf = sys_dumpfile_calltf;
|
||||
tf_data.compiletf = sys_dumpfile_compiletf;
|
||||
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpfile";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -798,7 +787,7 @@ void sys_vcd_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpflush";
|
||||
tf_data.calltf = sys_dumpflush_calltf;
|
||||
tf_data.compiletf = sys_dumpflush_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpflush";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -806,7 +795,7 @@ void sys_vcd_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumplimit";
|
||||
tf_data.calltf = sys_dumplimit_calltf;
|
||||
tf_data.compiletf = sys_dumplimit_compiletf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumplimit";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -814,7 +803,7 @@ void sys_vcd_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpoff";
|
||||
tf_data.calltf = sys_dumpoff_calltf;
|
||||
tf_data.compiletf = sys_dumpoff_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpoff";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -822,7 +811,7 @@ void sys_vcd_register()
|
|||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpon";
|
||||
tf_data.calltf = sys_dumpon_calltf;
|
||||
tf_data.compiletf = sys_dumpon_compiletf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpon";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -835,4 +824,3 @@ void sys_vcd_register()
|
|||
tf_data.user_data = "$dumpvars";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2003-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
|
||||
|
|
@ -16,9 +16,6 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: sys_vcdoff.c,v 1.5 2007/03/14 04:05:52 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "sys_priv.h"
|
||||
|
||||
|
|
@ -40,7 +37,7 @@
|
|||
|
||||
static int dump_flag = 0;
|
||||
|
||||
static PLI_INT32 sys_dump_calltf(PLI_BYTE8*name)
|
||||
static PLI_INT32 sys_dummy_calltf(PLI_BYTE8*name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -48,7 +45,7 @@ static PLI_INT32 sys_dump_calltf(PLI_BYTE8*name)
|
|||
static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
|
||||
{
|
||||
if (dump_flag == 0) {
|
||||
vpi_mcd_printf(1, "VCD info: dumping is suppressed.\n");
|
||||
vpi_printf("VCD info: dumping is suppressed.\n");
|
||||
dump_flag = 1;
|
||||
}
|
||||
|
||||
|
|
@ -63,48 +60,48 @@ void sys_vcdoff_register()
|
|||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpall";
|
||||
tf_data.calltf = sys_dump_calltf;
|
||||
tf_data.compiletf = sys_dumpall_compiletf;
|
||||
tf_data.calltf = sys_dummy_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpall";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpfile";
|
||||
tf_data.calltf = sys_dump_calltf;
|
||||
tf_data.compiletf = sys_dumpfile_compiletf;
|
||||
tf_data.calltf = sys_dummy_calltf;
|
||||
tf_data.compiletf = sys_one_string_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpfile";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpflush";
|
||||
tf_data.calltf = sys_dump_calltf;
|
||||
tf_data.compiletf = sys_dumpflush_compiletf;
|
||||
tf_data.calltf = sys_dummy_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpflush";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumplimit";
|
||||
tf_data.calltf = sys_dump_calltf;
|
||||
tf_data.compiletf = sys_dumplimit_compiletf;
|
||||
tf_data.calltf = sys_dummy_calltf;
|
||||
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumplimit";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpoff";
|
||||
tf_data.calltf = sys_dump_calltf;
|
||||
tf_data.compiletf = sys_dumpoff_compiletf;
|
||||
tf_data.calltf = sys_dummy_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpoff";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysTask;
|
||||
tf_data.tfname = "$dumpon";
|
||||
tf_data.calltf = sys_dump_calltf;
|
||||
tf_data.compiletf = sys_dumpon_compiletf;
|
||||
tf_data.calltf = sys_dummy_calltf;
|
||||
tf_data.compiletf = sys_no_arg_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.user_data = "$dumpon";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
|
@ -117,4 +114,3 @@ void sys_vcdoff_register()
|
|||
tf_data.user_data = "$dumpvars";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,4 +14,6 @@ $dist_poisson vpiSysFuncInt
|
|||
$dist_chi_square vpiSysFuncInt
|
||||
$dist_t vpiSysFuncInt
|
||||
$dist_erlang vpiSysFuncInt
|
||||
$vvp_cpu_wordsize vpiSysFuncInt
|
||||
|
||||
$simparam vpiSysFuncReal
|
||||
$simparam$str vpiSysFuncSized 1024 unsigned
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ typedef struct s_single_data {
|
|||
static t_single_data va_single_data[]= {
|
||||
{"$sqrt", sqrt},
|
||||
{"$ln", log},
|
||||
{"$log", log10}, /* FIXME: The $log function is replaced by the
|
||||
{"$log", log10}, /* NOTE: The $log function is replaced by the
|
||||
$log10 function to eliminate confusion with
|
||||
the natural log. In C, "log" is ln and log10
|
||||
is log-base-10. The $log is being left in for
|
||||
|
|
@ -141,8 +141,8 @@ typedef struct {
|
|||
* Standard error message routine. The format string must take one
|
||||
* string argument (the name of the function).
|
||||
*/
|
||||
static void va_error_message(vpiHandle callh, const char* format,
|
||||
const char* name) {
|
||||
static void va_error_message(vpiHandle callh, const char *format,
|
||||
const char *name) {
|
||||
vpi_printf("%s:%d: error: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf(format, name);
|
||||
|
|
@ -153,8 +153,8 @@ static void va_error_message(vpiHandle callh, const char* format,
|
|||
/*
|
||||
* Process an argument.
|
||||
*/
|
||||
vpiHandle va_process_argument(vpiHandle callh, const char* name,
|
||||
vpiHandle arg, const char* post) {
|
||||
vpiHandle va_process_argument(vpiHandle callh, const char *name,
|
||||
vpiHandle arg, const char *post) {
|
||||
PLI_INT32 type;
|
||||
|
||||
if (arg == NULL) return 0;
|
||||
|
|
@ -178,7 +178,7 @@ vpiHandle va_process_argument(vpiHandle callh, const char* name,
|
|||
/*
|
||||
* Routine to check all the single argument math functions.
|
||||
*/
|
||||
static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8* ud)
|
||||
static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8 *ud)
|
||||
{
|
||||
assert(ud != 0);
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
|
|
@ -225,7 +225,7 @@ static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8* ud)
|
|||
/*
|
||||
* Routine to implement the single argument math functions.
|
||||
*/
|
||||
static PLI_INT32 va_single_argument_calltf(PLI_BYTE8* ud)
|
||||
static PLI_INT32 va_single_argument_calltf(PLI_BYTE8 *ud)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
s_vpi_value val;
|
||||
|
|
@ -249,7 +249,7 @@ static PLI_INT32 va_single_argument_calltf(PLI_BYTE8* ud)
|
|||
/*
|
||||
* Routine to check all the double argument math functions.
|
||||
*/
|
||||
static PLI_INT32 va_double_argument_compiletf(PLI_BYTE8* ud)
|
||||
static PLI_INT32 va_double_argument_compiletf(PLI_BYTE8 *ud)
|
||||
{
|
||||
assert(ud != 0);
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* 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>
|
||||
#ifdef HAVE_MALLOC_H
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <vpi_user.h>
|
||||
#include "sys_priv.h"
|
||||
|
||||
/* Once we have real string objects replace this with a dynamic string. */
|
||||
#define MAX_STRING_RESULT 1024
|
||||
|
||||
|
||||
/*
|
||||
* Check that the routines are called with the correct arguments.
|
||||
*/
|
||||
static PLI_INT32 simparam_compiletf(PLI_BYTE8 *name_ext)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
assert(callh != 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
|
||||
/* We must have at least one argument. */
|
||||
if (argv == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("$simparam%s requires a string argument.\n", name_ext);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The first argument must be a string. */
|
||||
arg = vpi_scan(argv);
|
||||
if (! is_string_obj(arg)) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("The first argument to $simparam%s must be a string.\n",
|
||||
name_ext);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* The second argument (default value) is optional. */
|
||||
arg = vpi_scan(argv);
|
||||
if (arg == 0) return 0;
|
||||
|
||||
/* For the string version the default must also be a string. */
|
||||
if (strcmp(name_ext, "$str") == 0) {
|
||||
if (! is_string_obj(arg)) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("When provided, the second argument to $simparam%s"
|
||||
"must be a string.\n", name_ext);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
/* For the rest the default must be numeric. */
|
||||
} else {
|
||||
if (! is_numeric_obj(arg)) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("When provided, the second argument to $simparam%s"
|
||||
"must be numeric.\n", name_ext);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* We can have a maximum of two arguments. */
|
||||
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 $simparam%s takes at most two arguments.\n",
|
||||
msg, name_ext);
|
||||
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 simparam_calltf(PLI_BYTE8 *name_ext)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
s_vpi_value val;
|
||||
char *param;
|
||||
unsigned have_def_val = 0;
|
||||
double retval, defval = 0.0;
|
||||
|
||||
/* Get the parameter we are looking for. */
|
||||
arg = vpi_scan(argv);
|
||||
val.format = vpiStringVal;
|
||||
vpi_get_value(arg, &val);
|
||||
param = strdup(val.value.str);
|
||||
|
||||
/* See if there is a default value. */
|
||||
arg = vpi_scan(argv);
|
||||
if (arg != 0) {
|
||||
vpi_free_object(argv);
|
||||
have_def_val = 1;
|
||||
val.format = vpiRealVal;
|
||||
vpi_get_value(arg, &val);
|
||||
defval = val.value.real;
|
||||
}
|
||||
|
||||
/* Now check the various things we can return. */
|
||||
if (strcmp(param, "gdev") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "gmin") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "imax") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "imelt") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "iteration") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "scale") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "shrink") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "simulatorSubversion") == 0) {
|
||||
retval = 0.0;
|
||||
} else if (strcmp(param, "simulatorVersion") == 0) {
|
||||
retval = 0.9;
|
||||
} else if (strcmp(param, "sourceScaleFactor") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "tnom") == 0) {
|
||||
retval = 0.0; /* Nothing for now. */
|
||||
} else if (strcmp(param, "timeUnit") == 0) {
|
||||
retval = pow(10, vpi_get(vpiTimeUnit, sys_func_module(callh)));
|
||||
} else if (strcmp(param, "timePrecision") == 0) {
|
||||
retval = pow(10, vpi_get(vpiTimePrecision, sys_func_module(callh)));
|
||||
} else if (strcmp(param, "CPUWordSize") == 0) {
|
||||
retval = 8.0*sizeof(long);
|
||||
} else {
|
||||
if (! have_def_val) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("$simparam%s unknown parameter name \"%s\".\n",
|
||||
name_ext, param);
|
||||
}
|
||||
retval = defval;
|
||||
}
|
||||
|
||||
free(param);
|
||||
|
||||
/* Return the value to the system. */
|
||||
val.format = vpiRealVal;
|
||||
val.value.real = retval;
|
||||
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 simparam_str_calltf(PLI_BYTE8 *name_ext)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
vpiHandle arg;
|
||||
s_vpi_value val;
|
||||
char *param;
|
||||
char *retval, *defval = NULL;
|
||||
|
||||
/* Get the parameter we are looking for. */
|
||||
arg = vpi_scan(argv);
|
||||
val.format = vpiStringVal;
|
||||
vpi_get_value(arg, &val);
|
||||
param = strdup(val.value.str);
|
||||
|
||||
/* See if there is a default value. */
|
||||
arg = vpi_scan(argv);
|
||||
if (arg != 0) {
|
||||
vpi_free_object(argv);
|
||||
val.format = vpiStringVal;
|
||||
vpi_get_value(arg, &val);
|
||||
defval = strdup(val.value.str);
|
||||
}
|
||||
|
||||
/* Now check the various things we can return. */
|
||||
/* For now we limit the result to 1024 characters. */
|
||||
if (strcmp(param, "analysis_name") == 0) {
|
||||
retval = strdup("N/A"); /* Nothing for now. */
|
||||
} else if (strcmp(param, "analysis_type") == 0) {
|
||||
retval = strdup("N/A"); /* Nothing for now. */
|
||||
} else if (strcmp(param, "cwd") == 0) {
|
||||
char path [MAX_STRING_RESULT];
|
||||
char *ptr = getcwd(path, MAX_STRING_RESULT);
|
||||
if (ptr == NULL) {
|
||||
ptr = strcpy(path, "<error getting the cwd, "
|
||||
"is it too long?>");
|
||||
}
|
||||
retval = strdup(path);
|
||||
} else if (strcmp(param, "module") == 0) {
|
||||
retval = strdup(vpi_get_str(vpiDefName, sys_func_module(callh)));
|
||||
} else if (strcmp(param, "instance") == 0) {
|
||||
retval = strdup(vpi_get_str(vpiFullName, sys_func_module(callh)));
|
||||
} else if (strcmp(param, "path") == 0) {
|
||||
retval = strdup(vpi_get_str(vpiFullName,
|
||||
vpi_handle(vpiScope,callh)));
|
||||
} else {
|
||||
if (defval == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("$simparam%s unknown parameter name \"%s\".\n",
|
||||
name_ext, param);
|
||||
defval = strdup("<error>");
|
||||
}
|
||||
retval = defval;
|
||||
}
|
||||
|
||||
free(param);
|
||||
|
||||
/* Return the value to the system. */
|
||||
val.format = vpiStringVal;
|
||||
val.value.str = retval;
|
||||
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
||||
free(retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PLI_INT32 simparam_str_sizetf(PLI_BYTE8 *name_ext)
|
||||
{
|
||||
(void) name_ext; /* Not used! */
|
||||
|
||||
return MAX_STRING_RESULT; // 128 characters max!
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the function with Verilog.
|
||||
*/
|
||||
void vams_simparam_register(void)
|
||||
{
|
||||
s_vpi_systf_data tf_data;
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.sysfunctype = vpiRealFunc;
|
||||
tf_data.calltf = simparam_calltf;
|
||||
tf_data.compiletf = simparam_compiletf;
|
||||
tf_data.sizetf = 0;
|
||||
tf_data.tfname = "$simparam";
|
||||
tf_data.user_data = "";
|
||||
vpi_register_systf(&tf_data);
|
||||
|
||||
tf_data.type = vpiSysFunc;
|
||||
tf_data.sysfunctype = vpiSizedFunc; /* What should this be? */
|
||||
tf_data.calltf = simparam_str_calltf;
|
||||
tf_data.compiletf = simparam_compiletf;
|
||||
tf_data.sizetf = simparam_str_sizetf; /* Only 128 characters! */
|
||||
tf_data.tfname = "$simparam$str";
|
||||
tf_data.user_data = "$str";
|
||||
vpi_register_systf(&tf_data);
|
||||
}
|
||||
227
vpi/vcd_priv.c
227
vpi/vcd_priv.c
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2003-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
|
||||
|
|
@ -16,21 +16,19 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: vcd_priv.c,v 1.6 2004/10/04 01:10:58 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "vpi_config.h"
|
||||
# include "vcd_priv.h"
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <assert.h>
|
||||
#include "vpi_config.h"
|
||||
#include "vcd_priv.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_MALLOC_H
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
# include <ctype.h>
|
||||
# include "stringheap.h"
|
||||
#include <ctype.h>
|
||||
#include "stringheap.h"
|
||||
#include <sys_priv.h>
|
||||
|
||||
int is_escaped_id(const char *name)
|
||||
{
|
||||
|
|
@ -133,7 +131,7 @@ void vcd_names_sort(struct vcd_names_list_s*tab)
|
|||
|
||||
The new signal will be listed as a $var, but no callback
|
||||
will be installed. This saves considerable CPU time and leads
|
||||
to smalle VCD files.
|
||||
to smaller VCD files.
|
||||
|
||||
The _vpiNexusId is a private (int) property of IVL simulators.
|
||||
*/
|
||||
|
|
@ -188,184 +186,12 @@ void set_nexus_ident(int nex, const char *id)
|
|||
vcd_ids[ihash(nex)] = bucket;
|
||||
}
|
||||
|
||||
/* This is used by the compiletf routines to check if an argument
|
||||
* is numeric. */
|
||||
static void check_numeric_arg(vpiHandle arg, char *msg, PLI_BYTE8 *name)
|
||||
{
|
||||
assert(arg);
|
||||
|
||||
switch (vpi_get(vpiType, arg)) {
|
||||
case vpiConstant:
|
||||
case vpiParameter:
|
||||
/* String constants are invalid numeric values. */
|
||||
if (vpi_get(vpiConstType, arg) == vpiStringConst) {
|
||||
vpi_mcd_printf(1, msg, name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
/* These have valid numeric values. */
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiNet:
|
||||
case vpiRealVar:
|
||||
case vpiReg:
|
||||
case vpiTimeVar:
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Anything else is not a numeric value. */
|
||||
vpi_mcd_printf(1, msg, name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is used by the compiletf routines to check if an argument
|
||||
* is a string value. */
|
||||
static void check_string_arg(vpiHandle arg, char *msg, PLI_BYTE8 *name)
|
||||
{
|
||||
assert(arg);
|
||||
PLI_INT32 ctype = 0;
|
||||
|
||||
switch (vpi_get(vpiType, arg)) {
|
||||
case vpiConstant:
|
||||
case vpiParameter:
|
||||
/* These must be a string or binary constant. */
|
||||
ctype = vpi_get(vpiConstType, arg);
|
||||
if (ctype != vpiStringConst && ctype != vpiBinaryConst) {
|
||||
vpi_mcd_printf(1, msg, name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
/* These have valid string values. */
|
||||
case vpiIntegerVar:
|
||||
case vpiMemoryWord:
|
||||
case vpiNet:
|
||||
case vpiReg:
|
||||
case vpiTimeVar:
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Anything else is not a string. */
|
||||
vpi_mcd_printf(1, msg, name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the compiletf routines are all the same they are located here,
|
||||
* so we only need a single copy.
|
||||
* so we only need a single copy. Some are generic enough they can use
|
||||
* the ones in sys_priv.c (no arg, one numeric argument, etc.).
|
||||
*/
|
||||
|
||||
/* $dumpall does not take an argument. */
|
||||
PLI_INT32 sys_dumpall_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
if (argv != 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* $dumpfile takes a single string argument. */
|
||||
PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
/* Check that there is an argument and that it is a string. */
|
||||
if (argv == 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s requires an argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
check_string_arg(vpi_scan(argv), "ERROR: %s's argument must be a"
|
||||
" string.\n", name);
|
||||
|
||||
/* Check that there is only a single argument. */
|
||||
if (vpi_scan(argv) != 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s takes a single argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* $dumpflush does not take an argument. */
|
||||
PLI_INT32 sys_dumpflush_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
if (argv != 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* $dumplimit takes a single numeric argument. */
|
||||
PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
/* Check that there is an argument and that it is numeric. */
|
||||
if (argv == 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s requires an argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
check_numeric_arg(vpi_scan(argv), "ERROR: %s's argument must be"
|
||||
" numeric.\n", name);
|
||||
|
||||
/* Check that there is only a single argument. */
|
||||
if (vpi_scan(argv) != 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s takes a single argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* $dumpoff does not take an argument. */
|
||||
PLI_INT32 sys_dumpoff_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
if (argv != 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* $dumpon does not take an argument. */
|
||||
PLI_INT32 sys_dumpon_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||
|
||||
if (argv != 0) {
|
||||
vpi_mcd_printf(1, "ERROR: %s does not take an argument.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* $dumpvars takes a variety of arguments. */
|
||||
PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name)
|
||||
{
|
||||
|
|
@ -374,16 +200,27 @@ PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name)
|
|||
vpiHandle arg;
|
||||
|
||||
/* No arguments is OK, dump everything. */
|
||||
if (argv == 0)
|
||||
return 0;
|
||||
if (argv == 0) return 0;
|
||||
|
||||
/* The first argument is the numeric level. */
|
||||
check_numeric_arg(vpi_scan(argv), "ERROR: %s's first argument must be"
|
||||
" numeric.\n", name);
|
||||
if (! is_numeric_obj(vpi_scan(argv))) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s's argument must be numeric.\n", name);
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
|
||||
/* The rest of the arguments are either a module or a variable. */
|
||||
while ((arg=vpi_scan(argv)) != NULL) {
|
||||
switch(vpi_get(vpiType, arg)) {
|
||||
case vpiMemoryWord:
|
||||
if (vpi_get(vpiConstantSelect, arg) == 0) {
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s cannot dump a non-constant select %s.\n", name,
|
||||
vpi_get_str(vpiType, arg));
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
/* The module types. */
|
||||
case vpiModule:
|
||||
case vpiTask:
|
||||
|
|
@ -393,18 +230,18 @@ PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name)
|
|||
/* The variable types. */
|
||||
case vpiNet:
|
||||
case vpiReg:
|
||||
case vpiMemoryWord:
|
||||
case vpiIntegerVar:
|
||||
case vpiTimeVar:
|
||||
case vpiRealVar:
|
||||
break;
|
||||
default:
|
||||
vpi_mcd_printf(1, "ERROR: %s cannot dump a %s.\n",
|
||||
name, vpi_get_str(vpiType, arg));
|
||||
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||
(int)vpi_get(vpiLineNo, callh));
|
||||
vpi_printf("%s cannot dump a %s.\n", name,
|
||||
vpi_get_str(vpiType, arg));
|
||||
vpi_control(vpiFinish, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __vcd_priv_H
|
||||
#define __vcd_priv_H
|
||||
/*
|
||||
* Copyright (c) 2003 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2003-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
|
||||
|
|
@ -18,9 +18,6 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: vcd_priv.h,v 1.2 2003/02/13 18:13:28 steve Exp $"
|
||||
#endif
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
|
|
@ -47,12 +44,6 @@ extern const char*find_nexus_ident(int nex);
|
|||
extern void set_nexus_ident(int nex, const char *id);
|
||||
|
||||
/* The compiletf routines are common for the VCD, LXT and LXT2 dumpers. */
|
||||
extern PLI_INT32 sys_dumpall_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_dumpflush_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_dumpoff_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_dumpon_compiletf(PLI_BYTE8 *name);
|
||||
extern PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ typedef struct t_vpi_value {
|
|||
(1) The number of delays to be retrieved
|
||||
( normally this is used in vpi_get_delays (..) )
|
||||
{
|
||||
(1.1) Setted by "no_of_delays" field
|
||||
(1.1) Set by "no_of_delays" field
|
||||
|
||||
(1.2) For the primitive_object, the no_of_delays
|
||||
shall be 2 or 3
|
||||
|
|
@ -279,6 +279,7 @@ typedef struct t_vpi_delay {
|
|||
#define vpiNamedFork 35
|
||||
#define vpiNet 36
|
||||
#define vpiParameter 41
|
||||
#define vpiPartSelect 42
|
||||
#define vpiPathTerm 43
|
||||
#define vpiRealVar 47
|
||||
#define vpiReg 48
|
||||
|
|
@ -297,6 +298,7 @@ typedef struct t_vpi_delay {
|
|||
#define vpiModPathIn 95
|
||||
#define vpiModPathOut 96
|
||||
#define vpiVariables 100
|
||||
#define vpiExpr 102
|
||||
|
||||
#define vpiCallback 1000
|
||||
|
||||
|
|
@ -346,8 +348,8 @@ typedef struct t_vpi_delay {
|
|||
# define vpiSysFuncReal vpiRealFunc
|
||||
# define vpiSysFuncTime vpiTimeFunc
|
||||
# define vpiSysFuncSized vpiSizedFunc
|
||||
#define vpiSigned 65
|
||||
#define vpiExpr 102
|
||||
#define vpiConstantSelect 53
|
||||
#define vpiSigned 65
|
||||
/* IVL private properties */
|
||||
#define _vpiNexusId 0x1000000
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ void vpip_run_value_changes(struct __vpiSignal*sig)
|
|||
/*
|
||||
* Handle read-only synch events. This causes the callback to be
|
||||
* scheduled for a moment at the end of the time period. This method
|
||||
* handles scheduling with itme delays.
|
||||
* handles scheduling with time delays.
|
||||
*/
|
||||
static void go_readonly_synch(struct __vpiCallback*rfp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ O = main.o parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \
|
|||
concat.o \
|
||||
dff.o extend.o npmos.o part.o reduce.o resolv.o sfunc.o stop.o symbols.o \
|
||||
ufunc.o codes.o \
|
||||
vthread.o schedule.o statistics.o tables.o udp.o vvp_net.o \
|
||||
vthread.o schedule.o statistics.o tables.o udp.o vvp_island.o vvp_net.o \
|
||||
event.o logic.o delay.o words.o $V
|
||||
|
||||
ifeq (@WIN32@,yes)
|
||||
|
|
|
|||
149
vvp/README.txt
149
vvp/README.txt
|
|
@ -345,6 +345,24 @@ The .alias statements do not create new nodes, but instead create net
|
|||
names that are aliases of an existing node. This handles special cases
|
||||
where a net has different names, possibly in different scopes.
|
||||
|
||||
CAST STATEMENTS:
|
||||
|
||||
Sometimes nets need to be cast from a real valued net to a bit based
|
||||
net or from a bit based net to a real valued net. These statements
|
||||
are used to performa that operation:
|
||||
|
||||
<label> .case/int <width>, <symbol>;
|
||||
<label> .case/real <symbol>;
|
||||
<label> .case/real.s <symbol>;
|
||||
|
||||
For .case/int the output <label> is a bit based net that is <width>
|
||||
bits wide. The input <symbol> is expected to put real values to
|
||||
this functor.
|
||||
|
||||
For .case/real the output <label> is a real valued net. The input
|
||||
<symbol> is expected to put bit based values and for .case/real.s
|
||||
the bits will be interpreted as a signed value.
|
||||
|
||||
DELAY STATEMENTS:
|
||||
|
||||
Delay nodes are structural net delay nodes that carry and manage
|
||||
|
|
@ -386,82 +404,6 @@ at <src>.
|
|||
|
||||
<label> .array "name", <src> ;
|
||||
|
||||
MEMORY STATEMENTS:
|
||||
|
||||
Memories are arrays of words, each word a vvp_vector4_t vector of the
|
||||
same width. The memory is canonically addressed as a 1-dimensional
|
||||
array of words, although indices are stored with the memory for
|
||||
calculating a canonical address from a multi-dimensional address.
|
||||
|
||||
Three types of memory statement perform (1) creation of a memory, (2)
|
||||
connecting a read port to an existing memory, and (3) initializing the
|
||||
memory's contents.
|
||||
|
||||
<label> .mem "name", <msb>,<lsb>, <last>,<first> ... ;
|
||||
|
||||
The pair of numbers <msb>,<lsb> defines the word width. The pair
|
||||
<last>,<first> defines the address range. Multiple address ranges are
|
||||
allowed for multidimensional indexing. This statement creates the
|
||||
memory array and makes it available to procedural code.
|
||||
|
||||
Procedural access to the memory references the memory as single array
|
||||
of words, with the base address==0, and the last address the size (in
|
||||
words) of the memory -1. It is up to the compiler to convert Verilog
|
||||
index sets to a canonical address. The multi-dimensional index set is
|
||||
available for VPI use.
|
||||
|
||||
Structural read access is implemented in terms of address and data
|
||||
ports. The addresses applied to the address port are expected to be
|
||||
in canonical form.
|
||||
|
||||
A read port is a functor that takes a single input, the read address,
|
||||
and outputs the word value at the given (canonical) address.
|
||||
|
||||
<label> .mem/port <memid>, <address> ;
|
||||
|
||||
<label> identifies the vector of output functors, to allow connections
|
||||
to the data output. <memid> is the label of the memory.
|
||||
|
||||
Any address input change, or any change in the addressed memory
|
||||
contents, is immediately propagated to the port output.
|
||||
|
||||
A write port is a superset of a read port. It is a 4-input functor
|
||||
that accepts the word address, an event input, a write enable input,
|
||||
and the data input.
|
||||
|
||||
<label> .mem/port <memid>, <address>, <event>, <we>, <data> ;
|
||||
|
||||
<event> is an event functor that triggers a write, if the <we> input
|
||||
is true. <data> is the input that connect to the data input
|
||||
port. For asynchronous transparent write operation, connect
|
||||
<event> to C4<z>, the RAM will transparently follow any changes on
|
||||
address and data lines, while <we> is true.
|
||||
|
||||
There is no Verilog construct that calls for a structural write port
|
||||
to a memory, but synthesis may ask for lpm_ram_d[pq] objects.
|
||||
|
||||
To initialize a memory, use:
|
||||
|
||||
.mem/init <memid> <start>, val , val ... ;
|
||||
|
||||
<memid> is the label of the memory, and the <start> is the start
|
||||
address (canonical) of the first word to be initialized. The start
|
||||
address allows multiple statements be used to initialize words of a
|
||||
memory.
|
||||
|
||||
The values are one per word.
|
||||
|
||||
Procedural access to the memory employs an index register to address a
|
||||
bit location in the memory, via the commands:
|
||||
|
||||
%load/m <bit>, <memid> ;
|
||||
%set/m <memid>, <bit> ;
|
||||
%assign/m <memid>, <delay>, <bit> ;
|
||||
|
||||
The memory bit is addressed by index register 3. The value of
|
||||
register 3 is the index in the memory's bit space, where each data
|
||||
word occupies a multiple of four bits.
|
||||
|
||||
|
||||
EVENT STATEMENTS
|
||||
|
||||
|
|
@ -830,6 +772,61 @@ by the fork atomically joins that scope. Once the transient thread
|
|||
joins the scope, it stays there until it ends. Threads never change
|
||||
scopes, not even transient threads.
|
||||
|
||||
VPI TASK/FUNCTION CALLS
|
||||
|
||||
Threads call vpi tasks with the %vpi_call or %vpi_func
|
||||
instructions. The formats are:
|
||||
|
||||
%vpi_call <file-index> <lineno> <name>, <args>... ;
|
||||
%vpi_func <file-index> <lineno> <name>, <args>... ;
|
||||
%vpi_func/r <file-index> <lineno> <name>, <args>... ;
|
||||
|
||||
The <file-index> is an index into the string table. The indexed string
|
||||
is the source code file name where this call appears. The <lineno> is
|
||||
the line number from the source code where this task/function appears.
|
||||
|
||||
The <name> is a string that is the name of the system
|
||||
task/function. For example, "$display", $strobe", etc. This name is
|
||||
looked up and compared with the registered system tasks/functions.
|
||||
|
||||
The <args>... is a comma (",") separated list of arguments. These are
|
||||
made available to the VPI code as vpi handles.
|
||||
|
||||
* The &A<> argument
|
||||
|
||||
The &A<> argument is a reference to the word of a variable array. The
|
||||
syntax is:
|
||||
|
||||
&A '<' <symbol> , <number> '>'
|
||||
&A '<' <symbol> , <base> <width> '>'
|
||||
|
||||
The <symbol> is the label for a variable array, and the <number> is
|
||||
the canonical word index as an unsigned integer. The second form
|
||||
retrives the index from thread space (<width> bits starting at <base>).
|
||||
|
||||
* The &PV<> argument
|
||||
|
||||
The &PV<> argument is a reference to part of a signal. The syntax is:
|
||||
|
||||
&PV '<' <symbol> , <base> , <width> '>'
|
||||
&PV '<' <symbol> , <tbase> <twid> , <width> '>'
|
||||
|
||||
The <symbol> is the label for a signal, the <base> is the canonical
|
||||
starting bit of the part select and <width> is the number of bits in
|
||||
the select. The second form retrieves the <base> from thread space
|
||||
using <twid> bits starting at <tbase>.
|
||||
|
||||
* The T<> argument
|
||||
|
||||
This is the catch-all for arguments that are not otherwise
|
||||
handled. This references the bits directly in the thread. The format
|
||||
is:
|
||||
|
||||
T '<' <base>, <wid>, <su> '>'
|
||||
|
||||
The <base> and <wid> are the base of a vector value in the thread and
|
||||
the width of the vector. The <su> is 's' or 'u' for signed or unsigned.
|
||||
|
||||
TRUTH TABLES
|
||||
|
||||
The logic that a functor represents is expressed as a truth table. The
|
||||
|
|
|
|||
32
vvp/arith.cc
32
vvp/arith.cc
|
|
@ -90,6 +90,36 @@ void vvp_arith_abs::recv_real(vvp_net_ptr_t ptr, double bit)
|
|||
vvp_send_real(ptr.ptr()->out, out);
|
||||
}
|
||||
|
||||
vvp_arith_cast_int::vvp_arith_cast_int(unsigned wid)
|
||||
: wid_(wid)
|
||||
{
|
||||
}
|
||||
|
||||
vvp_arith_cast_int::~vvp_arith_cast_int()
|
||||
{
|
||||
}
|
||||
|
||||
void vvp_arith_cast_int::recv_real(vvp_net_ptr_t ptr, double bit)
|
||||
{
|
||||
vvp_send_vec4(ptr.ptr()->out, vvp_vector4_t(wid_, bit));
|
||||
}
|
||||
|
||||
vvp_arith_cast_real::vvp_arith_cast_real(bool signed_flag)
|
||||
: signed_(signed_flag)
|
||||
{
|
||||
}
|
||||
|
||||
vvp_arith_cast_real::~vvp_arith_cast_real()
|
||||
{
|
||||
}
|
||||
|
||||
void vvp_arith_cast_real::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit)
|
||||
{
|
||||
double val;
|
||||
vector4_to_value(bit, val, signed_);
|
||||
vvp_send_real(ptr.ptr()->out, val);
|
||||
}
|
||||
|
||||
// Division
|
||||
|
||||
vvp_arith_div::vvp_arith_div(unsigned wid, bool signed_flag)
|
||||
|
|
@ -459,7 +489,7 @@ void vvp_arith_pow::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit)
|
|||
vector4_to_value(op_a_, ad, true);
|
||||
vector4_to_value(op_b_, bd, true);
|
||||
|
||||
res4 = double_to_vector4(pow(ad, bd), wid_);
|
||||
res4 = vvp_vector4_t(wid_, pow(ad, bd));
|
||||
} else {
|
||||
vvp_vector2_t a2 (op_a_);
|
||||
vvp_vector2_t b2 (op_b_);
|
||||
|
|
|
|||
22
vvp/arith.h
22
vvp/arith.h
|
|
@ -60,6 +60,28 @@ class vvp_arith_abs : public vvp_net_fun_t {
|
|||
private:
|
||||
};
|
||||
|
||||
class vvp_arith_cast_int : public vvp_net_fun_t {
|
||||
public:
|
||||
explicit vvp_arith_cast_int(unsigned wid);
|
||||
~vvp_arith_cast_int();
|
||||
|
||||
void recv_real(vvp_net_ptr_t ptr, double bit);
|
||||
|
||||
private:
|
||||
unsigned wid_;
|
||||
};
|
||||
|
||||
class vvp_arith_cast_real : public vvp_net_fun_t {
|
||||
public:
|
||||
explicit vvp_arith_cast_real(bool signed_flag);
|
||||
~vvp_arith_cast_real();
|
||||
|
||||
void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit);
|
||||
|
||||
private:
|
||||
bool signed_;
|
||||
};
|
||||
|
||||
class vvp_arith_div : public vvp_arith_ {
|
||||
|
||||
public:
|
||||
|
|
|
|||
338
vvp/array.cc
338
vvp/array.cc
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2007-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
|
||||
|
|
@ -30,7 +30,14 @@
|
|||
# include "compile.h"
|
||||
# include <assert.h>
|
||||
|
||||
static symbol_table_t array_table =0;
|
||||
unsigned long count_net_arrays = 0;
|
||||
unsigned long count_net_array_words = 0;
|
||||
unsigned long count_var_arrays = 0;
|
||||
unsigned long count_var_array_words = 0;
|
||||
unsigned long count_real_arrays = 0;
|
||||
unsigned long count_real_array_words = 0;
|
||||
|
||||
static symbol_map_s<struct __vpiArray>* array_table =0;
|
||||
|
||||
class vvp_fun_arrayport;
|
||||
static void array_attach_port(vvp_array_t, vvp_fun_arrayport*);
|
||||
|
|
@ -40,8 +47,8 @@ vvp_array_t array_find(const char*label)
|
|||
if (array_table == 0)
|
||||
return 0;
|
||||
|
||||
symbol_value_t v = sym_get_value(array_table, label);
|
||||
return (vvp_array_t)v.ptr;
|
||||
vvp_array_t v = array_table->sym_get_value(label);
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -49,17 +56,21 @@ vvp_array_t array_find(const char*label)
|
|||
* represent the words of the array. The vpi_array_t is a pointer to this.
|
||||
*/
|
||||
struct __vpiArray {
|
||||
__vpiArray() { }
|
||||
|
||||
struct __vpiHandle base;
|
||||
struct __vpiScope*scope;
|
||||
const char*name; /* Permanently allocated string */
|
||||
unsigned array_count;
|
||||
struct __vpiDecConst first_addr;
|
||||
struct __vpiDecConst last_addr;
|
||||
struct __vpiDecConst msb;
|
||||
struct __vpiDecConst lsb;
|
||||
unsigned vals_width;
|
||||
// If this is a net array, nets lists the handles.
|
||||
vpiHandle*nets;
|
||||
// If this is a var array, then these are used instead of nets.
|
||||
vvp_vector4_t *vals;
|
||||
unsigned vals_width;
|
||||
vvp_vector4array_t *vals;
|
||||
struct __vpiArrayWord*vals_words;
|
||||
|
||||
class vvp_fun_arrayport*ports_;
|
||||
|
|
@ -81,7 +92,35 @@ struct __vpiArrayIndex {
|
|||
struct __vpiArrayVthrA {
|
||||
struct __vpiHandle base;
|
||||
struct __vpiArray*array;
|
||||
// If this is set, then use it to get the index value.
|
||||
vpiHandle address_handle;
|
||||
// If wid==0, then address is the address into the array.
|
||||
unsigned address;
|
||||
// If wid >0, then the address is the base and wid the vector
|
||||
// width of the index to pull from the thread.
|
||||
unsigned wid;
|
||||
|
||||
unsigned get_address() const
|
||||
{
|
||||
if (address_handle) {
|
||||
s_vpi_value vp;
|
||||
vp.format = vpiIntVal;
|
||||
vpi_get_value(address_handle, &vp);
|
||||
return vp.value.integer;
|
||||
}
|
||||
|
||||
if (wid == 0)
|
||||
return address;
|
||||
|
||||
vvp_vector4_t tmp (wid);
|
||||
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
||||
vvp_bit4_t bit = vthread_get_bit(vpip_current_vthread, address+idx);
|
||||
tmp.set_bit(idx, bit);
|
||||
}
|
||||
unsigned long val;
|
||||
vector4_to_value(tmp, val);
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -98,9 +137,16 @@ struct __vpiArrayVthrA {
|
|||
* pointer.
|
||||
*
|
||||
* To then get to the parent, use word0[-1].parent.
|
||||
*
|
||||
* The vpiArrayWord is also used as a handle for the index (vpiIndex)
|
||||
* for the word. To make that work, return the pointer to the as_index
|
||||
* member instead of the as_word member. The result is a different set
|
||||
* of vpi functions is bound to the same structure. All the details
|
||||
* for the word also apply when treating this as an index.
|
||||
*/
|
||||
struct __vpiArrayWord {
|
||||
struct __vpiHandle base;
|
||||
struct __vpiHandle as_word;
|
||||
struct __vpiHandle as_index;
|
||||
union {
|
||||
struct __vpiArray*parent;
|
||||
struct __vpiArrayWord*word0;
|
||||
|
|
@ -121,12 +167,18 @@ static vpiHandle array_index_scan(vpiHandle ref, int);
|
|||
static int array_index_free_object(vpiHandle ref);
|
||||
|
||||
static int vpi_array_var_word_get(int code, vpiHandle);
|
||||
static char*vpi_array_var_word_get_str(int code, vpiHandle);
|
||||
static void vpi_array_var_word_get_value(vpiHandle, p_vpi_value);
|
||||
static vpiHandle vpi_array_var_word_put_value(vpiHandle, p_vpi_value, int);
|
||||
static vpiHandle vpi_array_var_word_get_handle(int code, vpiHandle ref);
|
||||
|
||||
static void vpi_array_var_index_get_value(vpiHandle, p_vpi_value);
|
||||
|
||||
static int vpi_array_vthr_A_get(int code, vpiHandle);
|
||||
static char*vpi_array_vthr_A_get_str(int code, vpiHandle);
|
||||
static void vpi_array_vthr_A_get_value(vpiHandle, p_vpi_value);
|
||||
static vpiHandle vpi_array_vthr_A_put_value(vpiHandle, p_vpi_value, int);
|
||||
static vpiHandle vpi_array_vthr_A_get_handle(int code, vpiHandle ref);
|
||||
|
||||
static const struct __vpirt vpip_arraymem_rt = {
|
||||
vpiMemory,
|
||||
|
|
@ -169,9 +221,21 @@ static const struct __vpirt vpip_array_index_rt = {
|
|||
static const struct __vpirt vpip_array_var_word_rt = {
|
||||
vpiMemoryWord,
|
||||
&vpi_array_var_word_get,
|
||||
0,
|
||||
&vpi_array_var_word_get_str,
|
||||
&vpi_array_var_word_get_value,
|
||||
&vpi_array_var_word_put_value,
|
||||
&vpi_array_var_word_get_handle,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct __vpirt vpip_array_var_index_rt = {
|
||||
vpiIndex,
|
||||
0,
|
||||
0,
|
||||
&vpi_array_var_index_get_value,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
|
@ -181,10 +245,10 @@ static const struct __vpirt vpip_array_var_word_rt = {
|
|||
static const struct __vpirt vpip_array_vthr_A_rt = {
|
||||
vpiMemoryWord,
|
||||
&vpi_array_vthr_A_get,
|
||||
0,
|
||||
&vpi_array_vthr_A_get_str,
|
||||
&vpi_array_vthr_A_get_value,
|
||||
&vpi_array_vthr_A_put_value,
|
||||
0,
|
||||
&vpi_array_vthr_A_get_handle,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
|
|
@ -203,6 +267,16 @@ static struct __vpiArrayWord* array_var_word_from_handle(vpiHandle ref)
|
|||
return (struct __vpiArrayWord*) ref;
|
||||
}
|
||||
|
||||
static struct __vpiArrayWord* array_var_index_from_handle(vpiHandle ref)
|
||||
{
|
||||
if (ref == 0)
|
||||
return 0;
|
||||
if (ref->vpi_type != &vpip_array_var_index_rt)
|
||||
return 0;
|
||||
|
||||
return (struct __vpiArrayWord*) (ref-1);
|
||||
}
|
||||
|
||||
static struct __vpiArrayVthrA* array_vthr_a_from_handle(vpiHandle ref)
|
||||
{
|
||||
if (ref == 0)
|
||||
|
|
@ -225,7 +299,8 @@ static void array_make_vals_words(struct __vpiArray*parent)
|
|||
|
||||
struct __vpiArrayWord*words = parent->vals_words;
|
||||
for (unsigned idx = 0 ; idx < parent->array_count ; idx += 1) {
|
||||
words[idx].base.vpi_type = &vpip_array_var_word_rt;
|
||||
words[idx].as_word.vpi_type = &vpip_array_var_word_rt;
|
||||
words[idx].as_index.vpi_type = &vpip_array_var_index_rt;
|
||||
words[idx].word0 = words;
|
||||
}
|
||||
}
|
||||
|
|
@ -325,7 +400,7 @@ static vpiHandle vpi_array_index(vpiHandle ref, int index)
|
|||
if (obj->vals_words == 0)
|
||||
array_make_vals_words(obj);
|
||||
|
||||
return &(obj->vals_words[index].base);
|
||||
return &(obj->vals_words[index].as_word);
|
||||
}
|
||||
|
||||
static int vpi_array_var_word_get(int code, vpiHandle ref)
|
||||
|
|
@ -338,13 +413,36 @@ static int vpi_array_var_word_get(int code, vpiHandle ref)
|
|||
|
||||
switch (code) {
|
||||
case vpiSize:
|
||||
return (int) parent->vals_width;
|
||||
return (int) parent->vals->width();
|
||||
|
||||
case vpiLeftRange:
|
||||
return parent->msb.value;
|
||||
|
||||
case vpiRightRange:
|
||||
return parent->lsb.value;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static char*vpi_array_var_word_get_str(int code, vpiHandle ref)
|
||||
{
|
||||
struct __vpiArrayWord*obj = array_var_word_from_handle(ref);
|
||||
struct __vpiArray*parent;
|
||||
|
||||
assert(obj);
|
||||
unsigned index = decode_array_word_pointer(obj, parent);
|
||||
|
||||
if (code == vpiFile) { // Not implemented for now!
|
||||
return simple_set_rbuf_str(file_names[0]);
|
||||
}
|
||||
|
||||
char sidx [64];
|
||||
snprintf(sidx, 63, "%d", (int)index + parent->first_addr.value);
|
||||
return generic_get_str(code, &parent->scope->base, parent->name, sidx);
|
||||
}
|
||||
|
||||
static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value)
|
||||
{
|
||||
struct __vpiArrayWord*obj = array_var_word_from_handle(ref);
|
||||
|
|
@ -352,9 +450,9 @@ static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value)
|
|||
|
||||
assert(obj);
|
||||
unsigned index = decode_array_word_pointer(obj, parent);
|
||||
unsigned width = parent->vals->width();
|
||||
|
||||
vpip_vec4_get_value(parent->vals[index], parent->vals_width,
|
||||
false, value);
|
||||
vpip_vec4_get_value(parent->vals->get_word(index), width, false, value);
|
||||
}
|
||||
|
||||
static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int flags)
|
||||
|
|
@ -370,6 +468,47 @@ static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int
|
|||
return ref;
|
||||
}
|
||||
|
||||
static vpiHandle vpi_array_var_word_get_handle(int code, vpiHandle ref)
|
||||
{
|
||||
struct __vpiArrayWord*obj = array_var_word_from_handle(ref);
|
||||
struct __vpiArray*parent;
|
||||
|
||||
assert(obj);
|
||||
decode_array_word_pointer(obj, parent);
|
||||
|
||||
switch (code) {
|
||||
|
||||
case vpiIndex:
|
||||
return &(obj->as_index);
|
||||
|
||||
case vpiLeftRange:
|
||||
return &parent->msb.base;
|
||||
|
||||
case vpiRightRange:
|
||||
return &parent->lsb.base;
|
||||
|
||||
case vpiParent:
|
||||
return &parent->base;
|
||||
|
||||
case vpiScope:
|
||||
return &parent->scope->base;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpi_array_var_index_get_value(vpiHandle ref, p_vpi_value value)
|
||||
{
|
||||
struct __vpiArrayWord*obj = array_var_index_from_handle(ref);
|
||||
struct __vpiArray*parent;
|
||||
|
||||
assert(obj);
|
||||
unsigned index = decode_array_word_pointer(obj, parent);
|
||||
|
||||
assert(value->format == vpiIntVal);
|
||||
value->value.integer = index;
|
||||
}
|
||||
|
||||
# define ARRAY_ITERATOR(ref) (assert(ref->vpi_type->type_code==vpiIterator), \
|
||||
(struct __vpiArrayIterator*)ref)
|
||||
|
||||
|
|
@ -393,7 +532,7 @@ static vpiHandle array_iterator_scan(vpiHandle ref, int)
|
|||
if (obj->array->vals_words == 0)
|
||||
array_make_vals_words(obj->array);
|
||||
|
||||
return &(obj->array->vals_words[use_index].base);
|
||||
return &(obj->array->vals_words[use_index].as_word);
|
||||
}
|
||||
|
||||
static int array_iterator_free_object(vpiHandle ref)
|
||||
|
|
@ -453,13 +592,39 @@ static int vpi_array_vthr_A_get(int code, vpiHandle ref)
|
|||
return 0; // Not implemented for now!
|
||||
|
||||
case vpiSize:
|
||||
assert(parent->vals);
|
||||
return parent->vals_width;
|
||||
|
||||
case vpiLeftRange:
|
||||
return parent->msb.value;
|
||||
|
||||
case vpiRightRange:
|
||||
return parent->lsb.value;
|
||||
|
||||
// For now &A<> is only a constant select. This will need
|
||||
// to be changed when it supports variable selection.
|
||||
case vpiConstantSelect:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static char*vpi_array_vthr_A_get_str(int code, vpiHandle ref)
|
||||
{
|
||||
struct __vpiArrayVthrA*obj = array_vthr_a_from_handle(ref);
|
||||
assert(obj);
|
||||
struct __vpiArray*parent = obj->array;
|
||||
|
||||
if (code == vpiFile) { // Not implemented for now!
|
||||
return simple_set_rbuf_str(file_names[0]);
|
||||
}
|
||||
|
||||
char sidx [64];
|
||||
snprintf(sidx, 63, "%d", (int)obj->get_address() + parent->first_addr.value);
|
||||
return generic_get_str(code, &parent->scope->base, parent->name, sidx);
|
||||
}
|
||||
|
||||
static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value)
|
||||
{
|
||||
struct __vpiArrayVthrA*obj = array_vthr_a_from_handle(ref);
|
||||
|
|
@ -467,11 +632,10 @@ static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value)
|
|||
struct __vpiArray*parent = obj->array;
|
||||
|
||||
assert(parent);
|
||||
assert(parent->vals);
|
||||
assert(obj->address < parent->array_count);
|
||||
|
||||
vpip_vec4_get_value(parent->vals[obj->address],
|
||||
parent->vals_width, false, 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);
|
||||
}
|
||||
|
||||
static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int)
|
||||
|
|
@ -480,10 +644,10 @@ static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int)
|
|||
assert(obj);
|
||||
struct __vpiArray*parent = obj->array;
|
||||
|
||||
unsigned index = obj->address;
|
||||
unsigned index = obj->get_address();
|
||||
|
||||
assert(parent);
|
||||
assert(obj->address < parent->array_count);
|
||||
assert(index < parent->array_count);
|
||||
|
||||
vvp_vector4_t val = vec4_from_vpi_value(vp, parent->vals_width);
|
||||
array_set_word(parent, index, 0, val);
|
||||
|
|
@ -491,6 +655,32 @@ static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int)
|
|||
return ref;
|
||||
}
|
||||
|
||||
static vpiHandle vpi_array_vthr_A_get_handle(int code, vpiHandle ref)
|
||||
{
|
||||
struct __vpiArrayVthrA*obj = array_vthr_a_from_handle(ref);
|
||||
assert(obj);
|
||||
struct __vpiArray*parent = obj->array;
|
||||
|
||||
switch (code) {
|
||||
|
||||
case vpiIndex:
|
||||
break; // Not implemented!
|
||||
|
||||
case vpiLeftRange:
|
||||
return &parent->msb.base;
|
||||
|
||||
case vpiRightRange:
|
||||
return &parent->lsb.base;
|
||||
|
||||
case vpiParent:
|
||||
return &parent->base;
|
||||
|
||||
case vpiScope:
|
||||
return &parent->scope->base;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void array_set_word(vvp_array_t arr,
|
||||
unsigned address,
|
||||
|
|
@ -503,18 +693,18 @@ void array_set_word(vvp_array_t arr,
|
|||
if (arr->vals) {
|
||||
assert(arr->nets == 0);
|
||||
if (part_off != 0 || val.size() != arr->vals_width) {
|
||||
if (arr->vals[address].size() == 0)
|
||||
arr->vals[address] = vvp_vector4_t(arr->vals_width, BIT4_X);
|
||||
if ((part_off + val.size()) > arr->vals[address].size()) {
|
||||
vvp_vector4_t tmp = arr->vals->get_word(address);
|
||||
if ((part_off + val.size()) > tmp.size()) {
|
||||
cerr << "part_off=" << part_off
|
||||
<< " val.size()=" << val.size()
|
||||
<< " arr->vals[address].size()=" << arr->vals[address].size()
|
||||
<< " arr->vals[address].size()=" << tmp.size()
|
||||
<< " arr->vals_width=" << arr->vals_width << endl;
|
||||
assert(0);
|
||||
}
|
||||
arr->vals[address].set_vec(part_off, val);
|
||||
tmp.set_vec(part_off, val);
|
||||
arr->vals->set_word(address, tmp);
|
||||
} else {
|
||||
arr->vals[address] = val;
|
||||
arr->vals->set_word(address, val);
|
||||
}
|
||||
array_word_change(arr, address);
|
||||
return;
|
||||
|
|
@ -537,14 +727,7 @@ vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address)
|
|||
if (arr->vals) {
|
||||
assert(arr->nets == 0);
|
||||
|
||||
vvp_vector4_t tmp;
|
||||
if (address < arr->array_count)
|
||||
tmp = arr->vals[address];
|
||||
|
||||
if (tmp.size() == 0)
|
||||
tmp = vvp_vector4_t(arr->vals_width, BIT4_X);
|
||||
|
||||
return tmp;
|
||||
return arr->vals->get_word(address);
|
||||
}
|
||||
|
||||
assert(arr->vals == 0);
|
||||
|
|
@ -596,6 +779,8 @@ static vpiHandle vpip_make_array(char*label, const char*name,
|
|||
obj->nets = 0;
|
||||
obj->vals = 0;
|
||||
obj->vals_width = 0;
|
||||
vpip_make_dec_const(&obj->msb, 0);
|
||||
vpip_make_dec_const(&obj->lsb, 0);
|
||||
obj->vals_words = 0;
|
||||
|
||||
// Initialize (clear) the read-ports list.
|
||||
|
|
@ -604,12 +789,10 @@ static vpiHandle vpip_make_array(char*label, const char*name,
|
|||
|
||||
/* Add this symbol to the array_symbols table for later lookup. */
|
||||
if (!array_table)
|
||||
array_table = new_symbol_table();
|
||||
array_table = new symbol_map_s<struct __vpiArray>;
|
||||
|
||||
assert(!array_find(label));
|
||||
symbol_value_t v;
|
||||
v.ptr = obj;
|
||||
sym_set_value(array_table, label, v);
|
||||
array_table->sym_set_value(label, obj);
|
||||
|
||||
/* Add this into the table of VPI objects. This is used for
|
||||
contexts that try to look up VPI objects in
|
||||
|
|
@ -641,7 +824,8 @@ void array_attach_word(vvp_array_t array, unsigned long addr, vpiHandle word)
|
|||
vvp_fun_signal_base*fun = dynamic_cast<vvp_fun_signal_base*>(net->fun);
|
||||
assert(fun);
|
||||
fun->attach_as_word(array, addr);
|
||||
sig->parent = &array->base;
|
||||
sig->is_netarray = 1;
|
||||
sig->within.parent = &array->base;
|
||||
sig->id.index = vpip_make_dec_const(addr + array->first_addr.value);
|
||||
}
|
||||
}
|
||||
|
|
@ -654,8 +838,13 @@ void compile_var_array(char*label, char*name, int last, int first,
|
|||
struct __vpiArray*arr = ARRAY_HANDLE(obj);
|
||||
|
||||
/* Make the words. */
|
||||
arr->vals = new vvp_vector4_t[arr->array_count];
|
||||
arr->vals_width = labs(msb-lsb) + 1;
|
||||
arr->vals = new vvp_vector4array_t(arr->vals_width, arr->array_count);
|
||||
vpip_make_dec_const(&arr->msb, msb);
|
||||
vpip_make_dec_const(&arr->lsb, lsb);
|
||||
|
||||
count_var_arrays += 1;
|
||||
count_var_array_words += arr->array_count;
|
||||
|
||||
free(label);
|
||||
free(name);
|
||||
|
|
@ -676,6 +865,9 @@ void compile_real_array(char*label, char*name, int last, int first,
|
|||
compile_varw_real(strdup(buf), array, idx, msb, lsb);
|
||||
}
|
||||
|
||||
count_real_arrays += 1;
|
||||
count_real_array_words += arr->array_count;
|
||||
|
||||
free(label);
|
||||
free(name);
|
||||
}
|
||||
|
|
@ -687,6 +879,9 @@ void compile_net_array(char*label, char*name, int last, int first)
|
|||
struct __vpiArray*arr = ARRAY_HANDLE(obj);
|
||||
arr->nets = (vpiHandle*)calloc(arr->array_count, sizeof(vpiHandle));
|
||||
|
||||
count_net_arrays += 1;
|
||||
count_net_array_words += arr->array_count;
|
||||
|
||||
free(label);
|
||||
free(name);
|
||||
}
|
||||
|
|
@ -768,7 +963,7 @@ void array_word_change(vvp_array_t array, unsigned long addr)
|
|||
for (vvp_fun_arrayport*cur = array->ports_; cur; cur = cur->next_)
|
||||
cur->check_word_change(addr);
|
||||
|
||||
// Run callbacks attatched to the array itself.
|
||||
// Run callbacks attached to the array itself.
|
||||
struct __vpiCallback *next = array->vpi_callbacks;
|
||||
struct __vpiCallback *prev = 0;
|
||||
|
||||
|
|
@ -784,7 +979,8 @@ void array_word_change(vvp_array_t array, unsigned long addr)
|
|||
|
||||
if (cur->cb_data.cb_rtn != 0) {
|
||||
if (cur->cb_data.value)
|
||||
vpip_vec4_get_value(array->vals[addr], array->vals_width,
|
||||
vpip_vec4_get_value(array->vals->get_word(addr),
|
||||
array->vals_width,
|
||||
false, cur->cb_data.value);
|
||||
|
||||
callback_execute(cur);
|
||||
|
|
@ -909,9 +1105,7 @@ void compile_array_alias(char*label, char*name, char*src)
|
|||
|
||||
assert(array_table);
|
||||
assert(!array_find(label));
|
||||
symbol_value_t v;
|
||||
v.ptr = obj;
|
||||
sym_set_value(array_table, label, v);
|
||||
array_table->sym_set_value(label, obj);
|
||||
|
||||
compile_vpi_symbol(label, &obj->base);
|
||||
vpip_attach_to_current_scope(&obj->base);
|
||||
|
|
@ -930,9 +1124,57 @@ vpiHandle vpip_make_vthr_A(char*label, unsigned addr)
|
|||
|
||||
obj->array = array_find(label);
|
||||
assert(obj->array);
|
||||
free(label);
|
||||
|
||||
obj->address_handle = 0;
|
||||
obj->address = addr;
|
||||
obj->wid = 0;
|
||||
assert(addr < obj->array->array_count);
|
||||
|
||||
return &(obj->base);
|
||||
}
|
||||
|
||||
vpiHandle vpip_make_vthr_A(char*label, unsigned tbase, unsigned twid)
|
||||
{
|
||||
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 = 0;
|
||||
obj->address = tbase;
|
||||
obj->wid = twid;
|
||||
|
||||
return &(obj->base);
|
||||
}
|
||||
|
||||
vpiHandle vpip_make_vthr_A(char*label, char*symbol)
|
||||
{
|
||||
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 = 0;
|
||||
compile_vpi_lookup(&obj->address_handle, symbol);
|
||||
obj->address = 0;
|
||||
obj->wid = 0;
|
||||
|
||||
return &(obj->base);
|
||||
}
|
||||
|
||||
void compile_array_cleanup(void)
|
||||
{
|
||||
if (array_table) {
|
||||
delete array_table;
|
||||
array_table = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ extern void array_set_word(vvp_array_t arr,
|
|||
unsigned off,
|
||||
vvp_vector4_t val);
|
||||
|
||||
extern vvp_vector4_t array_get_word(vvp_array_t array, unsigned adddress);
|
||||
extern vvp_vector4_t array_get_word(vvp_array_t array, unsigned address);
|
||||
|
||||
/* VPI hooks */
|
||||
|
||||
|
|
|
|||
|
|
@ -97,13 +97,13 @@ extern bool of_JMP1(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_JOIN(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AV(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AVP0(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AVP0_S(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_AVX_P(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_NX(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_VEC(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_VP0(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_VP0_S(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_WR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_X(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_XP(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOAD_X1P(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_LOADI_WR(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MOD(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_MOD_S(vthread_t thr, vvp_code_t code);
|
||||
|
|
|
|||
|
|
@ -140,13 +140,13 @@ const static struct opcode_table_s opcode_table[] = {
|
|||
{ "%join", of_JOIN, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%load/av",of_LOAD_AV,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
{ "%load/avp0",of_LOAD_AVP0,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
{ "%load/avp0/s",of_LOAD_AVP0_S,3,{OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
{ "%load/avx.p",of_LOAD_AVX_P,3,{OA_BIT1, OA_ARR_PTR, OA_BIT2} },
|
||||
{ "%load/nx",of_LOAD_NX,3, {OA_BIT1, OA_VPI_PTR, OA_BIT2} },
|
||||
{ "%load/v", of_LOAD_VEC,3, {OA_BIT1, OA_FUNC_PTR, OA_BIT2} },
|
||||
{ "%load/vp0",of_LOAD_VP0,3,{OA_BIT1, OA_FUNC_PTR, OA_BIT2} },
|
||||
{ "%load/vp0/s",of_LOAD_VP0_S,3,{OA_BIT1, OA_FUNC_PTR, OA_BIT2} },
|
||||
{ "%load/wr",of_LOAD_WR,2, {OA_BIT1, OA_VPI_PTR, OA_BIT2} },
|
||||
{ "%load/x", of_LOAD_X, 3, {OA_BIT1, OA_FUNC_PTR, OA_BIT2} },
|
||||
{ "%load/x.p",of_LOAD_XP, 3,{OA_BIT1, OA_FUNC_PTR, OA_BIT2} },
|
||||
{ "%load/x1p",of_LOAD_X1P,3,{OA_BIT1, OA_FUNC_PTR, OA_BIT2} },
|
||||
{ "%loadi/wr",of_LOADI_WR,3,{OA_BIT1, OA_NUMBER, OA_BIT2} },
|
||||
{ "%mod", of_MOD, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
||||
{ "%mod/s", of_MOD_S, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} },
|
||||
|
|
@ -645,6 +645,8 @@ void compile_cleanup(void)
|
|||
delete_symbol_table(sym_functors);
|
||||
sym_functors = 0;
|
||||
|
||||
compile_island_cleanup();
|
||||
|
||||
if (verbose_flag) {
|
||||
fprintf(stderr, " ... Compiletf functions\n");
|
||||
fflush(stderr);
|
||||
|
|
@ -887,6 +889,38 @@ template <class T_> void make_arith(T_ *arith, char*label,
|
|||
free(argv);
|
||||
}
|
||||
|
||||
void compile_arith_cast_int(char*label, long width,
|
||||
unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
vvp_arith_cast_int*arith = new vvp_arith_cast_int((unsigned) width);
|
||||
|
||||
vvp_net_t* ptr = new vvp_net_t;
|
||||
ptr->fun = arith;
|
||||
|
||||
define_functor_symbol(label, ptr);
|
||||
free(label);
|
||||
|
||||
assert(argc == 1);
|
||||
inputs_connect(ptr, argc, argv);
|
||||
free(argv);
|
||||
}
|
||||
|
||||
void compile_arith_cast_real(char*label, bool signed_flag,
|
||||
unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
vvp_arith_cast_real*arith = new vvp_arith_cast_real(signed_flag);
|
||||
|
||||
vvp_net_t* ptr = new vvp_net_t;
|
||||
ptr->fun = arith;
|
||||
|
||||
define_functor_symbol(label, ptr);
|
||||
free(label);
|
||||
|
||||
assert(argc == 1);
|
||||
inputs_connect(ptr, argc, argv);
|
||||
free(argv);
|
||||
}
|
||||
|
||||
void compile_arith_abs(char*label, unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
vvp_arith_abs*arith = new vvp_arith_abs;
|
||||
|
|
@ -992,11 +1026,6 @@ void compile_arith_pow(char*label, long wid, bool signed_flag,
|
|||
unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
assert( wid > 0 );
|
||||
/* For now we need to do a double to long cast, so the number
|
||||
of bits is limited. This should be caught in the compiler. */
|
||||
if (signed_flag) {
|
||||
assert( wid <= (long)(8*sizeof(long)) );
|
||||
}
|
||||
|
||||
if (argc != 2) {
|
||||
const char *suffix = "";
|
||||
|
|
@ -1397,16 +1426,16 @@ void compile_resolver(char*label, char*type, unsigned argc, struct symb_s*argv)
|
|||
vvp_net_fun_t* obj = 0;
|
||||
|
||||
if (strcmp(type,"tri") == 0) {
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_Z, 0));
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_Z, 0,0));
|
||||
|
||||
} else if (strncmp(type,"tri$",4) == 0) {
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_Z, 0), strdup(type+4));
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_Z, 0,0), strdup(type+4));
|
||||
|
||||
} else if (strcmp(type,"tri0") == 0) {
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_0, 5));
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_0, 5,5));
|
||||
|
||||
} else if (strcmp(type,"tri1") == 0) {
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_1, 5));
|
||||
obj = new resolv_functor(vvp_scalar_t(BIT4_1, 5,5));
|
||||
|
||||
} else if (strcmp(type,"triand") == 0) {
|
||||
obj = new resolv_triand;
|
||||
|
|
|
|||
|
|
@ -151,6 +151,10 @@ extern void compile_arith_pow(char*label, long width, bool signed_flag,
|
|||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_arith_abs(char*label,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_arith_cast_int(char*label, long width,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_arith_cast_real(char*label, bool signed_flag,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_arith_div(char*label, long width, bool signed_flag,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_arith_mod(char*label, long width,
|
||||
|
|
@ -335,6 +339,8 @@ extern void compile_array_port(char*label, char*name, char*addr);
|
|||
/* Index is a constant address */
|
||||
extern void compile_array_port(char*label, char*name, long addr);
|
||||
|
||||
extern void compile_array_cleanup(void);
|
||||
|
||||
/*
|
||||
* Compile the .ufunc statement.
|
||||
*/
|
||||
|
|
@ -451,4 +457,16 @@ extern void compile_aliasw(char*label, char*array_symbol,
|
|||
unsigned long array_addr, int msb, int lsb,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
|
||||
extern void compile_island(char*label, char*type);
|
||||
extern void compile_island_port(char*label, char*island, char*src);
|
||||
extern void compile_island_import(char*label, char*island, char*src);
|
||||
extern void compile_island_export(char*label, char*island);
|
||||
|
||||
extern void compile_island_tranif(int sense, char*island,
|
||||
char*ba, char*bb, char*src);
|
||||
extern void compile_island_tranvp(char*island, char*ba, char*bb,
|
||||
unsigned width, unsigned part, unsigned off);
|
||||
|
||||
extern void compile_island_cleanup(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ void vvp_fun_delay::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit)
|
|||
}
|
||||
}
|
||||
|
||||
void vvp_fun_delay::recv_vec8(vvp_net_ptr_t port, vvp_vector8_t bit)
|
||||
void vvp_fun_delay::recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit)
|
||||
{
|
||||
assert(port.port() == 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class vvp_fun_delay : public vvp_net_fun_t, private vvp_gen_event_s {
|
|||
~vvp_fun_delay();
|
||||
|
||||
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit);
|
||||
void recv_vec8(vvp_net_ptr_t port, vvp_vector8_t bit);
|
||||
void recv_vec8(vvp_net_ptr_t port, const vvp_vector8_t&bit);
|
||||
void recv_real(vvp_net_ptr_t port, double bit);
|
||||
//void recv_long(vvp_net_ptr_t port, long bit);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
/*
|
||||
* The vvp_dff implements a D-type FF that is agnostic to the data
|
||||
* type that is holds. The clock and clock-enable inputs are single
|
||||
* bits and may be invertable. An output is propagated on the logical
|
||||
* bits and may be invertible. An output is propagated on the logical
|
||||
* rising edge of the clock input, or whenever an asynchronous input
|
||||
* is received. Ports are:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ m .var "m", 6,0;
|
|||
;;; memory words occupy 8 bits each, that is 7 rounded up to the next
|
||||
;;; multiple of 4.
|
||||
|
||||
;;; Four bits ber byte. Word fill bits are included. Commas are
|
||||
;;; Four bits per byte. Word fill bits are included. Commas are
|
||||
;;; optional, there may be a comma after the last byte.
|
||||
|
||||
.mem/init memory[20],
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@
|
|||
".array/real" { return K_ARRAY_R; }
|
||||
".array/s" { return K_ARRAY_S; }
|
||||
".array/port" { return K_ARRAY_PORT; }
|
||||
".cast/int" { return K_CAST_INT; }
|
||||
".cast/real" { return K_CAST_REAL; }
|
||||
".cast/real.s" { return K_CAST_REAL_S; }
|
||||
".cmp/eeq" { return K_CMP_EEQ; }
|
||||
".cmp/eq" { return K_CMP_EQ; }
|
||||
".cmp/eq.r" { return K_CMP_EQ_R; }
|
||||
|
|
@ -124,8 +127,11 @@
|
|||
".dff" { return K_DFF; }
|
||||
".event" { return K_EVENT; }
|
||||
".event/or" { return K_EVENT_OR; }
|
||||
".export" { return K_EXPORT; }
|
||||
".extend/s" { return K_EXTEND_S; }
|
||||
".functor" { return K_FUNCTOR; }
|
||||
".import" { return K_IMPORT; }
|
||||
".island" { return K_ISLAND; }
|
||||
".modpath" { return K_MODPATH; }
|
||||
".net" { return K_NET; }
|
||||
".net8" { return K_NET8; }
|
||||
|
|
@ -138,6 +144,7 @@
|
|||
".part" { return K_PART; }
|
||||
".part/pv" { return K_PART_PV; }
|
||||
".part/v" { return K_PART_V; }
|
||||
".port" { return K_PORT; }
|
||||
".reduce/and" { return K_REDUCE_AND; }
|
||||
".reduce/or" { return K_REDUCE_OR; }
|
||||
".reduce/xor" { return K_REDUCE_XOR; }
|
||||
|
|
@ -153,6 +160,10 @@
|
|||
".shift/rs" { return K_SHIFTRS; }
|
||||
".thread" { return K_THREAD; }
|
||||
".timescale" { return K_TIMESCALE; }
|
||||
".tran" { return K_TRAN; }
|
||||
".tranif0" { return K_TRANIF0; }
|
||||
".tranif1" { return K_TRANIF1; }
|
||||
".tranvp" { return K_TRANVP; }
|
||||
".ufunc" { return K_UFUNC; }
|
||||
".var" { return K_VAR; }
|
||||
".var/real" { return K_VAR_R; }
|
||||
|
|
@ -173,22 +184,24 @@
|
|||
"%disable" { return K_disable; }
|
||||
"%fork" { return K_fork; }
|
||||
|
||||
/* Handle the specialized variable access functions. */
|
||||
|
||||
"&A" { return K_A; }
|
||||
"&PV" { return K_PV; }
|
||||
|
||||
"%"[.$_/a-zA-Z0-9]+ {
|
||||
yylval.text = strdup(yytext);
|
||||
assert(yylval.text);
|
||||
return T_INSTR; }
|
||||
|
||||
[0-9][0-9]* {
|
||||
yylval.numb = strtol(yytext, 0, 0);
|
||||
yylval.numb = strtoul(yytext, 0, 0);
|
||||
return T_NUMBER; }
|
||||
|
||||
"0x"[0-9a-fA-F]+ {
|
||||
yylval.numb = strtol(yytext, 0, 0);
|
||||
yylval.numb = strtoul(yytext, 0, 0);
|
||||
return T_NUMBER; }
|
||||
|
||||
|
||||
"&A" { return K_A; }
|
||||
|
||||
/* Handle some specialized constant/literals as symbols. */
|
||||
|
||||
"C4<"[01xz]*">" {
|
||||
|
|
@ -218,7 +231,7 @@
|
|||
|
||||
/* Symbols are pretty much what is left. They are used to refer to
|
||||
labels so the rule must match a string that a label would match. */
|
||||
[.$_a-zA-Z\\][.$_a-zA-Z\\0-9<>/]* {
|
||||
[.$_a-zA-Z\\]([.$_a-zA-Z\\0-9/]|(\\.))* {
|
||||
yylval.text = strdup(yytext);
|
||||
assert(yylval.text);
|
||||
return T_SYMBOL; }
|
||||
|
|
|
|||
39
vvp/main.cc
39
vvp/main.cc
|
|
@ -176,7 +176,7 @@ int main(int argc, char*argv[])
|
|||
" -M path VPI module directory\n"
|
||||
" -M - Clear VPI module path\n"
|
||||
" -m module Load vpi module.\n"
|
||||
" -n Non-interctive ($stop = $finish).\n"
|
||||
" -n Non-interactive ($stop = $finish).\n"
|
||||
" -s $stop right away.\n"
|
||||
" -v Verbose progress messages.\n" );
|
||||
exit(0);
|
||||
|
|
@ -274,15 +274,25 @@ int main(int argc, char*argv[])
|
|||
}
|
||||
|
||||
if (verbose_flag) {
|
||||
vpi_mcd_printf(1, " ... %8lu functors\n", count_functors);
|
||||
vpi_mcd_printf(1, " ... %8lu functors (net_fun pool=%zu bytes)\n",
|
||||
count_functors, size_vvp_net_funs);
|
||||
vpi_mcd_printf(1, " %8lu logic\n", count_functors_logic);
|
||||
vpi_mcd_printf(1, " %8lu bufif\n", count_functors_bufif);
|
||||
vpi_mcd_printf(1, " %8lu resolv\n",count_functors_resolv);
|
||||
vpi_mcd_printf(1, " %8lu signals\n", count_functors_sig);
|
||||
vpi_mcd_printf(1, " ... %8lu opcodes (%lu bytes)\n",
|
||||
count_opcodes, (unsigned long)size_opcodes);
|
||||
vpi_mcd_printf(1, " ... %8lu opcodes (%zu bytes)\n",
|
||||
count_opcodes, size_opcodes);
|
||||
vpi_mcd_printf(1, " ... %8lu nets\n", count_vpi_nets);
|
||||
vpi_mcd_printf(1, " ... %8lu memories\n", count_vpi_memories);
|
||||
vpi_mcd_printf(1, " ... %8lu vvp_nets (%zu bytes)\n",
|
||||
count_vvp_nets, size_vvp_nets);
|
||||
vpi_mcd_printf(1, " ... %8lu arrays (%lu words)\n",
|
||||
count_net_arrays, count_net_array_words);
|
||||
vpi_mcd_printf(1, " ... %8lu memories\n",
|
||||
count_var_arrays+count_real_arrays);
|
||||
vpi_mcd_printf(1, " %8lu logic (%lu words)\n",
|
||||
count_var_arrays, count_var_array_words);
|
||||
vpi_mcd_printf(1, " %8lu real (%lu words)\n",
|
||||
count_real_arrays, count_real_array_words);
|
||||
vpi_mcd_printf(1, " ... %8lu scopes\n", count_vpi_scopes);
|
||||
}
|
||||
|
||||
|
|
@ -299,16 +309,23 @@ int main(int argc, char*argv[])
|
|||
my_getrusage(cycles+2);
|
||||
print_rusage(cycles+2, cycles+1);
|
||||
|
||||
vpi_mcd_printf(1, "Event counts: (event pool = %lu)\n",
|
||||
count_event_pool);
|
||||
vpi_mcd_printf(1, "Event counts:\n");
|
||||
vpi_mcd_printf(1, " %8lu time steps (pool=%lu)\n",
|
||||
count_time_events, count_time_pool());
|
||||
vpi_mcd_printf(1, " %8lu thread schedule events\n",
|
||||
count_thread_events);
|
||||
vpi_mcd_printf(1, " %8lu propagation events\n",
|
||||
count_prop_events);
|
||||
vpi_mcd_printf(1, " %8lu assign events\n",
|
||||
count_assign_events);
|
||||
vpi_mcd_printf(1, " %8lu other events\n",
|
||||
count_gen_events);
|
||||
vpi_mcd_printf(1, " ...assign(vec4) pool=%lu\n",
|
||||
count_assign4_pool());
|
||||
vpi_mcd_printf(1, " ...assign(vec8) pool=%lu\n",
|
||||
count_assign8_pool());
|
||||
vpi_mcd_printf(1, " ...assign(real) pool=%lu\n",
|
||||
count_assign_real_pool());
|
||||
vpi_mcd_printf(1, " ...assign(word) pool=%lu\n",
|
||||
count_assign_aword_pool());
|
||||
vpi_mcd_printf(1, " %8lu other events (pool=%lu)\n",
|
||||
count_gen_events, count_gen_pool());
|
||||
}
|
||||
|
||||
return vvp_return_value;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue