Support for Uniquification. Rewrite of the Blif parser.

* New: In Hurricane, In NetAlias & Net, add an AliasList collection to
    iterate over all the aliases names (this *not* include the main
    net name).
* New: In Hurricane, In Cell, Instance & Net add support for cloning and
    uniquification (update documentation).
      All Cells that are uniquified from a given model are kept in an
    Uniquify relation. This relation allows to get all the clones and
    generate unique clone names by adding a "_uXX" suffix.
* Bug: In Hurricane, the Cell::_isFlattenLeaf attribute was not initialized
    in the constructor.
* Change: In CRL Core, in AllianceFramework::getCell(), no longer destroy
    a partially loaded Cell in case of parser failure, let the incomplete
    Cell be.
* New: In CRL Core, in ApParser, if all the instances are placed, sets the
    Cell::Placed flag.
* Change: In CRL Core, in BlifParser, complete rewrite of the parser.
    Not so good an idea afterwards...
* New: In Etesian, uniquify the design before placing.
* Change: In Kite, display the list of unrouted wired after all other
    statistics.
This commit is contained in:
Jean-Paul Chaput 2015-04-25 16:52:18 +02:00
parent 4023481da3
commit 770b1fbbbc
26 changed files with 1252 additions and 506 deletions

View File

@ -304,7 +304,7 @@ namespace CRL {
(parser->getParsCell())( _environment.getLIBRARIES().getSelected() , state->getCell() );
} catch ( ... ) {
if ( createCell )
state->getCell()->destroy();
//state->getCell()->destroy();
throw;
}
}

View File

