Added Verilog driver for netlist export (#80)

CRL.Verilog.save(cell, 0) -> exports cell into Verilog netlist file

Co-authored-by: Serge Rabyking <serge.rabyking@chipflow.io>
This commit is contained in:
lanserge 2023-11-02 14:09:33 +00:00 committed by GitHub
parent f840563712
commit 44ce8dd162
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 635 additions and 0 deletions

View File

@ -106,6 +106,8 @@
spice/SpiceParser.cpp
spice/SpiceDriver.cpp
)
set ( verilog_cpps verilog/VerilogDriver.cpp
)
set ( bookshelf_cpps bookshelf/BookshelfParser.cpp
bookshelf/BookshelfDriver.cpp
)
@ -299,6 +301,7 @@
${ispd05_cpps}
${blif_cpps}
${spice_cpps}
${verilog_cpps}
${lefdef_cpps}
${openaccess_cpps}
)

View File

@ -0,0 +1,37 @@
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) SU/LIP6 2021-2023, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | Verilog / Hurricane Interface |
// | |
// | Authors : Jean-Paul CHAPUT, Serge Rabyking |
// | E-mail : Jean-Paul.Chaput@lip6.fr |
// | =============================================================== |
// | C++ Header : "./crlcore/Verilog.h" |
// +-----------------------------------------------------------------+
#pragma once
#include <string>
namespace Hurricane {
class Cell;
class Library;
}
namespace CRL {
using Hurricane::Cell;
using Hurricane::Library;
class Verilog {
public:
static const uint64_t TopCell = (1 << 0);
public:
static bool save ( Cell*, uint64_t flags );
};
} // CRL namespace.

View File

