110448Snilay@cs.wisc.edu/* Copyright (c) 2012 Massachusetts Institute of Technology
210448Snilay@cs.wisc.edu *
310448Snilay@cs.wisc.edu * Permission is hereby granted, free of charge, to any person obtaining a copy
410448Snilay@cs.wisc.edu * of this software and associated documentation files (the "Software"), to deal
510448Snilay@cs.wisc.edu * in the Software without restriction, including without limitation the rights
610448Snilay@cs.wisc.edu * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
710448Snilay@cs.wisc.edu * copies of the Software, and to permit persons to whom the Software is
810448Snilay@cs.wisc.edu * furnished to do so, subject to the following conditions:
910448Snilay@cs.wisc.edu *
1010448Snilay@cs.wisc.edu * The above copyright notice and this permission notice shall be included in
1110448Snilay@cs.wisc.edu * all copies or substantial portions of the Software.
1210448Snilay@cs.wisc.edu *
1310448Snilay@cs.wisc.edu * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1410448Snilay@cs.wisc.edu * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1510448Snilay@cs.wisc.edu * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1610448Snilay@cs.wisc.edu * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1710448Snilay@cs.wisc.edu * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1810448Snilay@cs.wisc.edu * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1910448Snilay@cs.wisc.edu * THE SOFTWARE.
2010448Snilay@cs.wisc.edu */
2110447Snilay@cs.wisc.edu
2210447Snilay@cs.wisc.edu#include <cstdlib>
2310447Snilay@cs.wisc.edu#include <iostream>
2410447Snilay@cs.wisc.edu
2510448Snilay@cs.wisc.edu#include "DSENT.h"
2610448Snilay@cs.wisc.edu#include "model/std_cells/StdCellLib.h"
2710448Snilay@cs.wisc.edu
2810448Snilay@cs.wisc.eduusing namespace std;
2910448Snilay@cs.wisc.edu
3010447Snilay@cs.wisc.edunamespace DSENT
3110447Snilay@cs.wisc.edu{
3210448Snilay@cs.wisc.edu    static void performTimingOpt(const map<String, String> &params,
3310448Snilay@cs.wisc.edu                                 Model *ms_model)
3410448Snilay@cs.wisc.edu    {
3510448Snilay@cs.wisc.edu        // Get the frequency it is optimizing to
3610448Snilay@cs.wisc.edu        double freq = params.at("Frequency").toDouble();
3710447Snilay@cs.wisc.edu
3810448Snilay@cs.wisc.edu        // Get all the starting net names
3910448Snilay@cs.wisc.edu        const vector<String>& start_net_names =
4010448Snilay@cs.wisc.edu            params.at("TimingOptimization->StartNetNames").split("[,]");
4110447Snilay@cs.wisc.edu
4210448Snilay@cs.wisc.edu        ASSERT((start_net_names.size() > 0),
4310448Snilay@cs.wisc.edu               "[Error] Expecting net names in TimingOptimization->StartNetNames");
4410447Snilay@cs.wisc.edu
4510448Snilay@cs.wisc.edu        if(start_net_names[0] == "*")
4610448Snilay@cs.wisc.edu        {
4710448Snilay@cs.wisc.edu            // Optimize from all input ports
4810448Snilay@cs.wisc.edu            ElectricalModel* electrical_model = (ElectricalModel*)ms_model;
4910447Snilay@cs.wisc.edu
5010448Snilay@cs.wisc.edu            ElectricalTimingOptimizer timing_optimizer(
5110448Snilay@cs.wisc.edu                    "Optimizer", electrical_model->getTechModel());
5210448Snilay@cs.wisc.edu            timing_optimizer.setModel(electrical_model);
5310448Snilay@cs.wisc.edu            timing_optimizer.construct();
5410448Snilay@cs.wisc.edu            timing_optimizer.update();
5510448Snilay@cs.wisc.edu
5610448Snilay@cs.wisc.edu            ElectricalTimingTree timing_tree(
5710448Snilay@cs.wisc.edu                    timing_optimizer.getInstanceName(), &timing_optimizer);
5810448Snilay@cs.wisc.edu
5910448Snilay@cs.wisc.edu            const Map<PortInfo*>* input_ports = timing_optimizer.getInputs();
6010448Snilay@cs.wisc.edu            Map<PortInfo*>::ConstIterator it_begin = input_ports->begin();
6110448Snilay@cs.wisc.edu            Map<PortInfo*>::ConstIterator it_end = input_ports->end();
6210448Snilay@cs.wisc.edu            Map<PortInfo*>::ConstIterator it;
6310448Snilay@cs.wisc.edu            for(it = it_begin; it != it_end; ++it)
6410448Snilay@cs.wisc.edu            {
6510448Snilay@cs.wisc.edu                const String& net_name = it->first;
6610448Snilay@cs.wisc.edu                Log::printLine("Optimizing net: " + net_name);
6710448Snilay@cs.wisc.edu                timing_tree.performTimingOpt(
6810448Snilay@cs.wisc.edu                        timing_optimizer.getNet(net_name, makeNetIndex(0)), 1.0 / freq);
6910448Snilay@cs.wisc.edu            }
7010448Snilay@cs.wisc.edu
7110448Snilay@cs.wisc.edu            // Loop the second times
7210448Snilay@cs.wisc.edu            for(it = it_begin; it != it_end; ++it)
7310448Snilay@cs.wisc.edu            {
7410448Snilay@cs.wisc.edu                const String& net_name = it->first;
7510448Snilay@cs.wisc.edu                Log::printLine("Optimizing net: " + net_name);
7610448Snilay@cs.wisc.edu            }
7710448Snilay@cs.wisc.edu        }
7810448Snilay@cs.wisc.edu        else
7910448Snilay@cs.wisc.edu        {
8010448Snilay@cs.wisc.edu            // TODO : parse the net name so that we could do hierarchical optimization
8110448Snilay@cs.wisc.edu            // Currently we can only optimize timing at the top level
8210448Snilay@cs.wisc.edu            ElectricalModel* electrical_model = (ElectricalModel*)ms_model;
8310448Snilay@cs.wisc.edu            ElectricalTimingTree timing_tree(
8410448Snilay@cs.wisc.edu                    electrical_model->getInstanceName(), electrical_model);
8510448Snilay@cs.wisc.edu
8610448Snilay@cs.wisc.edu            for(unsigned int i = 0; i < start_net_names.size(); ++i)
8710448Snilay@cs.wisc.edu            {
8810448Snilay@cs.wisc.edu                const String& net_name = start_net_names[i];
8910448Snilay@cs.wisc.edu                timing_tree.performTimingOpt(
9010448Snilay@cs.wisc.edu                        electrical_model->getNet(net_name), 1.0 / freq);
9110448Snilay@cs.wisc.edu            }
9210448Snilay@cs.wisc.edu        }
9310447Snilay@cs.wisc.edu    }
9410447Snilay@cs.wisc.edu
9510448Snilay@cs.wisc.edu    static void reportTiming(const map<String, String> &params, Model *ms_model)
9610447Snilay@cs.wisc.edu    {
9710448Snilay@cs.wisc.edu        // Get all the starting net names
9810448Snilay@cs.wisc.edu        const vector<String>& start_net_names =
9910448Snilay@cs.wisc.edu            params.at("ReportTiming->StartNetNames").split("[,]");
10010447Snilay@cs.wisc.edu
10110448Snilay@cs.wisc.edu        ElectricalModel* electrical_model = (ElectricalModel*)ms_model;
10210448Snilay@cs.wisc.edu        ElectricalTimingTree timing_tree(
10310448Snilay@cs.wisc.edu                electrical_model->getInstanceName(), electrical_model);
10410447Snilay@cs.wisc.edu
10510448Snilay@cs.wisc.edu        cout << "Report timing:" << endl;
10610448Snilay@cs.wisc.edu        cout << "==============" << endl;
10710448Snilay@cs.wisc.edu        for(unsigned int i = 0; i < start_net_names.size(); ++i)
10810448Snilay@cs.wisc.edu        {
10910448Snilay@cs.wisc.edu            const String& net_name = start_net_names[i];
11010448Snilay@cs.wisc.edu            double timing = timing_tree.performCritPathExtract(electrical_model->getNet(net_name));
11110448Snilay@cs.wisc.edu            cout << net_name << " = " << timing << endl;
11210448Snilay@cs.wisc.edu        }
11310448Snilay@cs.wisc.edu        cout << "==============" << endl;
11410447Snilay@cs.wisc.edu    }
11510447Snilay@cs.wisc.edu
11610448Snilay@cs.wisc.edu    static Model *buildModel(const map<String, String> &params,
11710448Snilay@cs.wisc.edu                             TechModel *tech_model)
11810447Snilay@cs.wisc.edu    {
11910448Snilay@cs.wisc.edu        // Create the model specified
12010448Snilay@cs.wisc.edu        const String& model_name = params.at("ModelName");
12110448Snilay@cs.wisc.edu        Model *ms_model = ModelGen::createModel(model_name, model_name,
12210448Snilay@cs.wisc.edu                                                tech_model);
12310447Snilay@cs.wisc.edu
12410448Snilay@cs.wisc.edu        // Construct the model
12510448Snilay@cs.wisc.edu        // Read all parameters the model requires
12610448Snilay@cs.wisc.edu        const vector<String>* parameter_names = ms_model->getParameterNames();
12710448Snilay@cs.wisc.edu        // For all parameters, grab values from the config file
12810448Snilay@cs.wisc.edu        for(vector<String>::const_iterator it = parameter_names->begin();
12910448Snilay@cs.wisc.edu            it != parameter_names->end(); ++it)
13010447Snilay@cs.wisc.edu        {
13110448Snilay@cs.wisc.edu            const String& parameter_name = *it;
13210448Snilay@cs.wisc.edu            // If it exists in the config file, set the parameter
13310448Snilay@cs.wisc.edu            if(params.count(parameter_name) > 0)
13410447Snilay@cs.wisc.edu            {
13510448Snilay@cs.wisc.edu                ms_model->setParameter(parameter_name,
13610448Snilay@cs.wisc.edu                                       params.at(parameter_name));
13710447Snilay@cs.wisc.edu            }
13810447Snilay@cs.wisc.edu        }
13910447Snilay@cs.wisc.edu
14010448Snilay@cs.wisc.edu        ms_model->construct();
14110447Snilay@cs.wisc.edu
14210447Snilay@cs.wisc.edu        // Update the model
14310447Snilay@cs.wisc.edu        // Read all properties the model requires
14410448Snilay@cs.wisc.edu        const vector<String>* property_names = ms_model->getPropertyNames();
14510447Snilay@cs.wisc.edu        // For all properties, grab values from the config file
14610448Snilay@cs.wisc.edu        for(vector<String>::const_iterator it = property_names->begin();
14710448Snilay@cs.wisc.edu            it != property_names->end(); ++it)
14810447Snilay@cs.wisc.edu        {
14910447Snilay@cs.wisc.edu            const String& property_name = *it;
15010447Snilay@cs.wisc.edu            // If it exists in the config file, set the parameter
15110448Snilay@cs.wisc.edu            if(params.count(property_name) > 0)
15210447Snilay@cs.wisc.edu            {
15310448Snilay@cs.wisc.edu                ms_model->setProperty(property_name,
15410448Snilay@cs.wisc.edu                                      params.at(property_name));
15510447Snilay@cs.wisc.edu            }
15610447Snilay@cs.wisc.edu        }
15710448Snilay@cs.wisc.edu        ms_model->update();
15810447Snilay@cs.wisc.edu
15910447Snilay@cs.wisc.edu        // Evaluate the model
16010447Snilay@cs.wisc.edu        // Perform timing optimization if needed
16110448Snilay@cs.wisc.edu        if(params.find("IsPerformTimingOptimization") != params.end() &&
16210448Snilay@cs.wisc.edu           params.at("IsPerformTimingOptimization").toBool())
16310447Snilay@cs.wisc.edu        {
16410448Snilay@cs.wisc.edu            performTimingOpt(params, ms_model);
16510447Snilay@cs.wisc.edu        }
16610448Snilay@cs.wisc.edu        ms_model->evaluate();
16710447Snilay@cs.wisc.edu
16810447Snilay@cs.wisc.edu        // Report timing if needed
16910448Snilay@cs.wisc.edu        if(params.count("IsReportTiming") > 0 &&
17010448Snilay@cs.wisc.edu           params.at("IsReportTiming") != "false")
17110447Snilay@cs.wisc.edu        {
17210448Snilay@cs.wisc.edu            reportTiming(params, ms_model);
17310447Snilay@cs.wisc.edu        }
17410447Snilay@cs.wisc.edu
17510448Snilay@cs.wisc.edu        return ms_model;
17610447Snilay@cs.wisc.edu    }
17710447Snilay@cs.wisc.edu
17810448Snilay@cs.wisc.edu    static const void* processQuery(const String& query_str_,
17910448Snilay@cs.wisc.edu                                    Model *ms_model, bool is_print_)
18010447Snilay@cs.wisc.edu    {
18110447Snilay@cs.wisc.edu        vector<String> type_split = query_str_.splitByString(Model::TYPE_SEPARATOR);
18210447Snilay@cs.wisc.edu        ASSERT((type_split.size() == 2), "[Error] Invalid query format: " + query_str_);
18310447Snilay@cs.wisc.edu        String query_type = type_split[0];
18410447Snilay@cs.wisc.edu
18510448Snilay@cs.wisc.edu        vector<String> detail_split =
18610448Snilay@cs.wisc.edu            type_split[1].splitByString(Model::DETAIL_SEPARATOR);
18710448Snilay@cs.wisc.edu
18810447Snilay@cs.wisc.edu        ASSERT((detail_split.size() == 2), "[Error] Invalid query format: " + query_str_);
18910447Snilay@cs.wisc.edu        String query_detail = detail_split[1];
19010447Snilay@cs.wisc.edu
19110448Snilay@cs.wisc.edu        vector<String> subfield_split =
19210448Snilay@cs.wisc.edu            detail_split[0].splitByString(Model::SUBFIELD_SEPARATOR);
19310448Snilay@cs.wisc.edu
19410448Snilay@cs.wisc.edu        ASSERT(((subfield_split.size() == 2) || (subfield_split.size() == 1)),
19510448Snilay@cs.wisc.edu               "[Error] Invalid query format: " + query_str_);
19610448Snilay@cs.wisc.edu
19710447Snilay@cs.wisc.edu        String query_hier = subfield_split[0];
19810447Snilay@cs.wisc.edu        String query_subfield = "";
19910448Snilay@cs.wisc.edu
20010447Snilay@cs.wisc.edu        if(subfield_split.size() == 2)
20110447Snilay@cs.wisc.edu        {
20210447Snilay@cs.wisc.edu            query_subfield = subfield_split[1];
20310447Snilay@cs.wisc.edu        }
20410447Snilay@cs.wisc.edu
20510448Snilay@cs.wisc.edu        const void* query_result = ms_model->parseQuery(query_type, query_hier,
20610448Snilay@cs.wisc.edu                                                        query_subfield);
20710447Snilay@cs.wisc.edu        if(query_type == "Property")
20810447Snilay@cs.wisc.edu        {
20910447Snilay@cs.wisc.edu            const PropertyMap* property = (const PropertyMap*)query_result;
21010447Snilay@cs.wisc.edu            if(is_print_)
21110447Snilay@cs.wisc.edu            {
21210447Snilay@cs.wisc.edu                cout << *property;
21310447Snilay@cs.wisc.edu            }
21410447Snilay@cs.wisc.edu        }
21510447Snilay@cs.wisc.edu        else if(query_type == "Parameter")
21610447Snilay@cs.wisc.edu        {
21710447Snilay@cs.wisc.edu            const ParameterMap* parameter = (const ParameterMap*)query_result;
21810447Snilay@cs.wisc.edu            if(is_print_)
21910447Snilay@cs.wisc.edu            {
22010447Snilay@cs.wisc.edu                cout << *parameter;
22110447Snilay@cs.wisc.edu            }
22210447Snilay@cs.wisc.edu        }
22310447Snilay@cs.wisc.edu        else if(query_type.contain("Hier"))
22410447Snilay@cs.wisc.edu        {
22510447Snilay@cs.wisc.edu            const Model* model = (const Model*)query_result;
22610447Snilay@cs.wisc.edu            if(is_print_)
22710447Snilay@cs.wisc.edu            {
22810447Snilay@cs.wisc.edu                model->printHierarchy(query_type, query_subfield, "", query_detail, cout);
22910447Snilay@cs.wisc.edu            }
23010447Snilay@cs.wisc.edu        }
23110447Snilay@cs.wisc.edu        else
23210447Snilay@cs.wisc.edu        {
23310447Snilay@cs.wisc.edu            const Result* result = (const Result*)query_result;
23410447Snilay@cs.wisc.edu            if(is_print_)
23510447Snilay@cs.wisc.edu            {
23610447Snilay@cs.wisc.edu                result->print(query_type + Model::TYPE_SEPARATOR + query_hier +
23710447Snilay@cs.wisc.edu                        Model::SUBFIELD_SEPARATOR + query_subfield, query_detail, cout);
23810447Snilay@cs.wisc.edu            }
23910447Snilay@cs.wisc.edu        }
24010447Snilay@cs.wisc.edu        return query_result;
24110447Snilay@cs.wisc.edu    }
24210447Snilay@cs.wisc.edu
24310448Snilay@cs.wisc.edu    void processQuery(const vector<String> &queries,
24410448Snilay@cs.wisc.edu                      Model *ms_model, vector<String> &outputs)
24510447Snilay@cs.wisc.edu    {
24610448Snilay@cs.wisc.edu        for(unsigned int i = 0; i < queries.size(); ++i)
24710448Snilay@cs.wisc.edu        {
24810448Snilay@cs.wisc.edu            const String& curr_query = queries[i];
24910448Snilay@cs.wisc.edu            processQuery(curr_query, ms_model, true);
25010447Snilay@cs.wisc.edu
25110448Snilay@cs.wisc.edu        }
25210448Snilay@cs.wisc.edu    }
25310448Snilay@cs.wisc.edu
25410448Snilay@cs.wisc.edu    static TechModel* constructTechModel(const map<String, String>& params)
25510448Snilay@cs.wisc.edu    {
25610448Snilay@cs.wisc.edu        // Allocate static TechModel instance
25710448Snilay@cs.wisc.edu        const String& electrical_tech_model_filename =
25810448Snilay@cs.wisc.edu            params.at("ElectricalTechModelFilename");
25910448Snilay@cs.wisc.edu
26010448Snilay@cs.wisc.edu        TechModel* tech_model = new TechModel();
26110448Snilay@cs.wisc.edu        tech_model->readFile(electrical_tech_model_filename);
26210448Snilay@cs.wisc.edu
26310448Snilay@cs.wisc.edu        if (params.count("PhotonicTechModelFilename") != 0) {
26410448Snilay@cs.wisc.edu            const String& photonic_tech_model_filename =
26510448Snilay@cs.wisc.edu                params.at("PhotonicTechModelFilename");
26610448Snilay@cs.wisc.edu            tech_model->readFile(photonic_tech_model_filename);
26710448Snilay@cs.wisc.edu        }
26810448Snilay@cs.wisc.edu
26910448Snilay@cs.wisc.edu        // Allocate static StdCellLib instance
27010448Snilay@cs.wisc.edu        StdCellLib* std_cell_lib = new StdCellLib(tech_model);
27110448Snilay@cs.wisc.edu
27210448Snilay@cs.wisc.edu        // Set the StdCellLib pointer in static TechModel instance
27310448Snilay@cs.wisc.edu        tech_model->setStdCellLib(std_cell_lib);
27410448Snilay@cs.wisc.edu        return tech_model;
27510448Snilay@cs.wisc.edu    }
27610448Snilay@cs.wisc.edu
27710448Snilay@cs.wisc.edu    Model *initialize(const char *config_file_name, map<String, String> &config)
27810448Snilay@cs.wisc.edu    {
27910448Snilay@cs.wisc.edu        // Init the log file
28010448Snilay@cs.wisc.edu        Log::allocate("/tmp/dsent.log");
28110448Snilay@cs.wisc.edu
28210448Snilay@cs.wisc.edu        // Init the config file
28310448Snilay@cs.wisc.edu        LibUtil::readFile(config_file_name, config);
28410448Snilay@cs.wisc.edu
28510448Snilay@cs.wisc.edu        // Overwrite the technology file
28610448Snilay@cs.wisc.edu        TechModel *tech_model = constructTechModel(config);
28710448Snilay@cs.wisc.edu
28810448Snilay@cs.wisc.edu        // Build the specified model in the config file
28910448Snilay@cs.wisc.edu        return buildModel(config, tech_model);
29010448Snilay@cs.wisc.edu    }
29110448Snilay@cs.wisc.edu
29210448Snilay@cs.wisc.edu    void finalize(map<String, String> &config, Model *ms_model)
29310448Snilay@cs.wisc.edu    {
29410448Snilay@cs.wisc.edu        // Delete the model
29510448Snilay@cs.wisc.edu        delete ms_model;
29610448Snilay@cs.wisc.edu
29710448Snilay@cs.wisc.edu        // Discard all the (key, value) pairs.
29810448Snilay@cs.wisc.edu        config.clear();
29910447Snilay@cs.wisc.edu
30010447Snilay@cs.wisc.edu        // Release the log file
30110447Snilay@cs.wisc.edu        Log::release();
30210447Snilay@cs.wisc.edu    }
30310447Snilay@cs.wisc.edu
30410448Snilay@cs.wisc.edu    void run(const map<String, String> &params, Model *ms_model,
30510448Snilay@cs.wisc.edu             map<string, double> &outputs)
30610447Snilay@cs.wisc.edu    {
30710448Snilay@cs.wisc.edu        // Process the specified queries
30810448Snilay@cs.wisc.edu        const auto &it = params.find("EvaluateString");
30910448Snilay@cs.wisc.edu        if(it == params.end()) {
31010448Snilay@cs.wisc.edu            return;
31110448Snilay@cs.wisc.edu        }
31210447Snilay@cs.wisc.edu
31310448Snilay@cs.wisc.edu        String eval_str = it->second;
31410447Snilay@cs.wisc.edu
31510448Snilay@cs.wisc.edu        if (eval_str == "") {
31610448Snilay@cs.wisc.edu            return;
31710448Snilay@cs.wisc.edu        }
31810447Snilay@cs.wisc.edu
31910448Snilay@cs.wisc.edu        DSENTCalculator calc;
32010448Snilay@cs.wisc.edu        calc.evaluateString(eval_str, params, ms_model, outputs);
32110447Snilay@cs.wisc.edu    }
32210447Snilay@cs.wisc.edu
32310448Snilay@cs.wisc.edu    DSENTCalculator::DSENTCalculator() {}
32410448Snilay@cs.wisc.edu
32510448Snilay@cs.wisc.edu    DSENTCalculator::~DSENTCalculator() {}
32610448Snilay@cs.wisc.edu
32710448Snilay@cs.wisc.edu    double DSENTCalculator::getEnvVar(const String& var_name_,
32810448Snilay@cs.wisc.edu                                      const map<String, String> &config,
32910448Snilay@cs.wisc.edu                                      Model *ms_model) const
33010447Snilay@cs.wisc.edu    {
33110448Snilay@cs.wisc.edu        if (m_var_.keyExist(var_name_)) {
33210447Snilay@cs.wisc.edu            return m_var_.get(var_name_);
33310448Snilay@cs.wisc.edu        } else if (config.count(var_name_) > 0) {
33410448Snilay@cs.wisc.edu            return config.at(var_name_);
33510448Snilay@cs.wisc.edu        } else {
33610448Snilay@cs.wisc.edu            // Wish there was a way to not have to pass in a stream if we aren't
33710448Snilay@cs.wisc.edu            // doing anything with it
33810448Snilay@cs.wisc.edu            const Result* result = (const Result*)DSENT::processQuery(
33910448Snilay@cs.wisc.edu                var_name_ + "@0", ms_model, false);
34010447Snilay@cs.wisc.edu            return result->calculateSum();
34110447Snilay@cs.wisc.edu        }
34210447Snilay@cs.wisc.edu    }
34310447Snilay@cs.wisc.edu} // namespace DSENT
344