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