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/electrical/MuxTreeSerializer.h"
23
24#include <cmath>
25
26#include "model/PortInfo.h"
27#include "model/TransitionInfo.h"
28#include "model/EventInfo.h"
29#include "model/std_cells/StdCellLib.h"
30#include "model/std_cells/StdCell.h"
31#include "model/electrical/Multiplexer.h"
32#include "model/timing_graph/ElectricalNet.h"
33
34namespace DSENT
35{
36    using std::ceil;
37
38    MuxTreeSerializer::MuxTreeSerializer(const String& instance_name_, const TechModel* tech_model_)
39        : ElectricalModel(instance_name_, tech_model_)
40    {
41        initParameters();
42        initProperties();
43    }
44
45    MuxTreeSerializer::~MuxTreeSerializer()
46    {}
47
48    void MuxTreeSerializer::initParameters()
49    {
50        addParameterName("InDataRate");
51        addParameterName("OutDataRate");
52        addParameterName("InBits");              //Output width will just be input width / serialization ratio
53    }
54
55    void MuxTreeSerializer::initProperties()
56    {
57        return;
58    }
59
60    MuxTreeSerializer* MuxTreeSerializer::clone() const
61    {
62        // TODO
63        return NULL;
64    }
65
66    void MuxTreeSerializer::constructModel()
67    {
68        // Get parameters
69        double in_data_rate = getParameter("InDataRate").toDouble();
70        double out_data_rate = getParameter("OutDataRate").toDouble();
71        unsigned int in_bits = getParameter("InBits").toUInt();
72
73        // Calculate serialization ratio
74        unsigned int serialization_ratio = (unsigned int) floor(out_data_rate / in_data_rate);
75        ASSERT(serialization_ratio == out_data_rate / in_data_rate,
76            "[Error] " + getInstanceName() + " -> Cannot have non-integer serialization ratios " +
77            "(" + (String) (in_data_rate / out_data_rate) + ")!");
78
79        // Calculate output width
80        ASSERT(floor((double) in_bits / serialization_ratio) == (double) in_bits / serialization_ratio,
81            "[Error] " + getInstanceName() + " -> Input width (" + (String) in_bits + ") " +
82            "must be a multiple of the serialization ratio (" + (String) serialization_ratio + ")!");
83        unsigned int output_bits = in_bits / serialization_ratio;
84
85        // Calculate the number of multiplexer stages
86        unsigned int number_stages = (unsigned int)ceil(log2((double) serialization_ratio));
87
88        // Store calculated values
89        getGenProperties()->set("SerializationRatio", serialization_ratio);
90        getGenProperties()->set("OutputBits", output_bits);
91        getGenProperties()->set("NumberStages", number_stages);
92
93        // Create ports
94        createInputPort("In", makeNetIndex(0, in_bits-1));
95        createInputPort("OutCK");
96        createOutputPort("Out", makeNetIndex(0, output_bits-1));
97
98        //Create energy, power, and area results
99        createElectricalResults();
100        createElectricalEventResult("Serialize");
101        getEventInfo("Serialize")->setTransitionInfo("OutCK", TransitionInfo(0.0, (double) serialization_ratio / 2.0, 0.0));
102        //Set conditions during idle state
103        getEventInfo("Idle")->setStaticTransitionInfos();
104        getEventInfo("Idle")->setTransitionInfo("OutCK", TransitionInfo(0.0, (double) serialization_ratio / 2.0, 0.0));
105
106        // Mark OutCK as a false path (since timing tool will do strange stuff due to all the clock divides and stuff)
107        getNet("OutCK")->setFalsePath(true);
108
109        // Create mux-tree instance
110        if (serialization_ratio == 1)
111        {
112            // No need to do anything, hohoho
113            assign("Out", "In");
114        }
115        else
116        {
117            // Create multiplexer
118            String mux_tree_name = "MuxTree";
119            ElectricalModel* mux_tree = new Multiplexer(mux_tree_name, getTechModel());
120            mux_tree->setParameter("NumberInputs", serialization_ratio);
121            mux_tree->setParameter("NumberBits", output_bits);
122            mux_tree->setParameter("BitDuplicate", "TRUE");
123            mux_tree->construct();
124            // Create nets
125            if (number_stages > 1)
126                createNet("MuxSel_b", makeNetIndex(0, number_stages-2));
127            createNet("MuxSel", makeNetIndex(0, number_stages-1));
128            assign("MuxSel", makeNetIndex(number_stages-1), "OutCK");
129            // Create reindexed net (to help out with indexing)
130            createNet("InTmp", makeNetIndex(0, in_bits-1));
131            for (unsigned int i = 0; i < serialization_ratio; ++i)
132                for (unsigned int j = 0; j < output_bits; ++j)
133                    assign("InTmp", makeNetIndex(i*output_bits+j), "In", makeNetIndex(j*serialization_ratio+i));
134
135            // Connect ports
136            for (unsigned int i = 0; i < serialization_ratio; ++i)
137                portConnect(mux_tree, "In" + (String) i, "InTmp", makeNetIndex(i*output_bits, (i+1)*output_bits-1));
138
139            for (unsigned int i = 0; i < number_stages; ++i)
140                portConnect(mux_tree, "Sel" + (String) i, "MuxSel", makeNetIndex(i));
141            portConnect(mux_tree, "Out", "Out");
142
143            // Add subinstance and events
144            addSubInstances(mux_tree, 1.0);
145            addElectricalSubResults(mux_tree, 1.0);
146            // Add serialize event/power
147            getEventResult("Serialize")->addSubResult(mux_tree->getEventResult("Mux"), mux_tree_name, 1.0);
148
149            // Create clock dividers (assumes power of 2...), don't need divider for fastest output stage
150            for (unsigned int i = 0; i < number_stages - 1; ++i)
151            {
152                // Clk dividing registers
153                const String& clk_div_dff_name = "ClkDivDFF_" + (String) i;
154                StdCell* clk_div_dff = getTechModel()->getStdCellLib()->createStdCell("DFFQ", clk_div_dff_name);
155                clk_div_dff->construct();
156                portConnect(clk_div_dff, "D", "MuxSel_b", makeNetIndex(i));
157                portConnect(clk_div_dff, "Q", "MuxSel", makeNetIndex(i));
158                portConnect(clk_div_dff, "CK", "MuxSel", makeNetIndex(i+1));
159                addSubInstances(clk_div_dff, 1.0);
160                addElectricalSubResults(clk_div_dff, 1.0);
161
162                // Inversions
163                const String& clk_div_inv_name = "ClkDivINV_" + (String) i;
164                StdCell* clk_div_inv = getTechModel()->getStdCellLib()->createStdCell("INV", clk_div_inv_name);
165                clk_div_inv->construct();
166                portConnect(clk_div_inv, "A", "MuxSel", makeNetIndex(i));
167                portConnect(clk_div_inv, "Y", "MuxSel_b", makeNetIndex(i));
168                addSubInstances(clk_div_inv, 1.0);
169                addElectricalSubResults(clk_div_inv, 1.0);
170
171                getEventResult("Serialize")->addSubResult(clk_div_dff->getEventResult("CK"), clk_div_dff_name, 1.0);
172                getEventResult("Serialize")->addSubResult(clk_div_dff->getEventResult("DFFD"), clk_div_dff_name, 1.0);
173                getEventResult("Serialize")->addSubResult(clk_div_dff->getEventResult("DFFQ"), clk_div_dff_name, 1.0);
174                getEventResult("Serialize")->addSubResult(clk_div_inv->getEventResult("INV"), clk_div_inv_name, 1.0);
175            }
176        }
177
178        return;
179    }
180
181    void MuxTreeSerializer::propagateTransitionInfo()
182    {
183        // Get some generated properties
184        const unsigned int serialization_ratio = getGenProperties()->get("SerializationRatio");
185        const unsigned int number_stages = getGenProperties()->get("NumberStages");
186
187        // Set transition info of the mux tree and clock divide DFF
188        if (serialization_ratio == 1)
189        {
190            // If no serialization, then just propagate input transition info to output port
191            propagatePortTransitionInfo("Out", "In");
192        }
193        else
194        {
195
196            // Propagate transition probabilities to the mux tree
197            ElectricalModel* mux_tree = (ElectricalModel*) getSubInstance("MuxTree");
198            // All input ports of the mux have the same probability
199            for (unsigned int i = 0; i < serialization_ratio; ++i)
200                propagatePortTransitionInfo(mux_tree, "In" + (String) i, "In");
201            // Connect last stage of the mux
202            propagatePortTransitionInfo(mux_tree, "Sel" + (String) (number_stages - 1), "OutCK");
203            // Keep track of the last clock divider
204            ElectricalModel* last_clk_div_dff = NULL;
205            // Find P01 of OutCK
206            double last_P01_CK = getInputPort("OutCK")->getTransitionInfo().getNumberTransitions01();
207            // Start from the last stage (since it is the stage with no clock division)
208            for (unsigned int i = 0; i < number_stages - 1; ++i)
209            {
210                const String& clk_div_dff_name = "ClkDivDFF_" + (String) (number_stages - i - 2);
211                const String& clk_div_inv_name = "ClkDivINV_" + (String) (number_stages - i - 2);
212
213                ElectricalModel* clk_div_dff = (ElectricalModel*) getSubInstance(clk_div_dff_name);
214                if (last_clk_div_dff == NULL)
215                    propagatePortTransitionInfo(clk_div_dff, "CK", "OutCK");
216                else
217                    propagatePortTransitionInfo(clk_div_dff, "CK", last_clk_div_dff, "Q");
218                // Since it is a clock divider, P01 is D and Q are simply half the P01 of D and Q of
219                // the input clock
220                if (last_P01_CK != 0) clk_div_dff->getInputPort("D")->setTransitionInfo(TransitionInfo(0.0, last_P01_CK * 0.5, 0.0));
221                else clk_div_dff->getInputPort("D")->setTransitionInfo(TransitionInfo(0.5, 0.0, 0.5));
222
223                clk_div_dff->use();
224
225                ElectricalModel* clk_div_inv = (ElectricalModel*) getSubInstance(clk_div_inv_name);
226                propagatePortTransitionInfo(clk_div_inv, "A", clk_div_dff, "Q");
227                clk_div_inv->use();
228
229                // Connect select port of the mux
230                propagatePortTransitionInfo(mux_tree, "Sel" + (String) (number_stages - i - 2), clk_div_dff, "Q");
231
232                // Clk divide by 2;
233                last_P01_CK = last_P01_CK * 0.5;
234                // Remember the last clk div DFF
235                last_clk_div_dff = clk_div_dff;
236            }
237
238            mux_tree->use();
239            // Set output transition info to be the output transition info of the mux tree
240            propagatePortTransitionInfo("Out", mux_tree, "Out");
241        }
242
243        return;
244    }
245
246} // namespace DSENT
247
248