Added support for SPICE terminal ordering from .spi files.

After a Cell has been created in memory (by parsers or Python scripts)
we can annotate it with the Spice parser so it will know the right
order with which to create the subcircuit call ('x').

* New: In CRL::Spice::load(), add support to read the ".subckt" card
    and guess the right ordering for generating the 'x' (subcircuit
    card call).
* Bug: In Spice::SpiceBit & Spice::BitExtension, when a Net bit property
    is removed, if it's the currently cached property in BitExtension
    it may lead to a crash. So when a property is destroyed, we must
    also clear the cache (see remove(), clearCache() & onReleasedby()).
      I'm wary that this could also happen on other kind of cached
    extensions...
* New: In CRL::NamingScheme, new method vhdlToVlog() to translate back
    VHDL net name into Verilog. Currently only changes "()" into "[]".
    Used to generate the commented SPICE interface for Alliance compliance.
* Change: In Spice::Entity, previously all the ordering where removed
    between each run of the SPICE parser, but the orders read from
    SPICE file (mostly standard cells) must be kept. So add a flag
    ReferenceCell to prevent the removal by ::destroyAll().
This commit is contained in:
Jean-Paul Chaput 2022-04-03 13:18:42 +02:00
parent fd624f03b6
commit 4ffd91822f
9 changed files with 387 additions and 58 deletions

View File

