summaryrefslogtreecommitdiff
path: root/ext/dsent/model/optical/RingModulator.cc
blob: 8447f188be6f51245e2ad3d9d8e68b9a5fe4c5bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/* 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/RingModulator.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/OpticalModulator.h"
#include "model/optical_graph/OpticalFilter.h"
#include "model/optical_graph/OpticalTransmitter.h"
#include "model/timing_graph/ElectricalNet.h"
#include "model/timing_graph/ElectricalLoad.h"
#include "model/timing_graph/ElectricalTimingTree.h"

namespace DSENT
{
    using std::max;
    using std::min;
    
    // TODO: Don't like the way this is written right now. Probably fix in a future version

    RingModulator::RingModulator(const String& instance_name_, const TechModel* tech_model_)
        : OpticalModel(instance_name_, tech_model_)
    {
        initParameters();
        initProperties();
    }

    RingModulator::~RingModulator()
    {}

    void RingModulator::initParameters()
    {
        addParameterName("DataRate");
        addParameterName("InStart");
        addParameterName("InEnd");
        addParameterName("ModStart");
        addParameterName("ModEnd");
        addParameterName("OptimizeLoss", "TRUE");
        return;
    }

    void RingModulator::initProperties()
    {
        addPropertyName("ExtinctionRatio", 6);  //default properties
        addPropertyName("InsertionLoss", 2);    //default properties
        return;
    }

    void RingModulator::constructModel()
    {
        // Create electrical results
        createElectricalAtomicResults();
        // Create Area result
        addAreaResult(new AtomicResult("Photonic"));
        // Create Modulate result
        createElectricalEventAtomicResult("Modulate");

        // Get parameters
        WavelengthGroup in_wavelengths = makeWavelengthGroup(getParameter("InStart"), getParameter("InEnd"));
        WavelengthGroup mod_wavelengths = makeWavelengthGroup(getParameter("ModStart"), getParameter("ModEnd"));
        int number_wavelengths = mod_wavelengths.second - mod_wavelengths.first + 1;
        bool optimize_loss = getParameter("OptimizeLoss");

        getGenProperties()->set("NumberWavelengths", number_wavelengths);
        
        // Create optical ports
        createOpticalInputPort(         "In",   in_wavelengths);
        createOpticalOutputPort(        "Out",  in_wavelengths);
        // Create the filter and modulator
        createFilter(                   "RingFilter",       in_wavelengths, true, mod_wavelengths);
        createModulator(                "RingModulator",    mod_wavelengths, optimize_loss, this);
        createWaveguide(                "RingTemp",         mod_wavelengths);
        OpticalFilter* ring_filter = getFilter("RingFilter");
        OpticalModulator* ring_modulator = getModulator("RingModulator");        
        // Connect the filter and modulator
        getWaveguide("In")->addDownstreamNode(ring_filter);
        ring_filter->addDownstreamNode(getWaveguide("Out"));
        ring_filter->setDropPort(ring_modulator);
        ring_modulator->addDownstreamNode(getWaveguide("Out"));
        
        // Create electrical ports
        createInputPort(                "In", makeNetIndex(0, number_wavelengths-1));
        // Create driver
        createNet("PredriverIn");
        // VFI from In to PredriverIn
        assignVirtualFanin("PredriverIn", "In");        
        // Create input load (due to predrivers)
        createLoad("PredriverCap");
        getNet("PredriverIn")->addDownstreamNode(getLoad("PredriverCap"));
        
        // Precompute some values
        precomputeTech();
        
        return;
    }
    
    void RingModulator::updateModel()
    {
        // Get properties
        double ER_dB = getProperty("ExtinctionRatio").toDouble();
        double IL_dB = getProperty("InsertionLoss").toDouble();
            
        // Get Gen properties
        int number_wavelengths = getGenProperties()->get("NumberWavelengths");

        // Get tech model parameters
        double ring_area = getTechModel()->get("Ring->Area").toDouble();
        double thru_loss = getTechModel()->get("Ring->ThroughLoss").toDouble();
        
        // Design the modulator and the modulator driver
        bool success = designModulator(IL_dB, ER_dB);        
        getGenProperties()->set("Success", success);     

        // If not successful, make the modulate energy extremely large
        if (!success) getEventResult("Modulate")->setValue(1e99);
        
        // Update losses
        // Connect the filter and modulator
        OpticalFilter* ring_filter = getFilter("RingFilter");
        ring_filter->setLoss(thru_loss * number_wavelengths);
        ring_filter->setDropLoss(thru_loss * number_wavelengths);     // Assume worst-case through loss for a dropped wavelength
        // Update area
        getAreaResult("Photonic")->setValue(ring_area * (number_wavelengths));        
    }
    
    void RingModulator::useModel()
    {
        // Propagate the transition info and get the 0->1 transtion count
        propagateTransitionInfo();
        double P_In = getInputPort("In")->getTransitionInfo().getProbability1();
        double P_num_trans_01 = getInputPort("In")->getTransitionInfo().getNumberTransitions01();
        
        // Get Gen properties
        int number_wavelengths = getGenProperties()->get("NumberWavelengths");
        
        // If I can't build it...then it is infinitely expensive!
        bool success = getGenProperties()->get("Success");
        double driver_size = 1e99;
        double total_predriver_size = 1e99;
        if (success)
        {
            driver_size = getGenProperties()->get("DriverSize");
            total_predriver_size = getGenProperties()->get("TotalPredriverSize");
        }

        // 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
        double total_leakage = number_wavelengths * 0.5 * ((driver_size + total_predriver_size) * P_In * unit_leak_1 + 
                                                            (driver_size + total_predriver_size) * (1 - P_In) * unit_leak_0);
        
        getNddPowerResult("Leakage")->setValue(total_leakage);
        getEventResult("Modulate")->setValue(calcModulatorEnergy() * P_num_trans_01);
        
        return;
    }
    
    void RingModulator::propagateTransitionInfo()
    {
        // Very simple...whatever comes in electrically is encoded optically
        getOpticalOutputPort("Out")->setTransitionInfo(getInputPort("In")->getTransitionInfo());

        return;
    }
        
    void RingModulator::precomputeTech()
    {
        // Get parameters
        double data_rate = getParameter("DataRate");
                
        // Constants shortcuts
        double pi = Constants::pi;
        double c = Constants::c;
        double k = Constants::k;
        double e0 = Constants::e0;
        double es = Constants::es;
        double q = Constants::q;   
        double T = getTechModel()->get("Temperature");
        
        // Get modulator parameters
        double lambda = getTechModel()->get("Ring->Lambda").toDouble();
        double n_f = getTechModel()->get("Modulator->Ring->FCPDEffect").toDouble();
        double NA = getTechModel()->get("Modulator->Ring->NA").toDouble();
        double ND = getTechModel()->get("Modulator->Ring->ND").toDouble();
        double ni = getTechModel()->get("Modulator->Ring->ni").toDouble();
        double L_j = getTechModel()->get("Modulator->Ring->JunctionRatio").toDouble();
        double H = getTechModel()->get("Modulator->Ring->Height").toDouble();
        double W = getTechModel()->get("Modulator->Ring->Width").toDouble();
        double g_c = getTechModel()->get("Modulator->Ring->ConfinementFactor").toDouble();
        // Get ring parameters
        double R = getTechModel()->get("Ring->Radius").toDouble();
        double n_g = getTechModel()->get("Ring->GroupIndex").toDouble();
        double Q_max = getTechModel()->get("Ring->MaxQualityFactor").toDouble();

        // Setup calculations
        double f0 = c / lambda;
        double BW = data_rate;                      // Modulator bandwidth
        double Q_f = std::min(f0 / BW, Q_max);      // Quality factor
        double L_tot = 2 * pi * R;                  // Optical length of the ring

        double V_bi = k * T / q * log(NA * ND / (ni * ni));                     // Junction Built-in voltage
        double x_d0 = sqrt(2 * e0 * es / q * V_bi * (NA + ND) / (NA * ND));     // Junction nominal depletion width
        double C_j0 = e0 * es * L_tot * L_j * W / x_d0;                         // Junction nominal cap        
        double Q_0 = q * n_g * (L_tot * H * W) / (2 * n_f * Q_f * g_c);         // Charge in depletion region

        // Store into precomputed values
        m_precompute_V_bi_ = V_bi;
        m_precompute_x_d0_ = x_d0;
        m_precompute_C_j0_ = C_j0;
        m_precompute_Q_0_ = Q_0;

        return;        
    }
    
    bool RingModulator::designModulator(double IL_dB_, double ER_dB_)
    {
        // Get parameters
        double vdd = getTechModel()->get("Vdd");
        double data_rate = getParameter("DataRate");
        unsigned int max_predriver_stages = 20;         //TODO: Make this not hardcoded
        // Get modulator parameters
        double boost_ratio = getTechModel()->get("Modulator->Ring->SupplyBoostRatio");
        double Tn = getTechModel()->get("Modulator->Ring->Tn").toDouble();;
        double H = getTechModel()->get("Modulator->Ring->Height").toDouble();

        // Get Gen properties
        int number_wavelengths = getGenProperties()->get("NumberWavelengths");
                
        // Checking ASSERTions (input properties that don't make any sense)
        ASSERT(ER_dB_ > 0, "[Error] " + getInstanceName() + " -> Extinction ratio must be > 0!");
        ASSERT(IL_dB_ > 0, "[Error] " + getInstanceName() + " -> Insertion loss must be > 0!");        
        
        // Setup calculations
        double ER = pow(10, ER_dB_ / 10);            // Extinction ratio
        double T1 = pow(10, -IL_dB_ / 10);           // Transmisivity on
        double T0 = T1 / ER;                        // Transmisivity off

        // Get precomputed values
        double V_bi = m_precompute_V_bi_;
        double x_d0 = m_precompute_x_d0_;
        double C_j0 = m_precompute_C_j0_;
        double Q_0 = m_precompute_Q_0_;
        
        // Charge
        double int_c = -2 * V_bi * C_j0;
        // Calculate shift using lorentzian
        double gamma = sqrt((1 - Tn)/(1 - T1) - 1) - sqrt((1 - Tn)/(1 - T0) - 1);   // gamma = delta_f / delta_f_FWHM        
        double Q = gamma * Q_0;                                                     // Charge required to hit given Tf
        // Voltage required
        double V_a = V_bi * (pow( (Q - int_c)/(2 * V_bi * C_j0), 2) - 1);
        // Calculate driver vdd
        double hvdd = V_a * boost_ratio;
        // Depletion region required
        double x_d = x_d0 * sqrt((V_bi + V_a) / V_bi);

        // Calculate C_eff
        double c_eff = Q / V_a;
        
        // Feasibility checks
        // Not feasible if the transmisivity when transmitting an optical 1 is greater than 1.0...
        if (T1 >= 1) return false;
        // Not feasible if the transmisivity when transmitting an optical 0 is smaller than the notch of the ring
        if (T0 <= Tn) return false;
        // Not feasible if the extinction ratio is greater than the notch of the ring
        if (ER >= 1 / Tn) return false;
        // Not feasible if the required depletion width is greater than the height of the junction
        if (x_d >= H) return false;

        // Analytically calculate driver sizes
        // Get parameters corresponding to a unit-inverter
        double unit_c_g = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->A");
        double unit_c_d = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->Y");
        double unit_r_o = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->DriveRes->Y");
        double unit_area_active = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Area->Active");
        double unit_area_metal1 = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Area->Metal1Wire");
        
        // Get device resistance/cap
        double device_par_res = getTechModel()->get("Modulator->Ring->ParasiticRes");
        double device_par_cap = getTechModel()->get("Modulator->Ring->ParasiticCap");
        
        // Use timing tree to size modulator drivers
        // Coefficient of R*C to give a 0->V_a transition
        double transition_scale = log(hvdd / (hvdd - V_a));             
        double transition_required = 1 / (4 * data_rate);      // I am not sure what the factor of 4 is for...
        
        // Calculate inverter intrinsic transition time
        double transition_intrinsic = transition_scale * unit_c_d * unit_r_o;
        // Calculate minimum possible device transition time
        double min_transition_intrinsic = transition_intrinsic + transition_scale * device_par_res * c_eff;
        // If the minimum possible transition time is already bigger
        // than the required transition, then this particular driver is not possible...
        if (min_transition_intrinsic > transition_required)
            return false;

        // Calculate driver size
        double driver_size = max(1.0, transition_scale * unit_r_o * (c_eff + device_par_cap) / (transition_required - min_transition_intrinsic));
        // Keep track of the total multiplier of unit inverters (for area, leakage calculations)
        double total_unit_inverters = driver_size * max(1.0, hvdd / vdd);
        // Calculate load cap for predriver stages
        double current_load_cap = driver_size * unit_c_g;
        // Number of predriver stages
        unsigned int predriver_stages = 0;
        // Add predriver stages until the input cap is less than the unit INV_X1 gate cap or
        // if the signal is still inverted (need an odd number of predriver stages)
        while (current_load_cap > unit_c_g || (predriver_stages == 0) || ((predriver_stages & 0x1) == 0))
        {
            // Calculate the size of the current predriver stage
            double current_predriver_size = max(1.0, unit_r_o * current_load_cap / (transition_required - transition_intrinsic));
            // Calculate load cap for the next predriver stage
            current_load_cap = current_predriver_size * unit_c_g;
            // Add cap to total predriver total cap
            total_unit_inverters += current_predriver_size;
            // Consider this a failure if the number of predriver stages exceed some maximum
            if (predriver_stages > max_predriver_stages)
                return false;

            ++predriver_stages;
        }
        // Set the input load capacitance
        getLoad("PredriverCap")->setLoadCap(current_load_cap);

        // Set generated properties
        getGenProperties()->set("DriverSize", driver_size);
        getGenProperties()->set("FirstPredriverSize", current_load_cap);
        getGenProperties()->set("TotalPredriverSize", total_unit_inverters - driver_size);
        getGenProperties()->set("Hvdd", hvdd);
        getGenProperties()->set("Ceff", c_eff);
        
        // Calculate leakage, area, energy consumption
        double area_active = total_unit_inverters * unit_area_active;
        double area_metal1 = total_unit_inverters * unit_area_metal1;
                
        // Set results
        getAreaResult("Active")->setValue(area_active * number_wavelengths);
        getAreaResult("Metal1Wire")->setValue(area_metal1 * number_wavelengths);

        // Only if everything was successful do we set the modulator specification
        getModulator("RingModulator")->setLosses(IL_dB_, ER_dB_);
        return true;        
    }
    
    double RingModulator::calcModulatorEnergy() const
    {
        // Get tech parameters
        double vdd = getTechModel()->get("Vdd");
        double device_par_cap = getTechModel()->get("Modulator->Ring->ParasiticCap");

        // Get Gen properties
        int number_wavelengths = getGenProperties()->get("NumberWavelengths");
        
        bool success = getGenProperties()->get("Success");
        if (success)
        {
            double driver_size = getGenProperties()->get("DriverSize");
            double total_predriver_size = getGenProperties()->get("TotalPredriverSize");
            double first_predriver_size = getGenProperties()->get("FirstPredriverSize");
            double c_eff = getGenProperties()->get("Ceff");
            double hvdd = getGenProperties()->get("Hvdd");
            
            // Get parameters corresponding to a unit-inverter
            double unit_c_g = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->A");
            double unit_c_d = getTechModel()->getStdCellLib()->getStdCellCache()->get("INV_X1->Cap->Y");
            
            // Approximate leakage
            double energy_predriver = number_wavelengths * vdd * vdd * ((unit_c_d * total_predriver_size + 
                                            unit_c_g * (total_predriver_size + driver_size - first_predriver_size)));
            double energy_driver = number_wavelengths * hvdd * std::max(hvdd, vdd) * (driver_size * unit_c_d + c_eff + device_par_cap);
            
            return (energy_predriver + energy_driver);
        }
        else
            return 1e99;    // An infinitely expensive modulator
    }
    
    bool RingModulator::setTransmitterSpec(double IL_dB_, double ER_dB_)
    {
        setProperty("InsertionLoss", IL_dB_);
        setProperty("ExtinctionRatio", ER_dB_);
        update();
        evaluate();
        
        return getGenProperties()->get("Success");
    }
    
    double RingModulator::getPower(double util_) const
    {
        // Get parameters
        double data_rate = getParameter("DataRate");
		// Check arguments
        ASSERT((util_ <= 1.0) && (util_ >= 0.0), "[Error] " + getInstanceName() + " -> Modulator utilization must be between 0.0 and 1.0!");

        return calcModulatorEnergy() * 0.25 * util_ * data_rate;
    }
    
} // namespace DSENT