blob: 010e458d88586f1f7d4a72e159db321a102d4726 [file] [log] [blame]
/* 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/optical/RingDetector.h"
#include <cmath>
#include "util/Constants.h"
#include "model/PortInfo.h"
#include "model/TransitionInfo.h"
#include "model/EventInfo.h"
#include "model/std_cells/StdCell.h"
#include "model/std_cells/StdCellLib.h"
#include "model/optical_graph/OpticalWaveguide.h"
#include "model/optical_graph/OpticalDetector.h"
#include "model/optical_graph/OpticalFilter.h"
#include "model/timing_graph/ElectricalDriver.h"
#include "model/timing_graph/ElectricalNet.h"
namespace DSENT
{
// TODOs for this model
// Add the other receiver topologies from [Georgas, CICC 2011]
// Split integ_time_ratio = SA integ time ratio
// Right now perfect clock gating is assumed...may not be what we want
// Constants
const String RingDetector::INTEGRATINGSENSEAMP = "INTSA";
RingDetector::RingDetector(const String& instance_name_, const TechModel* tech_model_)
: OpticalModel(instance_name_, tech_model_), OpticalReceiver()
{
initParameters();
initProperties();
}
RingDetector::~RingDetector()
{}
void RingDetector::initParameters()
{
addParameterName("DataRate");
addParameterName("InStart");
addParameterName("InEnd");
addParameterName("DetStart");
addParameterName("DetEnd");
addParameterName("DropAll");
addParameterName("Topology");
return;
}
void RingDetector::initProperties()
{
return;
}
void RingDetector::constructModel()
{
// Get parameters
WavelengthGroup in_wavelengths = makeWavelengthGroup(getParameter("InStart"), getParameter("InEnd"));
WavelengthGroup det_wavelengths = makeWavelengthGroup(getParameter("DetStart"), getParameter("DetEnd"));
int number_wavelengths = det_wavelengths.second - det_wavelengths.first + 1;
bool drop_all = getParameter("DropAll");
const String& topology = getParameter("Topology");
// Set some generated properties
getGenProperties()->set("NumberWavelengths", number_wavelengths);
// Create device area result
addAreaResult(new AtomicResult("Photonic"));
// Create electrical results
createElectricalAtomicResults();
if (topology == INTEGRATINGSENSEAMP) addEventResult(new AtomicResult("Receive"));
else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
// Create optical ports
createOpticalInputPort( "In", in_wavelengths);
createOpticalOutputPort( "Out", in_wavelengths);
// Create the filter and modulator
createFilter( "RingFilter", in_wavelengths, drop_all, det_wavelengths);
createDetector( "RingDetector", det_wavelengths, this);
OpticalFilter* ring_filter = getFilter("RingFilter");
OpticalDetector* ring_detector = getDetector("RingDetector");
// Connect the filter and modulator
getWaveguide("In")->addDownstreamNode(ring_filter);
ring_filter->addDownstreamNode(getWaveguide("Out"));
ring_filter->setDropPort(ring_detector);
// Create electrical ports
createOutputPort("Out", makeNetIndex(0, number_wavelengths-1));
// Create net
createNet("OutVFO");
// Create output driver
createDriver("OutDriver", false);
// Connect driver
getDriver("OutDriver")->addDownstreamNode(getNet("OutVFO"));
// Connect output
assignVirtualFanout("Out", "OutVFO");
// Precompute some technology values
precomputeTech();
return;
}
void RingDetector::updateModel()
{
// Get some generated properties
unsigned int number_wavelengths = getGenProperties()->get("NumberWavelengths");
// Get tech model numbers
double ring_area = getTechModel()->get("Ring->Area");
double thru_loss = getTechModel()->get("Ring->ThroughLoss");
double drop_loss = getTechModel()->get("Ring->DropLoss");
double pd_loss = getTechModel()->get("Photodetector->Loss");
double pd_responsivity = getTechModel()->get("Photodetector->Responsivity");
// Design the receiver
designReceiver();
// Update losses
// Connect the filter and modulator
OpticalFilter* ring_filter = getFilter("RingFilter");
OpticalDetector* ring_detector = getDetector("RingDetector");
ring_filter->setLoss(thru_loss * number_wavelengths);
ring_filter->setDropLoss(drop_loss + thru_loss * number_wavelengths);
ring_detector->setLoss(pd_loss);
ring_detector->setResponsivity(pd_responsivity);
// Update device area
getAreaResult("Photonic")->setValue(ring_area * (number_wavelengths));
return;
}
void RingDetector::useModel()
{
// Get parameters
const String& topology = getParameter("Topology");
// Get some generated properties
unsigned int number_wavelengths = getGenProperties()->get("NumberWavelengths");
// Get optical input transition info
const TransitionInfo& in_trans = getOpticalInputPort("In")->getTransitionInfo();
// Get tech models
double vdd = getTechModel()->get("Vdd");
// Get caps
double unit_gate_cap = getTechModel()->get("Gate->MinWidth").toDouble() * getTechModel()->get("Gate->CapPerWidth").toDouble();
double unit_drain_cap = getTechModel()->get("Gate->MinWidth").toDouble() * getTechModel()->get("Drain->CapPerWidth").toDouble();
double inv_x1_gate_cap = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->A");
double inv_x1_drain_cap = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->Y");
// Construct a simple sense-amp model
if(topology == INTEGRATINGSENSEAMP)
{
// Use ratios from the receiver published in [Georgas, ESSCIRC 2011]
// Note:
// The numbers in the paper (43fJ/b, 50 fJ/b in the cited work) is done with the clock buffer (there are 4 receivers),
// capacitive DAC, and extra output flops used in the physical layout, as the compared receiver is extremely conservative
// We simplified this model to not have the capacitive DAC, the clock buffer (since this is an individual receiver), or
// the extra output flops (since receiver structure is already a posedge flop functionally).
// Look for an upcoming paper [Georgas, JSSC 2012] (when it is published) for the power breakdown pie-chart for the receiver.
// This model only models the latch (sampler) and the dynamic to static (RS latch) part of the design, which is all you really
// need in the receiver.
// Gate caps
double c_gate_sampler = unit_gate_cap * (4 * 2.0 + 2 * 1.0 + 2 * 3.0 + 2 * 5.0) + unit_gate_cap * (2 * 6.0 + 2 * 1.0) + inv_x1_gate_cap;
double c_gate_rslatch = unit_gate_cap * (4 * 1.0) + inv_x1_gate_cap;
// Drain caps
double c_drain_sampler = unit_drain_cap * (2 * 2.0 + 2 * 1.0 + 3 * 5.0 + 1 * 3.0) + inv_x1_drain_cap;
double c_drain_rslatch = unit_drain_cap * (2 * 6.0) + inv_x1_drain_cap;
// Sum up cap switched for the sampler
double c_sampler = c_gate_sampler + c_drain_sampler;
double c_rslatch = c_gate_rslatch + c_drain_rslatch;
// Average cap switched
// Sampler is differential, one side will always switch (R or S in the latch) regardless of probability
double avg_cap = c_sampler + c_rslatch * in_trans.getProbability0() * in_trans.getProbability1();
// Get parameters corresponding to a unit-inverter
double unit_leak_0 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Leakage->!A");
double unit_leak_1 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Leakage->A");
// Approximate leakage (curve fit with design)
double total_leakage = 0.5 * (unit_leak_0 + unit_leak_1) * 7.43;
// Create results
getEventResult("Receive")->setValue(vdd * vdd * avg_cap * number_wavelengths);
getNddPowerResult("Leakage")->setValue(total_leakage * number_wavelengths);
}
else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
return;
}
void RingDetector::propagateTransitionInfo()
{
// Propagate probabilities from optical input to electrical output port
getOutputPort("Out")->setTransitionInfo(getOpticalInputPort("In")->getTransitionInfo());
return;
}
void RingDetector::precomputeTech()
{
// Get parameters
const double data_rate = getParameter("DataRate");
const String& topology = getParameter("Topology");
// Get tech model numbers
double pd_cap = getTechModel()->get("Photodetector->Cap");
double parasitic_cap = getTechModel()->get("Photodetector->ParasiticCap");
double apd = getTechModel()->get("Photodetector->AvalancheGain");
double vdd = getTechModel()->get("Vdd");
// Constants shortcuts
double pi = Constants::pi;
double k = Constants::k;
double q = Constants::q;
double T = getTechModel()->get("Temperature");
if(topology == INTEGRATINGSENSEAMP)
{
// Get more tech parameters
double integ_time_ratio = getTechModel()->get("Receiver->Int->IntegrationTimeRatio");
double BER = getTechModel()->get("SenseAmp->BER");
double CMRR = getTechModel()->get("SenseAmp->CMRR");
double offset_comp_bits = getTechModel()->get("SenseAmp->OffsetCompensationBits");
double offset = getTechModel()->get("SenseAmp->OffsetRatio").toDouble() * vdd;
double supply_noise_rand = getTechModel()->get("SenseAmp->SupplyNoiseRandRatio").toDouble() * vdd;
double supply_noise_det = getTechModel()->get("SenseAmp->SupplyNoiseDetRatio").toDouble() * vdd;
double noise_margin = getTechModel()->get("SenseAmp->NoiseMargin");
double jitter_ratio = getTechModel()->get("SenseAmp->JitterRatio");
// Approximate tao using FO4
double unit_drain_cap = getTechModel()->get("Gate->MinWidth").toDouble() * getTechModel()->get("Drain->CapPerWidth").toDouble();
double c_g = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->A");
double c_d = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->Y");
double r_o = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->DriveRes->Y");
// Calculate sense amp tau from sense amp output loading
double tau = r_o * (c_g + c_d);
// Set output inverter drive strength
getDriver("OutDriver")->setOutputRes(r_o);
// Calculate sense amp input cap based on schematic
double sense_amp_cap_in = unit_drain_cap * (2.0 + 3.0 + 5.0 + 1.0);
// Residual offset
double v_residual = 3 * offset / pow(2, offset_comp_bits);
// Noise
double v_noise = supply_noise_rand * supply_noise_rand / (CMRR * CMRR);
// Sense amp voltage build-up minimum
double v_sense = vdd * exp(-(1 - integ_time_ratio) / (data_rate * tau)) + noise_margin + v_residual + supply_noise_det / CMRR;
// Sigmas corresponding to BER
double sigma = calcInvNormCdf(BER);
//K_int is the time the bit is valid for evaluation
// Total input cap load
double input_node_cap = sense_amp_cap_in + pd_cap + parasitic_cap;
double z_int = integ_time_ratio / (data_rate * input_node_cap); //should use K_int
// Store precalculated values
m_quad_a_ = 1 - (sigma * sigma * jitter_ratio * jitter_ratio);
m_quad_b1_ = - 2 * pi / 2 * sigma * sigma * q * 0.7 * data_rate;
m_quad_b2_ = -2 * v_sense / (z_int * apd);
m_quad_c_ = 1 / (z_int * z_int) * (v_sense * v_sense - sigma * sigma * (k * T / input_node_cap + v_noise));
}
else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
return;
}
void RingDetector::designReceiver()
{
// Get some generated properties
unsigned int number_wavelengths = getGenProperties()->get("NumberWavelengths");
// Get relevant properties/parameters
const String& topology = getParameter("Topology");
// Construct a simple sense-amp model
if(topology == INTEGRATINGSENSEAMP)
{
// No really good way to estimate the area...can assume each receiver is the size of 40 inverters, which is
// about the right size for just the sense amp in the layout
double unit_area_active = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Area->Active");
double unit_area_metal1 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Area->Metal1Wire");
getAreaResult("Active")->setValue(unit_area_active * 40 * number_wavelengths);
getAreaResult("Metal1Wire")->setValue(unit_area_metal1 * 40 * number_wavelengths);
}
else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
return;
}
double RingDetector::getSensitivity(double ER_dB_) const
{
// Get parameters
const String& topology = getParameter("Topology");
// Turn extinction ratio into a ratio from dB scale
double ER = pow(10, ER_dB_ / 10);
// Initialize sensitivity
double sensitivity = 1e99;
// Construct a simple sense-amp model
if(topology == INTEGRATINGSENSEAMP)
{
// Scale photodetector shot noise using ER, add rest of noise source
double b = m_quad_b1_ * (1 + ER) / (2 * (ER - 1)) + m_quad_b2_;
// Find sensitivity (-b + sqrt(b^2-4ac)) / 2a
sensitivity = ((-b + sqrt(b * b - 4 * m_quad_a_ * m_quad_c_)) / (2 * m_quad_a_));
}
else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
return sensitivity;
}
double RingDetector::calcInvNormCdf(double num_)
{
// 53 bit precision for double FP
unsigned int num_iterations = 20;
// Upperbound the step
double step = 20;
double out = step;
// Iteratively guess and check calculation
for (unsigned int i = 0; i < num_iterations; ++i)
{
double current = 0.5 * erfc(out / sqrt(2));
if (current > num_) out += step;
else out -= step;
step = step * 0.5;
}
return out;
}
} // namespace DSENT