From 390c2b67e4416b495374eb654dd99a3afc05bb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20M=C3=BCck?= Date: Wed, 17 May 2023 19:06:15 -0500 Subject: [PATCH] mem-ruby: Implement a CHI generic controller Component implementing a generic controller that allow classic caches interaction with Ruby/CHI. The CHIGenericController provides an interface to send/receive CHI messages to/from the interconnect. This is implement in C++ rather then SLICC. This controller is seen as a MachineType:Cache by the CHI implementation in SLICC. Change-Id: I3afc4363f4290095c2f7428c8487bccd932e0300 --- .../ruby/protocol/chi/generic/CHIGeneric.py | 55 ++++ .../chi/generic/CHIGenericController.cc | 290 ++++++++++++++++++ .../chi/generic/CHIGenericController.hh | 202 ++++++++++++ src/mem/ruby/protocol/chi/generic/SConscript | 49 +++ 4 files changed, 596 insertions(+) create mode 100644 src/mem/ruby/protocol/chi/generic/CHIGeneric.py create mode 100644 src/mem/ruby/protocol/chi/generic/CHIGenericController.cc create mode 100644 src/mem/ruby/protocol/chi/generic/CHIGenericController.hh create mode 100644 src/mem/ruby/protocol/chi/generic/SConscript diff --git a/src/mem/ruby/protocol/chi/generic/CHIGeneric.py b/src/mem/ruby/protocol/chi/generic/CHIGeneric.py new file mode 100644 index 0000000000..09260dc5a4 --- /dev/null +++ b/src/mem/ruby/protocol/chi/generic/CHIGeneric.py @@ -0,0 +1,55 @@ +# Copyright (c) 2023 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. + +from m5.objects.Controller import RubyController +from m5.params import * + + +class CHIGenericController(RubyController): + type = "CHIGenericController" + cxx_header = "mem/ruby/protocol/chi/generic/CHIGenericController.hh" + cxx_class = "gem5::ruby::CHIGenericController" + abstract = True + + data_channel_size = Param.Int("") + + reqOut = Param.MessageBuffer("") + snpOut = Param.MessageBuffer("") + rspOut = Param.MessageBuffer("") + datOut = Param.MessageBuffer("") + reqIn = Param.MessageBuffer("") + snpIn = Param.MessageBuffer("") + rspIn = Param.MessageBuffer("") + datIn = Param.MessageBuffer("") diff --git a/src/mem/ruby/protocol/chi/generic/CHIGenericController.cc b/src/mem/ruby/protocol/chi/generic/CHIGenericController.cc new file mode 100644 index 0000000000..6758c14a1b --- /dev/null +++ b/src/mem/ruby/protocol/chi/generic/CHIGenericController.cc @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2023 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 "mem/ruby/protocol/chi/generic/CHIGenericController.hh" + +#include +#include + +#include +#include +#include +#include + +#include "debug/RubyCHIGeneric.hh" +#include "mem/ruby/network/Network.hh" +#include "mem/ruby/protocol/MemoryMsg.hh" +#include "mem/ruby/system/RubySystem.hh" +#include "mem/ruby/system/Sequencer.hh" + +namespace gem5 +{ + +namespace ruby +{ + +CHIGenericController::CHIGenericController(const Params &p) + : AbstractController(p), + reqOut(p.reqOut), snpOut(p.snpOut), + rspOut(p.rspOut), datOut(p.datOut), + reqIn(p.reqIn), snpIn(p.snpIn), + rspIn(p.rspIn), datIn(p.datIn), + cacheLineSize(p.ruby_system->getBlockSizeBytes()), + cacheLineBits(floorLog2(cacheLineSize)), + dataChannelSize(p.data_channel_size), + dataMsgsPerLine(cacheLineSize / p.data_channel_size) +{ + m_machineID.type = MachineType_Cache; + m_machineID.num = m_version; + p.ruby_system->registerAbstractController(this); + p.ruby_system->m_num_controllers[m_machineID.type]++; + m_ruby_system = p.ruby_system; +} + +void +CHIGenericController::initNetQueues() +{ + int base = m_ruby_system->MachineType_base_number(m_machineID.type); + + assert(m_net_ptr != nullptr); + + m_net_ptr->setToNetQueue(m_version + base, reqOut->getOrdered(), + CHI_REQ, "none", reqOut); + m_net_ptr->setToNetQueue(m_version + base, snpOut->getOrdered(), + CHI_SNP, "none", snpOut); + m_net_ptr->setToNetQueue(m_version + base, rspOut->getOrdered(), + CHI_RSP, "none", rspOut); + m_net_ptr->setToNetQueue(m_version + base, datOut->getOrdered(), + CHI_DAT, "response", datOut); + + m_net_ptr->setFromNetQueue(m_version + base, reqIn->getOrdered(), + CHI_REQ, "none", reqIn); + m_net_ptr->setFromNetQueue(m_version + base, snpIn->getOrdered(), + CHI_SNP, "none", snpIn); + m_net_ptr->setFromNetQueue(m_version + base, rspIn->getOrdered(), + CHI_RSP, "none", rspIn); + m_net_ptr->setFromNetQueue(m_version + base, datIn->getOrdered(), + CHI_DAT, "response", datIn); +} + +void +CHIGenericController::init() +{ + AbstractController::init(); + + rspIn->setConsumer(this); + datIn->setConsumer(this); + snpIn->setConsumer(this); + reqIn->setConsumer(this); + + resetStats(); +} + +void +CHIGenericController::addSequencer(RubyPort *seq) +{ + assert(seq != nullptr); + sequencers.emplace_back(seq); +} + +void +CHIGenericController::print(std::ostream& out) const +{ + out << "[CHIGenericController " << m_version << "]"; +} + +Sequencer* +CHIGenericController::getCPUSequencer() const +{ + // CHIGenericController doesn't have a CPUSequencer + return nullptr; +} + +DMASequencer* +CHIGenericController::getDMASequencer() const +{ + // CHIGenericController doesn't have a DMASequencer + return nullptr; +} + +GPUCoalescer* +CHIGenericController::getGPUCoalescer() const +{ + // CHIGenericController doesn't have a GPUCoalescer + return nullptr; +} + +MessageBuffer* +CHIGenericController::getMandatoryQueue() const +{ + // CHIGenericController doesn't have a MandatoryQueue + return nullptr; +} + +MessageBuffer* +CHIGenericController::getMemReqQueue() const +{ + // CHIGenericController doesn't have a MemReqQueue + return nullptr; +} + +MessageBuffer* +CHIGenericController::getMemRespQueue() const +{ + // CHIGenericController doesn't have a MemRespQueue + return nullptr; +} + +void +CHIGenericController::regStats() +{ + AbstractController::regStats(); +} + +void +CHIGenericController::collateStats() +{ + +} + +void +CHIGenericController::resetStats() +{ + AbstractController::resetStats(); +} + + +void +CHIGenericController::wakeup() +{ + bool pending = false; + + DPRINTF(RubyCHIGeneric, "wakeup: checking incoming rsp messages\n"); + pending = pending || receiveAllRdyMessages(rspIn, + [this](const CHIResponseMsg* msg){ return recvResponseMsg(msg); }); + + DPRINTF(RubyCHIGeneric, "wakeup: checking incoming dat messages\n"); + pending = pending || receiveAllRdyMessages(datIn, + [this](const CHIDataMsg* msg){ return recvDataMsg(msg); }); + + DPRINTF(RubyCHIGeneric, "wakeup: checking incoming snp messages\n"); + pending = pending || receiveAllRdyMessages(snpIn, + [this](const CHIRequestMsg* msg){ return recvSnoopMsg(msg); }); + + DPRINTF(RubyCHIGeneric, "wakeup: checking incoming req messages\n"); + pending = pending || receiveAllRdyMessages(reqIn, + [this](const CHIRequestMsg* msg){ return recvRequestMsg(msg); }); + + if (pending) { + DPRINTF(RubyCHIGeneric, "wakeup: messages pending\n"); + scheduleEvent(Cycles(1)); + } +} + +void +CHIGenericController::recordCacheTrace(int cntrl, CacheRecorder* tr) +{ + panic("CHIGenericController doesn't implement recordCacheTrace"); +} + +AccessPermission +CHIGenericController::getAccessPermission(const Addr& param_addr) +{ + return AccessPermission_NotPresent; +} + +void +CHIGenericController::functionalRead( + const Addr& param_addr, Packet* param_pkt, WriteMask& param_mask) +{ + panic("CHIGenericController doesn't expect functionalRead"); +} + +int +CHIGenericController::functionalWrite( + const Addr& param_addr, Packet* param_pkt) +{ + panic("CHIGenericController doesn't expect functionalRead"); + return 0; +} + +int +CHIGenericController::functionalWriteBuffers(PacketPtr& pkt) +{ + int num_functional_writes = 0; + num_functional_writes += reqOut->functionalWrite(pkt); + num_functional_writes += snpOut->functionalWrite(pkt); + num_functional_writes += rspOut->functionalWrite(pkt); + num_functional_writes += datOut->functionalWrite(pkt); + num_functional_writes += reqIn->functionalWrite(pkt); + num_functional_writes += snpIn->functionalWrite(pkt); + num_functional_writes += rspIn->functionalWrite(pkt); + num_functional_writes += datIn->functionalWrite(pkt); + return num_functional_writes; +} + +bool +CHIGenericController::functionalReadBuffers(PacketPtr& pkt) +{ + if (reqOut->functionalRead(pkt)) return true; + if (snpOut->functionalRead(pkt)) return true; + if (rspOut->functionalRead(pkt)) return true; + if (datOut->functionalRead(pkt)) return true; + if (reqIn->functionalRead(pkt)) return true; + if (snpIn->functionalRead(pkt)) return true; + if (rspIn->functionalRead(pkt)) return true; + if (datIn->functionalRead(pkt)) return true; + return false; +} + +bool +CHIGenericController::functionalReadBuffers(PacketPtr& pkt, WriteMask &mask) +{ + bool read = false; + if (reqOut->functionalRead(pkt, mask)) read = true; + if (snpOut->functionalRead(pkt, mask)) read = true; + if (rspOut->functionalRead(pkt, mask)) read = true; + if (datOut->functionalRead(pkt, mask)) read = true; + if (reqIn->functionalRead(pkt, mask)) read = true; + if (snpIn->functionalRead(pkt, mask)) read = true; + if (rspIn->functionalRead(pkt, mask)) read = true; + if (datIn->functionalRead(pkt, mask)) read = true; + return read; +} + +} // namespace ruby +} // namespace gem5 diff --git a/src/mem/ruby/protocol/chi/generic/CHIGenericController.hh b/src/mem/ruby/protocol/chi/generic/CHIGenericController.hh new file mode 100644 index 0000000000..3f6ce3d851 --- /dev/null +++ b/src/mem/ruby/protocol/chi/generic/CHIGenericController.hh @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2023 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. + */ + +#ifndef __MEM_RUBY_PROTOCOL_CHI_CHIGenericController_HH__ +#define __MEM_RUBY_PROTOCOL_CHI_CHIGenericController_HH__ + +#include +#include +#include + +#include "mem/ruby/common/Consumer.hh" +#include "mem/ruby/network/MessageBuffer.hh" +#include "mem/ruby/protocol/AccessPermission.hh" +#include "mem/ruby/slicc_interface/AbstractController.hh" +#include "mem/ruby/system/CacheRecorder.hh" +#include "params/CHIGenericController.hh" + +// Generated from SLICC +#include "mem/ruby/protocol/CHIDataMsg.hh" +#include "mem/ruby/protocol/CHIRequestMsg.hh" +#include "mem/ruby/protocol/CHIResponseMsg.hh" +#include "mem/ruby/protocol/Cache_Controller.hh" + +namespace gem5 +{ + +namespace ruby +{ + +class CHIGenericController : public AbstractController +{ + public: + PARAMS(CHIGenericController); + CHIGenericController(const Params &p); + + void init() override; + + MessageBuffer *getMandatoryQueue() const override; + MessageBuffer *getMemReqQueue() const override; + MessageBuffer *getMemRespQueue() const override; + void initNetQueues() override; + + void print(std::ostream& out) const override; + void wakeup() override; + void resetStats() override; + void regStats() override; + void collateStats() override; + + void recordCacheTrace(int cntrl, CacheRecorder* tr) override; + Sequencer* getCPUSequencer() const override; + DMASequencer* getDMASequencer() const override; + GPUCoalescer* getGPUCoalescer() const override; + + void addSequencer(RubyPort* seq); + + bool functionalReadBuffers(PacketPtr&) override; + bool functionalReadBuffers(PacketPtr&, WriteMask&) override; + int functionalWriteBuffers(PacketPtr&) override; + + AccessPermission getAccessPermission(const Addr& param_addr) override; + + void functionalRead(const Addr& param_addr, Packet* param_pkt, + WriteMask& param_mask) override; + int functionalWrite(const Addr& param_addr, Packet* param_pkt) override; + + protected: + MessageBuffer* const reqOut; + MessageBuffer* const snpOut; + MessageBuffer* const rspOut; + MessageBuffer* const datOut; + MessageBuffer* const reqIn; + MessageBuffer* const snpIn; + MessageBuffer* const rspIn; + MessageBuffer* const datIn; + + std::vector sequencers; + + public: + const int cacheLineSize; + const int cacheLineBits; + const int dataChannelSize; + const int dataMsgsPerLine; + + // interface to generic requesters and responders + + enum CHIChannel + { + CHI_REQ = 0, + CHI_SNP = 1, + CHI_RSP = 2, + CHI_DAT = 3 + }; + + typedef std::shared_ptr CHIRequestMsgPtr; + typedef std::shared_ptr CHIResponseMsgPtr; + typedef std::shared_ptr CHIDataMsgPtr; + + bool + sendRequestMsg(CHIRequestMsgPtr msg) + { + return sendMessage(msg, reqOut); + } + + bool + sendSnoopMsg(CHIRequestMsgPtr msg) + { + return sendMessage(msg, snpOut); + } + + bool + sendResponseMsg(CHIResponseMsgPtr msg) + { + return sendMessage(msg, rspOut); + } + + bool + sendDataMsg(CHIDataMsgPtr msg) + { + return sendMessage(msg, datOut); + } + + protected: + virtual bool recvRequestMsg(const CHIRequestMsg *msg) = 0; + virtual bool recvSnoopMsg(const CHIRequestMsg *msg) = 0; + virtual bool recvResponseMsg(const CHIResponseMsg *msg) = 0; + virtual bool recvDataMsg(const CHIDataMsg *msg) = 0; + + private: + template + bool receiveAllRdyMessages(MessageBuffer *buffer, + const std::function &callback) + { + bool pending = false; + Tick cur_tick = curTick(); + while (buffer->isReady(cur_tick)) { + const MsgType *msg = + dynamic_cast(buffer->peek()); + assert(msg); + if (callback(msg)) + buffer->dequeue(cur_tick); + else { + pending = true; + break; + } + } + return pending; + } + + template + bool sendMessage(MessageType &msg, MessageBuffer *buffer) + { + Tick cur_tick = curTick(); + if (buffer->areNSlotsAvailable(1, cur_tick)) { + buffer->enqueue(msg, curTick(), cyclesToTicks(Cycles(1)), + m_ruby_system->getRandomization(), + m_ruby_system->getWarmupEnabled()); + return true; + } else { + return false; + } + } + +}; + +} // namespace ruby +} // namespace gem5 + +#endif // __CHIGenericController_H__ diff --git a/src/mem/ruby/protocol/chi/generic/SConscript b/src/mem/ruby/protocol/chi/generic/SConscript new file mode 100644 index 0000000000..135c988b8e --- /dev/null +++ b/src/mem/ruby/protocol/chi/generic/SConscript @@ -0,0 +1,49 @@ +# -*- mode:python -*- + +# Copyright (c) 2023 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. + +Import('*') + +if env['CONF']['PROTOCOL'] != 'CHI': + Return() + +SimObject('CHIGeneric.py', + sim_objects=['CHIGenericController']) + +DebugFlag('RubyCHIGeneric') +DebugFlag('RubyCHIGenericVerbose') + +Source('CHIGenericController.cc')