@ -1,39 +1,17 @@
// -*- C++ -*-
//
// This file is part of the Coriolis Project.
// Copyright (C) Laboratoire LIP6 - Departement ASIM
// Universite Pierre et Marie Curie
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2008-2015, All Rights Reserved
//
// Main contributors :
// Christophe Alexandre <Christophe.Alexandre@lip6.fr>
// Sophie Belloeil <Sophie.Belloeil@lip6.fr>
// Hugo Clément <Hugo.Clement@lip6.fr>
// Jean-Paul Chaput <Jean-Paul.Chaput@lip6.fr>
// Damien Dupuis <Damien.Dupuis@lip6.fr>
// Christian Masson <Christian.Masson@lip6.fr>
// Marek Sroka <Marek.Sroka@lip6.fr>
//
// The Coriolis Project 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 2 of
// the License, or (at your option) any later version.
//
// The Coriolis Project 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 the Coriolis Project; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
// License-Tag
// Authors-Tag
// ===================================================================
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | Alliance / Hurricane Interface |
// | |
// | Author : Jean-Paul CHAPUT |
// | E-mail : Jean-Paul.Chaput@asim.lip6.fr |
// | =============================================================== |
// | C++ Header : "./alliance/ap/Ap.h" |
// +-----------------------------------------------------------------+
#include <string>
@ -44,8 +22,8 @@ namespace Hurricane {
}
#ifndef __AP_H__
#define __AP_H__
#ifndef CRL_AP_H
#define CRL_AP_H
namespace CRL {
@ -60,4 +38,4 @@ namespace CRL {
}
# endif
#endif // CRL_AP_H

View File

@ -838,6 +838,15 @@ namespace {
if (materializationState) Go::disableAutoMaterialization ();
bool isPlaced = true;
forEach ( Instance*, iinstance, _cell->getInstances() ) {
if (iinstance->getPlacementStatus() == Instance::PlacementStatus::UNPLACED) {
isPlaced = false;
break;
}
}
if (isPlaced) _cell->setFlags( Cell::Placed );
fileStream.close ();
}

View File

@ -1,14 +1,16 @@
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2008-2014, All Rights Reserved
// Copyright (c) UPMC 2015-2015, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | Alliance / Hurricane Interface |
// | Yacc Grammar for Alliance Structural VHDL |
// | Yosys & Blif / Hurricane Interface |
// | |
// | Author : Jean-Paul CHAPUT |
// | E-mail : Jean-Paul.Chaput@asim.lip6.fr |
// | |
// | =============================================================== |
// | C++ Module : "./blif/BlifParser.cpp" |
// +-----------------------------------------------------------------+
@ -16,6 +18,7 @@
#include <string.h>
#include <string>
#include <sstream>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@ -39,111 +42,464 @@ using namespace CRL;
namespace {
using namespace std;
void addSupplyNets ( Cell* cell )
{
Net* vss = Net::create ( cell, "vss" );
vss->setExternal ( true );
vss->setGlobal ( true );
vss->setType ( Net::Type::GROUND );
Net* vdd = Net::create ( cell, "vdd" );
vdd->setExternal ( true );
vdd->setGlobal ( true );
vdd->setType ( Net::Type::POWER );
inline bool isAbcAutomaticName ( string name )
{ return (name.substr(0,5) == "$abc$"); }
// -------------------------------------------------------------------
// Class : "::Tokenize".
class Tokenize {
public:
enum State { Init = 0x00000001
, Unknown = 0x00000002
, Model = 0x00000004
, End = 0x00000008
, Subckt = 0x00000010
, Latch = 0x00000020
, Inputs = 0x00000040
, Outputs = 0x00000080
, Clock = 0x00000100
, Names = 0x00000200
, CoverZero = 0x00001000
, CoverOne = 0x00002000
, CoverLogic = 0x00004000
, CoverAlias = 0x00008000
};
typedef vector< pair<string,string> > CoverTable;
public:
Tokenize ( string blifFile );
inline size_t lineno () const;
inline unsigned int state () const;
const vector<string>& blifLine () const;
const CoverTable& coverTable () const;
bool readEntry ();
private:
bool _readline ();
private:
size_t _lineno;
unsigned int _state;
ifstream _stream;
vector<string> _tokens;
vector<string> _blifLine;
CoverTable _coverTable;
};
Tokenize::Tokenize ( string blifFile )
: _lineno (0)
, _state (Init)
, _stream ()
, _tokens ()
, _blifLine ()
, _coverTable()
{
_stream.open( blifFile+".blif" );
if (_stream.fail())
throw Error( "Unable to open BLIF file %s.blif\n", blifFile.c_str() );
_readline();
}
void connectSupplyNets ( Instance * instance, Cell* design )
{
Cell* instcell = instance->getCell();
if(instcell == NULL){
throw Error("Instance's cell is null\n");
}
Net* vssint = instcell->getNet( "vss" );
Net* vddint = instcell->getNet( "vdd" );
Net* vssext = design->getNet( "vss" );
Net* vddext = design->getNet( "vdd" );
auto vssplug = instance->getPlug( vssint );
auto vddplug = instance->getPlug( vddint );
vssplug->setNet( vssext );
vddplug->setNet( vddext );
}
void SetNetType ( Net* net, AllianceFramework * framework )
inline size_t Tokenize::lineno () const { return (_lineno) ? _lineno-1 : 0; }
inline unsigned int Tokenize::state () const { return _state; }
inline const vector<string>& Tokenize::blifLine () const { return _blifLine; }
inline const Tokenize::CoverTable& Tokenize::coverTable() const { return _coverTable; }
bool Tokenize::readEntry ()
{
if ( framework->isPOWER(net->getName()) ) {
net->setType ( Net::Type::POWER );
net->setGlobal ( true );
} else if ( framework->isGROUND(net->getName()) ) {
net->setType ( Net::Type::GROUND );
net->setGlobal ( true );
} else if ( framework->isCLOCK(net->getName()) ) {
net->setType ( Net::Type::CLOCK );
_blifLine .clear();
_coverTable.clear();
_blifLine = _tokens;
_state = 0;
if (_stream.eof()) return false;
if (_tokens.front() == ".model" ) { _state = Model; }
if (_tokens.front() == ".end" ) { _state = End; }
if (_tokens.front() == ".inputs" ) { _state = Inputs; }
if (_tokens.front() == ".outputs") { _state = Outputs; }
if (_tokens.front() == ".clock" ) { _state = Clock; }
if (_tokens.front() == ".subckt" ) { _state = Subckt; }
if (_tokens.front() == ".latch" ) { _state = Latch; }
if (_tokens.front() == ".names" ) {
_state = Names;
while ( _readline() and (_tokens.front()[0] != '.')) {
switch ( _tokens.size() ) {
case 0: break;
case 1: _coverTable.push_back( make_pair(_tokens[0],string()) ); break;
default:
case 2: _coverTable.push_back( make_pair(_tokens[0],_tokens[1]) ); break;
}
}
if (_coverTable.empty()) _state |= CoverZero;
else if (_coverTable.size () == 1) {
if ( (_coverTable[0].first == "1") and (_coverTable[0].second.empty()) ) _state |= CoverOne;
else if ( (_coverTable[0].first == "1") and (_coverTable[0].second == "1") ) _state |= CoverAlias;
} else {
_state |= CoverLogic;
}
} else
net->setType ( Net::Type::LOGICAL );
_readline();
return true;
}
enum ParserState{
EXT = 0x00,
MODEL = 0x01,
SUBCKT = 0x02,
NAMES = 0x04,
INPUTS = 0x08,
OUTPUTS = 0x16
};
struct subckt{
string cell;
vector<pair<string, string> > pins;
};
bool Tokenize::_readline ()
{
_tokens.clear();
struct model{
string name;
unordered_map<string, Net::Direction> pins;
vector<subckt> subcircuits;
vector<pair<string, string> > aliases;
bool operator<(model const & o) const{ return name < o.name; }
};
bool nextLine = true;
std::vector<std::string> tokenize(std::string const & s){
std::vector<std::string> ret;
auto b_it = s.begin();
while(b_it != s.end()){
// Remove whitespace
while(b_it != s.end() && (*b_it == ' ' or *b_it == '\t'))
++b_it;
// Find the end of the token
auto e_it = b_it;
while(e_it != s.end() && *e_it != ' ' && *e_it != '\t' and *e_it != '#')
++e_it;
// Create the token
if(e_it != b_it)
ret.push_back(std::string(b_it, e_it));
// Handle comments
if(e_it != s.end() && *e_it == '#')
e_it = s.end();
b_it = e_it;
while ( nextLine ) {
if (_stream.eof()) return false;
nextLine = false;
++_lineno;
string line;
getline( _stream, line );
bool comment = false;
size_t tokstart = 0;
for ( size_t i=0 ; i<line.size() ; ++i ) {
size_t nextTokstart = i+1;
switch ( line[i] ) {
case '\\':
if (i == line.size()-1) nextLine = true;
default: continue;
case ' ':
case '\t': break;
case '#': comment = true; break;
}
if (i > tokstart)
_tokens.push_back( line.substr(tokstart,i-tokstart) );
tokstart = nextTokstart;
if (comment) break;
}
if ( (not comment) and (tokstart < line.size()) )
_tokens.push_back( line.substr(tokstart) );
if (_tokens.empty())
nextLine = true;
}
std::reverse(ret.begin(), ret.end());
return ret;
return not _tokens.empty();
}
} // End of anonymous namespace.
// -------------------------------------------------------------------
// Class : "::Subckt" (declaration).
class Model;
class Subckt {
public:
typedef vector< pair<string,string> > Connections;
public:
Subckt ( string modelName, string instanceName );
inline string getModelName () const;
inline string getInstanceName () const;
inline const Connections& getConnections () const;
inline size_t getDepth () const;
inline Model* getModel () const;
inline void setModel ( Model* );
inline void addConnection ( const pair<string,string>& );
void connectSubckts ();
private:
string _modelName;
string _instanceName;
Connections _connections;
Model* _model;
};
typedef vector<Subckt*> Subckts;
// -------------------------------------------------------------------
// Class : "::Model" (declaration).
class Model {
public:
typedef unordered_map<string,Model*> Lut;
static Lut _blifLut;
static vector<Model*> _blifOrder;
public:
static Model* find ( string modelName );
static void orderModels ();
static void connectModels ();
static void toVhdlModels ();
static void clearStatic ();
static const Lut& getLut ();
public:
Model ( Cell* );
inline ~Model ();
inline Cell* getCell () const;
inline size_t getDepth () const;
inline const Subckts& getSubckts () const;
Subckt* addSubckt ( string modelName );
size_t computeDepth ();
void connectSubckts ();
Net* mergeNet ( string name, bool isExternal, unsigned int );
Net* mergeAlias ( string name1, string name2 );
private:
Cell* _cell;
Subckts _subckts;
size_t _depth;
};
// -------------------------------------------------------------------
// Class : "::Subckt" (implementation).
Subckt::Subckt ( string modelName, string instanceName )
: _modelName (modelName)
, _instanceName(instanceName)
, _connections ()
, _model (NULL)
{
Cell* cell = AllianceFramework::get()->getCell( modelName, Catalog::State::Views, 0 );
if (cell) {
_model = Model::find( getString(cell->getName()) );
if (not _model) {
_model = new Model ( cell );
}
}
}
inline Model* Subckt::getModel () const { return _model; }
inline string Subckt::getModelName () const { return _modelName; }
inline string Subckt::getInstanceName () const { return _instanceName; }
inline const Subckt::Connections&
Subckt::getConnections () const { return _connections; }
inline size_t Subckt::getDepth () const { return (_model) ? _model->getDepth() : 0; }
inline void Subckt::setModel ( Model* model ) { _model = model; }
inline void Subckt::addConnection ( const pair<string,string>& connection ) { _connections.push_back(connection); }
// -------------------------------------------------------------------
// Class : "::Model" (implementation).
Model::Lut Model::_blifLut;
vector<Model*> Model::_blifOrder;
struct CompareByDepth {
inline bool operator() ( const Model* lhs, const Model* rhs )
{
if (lhs->getDepth() != rhs->getDepth()) return lhs->getDepth() < rhs->getDepth();
return lhs->getCell()->getId() < rhs->getCell()->getId();
}
};
Model* Model::find ( string modelName )
{
Lut::iterator ibcell = _blifLut.find( modelName );
if (ibcell == _blifLut.end()) return NULL;
return ibcell->second;
}
void Model::orderModels ()
{
for ( auto element : _blifLut ) {
element.second->computeDepth();
_blifOrder.push_back( element.second );
}
sort( _blifOrder.begin(), _blifOrder.end(), CompareByDepth() );
}
void Model::connectModels ()
{
for ( Model* blifModel : _blifOrder )
blifModel->connectSubckts();
}
void Model::toVhdlModels ()
{
for ( Model* model : _blifOrder )
CRL::NamingScheme::toVhdl( model->getCell()
, CRL::NamingScheme::Recursive|CRL::NamingScheme::FromVerilog );
}
void Model::clearStatic ()
{
for ( auto ibcell : _blifLut ) delete ibcell.second;
_blifLut.clear();
_blifOrder.clear();
}
Model::Model ( Cell* cell )
: _cell (cell)
, _subckts()
, _depth (0)
{
_blifLut.insert( make_pair(getString(_cell->getName()), this) );
if (_cell->isTerminal())
_depth = 1;
else {
cmess2 << " " << tab++ << "+ " << cell->getName() << " [.model]" << endl;
Net* vss = Net::create ( _cell, "vss" );
vss->setExternal( true );
vss->setGlobal ( true );
vss->setType ( Net::Type::GROUND );
Net* vdd = Net::create ( _cell, "vdd" );
vdd->setExternal( true );
vdd->setGlobal ( true );
vdd->setType ( Net::Type::POWER );
}
}
inline Model::~Model ()
{ for ( auto subckt : _subckts ) delete subckt; }
inline const Model::Lut& Model::getLut () { return _blifLut; }
inline Cell* Model::getCell () const { return _cell; }
inline size_t Model::getDepth () const { return _depth; }
inline const Subckts& Model::getSubckts () const { return _subckts; }
Net* Model::mergeNet ( string name, bool isExternal, unsigned int direction )
{
Net* net = _cell->getNet( name );
if (not net) {
net = Net::create( _cell, name );
net->setExternal ( isExternal );
net->setDirection( (Net::Direction::Code)direction );
} else {
net->addAlias( name );
if (isExternal) net->setExternal( true );
direction |= net->getDirection();
net->setDirection( (Net::Direction::Code)direction );
}
return net;
}
Net* Model::mergeAlias ( string name1, string name2 )
{
Net* net1 = _cell->getNet( name1 );
Net* net2 = _cell->getNet( name2 );
if (net1 and (net1 == net2)) return net1;
if (net1 and net2) { net1->merge( net2 ); return net1; }
if (net2) {
swap( net1 , net2 );
swap( name1, name2 );
}
if (not net1) {
net1 = Net::create( _cell, name1 );
net1->setExternal ( false );
}
net1->addAlias( name2 );
return net1;
}
Subckt* Model::addSubckt ( string modelName )
{
string instanceName = "subckt_" + getString(_subckts.size()) + "_" + modelName;
_subckts.push_back( new Subckt( modelName, instanceName ) );
return _subckts.back();
}
size_t Model::computeDepth ()
{
if (_depth) return _depth;
for ( auto subckt : _subckts ) {
Model* subcktModel = subckt->getModel();
if (not subcktModel) {
subcktModel = Model::find( subckt->getModelName() );
subckt->setModel( subcktModel );
}
size_t subcktDepth = subckt->getDepth();
if (not subcktDepth and subckt->getModel())
subcktDepth = subckt->getModel()->computeDepth();
_depth = std::max( _depth, subcktDepth );
}
return _depth;
}
void Model::connectSubckts ()
{
for ( Subckt* subckt : _subckts ) {
Instance* instance = Instance::create( _cell
, subckt->getInstanceName()
, subckt->getModel()->getCell()
);
for ( auto connection : subckt->getConnections() ) {
string masterNetName = connection.first;
string netName = connection.second;
Net* net = _cell->getNet( netName );
Plug* plug = instance->getPlug( instance->getMasterCell()->getNet(masterNetName) );
Net* plugNet = plug->getNet();
if (not plugNet) {
if (not net) net = Net::create( _cell, netName );
plug->setNet( net );
continue;
}
if (not net) { plugNet->addAlias( netName ); continue; }
if (plugNet == net) continue;
plugNet->merge( net );
}
}
}
} // Anonymous namespace.
namespace CRL {
//
// Can only parse simple, netlist BLIF files generated by Yosys
// Ignores all ".names" and uses only the .subckt, .model, .input and .output
//
Cell* Blif::load ( string cellPath )
{
using namespace std;
Cell* mainModel = NULL;
string mainName;
string blifFile = cellPath;
size_t dot = cellPath.find_first_of('.');
@ -152,280 +508,136 @@ namespace CRL {
mainName = cellPath.substr( dot+1 );
}
auto framework = AllianceFramework::get ();
auto framework = AllianceFramework::get();
std::ifstream ccell ( blifFile+".blif" );
if(ccell.fail()){
throw Error( "Unable to open BLIF file %s.blif\n", blifFile.c_str() );
}
cmess2 << " " << tab++ << "+ " << blifFile << " [blif]" << endl;
std::vector<model> models;
ParserState state = EXT;
bool hasName = true;
string line;
vector<string> current_names;
while(getline(ccell, line)){
istringstream linestream(line);
vector<string> tokens = tokenize(line);
if(not tokens.empty()){
string const token = tokens.back();
tokens.pop_back();
assert(not token.empty());
if(token[0] == '.'){
// Process finished .names statement
if(not current_names.empty()){
if( current_names.size() != 4
or current_names[2] != "1" or current_names[3] != "1"
){
ostringstream mess;
mess << "\'.names\' statement is not an alias and will be ignored.\n"
<< " Map to standard cells for functions and tie cells for constants.\n"
<< " ";
for ( size_t iname=0 ; iname<current_names.size() ; ++iname ) {
if (iname) mess << ", ";
mess << "\'" << current_names[iname] << "\'";
}
cerr << Warning(mess.str()) << endl;
}
else{
// Name statement is an alias: the second signal will map to the first
models.back().aliases.push_back(pair<string, string>(current_names[0], current_names[1]));
}
current_names.clear();
}
if(token == ".model"){
if(state != EXT)
throw Error("Nested model are not supported\n");
state = MODEL;
hasName = false;
models.push_back(model());
}
else if(token == ".subckt"){
if(state == EXT)
throw Error("Subcircuit without an enclosing model are not supported\n");
if(state == MODEL and not hasName)
throw Error("Model has no name\n");
state = SUBCKT;
hasName = false;
models.back().subcircuits.push_back(subckt());
}
else if(token == ".names"){
if(state == EXT)
throw Error("Names without an enclosing model are not supported\n");
if(state == MODEL and not hasName)
throw Error("Model has no name\n");
state = NAMES;
}
else if(token == ".latch"){
throw Error("Latch constructs are not understood by the parser\n");
}
else if(token == ".inputs"){
if(state == EXT)
throw Error("Inputs have been found without an enclosing model\n");
state = INPUTS;
}
else if(token == ".outputs"){
if(state == EXT)
throw Error("Outputs have been found without an enclosing model\n");
state = OUTPUTS;
}
else if(token == ".end"){
if(state == EXT)
throw Error("A .end has been found out of a model\n");
state = EXT;
}
else{
ostringstream mess;
mess << "Unknown control token <" << token << ">\n";
throw Error(mess.str());
}
}
else if(state == NAMES){
// Part of a cover for a logic function
current_names.push_back(token);
}
else{
ostringstream mess;
mess << "Encountered a non-control token at the beginning of a line: <" << token << ">\n";
throw Error(mess.str());
Cell* mainModel = NULL;
Model* blifModel = NULL;
Tokenize tokenize ( blifFile );
const vector<string>& blifLine = tokenize.blifLine();
while ( tokenize.readEntry() ) {
if (tokenize.state() == Tokenize::Model) {
if (blifModel) {
cerr << Error( "Blif::load() Previous \".model\" %s not closed (missing \".end\"?).\n"
" File %s.blif at line %u."
, getString(blifModel->getCell()->getName()).c_str()
, blifFile.c_str()
, tokenize.lineno()
) << endl;
blifModel = NULL;
--tab;
}
Cell* cell = framework->createCell( blifLine[1] );
cell->setTerminal( false );
blifModel = new Model ( cell );
if (not mainModel or (blifLine[1] == mainName))
mainModel = blifModel->getCell();
}
if (tokenize.state() == Tokenize::End) {
if (blifModel) { blifModel = NULL; --tab; continue; }
}
// Process all tokens after the control
while(not tokens.empty()){
string const token = tokens.back();
tokens.pop_back();
assert(not token.empty());
// Either a pin or an input/output definition
if(state == INPUTS or state == OUTPUTS){
auto it = models.back().pins.find(token);
Net::Direction D = (state == INPUTS)? Net::Direction::DirIn : Net::Direction::DirOut;
if(it != models.back().pins.end()){
it->second = static_cast<Net::Direction::Code>(D | it->second);
}
else{
models.back().pins.insert(pair<string, Net::Direction>(token, D));
}
}
else if(state == SUBCKT){
if(hasName){
// Encountered a pin: need to be processed
auto equal_pos = token.find_first_of('=');
if(equal_pos+1 < token.size()){
string before_space = token.substr(0, equal_pos);
string after_space = token.substr(equal_pos+1, string::npos);
models.back().subcircuits.back().pins.push_back(pair<string, string>(before_space, after_space));
}
else{
ostringstream mess;
mess << "Unable to parse the subckt pin specification <"
<< token << ">\n";
Error(mess.str());
}
}
else{
models.back().subcircuits.back().cell = token;
hasName = true;
}
}
else if(state == NAMES){
current_names.push_back(token);
}
else if(state == MODEL){
if(hasName)
throw Error("Unexpected token after model name\n");
else{
models.back().name = token;
cmess2 << " " << tab << "+ " << token << " [interface+signals]" << endl;
hasName = true;
}
}
else{
throw Error("Unexpected token\n");
}
if (tokenize.state() == Tokenize::Clock) {
cerr << Error( "Blif::load() \".clock\" command is not supported.\n"
" File %s.blif at line %u."
, blifFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
line.clear();
}
if(state != EXT){
cerr << Warning("End of model has not been found\n");
}
/*
for(auto & M : models){
cout << "Model: " << M.name << endl;
for(auto & S : M.subcircuits){
cout << "\tInstance of " << S.cell;
for(auto & P : S.pins){
cout << " " << P.first << ":" << P.second;
}
cout << endl;
if (tokenize.state() == Tokenize::Latch) {
cerr << Error( "Blif::load() \".latch\" command is not supported.\n"
" File %s.blif at line %u."
, blifFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
}
*/
// Two passes: first create the cells and their nets, then create the internals
std::vector<Cell*> model_cells;
for(auto M : models){
Cell * design = framework->createCell(M.name);
if(design == NULL){
throw Error("Model " + M.name + " is NULL\n");
if (not blifModel) {
cerr << Error( "Blif::load() Unexpected command \"%s\" outside of .model definition.\n"
" File %s.blif at line %u."
, blifLine[0].c_str()
, blifFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
if (M.name == mainName) mainModel = design;
model_cells.push_back(design);
addSupplyNets(design);
unordered_set<string> net_names;
for(auto const & S : M.subcircuits){
for(auto const & P : S.pins){
net_names.insert(P.second);
}
for(auto const & A : M.aliases){
net_names.insert(A.first);
net_names.insert(A.second);
}
if (tokenize.state() == Tokenize::Inputs) {
for ( size_t i=1 ; i<blifLine.size() ; ++i )
blifModel->mergeNet( blifLine[i], true, Net::Direction::IN );
}
for(auto const & P : M.pins){
net_names.insert(P.first);
if (tokenize.state() == Tokenize::Outputs) {
for ( size_t i=1 ; i<blifLine.size() ; ++i )
blifModel->mergeNet( blifLine[i], true, Net::Direction::OUT );
}
for(string const & N : net_names){
Net* new_net = Net::create( design, N );
auto it = M.pins.find(N);
if(it != M.pins.end()){
new_net->setExternal( true );
new_net->setDirection( it->second );
}
}
}
// Second pass: every cell and its nets have already been created
for(size_t i=0; i<models.size(); ++i){
cmess2 << " " << tab++ << "+ " << models[i].name << " [instances]" << endl;
auto const & M = models[i];
Cell * design = model_cells[i];
for(size_t j=0; j<M.subcircuits.size(); ++j){
auto & S = M.subcircuits[j];
ostringstream subckt_name;
subckt_name << "subckt_" << j;
Cell * cell = framework->getCell(S.cell, Catalog::State::Views, 0);
if(cell == NULL){
throw Error("Cell <" + S.cell + "> to instanciate hasn't been found\n");
}
//cmess2 << "Creating instance <" << subckt_name.str() << "> of <" << S.cell << "> in <" << models[i].name << ">" << endl;
Instance* instance = Instance::create( design, subckt_name.str(), cell);
for(auto & P : S.pins){
Net* internalNet = cell->getNet( P.first );
Net* externalNet = design->getNet( P.second );
assert(internalNet != NULL and externalNet != NULL and instance != NULL);
instance->getPlug( internalNet )->setNet( externalNet );
}
//connectSupplyNets(instance, design);
}
// Merge the aliased nets
for(auto alias : M.aliases){
//cmess2 << "Merging nets <" << alias.first << "> and <" << alias.second << ">" << endl;
Net * first_net = design->getNet( alias.first );
Net * second_net = design->getNet( alias.second );
if(first_net == NULL or second_net == NULL){
ostringstream mess;
mess << "Trying to create an alias for non-instantiated nets:";
if(first_net == NULL)
mess << " <" << alias.first << ">";
if(second_net == NULL)
mess << " <" << alias.second << ">";
mess << ", will be ignored\n";
cerr << Warning(mess.str());
if (tokenize.state() & Tokenize::Names) {
if (tokenize.state() & Tokenize::CoverAlias) {
blifModel->mergeAlias( blifLine[1], blifLine[2] );
} else if (tokenize.state() & Tokenize::CoverZero) {
} else if (tokenize.state() & Tokenize::CoverOne ) {
} else {
cerr << Error( "Blif::load() Unsupported \".names\" cover construct.\n"
" File %s.blif at line %u."
, blifFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
if(!first_net->isExternal())
swap(first_net, second_net); // If only one net is external, it needs to be the first
first_net->merge(second_net);
}
--tab;
if (tokenize.state() == Tokenize::Subckt) {
Subckt* subckt = blifModel->addSubckt( blifLine[1] );
for ( size_t i=2 ; i<blifLine.size() ; ++i ) {
size_t equal = blifLine[i].find('=');
if (equal == string::npos) {
cerr << Error( "Blif::load() Bad affectation in \".subckt\": %s.\n"
" File %s.blif at line %u."
, blifLine[i].c_str()
, blifFile.c_str()
, tokenize.lineno()
) << endl;
continue;
}
subckt->addConnection( make_pair(blifLine[i].substr(0,equal)
,blifLine[i].substr( equal+1)) );
}
}
}
if (blifModel) {
cerr << Error( "Blif::load() Last \".model\" %s was not closed (missing \".end\"?).\n"
" File %s.blif at line %u."
, getString(blifModel->getCell()->getName()).c_str()
, blifFile.c_str()
, tokenize.lineno()
) << endl;
tab--;
}
tab--;
Model::orderModels();
Model::connectModels();
Model::toVhdlModels();
Model::clearStatic();
--tab;
for ( auto model : model_cells )
CRL::NamingScheme::toVhdl( model, CRL::NamingScheme::Recursive|CRL::NamingScheme::FromVerilog );
if(model_cells.empty()){
throw Error("No model found in the file\n");
}
else if(mainModel) {
return mainModel;
}
else if (model_cells.size() > 1){
cerr << Warning( "Blif::load(): Several models found, returned the first one %s.\n"
, getString(model_cells[0]->getName()).c_str()
if (not mainModel)
cerr << Warning( "Blif::load(): File %s.blif doesn't contains any \".model\".\n"
, blifFile.c_str()
);
}
return model_cells[0];
return mainModel;
}

View File

@ -32,19 +32,38 @@ namespace CRL {
{
string vhdlName;
size_t parCount = 0;
// VHDL reserved keywords (scalar).
if (vlogName == Name("in" )) return "in_v";
if (vlogName == Name("out" )) return "out_v";
if (vlogName == Name("inout")) return "inout_v";
size_t parCount = 0;
size_t posLeftPar = 0;
size_t posRightPar = 0;
for ( size_t i=0 ; i<vlogName.size() ; ++i ) {
if (vlogName[i] == '(') ++parCount;
if (vlogName[i] == '[') ++parCount;
if (vlogName[i] == '(') { ++parCount; posLeftPar=i; }
if (vlogName[i] == '[') { ++parCount; posLeftPar=i; }
if (vlogName[i] == ')') { posRightPar=i; }
if (vlogName[i] == ']') { posRightPar=i; }
}
char leftPar = (parCount > 1) ? '_' : '(';
char rightPar = (parCount > 1) ? '_' : ')';
if (parCount == 1) {
for ( size_t i=posLeftPar+1 ; i<posRightPar ; ++i ) {
if (not isdigit(vlogName[i])) {
leftPar = rightPar = '_';
break;
}
}
}
for ( size_t i=0 ; i<vlogName.size() ; ++i ) {
char translated = tolower( vlogName[i] );
if ( (i == 0) and (isdigit(translated)) )
vhdlName += 'N';
if ( vhdlName.empty() and (isdigit(translated)) )
vhdlName += 'n';
if (translated == '\\') translated = '_';
if (translated == '$' ) translated = '_';
@ -54,7 +73,7 @@ namespace CRL {
if (translated == ']' ) translated = rightPar;
if (translated == '_') {
if (i == 0 ) continue;
if (vhdlName.empty() ) continue;
if (i == vlogName.size()-1) break;
if (vhdlName.back() == '_') continue;
}
@ -62,6 +81,11 @@ namespace CRL {
vhdlName += translated;
}
// VHDL reserved keywords (vector).
if (vhdlName.substr(0,3) == "in(" ) vhdlName.insert(2,"_v");
if (vhdlName.substr(0,4) == "out(" ) vhdlName.insert(3,"_v");
if (vhdlName.substr(0,6) == "inout(") vhdlName.insert(5,"_v");
return Name(vhdlName);
}
@ -91,7 +115,9 @@ namespace CRL {
for ( auto inst : instances ) inst->setName( converter( inst->getName() ) );
if (flags & Recursive)
for ( auto model : models ) toVhdl( model, flags );
for ( auto model : models ) {
if (not model->isTerminal()) toVhdl( model, flags );
}
}

View File

@ -579,6 +579,8 @@ namespace Etesian {
using namespace coloquinte::gp;
using namespace coloquinte::dp;
getCell()->uniquify();
getConfiguration()->print( getCell() );
if (getCell()->getAbutmentBox().isEmpty()) setDefaultAb();

View File

@ -198,6 +198,28 @@
* De-materializes all components of all the nets of the Cell.
*/
//! \function void Cell::uniquify ( unsigned int depth=(unsigned int)-1 );
//! \param depth Recursively perform the uniquification until that
//! hierarchical depth.
//!
//! Multiple instances of a same model are modificated so that
//! each instance has it's own \e unique copy of the model.
//!
//! This is equivalent to a flatten operation, but without
//! destroying the intermediate level of hierarchy. Only the
//! \b netlist and instance's placements gets duplicated.
//! <em>No physical components will be duplicated.</em>
//!
//! \see Cell::getClone(), Instance::uniquify().
//! \function Cell* Cell::getClone ();
//! Build a duplicate of the Cell. The database keep track of all
//! the duplicate of one original Cell. The name of a duplicate
//! is generated by appending \c '_uXX' where \c 'XX' is the number
//! of the duplicate.
//!
//! Only logical information (netlist) and instance's placements
//! gets duplicated. <em>No physical components will be duplicated.</em>
/*! \typedef typedef GenericCollection<Cell*> Cells;
* Generic collection representing a set of cell objects.

View File

@ -163,6 +163,18 @@
* occurences are therefore deleted.
*/
//! \function void Instance::uniquify ();
//! Replace the \c <masterCell> of this instance by a cloned copy.
//!
//! \see Cell::getClone().
//! \function Instance* Instance::getClone ( Cell* cloneCell );
//! Build a duplicate of instance (\c <this>) inside a cloned Cell \c <cloneCell>.
//! The connections (Plug) on the copied instance are copied. That is,
//! connected to Net with identical names in \c <cloneCell>.
//!
//! \important In \c <cloneCell>, a copy (by name) of all the nets this instance
//! is connected to must exits.
//! \name Instance Collection
// \{

View File

@ -291,6 +291,13 @@
* their occurences).
*/
//! \function Net* Net::getClone ( Cell* cloneCell );
//! Build a duplicate of net (\c <this>) inside a cloned Cell \c <cloneCell>.
//! The connectivity (Plug) or components of the original net are \b not
//! copied.
//!
//! \remark It is likely that \c <cloneCell> is a copy of this net's onwer Cell,
//! but it is not mandatory.
//! \name Net Collection
// \{

View File

@ -38,6 +38,72 @@
namespace Hurricane {
const Name Cell::UniquifyRelation::_name = "Cell::UniquifyRelation";
Cell::UniquifyRelation::UniquifyRelation ( Cell* masterOwner )
: Relation (masterOwner)
, _duplicates(1)
{ }
Cell::UniquifyRelation* Cell::UniquifyRelation::create ( Cell* masterOwner )
{
UniquifyRelation* relation = new UniquifyRelation(masterOwner);
relation->_postCreate();
return relation;
}
void Cell::UniquifyRelation::_preDestroy ()
{
Relation::_preDestroy();
}
Cell::UniquifyRelation* Cell::UniquifyRelation::get ( const Cell* cell )
{
if (not cell)
throw Error( "Can't get Cell::UniquifyRelation : empty cell" );
Property* property = cell->getProperty( staticGetName() );
if (property) {
UniquifyRelation* relation = dynamic_cast<UniquifyRelation*>(property);
if (not relation )
throw Error ( "Bad Property type: Must be a UniquifyRelation" );
return relation;
}
return NULL;
}
Name Cell::UniquifyRelation::staticGetName () { return _name; }
Name Cell::UniquifyRelation::getName () const { return _name; }
string Cell::UniquifyRelation::_getTypeName () const { return "Cell::UniquifyRelation"; }
Name Cell::UniquifyRelation::getUniqueName ()
{
Cell* owner = dynamic_cast<Cell*>( getMasterOwner() );
ostringstream name;
name << getString(owner->getName()) << "_u" << setw(2) << setfill('0') << _duplicates++;
return Name(name.str());
}
Record* Cell::UniquifyRelation::_getRecord () const
{
Record* record = Relation::_getRecord();
if (record) {
record->add( getSlot( "_duplicates", &_duplicates ) );
}
return record;
}
void Cell::_insertSlice ( ExtensionSlice* slice )
{
ExtensionSliceMap::iterator islice = _extensionSlices.find ( slice->getName() );
@ -100,6 +166,7 @@ Cell::Cell(Library* library, const Name& name)
_abutmentBox(),
_boundingBox(),
_isTerminal(true),
_isFlattenLeaf(false),
_isPad(false),
_nextOfLibraryCellMap(NULL),
_nextOfSymbolCellSet(NULL),
@ -343,6 +410,80 @@ void Cell::flattenNets(unsigned int flags)
UpdateSession::close();
}
Cell* Cell::getCloneMaster() const
// *******************************
{
UniquifyRelation* uniquify = UniquifyRelation::get( this );
if (not uniquify) return const_cast<Cell*>(this);
uniquify = UniquifyRelation::get( this );
return dynamic_cast<Cell*>( uniquify->getMasterOwner() );
}
Cell* Cell::getClone()
// *******************
{
UpdateSession::open();
UniquifyRelation* uniquify = UniquifyRelation::get( this );
if (not uniquify) {
uniquify = UniquifyRelation::create( this );
}
Cell* clone = Cell::create( getLibrary(), uniquify->getUniqueName() );
clone->put ( uniquify );
clone->setTerminal ( isTerminal () );
clone->setFlattenLeaf( isFlattenLeaf () );
clone->setPad ( isPad () );
clone->setAbutmentBox( getAbutmentBox() );
forEach( Net*, inet, getNets() ) {
if (dynamic_cast<DeepNet*>(*inet)) continue;
inet->getClone( clone );
}
bool isPlaced = true;
forEach( Instance*, iinstance, getInstances() ) {
if (iinstance->getClone(clone)->getPlacementStatus() == Instance::PlacementStatus::UNPLACED)
isPlaced = false;
}
if (isPlaced) clone->setFlags( Placed );
UpdateSession::close();
return clone;
}
void Cell::uniquify(unsigned int depth)
// ************************************
{
vector<Instance*> toUniquify;
set<Cell*> masterCells;
forEach ( Instance*, iinstance, getInstances() ) {
Cell* masterCell = iinstance->getMasterCell();
if (masterCell->isTerminal()) continue;
masterCells.insert( masterCell );
if (masterCell->getSlaveInstances().getSize() > 1) {
toUniquify.push_back( *iinstance );
}
}
for ( auto iinst : toUniquify ) {
iinst->uniquify();
masterCells.insert( iinst->getMasterCell() );
}
if (depth > 0) {
for ( auto icell : masterCells )
icell->uniquify( depth-1 );
}
}
void Cell::materialize()
// *********************
{
@ -509,6 +650,66 @@ void Cell::notify(unsigned flags)
_observers.notify(flags);
}
// ****************************************************************************************************
// Cell::ClonedSet implementation
// ****************************************************************************************************
Cell::ClonedSet::Locator::Locator ( const Cell* cell )
: Hurricane::Locator<Cell*>()
, _dboLocator (NULL)
{
UniquifyRelation* uniquify = UniquifyRelation::get( cell );
if (uniquify) {
_dboLocator = uniquify->getSlaveOwners().getLocator();
}
}
Locator<Cell*>* Cell::ClonedSet::Locator::getClone () const
{ return new Locator(*this); }
Cell* Cell::ClonedSet::Locator::getElement () const
{ return (_dboLocator and _dboLocator->isValid())
? dynamic_cast<Cell*>(_dboLocator->getElement()) : NULL; }
bool Cell::ClonedSet::Locator::isValid () const
{ return (_dboLocator and _dboLocator->isValid()); }
void Cell::ClonedSet::Locator::progress ()
{
_dboLocator->progress();
}
string Cell::ClonedSet::Locator::_getString () const
{
string s = "<" + _TName("Cell::ClonedSet::Locator")
+ getString(getElement())
+ ">";
return s;
}
Collection<Cell*>* Cell::ClonedSet::getClone () const
{ return new ClonedSet(*this); }
Locator<Cell*>* Cell::ClonedSet::getLocator () const
{ return new Locator(_cell); }
string Cell::ClonedSet::_getString () const
{
string s = "<" + _TName("Cell_ClonedSet") + " "
+ getString(_cell->getName())
+ ">";
return s;
}
// ****************************************************************************************************
// Cell::InstanceMap implementation
// ****************************************************************************************************

View File

@ -1980,6 +1980,12 @@ Cells Cell::getSubCells() const
return Cell_SubCells(this);
}
Cells Cell::getClonedCells() const
// *******************************
{
return ClonedSet(this);
}
Components Cell::getComponents(const Layer::Mask& mask) const
// **********************************************************
{

View File

@ -1,7 +1,7 @@
// ****************************************************************************************************
// File: ./Instance.cpp
// Authors: R. Escassut
// Copyright (c) BULL S.A. 2000-2009, All Rights Reserved
// Copyright (c) BULL S.A. 2000-2015, All Rights Reserved
//
// This file is part of Hurricane.
//
@ -17,6 +17,8 @@
// not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************************************
#include "hurricane/Warning.h"
#include "hurricane/UpdateSession.h"
#include "hurricane/SharedPath.h"
#include "hurricane/Instance.h"
#include "hurricane/Cell.h"
@ -392,6 +394,7 @@ void Instance::setMasterCell(Cell* masterCell, bool secureFlag)
// ************************************************************
{
if (masterCell != _masterCell) {
UpdateSession::open();
if (!masterCell)
throw Error("Can't set master : null master cell");
@ -442,9 +445,51 @@ void Instance::setMasterCell(Cell* masterCell, bool secureFlag)
if (!getPlug(externalNet)) Plug::_create(this, externalNet);
end_for;
}
UpdateSession::close();
}
}
void Instance::uniquify()
// **********************
{
if (_masterCell->getSlaveInstances().getSize() == 1) {
cerr << Warning( "Instance::uniquify(): Master Cell %s of %s is already unique."
, getString(_masterCell->getName()).c_str()
, getString(getName()).c_str()
) << endl;
}
setMasterCell( _masterCell->getClone() );
}
Instance* Instance::getClone(Cell* cloneCell) const
// ************************************************
{
Instance* clone = Instance::create( cloneCell
, getName()
, getMasterCell()
, getPlacementStatus()
);
forEach( Plug*, iplug, getPlugs() ) {
if (iplug->isConnected()) {
Plug* clonePlug = clone->getPlug( iplug->getMasterNet() );
Net* cloneNet = cloneCell->getNet( iplug->getNet()->getName() );
if (cloneNet) {
clonePlug->setNet( cloneNet );
} else {
cerr << Warning( "Instance::getClone(): While cloning instance %s, missing net %s in cloned cell %s."
, getString(getName()).c_str()
, getString(iplug->getNet()->getName()).c_str()
, getString(cloneCell->getName()).c_str()
) << endl;
}
}
}
return clone;
}
void Instance::_postCreate()
// *************************
{

View File

@ -536,6 +536,19 @@ bool Net::removeAlias(const Name& name )
return false;
}
Net* Net::getClone(Cell* clonedCell)
// *********************************
{
Net* clonedNet = Net::create( clonedCell, getName() );
clonedNet->setArity ( getArity() );
clonedNet->setGlobal ( isGlobal() );
clonedNet->setExternal ( isExternal() );
clonedNet->setType ( getType() );
clonedNet->setDirection( getDirection() );
return clonedNet;
}
void Net::materialize()
// ********************
{
@ -639,6 +652,8 @@ void Net::merge(Net* net)
net->_mainName.detach();
}
if (net->isExternal() and not isExternal())
setExternal( true );
net->destroy();
if (slaves) _mainName.attach( slaves );

View File

@ -163,10 +163,10 @@ namespace Hurricane {
Net* NetMainName::getNet () const { return (Net*)((ptrdiff_t)this - _offset); }
string NetMainName::_getString () const { return "<NetMainName " + getString(getName()) + ">"; }
// -------------------------------------------------------------------
// Class : "Hurricane::NetAliasName".
NetAliasName::NetAliasName ( Name name )
: NetAliasHook()
, _name (name)
@ -210,4 +210,56 @@ namespace Hurricane {
return record;
}
// -------------------------------------------------------------------
// Class : "Hurricane::AliasList" (Collection).
AliasList::Locator::Locator ( const Net* net )
: Hurricane::Locator<NetAliasHook*>()
, _hook (net->getMainName()->getNext())
{ }
Locator<NetAliasHook*>* AliasList::Locator::getClone () const
{ return new Locator(*this); }
NetAliasHook* AliasList::Locator::getElement () const
{ return isValid() ? _hook : NULL; }
bool AliasList::Locator::isValid () const
{ return (_hook and not _hook->isMaster()); }
void AliasList::Locator::progress ()
{ if (not _hook->isMaster()) _hook = _hook->getNext(); }
string AliasList::Locator::_getString () const
{
string s = "<" + _TName("AliasList::Locator")
+ getString(getElement())
+ ">";
return s;
}
Collection<NetAliasHook*>* AliasList::getClone () const
{ return new AliasList(*this); }
Locator<NetAliasHook*>* AliasList::getLocator () const
{ return new Locator(_net); }
string AliasList::_getString () const
{
string s = "<" + _TName("NetAliasHook") + " "
+ getString(_net->getName())
+ ">";
return s;
}
} // Hurricane namespace.

View File

@ -1,7 +1,7 @@
// ****************************************************************************************************
// File: ./Relation.cpp
// Authors: R. Escassut
// Copyright (c) BULL S.A. 2000-2009, All Rights Reserved
// Copyright (c) BULL S.A. 2000-2015, All Rights Reserved
//
// This file is part of Hurricane.
//

View File

@ -17,10 +17,12 @@
// not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************************************
#ifndef HURRICANE_CELL
#define HURRICANE_CELL
#ifndef HURRICANE_CELL_H
#define HURRICANE_CELL_H
#include <limits>
#include "hurricane/Observer.h"
#include "hurricane/Relation.h"
#include "hurricane/Pathes.h"
#include "hurricane/Entity.h"
#include "hurricane/Cells.h"
@ -82,6 +84,50 @@ class Cell : public Entity {
public: typedef Entity Inherit;
public: typedef map<Name,ExtensionSlice*> ExtensionSliceMap;
class UniquifyRelation : public Relation {
public:
static UniquifyRelation* create ( Cell* );
static UniquifyRelation* get ( const Cell* );
virtual Name getName () const;
static Name staticGetName ();
Name getUniqueName ();
virtual string _getTypeName () const;
virtual Record* _getRecord () const;
private:
static const Name _name;
unsigned int _duplicates;
private:
UniquifyRelation ( Cell* );
protected:
virtual void _preDestroy ();
};
class ClonedSet : public Collection<Cell*> {
public:
// Sub-Class: Locator.
class Locator : public Hurricane::Locator<Cell*> {
public:
Locator ( const Cell* );
inline Locator ( const Locator& );
virtual Cell* getElement () const;
virtual Hurricane::Locator<Cell*>* getClone () const;
virtual bool isValid () const;
virtual void progress ();
virtual string _getString () const;
protected:
Hurricane::Locator<DBo*>* _dboLocator;
};
public:
inline ClonedSet ( const Cell* cell );
inline ClonedSet ( const ClonedSet& );
virtual Hurricane::Collection<Cell*>* getClone () const;
virtual Hurricane::Locator<Cell*>* getLocator () const;
virtual string _getString () const;
protected:
const Cell* _cell;
};
class InstanceMap : public IntrusiveMap<Name, Instance> {
// ****************************************************
@ -289,8 +335,8 @@ class Cell : public Entity {
public: References getReferences() const;
public: Components getComponents(const Layer::Mask& mask = ~0) const;
public: Components getComponentsUnder(const Box& area, const Layer::Mask& mask = ~0) const;
public: Occurrences getOccurrences(unsigned searchDepth = (unsigned)-1) const;
public: Occurrences getOccurrencesUnder(const Box& area, unsigned searchDepth = (unsigned)-1) const;
public: Occurrences getOccurrences(unsigned searchDepth = std::numeric_limits<unsigned int>::max()) const;
public: Occurrences getOccurrencesUnder(const Box& area, unsigned searchDepth = std::numeric_limits<unsigned int>::max()) const;
public: Occurrences getTerminalInstanceOccurrences() const;
public: Occurrences getTerminalInstanceOccurrencesUnder(const Box& area) const;
public: Occurrences getLeafInstanceOccurrences() const;
@ -305,6 +351,8 @@ class Cell : public Entity {
public: Gos getExtensionGosUnder ( const Box& area, const Name& name ) const;
public: Gos getExtensionGosUnder ( const Box& area, ExtensionSlice::Mask mask = ~0 ) const;
public: Cells getSubCells() const;
public: Cells getClonedCells() const;
public: Cell* getCloneMaster() const;
public: Pathes getRecursiveSlavePathes() const;
public: const Box& getAbutmentBox() const {return _abutmentBox;};
@ -334,6 +382,8 @@ class Cell : public Entity {
public: void resetFlags(unsigned int flags) { _flags &= ~flags; }
public: void materialize();
public: void unmaterialize();
public: Cell* getClone();
public: void uniquify(unsigned int depth=std::numeric_limits<unsigned int>::max());
public: void addObserver(BaseObserver*);
public: void removeObserver(BaseObserver*);
public: void notify(unsigned flags);
@ -341,6 +391,22 @@ class Cell : public Entity {
};
inline Cell::ClonedSet::Locator::Locator ( const Locator& other )
: Hurricane::Locator<Cell*>()
, _dboLocator(other._dboLocator)
{ }
inline Cell::ClonedSet::ClonedSet ( const Cell* cell )
: Hurricane::Collection<Cell*>()
, _cell(cell)
{ }
inline Cell::ClonedSet::ClonedSet ( const ClonedSet& other )
: Hurricane::Collection<Cell*>()
, _cell(other._cell)
{ }
} // End of Hurricane namespace.
@ -353,7 +419,7 @@ INSPECTOR_P_SUPPORT(Hurricane::Cell::SliceMap);
INSPECTOR_P_SUPPORT(Hurricane::Cell::MarkerSet);
#endif // HURRICANE_CELL
#endif // HURRICANE_CELL_H
// ****************************************************************************************************

View File

@ -1,7 +1,7 @@
// ****************************************************************************************************
// File: ./hurricane/Instance.h
// Authors: R. Escassut
// Copyright (c) BULL S.A. 2000-2009, All Rights Reserved
// Copyright (c) BULL S.A. 2000-2015, All Rights Reserved
//
// This file is part of Hurricane.
//
@ -17,8 +17,8 @@
// not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************************************
#ifndef HURRICANE_INSTANCE
#define HURRICANE_INSTANCE
#ifndef HURRICANE_INSTANCE_H
#define HURRICANE_INSTANCE_H
#include "hurricane/Go.h"
#include "hurricane/Plug.h"
@ -161,6 +161,8 @@ class Instance : public Go {
public: void setTransformation(const Transformation& transformation);
public: void setPlacementStatus(const PlacementStatus& placementstatus);
public: void setMasterCell(Cell* masterCell, bool secureFlag = true);
public: void uniquify();
public: Instance* getClone(Cell* cloneCell) const;
// Others
// ******

View File

@ -169,6 +169,7 @@ class Net : public Entity {
public: virtual Cell* getCell() const {return _cell;};
public: virtual Box getBoundingBox() const;
public: const Name& getName() const {return _name;};
public: const NetMainName* getMainName() const { return &_mainName; }
public: const Arity& getArity() const {return _arity;};
public: const Type& getType() const {return _type;};
public: const Direction& getDirection() const {return _direction;};
@ -188,6 +189,7 @@ class Net : public Entity {
public: Plugs getSlavePlugs() const;
public: Plugs getConnectedSlavePlugs() const;
public: Plugs getUnconnectedSlavePlugs() const;
public: Aliases getAliases() const { return new AliasList(this); };
// Filters
// *******
@ -231,6 +233,7 @@ class Net : public Entity {
public: bool addAlias(const Name& name);
public: bool removeAlias(const Name& name);
public: void merge(Net* net);
public: Net* getClone(Cell* cloneCell);
// Others
// ******

View File

@ -100,7 +100,7 @@ namespace Hurricane {
class NetAliasName : public NetAliasHook {
public:
class Less {
class CompareByName {
public:
inline bool operator() ( const NetAliasName* lhs, const NetAliasName* rhs ) const;
};
@ -122,12 +122,63 @@ namespace Hurricane {
};
inline bool NetAliasName::Less::operator() ( const NetAliasName* lhs
, const NetAliasName* rhs ) const
inline bool NetAliasName::CompareByName::operator() ( const NetAliasName* lhs
, const NetAliasName* rhs ) const
{ return lhs->getName() < rhs->getName(); }
typedef std::set<NetAliasName*,NetAliasName::Less> AliasNameSet;
typedef std::set<NetAliasName*,NetAliasName::CompareByName> AliasNameSet;
// -------------------------------------------------------------------
// Class : "Hurricane::AliasList" (Collection).
typedef GenericCollection<NetAliasHook*> Aliases;
typedef GenericLocator <NetAliasHook*> AliasesLocator;
typedef GenericFilter <NetAliasHook*> AliasesFilter;
class AliasList : public Collection<NetAliasHook*> {
public:
// Sub-Class: Locator.
class Locator : public Hurricane::Locator<NetAliasHook*> {
public:
Locator ( const Net* );
inline Locator ( const Locator& );
virtual NetAliasHook* getElement () const;
virtual Hurricane::Locator<NetAliasHook*>* getClone () const;
virtual bool isValid () const;
virtual void progress ();
virtual string _getString () const;
protected:
NetAliasHook* _hook;
};
public:
inline AliasList ( const Net* net );
inline AliasList ( const AliasList& );
virtual Hurricane::Collection<NetAliasHook*>* getClone () const;
virtual Hurricane::Locator<NetAliasHook*>* getLocator () const;
virtual string _getString () const;
protected:
const Net* _net;
};
inline AliasList::Locator::Locator ( const Locator& other )
: Hurricane::Locator<NetAliasHook*>()
, _hook(other._hook)
{ }
inline AliasList::AliasList ( const Net* net )
: Hurricane::Collection<NetAliasHook*>()
, _net(net)
{ }
inline AliasList::AliasList ( const AliasList& other )
: Hurricane::Collection<NetAliasHook*>()
, _net(other._net)
{ }
} // Namespace Hurricane.

View File

@ -1,7 +1,7 @@
// ****************************************************************************************************
// File: ./hurricane/Relation.h
// Authors: R. Escassut
// Copyright (c) BULL S.A. 2000-2009, All Rights Reserved
// Copyright (c) BULL S.A. 2000-2015, All Rights Reserved
//
// This file is part of Hurricane.
//
@ -17,8 +17,8 @@
// not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************************************
#ifndef HURRICANE_RELATION
#define HURRICANE_RELATION
#ifndef HURRICANE_RELATION_H
#define HURRICANE_RELATION_H
#include "hurricane/Property.h"

View File

@ -652,6 +652,41 @@ extern "C" {
HCATCH
Py_RETURN_NONE;
}
// ---------------------------------------------------------------
// Attribute Method : "PyCell_uniquify ()"
static PyObject* PyCell_uniquify ( PyCell *self, PyObject* args ) {
trace << "PyCell_uniquify ()" << endl;
HTRY
METHOD_HEAD ( "Cell.uniquify()" )
unsigned int depth;
if (not PyArg_ParseTuple(args,"I:Cell.uniquify", &depth)) {
PyErr_SetString(ConstructorError, "Cell.uniquify(): Invalid number/bad type of parameter.");
return NULL;
}
cell->uniquify( depth );
HCATCH
Py_RETURN_NONE;
}
// ---------------------------------------------------------------
// Attribute Method : "PyCell_getClone ()"
static PyObject* PyCell_getClone ( PyCell *self ) {
trace << "PyCell_getClone ()" << endl;
Cell* cloneCell = NULL;
HTRY
METHOD_HEAD( "Cell.getClone()" )
cloneCell = cell->getClone();
HCATCH
return PyCell_Link( cloneCell );
}
// Standart Predicates (Attributes).
@ -692,10 +727,12 @@ extern "C" {
, { "getAbutmentBox" , (PyCFunction)PyCell_getAbutmentBox , METH_NOARGS , "Returns the abutment box of the cell(which is defined by the designer unlike the bounding box which is managed dynamically)" }
, { "isTerminal" , (PyCFunction)PyCell_isTerminal , METH_NOARGS , "Returns true if the cell is marked as terminal, else false." }
, { "isLeaf" , (PyCFunction)PyCell_isLeaf , METH_NOARGS , "Returns true if the cell is a leaf of the hierarchy, else false." }
, { "isBound" , (PyCFunction)PyCell_isPyBound , METH_NOARGS, "Returns true if the cell is bounded to the hurricane cell" }
, { "isBound" , (PyCFunction)PyCell_isPyBound , METH_NOARGS , "Returns true if the cell is bounded to the hurricane cell" }
, { "setName" , (PyCFunction)PyCell_setName , METH_VARARGS, "Allows to change the cell name." }
, { "setAbutmentBox" , (PyCFunction)PyCell_setAbutmentBox , METH_VARARGS, "Sets the cell abutment box." }
, { "setTerminal" , (PyCFunction)PyCell_setTerminal , METH_VARARGS, "Sets the cell terminal status." }
, { "uniquify" , (PyCFunction)PyCell_uniquify , METH_VARARGS, "Uniquify the Cell and it's instances up to <depth>." }
, { "getClone" , (PyCFunction)PyCell_getClone , METH_NOARGS , "Return a copy of the Cell (placement only)." }
, { "destroy" , (PyCFunction)PyCell_destroy , METH_NOARGS
, "Destroy associated hurricane object The python object remains." }
, {NULL, NULL, 0, NULL} /* sentinel */

View File

@ -38,25 +38,18 @@ extern "C" {
#define ACCESS_CLASS(_pyObject) &(_pyObject->_baseObject)
#define METHOD_HEAD(function) GENERIC_METHOD_HEAD(Instance,instance,function)
#define LOAD_CONSTANT(CONSTANT_VALUE,CONSTANT_NAME) \
#define LOAD_CONSTANT(CONSTANT_VALUE,CONSTANT_NAME) \
constant = PyInt_FromLong ( (long)CONSTANT_VALUE ); \
PyDict_SetItemString ( dictionnary, CONSTANT_NAME, constant ); \
Py_DECREF ( constant );
// x=================================================================x
// +=================================================================+
// | "PyInstance" Python Module Code Part |
// x=================================================================x
// +=================================================================+
#if defined(__PYTHON_MODULE__)
// x-------------------------------------------------------------x
// | "PyInstance" Local Functions |
// x-------------------------------------------------------------x
// ---------------------------------------------------------------
// Local Function : "PyInt_AsType ()"
static Instance::PlacementStatus PyInt_AsPlacementStatus ( PyObject* object )
{
@ -68,13 +61,6 @@ extern "C" {
return ( Instance::PlacementStatus(Instance::PlacementStatus::UNPLACED) );
}
// x-------------------------------------------------------------x
// | "PyInstance" Attribute Methods |
// x-------------------------------------------------------------x
// Standart Accessors (Attributes).
@ -82,15 +68,11 @@ extern "C" {
// Standart destroy (Attribute).
DBoDestroyAttribute(PyInstance_destroy,PyInstance)
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getName ()"
GetNameMethod(Instance, instance)
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_create ()"
PyObject* PyInstance_create ( PyObject*, PyObject *args ) {
static PyObject* PyInstance_create ( PyObject*, PyObject *args )
{
trace << "PyInstance_create ()" << endl;
Instance* instance = NULL;
@ -132,10 +114,9 @@ extern "C" {
return PyInstance_Link ( instance );
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getMasterCell ()"
static PyObject* PyInstance_getMasterCell ( PyInstance *self ) {
static PyObject* PyInstance_getMasterCell ( PyInstance *self )
{
trace << "PyInstance_getMasterCell ()" << endl;
Cell* cell = NULL;
@ -148,10 +129,9 @@ extern "C" {
return PyCell_Link ( cell );
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getPlacementStatus ()"
static PyObject* PyInstance_getPlacementStatus ( PyInstance *self ) {
static PyObject* PyInstance_getPlacementStatus ( PyInstance *self )
{
trace << "PyInstance_getPlacementStatus ()" << endl;
METHOD_HEAD ( "Instance.getPlacementStatus()" );
@ -164,10 +144,9 @@ extern "C" {
return pyObject;
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_SetPlacementStatus ()"
static PyObject* PyInstance_setPlacementStatus ( PyInstance *self, PyObject* args ) {
static PyObject* PyInstance_setPlacementStatus ( PyInstance *self, PyObject* args )
{
trace << "PyInstance_setPlacementStatus()" << endl;
METHOD_HEAD ( "Instance.setPlacementStatus()" )
@ -181,10 +160,9 @@ extern "C" {
Py_RETURN_NONE;
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getTransformation ()"
static PyObject* PyInstance_getTransformation ( PyInstance *self ) {
static PyObject* PyInstance_getTransformation ( PyInstance *self )
{
trace << "PyInstance_getTransformation ()" << endl;
METHOD_HEAD ( "Instance.getTransformation()" );
@ -198,10 +176,9 @@ extern "C" {
return ( (PyObject*)resultPyTransf );
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getPlug ()"
static PyObject* PyInstance_getPlug ( PyInstance *self, PyObject* args ) {
static PyObject* PyInstance_getPlug ( PyInstance *self, PyObject* args )
{
trace << "PyInstance_getPlug ()" << endl;
Plug* plug = NULL;
@ -218,10 +195,9 @@ extern "C" {
return PyPlug_Link ( plug );
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getPlugs()"
static PyObject* PyInstance_getPlugs(PyInstance *self ) {
static PyObject* PyInstance_getPlugs(PyInstance *self )
{
trace << "PyInstance_getPlugs()" << endl;
METHOD_HEAD ( "Instance.getPlugs()" )
@ -243,12 +219,8 @@ extern "C" {
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getConnectedPlugs()"
static PyObject* PyInstance_getConnectedPlugs(PyInstance *self) {
static PyObject* PyInstance_getConnectedPlugs(PyInstance *self)
{
trace << "PyInstance_getConnectedPlugs ()" << endl;
METHOD_HEAD ( "Instance.getConnectedPlugs()")
@ -269,10 +241,9 @@ extern "C" {
return (PyObject*)pyPlugCollection;
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getUnconnectedPlugsLocator ()"
static PyObject* PyInstance_getUnconnectedPlugs(PyInstance *self) {
static PyObject* PyInstance_getUnconnectedPlugs(PyInstance *self)
{
trace << "PyInstance_getUnconnectedPlugs ()" << endl;
METHOD_HEAD ( "Instance.getUnconnectedPlugs()")
@ -293,10 +264,9 @@ extern "C" {
return (PyObject*)pyPlugCollection;
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_getAbutmentBox ()"
static PyObject* PyInstance_getAbutmentBox ( PyInstance *self ) {
static PyObject* PyInstance_getAbutmentBox ( PyInstance *self )
{
trace << "PyInstance_getAbutmentBox ()" << endl;
METHOD_HEAD ( "Instance.getAbutmentBox()" )
@ -309,20 +279,46 @@ extern "C" {
return ( (PyObject*)pyBox );
}
static PyObject* PyInstance_uniquify ( PyInstance *self )
{
trace << "PyInstance_uniquify ()" << endl;
HTRY
METHOD_HEAD ( "Instance.uniquify()" )
instance->uniquify();
HCATCH
Py_RETURN_NONE;
}
static PyObject* PyInstance_getClone ( PyInstance *self, PyObject* args )
{
trace << "PyInstance_getClone ()" << endl;
Instance* cloneInstance = NULL;
HTRY
METHOD_HEAD( "Instance.getClone()" )
PyCell* pyCloneCell;
if (PyArg_ParseTuple(args, "O!:Instance.getClone", &PyTypeCell, &pyCloneCell)) {
cloneInstance = instance->getClone( PYCELL_O(pyCloneCell) );
} else {
PyErr_SetString (ConstructorError, "Instance.getClone(): invalid number/bad type of parameter(s).");
return NULL;
}
HCATCH
return PyInstance_Link( cloneInstance );
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_setName ()"
SetNameMethod(Instance, instance)
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_SetTransformation ()"
static PyObject* PyInstance_setTransformation ( PyInstance *self, PyObject* args ) {
static PyObject* PyInstance_setTransformation ( PyInstance *self, PyObject* args )
{
trace << "PyInstance_setTransformation()" << endl;
METHOD_HEAD ( "Instance.setTransformation()" )
@ -337,12 +333,8 @@ extern "C" {
}
// ---------------------------------------------------------------
// Attribute Method : "PyInstance_SetMasterCell ()"
static PyObject* PyInstance_setMasterCell ( PyInstance *self, PyObject* args ) {
static PyObject* PyInstance_setMasterCell ( PyInstance *self, PyObject* args )
{
trace << "Instance.setMasterCell()" << endl;
METHOD_HEAD ( "Instance.setMasterCell()" )
@ -356,12 +348,12 @@ extern "C" {
Py_RETURN_NONE;
}
// Standart Predicates (Attributes).
DirectGetBoolAttribute(PyInstance_isTerminal ,isTerminal ,PyInstance,Instance)
DirectGetBoolAttribute(PyInstance_isLeaf ,isLeaf ,PyInstance,Instance)
GetBoundStateAttribute(PyInstance_isPyBound ,PyInstance,Instance)
// ---------------------------------------------------------------
@ -386,17 +378,13 @@ extern "C" {
, { "setTransformation" , (PyCFunction)PyInstance_setTransformation , METH_VARARGS, "Allows to modify the instance transformation." }
, { "setPlacementStatus" , (PyCFunction)PyInstance_setPlacementStatus , METH_VARARGS, "Allows to modify the instance placement status." }
, { "setMasterCell" , (PyCFunction)PyInstance_setMasterCell , METH_VARARGS, "Allows to change the cell referenced by this instance." }
, { "uniquify" , (PyCFunction)PyInstance_uniquify , METH_NOARGS , "Uniquify the Instance (clone it's master Cell)." }
, { "getClone" , (PyCFunction)PyInstance_getClone , METH_VARARGS, "Create a clone of this Instance into the cloned Cell (placement only)." }
, { "destroy" , (PyCFunction)PyInstance_destroy , METH_NOARGS , "Destroy associated hurricane object The python object remains." }
, {NULL, NULL, 0, NULL} /* sentinel */
};
// x-------------------------------------------------------------x
// | "PyInstance" Object Methods |
// x-------------------------------------------------------------x
DBoDeleteMethod(Instance)
PyTypeObjectLinkPyType(Instance)
@ -404,19 +392,15 @@ extern "C" {
#else // End of Python Module Code Part.
// x=================================================================x
// +=================================================================+
// | "PyInstance" Shared Library Code Part |
// x=================================================================x
// +=================================================================+
// Link/Creation Method.
DBoLinkCreateMethod(Instance)
// ---------------------------------------------------------------
// PyInstance Object Definitions.
PyTypeInheritedObjectDefinitions(Instance, Entity)
@ -430,10 +414,6 @@ extern "C" {
#endif // End of Shared Library Code Part.
} // End of extern "C".
} // End of Isobar namespace.

View File

@ -468,22 +468,39 @@ extern "C" {
trace << "PyNet_merge()" << endl;
HTRY
PyNet* pyNetToMerge;
METHOD_HEAD ( "Net.merge()" )
if (PyArg_ParseTuple(args, "O!:Net.merge", &PyTypeNet, &pyNetToMerge)) {
net->merge(PYNET_O(pyNetToMerge));
} else {
PyErr_SetString (ConstructorError, "invalid number of parameters for Net.merge.");
return NULL;
}
PyNet* pyNetToMerge;
METHOD_HEAD ( "Net.merge()" )
if (PyArg_ParseTuple(args, "O!:Net.merge", &PyTypeNet, &pyNetToMerge)) {
net->merge(PYNET_O(pyNetToMerge));
} else {
PyErr_SetString (ConstructorError, "invalid number of parameters for Net.merge.");
return NULL;
}
HCATCH
Py_RETURN_NONE;
}
// ---------------------------------------------------------------
// Attribute Method : "PyNet_merge ()"
static PyObject* PyNet_getClone ( PyNet *self, PyObject* args ) {
trace << "PyNet_getClone()" << endl;
Net* cloneNet = NULL;
HTRY
PyCell* pyCloneCell;
METHOD_HEAD ( "Net.getClone()" )
if (PyArg_ParseTuple(args, "O!:Net.getClone", &PyTypeCell, &pyCloneCell)) {
cloneNet = net->getClone( PYCELL_O(pyCloneCell) );
} else {
PyErr_SetString (ConstructorError, "Net.getClone(): invalid number/bad type of parameter(s).");
return NULL;
}
HCATCH
return PyNet_Link(cloneNet);
}
// ---------------------------------------------------------------
@ -519,8 +536,9 @@ extern "C" {
, { "addAlias" , (PyCFunction)PyNet_addAlias , METH_VARARGS, "Add an alias name to the net." }
, { "removeAlias" , (PyCFunction)PyNet_removeAlias , METH_VARARGS, "Remove an alias name from the net." }
, { "merge" , (PyCFunction)PyNet_merge , METH_VARARGS, "Merges the net <net> to the net <this> which keeps its characteristics (arity, global, external and direction)." }
, { "getClone" , (PyCFunction)PyNet_getClone , METH_VARARGS, "Create a clone of this net into the cloned cell (components not cloneds)." }
, { "destroy" , (PyCFunction)PyNet_destroy , METH_NOARGS , "Destroy associated hurricane object, the python object remains." }
, {NULL, NULL, 0, NULL} /* sentinel */
, {NULL, NULL, 0, NULL} /* sentinel */
};

View File

@ -382,6 +382,8 @@ namespace Katabatic {
delete [] _blockages;
delete [] _densities;
delete [] _feedthroughs;
delete [] _fragmentations;
delete [] _globalsCount;
//delete [] _saturateDensities;
_allocateds--;

View File

@ -54,6 +54,7 @@ namespace Kite {
using std::cout;
using std::cerr;
using std::endl;
using std::dec;
using std::setw;
using std::left;
using std::ostream;
@ -604,6 +605,15 @@ namespace Kite {
float segmentRatio = (float)(routeds) / (float)(routeds+unrouteds.size()) * 100.0;
float wireLengthRatio = (float)(routedWireLength) / (float)(totalWireLength) * 100.0;
_toolSuccess = (unrouteds.empty());
if (not unrouteds.empty()) {
cerr << " o Routing did not complete, unrouted segments:" << endl;
for ( size_t i=0; i<unrouteds.size() ; ++i ) {
cerr << " " << dec << setw(4) << (i+1) << "| " << unrouteds[i] << endl;
}
}
result << setprecision(4) << segmentRatio
<< "% [" << routeds << "+" << unrouteds.size() << "]";
cmess1 << Dots::asString( " - Track Segment Completion Ratio", result.str() ) << endl;
@ -623,15 +633,6 @@ namespace Kite {
cmess1 << Dots::asString( " - Wire Length Expand Ratio", result.str() ) << endl;
}
_toolSuccess = (unrouteds.empty());
if (not unrouteds.empty()) {
cerr << " o Routing did not complete, unrouted segments:" << endl;
for ( size_t i=0; i<unrouteds.size() ; ++i ) {
cerr << " " << setw(4) << (i+1) << "| " << unrouteds[i] << endl;
}
}
addMeasure<size_t> ( getCell(), "Segs" , routeds+unrouteds.size() );
addMeasure<unsigned long long>( getCell(), "DWL(l)" , totalWireLength , 12 );
addMeasure<unsigned long long>( getCell(), "fWL(l)" , totalWireLength-routedWireLength , 12 );

View File

@ -48,7 +48,6 @@ def credits ():
s += ' Coloquinte software credits (used by Etesian)\n'
s += ' Author ........................................ Gabriel Gouvine\n'
s += ' URL .............. https://github.com/Coloquinte/Coloquinte.git\n\n'
s += ' FLUTE software credits (used by Knik)\n'
s += ' Author ........................................ Chris C. N. CHU\n'