From 4ffd91822f349d45664d4c1a10f001eedf9988b5 Mon Sep 17 00:00:00 2001 From: Jean-Paul Chaput Date: Sun, 3 Apr 2022 13:18:42 +0200 Subject: [PATCH] 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(). --- crlcore/src/ccore/crlcore/Spice.h | 4 +- crlcore/src/ccore/crlcore/SpiceBit.h | 19 +- crlcore/src/ccore/crlcore/SpiceEntity.h | 39 +-- crlcore/src/ccore/crlcore/ToolBox.h | 1 + crlcore/src/ccore/spice/SpiceBit.cpp | 15 +- crlcore/src/ccore/spice/SpiceEntity.cpp | 62 +++-- crlcore/src/ccore/spice/SpiceParser.cpp | 279 ++++++++++++++++++++- crlcore/src/ccore/toolbox/NamingScheme.cpp | 14 ++ crlcore/src/pyCRL/PySpice.cpp | 12 +- 9 files changed, 387 insertions(+), 58 deletions(-) diff --git a/crlcore/src/ccore/crlcore/Spice.h b/crlcore/src/ccore/crlcore/Spice.h index 13a4c4d5..e1585c24 100644 --- a/crlcore/src/ccore/crlcore/Spice.h +++ b/crlcore/src/ccore/crlcore/Spice.h @@ -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 (); }; diff --git a/crlcore/src/ccore/crlcore/SpiceBit.h b/crlcore/src/ccore/crlcore/SpiceBit.h index 16f9bb06..38cb4be3 100644 --- a/crlcore/src/ccore/crlcore/SpiceBit.h +++ b/crlcore/src/ccore/crlcore/SpiceBit.h @@ -61,9 +61,6 @@ namespace Spice { inline std::string Bit::getName () const { return _name; } - typedef std::vector 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( _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(); } diff --git a/crlcore/src/ccore/crlcore/SpiceEntity.h b/crlcore/src/ccore/crlcore/SpiceEntity.h index bf74b830..754d92e7 100644 --- a/crlcore/src/ccore/crlcore/SpiceEntity.h +++ b/crlcore/src/ccore/crlcore/SpiceEntity.h @@ -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& - getAllEntities (); - static void orderPlugs ( Instance*, std::vector& ); + getAllEntities (); + static void orderPlugs ( Instance*, std::vector& ); 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& + getBits () const; + inline void setFlags ( uint64_t ); + void setOrder ( const std::vector& ); + void toNodeList ( ostream&, bool asInterf=true ) const; + void toEntity ( ostream& ) const; + std::string _getString () const; + Record* _getRecord () const; private: static std::vector _entities; static std::ptrdiff_t _offset; - BitVector _bits; + std::vector _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& Entity::getBits () const { return _bits; } + inline void Entity::setFlags ( uint64_t flags ) { _flags |= flags; } // ------------------------------------------------------------------- diff --git a/crlcore/src/ccore/crlcore/ToolBox.h b/crlcore/src/ccore/crlcore/ToolBox.h index 70bcbaf1..1865720d 100644 --- a/crlcore/src/ccore/crlcore/ToolBox.h +++ b/crlcore/src/ccore/crlcore/ToolBox.h @@ -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 ); diff --git a/crlcore/src/ccore/spice/SpiceBit.cpp b/crlcore/src/ccore/spice/SpiceBit.cpp index 1529251a..3fdfcae5 100644 --- a/crlcore/src/ccore/spice/SpiceBit.cpp +++ b/crlcore/src/ccore/spice/SpiceBit.cpp @@ -102,7 +102,10 @@ namespace Spice { void BitProperty::onReleasedBy ( DBo* owner ) - { PrivateProperty::onReleasedBy( owner ); } + { + BitExtension::clearCache( static_cast(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 )->remove( property ); + } + if (net == _owner) _owner = NULL; + } + + Bit* BitExtension::get ( const Net* net ) { if (net == _owner) return _cache; diff --git a/crlcore/src/ccore/spice/SpiceEntity.cpp b/crlcore/src/ccore/spice/SpiceEntity.cpp index baf4af9f..ab082794 100644 --- a/crlcore/src/ccore/spice/SpiceEntity.cpp +++ b/crlcore/src/ccore/spice/SpiceEntity.cpp @@ -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& 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 ( "" ); 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& 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(entities[current]->getCell()) ); + } + } - while ( not entities.empty() ) destroy( const_cast(entities.back()->getCell()) ); + //while ( not entities.empty() ) destroy( const_cast(entities.back()->getCell()) ); } diff --git a/crlcore/src/ccore/spice/SpiceParser.cpp b/crlcore/src/ccore/spice/SpiceParser.cpp index ccc0bd2f..ec5efc62 100644 --- a/crlcore/src/ccore/spice/SpiceParser.cpp +++ b/crlcore/src/ccore/spice/SpiceParser.cpp @@ -14,6 +14,7 @@ // +-----------------------------------------------------------------+ +#include #include #include #include @@ -23,7 +24,7 @@ #include #include #include -#include +#include 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& tokens (); + bool readEntry (); + private: + bool _readline (); + private: + size_t _lineno; + uint32_t _flags; + ifstream _stream; + list _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& 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 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& 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 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 > 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; } diff --git a/crlcore/src/ccore/toolbox/NamingScheme.cpp b/crlcore/src/ccore/toolbox/NamingScheme.cpp index 1f8af188..7b8e9998 100644 --- a/crlcore/src/ccore/toolbox/NamingScheme.cpp +++ b/crlcore/src/ccore/toolbox/NamingScheme.cpp @@ -29,6 +29,20 @@ namespace CRL { using Hurricane::Instance; + Name NamingScheme::vhdlToVlog ( const Name& vhdlName ) + { + string vlogName; + + for ( size_t i=0 ; i