@ -3,6 +3,7 @@ crlcore_includes = include_directories(
'acmsigda',
'iccad04',
'spice',
'verilog',
'lefdef',
'blif',
'alliance/ap',
@ -85,6 +86,7 @@ crlcore = shared_library(
'spice/SpiceEntity.cpp',
'spice/SpiceParser.cpp',
'spice/SpiceDriver.cpp',
'verilog/VerilogDriver.cpp',
'alliance/ap/ApParser.cpp',
'alliance/ap/ApDriver.cpp',
'gds/GdsDriver.cpp',

View File

@ -0,0 +1,371 @@
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) SU 2021-2023, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | Verilog / Hurricane Interface |
// | |
// | Authors : Jean-Paul CHAPUT, Serge Rabyking |
// | E-mail : Jean-Paul.Chaput@lip6.fr |
// | =============================================================== |
// | C++ Module : "./verilog/VerilogDriver.cpp" |
// +-----------------------------------------------------------------+
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cfenv>
#include <string>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <set>
using namespace std;
#include "hurricane/configuration/Configuration.h"
#include "hurricane/Warning.h"
#include "hurricane/DataBase.h"
#include "hurricane/BasicLayer.h"
#include "hurricane/Technology.h"
#include "hurricane/Horizontal.h"
#include "hurricane/Vertical.h"
#include "hurricane/Diagonal.h"
#include "hurricane/Rectilinear.h"
#include "hurricane/Polygon.h"
#include "hurricane/Pad.h"
#include "hurricane/Net.h"
#include "hurricane/NetExternalComponents.h"
#include "hurricane/Cell.h"
#include "hurricane/Plug.h"
#include "hurricane/Instance.h"
using namespace Hurricane;
#include "crlcore/Utilities.h"
#include "crlcore/NetExtension.h"
#include "crlcore/ToolBox.h"
#include "crlcore/Verilog.h"
//#include "crlcore/VerilogBit.h"
//#include "crlcore/VerilogEntity.h"
using namespace CRL;
namespace CRL {
static void _write_hdr(ofstream &out)
{
time_t clock = time( nullptr );
tm tm = *localtime( &clock );
char stamp[1024];
strftime( stamp, 1024, "%b %d, %Y, %H:%M", &tm );
out << "/* Coriolis Verilog Driver */" << std::endl;
out << "/* Generated on " << stamp << " */" << std::endl;
}
static void _populate_non_terminal_cells(Cell* cell, std::set<Cell*> &cells)
{
if(!cells.insert(cell).second)
{
// insertion not happend because it was there already
return;
}
for(Occurrence occurrence: cell->getNonTerminalNetlistInstanceOccurrences())
{
Cell* mod = static_cast<Instance*>( occurrence.getEntity() )->getMasterCell();
// recursively find all related Non-terminal cells (part of the user's design hierarchy)
_populate_non_terminal_cells(mod, cells);
}
}
std::pair<std::string, int> _wire2bus(std::string name)
{
std::pair<std::string, int> bus(name, -1);
int i = name.size() - 1;
while (std::isspace(name[i])) --i;// remove trailing spaces just in case
if(name[i] == ')') // name ends with bracket so is in the form "some_wire_name(index)"
{
for(int j = i;;)
{
if(--j < 0)
{
// didn't find starting bracket
// this should never happen
std::cerr << "Wrongly formatted net name: " << name << std::endl;
assert(false);
}
if(name[j] == '(') // find starting bracket
{
bus.second = std::stoi(name.substr(j+1, i-j-1)); // get index
while (std::isspace(name[j-1])) --j; // ignore spaces before starting bracket
bus.first.erase(j); // trim name to not include index part with brackets
break;
}
}
}
return bus;
}
static void _write_cell(ofstream &out, Cell* cell)
{
out << std::endl;
std::set<Net*> nets;
std::vector<Net*> ports, wires;
for(Instance* instance: cell->getInstances()) // go through all cells instances that form our cell
{
for(Plug* plug: instance->getPlugs()) // plugs are connect points of the cells
{
Net* net = plug->getMasterNet();
if(net->isPower() || net->isGround()) // VDD and VSS are not part of Verilog netlist
{
continue;
}
net = plug->getNet();
if(net->isExternal()) // propogates through ports
{
ports.push_back(net);
nets.insert(net); // collect all nets we use
}
else
{
if(!nets.insert(net).second) // the insertion not happend as it was already there
{
// this net was already enumerated in some other cell
// so this wire used at least between two cells and need to be declared
wires.push_back(net);
}
}
}
}
std::map<Net*, std::pair<std::string, int> > net2bus;
for(Net *net: nets)
{
net2bus.insert(std::make_pair(net, _wire2bus(getString(net->getName()))));
}
std::vector<std::string> names;
// collect all port names without indexes in ordered way
for(Net *net: ports)
{
std::string &name = net2bus[net].first;
auto it = std::lower_bound(names.begin(), names.end(), name);
if(it == names.end() || (*it) != name) // name not in the list yet
{
names.insert(it, name);
}
}
// declare Verilog module with ports names
out << "module " << getString(cell->getName()) << "(";
for ( auto it = names.begin(); it != names.end(); ++it)
{
out << (*it);
if (it + 1 != names.end())
{
out << ", ";
}
}
out << ");\n";
// now individually declare each port with direction and width
for (auto &name: names)
{
int idx_min = -1, idx_max;
Net::Direction dir;
for (auto it: net2bus)
{
if (it.second.first == name) // find net with name
{
int idx = it.second.second;
if (idx < 0)
{
if (idx_min >= 0)
{
std::cerr << "Net name \"" << name << "\" used with index " << idx_min
<< " and without index" << std::endl;
assert(false);
}
dir = it.first->getDirection();
break;
}
if (idx_min < 0)
{
idx_min = idx_max = idx;
dir = it.first->getDirection();
}
else
{
if (idx < idx_min)
{
idx_min = idx;
}
if (idx > idx_max)
{
idx_max = idx;
}
if (dir != it.first->getDirection())
{
int other = (idx == idx_min)?idx_max:idx_min;
std::cerr << "Net \"" << name << "\" with index " << idx << " and " << other
<< " has different directions" << std::endl;
assert(false);
}
}
}
}
if (dir == Net::Direction::IN)
{
out << " input ";
}
else if (dir == Net::Direction::INOUT)
{
out << " inout ";
}
else if (dir == Net::Direction::OUT)
{
out << " output ";
}
else
{
std::cerr << "Undetermined direction " << dir << " for the net \"" << name << "\"" << std::endl;
assert(false);
}
if (idx_min >= 0)
{
out << "[" << idx_max << ":" << idx_min << "] ";
}
out << name << ";" << std::endl;
}
// collect all wires (and not ports) names without indexes in ordered way
names.clear();
for(Net *net: wires)
{
std::string &name = net2bus[net].first;
auto it = std::lower_bound(names.begin(), names.end(), name);
if(it == names.end() || (*it) != name) // name not in the list yet
{
names.insert(it, name);
}
}
// now individually declare each wire with width
for (auto &name: names)
{
int idx_min = -1, idx_max = -1;
for (auto it: net2bus)
{
if (it.second.first == name) // find all nets by name
{
int idx = it.second.second;
if (idx < 0) // without index
{
if (idx_min >= 0)
{
std::cerr << "Net name \"" << name << "\" used with index " << idx_min
<< " and without index" << std::endl;
assert(false);
}
break;
}
if (idx_min < 0) // first time encountered
{
idx_min = idx_max = idx;
}
else
{
if (idx < idx_min)
{
idx_min = idx;
}
if (idx > idx_max)
{
idx_max = idx;
}
}
}
}
out << " wire ";
if (idx_min >= 0)
{
out << "[" << idx_max << ":" << idx_min << "] ";
}
out << name << ";" << std::endl;
}
// free not needed resources
names.clear();
ports.clear();
wires.clear();
nets.clear();
// declare instancies with connections
for(Instance* instance: cell->getInstances()) // go through all cells instances that form our cell
{
std::vector<Plug*> conns;
for(Plug* plug: instance->getPlugs()) // plugs are connect points of the cells
{
Net* net = plug->getMasterNet();
if(net->isPower() || net->isGround()) // VDD and VSS are not part of Verilog netlist
{
continue;
}
// insert in sorted order
auto it = std::lower_bound(conns.begin(), conns.end(), plug,
[](Plug* lhs, Plug* rhs) -> bool
{ return getString(lhs->getMasterNet()->getName()) <
getString(rhs->getMasterNet()->getName()); });
conns.insert(it, plug);
}
if (conns.empty()) // instance has no connections apart from VSS&VDD
{
continue;
}
// declare instance
out << " " << getString(instance->getMasterCell()->getName()) << " "
<< getString(instance->getName()) << " (" << std::endl;
// declare connections
for (auto it = conns.begin(); it != conns.end(); ++it)
{
auto &info = net2bus[(*it)->getNet()];
out << " ." << getString((*it)->getMasterNet()->getName()) << "(" << info.first;
if (info.second >= 0) // indexed wire
{
out << "[" << info.second << "]";
}
out << ")";
if ( it+1 != conns.end()) // not last, so need comma
{
out << ",";
}
out << std::endl;
}
out << " );" << std::endl;
}
out << "endmodule" << std::endl;
}
// -------------------------------------------------------------------
// Class : "CRL::Verilog".
bool Verilog::save ( Cell* cell, uint64_t flags )
{
ofstream out(getString(cell->getName()) + ".v");
_write_hdr(out);
if (flags & TopCell)
{
_write_cell(out, cell);
}
else
{
std::set<Cell*> cells;
_populate_non_terminal_cells(cell, cells);
for (auto &it: cells)
{
_write_cell(out, it);
}
}
out.close();
return true;
}
} // CRL namespace.

View File

@ -56,6 +56,7 @@
PyAcmSigda.cpp
#PyIspd05.cpp
PySpice.cpp
PyVerilog.cpp
PyBlif.cpp
PyGds.cpp
PyLefImport.cpp
@ -80,6 +81,7 @@
crlcore/PyAcmSigda.h
#crlcore/PyIspd05.h
crlcore/PySpice.h
crlcore/PyVerilog.h
crlcore/PyBlif.h
crlcore/PyGds.h
crlcore/PyLefImport.h

View File

@ -34,6 +34,7 @@
#include "crlcore/PyAcmSigda.h"
// #include "crlcore/PyIspd05.h"
#include "crlcore/PySpice.h"
#include "crlcore/PyVerilog.h"
#include "crlcore/PyBlif.h"
#include "crlcore/PyGds.h"
#include "crlcore/PyLefImport.h"
@ -129,6 +130,7 @@ extern "C" {
PyAcmSigda_LinkPyType ();
// PyIspd05_LinkPyType ();
PySpice_LinkPyType ();
PyVerilog_LinkPyType ();
PyBlif_LinkPyType ();
PyGds_LinkPyType ();
PyLefImport_LinkPyType ();
@ -154,6 +156,7 @@ extern "C" {
PYTYPE_READY_NEW ( AcmSigda );
// PYTYPE_READY_NEW ( Ispd05 );
PYTYPE_READY_NEW ( Spice );
PYTYPE_READY_NEW ( Verilog );
PYTYPE_READY_NEW ( Blif );
PYTYPE_READY_NEW ( Gds );
PYTYPE_READY_NEW ( LefImport );
@ -205,6 +208,8 @@ extern "C" {
// PyModule_AddObject ( module, "Ispd05", (PyObject*)&PyTypeIspd05 );
Py_INCREF ( &PyTypeSpice );
PyModule_AddObject ( module, "Spice", (PyObject*)&PyTypeSpice );
Py_INCREF ( &PyTypeVerilog );
PyModule_AddObject ( module, "Verilog", (PyObject*)&PyTypeVerilog );
Py_INCREF ( &PyTypeBlif );
PyModule_AddObject ( module, "Blif", (PyObject*)&PyTypeBlif );
Py_INCREF ( &PyTypeGds );
@ -224,6 +229,7 @@ extern "C" {
PyRoutingLayerGauge_postModuleInit ();
PyAllianceFramework_postModuleInit ();
PySpice_postModuleInit ();
PyVerilog_postModuleInit ();
PyGds_postModuleInit ();
PyDefExport_postModuleInit ();

View File

@ -0,0 +1,163 @@
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) SU 2021-2021, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | Verilog / Hurricane Interface |
// | |
// | Authors : Jean-Paul CHAPUT, Serge Rabyking |
// | E-mail : Jean-Paul.Chaput@lip6.fr |
// | =============================================================== |
// | C++ Module : "./PyVerilog.cpp" |
// +-----------------------------------------------------------------+
#include "crlcore/PyVerilog.h"
#include "hurricane/isobar/PyCell.h"
#include "hurricane/isobar/PyLibrary.h"
//#include "crlcore/VerilogEntity.h"
#include <string>
#include <sstream>
namespace CRL {
using std::cerr;
using std::endl;
using std::hex;
using std::string;
using std::ostringstream;
using Hurricane::tab;
using Hurricane::Exception;
using Hurricane::Bug;
using Hurricane::Error;
using Hurricane::Warning;
using Isobar::ProxyProperty;
using Isobar::ProxyError;
using Isobar::ConstructorError;
using Isobar::HurricaneError;
using Isobar::HurricaneWarning;
using Isobar::getPyHash;
using Isobar::ParseOneArg;
using Isobar::ParseTwoArg;
using Isobar::__cs;
using Isobar::PyCell;
using Isobar::PyTypeCell;
using Isobar::PyCell_Link;
using Isobar::PyLibrary;
using Isobar::PyTypeLibrary;
using Isobar::PyLibrary_Link;
extern "C" {
#if defined(__PYTHON_MODULE__)
// +=================================================================+
// | "PyVerilog" Python Module Code Part |
// +=================================================================+
static PyObject* PyVerilog_save ( PyObject*, PyObject* args )
{
cdebug_log(30,0) << "PyVerilog_save()" << endl;
HTRY
long flags = 0;
PyObject* pyCell = NULL;
if (PyArg_ParseTuple( args, "Ol:Verilog.save", &pyCell, &flags )) {
if (IsPyCell(pyCell)) {
Verilog::save( PYCELL_O(pyCell), flags );
} else {
PyErr_SetString( ConstructorError, "Verilog.save(): Bad parameter type (not a Cell)." );
return NULL;
}
} else {
std::cout << ConstructorError << std::endl;
PyErr_SetString( ConstructorError, "Verilog.save(): Bad number of parameters." );
return NULL;
}
HCATCH
Py_RETURN_NONE;
}
// static PyObject* PyVerilog_load ( PyObject*, PyObject* args )
// {
// cdebug_log(30,0) << "PyVerilog_load()" << endl;
// HTRY
// unsigned long mode = 0;
// char* path = NULL;
// PyObject* pyLibrary = NULL;
// if (PyArg_ParseTuple( args, "Osk:Verilog.load", &pyLibrary, &path, &mode )) {
// if (IsPyLibrary(pyLibrary)) {
// Verilog::load( PYLIBRARY_O(pyLibrary), string(path), (uint64_t)mode );
// } else {
// PyErr_SetString( ConstructorError, "Verilog.load(): Bad parameter type (not a Library)." );
// return NULL;
// }
// } else {
// PyErr_SetString( ConstructorError, "Verilog.load(): Bad number of parameters." );
// return NULL;
// }
// HCATCH
// Py_RETURN_NONE;
// }
// static PyObject* PyVerilog_clearProperties ( PyObject* )
// {
// cdebug_log(30,0) << "PyVerilog_clearProperties()" << endl;
// HTRY
// Verilog::clearProperties();
// HCATCH
// Py_RETURN_NONE;
// }
// Standart Destroy (Attribute).
PyMethodDef PyVerilog_Methods[] =
{ { "save" , (PyCFunction)PyVerilog_save , METH_VARARGS|METH_STATIC
, "Save a complete Verilog design." }
// , { "load" , (PyCFunction)PyVerilog_load , METH_VARARGS|METH_STATIC
// , "Load a Verilog layout inside a Cell (cumulative)." }
// , { "clearProperties" , (PyCFunction)PyVerilog_clearProperties, METH_NOARGS|METH_STATIC
// , "Remove all Verilog related properties from the Cells." }
, {NULL, NULL, 0, NULL} /* sentinel */
};
NoObjectDeleteMethod(Verilog)
PyTypeObjectLinkPyTypeWithoutObject(Verilog,Verilog)
#else // End of Python Module Code Part.
// +=================================================================+
// | "PyVerilog" Shared Library Code Part |
// +=================================================================+
// Type Definition.
PyTypeObjectDefinitionsOfModule(CRL,Verilog)
extern void PyVerilog_postModuleInit ()
{
PyObject* constant;
LoadObjectConstant(PyTypeVerilog.tp_dict,::CRL::Verilog::TopCell ,"TopCell");
}
#endif // End of Shared Library Code Part.
} // extern "C".
} // CRL namespace.

View File

@ -0,0 +1,50 @@
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) SU 2021-2023, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | Verilog / Hurricane Interface |
// | |
// | Authors : Jean-Paul CHAPUT, Serge Rabyking |
// | E-mail : Jean-Paul.Chaput@lip6.fr |
// | =============================================================== |
// | C++ Header : "./crlcore/PyVerilog.h" |
// +-----------------------------------------------------------------+
#pragma once
#include "hurricane/isobar/PyHurricane.h"
#include "crlcore/Verilog.h"
namespace CRL {
extern "C" {
// -------------------------------------------------------------------
// Python Object : "PyVerilog".
typedef struct {
PyObject_HEAD
} PyVerilog;
// -------------------------------------------------------------------
// Functions & Types exported to "PyCRL.ccp".
extern PyTypeObject PyTypeVerilog;
extern PyMethodDef PyVerilog_Methods[];
extern void PyVerilog_LinkPyType ();
extern void PyVerilog_postModuleInit ();
#define IsPyVerilog(v) ( (v)->ob_type == &PyTypeVerilog )
#define PY_VERILOG(v) ( (PyVerilog*)(v) )
} // extern "C".
} // Hurricane namespace.

View File

@ -18,6 +18,7 @@ pyCRL_files = files([
'PyGraphicToolEngine.cpp',
'PyAcmSigda.cpp',
'PySpice.cpp',
'PyVerilog.cpp',
'PyBlif.cpp',
'PyGds.cpp',
'PyLefImport.cpp',