fastmodel: Templatize the xn versions of the CortexA76.

This will make it a lot easier and more succinct to define the x2-x4
versions of that CPU.

Change-Id: I951cd3af4419c62892c57968e729fd11c0e4a59e
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/21503
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabe Black
2019-09-20 17:42:12 -07:00
parent 38cf52b507
commit eb49dec51e
8 changed files with 296 additions and 174 deletions

View File

@@ -40,7 +40,7 @@ from m5.objects.SystemC import SystemC_ScModule
class FastModelCortexA76(FastModelArmCPU):
type = 'FastModelCortexA76'
cxx_class = 'FastModel::CortexA76'
cxx_header = 'arch/arm/fastmodel/CortexA76x1/cortex_a76x1.hh'
cxx_header = 'arch/arm/fastmodel/CortexA76/cortex_a76.hh'
cntfrq = 0x1800000
@@ -142,7 +142,7 @@ class FastModelCortexA76(FastModelArmCPU):
class FastModelCortexA76Cluster(SimObject):
type = 'FastModelCortexA76Cluster'
cxx_class = 'FastModel::CortexA76Cluster'
cxx_header = 'arch/arm/fastmodel/CortexA76x1/cortex_a76x1.hh'
cxx_header = 'arch/arm/fastmodel/CortexA76/cortex_a76.hh'
cores = VectorParam.FastModelCortexA76(
'Core in a given cluster of CortexA76s')
@@ -356,8 +356,9 @@ class FastModelCortexA76Cluster(SimObject):
class FastModelScxEvsCortexA76x1(SystemC_ScModule):
type = 'FastModelScxEvsCortexA76x1'
cxx_class = 'FastModel::ScxEvsCortexA76x1'
cxx_header = 'arch/arm/fastmodel/CortexA76x1/cortex_a76x1.hh'
cxx_class = 'FastModel::ScxEvsCortexA76<FastModel::ScxEvsCortexA76x1Types>'
cxx_template_params = [ 'class Types' ]
cxx_header = 'arch/arm/fastmodel/CortexA76/evs.hh'
class FastModelCortexA76x1(FastModelCortexA76Cluster):
cores = [ FastModelCortexA76(thread_paths=[ 'core.cpu0' ]) ]

View File

