commit 3fb7a053bef0b62ca4b47ad629fca3e789cd2a19 Author: steve Date: Tue Nov 3 23:28:49 1998 +0000 Introduce verilog to CVS. diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 000000000..93d295a19 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,5 @@ +parse.h +parse.cc +parse.cc.output +lexor.cc +vl diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..126dfce14 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ + +CXXFLAGS = -O -g -Wall -Wno-uninitialized + +%.o dep/%.d: %.cc + $(CXX) $(CXXFLAGS) -MD -c $< -o $*.o + mv $*.d dep + +#TT = t-debug.o t-vvm.o +TT = t-verilog.o t-vvm.o + +O = main.o design_dump.o elaborate.o emit.o eval.o lexor.o mangle.o \ +netlist.o parse.o parse_misc.o pform.o pform_dump.o stupid.o verinum.o \ +target.o targets.o Module.o PExpr.o Statement.o $(TT) + +vl: $O + $(CXX) $(CXXFLAGS) -o vl $O + +clean: + rm *.o parse.cc parse.cc.output parse.h dep/*.d lexor.cc + +lexor.o dep/lexor.d: lexor.cc parse.h + +parse.h parse.cc: parse.y + bison --verbose -t -p VL -d parse.y -o parse.cc + mv parse.cc.h parse.h + +lexor.cc: lexor.lex + flex -PVL -s -olexor.cc lexor.lex + +-include $(patsubst %.o, dep/%.d, $O) diff --git a/Module.cc b/Module.cc new file mode 100644 index 000000000..c905d0f6b --- /dev/null +++ b/Module.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: Module.cc,v 1.1 1998/11/03 23:28:51 steve Exp $" +#endif + +# include "Module.h" +# include "PWire.h" + +void Module::add_gate(PGate*gate) +{ + gates_.push_back(gate); +} + +void Module::add_wire(PWire*wire) +{ + wires_.push_back(wire); +} + +void Module::add_behavior(PProcess*b) +{ + behaviors_.push_back(b); +} + +PWire* Module::get_wire(const string&name) +{ + for (list::iterator cur = wires_.begin() + ; cur != wires_.end() + ; cur ++ ) { + + if ((*cur)->name == name) + return *cur; + } + + return 0; +} + + +/* + * $Log: Module.cc,v $ + * Revision 1.1 1998/11/03 23:28:51 steve + * Introduce verilog to CVS. + * + */ + diff --git a/Module.h b/Module.h new file mode 100644 index 000000000..fb45f56dc --- /dev/null +++ b/Module.h @@ -0,0 +1,81 @@ +#ifndef __Module_H +#define __Module_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: Module.h,v 1.1 1998/11/03 23:28:52 steve Exp $" +#endif + +# include +# include +# include +class PGate; +class PWire; +class PProcess; +class Design; + +/* + * A module is a named container and scope. A module holds a bunch of + * semantic quantities such as wires and gates. The module is + * therefore the handle for grasping the described circuit. + */ + +class Module { + public: + explicit Module(const string&name, unsigned nports) + : ports(nports), name_(name) { } + + vector ports; + + const string&get_name() const { return name_; } + + void add_gate(PGate*gate); + void add_wire(PWire*wire); + void add_behavior(PProcess*behave); + + // Find a wire by name. This is used for connecting gates to + // existing wires, etc. + PWire* get_wire(const string&name); + + const list& get_wires() const { return wires_; } + const list& get_gates() const { return gates_; } + const list& get_behaviors() const { return behaviors_; } + + void elaborate(Design*, const string&path) const; + + private: + const string name_; + + list wires_; + list gates_; + list behaviors_; + + private: // Not implemented + Module(const Module&); + Module& operator= (const Module&); +}; + + +/* + * $Log: Module.h,v $ + * Revision 1.1 1998/11/03 23:28:52 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/PExpr.cc b/PExpr.cc new file mode 100644 index 000000000..db1e00a6b --- /dev/null +++ b/PExpr.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1998 Stephen Williams + * + * 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) +#ident "$Id: PExpr.cc,v 1.1 1998/11/03 23:28:53 steve Exp $" +#endif + +# include "PExpr.h" + +PExpr::~PExpr() +{ +} + +/* + * $Log: PExpr.cc,v $ + * Revision 1.1 1998/11/03 23:28:53 steve + * Introduce verilog to CVS. + * + */ + diff --git a/PExpr.h b/PExpr.h new file mode 100644 index 000000000..d299872b9 --- /dev/null +++ b/PExpr.h @@ -0,0 +1,144 @@ +#ifndef __PExpr_H +#define __PExpr_H +/* + * Copyright (c) 1998 Stephen Williams + * + * 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) +#ident "$Id: PExpr.h,v 1.1 1998/11/03 23:28:54 steve Exp $" +#endif + +# include +# include "verinum.h" + +class Design; +class NetNet; +class NetExpr; + +/* + * The PExpr class hierarchy supports the description of + * expressions. The parser can generate expression objects from the + * source, possibly reducing things that it knows how to reduce. + * + * The elaborate_net method is used by structural elaboration to build + * up a netlist interpretation of the expression. + */ + +class PExpr { + public: + virtual ~PExpr(); + + virtual void dump(ostream&) const; + virtual NetNet* elaborate_net(Design*des, const string&path) const; + virtual NetExpr*elaborate_expr(Design*des, const string&path) const; + + // This attempts to evaluate a constant expression, and return + // a verinum as a result. If the expression cannot be + // evaluated, return 0. + virtual verinum* eval_const() const; +}; + +ostream& operator << (ostream&, const PExpr&); + +class PEIdent : public PExpr { + + public: + explicit PEIdent(const string&s) + : text_(s), msb_(0), lsb_(0) { } + + virtual void dump(ostream&) const; + virtual NetNet* elaborate_net(Design*des, const string&path) const; + virtual NetExpr*elaborate_expr(Design*des, const string&path) const; + + + private: + string text_; + + public: + // Use these to support bit- and part-select operators. + PExpr*msb_; + PExpr*lsb_; +}; + +class PENumber : public PExpr { + + public: + explicit PENumber(verinum*vp) + : value_(vp) { assert(vp); } + ~PENumber() { delete value_; } + + const verinum& value() const { return *value_; } + + virtual void dump(ostream&) const; + virtual NetExpr*elaborate_expr(Design*des, const string&path) const; + virtual verinum* eval_const() const; + + private: + verinum*const value_; +}; + +class PEString : public PExpr { + + public: + explicit PEString(const string&s) + : text_(s) { } + + string value() const { return text_; } + virtual void dump(ostream&) const; + virtual NetExpr*elaborate_expr(Design*des, const string&path) const; + + private: + const string text_; +}; + +class PEUnary : public PExpr { + + public: + explicit PEUnary(char op, PExpr*ex) + : op_(op), expr_(ex) { } + + virtual void dump(ostream&out) const; + virtual NetNet* elaborate_net(Design*des, const string&path) const; + virtual NetExpr*elaborate_expr(Design*des, const string&path) const; + + private: + char op_; + PExpr*expr_; +}; + +class PEBinary : public PExpr { + + public: + explicit PEBinary(char op, PExpr*l, PExpr*r) + : op_(op), left_(l), right_(r) { } + + virtual void dump(ostream&out) const; + virtual NetNet* elaborate_net(Design*des, const string&path) const; + + private: + char op_; + PExpr*left_; + PExpr*right_; +}; + +/* + * $Log: PExpr.h,v $ + * Revision 1.1 1998/11/03 23:28:54 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/PGate.h b/PGate.h new file mode 100644 index 000000000..c0965af06 --- /dev/null +++ b/PGate.h @@ -0,0 +1,136 @@ +#ifndef __PGate_H +#define __PGate_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: PGate.h,v 1.1 1998/11/03 23:28:54 steve Exp $" +#endif + +# include +class PExpr; +class Design; + +/* + * A PGate represents a Verilog gate. The gate has a name and other + * properties, and a set of pins that connect to wires. It is known at + * the time a gate is constructed how many pins the gate has. + * + * This pins of a gate are connected to expressions. The elaboration + * step will need to convert expressions to a network of gates in + * order to elaborate expression inputs, but that can easily be done. + */ +class PGate { + + public: + explicit PGate(const string&name, const vector&pins, long del) + : name_(name), delay_(del), pins_(pins) { } + + virtual ~PGate() { } + + const string& get_name() const { return name_; } + + long get_delay() const { return delay_; } + + unsigned pin_count() const { return pins_.size(); } + const PExpr*pin(unsigned idx) const { return pins_[idx]; } + + virtual void dump(ostream&out) const; + virtual void elaborate(Design*des, const string&path) const; + + protected: + void dump_pins(ostream&out) const; + + private: + const string name_; + const unsigned long delay_; + const vector pins_; + + private: // not implemented + PGate(const PGate&); + PGate& operator= (const PGate&); +}; + +/* A continuous assignment has a single output and a single input. The + input is passed directly to the output. This is different from a + BUF because elaboration may need to turn this into a vector of + gates. */ +class PGAssign : public PGate { + + public: + explicit PGAssign(const vector&pins) + : PGate("", pins, 0) { assert(pins.size() == 2); } + + void dump(ostream&out) const; + virtual void elaborate(Design*des, const string&path) const; + + private: +}; + + +/* The Builtin class is specifically a gate with one of the builtin + types. The parser recognizes these types during parse. These types + have special properties that allow them to be treated specially. */ +class PGBuiltin : public PGate { + + public: + enum Type { AND, NAND, OR, NOR, XOR, XNOR, BUF, BUFIF0, BUFIF1, + NOT, NOTIF0, NOTIF1, PULLDOWN, PULLUP, NMOS, RNMOS, + PMOS, RPMOS, CMOS, RCMOS, TRAN, RTRAN, TRANIF0, + TRANIF1, RTRANIF0, RTRANIF1 }; + + public: + explicit PGBuiltin(Type t, const string&name, + const vector&pins, long del = 0) + : PGate(name, pins, del), type_(t) + { } + + Type type() const { return type_; } + + virtual void dump(ostream&out) const; + virtual void elaborate(Design*, const string&path) const; + + private: + Type type_; +}; + +/* + * This kind of gate is an instantiation of a module. The stored type + * is the name of a module definition somewhere in the pform. + */ +class PGModule : public PGate { + + public: + explicit PGModule(const string&type, const string&name, + const vector&pins) + : PGate(name, pins, 0), type_(type) { } + + virtual void dump(ostream&out) const; + virtual void elaborate(Design*, const string&path) const; + + private: + string type_; +}; + +/* + * $Log: PGate.h,v $ + * Revision 1.1 1998/11/03 23:28:54 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/PWire.h b/PWire.h new file mode 100644 index 000000000..a65aa6ea8 --- /dev/null +++ b/PWire.h @@ -0,0 +1,65 @@ +#ifndef __PWire_H +#define __PWire_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: PWire.h,v 1.1 1998/11/03 23:28:55 steve Exp $" +#endif + +# include "netlist.h" +class ostream; +class PExpr; +class Design; + +/* + * Wires include nets, registers and ports. A net or register becomes + * a port by declaration, so ports are not seperate. The module + * identifies a port by keeping it in its port list. + */ +class PWire { + + public: + PWire(const string&n, NetNet::Type t =NetNet::IMPLICIT) + : name(n), type(t), port_type(NetNet::NOT_A_PORT), msb(0), lsb(0) + { } + + string name; + NetNet::Type type; + NetNet::PortType port_type; + + PExpr*msb; + PExpr*lsb; + + // Write myself to the specified stream. + void dump(ostream&out) const; + + void elaborate(Design*, const string&path) const; + + private: // not implemented + PWire(const PWire&); + PWire& operator= (const PWire&); +}; + +/* + * $Log: PWire.h,v $ + * Revision 1.1 1998/11/03 23:28:55 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/Statement.cc b/Statement.cc new file mode 100644 index 000000000..70523e78e --- /dev/null +++ b/Statement.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: Statement.cc,v 1.1 1998/11/03 23:28:55 steve Exp $" +#endif + +# include "Statement.h" +# include "PExpr.h" + +Statement::~Statement() +{ +} + +PBlock::PBlock(BL_TYPE t, const list&st) +: bl_type_(t) +{ + nlist_ = st.size(); + list_ = new Statement*[nlist_]; + + list::const_iterator s = st.begin(); + for (unsigned idx = 0 ; s != st.end() ; s ++, idx += 1 ) { + list_[idx] = *s; + } +} + +PBlock::~PBlock() +{ + for (unsigned idx = 0 ; idx < nlist_ ; idx += 1) + delete list_[idx]; + + delete[]list_; +} + +PCallTask::PCallTask(const string&n, const list&p) +: name_(n), nparms_(p.size()), parms_(nparms_?new PExpr*[nparms_]:0) +{ + list::const_iterator s = p.begin(); + for (unsigned idx = 0 ; s != p.end() ; s++, idx += 1) { + parms_[idx] = *s; + } + +} + + +/* + * $Log: Statement.cc,v $ + * Revision 1.1 1998/11/03 23:28:55 steve + * Introduce verilog to CVS. + * + */ + diff --git a/Statement.h b/Statement.h new file mode 100644 index 000000000..e52878a54 --- /dev/null +++ b/Statement.h @@ -0,0 +1,193 @@ +#ifndef __Statement_H +#define __Statement_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: Statement.h,v 1.1 1998/11/03 23:28:56 steve Exp $" +#endif + +# include +# include +# include "netlist.h" +class PExpr; +class Statement; + +/* + * The PProcess is the root of a behavioral process. Each process gets + * one of these, which contains its type (initial or always) and a + * pointer to the single statement that is the process. A module may + * have several concurrent processes. + */ +class PProcess { + + public: + enum Type { PR_INITIAL, PR_ALWAYS }; + + PProcess(Type t, Statement*st) + : type_(t), statement_(st) { } + + Type type() const { return type_; } + Statement*statement() { return statement_; } + + virtual void dump(ostream&out, unsigned ind) const; + + private: + Type type_; + Statement*statement_; +}; + +/* + * The PProcess is a process, the Statement is the actual action. In + * fact, the Statement class is abstract and represents all the + * possible kinds of statements that exist in Verilog. + */ +class Statement { + + public: + Statement() { } + virtual ~Statement() =0; + + virtual void dump(ostream&out, unsigned ind) const; + virtual NetProc* elaborate(Design*des, const string&path) const; +}; + +/* + * Assignment statements of the various forms are handled by this + * type. The rvalue is an expression. The lvalue needs to be figured + * out by the parser as much as possible. + */ +class PAssign : public Statement { + + public: + explicit PAssign(const string&name, PExpr*ex) + : to_name_(name), expr_(ex) { } + + string lval() const { return to_name_; } + const PExpr* get_expr() const { return expr_; } + + virtual void dump(ostream&out, unsigned ind) const; + virtual NetProc* elaborate(Design*des, const string&path) const; + + private: + const string to_name_; + PExpr*const expr_; +}; + +/* + * A block statement is an ordered list of statements that make up the + * block. The block can be sequential or parallel, which only affects + * how the block is interpreted. The parser collects the list of + * statements before constructing this object, so it knows a priori + * what is contained. + */ +class PBlock : public Statement { + + public: + enum BL_TYPE { BL_SEQ, BL_PAR }; + + explicit PBlock(BL_TYPE t, const list&st); + ~PBlock(); + + BL_TYPE bl_type() const { return bl_type_; } + + unsigned size() const { return nlist_; } + const Statement*stat(unsigned idx) const { return list_[idx]; } + + virtual void dump(ostream&out, unsigned ind) const; + virtual NetProc* elaborate(Design*des, const string&path) const; + + private: + const BL_TYPE bl_type_; + unsigned nlist_; + Statement**list_; +}; + +class PCallTask : public Statement { + + public: + explicit PCallTask(const string&n, const list&parms); + + string name() const { return name_; } + + unsigned nparms() const { return nparms_; } + + PExpr*&parm(unsigned idx) + { assert(idx < nparms_); + return parms_[idx]; + } + + PExpr* parm(unsigned idx) const + { assert(idx < nparms_); + return parms_[idx]; + } + + virtual void dump(ostream&out, unsigned ind) const; + virtual NetProc* elaborate(Design*des, const string&path) const; + + private: + const string name_; + const unsigned nparms_; + PExpr**const parms_; +}; + +class PDelayStatement : public Statement { + + public: + PDelayStatement(PExpr*d, Statement*st) + : delay_(d), statement_(st) { } + + virtual void dump(ostream&out, unsigned ind) const; + virtual NetProc* elaborate(Design*des, const string&path) const; + + private: + PExpr*delay_; + Statement*statement_; +}; + +class PEventStatement : public Statement { + + public: + + PEventStatement(NetPEvent::Type t, PExpr*ee) + : type_(t), expr_(ee), statement_(0) { } + + void set_statement(Statement*st) { statement_ = st; } + + virtual void dump(ostream&out, unsigned ind) const; + virtual NetProc* elaborate(Design*des, const string&path) const; + + private: + NetPEvent::Type type_; + PExpr*expr_; + Statement*statement_; +}; + +class PNoop : public Statement { + + public: + PNoop() { } +}; + +/* + * $Log: Statement.h,v $ + * Revision 1.1 1998/11/03 23:28:56 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/design_dump.cc b/design_dump.cc new file mode 100644 index 000000000..7196f72db --- /dev/null +++ b/design_dump.cc @@ -0,0 +1,323 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: design_dump.cc,v 1.1 1998/11/03 23:28:56 steve Exp $" +#endif + +/* + * This file contains all the dump methods of the netlist classes. + */ +# include +# include +# include +# include "netlist.h" + +static ostream& operator<< (ostream&o, NetNet::Type t) +{ + switch (t) { + case NetNet::IMPLICIT: + o << "implicit wire"; + break; + case NetNet::WIRE: + o << "wire"; + break; + case NetNet::REG: + o << "reg"; + break; + } + return o; +} + +static ostream& operator<< (ostream&o, NetBlock::Type t) +{ + switch (t) { + case NetBlock::SEQU: + o << "begin"; + break; + case NetBlock::PARA: + o << "fork"; + break; + } + return o; +} + +/* Dump a net. This can be a wire or register. */ +void NetNet::dump_net(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << type() << ": " << name() << "[" << + pin_count() << "]"; + if (local_flag_) + o << " (local)"; + o << " #(" << delay1() << "," << delay2() << "," << delay3() << + ")" << endl; +} + + +/* Dump a NetNode and its pins. Dump what I know about the netnode on + the first line, then list all the pins, with the name of the + connected signal. */ +void NetNode::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "node: "; + o << typeid(*this).name() << " #(" << delay1() + << "," << delay2() << "," << delay3() << ") " << name() + << endl; + + dump_node_pins(o, ind+4); +} + +/* This is the generic dumping of all the signals connected to each + pin of the object. The "this" object is not printed, only the + signals connected to this. */ +void NetObj::dump_node_pins(ostream&o, unsigned ind) const +{ + for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { + o << setw(ind) << "" << idx << ":"; + + unsigned cpin; + const NetObj*cur; + for (pin(idx).next_link(cur, cpin) + ; (cur != this) || (cpin != idx) + ; cur->pin(cpin).next_link(cur, cpin)) { + + const NetNet*sig = dynamic_cast(cur); + if (sig) o << " " << sig->name() << "[" << cpin << "]"; + } + o << endl; + } +} + +void NetAssign::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Procedural assign: " << *rval_ << endl; + dump_node_pins(o, ind+4); +} + +void NetBUFZ::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "BUFZ: " << name() << endl; + dump_node_pins(o, ind+4); +} + +void NetLogic::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "logic: "; + switch (type_) { + case AND: + o << "and"; + break; + case NAND: + o << "nand"; + break; + case NOR: + o << "nor"; + break; + case NOT: + o << "not"; + break; + case OR: + o << "or"; + break; + case XOR: + o << "xor"; + break; + } + o << " #(" << delay1() + << "," << delay2() << "," << delay3() << ") " << name() + << endl; + + dump_node_pins(o, ind+4); +} + +void NetPEvent::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "event: "; + switch (edge_) { + case ANYEDGE: + break; + case POSEDGE: + o << "posedge "; + break; + case NEGEDGE: + o << "negedge "; + break; + } + + o << name() << endl; + + dump_node_pins(o, ind+4); +} + +void NetProcTop::dump(ostream&o, unsigned ind) const +{ + switch (type_) { + case NetProcTop::KINITIAL: + o << "initial" << endl; + break; + case NetProcTop::KALWAYS: + o << "always" << endl; + break; + } + + statement_->dump(o, ind+2); +} + +/* Dump an assignment statement */ +void NetAssign::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << lval_->name() << " = "; + rval_->dump(o); + o << ";" << endl; +} + +/* Dump a block statement */ +void NetBlock::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << type_ << endl; + + if (last_) { + const NetProc*cur = last_; + do { + cur = cur->next_; + cur->dump(o, ind+4); + } while (cur != last_); + } + + o << setw(ind) << "" << "end" << endl; +} + +void NetPDelay::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "#" << delay_ << endl; + statement_->dump(o, ind+2); +} + +void NetPEvent::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "@"; + switch (edge_) { + case NEGEDGE: + o << "(negedge " << name() << ")"; + break; + case POSEDGE: + o << "(posedge " << name() << ")"; + break; + case ANYEDGE: + o << name(); + break; + } + o << endl; + + statement_->dump(o, ind+2); +} + + + +void NetTask::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << name_; + + if (nparms_ > 0) { + o << "("; + if (parms_[0]) + parms_[0]->dump(o); + + for (unsigned idx = 1 ; idx < nparms_ ; idx += 1) { + o << ", "; + if (parms_[idx]) + parms_[idx]->dump(o); + } + + o << ")"; + } + o << ";" << endl; +} + +/* Dump a statement type that someone didn't write a dump for. */ +void NetProc::dump(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "// " << typeid(*this).name() << endl; +} + +/* Dump an expression that noone wrote a dump method for. */ +void NetExpr::dump(ostream&o) const +{ + o << "(?)"; +} + +void NetEConst::dump(ostream&o) const +{ + if (value_.is_string()) + o << "\"" << value_.as_string() << "\""; + else + o << value_; +} + +void NetEIdent::dump(ostream&o) const +{ + o << name_; +} + +void NetEUnary::dump(ostream&o) const +{ + o << op_ << "("; + expr_->dump(o); + o << ")"; +} + +void Design::dump(ostream&o) const +{ + + o << "ELABORATED SIGNALS:" << endl; + + // Dump the signals, + { + NetNet*cur = signals_->sig_next_; + do { + cur->dump_net(o, 0); + cur = cur->sig_next_; + } while (cur != signals_->sig_next_); + } + + o << "ELABORATED NODES:" << endl; + + // dump the nodes, + { + NetNode*cur = nodes_->node_next_; + do { + cur->dump_node(o, 0); + cur = cur->node_next_; + } while (cur != nodes_->node_next_); + } + + o << "ELABORATED PROCESSES:" << endl; + + // Dump the processes. + for (const NetProcTop*idx = procs_ ; idx ; idx = idx->next_) + idx->dump(o, 0); + +} + +/* + * $Log: design_dump.cc,v $ + * Revision 1.1 1998/11/03 23:28:56 steve + * Introduce verilog to CVS. + * + */ + diff --git a/elaborate.cc b/elaborate.cc new file mode 100644 index 000000000..53823d7ab --- /dev/null +++ b/elaborate.cc @@ -0,0 +1,605 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: elaborate.cc,v 1.1 1998/11/03 23:28:56 steve Exp $" +#endif + +/* + * Elaboration takes as input a complete parse tree and the name of a + * root module, and generates as output the elaborated design. This + * elaborated design is presented as a Module, which does not + * reference any other modules. It is entirely self contained. + */ + +# include +# include +# include "pform.h" +# include "netlist.h" + +static string local_symbol(const string&path) +{ + static unsigned counter = 0; + string result = "_L"; + + strstream res; + res << "_L" << (counter++) << ends; + return path + "." + res.str(); +} + +static void do_assign(Design*des, const string&path, + NetNet*lval, NetNet*rval) +{ + assert(lval->pin_count() == rval->pin_count()); + const unsigned pin_count = lval->pin_count(); + + if (NetTmp* tmp = dynamic_cast(rval)) { + + for (unsigned idx = 0 ; idx < pin_count ; idx += 1) + connect(lval->pin(idx), tmp->pin(idx)); + delete tmp; + + if (tmp = dynamic_cast(lval)) + delete tmp; + + } else if (NetTmp* tmp = dynamic_cast(lval)) { + + for (unsigned idx = 0 ; idx < pin_count ; idx += 1) + connect(tmp->pin(idx), rval->pin(idx)); + delete tmp; + + } else if (rval->local_flag()) { + + for (unsigned idx = 0 ; idx < pin_count ; idx += 1) + connect(lval->pin(idx), rval->pin(idx)); + delete rval; + + } else if (lval->local_flag()) { + + for (unsigned idx = 0 ; idx < pin_count ; idx += 1) + connect(lval->pin(idx), rval->pin(idx)); + delete lval; + + } else for (unsigned idx = 0 ; idx < pin_count ; idx += 1) { + NetBUFZ*cur = new NetBUFZ(local_symbol(path)); + + connect(cur->pin(0), lval->pin(idx)); + connect(cur->pin(1), rval->pin(idx)); + + des->add_node(cur); + } +} + + + // Urff, I don't like this global variable. I *will* figure out a + // way to get rid of it. But, for now the PGModule::elaborate method + // needs it to find the module definition. +static const list* modlist = 0; + +/* Elaborate a source wire. Generally pretty easy. */ +void PWire::elaborate(Design*des, const string&path) const +{ + NetNet::Type wtype = type; + if (wtype == NetNet::IMPLICIT) + wtype = NetNet::WIRE; + + unsigned wid = 1; + + if (msb && lsb) { + verinum*mval = msb->eval_const(); + assert(mval); + verinum*lval = lsb->eval_const(); + assert(lval); + + long mnum = mval->as_long(); + long lnum = lval->as_long(); + delete mval; + delete lval; + + if (mnum > lnum) + wid = mnum - lnum + 1; + else + wid = lnum - mnum + 1; + + } else if (msb) { + verinum*val = msb->eval_const(); + assert(val); + assert(val->as_ulong() > 0); + wid = val->as_ulong(); + } + + NetNet*sig = new NetNet(path + "." + name, wtype, wid); + sig->port_type(port_type); + des->add_signal(sig); +} + +void PGate::elaborate(Design*des, const string&path) const +{ + cerr << "what kind of gate? " << typeid(*this).name() << endl; +} + +/* Elaborate the continuous assign. (This is *not* the procedural + assign.) Elaborate the lvalue and rvalue, and do the assignment. */ +void PGAssign::elaborate(Design*des, const string&path) const +{ + NetNet*lval = pin(0)->elaborate_net(des, path); + NetNet*rval = pin(1)->elaborate_net(des, path); + assert(lval && rval); + + do_assign(des, path, lval, rval); +} + +/* Elaborate a Builtin gate. These normally get translated into + NetLogic nodes that reflect the particular logic function. */ +void PGBuiltin::elaborate(Design*des, const string&path) const +{ + NetLogic*cur = 0; + + switch (type()) { + case AND: + cur = new NetLogic(get_name(), pin_count(), NetLogic::AND); + break; + case NAND: + cur = new NetLogic(get_name(), pin_count(), NetLogic::NAND); + break; + case NOR: + cur = new NetLogic(get_name(), pin_count(), NetLogic::NOR); + break; + case NOT: + cur = new NetLogic(get_name(), pin_count(), NetLogic::NOT); + break; + case OR: + cur = new NetLogic(get_name(), pin_count(), NetLogic::OR); + break; + case XOR: + cur = new NetLogic(get_name(), pin_count(), NetLogic::XOR); + break; + } + + assert(cur); + cur->delay1(get_delay()); + cur->delay2(get_delay()); + cur->delay3(get_delay()); + des->add_node(cur); + + for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { + const PExpr*ex = pin(idx); + NetNet*sig = ex->elaborate_net(des, path); + assert(sig); + connect(cur->pin(idx), sig->pin(0)); + if (NetTmp*tmp = dynamic_cast(sig)) + delete tmp; + } +} + +/* + * Instantiate a module by recursively elaborating it. Set the path of + * the recursive elaboration so that signal names get properly + * set. Connect the ports of the instantiated module to the signals of + * the parameters. This is done with BUFZ gates so that they look just + * like continuous assignment connections. + */ +void PGModule::elaborate(Design*des, const string&path) const +{ + // Look for the module type + Module*rmod = 0; + for (list::const_iterator mod = modlist->begin() + ; mod != modlist->end() + ; mod ++ ) { + + if ((*mod)->get_name() == type_) { + rmod = *mod; + break; + } + } + + if (rmod == 0) { + cerr << "Unknown module: " << type_ << endl; + return; + } + + // Elaborate this instance of the module. The recursive + // elaboration causes the module to generate a netlist with + // the ports represented by NetNet objects. I will find them + // later. + rmod->elaborate(des, path + "." + get_name()); + + // Now connect the ports of the newly elaborated designs to + // the expressions that are the instantiation parameters. + + assert(pin_count() == rmod->ports.size()); + + for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { + NetNet*sig = pin(idx)->elaborate_net(des, path); + if (sig == 0) { + cerr << "Expression too complicated for elaboration." << endl; + continue; + } + + assert(sig); + NetNet*prt = des->find_signal(path + "." + get_name() + "." + + rmod->ports[idx]->name); + assert(prt); + + assert(prt->pin_count() == sig->pin_count()); + switch (prt->port_type()) { + case NetNet::PINPUT: + do_assign(des, path, prt, sig); + break; + case NetNet::POUTPUT: + do_assign(des, path, sig, prt); + break; + default: + assert(0); + } + + if (NetTmp*tmp = dynamic_cast(sig)) + delete tmp; + } +} + +NetNet* PExpr::elaborate_net(Design*des, const string&path) const +{ + cerr << "Don't know how to elaborate `" << *this << "' as gates." << endl; + return 0; +} + +/* + * Elaborating binary operations generally involves elaborating the + * left and right expressions, then making an output wire and + * connecting the lot together with the right kind of gate. + */ +NetNet* PEBinary::elaborate_net(Design*des, const string&path) const +{ + NetNet*lsig = left_->elaborate_net(des, path), + *rsig = right_->elaborate_net(des, path); + if (lsig == 0) { + cerr << "Cannot elaborate "; + left_->dump(cerr); + cerr << endl; + return 0; + } + if (rsig == 0) { + cerr << "Cannot elaborate "; + right_->dump(cerr); + cerr << endl; + return 0; + } + + NetNet*osig; + NetLogic*gate; + + switch (op_) { + case '^': // XOR + assert(lsig->pin_count() == 1); + assert(rsig->pin_count() == 1); + gate = new NetLogic(local_symbol(path), 3, NetLogic::XOR); + connect(gate->pin(1), lsig->pin(0)); + connect(gate->pin(2), rsig->pin(0)); + osig = new NetNet(local_symbol(path), NetNet::WIRE); + osig->local_flag(true); + connect(gate->pin(0), osig->pin(0)); + des->add_signal(osig); + des->add_node(gate); + break; + + case '&': // AND + assert(lsig->pin_count() == 1); + assert(rsig->pin_count() == 1); + gate = new NetLogic(local_symbol(path), 3, NetLogic::AND); + connect(gate->pin(1), lsig->pin(0)); + connect(gate->pin(2), rsig->pin(0)); + osig = new NetNet(local_symbol(path), NetNet::WIRE); + osig->local_flag(true); + connect(gate->pin(0), osig->pin(0)); + des->add_signal(osig); + des->add_node(gate); + break; + + default: + cerr << "Unhandled BINARY '" << op_ << "'" << endl; + osig = 0; + } + + if (NetTmp*tmp = dynamic_cast(lsig)) + delete tmp; + if (NetTmp*tmp = dynamic_cast(rsig)) + delete tmp; + + return osig; +} + +NetNet* PEIdent::elaborate_net(Design*des, const string&path) const +{ + NetNet*sig = des->find_signal(path+"."+text_); + + if (msb_ && lsb_) { + verinum*mval = msb_->eval_const(); + assert(mval); + verinum*lval = lsb_->eval_const(); + assert(lval); + unsigned midx = sig->sb_to_idx(mval->as_long()); + unsigned lidx = sig->sb_to_idx(lval->as_long()); + + if (midx >= lidx) { + NetTmp*tmp = new NetTmp(midx-lidx+1); + for (unsigned idx = lidx ; idx <= midx ; idx += 1) + connect(tmp->pin(idx-lidx), sig->pin(idx)); + + sig = tmp; + + } else { + NetTmp*tmp = new NetTmp(lidx-midx+1); + for (unsigned idx = lidx ; idx >= midx ; idx -= 1) + connect(tmp->pin(idx-midx), sig->pin(idx)); + + sig = tmp; + } + + } else if (msb_) { + verinum*mval = msb_->eval_const(); + assert(mval); + unsigned idx = sig->sb_to_idx(mval->as_long()); + NetTmp*tmp = new NetTmp(1); + connect(tmp->pin(0), sig->pin(idx)); + sig = tmp; + } + + return sig; +} + +NetNet* PEUnary::elaborate_net(Design*des, const string&path) const +{ + NetNet* sub_sig = expr_->elaborate_net(des, path); + assert(sub_sig); + + NetNet* sig; + NetLogic*gate; + switch (op_) { + case '~': // Bitwise NOT + assert(sub_sig->pin_count() == 1); + sig = new NetNet(local_symbol(path), NetNet::WIRE); + sig->local_flag(true); + gate = new NetLogic(local_symbol(path), 2, NetLogic::NOT); + connect(gate->pin(0), sig->pin(0)); + connect(gate->pin(1), sub_sig->pin(0)); + des->add_signal(sig); + des->add_node(gate); + break; + + case '&': // Reduction AND + sig = new NetNet(local_symbol(path), NetNet::WIRE); + sig->local_flag(true); + gate = new NetLogic(local_symbol(path), + 1+sub_sig->pin_count(), + NetLogic::AND); + connect(gate->pin(0), sig->pin(0)); + for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1) + connect(gate->pin(idx+1), sub_sig->pin(idx)); + + des->add_signal(sig); + des->add_node(gate); + break; + + default: + cerr << "Unhandled UNARY '" << op_ << "'" << endl; + sig = 0; + } + + if (NetTmp*tmp = dynamic_cast(sub_sig)) + delete tmp; + + return sig; +} + +NetExpr* PENumber::elaborate_expr(Design*des, const string&path) const +{ + assert(value_); + return new NetEConst(*value_); +} + +NetExpr* PEString::elaborate_expr(Design*des, const string&path) const +{ + return new NetEConst(value()); +} + +NetExpr*PEIdent::elaborate_expr(Design*des, const string&path) const +{ + if (text_[0] == '$') + return new NetEIdent(text_); + else + return new NetEIdent(path+"."+text_); +} + +NetExpr* PExpr::elaborate_expr(Design*des, const string&path) const +{ + cerr << "Cannot elaborate expression: " << *this << endl; + return new NetEConst(verinum()); +} + +NetExpr* PEUnary::elaborate_expr(Design*des, const string&path) const +{ + return new NetEUnary(op_, expr_->elaborate_expr(des, path)); +} + +NetProc* Statement::elaborate(Design*des, const string&path) const +{ + cerr << "What kind of statement? " << typeid(*this).name() << endl; + NetProc*cur = new NetProc; + return cur; +} + +NetProc* PAssign::elaborate(Design*des, const string&path) const +{ + NetNet*reg = des->find_signal(path+"."+lval()); + if (reg == 0) { + cerr << "Could not match signal: " << lval() << endl; + return new NetProc; + } + assert(reg); + assert(reg->type() == NetNet::REG); + assert(expr_); + + NetExpr*rval = expr_->elaborate_expr(des, path); + assert(rval); + + NetAssign*cur = new NetAssign(reg, rval); + des->add_node(cur); + + return cur; +} + +NetProc* PBlock::elaborate(Design*des, const string&path) const +{ + NetBlock*cur = new NetBlock(NetBlock::SEQU); + for (unsigned idx = 0 ; idx < size() ; idx += 1) { + cur->append(stat(idx)->elaborate(des, path)); + } + + return cur; +} + +NetProc* PCallTask::elaborate(Design*des, const string&path) const +{ + NetTask*cur = new NetTask(name(), nparms()); + + for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { + PExpr*ex = parm(idx); + cur->parm(idx, ex? ex->elaborate_expr(des, path) : 0); + } + + return cur; +} + +NetProc* PDelayStatement::elaborate(Design*des, const string&path) const +{ + verinum*num = delay_->eval_const(); + assert(num); + + unsigned long val = num->as_ulong(); + return new NetPDelay(val, statement_->elaborate(des, path)); +} + +/* + * An event statement gets elaborated as a gate net that drives a + * special node, the NetPEvent. The NetPEvent is also a NetProc class + * becuase execution flows through it. Thus, the NetPEvent connects + * the structural and the behavioral. + */ +NetProc* PEventStatement::elaborate(Design*des, const string&path) const +{ + NetProc*enet = statement_->elaborate(des, path); + NetPEvent*ev = new NetPEvent(local_symbol(path), type_, enet); + + NetNet*expr = expr_->elaborate_net(des, path); + if (expr == 0) { + cerr << "Failed to elaborate expression: "; + expr_->dump(cerr); + cerr << endl; + exit(1); + } + assert(expr); + connect(ev->pin(0), expr->pin(0)); + + des->add_node(ev); + return ev; +} + +void Module::elaborate(Design*des, const string&path) const +{ + // Get all the explicitly declared wires of the module and + // start the signals list with them. + const list&wl = get_wires(); + + for (list::const_iterator wt = wl.begin() + ; wt != wl.end() + ; wt ++ ) { + + (*wt)->elaborate(des, path); + } + + // Get all the gates of the module and elaborate them by + // connecting them to the signals. The gate may be simple or + // complex. + const list&gl = get_gates(); + + for (list::const_iterator gt = gl.begin() + ; gt != gl.end() + ; gt ++ ) { + + (*gt)->elaborate(des, path); + } + + // Elaborate the behaviors, making processes out of them. + const list&sl = get_behaviors(); + + for (list::const_iterator st = sl.begin() + ; st != sl.end() + ; st ++ ) { + + NetProc*cur = (*st)->statement()->elaborate(des, path); + NetProcTop*top; + switch ((*st)->type()) { + case PProcess::PR_INITIAL: + top = new NetProcTop(NetProcTop::KINITIAL, cur); + break; + case PProcess::PR_ALWAYS: + top = new NetProcTop(NetProcTop::KALWAYS, cur); + break; + } + + des->add_process(top); + } +} + +Design* elaborate(const list&modules, const string&root) +{ + // Look for the root module in the list. + Module*rmod = 0; + for (list::const_iterator mod = modules.begin() + ; mod != modules.end() + ; mod ++ ) { + + if ((*mod)->get_name() == root) { + rmod = *mod; + break; + } + } + + if (rmod == 0) + return 0; + + // This is the output design. I fill it in as I scan the root + // module and elaborate what I find. + Design*des = new Design; + + modlist = &modules; + rmod->elaborate(des, root); + modlist = 0; + + return des; +} + + +/* + * $Log: elaborate.cc,v $ + * Revision 1.1 1998/11/03 23:28:56 steve + * Introduce verilog to CVS. + * + */ + diff --git a/emit.cc b/emit.cc new file mode 100644 index 000000000..050a5b956 --- /dev/null +++ b/emit.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: emit.cc,v 1.1 1998/11/03 23:28:57 steve Exp $" +#endif + +/* + * The emit function is called to generate the output required of the + * target. + */ +# include "target.h" +# include "netlist.h" +# include +# include +# include + +void NetNode::emit_node(ostream&o, struct target_t*tgt) const +{ + cerr << "EMIT: Gate type? " << typeid(*this).name() << endl; +} + +void NetLogic::emit_node(ostream&o, struct target_t*tgt) const +{ + tgt->logic(o, this); +} + +void NetAssign::emit_node(ostream&o, struct target_t*tgt) const +{ + tgt->net_assign(o, this); +} + +void NetPEvent::emit_node(ostream&o, struct target_t*tgt) const +{ + tgt->net_pevent(o, this); +} + +void NetBUFZ::emit_node(ostream&o, struct target_t*tgt) const +{ + tgt->bufz(o, this); +} + +void NetProcTop::emit(ostream&o, struct target_t*tgt) const +{ + tgt->start_process(o, this); + + statement_->emit_proc(o, tgt); + + tgt->end_process(o, this); +} + +void NetProc::emit_proc(ostream&o, struct target_t*tgt) const +{ + cerr << "EMIT: Proc type? " << typeid(*this).name() << endl; +} + +void NetAssign::emit_proc(ostream&o, struct target_t*tgt) const +{ + tgt->proc_assign(o, this); +} + +void NetBlock::emit_proc(ostream&o, struct target_t*tgt) const +{ + tgt->proc_block(o, this); +} + +void NetPDelay::emit_proc(ostream&o, struct target_t*tgt) const +{ + tgt->proc_delay(o, this); +} + +void NetPDelay::emit_proc_recurse(ostream&o, struct target_t*tgt) const +{ + statement_->emit_proc(o, tgt); +} + +void NetPEvent::emit_proc(ostream&o, struct target_t*tgt) const +{ + tgt->proc_event(o, this); +} + +void NetPEvent::emit_proc_recurse(ostream&o, struct target_t*tgt) const +{ + statement_->emit_proc(o, tgt); +} + +void NetTask::emit_proc(ostream&o, struct target_t*tgt) const +{ + tgt->proc_task(o, this); +} + +void NetBlock::emit_recurse(ostream&o, struct target_t*tgt) const +{ + if (last_ == 0) + return; + + NetProc*cur = last_; + do { + cur = cur->next_; + cur->emit_proc(o, tgt); + } while (cur != last_); +} + +void Design::emit(ostream&o, struct target_t*tgt) const +{ + tgt->start_design(o, this); + + // emit signals + { + NetNet*cur = signals_->sig_next_; + do { + tgt->signal(o, cur); + cur = cur->sig_next_; + } while (cur != signals_->sig_next_); + } + + + // emit nodes + { + NetNode*cur = nodes_->node_next_; + do { + cur->emit_node(o, tgt); + cur = cur->node_next_; + } while (cur != nodes_->node_next_); + } + + + // emit the processes + for (const NetProcTop*idx = procs_ ; idx ; idx = idx->next_) + idx->emit(o, tgt); + + tgt->end_design(o, this); +} + +void NetEConst::expr_scan(struct expr_scan_t*tgt) const +{ + tgt->expr_const(this); +} + +void NetEIdent::expr_scan(struct expr_scan_t*tgt) const +{ + tgt->expr_ident(this); +} + +void NetEUnary::expr_scan(struct expr_scan_t*tgt) const +{ + tgt->expr_unary(this); +} + +void emit(ostream&o, const Design*des, const char*type) +{ + for (unsigned idx = 0 ; target_table[idx] ; idx += 1) { + const struct target*tgt = target_table[idx]; + if (tgt->name == type) { + des->emit(o, tgt->meth); + return; + } + } +} + + +/* + * $Log: emit.cc,v $ + * Revision 1.1 1998/11/03 23:28:57 steve + * Introduce verilog to CVS. + * + */ + diff --git a/eval.cc b/eval.cc new file mode 100644 index 000000000..b80f69581 --- /dev/null +++ b/eval.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: eval.cc,v 1.1 1998/11/03 23:28:58 steve Exp $" +#endif + +# include "PExpr.h" + +verinum* PExpr::eval_const() const +{ + return 0; +} + +verinum* PENumber::eval_const() const +{ + return new verinum(value()); +} + +/* + * $Log: eval.cc,v $ + * Revision 1.1 1998/11/03 23:28:58 steve + * Introduce verilog to CVS. + * + */ + diff --git a/lexor.lex b/lexor.lex new file mode 100644 index 000000000..f87a83bec --- /dev/null +++ b/lexor.lex @@ -0,0 +1,426 @@ + +%{ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: lexor.lex,v 1.1 1998/11/03 23:28:59 steve Exp $" +#endif + + //# define YYSTYPE lexval + +# include +# include "parse_misc.h" +# include "parse.h" +# include + +extern FILE*vl_input; +extern const char*vl_file; + +# define YY_USER_INIT reset_lexor(); +# define yylval VLlval +extern YYLTYPE yylloc; + +static void reset_lexor(); +static int check_identifier(const char*name); +static verinum*make_sized_binary(const char*txt); +static verinum*make_sized_octal(const char*txt); +static verinum*make_sized_hex(const char*txt); + +%} + +%option noyywrap + +%x CCOMMENT +%x CSTRING + +%% + +[ \t\b\f\r] { ; } +\n { yylloc.first_line += 1; } + +"//".* { ; } + +"/*" { BEGIN(CCOMMENT); } +. { yymore(); } +\n { yylloc.first_line += 1; yymore(); } +"*/" { BEGIN(0); } + +"<=" { return K_LE; } +">=" { return K_GE; } + +[;:\[\],()#=.@&!<|^~+-] { return yytext[0]; } + +\" { BEGIN(CSTRING); } +\\\" { yymore(); } +\n { BEGIN(0); + yylval.text = new string(yytext, strlen(yytext)); + VLerror(yylloc, "Missing close quote of string."); + return STRING; } +\" { BEGIN(0); + yylval.text = new string(yytext, strlen(yytext)-1); + return STRING; } +. { yymore(); } + +[a-zA-Z_][a-zA-Z0-9$_]* { + int rc = check_identifier(yytext); + if (rc == IDENTIFIER) + yylval.text = new string(yytext); + else + yylval.text = 0; + + return rc; } + +\\[^ \t\b\f\r]+ { + yylval.text = new string(yytext); + return IDENTIFIER; } + +\$([a-zA-Z0-9$_]+) { + yylval.text = new string(yytext); + return SYSTEM_IDENTIFIER; } + +([0-9][0-9_])?\'d[0-9][0-9_]* { yylval.number = 0; + return NUMBER; } +([0-9][0-9_])?\'[bB][0-1xz_]+ { yylval.number = make_sized_binary(yytext); + return NUMBER; } +([0-9][0-9_])?\'[oO][0-7xz_]+ { yylval.number = make_sized_octal(yytext); + return NUMBER; } +([0-9][0-9_])?\'[hH][0-9a-fA-Fxz_]+ { yylval.number = make_sized_hex(yytext); + return NUMBER; } + +[0-9][0-9_]* { + /* Handle the special case of the unsized decimal number. */ + unsigned long value = 0; + for (const char*cp = yytext ; *cp ; cp += 1) { + if (*cp != '_') + value = 10 * value + (*cp - '0'); + } + + unsigned nbits = 8 * sizeof value; + verinum::V*bits = new verinum::V[8 * sizeof value]; + + for (unsigned idx = 0 ; idx < nbits ; idx += 1, value >>= 1) { + bits[idx] = (value&1) ? verinum::V1 : verinum::V0; + } + + yylval.number = new verinum(bits, nbits); + delete[]bits; + return NUMBER; } + +. { cerr << yylloc.first_line << ": unmatched character ("; + if (isgraph(yytext[0])) + cerr << yytext[0]; + else + cerr << (unsigned)yytext[0]; + + cerr << ")" << endl; } + +%% + +static const struct { const char*name; int code; } key_table[] = { + { "always", K_always }, + { "and", K_and }, + { "assign", K_assign }, + { "begin", K_begin }, + { "buf", K_buf }, + { "bufif0", K_bufif0 }, + { "bufif1", K_bufif1 }, + { "case", K_case }, + { "casex", K_casex }, + { "casez", K_casez }, + { "cmos", K_cmos }, + { "deassign", K_deassign }, + { "default", K_default }, + { "defparam", K_defparam }, + { "disable", K_disable }, + { "edge", K_edge }, + { "else", K_else }, + { "end", K_end }, + { "endcase", K_endcase }, + { "endfunction", K_endfunction }, + { "endmodule", K_endmodule }, + { "endprimitive", K_endprimitive }, + { "endspecify", K_endspecify }, + { "endtable", K_endtable }, + { "event", K_event }, + { "for", K_for }, + { "force", K_force }, + { "forever", K_forever }, + { "fork", K_fork }, + { "function", K_function }, + { "highz0", K_highz0 }, + { "highz1", K_highz1 }, + { "if", K_if }, + { "initial", K_initial }, + { "inout", K_inout }, + { "input", K_input }, + { "integer", K_integer }, + { "join", K_join }, + { "large", K_large }, + { "macromodule", K_macromodule }, + { "medium", K_medium }, + { "module", K_module }, + { "nand", K_nand }, + { "negedge", K_negedge }, + { "nmos", K_nmos }, + { "nor", K_nor }, + { "not", K_not }, + { "notif0", K_notif0 }, + { "notif1", K_notif1 }, + { "or", K_or }, + { "output", K_output }, + { "pmos", K_pmos }, + { "posedge", K_posedge }, + { "primitive", K_primitive }, + { "pull0", K_pull0 }, + { "pull1", K_pull1 }, + { "pulldown", K_pulldown }, + { "pullup", K_pullup }, + { "rcmos", K_rcmos }, + { "reg", K_reg }, + { "release", K_release }, + { "repeat", K_repeat }, + { "rnmos", K_rnmos }, + { "rpmos", K_rpmos }, + { "rtran", K_rtran }, + { "rtranif0", K_rtranif0 }, + { "rtranif1", K_rtranif1 }, + { "scalered", K_scalered }, + { "small", K_small }, + { "specify", K_specify }, + { "specparam", K_specparam }, + { "strong0", K_strong0 }, + { "strong1", K_strong1 }, + { "supply0", K_supply0 }, + { "supply1", K_supply1 }, + { "table", K_table }, + { "task", K_task }, + { "time", K_time }, + { "tran", K_tran }, + { "tranif0", K_tranif0 }, + { "tranif1", K_tranif1 }, + { "tri", K_tri }, + { "tri0", K_tri0 }, + { "tri1", K_tri1 }, + { "triand", K_triand }, + { "trior", K_trior }, + { "vectored", K_vectored }, + { "wait", K_wait }, + { "wand", K_wand }, + { "weak0", K_weak0 }, + { "weak1", K_weak1 }, + { "while", K_while }, + { "wire", K_wire }, + { "wor", K_wor }, + { "xnor", K_xnor }, + { "xor", K_xor }, + { 0, IDENTIFIER } +}; + +static int check_identifier(const char*name) +{ + for (unsigned idx = 0 ; key_table[idx].name ; idx += 1) + if (strcmp(key_table[idx].name, name) == 0) + return key_table[idx].code; + + return IDENTIFIER; +} + +static verinum*make_sized_binary(const char*txt) +{ + char*ptr; + unsigned size = strtoul(txt,&ptr,10); + assert(*ptr == '\''); + ptr += 1; + assert(tolower(*ptr) == 'b'); + + verinum::V*bits = new verinum::V[size]; + + unsigned idx = size; + char*eptr = ptr + strlen(ptr) - 1; + while ((eptr > ptr) && (idx < size)) { + + if (*eptr == '_') { + eptr -= 1; + continue; + } + + switch (*eptr) { + case '0': + bits[idx++] = verinum::V0; + break; + case '1': + bits[idx++] = verinum::V1; + break; + case 'z': + bits[idx++] = verinum::Vz; + break; + case 'x': + bits[idx++] = verinum::Vx; + break; + default: + assert(0); + } + + eptr -= 1; + } + + // Zero-extend binary number, except that z or x is extended + // if it is the highest supplied digit. + while (idx > 0) { + switch (ptr[1]) { + case '0': + case '1': + bits[idx++] = verinum::V0; + break; + case 'z': + bits[idx++] = verinum::Vz; + break; + case 'x': + bits[idx++] = verinum::Vx; + break; + default: + assert(0); + } + } + + return new verinum(bits, size); +} + +static verinum*make_sized_octal(const char*txt) +{ + char*ptr; + unsigned size = strtoul(txt,&ptr,10); + assert(*ptr == '\''); + ptr += 1; + assert(tolower(*ptr) == 'o'); + + verinum::V*bits = new verinum::V[size]; + + unsigned idx = size; + char*eptr = ptr + strlen(ptr); + + while ((eptr > ptr) && (idx < (size-3))) { + switch (*eptr) { + case 'x': + bits[idx++] = verinum::Vx; + bits[idx++] = verinum::Vx; + bits[idx++] = verinum::Vx; + break; + case 'z': + bits[idx++] = verinum::Vz; + bits[idx++] = verinum::Vz; + bits[idx++] = verinum::Vz; + break; + default: { + unsigned val = *eptr - '0'; + bits[idx++] = (val&1)? verinum::V1 : verinum::V0; + bits[idx++] = (val&2)? verinum::V1 : verinum::V0; + bits[idx++] = (val&4)? verinum::V1 : verinum::V0; + } + } + + eptr -= 1; + } + + // zero extend octal numbers + while (idx > 0) switch (ptr[1]) { + case 'x': + bits[idx++] = verinum::Vx; + break; + case 'z': + bits[idx++] = verinum::Vz; + break; + default: + bits[idx++] = verinum::V0; + } + + return new verinum(bits, size); +} + +static verinum*make_sized_hex(const char*txt) +{ + char*ptr; + unsigned size = strtoul(txt,&ptr,10); + assert(*ptr == '\''); + ptr += 1; + assert(tolower(*ptr) == 'h'); + + verinum::V*bits = new verinum::V[size]; + + unsigned idx = size; + char*eptr = ptr + strlen(ptr); + + while ((eptr > ptr) && (idx < (size-4))) { + switch (*eptr) { + case 'x': + bits[idx++] = verinum::Vx; + bits[idx++] = verinum::Vx; + bits[idx++] = verinum::Vx; + bits[idx++] = verinum::Vx; + break; + case 'z': + bits[idx++] = verinum::Vz; + bits[idx++] = verinum::Vz; + bits[idx++] = verinum::Vz; + bits[idx++] = verinum::Vz; + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + { + unsigned val = tolower(*eptr) - 'a' + 10; + bits[idx++] = (val&1)? verinum::V1 : verinum::V0; + bits[idx++] = (val&2)? verinum::V1 : verinum::V0; + bits[idx++] = (val&4)? verinum::V1 : verinum::V0; + bits[idx++] = (val&8)? verinum::V1 : verinum::V0; + break; + } + default: + { + unsigned val = *eptr - '0'; + bits[idx++] = (val&1)? verinum::V1 : verinum::V0; + bits[idx++] = (val&2)? verinum::V1 : verinum::V0; + bits[idx++] = (val&4)? verinum::V1 : verinum::V0; + bits[idx++] = (val&8)? verinum::V1 : verinum::V0; + } + } + + eptr -= 1; + } + + // zero extend octal numbers + while (idx > 0) switch (ptr[1]) { + case 'x': + bits[idx++] = verinum::Vx; + break; + case 'z': + bits[idx++] = verinum::Vz; + break; + default: + bits[idx++] = verinum::V0; + } + + return new verinum(bits, size); +} + +static void reset_lexor() +{ + yyrestart(vl_input); + yylloc.first_line = 1; + yylloc.text = vl_file; +} diff --git a/main.cc b/main.cc new file mode 100644 index 000000000..1e9419aad --- /dev/null +++ b/main.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: main.cc,v 1.1 1998/11/03 23:28:59 steve Exp $" +#endif + +# include +# include +# include +# include +# include "pform.h" +# include "netlist.h" +# include "target.h" + +extern void pform_parse(); + +const char*vl_file = ""; +const char*target = "verilog"; +string start_module = ""; + +extern Design* elaborate(const list&modules, const string&root); +extern void emit(ostream&o, const Design*, const char*); +extern void stupid(Design*des); + +int main(int argc, char*argv[]) +{ + bool dump_flag = false; + bool optimize_flag = false; + const char* out_path = 0; + int opt; + unsigned flag_errors = 0; + + while ((opt = getopt(argc, argv, "DOo:s:t:")) != EOF) switch (opt) { + case 'D': + dump_flag = true; + break; + case 'O': + optimize_flag = true; + break; + case 'o': + out_path = optarg; + break; + case 's': + start_module = optarg; + break; + case 't': + target = optarg; + break; + default: + flag_errors += 1; + break; + } + + if (flag_errors) + return flag_errors; + + if (optind == argc) { + cerr << "No input files." << endl; + return 1; + } + + /* Open the input (source) file. */ + vl_file = argv[optind]; + FILE*input = fopen(vl_file, "r"); + if (input == 0) { + cerr << "Unable to open " <modules; + int rc = pform_parse(input, modules); + + if (rc) { + cerr << "I give up." << endl; + return rc; + } + + if (dump_flag) { + ofstream out ("a.pf"); + out << "PFORM DUMP:" << endl; + for (list::iterator mod = modules.begin() + ; mod != modules.end() + ; mod ++ ) { + pform_dump(out, *mod); + } + } + + + /* Select a root module, and elaborate the design. */ + if ((start_module == "") && (modules.size() == 1)) { + Module*mod = modules.front(); + start_module = mod->get_name(); + } + + Design*des = elaborate(modules, start_module); + if (des == 0) { + cerr << "Unable to elaborate design." << endl; + return 1; + } + + if (optimize_flag) { + stupid(des); + } + + if (dump_flag) { + ofstream out ("a.net"); + des->dump(out); + } + + + if (out_path) { + ofstream out; + out.open(out_path); + if (! out.is_open()) { + cerr << "Unable to open " << out_path << " for writing." + << endl; + return 1; + } + + emit(out, des, target); + + } else { + emit(cout, des, target); + } + + return 0; +} + +/* + * $Log: main.cc,v $ + * Revision 1.1 1998/11/03 23:28:59 steve + * Introduce verilog to CVS. + * + */ + diff --git a/mangle.cc b/mangle.cc new file mode 100644 index 000000000..19e7a3ce7 --- /dev/null +++ b/mangle.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: mangle.cc,v 1.1 1998/11/03 23:29:00 steve Exp $" +#endif + +# include "target.h" +# include + +string mangle(const string&str) +{ + if (str[0] == '$') + return str; + + ostrstream res; + string tmp = str; + + res << "X"; + + for (;;) { + size_t pos = tmp.find('.'); + if (pos > tmp.length()) + pos = tmp.length(); + + res << pos << tmp.substr(0, pos); + if (pos >= tmp.length()) + break; + + tmp = tmp.substr(pos+1); + } + res << ends; + return res.str(); +} + +/* + * $Log: mangle.cc,v $ + * Revision 1.1 1998/11/03 23:29:00 steve + * Introduce verilog to CVS. + * + */ + diff --git a/netlist.cc b/netlist.cc new file mode 100644 index 000000000..2736b2415 --- /dev/null +++ b/netlist.cc @@ -0,0 +1,242 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: netlist.cc,v 1.1 1998/11/03 23:29:00 steve Exp $" +#endif + +# include +# include "netlist.h" + +void connect(NetObj::Link&l, NetObj::Link&r) +{ + NetObj::Link* cur = &l; + do { + NetObj::Link*tmp = cur->next_; + // Pull cur out of left list... + cur->prev_->next_ = cur->next_; + cur->next_->prev_ = cur->prev_; + + // Put cur in right list + cur->next_ = r.next_; + cur->prev_ = &r; + cur->next_->prev_ = cur; + cur->prev_->next_ = cur; + + // Go to the next item in the left list. + cur = tmp; + } while (cur != &l); +} + +const NetNet* find_link_signal(const NetObj*net, unsigned pin, unsigned&bidx) +{ + const NetObj*cur; + unsigned cpin; + net->pin(pin).next_link(cur, cpin); + + while (cur != net) { + const NetNet*sig = dynamic_cast(cur); + if (sig) { + bidx = cpin; + return sig; + } + cur->pin(cpin).next_link(cur, cpin); + } + + return 0; +} + +NetObj::NetObj(const string&n, unsigned np) +: name_(n), npins_(np), delay1_(0), delay2_(0), delay3_(0) +{ + pins_ = new Link[npins_]; + for (unsigned idx = 0 ; idx < npins_ ; idx += 1) { + pins_[idx].node_ = this; + pins_[idx].pin_ = idx; + } +} + +NetObj::~NetObj() +{ + delete[]pins_; +} + +NetNode::~NetNode() +{ + if (design_) + design_->del_node(this); +} + +NetNet::~NetNet() +{ + if (design_) + design_->del_signal(this); +} + +NetProc::~NetProc() +{ +} + +NetAssign::NetAssign(NetNet*lv, NetExpr*rv) +: NetNode("@assign", lv->pin_count()), lval_(lv), rval_(rv) +{ + for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) { + connect(pin(idx), lv->pin(idx)); + } +} + +NetAssign::~NetAssign() +{ +} + +NetBlock::~NetBlock() +{ +} + +void NetBlock::append(NetProc*cur) +{ + if (last_ == 0) { + last_ = cur; + cur->next_ = cur; + } else { + cur->next_ = last_->next_; + last_->next_ = cur; + last_ = cur; + } +} + +NetTask::~NetTask() +{ + delete[]parms_; +} + +NetExpr::~NetExpr() +{ +} + +NetEConst::~NetEConst() +{ +} + +void Design::add_signal(NetNet*net) +{ + assert(net->design_ == 0); + if (signals_ == 0) { + net->sig_next_ = net; + net->sig_prev_ = net; + } else { + net->sig_next_ = signals_->sig_next_; + net->sig_prev_ = signals_; + net->sig_next_->sig_prev_ = net; + net->sig_prev_->sig_next_ = net; + } + signals_ = net; + net->design_ = this; +} + +void Design::del_signal(NetNet*net) +{ + assert(net->design_ == this); + if (signals_ == net) + signals_ = net->sig_prev_; + + if (signals_ == net) { + signals_ = 0; + } else { + net->sig_prev_->sig_next_ = net->sig_next_; + net->sig_next_->sig_prev_ = net->sig_prev_; + } + net->design_ = 0; +} + +NetNet* Design::find_signal(const string&name) +{ + if (signals_ == 0) + return 0; + + NetNet*cur = signals_; + do { + if (cur->name() == name) + return cur; + + cur = cur->sig_prev_; + } while (cur != signals_); + + return 0; +} + +void Design::scan_signals(SigFunctor*fun) +{ + if (signals_ == 0) + return; + + NetNet*cur = signals_->sig_next_; + do { + NetNet*next = cur->sig_next_; + fun->sig_function(cur); + cur = next; + } while (cur != signals_->sig_next_); +} + + +void Design::add_node(NetNode*net) +{ + assert(net->design_ == 0); + if (nodes_ == 0) { + net->node_next_ = net; + net->node_prev_ = net; + } else { + net->node_next_ = nodes_->node_next_; + net->node_prev_ = nodes_; + net->node_next_->node_prev_ = net; + net->node_prev_->node_next_ = net; + } + nodes_ = net; + net->design_ = this; +} + +void Design::del_node(NetNode*net) +{ + assert(net->design_ == this); + if (nodes_ == net) + nodes_ = net->node_prev_; + + if (nodes_ == net) { + nodes_ = 0; + } else { + net->node_next_->node_prev_ = net->node_prev_; + net->node_prev_->node_next_ = net->node_next_; + } + + net->design_ = 0; +} + +void Design::add_process(NetProcTop*pro) +{ + pro->next_ = procs_; + procs_ = pro; +} + + +/* + * $Log: netlist.cc,v $ + * Revision 1.1 1998/11/03 23:29:00 steve + * Introduce verilog to CVS. + * + */ + diff --git a/netlist.h b/netlist.h new file mode 100644 index 000000000..1a1d60a8e --- /dev/null +++ b/netlist.h @@ -0,0 +1,594 @@ +#ifndef __netlist_H +#define __netlist_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: netlist.h,v 1.1 1998/11/03 23:29:01 steve Exp $" +#endif + +/* + * The netlist types, as described in this header file, are intended + * to be the output from elaboration of the source design. The design + * can be passed around in this form to the various stages and design + * processors. + */ +# include +# include "verinum.h" + +class NetNode; +class NetProc; +class NetProcTop; +class NetExpr; +class ostream; + + +struct target; + +/* ========= + * A NetObj is anything that has any kind of behavior in the + * netlist. Nodes can be gates, registers, etc. and are linked + * together to form a design web. + * + * The web of nodes that makes up a circuit is held together by the + * Link class. There is a link for each pin. All mutually connected + * pins form a ring of links. + */ +class NetObj { + + public: + class Link { + friend void connect(Link&, Link&); + friend class NetObj; + + public: + Link() : next_(this), prev_(this) { } + ~Link() { unlink(); } + + void cur_link(NetObj*&net, unsigned &pin) + { net = node_; + pin = pin_; + } + + void next_link(NetObj*&net, unsigned&pin) + { net = next_->node_; + pin = next_->pin_; + } + + void next_link(const NetObj*&net, unsigned&pin) const + { net = next_->node_; + pin = next_->pin_; + } + + // Remove this link from the set of connected pins. The + // destructor will automatically do this if needed. + void unlink() + { next_->prev_ = prev_; + prev_->next_ = next_; + next_ = prev_ = this; + } + + // Return true if this link is connected to anything else. + bool is_linked() const { return next_ != this; } + + private: + // The NetNode manages these. They point back to the + // NetNode so that following the links can get me here. + NetObj *node_; + unsigned pin_; + + private: + Link *next_; + Link *prev_; + + private: // not implemented + Link(const Link&); + Link& operator= (const Link&); + }; + + public: + explicit NetObj(const string&n, unsigned npins); + virtual ~NetObj(); + + const string& name() const { return name_; } + + unsigned pin_count() const { return npins_; } + + unsigned delay1() const { return delay1_; } + unsigned delay2() const { return delay2_; } + unsigned delay3() const { return delay3_; } + + void delay1(unsigned d) { delay1_ = d; } + void delay2(unsigned d) { delay2_ = d; } + void delay3(unsigned d) { delay3_ = d; } + + Link&pin(unsigned idx) { return pins_[idx]; } + const Link&pin(unsigned idx) const { return pins_[idx]; } + + void dump_node_pins(ostream&, unsigned) const; + + + private: + string name_; + Link*pins_; + const unsigned npins_; + unsigned delay1_; + unsigned delay2_; + unsigned delay3_; +}; + +/* + * A NetNode is a device of some sort, where each pin has a different + * meaning. (i.e. pin(0) is the output to an and gate.) NetNode + * objects are listed in the nodes_ of the Design object. + */ +class NetNode : public NetObj { + + public: + explicit NetNode(const string&n, unsigned npins) + : NetObj(n, npins), node_next_(0), node_prev_(0), design_(0) { } + + virtual ~NetNode(); + + virtual void emit_node(ostream&, struct target_t*) const; + virtual void dump_node(ostream&, unsigned) const; + + private: + friend class Design; + NetNode*node_next_, *node_prev_; + Design*design_; +}; + + +/* + * NetNet is a special kind of NetObj that doesn't really do anything, + * but carries the properties of the wire/reg/trireg. Thus, a set of + * pins connected together would also be connected to exactly one of + * these. + * + * Note that a net of any sort has exactly one pin. The pins feature + * of the NetObj class is used to make a set of identical wires, in + * order to support ranges, or busses. + */ +class NetNet : public NetObj { + + public: + enum Type { IMPLICIT, WIRE, TRI, TRI1, SUPPLY0, WAND, TRIAND, + TRI0, SUPPLY1, WOR, TRIOR, REG }; + + enum PortType { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT }; + + explicit NetNet(const string&n, Type t, unsigned npins =1) + : NetObj(n, npins), sig_next_(0), sig_prev_(0), design_(0), + type_(t), port_type_(NOT_A_PORT), msb_(npins-1), lsb_(0), + local_flag_(false) { } + + explicit NetNet(const string&n, Type t, long ms, long ls) + : NetObj(n, ((ms>ls)?ms-ls:ls-ms) + 1), sig_next_(0), + sig_prev_(0), design_(0), type_(t), port_type_(NOT_A_PORT), + msb_(ms), lsb_(ls), local_flag_(false) { } + + virtual ~NetNet(); + + // Every signal has a name, even if it is null. + Type type() const { return type_; } + void type(Type t) { type_ = t; } + + PortType port_type() const { return port_type_; } + void port_type(PortType t) { port_type_ = t; } + + long msb() const { return msb_; } + long lsb() const { return lsb_; } + + unsigned sb_to_idx(long sb) const + { if (msb_ >= lsb_) + return sb - lsb_; + else + return lsb_ - sb; + } + + bool local_flag() const { return local_flag_; } + void local_flag(bool f) { local_flag_ = f; } + + virtual void dump_net(ostream&, unsigned) const; + + private: + // The Design class uses this for listing signals. + friend class Design; + NetNet*sig_next_, *sig_prev_; + Design*design_; + + private: + Type type_; + PortType port_type_; + + long msb_, lsb_; + + bool local_flag_; +}; + +/* + * The NetTmp object is a network that is only used momentarily by + * elaboration to carry links around. A completed netlist cannot have + * any of these within. This is a kind of wire, so it is NetNet type. + */ +class NetTmp : public NetNet { + + public: + explicit NetTmp(unsigned npins =1) + : NetNet("@", IMPLICIT, npins) { } + +}; + +/* + * The NetBUFZ is a magic device that represents the continuous + * assign, with the output being the target register and the input + * the logic that feeds it. The netlist preserves the directional + * nature of that assignment with the BUFZ. The target may elide it if + * that makes sense for the technology. + */ +class NetBUFZ : public NetNode { + + public: + explicit NetBUFZ(const string&n) + : NetNode(n, 2) { } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual void emit_node(ostream&, struct target_t*) const; +}; + +/* + * This class represents all manner of logic gates. + */ +class NetLogic : public NetNode { + + public: + enum TYPE { AND, NAND, NOR, NOT, OR, XOR }; + + explicit NetLogic(const string&n, unsigned pins, TYPE t) + : NetNode(n, pins), type_(t) { } + + TYPE type() const { return type_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual void emit_node(ostream&, struct target_t*) const; + + private: + const TYPE type_; +}; + +/* ========= + * A process is a behavioral-model description. A process is a + * statement that may be compound. the various statement types may + * refer to places in a netlist (by pointing to nodes) but is not + * linked into the netlist. However, elaborating a process may cause + * special nodes to be created to handle things like events. + */ +class NetProc { + + public: + explicit NetProc() : next_(0) { } + virtual ~NetProc(); + + virtual void emit_proc(ostream&, struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + + private: + friend class NetBlock; + NetProc*next_; +}; + +/* This is a procedural assignment. The lval is a register, and the + assignment happens when the code is executed by the design. The + node part of the NetAssign has as many pins as the width of the + lvalue object. */ +class NetAssign : public NetProc, public NetNode { + public: + explicit NetAssign(NetNet*lv, NetExpr*rv); + ~NetAssign(); + + const NetNet* lval() const { return lval_; } + const NetExpr*rval() const { return rval_; } + + virtual void emit_proc(ostream&, struct target_t*) const; + virtual void emit_node(ostream&, struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + virtual void dump_node(ostream&, unsigned ind) const; + + private: + NetNet*const lval_; + NetExpr*const rval_; +}; + +/* A block is stuff line begin-end blocks, that contain and ordered + list of NetProc statements. + + NOTE: The emit method calls the target->proc_block function but + does not recurse. It is up to the target-supplied proc_block + function to call emit_recurse. */ +class NetBlock : public NetProc { + + public: + enum Type { SEQU, PARA }; + + NetBlock(Type t) : type_(t), last_(0) { } + ~NetBlock(); + + const Type type() const { return type_; } + + void append(NetProc*); + + void emit_recurse(ostream&, struct target_t*) const; + virtual void emit_proc(ostream&, struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + + private: + const Type type_; + + NetProc*last_; +}; + +class NetPDelay : public NetProc { + + public: + NetPDelay(unsigned long d, NetProc*st) + : delay_(d), statement_(st) { } + + unsigned long delay() const { return delay_; } + + virtual void emit_proc(ostream&, struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + + void emit_proc_recurse(ostream&, struct target_t*) const; + + private: + unsigned long delay_; + NetProc*statement_; +}; + +/* + * The NetPEvent is a NetNode that connects to the structural part of + * the design. It has only inputs, which cause the side effect of + * triggering an event that the procedural part of the design can use. + */ +class NetPEvent : public NetProc, public NetNode { + + public: + enum Type { ANYEDGE, POSEDGE, NEGEDGE }; + + public: + NetPEvent(const string&ev, Type ed, NetProc*st) + : NetNode(ev, 1), edge_(ed), statement_(st) { } + + Type edge() const { return edge_; } + + virtual void emit_proc(ostream&, struct target_t*) const; + virtual void emit_node(ostream&, struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + virtual void dump_node(ostream&, unsigned ind) const; + + void emit_proc_recurse(ostream&, struct target_t*) const; + + private: + Type edge_; + NetProc*statement_; +}; + + +/* The elaborator should expand all the user defined tasks in line, so + this leaves the NetTask to represent activations of system tasks, + or external tasks that are not known at compile time. */ +class NetTask : public NetProc { + + public: + NetTask(const string&na, unsigned np) + : name_(na), nparms_(np) + { parms_ = new NetExpr*[nparms_]; + for (unsigned idx = 0 ; idx < nparms_ ; idx += 1) + parms_[idx] = 0; + } + ~NetTask(); + + const string& name() const { return name_; } + + unsigned nparms() const { return nparms_; } + + void parm(unsigned idx, NetExpr*p) + { assert(idx < nparms_); + parms_[idx] = p; + } + + NetExpr* parm(unsigned idx) const + { assert(idx < nparms_); + return parms_[idx]; + } + + virtual void emit_proc(ostream&, struct target_t*) const; + virtual void dump(ostream&, unsigned ind) const; + + private: + string name_; + unsigned nparms_; + NetExpr**parms_; +}; + +/* The is the top of any process. It carries the type (initial or + always) and a pointer to the statement, probably a block, that + makes up the process. */ +class NetProcTop { + + public: + enum Type { KINITIAL, KALWAYS }; + + NetProcTop(Type t, NetProc*st) : type_(t), statement_(st) { } + + Type type() const { return type_; } + const NetProc*statement() const { return statement_; } + + void dump(ostream&, unsigned ind) const; + void emit(ostream&, struct target_t*tgt) const; + + private: + const Type type_; + NetProc*const statement_; + + friend class Design; + NetProcTop*next_; +}; + +/* ========= + * There are cases where expressions need to be represented. The + * NetExpr class is the root of a heirarchy that serves that purpose. + */ +class NetExpr { + public: + NetExpr() { } + virtual ~NetExpr() =0; + + virtual void expr_scan(struct expr_scan_t*) const =0; + virtual void dump(ostream&) const; + + private: // not implemented + NetExpr(const NetExpr&); + NetExpr& operator=(const NetExpr&); +}; + +class NetEConst : public NetExpr { + + public: + NetEConst(const verinum&val) : value_(val) { } + ~NetEConst(); + + const verinum&value() const { return value_; } + + virtual void expr_scan(struct expr_scan_t*) const; + virtual void dump(ostream&) const; + + private: + verinum value_; +}; + +class NetEUnary : public NetExpr { + + public: + NetEUnary(char op, NetExpr*ex) + : op_(op), expr_(ex) { } + + char op() const { return op_; } + const NetExpr* expr() const { return expr_; } + + virtual void expr_scan(struct expr_scan_t*) const; + virtual void dump(ostream&) const; + + private: + char op_; + NetExpr*expr_; +}; + +/* XXXX Note: I do not know what to do about this. Elaboration should + expand vectors to scalers, but identifiers identify vectors, and + other issues. This class exists for now until I figure out the + right way to deal with identifiers. */ +class NetEIdent : public NetExpr { + + public: + NetEIdent(const string&n) : name_(n) { } + + const string& name() const { return name_; } + + virtual void expr_scan(struct expr_scan_t*) const; + virtual void dump(ostream&) const; + + private: + string name_; +}; + +/* + * This class contains an entire design. It includes processes and a + * netlist, and can be passed around from function to function. + */ +class Design { + + public: + Design() : signals_(0), nodes_(0), procs_(0) { } + + + // SIGNALS + + void add_signal(NetNet*); + void del_signal(NetNet*); + NetNet*find_signal(const string&name); + + // NODES + void add_node(NetNode*); + void del_node(NetNode*); + + // PROCESSES + void add_process(NetProcTop*); + + // Iterate over the design... + void dump(ostream&) const; + void emit(ostream&, struct target_t*) const; + + class SigFunctor { + public: + virtual void sig_function(NetNet*) =0; + }; + + void scan_signals(SigFunctor*); + + private: + // List all the signals in the design. + NetNet*signals_; + + // List the nodes in the design + NetNode*nodes_; + + // List the processes in the design. + NetProcTop*procs_; + + private: // not implemented + Design(const Design&); + Design& operator= (const Design&); +}; + + +/* ======= + */ + + +/* Connect the pins of two nodes together. Either may already be + connected to other things, connect is transitive. */ +extern void connect(NetObj::Link&, NetObj::Link&); + +/* Find the signal connected to the given node pin. There should + always be exactly one signal. The bidx parameter get filled with + the signal index of the Net, in case it is a vector. */ +const NetNet* find_link_signal(const NetObj*net, unsigned pin, + unsigned&bidx); + +inline ostream& operator << (ostream&o, const NetExpr&exp) +{ exp.dump(o); return o; } + +/* + * $Log: netlist.h,v $ + * Revision 1.1 1998/11/03 23:29:01 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/parse.y b/parse.y new file mode 100644 index 000000000..6deb9264f --- /dev/null +++ b/parse.y @@ -0,0 +1,548 @@ + +%{ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: parse.y,v 1.1 1998/11/03 23:29:01 steve Exp $" +#endif + +# include "parse_misc.h" +# include "pform.h" + +%} + +%union { + string*text; + list*strings; + + lgate*gate; + list*gates; + + PExpr*expr; + list*exprs; + + NetNet::Type nettype; + PGBuiltin::Type gatetype; + NetNet::PortType porttype; + + PWire*wire; + list*wires; + + PEventStatement*event_statement; + Statement*statement; + list*statement_list; + + verinum* number; +}; + +%token IDENTIFIER SYSTEM_IDENTIFIER STRING +%token NUMBER +%token K_LE K_GE +%token K_always K_and K_assign K_begin K_buf K_bufif0 K_bufif1 K_case +%token K_casex K_casez K_cmos K_deassign K_default K_defparam K_disable +%token K_edge K_else K_end K_endcase K_endfunction K_endmodule +%token K_endprimitive K_endspecify K_endtable K_endtask K_event K_for +%token K_force K_forever K_fork K_function K_highz0 K_highz1 K_if +%token K_initial K_inout K_input K_integer K_join K_large K_macromodule +%token K_medium K_module K_nand K_negedge K_nmos K_nor K_not K_notif0 +%token K_notif1 K_or K_output K_pmos K_posedge K_primitive K_pull0 +%token K_pull1 K_pulldown K_pullup K_rcmos K_reg K_release K_repeat +%token K_rnmos K_rpmos K_rtran K_rtranif0 K_rtranif1 K_scalered +%token K_small K_specify +%token K_specparam K_strong0 K_strong1 K_supply0 K_supply1 K_table K_task +%token K_time K_tran K_tranif0 K_tranif1 K_tri K_tri0 K_tri1 K_triand +%token K_trior K_vectored K_wait K_wand K_weak0 K_weak1 K_while K_wire +%token K_wor K_xnor K_xor + +%type identifier lvalue register_variable +%type list_of_register_variables +%type list_of_variables + +%type port +%type list_of_ports list_of_ports_opt + +%type gate_instance +%type gate_instance_list + +%type bitsel delay delay_opt expression expr_primary const_expression +%type expression_list + +%type range range_opt +%type net_type +%type gatetype +%type port_type + +%type event_control event_expression +%type statement statement_opt +%type statement_list + +%left UNARY_PREC +%left '+' '-' +%left K_GE K_LE '<' '>' +%left '&' +%left '^' + +%% + +source_file + : description + | source_file description + ; + +bitsel + : '[' const_expression ']' + { $$ = $2; } + ; + +const_expression + : NUMBER + { $$ = new PENumber($1); + } + | STRING + { $$ = new PEString(*$1); + delete $1; + } + ; + +delay + : '#' NUMBER + { $$ = new PENumber($2); + } + | '#' IDENTIFIER + { $$ = new PEIdent(*$2); + delete $2; + } + ; + +delay_opt + : delay { $$ = $1; } + | { $$ = 0; } + ; + +description + : module + | primitive + ; + +event_control + : '@' IDENTIFIER + { yyerror(@1, "Sorry, event control not supported."); + $$ = 0; + } + | '@' '(' event_expression ')' + { $$ = $3; + } + ; + +event_expression + : K_posedge expression + { $$ = new PEventStatement(NetPEvent::POSEDGE, $2); + } + | K_negedge expression + { $$ = new PEventStatement(NetPEvent::NEGEDGE, $2); + } + ; + +expression + : expr_primary + { $$ = $1; } + | '(' expression ')' + { $$ = $2; } + | '~' expression %prec UNARY_PREC + { $$ = new PEUnary('~', $2); + } + | '&' expression %prec UNARY_PREC + { $$ = new PEUnary('&', $2); + } + | expression '^' expression + { $$ = new PEBinary('^', $1, $3); + } + | expression '+' expression + { $$ = new PEBinary('+', $1, $3); + } + | expression '&' expression + { $$ = new PEBinary('&', $1, $3); + } + ; + + +expression_list + : expression_list ',' expression + { list*tmp = $1; + tmp->push_back($3); + $$ = tmp; + } + | expression + { list*tmp = new list; + tmp->push_back($1); + $$ = tmp; + } + | expression_list ',' + { list*tmp = $1; + tmp->push_back(0); + $$ = tmp; + } + ; + + +expr_primary + : NUMBER + { $$ = new PENumber($1); + } + | STRING + { $$ = new PEString(*$1); + delete $1; + } + | identifier + { $$ = new PEIdent(*$1); + delete $1; + } + | SYSTEM_IDENTIFIER + { $$ = new PEIdent(*$1); + delete $1; + } + | identifier '[' expression ']' + { PEIdent*tmp = new PEIdent(*$1); + tmp->msb_ = $3; + delete $1; + $$ = tmp; + } + | identifier '[' expression ':' expression ']' + { PEIdent*tmp = new PEIdent(*$1); + tmp->msb_ = $3; + tmp->lsb_ = $5; + delete $1; + $$ = tmp; + } + ; + +gate_instance + : IDENTIFIER '(' expression_list ')' + { lgate*tmp = new lgate; + tmp->name = *$1; + tmp->parms = $3; + delete $1; + $$ = tmp; + } + | '(' expression_list ')' + { lgate*tmp = new lgate; + tmp->name = ""; + tmp->parms = $2; + $$ = tmp; + } + ; + +gate_instance_list + : gate_instance_list ',' gate_instance + { list*tmp = $1; + tmp->push_back(*$3); + delete $3; + $$ = tmp; + } + | gate_instance + { list*tmp = new list; + tmp->push_back(*$1); + delete $1; + $$ = tmp; + } + ; + +gatetype + : K_and { $$ = PGBuiltin::AND; } + | K_nand { $$ = PGBuiltin::NAND; } + | K_or { $$ = PGBuiltin::OR; } + | K_nor { $$ = PGBuiltin::NOR; } + | K_xor { $$ = PGBuiltin::XOR; } + | K_xnor { $$ = PGBuiltin::XNOR; } + | K_buf { $$ = PGBuiltin::BUF; } + | K_bufif0 { $$ = PGBuiltin::BUFIF0; } + | K_bufif1 { $$ = PGBuiltin::BUFIF1; } + | K_not { $$ = PGBuiltin::NOT; } + | K_notif0 { $$ = PGBuiltin::NOTIF0; } + | K_notif1 { $$ = PGBuiltin::NOTIF1; } + | K_pulldown { $$ = PGBuiltin::PULLDOWN; } + | K_pullup { $$ = PGBuiltin::PULLUP; } + | K_nmos { $$ = PGBuiltin::NMOS; } + | K_rnmos { $$ = PGBuiltin::RNMOS; } + | K_pmos { $$ = PGBuiltin::PMOS; } + | K_rpmos { $$ = PGBuiltin::RPMOS; } + | K_cmos { $$ = PGBuiltin::CMOS; } + | K_rcmos { $$ = PGBuiltin::RCMOS; } + | K_tran { $$ = PGBuiltin::TRAN; } + | K_rtran { $$ = PGBuiltin::RTRAN; } + | K_tranif0 { $$ = PGBuiltin::TRANIF0; } + | K_tranif1 { $$ = PGBuiltin::TRANIF1; } + | K_rtranif0 { $$ = PGBuiltin::RTRANIF0; } + | K_rtranif1 { $$ = PGBuiltin::RTRANIF1; } + ; + +identifier + : identifier '.' IDENTIFIER + { yyerror(@1, "Sorry, qualified identifiers not supported."); + $$ = $3; + delete $1; + } + | IDENTIFIER + { $$ = $1; } + ; + +list_of_ports + : port + { list*tmp = new list; + tmp->push_back($1); + $$ = tmp; + } + | list_of_ports ',' port + { list*tmp = $1; + tmp->push_back($3); + $$ = tmp; + } + ; + +list_of_ports_opt + : '(' list_of_ports ')' { $$ = $2; } + | '(' ')' { $$ = 0; } + | { $$ = 0; } + ; + +list_of_register_variables + : register_variable + { list*tmp = new list; + tmp->push_back(*$1); + delete $1; + $$ = tmp; + } + | list_of_register_variables ',' register_variable + { list*tmp = $1; + tmp->push_back(*$3); + delete $3; + $$ = tmp; + } + ; +list_of_variables + : IDENTIFIER + { list*tmp = new list; + tmp->push_back(*$1); + delete $1; + $$ = tmp; + } + | list_of_variables ',' IDENTIFIER + { list*tmp = $1; + tmp->push_back(*$3); + delete $3; + $$ = tmp; + } + ; + +lvalue + : identifier { $$ = $1; } + ; + +module + : K_module IDENTIFIER list_of_ports_opt ';' + { pform_startmodule(*$2, $3); + } + module_item_list + K_endmodule + { pform_endmodule(*$2); + delete $2; + } + ; + +module_item + : net_type range_opt list_of_variables ';' + { pform_makewire($3, $1); + if ($2) { + pform_set_net_range($3, $2); + delete $2; + } + delete $3; + } + | port_type range_opt list_of_variables ';' + { pform_set_port_type($3, $1); + if ($2) { + pform_set_net_range($3, $2); + delete $2; + } + delete $3; + } + | K_reg range_opt list_of_register_variables ';' + { pform_makewire($3, NetNet::REG); + if ($2) { + pform_set_net_range($3, $2); + delete $2; + } + delete $3; + } + | gatetype delay_opt gate_instance_list ';' + { pform_makegates($1, $2, $3); + } + | IDENTIFIER gate_instance_list ';' + { pform_make_modgates(*$1, $2); + delete $1; + } + | K_assign IDENTIFIER '=' expression ';' + { pform_make_pgassign(*$2, $4); + delete $2; + } + | K_assign IDENTIFIER bitsel '=' expression ';' + { pform_make_pgassign(*$2, $3, $5); + delete $2; + } + | K_assign IDENTIFIER range '=' expression ';' + { pform_make_pgassign(*$2, $5); + yyerror(@3, "Sorry, lvalue bit range not supported."); + delete $2; + delete $3; + } + | K_always statement + { pform_make_behavior(PProcess::PR_ALWAYS, $2); + } + | K_initial statement + { pform_make_behavior(PProcess::PR_INITIAL, $2); + } + ; + +module_item_list + : module_item_list module_item + | module_item + ; + +net_type + : K_wire { $$ = NetNet::WIRE; } + | K_tri { $$ = NetNet::TRI; } + | K_tri1 { $$ = NetNet::TRI1; } + | K_supply0 { $$ = NetNet::SUPPLY0; } + | K_wand { $$ = NetNet::WAND; } + | K_triand { $$ = NetNet::TRIAND; } + | K_tri0 { $$ = NetNet::TRI0; } + | K_supply1 { $$ = NetNet::SUPPLY1; } + | K_wor { $$ = NetNet::WOR; } + | K_trior { $$ = NetNet::TRIOR; } + ; + +port + : IDENTIFIER + { $$ = new PWire(*$1, NetNet::IMPLICIT); + $$->port_type = NetNet::PIMPLICIT; + delete $1; + } + | IDENTIFIER '[' const_expression ':' const_expression ']' + { $$ = new PWire(*$1, NetNet::IMPLICIT); + $$->port_type = NetNet::PIMPLICIT; + $$->msb = $3; + $$->lsb = $5; + delete $1; + } + | IDENTIFIER '[' error ']' + { yyerror(@1, "invalid port bit select"); + $$ = new PWire(*$1, NetNet::IMPLICIT); + $$->port_type = NetNet::PIMPLICIT; + delete $1; + } + ; + +port_type + : K_input { $$ = NetNet::PINPUT; } + | K_output { $$ = NetNet::POUTPUT; } + | K_inout { $$ = NetNet::PINOUT; } + ; + +primitive + : K_primitive IDENTIFIER '(' error ')' ';' K_endprimitive + { yyerror(@1, "Sorry, primitives not supported."); } + ; + +range + : '[' NUMBER ':' NUMBER ']' + { list*tmp = new list; + tmp->push_back(new PENumber($2)); + tmp->push_back(new PENumber($4)); + $$ = tmp; + } + ; + +range_opt + : range + | { $$ = 0; } + ; + +register_variable + : IDENTIFIER + { $$ = $1; } + | IDENTIFIER '[' error ']' + { yyerror(@1, "Sorry, register regions not implemented."); + $$ = $1; + } + ; + +statement + : K_begin statement_list K_end + { $$ = pform_make_block(PBlock::BL_SEQ, $2); } + | K_fork statement_list K_join + { $$ = pform_make_block(PBlock::BL_PAR, $2); } + | K_begin K_end + { $$ = pform_make_block(PBlock::BL_SEQ, 0); } + | K_fork K_join + { $$ = pform_make_block(PBlock::BL_PAR, 0); } + | delay statement_opt + { PDelayStatement*tmp = new PDelayStatement($1, $2); + $$ = tmp; + } + | event_control statement_opt + { PEventStatement*tmp = $1; + tmp->set_statement($2); + $$ = tmp; + } + | lvalue '=' expression ';' + { $$ = pform_make_assignment($1, $3); + } + | lvalue K_LE expression ';' + { $$ = pform_make_assignment($1, $3); + yyerror(@1, "Sorry, non-blocking assignment not implemented."); + } + | SYSTEM_IDENTIFIER '(' expression_list ')' ';' + { $$ = pform_make_calltask($1, $3); + } + | SYSTEM_IDENTIFIER ';' + { $$ = pform_make_calltask($1); + } + | error ';' + { yyerror(@1, "malformed statement"); + $$ = new PNoop; + } + ; + +statement_list + : statement_list statement + { list*tmp = $1; + tmp->push_back($2); + $$ = tmp; + } + | statement + { list*tmp = new list(); + tmp->push_back($1); + $$ = tmp; + } + ; + +statement_opt + : statement + | ';' { $$ = 0; } + ; diff --git a/parse_misc.cc b/parse_misc.cc new file mode 100644 index 000000000..330a0a76e --- /dev/null +++ b/parse_misc.cc @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: parse_misc.cc,v 1.1 1998/11/03 23:29:02 steve Exp $" +#endif + +# include "parse_misc.h" +# include + +extern const char*vl_file; + +void VLerror(const char*msg) +{ + cerr << yylloc.text << ":" << yylloc.first_line << ": " << msg << endl; +} + +void VLerror(const YYLTYPE&loc, const char*msg) +{ + if (loc.text) + cerr << loc.text << ":"; + + cerr << loc.first_line << ": " << msg << endl; +} + +void yywarn(const YYLTYPE&loc, const char*msg) +{ + if (loc.text) + cerr << loc.text << ":"; + + cerr << loc.first_line << ": warning -- " << msg << endl; +} + +int VLwrap() +{ + return -1; +} + +/* + * $Log: parse_misc.cc,v $ + * Revision 1.1 1998/11/03 23:29:02 steve + * Introduce verilog to CVS. + * + */ + diff --git a/parse_misc.h b/parse_misc.h new file mode 100644 index 000000000..93b83f8c8 --- /dev/null +++ b/parse_misc.h @@ -0,0 +1,60 @@ +#ifndef __parse_misc_H +#define __parse_misc_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: parse_misc.h,v 1.1 1998/11/03 23:29:03 steve Exp $" +#endif + +# include +# include +# include "pform.h" + +/* + * The vlltype supports the passing of detailed source file location + * information between the lexical analyzer and the parser. Defining + * YYLTYPE compels the lexor to use this type and not something other. + */ +struct vlltype { + unsigned first_line; + unsigned first_column; + unsigned last_line; + unsigned last_column; + const char*text; +}; +# define YYLTYPE vlltype +extern YYLTYPE yylloc; + +/* + * Interface into the lexical analyzer. ... + */ +extern int VLlex(); +extern void VLerror(const char*msg); +extern void VLerror(const YYLTYPE&loc, const char*msg); +#define yywarn VLwarn +extern void VLwarn(const YYLTYPE&loc, const char*msg); + + +/* + * $Log: parse_misc.h,v $ + * Revision 1.1 1998/11/03 23:29:03 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/pform.cc b/pform.cc new file mode 100644 index 000000000..64ef2a6a3 --- /dev/null +++ b/pform.cc @@ -0,0 +1,288 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: pform.cc,v 1.1 1998/11/03 23:29:03 steve Exp $" +#endif + +# include "pform.h" +# include "parse_misc.h" +# include +# include +# include + +extern int VLparse(); + +static Module*cur_module = 0; + +static list*vl_modules = 0; + +/* + * This function evaluates delay expressions. The result should be a + * simple constant that I can interpret as an unsigned number. + */ +static unsigned long evaluate_delay(PExpr*delay) +{ + PENumber*pp = dynamic_cast(delay); + if (pp == 0) { + VLerror("Sorry, delay expression is too complicated."); + return 0; + } + + return pp->value().as_ulong(); +} + +void pform_startmodule(const string&name, list*ports) +{ + assert( cur_module == 0 ); + cur_module = new Module(name, ports? ports->size() : 0); + + vl_modules->push_back(cur_module); + + if (ports) { + unsigned idx = 0; + for (list::iterator cur = ports->begin() + ; cur != ports->end() + ; cur ++ ) { + cur_module->add_wire(*cur); + cur_module->ports[idx++] = *cur; + } + + delete ports; + } +} + +void pform_endmodule(const string&name) +{ + assert(cur_module); + assert(name == cur_module->get_name()); + cur_module = 0; +} + +void pform_makegate(PGBuiltin::Type type, + const string&name, + const vector&wires, + unsigned long delay_val) +{ + PGate*cur = new PGBuiltin(type, name, wires, delay_val); + cur_module->add_gate(cur); +} + +void pform_makegates(PGBuiltin::Type type, + PExpr*delay, list*gates) +{ + unsigned long delay_val = evaluate_delay(delay); + delete delay; + + while (! gates->empty()) { + lgate cur = gates->front(); + gates->pop_front(); + + vectorwires (cur.parms->size()); + for (unsigned idx = 0 ; idx < wires.size() ; idx += 1) { + PExpr*ep = cur.parms->front(); + cur.parms->pop_front(); + + wires[idx] = ep; + } + + pform_makegate(type, cur.name, wires, delay_val); + } + + delete gates; +} + +void pform_make_modgate(const string&type, + const string&name, + const vector&wires) +{ + PGate*cur = new PGModule(type, name, wires); + cur_module->add_gate(cur); +} + +void pform_make_modgates(const string&type, list*gates) +{ + while (! gates->empty()) { + lgate cur = gates->front(); + gates->pop_front(); + + vectorwires (cur.parms->size()); + for (unsigned idx = 0 ; idx < wires.size() ; idx += 1) { + PExpr*ep = cur.parms->front(); + cur.parms->pop_front(); + + wires[idx] = ep; + } + + pform_make_modgate(type, cur.name, wires); + } + + delete gates; +} + +void pform_make_pgassign(const string&lval, PExpr*rval) +{ + vector wires (2); + wires[0] = new PEIdent(lval); + wires[1] = rval; + PGAssign*cur = new PGAssign(wires); + cur_module->add_gate(cur); +} + +void pform_make_pgassign(const string&lval, PExpr*sel, PExpr*rval) +{ + vector wires (2); + PEIdent*tmp = new PEIdent(lval); + tmp->msb_ = sel; + wires[0] = tmp; + wires[1] = rval; + PGAssign*cur = new PGAssign(wires); + cur_module->add_gate(cur); +} + +void pform_makewire(const string&name, NetNet::Type type) +{ + PWire*cur = cur_module->get_wire(name); + if (cur) { + if (cur->type != NetNet::IMPLICIT) + VLerror("Extra definition of wire."); + + cur->type = type; + return; + } + + cur = new PWire(name, type); + cur_module->add_wire(cur); +} + +void pform_makewire(const list*names, NetNet::Type type) +{ + for (list::const_iterator cur = names->begin() + ; cur != names->end() + ; cur ++ ) + pform_makewire(*cur, type); + +} + +void pform_set_port_type(const string&name, NetNet::PortType pt) +{ + PWire*cur = cur_module->get_wire(name); + if (cur == 0) { + VLerror("name is not a port."); + return; + } + + if (cur->port_type != NetNet::PIMPLICIT) { + VLerror("error setting port direction."); + return; + } + + cur->port_type = pt; +} + +static void pform_set_net_range(const string&name, list*range) +{ + assert(range->size() == 2); + + PWire*cur = cur_module->get_wire(name); + if (cur == 0) { + VLerror("name is not a valid net."); + return; + } + + if ((cur->msb == 0) && (cur->lsb == 0)){ + list::const_iterator idx = range->begin(); + cur->msb = *idx; + idx ++; + cur->lsb = *idx; + } else { + VLwarn(yylloc, "net ranges not checked."); + } +} + +void pform_set_port_type(list*names, NetNet::PortType pt) +{ + for (list::const_iterator cur = names->begin() + ; cur != names->end() + ; cur ++ ) { + pform_set_port_type(*cur, pt); + } +} + +void pform_set_net_range(list*names, list*range) +{ + assert(range->size() == 2); + + for (list::const_iterator cur = names->begin() + ; cur != names->end() + ; cur ++ ) { + pform_set_net_range(*cur, range); + } +} + +void pform_make_behavior(PProcess::Type type, Statement*st) +{ + PProcess*pp = new PProcess(type, st); + cur_module->add_behavior(pp); +} + +Statement* pform_make_block(PBlock::BL_TYPE type, list*sl) +{ + if (sl == 0) + sl = new list; + + PBlock*bl = new PBlock(type, *sl); + delete sl; + return bl; +} + +Statement* pform_make_assignment(string*text, PExpr*ex) +{ + PAssign*st = new PAssign (*text, ex); + delete text; + return st; +} + +Statement* pform_make_calltask(string*name, list*parms) +{ + if (parms == 0) + parms = new list; + + PCallTask*ct = new PCallTask(*name, *parms); + delete name; + delete parms; + return ct; +} + +FILE*vl_input = 0; +int pform_parse(FILE*input, list&modules) +{ + vl_input = input; + vl_modules = &modules; + int rc = VLparse(); + return rc; +} + + +/* + * $Log: pform.cc,v $ + * Revision 1.1 1998/11/03 23:29:03 steve + * Introduce verilog to CVS. + * + */ + diff --git a/pform.h b/pform.h new file mode 100644 index 000000000..8077c28d6 --- /dev/null +++ b/pform.h @@ -0,0 +1,127 @@ +#ifndef __pform_H +#define __pform_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: pform.h,v 1.1 1998/11/03 23:29:04 steve Exp $" +#endif + +# include "netlist.h" +# include "Module.h" +# include "Statement.h" +# include "PGate.h" +# include "PExpr.h" +# include "PWire.h" +# include "verinum.h" +# include +# include +# include +# include +# include + +/* + * These classes implement the parsed form (P-form for short) of the + * original verilog source. the parser generates the pform for the + * convenience of later processing steps. + */ + + +/* + * Wire objects represent the named wires (of various flavor) declared + * in the source. + * + * Gate objects are the functional modules that are connected together + * by wires. + * + * Wires and gates, connected by joints, represent a netlist. The + * netlist is therefore a representation of the desired circuit. + */ +class PGate; +class PExpr; + +/* + * These type are lexical types -- that is, types that are used as + * lexical values to decorate the parse tree during parsing. They are + * not in any way preserved once parsing is done. + */ + +struct lgate { + string name; + list*parms; +}; + + +/* + * The parser uses startmodule and endmodule together to build up a + * module as it parses it. The startmodule tells the pform code that a + * module has been noticed in the source file and the following events + * are to apply to the scope of that module. The endmodule causes the + * pform to close up and finish the named module. + */ +extern void pform_startmodule(const string&, list*ports); +extern void pform_endmodule(const string&); + +/* + * The makewire functions announce to the pform code new wires. These + * go into a module that is currently opened. + */ +extern void pform_makewire(const string&name, NetNet::Type type); +extern void pform_makewire(const list*names, NetNet::Type type); +extern void pform_set_port_type(list*names, NetNet::PortType); +extern void pform_set_net_range(list*names, list*); +extern void pform_make_behavior(PProcess::Type, Statement*); +extern Statement* pform_make_block(PBlock::BL_TYPE, list*); +extern Statement* pform_make_assignment(string*t, PExpr*e); +extern Statement* pform_make_calltask(string*t, list* =0); + +/* + * The makegate function creates a new gate (which need not have a + * name) and connects it to the specified wires. + */ +extern void pform_makegate(PGBuiltin::Type type, + const string&name, + const vector&wires, + unsigned long delay_value); + +extern void pform_makegates(PGBuiltin::Type type, + PExpr*delay, + list*gates); + +extern void pform_make_modgates(const string&type, list*gates); + +/* Make a continuous assignment node, with optional bit- or part- select. */ +extern void pform_make_pgassign(const string&lval, PExpr*rval); +extern void pform_make_pgassign(const string&lval, PExpr*sel, PExpr*rval); + +/* + * These are functions that the outside-the-parser code uses the do + * interesting things to the verilog. The parse function reads and + * parses the source file and places all the modules it finds into the + * mod list. The dump function dumps a module to the output stream. + */ +extern int pform_parse(FILE*, list&mod); +extern void pform_dump(ostream&out, Module*mod); + +/* + * $Log: pform.h,v $ + * Revision 1.1 1998/11/03 23:29:04 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/pform_dump.cc b/pform_dump.cc new file mode 100644 index 000000000..634c2b3d5 --- /dev/null +++ b/pform_dump.cc @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: pform_dump.cc,v 1.1 1998/11/03 23:29:04 steve Exp $" +#endif + +/* + * This file provides the pform_dump function, that dumps the module + * passed as a parameter. The dump is as much as possible in Verilog + * syntax, so that a human can tell that it really does describe the + * module in question. + */ +# include "pform.h" +# include +# include +# include + +ostream& operator << (ostream&out, const PExpr&obj) +{ + obj.dump(out); + return out; +} + +void PExpr::dump(ostream&out) const +{ + out << typeid(*this).name(); +} + +void PENumber::dump(ostream&out) const +{ + out << value(); +} + +void PEIdent::dump(ostream&out) const +{ + out << text_; + if (msb_) { + out << "[" << *msb_; + if (lsb_) { + out << ":" << *lsb_; + } + out << "]"; + } +} + +void PEString::dump(ostream&out) const +{ + out << "\"" << text_ << "\""; +} + +void PEUnary::dump(ostream&out) const +{ + out << op_ << "(" << *expr_ << ")"; +} + +void PEBinary::dump(ostream&out) const +{ + out << "(" << *left_ << ")" << op_ << "(" << *right_ << ")"; +} + + +void PWire::dump(ostream&out) const +{ + switch (type) { + case NetNet::IMPLICIT: + out << " implicit wire "; + break; + case NetNet::WIRE: + out << " wire "; + break; + case NetNet::REG: + out << " reg "; + break; + default: + out << " (" << type << ") "; + break; + } + + switch (port_type) { + case NetNet::PIMPLICIT: + out << "(implicit input) "; + break; + case NetNet::PINPUT: + out << "(input) "; + break; + case NetNet::POUTPUT: + out << "(output) "; + break; + case NetNet::PINOUT: + out << "(input output) "; + break; + case NetNet::NOT_A_PORT: + break; + } + + if (lsb || msb) { + assert(lsb && msb); + out << "[" << *msb << ":" << *lsb << "] "; + } + + out << name << ";" << endl; +} + +void PGate::dump_pins(ostream&out) const +{ + if (pin_count()) { + out << *pin(0); + + for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) { + out << ", " << *pin(idx); + } + } +} + +void PGate::dump(ostream&out) const +{ + out << " " << typeid(*this).name() << " #" + << get_delay() << " " << get_name() << "("; + dump_pins(out); + out << ");" << endl; + +} + +void PGAssign::dump(ostream&out) const +{ + out << " assign " << *pin(0) << " = " << *pin(1) << ";" << endl; +} + +void PGBuiltin::dump(ostream&out) const +{ + switch (type()) { + case PGBuiltin::NAND: + out << " nand #"; + break; + default: + out << " builtin gate #"; + } + + out << get_delay() << " " << get_name() << "("; + dump_pins(out); + out << ");" << endl; +} + +void PGModule::dump(ostream&out) const +{ + out << " " << type_ << " " << get_name() << "("; + dump_pins(out); + out << ");" << endl; +} + +void Statement::dump(ostream&out, unsigned ind) const +{ + /* I give up. I don't know what type this statement is, + so just print the C++ typeid and let the user figure + it out. */ + out << setw(ind) << ""; + out << "/* " << typeid(*this) .name() << " */ ;" << endl; +} + +void PAssign::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << ""; + out << to_name_ << " = " << *expr_ << ";" << endl; +} + +void PBlock::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "begin" << endl; + + for (unsigned idx = 0 ; idx < size() ; idx += 1) { + stat(idx)->dump(out, ind+2); + } + + out << setw(ind) << "" << "end" << endl; +} + +void PCallTask::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << name_; + + if (nparms_) { + out << "("; + if (parms_[0]) + out << *parms_[0]; + + for (unsigned idx = 1 ; idx < nparms_ ; idx += 1) { + out << ", "; + if (parms_[idx]) + out << *parms_[idx]; + } + out << ")"; + } + + out << ";" << endl; +} + + +void PDelayStatement::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "#" << *delay_ << endl; + statement_->dump(out, ind+2); +} + +void PEventStatement::dump(ostream&out, unsigned ind) const +{ + out << setw(ind) << "" << "@("; + switch (type_) { + case NetPEvent::ANYEDGE: + break; + case NetPEvent::POSEDGE: + out << "posedge "; + break; + case NetPEvent::NEGEDGE: + out << "negedge "; + break; + } + out << *expr_ << ")" << endl; + + statement_->dump(out, ind+2); +} + +void PProcess::dump(ostream&out, unsigned ind) const +{ + switch (type_) { + case PProcess::PR_INITIAL: + out << setw(ind) << "" << "initial" << endl; + break; + case PProcess::PR_ALWAYS: + out << setw(ind) << "" << "always" << endl; + break; + } + + statement_->dump(out, ind+2); +} + +void pform_dump(ostream&out, Module*mod) +{ + out << "module " << mod->get_name() << ";" << endl; + + // Iterate through and display all the wires. + const list&wires = mod->get_wires(); + for (list::const_iterator wire = wires.begin() + ; wire != wires.end() + ; wire ++ ) { + + (*wire)->dump(out); + } + + + // Iterate through and display all the gates + const list&gates = mod->get_gates(); + for (list::const_iterator gate = gates.begin() + ; gate != gates.end() + ; gate ++ ) { + + (*gate)->dump(out); + } + + + const list&behaves = mod->get_behaviors(); + for (list::const_iterator behav = behaves.begin() + ; behav != behaves.end() + ; behav ++ ) { + + (*behav)->dump(out, 4); + } + + out << "endmodule" << endl; +} + + +/* + * $Log: pform_dump.cc,v $ + * Revision 1.1 1998/11/03 23:29:04 steve + * Introduce verilog to CVS. + * + */ + diff --git a/stupid.cc b/stupid.cc new file mode 100644 index 000000000..5cf907ce0 --- /dev/null +++ b/stupid.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: stupid.cc,v 1.1 1998/11/03 23:29:05 steve Exp $" +#endif + +# include "netlist.h" +# include + +vector* list_link_nodes(NetObj::Link&link) +{ + NetObj*net; + unsigned npin; + vector*result = new vector; + + link.cur_link(net, npin); + NetObj*cur = net; + unsigned cpin = npin; + do { + if (dynamic_cast(cur)) + result->push_back(&cur->pin(cpin)); + + cur->pin(cpin).next_link(cur, cpin); + } while ((cur != net) || (cpin != npin)); + + return result; +} + +/* + * This function scans a design and removes artifacts from the + * elaboration step, and maybe a few other stupid inefficiencies. + */ + +class Functor : public Design::SigFunctor { + + public: + virtual void sig_function(NetNet*); +}; + + +void Functor::sig_function(NetNet*net) +{ + for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) { + vector*nodes = list_link_nodes(net->pin(idx)); +#if 0 + cerr << "XXXX " << net->name() << "[" << idx << "] " + "nodes->size() == " << nodes->size() << endl; +#endif + delete nodes; + } +} + +void stupid(Design*des) +{ + Functor fun; + des->scan_signals(&fun); +} + +/* + * $Log: stupid.cc,v $ + * Revision 1.1 1998/11/03 23:29:05 steve + * Introduce verilog to CVS. + * + */ + diff --git a/t-verilog.cc b/t-verilog.cc new file mode 100644 index 000000000..4db14054f --- /dev/null +++ b/t-verilog.cc @@ -0,0 +1,298 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: t-verilog.cc,v 1.1 1998/11/03 23:29:05 steve Exp $" +#endif + +/* + * This rather interesting target generates Verilog from the + * design. This is interesting since by the time I get here, + * elaboration and generic optimizations have been performed. This is + * useful for feeding optimized designs to other tools, or the human + * for debugging. + */ + +# include +# include +# include +# include +# include "target.h" + +extern const struct target tgt_verilog; + +class target_verilog : public target_t { + public: + virtual void start_design(ostream&os, const Design*); + virtual void signal(ostream&os, const NetNet*); + virtual void logic(ostream&os, const NetLogic*); + virtual void bufz(ostream&os, const NetBUFZ*); + virtual void start_process(ostream&os, const NetProcTop*); + virtual void proc_assign(ostream&os, const NetAssign*); + virtual void proc_block(ostream&os, const NetBlock*); + virtual void proc_delay(ostream&os, const NetPDelay*); + virtual void proc_event(ostream&os, const NetPEvent*); + virtual void proc_task(ostream&os, const NetTask*); + virtual void end_design(ostream&os, const Design*); + private: + unsigned indent_; + + void emit_expr_(ostream&os, const NetExpr*); +}; + + +static ostream& operator<< (ostream&o, NetNet::Type t) +{ + switch (t) { + case NetNet::WIRE: + o << "wire"; + break; + case NetNet::REG: + o << "reg"; + break; + } + return o; +} + +/* + * The output of the design starts here. The emit operation calls the + * design_start and design_end target methods around the scan of the + * design. Targets can use these hooks to generate header or footer + * information if desired. + */ +void target_verilog::start_design(ostream&os, const Design*) +{ + indent_ = 0; + os << "module " << "main" << ";" << endl; +} + +/* + * Emit first iterates over all the signals. This gives the target a + * chance to declare signal variables before the network is assembled + * or behaviors are written. + */ +void target_verilog::signal(ostream&os, const NetNet*net) +{ + os << " " << net->type(); + if (net->pin_count() > 1) + os << " [" << net->msb() << ":" << net->lsb() << "]"; + + if (net->delay1()) + os << " #" << net->delay1(); + + os << " " << mangle(net->name()) << ";" << endl; +} + +void target_verilog::logic(ostream&os, const NetLogic*net) +{ + switch (net->type()) { + + case NetLogic::AND: + os << " and"; + break; + case NetLogic::NAND: + os << " nand"; + break; + case NetLogic::NOR: + os << " nor"; + break; + case NetLogic::NOT: + os << " not"; + break; + case NetLogic::OR: + os << " or"; + break; + case NetLogic::XOR: + os << " xor"; + break; + } + + os << " #" << net->delay1() << " " << mangle(net->name()) << "("; + + unsigned sidx; + const NetNet*sig = find_link_signal(net, 0, sidx); + os << mangle(sig->name()) << "[" << sidx << "]"; + for (unsigned idx = 1 ; idx < net->pin_count() ; idx += 1) { + sig = find_link_signal(net, idx, sidx); + assert(sig); + os << ", " << mangle(sig->name()) << "[" << sidx << "]"; + } + os << ");" << endl; +} + +void target_verilog::bufz(ostream&os, const NetBUFZ*net) +{ + assert( net->pin_count() == 2 ); + os << " assign "; + + unsigned sidx; + const NetNet*sig = find_link_signal(net, 0, sidx); + os << mangle(sig->name()) << "[" << sidx << "] = "; + + sig = find_link_signal(net, 1, sidx); + os << mangle(sig->name()) << "[" << sidx << "];" << endl; +} + +void target_verilog::start_process(ostream&os, const NetProcTop*proc) +{ + switch (proc->type()) { + case NetProcTop::KINITIAL: + os << " initial" << endl; + break; + case NetProcTop::KALWAYS: + os << " always" << endl; + break; + } + + indent_ = 6; +} + +void target_verilog::proc_assign(ostream&os, const NetAssign*net) +{ + os << setw(indent_) << ""; + + const NetNet*lval = net->lval(); + + os << mangle(lval->name()) << " = "; + + emit_expr_(os, net->rval()); + + os << ";" << endl; +} + +void target_verilog::emit_expr_(ostream&os, const NetExpr*expr) +{ + + if (const NetEConst*pp = dynamic_cast(expr)) { + + os << pp->value(); + + } else if (const NetEIdent*pp = dynamic_cast(expr)) { + + os << mangle(pp->name()); + + } else if (const NetEUnary*pp = dynamic_cast(expr)) { + + os << pp->op() << "("; + emit_expr_(os, pp->expr()); + os << ")"; + + } else { + os << "(huh?)"; + } +} + +void target_verilog::proc_block(ostream&os, const NetBlock*net) +{ + os << setw(indent_) << "" << "begin" << endl; + indent_ += 4; + net->emit_recurse(os, this); + indent_ -= 4; + os << setw(indent_) << "" << "end" << endl; +} + +void target_verilog::proc_delay(ostream&os, const NetPDelay*net) +{ + os << setw(indent_) << "" << "#" << net->delay() << endl; + + indent_ += 4; + net->emit_proc_recurse(os, this); + indent_ -= 4; +} + +void target_verilog::proc_event(ostream&os, const NetPEvent*net) +{ + os << setw(indent_) << "" << "@"; + + unsigned sidx; + const NetNet*sig = find_link_signal(net, 0, sidx); + + switch (net->edge()) { + case NetPEvent::ANYEDGE: + os << mangle(sig->name()) << "[" << sidx << "]"; + break; + case NetPEvent::POSEDGE: + os << "(posedge " << mangle(sig->name()) << "[" << sidx << "])"; + break; + case NetPEvent::NEGEDGE: + os << "(negedge " << mangle(sig->name()) << "[" << sidx << "])"; + break; + } + + os << endl; + + indent_ += 4; + net->emit_proc_recurse(os, this); + indent_ -= 4; +} + +static void vtask_parm(ostream&os, const NetExpr*ex) +{ + if (const NetEConst*pp = dynamic_cast(ex)) { + if (pp->value().is_string()) + os << "\"" << pp->value().as_string() << "\""; + else + os << pp->value(); + + } else if (const NetEIdent*pp = dynamic_cast(ex)) { + os << mangle(pp->name()); + + } else { + } +} + +void target_verilog::proc_task(ostream&os, const NetTask*net) +{ + os << setw(indent_) << "" << net->name(); + if (net->nparms() > 0) { + os << "("; + vtask_parm(os, net->parm(0)); + for (unsigned idx = 1 ; idx < net->nparms() ; idx += 1) { + os << ", "; + vtask_parm(os, net->parm(idx)); + } + os << ")"; + } + + os << ";" << endl; +} + + +/* + * All done with the design. Flush any output that I've been holding + * off, and write the footers for the target. + */ +void target_verilog::end_design(ostream&os, const Design*) +{ + os << "endmodule" << endl; +} + +static target_verilog tgt_verilog_obj; + +const struct target tgt_verilog = { + "verilog", + &tgt_verilog_obj +}; + +/* + * $Log: t-verilog.cc,v $ + * Revision 1.1 1998/11/03 23:29:05 steve + * Introduce verilog to CVS. + * + */ + diff --git a/t-vvm.cc b/t-vvm.cc new file mode 100644 index 000000000..7df05ba45 --- /dev/null +++ b/t-vvm.cc @@ -0,0 +1,493 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: t-vvm.cc,v 1.1 1998/11/03 23:29:05 steve Exp $" +#endif + +# include +# include +# include +# include +# include "netlist.h" +# include "target.h" + +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 { + public: + virtual void start_design(ostream&os, const Design*); + virtual void signal(ostream&os, const NetNet*); + virtual void logic(ostream&os, const NetLogic*); + virtual void bufz(ostream&os, const NetBUFZ*); + virtual void net_pevent(ostream&os, const NetPEvent*); + virtual void start_process(ostream&os, const NetProcTop*); + virtual void proc_assign(ostream&os, const NetAssign*); + virtual void proc_block(ostream&os, const NetBlock*); + virtual void proc_task(ostream&os, const NetTask*); + virtual void proc_event(ostream&os, const NetPEvent*); + virtual void proc_delay(ostream&os, const NetPDelay*); + virtual void end_process(ostream&os, const NetProcTop*); + virtual void end_design(ostream&os, const Design*); + + private: + void emit_gate_outputfun_(const NetNode*); + + ostrstream delayed; + unsigned process_counter; + unsigned thread_step_; +}; + +class vvm_proc_rval : public expr_scan_t { + + public: + explicit vvm_proc_rval(ostream&os, unsigned width) + : result(""), os_(os), width_(width) { } + + string result; + + private: + ostream&os_; + unsigned width_; + + private: + virtual void expr_const(const NetEConst*); + virtual void expr_ident(const NetEIdent*); + virtual void expr_unary(const NetEUnary*); +}; + +void vvm_proc_rval::expr_const(const NetEConst*expr) +{ + string tname = make_temp(); + os_ << " vvm_bitset_t<" << width_ << "> " << tname << ";" << endl; + for (unsigned idx = 0 ; idx < width_ ; idx += 1) { + os_ << " " << tname << "[" << idx << "] = "; + 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()); +} + +void vvm_proc_rval::expr_unary(const NetEUnary*expr) +{ + expr->expr()->expr_scan(this); + string tname = make_temp(); + + os_ << " vvm_bitset_t<" << width_ << "> " << tname << " = "; + switch (expr->op()) { + case '~': + os_ << "vvm_unop_not(" << result << ");" + << endl; + break; + default: + cerr << "vvm: Unhandled unary op `" << expr->op() << "'" + << endl; + os_ << result << ";" << endl; + break; + } + + result = tname; +} + +static string emit_proc_rval(ostream&os, unsigned width, const NetExpr*expr) +{ + vvm_proc_rval scan (os, width); + expr->expr_scan(&scan); + return scan.result; +} + +class vvm_parm_rval : public expr_scan_t { + + public: + explicit vvm_parm_rval(ostream&os) + : result(""), os_(os) { } + + string result; + + private: + virtual void expr_const(const NetEConst*); + virtual void expr_ident(const NetEIdent*); + + private: + ostream&os_; +}; + +void vvm_parm_rval::expr_const(const NetEConst*expr) +{ + if (expr->value().is_string()) { + result = "\""; + result = result + expr->value().as_string() + "\""; + return; + } +} + +void vvm_parm_rval::expr_ident(const NetEIdent*expr) +{ + if (expr->name() == "$time") { + string res = make_temp(); + os_ << " vvm_calltf_parm " << res << + "(vvm_calltf_parm::TIME);" << endl; + result = res; + } else { + string res = make_temp(); + os_ << " vvm_calltf_parm::SIG " << res << ";" << endl; + os_ << " " << res << ".bits = &" << + mangle(expr->name()) << ";" << endl; + os_ << " " << res << ".mon = &" << + mangle(expr->name()) << "_mon;" << endl; + result = res; + } +} + +static string emit_parm_rval(ostream&os, const NetExpr*expr) +{ + vvm_parm_rval scan (os); + expr->expr_scan(&scan); + return scan.result; +} + +void target_vvm::start_design(ostream&os, const Design*mod) +{ + 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; + process_counter = 0; +} + +void target_vvm::end_design(ostream&os, const Design*mod) +{ + delayed << ends; + os << delayed.str(); + + os << "main()" << endl << "{" << endl; + os << " vvm_simulation sim;" << endl; + + for (unsigned idx = 0 ; idx < process_counter ; idx += 1) + os << " thread" << (idx+1) << "_t thread_" << + (idx+1) << "(&sim);" << endl; + + os << " sim.run();" << endl; + os << "}" << endl; +} + +void target_vvm::signal(ostream&os, const NetNet*sig) +{ + os << "static vvm_bitset_t<" << sig->pin_count() << "> " << + mangle(sig->name()) << "; /* " << sig->name() << " */" << endl; + os << "static vvm_monitor_t " << mangle(sig->name()) << "_mon(\"" + << sig->name() << "\");" << endl; +} + +/* + * This method handles writing output functions for gates that have a + * single output (at pin 0). This writes the output_fun method into + * the delayed stream to be emitted to the output file later. + */ +void target_vvm::emit_gate_outputfun_(const NetNode*gate) +{ + delayed << "static void " << mangle(gate->name()) << + "_output_fun(vvm_simulation*sim, vvm_bit_t val)" << + endl << "{" << endl; + + /* The output function connects to pin 0 of the netlist part + and causes the inputs that it is connected to to be set + with the new value. */ + + const NetObj*cur; + unsigned pin; + gate->pin(0).next_link(cur, pin); + while (cur != gate) { + // Skip signals + if (const NetNet*sig = dynamic_cast(cur)) { + + delayed << " " << mangle(sig->name()) << "[" << + pin << "] = val;" << endl; + delayed << " " << mangle(sig->name()) << + "_mon.trigger(sim);" << endl; + + } else { + delayed << " " << mangle(cur->name()) << ".set(sim, " + << pin << ", val);" << endl; + } + + cur->pin(pin).next_link(cur, pin); + } + + delayed << "}" << endl; +} + +void target_vvm::logic(ostream&os, const NetLogic*gate) +{ + os << "static void " << mangle(gate->name()) << + "_output_fun(vvm_simulation*, vvm_bit_t);" << endl; + + switch (gate->type()) { + case NetLogic::AND: + os << "static vvm_and" << "<" << gate->pin_count()-1 << + "," << gate->delay1() << "> "; + break; + case NetLogic::NAND: + os << "static vvm_nand" << "<" << gate->pin_count()-1 << + "," << gate->delay1() << "> "; + break; + case NetLogic::NOR: + os << "static vvm_nor" << "<" << gate->pin_count()-1 << + "," << gate->delay1() << "> "; + break; + case NetLogic::NOT: + os << "static vvm_not" << "<" << gate->delay1() << "> "; + break; + case NetLogic::OR: + os << "static vvm_or" << "<" << gate->pin_count()-1 << + "," << gate->delay1() << "> "; + break; + case NetLogic::XOR: + os << "static vvm_xor" << "<" << gate->pin_count()-1 << + "," << gate->delay1() << "> "; + break; + } + + os << mangle(gate->name()) << "(&" << + mangle(gate->name()) << "_output_fun);" << endl; + + emit_gate_outputfun_(gate); +} + +void target_vvm::bufz(ostream&os, const NetBUFZ*gate) +{ + os << "static void " << mangle(gate->name()) << + "_output_fun(vvm_simulation*, vvm_bit_t);" << endl; + + os << "static vvm_bufz " << mangle(gate->name()) << "(&" << + mangle(gate->name()) << "_output_fun);" << endl; + + emit_gate_outputfun_(gate); +} + +/* + * The net_pevent device is a synthetic device type--a fabrication of + * the elaboration phase. An event device receives value changes from + * the attached signal. It is an input only device, its only value + * being the side-effects that threads waiting on events can be + * awakened. + * + * The proc_event method handles the other half of this, the process + * that blocks on the event. + */ +void target_vvm::net_pevent(ostream&os, const NetPEvent*gate) +{ + os << "static vvm_pevent " << mangle(gate->name()) << ";" + " /* " << gate->name() << " */" << endl; +} + +void target_vvm::start_process(ostream&os, const NetProcTop*proc) +{ + process_counter += 1; + thread_step_ = 0; + + os << "class thread" << process_counter << + "_t : public vvm_thread {" << endl; + + os << " public:" << endl; + os << " thread" << process_counter << + "_t(vvm_simulation*sim)" << endl; + os << " : vvm_thread(sim), step_(&step_0_)" << endl; + os << " { }" << endl; + os << " ~thread" << process_counter << "_t() { }" << endl; + os << endl; + os << " void go() { (this->*step_)(); }" << endl; + os << " private:" << endl; + os << " void (thread" << process_counter << + "_t::*step_)();" << endl; + + os << " void step_0_() {" << endl; +} + +/* + * This method generates code for a procedural assignment. The lval is + * a signal, but the assignment should generate code to go to all the + * connected devices/events. + */ +void target_vvm::proc_assign(ostream&os, const NetAssign*net) +{ + string rval = emit_proc_rval(os, net->lval()->pin_count(), net->rval()); + + os << " // " << net->lval()->name() << " = "; + net->rval()->dump(os); + os << endl; + + os << " " << mangle(net->lval()->name()) << " = " << rval << + ";" << endl; + + os << " " << mangle(net->lval()->name()) << + "_mon.trigger(sim_);" << endl; + + + /* Not only is the lvalue signal assigned to, send the bits to + all the other pins that are connected to this signal. */ + + for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1) { + const NetObj*cur; + unsigned pin; + for (net->lval()->pin(0).next_link(cur, pin) + ; cur != net->lval() + ; cur->pin(pin).next_link(cur, pin)) { + + // Skip NetAssign nodes. They are output-only. + if (dynamic_cast(cur)) + continue; + + if (const NetNet*sig = dynamic_cast(cur)) { + os << " " << mangle(sig->name()) << "[" << + pin << "] = " << rval << "[" << idx << + "];" << endl; + os << " " << mangle(sig->name()) << + "_mon.trigger(sim_);" << endl; + + } else { + os << " " << mangle(cur->name()) << + ".set(sim_, " << pin << ", " << + rval << "[" << idx << "]);" << endl; + } + } + } +} + +void target_vvm::proc_block(ostream&os, const NetBlock*net) +{ + net->emit_recurse(os, this); +} + +void target_vvm::proc_task(ostream&os, const NetTask*net) +{ + if (net->name()[0] == '$') { + string ptmp = make_temp(); + os << " struct vvm_calltf_parm " << ptmp << "[" << + net->nparms() << "];" << endl; + for (unsigned idx = 0 ; idx < net->nparms() ; idx += 1) + if (net->parm(idx)) { + string val = emit_parm_rval(os, net->parm(idx)); + os << " " << ptmp << "[" << idx << "] = " << + val << ";" << endl; + } + os << " vvm_calltask(sim_, \"" << net->name() << "\", " << + net->nparms() << ", " << ptmp << ");" << endl; + } else { + os << " // Huh? " << net->name() << endl; + } +} + +/* + * 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; + os << " step_ = &step_" << thread_step_ << "_;" << endl; + os << " " << mangle(proc->name()) << ".wait(vvm_pevent::"; + switch (proc->edge()) { + case NetPEvent::ANYEDGE: + os << "ANYEDGE"; + break; + case NetPEvent::POSEDGE: + os << "POSEDGE"; + break; + case NetPEvent::NEGEDGE: + os << "NEGEDGE"; + break; + } + os << ", this);" << endl; + os << " }" << endl; + os << " void step_" << thread_step_ << "_()" << endl; + os << " {" << endl; + + proc->emit_proc_recurse(os, this); +} + +/* + * A delay suspends the thread for a period of time. + */ +void target_vvm::proc_delay(ostream&os, const NetPDelay*proc) +{ + thread_step_ += 1; + os << " step_ = &step_" << thread_step_ << "_;" << endl; + os << " sim_->thread_delay(" << proc->delay() << ", this);" + << endl; + os << " }" << endl; + os << " void step_" << thread_step_ << "_()" << endl; + os << " {" << endl; + + proc->emit_proc_recurse(os, this); +} + +void target_vvm::end_process(ostream&os, const NetProcTop*proc) +{ + if (proc->type() == NetProcTop::KALWAYS) { + os << " step_ = &step_0_;" << endl; + os << " step_0_(); // XXXX" << endl; + } else { + os << " step_ = 0;" << endl; + } + + os << " }" << endl; + os << "};" << endl; +} + + +static target_vvm target_vvm_obj; + +extern const struct target tgt_vvm = { + "vvm", + &target_vvm_obj +}; +/* + * $Log: t-vvm.cc,v $ + * Revision 1.1 1998/11/03 23:29:05 steve + * Introduce verilog to CVS. + * + */ + diff --git a/target.cc b/target.cc new file mode 100644 index 000000000..16478b99a --- /dev/null +++ b/target.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1998 Stephen Williams + * + * 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) +#ident "$Id: target.cc,v 1.1 1998/11/03 23:29:06 steve Exp $" +#endif + +# include "target.h" +# include + +target_t::~target_t() +{ +} + +void target_t::start_design(ostream&os, const Design*) +{ +} + +void target_t::signal(ostream&os, const NetNet*) +{ +} + +void target_t::logic(ostream&os, const NetLogic*) +{ +} + +void target_t::bufz(ostream&os, const NetBUFZ*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled continuous assign (BUFZ)." << endl; +} + +void target_t::net_assign(ostream&os, const NetAssign*) +{ +} + +void target_t::net_pevent(ostream&os, const NetPEvent*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled EVENT node." << endl; +} + +void target_t::start_process(ostream&os, const NetProcTop*) +{ +} + +void target_t::proc_assign(ostream&os, const NetAssign*) +{ +} + +void target_t::proc_block(ostream&os, const NetBlock*) +{ +} + +void target_t::proc_delay(ostream&os, const NetPDelay*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled proc_delay." << endl; +} + +void target_t::proc_event(ostream&os, const NetPEvent*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled proc_event." << endl; +} + +void target_t::proc_task(ostream&os, const NetTask*) +{ +} + +void target_t::end_process(ostream&os, const NetProcTop*) +{ +} + +void target_t::end_design(ostream&os, const Design*) +{ +} + +expr_scan_t::~expr_scan_t() +{ +} + +void expr_scan_t::expr_const(const NetEConst*) +{ + cerr << "expr_scan_t (" << typeid(*this).name() << "): " + "unhandled expr_const." << endl; +} + +void expr_scan_t::expr_ident(const NetEIdent*) +{ + cerr << "expr_scan_t (" << typeid(*this).name() << "): " + "unhandled expr_ident." << endl; +} + +void expr_scan_t::expr_unary(const NetEUnary*) +{ + cerr << "expr_scan_t (" << typeid(*this).name() << "): " + "unhandled expr_unary." << endl; +} + +/* + * $Log: target.cc,v $ + * Revision 1.1 1998/11/03 23:29:06 steve + * Introduce verilog to CVS. + * + */ + diff --git a/target.h b/target.h new file mode 100644 index 000000000..fa69f561c --- /dev/null +++ b/target.h @@ -0,0 +1,115 @@ +#ifndef __target_H +#define __target_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: target.h,v 1.1 1998/11/03 23:29:06 steve Exp $" +#endif + +# include "netlist.h" +class ostream; + +/* + * This header file describes the types and constants used to describe + * the possible target output types of the compiler. The backends + * provide one of these in order to tell the previous steps what the + * backend is able to do. + */ + +/* + * The backend driver is hooked into the compiler, and given a name, + * by creating an instance of the target structure. The structure has + * the name that the compiler will use to locate the driver, and a + * pointer to a target_t object that is the actual driver. + */ +struct target { + string name; + struct target_t* meth; +}; + +/* + * The emit process uses a target_t driver to send the completed + * design to a file. It is up to the driver object to follow along in + * the iteration through the design, generating output as it can. + */ + +struct target_t { + virtual ~target_t(); + + /* Start the design. */ + virtual void start_design(ostream&os, const Design*); + + /* Output a signal (called for each signal) */ + virtual void signal(ostream&os, const NetNet*); + + /* Output a gate (called for each gate) */ + virtual void logic(ostream&os, const NetLogic*); + virtual void bufz(ostream&os, const NetBUFZ*); + virtual void net_assign(ostream&os, const NetAssign*); + virtual void net_pevent(ostream&os, const NetPEvent*); + + /* Output a process (called for each process) */ + virtual void start_process(ostream&os, const NetProcTop*); + + /* Various kinds of process nodes are dispatched through these. */ + virtual void proc_assign(ostream&os, const NetAssign*); + virtual void proc_block(ostream&os, const NetBlock*); + virtual void proc_task(ostream&os, const NetTask*); + + virtual void proc_event(ostream&os, const NetPEvent*); + virtual void proc_delay(ostream&os, const NetPDelay*); + + /* (called for each process) */ + virtual void end_process(ostream&os, const NetProcTop*); + + /* Done with the design. */ + virtual void end_design(ostream&os, const Design*); +}; + +/* This class is used by the NetExpr class to help with the scanning + of expressions. */ +struct expr_scan_t { + virtual ~expr_scan_t(); + virtual void expr_const(const NetEConst*); + virtual void expr_ident(const NetEIdent*); + virtual void expr_unary(const NetEUnary*); +}; + + +/* The emit functions take a design and emit it to the output stream + using the specified target. If the target is given by name, it is + located in the target_table and used. */ +extern void emit(ostream&o, const Design*des, const char*type); + +/* This function takes a fully qualified verilog name (which may have, + for example, dots in it) and produces a mangled version that can be + used by most any language. */ +extern string mangle(const string&str); + +/* This is the table of supported output targets. It is a null + terminated array of pointers to targets. */ +extern const struct target *target_table[]; + +/* + * $Log: target.h,v $ + * Revision 1.1 1998/11/03 23:29:06 steve + * Introduce verilog to CVS. + * + */ +#endif diff --git a/targets.cc b/targets.cc new file mode 100644 index 000000000..8290f545b --- /dev/null +++ b/targets.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: targets.cc,v 1.1 1998/11/03 23:29:07 steve Exp $" +#endif + +# include "target.h" + +extern const struct target tgt_verilog; +extern const struct target tgt_vvm; +const struct target *target_table[] = { + &tgt_verilog, + &tgt_vvm, + 0 +}; + +/* + * $Log: targets.cc,v $ + * Revision 1.1 1998/11/03 23:29:07 steve + * Introduce verilog to CVS. + * + */ + diff --git a/verinum.cc b/verinum.cc new file mode 100644 index 000000000..08bace2e7 --- /dev/null +++ b/verinum.cc @@ -0,0 +1,219 @@ +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: verinum.cc,v 1.1 1998/11/03 23:29:07 steve Exp $" +#endif + +# include "verinum.h" +# include +# include + +verinum::verinum() +: bits_(0), nbits_(0), string_flag_(false) +{ +} + +verinum::verinum(const V*bits, unsigned nbits) +: string_flag_(false) +{ + nbits_ = nbits; + bits_ = new V [nbits]; + for (unsigned idx = 0 ; idx < nbits ; idx += 1) { + bits_[idx] = bits[idx]; + } +} + +verinum::verinum(const string&str) +: string_flag_(true) +{ + nbits_ = str.length() * 8; + bits_ = new V [nbits_]; + + unsigned idx, cp; + V*bp = bits_+nbits_; + for (idx = nbits_, cp = 0 ; idx > 0 ; idx -= 8, cp += 1) { + char ch = str[cp]; + *(--bp) = (ch&0x80) ? V1 : V0; + *(--bp) = (ch&0x40) ? V1 : V0; + *(--bp) = (ch&0x20) ? V1 : V0; + *(--bp) = (ch&0x10) ? V1 : V0; + *(--bp) = (ch&0x08) ? V1 : V0; + *(--bp) = (ch&0x04) ? V1 : V0; + *(--bp) = (ch&0x02) ? V1 : V0; + *(--bp) = (ch&0x01) ? V1 : V0; + } +} + +verinum::verinum(const verinum&that) +{ + string_flag_ = that.string_flag_; + nbits_ = that.nbits_; + bits_ = new V[nbits_]; + for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) + bits_[idx] = that.bits_[idx]; +} + +verinum::~verinum() +{ + delete[]bits_; +} + +verinum::V verinum::get(unsigned idx) const +{ + return bits_[idx]; +} + +unsigned long verinum::as_ulong() const +{ + assert(nbits_ <= 8 * sizeof(unsigned long)); + + if (nbits_ == 0) + return 0; + + unsigned long val = 0; + for (unsigned idx = nbits_ ; idx > 0 ; idx -= 1) { + val <<= 1; + switch (bits_[idx-1]) { + case V0: + break; + case V1: + val |= 1; + break; + case Vx: + case Vz: + // what should I do here? Throw an exception? + break; + } + } + return val; +} + +signed long verinum::as_long() const +{ + assert(nbits_ <= 8 * sizeof(signed long)); + + if (nbits_ == 0) + return 0; + + signed long val = 0; + + // Extend the sign bit to fill the long. + if (bits_[nbits_-1] == V1) + val = -1; + + for (unsigned idx = nbits_ ; idx > 0 ; idx -= 1) { + val <<= 1; + switch (bits_[idx-1]) { + case V0: + break; + case V1: + val |= 1; + break; + case Vx: + case Vz: + // what should I do here? Throw an exception? + break; + } + } + + return val; +} + +string verinum::as_string() const +{ + assert( nbits_%8 == 0 ); + if (nbits_ == 0) + return ""; + + char*tmp = new char[nbits_/8+1]; + char*cp = tmp; + for (unsigned idx = nbits_ ; idx > 0 ; idx -= 8, cp += 1) { + V*bp = bits_+idx; + *cp = 0; + if (*(--bp) == V1) *cp |= 0x80; + if (*(--bp) == V1) *cp |= 0x40; + if (*(--bp) == V1) *cp |= 0x20; + if (*(--bp) == V1) *cp |= 0x10; + if (*(--bp) == V1) *cp |= 0x08; + if (*(--bp) == V1) *cp |= 0x04; + if (*(--bp) == V1) *cp |= 0x02; + if (*(--bp) == V1) *cp |= 0x01; + } + + tmp[nbits_/8] = 0; + string result = string(tmp); + delete[]tmp; + return result; +} + +ostream& operator<< (ostream&o, const verinum&v) +{ + o << v.len() << "'b"; + + + verinum::V trim_left = v.get(v.len()-1); + unsigned idx; + + for (idx = v.len()-1; idx > 0; idx -= 1) + if (trim_left != v.get(idx-1)) + break; + + switch (trim_left) { + case verinum::V0: + o << "0"; + break; + case verinum::V1: + o << "1"; + break; + case verinum::Vx: + o << "x"; + break; + case verinum::Vz: + o << "z"; + break; + } + + while (idx > 0) { + switch (v.get(idx-1)) { + case verinum::V0: + o << "0"; + break; + case verinum::V1: + o << "1"; + break; + case verinum::Vx: + o << "x"; + break; + case verinum::Vz: + o << "z"; + break; + } + idx -= 1; + } + + return o; +} + +/* + * $Log: verinum.cc,v $ + * Revision 1.1 1998/11/03 23:29:07 steve + * Introduce verilog to CVS. + * + */ + diff --git a/verinum.h b/verinum.h new file mode 100644 index 000000000..2bf903921 --- /dev/null +++ b/verinum.h @@ -0,0 +1,84 @@ +#ifndef __verinum_H +#define __verinum_H +/* + * Copyright (c) 1998 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: verinum.h,v 1.1 1998/11/03 23:29:08 steve Exp $" +#endif + +# include + +/* + * Numbers in verlog are multibit strings, where each bit has 4 + * possible values: 0, 1, x or z. The verinum number is store in + * little-endian format. This means that if the long value is 2b'10, + * get(0) is 0 and get(1) is 1. + */ +class verinum { + + public: + enum V { V0, V1, Vx, Vz }; + + verinum(); + verinum(const string&str); + verinum(const V*v, unsigned nbits); + verinum(unsigned long val, unsigned bits); + verinum(const verinum&); + ~verinum(); + + // Number of significant bits in this number. + unsigned len() const { return nbits_; } + + // Individual bits can be accessed with the get and set + // methods. + V get(unsigned idx) const; + V set(unsigned idx, V val); + + V operator[] (unsigned idx) const { return get(idx); } + + + unsigned long as_ulong() const; + signed long as_long() const; + string as_string() const; + + bool is_string() const { return string_flag_; } + + private: + V* bits_; + unsigned nbits_; + + // These are some convenience flags that help us do a better + // job of pretty-printing values. + bool string_flag_; + + private: // not implemented + verinum& operator= (const verinum&); +}; + + +class ostream; +ostream& operator<< (ostream&, const verinum&); + +/* + * $Log: verinum.h,v $ + * Revision 1.1 1998/11/03 23:29:08 steve + * Introduce verilog to CVS. + * + */ +#endif