c1aecc05e6
This patch extensively modifies DSENT so that it can be accessed using Python. To access the Python interface, DSENT needs to compiled as a shared library. For this purpose a CMakeLists.txt file has been added. Some of the code that is not required is being removed.
421 lines
19 KiB
C++
421 lines
19 KiB
C++
/* Copyright (c) 2012 Massachusetts Institute of Technology
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "model/electrical/BroadcastHTree.h"
|
|
|
|
#include <cmath>
|
|
#include <vector>
|
|
|
|
#include "model/PortInfo.h"
|
|
#include "model/EventInfo.h"
|
|
#include "model/TransitionInfo.h"
|
|
#include "model/std_cells/StdCellLib.h"
|
|
#include "model/std_cells/StdCell.h"
|
|
#include "model/timing_graph/ElectricalLoad.h"
|
|
#include "model/timing_graph/ElectricalDelay.h"
|
|
#include "model/timing_graph/ElectricalDriver.h"
|
|
#include "model/timing_graph/ElectricalTimingTree.h"
|
|
#include "model/timing_graph/ElectricalNet.h"
|
|
|
|
namespace DSENT
|
|
{
|
|
using std::pow;
|
|
using std::vector;
|
|
|
|
BroadcastHTree::BroadcastHTree(const String& instance_name_, const TechModel* tech_model_)
|
|
: ElectricalModel(instance_name_, tech_model_)
|
|
{
|
|
initParameters();
|
|
initProperties();
|
|
|
|
m_leaf_load_ = NULL;
|
|
m_leaf_head_driver_ = NULL;
|
|
m_leaf_head_load_ = NULL;
|
|
}
|
|
|
|
BroadcastHTree::~BroadcastHTree()
|
|
{
|
|
clearPtrVector<StdCell>(&m_repeaters_);
|
|
clearPtrVector<ElectricalLoad>(&m_repeater_loads_);
|
|
clearPtrVector<ElectricalTimingTree>(&m_timing_trees_);
|
|
clearPtrVector<StdCell>(&m_leaf_drivers_);
|
|
delete m_leaf_load_;
|
|
delete m_leaf_head_driver_;
|
|
delete m_leaf_head_load_;
|
|
}
|
|
|
|
void BroadcastHTree::initParameters()
|
|
{
|
|
addParameterName("NumberLevels");
|
|
addParameterName("NumberBits");
|
|
addParameterName("WireLayer");
|
|
addParameterName("WireWidthMultiplier", 1.0);
|
|
addParameterName("WireSpacingMultiplier", 1.0);
|
|
return;
|
|
}
|
|
|
|
void BroadcastHTree::initProperties()
|
|
{
|
|
addPropertyName("SitePitch");
|
|
addPropertyName("TotalLoadCapPerBit");
|
|
return;
|
|
}
|
|
|
|
BroadcastHTree* BroadcastHTree::clone() const
|
|
{
|
|
// TODO
|
|
return NULL;
|
|
}
|
|
|
|
void BroadcastHTree::constructModel()
|
|
{
|
|
// Get parameters
|
|
unsigned int number_levels = getParameter("NumberLevels").toUInt();
|
|
unsigned int number_bits = getParameter("NumberBits").toUInt();
|
|
const String& wire_layer = getParameter("WireLayer");
|
|
double wire_width_multiplier = getParameter("WireWidthMultiplier").toDouble();
|
|
double wire_spacing_multiplier = getParameter("WireSpacingMultiplier").toDouble();
|
|
|
|
ASSERT(number_levels > 0, "[Error] " + getInstanceName() +
|
|
" -> Number of levels must be > 0!");
|
|
ASSERT(number_bits > 0, "[Error] " + getInstanceName() +
|
|
" -> Number of bits must be > 0!");
|
|
ASSERT(getTechModel()->isWireLayerExist(wire_layer), "[Error] " + getInstanceName() +
|
|
" -> Wire layer does not exist!");
|
|
ASSERT(wire_width_multiplier >= 1.0, "[Error] " + getInstanceName() +
|
|
" -> Wire width multiplier must be >= 1.0!");
|
|
ASSERT(wire_spacing_multiplier >= 1.0, "[Error] " + getInstanceName() +
|
|
" -> Wire spacing multiplier must be >= 1.0!");
|
|
|
|
double wire_min_width = getTechModel()->get("Wire->" + wire_layer + "->MinWidth").toDouble();
|
|
double wire_min_spacing = getTechModel()->get("Wire->" + wire_layer + "->MinSpacing").toDouble();
|
|
|
|
double wire_width = wire_min_width * wire_width_multiplier;
|
|
double wire_spacing = wire_min_spacing * wire_spacing_multiplier;
|
|
|
|
double wire_cap_per_len = getTechModel()->calculateWireCapacitance(wire_layer, wire_width, wire_spacing, 1.0);
|
|
double wire_res_per_len = getTechModel()->calculateWireResistance(wire_layer, wire_width, 1.0);
|
|
|
|
getGenProperties()->set("WireWidth", wire_width);
|
|
getGenProperties()->set("WireSpacing", wire_spacing);
|
|
getGenProperties()->set("WireCapacitancePerLength", wire_cap_per_len);
|
|
getGenProperties()->set("WireResistancePerLength", wire_res_per_len);
|
|
|
|
// Create ports
|
|
createInputPort("In", makeNetIndex(0, number_bits-1));
|
|
createOutputPort("Out", makeNetIndex(0, number_bits-1));
|
|
|
|
// Create connections
|
|
createNet("InTmp");
|
|
createNet("OutTmp");
|
|
assignVirtualFanin("InTmp", "In");
|
|
assignVirtualFanout("Out", "OutTmp");
|
|
|
|
createLoad("In_Cap");
|
|
createDelay("In_to_Out_delay");
|
|
|
|
ElectricalLoad* in_cap = getLoad("In_Cap");
|
|
ElectricalDelay* in_to_out_delay = getDelay("In_to_Out_delay");
|
|
|
|
getNet("InTmp")->addDownstreamNode(in_cap);
|
|
in_cap->addDownstreamNode(in_to_out_delay);
|
|
|
|
// Init
|
|
for(unsigned int i = 0; i < number_levels; ++i)
|
|
{
|
|
StdCell* repeater = getTechModel()->getStdCellLib()->createStdCell("INV", "Repeater" + (String)i);
|
|
ElectricalLoad* repeater_load = new ElectricalLoad("RepeaterIn_Cap" + (String)i, this);
|
|
ElectricalTimingTree* timing_tree = new ElectricalTimingTree("RepeatedLink" + (String)i, this);
|
|
|
|
repeater->construct();
|
|
repeater->getNet("Y")->addDownstreamNode(repeater_load);
|
|
m_repeaters_.push_back(repeater);
|
|
m_repeater_loads_.push_back(repeater_load);
|
|
m_timing_trees_.push_back(timing_tree);
|
|
}
|
|
|
|
// Create area, power, and event results
|
|
createElectricalAtomicResults();
|
|
createElectricalEventResult("Send");
|
|
addEventResult(new AtomicResult("DriveLoad"));
|
|
addEventResult(new AtomicResult("DriveTree"));
|
|
|
|
getEventResult("Send")->addSubResult(getEventResult("DriveLoad"), "Self", 1.0);
|
|
getEventResult("Send")->addSubResult(getEventResult("DriveTree"), "Self", 1.0);
|
|
return;
|
|
}
|
|
|
|
void BroadcastHTree::updateModel()
|
|
{
|
|
// Get properties
|
|
double site_pitch = getProperty("SitePitch").toDouble();
|
|
double total_load_cap_per_bit = getProperty("TotalLoadCapPerBit").toDouble();
|
|
|
|
ASSERT(site_pitch > 0, "[Error] " + getInstanceName() +
|
|
" -> Site pitch must be > 0!");
|
|
ASSERT(total_load_cap_per_bit >= 0.0, "[Error] " + getInstanceName() +
|
|
" -> Total load capacitance per bit must be >= 0!");
|
|
|
|
// Get parameters
|
|
unsigned int number_levels = getParameter("NumberLevels");
|
|
unsigned int number_bits = getParameter("NumberBits");
|
|
|
|
const String& wire_layer = getParameter("WireLayer");
|
|
double wire_width = getGenProperties()->get("WireWidth").toDouble();
|
|
double wire_spacing = getGenProperties()->get("WireSpacing").toDouble();
|
|
double wire_cap_per_len = getGenProperties()->get("WireCapacitancePerLength").toDouble();
|
|
double wire_res_per_len = getGenProperties()->get("WireResistancePerLength").toDouble();
|
|
|
|
double leaf_load_cap = total_load_cap_per_bit / pow(2.0, (double)(number_levels-1));
|
|
|
|
vector<double> wire_caps(number_levels, 0.0);
|
|
vector<double> wire_ress(number_levels, 0.0);
|
|
double wire_length = site_pitch / 2.0;
|
|
for(unsigned int i = 0; i < number_levels; ++i)
|
|
{
|
|
wire_caps[i] = wire_cap_per_len * wire_length;
|
|
wire_ress[i] = wire_res_per_len * wire_length;
|
|
wire_length /= 2.0;
|
|
}
|
|
|
|
// Start sizing each stage of repeaters for a transition times. TODO: Find a heuristic about
|
|
// how the transition time is done...place and route tools make this user-specified
|
|
double required_transition = 40e-12;
|
|
m_number_segments_.resize(number_levels, 1);
|
|
for(unsigned int i = 0; i < number_levels; ++i)
|
|
{
|
|
Log::printLine(getInstanceName() + " -> Beginning Repeater Insertion " + (String)i);
|
|
|
|
double transition;
|
|
unsigned int iteration = 0;
|
|
m_repeaters_[i]->setMinDrivingStrength();
|
|
m_repeaters_[i]->getNet("Y")->setDistributedCap(wire_caps[i] / m_number_segments_[i]);
|
|
m_repeaters_[i]->getNet("Y")->setDistributedRes(wire_ress[i] / m_number_segments_[i]);
|
|
m_repeater_loads_[i]->setLoadCap(m_repeaters_[i]->getNet("A")->getTotalDownstreamCap());
|
|
|
|
transition = m_timing_trees_[i]->calculateNodeTransition(m_repeaters_[i]->getNet("Y"));
|
|
|
|
while(required_transition < transition)
|
|
{
|
|
Log::printLine(getInstanceName() + " -> Repeater Insertion Iteration " + (String)iteration +
|
|
": Required transition = " + (String)required_transition +
|
|
", Transition = " + (String)transition +
|
|
", Slack = " + (String)(required_transition - transition) +
|
|
", Number of repeaters = " + (String)m_number_segments_[i]);
|
|
|
|
// Size up if transition is not met
|
|
while(required_transition < transition)
|
|
{
|
|
if(m_repeaters_[i]->hasMaxDrivingStrength())
|
|
{
|
|
break;
|
|
}
|
|
m_repeaters_[i]->increaseDrivingStrength();
|
|
m_repeater_loads_[i]->setLoadCap(m_repeaters_[i]->getNet("A")->getTotalDownstreamCap());
|
|
transition = m_timing_trees_[i]->calculateNodeTransition(m_repeaters_[i]->getNet("Y"));
|
|
|
|
iteration++;
|
|
Log::printLine(getInstanceName() + " -> Slack: " + (String)(required_transition - transition));
|
|
}
|
|
// Increase number of segments if thansition is not met
|
|
if(required_transition < transition)
|
|
{
|
|
m_number_segments_[i]++;
|
|
m_repeaters_[i]->setMinDrivingStrength();
|
|
m_repeaters_[i]->getNet("Y")->setDistributedCap(wire_caps[i] / m_number_segments_[i]);
|
|
m_repeaters_[i]->getNet("Y")->setDistributedRes(wire_ress[i] / m_number_segments_[i]);
|
|
m_repeater_loads_[i]->setLoadCap(m_repeaters_[i]->getNet("A")->getTotalDownstreamCap());
|
|
transition = m_timing_trees_[i]->calculateNodeTransition(m_repeaters_[i]->getNet("Y"));
|
|
}
|
|
}
|
|
Log::printLine(getInstanceName() + " -> Repeater Insertion " + (String)i + " Ended after Iteration: " + (String)iteration +
|
|
": Required transition = " + (String)required_transition +
|
|
", Transition = " + (String)transition +
|
|
", Slack = " + (String)(required_transition - transition) +
|
|
", Number of repeaters = " + (String)m_number_segments_[i]);
|
|
}
|
|
|
|
// Insert inverters to ensure the transition time at the leaf
|
|
int min_driving_strength_idx = m_repeaters_[number_levels-1]->getDrivingStrengthIdx();
|
|
|
|
// Remove everything and rebuild again
|
|
clearPtrVector<StdCell>(&m_leaf_drivers_);
|
|
delete m_leaf_load_;
|
|
delete m_leaf_head_driver_;
|
|
delete m_leaf_head_load_;
|
|
|
|
m_leaf_head_driver_ = getTechModel()->getStdCellLib()->createStdCell("INV", "LeafHeadDriver");
|
|
m_leaf_head_driver_->construct();
|
|
m_leaf_head_driver_->setDrivingStrengthIdx(min_driving_strength_idx);
|
|
|
|
m_leaf_head_load_ = new ElectricalLoad("LeafHead_Cap", this);
|
|
m_leaf_head_driver_->getNet("Y")->addDownstreamNode(m_leaf_head_load_);
|
|
|
|
m_leaf_load_ = new ElectricalLoad("Leaf_Cap", this);
|
|
m_leaf_load_->setLoadCap(leaf_load_cap);
|
|
|
|
StdCell* inv = getTechModel()->getStdCellLib()->createStdCell("INV", "LeafDriver0");
|
|
inv->construct();
|
|
inv->getNet("Y")->addDownstreamNode(m_leaf_load_);
|
|
inv->setDrivingStrengthIdx(min_driving_strength_idx);
|
|
m_leaf_drivers_.push_back(inv);
|
|
|
|
m_leaf_head_load_->setLoadCap(m_leaf_drivers_[0]->getNet("A")->getTotalDownstreamCap());
|
|
|
|
// Start inserting the buffers
|
|
ElectricalTimingTree t2("LeafHead", m_leaf_head_driver_);
|
|
int curr_driver = 0;
|
|
unsigned int iteration = 0;
|
|
while(true)
|
|
{
|
|
ElectricalTimingTree t("LeafDriver", m_leaf_drivers_[curr_driver]);
|
|
double transition = t.calculateNodeTransition(m_leaf_drivers_[curr_driver]->getNet("Y"));
|
|
Log::printLine(getInstanceName() + " -> Buffer Insertion : " + (String)iteration +
|
|
": Required transition = " + (String)required_transition +
|
|
", Transition = " + (String)transition +
|
|
", Slack = " + (String)(required_transition - transition) +
|
|
", Number of buffers = " + (String)(curr_driver+1));
|
|
|
|
// Size up the inverter at curr_driver so that it could drive the next stage
|
|
while(required_transition < transition)
|
|
{
|
|
if(m_leaf_drivers_[curr_driver]->hasMaxDrivingStrength())
|
|
{
|
|
const String& warning_msg = "[Warning] " + getInstanceName() + " -> Transition not met" +
|
|
": Required transition = " + (String)required_transition +
|
|
", Transition = " + (String)transition +
|
|
", Slack = " + (String)(required_transition - transition);
|
|
Log::printLine(std::cerr, warning_msg);
|
|
break;
|
|
}
|
|
m_leaf_drivers_[curr_driver]->increaseDrivingStrength();
|
|
transition = t.calculateNodeTransition(m_leaf_drivers_[curr_driver]->getNet("Y"));
|
|
iteration++;
|
|
}
|
|
// Add an additional inverter if the transition for the first stage does not meet the required transition
|
|
m_leaf_head_load_->setLoadCap(m_leaf_drivers_[curr_driver]->getNet("A")->getTotalDownstreamCap());
|
|
transition = t2.calculateNodeTransition(m_leaf_head_driver_->getNet("Y"));
|
|
if(required_transition < transition)
|
|
{
|
|
inv = getTechModel()->getStdCellLib()->createStdCell("INV", "LeafDriver" + (String)(curr_driver+1));
|
|
inv->construct();
|
|
inv->getNet("Y")->addDownstreamNode(m_leaf_drivers_[curr_driver]->getNet("A"));
|
|
inv->setDrivingStrengthIdx(min_driving_strength_idx);
|
|
m_leaf_drivers_.push_back(inv);
|
|
curr_driver++;
|
|
}
|
|
else
|
|
{
|
|
Log::printLine(getInstanceName() + " -> Buffer Insertion Ended after Iteration: " + (String)iteration +
|
|
", Number of buffers = " + (String)(curr_driver+1));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Update electrical interfaces
|
|
getLoad("In_Cap")->setLoadCap(m_repeaters_[0]->getNet("A")->getTotalDownstreamCap());
|
|
// TODO
|
|
getDelay("In_to_Out_delay")->setDelay(0.0);
|
|
|
|
// Reset all the atomic results to 0 before start updating new results
|
|
resetElectricalAtomicResults();
|
|
|
|
// Update area, power results
|
|
double wire_area = 0.0;
|
|
wire_length = site_pitch / 2.0;
|
|
unsigned int number_branches = 1;
|
|
for(unsigned int i = 0; i < number_levels; ++i)
|
|
{
|
|
wire_area += wire_length * (wire_width + wire_spacing) * number_branches * number_bits;
|
|
addElecticalAtomicResultValues(m_repeaters_[i], m_number_segments_[i] * number_branches * number_bits);
|
|
wire_length /= 2.0;
|
|
number_branches *= 2;
|
|
}
|
|
number_branches = (unsigned int)pow(2.0, (double)number_levels-1);
|
|
addElecticalAtomicResultValues(m_leaf_head_driver_, number_branches * number_bits);
|
|
for(unsigned int i = 0; i < m_leaf_drivers_.size(); ++i)
|
|
{
|
|
addElecticalAtomicResultValues(m_leaf_drivers_[i], number_branches * number_bits);
|
|
}
|
|
addElecticalWireAtomicResultValue(wire_layer, wire_area);
|
|
|
|
return;
|
|
}
|
|
|
|
void BroadcastHTree::useModel()
|
|
{
|
|
unsigned int number_bits = getParameter("NumberBits").toUInt();
|
|
unsigned int number_levels = getParameter("NumberLevels").toUInt();
|
|
|
|
// Update the transition information for the modeled repeaters
|
|
// Since we only modeled one repeater. So the transition information for 0->0 and 1->1
|
|
// is averaged out
|
|
const TransitionInfo& trans_In = getInputPort("In")->getTransitionInfo();
|
|
double average_static_transition = (trans_In.getNumberTransitions00() + trans_In.getNumberTransitions11()) / 2.0;
|
|
TransitionInfo mod_trans_In(average_static_transition, trans_In.getNumberTransitions01(), average_static_transition);
|
|
|
|
// Propagate the transition information
|
|
propagateTransitionInfo();
|
|
|
|
// Update leakage and event
|
|
double energy = 0.0;
|
|
double power = 0.0;
|
|
unsigned int number_branches = 1;
|
|
for(unsigned int i = 0; i < number_levels; ++i)
|
|
{
|
|
assignPortTransitionInfo(m_repeaters_[i], "A", mod_trans_In);
|
|
m_repeaters_[i]->use();
|
|
power += m_repeaters_[i]->getNddPowerResult("Leakage")->calculateSum() * m_number_segments_[i] * number_branches;
|
|
energy += m_repeaters_[i]->getEventResult("INV")->calculateSum() * m_number_segments_[i] * number_branches;
|
|
number_branches *= 2;
|
|
}
|
|
energy *= number_bits;
|
|
getEventResult("DriveTree")->setValue(energy);
|
|
|
|
energy = 0.0;
|
|
assignPortTransitionInfo(m_leaf_head_driver_, "A", mod_trans_In);
|
|
m_leaf_head_driver_->use();
|
|
number_branches = (unsigned int)pow(2.0, (double)number_levels-1);
|
|
power += m_leaf_head_driver_->getNddPowerResult("Leakage")->calculateSum() * number_branches;
|
|
energy += m_leaf_head_driver_->getEventResult("INV")->calculateSum() * number_branches;
|
|
for(unsigned int i = 0; i < m_leaf_drivers_.size(); ++i)
|
|
{
|
|
assignPortTransitionInfo(m_leaf_drivers_[i], "A", mod_trans_In);
|
|
m_leaf_drivers_[i]->use();
|
|
power += m_leaf_drivers_[i]->getNddPowerResult("Leakage")->calculateSum() * number_branches;
|
|
energy += m_leaf_drivers_[i]->getEventResult("INV")->calculateSum() * number_branches;
|
|
}
|
|
power *= number_bits;
|
|
energy *= number_bits;
|
|
getEventResult("DriveLoad")->setValue(energy);
|
|
getNddPowerResult("Leakage")->setValue(power);
|
|
|
|
return;
|
|
}
|
|
|
|
void BroadcastHTree::propagateTransitionInfo()
|
|
{
|
|
propagatePortTransitionInfo("Out", "In");
|
|
return;
|
|
}
|
|
} // namespace DSENT
|
|
|