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.
359 lines
16 KiB
C++
359 lines
16 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/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
|
|
|