@ -31,9 +31,11 @@ namespace CRL {
class Spice {
public:
static const uint64_t PIN_ORDERING = (1<<0);
public:
static bool save ( Cell*, uint64_t flags );
static bool load ( Library*, std::string spicePath );
static bool load ( Library*, std::string spicePath, uint64_t mode );
static void clearProperties ();
};

View File

@ -61,9 +61,6 @@ namespace Spice {
inline std::string Bit::getName () const { return _name; }
typedef std::vector<Bit*> BitVector;
struct GreaterBitByIndex {
inline bool operator() ( const Bit* lhs, const Bit* rhs ) const
{ return lhs->getIndex() > rhs->getIndex(); }
@ -82,6 +79,7 @@ namespace Spice {
static Name getPropertyName ();
virtual Name getName () const;
inline Bit* getBit ();
inline Net* getNet ();
inline std::string getBitName () const;
virtual void onReleasedBy ( DBo* owner );
virtual std::string _getTypeName () const;
@ -100,6 +98,7 @@ namespace Spice {
: PrivateProperty(), _bit(this,owner,index)
{ }
inline Net* BitProperty::getNet () { return const_cast<Net*>( _bit.getNet() ); }
inline Bit* BitProperty::getBit () { return &_bit; }
inline std::string BitProperty::getBitName () const { return _bit.getName(); }
@ -109,10 +108,12 @@ namespace Spice {
class BitExtension {
public:
static inline size_t getIndex ( const Net* );
static inline std::string getName ( const Net* );
static Bit* get ( const Net* );
static Bit* create ( Net*, size_t index=Bit::nindex );
static inline size_t getIndex ( const Net* );
static inline std::string getName ( const Net* );
static Bit* get ( const Net* );
static void remove ( const Net* );
static Bit* create ( Net*, size_t index=Bit::nindex );
static inline void clearCache ( Net* );
private:
static const Net* _owner;
static Bit* _cache;
@ -125,13 +126,15 @@ namespace Spice {
return (bit == NULL) ? false : bit->getIndex();
}
inline std::string BitExtension::getName ( const Net* net )
{
Bit* bit = get( net );
return (bit == NULL) ? "?" : bit->getName();
}
inline void BitExtension::clearCache ( Net* net )
{ if (_owner == net) { _owner = NULL; _cache = NULL; } }
inline BitProperty* Bit::getProperty () const { return (BitProperty*)((ptrdiff_t)(this) - _offset); }
inline const Net* Bit::getNet () const { return (const Net*)getProperty()->getOwner(); }

View File

@ -50,33 +50,42 @@ namespace Spice {
class Entity {
public:
static const uint64_t TopCell = (1 << 0);
static const uint64_t TopCell = (1 << 0);
static const uint64_t ReferenceCell = (1 << 1);
public:
static std::vector<Entity*>&
getAllEntities ();
static void orderPlugs ( Instance*, std::vector<Plug*>& );
getAllEntities ();
static void orderPlugs ( Instance*, std::vector<Plug*>& );
public:
Entity ( EntityProperty*, Cell*, uint64_t flags );
~Entity ();
inline uint64_t getFlags () const;
const Cell* getCell () const;
inline const BitVector& getBits () const;
void toNodeList ( ostream&, bool asInterf=true ) const;
void toEntity ( ostream& ) const;
std::string _getString () const;
Record* _getRecord () const;
Entity ( EntityProperty*, Cell*, uint64_t flags );
~Entity ();
inline bool isTopCell () const;
inline bool isReferenceCell () const;
inline uint64_t getFlags () const;
const Cell* getCell () const;
inline const std::vector<Bit*>&
getBits () const;
inline void setFlags ( uint64_t );
void setOrder ( const std::vector<Net*>& );
void toNodeList ( ostream&, bool asInterf=true ) const;
void toEntity ( ostream& ) const;
std::string _getString () const;
Record* _getRecord () const;
private:
static std::vector<Entity*> _entities;
static std::ptrdiff_t _offset;
BitVector _bits;
std::vector<Bit*> _bits;
size_t _powerNode;
size_t _groundNode;
uint64_t _flags;
};
inline uint64_t Entity::getFlags () const { return _flags; }
inline const BitVector& Entity::getBits () const { return _bits; }
inline bool Entity::isTopCell () const { return _flags & TopCell; }
inline bool Entity::isReferenceCell () const { return _flags & ReferenceCell; }
inline uint64_t Entity::getFlags () const { return _flags; }
inline const std::vector<Bit*>& Entity::getBits () const { return _bits; }
inline void Entity::setFlags ( uint64_t flags ) { _flags |= flags; }
// -------------------------------------------------------------------

View File

@ -74,6 +74,7 @@ namespace CRL {
public:
typedef std::function< Name(const Name&,uint32_t) > converter_t;
public:
static Name vhdlToVlog ( const Name& vhdlName );
static Name vlogToVhdl ( const Name& vlogName, uint32_t flags );
static void toVhdl ( Cell* topCell, uint32_t flags );
NamingScheme ( uint32_t flags );

View File

@ -102,7 +102,10 @@ namespace Spice {
void BitProperty::onReleasedBy ( DBo* owner )
{ PrivateProperty::onReleasedBy( owner ); }
{
BitExtension::clearCache( static_cast<Net*>(owner) );
PrivateProperty::onReleasedBy( owner );
}
Name BitProperty::getPropertyName ()
@ -145,6 +148,16 @@ namespace Spice {
Bit* BitExtension::_cache = NULL;
void BitExtension::remove ( const Net* net )
{
Property* property = net->getProperty( BitProperty::getPropertyName() );
if (property) {
const_cast<Net*>( net )->remove( property );
}
if (net == _owner) _owner = NULL;
}
Bit* BitExtension::get ( const Net* net )
{
if (net == _owner) return _cache;

View File

@ -21,18 +21,6 @@
#include "crlcore/SpiceEntity.h"
namespace {
using Hurricane::Net;
using Hurricane::Instance;
using Hurricane::Occurrence;
using Spice::Bit;
using Spice::Entity;
}
namespace Spice {
using namespace std;
@ -41,6 +29,8 @@ namespace Spice {
using Hurricane::Property;
using Hurricane::_TName;
using Hurricane::Plug;
using Hurricane::Occurrence;
using CRL::NamingScheme;
class ComparePlugBySpiceIndex {
@ -64,6 +54,7 @@ namespace Spice {
, _groundNode(Bit::nindex)
, _flags(flags)
{
//cerr << "Entity::Entity() on " << cell << endl;
if (not _offset) {
//_offset = offsetof(EntityProperty,_entity);
_offset = (ptrdiff_t)this - (ptrdiff_t)property;
@ -84,15 +75,31 @@ namespace Spice {
}
void Entity::setOrder ( const vector<Net*>& orderedNets )
{
for ( auto bit : _bits )
BitExtension::remove( bit->getNet() );
_bits.clear();
for ( auto net : orderedNets ) {
Bit* bit = BitExtension::create( net, _bits.size() );
_bits.push_back( bit );
if (net->isPower ()) _powerNode = bit->getIndex();
if (net->isGround()) _groundNode = bit->getIndex();
}
}
Entity::~Entity ()
{
for ( auto bit : _bits ) bit->destroy();
for ( auto ientity=_entities.begin() ; ientity!=_entities.end() ; ++ientity ) {
if (*ientity == this) {
_entities.erase( ientity );
break;
}
}
for ( auto bit : _bits )
BitExtension::remove( bit->getNet() );
}
@ -122,9 +129,13 @@ namespace Spice {
for ( Bit* bit : _bits ) {
const Net* net = bit->getNet();
if (not net->isExternal() and asInterf) continue;
out << "* " << ((asInterf) ? "INTERF" : "NET")
<< setw(6) << BitExtension::getName(net)
<< " " << net->getName() << ".\n";
if (asInterf) {
out << "* " << "INTERF" //<< setw(6) << BitExtension::getName(net)
<< " " << NamingScheme::vhdlToVlog(net->getName()) << "\n";
} else {
out << "* " << "NET" << setw(6) << BitExtension::getName(net)
<< " = " << NamingScheme::vhdlToVlog(net->getName()) << "\n";
}
}
out << "\n";
}
@ -233,10 +244,10 @@ namespace Spice {
{
Record* record = new Record ( "<Entity " + _getString() + " >" );
if (record != NULL) {
record->add( getSlot("_bits" , _bits ) );
record->add( getSlot("_powerNode" , _powerNode ) );
record->add( getSlot("_groundNode", _groundNode ) );
record->add( getSlot("_flags" , _flags ) );
record->add( getSlot("_bits" , &_bits ) );
record->add( getSlot("_powerNode" , _powerNode ) );
record->add( getSlot("_groundNode", _groundNode ) );
record->add( getSlot("_flags" , _flags ) );
}
return record;
}
@ -340,8 +351,17 @@ namespace Spice {
void EntityExtension::destroyAll ()
{
vector<Entity*>& entities = Entity::getAllEntities();
size_t current = 0;
while ( current < entities.size() ) {
if (entities[current]->isReferenceCell()) {
++current;
} else {
//cerr << "Destroy SPICE entity on " << entities[current]->getCell() << endl;
destroy( const_cast<Cell*>(entities[current]->getCell()) );
}
}
while ( not entities.empty() ) destroy( const_cast<Cell*>(entities.back()->getCell()) );
//while ( not entities.empty() ) destroy( const_cast<Cell*>(entities.back()->getCell()) );
}

View File

@ -14,6 +14,7 @@
// +-----------------------------------------------------------------+
#include <cctype>
#include <ctime>
#include <cstdio>
#include <string>
@ -23,7 +24,7 @@
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <list>
using namespace std;
#include "hurricane/configuration/Configuration.h"
@ -51,29 +52,293 @@ using namespace Hurricane;
#include "crlcore/NetExtension.h"
#include "crlcore/ToolBox.h"
#include "crlcore/Spice.h"
#include "crlcore/SpiceBit.h"
#include "crlcore/SpiceEntity.h"
using namespace CRL;
namespace {
using namespace std;
using CRL::NamingScheme;
string& uppercase ( string& s )
{
for ( size_t i=0 ; i<s.size() ; ++i )
s[i] = toupper( s[i] );
return s;
}
// -------------------------------------------------------------------
// Class : "::Tokenize".
class Tokenize {
public:
static const uint32_t COMMENT = (1<< 0);
static const uint32_t END = (1<< 1);
static const uint32_t RESISTOR = (1<< 2);
static const uint32_t CAPACITOR = (1<< 3);
static const uint32_t INDUCTOR = (1<< 4);
static const uint32_t COUPLED_INDUCTOR = (1<< 5);
static const uint32_t DIODE = (1<< 6);
static const uint32_t BJT = (1<< 7);
static const uint32_t JFET = (1<< 8);
static const uint32_t MOSFET = (1<< 9);
static const uint32_t MODEL = (1<< 0);
static const uint32_t SUBCKT = (1<<10);
static const uint32_t ENDS = (1<<11);
static const uint32_t SUBCIRCUIT = (1<<12);
public:
Tokenize ( string blifFile );
inline size_t lineno () const;
inline uint32_t flags () const;
list<string>& tokens ();
bool readEntry ();
private:
bool _readline ();
private:
size_t _lineno;
uint32_t _flags;
ifstream _stream;
list<string> _tokens;
};
Tokenize::Tokenize ( string spiceFile )
: _lineno (0)
, _flags (0)
, _stream ()
, _tokens ()
{
_stream.open( spiceFile );
if (_stream.fail())
throw Error( "Unable to open SPICE file %s\n", spiceFile.c_str() );
}
inline size_t Tokenize::lineno () const { return (_lineno) ? _lineno-1 : 0; }
inline uint32_t Tokenize::flags () const { return _flags; }
inline list<string>& Tokenize::tokens () { return _tokens; }
bool Tokenize::readEntry ()
{
_flags = 0;
if (_stream.eof()) return false;
if (not _readline()) return false;
string first = _tokens.front();
uppercase( first );
if (first[0] == '*' ) { _flags |= COMMENT; }
else if (first[0] == '.' ) {
if (first == ".MODEL" ) { _flags |= MODEL; }
else if (first == ".END" ) { _flags |= END; }
else if (first == ".SUBCKT" ) { _flags |= SUBCKT; }
else if (first == ".ENDS" ) { _flags |= ENDS; }
}
else if (first[0] == 'R') { _flags |= RESISTOR; }
else if (first[0] == 'C') { _flags |= CAPACITOR; }
else if (first[0] == 'L') { _flags |= INDUCTOR; }
else if (first[0] == 'K') { _flags |= COUPLED_INDUCTOR; }
else if (first[0] == 'D') { _flags |= DIODE; }
else if (first[0] == 'Q') { _flags |= BJT; }
else if (first[0] == 'J') { _flags |= JFET; }
else if (first[0] == 'M') { _flags |= MOSFET; }
else if (first[0] == 'X') { _flags |= SUBCIRCUIT; }
return true;
}
bool Tokenize::_readline ()
{
_tokens.clear();
bool nextLine = true;
while ( nextLine ) {
if (_stream.eof()) return false;
nextLine = false;
++_lineno;
string line;
getline( _stream, line );
size_t tokstart = 0;
for ( size_t i=0 ; i<line.size() ; ++i ) {
if ((i == 0) and (line[i] == '*')) {
_tokens.push_back( line );
return true;
}
size_t nextTokstart = i+1;
switch ( line[i] ) {
case ' ':
case '\t': break;
default: continue;
}
if (i > tokstart)
_tokens.push_back( line.substr(tokstart,i-tokstart) );
tokstart = nextTokstart;
}
if (tokstart < line.size()) {
if (line.substr(tokstart) == ";")
nextLine = true;
else
_tokens.push_back( line.substr(tokstart) );
}
if (_tokens.empty())
nextLine = true;
}
return not _tokens.empty();
}
} // Anonymous namespace.
namespace CRL {
// -------------------------------------------------------------------
// Class : "CRL::Spice".
bool Spice::load ( Library* library, string spicePath )
bool Spice::load ( Library* library, string spiceFile, uint64_t mode )
{
if (mode != PIN_ORDERING) {
cerr << Error( "Spice::load(): SPICE parser only support PIN_ORDERING mode.\n"
" \"%s\"."
, spiceFile.c_str()
) << endl;
return false;
}
//DebugSession::open( 101, 110 );
UpdateSession::open();
cerr << Error( "Spice::load(): SPICE parser is not implemented yet.\n"
" \"%s\"."
, spicePath.c_str()
) << endl;
Tokenize tokenize ( spiceFile );
while ( tokenize.readEntry() ) {
if (tokenize.flags() & Tokenize::SUBCKT) {
list<string>& tokens = tokenize.tokens();
// cerr << "Found SUBCKT:";
// for ( const string& item : tokens )
// cerr << " " << item;
// cerr << endl;
if (tokens.size() < 2) {
cerr << Error( "Spice::load(): Invalid SUBCKT, no parameters at all.\n"
" File %s at line %u."
, spiceFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
string cellName = *(next( tokens.begin(), 1));
Cell* cell = library->getCell( cellName );
if (not cell) {
cerr << Error( "Spice::load(): Library \"%s\" has no Cell named \"%s\".\n"
" File %s at line %u."
, getString( library->getName() ).c_str()
, getString( cellName ).c_str()
, spiceFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
tokens.pop_front();
tokens.pop_front();
// cerr << "Net order of " << cell;
// for ( auto& name : tokens )
// cerr << " " << name;
// cerr << endl;
bool hasErrors = false;
vector<Net*> orderedNets;
for ( const string& netName : tokens ) {
Net* net = cell->getNet( netName );
if (not net) {
cerr << Error( "Spice::load(): Cell \"%s\" has no Net \"%s\".\n"
" File %s at line %u."
, getString( cell->getName() ).c_str()
, netName.c_str()
, spiceFile.c_str()
, tokenize.lineno()
) << endl;
hasErrors = true;
continue;
}
if (not net->isExternal()) {
cerr << Error( "Spice::load(): In cell \"%s\", net \"%s\" is *not* external.\n"
" File %s at line %u."
, getString( cell->getName() ).c_str()
, getString( net ->getName() ).c_str()
, spiceFile.c_str()
, tokenize.lineno()
) << endl;
hasErrors = true;
continue;
}
orderedNets.push_back( net );
}
map< string, Net*, greater<string> > internalNets;
for ( Net* net : cell->getNets() ) {
if (net->isExternal()) continue;
internalNets.insert( make_pair( getString(net->getName()), net ) );
}
for ( auto item : internalNets ) {
orderedNets.push_back( item.second );
}
// for ( Net* net : orderedNets )
// cerr << " " << net->getName();
// cerr << endl;
for ( Net* net : cell->getExternalNets() ) {
if (find( orderedNets.begin(), orderedNets.end(), net) == orderedNets.end()) {
cerr << Error( "Spice::load(): In cell \"%s\", external net \"%s\" *not* ordered.\n"
" File %s at line %u."
, getString( cell->getName() ).c_str()
, getString( net ->getName() ).c_str()
, spiceFile.c_str()
, tokenize.lineno()
) << endl;
hasErrors = true;
break;
}
}
if (hasErrors) continue;
::Spice::Entity* spiceEntity = ::Spice::EntityExtension::get( cell );
if (spiceEntity) {
if (spiceEntity->getFlags() & ::Spice::Entity::ReferenceCell) {
cerr << Warning( "Spice::load(): Redefinition of external net order of \"%s\".\n"
" (from file: \"%s\")"
, getString( cell->getName() ).c_str()
, spiceFile.c_str()
) << endl;
} else {
spiceEntity->setFlags( ::Spice::Entity::ReferenceCell );
}
} else {
spiceEntity = ::Spice::EntityExtension::create( cell, ::Spice::Entity::ReferenceCell );
}
spiceEntity->setOrder( orderedNets );
}
}
UpdateSession::close();
//DebugSession::close();
return false;
return true;
}

View File

@ -29,6 +29,20 @@ namespace CRL {
using Hurricane::Instance;
Name NamingScheme::vhdlToVlog ( const Name& vhdlName )
{
string vlogName;
for ( size_t i=0 ; i<vhdlName.size() ; ++i ) {
char translated = vhdlName[i];
if (translated == '(' ) translated = '[';
if (translated == ')' ) translated = ']';
vlogName += translated;
}
return Name(vlogName);
}
Name NamingScheme::vlogToVhdl ( const Name& vlogName, uint32_t flags )
{
string vhdlName;

View File

@ -89,13 +89,14 @@ extern "C" {
{
cdebug_log(30,0) << "PySpice_load()" << endl;
char* path = NULL;
HTRY
PyObject* pyLibrary = NULL;
if (PyArg_ParseTuple( args, "Os:Spice.load", &pyLibrary, &path )) {
unsigned long mode = 0;
char* path = NULL;
PyObject* pyLibrary = NULL;
if (PyArg_ParseTuple( args, "Osk:Spice.load", &pyLibrary, &path, &mode )) {
if (IsPyLibrary(pyLibrary)) {
Spice::load( PYLIBRARY_O(pyLibrary), string(path) );
Spice::load( PYLIBRARY_O(pyLibrary), string(path), (uint64_t)mode );
} else {
PyErr_SetString( ConstructorError, "Spice.load(): Bad parameter type (not a Library)." );
return NULL;
@ -153,7 +154,8 @@ extern "C" {
{
PyObject* constant;
LoadObjectConstant(PyTypeSpice.tp_dict,::Spice::Entity::TopCell,"TopCell");
LoadObjectConstant(PyTypeSpice.tp_dict,::Spice::Entity::TopCell ,"TopCell");
LoadObjectConstant(PyTypeSpice.tp_dict,::CRL::Spice::PIN_ORDERING,"PIN_ORDERING");
}