diff --git a/hurricane/src/hurricane/Rectilinear.cpp b/hurricane/src/hurricane/Rectilinear.cpp index 2a869db2..42bc911b 100644 --- a/hurricane/src/hurricane/Rectilinear.cpp +++ b/hurricane/src/hurricane/Rectilinear.cpp @@ -40,6 +40,284 @@ #include "hurricane/Warning.h" +namespace { + + using namespace std; + using Hurricane::DbU; + using Hurricane::Point; + using Hurricane::Box; + using Hurricane::Interval; + using Hurricane::Rectilinear; + + + class SweepInterval : public Interval { + public: + inline SweepInterval ( DbU::Unit vmin , DbU::Unit vmax, DbU::Unit xmin ); + inline SweepInterval ( Interval&, DbU::Unit xmin ); + inline SweepInterval& inflate ( DbU::Unit dvMin, DbU::Unit dvMax ); + inline SweepInterval& merge ( DbU::Unit v ); + inline DbU::Unit getXMin () const; + inline void setXMin ( DbU::Unit ); + inline string _getString () const; + private: + DbU::Unit _xMin; + }; + + + inline SweepInterval::SweepInterval ( DbU::Unit vmin , DbU::Unit vmax, DbU::Unit xmin ) + : Interval(vmin,vmax) + , _xMin (xmin) + { } + + inline SweepInterval::SweepInterval ( Interval& base, DbU::Unit xmin ) + : Interval(base) + , _xMin (xmin) + { } + + inline SweepInterval& SweepInterval::inflate ( DbU::Unit dvMin, DbU::Unit dvMax ) { Interval::inflate(dvMin,dvMax); return *this; } + inline SweepInterval& SweepInterval::merge ( DbU::Unit v ) { Interval::merge(v); return *this; } + inline DbU::Unit SweepInterval::getXMin () const { return _xMin; } + inline void SweepInterval::setXMin ( DbU::Unit xmin ) { _xMin=xmin; } + + inline string SweepInterval::_getString () const + { + string s; + s += "@" + DbU::getValueString(_xMin); + s += " [" + DbU::getValueString(getVMin()); + s += " " + DbU::getValueString(getVMax()) + "]"; + return s; + } + + +} // Anonymous namespace. + + +GETSTRING_VALUE_SUPPORT(::SweepInterval); + + +namespace { + + + class SweepLine { + public: + SweepLine ( const Rectilinear*, vector& ); + ~SweepLine (); + void addVEdge ( DbU::Unit ymin, DbU::Unit ymax, DbU::Unit x ); + void loadVEdges (); + void process ( Interval ); + void process ( const pair< DbU::Unit, list >& ); + void toBox ( SweepInterval& ); + void asRectangles (); + private: + const Rectilinear* _rectilinear; + vector& _boxes; + list< pair< DbU::Unit, list > > _vedges; + list< SweepInterval > _sweepLine; + DbU::Unit _prevX; + DbU::Unit _currX; + }; + + + SweepLine::SweepLine ( const Rectilinear* r, vector& boxes ) + : _rectilinear(r) + , _boxes (boxes) + , _vedges () + , _sweepLine () + , _prevX (0) + , _currX (0) + { + cdebug_log(17,1) << "SweepLine::SweepLine()" << endl; + } + + + SweepLine::~SweepLine () + { + cdebug_tabw(17,-1); + } + + + void SweepLine::addVEdge ( DbU::Unit ymin, DbU::Unit ymax, DbU::Unit x ) + { + if (ymin > ymax) std::swap( ymin, ymax ); + + cdebug_log(17,1) << "SweepLine::addVEdge() @"<< DbU::getValueString(x) + << " [" << DbU::getValueString(ymin) + << " " << DbU::getValueString(ymax) << "]" << endl; + + bool inserted = false; + for ( auto ix = _vedges.begin() ; ix != _vedges.end() ; ++ix ) { + cdebug_log(17,0) << "| Looking @" << DbU::getValueString(ix->first) + << " size=" << ix->second.size() << endl; + + if (ix->first > x) { + _vedges.insert( ix, make_pair( x, list() )); + cdebug_log(17,0) << "+ add new @" << DbU::getValueString(x) << endl; + --ix; + } + if (ix->first == x) { + for ( auto iintv = ix->second.begin() ; iintv != ix->second.end() ; ++iintv ) { + if (iintv->getVMin() >= ymax) { + ix->second.insert( iintv, Interval(ymin,ymax) ); + inserted = true; + break; + } + } + if (not inserted) { + ix->second.push_back( Interval(ymin,ymax) ); + inserted = true; + } + break; + } + } + if (not inserted) { + cdebug_log(17,0) << "+ add new (back) @" << DbU::getValueString(x) << endl; + _vedges.push_back( make_pair( x, list() )); + _vedges.back().second.push_back( Interval(ymin,ymax) ); + } + + cdebug_tabw(17,-1); + } + + + void SweepLine::loadVEdges () + { + const vector& points = _rectilinear->getPoints(); + for ( size_t i=0 ; igetVMin()) { + _sweepLine.insert( iintv, SweepInterval(v,_currX) ); + done = true; + break; + } + // Extractor p. 9 (f). + if ( (v.getVMin() == iintv->getVMin()) + and (v.getVMax() == iintv->getVMax()) ) { + toBox( *iintv ); + _sweepLine.erase( iintv ); + done = true; + break; + } + // Extractor p. 9 (b). + if (v.getVMax() == iintv->getVMin()) { + toBox( *iintv ); + iintv->merge( v.getVMin() ); + done = true; + break; + } + // Extractor p. 9 (g). + if (v.getVMax() == iintv->getVMax()) { + toBox( *iintv ); + cdebug_log(17,0) << "case (g): carve" << endl; + iintv->inflate( 0, v.getVMin() - iintv->getVMax() ); + cdebug_log(17,0) << "| " << (*iintv) << endl; + done = true; + break; + } + // Extractor p. 9 (h). + if (v.getVMin() == iintv->getVMin()) { + toBox( *iintv ); + iintv->inflate(iintv->getVMin() - v.getVMax(), 0 ); + done = true; + break; + } + // Extractor p. 9 (c). + if ( (v.getVMin() > iintv->getVMin()) + and (v.getVMax() < iintv->getVMax()) ) { + toBox( *iintv ); + cdebug_log(17,0) << "case (c): carve" << endl; + DbU::Unit wholeVMin = iintv->getVMin(); + iintv->inflate( iintv->getVMin() - v.getVMax(), 0 ); + cdebug_log(17,0) << "| " << (*iintv) << endl; + _sweepLine.insert( iintv, SweepInterval( wholeVMin, v.getVMin(), _currX ) ); + cdebug_log(17,0) << "| " << (*(--iintv)) << endl; + done = true; + break; + } + // Extractor p. 9 (d,e). + if (v.getVMin() == iintv->getVMax()) { + auto iintvNext = iintv; + ++iintvNext; + // Extractor p. 9 (d). + if (iintvNext == _sweepLine.end()) { + toBox( *iintv ); + iintv->merge( v.getVMax() ); + } else { + // Extractor p. 9 (d). + if (v.getVMax() < iintvNext->getVMin()) { + toBox( *iintv ); + iintv->merge( v.getVMax() ); + } else { + // Extractor p. 9 (e). + toBox( *iintv ); + toBox( *iintvNext ); + iintv->merge( iintvNext->getVMax() ); + _sweepLine.erase( iintvNext ); + } + } + done = true; + break; + } + } + if (not done) { + _sweepLine.push_back( SweepInterval(v,_currX) ); + } + + cdebug_tabw(17,-1); + } + + + void SweepLine::process ( const pair< DbU::Unit, list >& intervals ) + { + cdebug_log(17,1) << "SweepLine::process() @"<< DbU::getValueString(intervals.first) + << " size=" << intervals.second.size() << endl; + _currX = intervals.first; + for ( const Interval& v : intervals.second ) process( v ); + cdebug_tabw(17,-1); + } + + + void SweepLine::asRectangles () + { + loadVEdges(); + for ( auto intervals : _vedges ) { + process( intervals ); + } + cdebug_log(17,0) << "SweepLine::asRectangles() size=" << _boxes.size() << endl; + for ( const Box& b : _boxes ) + cdebug_log(17,0) << "| " << b << endl; + } + + +} // Anonymous namespace. + + namespace Hurricane { @@ -50,6 +328,7 @@ namespace Hurricane { : Super (net) , _layer (layer) , _points(points) + , _flags (IsRectilinear) { } @@ -61,6 +340,7 @@ namespace Hurricane { if (points.size() > 1000) throw Error( "Rectilinear::create(): Rectlinear polygons must not exceed 1000 vertexes." ); + bool isRect = true; DbU::Unit oneGrid = DbU::fromGrid( 1.0 ); for ( size_t i=0 ; i_flags &= ~IsRectilinear; rectilinear->_postCreate(); return rectilinear; @@ -176,6 +459,15 @@ namespace Hurricane { } + bool Rectilinear::getAsRectangles ( std::vector& rectangles ) const + { + rectangles.clear(); + if (not isRectilinear()) return false; + SweepLine( this, rectangles ).asRectangles(); + return true; + } + + void Rectilinear::_toJson ( JsonWriter* writer ) const { Inherit::_toJson( writer ); diff --git a/hurricane/src/hurricane/hurricane/Rectilinear.h b/hurricane/src/hurricane/hurricane/Rectilinear.h index 1df05c38..5f08876a 100644 --- a/hurricane/src/hurricane/hurricane/Rectilinear.h +++ b/hurricane/src/hurricane/hurricane/Rectilinear.h @@ -1,6 +1,6 @@ // -*- C++ -*- // -// Copyright (c) BULL S.A. 2018-2018, All Rights Reserved +// Copyright (c) BULL S.A. 2018-2023, All Rights Reserved // // This file is part of Hurricane. // @@ -28,10 +28,7 @@ // | C++ Header : "./hurricane/Rectilinear.h" | // +-----------------------------------------------------------------+ - -#ifndef HURRICANE_RECTILINEAR_H -#define HURRICANE_RECTILINEAR_H - +#pragma once #include "hurricane/Component.h" @@ -46,11 +43,13 @@ namespace Hurricane { class Rectilinear : public Component { public: typedef Component Super; + static const uint32_t IsRectilinear = (1<<0); public: static Rectilinear* create ( Net*, const Layer*, const vector& ); // Accessors. virtual bool isNonRectangle () const; + inline bool isRectilinear () const; virtual DbU::Unit getX () const; virtual DbU::Unit getY () const; virtual Box getBoundingBox () const; @@ -59,6 +58,7 @@ namespace Hurricane { virtual Point getPoint ( size_t i ) const; virtual const Layer* getLayer () const; inline Points getContour () const; + bool getAsRectangles ( std::vector& ) const; inline const vector& getPoints () const; // Mutators. void setLayer ( const Layer* ); @@ -75,11 +75,13 @@ namespace Hurricane { private: const Layer* _layer; vector _points; + uint32_t _flags; }; - inline Points Rectilinear::getContour () const { return new VectorCollection(_points); } - inline const vector& Rectilinear::getPoints () const { return _points; } + inline bool Rectilinear::isRectilinear () const { return _flags & IsRectilinear; } + inline Points Rectilinear::getContour () const { return new VectorCollection(_points); } + inline const vector& Rectilinear::getPoints () const { return _points; } // ------------------------------------------------------------------- @@ -99,5 +101,3 @@ namespace Hurricane { INSPECTOR_P_SUPPORT(Hurricane::Rectilinear); - -#endif // HURRICANE_RECTILINEAR_H diff --git a/hurricane/src/isobar/PyRectilinear.cpp b/hurricane/src/isobar/PyRectilinear.cpp index 0f837ff9..ca0e371b 100644 --- a/hurricane/src/isobar/PyRectilinear.cpp +++ b/hurricane/src/isobar/PyRectilinear.cpp @@ -45,6 +45,24 @@ namespace Isobar { } + PyObject* VectorToList ( const std::vector& v ) + { + PyObject* pyList = PyList_New( v.size() ); + + for ( size_t i=0 ; i_object = new Box ( v[i] ); + HCATCH + PyList_SetItem( pyList, i, (PyObject*)pyBox ); + } + + return pyList; + } + + extern "C" { @@ -168,20 +186,54 @@ extern "C" { } + static PyObject* PyRectilinear_getAsRectangles ( PyRectilinear *self, PyObject* args ) + { + cdebug_log(20,0) << "Rectilinear.getAsRectangles()" << endl; + + HTRY + METHOD_HEAD( "Rectilinear.getAsRectangles()" ) + + PyObject* pyList = NULL; + if (not PyArg_ParseTuple( args, "O:Rectilinear.getAsRectangles", &pyList )) { + PyErr_SetString( ConstructorError, "Rectilinear.getAsRectangles(): Must have exactly one parameter." ); + return NULL; + } + if (not PyList_Check(pyList)) { + PyErr_SetString( ConstructorError, "Rectilinear.getAsRectangles(): Argument must be a list." ); + return NULL; + } + + PyList_SetSlice( pyList, 0, PyList_Size(pyList), NULL ); + vector boxes; + rectilinear->getAsRectangles( boxes ); + for ( size_t i=0 ; i_object = new Box ( boxes[i] ); + PyList_Append( pyList, (PyObject*)pyBox ); + } + HCATCH + + Py_RETURN_NONE; + } + + // --------------------------------------------------------------- // PyRectilinear Attribute Method table. PyMethodDef PyRectilinear_Methods[] = - { { "create" , (PyCFunction)PyRectilinear_create , METH_VARARGS|METH_STATIC - , "Create a new Rectilinear polygon." } - , { "isNonRectangle", (PyCFunction)PyRectilinear_isNonRectangle, METH_NOARGS , "Tells if the shape is not a rectangle." } - , { "getX" , (PyCFunction)PyRectilinear_getX , METH_NOARGS , "Return the Rectilinear X value." } - , { "getY" , (PyCFunction)PyRectilinear_getY , METH_NOARGS , "Return the Rectilinear Y value." } - , { "getBoundingBox", (PyCFunction)PyRectilinear_getBoundingBox, METH_NOARGS , "Return the Rectilinear Bounding Box." } - , { "setPoints" , (PyCFunction)PyRectilinear_setPoints , METH_VARARGS, "Sets the Rectilinear Bounding Box." } - , { "translate" , (PyCFunction)PyRectilinear_translate , METH_VARARGS, "Translates the Rectilinear of dx and dy." } - , { "destroy" , (PyCFunction)PyRectilinear_destroy , METH_NOARGS - , "Destroy associated hurricane object, the python object remains." } + { { "create" , (PyCFunction)PyRectilinear_create , METH_VARARGS|METH_STATIC + , "Create a new Rectilinear polygon." } + , { "isNonRectangle" , (PyCFunction)PyRectilinear_isNonRectangle , METH_NOARGS , "Tells if the shape is not a rectangle." } + , { "getX" , (PyCFunction)PyRectilinear_getX , METH_NOARGS , "Return the Rectilinear X value." } + , { "getY" , (PyCFunction)PyRectilinear_getY , METH_NOARGS , "Return the Rectilinear Y value." } + , { "getBoundingBox" , (PyCFunction)PyRectilinear_getBoundingBox , METH_NOARGS , "Return the Rectilinear Bounding Box." } + , { "setPoints" , (PyCFunction)PyRectilinear_setPoints , METH_VARARGS, "Sets the Rectilinear Bounding Box." } + , { "translate" , (PyCFunction)PyRectilinear_translate , METH_VARARGS, "Translates the Rectilinear of dx and dy." } + , { "getAsRectangles", (PyCFunction)PyRectilinear_getAsRectangles, METH_VARARGS, "Return the rectangle coverage." } + , { "destroy" , (PyCFunction)PyRectilinear_destroy , METH_NOARGS + , "Destroy associated hurricane object, the python object remains." } , {NULL, NULL, 0, NULL} /* sentinel */ }; diff --git a/unittests/python/test_rectilinear.py b/unittests/python/test_rectilinear.py new file mode 100644 index 00000000..10b1db34 --- /dev/null +++ b/unittests/python/test_rectilinear.py @@ -0,0 +1,157 @@ +#!/usr/bin/python + +import sys +from coriolis.Hurricane import DataBase, Net, \ + DbU, Point, Box, Pad, Rectilinear +from coriolis import Cfg +from coriolis.CRL import AllianceFramework, Catalog, Gds +from coriolis.helpers import l, u +from coriolis.helpers.overlay import CfgCache, UpdateSession + + +def testRectilinear ( editor ): + """Check Hurricane.Rectilinear class.""" + with CfgCache(priority=Cfg.Parameter.Priority.UserFile) as cfg: + cfg.misc.minTraceLevel = 17000 + cfg.misc.maxTraceLevel = 18000 + with UpdateSession(): + cell = AllianceFramework.get().createCell( 'Rectilinear' ) + cell.setTerminalNetlist( True ) + cell.setAbutmentBox( Box( l(-5.0), l(-5.0), l(400.0), l(200.0) ) ) + #cell.setAbutmentBox( Box( l(-5.0), l(-5.0), l(21.0), l(35.0) ) ) + if editor: + editor.setCell( cell ) + editor.fit() + + technology = DataBase.getDB().getTechnology() + metal1 = technology.getLayer( "METAL1" ) + metal2 = technology.getLayer( "METAL2" ) + metal3 = technology.getLayer( "METAL3" ) + metal4 = technology.getLayer( "METAL4" ) + poly = technology.getLayer( "POLY" ) + ptrans = technology.getLayer( "PTRANS" ) + ntrans = technology.getLayer( "NTRANS" ) + pdif = technology.getLayer( "PDIF" ) + ndif = technology.getLayer( "NDIF" ) + contdifn = technology.getLayer( "CONT_DIF_N" ) + contdifp = technology.getLayer( "CONT_DIF_P" ) + nwell = technology.getLayer( "NWELL" ) + contpoly = technology.getLayer( "CONT_POLY" ) + ntie = technology.getLayer( "NTIE" ) + + with UpdateSession(): + net = Net.create( cell, 'my_net' ) + net.setExternal( True ) + + points = [ Point( l( 0.0), l( 0.0) ) + , Point( l( 0.0), l( 10.0) ) + , Point( l( 20.0), l( 30.0) ) + , Point( l( 30.0), l( 30.0) ) + , Point( l( 30.0), l( 20.0) ) + , Point( l( 10.0), l( 0.0) ) ] + r = Rectilinear.create( net, metal2, points ) + + #print( 'Normalized and manhattanized contour:' ) + #i = 0 + #for point in p.getMContour(): + # print( '| %d '%i, point, '[%fum %fum]' % ( u(point.getX()), u(point.getY()) )) + # i += 1 + + #points = [ Point( l( 0.0), l( 40.0) ) # 0 + # , Point( l( 30.0), l( 40.0) ) # 1 + # , Point( l( 30.0), l( 60.0) ) # 2 + # , Point( l( 50.0), l( 60.0) ) # 3 + # , Point( l( 50.0), l( 80.0) ) # 4 + # , Point( l( 90.0), l( 80.0) ) # 5 + # , Point( l( 90.0), l( 50.0) ) # 6 + # , Point( l( 60.0), l( 50.0) ) # 7 + # , Point( l( 60.0), l( 30.0) ) # 8 + # , Point( l( 70.0), l( 30.0) ) # 9 + # , Point( l( 70.0), l( 20.0) ) # 10 + # , Point( l( 90.0), l( 20.0) ) # 11 + # , Point( l( 90.0), l( 0.0) ) # 12 + # , Point( l( 20.0), l( 0.0) ) # 13 + # , Point( l( 20.0), l( 20.0) ) # 14 + # , Point( l( 0.0), l( 20.0) ) ] # 15 + points = [ Point( l( 0.0), l( 0.0) ) # 0 + , Point( l( 0.0), l( 20.0) ) # 1 + , Point( l( 10.0), l( 20.0) ) # 2 + , Point( l( 10.0), l( 30.0) ) # 3 + , Point( l( 20.0), l( 30.0) ) # 4 + , Point( l( 20.0), l( 40.0) ) # 5 + , Point( l( 40.0), l( 40.0) ) # 6 + , Point( l( 40.0), l( 80.0) ) # 7 + , Point( l( 20.0), l( 80.0) ) # 8 + , Point( l( 20.0), l( 70.0) ) # 9 + + , Point( l( 10.0), l( 70.0) ) # 10 + , Point( l( 10.0), l( 60.0) ) # 11 + , Point( l( 0.0), l( 60.0) ) # 12 + , Point( l( 0.0), l(120.0) ) # 13 + , Point( l( 10.0), l(120.0) ) # 14 + , Point( l( 10.0), l(110.0) ) # 15 + , Point( l( 20.0), l(110.0) ) # 16 + , Point( l( 20.0), l(100.0) ) # 17 + , Point( l( 40.0), l(100.0) ) # 18 + , Point( l( 40.0), l(140.0) ) # 19 + + , Point( l( 20.0), l(140.0) ) # 20 + , Point( l( 20.0), l(150.0) ) # 21 + , Point( l( 10.0), l(150.0) ) # 22 + , Point( l( 10.0), l(160.0) ) # 23 + , Point( l( 0.0), l(160.0) ) # 24 + , Point( l( 0.0), l(180.0) ) # 25 + , Point( l( 40.0), l(180.0) ) # 26 + , Point( l( 40.0), l(170.0) ) # 27 + , Point( l( 50.0), l(170.0) ) # 28 + , Point( l( 50.0), l(160.0) ) # 29 + + , Point( l(150.0), l(160.0) ) # 30 + , Point( l(150.0), l(150.0) ) # 31 + , Point( l(130.0), l(150.0) ) # 32 + , Point( l(130.0), l(140.0) ) # 33 + , Point( l(120.0), l(140.0) ) # 34 + , Point( l(120.0), l(130.0) ) # 35 + , Point( l(110.0), l(130.0) ) # 36 + , Point( l(110.0), l(110.0) ) # 37 + , Point( l(120.0), l(110.0) ) # 38 + , Point( l(120.0), l(100.0) ) # 39 + + , Point( l(130.0), l(100.0) ) # 40 + , Point( l(130.0), l( 90.0) ) # 41 + , Point( l(150.0), l( 90.0) ) # 42 + , Point( l(150.0), l( 80.0) ) # 43 + , Point( l(120.0), l( 80.0) ) # 44 + , Point( l(120.0), l( 70.0) ) # 45 + , Point( l(110.0), l( 70.0) ) # 46 + , Point( l(110.0), l( 50.0) ) # 47 + , Point( l(120.0), l( 50.0) ) # 48 + , Point( l(120.0), l( 40.0) ) # 49 + + , Point( l(130.0), l( 40.0) ) # 50 + , Point( l(130.0), l( 30.0) ) # 51 + , Point( l(150.0), l( 30.0) ) # 52 + , Point( l(150.0), l( 20.0) ) # 53 + , Point( l( 50.0), l( 20.0) ) # 54 + , Point( l( 50.0), l( 10.0) ) # 55 + , Point( l( 40.0), l( 10.0) ) # 56 + , Point( l( 40.0), l( 0.0) ) ] # 57 + r = Rectilinear.create( net, metal2, points ) + + boxes = [] + r.getAsRectangles( boxes ) + #print( 'boxes={}'.format( boxes )) + for box in boxes: + box.translate( l(180.0), l(0.0) ) + Pad.create( net, metal3, box ) + + Gds.save( cell ) + + +def scriptMain ( **kw ): + """The mandatory function to be called by Coriolis CGT/Unicorn.""" + editor = None + if 'editor' in kw and kw['editor']: + editor = kw['editor'] + testRectilinear( editor ) + return True