@@ -32,10 +32,11 @@ if not env['USE_ARM_FASTMODEL'] or env['TARGET_ISA'] != 'arm':
protocol_dir = Dir('..').Dir('protocol')
ArmFastModelComponent(File('CortexA76x1.sgproj'),
File('CortexA76x1.lisa'),
ArmFastModelComponent(File('x1.sgproj'),
File('x1.lisa'),
protocol_dir.File(
'ExportedClockRateControlProtocol.lisa')
).prepare_env(env)
SimObject('FastModelCortexA76x1.py')
Source('cortex_a76x1.cc')
SimObject('FastModelCortexA76.py')
Source('cortex_a76.cc')
Source('evs.cc')

View File

@@ -27,7 +27,7 @@
* Authors: Gabe Black
*/
#include "arch/arm/fastmodel/CortexA76x1/cortex_a76x1.hh"
#include "arch/arm/fastmodel/CortexA76/cortex_a76.hh"
#include "arch/arm/fastmodel/arm/cpu.hh"
#include "arch/arm/fastmodel/iris/cpu.hh"
@@ -197,100 +197,6 @@ CortexA76Cluster::getPort(const std::string &if_name, PortID idx)
}
}
void
ScxEvsCortexA76x1::clockChangeHandler()
{
clockRateControl->set_mul_div(SimClock::Int::s, clockPeriod.value);
}
ScxEvsCortexA76x1::ScxEvsCortexA76x1(const sc_core::sc_module_name &mod_name,
const Params &p) :
scx_evs_CortexA76x1(mod_name),
amba(scx_evs_CortexA76x1::amba, p.name + ".amba", -1),
redist {
new TlmGicTarget(redistributor[0],
csprintf("%s.redistributor[%d]", name(), 0), 0)
},
cnthpirq("cnthpirq"), cnthvirq("cnthvirq"), cntpsirq("cntpsirq"),
cntvirq("cntvirq"), commirq("commirq"), ctidbgirq("ctidbgirq"),
pmuirq("pmuirq"), vcpumntirq("vcpumntirq"), cntpnsirq("cntpnsirq"),
clockChanged(Iris::ClockEventName.c_str()),
clockPeriod(Iris::PeriodAttributeName.c_str()),
gem5CpuCluster(Iris::Gem5CpuClusterAttributeName.c_str()),
sendFunctional(Iris::SendFunctionalAttributeName.c_str()),
params(p)
{
clockRateControl.bind(clock_rate_s);
add_attribute(gem5CpuCluster);
add_attribute(clockPeriod);
SC_METHOD(clockChangeHandler);
dont_initialize();
sensitive << clockChanged;
scx_evs_CortexA76x1::cnthpirq[0].bind(cnthpirq.signal_in);
scx_evs_CortexA76x1::cnthvirq[0].bind(cnthvirq.signal_in);
scx_evs_CortexA76x1::cntpsirq[0].bind(cntpsirq.signal_in);
scx_evs_CortexA76x1::cntvirq[0].bind(cntvirq.signal_in);
scx_evs_CortexA76x1::commirq[0].bind(commirq.signal_in);
scx_evs_CortexA76x1::ctidbgirq[0].bind(ctidbgirq.signal_in);
scx_evs_CortexA76x1::pmuirq[0].bind(pmuirq.signal_in);
scx_evs_CortexA76x1::vcpumntirq[0].bind(vcpumntirq.signal_in);
scx_evs_CortexA76x1::cntpnsirq[0].bind(cntpnsirq.signal_in);
sendFunctional.value = [this](PacketPtr pkt) { sendFunc(pkt); };
add_attribute(sendFunctional);
}
void
ScxEvsCortexA76x1::sendFunc(PacketPtr pkt)
{
auto *trans = sc_gem5::packet2payload(pkt);
panic_if(scx_evs_CortexA76x1::amba->transport_dbg(*trans) !=
trans->get_data_length(), "Didn't send entire functional packet!");
trans->release();
}
void
ScxEvsCortexA76x1::before_end_of_elaboration()
{
scx_evs_CortexA76x1::before_end_of_elaboration();
auto *cluster = gem5CpuCluster.value;
auto set_on_change = [cluster](
SignalReceiver &recv, ArmInterruptPinGen *gen, int num)
{
auto *pin = gen->get(cluster->getCore(num)->getContext(0));
auto handler = [pin](bool status)
{
status ? pin->raise() : pin->clear();
};
recv.onChange(handler);
};
set_on_change(cnthpirq, cluster->params().cnthpirq, 0);
set_on_change(cnthvirq, cluster->params().cnthvirq, 0);
set_on_change(cntpsirq, cluster->params().cntpsirq, 0);
set_on_change(cntvirq, cluster->params().cntvirq, 0);
set_on_change(commirq, cluster->params().commirq, 0);
set_on_change(ctidbgirq, cluster->params().ctidbgirq, 0);
set_on_change(pmuirq, cluster->params().pmuirq, 0);
set_on_change(vcpumntirq, cluster->params().vcpumntirq, 0);
set_on_change(cntpnsirq, cluster->params().cntpnsirq, 0);
}
Port &
ScxEvsCortexA76x1::gem5_getPort(const std::string &if_name, int idx)
{
if (if_name == "redistributor")
return *redist.at(idx);
else if (if_name == "amba")
return amba;
else
return scx_evs_CortexA76x1::gem5_getPort(if_name, idx);
}
} // namespace FastModel
FastModel::CortexA76 *
@@ -304,9 +210,3 @@ FastModelCortexA76ClusterParams::create()
{
return new FastModel::CortexA76Cluster(*this);
}
FastModel::ScxEvsCortexA76x1 *
FastModelScxEvsCortexA76x1Params::create()
{
return new FastModel::ScxEvsCortexA76x1(name.c_str(), *this);
}

View File

