iverilog/t-vvm.cc

2058 lines
64 KiB
C++
Raw Normal View History

1998-11-04 00:28:49 +01:00
/*
* Copyright (c) 1998-1999 Stephen Williams (steve@icarus.com)
1998-11-04 00:28:49 +01:00
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
1999-11-29 00:59:22 +01:00
#ident "$Id: t-vvm.cc,v 1.85 1999/11/28 23:59:22 steve Exp $"
1998-11-04 00:28:49 +01:00
#endif
# include <iostream>
# include <fstream>
1998-11-04 00:28:49 +01:00
# include <strstream>
# include <iomanip>
1998-11-04 00:28:49 +01:00
# include <string>
# include <typeinfo>
# include <unistd.h>
1998-11-04 00:28:49 +01:00
# include "netlist.h"
# include "target.h"
// Comparison for use in sorting algorithms.
struct less_verinum {
bool operator() (const verinum&left, const verinum&right)
{ return left.is_before(right); }
};
1998-11-04 00:28:49 +01:00
static string make_temp()
{
static unsigned counter = 0;
ostrstream str;
str << "TMP" << counter << ends;
counter += 1;
return str.str();
}
class target_vvm : public target_t {
friend class vvm_parm_rval;
1998-11-04 00:28:49 +01:00
public:
target_vvm();
1998-11-04 00:28:49 +01:00
virtual void start_design(ostream&os, const Design*);
1999-11-27 20:07:57 +01:00
virtual void scope(ostream&os, const NetScope*);
1998-11-04 00:28:49 +01:00
virtual void signal(ostream&os, const NetNet*);
virtual void memory(ostream&os, const NetMemory*);
1999-07-07 06:20:57 +02:00
virtual void task_def(ostream&os, const NetTaskDef*);
virtual void func_def(ostream&os, const NetFuncDef*);
1999-09-04 03:57:15 +02:00
virtual void lpm_add_sub(ostream&os, const NetAddSub*);
virtual void lpm_clshift(ostream&os, const NetCLShift*);
1999-11-15 00:43:45 +01:00
virtual void lpm_compare(ostream&os, const NetCompare*);
virtual void lpm_ff(ostream&os, const NetFF*);
1999-11-13 04:46:52 +01:00
virtual void lpm_mux(ostream&os, const NetMux*);
virtual void lpm_ram_dq(ostream&os, const NetRamDq*);
1999-09-04 03:57:15 +02:00
1998-11-04 00:28:49 +01:00
virtual void logic(ostream&os, const NetLogic*);
virtual void bufz(ostream&os, const NetBUFZ*);
virtual void udp(ostream&os, const NetUDP*);
virtual void net_assign_nb(ostream&os, const NetAssignNB*);
1999-10-10 03:59:54 +02:00
virtual void net_case_cmp(ostream&os, const NetCaseCmp*);
virtual void net_const(ostream&os, const NetConst*);
virtual void net_event(ostream&os, const NetNEvent*);
1999-09-22 18:57:23 +02:00
virtual bool process(ostream&os, const NetProcTop*);
1998-11-04 00:28:49 +01:00
virtual void proc_assign(ostream&os, const NetAssign*);
virtual void proc_assign_mem(ostream&os, const NetAssignMem*);
virtual void proc_assign_nb(ostream&os, const NetAssignNB*);
1999-09-22 18:57:23 +02:00
virtual bool proc_block(ostream&os, const NetBlock*);
virtual void proc_case(ostream&os, const NetCase*net);
virtual void proc_condit(ostream&os, const NetCondit*);
virtual void proc_forever(ostream&os, const NetForever*);
virtual void proc_repeat(ostream&os, const NetRepeat*);
1999-07-03 04:12:51 +02:00
virtual void proc_stask(ostream&os, const NetSTask*);
1999-07-07 06:20:57 +02:00
virtual void proc_utask(ostream&os, const NetUTask*);
virtual void proc_while(ostream&os, const NetWhile*);
1998-11-04 00:28:49 +01:00
virtual void proc_event(ostream&os, const NetPEvent*);
virtual void proc_delay(ostream&os, const NetPDelay*);
virtual void end_design(ostream&os, const Design*);
1999-07-17 05:39:11 +02:00
void start_process(ostream&os, const NetProcTop*);
void end_process(ostream&os, const NetProcTop*);
1998-11-04 00:28:49 +01:00
private:
void emit_gate_outputfun_(const NetNode*, unsigned);
string defn_gate_outputfun_(ostream&os, const NetNode*, unsigned);
1998-11-04 00:28:49 +01:00
// This is the name of the thread (process or task) that is
// being generated.
string thread_class_;
// Method definitions go into this file.
char*defn_name;
ofstream defn;
char*delayed_name;
ofstream delayed;
char*init_code_name;
ofstream init_code;
char *start_code_name;
ofstream start_code;
1998-11-04 00:28:49 +01:00
unsigned process_counter;
unsigned thread_step_;
// These methods are use to help prefent duplicate printouts
// of things that may be scanned multiple times.
map<string,bool>esignal_printed_flag;
map<string,bool>pevent_printed_flag;
// String constants that are made into vpiHandles have th
// handle name mapped by this.
map<string,unsigned>string_constants;
unsigned string_counter;
map<verinum,unsigned,less_verinum>number_constants;
unsigned number_counter;
1998-11-04 00:28:49 +01:00
};
target_vvm::target_vvm()
: delayed_name(0), init_code_name(0)
{
}
/*
* This class emits code for the rvalue of a procedural
* assignment. The expression is evaluated to fit the width
* specified.
*/
1998-11-04 00:28:49 +01:00
class vvm_proc_rval : public expr_scan_t {
public:
explicit vvm_proc_rval(ostream&os, unsigned i)
: result(""), os_(os), indent_(i) { }
1998-11-04 00:28:49 +01:00
string result;
private:
ostream&os_;
unsigned indent_;
1998-11-04 00:28:49 +01:00
private:
virtual void expr_const(const NetEConst*);
virtual void expr_concat(const NetEConcat*);
1998-11-04 00:28:49 +01:00
virtual void expr_ident(const NetEIdent*);
1999-11-10 03:52:24 +01:00
virtual void expr_memory(const NetEMemory*mem);
virtual void expr_signal(const NetESignal*);
1999-04-26 00:52:32 +02:00
virtual void expr_subsignal(const NetESubSignal*sig);
virtual void expr_ternary(const NetETernary*);
1998-11-04 00:28:49 +01:00
virtual void expr_unary(const NetEUnary*);
virtual void expr_binary(const NetEBinary*);
virtual void expr_ufunc(const NetEUFunc*);
1998-11-04 00:28:49 +01:00
};
/*
* Handle the concatenation operator in a procedural r-value
* expression. Evaluate the concatenation into a temporary variable
* with the right width, and return the name of that temporary as the
* symbol that the context can use.
*/
void vvm_proc_rval::expr_concat(const NetEConcat*expr)
{
assert(expr->repeat() > 0);
string tname = make_temp();
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
expr->expr_width() << "> " << tname << ";" << endl;
unsigned pos = 0;
for (unsigned rep = 0 ; rep < expr->repeat() ; rep += 1)
for (unsigned idx = 0 ; idx < expr->nparms() ; idx += 1) {
NetExpr*pp = expr->parm(expr->nparms() - idx - 1);
pp->expr_scan(this);
for (unsigned bit = 0 ; bit < pp->expr_width() ; bit += 1) {
os_ << setw(indent_) << "" << tname << "[" << pos <<
"] = " << result << "[" << bit << "];" <<
endl;
pos+= 1;
}
assert(pos <= expr->expr_width());
}
/* Check that the positions came out to the right number of
bits. */
if (pos != expr->expr_width()) {
os_ << "#error \"" << expr->get_line() << ": vvm eror: "
"width is " << expr->expr_width() << ", but I count "
<< pos << " bits.\"" << endl;
}
result = tname;
}
1998-11-04 00:28:49 +01:00
void vvm_proc_rval::expr_const(const NetEConst*expr)
{
string tname = make_temp();
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
expr->expr_width() << "> " << tname << ";" << endl;
for (unsigned idx = 0 ; idx < expr->expr_width() ; idx += 1) {
os_ << setw(indent_) << "" << tname << "[" << idx << "] = ";
1998-11-04 00:28:49 +01:00
switch (expr->value().get(idx)) {
case verinum::V0:
os_ << "V0";
break;
case verinum::V1:
os_ << "V1";
break;
case verinum::Vx:
os_ << "Vx";
break;
case verinum::Vz:
os_ << "Vz";
break;
}
os_ << ";" << endl;
}
result = tname;
}
void vvm_proc_rval::expr_ident(const NetEIdent*expr)
{
result = mangle(expr->name());
}
1999-11-10 03:52:24 +01:00
void vvm_proc_rval::expr_memory(const NetEMemory*mem)
{
const string mname = mangle(mem->name());
mem->index()->expr_scan(this);
result = mname + ".get_word(" + result + ".as_unsigned())";
}
void vvm_proc_rval::expr_signal(const NetESignal*expr)
{
result = mangle(expr->name()) + "_bits";
}
1999-04-26 00:52:32 +02:00
void vvm_proc_rval::expr_subsignal(const NetESubSignal*sig)
{
string idx = make_temp();
string val = make_temp();
if (const NetEConst*cp = dynamic_cast<const NetEConst*>(sig->index())) {
os_ << setw(indent_) << "" << "const unsigned " << idx <<
" = " << cp->value().as_ulong() << ";" << endl;
} else {
sig->index()->expr_scan(this);
os_ << setw(indent_) << "" << "const unsigned " <<
idx << " = " << result << ".as_unsigned();" <<
endl;
}
os_ << setw(indent_) << "" << "vvm_bitset_t<1>" << val << ";" << endl;
os_ << setw(indent_) << "" << val << "[0] = " <<
mangle(sig->name()) << "_bits[" << idx << "];" << endl;
result = val;
}
void vvm_proc_rval::expr_ternary(const NetETernary*expr)
{
expr->cond_expr()->expr_scan(this);
string cond_val = result;
expr->true_expr()->expr_scan(this);
string true_val = result;
expr->false_expr()->expr_scan(this);
string false_val = result;
result = make_temp();
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
expr->expr_width() << ">" << result << ";" << endl;
os_ << setw(indent_) << "" << result << " = vvm_ternary(" <<
cond_val << "[0], " << true_val << ", " << false_val << ");"
<< endl;
}
/*
* A function call is handled by assigning the parameters from the
* input expressions, then calling the function. After the function
* returns, copy the result into a temporary variable.
*
* Function calls are different from tasks in this regard--tasks had
* all this assigning arranged during elaboration. For functions, we
* must do it ourselves.
*/
void vvm_proc_rval::expr_ufunc(const NetEUFunc*expr)
{
const NetFuncDef*def = expr->definition();
const unsigned pcnt = expr->parm_count();
assert(pcnt == (def->port_count()-1));
/* Scan the parameter expressions, and assign the values to
the parameter port register. */
for (unsigned idx = 0 ; idx < pcnt ; idx += 1) {
expr->parm(idx)->expr_scan(this);
os_ << " " << mangle(def->port(idx+1)->name()) <<
"_bits = " << result << ";" << endl;
}
/* Make the function call. */
os_ << " " << mangle(expr->name()) << "(sim_);" << endl;
/* Save the return value in a temporary. */
result = make_temp();
string rbits = mangle(expr->result()->name()) + "_bits";
os_ << " vvm_bitset_t<" << expr->expr_width() << "> " <<
result << " = " << rbits << ";" << endl;
}
1998-11-04 00:28:49 +01:00
void vvm_proc_rval::expr_unary(const NetEUnary*expr)
{
expr->expr()->expr_scan(this);
string tname = make_temp();
1999-10-05 08:19:46 +02:00
os_ << " vvm_bitset_t<" << expr->expr_width() << "> "
<< tname << " = ";
1998-11-04 00:28:49 +01:00
switch (expr->op()) {
case '~':
1999-10-05 08:19:46 +02:00
os_ << "vvm_unop_not(" << result << ");" << endl;
1998-11-04 00:28:49 +01:00
break;
case '&':
1999-10-05 08:19:46 +02:00
os_ << "vvm_unop_and(" << result << ");" << endl;
break;
1999-10-08 04:00:48 +02:00
case '|':
os_ << "vvm_unop_or(" << result << ");" << endl;
break;
case '^':
1999-10-05 08:19:46 +02:00
os_ << "vvm_unop_xor(" << result << ");" << endl;
break;
case '!':
1999-10-05 08:19:46 +02:00
os_ << "vvm_unop_lnot(" << result << ");" << endl;
break;
case '-':
1999-10-05 08:19:46 +02:00
os_ << "vvm_unop_uminus(" << result << ");" << endl;
break;
case 'N':
os_ << "vvm_unop_nor(" << result << ");" << endl;
break;
case 'X':
1999-10-05 08:19:46 +02:00
os_ << "vvm_unop_xnor(" << result << ");" << endl;
break;
1998-11-04 00:28:49 +01:00
default:
1999-10-08 04:00:48 +02:00
cerr << "vvm error: Unhandled unary op `" << expr->op() << "'"
1998-11-04 00:28:49 +01:00
<< endl;
1999-09-23 05:56:57 +02:00
os_ << "#error \"" << expr->get_line() << ": vvm error: "
"Unhandled unary op: " << *expr << "\"" << endl;
1998-11-04 00:28:49 +01:00
os_ << result << ";" << endl;
break;
}
result = tname;
}
void vvm_proc_rval::expr_binary(const NetEBinary*expr)
{
expr->left()->expr_scan(this);
string lres = result;
expr->right()->expr_scan(this);
string rres = result;
result = make_temp();
os_ << setw(indent_) << "" << "// " << expr->get_line() <<
": expression node." << endl;
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
expr->expr_width() << ">" << result << ";" << endl;
switch (expr->op()) {
case 'a': // logical and (&&)
os_ << setw(indent_) << "" << result << " = vvm_binop_land("
<< lres << "," << rres << ");" << endl;
break;
1999-06-24 06:21:45 +02:00
case 'E': // ===
os_ << setw(indent_) << "" << result << " = vvm_binop_eeq("
<< lres << "," << rres << ");" << endl;
break;
case 'e': // ==
os_ << setw(indent_) << "" << result << " = vvm_binop_eq("
<< lres << "," << rres << ");" << endl;
break;
case 'G': // >=
os_ << setw(indent_) << "" << result << " = vvm_binop_ge("
<< lres << "," << rres << ");" << endl;
break;
1999-09-23 05:56:57 +02:00
case 'l': // left shift(<<)
os_ << setw(indent_) << "" << result << " = vvm_binop_shiftl("
<< lres << "," << rres << ");" << endl;
break;
case 'L': // <=
os_ << setw(indent_) << "" << result << " = vvm_binop_le("
<< lres << "," << rres << ");" << endl;
break;
1999-06-24 06:21:45 +02:00
case 'N': // !==
os_ << setw(indent_) << "" << result << " = vvm_binop_nee("
<< lres << "," << rres << ");" << endl;
break;
case 'n':
os_ << setw(indent_) << "" << result << " = vvm_binop_ne("
<< lres << "," << rres << ");" << endl;
break;
case '<':
os_ << setw(indent_) << "" << result << " = vvm_binop_lt("
<< lres << "," << rres << ");" << endl;
break;
case '>':
os_ << setw(indent_) << "" << result << " = vvm_binop_gt("
<< lres << "," << rres << ");" << endl;
break;
case 'o': // logical or (||)
os_ << setw(indent_) << "" << result << " = vvm_binop_lor("
<< lres << "," << rres << ");" << endl;
1999-09-23 05:56:57 +02:00
case 'r': // right shift(>>)
os_ << setw(indent_) << "" << result << " = vvm_binop_shiftr("
<< lres << "," << rres << ");" << endl;
break;
case '+':
os_ << setw(indent_) << "" << result << " = vvm_binop_plus("
<< lres << "," << rres << ");" << endl;
break;
case '-':
os_ << setw(indent_) << "" << result << " = vvm_binop_minus("
<< lres << "," << rres << ");" << endl;
break;
1999-02-22 04:01:12 +01:00
case '&':
os_ << setw(indent_) << "" << result << " = vvm_binop_and("
<< lres << "," << rres << ");" << endl;
break;
case '|':
os_ << setw(indent_) << "" << result << " = vvm_binop_or("
<< lres << "," << rres << ");" << endl;
break;
case '^':
os_ << setw(indent_) << "" << result << " = vvm_binop_xor("
<< lres << "," << rres << ");" << endl;
break;
default:
cerr << "vvm: Unhandled binary op `" << expr->op() << "': "
<< *expr << endl;
1999-09-23 05:56:57 +02:00
os_ << "#error \"" << expr->get_line() << ": vvm error: "
"Unhandled binary op: " << *expr << "\"" << endl;
result = lres;
break;
}
}
static string emit_proc_rval(ostream&os, unsigned indent, const NetExpr*expr)
1998-11-04 00:28:49 +01:00
{
vvm_proc_rval scan (os, indent);
1998-11-04 00:28:49 +01:00
expr->expr_scan(&scan);
return scan.result;
}
/*
* The vvm_parm_rval class scans expressions for the purpose of making
* parameters for system tasks/functions. Thus, the generated code is
* geared towards making the handles needed to make the call.
*
* The result of any parm rval scan is a vpiHandle, or a string that
* automatically converts to a vpiHandle on assignment.
*/
1998-11-04 00:28:49 +01:00
class vvm_parm_rval : public expr_scan_t {
public:
explicit vvm_parm_rval(ostream&o, target_vvm*t)
: result(""), os_(o), tgt_(t) { }
1998-11-04 00:28:49 +01:00
string result;
private:
1999-11-27 20:07:57 +01:00
virtual void expr_const(const NetEConst*);
1998-11-04 00:28:49 +01:00
virtual void expr_ident(const NetEIdent*);
1999-11-27 20:07:57 +01:00
virtual void expr_memory(const NetEMemory*);
virtual void expr_scope(const NetEScope*);
virtual void expr_signal(const NetESignal*);
1998-11-04 00:28:49 +01:00
private:
ostream&os_;
target_vvm*tgt_;
1998-11-04 00:28:49 +01:00
};
1999-07-10 03:02:08 +02:00
void vvm_parm_rval::expr_const(const NetEConst*expr)
{
if (expr->value().is_string()) {
unsigned& res = tgt_->string_constants[expr->value().as_string()];
if (res == 0) {
res = tgt_->string_counter ++;
tgt_->init_code << " vpip_make_string_const("
"&string_table[" << res << "], \"" <<
expr->value().as_string() << "\");" << endl;
}
ostrstream tmp;
tmp << "&string_table[" << res << "].base" << ends;
result = tmp.str();
1999-07-10 03:02:08 +02:00
return;
}
unsigned&res = tgt_->number_constants[expr->value()];
if (res == 0) {
res = tgt_->number_counter ++;
unsigned width = expr->expr_width();
tgt_->init_code << " { vpip_bit_t*bits = new vpip_bit_t["
<< width << "];" << endl;
for (unsigned idx = 0 ; idx < width ; idx += 1) {
tgt_->init_code << " bits[" << idx << "] = ";
switch(expr->value().get(idx)) {
case verinum::V0:
tgt_->init_code << "V0;" << endl;
break;
case verinum::V1:
tgt_->init_code << "V1;" << endl;
break;
case verinum::Vx:
tgt_->init_code << "Vx;" << endl;
break;
case verinum::Vz:
tgt_->init_code << "Vz;" << endl;
break;
}
1999-07-10 03:02:08 +02:00
}
tgt_->init_code << " vpip_make_number_const("
"&number_table[" << res << "], bits, " << width <<
");" << endl;
tgt_->init_code << " }" << endl;
1999-07-10 03:02:08 +02:00
}
ostrstream tmp;
tmp << "&number_table[" << res << "].base" << ends;
result = tmp.str();
return;
1999-07-10 03:02:08 +02:00
}
1998-11-04 00:28:49 +01:00
void vvm_parm_rval::expr_ident(const NetEIdent*expr)
{
if (expr->name() == "$time") {
result = string("vpip_sim_time()");
1998-11-04 00:28:49 +01:00
} else {
cerr << "Unhandled identifier: " << expr->name() << endl;
1998-11-04 00:28:49 +01:00
}
}
1999-11-10 03:52:24 +01:00
void vvm_parm_rval::expr_memory(const NetEMemory*mem)
{
assert(mem->index() == 0);
result = string("&") + mangle(mem->name()) + ".base";
}
1999-11-27 20:07:57 +01:00
void vvm_parm_rval::expr_scope(const NetEScope*escope)
{
result = string("&") + mangle(escope->scope()->name()) + "_scope.base";
}
void vvm_parm_rval::expr_signal(const NetESignal*expr)
{
string res = string("&") + mangle(expr->name()) + ".base";
result = res;
}
static string emit_parm_rval(ostream&os, target_vvm*tgt, const NetExpr*expr)
1998-11-04 00:28:49 +01:00
{
vvm_parm_rval scan (os, tgt);
1998-11-04 00:28:49 +01:00
expr->expr_scan(&scan);
return scan.result;
}
void target_vvm::start_design(ostream&os, const Design*mod)
{
defn_name = tempnam(0, "ivldf");
defn.open(defn_name, ios::in | ios::out | ios::trunc);
delayed_name = tempnam(0, "ivlde");
delayed.open(delayed_name, ios::in | ios::out | ios::trunc);
init_code_name = tempnam(0, "ivlic");
init_code.open(init_code_name, ios::in | ios::out | ios::trunc);
start_code_name = tempnam(0, "ivlsc");
start_code.open(start_code_name, ios::in | ios::out | ios::trunc);
1998-11-04 00:28:49 +01:00
os << "# include \"vvm.h\"" << endl;
os << "# include \"vvm_gates.h\"" << endl;
os << "# include \"vvm_func.h\"" << endl;
os << "# include \"vvm_calltf.h\"" << endl;
os << "# include \"vvm_thread.h\"" << endl;
os << "# include \"vpi_user.h\"" << endl;
os << "# include \"vpi_priv.h\"" << endl;
1998-11-04 00:28:49 +01:00
process_counter = 0;
string_counter = 1;
number_counter = 1;
os << "static struct __vpiStringConst string_table[];" << endl;
os << "static struct __vpiNumberConst number_table[];" << endl;
init_code << "static void design_init(vvm_simulation&sim)" << endl;
init_code << "{" << endl;
init_code << " vpip_init_simulation();"
<< endl;
1999-01-01 02:46:01 +01:00
start_code << "static void design_start(vvm_simulation&sim)" << endl;
start_code << "{" << endl;
1998-11-04 00:28:49 +01:00
}
1999-11-27 20:07:57 +01:00
void target_vvm::scope(ostream&os, const NetScope*scope)
{
string hname = mangle(scope->name()) + "_scope";
os << "// SCOPE: " << scope->name() << endl;
os << "static struct __vpiScope " << hname << ";" << endl;
string type_code;
switch (scope->type()) {
case NetScope::MODULE:
type_code = "vpiModule";
break;
case NetScope::BEGIN_END:
type_code = "vpiNamedBegin";
break;
case NetScope::FORK_JOIN:
type_code = "vpiNamedFork";
break;
}
init_code << " vpip_make_scope(&" << hname << ", " <<
type_code << ", \"" << scope->name() << "\");" << endl;
}
1998-11-04 00:28:49 +01:00
void target_vvm::end_design(ostream&os, const Design*mod)
{
os << "static struct __vpiStringConst string_table[" <<
string_counter+1 << "];" << endl;
os << "static struct __vpiNumberConst number_table[" <<
number_counter+1 << "];" << endl;
defn.close();
os << "// **** Definition code" << endl;
{ ifstream rdefn (defn_name);
os << rdefn.rdbuf();
}
unlink(defn_name);
free(defn_name);
defn_name = 0;
os << "// **** end definition code" << endl;
delayed.close();
os << "// **** Delayed code" << endl;
{ ifstream rdelayed (delayed_name);
os << rdelayed.rdbuf();
}
unlink(delayed_name);
free(delayed_name);
delayed_name = 0;
os << "// **** end delayed code" << endl;
os << "// **** init_code" << endl;
init_code << "}" << endl;
init_code.close();
{ ifstream rinit_code (init_code_name);
os << rinit_code.rdbuf();
}
unlink(init_code_name);
free(init_code_name);
init_code_name = 0;
os << "// **** end init_code" << endl;
1998-11-04 00:28:49 +01:00
os << "// **** start_code" << endl;
start_code << "}" << endl;
start_code.close();
{ ifstream rstart_code (start_code_name);
os << rstart_code.rdbuf();
}
unlink(start_code_name);
free(start_code_name);
start_code_name = 0;
os << "// **** end start_code" << endl;
1999-01-01 02:46:01 +01:00
1998-11-04 00:28:49 +01:00
os << "main()" << endl << "{" << endl;
string vpi_module_path = mod->get_flag("VPI_MODULE_PATH");
if (vpi_module_path.length() > 0)
os << " vvm_set_module_path(\"" << vpi_module_path <<
"\");" << endl;
os << " vvm_load_vpi_module(\"system.vpi\");" << endl;
1998-11-04 00:28:49 +01:00
os << " vvm_simulation sim;" << endl;
os << " design_init(sim);" << endl;
1999-01-01 02:46:01 +01:00
os << " design_start(sim);" << endl;
1998-11-04 00:28:49 +01:00
for (unsigned idx = 0 ; idx < process_counter ; idx += 1)
os << " thread" << (idx+1) << "_t thread_" <<
(idx+1) << "(&sim);" << endl;
os << " sim.run();" << endl;
os << "}" << endl;
}
1999-09-22 18:57:23 +02:00
bool target_vvm::process(ostream&os, const NetProcTop*top)
1999-07-17 05:39:11 +02:00
{
start_process(os, top);
1999-09-22 18:57:23 +02:00
bool rc = top->statement()->emit_proc(os, this);
1999-07-17 05:39:11 +02:00
end_process(os, top);
1999-09-22 18:57:23 +02:00
return rc;
1999-07-17 05:39:11 +02:00
}
1998-11-04 00:28:49 +01:00
void target_vvm::signal(ostream&os, const NetNet*sig)
{
string net_name = mangle(sig->name());
os << "static vvm_bitset_t<" << sig->pin_count() << "> " <<
net_name<< "_bits; /* " << sig->name() <<
" */" << endl;
os << "static vvm_signal_t<" << sig->pin_count() << "> " <<
net_name << "(&" << net_name << "_bits);" << endl;
init_code << " vpip_make_reg(&" << net_name <<
", \"" << sig->name() << "\");" << endl;
if (const NetScope*scope = sig->scope()) {
string sname = mangle(scope->name()) + "_scope";
init_code << " vpip_attach_to_scope(&" << sname
<< ", &" << net_name << ".base);" << endl;
}
/* Scan the signals of the vector, passing the initial value
to the inputs of all the connected devices. */
for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) {
if (sig->get_ival(idx) == verinum::Vz)
continue;
map<string,bool>written;
1999-07-10 05:00:05 +02:00
for (const NetObj::Link*lnk = sig->pin(idx).next_link()
; (*lnk) != sig->pin(idx) ; lnk = lnk->next_link()) {
if (lnk->get_dir() == NetObj::Link::OUTPUT)
continue;
// Check to see if the name has already been
// written to. This can happen if the object is a
// NetESignal, because there can be many of them
// with the same name.
if (written[lnk->get_obj()->name()])
continue;
written[lnk->get_obj()->name()] = true;
if (dynamic_cast<const NetNode*>(lnk->get_obj())) {
init_code << " " <<
mangle(lnk->get_obj()->name()) <<
".init_" << lnk->get_name() << "(" <<
lnk->get_inst() << ", V" <<
sig->get_ival(idx) << ");" << endl;
}
}
}
1998-11-04 00:28:49 +01:00
}
void target_vvm::memory(ostream&os, const NetMemory*mem)
{
1999-11-10 03:52:24 +01:00
const string mname = mangle(mem->name());
os << "static vvm_memory_t<" << mem->width() << ", " <<
mem->count() << "> " << mname << ";"
" /* " << mem->name() << " */" << endl;
init_code << " vpip_make_memory(&" << mname << ", \"" <<
mem->name() << "\", " << mem->width() << ", " <<
mem->count() << ");" << endl;
}
1999-07-07 06:20:57 +02:00
void target_vvm::task_def(ostream&os, const NetTaskDef*def)
{
thread_step_ = 0;
const string name = mangle(def->name());
const string save_thread_class = thread_class_;
thread_class_ = name;
1999-07-07 06:20:57 +02:00
os << "class " << name << " : public vvm_thread {" << endl;
os << " public:" << endl;
os << " " << name << "(vvm_simulation*sim, vvm_thread*th)" << endl;
1999-10-21 04:15:06 +02:00
os << " : vvm_thread(sim), back_(th), step_(&" << name << "::step_0_)" << endl;
1999-07-07 06:20:57 +02:00
os << " { }" << endl;
os << " ~" << name << "() { }" << endl;
os << " bool go() { return (this->*step_)(); }" << endl;
os << " private:" << endl;
os << " vvm_thread*back_;" << endl;
os << " bool (" << name << "::*step_)();" << endl;
1999-09-30 23:26:59 +02:00
os << " vvm_thread*callee_;" << endl;
os << " bool step_0_();" << endl;
defn << "bool " << thread_class_ << "::step_0_() {" << endl;
1999-07-07 06:20:57 +02:00
def->proc()->emit_proc(os, this);
defn << " sim_->thread_active(back_);" << endl;
defn << " return false;" << endl;
defn << "}" << endl;
os << "};" << endl;
thread_class_ = save_thread_class;
1999-07-07 06:20:57 +02:00
}
/*
* A function definition is emitted as a C++ function that takes no
* parameters and returns no result. The actual parameter passing
* happens in the function call, where the signals that are the inputs
* are assigned by the caller, the caller calls the function (which
* writes the result) then the caller copies the result out of the
* magic result register.
*/
void target_vvm::func_def(ostream&os, const NetFuncDef*def)
{
thread_step_ = 0;
const string name = mangle(def->name());
os << "// Function " << def->name() << endl;
os << "static void " << name << "(vvm_simulation*);" << endl;
defn << "// Function " << def->name() << endl;
defn << "static void " << name << "(vvm_simulation*sim_)" << endl;
defn << "{" << endl;
def->proc()->emit_proc(os, this);
defn << "}" << endl;
}
string target_vvm::defn_gate_outputfun_(ostream&os,
const NetNode*gate,
unsigned gpin)
{
const NetObj::Link&lnk = gate->pin(gpin);
ostrstream tmp;
tmp << mangle(gate->name()) << "_output_" << lnk.get_name() <<
"_" << lnk.get_inst() << ends;
string name = tmp.str();
os << "static void " << name << "(vvm_simulation*, vpip_bit_t);" << endl;
return name;
}
1998-11-04 00:28:49 +01:00
/*
* This method handles writing output functions for gates that have a
* single output (at pin 0). This writes the output_fun method into
* the delayed stream to be emitted to the output file later.
*/
void target_vvm::emit_gate_outputfun_(const NetNode*gate, unsigned gpin)
1998-11-04 00:28:49 +01:00
{
const NetObj::Link&lnk = gate->pin(gpin);
1998-11-04 00:28:49 +01:00
delayed << "static void " << mangle(gate->name()) <<
"_output_" << lnk.get_name() << "_" << lnk.get_inst() <<
"(vvm_simulation*sim, vpip_bit_t val)" <<
1998-11-04 00:28:49 +01:00
endl << "{" << endl;
/* The output function connects to gpin of the netlist part
1998-11-04 00:28:49 +01:00
and causes the inputs that it is connected to to be set
with the new value. */
const NetObj*cur;
unsigned pin;
gate->pin(gpin).next_link(cur, pin);
1998-11-04 00:28:49 +01:00
while (cur != gate) {
1999-11-29 00:59:22 +01:00
if (cur->pin(pin).get_name() != "") {
delayed << " " << mangle(cur->name()) << ".set_"
<< cur->pin(pin).get_name() << "(sim, " <<
cur->pin(pin).get_inst() << ", val);" << endl;
1998-11-04 00:28:49 +01:00
} else {
1998-11-04 00:28:49 +01:00
delayed << " " << mangle(cur->name()) << ".set(sim, "
<< pin << ", val);" << endl;
}
cur->pin(pin).next_link(cur, pin);
}
delayed << "}" << endl;
}
1999-09-04 03:57:15 +02:00
void target_vvm::lpm_add_sub(ostream&os, const NetAddSub*gate)
{
os << "static vvm_add_sub<" << gate->width() << "> " <<
mangle(gate->name()) << ";" << endl;
for (unsigned idx = 0 ; idx < gate->width() ; idx += 1) {
unsigned pin = gate->pin_Result(idx).get_pin();
string outfun = defn_gate_outputfun_(os, gate, pin);
init_code << " " << mangle(gate->name()) <<
".config_rout(" << idx << ", &" << outfun << ");" << endl;
emit_gate_outputfun_(gate, pin);
}
if (gate->attribute("LPM_Direction") == "ADD") {
init_code << " " << mangle(gate->name()) <<
".init_Add_Sub(0, V1);" << endl;
} else if (gate->attribute("LPM_Direction") == "SUB") {
init_code << " " << mangle(gate->name()) <<
".init_Add_Sub(0, V0);" << endl;
}
1999-09-04 03:57:15 +02:00
}
void target_vvm::lpm_clshift(ostream&os, const NetCLShift*gate)
{
os << "static vvm_clshift<" << gate->width() << "," <<
gate->width_dist() << "> " << mangle(gate->name()) << ";"
<< endl;
for (unsigned idx = 0 ; idx < gate->width() ; idx += 1) {
unsigned pin = gate->pin_Result(idx).get_pin();
string outfun = defn_gate_outputfun_(os, gate, pin);
init_code << " " << mangle(gate->name()) <<
".config_rout(" << idx << ", &" << outfun << ");" << endl;
emit_gate_outputfun_(gate, pin);
}
}
1999-11-15 00:43:45 +01:00
void target_vvm::lpm_compare(ostream&os, const NetCompare*gate)
{
os << "static vvm_compare<" << gate->width() << "> " <<
mangle(gate->name()) << ";" << endl;
1999-11-24 05:38:49 +01:00
if (gate->pin_ALB().is_linked()) {
unsigned pin = gate->pin_ALB().get_pin();
string outfun = defn_gate_outputfun_(os,gate,pin);
init_code << " " << mangle(gate->name()) <<
".config_ALB_out(&" << outfun << ");" << endl;
emit_gate_outputfun_(gate,pin);
}
1999-11-15 00:43:45 +01:00
if (gate->pin_ALEB().is_linked()) {
unsigned pin = gate->pin_ALEB().get_pin();
string outfun = defn_gate_outputfun_(os, gate, pin);
init_code << " " << mangle(gate->name()) <<
".config_ALEB_out(&" << outfun << ");" << endl;
emit_gate_outputfun_(gate, pin);
}
1999-11-24 05:38:49 +01:00
if (gate->pin_AGB().is_linked()) {
unsigned pin = gate->pin_AGB().get_pin();
string outfun = defn_gate_outputfun_(os,gate,pin);
init_code << " " << mangle(gate->name()) <<
".config_AGB_out(&" << outfun << ");" << endl;
emit_gate_outputfun_(gate,pin);
}
1999-11-15 00:43:45 +01:00
if (gate->pin_AGEB().is_linked()) {
unsigned pin = gate->pin_AGEB().get_pin();
string outfun = defn_gate_outputfun_(os, gate, pin);
init_code << " " << mangle(gate->name()) <<
".config_AGEB_out(&" << outfun << ");" << endl;
emit_gate_outputfun_(gate, pin);
}
}
void target_vvm::lpm_ff(ostream&os, const NetFF*gate)
{
string mname = mangle(gate->name());
os << "static vvm_ff<" << gate->width() << "> " << mname << ";"
<< endl;
for (unsigned idx = 0 ; idx < gate->width() ; idx += 1) {
unsigned pin = gate->pin_Q(idx).get_pin();
string outfun = defn_gate_outputfun_(os, gate, pin);
init_code << " " << mangle(gate->name()) <<
".config_rout(" << idx << ", &" << outfun << ");" << endl;
emit_gate_outputfun_(gate, pin);
}
}
1999-11-13 04:46:52 +01:00
void target_vvm::lpm_mux(ostream&os, const NetMux*mux)
{
string mname = mangle(mux->name());
os << "static vvm_mux<" << mux->width() << "," << mux->size() <<
"," << mux->sel_width() << "> " << mname << ";" << endl;
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) {
unsigned pin = mux->pin_Result(idx).get_pin();
string outfun = defn_gate_outputfun_(os, mux, pin);
init_code << " " << mangle(mux->name()) <<
".config_rout(" << idx << ", &" << outfun << ");" << endl;
emit_gate_outputfun_(mux, pin);
}
}
void target_vvm::lpm_ram_dq(ostream&os, const NetRamDq*ram)
{
string mname = mangle(ram->name());
os << "static vvm_ram_dq<" << ram->width() << "," <<
ram->awidth() << "," << ram->size() << "> " << mname <<
"(&" << mangle(ram->mem()->name()) << ");" << endl;
for (unsigned idx = 0 ; idx < ram->width() ; idx += 1) {
unsigned pin = ram->pin_Q(idx).get_pin();
string outfun = defn_gate_outputfun_(os, ram, pin);
init_code << " " << mangle(ram->name()) <<
".config_rout(" << idx << ", &" << outfun << ");" << endl;
emit_gate_outputfun_(ram, pin);
}
}
1998-11-04 00:28:49 +01:00
void target_vvm::logic(ostream&os, const NetLogic*gate)
{
string outfun = defn_gate_outputfun_(os, gate, 0);
1998-11-04 00:28:49 +01:00
switch (gate->type()) {
case NetLogic::AND:
os << "static vvm_and" << "<" << gate->pin_count()-1 <<
"," << gate->rise_time() << "> ";
1998-11-04 00:28:49 +01:00
break;
case NetLogic::BUF:
os << "static vvm_buf<" << gate->rise_time() << "> ";
break;
1999-02-15 03:06:15 +01:00
case NetLogic::BUFIF0:
os << "static vvm_bufif0<" << gate->rise_time() << "> ";
1999-02-15 03:06:15 +01:00
break;
case NetLogic::BUFIF1:
os << "static vvm_bufif1<" << gate->rise_time() << "> ";
1999-02-15 03:06:15 +01:00
break;
1998-11-04 00:28:49 +01:00
case NetLogic::NAND:
os << "static vvm_nand" << "<" << gate->pin_count()-1 <<
"," << gate->rise_time() << "> ";
1998-11-04 00:28:49 +01:00
break;
case NetLogic::NOR:
os << "static vvm_nor" << "<" << gate->pin_count()-1 <<
"," << gate->rise_time() << "> ";
1998-11-04 00:28:49 +01:00
break;
case NetLogic::NOT:
os << "static vvm_not" << "<" << gate->rise_time() << "> ";
1998-11-04 00:28:49 +01:00
break;
case NetLogic::OR:
os << "static vvm_or" << "<" << gate->pin_count()-1 <<
"," << gate->rise_time() << "> ";
1998-11-04 00:28:49 +01:00
break;
case NetLogic::XNOR:
os << "static vvm_xnor" << "<" << gate->pin_count()-1 <<
"," << gate->rise_time() << "> ";
break;
1998-11-04 00:28:49 +01:00
case NetLogic::XOR:
os << "static vvm_xor" << "<" << gate->pin_count()-1 <<
"," << gate->rise_time() << "> ";
1998-11-04 00:28:49 +01:00
break;
default:
os << "#error \"internal ivl error:bad gate type for " <<
gate->name() << "\"" << endl;
1998-11-04 00:28:49 +01:00
}
os << mangle(gate->name()) << "(&" << outfun << ");" << endl;
1998-11-04 00:28:49 +01:00
emit_gate_outputfun_(gate, 0);
1999-01-01 02:46:01 +01:00
start_code << " " << mangle(gate->name()) <<
".start(&sim);" << endl;
1998-11-04 00:28:49 +01:00
}
void target_vvm::bufz(ostream&os, const NetBUFZ*gate)
{
string outfun = defn_gate_outputfun_(os, gate, 0);
1998-11-04 00:28:49 +01:00
os << "static vvm_bufz " << mangle(gate->name()) << "(&" <<
outfun << ");" << endl;
1998-11-04 00:28:49 +01:00
emit_gate_outputfun_(gate, 0);
1998-11-04 00:28:49 +01:00
}
static string state_to_string(unsigned state, unsigned npins)
{
static const char cur_table[3] = { '0', '1', 'x' };
string res = "";
for (unsigned idx = 0 ; idx < npins ; idx += 1) {
char cur = cur_table[state%3];
res = cur + res;
state /= 3;
}
return res;
}
void target_vvm::udp(ostream&os, const NetUDP*gate)
{
assert(gate->pin_count() <= 9);
assert(gate->is_sequential());
unsigned states = 1;
for (unsigned idx = 0 ; idx < gate->pin_count() ; idx += 1)
states *= 3;
os << "static vvm_u32 " << mangle(gate->name()) << "_table[" <<
states << "] = {" << endl;
os << hex;
for (unsigned state = 0 ; state < states ; state += 1) {
string sstr = state_to_string(state, gate->pin_count());
unsigned long entry = 0;
for (unsigned idx = 1 ; idx < sstr.length() ; idx += 1) {
char o[2];
switch (sstr[idx]) {
case '0':
o[0] = '1';
o[1] = 'x';
break;
case '1':
o[0] = '0';
o[1] = 'x';
break;
case 'x':
o[0] = '0';
o[1] = '1';
break;
}
o[0] = gate->table_lookup(sstr, o[0], idx);
o[1] = gate->table_lookup(sstr, o[1], idx);
entry <<= 2;
entry |= (o[0] == '0')? 0 : (o[0] == '1')? 1 : 2;
entry <<= 2;
entry |= (o[1] == '0')? 0 : (o[1] == '1')? 1 : 2;
}
os << " 0x" << setw(8) << setfill('0') << entry << ",";
if (state % 3 == 2)
os << endl;
}
os << "};" << dec << setfill(' ') << endl;
string outfun = defn_gate_outputfun_(os, gate, 0);
os << "static vvm_udp_ssequ<" << gate->pin_count()-1 << "> " <<
mangle(gate->name()) << "(&" << outfun << ", V" <<
gate->get_initial() << ", " << mangle(gate->name()) <<
"_table);" << endl;
/* The UDP output function is much like other logic gates. Use
this general method to output the output function. */
emit_gate_outputfun_(gate, 0);
}
/*
* The non-blocking assignment works by creating an event to do the
* assignment at the right time. The value to be assigned is saved in
* the event and the event function performs the actual assignment.
*
* The net part of the assign generates a type ot represent the
* assignment. Creating instances of this event will be dealt with
* later.
*/
void target_vvm::net_assign_nb(ostream&os, const NetAssignNB*net)
{
const string name = mangle(net->name());
unsigned iwid = net->pin_count();
os << "class " << name << " : public vvm_event {" << endl;
os << " public:" << endl;
if (net->bmux()) {
os << " " << name << "(vvm_simulation*s, const vvm_bitset_t<"
<< iwid << ">&v, unsigned idx)" << endl;
os << " : sim_(s), value_(v), idx_(idx) { }" << endl;
} else {
os << " " << name << "(vvm_simulation*s, const vvm_bits_t&v)"
<< endl;
os << " : sim_(s), value_(v) { }" << endl;
}
os << " void event_function();" << endl;
os << " private:" << endl;
os << " vvm_simulation*sim_;" << endl;
os << " vvm_bitset_t<" << iwid << ">value_;" << endl;
if (net->bmux())
os << " unsigned idx_;" << endl;
os << "};" << endl;
/* Write the event_function to do the actual assignment. */
delayed << "void " << name << "::event_function()" << endl;
delayed << "{" << endl;
if (net->bmux()) {
/* If the assignment is to a single bit (with a mux)
then write a switch statement that selects which pins
to write to. */
delayed << " switch (idx_) {" << endl;
for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) {
const NetObj*cur;
unsigned pin;
delayed << " case " << idx << ":" << endl;
for (net->pin(idx).next_link(cur, pin)
; net->pin(idx) != cur->pin(pin)
; cur->pin(pin).next_link(cur, pin)) {
const NetObj::Link&lnk = cur->pin(pin);
// Skip output only pins.
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
continue;
delayed << " " << mangle(cur->name())
<< ".set_" << lnk.get_name() << "sim_, "
<< lnk.get_inst() << ", value_[0]);" << endl;
}
delayed << " break;" << endl;
}
delayed << " }" << endl;
} else {
/* If there is no BMUX, then write all the bits of the
value to all the pins. */
for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) {
const NetObj*cur;
unsigned pin;
for (net->pin(idx).next_link(cur, pin)
; net->pin(idx) != cur->pin(pin)
; cur->pin(pin).next_link(cur, pin)) {
const NetObj::Link&lnk = cur->pin(pin);
// Skip output only pins.
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
continue;
delayed << " " << mangle(cur->name()) <<
".set_" << lnk.get_name() << "(sim_, "
<< lnk.get_inst() << ", value_[" <<
idx << "]);" << endl;
}
}
}
delayed << "}" << endl;
}
1999-10-10 03:59:54 +02:00
void target_vvm::net_case_cmp(ostream&os, const NetCaseCmp*gate)
{
os << "static void " << mangle(gate->name()) <<
"_output_fun(vvm_simulation*, vpip_bit_t);" << endl;
1999-10-10 03:59:54 +02:00
assert(gate->pin_count() == 3);
os << "static vvm_eeq" << "<" << gate->rise_time() << "> "
<< mangle(gate->name()) << "(&" << mangle(gate->name()) <<
"_output_fun);" << endl;
emit_gate_outputfun_(gate, 0);
1999-10-10 03:59:54 +02:00
start_code << " " << mangle(gate->name()) <<
".start(&sim);" << endl;
}
/*
* The NetConst is a synthetic device created to represent constant
* values. I represent them in the output as a vvm_bufz object that
* has its input connected to nothing but is initialized to the
* desired constant value.
*/
void target_vvm::net_const(ostream&os, const NetConst*gate)
{
string outfun = defn_gate_outputfun_(os, gate, 0);
os << "static vvm_bufz " << mangle(gate->name()) << "(&" <<
outfun << ");" << endl;
init_code << " " << mangle(gate->name()) << ".set(&sim, 1, ";
switch (gate->value()) {
case verinum::V0:
init_code << "V0";
break;
case verinum::V1:
init_code << "V1";
break;
case verinum::Vx:
init_code << "Vx";
break;
case verinum::Vz:
init_code << "Vz";
break;
}
init_code << ");" << endl;
emit_gate_outputfun_(gate, 0);
}
1998-11-04 00:28:49 +01:00
/*
* The net_event device is a synthetic device type--a fabrication of
1998-11-04 00:28:49 +01:00
* the elaboration phase. An event device receives value changes from
* the attached signal. It is an input only device, its only value
* being the side-effects that threads waiting on events can be
* awakened.
*
* The proc_event method handles the other half of this, the process
* that blocks on the event.
*/
void target_vvm::net_event(ostream&os, const NetNEvent*gate)
1998-11-04 00:28:49 +01:00
{
string pevent = mangle(gate->fore_ptr()->name());
os << " /* " << gate->name() << " */" << endl;
bool&printed = pevent_printed_flag[pevent];
if (! printed) {
printed = true;
os << "static vvm_sync " << pevent << ";" << endl;
}
os << "static vvm_pevent<" << gate->pin_count() << "> " <<
mangle(gate->name()) << "(&" << pevent << ", ";
switch (gate->type()) {
case NetNEvent::POSEDGE:
case NetNEvent::POSITIVE:
os << "vvm_pevent<" << gate->pin_count() << ">::POSEDGE";
break;
case NetNEvent::NEGEDGE:
os << "vvm_pevent<"<< gate->pin_count() << ">::NEGEDGE";
break;
case NetNEvent::ANYEDGE:
os << "vvm_pevent<" << gate->pin_count() << ">::ANYEDGE";
break;
}
os << ");" << endl;
1998-11-04 00:28:49 +01:00
}
void target_vvm::start_process(ostream&os, const NetProcTop*proc)
{
process_counter += 1;
{ ostrstream ts;
ts << "thread" << process_counter << "_t" << ends;
thread_class_ = ts.str();
}
1998-11-04 00:28:49 +01:00
thread_step_ = 0;
os << "class " << thread_class_ << " : public vvm_thread {" << endl;
1998-11-04 00:28:49 +01:00
os << " public:" << endl;
os << " " << thread_class_ << "(vvm_simulation*sim)" << endl;
os << " : vvm_thread(sim), step_(&" << thread_class_ <<
"::step_0_)" << endl;
1998-11-04 00:28:49 +01:00
os << " { }" << endl;
os << " ~" << thread_class_ << "() { }" << endl;
1998-11-04 00:28:49 +01:00
os << endl;
os << " bool go() { return (this->*step_)(); }" << endl;
1998-11-04 00:28:49 +01:00
os << " private:" << endl;
os << " bool (" << thread_class_ << "::*step_)();" << endl;
1999-07-07 06:20:57 +02:00
os << " vvm_thread*callee_;" << endl;
os << " bool step_0_();" << endl;
defn << "bool " << thread_class_ << "::step_0_() {" << endl;
1998-11-04 00:28:49 +01:00
}
/*
* This method generates code for a procedural assignment. The lval is
* a signal, but the assignment should generate code to go to all the
* connected devices/events.
*/
void target_vvm::proc_assign(ostream&os, const NetAssign*net)
{
string rval = emit_proc_rval(defn, 8, net->rval());
1998-11-04 00:28:49 +01:00
defn << " // " << net->get_line() << ": " << endl;
if (net->bmux()) {
1998-11-04 00:28:49 +01:00
// This is a bit select. Assign the low bit of the rval
// to the selected bit of the lval.
string bval = emit_proc_rval(defn, 8, net->bmux());
defn << " switch (" << bval << ".as_unsigned()) {" << endl;
1998-11-04 00:28:49 +01:00
for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) {
const NetObj*cur;
unsigned pin;
map<string,bool> written;
1998-11-04 00:28:49 +01:00
defn << " case " << idx << ":" << endl;
for (net->pin(idx).next_link(cur, pin)
; net->pin(idx) != cur->pin(pin)
; cur->pin(pin).next_link(cur, pin)) {
// Skip output only pins.
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
continue;
// It is possible for a named device to show up
// several times in a link. This is the classic
// case with NetESignal objects, which are
// repeated for each expression that uses it.
if (written[cur->name()])
continue;
written[cur->name()] = true;
defn << " " << mangle(cur->name()) <<
".set_" << cur->pin(pin).get_name() <<
"(sim_, " << cur->pin(pin).get_inst() <<
", " << rval << "[0]);" << endl;
}
defn << " break;" << endl;
}
defn << " }" << endl;
} else {
/* Not only is the lvalue signal assigned to, send the
bits to all the other pins that are connected to this
signal. */
for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) {
const NetObj*cur;
unsigned pin;
map<string,bool> written;
for (net->pin(idx).next_link(cur, pin)
; net->pin(idx) != cur->pin(pin)
; cur->pin(pin).next_link(cur, pin)) {
// Skip output only pins.
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
continue;
// It is possible for a named device to show up
// several times in a link. This is the classic
// case with NetESignal objects, which are
// repeated for each expression that uses it.
if (written[cur->name()])
continue;
written[cur->name()] = true;
defn << " " << mangle(cur->name()) <<
".set_" << cur->pin(pin).get_name() <<
"(sim_, " << cur->pin(pin).get_inst() <<
", " << rval << "[" << idx << "]);" << endl;
}
1998-11-04 00:28:49 +01:00
}
}
}
/*
* Generate an assignment to a memory location. This causes the index
* expression to be evaluated (run-time) and the index used as the
* location to store the value.
*/
void target_vvm::proc_assign_mem(ostream&os, const NetAssignMem*amem)
{
string index = emit_proc_rval(defn, 8, amem->index());
string rval = emit_proc_rval(defn, 8, amem->rval());
const NetMemory*mem = amem->memory();
defn << " /* " << amem->get_line() << " */" << endl;
if (mem->width() == amem->rval()->expr_width()) {
defn << " " << mangle(mem->name()) <<
".set_word(sim_, " << index << ".as_unsigned(), " <<
rval << ");" << endl;
} else {
assert(mem->width() <= amem->rval()->expr_width());
string tmp = make_temp();
defn << " vvm_bitset_t<" << mem->width() << ">" <<
tmp << ";" << endl;
for (unsigned idx = 0 ; idx < mem->width() ; idx += 1)
defn << " " << tmp << "[" << idx << "] = " <<
rval << "[" << idx << "];" << endl;
defn << " " << mangle(mem->name()) << ".set_word(sim_, "
<< index << ".as_unsigned(), " << tmp << ");" << endl;
}
}
void target_vvm::proc_assign_nb(ostream&os, const NetAssignNB*net)
{
string rval = emit_proc_rval(defn, 8, net->rval());
if (net->bmux()) {
string bval = emit_proc_rval(defn, 8, net->bmux());
defn << " sim_->insert_event(" << net->rise_time()
<< ", new " << mangle(net->name()) << "(sim_, " << rval
<< ", " << bval << ".as_unsigned()));" << endl;
} else {
defn << " sim_->insert_event(" << net->rise_time()
<< ", new " << mangle(net->name()) << "(sim_, " << rval
<< "));" << endl;
}
}
1999-09-22 18:57:23 +02:00
bool target_vvm::proc_block(ostream&os, const NetBlock*net)
1998-11-04 00:28:49 +01:00
{
if (net->type() == NetBlock::PARA) {
1999-09-22 18:57:23 +02:00
cerr << "sorry: vvm cannot emit parallel blocks." << endl;
return false;
}
1998-11-04 00:28:49 +01:00
net->emit_recurse(os, this);
1999-09-22 18:57:23 +02:00
return true;
1998-11-04 00:28:49 +01:00
}
/*
* The code for a case statement introduces basic blocks so causes
* steps to be created. There is a step for each case, and the
* out. For example:
*
* case (foo)
* 1 : X;
* 2 : Y;
* endcase
* Z;
*
* causes code for Z to be generated, and also code for X and Y that
* each branch to Z when they finish. X, Y and Z all generate at least
* one step.
*/
void target_vvm::proc_case(ostream&os, const NetCase*net)
{
1999-09-29 20:36:02 +02:00
string test_func = "";
switch (net->type()) {
case NetCase::EQ:
test_func = "vvm_binop_eeq";
break;
case NetCase::EQX:
test_func = "vvm_binop_xeq";
break;
case NetCase::EQZ:
test_func = "vvm_binop_zeq";
break;
}
defn << " /* case (" << *net->expr() << ") */" << endl;
string expr = emit_proc_rval(defn, 8, net->expr());
1999-02-22 04:01:12 +01:00
unsigned exit_step = thread_step_ + 1;
thread_step_ += 1;
1999-02-22 04:01:12 +01:00
unsigned default_idx = net->nitems();
/* This iteration generates all the comparisons with the case
expression. If a comparison matches, the next step is set
and return true branches to that step. */
for (unsigned idx = 0 ; idx < net->nitems() ; idx += 1) {
1999-02-22 04:01:12 +01:00
if (net->expr(idx) == 0) {
assert(default_idx == net->nitems());
default_idx = idx;
continue;
}
assert(net->expr(idx));
thread_step_ += 1;
defn << " /* " << *net->expr(idx) << " */" << endl;
string guard = emit_proc_rval(defn, 8, net->expr(idx));
defn << " if (V1 == " << test_func << "(" << guard << ","
1999-09-29 20:36:02 +02:00
<< expr << ")[0]) {" << endl;
defn << " step_ = &" << thread_class_ <<
"::step_" << thread_step_ << "_;" << endl;
defn << " return true;" << endl;
defn << " }" << endl;
}
/* If none of the above tests pass, then branch to the default
step (or the exit step if there is no default.) */
if (default_idx < net->nitems()) {
thread_step_ += 1;
defn << " /* default : */" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
thread_step_ << "_;" << endl;
} else {
defn << " /* no default ... fall out of case. */" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
exit_step << "_;" << endl;
}
defn << " return true;" << endl;
defn << "}" << endl;
/* Run through the cases again, this time generating the case
steps. Note that I already know which item is the default,
so I just assert that this iteration agrees. */
unsigned step_num = exit_step;
for (unsigned idx = 0 ; idx < net->nitems() ; idx += 1) {
if (net->expr(idx) == 0) {
assert(default_idx == idx);
continue;
}
assert(net->expr(idx));
step_num += 1;
os << " bool step_" << step_num << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << step_num
<< "_() {" << endl;
1999-09-08 04:24:39 +02:00
if (net->stat(idx))
net->stat(idx)->emit_proc(os, this);
defn << " step_ = &" << thread_class_ << "::step_" <<
exit_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
}
/* If there is a default case, generate the default step. */
1999-02-22 04:01:12 +01:00
if (default_idx < net->nitems()) {
step_num += 1;
1999-02-22 04:01:12 +01:00
os << " bool step_" << step_num << "_();" << endl;
1999-02-22 04:01:12 +01:00
defn << "bool " << thread_class_ << "::step_" << step_num
<< "_() {" << endl;
1999-09-08 04:24:39 +02:00
if (net->stat(default_idx))
net->stat(default_idx)->emit_proc(os, this);
defn << " step_ = &" << thread_class_ << "::step_" <<
exit_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
1999-02-22 04:01:12 +01:00
}
/* Finally, start the exit step. */
os << " bool step_" << exit_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << exit_step <<
"_() {" << endl;
}
void target_vvm::proc_condit(ostream&os, const NetCondit*net)
{
string expr = emit_proc_rval(defn, 8, net->expr());
unsigned if_step = ++thread_step_;
unsigned else_step = ++thread_step_;
unsigned out_step = ++thread_step_;
defn << " if (" << expr << "[0] == V1)" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
if_step << "_;" << endl;
defn << " else" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
else_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << if_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << if_step <<
"_() {" << endl;
net->emit_recurse_if(os, this);
defn << " step_ = &" << thread_class_ << "::step_" <<
out_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << else_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << else_step <<
"_() {" << endl;
net->emit_recurse_else(os, this);
defn << " step_ = &" << thread_class_ << "::step_" <<
out_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << out_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << out_step <<
"_() {" << endl;
}
/*
* The forever loop is implemented by starting a basic block, handing
* the statement, and putting in a goto to the beginning of the block.
*/
void target_vvm::proc_forever(ostream&os, const NetForever*net)
{
unsigned top_step = ++thread_step_;
unsigned out_step = ++thread_step_;
defn << " step_ = &" << thread_class_ << "::step_" <<
top_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << top_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << top_step <<
"_() {" << endl;
net->emit_recurse(os, this);
defn << " step_ = &" << thread_class_ << "::step_" <<
top_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << out_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << out_step <<
"_() {" << endl;
}
void target_vvm::proc_repeat(ostream&os, const NetRepeat*net)
{
string expr = emit_proc_rval(defn, 8, net->expr());
unsigned top_step = ++thread_step_;
unsigned out_step = ++thread_step_;
defn << " step_" << top_step << "_idx_ = " << expr <<
".as_unsigned();" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
top_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " unsigned step_" << top_step << "_idx_;" << endl;
os << " bool step_" << top_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << top_step <<
"_() {" << endl;
defn << " if (step_" << top_step << "_idx_ == 0) {" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
out_step << "_;" << endl;
defn << " return true;" << endl;
defn << " }" << endl;
defn << " step_" << top_step << "_idx_ -= 1;" << endl;
net->emit_recurse(os,this);
defn << " step_ = &" << thread_class_ << "::step_" <<
top_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << out_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << out_step <<
"_() {" << endl;
}
1999-07-03 04:12:51 +02:00
/*
* Calls to system tasks are done here. We know that this is a system
* task and that I need to generate an external call. Calls to user
* defined tasks are handled elsewhere.
*/
void target_vvm::proc_stask(ostream&os, const NetSTask*net)
1998-11-04 00:28:49 +01:00
{
1999-07-03 04:12:51 +02:00
string ptmp = make_temp();
defn << " vpiHandle " << ptmp << "[" << net->nparms() <<
"];" << endl;
for (unsigned idx = 0 ; idx < net->nparms() ; idx += 1) {
string val;
if (net->parm(idx)) {
val = emit_parm_rval(os, this, net->parm(idx));
} else {
val = string("&vpip_null.base");
1999-07-03 04:12:51 +02:00
}
defn << " " << ptmp << "[" << idx << "] = " << val << ";"
<< endl;
}
defn << " vpip_calltask(\"" << net->name() << "\", " <<
1999-07-03 04:12:51 +02:00
net->nparms() << ", " << ptmp << ");" << endl;
defn << " if (vpip_finished()) return false;" << endl;
1998-11-04 00:28:49 +01:00
}
1999-07-07 06:20:57 +02:00
void target_vvm::proc_utask(ostream&os, const NetUTask*net)
{
unsigned out_step = ++thread_step_;
const string name = mangle(net->name());
defn << " callee_ = new " << name << "(sim_, this);" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
out_step << "_;" << endl;
defn << " return false;" << endl;
defn << "}" << endl;
os << " bool step_" << out_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << out_step <<
"_() {" << endl;
defn << " delete callee_;" << endl;
1999-07-07 06:20:57 +02:00
}
/*
* The while loop is implemented by making each iteration one [or
* more] basic block and letting the loop condition skip to the block
* after or continue with the current block. This is similar to how
* the condit is handled. The basic structure of the loop is as follows:
*
* head_step:
* evaluate condition
* if false, go to out_step
* execute body
*
* out_step:
*/
void target_vvm::proc_while(ostream&os, const NetWhile*net)
{
unsigned head_step = ++thread_step_;
unsigned out_step = ++thread_step_;
defn << " step_ = &" << thread_class_ << "::step_" <<
head_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << head_step << "_();" << endl;
defn << "// " << net->expr()->get_line() <<
": top of while condition." << endl;
defn << "bool " << thread_class_ << "::step_" << head_step <<
"_() {" << endl;
string expr = emit_proc_rval(defn, 8, net->expr());
defn << "// " << net->expr()->get_line() <<
": test while condition." << endl;
defn << " if (" << expr << "[0] != V1) {" << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
out_step << "_;" << endl;
defn << " return true;" << endl;
defn << " }" << endl;
net->emit_proc_recurse(os, this);
defn << "// " << net->expr()->get_line() <<
": end of while loop." << endl;
defn << " step_ = &" << thread_class_ << "::step_" <<
head_step << "_;" << endl;
defn << " return true;" << endl;
defn << "}" << endl;
os << " bool step_" << out_step << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << out_step <<
"_() {" << endl;
}
1998-11-04 00:28:49 +01:00
/*
* Within a process, the proc_event is a statement that is blocked
* until the event is signalled.
*/
void target_vvm::proc_event(ostream&os, const NetPEvent*proc)
{
thread_step_ += 1;
defn << " step_ = &" << thread_class_ << "::step_" <<
thread_step_ << "_;" << endl;
/* POSITIVE is for the wait construct, and needs to be handled
specially. The structure of the generated code is:
if (event.get()==V1) {
return true;
} else {
event.wait(this);
return false;
}
This causes the wait to not even block the thread if the
event value is already positive, otherwise wait for a
rising edge. All the edge triggers look like this:
event.wait(vvm_pevent::POSEDGE, this);
return false;
POSEDGE is replaced with the correct type for the desired
edge. */
svector<const NetNEvent*>*list = proc->back_list();
if ((list->count()==1) && ((*list)[0]->type() == NetNEvent::POSITIVE)) {
defn << " if (" << mangle((*list)[0]->name()) <<
".get()[0]==V1) {" << endl;
defn << " return true;" << endl;
defn << " } else {" << endl;
defn << " " << mangle(proc->name()) <<
".wait(this);" << endl;
defn << " return false;" << endl;
defn << " }" << endl;
} else {
/* The canonical wait for an edge puts the thread into
the correct wait object, then returns false from the
thread to suspend execution. When things are ready to
proceed, the correct vvm_pevent will send a wakeup to
start the next basic block. */
defn << " " << mangle(proc->name()) << ".wait(this);" << endl;
defn << " return false;" << endl;
1998-11-04 00:28:49 +01:00
}
defn << "}" << endl;
os << " bool step_" << thread_step_ << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << thread_step_ <<
"_() {" << endl;
1998-11-04 00:28:49 +01:00
proc->emit_proc_recurse(os, this);
delete list;
1998-11-04 00:28:49 +01:00
}
/*
* A delay suspends the thread for a period of time.
*/
void target_vvm::proc_delay(ostream&os, const NetPDelay*proc)
{
thread_step_ += 1;
defn << " step_ = &" << thread_class_ << "::step_" <<
thread_step_ << "_;" << endl;
defn << " sim_->thread_delay(" << proc->delay() << ", this);"
1998-11-04 00:28:49 +01:00
<< endl;
defn << " return false;" << endl;
defn << "}" << endl;
os << " bool step_" << thread_step_ << "_();" << endl;
defn << "bool " << thread_class_ << "::step_" << thread_step_ <<
"_() {" << endl;
1998-11-04 00:28:49 +01:00
proc->emit_proc_recurse(os, this);
}
void target_vvm::end_process(ostream&os, const NetProcTop*proc)
{
if (proc->type() == NetProcTop::KALWAYS) {
defn << " step_ = &" << thread_class_ << "::step_0_;"
<< endl;
defn << " return true;" << endl;
1998-11-04 00:28:49 +01:00
} else {
defn << " step_ = 0;" << endl;
defn << " return false;" << endl;
1998-11-04 00:28:49 +01:00
}
defn << "}" << endl;
1998-11-04 00:28:49 +01:00
os << "};" << endl;
}
static target_vvm target_vvm_obj;
extern const struct target tgt_vvm = {
"vvm",
&target_vvm_obj
};
/*
* $Log: t-vvm.cc,v $
1999-11-29 00:59:22 +01:00
* Revision 1.85 1999/11/28 23:59:22 steve
* Remove useless tests for NetESignal.
*
* Revision 1.84 1999/11/28 23:42:03 steve
* NetESignal object no longer need to be NetNode
* objects. Let them keep a pointer to NetNet objects.
*
* Revision 1.83 1999/11/28 18:05:37 steve
* Set VPI_MODULE_PATH in the target code, if desired.
*
* Revision 1.82 1999/11/28 01:16:19 steve
* gate outputs need to set signal values.
*
* Revision 1.81 1999/11/28 00:56:08 steve
* Build up the lists in the scope of a module,
* and get $dumpvars to scan the scope for items.
*
1999-11-27 20:07:57 +01:00
* Revision 1.80 1999/11/27 19:07:58 steve
* Support the creation of scopes.
*
1999-11-24 05:38:49 +01:00
* Revision 1.79 1999/11/24 04:38:49 steve
* LT and GT fixes from Eric Aardoom.
*
* Revision 1.78 1999/11/21 01:16:51 steve
* Fix coding errors handling names of logic devices,
* and add support for buf device in vvm.
*
* Revision 1.77 1999/11/21 00:13:09 steve
* Support memories in continuous assignments.
*
1999-11-15 00:43:45 +01:00
* Revision 1.76 1999/11/14 23:43:45 steve
* Support combinatorial comparators.
*
* Revision 1.75 1999/11/14 20:24:28 steve
* Add support for the LPM_CLSHIFT device.
*
1999-11-13 04:46:52 +01:00
* Revision 1.74 1999/11/13 03:46:52 steve
* Support the LPM_MUX in vvm.
*
1999-11-10 03:52:24 +01:00
* Revision 1.73 1999/11/10 02:52:24 steve
* Create the vpiMemory handle type.
*
* Revision 1.72 1999/11/06 16:00:17 steve
* Put number constants into a static table.
*
* Revision 1.71 1999/11/01 02:07:41 steve
* Add the synth functor to do generic synthesis
* and add the LPM_FF device to handle rows of
* flip-flops.
*
* Revision 1.70 1999/10/31 20:08:24 steve
* Include subtraction in LPM_ADD_SUB device.
*
* Revision 1.69 1999/10/31 04:11:28 steve
* Add to netlist links pin name and instance number,
* and arrange in vvm for pin connections by name
* and instance number.
*
* Revision 1.68 1999/10/28 21:51:21 steve
* gate output pins use vpip_bit_t (Eric Aardoom)
*
* Revision 1.67 1999/10/28 21:36:00 steve
* Get rid of monitor_t and fold __vpiSignal into signal.
*
* Revision 1.66 1999/10/28 04:48:29 steve
* Put strings into a single string table.
*
* Revision 1.65 1999/10/28 00:47:24 steve
* Rewrite vvm VPI support to make objects more
* persistent, rewrite the simulation scheduler
* in C (to interface with VPI) and add VPI support
* for callbacks.
*
* Revision 1.64 1999/10/23 16:27:53 steve
* assignment to bit select is aa single bit.
*
1999-10-21 04:15:06 +02:00
* Revision 1.63 1999/10/21 02:15:06 steve
* Make generated code ISO legal.
*
1999-10-10 03:59:54 +02:00
* Revision 1.62 1999/10/10 01:59:55 steve
* Structural case equals device.
*
1999-10-08 04:00:48 +02:00
* Revision 1.61 1999/10/08 02:00:48 steve
* vvm supports unary | operator.
*
* Revision 1.60 1999/10/07 05:25:34 steve
* Add non-const bit select in l-value of assignment.
*
* Revision 1.59 1999/10/06 01:28:18 steve
* The $finish task should work immediately.
*
1999-10-05 08:19:46 +02:00
* Revision 1.58 1999/10/05 06:19:46 steve
* Add support for reduction NOR.
*
* Revision 1.57 1999/10/05 04:02:10 steve
* Relaxed width handling for <= assignment.
*
* Revision 1.56 1999/10/01 15:26:28 steve
* Add some vvm operators from Eric Aardoom.
*
* Revision 1.55 1999/10/01 03:58:37 steve
* More resilient assignment to memory location.
1998-11-04 00:28:49 +01:00
*/