1998-11-04 00:28:49 +01:00
|
|
|
/*
|
1999-05-12 06:03:19 +02: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)
|
2000-02-13 20:59:33 +01:00
|
|
|
#ident "$Id: t-vvm.cc,v 1.100 2000/02/13 19:59:33 steve Exp $"
|
1998-11-04 00:28:49 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
# include <iostream>
|
1999-09-29 01:45:09 +02:00
|
|
|
# include <fstream>
|
1998-11-04 00:28:49 +01:00
|
|
|
# include <strstream>
|
1998-11-09 19:55:33 +01:00
|
|
|
# include <iomanip>
|
1998-11-04 00:28:49 +01:00
|
|
|
# include <string>
|
|
|
|
|
# include <typeinfo>
|
1999-09-29 01:45:09 +02:00
|
|
|
# include <unistd.h>
|
1998-11-04 00:28:49 +01:00
|
|
|
# include "netlist.h"
|
|
|
|
|
# include "target.h"
|
|
|
|
|
|
1999-11-06 17:00:17 +01:00
|
|
|
// 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 {
|
1999-10-28 02:47:24 +02:00
|
|
|
|
|
|
|
|
friend class vvm_parm_rval;
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
public:
|
1999-09-29 01:45:09 +02:00
|
|
|
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*);
|
1999-04-19 03:59:36 +02:00
|
|
|
virtual void memory(ostream&os, const NetMemory*);
|
1999-07-07 06:20:57 +02:00
|
|
|
virtual void task_def(ostream&os, const NetTaskDef*);
|
1999-09-01 00:38:29 +02:00
|
|
|
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*);
|
1999-11-14 21:24:28 +01:00
|
|
|
virtual void lpm_clshift(ostream&os, const NetCLShift*);
|
1999-11-15 00:43:45 +01:00
|
|
|
virtual void lpm_compare(ostream&os, const NetCompare*);
|
1999-11-01 03:07:40 +01:00
|
|
|
virtual void lpm_ff(ostream&os, const NetFF*);
|
2000-01-13 04:35:35 +01:00
|
|
|
virtual void lpm_mult(ostream&os, const NetMult*);
|
1999-11-13 04:46:52 +01:00
|
|
|
virtual void lpm_mux(ostream&os, const NetMux*);
|
1999-11-21 01:13:08 +01:00
|
|
|
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*);
|
1998-12-18 00:54:58 +01:00
|
|
|
virtual void udp(ostream&os, const NetUDP*);
|
1999-06-07 04:23:31 +02:00
|
|
|
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*);
|
1998-11-09 19:55:33 +01:00
|
|
|
virtual void net_const(ostream&os, const NetConst*);
|
1999-05-01 04:57:52 +02:00
|
|
|
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*);
|
1999-05-12 06:03:19 +02:00
|
|
|
virtual void proc_assign_mem(ostream&os, const NetAssignMem*);
|
1999-06-07 04:23:31 +02:00
|
|
|
virtual void proc_assign_nb(ostream&os, const NetAssignNB*);
|
2000-01-08 04:09:14 +01:00
|
|
|
virtual void proc_assign_mem_nb(ostream&os, const NetAssignMemNB*);
|
1999-09-22 18:57:23 +02:00
|
|
|
virtual bool proc_block(ostream&os, const NetBlock*);
|
1999-02-08 03:49:56 +01:00
|
|
|
virtual void proc_case(ostream&os, const NetCase*net);
|
1998-11-07 18:05:05 +01:00
|
|
|
virtual void proc_condit(ostream&os, const NetCondit*);
|
1999-06-19 23:06:16 +02:00
|
|
|
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*);
|
1998-11-09 19:55:33 +01:00
|
|
|
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:
|
1999-12-17 04:38:46 +01:00
|
|
|
void emit_init_value_(const NetObj::Link&lnk, verinum::V val);
|
1999-10-31 05:11:27 +01:00
|
|
|
void emit_gate_outputfun_(const NetNode*, unsigned);
|
|
|
|
|
string defn_gate_outputfun_(ostream&os, const NetNode*, unsigned);
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-09-28 05:11:09 +02:00
|
|
|
// This is the name of the thread (process or task) that is
|
|
|
|
|
// being generated.
|
|
|
|
|
string thread_class_;
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
// Method definitions go into this file.
|
|
|
|
|
char*defn_name;
|
|
|
|
|
ofstream defn;
|
|
|
|
|
|
1999-09-29 01:45:09 +02:00
|
|
|
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_;
|
1999-07-18 23:17:50 +02:00
|
|
|
|
|
|
|
|
// 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;
|
1999-10-28 02:47:24 +02:00
|
|
|
|
|
|
|
|
// String constants that are made into vpiHandles have th
|
|
|
|
|
// handle name mapped by this.
|
1999-10-28 06:48:29 +02:00
|
|
|
map<string,unsigned>string_constants;
|
|
|
|
|
unsigned string_counter;
|
1999-11-06 17:00:17 +01:00
|
|
|
|
|
|
|
|
map<verinum,unsigned,less_verinum>number_constants;
|
|
|
|
|
unsigned number_counter;
|
1998-11-04 00:28:49 +01:00
|
|
|
};
|
|
|
|
|
|
1998-12-18 00:54:58 +01:00
|
|
|
|
1999-09-29 01:45:09 +02:00
|
|
|
target_vvm::target_vvm()
|
|
|
|
|
: delayed_name(0), init_code_name(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-07 18:05:05 +01:00
|
|
|
/*
|
|
|
|
|
* 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:
|
1998-11-09 19:55:33 +01:00
|
|
|
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_;
|
1998-11-09 19:55:33 +01:00
|
|
|
unsigned indent_;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
virtual void expr_const(const NetEConst*);
|
1999-06-09 05:00:05 +02:00
|
|
|
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);
|
1998-11-07 18:05:05 +01:00
|
|
|
virtual void expr_signal(const NetESignal*);
|
1999-04-26 00:52:32 +02:00
|
|
|
virtual void expr_subsignal(const NetESubSignal*sig);
|
1999-09-11 06:43:17 +02:00
|
|
|
virtual void expr_ternary(const NetETernary*);
|
1998-11-04 00:28:49 +01:00
|
|
|
virtual void expr_unary(const NetEUnary*);
|
1998-11-07 18:05:05 +01:00
|
|
|
virtual void expr_binary(const NetEBinary*);
|
1999-09-01 00:38:29 +02:00
|
|
|
virtual void expr_ufunc(const NetEUFunc*);
|
1998-11-04 00:28:49 +01:00
|
|
|
};
|
|
|
|
|
|
1999-09-28 03:53:37 +02: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.
|
|
|
|
|
*/
|
1999-06-09 05:00:05 +02:00
|
|
|
void vvm_proc_rval::expr_concat(const NetEConcat*expr)
|
|
|
|
|
{
|
1999-09-28 03:53:37 +02:00
|
|
|
assert(expr->repeat() > 0);
|
1999-06-09 05:00:05 +02:00
|
|
|
string tname = make_temp();
|
|
|
|
|
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
|
|
|
|
|
expr->expr_width() << "> " << tname << ";" << endl;
|
|
|
|
|
|
|
|
|
|
unsigned pos = 0;
|
1999-09-28 03:53:37 +02:00
|
|
|
for (unsigned rep = 0 ; rep < expr->repeat() ; rep += 1)
|
|
|
|
|
for (unsigned idx = 0 ; idx < expr->nparms() ; idx += 1) {
|
1999-06-09 05:00:05 +02:00
|
|
|
|
1999-09-28 03:53:37 +02:00
|
|
|
NetExpr*pp = expr->parm(expr->nparms() - idx - 1);
|
|
|
|
|
pp->expr_scan(this);
|
1999-06-09 05:00:05 +02:00
|
|
|
|
1999-09-28 03:53:37 +02:00
|
|
|
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());
|
1999-06-09 05:00:05 +02:00
|
|
|
}
|
1999-09-28 03:53:37 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
1999-06-09 05:00:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = tname;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
void vvm_proc_rval::expr_const(const NetEConst*expr)
|
|
|
|
|
{
|
|
|
|
|
string tname = make_temp();
|
1998-11-09 19:55:33 +01:00
|
|
|
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
|
|
|
|
|
expr->expr_width() << "> " << tname << ";" << endl;
|
1998-11-07 20:17:10 +01:00
|
|
|
for (unsigned idx = 0 ; idx < expr->expr_width() ; idx += 1) {
|
1998-11-09 19:55:33 +01:00
|
|
|
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());
|
1999-12-12 07:03:14 +01:00
|
|
|
assert(mem->index());
|
1999-11-10 03:52:24 +01:00
|
|
|
mem->index()->expr_scan(this);
|
|
|
|
|
result = mname + ".get_word(" + result + ".as_unsigned())";
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-07 18:05:05 +01:00
|
|
|
void vvm_proc_rval::expr_signal(const NetESignal*expr)
|
|
|
|
|
{
|
1999-02-08 04:55:55 +01:00
|
|
|
result = mangle(expr->name()) + "_bits";
|
1998-11-07 18:05:05 +01:00
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-11 06:43:17 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-01 22:46:19 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
1999-09-01 00:38:29 +02:00
|
|
|
void vvm_proc_rval::expr_ufunc(const NetEUFunc*expr)
|
|
|
|
|
{
|
1999-09-01 22:46:19 +02:00
|
|
|
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. */
|
1999-12-12 20:47:54 +01:00
|
|
|
os_ << " " << mangle(expr->name()) << "();" << endl;
|
1999-09-01 22:46:19 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
1999-09-01 00:38:29 +02:00
|
|
|
}
|
|
|
|
|
|
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() << "> "
|
1998-11-07 20:17:10 +01:00
|
|
|
<< 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;
|
1999-05-01 22:43:55 +02:00
|
|
|
case '&':
|
1999-10-05 08:19:46 +02:00
|
|
|
os_ << "vvm_unop_and(" << result << ");" << endl;
|
1999-05-01 22:43:55 +02:00
|
|
|
break;
|
1999-10-08 04:00:48 +02:00
|
|
|
case '|':
|
|
|
|
|
os_ << "vvm_unop_or(" << result << ");" << endl;
|
|
|
|
|
break;
|
1999-10-01 17:26:28 +02:00
|
|
|
case '^':
|
1999-10-05 08:19:46 +02:00
|
|
|
os_ << "vvm_unop_xor(" << result << ");" << endl;
|
1999-10-01 17:26:28 +02:00
|
|
|
break;
|
1999-03-15 03:43:32 +01:00
|
|
|
case '!':
|
1999-10-05 08:19:46 +02:00
|
|
|
os_ << "vvm_unop_lnot(" << result << ");" << endl;
|
1999-03-15 03:43:32 +01:00
|
|
|
break;
|
1999-10-01 17:26:28 +02:00
|
|
|
case '-':
|
1999-10-05 08:19:46 +02:00
|
|
|
os_ << "vvm_unop_uminus(" << result << ");" << endl;
|
|
|
|
|
break;
|
|
|
|
|
case 'N':
|
|
|
|
|
os_ << "vvm_unop_nor(" << result << ");" << endl;
|
1999-10-01 17:26:28 +02:00
|
|
|
break;
|
|
|
|
|
case 'X':
|
1999-10-05 08:19:46 +02:00
|
|
|
os_ << "vvm_unop_xnor(" << result << ");" << endl;
|
1999-10-01 17:26:28 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-07 18:05:05 +01:00
|
|
|
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;
|
|
|
|
|
|
1998-11-07 20:17:10 +01:00
|
|
|
result = make_temp();
|
1999-09-29 02:42:25 +02:00
|
|
|
os_ << setw(indent_) << "" << "// " << expr->get_line() <<
|
|
|
|
|
": expression node." << endl;
|
1998-11-09 19:55:33 +01:00
|
|
|
os_ << setw(indent_) << "" << "vvm_bitset_t<" <<
|
|
|
|
|
expr->expr_width() << ">" << result << ";" << endl;
|
1998-11-07 18:05:05 +01:00
|
|
|
switch (expr->op()) {
|
1999-09-04 21:11:45 +02:00
|
|
|
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': // ==
|
1998-11-09 19:55:33 +01:00
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_eq("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1999-09-28 03:13:15 +02:00
|
|
|
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;
|
1999-09-11 06:43:17 +02:00
|
|
|
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;
|
1998-11-09 19:55:33 +01:00
|
|
|
case 'n':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_ne("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
1998-11-07 18:05:05 +01:00
|
|
|
break;
|
1999-06-07 04:23:31 +02:00
|
|
|
case '<':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_lt("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1999-09-28 03:13:15 +02:00
|
|
|
case '>':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_gt("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1999-09-04 21:11:45 +02:00
|
|
|
case 'o': // logical or (||)
|
1999-03-15 03:43:32 +01:00
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_lor("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
2000-01-18 05:53:57 +01:00
|
|
|
break;
|
1999-09-23 05:56:57 +02:00
|
|
|
case 'r': // right shift(>>)
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_shiftr("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
1999-03-15 03:43:32 +01:00
|
|
|
break;
|
2000-01-13 07:05:46 +01:00
|
|
|
case 'X':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_xnor("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1998-11-07 18:05:05 +01:00
|
|
|
case '+':
|
1998-11-09 19:55:33 +01:00
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_plus("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
|
|
|
|
case '-':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_minus("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
1998-11-07 18:05:05 +01:00
|
|
|
break;
|
1999-02-22 04:01:12 +01:00
|
|
|
case '&':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_and("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1999-03-15 03:43:32 +01:00
|
|
|
case '|':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_or("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1999-05-01 22:43:55 +02:00
|
|
|
case '^':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_xor("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
2000-01-13 04:35:35 +01:00
|
|
|
case '*':
|
|
|
|
|
os_ << setw(indent_) << "" << "vvm_binop_mult(" << result
|
|
|
|
|
<< "," << lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
|
|
|
|
case '/':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_idiv("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
|
|
|
|
case '%':
|
|
|
|
|
os_ << setw(indent_) << "" << result << " = vvm_binop_imod("
|
|
|
|
|
<< lres << "," << rres << ");" << endl;
|
|
|
|
|
break;
|
1998-11-07 18:05:05 +01:00
|
|
|
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;
|
1998-11-07 18:05:05 +01:00
|
|
|
result = lres;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-09 19:55:33 +01:00
|
|
|
static string emit_proc_rval(ostream&os, unsigned indent, const NetExpr*expr)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1998-11-09 19:55:33 +01:00
|
|
|
vvm_proc_rval scan (os, indent);
|
1998-11-04 00:28:49 +01:00
|
|
|
expr->expr_scan(&scan);
|
|
|
|
|
return scan.result;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-15 03:23:56 +02:00
|
|
|
/*
|
|
|
|
|
* 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:
|
1999-10-28 02:47:24 +02:00
|
|
|
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*);
|
1998-11-07 18:05:05 +01:00
|
|
|
virtual void expr_signal(const NetESignal*);
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ostream&os_;
|
1999-10-28 02:47:24 +02:00
|
|
|
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()) {
|
1999-10-28 02:47:24 +02:00
|
|
|
|
1999-10-28 06:48:29 +02:00
|
|
|
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;
|
1999-10-28 02:47:24 +02:00
|
|
|
}
|
|
|
|
|
|
1999-10-28 06:48:29 +02:00
|
|
|
ostrstream tmp;
|
|
|
|
|
tmp << "&string_table[" << res << "].base" << ends;
|
|
|
|
|
result = tmp.str();
|
1999-07-10 03:02:08 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-06 17:00:17 +01:00
|
|
|
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
|
|
|
}
|
1999-11-06 17:00:17 +01: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
|
|
|
}
|
|
|
|
|
|
1999-11-06 17:00:17 +01: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") {
|
1999-10-28 02:47:24 +02:00
|
|
|
result = string("vpip_sim_time()");
|
1998-11-04 00:28:49 +01:00
|
|
|
} else {
|
1998-11-07 18:05:05 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2000-02-13 20:18:27 +01:00
|
|
|
if (mem->index() == 0) {
|
|
|
|
|
/* If the expression is a memory without an index, then
|
|
|
|
|
return the handle for the memory object. System tasks
|
|
|
|
|
can take such things as parameters. */
|
|
|
|
|
result = string("&") + mangle(mem->name()) + ".base";
|
|
|
|
|
|
|
|
|
|
} else if (const NetEConst*idx = dynamic_cast<const NetEConst*>(mem->index())){
|
|
|
|
|
|
|
|
|
|
/* If the expression is a memory with a constant index,
|
|
|
|
|
then generate a call to vpi_handle_by_index() to get
|
|
|
|
|
the memory word handle. */
|
|
|
|
|
unsigned long val = idx->value().as_ulong();
|
|
|
|
|
ostrstream res;
|
|
|
|
|
res << "vpi_handle_by_index(&" << mangle(mem->name()) <<
|
|
|
|
|
".base, " << val << ")" << ends;
|
|
|
|
|
result = res.str();
|
|
|
|
|
|
|
|
|
|
} else {
|
2000-02-13 20:59:33 +01:00
|
|
|
|
|
|
|
|
/* Otherwise, evaluate the index at run time and use
|
|
|
|
|
that to select the memory word. */
|
|
|
|
|
string rval = emit_proc_rval(tgt_->defn, 6, mem->index());
|
|
|
|
|
result = "vpi_handle_by_index(&" + mangle(mem->name()) +
|
|
|
|
|
".base, " + rval + ".as_unsigned())";
|
2000-02-13 20:18:27 +01:00
|
|
|
}
|
1999-11-10 03:52:24 +01:00
|
|
|
}
|
|
|
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-07 18:05:05 +01:00
|
|
|
void vvm_parm_rval::expr_signal(const NetESignal*expr)
|
|
|
|
|
{
|
1999-10-28 23:36:00 +02:00
|
|
|
string res = string("&") + mangle(expr->name()) + ".base";
|
1998-11-07 18:05:05 +01:00
|
|
|
result = res;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-28 02:47:24 +02:00
|
|
|
static string emit_parm_rval(ostream&os, target_vvm*tgt, const NetExpr*expr)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-10-28 02:47:24 +02: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)
|
|
|
|
|
{
|
1999-10-01 05:15:00 +02:00
|
|
|
defn_name = tempnam(0, "ivldf");
|
|
|
|
|
defn.open(defn_name, ios::in | ios::out | ios::trunc);
|
|
|
|
|
|
1999-09-29 01:45:09 +02:00
|
|
|
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;
|
1999-08-15 03:23:56 +02:00
|
|
|
os << "# include \"vpi_user.h\"" << endl;
|
|
|
|
|
os << "# include \"vpi_priv.h\"" << endl;
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
process_counter = 0;
|
1999-10-28 06:48:29 +02:00
|
|
|
string_counter = 1;
|
1999-11-06 17:00:17 +01:00
|
|
|
number_counter = 1;
|
1999-10-28 06:48:29 +02:00
|
|
|
|
|
|
|
|
os << "static struct __vpiStringConst string_table[];" << endl;
|
1999-11-06 17:00:17 +01:00
|
|
|
os << "static struct __vpiNumberConst number_table[];" << endl;
|
1998-12-20 03:05:41 +01:00
|
|
|
|
1999-12-12 20:47:54 +01:00
|
|
|
init_code << "static void design_init()" << endl;
|
1998-12-20 03:05:41 +01:00
|
|
|
init_code << "{" << endl;
|
1999-10-28 02:47:24 +02:00
|
|
|
init_code << " vpip_init_simulation();"
|
1999-08-15 03:23:56 +02:00
|
|
|
<< endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
start_code << "static void design_start()" << endl;
|
1999-01-01 02:46:01 +01:00
|
|
|
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)
|
|
|
|
|
{
|
1999-10-28 06:48:29 +02:00
|
|
|
os << "static struct __vpiStringConst string_table[" <<
|
|
|
|
|
string_counter+1 << "];" << endl;
|
1999-11-06 17:00:17 +01:00
|
|
|
os << "static struct __vpiNumberConst number_table[" <<
|
|
|
|
|
number_counter+1 << "];" << endl;
|
1999-10-28 06:48:29 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
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;
|
|
|
|
|
|
1999-09-29 01:45:09 +02:00
|
|
|
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
|
|
|
|
1999-09-29 01:45:09 +02: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;
|
1998-12-20 03:05:41 +01:00
|
|
|
|
1999-01-01 02:46:01 +01:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
os << "main()" << endl << "{" << endl;
|
1999-11-28 19:05:37 +01:00
|
|
|
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;
|
2000-01-13 06:11:24 +01:00
|
|
|
|
|
|
|
|
string vpi_module_list = mod->get_flag("VPI_MODULE_LIST");
|
|
|
|
|
while (vpi_module_list.length()) {
|
|
|
|
|
string name;
|
|
|
|
|
unsigned pos = vpi_module_list.find(',');
|
|
|
|
|
if (pos < vpi_module_list.length()) {
|
|
|
|
|
name = vpi_module_list.substr(0, pos);
|
|
|
|
|
vpi_module_list = vpi_module_list.substr(pos+1);
|
|
|
|
|
} else {
|
|
|
|
|
name = vpi_module_list;
|
|
|
|
|
vpi_module_list = "";
|
|
|
|
|
}
|
|
|
|
|
os << " vvm_load_vpi_module(\"" << name << ".vpi\");" << endl;
|
|
|
|
|
}
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " design_init();" << endl;
|
|
|
|
|
os << " design_start();" << endl;
|
1998-11-09 19:55:33 +01:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
for (unsigned idx = 0 ; idx < process_counter ; idx += 1)
|
|
|
|
|
os << " thread" << (idx+1) << "_t thread_" <<
|
1999-12-12 20:47:54 +01:00
|
|
|
(idx+1) << ";" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " vpip_simulation_run();" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
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)
|
|
|
|
|
{
|
1999-11-28 01:56:08 +01:00
|
|
|
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;
|
|
|
|
|
}
|
1999-11-28 02:16:18 +01:00
|
|
|
|
1998-12-20 03:05:41 +01:00
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
1999-11-29 01:38:27 +01:00
|
|
|
init_code << " " << mangle(sig->name()) << ".init_P("
|
|
|
|
|
<< idx << ", V" << sig->get_ival(idx) << ");"
|
|
|
|
|
<< endl;
|
|
|
|
|
|
1999-12-17 04:38:46 +01:00
|
|
|
// Propogate the initial value to inputs throughout.
|
|
|
|
|
emit_init_value_(sig->pin(idx), sig->get_ival(idx));
|
1998-12-20 03:05:41 +01:00
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-04-19 03:59:36 +02: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-04-19 03:59:36 +02:00
|
|
|
}
|
|
|
|
|
|
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());
|
1999-09-28 05:11:09 +02:00
|
|
|
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;
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " " << name << "(vvm_thread*th)" << endl;
|
2000-01-08 04:09:14 +01:00
|
|
|
os << " : vvm_thread(), back_(th), step_(&" << name <<
|
|
|
|
|
"::step_0_), callee_(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;
|
1999-10-01 05:15:00 +02:00
|
|
|
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);
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << " back_ -> thread_yield();" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return false;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
1999-09-28 05:11:09 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
os << "};" << endl;
|
1999-09-28 05:11:09 +02:00
|
|
|
thread_class_ = save_thread_class;
|
1999-07-07 06:20:57 +02:00
|
|
|
}
|
|
|
|
|
|
1999-09-01 00:38:29 +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;
|
1999-12-12 20:47:54 +01:00
|
|
|
os << "static void " << name << "();" << endl;
|
1999-09-01 22:46:19 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "// Function " << def->name() << endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << "static void " << name << "()" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "{" << endl;
|
|
|
|
|
def->proc()->emit_proc(os, this);
|
|
|
|
|
defn << "}" << endl;
|
1999-09-01 00:38:29 +02:00
|
|
|
}
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
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();
|
|
|
|
|
|
1999-12-12 20:47:54 +01:00
|
|
|
os << "static void " << name << "(vpip_bit_t);" << endl;
|
1999-10-31 05:11:27 +01:00
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
1999-12-17 04:38:46 +01:00
|
|
|
void target_vvm::emit_init_value_(const NetObj::Link&lnk, verinum::V val)
|
|
|
|
|
{
|
|
|
|
|
map<string,bool>written;
|
|
|
|
|
|
|
|
|
|
for (const NetObj::Link*cur = lnk.next_link()
|
|
|
|
|
; (*cur) != lnk ; cur = cur->next_link()) {
|
|
|
|
|
|
|
|
|
|
if (cur->get_dir() == NetObj::Link::OUTPUT)
|
|
|
|
|
continue;
|
|
|
|
|
|
2000-01-02 18:57:56 +01:00
|
|
|
if (! dynamic_cast<const NetObj*>(cur->get_obj()))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Build an init statement for the link, that writes the
|
|
|
|
|
// value.
|
|
|
|
|
ostrstream line;
|
|
|
|
|
line << " " << mangle(cur->get_obj()->name()) <<
|
|
|
|
|
".init_" << cur->get_name() << "(" <<
|
|
|
|
|
cur->get_inst() << ", V" << val << ");" << endl << ends;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check to see if the line has already been
|
1999-12-17 04:38:46 +01:00
|
|
|
// written to. This can happen if the object is a
|
|
|
|
|
// NetESignal, because there can be many of them
|
|
|
|
|
// with the same name.
|
2000-01-02 18:57:56 +01:00
|
|
|
if (written[line.str()])
|
1999-12-17 04:38:46 +01:00
|
|
|
continue;
|
|
|
|
|
|
2000-01-02 18:57:56 +01:00
|
|
|
written[line.str()] = true;
|
1999-12-17 04:38:46 +01:00
|
|
|
|
2000-01-02 18:57:56 +01:00
|
|
|
init_code << line.str();
|
1999-12-17 04:38:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
|
*/
|
1999-10-31 05:11:27 +01:00
|
|
|
void target_vvm::emit_gate_outputfun_(const NetNode*gate, unsigned gpin)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-10-31 05:11:27 +01:00
|
|
|
const NetObj::Link&lnk = gate->pin(gpin);
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
delayed << "static void " << mangle(gate->name()) <<
|
1999-10-31 05:11:27 +01:00
|
|
|
"_output_" << lnk.get_name() << "_" << lnk.get_inst() <<
|
1999-12-12 20:47:54 +01:00
|
|
|
"(vpip_bit_t val)" <<
|
1998-11-04 00:28:49 +01:00
|
|
|
endl << "{" << endl;
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
/* 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;
|
1999-10-31 05:11:27 +01:00
|
|
|
gate->pin(gpin).next_link(cur, pin);
|
1999-12-17 04:38:46 +01:00
|
|
|
for ( ; cur != gate ; cur->pin(pin).next_link(cur, pin)) {
|
|
|
|
|
|
|
|
|
|
// Skip pins that are output only.
|
|
|
|
|
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
|
|
|
|
|
continue;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-11-29 00:59:22 +01:00
|
|
|
if (cur->pin(pin).get_name() != "") {
|
1999-10-31 05:11:27 +01:00
|
|
|
|
|
|
|
|
delayed << " " << mangle(cur->name()) << ".set_"
|
1999-12-12 20:47:54 +01:00
|
|
|
<< cur->pin(pin).get_name() << "(" <<
|
1999-10-31 05:11:27 +01:00
|
|
|
cur->pin(pin).get_inst() << ", val);" << endl;
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
} else {
|
1999-02-08 04:55:55 +01:00
|
|
|
|
1999-12-12 20:47:54 +01:00
|
|
|
delayed << " " << mangle(cur->name()) << ".set("
|
1998-11-04 00:28:49 +01:00
|
|
|
<< pin << ", val);" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
1999-10-31 05:11:27 +01:00
|
|
|
|
|
|
|
|
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-12-16 03:42:14 +01:00
|
|
|
// Connect the carry output if necessary.
|
|
|
|
|
if (gate->pin_Cout().is_linked()) {
|
|
|
|
|
unsigned pin = gate->pin_Cout().get_pin();
|
|
|
|
|
string outfun = defn_gate_outputfun_(os, gate, pin);
|
|
|
|
|
init_code << " " << mangle(gate->name()) <<
|
|
|
|
|
".config_cout(&" << outfun << ");" << endl;
|
|
|
|
|
emit_gate_outputfun_(gate, pin);
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-31 21:08:24 +01:00
|
|
|
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-12-16 03:42:14 +01:00
|
|
|
|
|
|
|
|
start_code << " " << mangle(gate->name()) << ".start();" << endl;
|
1999-09-04 03:57:15 +02:00
|
|
|
}
|
|
|
|
|
|
1999-11-14 21:24:28 +01: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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-01 03:07:40 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-13 04:35:35 +01:00
|
|
|
void target_vvm::lpm_mult(ostream&os, const NetMult*mul)
|
|
|
|
|
{
|
|
|
|
|
string mname = mangle(mul->name());
|
|
|
|
|
os << "static vvm_mult " << mname << "(" << mul->width_r() <<
|
|
|
|
|
"," << mul->width_a() << "," << mul->width_b() << "," <<
|
|
|
|
|
mul->width_s() << ");" << endl;
|
|
|
|
|
|
|
|
|
|
// Write the output functions for the multiplier device.
|
|
|
|
|
for (unsigned idx = 0 ; idx < mul->width_r() ; idx += 1) {
|
|
|
|
|
unsigned pin = mul->pin_Result(idx).get_pin();
|
|
|
|
|
string outfun = defn_gate_outputfun_(os, mul, pin);
|
|
|
|
|
init_code << " " << mangle(mul->name()) <<
|
|
|
|
|
".config_rout(" << idx << ", &" << outfun << ");" << endl;
|
|
|
|
|
emit_gate_outputfun_(mul, 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
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)
|
|
|
|
|
{
|
1999-10-31 05:11:27 +01:00
|
|
|
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 <<
|
1999-08-01 18:34:50 +02:00
|
|
|
"," << gate->rise_time() << "> ";
|
1998-11-04 00:28:49 +01:00
|
|
|
break;
|
1999-11-21 02:16:51 +01:00
|
|
|
case NetLogic::BUF:
|
|
|
|
|
os << "static vvm_buf<" << gate->rise_time() << "> ";
|
|
|
|
|
break;
|
1999-02-15 03:06:15 +01:00
|
|
|
case NetLogic::BUFIF0:
|
1999-08-01 18:34:50 +02:00
|
|
|
os << "static vvm_bufif0<" << gate->rise_time() << "> ";
|
1999-02-15 03:06:15 +01:00
|
|
|
break;
|
|
|
|
|
case NetLogic::BUFIF1:
|
1999-08-01 18:34:50 +02:00
|
|
|
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 <<
|
1999-08-01 18:34:50 +02:00
|
|
|
"," << gate->rise_time() << "> ";
|
1998-11-04 00:28:49 +01:00
|
|
|
break;
|
|
|
|
|
case NetLogic::NOR:
|
|
|
|
|
os << "static vvm_nor" << "<" << gate->pin_count()-1 <<
|
1999-08-01 18:34:50 +02:00
|
|
|
"," << gate->rise_time() << "> ";
|
1998-11-04 00:28:49 +01:00
|
|
|
break;
|
|
|
|
|
case NetLogic::NOT:
|
1999-08-01 18:34:50 +02:00
|
|
|
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 <<
|
1999-08-01 18:34:50 +02:00
|
|
|
"," << gate->rise_time() << "> ";
|
1998-11-04 00:28:49 +01:00
|
|
|
break;
|
1998-11-09 19:55:33 +01:00
|
|
|
case NetLogic::XNOR:
|
|
|
|
|
os << "static vvm_xnor" << "<" << gate->pin_count()-1 <<
|
1999-08-01 18:34:50 +02:00
|
|
|
"," << gate->rise_time() << "> ";
|
1998-11-09 19:55:33 +01:00
|
|
|
break;
|
1998-11-04 00:28:49 +01:00
|
|
|
case NetLogic::XOR:
|
|
|
|
|
os << "static vvm_xor" << "<" << gate->pin_count()-1 <<
|
1999-08-01 18:34:50 +02:00
|
|
|
"," << gate->rise_time() << "> ";
|
1998-11-04 00:28:49 +01:00
|
|
|
break;
|
1999-11-21 02:16:51 +01:00
|
|
|
default:
|
|
|
|
|
os << "#error \"internal ivl error:bad gate type for " <<
|
|
|
|
|
gate->name() << "\"" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
os << mangle(gate->name()) << "(&" << outfun << ");" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
emit_gate_outputfun_(gate, 0);
|
1999-01-01 02:46:01 +01:00
|
|
|
|
1999-12-16 03:42:14 +01:00
|
|
|
start_code << " " << mangle(gate->name()) << ".start();" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void target_vvm::bufz(ostream&os, const NetBUFZ*gate)
|
|
|
|
|
{
|
1999-10-31 05:11:27 +01:00
|
|
|
string outfun = defn_gate_outputfun_(os, gate, 0);
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
os << "static vvm_bufz " << mangle(gate->name()) << "(&" <<
|
1999-10-31 05:11:27 +01:00
|
|
|
outfun << ");" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
emit_gate_outputfun_(gate, 0);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1998-12-18 00:54:58 +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;
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
string outfun = defn_gate_outputfun_(os, gate, 0);
|
1998-12-18 00:54:58 +01:00
|
|
|
|
|
|
|
|
os << "static vvm_udp_ssequ<" << gate->pin_count()-1 << "> " <<
|
1999-10-31 05:11:27 +01:00
|
|
|
mangle(gate->name()) << "(&" << outfun << ", V" <<
|
|
|
|
|
gate->get_initial() << ", " << mangle(gate->name()) <<
|
|
|
|
|
"_table);" << endl;
|
1998-12-18 00:54:58 +01:00
|
|
|
|
1998-12-20 03:05:41 +01:00
|
|
|
/* The UDP output function is much like other logic gates. Use
|
|
|
|
|
this general method to output the output function. */
|
1999-10-31 05:11:27 +01:00
|
|
|
emit_gate_outputfun_(gate, 0);
|
1998-12-20 03:05:41 +01:00
|
|
|
|
1998-12-18 00:54:58 +01:00
|
|
|
}
|
|
|
|
|
|
1999-06-07 04:23:31 +02:00
|
|
|
/*
|
|
|
|
|
* 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());
|
1999-10-05 06:02:10 +02:00
|
|
|
unsigned iwid = net->pin_count();
|
1999-06-07 04:23:31 +02:00
|
|
|
os << "class " << name << " : public vvm_event {" << endl;
|
|
|
|
|
os << " public:" << endl;
|
|
|
|
|
|
|
|
|
|
if (net->bmux()) {
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " " << name << "(const vvm_bitset_t<"
|
1999-06-07 04:23:31 +02:00
|
|
|
<< iwid << ">&v, unsigned idx)" << endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " : value_(v), idx_(idx) { }" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
} else {
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " " << name << "(const vvm_bits_t&v)"
|
1999-10-05 06:02:10 +02:00
|
|
|
<< endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " : value_(v) { }" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
}
|
|
|
|
|
os << " void event_function();" << endl;
|
|
|
|
|
|
|
|
|
|
os << " private:" << 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)) {
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
const NetObj::Link&lnk = cur->pin(pin);
|
|
|
|
|
|
1999-06-07 04:23:31 +02:00
|
|
|
// Skip output only pins.
|
|
|
|
|
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
|
|
|
|
|
continue;
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
delayed << " " << mangle(cur->name())
|
1999-12-12 20:47:54 +01:00
|
|
|
<< ".set_" << lnk.get_name()
|
1999-10-31 05:11:27 +01:00
|
|
|
<< lnk.get_inst() << ", value_[0]);" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
const NetObj::Link&lnk = cur->pin(pin);
|
|
|
|
|
|
1999-06-07 04:23:31 +02:00
|
|
|
// Skip output only pins.
|
|
|
|
|
if (cur->pin(pin).get_dir() == NetObj::Link::OUTPUT)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
delayed << " " << mangle(cur->name()) <<
|
1999-12-12 20:47:54 +01:00
|
|
|
".set_" << lnk.get_name() << "("
|
1999-10-31 05:11:27 +01:00
|
|
|
<< lnk.get_inst() << ", value_[" <<
|
1999-06-07 04:23:31 +02:00
|
|
|
idx << "]);" << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delayed << "}" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-10 03:59:54 +02:00
|
|
|
void target_vvm::net_case_cmp(ostream&os, const NetCaseCmp*gate)
|
|
|
|
|
{
|
1999-12-02 17:58:58 +01:00
|
|
|
const NetObj::Link&lnk = gate->pin(0);
|
|
|
|
|
|
1999-10-10 03:59:54 +02:00
|
|
|
os << "static void " << mangle(gate->name()) <<
|
1999-12-02 17:58:58 +01:00
|
|
|
"_output_" << lnk.get_name() << "_" << lnk.get_inst() <<
|
1999-12-12 20:47:54 +01:00
|
|
|
"(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()) <<
|
1999-12-02 17:58:58 +01:00
|
|
|
"_output_" << lnk.get_name() << "_" << lnk.get_inst() << ");" << endl;
|
1999-10-10 03:59:54 +02:00
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
emit_gate_outputfun_(gate, 0);
|
1999-10-10 03:59:54 +02:00
|
|
|
|
1999-12-12 20:47:54 +01:00
|
|
|
start_code << " " << mangle(gate->name()) << ".start();" << endl;
|
1999-10-10 03:59:54 +02:00
|
|
|
}
|
|
|
|
|
|
1998-11-09 19:55:33 +01:00
|
|
|
/*
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
1999-12-17 04:38:46 +01:00
|
|
|
for (unsigned idx = 0 ; idx < gate->pin_count() ; idx += 1)
|
|
|
|
|
emit_init_value_(gate->pin(idx), gate->value(idx));
|
1998-11-09 19:55:33 +01:00
|
|
|
}
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
/*
|
1999-05-01 04:57:52 +02: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.
|
|
|
|
|
*/
|
1999-05-01 04:57:52 +02:00
|
|
|
void target_vvm::net_event(ostream&os, const NetNEvent*gate)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-05-01 04:57:52 +02:00
|
|
|
string pevent = mangle(gate->fore_ptr()->name());
|
|
|
|
|
os << " /* " << gate->name() << " */" << endl;
|
|
|
|
|
|
1999-07-18 23:17:50 +02:00
|
|
|
bool&printed = pevent_printed_flag[pevent];
|
|
|
|
|
if (! printed) {
|
|
|
|
|
printed = true;
|
|
|
|
|
os << "static vvm_sync " << pevent << ";" << endl;
|
|
|
|
|
}
|
1999-05-01 04:57:52 +02:00
|
|
|
|
1999-05-01 22:43:55 +02:00
|
|
|
os << "static vvm_pevent<" << gate->pin_count() << "> " <<
|
|
|
|
|
mangle(gate->name()) << "(&" << pevent << ", ";
|
1999-05-01 04:57:52 +02:00
|
|
|
switch (gate->type()) {
|
|
|
|
|
case NetNEvent::POSEDGE:
|
1999-05-03 03:51:29 +02:00
|
|
|
case NetNEvent::POSITIVE:
|
1999-05-01 22:43:55 +02:00
|
|
|
os << "vvm_pevent<" << gate->pin_count() << ">::POSEDGE";
|
1999-05-01 04:57:52 +02:00
|
|
|
break;
|
|
|
|
|
case NetNEvent::NEGEDGE:
|
1999-05-01 22:43:55 +02:00
|
|
|
os << "vvm_pevent<"<< gate->pin_count() << ">::NEGEDGE";
|
1999-05-01 04:57:52 +02:00
|
|
|
break;
|
|
|
|
|
case NetNEvent::ANYEDGE:
|
1999-05-01 22:43:55 +02:00
|
|
|
os << "vvm_pevent<" << gate->pin_count() << ">::ANYEDGE";
|
1999-05-01 04:57:52 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
os << ");" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void target_vvm::start_process(ostream&os, const NetProcTop*proc)
|
|
|
|
|
{
|
|
|
|
|
process_counter += 1;
|
1999-09-28 05:11:09 +02:00
|
|
|
{ ostrstream ts;
|
|
|
|
|
ts << "thread" << process_counter << "_t" << ends;
|
|
|
|
|
thread_class_ = ts.str();
|
|
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
thread_step_ = 0;
|
|
|
|
|
|
1999-09-28 05:11:09 +02:00
|
|
|
os << "class " << thread_class_ << " : public vvm_thread {" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
os << " public:" << endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
os << " " << thread_class_ << "()" << endl;
|
|
|
|
|
os << " : vvm_thread(), step_(&" << thread_class_ <<
|
2000-01-08 04:09:14 +01:00
|
|
|
"::step_0_), callee_(0)" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
os << " { }" << endl;
|
1999-09-28 05:11:09 +02:00
|
|
|
os << " ~" << thread_class_ << "() { }" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
os << endl;
|
1998-11-10 01:48:31 +01:00
|
|
|
os << " bool go() { return (this->*step_)(); }" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
os << " private:" << endl;
|
1999-09-28 05:11:09 +02:00
|
|
|
os << " bool (" << thread_class_ << "::*step_)();" << endl;
|
1999-07-07 06:20:57 +02:00
|
|
|
os << " vvm_thread*callee_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
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)
|
|
|
|
|
{
|
1999-10-01 05:15:00 +02:00
|
|
|
string rval = emit_proc_rval(defn, 8, net->rval());
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " // " << net->get_line() << ": " << endl;
|
1998-11-23 01:20:22 +01:00
|
|
|
|
1999-10-07 07:25:33 +02:00
|
|
|
if (net->bmux()) {
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-10-23 18:27:53 +02:00
|
|
|
// This is a bit select. Assign the low bit of the rval
|
|
|
|
|
// to the selected bit of the lval.
|
1999-10-07 07:25:33 +02:00
|
|
|
string bval = emit_proc_rval(defn, 8, net->bmux());
|
1999-04-22 06:56:58 +02:00
|
|
|
|
1999-10-07 07:25:33 +02:00
|
|
|
defn << " switch (" << bval << ".as_unsigned()) {" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-10-07 07:25:33 +02: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
|
|
|
|
1999-10-07 07:25:33 +02:00
|
|
|
defn << " case " << idx << ":" << endl;
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-10-07 07:25:33 +02:00
|
|
|
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()) <<
|
1999-10-31 05:11:27 +01:00
|
|
|
".set_" << cur->pin(pin).get_name() <<
|
1999-12-12 20:47:54 +01:00
|
|
|
"(" << cur->pin(pin).get_inst() <<
|
1999-10-31 05:11:27 +01:00
|
|
|
", " << rval << "[0]);" << endl;
|
1999-10-07 07:25:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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. */
|
1999-08-02 02:19:16 +02:00
|
|
|
|
1999-10-07 07:25:33 +02:00
|
|
|
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()) <<
|
1999-10-31 05:11:27 +01:00
|
|
|
".set_" << cur->pin(pin).get_name() <<
|
1999-12-12 20:47:54 +01:00
|
|
|
"(" << cur->pin(pin).get_inst() <<
|
1999-10-31 05:11:27 +01:00
|
|
|
", " << rval << "[" << idx << "]);" << endl;
|
1999-10-07 07:25:33 +02:00
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-01 05:58:36 +02: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.
|
|
|
|
|
*/
|
1999-05-12 06:03:19 +02:00
|
|
|
void target_vvm::proc_assign_mem(ostream&os, const NetAssignMem*amem)
|
|
|
|
|
{
|
1999-12-05 03:24:08 +01:00
|
|
|
string index = mangle(amem->index()->name()) + "_bits";
|
1999-10-01 05:15:00 +02:00
|
|
|
string rval = emit_proc_rval(defn, 8, amem->rval());
|
1999-05-12 06:03:19 +02:00
|
|
|
const NetMemory*mem = amem->memory();
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " /* " << amem->get_line() << " */" << endl;
|
1999-10-01 05:58:36 +02:00
|
|
|
if (mem->width() == amem->rval()->expr_width()) {
|
1999-11-21 01:13:08 +01:00
|
|
|
defn << " " << mangle(mem->name()) <<
|
1999-12-12 20:47:54 +01:00
|
|
|
".set_word(" << index << ".as_unsigned(), " <<
|
1999-11-21 01:13:08 +01:00
|
|
|
rval << ");" << endl;
|
1999-10-01 05:58:36 +02:00
|
|
|
|
|
|
|
|
} 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;
|
|
|
|
|
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << " " << mangle(mem->name()) << ".set_word("
|
1999-11-21 01:13:08 +01:00
|
|
|
<< index << ".as_unsigned(), " << tmp << ");" << endl;
|
1999-10-01 05:58:36 +02:00
|
|
|
}
|
1999-05-12 06:03:19 +02:00
|
|
|
}
|
|
|
|
|
|
1999-06-07 04:23:31 +02:00
|
|
|
void target_vvm::proc_assign_nb(ostream&os, const NetAssignNB*net)
|
|
|
|
|
{
|
1999-10-01 05:15:00 +02:00
|
|
|
string rval = emit_proc_rval(defn, 8, net->rval());
|
1999-06-07 04:23:31 +02:00
|
|
|
|
|
|
|
|
if (net->bmux()) {
|
1999-10-01 05:15:00 +02:00
|
|
|
string bval = emit_proc_rval(defn, 8, net->bmux());
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << " (new " << mangle(net->name()) << "("
|
|
|
|
|
<< rval << ", " << bval << ".as_unsigned())) "
|
|
|
|
|
<< "-> schedule(" << net->rise_time() << ");" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
|
|
|
|
|
} else {
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << " (new " << mangle(net->name()) << "("
|
|
|
|
|
<< rval << ")) -> schedule(" << net->rise_time() <<
|
|
|
|
|
");" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-08 04:09:14 +01:00
|
|
|
void target_vvm::proc_assign_mem_nb(ostream&os, const NetAssignMemNB*amem)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
string index = mangle(amem->index()->name()) + "_bits";
|
|
|
|
|
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 << " (new vvm_memory_t<" << mem->width() << ","
|
|
|
|
|
<< mem->count() << ">::assign_nb(" << mangle(mem->name())
|
|
|
|
|
<< ", " << index << ".as_unsigned(), " << rval <<
|
|
|
|
|
")) -> schedule();" << 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 << " (new vvm_memory_t<" << mem->width() << ","
|
|
|
|
|
<< mem->count() << ">::assign_nb(" << mangle(mem->name())
|
|
|
|
|
<< ", " << index << ".as_unsigned(), " << tmp <<
|
|
|
|
|
")) -> schedule();" << 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
|
|
|
{
|
1999-09-22 06:30:04 +02:00
|
|
|
if (net->type() == NetBlock::PARA) {
|
1999-09-22 18:57:23 +02:00
|
|
|
cerr << "sorry: vvm cannot emit parallel blocks." << endl;
|
|
|
|
|
return false;
|
1999-09-22 06:30:04 +02:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
1999-02-08 03:49:56 +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)
|
1998-11-07 18:05:05 +01:00
|
|
|
{
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " /* case (" << *net->expr() << ") */" << endl;
|
|
|
|
|
string expr = emit_proc_rval(defn, 8, net->expr());
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-02-22 04:01:12 +01:00
|
|
|
unsigned exit_step = thread_step_ + 1;
|
1999-02-08 03:49:56 +01:00
|
|
|
thread_step_ += 1;
|
|
|
|
|
|
1999-02-22 04:01:12 +01:00
|
|
|
unsigned default_idx = net->nitems();
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
/* 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. */
|
1999-02-08 03:49:56 +01:00
|
|
|
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));
|
1999-02-08 03:49:56 +01:00
|
|
|
|
|
|
|
|
thread_step_ += 1;
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
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;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ <<
|
1999-09-28 05:11:09 +02:00
|
|
|
"::step_" << thread_step_ << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
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;
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
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))
|
1999-10-01 05:15:00 +02:00
|
|
|
net->stat(idx)->emit_proc(os, this);
|
|
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
exit_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
1999-02-08 03:49:56 +01:00
|
|
|
}
|
1998-11-09 19:55:33 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
/* If there is a default case, generate the default step. */
|
1999-02-22 04:01:12 +01:00
|
|
|
if (default_idx < net->nitems()) {
|
1999-10-01 05:15:00 +02:00
|
|
|
step_num += 1;
|
1999-02-22 04:01:12 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
os << " bool step_" << step_num << "_();" << endl;
|
1999-02-22 04:01:12 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << step_num
|
|
|
|
|
<< "_() {" << endl;
|
1999-09-08 04:24:39 +02:00
|
|
|
if (net->stat(default_idx))
|
1999-10-01 05:15:00 +02:00
|
|
|
net->stat(default_idx)->emit_proc(os, this);
|
|
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
exit_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
1999-02-22 04:01:12 +01:00
|
|
|
}
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
/* Finally, start the exit step. */
|
|
|
|
|
os << " bool step_" << exit_step << "_();" << endl;
|
|
|
|
|
|
|
|
|
|
defn << "bool " << thread_class_ << "::step_" << exit_step <<
|
|
|
|
|
"_() {" << endl;
|
1999-02-08 03:49:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void target_vvm::proc_condit(ostream&os, const NetCondit*net)
|
|
|
|
|
{
|
1999-10-01 05:15:00 +02:00
|
|
|
string expr = emit_proc_rval(defn, 8, net->expr());
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-02-08 04:55:55 +01:00
|
|
|
unsigned if_step = ++thread_step_;
|
1999-02-08 03:49:56 +01:00
|
|
|
unsigned else_step = ++thread_step_;
|
1999-02-08 04:55:55 +01:00
|
|
|
unsigned out_step = ++thread_step_;
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " if (" << expr << "[0] == V1)" << endl;
|
|
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
if_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " else" << endl;
|
|
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
else_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << if_step << "_();" << endl;
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << if_step <<
|
|
|
|
|
"_() {" << endl;
|
1998-11-07 18:05:05 +01:00
|
|
|
net->emit_recurse_if(os, this);
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
out_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << else_step << "_();" << endl;
|
1999-02-08 03:49:56 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << else_step <<
|
|
|
|
|
"_() {" << endl;
|
1998-11-07 18:05:05 +01:00
|
|
|
net->emit_recurse_else(os, this);
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
out_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
1998-11-09 19:55:33 +01:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
os << " bool step_" << out_step << "_();" << endl;
|
|
|
|
|
|
|
|
|
|
defn << "bool " << thread_class_ << "::step_" << out_step <<
|
|
|
|
|
"_() {" << endl;
|
1998-11-07 18:05:05 +01:00
|
|
|
}
|
|
|
|
|
|
1999-06-19 23:06:16 +02:00
|
|
|
/*
|
|
|
|
|
* 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_;
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
top_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
os << " bool step_" << top_step << "_();" << endl;
|
|
|
|
|
defn << "bool " << thread_class_ << "::step_" << top_step <<
|
|
|
|
|
"_() {" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
net->emit_recurse(os, this);
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
top_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
os << " bool step_" << out_step << "_();" << endl;
|
|
|
|
|
defn << "bool " << thread_class_ << "::step_" << out_step <<
|
|
|
|
|
"_() {" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void target_vvm::proc_repeat(ostream&os, const NetRepeat*net)
|
|
|
|
|
{
|
1999-10-01 05:15:00 +02:00
|
|
|
string expr = emit_proc_rval(defn, 8, net->expr());
|
1999-06-19 23:06:16 +02:00
|
|
|
unsigned top_step = ++thread_step_;
|
|
|
|
|
unsigned out_step = ++thread_step_;
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_" << top_step << "_idx_ = " << expr <<
|
1999-06-19 23:06:16 +02:00
|
|
|
".as_unsigned();" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
top_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
|
|
|
|
|
os << " unsigned step_" << top_step << "_idx_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
os << " bool step_" << top_step << "_();" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << top_step <<
|
|
|
|
|
"_() {" << endl;
|
|
|
|
|
defn << " if (step_" << top_step << "_idx_ == 0) {" << endl;
|
|
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
out_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << " }" << endl;
|
|
|
|
|
defn << " step_" << top_step << "_idx_ -= 1;" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
|
|
|
|
|
net->emit_recurse(os,this);
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
top_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << out_step << "_();" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << out_step <<
|
|
|
|
|
"_() {" << endl;
|
1999-06-19 23:06:16 +02:00
|
|
|
}
|
|
|
|
|
|
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();
|
1999-08-15 03:23:56 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " vpiHandle " << ptmp << "[" << net->nparms() <<
|
1999-08-15 03:23:56 +02:00
|
|
|
"];" << endl;
|
|
|
|
|
for (unsigned idx = 0 ; idx < net->nparms() ; idx += 1) {
|
|
|
|
|
string val;
|
|
|
|
|
if (net->parm(idx)) {
|
1999-10-28 02:47:24 +02:00
|
|
|
val = emit_parm_rval(os, this, net->parm(idx));
|
1999-08-15 03:23:56 +02:00
|
|
|
|
|
|
|
|
} else {
|
1999-10-28 02:47:24 +02:00
|
|
|
val = string("&vpip_null.base");
|
1999-07-03 04:12:51 +02:00
|
|
|
}
|
1999-08-15 03:23:56 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " " << ptmp << "[" << idx << "] = " << val << ";"
|
1999-08-15 03:23:56 +02:00
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-28 02:47:24 +02:00
|
|
|
defn << " vpip_calltask(\"" << net->name() << "\", " <<
|
1999-07-03 04:12:51 +02:00
|
|
|
net->nparms() << ", " << ptmp << ");" << endl;
|
1999-10-28 02:47:24 +02:00
|
|
|
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());
|
2000-01-08 04:09:14 +01:00
|
|
|
defn << " assert(callee_ == 0);" << endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << " callee_ = new " << name << "(this);" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
out_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return false;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << out_step << "_();" << endl;
|
|
|
|
|
defn << "bool " << thread_class_ << "::step_" << out_step <<
|
|
|
|
|
"_() {" << endl;
|
|
|
|
|
defn << " delete callee_;" << endl;
|
2000-01-08 04:09:14 +01:00
|
|
|
defn << " callee_ = 0;" << endl;
|
1999-07-07 06:20:57 +02:00
|
|
|
}
|
|
|
|
|
|
1999-06-07 04:23:31 +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:
|
|
|
|
|
*/
|
1998-11-09 19:55:33 +01:00
|
|
|
void target_vvm::proc_while(ostream&os, const NetWhile*net)
|
|
|
|
|
{
|
1999-06-07 04:23:31 +02:00
|
|
|
unsigned head_step = ++thread_step_;
|
|
|
|
|
unsigned out_step = ++thread_step_;
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
head_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << head_step << "_();" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "// " << net->expr()->get_line() <<
|
1999-06-07 04:23:31 +02:00
|
|
|
": top of while condition." << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << head_step <<
|
|
|
|
|
"_() {" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
string expr = emit_proc_rval(defn, 8, net->expr());
|
|
|
|
|
|
|
|
|
|
defn << "// " << net->expr()->get_line() <<
|
1999-06-07 04:23:31 +02:00
|
|
|
": test while condition." << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " if (" << expr << "[0] != V1) {" << endl;
|
|
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
out_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << " }" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
|
|
|
|
|
net->emit_proc_recurse(os, this);
|
|
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "// " << net->expr()->get_line() <<
|
1999-06-07 04:23:31 +02:00
|
|
|
": end of while loop." << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
head_step << "_;" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << out_step << "_();" << endl;
|
1999-06-07 04:23:31 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "bool " << thread_class_ << "::step_" << out_step <<
|
|
|
|
|
"_() {" << endl;
|
1998-11-09 19:55:33 +01:00
|
|
|
}
|
|
|
|
|
|
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;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
thread_step_ << "_;" << endl;
|
1998-11-10 01:48:31 +01:00
|
|
|
|
|
|
|
|
/* 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 {
|
1999-05-03 03:51:29 +02:00
|
|
|
event.wait(this);
|
1998-11-10 01:48:31 +01:00
|
|
|
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. */
|
|
|
|
|
|
1999-05-03 03:51:29 +02:00
|
|
|
svector<const NetNEvent*>*list = proc->back_list();
|
|
|
|
|
if ((list->count()==1) && ((*list)[0]->type() == NetNEvent::POSITIVE)) {
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " if (" << mangle((*list)[0]->name()) <<
|
1999-05-03 03:51:29 +02:00
|
|
|
".get()[0]==V1) {" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
|
|
|
|
defn << " } else {" << endl;
|
|
|
|
|
defn << " " << mangle(proc->name()) <<
|
1999-05-03 03:51:29 +02:00
|
|
|
".wait(this);" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return false;" << endl;
|
|
|
|
|
defn << " }" << endl;
|
1998-11-10 01:48:31 +01:00
|
|
|
} else {
|
1999-05-03 03:51:29 +02:00
|
|
|
/* 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. */
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " " << mangle(proc->name()) << ".wait(this);" << endl;
|
|
|
|
|
defn << " return false;" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
1999-05-03 03:51:29 +02:00
|
|
|
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << "}" << endl;
|
|
|
|
|
|
|
|
|
|
os << " bool step_" << thread_step_ << "_();" << endl;
|
|
|
|
|
|
|
|
|
|
defn << "bool " << thread_class_ << "::step_" << thread_step_ <<
|
|
|
|
|
"_() {" << endl;
|
1999-05-03 03:51:29 +02:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
proc->emit_proc_recurse(os, this);
|
1999-05-03 03:51:29 +02:00
|
|
|
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;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_" <<
|
1999-09-28 05:11:09 +02:00
|
|
|
thread_step_ << "_;" << endl;
|
1999-12-12 20:47:54 +01:00
|
|
|
defn << " thread_yield(" << proc->delay() << ");" << endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
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) {
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = &" << thread_class_ << "::step_0_;"
|
1999-09-28 05:11:09 +02:00
|
|
|
<< endl;
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " return true;" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
} else {
|
1999-10-01 05:15:00 +02:00
|
|
|
defn << " step_ = 0;" << endl;
|
|
|
|
|
defn << " return false;" << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-10-01 05:15:00 +02: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 $
|
2000-02-13 20:59:33 +01:00
|
|
|
* Revision 1.100 2000/02/13 19:59:33 steve
|
|
|
|
|
* Handle selecting memory words at run time.
|
|
|
|
|
*
|
2000-02-13 20:18:27 +01:00
|
|
|
* Revision 1.99 2000/02/13 19:18:27 steve
|
|
|
|
|
* Accept memory words as parameter to $display.
|
|
|
|
|
*
|
2000-01-18 05:53:57 +01:00
|
|
|
* Revision 1.98 2000/01/18 04:53:57 steve
|
|
|
|
|
* missing break is switch.
|
|
|
|
|
*
|
2000-01-13 07:05:46 +01:00
|
|
|
* Revision 1.97 2000/01/13 06:05:46 steve
|
|
|
|
|
* Add the XNOR operator.
|
|
|
|
|
*
|
2000-01-13 06:11:24 +01:00
|
|
|
* Revision 1.96 2000/01/13 05:11:25 steve
|
|
|
|
|
* Support for multiple VPI modules.
|
|
|
|
|
*
|
2000-01-13 04:35:35 +01:00
|
|
|
* Revision 1.95 2000/01/13 03:35:35 steve
|
|
|
|
|
* Multiplication all the way to simulation.
|
|
|
|
|
*
|
2000-01-08 04:09:14 +01:00
|
|
|
* Revision 1.94 2000/01/08 03:09:14 steve
|
|
|
|
|
* Non-blocking memory writes.
|
|
|
|
|
*
|
2000-01-02 18:57:56 +01:00
|
|
|
* Revision 1.93 2000/01/02 17:57:56 steve
|
|
|
|
|
* It is possible for node to initialize several pins of a signal.
|
|
|
|
|
*
|
1999-12-17 04:38:46 +01:00
|
|
|
* Revision 1.92 1999/12/17 03:38:46 steve
|
|
|
|
|
* NetConst can now hold wide constants.
|
|
|
|
|
*
|
1999-12-16 03:42:14 +01:00
|
|
|
* Revision 1.91 1999/12/16 02:42:15 steve
|
|
|
|
|
* Simulate carry output on adders.
|
|
|
|
|
*
|
1999-12-12 20:47:54 +01:00
|
|
|
* Revision 1.90 1999/12/12 19:47:54 steve
|
|
|
|
|
* Remove the useless vvm_simulation class.
|
|
|
|
|
*
|
1999-12-12 07:03:14 +01:00
|
|
|
* Revision 1.89 1999/12/12 06:03:14 steve
|
|
|
|
|
* Allow memories without indices in expressions.
|
|
|
|
|
*
|
1999-12-05 03:24:08 +01:00
|
|
|
* Revision 1.88 1999/12/05 02:24:09 steve
|
|
|
|
|
* Synthesize LPM_RAM_DQ for writes into memories.
|
|
|
|
|
*
|
1999-12-02 17:58:58 +01:00
|
|
|
* Revision 1.87 1999/12/02 16:58:58 steve
|
|
|
|
|
* Update case comparison (Eric Aardoom).
|
|
|
|
|
*
|
1999-11-29 01:38:27 +01:00
|
|
|
* Revision 1.86 1999/11/29 00:38:27 steve
|
|
|
|
|
* Properly initialize registers.
|
|
|
|
|
*
|
1999-11-29 00:59:22 +01:00
|
|
|
* Revision 1.85 1999/11/28 23:59:22 steve
|
|
|
|
|
* Remove useless tests for NetESignal.
|
|
|
|
|
*
|
1999-11-29 00:42:02 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
1999-11-28 19:05:37 +01:00
|
|
|
* Revision 1.83 1999/11/28 18:05:37 steve
|
|
|
|
|
* Set VPI_MODULE_PATH in the target code, if desired.
|
|
|
|
|
*
|
1999-11-28 02:16:18 +01:00
|
|
|
* Revision 1.82 1999/11/28 01:16:19 steve
|
|
|
|
|
* gate outputs need to set signal values.
|
|
|
|
|
*
|
1999-11-28 01:56:08 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
1999-11-21 02:16:51 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
1999-11-21 01:13:08 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
1999-11-14 21:24:28 +01:00
|
|
|
* 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.
|
|
|
|
|
*
|
1999-11-06 17:00:17 +01:00
|
|
|
* Revision 1.72 1999/11/06 16:00:17 steve
|
|
|
|
|
* Put number constants into a static table.
|
1998-11-04 00:28:49 +01:00
|
|
|
*/
|
|
|
|
|
|