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 <cstdlib>
23#include <iostream>
24
25#include "DSENT.h"
26#include "model/std_cells/StdCellLib.h"
27
28using namespace std;
29
30namespace DSENT
31{
32    static void performTimingOpt(const map<String, String> &params,
33                                 Model *ms_model)
34    {
35        // Get the frequency it is optimizing to
36        double freq = params.at("Frequency").toDouble();
37
38        // Get all the starting net names
39        const vector<String>& start_net_names =
40            params.at("TimingOptimization->StartNetNames").split("[,]");
41
42        ASSERT((start_net_names.size() > 0),
43               "[Error] Expecting net names in TimingOptimization->StartNetNames");
44
45        if(start_net_names[0] == "*")
46        {
47            // Optimize from all input ports
48            ElectricalModel* electrical_model = (ElectricalModel*)ms_model;
49
50            ElectricalTimingOptimizer timing_optimizer(
51                    "Optimizer", electrical_model->getTechModel());
52            timing_optimizer.setModel(electrical_model);
53            timing_optimizer.construct();
54            timing_optimizer.update();
55
56            ElectricalTimingTree timing_tree(
57                    timing_optimizer.getInstanceName(), &timing_optimizer);
58
59            const Map<PortInfo*>* input_ports = timing_optimizer.getInputs();
60            Map<PortInfo*>::ConstIterator it_begin = input_ports->begin();
61            Map<PortInfo*>::ConstIterator it_end = input_ports->end();
62            Map<PortInfo*>::ConstIterator it;
63            for(it = it_begin; it != it_end; ++it)
64            {
65                const String& net_name = it->first;
66                Log::printLine("Optimizing net: " + net_name);
67                timing_tree.performTimingOpt(
68                        timing_optimizer.getNet(net_name, makeNetIndex(0)), 1.0 / freq);
69            }
70
71            // Loop the second times
72            for(it = it_begin; it != it_end; ++it)
73            {
74                const String& net_name = it->first;
75                Log::printLine("Optimizing net: " + net_name);
76            }
77        }
78        else
79        {
80            // TODO : parse the net name so that we could do hierarchical optimization
81            // Currently we can only optimize timing at the top level
82            ElectricalModel* electrical_model = (ElectricalModel*)ms_model;
83            ElectricalTimingTree timing_tree(
84                    electrical_model->getInstanceName(), electrical_model);
85
86            for(unsigned int i = 0; i < start_net_names.size(); ++i)
87            {
88                const String& net_name = start_net_names[i];
89                timing_tree.performTimingOpt(
90                        electrical_model->getNet(net_name), 1.0 / freq);
91            }
92        }
93    }
94
95    static void reportTiming(const map<String, String> &params, Model *ms_model)
96    {
97        // Get all the starting net names
98        const vector<String>& start_net_names =
99            params.at("ReportTiming->StartNetNames").split("[,]");
100
101        ElectricalModel* electrical_model = (ElectricalModel*)ms_model;
102        ElectricalTimingTree timing_tree(
103                electrical_model->getInstanceName(), electrical_model);
104
105        cout << "Report timing:" << endl;
106        cout << "==============" << endl;
107        for(unsigned int i = 0; i < start_net_names.size(); ++i)
108        {
109            const String& net_name = start_net_names[i];
110            double timing = timing_tree.performCritPathExtract(electrical_model->getNet(net_name));
111            cout << net_name << " = " << timing << endl;
112        }
113        cout << "==============" << endl;
114    }
115
116    static Model *buildModel(const map<String, String> &params,
117                             TechModel *tech_model)
118    {
119        // Create the model specified
120        const String& model_name = params.at("ModelName");
121        Model *ms_model = ModelGen::createModel(model_name, model_name,
122                                                tech_model);
123
124        // Construct the model
125        // Read all parameters the model requires
126        const vector<String>* parameter_names = ms_model->getParameterNames();
127        // For all parameters, grab values from the config file
128        for(vector<String>::const_iterator it = parameter_names->begin();
129            it != parameter_names->end(); ++it)
130        {
131            const String& parameter_name = *it;
132            // If it exists in the config file, set the parameter
133            if(params.count(parameter_name) > 0)
134            {
135                ms_model->setParameter(parameter_name,
136                                       params.at(parameter_name));
137            }
138        }
139
140        ms_model->construct();
141
142        // Update the model
143        // Read all properties the model requires
144        const vector<String>* property_names = ms_model->getPropertyNames();
145        // For all properties, grab values from the config file
146        for(vector<String>::const_iterator it = property_names->begin();
147            it != property_names->end(); ++it)
148        {
149            const String& property_name = *it;
150            // If it exists in the config file, set the parameter
151            if(params.count(property_name) > 0)
152            {
153                ms_model->setProperty(property_name,
154                                      params.at(property_name));
155            }
156        }
157        ms_model->update();
158
159        // Evaluate the model
160        // Perform timing optimization if needed
161        if(params.find("IsPerformTimingOptimization") != params.end() &&
162           params.at("IsPerformTimingOptimization").toBool())
163        {
164            performTimingOpt(params, ms_model);
165        }
166        ms_model->evaluate();
167
168        // Report timing if needed
169        if(params.count("IsReportTiming") > 0 &&
170           params.at("IsReportTiming") != "false")
171        {
172            reportTiming(params, ms_model);
173        }
174
175        return ms_model;
176    }
177
178    static const void* processQuery(const String& query_str_,
179                                    Model *ms_model, bool is_print_)
180    {
181        vector<String> type_split = query_str_.splitByString(Model::TYPE_SEPARATOR);
182        ASSERT((type_split.size() == 2), "[Error] Invalid query format: " + query_str_);
183        String query_type = type_split[0];
184
185        vector<String> detail_split =
186            type_split[1].splitByString(Model::DETAIL_SEPARATOR);
187
188        ASSERT((detail_split.size() == 2), "[Error] Invalid query format: " + query_str_);
189        String query_detail = detail_split[1];
190
191        vector<String> subfield_split =
192            detail_split[0].splitByString(Model::SUBFIELD_SEPARATOR);
193
194        ASSERT(((subfield_split.size() == 2) || (subfield_split.size() == 1)),
195               "[Error] Invalid query format: " + query_str_);
196
197        String query_hier = subfield_split[0];
198        String query_subfield = "";
199
200        if(subfield_split.size() == 2)
201        {
202            query_subfield = subfield_split[1];
203        }
204
205        const void* query_result = ms_model->parseQuery(query_type, query_hier,
206                                                        query_subfield);
207        if(query_type == "Property")
208        {
209            const PropertyMap* property = (const PropertyMap*)query_result;
210            if(is_print_)
211            {
212                cout << *property;
213            }
214        }
215        else if(query_type == "Parameter")
216        {
217            const ParameterMap* parameter = (const ParameterMap*)query_result;
218            if(is_print_)
219            {
220                cout << *parameter;
221            }
222        }
223        else if(query_type.contain("Hier"))
224        {
225            const Model* model = (const Model*)query_result;
226            if(is_print_)
227            {
228                model->printHierarchy(query_type, query_subfield, "", query_detail, cout);
229            }
230        }
231        else
232        {
233            const Result* result = (const Result*)query_result;
234            if(is_print_)
235            {
236                result->print(query_type + Model::TYPE_SEPARATOR + query_hier +
237                        Model::SUBFIELD_SEPARATOR + query_subfield, query_detail, cout);
238            }
239        }
240        return query_result;
241    }
242
243    void processQuery(const vector<String> &queries,
244                      Model *ms_model, vector<String> &outputs)
245    {
246        for(unsigned int i = 0; i < queries.size(); ++i)
247        {
248            const String& curr_query = queries[i];
249            processQuery(curr_query, ms_model, true);
250
251        }
252    }
253
254    static TechModel* constructTechModel(const map<String, String>& params)
255    {
256        // Allocate static TechModel instance
257        const String& electrical_tech_model_filename =
258            params.at("ElectricalTechModelFilename");
259
260        TechModel* tech_model = new TechModel();
261        tech_model->readFile(electrical_tech_model_filename);
262
263        if (params.count("PhotonicTechModelFilename") != 0) {
264            const String& photonic_tech_model_filename =
265                params.at("PhotonicTechModelFilename");
266            tech_model->readFile(photonic_tech_model_filename);
267        }
268
269        // Allocate static StdCellLib instance
270        StdCellLib* std_cell_lib = new StdCellLib(tech_model);
271
272        // Set the StdCellLib pointer in static TechModel instance
273        tech_model->setStdCellLib(std_cell_lib);
274        return tech_model;
275    }
276
277    Model *initialize(const char *config_file_name, map<String, String> &config)
278    {
279        // Init the log file
280        Log::allocate("/tmp/dsent.log");
281
282        // Init the config file
283        LibUtil::readFile(config_file_name, config);
284
285        // Overwrite the technology file
286        TechModel *tech_model = constructTechModel(config);
287
288        // Build the specified model in the config file
289        return buildModel(config, tech_model);
290    }
291
292    void finalize(map<String, String> &config, Model *ms_model)
293    {
294        // Delete the model
295        delete ms_model;
296
297        // Discard all the (key, value) pairs.
298        config.clear();
299
300        // Release the log file
301        Log::release();
302    }
303
304    void run(const map<String, String> &params, Model *ms_model,
305             map<string, double> &outputs)
306    {
307        // Process the specified queries
308        const auto &it = params.find("EvaluateString");
309        if(it == params.end()) {
310            return;
311        }
312
313        String eval_str = it->second;
314
315        if (eval_str == "") {
316            return;
317        }
318
319        DSENTCalculator calc;
320        calc.evaluateString(eval_str, params, ms_model, outputs);
321    }
322
323    DSENTCalculator::DSENTCalculator() {}
324
325    DSENTCalculator::~DSENTCalculator() {}
326
327    double DSENTCalculator::getEnvVar(const String& var_name_,
328                                      const map<String, String> &config,
329                                      Model *ms_model) const
330    {
331        if (m_var_.keyExist(var_name_)) {
332            return m_var_.get(var_name_);
333        } else if (config.count(var_name_) > 0) {
334            return config.at(var_name_);
335        } else {
336            // Wish there was a way to not have to pass in a stream if we aren't
337            // doing anything with it
338            const Result* result = (const Result*)DSENT::processQuery(
339                var_name_ + "@0", ms_model, false);
340            return result->calculateSum();
341        }
342    }
343} // namespace DSENT
344