1/*
2 * Copyright (c) 2015 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: David Guillen Fandos
38 */
39
40#include "sim/power/thermal_model.hh"
41
42#include "base/statistics.hh"
43#include "params/ThermalCapacitor.hh"
44#include "params/ThermalReference.hh"
45#include "params/ThermalResistor.hh"
46#include "sim/clocked_object.hh"
47#include "sim/linear_solver.hh"
48#include "sim/power/thermal_domain.hh"
49#include "sim/sim_object.hh"
50
51/**
52 * ThermalReference
53 */
54ThermalReference::ThermalReference(const Params *p)
55    : SimObject(p), _temperature(p->temperature), node(NULL)
56{
57}
58
59ThermalReference *
60ThermalReferenceParams::create()
61{
62    return new ThermalReference(this);
63}
64
65void
66ThermalReference::serialize(CheckpointOut &cp) const
67{
68    SERIALIZE_SCALAR(_temperature);
69}
70
71void
72ThermalReference::unserialize(CheckpointIn &cp)
73{
74    UNSERIALIZE_SCALAR(_temperature);
75}
76
77LinearEquation
78ThermalReference::getEquation(ThermalNode * n, unsigned nnodes,
79                              double step) const {
80    // Just return an empty equation
81    return LinearEquation(nnodes);
82}
83
84/**
85 * ThermalResistor
86 */
87ThermalResistor::ThermalResistor(const Params *p)
88    : SimObject(p), _resistance(p->resistance), node1(NULL), node2(NULL)
89{
90}
91
92ThermalResistor *
93ThermalResistorParams::create()
94{
95    return new ThermalResistor(this);
96}
97
98void
99ThermalResistor::serialize(CheckpointOut &cp) const
100{
101    SERIALIZE_SCALAR(_resistance);
102}
103
104void
105ThermalResistor::unserialize(CheckpointIn &cp)
106{
107    UNSERIALIZE_SCALAR(_resistance);
108}
109
110LinearEquation
111ThermalResistor::getEquation(ThermalNode * n, unsigned nnodes,
112                             double step) const
113{
114    // i[n] = (Vn2 - Vn1)/R
115    LinearEquation eq(nnodes);
116
117    if (n != node1 && n != node2)
118        return eq;
119
120    if (node1->isref)
121        eq[eq.cnt()] += -node1->temp / _resistance;
122    else
123        eq[node1->id] += -1.0f / _resistance;
124
125    if (node2->isref)
126        eq[eq.cnt()] += node2->temp / _resistance;
127    else
128        eq[node2->id] += 1.0f / _resistance;
129
130    // We've assumed n was node1, reverse if necessary
131    if (n == node2)
132        eq *= -1.0f;
133
134    return eq;
135}
136
137/**
138 * ThermalCapacitor
139 */
140ThermalCapacitor::ThermalCapacitor(const Params *p)
141    : SimObject(p), _capacitance(p->capacitance), node1(NULL), node2(NULL)
142{
143}
144
145ThermalCapacitor *
146ThermalCapacitorParams::create()
147{
148    return new ThermalCapacitor(this);
149}
150
151void
152ThermalCapacitor::serialize(CheckpointOut &cp) const
153{
154    SERIALIZE_SCALAR(_capacitance);
155}
156
157void
158ThermalCapacitor::unserialize(CheckpointIn &cp)
159{
160    UNSERIALIZE_SCALAR(_capacitance);
161}
162
163LinearEquation
164ThermalCapacitor::getEquation(ThermalNode * n, unsigned nnodes,
165                              double step) const
166{
167    // i(t) = C * d(Vn2 - Vn1)/dt
168    // i[n] = C/step * (Vn2 - Vn1 - Vn2[n-1] + Vn1[n-1])
169    LinearEquation eq(nnodes);
170
171    if (n != node1 && n != node2)
172        return eq;
173
174    eq[eq.cnt()] += _capacitance / step * (node1->temp - node2->temp);
175
176    if (node1->isref)
177        eq[eq.cnt()] += _capacitance / step * (-node1->temp);
178    else
179        eq[node1->id] += -1.0f * _capacitance / step;
180
181    if (node2->isref)
182        eq[eq.cnt()] += _capacitance / step * (node2->temp);
183    else
184        eq[node2->id] += 1.0f * _capacitance / step;
185
186    // We've assumed n was node1, reverse if necessary
187    if (n == node2)
188        eq *= -1.0f;
189
190    return eq;
191}
192
193/**
194 * ThermalModel
195 */
196ThermalModel::ThermalModel(const Params *p)
197    : ClockedObject(p), stepEvent([this]{ doStep(); }, name()), _step(p->step)
198{
199}
200
201ThermalModel *
202ThermalModelParams::create()
203{
204    return new ThermalModel(this);
205}
206
207void
208ThermalModel::serialize(CheckpointOut &cp) const
209{
210    SERIALIZE_SCALAR(_step);
211}
212
213void
214ThermalModel::unserialize(CheckpointIn &cp)
215{
216    UNSERIALIZE_SCALAR(_step);
217}
218
219void
220ThermalModel::doStep()
221{
222    // Calculate new temperatures!
223    // For each node in the system, create the kirchhoff nodal equation
224    LinearSystem ls(eq_nodes.size());
225    for (unsigned i = 0; i < eq_nodes.size(); i++) {
226        auto n = eq_nodes[i];
227        LinearEquation node_equation (eq_nodes.size());
228        for (auto e : entities) {
229            LinearEquation eq = e->getEquation(n, eq_nodes.size(), _step);
230            node_equation = node_equation + eq;
231        }
232        ls[i] = node_equation;
233    }
234
235    // Get temperatures for this iteration
236    std::vector <double> temps = ls.solve();
237    for (unsigned i = 0; i < eq_nodes.size(); i++)
238        eq_nodes[i]->temp = temps[i];
239
240    // Schedule next computation
241    schedule(stepEvent, curTick() + SimClock::Int::s * _step);
242
243    // Notify everybody
244    for (auto dom : domains)
245        dom->emitUpdate();
246}
247
248void
249ThermalModel::startup()
250{
251    // Look for nodes connected to voltage references, these
252    // can be just set to the reference value (no nodal equation)
253    for (auto ref : references) {
254        ref->node->temp = ref->_temperature;
255        ref->node->isref = true;
256    }
257    // Setup the initial temperatures
258    for (auto dom : domains)
259        dom->getNode()->temp = dom->initialTemperature();
260
261    // Create a list of unknown temperature nodes
262    for (auto n : nodes) {
263        bool found = false;
264        for (auto ref : references)
265            if (ref->node == n) {
266                found = true;
267                break;
268            }
269        if (!found)
270            eq_nodes.push_back(n);
271    }
272
273    // Assign each node an ID
274    for (unsigned i = 0; i < eq_nodes.size(); i++)
275        eq_nodes[i]->id = i;
276
277    // Schedule first thermal update
278    schedule(stepEvent, curTick() + SimClock::Int::s * _step);
279}
280
281void ThermalModel::addDomain(ThermalDomain * d) {
282    domains.push_back(d);
283    entities.push_back(d);
284}
285void ThermalModel::addReference(ThermalReference * r) {
286    references.push_back(r);
287    entities.push_back(r);
288}
289void ThermalModel::addCapacitor(ThermalCapacitor * c) {
290    capacitors.push_back(c);
291    entities.push_back(c);
292}
293void ThermalModel::addResistor(ThermalResistor * r) {
294    resistors.push_back(r);
295    entities.push_back(r);
296}
297
298double ThermalModel::getTemp() const {
299    // Just pick the highest temperature
300    double temp = 0;
301    for (auto & n : eq_nodes)
302        temp = std::max(temp, n->temp);
303    return temp;
304}
305