1/* Copyright (c) 2012 Massachusetts Institute of Technology
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 */
21
22#include "model/optical/RingDetector.h"
23
24#include <cmath>
25
26#include "util/Constants.h"
27#include "model/PortInfo.h"
28#include "model/TransitionInfo.h"
29#include "model/EventInfo.h"
30#include "model/std_cells/StdCell.h"
31#include "model/std_cells/StdCellLib.h"
32#include "model/optical_graph/OpticalWaveguide.h"
33#include "model/optical_graph/OpticalDetector.h"
34#include "model/optical_graph/OpticalFilter.h"
35#include "model/timing_graph/ElectricalDriver.h"
36#include "model/timing_graph/ElectricalNet.h"
37
38namespace DSENT
39{
40    // TODOs for this model
41    // Add the other receiver topologies from [Georgas, CICC 2011]
42    // Split integ_time_ratio = SA integ time ratio
43    // Right now perfect clock gating is assumed...may not be what we want
44
45    // Constants
46    const String RingDetector::INTEGRATINGSENSEAMP = "INTSA";
47
48    RingDetector::RingDetector(const String& instance_name_, const TechModel* tech_model_)
49        : OpticalModel(instance_name_, tech_model_), OpticalReceiver()
50    {
51        initParameters();
52        initProperties();
53    }
54
55    RingDetector::~RingDetector()
56    {}
57
58    void RingDetector::initParameters()
59    {
60        addParameterName("DataRate");
61        addParameterName("InStart");
62        addParameterName("InEnd");
63        addParameterName("DetStart");
64        addParameterName("DetEnd");
65        addParameterName("DropAll");
66        addParameterName("Topology");
67        return;
68    }
69
70    void RingDetector::initProperties()
71    {
72        return;
73    }
74
75    void RingDetector::constructModel()
76    {
77        // Get parameters
78        WavelengthGroup in_wavelengths = makeWavelengthGroup(getParameter("InStart"), getParameter("InEnd"));
79        WavelengthGroup det_wavelengths = makeWavelengthGroup(getParameter("DetStart"), getParameter("DetEnd"));
80        int number_wavelengths = det_wavelengths.second - det_wavelengths.first + 1;
81        bool drop_all = getParameter("DropAll");
82        const String& topology = getParameter("Topology");
83
84        // Set some generated properties
85        getGenProperties()->set("NumberWavelengths", number_wavelengths);
86
87        // Create device area result
88        addAreaResult(new AtomicResult("Photonic"));
89        // Create electrical results
90        createElectricalAtomicResults();
91        if (topology == INTEGRATINGSENSEAMP) addEventResult(new AtomicResult("Receive"));
92        else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
93
94        // Create optical ports
95        createOpticalInputPort(         "In",   in_wavelengths);
96        createOpticalOutputPort(        "Out",  in_wavelengths);
97        // Create the filter and modulator
98        createFilter(                   "RingFilter",   in_wavelengths, drop_all, det_wavelengths);
99        createDetector(                 "RingDetector", det_wavelengths, this);
100        OpticalFilter* ring_filter = getFilter("RingFilter");
101        OpticalDetector* ring_detector = getDetector("RingDetector");
102        // Connect the filter and modulator
103        getWaveguide("In")->addDownstreamNode(ring_filter);
104        ring_filter->addDownstreamNode(getWaveguide("Out"));
105        ring_filter->setDropPort(ring_detector);
106
107        // Create electrical ports
108        createOutputPort("Out", makeNetIndex(0, number_wavelengths-1));
109        // Create net
110        createNet("OutVFO");
111        // Create output driver
112        createDriver("OutDriver", false);
113        // Connect driver
114        getDriver("OutDriver")->addDownstreamNode(getNet("OutVFO"));
115        // Connect output
116        assignVirtualFanout("Out", "OutVFO");
117
118        // Precompute some technology values
119        precomputeTech();
120
121        return;
122    }
123
124    void RingDetector::updateModel()
125    {
126        // Get some generated properties
127        unsigned int number_wavelengths = getGenProperties()->get("NumberWavelengths");
128
129        // Get tech model numbers
130        double ring_area = getTechModel()->get("Ring->Area");
131        double thru_loss = getTechModel()->get("Ring->ThroughLoss");
132        double drop_loss = getTechModel()->get("Ring->DropLoss");
133        double pd_loss = getTechModel()->get("Photodetector->Loss");
134        double pd_responsivity = getTechModel()->get("Photodetector->Responsivity");
135
136        // Design the receiver
137        designReceiver();
138
139        // Update losses
140        // Connect the filter and modulator
141        OpticalFilter* ring_filter = getFilter("RingFilter");
142        OpticalDetector* ring_detector = getDetector("RingDetector");
143        ring_filter->setLoss(thru_loss * number_wavelengths);
144        ring_filter->setDropLoss(drop_loss + thru_loss * number_wavelengths);
145        ring_detector->setLoss(pd_loss);
146        ring_detector->setResponsivity(pd_responsivity);
147        // Update device area
148        getAreaResult("Photonic")->setValue(ring_area * (number_wavelengths));
149
150        return;
151    }
152
153    void RingDetector::useModel()
154    {
155        // Get parameters
156        const String& topology = getParameter("Topology");
157
158        // Get some generated properties
159        unsigned int number_wavelengths = getGenProperties()->get("NumberWavelengths");
160
161        // Get optical input transition info
162        const TransitionInfo& in_trans = getOpticalInputPort("In")->getTransitionInfo();
163
164        // Get tech models
165        double vdd = getTechModel()->get("Vdd");
166        // Get caps
167        double unit_gate_cap = getTechModel()->get("Gate->MinWidth").toDouble() * getTechModel()->get("Gate->CapPerWidth").toDouble();
168        double unit_drain_cap = getTechModel()->get("Gate->MinWidth").toDouble() * getTechModel()->get("Drain->CapPerWidth").toDouble();
169        double inv_x1_gate_cap = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->A");
170        double inv_x1_drain_cap = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->Y");
171
172        // Construct a simple sense-amp model
173        if(topology == INTEGRATINGSENSEAMP)
174        {
175            // Use ratios from the receiver published in [Georgas, ESSCIRC 2011]
176            // Note:
177            // The numbers in the paper (43fJ/b, 50 fJ/b in the cited work) is done with the clock buffer (there are 4 receivers),
178            // capacitive DAC, and extra output flops used in the physical layout, as the compared receiver is extremely conservative
179            // We simplified this model to not have the capacitive DAC, the clock buffer (since this is an individual receiver), or
180            // the extra output flops (since receiver structure is already a posedge flop functionally).
181            // Look for an upcoming paper [Georgas, JSSC 2012] (when it is published) for the power breakdown pie-chart for the receiver.
182            // This model only models the latch (sampler) and the dynamic to static (RS latch) part of the design, which is all you really
183            // need in the receiver.
184
185            // Gate caps
186            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;
187            double c_gate_rslatch = unit_gate_cap * (4 * 1.0) + inv_x1_gate_cap;
188            // Drain caps
189            double c_drain_sampler = unit_drain_cap * (2 * 2.0 + 2 * 1.0 + 3 * 5.0 + 1 * 3.0) + inv_x1_drain_cap;
190            double c_drain_rslatch = unit_drain_cap * (2 * 6.0) + inv_x1_drain_cap;
191            // Sum up cap switched for the sampler
192            double c_sampler = c_gate_sampler + c_drain_sampler;
193            double c_rslatch = c_gate_rslatch + c_drain_rslatch;
194            // Average cap switched
195            // Sampler is differential, one side will always switch (R or S in the latch) regardless of probability
196            double avg_cap = c_sampler + c_rslatch * in_trans.getProbability0() * in_trans.getProbability1();
197
198            // Get parameters corresponding to a unit-inverter
199            double unit_leak_0 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Leakage->!A");
200            double unit_leak_1 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Leakage->A");
201
202            // Approximate leakage (curve fit with design)
203            double total_leakage = 0.5 * (unit_leak_0 + unit_leak_1) * 7.43;
204
205            // Create results
206            getEventResult("Receive")->setValue(vdd * vdd * avg_cap * number_wavelengths);
207            getNddPowerResult("Leakage")->setValue(total_leakage * number_wavelengths);
208
209        }
210        else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
211
212        return;
213    }
214
215    void RingDetector::propagateTransitionInfo()
216    {
217        // Propagate probabilities from optical input to electrical output port
218        getOutputPort("Out")->setTransitionInfo(getOpticalInputPort("In")->getTransitionInfo());
219
220        return;
221    }
222
223    void RingDetector::precomputeTech()
224    {
225        // Get parameters
226        const double data_rate = getParameter("DataRate");
227        const String& topology = getParameter("Topology");
228
229        // Get tech model numbers
230        double pd_cap = getTechModel()->get("Photodetector->Cap");
231        double parasitic_cap = getTechModel()->get("Photodetector->ParasiticCap");
232        double apd = getTechModel()->get("Photodetector->AvalancheGain");
233        double vdd = getTechModel()->get("Vdd");
234
235        // Constants shortcuts
236        double pi = Constants::pi;
237        double k = Constants::k;
238        double q = Constants::q;
239        double T = getTechModel()->get("Temperature");
240
241        if(topology == INTEGRATINGSENSEAMP)
242        {
243            // Get more tech parameters
244            double integ_time_ratio = getTechModel()->get("Receiver->Int->IntegrationTimeRatio");
245            double BER = getTechModel()->get("SenseAmp->BER");
246            double CMRR = getTechModel()->get("SenseAmp->CMRR");
247            double offset_comp_bits = getTechModel()->get("SenseAmp->OffsetCompensationBits");
248            double offset = getTechModel()->get("SenseAmp->OffsetRatio").toDouble() * vdd;
249            double supply_noise_rand = getTechModel()->get("SenseAmp->SupplyNoiseRandRatio").toDouble() * vdd;
250            double supply_noise_det = getTechModel()->get("SenseAmp->SupplyNoiseDetRatio").toDouble() * vdd;
251            double noise_margin = getTechModel()->get("SenseAmp->NoiseMargin");
252            double jitter_ratio = getTechModel()->get("SenseAmp->JitterRatio");
253
254            // Approximate tao using FO4
255            double unit_drain_cap = getTechModel()->get("Gate->MinWidth").toDouble() * getTechModel()->get("Drain->CapPerWidth").toDouble();
256            double c_g = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->A");
257            double c_d = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->Y");
258            double r_o = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->DriveRes->Y");
259            // Calculate sense amp tau from sense amp output loading
260            double tau = r_o * (c_g + c_d);
261            // Set output inverter drive strength
262            getDriver("OutDriver")->setOutputRes(r_o);
263
264            // Calculate sense amp input cap based on schematic
265            double sense_amp_cap_in = unit_drain_cap * (2.0 + 3.0 + 5.0 + 1.0);
266
267            // Residual offset
268            double v_residual = 3 * offset / pow(2, offset_comp_bits);
269            // Noise
270            double v_noise = supply_noise_rand * supply_noise_rand / (CMRR * CMRR);
271            // Sense amp voltage build-up minimum
272            double v_sense = vdd * exp(-(1 - integ_time_ratio) / (data_rate * tau)) + noise_margin + v_residual + supply_noise_det / CMRR;
273            // Sigmas corresponding to BER
274            double sigma = calcInvNormCdf(BER);
275
276            //K_int is the time the bit is valid for evaluation
277
278            // Total input cap load
279            double input_node_cap = sense_amp_cap_in + pd_cap + parasitic_cap;
280            double z_int = integ_time_ratio / (data_rate * input_node_cap); //should use K_int
281
282            // Store precalculated values
283            m_quad_a_ = 1 - (sigma * sigma * jitter_ratio * jitter_ratio);
284            m_quad_b1_ = - 2 * pi / 2 * sigma * sigma * q * 0.7 * data_rate;
285            m_quad_b2_ = -2 * v_sense / (z_int * apd);
286            m_quad_c_ = 1 / (z_int * z_int) * (v_sense * v_sense - sigma * sigma * (k * T / input_node_cap + v_noise));
287        }
288        else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
289
290        return;
291    }
292
293    void RingDetector::designReceiver()
294    {
295        // Get some generated properties
296        unsigned int number_wavelengths = getGenProperties()->get("NumberWavelengths");
297
298        // Get relevant properties/parameters
299        const String& topology = getParameter("Topology");
300
301        // Construct a simple sense-amp model
302        if(topology == INTEGRATINGSENSEAMP)
303        {
304            // No really good way to estimate the area...can assume each receiver is the size of 40 inverters, which is
305            // about the right size for just the sense amp in the layout
306            double unit_area_active = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Area->Active");
307            double unit_area_metal1 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Area->Metal1Wire");
308            getAreaResult("Active")->setValue(unit_area_active * 40 * number_wavelengths);
309            getAreaResult("Metal1Wire")->setValue(unit_area_metal1 * 40 * number_wavelengths);
310        }
311        else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
312
313        return;
314    }
315
316    double RingDetector::getSensitivity(double ER_dB_) const
317    {
318        // Get parameters
319        const String& topology = getParameter("Topology");
320        // Turn extinction ratio into a ratio from dB scale
321        double ER = pow(10, ER_dB_ / 10);
322
323        // Initialize sensitivity
324        double sensitivity = 1e99;
325        // Construct a simple sense-amp model
326        if(topology == INTEGRATINGSENSEAMP)
327        {
328            // Scale photodetector shot noise using ER, add rest of noise source
329            double b = m_quad_b1_ * (1 + ER) / (2 * (ER - 1)) + m_quad_b2_;
330
331            // Find sensitivity (-b + sqrt(b^2-4ac)) / 2a
332            sensitivity = ((-b + sqrt(b * b - 4 * m_quad_a_ * m_quad_c_)) / (2 * m_quad_a_));
333        }
334        else ASSERT(false, "[Error] " + getInstanceName() + " -> Unknown receiver topology (" + topology + ")!");
335
336        return sensitivity;
337    }
338
339    double RingDetector::calcInvNormCdf(double num_)
340    {
341        // 53 bit precision for double FP
342        unsigned int num_iterations = 20;
343        // Upperbound the step
344        double step = 20;
345        double out = step;
346        // Iteratively guess and check calculation
347        for (unsigned int i = 0; i < num_iterations; ++i)
348        {
349            double current = 0.5 * erfc(out / sqrt(2));
350            if (current > num_) out += step;
351            else out -= step;
352            step = step * 0.5;
353        }
354
355        return out;
356    }
357
358} // namespace DSENT
359
360