From b795d28ee83840fd8f6c8a81a4069984f4abccd0 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Tue, 6 Aug 2024 10:32:03 +0100 Subject: [PATCH] mem-ruby: Add a CHI-TLM CacheController This commit is extending the previously defined CHIGenericController to implement a CacheController which acts as a bridge between the AMBA TLM 2.0 implementation of CHI [1][2] with the gem5 (ruby) one. In other words it translates AMBA CHI transactions into ruby messages (which are then forwarded to the MessageQueues) and viceversa. ARM::CHI::Payload, CHIRequestMsg <--> CHIDataMsg ARM::CHI::Phase CHIResponseMsg CHIDataMsg [1]: https://developer.arm.com/documentation/101459/latest [2]: https://developer.arm.com/Architectures/AMBA#Downloads Change-Id: I6f35e7b4ade4d0de1b5e5d2dbf73ce796a9f9fb6 Signed-off-by: Giacomo Travaglini --- src/mem/ruby/protocol/chi/tlm/SConscript | 75 +++ src/mem/ruby/protocol/chi/tlm/SConsopts | 58 ++ .../ruby/protocol/chi/tlm/TlmController.py | 43 ++ src/mem/ruby/protocol/chi/tlm/controller.cc | 424 +++++++++++++++ src/mem/ruby/protocol/chi/tlm/controller.hh | 177 +++++++ src/mem/ruby/protocol/chi/tlm/utils.cc | 498 ++++++++++++++++++ src/mem/ruby/protocol/chi/tlm/utils.hh | 82 +++ 7 files changed, 1357 insertions(+) create mode 100644 src/mem/ruby/protocol/chi/tlm/SConscript create mode 100644 src/mem/ruby/protocol/chi/tlm/SConsopts create mode 100644 src/mem/ruby/protocol/chi/tlm/TlmController.py create mode 100644 src/mem/ruby/protocol/chi/tlm/controller.cc create mode 100644 src/mem/ruby/protocol/chi/tlm/controller.hh create mode 100644 src/mem/ruby/protocol/chi/tlm/utils.cc create mode 100644 src/mem/ruby/protocol/chi/tlm/utils.hh diff --git a/src/mem/ruby/protocol/chi/tlm/SConscript b/src/mem/ruby/protocol/chi/tlm/SConscript new file mode 100644 index 0000000000..53083ef9a1 --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/SConscript @@ -0,0 +1,75 @@ +# -*- mode:python -*- +# Copyright (c) 2024 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("*") + + +def get_tlm_paths(): + tlm_dir = Dir(env["CONF"]["TLM_PATH"]) + assert tlm_dir is not None and tlm_dir != "." + + include_dir = tlm_dir.Dir("include") + lib_dir = tlm_dir.Dir("lib") + + model_lib = "libarmtlmchi.so" + if not lib_dir.File(model_lib).exists(): + print(f"Error: Can't find {model_lib} in AMBA TLM directory.") + print(f"TLM path: {tlm_dir}") + Exit(1) + + return include_dir, lib_dir, "armtlmchi" + + +if env["CONF"]["BUILD_TLM"]: + include_path, lib_path, tlm_lib = get_tlm_paths() + + env.Append(CPPPATH=include_path) + env.Append(LIBPATH=lib_path) + env.Append(LIBS=[tlm_lib]) + + SimObject( + "TlmController.py", sim_objects=["TlmController"], tags="arm isa" + ) + Source("utils.cc", tags="arm isa") + Source("controller.cc", tags="arm isa") + DebugFlag("TLM", tags="arm isa") + + print( + "BUILD_TLM set: " + f"Building TLM integration with libarmtlmchi.so from '{lib_path}'\n" + ) +elif not GetOption("silent"): + print("BUILD_TLM not set, not building CHI-TLM integration\n") diff --git a/src/mem/ruby/protocol/chi/tlm/SConsopts b/src/mem/ruby/protocol/chi/tlm/SConsopts new file mode 100644 index 0000000000..463a1207e3 --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/SConsopts @@ -0,0 +1,58 @@ +# -*- 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('*') + +import os + +tlm_vars = Variables() +tlm_vars.AddVariables( + PathVariable('TLM_PATH', "CHI TLM library path", '.', PathVariable.PathIsDir), + ) +tlm_vars.Update(main) + +main["CONF"]["BUILD_TLM"] = False + +tlm_path_ev = os.environ.get("TLM_PATH", None) +if tlm_path_ev is not None and tlm_path_ev != ".": + print(f"Setting TLM_PATH from os.environ: {tlm_path_ev}") + main["CONF"]["TLM_PATH"] = tlm_path_ev + main["CONF"]["BUILD_TLM"] = True + +if "TLM_PATH" in main and main["TLM_PATH"] != ".": + print(f"Setting TLM_PATH to SCons command line: {main['TLM_PATH']}") + main["CONF"]["TLM_PATH"] = str(main["TLM_PATH"]) + main["CONF"]["BUILD_TLM"] = True diff --git a/src/mem/ruby/protocol/chi/tlm/TlmController.py b/src/mem/ruby/protocol/chi/tlm/TlmController.py new file mode 100644 index 0000000000..7dea6c57aa --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/TlmController.py @@ -0,0 +1,43 @@ +# 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.CHIGeneric import CHIGenericController +from m5.params import * + + +class TlmController(CHIGenericController): + type = "TlmController" + cxx_header = "mem/ruby/protocol/chi/tlm/controller.hh" + cxx_class = "gem5::tlm::chi::CacheController" diff --git a/src/mem/ruby/protocol/chi/tlm/controller.cc b/src/mem/ruby/protocol/chi/tlm/controller.cc new file mode 100644 index 0000000000..aa8623688a --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/controller.cc @@ -0,0 +1,424 @@ +/* + * 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/tlm/controller.hh" + +#include "debug/TLM.hh" +#include "mem/ruby/protocol/CHIDataMsg.hh" +#include "mem/ruby/protocol/CHIRequestMsg.hh" +#include "mem/ruby/protocol/CHIResponseMsg.hh" +#include "mem/ruby/protocol/chi/tlm/utils.hh" + +namespace gem5 { + +namespace tlm::chi { + +using namespace ruby; + +CacheController::CacheController(const Params &p) + : CHIGenericController(p) +{ +} + +bool +CacheController::recvRequestMsg(const CHIRequestMsg *msg) +{ + panic("recvRequestMsg"); + return true; +} + +bool +CacheController::recvSnoopMsg(const CHIRequestMsg *msg) +{ + ARM::CHI::Phase phase; + ARM::CHI::Payload *payload = ARM::CHI::Payload::new_payload(); + + payload->address = msg->m_addr; + payload->ns = msg->m_ns; + phase.channel = ARM::CHI::CHANNEL_SNP; + phase.snp_opcode = ruby_to_tlm::snpOpcode(msg->m_type); + phase.txn_id = msg->m_txnId % 1024; + + bw(payload, &phase); + + // payload->unref(); ?? + return true; +} + +void +CacheController::pCreditGrant(const CHIResponseMsg *msg) +{ + ARM::CHI::Phase phase; + ARM::CHI::Payload *payload = ARM::CHI::Payload::new_payload(); + phase.channel = ARM::CHI::CHANNEL_RSP; + phase.rsp_opcode = ARM::CHI::RSP_OPCODE_PCRD_GRANT; + phase.pcrd_type = 0; // TODO: set this one depending on allow retry + + bw(payload, &phase); + + payload->unref(); // TODO: check this +} + +bool +CacheController::recvResponseMsg(const CHIResponseMsg *msg) +{ + + if (msg->m_type == ruby::CHIResponseType_PCrdGrant) { + // P-credit grant does not refer to a specific transaction id + pCreditGrant(msg); + return true; + } + + auto txn_id = msg->m_txnId; + if (auto it = pendingTransactions.find(txn_id); + it != pendingTransactions.end()) { + + auto& transaction = it->second; + if (transaction->handle(msg)) + pendingTransactions.erase(it); + } else { + panic("recvResponseMsg"); + } + return true; +} + +bool +CacheController::recvDataMsg(const CHIDataMsg *msg) +{ + auto txn_id = msg->m_txnId; + if (auto it = pendingTransactions.find(txn_id); + it != pendingTransactions.end()) { + auto& transaction = it->second; + if (transaction->handle(msg)) + pendingTransactions.erase(it); + } else { + panic("Not able to find transaction"); + } + return true; +} + +bool +CacheController::Transaction::handle(const CHIResponseMsg *msg) +{ + const auto opcode = ruby_to_tlm::rspOpcode(msg->m_type); + phase.channel = ARM::CHI::CHANNEL_RSP; + phase.rsp_opcode = opcode; + phase.resp = ruby_to_tlm::rspResp(msg->m_type); + phase.txn_id = msg->m_txnId % 1024; + + controller->bw(payload, &phase); + return opcode != ARM::CHI::RSP_OPCODE_RETRY_ACK; +} + +bool +CacheController::ReadTransaction::handle(const CHIDataMsg *msg) +{ + dataMsgCnt++; + + for (auto byte = 0; byte < controller->cacheLineSize; byte++) { + if (msg->m_bitMask.test(byte)) + payload->data[byte] = msg->m_dataBlk.getByte(byte); + } + + // ARM::CHI::Phase phase; + phase.channel = ARM::CHI::CHANNEL_DAT; + phase.dat_opcode = ruby_to_tlm::datOpcode(msg->m_type); + phase.resp = ruby_to_tlm::datResp(msg->m_type); + phase.txn_id = msg->m_txnId % 1024; + phase.data_id = dataId(msg->m_addr + msg->m_bitMask.firstBitSet(true)); + + // This is a hack, we should fix it on the ruby side + if (forward(msg)) { + controller->bw(payload, &phase); + } + + if (dataMsgCnt == controller->dataMsgsPerLine) { + if (phase.exp_comp_ack == false) { + // The client is not sending a CompAck but ruby is + // expecting it so we send it anyway + controller->sendCompAck(*payload, phase); + } + return true; + } else { + return false; + } +} + +bool +CacheController::ReadTransaction::handle(const CHIResponseMsg *msg) +{ + /// TODO: remove this, DBID is not sent + phase.dbid = msg->m_dbid % 1024; + return Transaction::handle(msg); +} + +bool +CacheController::ReadTransaction::forward(const CHIDataMsg *msg) +{ + if (payload->size == 6) { + return true; + } else { + if (msg->m_bitMask.test(payload->address - msg->m_addr)) { + return true; + } else { + // This is not the packet holding the original address + // Do not forward it back (just drop it) + return false; + } + } +} + +bool +CacheController::DatalessTransaction::handle(const CHIResponseMsg *msg) +{ + const auto opcode = ruby_to_tlm::rspOpcode(msg->m_type); + assert(opcode == ARM::CHI::RSP_OPCODE_COMP || + opcode == ARM::CHI::RSP_OPCODE_RETRY_ACK); + + return Transaction::handle(msg); +} + +bool +CacheController::WriteTransaction::handle(const CHIResponseMsg *msg) +{ + const auto opcode = ruby_to_tlm::rspOpcode(msg->m_type); + if (opcode == ARM::CHI::RSP_OPCODE_COMP_DBID_RESP) { + recvComp = true; + recvDBID = true; + } + if (opcode == ARM::CHI::RSP_OPCODE_COMP) { + recvComp = true; + } + if (opcode == ARM::CHI::RSP_OPCODE_DBID_RESP) { + recvDBID = true; + } + + phase.dbid = msg->m_dbid % 1024; + Transaction::handle(msg); + + return recvComp && recvDBID; +} + +void +CacheController::sendCompAck(ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) +{ + auto res_msg = std::make_shared( + curTick(), cacheLineSize, m_ruby_system); + + res_msg->m_addr = ruby::makeLineAddress(payload.address, cacheLineBits); + res_msg->m_type = CHIResponseType_CompAck; + res_msg->m_Destination.add(mapAddressToDownstreamMachine(payload.address)); + res_msg->m_responder = getMachineID(); + + res_msg->m_txnId = phase.txn_id; + + sendResponseMsg(res_msg); +} + +void +CacheController::sendMsg(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase) +{ + switch (phase.channel) { + case ARM::CHI::CHANNEL_REQ: + sendRequestMsg(payload, phase); + break; + case ARM::CHI::CHANNEL_DAT: + sendDataMsg(payload, phase); + break; + case ARM::CHI::CHANNEL_RSP: + sendResponseMsg(payload, phase); + break; + default: + panic("Unimplemented channel: %d", phase.channel); + } +} + +Addr +CacheController::reqAddr(ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) const +{ + switch (phase.req_opcode) { + case ARM::CHI::REQ_OPCODE_WRITE_NO_SNP_PTL: + return ruby::makeLineAddress(payload.address, cacheLineBits) + + ctz64(payload.byte_enable); + default: + return payload.address; + } +} + +Addr +CacheController::reqSize(ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) const +{ + switch (phase.req_opcode) { + case ARM::CHI::REQ_OPCODE_WRITE_NO_SNP_PTL: + assert(transactionSize(payload.size) >= popCount(payload.byte_enable)); + return popCount(payload.byte_enable); + default: + return transactionSize(payload.size); + } +} + +void +CacheController::sendRequestMsg(ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) +{ + auto req_msg = std::make_shared( + curTick(), cacheLineSize, m_ruby_system); + + req_msg->m_addr = ruby::makeLineAddress(payload.address, cacheLineBits); + req_msg->m_accAddr = reqAddr(payload, phase); + req_msg->m_accSize = reqSize(payload, phase); + req_msg->m_requestor = getMachineID(); + req_msg->m_fwdRequestor = getMachineID(); + req_msg->m_dataToFwdRequestor = false; + req_msg->m_type = tlm_to_ruby::reqOpcode(phase.req_opcode); + req_msg->m_isSeqReqValid = false; + req_msg->m_is_local_pf = false; + req_msg->m_is_remote_pf = false; + req_msg->m_allowRetry = phase.allow_retry; + req_msg->m_Destination.add(mapAddressToDownstreamMachine(payload.address)); + + req_msg->m_txnId = phase.txn_id + (payload.lpid * 1024); + req_msg->m_ns = payload.ns; + + sendRequestMsg(req_msg); + + pendingTransactions[req_msg->m_txnId] = Transaction::gen( + this, payload, phase); +} + +void +CacheController::sendDataMsg(ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) +{ + auto data_msg = std::make_shared( + curTick(), cacheLineSize, m_ruby_system); + + data_msg->m_addr = ruby::makeLineAddress(payload.address, cacheLineBits); + data_msg->m_responder = getMachineID(); + data_msg->m_type = tlm_to_ruby::datOpcode(phase.dat_opcode, phase.resp); + data_msg->m_Destination.add( + mapAddressToDownstreamMachine(payload.address)); + data_msg->m_txnId = phase.txn_id + (payload.lpid * 1024); + data_msg->m_dataBlk.setData(payload.data, 0, cacheLineSize); + + std::vector byte_enabled(cacheLineSize, false); + + const Addr data_id_offset = phase.data_id * 16; + for (int bit = data_id_offset; bit < data_id_offset + 32; bit++) { + if (bits(payload.byte_enable, bit)) { + byte_enabled[bit] = true; + } + } + + data_msg->m_bitMask = WriteMask(byte_enabled.size(), byte_enabled); + + sendDataMsg(data_msg); +} + +void +CacheController::sendResponseMsg(ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) +{ + auto res_msg = std::make_shared( + curTick(), cacheLineSize, m_ruby_system); + + res_msg->m_addr = ruby::makeLineAddress(payload.address, cacheLineBits); + res_msg->m_responder = getMachineID(); + res_msg->m_type = tlm_to_ruby::rspOpcode(phase.rsp_opcode, phase.resp); + res_msg->m_Destination.add(mapAddressToDownstreamMachine(payload.address)); + res_msg->m_txnId = phase.txn_id + (payload.lpid * 1024); + + sendResponseMsg(res_msg); +} + +CacheController::Transaction::Transaction(CacheController *_controller, + ARM::CHI::Payload &_payload, + ARM::CHI::Phase &_phase) + : controller(_controller), payload(&_payload), phase(_phase) +{ + payload->ref(); +} + +CacheController::Transaction::~Transaction() +{ + payload->unref(); +} + +std::unique_ptr +CacheController::Transaction::gen(CacheController *controller, + ARM::CHI::Payload &payload, + ARM::CHI::Phase &phase) +{ + switch (phase.req_opcode) { + case ARM::CHI::REQ_OPCODE_READ_SHARED: + case ARM::CHI::REQ_OPCODE_READ_CLEAN: + case ARM::CHI::REQ_OPCODE_READ_ONCE: + case ARM::CHI::REQ_OPCODE_READ_NO_SNP: + case ARM::CHI::REQ_OPCODE_READ_UNIQUE: + case ARM::CHI::REQ_OPCODE_READ_NOT_SHARED_DIRTY: + case ARM::CHI::REQ_OPCODE_READ_PREFER_UNIQUE: + case ARM::CHI::REQ_OPCODE_MAKE_READ_UNIQUE: + return std::make_unique(controller, payload, phase); + case ARM::CHI::REQ_OPCODE_WRITE_NO_SNP_PTL: + case ARM::CHI::REQ_OPCODE_WRITE_NO_SNP_FULL: + case ARM::CHI::REQ_OPCODE_WRITE_UNIQUE_ZERO: + case ARM::CHI::REQ_OPCODE_WRITE_UNIQUE_FULL: + case ARM::CHI::REQ_OPCODE_WRITE_BACK_FULL: + // This is a write transaction for now. Will + // this still be the case once WriteEvictOrEvict will also be supported + // in ruby + case ARM::CHI::REQ_OPCODE_WRITE_EVICT_OR_EVICT: + return std::make_unique( + controller, payload, phase); + case ARM::CHI::REQ_OPCODE_CLEAN_UNIQUE: + case ARM::CHI::REQ_OPCODE_MAKE_UNIQUE: + case ARM::CHI::REQ_OPCODE_EVICT: + case ARM::CHI::REQ_OPCODE_STASH_ONCE_SEP_SHARED: + case ARM::CHI::REQ_OPCODE_STASH_ONCE_SEP_UNIQUE: + return std::make_unique( + controller, payload, phase); + default: + panic("Impossible to generate transaction object"); + } +} + +} // namespace tlm::chi + +} // namespace gem5 diff --git a/src/mem/ruby/protocol/chi/tlm/controller.hh b/src/mem/ruby/protocol/chi/tlm/controller.hh new file mode 100644 index 0000000000..8fc2272850 --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/controller.hh @@ -0,0 +1,177 @@ +/* + * 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_TLM_CONTROLLER_HH__ +#define __MEM_RUBY_PROTOCOL_CHI_TLM_CONTROLLER_HH__ + +#include + +#include "mem/ruby/protocol/chi/generic/CHIGenericController.hh" +#include "mem/ruby/protocol/CHIDataType.hh" +#include "mem/ruby/protocol/CHIRequestType.hh" +#include "mem/ruby/protocol/CHIResponseType.hh" +#include "mem/ruby/protocol/RequestStatus.hh" +#include "mem/ruby/protocol/WriteMask.hh" +#include "params/TlmController.hh" + +namespace gem5 { + +namespace ruby { +class CHIRequestMsg; +class CHIResponseMsg; +class CHIDataMsg; +} + +namespace tlm::chi { + +/** + * The tlm::chi::CacheController is a ruby CacheController which acts as + * a bridge between the AMBA TLM 2.0 implementation of CHI [1][2] with + * the gem5 (ruby) one. + * + * In other words it translates AMBA CHI transactions into ruby + * messages (which are then forwarded to the MessageQueues) + * and viceversa. + * + * ARM::CHI::Payload, CHIRequestMsg + * <--> CHIDataMsg + * ARM::CHI::Phase CHIResponseMsg + * CHIDataMsg + * + * To connect the tlm::chi::CacheController in python is relatively + * straightforward. The upstream initiator/RNF needs to have a pointer + * to the controller, and this can be done for example with + * SimObject params. + * Example: + * + * class MyRNF(): + * chi_controller = Param.TlmController("TLM-to-Ruby CacheController") + * + * The RNF C++ code would then have to set the + * tlm::chi::CacheController::bw callback to implement the backward path + * to send data from downstream to upstream. + * + * [1]: https://developer.arm.com/documentation/101459/latest + * [2]: https://developer.arm.com/Architectures/AMBA#Downloads + */ +class CacheController : public ruby::CHIGenericController +{ + public: + PARAMS(TlmController); + CacheController(const Params &p); + + /** Set this to send data upstream */ + std::function bw; + + bool recvRequestMsg(const ruby::CHIRequestMsg *msg) override; + bool recvSnoopMsg(const ruby::CHIRequestMsg *msg) override; + bool recvResponseMsg(const ruby::CHIResponseMsg *msg) override; + bool recvDataMsg(const ruby::CHIDataMsg *msg) override; + + void sendMsg(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase); + using CHIGenericController::sendRequestMsg; + void sendRequestMsg(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase); + using CHIGenericController::sendResponseMsg; + void sendResponseMsg(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase); + void sendCompAck(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase); + using CHIGenericController::sendDataMsg; + void sendDataMsg(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase); + + Addr reqAddr(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase) const; + Addr reqSize(ARM::CHI::Payload &payload, ARM::CHI::Phase &phase) const; + + void pCreditGrant(const ruby::CHIResponseMsg *msg); + + struct Transaction + { + enum class Type + { + READ, + WRITE, + DATALESS + }; + + Transaction(CacheController *parent, + ARM::CHI::Payload &_payload, + ARM::CHI::Phase &_phase); + ~Transaction(); + + static std::unique_ptr gen(CacheController *parent, + ARM::CHI::Payload &_payload, + ARM::CHI::Phase &_phase); + + virtual bool + handle(const ruby::CHIDataMsg *msg) + { + panic("Unimplemented"); + } + + virtual bool handle(const ruby::CHIResponseMsg *msg); + + CacheController *controller; + ARM::CHI::Payload *payload; + ARM::CHI::Phase phase; + }; + struct ReadTransaction : public Transaction + { + using Transaction::Transaction; + bool handle(const ruby::CHIDataMsg *msg) override; + bool handle(const ruby::CHIResponseMsg *msg) override; + bool forward(const ruby::CHIDataMsg *msg); + + uint8_t dataMsgCnt = 0; + }; + struct DatalessTransaction : public Transaction + { + using Transaction::Transaction; + bool handle(const ruby::CHIResponseMsg *msg) override; + }; + struct WriteTransaction : public Transaction + { + using Transaction::Transaction; + bool handle(const ruby::CHIResponseMsg *msg) override; + bool recvComp = false; + bool recvDBID = false; + }; + std::unordered_map> pendingTransactions; +}; + +} // namespace tlm::chi + +} // namespace gem5 + +#endif // __MEM_RUBY_PROTOCOL_CHI_TLM_CONTROLLER_HH__ diff --git a/src/mem/ruby/protocol/chi/tlm/utils.cc b/src/mem/ruby/protocol/chi/tlm/utils.cc new file mode 100644 index 0000000000..2959d032c3 --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/utils.cc @@ -0,0 +1,498 @@ +/* + * 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/tlm/utils.hh" + +#include "base/bitfield.hh" +#include "base/logging.hh" + +namespace gem5 { + +namespace tlm::chi { + +namespace { + +using namespace ARM::CHI; + +std::string +reqOpcodeToName(ReqOpcode req_opcode) +{ + switch (req_opcode) { + case REQ_OPCODE_REQ_LCRD_RETURN: return "ReqLCrdReturn"; + case REQ_OPCODE_READ_SHARED: return "ReadShared"; + case REQ_OPCODE_READ_CLEAN: return "ReadClean"; + case REQ_OPCODE_READ_ONCE: return "ReadOnce"; + case REQ_OPCODE_READ_NO_SNP: return "ReadNoSnp"; + case REQ_OPCODE_PCRD_RETURN: return "PCrdReturn"; + case REQ_OPCODE_READ_UNIQUE: return "ReadUnique"; + case REQ_OPCODE_CLEAN_SHARED: return "CleanShared"; + case REQ_OPCODE_CLEAN_INVALID: return "CleanInvalid"; + case REQ_OPCODE_MAKE_INVALID: return "MakeInvalid"; + case REQ_OPCODE_CLEAN_UNIQUE: return "CleanUnique"; + case REQ_OPCODE_MAKE_UNIQUE: return "MakeUnique"; + case REQ_OPCODE_EVICT: return "Evict"; + case REQ_OPCODE_EO_BARRIER: return "EoBarrier"; + case REQ_OPCODE_EC_BARRIER: return "EcBarrier"; + case REQ_OPCODE_READ_NO_SNP_SEP: return "ReadNoSnpSep"; + case REQ_OPCODE_CLEAN_SHARED_PERSIST_SEP: return "CleanSharedPersistSep"; + case REQ_OPCODE_DVM_OP: return "DvmOp"; + case REQ_OPCODE_WRITE_EVICT_FULL: return "WriteEvictFull"; + case REQ_OPCODE_WRITE_CLEAN_PTL: return "WriteCleanPtl"; + case REQ_OPCODE_WRITE_CLEAN_FULL: return "WriteCleanFull"; + case REQ_OPCODE_WRITE_UNIQUE_PTL: return "WriteUniquePtl"; + case REQ_OPCODE_WRITE_UNIQUE_FULL: return "WriteUniqueFull"; + case REQ_OPCODE_WRITE_BACK_PTL: return "WriteBackPtl"; + case REQ_OPCODE_WRITE_BACK_FULL: return "WriteBackFull"; + case REQ_OPCODE_WRITE_NO_SNP_PTL: return "WriteNoSnpPtl"; + case REQ_OPCODE_WRITE_NO_SNP_FULL: return "WriteNoSnpFull"; + case REQ_OPCODE_WRITE_UNIQUE_FULL_STASH: return "WriteUniqueFullStash"; + case REQ_OPCODE_WRITE_UNIQUE_PTL_STASH: return "WriteUniquePtlStash"; + case REQ_OPCODE_STASH_ONCE_SHARED: return "StashOnceShared"; + case REQ_OPCODE_STASH_ONCE_UNIQUE: return "StashOnceUnique"; + case REQ_OPCODE_READ_ONCE_CLEAN_INVALID: return "ReadOnceCleanInvalid"; + case REQ_OPCODE_READ_ONCE_MAKE_INVALID: return "ReadOnceMakeInvalid"; + case REQ_OPCODE_READ_NOT_SHARED_DIRTY: return "ReadNotSharedDirty"; + case REQ_OPCODE_CLEAN_SHARED_PERSIST: return "CleanSharedPersist"; + case REQ_OPCODE_ATOMIC_STORE_ADD: return "AtomicStoreAdd"; + case REQ_OPCODE_ATOMIC_STORE_CLR: return "AtomicStoreClr"; + case REQ_OPCODE_ATOMIC_STORE_EOR: return "AtomicStoreEor"; + case REQ_OPCODE_ATOMIC_STORE_SET: return "AtomicStoreSet"; + case REQ_OPCODE_ATOMIC_STORE_SMAX: return "AtomicStoreSmax"; + case REQ_OPCODE_ATOMIC_STORE_SMIN: return "AtomicStoreSmin"; + case REQ_OPCODE_ATOMIC_STORE_UMAX: return "AtomicStoreUmax"; + case REQ_OPCODE_ATOMIC_STORE_UMIN: return "AtomicStoreUmin"; + case REQ_OPCODE_ATOMIC_LOAD_ADD: return "AtomicLoadAdd"; + case REQ_OPCODE_ATOMIC_LOAD_CLR: return "AtomicLoadClr"; + case REQ_OPCODE_ATOMIC_LOAD_EOR: return "AtomicLoadEor"; + case REQ_OPCODE_ATOMIC_LOAD_SET: return "AtomicLoadSet"; + case REQ_OPCODE_ATOMIC_LOAD_SMAX: return "AtomicLoadSmax"; + case REQ_OPCODE_ATOMIC_LOAD_SMIN: return "AtomicLoadSmin"; + case REQ_OPCODE_ATOMIC_LOAD_UMAX: return "AtomicLoadUmax"; + case REQ_OPCODE_ATOMIC_LOAD_UMIN: return "AtomicLoadUmin"; + case REQ_OPCODE_ATOMIC_SWAP: return "AtomicSwap"; + case REQ_OPCODE_ATOMIC_COMPARE: return "AtomicCompare"; + case REQ_OPCODE_PREFETCH_TGT: return "PrefetchTgt"; + case REQ_OPCODE_MAKE_READ_UNIQUE: return "MakeReadUnique"; + case REQ_OPCODE_WRITE_EVICT_OR_EVICT: return "WriteEvictOrEvict"; + case REQ_OPCODE_WRITE_UNIQUE_ZERO: return "WriteUniqueZero"; + case REQ_OPCODE_WRITE_NO_SNP_ZERO: return "WriteNoSnpZero"; + case REQ_OPCODE_STASH_ONCE_SEP_SHARED: return "StashOnceSepShared"; + case REQ_OPCODE_STASH_ONCE_SEP_UNIQUE: return "StashOnceSepUnique"; + case REQ_OPCODE_READ_PREFER_UNIQUE: return "ReadPreferUnique"; + case REQ_OPCODE_WRITE_NO_SNP_FULL_CLEAN_SH: return "WriteNoSnpFullCleanSh"; + default: return std::string{}; + } +} + +std::string +snpOpcodeToName(SnpOpcode snp_opcode) +{ + switch (snp_opcode) { + case SNP_OPCODE_SNP_LCRD_RETURN: return "SnpLcrdReturn"; + case SNP_OPCODE_SNP_SHARED: return "SnpShared"; + case SNP_OPCODE_SNP_CLEAN: return "SnpClean"; + case SNP_OPCODE_SNP_ONCE: return "SnpOnce"; + case SNP_OPCODE_SNP_NOT_SHARED_DIRTY: return "SnpNotSharedDirty"; + case SNP_OPCODE_SNP_UNIQUE_STASH: return "SnpUniqueStash"; + case SNP_OPCODE_SNP_MAKE_INVALID_STASH: return "SnpMakeInvalidStash"; + case SNP_OPCODE_SNP_UNIQUE: return "SnpUnique"; + case SNP_OPCODE_SNP_CLEAN_SHARED: return "SnpCleanShared"; + case SNP_OPCODE_SNP_CLEAN_INVALID: return "SnpCleanInvalid"; + case SNP_OPCODE_SNP_MAKE_INVALID: return "SnpMakeInvalid"; + case SNP_OPCODE_SNP_STASH_UNIQUE: return "SnpStashUnique"; + case SNP_OPCODE_SNP_STASH_SHARED: return "SnpStashShared"; + case SNP_OPCODE_SNP_DVM_OP: return "SnpDvmOp"; + case SNP_OPCODE_SNP_QUERY: return "SnpQuery"; + case SNP_OPCODE_SNP_SHARED_FWD: return "SnpSharedFwd"; + case SNP_OPCODE_SNP_CLEAN_FWD: return "SnpCleanFwd"; + case SNP_OPCODE_SNP_ONCE_FWD: return "SnpOnceFwd"; + case SNP_OPCODE_SNP_NOT_SHARED_DIRTY_FWD: return "SnpNotSharedDirtyFwd"; + case SNP_OPCODE_SNP_PREFER_UNIQUE: return "SnpPreferUnique"; + case SNP_OPCODE_SNP_PREFER_UNIQUE_FWD: return "SnpPreferUniqueFwd"; + case SNP_OPCODE_SNP_UNIQUE_FWD: return "SnpUniqueFwd"; + default: return std::string{}; + } +} + +std::string +datOpcodeToName(DatOpcode dat_opcode) +{ + switch (dat_opcode) { + case DAT_OPCODE_DAT_LCRD_RETURN: return "DatLcrdReturn"; + case DAT_OPCODE_SNP_RESP_DATA: return "SnpRespData"; + case DAT_OPCODE_COPY_BACK_WR_DATA: return "CopyBackWrData"; + case DAT_OPCODE_NON_COPY_BACK_WR_DATA: return "NonCopyBackWrData"; + case DAT_OPCODE_COMP_DATA: return "CompData"; + case DAT_OPCODE_SNP_RESP_DATA_PTL: return "SnpRespDataPtl"; + case DAT_OPCODE_SNP_RESP_DATA_FWDED: return "SnpRespDataFwded"; + case DAT_OPCODE_WRITE_DATA_CANCEL: return "WriteDataCancel"; + case DAT_OPCODE_DATA_SEP_RESP: return "DataSepResp"; + case DAT_OPCODE_NCB_WR_DATA_COMP_ACK: return "NcbWrDataCompAck"; + default: return std::string{}; + } +} + +std::string +rspOpcodeToName(RspOpcode rsp_opcode) +{ + switch (rsp_opcode) { + case RSP_OPCODE_RSP_LCRD_RETURN: return "RspLcrdReturn"; + case RSP_OPCODE_SNP_RESP: return "SnpResp"; + case RSP_OPCODE_COMP_ACK: return "CompAck"; + case RSP_OPCODE_RETRY_ACK: return "RetryAck"; + case RSP_OPCODE_COMP: return "OpcodeComp"; + case RSP_OPCODE_COMP_DBID_RESP: return "CompDbidResp"; + case RSP_OPCODE_DBID_RESP: return "DbidResp"; + case RSP_OPCODE_PCRD_GRANT: return "PcrdGrant"; + case RSP_OPCODE_READ_RECEIPT: return "ReadReceipt"; + case RSP_OPCODE_SNP_RESP_FWDED: return "SnpRespFwded"; + case RSP_OPCODE_TAG_MATCH: return "TagMatch"; + case RSP_OPCODE_RESP_SEP_DATA: return "RespSepData"; + case RSP_OPCODE_PERSIST: return "Persist"; + case RSP_OPCODE_COMP_PERSIST: return "CompPersist"; + case RSP_OPCODE_DBID_RESP_ORD: return "DbidRespOrd"; + case RSP_OPCODE_STASH_DONE: return "StashDone"; + case RSP_OPCODE_COMP_STASH_DONE: return "CompStashDone"; + case RSP_OPCODE_COMP_CMO: return "CompCMO"; + default: return std::string{}; + } +} + +std::string +phaseToOpcodeName(const Phase &phase) +{ + switch (phase.channel) { + case CHANNEL_REQ: + return reqOpcodeToName(phase.req_opcode); + case CHANNEL_SNP: + return snpOpcodeToName(phase.snp_opcode); + case CHANNEL_DAT: + return datOpcodeToName(phase.dat_opcode); + case CHANNEL_RSP: + return rspOpcodeToName(phase.rsp_opcode); + default: + break; + } + return std::string{}; +} + +std::string +phaseToChannelName(const Phase &phase) +{ + switch (phase.channel) { + case CHANNEL_REQ: + return "REQ"; + case CHANNEL_SNP: + return "SNP"; + case CHANNEL_DAT: + return "DAT"; + case CHANNEL_RSP: + return "RSP"; + default: + return std::string{}; + } +} + +} // namespace + +std::string +transactionToString(const Payload &payload, + const Phase &phase) +{ + return csprintf("%s %s addr=0x%08lx ns=%d size=%d attrs=0x%x", + phaseToChannelName(phase), + phaseToOpcodeName(phase).c_str(), + payload.address, payload.ns, + (int)payload.size, (int)payload.mem_attr); +} + +namespace tlm_to_ruby { + +ruby::CHIRequestType +reqOpcode(ReqOpcode req) +{ + static std::unordered_map translation_map = { + { REQ_OPCODE_READ_SHARED, ruby::CHIRequestType_ReadShared }, + { REQ_OPCODE_READ_CLEAN, ruby::CHIRequestType_ReadOnce }, // TODO + { REQ_OPCODE_READ_ONCE, ruby::CHIRequestType_ReadOnce }, + { REQ_OPCODE_READ_NO_SNP, ruby::CHIRequestType_ReadNoSnp }, // TODO + { REQ_OPCODE_READ_UNIQUE, ruby::CHIRequestType_ReadUnique }, + { REQ_OPCODE_READ_NOT_SHARED_DIRTY, ruby::CHIRequestType_ReadNotSharedDirty }, + { REQ_OPCODE_READ_PREFER_UNIQUE, ruby::CHIRequestType_ReadUnique }, // TODO + { REQ_OPCODE_MAKE_READ_UNIQUE, ruby::CHIRequestType_MakeReadUnique }, // TODO + + { REQ_OPCODE_CLEAN_UNIQUE, ruby::CHIRequestType_CleanUnique }, + { REQ_OPCODE_MAKE_UNIQUE, ruby::CHIRequestType_CleanUnique }, // TODO + { REQ_OPCODE_EVICT, ruby::CHIRequestType_Evict }, + { REQ_OPCODE_STASH_ONCE_SEP_SHARED, ruby::CHIRequestType_StashOnceShared }, // TODO + { REQ_OPCODE_STASH_ONCE_SEP_UNIQUE, ruby::CHIRequestType_StashOnceUnique }, + { REQ_OPCODE_WRITE_NO_SNP_PTL, ruby::CHIRequestType_WriteUniquePtl }, + { REQ_OPCODE_WRITE_NO_SNP_FULL, ruby::CHIRequestType_WriteUniqueFull }, + { REQ_OPCODE_WRITE_UNIQUE_FULL, ruby::CHIRequestType_WriteUniqueFull }, + { REQ_OPCODE_WRITE_UNIQUE_ZERO, ruby::CHIRequestType_WriteUniqueZero }, + { REQ_OPCODE_WRITE_BACK_FULL, ruby::CHIRequestType_WriteBackFull }, + { REQ_OPCODE_WRITE_EVICT_OR_EVICT, ruby::CHIRequestType_WriteEvictFull }, // TODO + }; + + auto it = translation_map.find(req); + if (it != translation_map.end()) { + return it->second; + } else { + panic("Unsupported Translation: %s\n", reqOpcodeToName(req)); + } +} + +#define RESP_CASE(opc) \ + switch (resp) { \ + case RESP_I: return opc ## _ ## I; \ + case RESP_SC: return opc ## _ ## SC; \ + case RESP_UC: return opc ## _ ## UC; \ + case RESP_SD: return opc ## _ ## SD; \ + case RESP_I_PD: return opc ## _ ## I_PD; \ + case RESP_SC_PD: return opc ## _ ## SC_PD; \ + case RESP_UC_PD: return opc ## _ ## UC; \ + case RESP_SD_PD: return opc ## _ ## SD; \ + default: panic(""); \ + } + +ruby::CHIDataType +datOpcode(DatOpcode dat, Resp resp) +{ + switch (dat) { + case DAT_OPCODE_NON_COPY_BACK_WR_DATA: + return ruby::CHIDataType_NCBWrData; + case DAT_OPCODE_COPY_BACK_WR_DATA: + switch (resp) { + case RESP_I: return ruby::CHIDataType_CBWrData_I; + case RESP_UC: return ruby::CHIDataType_CBWrData_UC; + case RESP_SC: return ruby::CHIDataType_CBWrData_SC; + case RESP_UD_PD: return ruby::CHIDataType_CBWrData_UD_PD; + default: panic(""); + } + case DAT_OPCODE_SNP_RESP_DATA: + RESP_CASE(ruby::CHIDataType_SnpRespData) + default: + panic("Unsupported Translation: %s\n", datOpcodeToName(dat)); + } +} + +ruby::CHIResponseType +rspOpcode(RspOpcode opc, Resp resp) +{ + switch(opc) { + case RSP_OPCODE_COMP_ACK: return ruby::CHIResponseType_CompAck; + case RSP_OPCODE_SNP_RESP: + switch (resp) { + case RESP_I: return ruby::CHIResponseType_SnpResp_I; + default: panic("Invalid resp %d for %d\n", resp, opc); + } + default: + panic("Unsupported Translation: %s\n", rspOpcodeToName(opc)); + }; +} + +} + +namespace ruby_to_tlm { + +uint8_t +datOpcode(ruby::CHIDataType dat) +{ + switch (dat) { + case ruby::CHIDataType_CompData_I: + case ruby::CHIDataType_CompData_UC: + case ruby::CHIDataType_CompData_SC: + case ruby::CHIDataType_CompData_UD_PD: + case ruby::CHIDataType_CompData_SD_PD: + return 0x4; + case ruby::CHIDataType_DataSepResp_UC: + return 0xb; + case ruby::CHIDataType_CBWrData_UC: + case ruby::CHIDataType_CBWrData_SC: + case ruby::CHIDataType_CBWrData_UD_PD: + case ruby::CHIDataType_CBWrData_SD_PD: + case ruby::CHIDataType_CBWrData_I: + return 0x2; + case ruby::CHIDataType_NCBWrData: + return 0x3; + case ruby::CHIDataType_SnpRespData_I: + case ruby::CHIDataType_SnpRespData_I_PD: + case ruby::CHIDataType_SnpRespData_SC: + case ruby::CHIDataType_SnpRespData_SC_PD: + case ruby::CHIDataType_SnpRespData_SD: + case ruby::CHIDataType_SnpRespData_UC: + case ruby::CHIDataType_SnpRespData_UD: + return 0x1; + case ruby::CHIDataType_SnpRespData_SC_Fwded_SC: + case ruby::CHIDataType_SnpRespData_SC_Fwded_SD_PD: + case ruby::CHIDataType_SnpRespData_SC_PD_Fwded_SC: + case ruby::CHIDataType_SnpRespData_SD_Fwded_SC: + case ruby::CHIDataType_SnpRespData_I_Fwded_SD_PD: + case ruby::CHIDataType_SnpRespData_I_PD_Fwded_SC: + case ruby::CHIDataType_SnpRespData_I_Fwded_SC: + return 0x6; + default: + panic("Unrecognised data opcode: %d\n", dat); + } +} + +uint8_t +rspOpcode(ruby::CHIResponseType rsp) +{ + switch (rsp) { + case ruby::CHIResponseType_Comp_UD_PD: + case ruby::CHIResponseType_Comp_UC: + case ruby::CHIResponseType_Comp_I: + return RSP_OPCODE_COMP; + case ruby::CHIResponseType_CompDBIDResp: + return RSP_OPCODE_COMP_DBID_RESP; + case ruby::CHIResponseType_RetryAck: + return RSP_OPCODE_RETRY_ACK; + default: + panic("Unrecognised rsp opcode: %d\n", rsp); + } +} + +uint8_t +snpOpcode(ruby::CHIRequestType snp) +{ + switch (snp) { + case ruby::CHIRequestType_SnpOnceFwd: + return SNP_OPCODE_SNP_ONCE_FWD; + case ruby::CHIRequestType_SnpOnce: + return SNP_OPCODE_SNP_ONCE; + case ruby::CHIRequestType_SnpShared: + return SNP_OPCODE_SNP_SHARED; + case ruby::CHIRequestType_SnpCleanInvalid: + return SNP_OPCODE_SNP_CLEAN_INVALID; + case ruby::CHIRequestType_SnpUnique: + return SNP_OPCODE_SNP_UNIQUE; + default: + panic("Unrecognised snp opcode: %d\n", snp); + } +} + +Resp +datResp(ruby::CHIDataType dat) +{ + switch (dat) { + case ruby::CHIDataType_SnpRespData_I: + case ruby::CHIDataType_CompData_I: + case ruby::CHIDataType_CBWrData_I: + return RESP_I; + case ruby::CHIDataType_SnpRespData_SC: + case ruby::CHIDataType_CompData_SC: + case ruby::CHIDataType_CBWrData_SC: + return RESP_SC; + case ruby::CHIDataType_SnpRespData_UC: + case ruby::CHIDataType_CompData_UC: + case ruby::CHIDataType_CBWrData_UC: + case ruby::CHIDataType_DataSepResp_UC: + return RESP_UC; + case ruby::CHIDataType_SnpRespData_UD: + return RESP_UD; + case ruby::CHIDataType_SnpRespData_SD: + return RESP_SD; + case ruby::CHIDataType_SnpRespData_I_PD: + return RESP_I_PD; + case ruby::CHIDataType_SnpRespData_SC_PD: + return RESP_SC_PD; + case ruby::CHIDataType_CompData_UD_PD: + case ruby::CHIDataType_CBWrData_UD_PD: + return RESP_UD_PD; + case ruby::CHIDataType_CompData_SD_PD: + case ruby::CHIDataType_CBWrData_SD_PD: + return RESP_SD_PD; + // TODO + case ruby::CHIDataType_SnpRespData_SC_Fwded_SC: + case ruby::CHIDataType_SnpRespData_SC_Fwded_SD_PD: + case ruby::CHIDataType_SnpRespData_SC_PD_Fwded_SC: + case ruby::CHIDataType_SnpRespData_SD_Fwded_SC: + case ruby::CHIDataType_SnpRespData_I_Fwded_SD_PD: + case ruby::CHIDataType_SnpRespData_I_PD_Fwded_SC: + case ruby::CHIDataType_SnpRespData_I_Fwded_SC: + default: + panic("Unrecognised data opcode: %d\n", dat); + } +} + +Resp +rspResp(ruby::CHIResponseType rsp) +{ + switch (rsp) { + case ruby::CHIResponseType_Comp_I: + return RESP_I; + case ruby::CHIResponseType_Comp_UC: + return RESP_UC; + case ruby::CHIResponseType_Comp_UD_PD: + return RESP_UD_PD; + case ruby::CHIResponseType_CompDBIDResp: + return RESP_I; + case ruby::CHIResponseType_RetryAck: + // Just setup to zero + return RESP_I; + default: + panic("Unrecognised rsp opcode: %d\n", rsp); + } +} + +} + +Addr +transactionSize(Size sz) +{ + return 1 << sz; +} + +uint8_t +dataId(Addr address) +{ + uint64_t bus_width = 256; + switch (bus_width) { + case 128: + return bits(address, 5, 4); + case 256: + return bits(address, 5, 4) & 0b10; + case 512: + default: + return 0b00; + } +} + +} // namespace tlm::chi + +} // namespace gem5 diff --git a/src/mem/ruby/protocol/chi/tlm/utils.hh b/src/mem/ruby/protocol/chi/tlm/utils.hh new file mode 100644 index 0000000000..079ebb74b9 --- /dev/null +++ b/src/mem/ruby/protocol/chi/tlm/utils.hh @@ -0,0 +1,82 @@ +/* + * 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_TLM_UTILS_HH__ +#define __MEM_RUBY_PROTOCOL_CHI_TLM_UTILS_HH__ + +#include + +#include "base/types.hh" +#include "mem/ruby/protocol/CHIDataType.hh" +#include "mem/ruby/protocol/CHIRequestType.hh" +#include "mem/ruby/protocol/CHIResponseType.hh" + +namespace gem5 { + +namespace tlm::chi { + +std::string transactionToString(const ARM::CHI::Payload &payload, + const ARM::CHI::Phase &phase); + +namespace tlm_to_ruby { + +ruby::CHIRequestType reqOpcode(ARM::CHI::ReqOpcode req); +ruby::CHIDataType datOpcode(ARM::CHI::DatOpcode dat, ARM::CHI::Resp resp); +ruby::CHIResponseType rspOpcode(ARM::CHI::RspOpcode res, ARM::CHI::Resp resp); + +} + +namespace ruby_to_tlm { + +uint8_t datOpcode(ruby::CHIDataType dat); +uint8_t rspOpcode(ruby::CHIResponseType res); +uint8_t snpOpcode(ruby::CHIRequestType snp); + +ARM::CHI::Resp datResp(ruby::CHIDataType dat); +ARM::CHI::Resp rspResp(ruby::CHIResponseType rsp); + +} + +Addr transactionSize(ARM::CHI::Size sz); + +uint8_t dataId(Addr address); + +} // namespace tlm::chi + +} // namespace gem5 + +#endif // __MEM_RUBY_PROTOCOL_CHI_TLM_UTILS_HH__