| /* 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 | |