coriolis/bora/src/SlicingPlotWidget.cpp

412 lines
15 KiB
C++
Raw Normal View History

Analog integration part II. Analog place & route (slicing tree). * Change: In Hurricane::CellWidget, set the minimal size to 350 pixels to fit my normal DPI secondary screen... * Change: In Hurricane::Error(), reactivate the backtrace generation by default. Seriously slow down the program each time an Error is to be constructed. * Bug: In Analog::Device::preCreate(), check for NULL Technology before attempting to use it. * Change: In Hurricane/Analog, remove all '*Arguments*' classes and their Python interface. It was an obsoleted way of passing devices parameters to the Python layout generators (located in Oroshi). Now we just get them straight from the Device with the getParamter() method. * Change: In CRL::System CTOR, add Python pathes for Oroshi & Karakaze. * Change: In Oroshi/Python/WIP_*.py layout generator scripts, remove all uses of the "Arguments". Directly access the parameters through the device itself. Make the checkCoherency() with identical arguments as of layout(). * New: Bora tool that performs analog place & route. Based on a slicing tree representation. It is the thesis work of Eric Lao. Code beautyfication and some programming cleanup. * New: Karakaze tool, provide the Python base class AnalogDesign used to build an analog design. Create/configure devices and assemble them in a slicing tree. * Change: In Unicorn/cgt.py, display the stack trace in case of an ImportError exception as well as for other exceptions. Add Bora to the set for included tool engines.
2018-10-18 11:10:01 -05:00
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2015-2018, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | C O R I O L I S |
// | B o r a - A n a l o g S l i c i n g T r e e |
// | |
// | Authors : Jean-Paul Chaput, Eric LAO |
// | E-mail : Jean-Paul.Chaput@lip6.fr |
// | =============================================================== |
// | C++ Module : "./SlicingPlotWidget.cpp" |
// +-----------------------------------------------------------------+
#include <QColor>
#include <QPen>
#include <QPalette>
#include <QPoint>
#include <QFrame>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <qwt_scale_draw.h>
#include <qwt_scale_widget.h>
#include <qwt_symbol.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_picker.h>
#include <qwt_legend.h>
#include <math.h>
#include "hurricane/Error.h"
#include "hurricane/DbU.h"
#include "hurricane/viewer/Graphics.h"
#include "hurricane/viewer/CellViewer.h"
#include "hurricane/viewer/DynamicLabel.h"
#include "hurricane/analog/AnalogCellExtension.h"
#include "hurricane/analog/Device.h"
#include "hurricane/analog/TransistorFamily.h"
#include "crlcore/Utilities.h"
#include "bora/SlicingPlotWidget.h"
namespace {
class SlicingScaleDraw : public QwtScaleDraw {
protected:
virtual QwtText label ( double value ) const;
};
QwtText SlicingScaleDraw::label ( double value ) const
{ return QString("%1").arg( value, 0, 'f', 1 ); }
} // Anonymous namespace.
namespace Bora {
using namespace std;
using Hurricane::tab;
using Hurricane::Error;
using Hurricane::DbU;
using Hurricane::DynamicLabel;
using Hurricane::Graphics;
using Analog::AnalogCellExtension;
using Analog::Device;
using Analog::TransistorFamily;
SlicingPlotWidget::SlicingPlotWidget ( QWidget* parent )
: QWidget (parent)
, _viewer (NULL)
, _plot (new QwtPlot ())
, _picker (new QwtPlotPicker( QwtPlot::xBottom // X Axis id.
, QwtPlot::yLeft // Y Axis id.
, QwtPicker::CrossRubberBand
, QwtPicker::ActiveOnly
, _plot->canvas()) )
, _gridDisplay (new QGridLayout())
, _gridLabel () // Label line 2: text
, _widths (NULL) // Coordinates X for STreeCurve
, _heights (NULL) // Coordinates Y for STreeCurve
, _widthSelected (NULL) // Coordinate X for selectedPoint
, _heightSelected(NULL) // Coordinate Y for selectedPoint
, _pareto ()
, _STreeCurve (new QwtPlotCurve("SlicingTree's dimensions")) // Blue dots : possible dimensions
, _paretoCurve (new QwtPlotCurve("Pareto")) // Black curve: pareto curve
, _selectedPoint (new QwtPlotCurve("Selected")) // Red dot : selected placement
{
_picker->setStateMachine(new QwtPickerClickPointMachine());
setStyleSheet ( "border: 0px" );
int ptSize = Graphics::isHighDpi() ? 5 : 3;
QwtText xTitle ( QString::fromUtf8("Width (µm)") );
QwtText yTitle ( QString::fromUtf8("Height (µm)") );
xTitle.setFont( Graphics::getFixedFont(QFont::Bold,false,false,-1) );
yTitle.setFont( Graphics::getFixedFont(QFont::Bold,false,false,-1) );
_plot->setAxisTitle ( QwtPlot::xBottom, xTitle );
_plot->setAxisTitle ( QwtPlot::yLeft , yTitle );
_plot->insertLegend ( new QwtLegend(), QwtPlot::BottomLegend );
_plot->setAxisFont ( QwtPlot::xBottom, Graphics::getFixedFont(QFont::Normal,false,false,-2) );
_plot->setAxisFont ( QwtPlot::yLeft , Graphics::getFixedFont(QFont::Normal,false,false,-2) );
QString styleSheet("color: black; background:rgb(255,255,221); padding: 5px");
_plot->setStyleSheet(styleSheet);
_plot->canvas()->setStyleSheet(styleSheet);
_plot->legend()->setStyleSheet( QString("padding: 0px") );
QPen paretoPen ( Qt::green );
paretoPen.setWidth( Graphics::isHighDpi() ? 3 : 1 );
_paretoCurve->setStyle( QwtPlotCurve::Lines );
_paretoCurve->setPen ( paretoPen );
_paretoCurve->attach ( _plot );
QPen dotPen ( Qt::blue );
dotPen.setWidth( ptSize );
_STreeCurve->setStyle( QwtPlotCurve::Dots );
_STreeCurve->setPen ( dotPen );
_STreeCurve->attach ( _plot );
QwtSymbol* symbol = new QwtSymbol();
symbol->setStyle( QwtSymbol::Cross );
symbol->setSize ( 20 );
symbol->setPen ( dotPen );
_STreeCurve->setSymbol( symbol );
QPen selectPen ( Qt::red );
selectPen.setWidth( ptSize+2 );
_selectedPoint->setStyle( QwtPlotCurve::Dots );
_selectedPoint->setPen ( selectPen );
_selectedPoint->attach ( _plot );
connect( _picker, SIGNAL(selected(const QPointF&)), this, SLOT(onPointSelect(const QPointF&)) );
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->addWidget ( _plot );
QHBoxLayout* line = new QHBoxLayout ();
line->addStretch();
DynamicLabel* dlabel = new DynamicLabel();
dlabel->setStaticText ( QString::fromUtf8("Width (µm):") );
dlabel->setDynamicWidth( 4, DynamicLabel::NoIndent );
dlabel->setDynamicText( QString("%1").arg( "0" ) );
_gridLabel.push_back( dlabel );
line->addWidget( dlabel );
line->addStretch();
dlabel = new DynamicLabel();
dlabel->setStaticText ( QString::fromUtf8("Height (µm):") );
dlabel->setDynamicWidth( 4, DynamicLabel::NoIndent );
dlabel->setDynamicText( QString("%1").arg( "0" ) );
_gridLabel.push_back( dlabel );
line->addWidget( dlabel );
line->addStretch();
vLayout->addLayout( line );
line = new QHBoxLayout ();
line->addStretch();
dlabel = new DynamicLabel();
dlabel->setStaticText ( "Occupation Area (%):" );
dlabel->setDynamicWidth( 4, DynamicLabel::NoIndent );
dlabel->setDynamicText( QString("%1").arg( "100" ) );
_gridLabel.push_back( dlabel );
line->addWidget( dlabel );
line->addStretch();
dlabel = new DynamicLabel();
dlabel->setStaticText ( "Ratio(W/H):" );
dlabel->setDynamicWidth( 4, DynamicLabel::NoIndent );
dlabel->setDynamicText( QString("%1").arg( "0" ) );
_gridLabel.push_back( dlabel );
line->addWidget( dlabel );
line->addStretch();
dlabel = new DynamicLabel();
dlabel->setStaticText ( QString::fromUtf8("Area (µm²):") );
dlabel->setDynamicWidth( 4, DynamicLabel::NoIndent );
dlabel->setDynamicText( QString("%1").arg( "0" ) );
_gridLabel.push_back( dlabel );
line->addWidget( dlabel );
line->addStretch();
vLayout->addLayout( line );
QFrame* separator = new QFrame ();
separator->setFrameShape ( QFrame::HLine );
separator->setFrameShadow ( QFrame::Sunken );
vLayout->addWidget ( separator );
vLayout->addLayout ( _gridDisplay );
setLayout( vLayout );
}
SlicingPlotWidget::~SlicingPlotWidget ()
{
delete _STreeCurve;
delete _paretoCurve;
delete _selectedPoint;
delete _plot;
if ( _widths != NULL ) delete [] _widths;
if ( _heights != NULL ) delete [] _heights;
}
void SlicingPlotWidget::setViewer ( CellViewer* viewer )
{
if (_viewer) {
disconnect( _viewer, 0, this , 0 );
disconnect( this , 0, _viewer, 0 );
}
_viewer = viewer;
setDatas();
}
void SlicingPlotWidget::setDatas ()
{
// Clear previous datas
if (_widths != NULL) delete [] _widths;
if (_heights != NULL) delete [] _heights;
_pareto = Pareto();
for ( size_t igrid = 5 ; igrid < _gridLabel.size() ; ++igrid )
delete _gridLabel[igrid];
_gridLabel.resize( 5 );
while ( _gridDisplay->count() ) {
_gridDisplay->removeItem( _gridDisplay->itemAt(0) );
}
if (_widthSelected != NULL) delete [] _widthSelected;
if (_heightSelected != NULL) delete [] _heightSelected;
_selectedPoint->hide();
SlicingNode* slicingtree = AnalogCellExtension::get<SlicingNode>( _viewer->getCellWidget()->getCell() );
if (not slicingtree) {
_STreeCurve->hide();
_paretoCurve->hide();
_selectedPoint->hide();
return;
}
// Set new datas
_widths = new double [ slicingtree->getNodeSets()->size() ];
_heights = new double [ slicingtree->getNodeSets()->size() ];
size_t i = 0;
NodeSets* nodeSets = slicingtree->getNodeSets();
for (vector<BoxSet*>::iterator it = nodeSets->begin(); it != nodeSets->end(); it++){
_widths [i] = DbU::toPhysical ( (*it)->getWidth (), DbU::Micro );
_heights[i] = DbU::toPhysical ( (*it)->getHeight(), DbU::Micro );
_pareto.mergePoint( _widths[i], _heights[i] );
i++;
}
_STreeCurve->setSamples ( _widths , _heights , nodeSets->size() );
_paretoCurve->setSamples( _pareto.xs(), _pareto.ys(), _pareto.size() );
_STreeCurve->show();
_paretoCurve->show();
_plot->replot();
int ii = 0;
int jj = 0;
Cell* analogCell = _viewer->getCellWidget()->getCell();
vector<Instance*> instances = vector<Instance*>();
for( Instance* instance : analogCell->getInstances() ) {
Cell* model = instance->getMasterCell();
Device* device = dynamic_cast<Device*>( model );
if (device) instances.push_back( instance );
}
for( Instance* instance : instances ) {
Cell* model = instance->getMasterCell();
Device* device = dynamic_cast<Device*>( model );
if (device) {
TransistorFamily* tf = dynamic_cast<TransistorFamily*>( device );
if (tf) {
DynamicLabel* dlabel = new DynamicLabel();
dlabel->setDynamicWidth( 4, DynamicLabel::NoIndent );
dlabel->setStaticText ( QString("%1:").arg( instance->getName()._getString().c_str() ) );
dlabel->setDynamicText ( QString("%1" ).arg( 0 ) );
_gridDisplay->addWidget( dlabel, ii, jj );
_gridLabel.push_back( dlabel );
jj++;
if (jj == 5){
jj = 0;
ii++;
}
}
}
}
}
void SlicingPlotWidget::onPointSelect ( const QPointF& point )
{
// Clicking on SlicingTree's Pareto Graph:
// 1) Update slicing tree
// 2) Update selected point and labels
// 3) Update selected row
double distance = 0.0;
QwtScaleMap xMap = _plot->canvasMap( QwtPlot::xBottom );
QwtScaleMap yMap = _plot->canvasMap( QwtPlot::yLeft );
QPoint qpoint ( xMap.transform(point.x()), yMap.transform(point.y()) );
int iclosest = _STreeCurve->closestPoint( qpoint, &distance );
int dataSize = _STreeCurve->dataSize();
cdebug.log(539) << " Selection: [" << point.x() << " " << point.y() << "]" << endl;
if ( (iclosest >= 0) and (iclosest < dataSize) ) {
double x = _STreeCurve->sample( iclosest ).x();
double y = _STreeCurve->sample( iclosest ).y();
ostringstream message;
message << "(" << DbU::getValueString(x) << "," << DbU::getValueString(y) << ")";
cmess2 << " o Selecting analog placement" << endl;
cmess2 << Dots::asInt ( " - Index" , iclosest ) << endl;
cmess2 << Dots::asString( " - Size (w,h)", message.str() ) << endl;
SlicingNode* slicingtree = AnalogCellExtension::get<SlicingNode>( _viewer->getCellWidget()->getCell() );
BoxSet* boxSet = slicingtree->getPairHW( DbU::fromPhysical(y, DbU::Micro)
, DbU::fromPhysical(x, DbU::Micro)
);
cdebug.log(539) << " iclosest: " << iclosest
<< " (" << DbU::getValueString(DbU::fromPhysical(x, DbU::Micro))
<< "," << DbU::getValueString(DbU::fromPhysical(y, DbU::Micro))
<< ")" << endl;
emit updatePlacement( boxSet );
updateSelectedPoint ( x, y );
emit updateSelectedRow( x, y );
} else {
cdebug.log(539) << " No closest point " << iclosest << endl;
}
}
void SlicingPlotWidget::updateSelectedPoint ( double x, double y )
{
// Clicking on SlicingTree's possible dimensions table:
// 1) Update selected point
// 2) Update labels
if (_widthSelected != NULL) delete [] _widthSelected;
if (_heightSelected != NULL) delete [] _heightSelected;
double* _widthSelected = new double [ 1 ];
double* _heightSelected = new double [ 1 ];
_widthSelected [0] = x;
_heightSelected[0] = y;
_selectedPoint->setSamples ( _widthSelected, _heightSelected, 1 );
_selectedPoint->show();
_plot->replot();
SlicingNode* slicingtree = AnalogCellExtension::get<SlicingNode>( _viewer->getCellWidget()->getCell() );
_gridLabel[0]->setDynamicText( QString("%1").arg( DbU::getValueString(slicingtree->getWidth ()).c_str() ) );
_gridLabel[1]->setDynamicText( QString("%1").arg( DbU::getValueString(slicingtree->getHeight()).c_str() ) );
_gridLabel[2]->setDynamicText( QString("%1").arg( slicingtree->getOccupationArea() ));
if ( slicingtree->getHeight() != 0 ){
_gridLabel[3]->setDynamicText( QString("%1").arg( DbU::getPhysical( slicingtree->getWidth (), DbU::Micro )
/ DbU::getPhysical( slicingtree->getHeight(), DbU::Micro )
));
} else _gridLabel[3]->setDynamicText( QString("%1").arg(0));
_gridLabel[4]->setDynamicText( QString("%1").arg( DbU::getPhysical( slicingtree->getWidth (), DbU::Micro )
* DbU::getPhysical( slicingtree->getHeight(), DbU::Micro )
));
Cell* analogCell = _viewer->getCellWidget()->getCell();
size_t i = 0;
vector<Instance*> instances;
for( Instance* iInstance : analogCell->getInstances() ) {
Cell* model = iInstance->getMasterCell();
Device* device = dynamic_cast<Device*>(model);
if (device) instances.push_back(iInstance);
}
for( Instance* iInstance : instances ) {
Cell* model = iInstance->getMasterCell();
Device* device = dynamic_cast<Device*>(model);
if (device) {
TransistorFamily* tf = dynamic_cast<TransistorFamily*>( device );
if (tf) {
_gridLabel[i+5]->setDynamicText ( QString("%1" ).arg( tf->getNfing() ) );
i++;
}
Analog integration part II. Analog place & route (slicing tree). * Change: In Hurricane::CellWidget, set the minimal size to 350 pixels to fit my normal DPI secondary screen... * Change: In Hurricane::Error(), reactivate the backtrace generation by default. Seriously slow down the program each time an Error is to be constructed. * Bug: In Analog::Device::preCreate(), check for NULL Technology before attempting to use it. * Change: In Hurricane/Analog, remove all '*Arguments*' classes and their Python interface. It was an obsoleted way of passing devices parameters to the Python layout generators (located in Oroshi). Now we just get them straight from the Device with the getParamter() method. * Change: In CRL::System CTOR, add Python pathes for Oroshi & Karakaze. * Change: In Oroshi/Python/WIP_*.py layout generator scripts, remove all uses of the "Arguments". Directly access the parameters through the device itself. Make the checkCoherency() with identical arguments as of layout(). * New: Bora tool that performs analog place & route. Based on a slicing tree representation. It is the thesis work of Eric Lao. Code beautyfication and some programming cleanup. * New: Karakaze tool, provide the Python base class AnalogDesign used to build an analog design. Create/configure devices and assemble them in a slicing tree. * Change: In Unicorn/cgt.py, display the stack trace in case of an ImportError exception as well as for other exceptions. Add Bora to the set for included tool engines.
2018-10-18 11:10:01 -05:00
}
}
}
} // Bora namespace.