You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
567 lines
17 KiB
C++
567 lines
17 KiB
C++
/*$Id: lang_verilog.cc $ -*- C++ -*-
|
|
* Copyright (C) 2007 Albert Davis
|
|
* Author: Albert Davis <aldavis@gnu.org>
|
|
*
|
|
* This file is part of "Gnucap", the Gnu Circuit Analysis Package
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
//testing=script 2016.09.10
|
|
#include "u_nodemap.h"
|
|
#include "globals.h"
|
|
#include "c_comand.h"
|
|
#include "d_dot.h"
|
|
#include "d_coment.h"
|
|
#include "e_subckt.h"
|
|
#include "e_model.h"
|
|
#include "u_lang.h"
|
|
#include <stack>
|
|
/*--------------------------------------------------------------------------*/
|
|
std::stack<CARD*> owner_hack;
|
|
/*--------------------------------------------------------------------------*/
|
|
namespace {
|
|
/*--------------------------------------------------------------------------*/
|
|
class LANG_VERILOG : public LANGUAGE {
|
|
enum MODE {mDEFAULT, mPARAMSET} _mode;
|
|
mutable int arg_count;
|
|
enum {INACTIVE = -1};
|
|
public:
|
|
LANG_VERILOG() : arg_count(INACTIVE) {}
|
|
~LANG_VERILOG() {}
|
|
std::string name()const {return "verilog";}
|
|
bool case_insensitive()const {return false;}
|
|
UNITS units()const {return uSI;}
|
|
|
|
public: // override virtual, used by callback
|
|
std::string arg_front()const {untested();
|
|
switch (_mode) {
|
|
case mPARAMSET:untested(); return " ."; break;
|
|
case mDEFAULT:untested(); return (arg_count++ > 0) ? ", ." : "."; break;
|
|
}
|
|
unreachable();
|
|
return "";
|
|
}
|
|
std::string arg_mid()const {untested();
|
|
switch (_mode) {
|
|
case mPARAMSET:untested(); return "="; break;
|
|
case mDEFAULT:untested(); return "("; break;
|
|
}
|
|
unreachable();
|
|
return "";
|
|
}
|
|
std::string arg_back()const {untested();
|
|
switch (_mode) {
|
|
case mPARAMSET:untested(); return ";"; break;
|
|
case mDEFAULT:untested(); return ")"; break;
|
|
}
|
|
unreachable();
|
|
return "";
|
|
}
|
|
|
|
public: // override virtual, called by commands
|
|
void parse_top_item(CS&, CARD_LIST*);
|
|
DEV_COMMENT* parse_comment(CS&, DEV_COMMENT*);
|
|
DEV_DOT* parse_command(CS&, DEV_DOT*);
|
|
MODEL_CARD* parse_paramset(CS&, MODEL_CARD*);
|
|
BASE_SUBCKT* parse_module(CS&, BASE_SUBCKT*);
|
|
COMPONENT* parse_instance(CS&, COMPONENT*);
|
|
std::string find_type_in_string(CS&);
|
|
|
|
private: // override virtual, called by print_item
|
|
void print_paramset(OMSTREAM&, const MODEL_CARD*);
|
|
void print_module(OMSTREAM&, const BASE_SUBCKT*);
|
|
void print_instance(OMSTREAM&, const COMPONENT*);
|
|
void print_comment(OMSTREAM&, const DEV_COMMENT*);
|
|
void print_command(OMSTREAM& o, const DEV_DOT* c);
|
|
private: // local
|
|
void print_args(OMSTREAM&, const MODEL_CARD*);
|
|
void print_args(OMSTREAM&, const COMPONENT*);
|
|
} lang_verilog;
|
|
|
|
DISPATCHER<LANGUAGE>::INSTALL
|
|
d(&language_dispatcher, lang_verilog.name(), &lang_verilog);
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
static void parse_type(CS& cmd, CARD* x)
|
|
{
|
|
assert(x);
|
|
std::string new_type;
|
|
cmd >> new_type;
|
|
x->set_dev_type(new_type);
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void parse_args_paramset(CS& cmd, MODEL_CARD* x)
|
|
{ untested();
|
|
assert(x);
|
|
|
|
while (cmd >> '.') { untested();
|
|
size_t here = cmd.cursor();
|
|
std::string name, value;
|
|
try{ untested();
|
|
cmd >> name >> '=' >> value >> ';';
|
|
x->set_param_by_name(name, value);
|
|
}catch (Exception_No_Match&) {untested();
|
|
cmd.warn(bDANGER, here, x->long_label() + ": bad parameter " + name + " ignored");
|
|
}
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void parse_args_instance(CS& cmd, CARD* x)
|
|
{
|
|
assert(x);
|
|
|
|
if (cmd >> "#(") {
|
|
if (cmd.match1('.')) {
|
|
// by name
|
|
while (cmd >> '.') {
|
|
size_t here = cmd.cursor();
|
|
std::string name = cmd.ctos("(", "", "");
|
|
std::string value = cmd.ctos(",)", "(", ")");
|
|
cmd >> ',';
|
|
try{
|
|
x->set_param_by_name(name, value);
|
|
}catch (Exception_No_Match&) {untested();
|
|
cmd.warn(bDANGER, here, x->long_label() + ": bad parameter " + name + " ignored");
|
|
}
|
|
}
|
|
}else{
|
|
// by order
|
|
int index = 1;
|
|
while (cmd.is_alnum() || cmd.match1("+-.")) { untested();
|
|
size_t here = cmd.cursor();
|
|
try{ untested();
|
|
std::string value = cmd.ctos(",)", "", "");
|
|
x->set_param_by_index(x->param_count() - index++, value, 0/*offset*/);
|
|
}catch (Exception_Too_Many& e) {untested();
|
|
cmd.warn(bDANGER, here, e.message());
|
|
}
|
|
}
|
|
}
|
|
cmd >> ')';
|
|
}else{
|
|
// no args
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void parse_label(CS& cmd, CARD* x)
|
|
{
|
|
assert(x);
|
|
std::string my_name;
|
|
if (cmd >> my_name) {
|
|
x->set_label(my_name);
|
|
}else{
|
|
x->set_label(x->id_letter() + std::string("_unnamed")); //BUG// not unique
|
|
cmd.warn(bDANGER, "label required");
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void parse_ports(CS& cmd, COMPONENT* x, bool all_new)
|
|
{
|
|
assert(x);
|
|
|
|
if (cmd >> '(') {
|
|
if (cmd.is_alnum()) {
|
|
// by order
|
|
int index = 0;
|
|
while (cmd.is_alnum()) {
|
|
size_t here = cmd.cursor();
|
|
try{
|
|
std::string value;
|
|
cmd >> value;
|
|
x->set_port_by_index(index, value);
|
|
if (all_new) {
|
|
if (x->node_is_grounded(index)) {
|
|
cmd.warn(bDANGER, here, "node 0 not allowed here");
|
|
}else if (x->subckt() && x->subckt()->nodes()->how_many() != index+1) {
|
|
cmd.warn(bDANGER, here, "duplicate port name, skipping");
|
|
}else{
|
|
++index;
|
|
}
|
|
}else{
|
|
++index;
|
|
}
|
|
}catch (Exception_Too_Many& e) { untested();
|
|
cmd.warn(bDANGER, here, e.message());
|
|
}
|
|
}
|
|
if (index < x->min_nodes()) { untested();
|
|
cmd.warn(bDANGER, "need " + to_string(x->min_nodes()-index) +" more nodes, grounding");
|
|
for (int iii = index; iii < x->min_nodes(); ++iii) { untested();
|
|
x->set_port_to_ground(iii);
|
|
}
|
|
}else{
|
|
}
|
|
}else{ untested();
|
|
// by name
|
|
while (cmd >> '.') { untested();
|
|
size_t here = cmd.cursor();
|
|
try{ untested();
|
|
std::string name, value;
|
|
cmd >> name >> '(' >> value >> ')' >> ',';
|
|
x->set_port_by_name(name, value);
|
|
}catch (Exception_No_Match&) {untested();
|
|
cmd.warn(bDANGER, here, "mismatch, ignored");
|
|
}
|
|
}
|
|
for (int iii = 0; iii < x->min_nodes(); ++iii) {
|
|
if (!(x->node_is_connected(iii))) {untested();
|
|
cmd.warn(bDANGER, x->port_name(iii) + ": port unconnected, grounding");
|
|
x->set_port_to_ground(iii);
|
|
}else{
|
|
}
|
|
}
|
|
}
|
|
cmd >> ')';
|
|
}else{
|
|
cmd.warn(bDANGER, "'(' required (parse ports) (grounding)");
|
|
for (int iii = 0; iii < x->min_nodes(); ++iii) {
|
|
if (!(x->node_is_connected(iii))) {
|
|
cmd.warn(bDANGER, x->port_name(iii) + ": port unconnected, grounding");
|
|
x->set_port_to_ground(iii);
|
|
}else{
|
|
unreachable();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
DEV_COMMENT* LANG_VERILOG::parse_comment(CS& cmd, DEV_COMMENT* x)
|
|
{
|
|
assert(x);
|
|
x->set(cmd.fullstring());
|
|
return x;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
DEV_DOT* LANG_VERILOG::parse_command(CS& cmd, DEV_DOT* x)
|
|
{
|
|
assert(x);
|
|
// x has an owner. it gets lost here.
|
|
x->set(cmd.fullstring());
|
|
|
|
// owner is a BASE_SUBCKT....
|
|
// how can i set _subckt in a BASE_SUBCKT?
|
|
CARD_LIST* scope = (x->owner()) ? x->owner()->subckt() : &CARD_LIST::card_list;
|
|
|
|
// if cmd is a `preprocessor directive, then x->owner will be useful...
|
|
|
|
cmd.reset();
|
|
if(cmd.peek()=='`'){
|
|
trace1("PUSH SCOPE", cmd.fullstring());
|
|
owner_hack.push(x->owner());
|
|
CMD::cmdproc(cmd, scope);
|
|
trace1("POP SCOPE", cmd.fullstring());
|
|
owner_hack.pop();
|
|
}else{
|
|
CMD::cmdproc(cmd, scope);
|
|
}
|
|
|
|
delete x;
|
|
return NULL;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/* "paramset" <my_name> <base_name> ";"
|
|
* <paramset_item_declaration>*
|
|
* <paramset_statement>*
|
|
* "endparamset"
|
|
*/
|
|
//BUG// no paramset_item_declaration, falls back to spice mode
|
|
|
|
MODEL_CARD* LANG_VERILOG::parse_paramset(CS& cmd, MODEL_CARD* x)
|
|
{ untested();
|
|
assert(x);
|
|
cmd.reset();
|
|
cmd >> "paramset ";
|
|
parse_label(cmd, x);
|
|
parse_type(cmd, x);
|
|
cmd >> ';';
|
|
|
|
for (;;) { untested();
|
|
parse_args_paramset(cmd, x);
|
|
if (cmd >> "endparamset ") { untested();
|
|
break;
|
|
}else if (!cmd.more()) { untested();
|
|
cmd.get_line("verilog-paramset>");
|
|
}else{untested();
|
|
cmd.check(bWARNING, "what's this?");
|
|
break;
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/* "module" <name> "(" <ports> ")" ";"
|
|
* <declarations>
|
|
* <netlist>
|
|
* "endmodule"
|
|
*/
|
|
//BUG// strictly one device per line
|
|
|
|
BASE_SUBCKT* LANG_VERILOG::parse_module(CS& cmd, BASE_SUBCKT* x)
|
|
{
|
|
assert(x);
|
|
|
|
// header
|
|
cmd.reset();
|
|
(cmd >> "module |macromodule ");
|
|
parse_label(cmd, x);
|
|
parse_ports(cmd, x, true/*all new*/);
|
|
cmd >> ';';
|
|
|
|
// body
|
|
for (;;) {
|
|
cmd.get_line("verilog-module>");
|
|
|
|
if (cmd >> "endmodule ") {
|
|
break;
|
|
}else{
|
|
new__instance(cmd, x, x->subckt());
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
COMPONENT* LANG_VERILOG::parse_instance(CS& cmd, COMPONENT* x)
|
|
{
|
|
assert(x);
|
|
cmd.reset();
|
|
parse_type(cmd, x);
|
|
parse_args_instance(cmd, x);
|
|
parse_label(cmd, x);
|
|
parse_ports(cmd, x, false/*allow dups*/);
|
|
cmd >> ';';
|
|
cmd.check(bWARNING, "what's this?");
|
|
return x;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
std::string LANG_VERILOG::find_type_in_string(CS& cmd)
|
|
{
|
|
size_t here = cmd.cursor();
|
|
std::string type;
|
|
if ((cmd >> "//")) {
|
|
assert(here == 0);
|
|
type = "dev_comment";
|
|
}else{
|
|
cmd >> type;
|
|
}
|
|
cmd.reset(here);
|
|
return type;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::parse_top_item(CS& cmd, CARD_LIST* Scope)
|
|
{
|
|
cmd.get_line("gnucap-verilog>");
|
|
new__instance(cmd, NULL, Scope);
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_args(OMSTREAM& o, const MODEL_CARD* x)
|
|
{ untested();
|
|
assert(x);
|
|
if (x->use_obsolete_callback_print()) {untested();
|
|
x->print_args_obsolete_callback(o, this); //BUG//callback//
|
|
}else{ untested();
|
|
for (int ii = x->param_count() - 1; ii >= 0; --ii) { untested();
|
|
if (x->param_is_printable(ii)) { untested();
|
|
std::string arg = " ." + x->param_name(ii) + "=" + x->param_value(ii) + ";";
|
|
o << arg;
|
|
}else{ untested();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_args(OMSTREAM& o, const COMPONENT* x)
|
|
{
|
|
assert(x);
|
|
o << " #(";
|
|
if (x->use_obsolete_callback_print()) { untested();
|
|
arg_count = 0;
|
|
x->print_args_obsolete_callback(o, this); //BUG//callback//
|
|
arg_count = INACTIVE;
|
|
}else{
|
|
std::string sep = ".";
|
|
for (int ii = x->param_count() - 1; ii >= 0; --ii) {
|
|
if (x->param_is_printable(ii)) {
|
|
o << sep << x->param_name(ii) << "(" << x->param_value(ii) << ")";
|
|
sep = ",.";
|
|
}else{
|
|
}
|
|
}
|
|
}
|
|
o << ") ";
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void print_type(OMSTREAM& o, const COMPONENT* x)
|
|
{
|
|
assert(x);
|
|
o << x->dev_type();
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void print_label(OMSTREAM& o, const COMPONENT* x)
|
|
{
|
|
assert(x);
|
|
o << x->short_label();
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void print_ports_long(OMSTREAM& o, const COMPONENT* x)
|
|
{
|
|
// print in long form ... .name(value)
|
|
assert(x);
|
|
|
|
o << " (";
|
|
std::string sep = ".";
|
|
for (int ii = 0; x->port_exists(ii); ++ii) {
|
|
o << sep << x->port_name(ii) << '(' << x->port_value(ii) << ')';
|
|
sep = ",.";
|
|
}
|
|
for (int ii = 0; x->current_port_exists(ii); ++ii) {untested();
|
|
o << sep << x->current_port_name(ii) << '(' << x->current_port_value(ii) << ')';
|
|
sep = ",.";
|
|
}
|
|
o << ")";
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static void print_ports_short(OMSTREAM& o, const COMPONENT* x)
|
|
{
|
|
// print in short form ... value only
|
|
assert(x);
|
|
|
|
o << " (";
|
|
std::string sep = "";
|
|
for (int ii = 0; x->port_exists(ii); ++ii) {
|
|
o << sep << x->port_value(ii);
|
|
sep = ",";
|
|
}
|
|
for (int ii = 0; x->current_port_exists(ii); ++ii) {untested();
|
|
o << sep << x->current_port_value(ii);
|
|
sep = ",";
|
|
}
|
|
o << ")";
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_paramset(OMSTREAM& o, const MODEL_CARD* x)
|
|
{ untested();
|
|
assert(x);
|
|
_mode = mPARAMSET;
|
|
o << "paramset " << x->short_label() << ' ' << x->dev_type() << ";\\\n";
|
|
print_args(o, x);
|
|
o << "\\\n"
|
|
"endparmset\n\n";
|
|
_mode = mDEFAULT;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_module(OMSTREAM& o, const BASE_SUBCKT* x)
|
|
{
|
|
assert(x);
|
|
assert(x->subckt());
|
|
|
|
o << "module " << x->short_label();
|
|
print_ports_short(o, x);
|
|
o << ";\n";
|
|
|
|
for (CARD_LIST::const_iterator
|
|
ci = x->subckt()->begin(); ci != x->subckt()->end(); ++ci) {
|
|
print_item(o, *ci);
|
|
}
|
|
|
|
o << "endmodule // " << x->short_label() << "\n\n";
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_instance(OMSTREAM& o, const COMPONENT* x)
|
|
{
|
|
print_type(o, x);
|
|
print_args(o, x);
|
|
print_label(o, x);
|
|
print_ports_long(o, x);
|
|
o << ";\n";
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_comment(OMSTREAM& o, const DEV_COMMENT* x)
|
|
{
|
|
assert(x);
|
|
if ((x->comment().compare(0, 2, "//")) != 0) {
|
|
o << "//";
|
|
}else{
|
|
}
|
|
o << x->comment() << '\n';
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void LANG_VERILOG::print_command(OMSTREAM& o, const DEV_DOT* x)
|
|
{untested();
|
|
assert(x);
|
|
o << x->s() << '\n';
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
class CMD_PARAMSET : public CMD {
|
|
void do_it(CS& cmd, CARD_LIST* Scope)
|
|
{ untested();
|
|
// already got "paramset"
|
|
std::string my_name, base_name;
|
|
cmd >> my_name;
|
|
size_t here = cmd.cursor();
|
|
cmd >> base_name;
|
|
|
|
//const MODEL_CARD* p = model_dispatcher[base_name];
|
|
const CARD* p = lang_verilog.find_proto(base_name, NULL);
|
|
if (p) { untested();
|
|
MODEL_CARD* new_card = dynamic_cast<MODEL_CARD*>(p->clone());
|
|
if (new_card) { untested();
|
|
assert(!new_card->owner());
|
|
lang_verilog.parse_paramset(cmd, new_card);
|
|
Scope->push_back(new_card);
|
|
}else{untested();
|
|
cmd.warn(bDANGER, here, "paramset: base has incorrect type");
|
|
}
|
|
}else{untested();
|
|
cmd.warn(bDANGER, here, "paramset: no match");
|
|
}
|
|
}
|
|
} p1;
|
|
DISPATCHER<CMD>::INSTALL d1(&command_dispatcher, "paramset", &p1);
|
|
/*--------------------------------------------------------------------------*/
|
|
class CMD_MODULE : public CMD {
|
|
void do_it(CS& cmd, CARD_LIST* Scope)
|
|
{
|
|
BASE_SUBCKT* new_module = dynamic_cast<BASE_SUBCKT*>(device_dispatcher.clone("subckt"));
|
|
assert(new_module);
|
|
assert(!new_module->owner());
|
|
assert(new_module->subckt());
|
|
assert(new_module->subckt()->is_empty());
|
|
assert(!new_module->is_device());
|
|
lang_verilog.parse_module(cmd, new_module);
|
|
Scope->push_back(new_module);
|
|
}
|
|
} p2;
|
|
DISPATCHER<CMD>::INSTALL d2(&command_dispatcher, "module|macromodule", &p2);
|
|
/*--------------------------------------------------------------------------*/
|
|
class CMD_VERILOG : public CMD {
|
|
public:
|
|
void do_it(CS&, CARD_LIST* Scope)
|
|
{
|
|
command("options lang=verilog", Scope);
|
|
}
|
|
} p8;
|
|
DISPATCHER<CMD>::INSTALL d8(&command_dispatcher, "verilog", &p8);
|
|
/*--------------------------------------------------------------------------*/
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
/*--------------------------------------------------------------------------*/
|
|
// vim:ts=8:sw=2:noet:
|