@@ -27,19 +27,15 @@
* Authors: Gabe Black
*/
#ifndef __ARCH_ARM_FASTMODEL_CORTEXA76X1_CORETEX_A76X1_HH__
#define __ARCH_ARM_FASTMODEL_CORTEXA76X1_CORETEX_A76X1_HH__
#ifndef __ARCH_ARM_FASTMODEL_CORTEXA76_CORETEX_A76_HH__
#define __ARCH_ARM_FASTMODEL_CORTEXA76_CORETEX_A76_HH__
#include "arch/arm/fastmodel/amba_ports.hh"
#include "arch/arm/fastmodel/arm/cpu.hh"
#include "arch/arm/fastmodel/common/signal_receiver.hh"
#include "arch/arm/fastmodel/protocol/exported_clock_rate_control.hh"
#include "mem/port_proxy.hh"
#include "params/FastModelCortexA76.hh"
#include "params/FastModelCortexA76Cluster.hh"
#include "params/FastModelScxEvsCortexA76x1.hh"
#include "scx_evs_CortexA76x1.h"
#include "systemc/ext/core/sc_event.hh"
#include "scx/scx.h"
#include "sim/port.hh"
#include "systemc/ext/core/sc_module.hh"
class BaseCPU;
@@ -104,59 +100,6 @@ class CortexA76Cluster : public SimObject
PortID idx=InvalidPortID) override;
};
class ScxEvsCortexA76x1 : public scx_evs_CortexA76x1
{
private:
SC_HAS_PROCESS(ScxEvsCortexA76x1);
ClockRateControlInitiatorSocket clockRateControl;
typedef sc_gem5::TlmTargetBaseWrapper<
64, svp_gicv3_comms::gicv3_comms_fw_if,
svp_gicv3_comms::gicv3_comms_bw_if, 1,
sc_core::SC_ONE_OR_MORE_BOUND> TlmGicTarget;
AmbaInitiator amba;
std::vector<TlmGicTarget *> redist;
SignalReceiver cnthpirq;
SignalReceiver cnthvirq;
SignalReceiver cntpsirq;
SignalReceiver cntvirq;
SignalReceiver commirq;
SignalReceiver ctidbgirq;
SignalReceiver pmuirq;
SignalReceiver vcpumntirq;
SignalReceiver cntpnsirq;
sc_core::sc_event clockChanged;
sc_core::sc_attribute<Tick> clockPeriod;
sc_core::sc_attribute<CortexA76Cluster *> gem5CpuCluster;
sc_core::sc_attribute<PortProxy::SendFunctionalFunc> sendFunctional;
void sendFunc(PacketPtr pkt);
void clockChangeHandler();
typedef FastModelScxEvsCortexA76x1Params Params;
const Params &params;
public:
ScxEvsCortexA76x1(
const sc_core::sc_module_name &mod_name, const Params &p);
void before_end_of_elaboration() override;
Port &gem5_getPort(const std::string &if_name, int idx) override;
void
end_of_elaboration() override
{
scx_evs_CortexA76x1::end_of_elaboration();
scx_evs_CortexA76x1::start_of_simulation();
}
void start_of_simulation() override {}
};
template <class T>
inline void
CortexA76::set_evs_param(const std::string &n, T val)
@@ -167,4 +110,4 @@ CortexA76::set_evs_param(const std::string &n, T val)
} // namespace FastModel
#endif // __ARCH_ARM_FASTMODEL_CORTEXA76X1_CORETEX_A76X1_HH__
#endif // __ARCH_ARM_FASTMODEL_CORTEXA76_CORETEX_A76_HH__

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2019 Google, Inc.
*
* 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.
*
* Authors: Gabe Black
*/
#include "arch/arm/fastmodel/CortexA76/evs.hh"
#include "arch/arm/fastmodel/CortexA76/cortex_a76.hh"
#include "arch/arm/fastmodel/arm/cpu.hh"
#include "base/logging.hh"
#include "dev/arm/base_gic.hh"
#include "sim/core.hh"
#include "systemc/tlm_bridge/gem5_to_tlm.hh"
namespace FastModel
{
template <class Types>
void
ScxEvsCortexA76<Types>::clockChangeHandler()
{
clockRateControl->set_mul_div(SimClock::Int::s, clockPeriod.value);
}
template <class Types>
ScxEvsCortexA76<Types>::ScxEvsCortexA76(
const sc_core::sc_module_name &mod_name, const Params &p) :
Base(mod_name), amba(Base::amba, p.name + ".amba", -1),
clockChanged(Iris::ClockEventName.c_str()),
clockPeriod(Iris::PeriodAttributeName.c_str()),
gem5CpuCluster(Iris::Gem5CpuClusterAttributeName.c_str()),
sendFunctional(Iris::SendFunctionalAttributeName.c_str()),
params(p)
{
for (int i = 0; i < CoreCount; i++) {
redist.emplace_back(new TlmGicTarget(this->redistributor[i],
csprintf("%s.redistributor[%d]", name(), i), i));
cnthpirq.emplace_back(new SignalReceiver(csprintf("cnthpirq[%d]", i)));
cnthvirq.emplace_back(new SignalReceiver(csprintf("cnthvirq[%d]", i)));
cntpsirq.emplace_back(new SignalReceiver(csprintf("cntpsirq[%d]", i)));
cntvirq.emplace_back(new SignalReceiver(csprintf("cntvirq[%d]", i)));
commirq.emplace_back(new SignalReceiver(csprintf("commirq[%d]", i)));
ctidbgirq.emplace_back(
new SignalReceiver(csprintf("ctidbgirq[%d]", i)));
pmuirq.emplace_back(new SignalReceiver(csprintf("pmuirq[%d]", i)));
vcpumntirq.emplace_back(
new SignalReceiver(csprintf("vcpumntirq[%d]", i)));
cntpnsirq.emplace_back(
new SignalReceiver(csprintf("cntpnsirq[%d]", i)));
Base::cnthpirq[i].bind(cnthpirq[i]->signal_in);
Base::cnthvirq[i].bind(cnthvirq[i]->signal_in);
Base::cntpsirq[i].bind(cntpsirq[i]->signal_in);
Base::cntvirq[i].bind(cntvirq[i]->signal_in);
Base::commirq[i].bind(commirq[i]->signal_in);
Base::ctidbgirq[i].bind(ctidbgirq[i]->signal_in);
Base::pmuirq[i].bind(pmuirq[i]->signal_in);
Base::vcpumntirq[i].bind(vcpumntirq[i]->signal_in);
Base::cntpnsirq[i].bind(cntpnsirq[i]->signal_in);
}
clockRateControl.bind(this->clock_rate_s);
this->add_attribute(gem5CpuCluster);
this->add_attribute(clockPeriod);
SC_METHOD(clockChangeHandler);
this->dont_initialize();
this->sensitive << clockChanged;
sendFunctional.value = [this](PacketPtr pkt) { sendFunc(pkt); };
this->add_attribute(sendFunctional);
}
template <class Types>
void
ScxEvsCortexA76<Types>::sendFunc(PacketPtr pkt)
{
auto *trans = sc_gem5::packet2payload(pkt);
panic_if(Base::amba->transport_dbg(*trans) != trans->get_data_length(),
"Didn't send entire functional packet!");
trans->release();
}
template <class Types>
void
ScxEvsCortexA76<Types>::before_end_of_elaboration()
{
Base::before_end_of_elaboration();
auto *cluster = gem5CpuCluster.value;
auto set_on_change = [cluster](
SignalReceiver &recv, ArmInterruptPinGen *gen, int num)
{
auto *pin = gen->get(cluster->getCore(num)->getContext(0));
auto handler = [pin](bool status)
{
status ? pin->raise() : pin->clear();
};
recv.onChange(handler);
};
for (int i = 0; i < CoreCount; i++) {
set_on_change(*cnthpirq[i], cluster->params().cnthpirq, i);
set_on_change(*cnthvirq[i], cluster->params().cnthvirq, i);
set_on_change(*cntpsirq[i], cluster->params().cntpsirq, i);
set_on_change(*cntvirq[i], cluster->params().cntvirq, i);
set_on_change(*commirq[i], cluster->params().commirq, i);
set_on_change(*ctidbgirq[i], cluster->params().ctidbgirq, i);
set_on_change(*pmuirq[i], cluster->params().pmuirq, i);
set_on_change(*vcpumntirq[i], cluster->params().vcpumntirq, i);
set_on_change(*cntpnsirq[i], cluster->params().cntpnsirq, i);
}
}
template <class Types>
Port &
ScxEvsCortexA76<Types>::gem5_getPort(const std::string &if_name, int idx)
{
if (if_name == "redistributor")
return *redist.at(idx);
else if (if_name == "amba")
return amba;
else
return Base::gem5_getPort(if_name, idx);
}
template class ScxEvsCortexA76<ScxEvsCortexA76x1Types>;
} // namespace FastModel
FastModel::ScxEvsCortexA76x1 *
FastModelScxEvsCortexA76x1Params::create()
{
return new FastModel::ScxEvsCortexA76x1(name.c_str(), *this);
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2019 Google, Inc.
*
* 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.
*
* Authors: Gabe Black
*/
#ifndef __ARCH_ARM_FASTMODEL_CORTEXA76_EVS_HH__
#define __ARCH_ARM_FASTMODEL_CORTEXA76_EVS_HH__
#include <memory>
#include "arch/arm/fastmodel/amba_ports.hh"
#include "arch/arm/fastmodel/common/signal_receiver.hh"
#include "arch/arm/fastmodel/protocol/exported_clock_rate_control.hh"
#include "mem/port_proxy.hh"
#include "params/FastModelScxEvsCortexA76x1.hh"
#include "scx_evs_CortexA76x1.h"
#include "systemc/ext/core/sc_event.hh"
#include "systemc/ext/core/sc_module.hh"
#include "systemc/tlm_port_wrapper.hh"
namespace FastModel
{
class CortexA76Cluster;
template <class Types>
class ScxEvsCortexA76 : public Types::Base
{
private:
static const int CoreCount = Types::CoreCount;
using Base = typename Types::Base;
using Params = typename Types::Params;
SC_HAS_PROCESS(ScxEvsCortexA76);
ClockRateControlInitiatorSocket clockRateControl;
typedef sc_gem5::TlmTargetBaseWrapper<
64, svp_gicv3_comms::gicv3_comms_fw_if,
svp_gicv3_comms::gicv3_comms_bw_if, 1,
sc_core::SC_ONE_OR_MORE_BOUND> TlmGicTarget;
AmbaInitiator amba;
std::vector<std::unique_ptr<TlmGicTarget>> redist;
std::vector<std::unique_ptr<SignalReceiver>> cnthpirq;
std::vector<std::unique_ptr<SignalReceiver>> cnthvirq;
std::vector<std::unique_ptr<SignalReceiver>> cntpsirq;
std::vector<std::unique_ptr<SignalReceiver>> cntvirq;
std::vector<std::unique_ptr<SignalReceiver>> commirq;
std::vector<std::unique_ptr<SignalReceiver>> ctidbgirq;
std::vector<std::unique_ptr<SignalReceiver>> pmuirq;
std::vector<std::unique_ptr<SignalReceiver>> vcpumntirq;
std::vector<std::unique_ptr<SignalReceiver>> cntpnsirq;
sc_core::sc_event clockChanged;
sc_core::sc_attribute<Tick> clockPeriod;
sc_core::sc_attribute<CortexA76Cluster *> gem5CpuCluster;
sc_core::sc_attribute<PortProxy::SendFunctionalFunc> sendFunctional;
void sendFunc(PacketPtr pkt);
void clockChangeHandler();
const Params &params;
public:
ScxEvsCortexA76(const sc_core::sc_module_name &mod_name, const Params &p);
void before_end_of_elaboration() override;
Port &gem5_getPort(const std::string &if_name, int idx) override;
void
end_of_elaboration() override
{
Base::end_of_elaboration();
Base::start_of_simulation();
}
void start_of_simulation() override {}
};
struct ScxEvsCortexA76x1Types
{
using Base = scx_evs_CortexA76x1;
using Params = FastModelScxEvsCortexA76x1Params;
static const int CoreCount = 1;
};
using ScxEvsCortexA76x1 = ScxEvsCortexA76<ScxEvsCortexA76x1Types>;
extern template class ScxEvsCortexA76<ScxEvsCortexA76x1Types>;
} // namespace FastModel
#endif // __ARCH_ARM_FASTMODEL_CORTEXA76_EVS_HH__

View File

@@ -86,7 +86,7 @@ component CortexA76x1
// Clocks.
clock1Hz.clk_out => clockDiv.clk_in;
clock1Hz.clk_out => clockDivPeriph.clk_in;
clockDiv.clk_out => core.core_clk_in[0];
clockDiv.clk_out => core.core_clk_in;
clockDivPeriph.clk_out => core.clk_in;
}

View File

@@ -1,4 +1,4 @@
sgproject "CortexA76x1.sgproj"
sgproject "x1.sgproj"
{
TOP_LEVEL_COMPONENT = "CortexA76x1";
ACTIVE_CONFIG_LINUX = "gcc";
@@ -21,7 +21,7 @@ config "gcc"
}
files
{
path = "CortexA76x1.lisa";
path = "x1.lisa";
path = "${PVLIB_HOME}/etc/sglib.sgrepo";
path = "../protocol/ExportedClockRateControlProtocol.lisa";
}