blob: 8f92c6f778672bbb5789a2e6b2391c6178fa6385 [file] [log] [blame]
/*
* Copyright (c) 2015, 2021 Arm Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sim/power/thermal_model.hh"
#include "base/statistics.hh"
#include "params/ThermalCapacitor.hh"
#include "params/ThermalModel.hh"
#include "params/ThermalReference.hh"
#include "params/ThermalResistor.hh"
#include "sim/clocked_object.hh"
#include "sim/linear_solver.hh"
#include "sim/power/thermal_domain.hh"
#include "sim/sim_object.hh"
/**
* ThermalReference
*/
ThermalReference::ThermalReference(const Params &p)
: SimObject(p), _temperature(p.temperature), node(NULL)
{
}
LinearEquation
ThermalReference::getEquation(ThermalNode * n, unsigned nnodes,
double step) const {
// Just return an empty equation
return LinearEquation(nnodes);
}
/**
* ThermalResistor
*/
ThermalResistor::ThermalResistor(const Params &p)
: SimObject(p), _resistance(p.resistance), node1(NULL), node2(NULL)
{
}
LinearEquation
ThermalResistor::getEquation(ThermalNode * n, unsigned nnodes,
double step) const
{
// i[n] = (Vn2 - Vn1)/R
LinearEquation eq(nnodes);
if (n != node1 && n != node2)
return eq;
if (node1->isref)
eq[eq.cnt()] += -node1->temp.toKelvin() / _resistance;
else
eq[node1->id] += -1.0f / _resistance;
if (node2->isref)
eq[eq.cnt()] += node2->temp.toKelvin() / _resistance;
else
eq[node2->id] += 1.0f / _resistance;
// We've assumed n was node1, reverse if necessary
if (n == node2)
eq *= -1.0f;
return eq;
}
/**
* ThermalCapacitor
*/
ThermalCapacitor::ThermalCapacitor(const Params &p)
: SimObject(p), _capacitance(p.capacitance), node1(NULL), node2(NULL)
{
}
LinearEquation
ThermalCapacitor::getEquation(ThermalNode * n, unsigned nnodes,
double step) const
{
// i(t) = C * d(Vn2 - Vn1)/dt
// i[n] = C/step * (Vn2 - Vn1 - Vn2[n-1] + Vn1[n-1])
LinearEquation eq(nnodes);
if (n != node1 && n != node2)
return eq;
eq[eq.cnt()] += _capacitance / step *
(node1->temp - node2->temp).toKelvin();
if (node1->isref)
eq[eq.cnt()] += _capacitance / step * (-node1->temp.toKelvin());
else
eq[node1->id] += -1.0f * _capacitance / step;
if (node2->isref)
eq[eq.cnt()] += _capacitance / step * (node2->temp.toKelvin());
else
eq[node2->id] += 1.0f * _capacitance / step;
// We've assumed n was node1, reverse if necessary
if (n == node2)
eq *= -1.0f;
return eq;
}
/**
* ThermalModel
*/
ThermalModel::ThermalModel(const Params &p)
: ClockedObject(p), stepEvent([this]{ doStep(); }, name()), _step(p.step)
{
}
void
ThermalModel::doStep()
{
// Calculate new temperatures!
// For each node in the system, create the kirchhoff nodal equation
LinearSystem ls(eq_nodes.size());
for (unsigned i = 0; i < eq_nodes.size(); i++) {
auto n = eq_nodes[i];
LinearEquation node_equation (eq_nodes.size());
for (auto e : entities) {
LinearEquation eq = e->getEquation(n, eq_nodes.size(), _step);
node_equation = node_equation + eq;
}
ls[i] = node_equation;
}
// Get temperatures for this iteration
std::vector <double> temps = ls.solve();
for (unsigned i = 0; i < eq_nodes.size(); i++)
eq_nodes[i]->temp = Temperature::fromKelvin(temps[i]);
// Schedule next computation
schedule(stepEvent, curTick() + sim_clock::as_int::s * _step);
// Notify everybody
for (auto dom : domains)
dom->emitUpdate();
}
void
ThermalModel::startup()
{
// Look for nodes connected to voltage references, these
// can be just set to the reference value (no nodal equation)
for (auto ref : references) {
ref->node->temp = ref->_temperature;
ref->node->isref = true;
}
// Setup the initial temperatures
for (auto dom : domains)
dom->getNode()->temp = dom->initialTemperature();
// Create a list of unknown temperature nodes
for (auto n : nodes) {
bool found = false;
for (auto ref : references)
if (ref->node == n) {
found = true;
break;
}
if (!found)
eq_nodes.push_back(n);
}
// Assign each node an ID
for (unsigned i = 0; i < eq_nodes.size(); i++)
eq_nodes[i]->id = i;
// Schedule first thermal update
schedule(stepEvent, curTick() + sim_clock::as_int::s * _step);
}
void
ThermalModel::addDomain(ThermalDomain * d)
{
domains.push_back(d);
entities.push_back(d);
}
void
ThermalModel::addReference(ThermalReference * r)
{
references.push_back(r);
entities.push_back(r);
}
void
ThermalModel::addCapacitor(ThermalCapacitor * c)
{
capacitors.push_back(c);
entities.push_back(c);
}
void
ThermalModel::addResistor(ThermalResistor * r)
{
resistors.push_back(r);
entities.push_back(r);
}
Temperature
ThermalModel::getTemperature() const
{
// Just pick the highest temperature
Temperature temp = Temperature::fromKelvin(0.0);
for (auto & n : eq_nodes)
temp = std::max(temp, n->temp);
return temp;
}