111420Sdavid.guillen@arm.com/*
211420Sdavid.guillen@arm.com * Copyright (c) 2015 ARM Limited
311420Sdavid.guillen@arm.com * All rights reserved
411420Sdavid.guillen@arm.com *
511420Sdavid.guillen@arm.com * The license below extends only to copyright in the software and shall
611420Sdavid.guillen@arm.com * not be construed as granting a license to any other intellectual
711420Sdavid.guillen@arm.com * property including but not limited to intellectual property relating
811420Sdavid.guillen@arm.com * to a hardware implementation of the functionality of the software
911420Sdavid.guillen@arm.com * licensed hereunder.  You may use the software subject to the license
1011420Sdavid.guillen@arm.com * terms below provided that you ensure that this notice is replicated
1111420Sdavid.guillen@arm.com * unmodified and in its entirety in all distributions of the software,
1211420Sdavid.guillen@arm.com * modified or unmodified, in source code or in binary form.
1311420Sdavid.guillen@arm.com *
1411420Sdavid.guillen@arm.com * Redistribution and use in source and binary forms, with or without
1511420Sdavid.guillen@arm.com * modification, are permitted provided that the following conditions are
1611420Sdavid.guillen@arm.com * met: redistributions of source code must retain the above copyright
1711420Sdavid.guillen@arm.com * notice, this list of conditions and the following disclaimer;
1811420Sdavid.guillen@arm.com * redistributions in binary form must reproduce the above copyright
1911420Sdavid.guillen@arm.com * notice, this list of conditions and the following disclaimer in the
2011420Sdavid.guillen@arm.com * documentation and/or other materials provided with the distribution;
2111420Sdavid.guillen@arm.com * neither the name of the copyright holders nor the names of its
2211420Sdavid.guillen@arm.com * contributors may be used to endorse or promote products derived from
2311420Sdavid.guillen@arm.com * this software without specific prior written permission.
2411420Sdavid.guillen@arm.com *
2511420Sdavid.guillen@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2611420Sdavid.guillen@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2711420Sdavid.guillen@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2811420Sdavid.guillen@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2911420Sdavid.guillen@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3011420Sdavid.guillen@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3111420Sdavid.guillen@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3211420Sdavid.guillen@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3311420Sdavid.guillen@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3411420Sdavid.guillen@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3511420Sdavid.guillen@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3611420Sdavid.guillen@arm.com *
3711420Sdavid.guillen@arm.com * Authors: David Guillen Fandos
3811420Sdavid.guillen@arm.com */
3911420Sdavid.guillen@arm.com
4011420Sdavid.guillen@arm.com#include "sim/power/thermal_model.hh"
4111420Sdavid.guillen@arm.com
4211420Sdavid.guillen@arm.com#include "base/statistics.hh"
4311420Sdavid.guillen@arm.com#include "params/ThermalCapacitor.hh"
4411420Sdavid.guillen@arm.com#include "params/ThermalReference.hh"
4511420Sdavid.guillen@arm.com#include "params/ThermalResistor.hh"
4611420Sdavid.guillen@arm.com#include "sim/clocked_object.hh"
4711420Sdavid.guillen@arm.com#include "sim/linear_solver.hh"
4811420Sdavid.guillen@arm.com#include "sim/power/thermal_domain.hh"
4911420Sdavid.guillen@arm.com#include "sim/sim_object.hh"
5011420Sdavid.guillen@arm.com
5111420Sdavid.guillen@arm.com/**
5211420Sdavid.guillen@arm.com * ThermalReference
5311420Sdavid.guillen@arm.com */
5411420Sdavid.guillen@arm.comThermalReference::ThermalReference(const Params *p)
5511420Sdavid.guillen@arm.com    : SimObject(p), _temperature(p->temperature), node(NULL)
5611420Sdavid.guillen@arm.com{
5711420Sdavid.guillen@arm.com}
5811420Sdavid.guillen@arm.com
5911420Sdavid.guillen@arm.comThermalReference *
6011420Sdavid.guillen@arm.comThermalReferenceParams::create()
6111420Sdavid.guillen@arm.com{
6211420Sdavid.guillen@arm.com    return new ThermalReference(this);
6311420Sdavid.guillen@arm.com}
6411420Sdavid.guillen@arm.com
6511420Sdavid.guillen@arm.comvoid
6611420Sdavid.guillen@arm.comThermalReference::serialize(CheckpointOut &cp) const
6711420Sdavid.guillen@arm.com{
6811420Sdavid.guillen@arm.com    SERIALIZE_SCALAR(_temperature);
6911420Sdavid.guillen@arm.com}
7011420Sdavid.guillen@arm.com
7111420Sdavid.guillen@arm.comvoid
7211420Sdavid.guillen@arm.comThermalReference::unserialize(CheckpointIn &cp)
7311420Sdavid.guillen@arm.com{
7411420Sdavid.guillen@arm.com    UNSERIALIZE_SCALAR(_temperature);
7511420Sdavid.guillen@arm.com}
7611420Sdavid.guillen@arm.com
7711420Sdavid.guillen@arm.comLinearEquation
7811420Sdavid.guillen@arm.comThermalReference::getEquation(ThermalNode * n, unsigned nnodes,
7911420Sdavid.guillen@arm.com                              double step) const {
8011420Sdavid.guillen@arm.com    // Just return an empty equation
8111420Sdavid.guillen@arm.com    return LinearEquation(nnodes);
8211420Sdavid.guillen@arm.com}
8311420Sdavid.guillen@arm.com
8411420Sdavid.guillen@arm.com/**
8511420Sdavid.guillen@arm.com * ThermalResistor
8611420Sdavid.guillen@arm.com */
8711420Sdavid.guillen@arm.comThermalResistor::ThermalResistor(const Params *p)
8811420Sdavid.guillen@arm.com    : SimObject(p), _resistance(p->resistance), node1(NULL), node2(NULL)
8911420Sdavid.guillen@arm.com{
9011420Sdavid.guillen@arm.com}
9111420Sdavid.guillen@arm.com
9211420Sdavid.guillen@arm.comThermalResistor *
9311420Sdavid.guillen@arm.comThermalResistorParams::create()
9411420Sdavid.guillen@arm.com{
9511420Sdavid.guillen@arm.com    return new ThermalResistor(this);
9611420Sdavid.guillen@arm.com}
9711420Sdavid.guillen@arm.com
9811420Sdavid.guillen@arm.comvoid
9911420Sdavid.guillen@arm.comThermalResistor::serialize(CheckpointOut &cp) const
10011420Sdavid.guillen@arm.com{
10111420Sdavid.guillen@arm.com    SERIALIZE_SCALAR(_resistance);
10211420Sdavid.guillen@arm.com}
10311420Sdavid.guillen@arm.com
10411420Sdavid.guillen@arm.comvoid
10511420Sdavid.guillen@arm.comThermalResistor::unserialize(CheckpointIn &cp)
10611420Sdavid.guillen@arm.com{
10711420Sdavid.guillen@arm.com    UNSERIALIZE_SCALAR(_resistance);
10811420Sdavid.guillen@arm.com}
10911420Sdavid.guillen@arm.com
11011420Sdavid.guillen@arm.comLinearEquation
11111420Sdavid.guillen@arm.comThermalResistor::getEquation(ThermalNode * n, unsigned nnodes,
11211420Sdavid.guillen@arm.com                             double step) const
11311420Sdavid.guillen@arm.com{
11411420Sdavid.guillen@arm.com    // i[n] = (Vn2 - Vn1)/R
11511420Sdavid.guillen@arm.com    LinearEquation eq(nnodes);
11611420Sdavid.guillen@arm.com
11711420Sdavid.guillen@arm.com    if (n != node1 && n != node2)
11811420Sdavid.guillen@arm.com        return eq;
11911420Sdavid.guillen@arm.com
12011420Sdavid.guillen@arm.com    if (node1->isref)
12111420Sdavid.guillen@arm.com        eq[eq.cnt()] += -node1->temp / _resistance;
12211420Sdavid.guillen@arm.com    else
12311420Sdavid.guillen@arm.com        eq[node1->id] += -1.0f / _resistance;
12411420Sdavid.guillen@arm.com
12511420Sdavid.guillen@arm.com    if (node2->isref)
12611420Sdavid.guillen@arm.com        eq[eq.cnt()] += node2->temp / _resistance;
12711420Sdavid.guillen@arm.com    else
12811420Sdavid.guillen@arm.com        eq[node2->id] += 1.0f / _resistance;
12911420Sdavid.guillen@arm.com
13011420Sdavid.guillen@arm.com    // We've assumed n was node1, reverse if necessary
13111420Sdavid.guillen@arm.com    if (n == node2)
13211420Sdavid.guillen@arm.com        eq *= -1.0f;
13311420Sdavid.guillen@arm.com
13411420Sdavid.guillen@arm.com    return eq;
13511420Sdavid.guillen@arm.com}
13611420Sdavid.guillen@arm.com
13711420Sdavid.guillen@arm.com/**
13811420Sdavid.guillen@arm.com * ThermalCapacitor
13911420Sdavid.guillen@arm.com */
14011420Sdavid.guillen@arm.comThermalCapacitor::ThermalCapacitor(const Params *p)
14111420Sdavid.guillen@arm.com    : SimObject(p), _capacitance(p->capacitance), node1(NULL), node2(NULL)
14211420Sdavid.guillen@arm.com{
14311420Sdavid.guillen@arm.com}
14411420Sdavid.guillen@arm.com
14511420Sdavid.guillen@arm.comThermalCapacitor *
14611420Sdavid.guillen@arm.comThermalCapacitorParams::create()
14711420Sdavid.guillen@arm.com{
14811420Sdavid.guillen@arm.com    return new ThermalCapacitor(this);
14911420Sdavid.guillen@arm.com}
15011420Sdavid.guillen@arm.com
15111420Sdavid.guillen@arm.comvoid
15211420Sdavid.guillen@arm.comThermalCapacitor::serialize(CheckpointOut &cp) const
15311420Sdavid.guillen@arm.com{
15411420Sdavid.guillen@arm.com    SERIALIZE_SCALAR(_capacitance);
15511420Sdavid.guillen@arm.com}
15611420Sdavid.guillen@arm.com
15711420Sdavid.guillen@arm.comvoid
15811420Sdavid.guillen@arm.comThermalCapacitor::unserialize(CheckpointIn &cp)
15911420Sdavid.guillen@arm.com{
16011420Sdavid.guillen@arm.com    UNSERIALIZE_SCALAR(_capacitance);
16111420Sdavid.guillen@arm.com}
16211420Sdavid.guillen@arm.com
16311420Sdavid.guillen@arm.comLinearEquation
16411420Sdavid.guillen@arm.comThermalCapacitor::getEquation(ThermalNode * n, unsigned nnodes,
16511420Sdavid.guillen@arm.com                              double step) const
16611420Sdavid.guillen@arm.com{
16711420Sdavid.guillen@arm.com    // i(t) = C * d(Vn2 - Vn1)/dt
16811420Sdavid.guillen@arm.com    // i[n] = C/step * (Vn2 - Vn1 - Vn2[n-1] + Vn1[n-1])
16911420Sdavid.guillen@arm.com    LinearEquation eq(nnodes);
17011420Sdavid.guillen@arm.com
17111420Sdavid.guillen@arm.com    if (n != node1 && n != node2)
17211420Sdavid.guillen@arm.com        return eq;
17311420Sdavid.guillen@arm.com
17411420Sdavid.guillen@arm.com    eq[eq.cnt()] += _capacitance / step * (node1->temp - node2->temp);
17511420Sdavid.guillen@arm.com
17611420Sdavid.guillen@arm.com    if (node1->isref)
17711420Sdavid.guillen@arm.com        eq[eq.cnt()] += _capacitance / step * (-node1->temp);
17811420Sdavid.guillen@arm.com    else
17911420Sdavid.guillen@arm.com        eq[node1->id] += -1.0f * _capacitance / step;
18011420Sdavid.guillen@arm.com
18111420Sdavid.guillen@arm.com    if (node2->isref)
18211420Sdavid.guillen@arm.com        eq[eq.cnt()] += _capacitance / step * (node2->temp);
18311420Sdavid.guillen@arm.com    else
18411420Sdavid.guillen@arm.com        eq[node2->id] += 1.0f * _capacitance / step;
18511420Sdavid.guillen@arm.com
18611420Sdavid.guillen@arm.com    // We've assumed n was node1, reverse if necessary
18711420Sdavid.guillen@arm.com    if (n == node2)
18811420Sdavid.guillen@arm.com        eq *= -1.0f;
18911420Sdavid.guillen@arm.com
19011420Sdavid.guillen@arm.com    return eq;
19111420Sdavid.guillen@arm.com}
19211420Sdavid.guillen@arm.com
19311420Sdavid.guillen@arm.com/**
19411420Sdavid.guillen@arm.com * ThermalModel
19511420Sdavid.guillen@arm.com */
19611420Sdavid.guillen@arm.comThermalModel::ThermalModel(const Params *p)
19712088Sspwilson2@wisc.edu    : ClockedObject(p), stepEvent([this]{ doStep(); }, name()), _step(p->step)
19811420Sdavid.guillen@arm.com{
19911420Sdavid.guillen@arm.com}
20011420Sdavid.guillen@arm.com
20111420Sdavid.guillen@arm.comThermalModel *
20211420Sdavid.guillen@arm.comThermalModelParams::create()
20311420Sdavid.guillen@arm.com{
20411420Sdavid.guillen@arm.com    return new ThermalModel(this);
20511420Sdavid.guillen@arm.com}
20611420Sdavid.guillen@arm.com
20711420Sdavid.guillen@arm.comvoid
20811420Sdavid.guillen@arm.comThermalModel::serialize(CheckpointOut &cp) const
20911420Sdavid.guillen@arm.com{
21011420Sdavid.guillen@arm.com    SERIALIZE_SCALAR(_step);
21111420Sdavid.guillen@arm.com}
21211420Sdavid.guillen@arm.com
21311420Sdavid.guillen@arm.comvoid
21411420Sdavid.guillen@arm.comThermalModel::unserialize(CheckpointIn &cp)
21511420Sdavid.guillen@arm.com{
21611420Sdavid.guillen@arm.com    UNSERIALIZE_SCALAR(_step);
21711420Sdavid.guillen@arm.com}
21811420Sdavid.guillen@arm.com
21911420Sdavid.guillen@arm.comvoid
22011420Sdavid.guillen@arm.comThermalModel::doStep()
22111420Sdavid.guillen@arm.com{
22211420Sdavid.guillen@arm.com    // Calculate new temperatures!
22311420Sdavid.guillen@arm.com    // For each node in the system, create the kirchhoff nodal equation
22411420Sdavid.guillen@arm.com    LinearSystem ls(eq_nodes.size());
22511420Sdavid.guillen@arm.com    for (unsigned i = 0; i < eq_nodes.size(); i++) {
22611420Sdavid.guillen@arm.com        auto n = eq_nodes[i];
22711420Sdavid.guillen@arm.com        LinearEquation node_equation (eq_nodes.size());
22811420Sdavid.guillen@arm.com        for (auto e : entities) {
22911420Sdavid.guillen@arm.com            LinearEquation eq = e->getEquation(n, eq_nodes.size(), _step);
23011420Sdavid.guillen@arm.com            node_equation = node_equation + eq;
23111420Sdavid.guillen@arm.com        }
23211420Sdavid.guillen@arm.com        ls[i] = node_equation;
23311420Sdavid.guillen@arm.com    }
23411420Sdavid.guillen@arm.com
23511420Sdavid.guillen@arm.com    // Get temperatures for this iteration
23611420Sdavid.guillen@arm.com    std::vector <double> temps = ls.solve();
23711420Sdavid.guillen@arm.com    for (unsigned i = 0; i < eq_nodes.size(); i++)
23811420Sdavid.guillen@arm.com        eq_nodes[i]->temp = temps[i];
23911420Sdavid.guillen@arm.com
24011420Sdavid.guillen@arm.com    // Schedule next computation
24111420Sdavid.guillen@arm.com    schedule(stepEvent, curTick() + SimClock::Int::s * _step);
24211420Sdavid.guillen@arm.com
24311420Sdavid.guillen@arm.com    // Notify everybody
24411420Sdavid.guillen@arm.com    for (auto dom : domains)
24511420Sdavid.guillen@arm.com        dom->emitUpdate();
24611420Sdavid.guillen@arm.com}
24711420Sdavid.guillen@arm.com
24811420Sdavid.guillen@arm.comvoid
24911420Sdavid.guillen@arm.comThermalModel::startup()
25011420Sdavid.guillen@arm.com{
25111420Sdavid.guillen@arm.com    // Look for nodes connected to voltage references, these
25211420Sdavid.guillen@arm.com    // can be just set to the reference value (no nodal equation)
25311420Sdavid.guillen@arm.com    for (auto ref : references) {
25411420Sdavid.guillen@arm.com        ref->node->temp = ref->_temperature;
25511420Sdavid.guillen@arm.com        ref->node->isref = true;
25611420Sdavid.guillen@arm.com    }
25711420Sdavid.guillen@arm.com    // Setup the initial temperatures
25811420Sdavid.guillen@arm.com    for (auto dom : domains)
25911420Sdavid.guillen@arm.com        dom->getNode()->temp = dom->initialTemperature();
26011420Sdavid.guillen@arm.com
26111420Sdavid.guillen@arm.com    // Create a list of unknown temperature nodes
26211420Sdavid.guillen@arm.com    for (auto n : nodes) {
26311420Sdavid.guillen@arm.com        bool found = false;
26411420Sdavid.guillen@arm.com        for (auto ref : references)
26511420Sdavid.guillen@arm.com            if (ref->node == n) {
26611420Sdavid.guillen@arm.com                found = true;
26711420Sdavid.guillen@arm.com                break;
26811420Sdavid.guillen@arm.com            }
26911420Sdavid.guillen@arm.com        if (!found)
27011420Sdavid.guillen@arm.com            eq_nodes.push_back(n);
27111420Sdavid.guillen@arm.com    }
27211420Sdavid.guillen@arm.com
27311420Sdavid.guillen@arm.com    // Assign each node an ID
27411420Sdavid.guillen@arm.com    for (unsigned i = 0; i < eq_nodes.size(); i++)
27511420Sdavid.guillen@arm.com        eq_nodes[i]->id = i;
27611420Sdavid.guillen@arm.com
27711420Sdavid.guillen@arm.com    // Schedule first thermal update
27811420Sdavid.guillen@arm.com    schedule(stepEvent, curTick() + SimClock::Int::s * _step);
27911420Sdavid.guillen@arm.com}
28011420Sdavid.guillen@arm.com
28111420Sdavid.guillen@arm.comvoid ThermalModel::addDomain(ThermalDomain * d) {
28211420Sdavid.guillen@arm.com    domains.push_back(d);
28311420Sdavid.guillen@arm.com    entities.push_back(d);
28411420Sdavid.guillen@arm.com}
28511420Sdavid.guillen@arm.comvoid ThermalModel::addReference(ThermalReference * r) {
28611420Sdavid.guillen@arm.com    references.push_back(r);
28711420Sdavid.guillen@arm.com    entities.push_back(r);
28811420Sdavid.guillen@arm.com}
28911420Sdavid.guillen@arm.comvoid ThermalModel::addCapacitor(ThermalCapacitor * c) {
29011420Sdavid.guillen@arm.com    capacitors.push_back(c);
29111420Sdavid.guillen@arm.com    entities.push_back(c);
29211420Sdavid.guillen@arm.com}
29311420Sdavid.guillen@arm.comvoid ThermalModel::addResistor(ThermalResistor * r) {
29411420Sdavid.guillen@arm.com    resistors.push_back(r);
29511420Sdavid.guillen@arm.com    entities.push_back(r);
29611420Sdavid.guillen@arm.com}
29711420Sdavid.guillen@arm.com
29811420Sdavid.guillen@arm.comdouble ThermalModel::getTemp() const {
29911420Sdavid.guillen@arm.com    // Just pick the highest temperature
30011420Sdavid.guillen@arm.com    double temp = 0;
30111420Sdavid.guillen@arm.com    for (auto & n : eq_nodes)
30211420Sdavid.guillen@arm.com        temp = std::max(temp, n->temp);
30311420Sdavid.guillen@arm.com    return temp;
30411420Sdavid.guillen@arm.com}
305