Merge branch 'master' into elaborate-net-rework

This commit is contained in:
Stephen Williams 2008-08-21 18:11:21 -07:00
commit 04d49fcf35
41 changed files with 626 additions and 117 deletions

View File

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

View File

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

View File

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

View File

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

10
PTask.h
View File

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

View File

@ -998,6 +998,7 @@ void NetScope::dump(ostream&o) const
o << " generate block";
break;
}
if (is_auto()) o << " (automatic)";
o << endl;
for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1)

View File

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

View File

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

View File

@ -150,6 +150,7 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope,
}
NetScope*task_scope = new NetScope(scope, use_name,
NetScope::TASK);
task_scope->is_auto((*cur).second->is_auto());
task_scope->set_line((*cur).second);
if (debug_scopes)
@ -179,6 +180,7 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope,
}
NetScope*func_scope = new NetScope(scope, use_name,
NetScope::FUNC);
func_scope->is_auto((*cur).second->is_auto());
func_scope->set_line((*cur).second);
if (debug_scopes)

View File

@ -2532,8 +2532,8 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
}
NexusSet*nset = enet->nex_input(rem_out);
if (nset == 0) {
cerr << get_fileline() << ": internal error: No NexusSet"
<< " from statement." << endl;
cerr << get_fileline() << ": error: Unable to elaborate:"
<< endl;
enet->dump(cerr, 6);
des->errors += 1;
return enet;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -697,6 +697,9 @@ class NetScope : public Attrib {
unsigned get_def_lineno() const { return def_lineno_; };
bool in_func();
/* Is the task or function automatic. */
void is_auto(bool is_auto) { is_auto_ = is_auto; };
bool is_auto() const { return is_auto_; };
const NetTaskDef* task_def() const;
const NetFuncDef* func_def() const;
@ -840,6 +843,7 @@ class NetScope : public Attrib {
NetScope*sub_;
unsigned lcounter_;
bool is_auto_;
};
/*
@ -3455,6 +3459,8 @@ class NetESFunc : public NetExpr {
NetExpr* parm(unsigned idx);
const NetExpr* parm(unsigned idx) const;
virtual NetExpr* eval_tree(int prune_to_width = -1);
virtual ivl_variable_type_t expr_type() const;
virtual NexusSet* nex_input(bool rem_out = true);
virtual bool set_width(unsigned, bool last_chance);

61
parse.y
View File

@ -205,7 +205,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
%token K_PSTAR K_STARP
%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER
%token K_abs K_abstol K_access K_acos K_acosh K_asin K_analog K_asinh
%token K_atan K_atanh K_atan2
%token K_atan K_atanh K_atan2 K_automatic
%token K_always K_and K_assign K_begin K_bool K_buf K_bufif0 K_bufif1 K_case
%token K_casex K_casez K_ceil K_cmos K_continuous K_cos K_cosh
%token K_ddt_nature K_deassign K_default K_defparam K_disable K_discrete
@ -239,7 +239,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2)
%type <flag> from_exclude
%type <number> number
%type <flag> signed_opt udp_reg_opt edge_operator
%type <flag> signed_opt udp_reg_opt edge_operator automatic_opt
%type <drive> drive_strength drive_strength_opt dr_strength0 dr_strength1
%type <letter> udp_input_sym udp_output_sym
%type <text> udp_input_list udp_sequ_entry udp_comb_entry
@ -2094,41 +2094,41 @@ module_item
statements in the task body. But we continue to accept it as an
extension. */
| K_task IDENTIFIER ';'
| K_task automatic_opt IDENTIFIER ';'
{ assert(current_task == 0);
current_task = pform_push_task_scope($2);
current_task = pform_push_task_scope($3, $2);
FILE_NAME(current_task, @1);
}
task_item_list_opt
statement_or_null
K_endtask
{ current_task->set_ports($5);
current_task->set_statement($6);
{ current_task->set_ports($6);
current_task->set_statement($7);
pform_pop_scope();
current_task = 0;
delete[]$2;
delete[]$3;
}
| K_task IDENTIFIER
| K_task automatic_opt IDENTIFIER
{ assert(current_task == 0);
current_task = pform_push_task_scope($2);
current_task = pform_push_task_scope($3, $2);
FILE_NAME(current_task, @1);
}
'(' task_port_decl_list ')' ';'
block_item_decls_opt
statement_or_null
K_endtask
{ current_task->set_ports($5);
current_task->set_statement($9);
{ current_task->set_ports($6);
current_task->set_statement($10);
pform_pop_scope();
current_task = 0;
delete[]$2;
delete[]$3;
}
| K_task IDENTIFIER error K_endtask
| K_task automatic_opt IDENTIFIER error K_endtask
{
pform_pop_scope();
current_task = 0;
delete[]$2;
delete[]$3;
}
/* The function declaration rule matches the function declaration
@ -2136,42 +2136,42 @@ module_item
definitions in the func_body to take on the scope of the function
instead of the module. */
| K_function function_range_or_type_opt IDENTIFIER ';'
| K_function automatic_opt function_range_or_type_opt IDENTIFIER ';'
{ assert(current_function == 0);
current_function = pform_push_function_scope($3);
current_function = pform_push_function_scope($4, $2);
FILE_NAME(current_function, @1);
}
function_item_list statement
K_endfunction
{ current_function->set_ports($6);
current_function->set_statement($7);
current_function->set_return($2);
{ current_function->set_ports($7);
current_function->set_statement($8);
current_function->set_return($3);
pform_pop_scope();
current_function = 0;
delete[]$3;
delete[]$4;
}
| K_function function_range_or_type_opt IDENTIFIER
| K_function automatic_opt function_range_or_type_opt IDENTIFIER
{ assert(current_function == 0);
current_function = pform_push_function_scope($3);
current_function = pform_push_function_scope($4, $2);
FILE_NAME(current_function, @1);
}
'(' task_port_decl_list ')' ';'
block_item_decls_opt
statement
K_endfunction
{ current_function->set_ports($6);
current_function->set_statement($10);
current_function->set_return($2);
{ current_function->set_ports($7);
current_function->set_statement($11);
current_function->set_return($3);
pform_pop_scope();
current_function = 0;
delete[]$3;
delete[]$4;
}
| K_function function_range_or_type_opt IDENTIFIER error K_endfunction
| K_function automatic_opt function_range_or_type_opt IDENTIFIER error K_endfunction
{
pform_pop_scope();
current_task = 0;
delete[]$3;
delete[]$4;
}
/* A generate region can contain further module items. Actually, it
@ -2278,6 +2278,11 @@ module_item
{ yyerror(@1, "error: Malformed $attribute parameter list."); }
;
automatic_opt
: K_automatic { $$ = true; }
| { $$ = false;}
;
generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); }
generate_case_items

View File

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

View File

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

View File

@ -715,6 +715,7 @@ void PForStatement::dump(ostream&out, unsigned ind) const
void PFunction::dump(ostream&out, unsigned ind) const
{
out << setw(ind) << "" << "function ";
if (is_auto_) cout << "automatic ";
switch (return_type_.type) {
case PTF_NONE:
out << "?none? ";
@ -775,6 +776,9 @@ void PRepeat::dump(ostream&out, unsigned ind) const
void PTask::dump(ostream&out, unsigned ind) const
{
out << setw(ind) << "" << "task ";
if (is_auto_) cout << "automatic ";
out << pscope_name() << ";" << endl;
if (ports_)
for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) {
out << setw(ind) << "";

View File

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

View File

@ -575,6 +575,7 @@ void dll_target::add_root(ivl_design_s &des_, const NetScope *s)
root_->time_units = s->time_unit();
root_->nattr = s->attr_cnt();
root_->attr = fill_in_attributes(s);
root_->is_auto = 0;
des_.nroots_++;
if (des_.roots_)
@ -2319,7 +2320,8 @@ void dll_target::scope(const NetScope*net)
scope->time_precision = net->time_precision();
scope->time_units = net->time_unit();
scope->nattr = net->attr_cnt();
scope->attr = fill_in_attributes(net);
scope->attr = fill_in_attributes(net);
scope->is_auto = net->is_auto();
switch (net->type()) {
case NetScope::MODULE:

View File

@ -590,6 +590,7 @@ struct ivl_scope_s {
/* Scopes that are tasks/functions have a definition. */
ivl_statement_t def;
unsigned is_auto;
unsigned ports;
ivl_signal_t*port;

View File

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

View File

@ -1769,6 +1769,13 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent)
{
unsigned idx;
const char *type;
/* For now we do not support automatic tasks or functions. */
if (ivl_scope_is_auto(net)) {
fprintf(stderr, "%s:%u: vvp-tgt sorry: automatic tasks/functions "
"are not supported!\n",
ivl_scope_def_file(net), ivl_scope_def_lineno(net));
exit(1);
}
switch (ivl_scope_type(net)) {
case IVL_SCT_MODULE: type = "module"; break;
case IVL_SCT_FUNCTION: type = "function"; break;

View File

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

View File

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

View File

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

109
vpi/sys_clog2.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -137,6 +137,7 @@ struct __vpiCallback {
// user supplied callback data
struct t_cb_data cb_data;
struct t_vpi_time cb_time;
struct t_vpi_value cb_value;
// scheduled event
struct sync_cb* cb_sync;

View File

@ -728,23 +728,6 @@ static vvp_vector4_t from_stringval(const char*str, unsigned wid)
return val;
}
static vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar)
{
switch(scalar) {
case vpi0:
return BIT4_0;
case vpi1:
return BIT4_1;
case vpiX:
return BIT4_X;
case vpiZ:
return BIT4_Z;
default:
fprintf(stderr, "Unsupported scalar value %d.\n", scalar);
assert(0);
}
}
static vpiHandle signal_put_value(vpiHandle ref, s_vpi_value*vp, int flags)
{
unsigned wid;

View File

@ -2071,7 +2071,14 @@ bool of_FORK(vthread_t thr, vvp_code_t cp)
thr->fork_count += 1;
schedule_vthread(child, 0, true);
/* If the new child was created to evaluate a function,
run it immediately, then return to this thread. */
if (cp->scope->base.vpi_type->type_code == vpiFunction) {
child->is_scheduled = 1;
vthread_run(child);
} else {
schedule_vthread(child, 0, true);
}
return true;
}

View File

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

View File

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

View File

@ -20,6 +20,7 @@
*/
# include "config.h"
# include "vpi_user.h"
# include <stddef.h>
# include <string.h>
# include <new>
@ -67,6 +68,8 @@ inline char vvp_bit4_to_ascii(vvp_bit4_t a) { return "01zx"[a]; }
extern vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c);
extern vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar);
/* Return TRUE if the bit is BIT4_X or BIT4_Z. The fast
implementation here relies on the encoding of vvp_bit4_t values. */
inline bool bit4_is_xz(vvp_bit4_t a) { return a >= 2; }
@ -470,6 +473,9 @@ class vvp_vector2_t {
void set_bit(unsigned idx, int bit);
// Make the size just big enough to hold the first 1 bit.
void trim();
// Trim off extra 1 bit since this is representing a negative value.
// Always keep at least 32 bits.
void trim_neg();
private:
enum { BITS_PER_WORD = 8 * sizeof(unsigned long) };