| # Copyright (c) 2021 ARM Limited |
| # All rights reserved. |
| # |
| # The license below extends only to copyright in the software and shall |
| # not be construed as granting a license to any other intellectual |
| # property including but not limited to intellectual property relating |
| # to a hardware implementation of the functionality of the software |
| # licensed hereunder. You may use the software subject to the license |
| # terms below provided that you ensure that this notice is replicated |
| # unmodified and in its entirety in all distributions of the software, |
| # modified or unmodified, in source code or in binary form. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer; |
| # redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution; |
| # neither the name of the copyright holders nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| import math |
| import yaml |
| import m5 |
| from m5.objects import * |
| from m5.defines import buildEnv |
| from .Ruby import create_topology, setup_memory_controllers |
| |
| def define_options(parser): |
| parser.add_option("--noc-config", action="store", type="string", |
| default=None, |
| help="YAML NoC config. parameters and bindings. " |
| "required for CustomMesh topology") |
| |
| class Versions: |
| ''' |
| Helper class to obtain unique ids for a given controller class. |
| These are passed as the 'version' parameter when creating the controller. |
| ''' |
| _seqs = 0 |
| @classmethod |
| def getSeqId(cls): |
| val = cls._seqs |
| cls._seqs += 1 |
| return val |
| |
| _version = {} |
| @classmethod |
| def getVersion(cls, tp): |
| if tp not in cls._version: |
| cls._version[tp] = 0 |
| val = cls._version[tp] |
| cls._version[tp] = val + 1 |
| return val |
| |
| |
| class CHI_Node(SubSystem): |
| ''' |
| Base class with common functions for setting up Cache or Memory |
| controllers that are part of a CHI RNF, RNFI, HNF, or SNF nodes. |
| Notice getNetworkSideControllers and getAllControllers must be implemented |
| in the derived classes. |
| ''' |
| |
| def __init__(self, ruby_system): |
| super(CHI_Node, self).__init__() |
| self._ruby_system = ruby_system |
| self._network = ruby_system.network |
| |
| def getNetworkSideControllers(self): |
| ''' |
| Returns all ruby controllers that need to be connected to the |
| network |
| ''' |
| raise NotImplementedError() |
| |
| def getAllControllers(self): |
| ''' |
| Returns all ruby controllers associated with this node |
| ''' |
| raise NotImplementedError() |
| |
| def setDownstream(self, cntrls): |
| ''' |
| Sets cntrls as the downstream list of all controllers in this node |
| ''' |
| for c in self.getNetworkSideControllers(): |
| c.downstream_destinations = cntrls |
| |
| def connectController(self, cntrl): |
| ''' |
| Creates and configures the messages buffers for the CHI input/output |
| ports that connect to the network |
| ''' |
| cntrl.reqOut = MessageBuffer() |
| cntrl.rspOut = MessageBuffer() |
| cntrl.snpOut = MessageBuffer() |
| cntrl.datOut = MessageBuffer() |
| cntrl.reqIn = MessageBuffer() |
| cntrl.rspIn = MessageBuffer() |
| cntrl.snpIn = MessageBuffer() |
| cntrl.datIn = MessageBuffer() |
| |
| # All CHI ports are always connected to the network. |
| # Controllers that are not part of the getNetworkSideControllers list |
| # still communicate using internal routers, thus we need to wire-up the |
| # ports |
| cntrl.reqOut.out_port = self._network.in_port |
| cntrl.rspOut.out_port = self._network.in_port |
| cntrl.snpOut.out_port = self._network.in_port |
| cntrl.datOut.out_port = self._network.in_port |
| cntrl.reqIn.in_port = self._network.out_port |
| cntrl.rspIn.in_port = self._network.out_port |
| cntrl.snpIn.in_port = self._network.out_port |
| cntrl.datIn.in_port = self._network.out_port |
| |
| class TriggerMessageBuffer(MessageBuffer): |
| ''' |
| MessageBuffer for triggering internal controller events. |
| These buffers should not be affected by the Ruby tester randomization |
| and allow poping messages enqueued in the same cycle. |
| ''' |
| randomization = 'disabled' |
| allow_zero_latency = True |
| |
| class OrderedTriggerMessageBuffer(TriggerMessageBuffer): |
| ordered = True |
| |
| class CHI_Cache_Controller(Cache_Controller): |
| ''' |
| Default parameters for a Cache controller |
| The Cache_Controller can also be used as a DMA requester or as |
| a pure directory if all cache allocation policies are disabled. |
| ''' |
| |
| def __init__(self, ruby_system): |
| super(CHI_Cache_Controller, self).__init__( |
| version = Versions.getVersion(Cache_Controller), |
| ruby_system = ruby_system, |
| mandatoryQueue = MessageBuffer(), |
| prefetchQueue = MessageBuffer(), |
| triggerQueue = TriggerMessageBuffer(), |
| retryTriggerQueue = OrderedTriggerMessageBuffer(), |
| replTriggerQueue = OrderedTriggerMessageBuffer(), |
| reqRdy = TriggerMessageBuffer(), |
| snpRdy = TriggerMessageBuffer()) |
| # Set somewhat large number since we really a lot on internal |
| # triggers. To limit the controller performance, tweak other |
| # params such as: input port buffer size, cache banks, and output |
| # port latency |
| self.transitions_per_cycle = 128 |
| # This should be set to true in the data cache controller to enable |
| # timeouts on unique lines when a store conditional fails |
| self.sc_lock_enabled = False |
| |
| class CHI_L1Controller(CHI_Cache_Controller): |
| ''' |
| Default parameters for a L1 Cache controller |
| ''' |
| |
| def __init__(self, ruby_system, sequencer, cache, prefetcher): |
| super(CHI_L1Controller, self).__init__(ruby_system) |
| self.sequencer = sequencer |
| self.cache = cache |
| self.use_prefetcher = False |
| self.send_evictions = True |
| self.is_HN = False |
| self.enable_DMT = False |
| self.enable_DCT = False |
| # Strict inclusive MOESI |
| self.allow_SD = True |
| self.alloc_on_seq_acc = True |
| self.alloc_on_seq_line_write = False |
| self.alloc_on_readshared = True |
| self.alloc_on_readunique = True |
| self.alloc_on_readonce = True |
| self.alloc_on_writeback = True |
| self.dealloc_on_unique = False |
| self.dealloc_on_shared = False |
| self.dealloc_backinv_unique = True |
| self.dealloc_backinv_shared = True |
| # Some reasonable default TBE params |
| self.number_of_TBEs = 16 |
| self.number_of_repl_TBEs = 16 |
| self.number_of_snoop_TBEs = 4 |
| self.unify_repl_TBEs = False |
| |
| class CHI_L2Controller(CHI_Cache_Controller): |
| ''' |
| Default parameters for a L2 Cache controller |
| ''' |
| |
| def __init__(self, ruby_system, cache, prefetcher): |
| super(CHI_L2Controller, self).__init__(ruby_system) |
| self.sequencer = NULL |
| self.cache = cache |
| self.use_prefetcher = False |
| self.allow_SD = True |
| self.is_HN = False |
| self.enable_DMT = False |
| self.enable_DCT = False |
| self.send_evictions = False |
| # Strict inclusive MOESI |
| self.alloc_on_seq_acc = False |
| self.alloc_on_seq_line_write = False |
| self.alloc_on_readshared = True |
| self.alloc_on_readunique = True |
| self.alloc_on_readonce = True |
| self.alloc_on_writeback = True |
| self.dealloc_on_unique = False |
| self.dealloc_on_shared = False |
| self.dealloc_backinv_unique = True |
| self.dealloc_backinv_shared = True |
| # Some reasonable default TBE params |
| self.number_of_TBEs = 32 |
| self.number_of_repl_TBEs = 32 |
| self.number_of_snoop_TBEs = 16 |
| self.unify_repl_TBEs = False |
| |
| class CHI_HNFController(CHI_Cache_Controller): |
| ''' |
| Default parameters for a coherent home node (HNF) cache controller |
| ''' |
| |
| def __init__(self, ruby_system, cache, prefetcher, addr_ranges): |
| super(CHI_HNFController, self).__init__(ruby_system) |
| self.sequencer = NULL |
| self.cache = cache |
| self.use_prefetcher = False |
| self.addr_ranges = addr_ranges |
| self.allow_SD = True |
| self.is_HN = True |
| self.enable_DMT = True |
| self.enable_DCT = True |
| self.send_evictions = False |
| # MOESI / Mostly inclusive for shared / Exclusive for unique |
| self.alloc_on_seq_acc = False |
| self.alloc_on_seq_line_write = False |
| self.alloc_on_readshared = True |
| self.alloc_on_readunique = False |
| self.alloc_on_readonce = True |
| self.alloc_on_writeback = True |
| self.dealloc_on_unique = True |
| self.dealloc_on_shared = False |
| self.dealloc_backinv_unique = False |
| self.dealloc_backinv_shared = False |
| # Some reasonable default TBE params |
| self.number_of_TBEs = 32 |
| self.number_of_repl_TBEs = 32 |
| self.number_of_snoop_TBEs = 1 # should not receive any snoop |
| self.unify_repl_TBEs = False |
| |
| class CHI_DMAController(CHI_Cache_Controller): |
| ''' |
| Default parameters for a DMA controller |
| ''' |
| |
| def __init__(self, ruby_system, sequencer): |
| super(CHI_DMAController, self).__init__(ruby_system) |
| self.sequencer = sequencer |
| class DummyCache(RubyCache): |
| dataAccessLatency = 0 |
| tagAccessLatency = 1 |
| size = "128" |
| assoc = 1 |
| self.use_prefetcher = False |
| self.cache = DummyCache() |
| self.sequencer.dcache = NULL |
| # All allocations are false |
| # Deallocations are true (don't really matter) |
| self.allow_SD = False |
| self.is_HN = False |
| self.enable_DMT = False |
| self.enable_DCT = False |
| self.alloc_on_seq_acc = False |
| self.alloc_on_seq_line_write = False |
| self.alloc_on_readshared = False |
| self.alloc_on_readunique = False |
| self.alloc_on_readonce = False |
| self.alloc_on_writeback = False |
| self.dealloc_on_unique = False |
| self.dealloc_on_shared = False |
| self.dealloc_backinv_unique = False |
| self.dealloc_backinv_shared = False |
| self.send_evictions = False |
| self.number_of_TBEs = 16 |
| self.number_of_repl_TBEs = 1 |
| self.number_of_snoop_TBEs = 1 # should not receive any snoop |
| self.unify_repl_TBEs = False |
| |
| class CPUSequencerWrapper: |
| ''' |
| Other generic configuration scripts assume a matching number of sequencers |
| and cpus. This wraps the instruction and data sequencer so they are |
| compatible with the other scripts. This assumes all scripts are using |
| connectCpuPorts/connectIOPorts to bind ports |
| ''' |
| |
| def __init__(self, iseq, dseq): |
| # use this style due to __setattr__ override below |
| self.__dict__['inst_seq'] = iseq |
| self.__dict__['data_seq'] = dseq |
| self.__dict__['support_data_reqs'] = True |
| self.__dict__['support_inst_reqs'] = True |
| # Compatibility with certain scripts that wire up ports |
| # without connectCpuPorts |
| self.__dict__['slave'] = dseq.in_ports |
| self.__dict__['in_ports'] = dseq.in_ports |
| |
| def connectCpuPorts(self, cpu): |
| assert(isinstance(cpu, BaseCPU)) |
| cpu.icache_port = self.inst_seq.in_ports |
| for p in cpu._cached_ports: |
| if str(p) != 'icache_port': |
| exec('cpu.%s = self.data_seq.in_ports' % p) |
| cpu.connectUncachedPorts(self.data_seq) |
| |
| def connectIOPorts(self, piobus): |
| self.data_seq.connectIOPorts(piobus) |
| |
| def __setattr__(self, name, value): |
| setattr(self.inst_seq, name, value) |
| setattr(self.data_seq, name, value) |
| |
| class CHI_RNF(CHI_Node): |
| ''' |
| Defines a CHI request node. |
| Notice all contollers and sequencers are set as children of the cpus, so |
| this object acts more like a proxy for seting things up and has no topology |
| significance unless the cpus are set as its children at the top level |
| ''' |
| def __init__(self, cpus, ruby_system, |
| l1Icache_type, l1Dcache_type, |
| cache_line_size, |
| l1Iprefetcher_type=None, l1Dprefetcher_type=None): |
| super(CHI_RNF, self).__init__(ruby_system) |
| |
| self._block_size_bits = int(math.log(cache_line_size, 2)) |
| |
| # All sequencers and controllers |
| self._seqs = [] |
| self._cntrls = [] |
| |
| # Last level controllers in this node, i.e., the ones that will send |
| # requests to the home nodes |
| self._ll_cntrls = [] |
| |
| self._cpus = cpus |
| |
| # First creates L1 caches and sequencers |
| for cpu in self._cpus: |
| cpu.inst_sequencer = RubySequencer(version = Versions.getSeqId(), |
| ruby_system = ruby_system) |
| cpu.data_sequencer = RubySequencer(version = Versions.getSeqId(), |
| ruby_system = ruby_system) |
| |
| self._seqs.append(CPUSequencerWrapper(cpu.inst_sequencer, |
| cpu.data_sequencer)) |
| |
| # caches |
| l1i_cache = l1Icache_type(start_index_bit = self._block_size_bits, |
| is_icache = True) |
| |
| l1d_cache = l1Dcache_type(start_index_bit = self._block_size_bits, |
| is_icache = False) |
| |
| # Placeholders for future prefetcher support |
| if l1Iprefetcher_type != None or l1Dprefetcher_type != None: |
| m5.fatal('Prefetching not supported yet') |
| l1i_pf = NULL |
| l1d_pf = NULL |
| |
| # cache controllers |
| cpu.l1i = CHI_L1Controller(ruby_system, cpu.inst_sequencer, |
| l1i_cache, l1i_pf) |
| |
| cpu.l1d = CHI_L1Controller(ruby_system, cpu.data_sequencer, |
| l1d_cache, l1d_pf) |
| |
| cpu.inst_sequencer.dcache = NULL |
| cpu.data_sequencer.dcache = cpu.l1d.cache |
| |
| cpu.l1d.sc_lock_enabled = True |
| |
| cpu._ll_cntrls = [cpu.l1i, cpu.l1d] |
| for c in cpu._ll_cntrls: |
| self._cntrls.append(c) |
| self.connectController(c) |
| self._ll_cntrls.append(c) |
| |
| def getSequencers(self): |
| return self._seqs |
| |
| def getAllControllers(self): |
| return self._cntrls |
| |
| def getNetworkSideControllers(self): |
| return self._cntrls |
| |
| def setDownstream(self, cntrls): |
| for c in self._ll_cntrls: |
| c.downstream_destinations = cntrls |
| |
| def getCpus(self): |
| return self._cpus |
| |
| # Adds a private L2 for each cpu |
| def addPrivL2Cache(self, cache_type, pf_type=None): |
| self._ll_cntrls = [] |
| for cpu in self._cpus: |
| l2_cache = cache_type(start_index_bit = self._block_size_bits, |
| is_icache = False) |
| if pf_type != None: |
| m5.fatal('Prefetching not supported yet') |
| l2_pf = NULL |
| |
| cpu.l2 = CHI_L2Controller(self._ruby_system, l2_cache, l2_pf) |
| |
| self._cntrls.append(cpu.l2) |
| self.connectController(cpu.l2) |
| |
| self._ll_cntrls.append(cpu.l2) |
| |
| for c in cpu._ll_cntrls: |
| c.downstream_destinations = [cpu.l2] |
| cpu._ll_cntrls = [cpu.l2] |
| |
| |
| class CHI_HNF(CHI_Node): |
| ''' |
| Encapsulates an HNF cache/directory controller. |
| Before the first controller is created, the class method |
| CHI_HNF.createAddrRanges must be called before creating any CHI_HNF object |
| to set-up the interleaved address ranges used by the HNFs |
| ''' |
| |
| _addr_ranges = [] |
| @classmethod |
| def createAddrRanges(cls, sys_mem_ranges, cache_line_size, num_hnfs): |
| # Create the HNFs interleaved addr ranges |
| block_size_bits = int(math.log(cache_line_size, 2)) |
| cls._addr_ranges = [] |
| llc_bits = int(math.log(num_hnfs, 2)) |
| numa_bit = block_size_bits + llc_bits - 1 |
| for i in range(num_hnfs): |
| ranges = [] |
| for r in sys_mem_ranges: |
| addr_range = AddrRange(r.start, size = r.size(), |
| intlvHighBit = numa_bit, |
| intlvBits = llc_bits, |
| intlvMatch = i) |
| ranges.append(addr_range) |
| cls._addr_ranges.append((ranges, numa_bit, i)) |
| |
| @classmethod |
| def getAddrRanges(cls, hnf_idx): |
| assert(len(cls._addr_ranges) != 0) |
| return cls._addr_ranges[hnf_idx] |
| |
| # The CHI controller can be a child of this object or another if |
| # 'parent' if specified |
| def __init__(self, hnf_idx, ruby_system, llcache_type, parent): |
| super(CHI_HNF, self).__init__(ruby_system) |
| |
| addr_ranges,intlvHighBit,intlvMatch = CHI_HNF.getAddrRanges(hnf_idx) |
| # All ranges should have the same interleaving |
| assert(len(addr_ranges) >= 1) |
| assert(intlvMatch == hnf_idx) |
| |
| ll_cache = llcache_type(start_index_bit = intlvHighBit + 1) |
| self._cntrl = CHI_HNFController(ruby_system, ll_cache, NULL, |
| addr_ranges) |
| |
| if parent == None: |
| self.cntrl = self._cntrl |
| else: |
| parent.cntrl = self._cntrl |
| |
| self.connectController(self._cntrl) |
| |
| def getAllControllers(self): |
| return [self._cntrl] |
| |
| def getNetworkSideControllers(self): |
| return [self._cntrl] |
| |
| |
| class CHI_SNF_Base(CHI_Node): |
| ''' |
| Creates CHI node controllers for the memory controllers |
| ''' |
| |
| # The CHI controller can be a child of this object or another if |
| # 'parent' if specified |
| def __init__(self, ruby_system, parent): |
| super(CHI_SNF_Base, self).__init__(ruby_system) |
| |
| self._cntrl = Memory_Controller( |
| version = Versions.getVersion(Memory_Controller), |
| ruby_system = ruby_system, |
| triggerQueue = TriggerMessageBuffer(), |
| responseFromMemory = MessageBuffer(), |
| requestToMemory = MessageBuffer(ordered = True), |
| reqRdy = TriggerMessageBuffer()) |
| |
| self.connectController(self._cntrl) |
| |
| if parent: |
| parent.cntrl = self._cntrl |
| else: |
| self.cntrl = self._cntrl |
| |
| def getAllControllers(self): |
| return [self._cntrl] |
| |
| def getNetworkSideControllers(self): |
| return [self._cntrl] |
| |
| def getMemRange(self, mem_ctrl): |
| # TODO need some kind of transparent API for |
| # MemCtrl+DRAM vs SimpleMemory |
| if hasattr(mem_ctrl, 'range'): |
| return mem_ctrl.range |
| else: |
| return mem_ctrl.dram.range |
| |
| class CHI_SNF_BootMem(CHI_SNF_Base): |
| ''' |
| Create the SNF for the boot memory |
| ''' |
| def __init__(self, ruby_system, parent, bootmem): |
| super(CHI_SNF_BootMem, self).__init__(ruby_system, parent) |
| self._cntrl.memory_out_port = bootmem.port |
| self._cntrl.addr_ranges = self.getMemRange(bootmem) |
| |
| class CHI_SNF_MainMem(CHI_SNF_Base): |
| ''' |
| Create the SNF for a list main memory controllers |
| ''' |
| def __init__(self, ruby_system, parent, mem_ctrl = None): |
| super(CHI_SNF_MainMem, self).__init__(ruby_system, parent) |
| if mem_ctrl: |
| self._cntrl.memory_out_port = mem_ctrl.port |
| self._cntrl.addr_ranges = self.getMemRange(mem_ctrl) |
| # else bind ports and range later |
| |
| class CHI_RNI_Base(CHI_Node): |
| ''' |
| Request node without cache / DMA |
| ''' |
| |
| # The CHI controller can be a child of this object or another if |
| # 'parent' if specified |
| def __init__(self, ruby_system, parent): |
| super(CHI_RNI_Base, self).__init__(ruby_system) |
| |
| self._sequencer = RubySequencer(version = Versions.getSeqId(), |
| ruby_system = ruby_system, |
| clk_domain = ruby_system.clk_domain) |
| self._cntrl = CHI_DMAController(ruby_system, self._sequencer) |
| |
| if parent: |
| parent.cntrl = self._cntrl |
| else: |
| self.cntrl = self._cntrl |
| |
| self.connectController(self._cntrl) |
| |
| def getAllControllers(self): |
| return [self._cntrl] |
| |
| def getNetworkSideControllers(self): |
| return [self._cntrl] |
| |
| class CHI_RNI_DMA(CHI_RNI_Base): |
| ''' |
| DMA controller wiredup to a given dma port |
| ''' |
| def __init__(self, ruby_system, dma_port, parent): |
| super(CHI_RNI_DMA, self).__init__(ruby_system, parent) |
| assert(dma_port != None) |
| self._sequencer.in_ports = dma_port |
| |
| class CHI_RNI_IO(CHI_RNI_Base): |
| ''' |
| DMA controller wiredup to ruby_system IO port |
| ''' |
| def __init__(self, ruby_system, parent): |
| super(CHI_RNI_IO, self).__init__(ruby_system, parent) |
| ruby_system._io_port = self._sequencer |
| |
| def noc_params_from_config(config, noc_params): |
| # mesh options |
| noc_params.num_rows = config['mesh']['num_rows'] |
| noc_params.num_cols = config['mesh']['num_cols'] |
| if 'router_latency' in config['mesh']: |
| noc_params.router_latency = config['mesh']['router_latency'] |
| if 'link_latency' in config['mesh']: |
| noc_params.router_link_latency = config['mesh']['link_latency'] |
| noc_params.node_link_latency = config['mesh']['link_latency'] |
| if 'router_link_latency' in config['mesh']: |
| noc_params.router_link_latency = config['mesh']['router_link_latency'] |
| if 'node_link_latency' in config['mesh']: |
| noc_params.node_link_latency = config['mesh']['node_link_latency'] |
| if 'cross_links' in config['mesh']: |
| noc_params.cross_link_latency = \ |
| config['mesh']['cross_link_latency'] |
| noc_params.cross_links = [] |
| for x, y in config['mesh']['cross_links']: |
| noc_params.cross_links.append((x, y)) |
| noc_params.cross_links.append((y, x)) |
| else: |
| noc_params.cross_links = [] |
| noc_params.cross_link_latency = 0 |
| |
| # CHI_RNF options |
| noc_params.CHI_RNF = config['CHI_RNF'] |
| |
| # CHI_RNI_IO |
| noc_params.CHI_RNI_IO = config['CHI_RNI_IO'] |
| |
| # CHI_HNF options |
| noc_params.CHI_HNF = config['CHI_HNF'] |
| if 'pairing' in config['CHI_HNF']: |
| noc_params.pairing = config['CHI_HNF']['pairing'] |
| |
| # CHI_SNF_MainMem |
| noc_params.CHI_SNF_MainMem = config['CHI_SNF_MainMem'] |
| |
| # CHI_SNF_IO (applies to CHI_SNF_Bootmem) |
| noc_params.CHI_SNF_IO = config['CHI_SNF_IO'] |
| |
| |
| def create_system(options, full_system, system, dma_ports, bootmem, |
| ruby_system): |
| |
| if buildEnv['PROTOCOL'] != 'CHI': |
| m5.panic("This script requires the CHI build") |
| |
| if options.num_dirs < 1: |
| m5.fatal('--num-dirs must be at least 1') |
| |
| if options.num_l3caches < 1: |
| m5.fatal('--num-l3caches must be at least 1') |
| |
| # Default parameters for the network |
| class NoC_Params(object): |
| def __init__(self): |
| self.topology = options.topology |
| self.network = options.network |
| self.router_link_latency = 1 |
| self.node_link_latency = 1 |
| self.router_latency = 1 |
| self.router_buffer_size = 4 |
| self.cntrl_msg_size = 8 |
| self.data_width = 32 |
| params = NoC_Params() |
| |
| # read additional configurations from yaml file if provided |
| if options.noc_config: |
| with open(options.noc_config, 'r') as file: |
| noc_params_from_config(yaml.load(file), params) |
| elif params.topology == 'CustomMesh': |
| m5.fatal('--noc-config must be provided if topology is CustomMesh') |
| |
| # Declare caches and controller types used by the protocol |
| # Notice tag and data accesses are not concurrent, so the a cache hit |
| # latency = tag + data + response latencies. |
| # Default response latencies are 1 cy for all controllers. |
| # For L1 controllers the mandatoryQueue enqueue latency is always 1 cy and |
| # this is deducted from the initial tag read latency for sequencer requests |
| # dataAccessLatency may be set to 0 if one wants to consider parallel |
| # data and tag lookups |
| class L1ICache(RubyCache): |
| dataAccessLatency = 1 |
| tagAccessLatency = 1 |
| size = options.l1i_size |
| assoc = options.l1i_assoc |
| |
| class L1DCache(RubyCache): |
| dataAccessLatency = 2 |
| tagAccessLatency = 1 |
| size = options.l1d_size |
| assoc = options.l1d_assoc |
| |
| class L2Cache(RubyCache): |
| dataAccessLatency = 6 |
| tagAccessLatency = 2 |
| size = options.l2_size |
| assoc = options.l2_assoc |
| |
| class HNFCache(RubyCache): |
| dataAccessLatency = 10 |
| tagAccessLatency = 2 |
| size = options.l3_size |
| assoc = options.l3_assoc |
| |
| # other functions use system.cache_line_size assuming it has been set |
| assert(system.cache_line_size.value == options.cacheline_size) |
| |
| cpu_sequencers = [] |
| mem_cntrls = [] |
| mem_dests = [] |
| network_nodes = [] |
| network_cntrls = [] |
| hnf_dests = [] |
| all_cntrls = [] |
| |
| # Creates on RNF per cpu with priv l2 caches |
| assert(len(system.cpu) == options.num_cpus) |
| ruby_system.rnf = [ CHI_RNF([cpu], ruby_system, L1ICache, L1DCache, |
| system.cache_line_size.value) |
| for cpu in system.cpu ] |
| for rnf in ruby_system.rnf: |
| rnf.addPrivL2Cache(L2Cache) |
| cpu_sequencers.extend(rnf.getSequencers()) |
| all_cntrls.extend(rnf.getAllControllers()) |
| network_nodes.append(rnf) |
| network_cntrls.extend(rnf.getNetworkSideControllers()) |
| |
| # Look for other memories |
| other_memories = [] |
| if bootmem: |
| other_memories.append(bootmem) |
| if getattr(system, 'sram', None): |
| other_memories.append(getattr(system, 'sram', None)) |
| on_chip_mem_ports = getattr(system, '_on_chip_mem_ports', None) |
| if on_chip_mem_ports: |
| other_memories.extend([p.simobj for p in on_chip_mem_ports]) |
| |
| # Create the LLCs cntrls |
| sysranges = [] + system.mem_ranges |
| |
| for m in other_memories: |
| sysranges.append(m.range) |
| |
| CHI_HNF.createAddrRanges(sysranges, system.cache_line_size.value, |
| options.num_l3caches) |
| ruby_system.hnf = [ CHI_HNF(i, ruby_system, HNFCache, None) |
| for i in range(options.num_l3caches) ] |
| |
| for hnf in ruby_system.hnf: |
| network_nodes.append(hnf) |
| network_cntrls.extend(hnf.getNetworkSideControllers()) |
| assert(hnf.getAllControllers() == hnf.getNetworkSideControllers()) |
| all_cntrls.extend(hnf.getAllControllers()) |
| hnf_dests.extend(hnf.getAllControllers()) |
| |
| # Create the memory controllers |
| # Notice we don't define a Directory_Controller type so we don't use |
| # create_directories shared by other protocols. |
| |
| ruby_system.snf = [ CHI_SNF_MainMem(ruby_system, None, None) |
| for i in range(options.num_dirs) ] |
| for snf in ruby_system.snf: |
| network_nodes.append(snf) |
| network_cntrls.extend(snf.getNetworkSideControllers()) |
| assert(snf.getAllControllers() == snf.getNetworkSideControllers()) |
| mem_cntrls.extend(snf.getAllControllers()) |
| all_cntrls.extend(snf.getAllControllers()) |
| mem_dests.extend(snf.getAllControllers()) |
| |
| if len(other_memories) > 0: |
| ruby_system.rom_snf = [ CHI_SNF_BootMem(ruby_system, None, m) |
| for m in other_memories ] |
| for snf in ruby_system.rom_snf: |
| network_nodes.append(snf) |
| network_cntrls.extend(snf.getNetworkSideControllers()) |
| all_cntrls.extend(snf.getAllControllers()) |
| mem_dests.extend(snf.getAllControllers()) |
| |
| |
| # Creates the controller for dma ports and io |
| |
| if len(dma_ports) > 0: |
| ruby_system.dma_rni = [ CHI_RNI_DMA(ruby_system, dma_port, None) |
| for dma_port in dma_ports ] |
| for rni in ruby_system.dma_rni: |
| network_nodes.append(rni) |
| network_cntrls.extend(rni.getNetworkSideControllers()) |
| all_cntrls.extend(rni.getAllControllers()) |
| |
| if full_system: |
| ruby_system.io_rni = CHI_RNI_IO(ruby_system, None) |
| network_nodes.append(ruby_system.io_rni) |
| network_cntrls.extend(ruby_system.io_rni.getNetworkSideControllers()) |
| all_cntrls.extend(ruby_system.io_rni.getAllControllers()) |
| |
| |
| # Assign downstream destinations |
| for rnf in ruby_system.rnf: |
| rnf.setDownstream(hnf_dests) |
| if len(dma_ports) > 0: |
| for rni in ruby_system.dma_rni: |
| rni.setDownstream(hnf_dests) |
| if full_system: |
| ruby_system.io_rni.setDownstream(hnf_dests) |
| for hnf in ruby_system.hnf: |
| hnf.setDownstream(mem_dests) |
| |
| # Setup data message size for all controllers |
| for cntrl in all_cntrls: |
| cntrl.data_channel_size = params.data_width |
| |
| # Network configurations |
| # virtual networks: 0=request, 1=snoop, 2=response, 3=data |
| ruby_system.network.number_of_virtual_networks = 4 |
| |
| ruby_system.network.control_msg_size = params.cntrl_msg_size |
| ruby_system.network.data_msg_size = params.data_width |
| ruby_system.network.buffer_size = params.router_buffer_size |
| |
| if params.topology == 'CustomMesh': |
| topology = create_topology(network_nodes, params) |
| elif params.topology in ['Crossbar', 'Pt2Pt']: |
| topology = create_topology(network_cntrls, params) |
| else: |
| m5.fatal("%s not supported!" % params.topology) |
| |
| # Incorporate the params into options so it's propagated to |
| # makeTopology by the parent script |
| for k in dir(params): |
| if not k.startswith('__'): |
| setattr(options, k, getattr(params, k)) |
| |
| return (cpu_sequencers, mem_cntrls, topology) |