# Copyright 2019 Google, Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from m5.params import *
from m5.proxy import *
from m5.SimObject import SimObject

from m5.objects.ArmInterrupts import ArmInterrupts
from m5.objects.ArmISA import ArmISA
from m5.objects.FastModel import AmbaInitiatorSocket, AmbaTargetSocket
from m5.objects.FastModelGIC import Gicv3CommsTargetSocket
from m5.objects.Gic import ArmPPI
from m5.objects.Iris import IrisBaseCPU
from m5.objects.SystemC import SystemC_ScModule
from m5.util.fdthelper import FdtNode, FdtPropertyWords

class FastModelCortexA76(IrisBaseCPU):
    type = 'FastModelCortexA76'
    cxx_class = 'gem5::fastmodel::CortexA76'
    cxx_header = 'arch/arm/fastmodel/CortexA76/cortex_a76.hh'

    cntfrq = Param.UInt64(0x1800000, "Value for the CNTFRQ timer register")

    evs = Parent.evs

    redistributor = Gicv3CommsTargetSocket('GIC communication target')

    CFGEND = Param.Bool(False, "Endianness configuration at reset.  "\
            "0, little endian. 1, big endian.")
    CFGTE = Param.Bool(False, "Instruction set state when resetting "\
            "into AArch32.  0, A32. 1, T32.")
    CRYPTODISABLE = Param.Bool(False, "Disable cryptographic features.")
    RVBARADDR = Param.Addr(0x0, "Value of RVBAR_ELx register.")
    VINITHI = Param.Bool(False, "Reset value of SCTLR.V.")
    enable_trace_special_hlt_imm16 = Param.Bool(False,
            "Enable usage of parameter trace_special_hlt_imm16")
    l2cache_hit_latency = Param.UInt64(0, "L2 Cache timing annotation "\
            "latency for hit.  Intended to model the tag-lookup time.  This "\
            "is only used when l2cache-state_modelled=true.")
    l2cache_maintenance_latency = Param.UInt64(0, "L2 Cache timing "\
            "annotation latency for cache maintenance operations given in "\
            "total ticks. This is only used when dcache-state_modelled=true.")
    l2cache_miss_latency = Param.UInt64(0, "L2 Cache timing annotation "\
            "latency for miss.  Intended to model the time for failed "\
            "tag-lookup and allocation of intermediate buffers.  This is "\
            "only used when l2cache-state_modelled=true.")
    l2cache_read_access_latency = Param.UInt64(0, "L2 Cache timing "\
            "annotation latency for read accesses given in ticks per "\
            "access.  If this parameter is non-zero, per-access latencies "\
            "will be used instead of per-byte even if l2cache-read_latency "\
            "is set. This is in addition to the hit or miss latency, and "\
            "intended to correspond to the time taken to transfer across the "\
            "cache upstream bus, this is only used when "\
            "l2cache-state_modelled=true.")
    l2cache_read_latency = Param.UInt64(0, "L2 Cache timing annotation "\
            "latency for read accesses given in ticks per byte "\
            "accessed.l2cache-read_access_latency must be set to 0 for "\
            "per-byte latencies to be applied.  This is in addition to the "\
            "hit or miss latency, and intended to correspond to the time "\
            "taken to transfer across the cache upstream bus. This is only "\
            "used when l2cache-state_modelled=true.")
    l2cache_size = Param.MemorySize32('0x80000', "L2 Cache size in bytes.")
    l2cache_snoop_data_transfer_latency = Param.UInt64(0, "L2 Cache "\
            "timing annotation latency for received snoop accesses that "\
            "perform a data transfer given in ticks per byte accessed. This "\
            "is only used when dcache-state_modelled=true.")
    l2cache_snoop_issue_latency = Param.UInt64(0, "L2 Cache timing "\
            "annotation latency for snoop accesses issued by this cache in "\
            "total ticks. This is only used when dcache-state_modelled=true.")
    l2cache_write_access_latency = Param.UInt64(0, "L2 Cache timing "\
            "annotation latency for write accesses given in ticks per "\
            "access. If this parameter is non-zero, per-access latencies "\
            "will be used instead of per-byte even if l2cache-write_latency "\
            "is set. This is only used when l2cache-state_modelled=true.")
    l2cache_write_latency = Param.UInt64(0, "L2 Cache timing annotation "\
            "latency for write accesses given in ticks per byte accessed. "\
            "l2cache-write_access_latency must be set to 0 for per-byte "\
            "latencies to be applied. This is only used when "\
            "l2cache-state_modelled=true.")
    max_code_cache_mb = Param.MemorySize32('0x100', "Maximum size of "\
            "the simulation code cache (MiB). For platforms with more than 2 "\
            "cores this limit will be scaled down. (e.g 1/8 for 16 or more "\
            "cores)")
    min_sync_level = Param.Unsigned(0, "Force minimum syncLevel "\
            "(0=off=default,1=syncState,2=postInsnIO,3=postInsnAll)")
    semihosting_A32_HLT = Param.UInt16(0xf000,
            "A32 HLT number for semihosting calls.")
    semihosting_A64_HLT = Param.UInt16(0xf000,
            "A64 HLT number for semihosting calls.")
    semihosting_ARM_SVC = Param.UInt32(0x123456,
            "A32 SVC number for semihosting calls.")
    semihosting_T32_HLT = Param.Unsigned(60,
            "T32 HLT number for semihosting calls.")
    semihosting_Thumb_SVC = Param.Unsigned(171,
            "T32 SVC number for semihosting calls.")
    semihosting_cmd_line = Param.String("",
            "Command line available to semihosting calls.")
    semihosting_cwd = Param.String("",
            "Base directory for semihosting file access.")
    semihosting_enable = Param.Bool(True, "Enable semihosting SVC/HLT traps.")
    semihosting_heap_base = Param.Addr(0x0, "Virtual address of heap base.")
    semihosting_heap_limit = Param.Addr(0xf000000,
            "Virtual address of top of heap.")
    semihosting_stack_base = Param.Addr(0x10000000,
            "Virtual address of base of descending stack.")
    semihosting_stack_limit = Param.Addr(0xf000000,
            "Virtual address of stack limit.")
    trace_special_hlt_imm16 = Param.UInt16(0xf000, "For this HLT "\
            "number, IF enable_trace_special_hlt_imm16=true, skip performing "\
            "usual HLT execution but call MTI trace if registered")
    vfp_enable_at_reset = Param.Bool(False, "Enable VFP in CPACR, "\
            "CPPWR, NSACR at reset. Warning: Arm recommends going through "\
            "the implementation's suggested VFP power-up sequence!")

class FastModelCortexA76Cluster(SimObject):
    type = 'FastModelCortexA76Cluster'
    cxx_class = 'gem5::fastmodel::CortexA76Cluster'
    cxx_header = 'arch/arm/fastmodel/CortexA76/cortex_a76.hh'

    cores = VectorParam.FastModelCortexA76(
            'Core in a given cluster of CortexA76s')

    evs = Param.SystemC_ScModule(
            "Fast mo0del exported virtual subsystem holding cores")

    cnthpirq = Param.ArmInterruptPin(ArmPPI(num=10),
            "EL2 physical timer event")
    cnthvirq = Param.ArmInterruptPin(ArmPPI(num=12), "EL2 virtual timer event")
    cntpsirq = Param.ArmInterruptPin(ArmPPI(num=13),
            "EL1 Secure physical timer event")
    cntvirq = Param.ArmInterruptPin(ArmPPI(num=11), "Virtual timer event")
    commirq = Param.ArmInterruptPin(ArmPPI(num=6),
            "Interrupt signal from debug communications channel")
    ctidbgirq = Param.ArmInterruptPin(ArmPPI(num=8),
            "Cross Trigger Interface (CTI) interrupt trigger output")
    pmuirq = Param.ArmInterruptPin(ArmPPI(num=7),
            "Interrupt from performance monitoring unit")
    vcpumntirq = Param.ArmInterruptPin(ArmPPI(num=9),
            "Interrupt signal for virtual CPU maintenance IRQ")
    cntpnsirq = Param.ArmInterruptPin(ArmPPI(num=14),
            "Non-secure physical timer event")

    amba = AmbaInitiatorSocket(64, 'AMBA initiator socket')

    # These parameters are described in "Fast Models Reference Manual" section
    # 3.4.19, "ARMCortexA7x1CT".
    BROADCASTATOMIC = Param.Bool(True, "Enable broadcasting of atomic "\
            "operation. The broadcastatomic signal will override this value "\
            "if used")
    BROADCASTCACHEMAINT = Param.Bool(True, "Enable broadcasting of cache "\
            "maintenance operations to downstream caches. The "\
            "broadcastcachemaint signal will override this value if used.")
    BROADCASTOUTER = Param.Bool(True, "Enable broadcasting of Outer "\
            "Shareable transactions. The broadcastouter signal will override "\
            "this value if used.")
    BROADCASTPERSIST = Param.Bool(True, "Enable broadcasting  of cache clean "\
            "to the point of persistence operations. The broadcastpersist "\
            "signal will override this value if used")
    CLUSTER_ID = Param.UInt16(0x0, "Processor cluster ID value")
    GICDISABLE = Param.Bool(True, "Disable the new style GICv3 CPU interface "\
            "in each core model. Should be left enabled unless the platform "\
            "contains a GICv3 distributor.")
    cpi_div = Param.UInt32(1,
            "Divider for calculating CPI (Cycles Per Instruction)")
    cpi_mul = Param.UInt32(1,
            "Multiplier for calculating CPI (Cycles Per Instruction)")
    dcache_hit_latency = Param.UInt64(0, "L1 D-Cache timing annotation "\
            "latency for hit.  Intended to model the tag-lookup time.  This "\
            "is only used when dcache-state_modelled=true.")
    dcache_maintenance_latency = Param.UInt64(0, "L1 D-Cache timing "\
            "annotation latency for cache maintenance operations given in "\
            "total ticks. This is only used when dcache-state_modelled=true.")
    dcache_miss_latency = Param.UInt64(0, "L1 D-Cache timing annotation "\
            "latency for miss.  Intended to model the time for failed "\
            "tag-lookup and allocation of intermediate buffers.  This is "\
            "only used when dcache-state_modelled=true.")
    dcache_prefetch_enabled = Param.Bool(False, "Enable simulation of data "\
            "cache prefetching.  This is only used when "\
            "dcache-state_modelled=true")
    dcache_read_access_latency = Param.UInt64(0, "L1 D-Cache timing "\
            "annotation latency for read accesses given in ticks per access "\
            "(of size dcache-read_bus_width_in_bytes).  If this parameter is "\
            "non-zero, per-access latencies will be used instead of per-byte "\
            "even if dcache-read_latency is set. This is in addition to the "\
            "hit or miss latency, and intended to correspond to the time "\
            "taken to transfer across the cache upstream bus, this is only "\
            "used when dcache-state_modelled=true.")
    dcache_read_latency = Param.UInt64(0, "L1 D-Cache timing annotation "\
            "latency for read accesses given in ticks per byte "\
            "accessed.dcache-read_access_latency must be set to 0 for "\
            "per-byte latencies to be applied.  This is in addition to the "\
            "hit or miss latency, and intended to correspond to the time "\
            "taken to transfer across the cache upstream bus. This is only "\
            "used when dcache-state_modelled=true.")
    dcache_snoop_data_transfer_latency = Param.UInt64(0, "L1 D-Cache timing "\
        "annotation latency for received snoop accesses that perform a data "\
        "transfer given in ticks per byte accessed. This is only used when "\
        "dcache-state_modelled=true.")
    dcache_state_modelled = Param.Bool(False,
        "Set whether D-cache has stateful implementation")
    dcache_write_access_latency = Param.UInt64(0, "L1 D-Cache timing "\
        "annotation latency for write accesses given in ticks per access "\
        "(of size dcache-write_bus_width_in_bytes). If this parameter is "\
        "non-zero, per-access latencies will be used instead of per-byte "\
        "even if dcache-write_latency is set. This is only used when "\
        "dcache-state_modelled=true.")
    dcache_write_latency = Param.UInt64(0, "L1 D-Cache timing annotation "\
        "latency for write accesses given in ticks per byte accessed. "\
        "dcache-write_access_latency must be set to 0 for per-byte latencies "\
        "to be applied. This is only used when dcache-state_modelled=true.")
    default_opmode = Param.Unsigned(4, "Operating mode of DynamIQ coming out "\
            "of reset. 0: SFONLY ON, 1: 1/4 CACHE ON, 2: 1/2 CACHE ON, 3: "\
            "3/4 CACHE ON, 4: FULL CACHE ON")
    diagnostics = Param.Bool(False, "Enable DynamIQ diagnostic messages")
    enable_simulation_performance_optimizations = Param.Bool(True,
            "With this option enabled, the model will run more quickly, but "\
            "be less accurate to exact CPU behavior. The model will still be "\
            "functionally accurate for software, but may increase "\
            "differences seen between hardware behavior and model behavior "\
            "for certain workloads (it changes the micro-architectural value "\
            "of stage12_tlb_size parameter to 1024).")
    ext_abort_device_read_is_sync = Param.Bool(False,
            "Synchronous reporting of device-nGnRE read external aborts")
    ext_abort_device_write_is_sync = Param.Bool(False,
            "Synchronous reporting of device-nGnRE write external aborts")
    ext_abort_so_read_is_sync = Param.Bool(False,
            "Synchronous reporting of device-nGnRnE read external aborts")
    ext_abort_so_write_is_sync = Param.Bool(False,
            "Synchronous reporting of device-nGnRnE write external aborts")
    gicv3_cpuintf_mmap_access_level = Param.Unsigned(0, "Allowed values are: "\
            "0-mmap access is supported for GICC,GICH,GICV registers. 1-mmap "\
            "access is supported only for GICV registers. 2-mmap access is "\
            "not supported.")
    has_peripheral_port = Param.Bool(False,
            "If true, additional AXI peripheral port is configured.")
    has_statistical_profiling = Param.Bool(True,
            "Whether Statistical Based Profiling is implemented")
    icache_hit_latency = Param.UInt64(0, "L1 I-Cache timing annotation "\
            "latency for hit.  Intended to model the tag-lookup time.  This "\
            "is only used when icache-state_modelled=true.")
    icache_maintenance_latency = Param.UInt64(0, "L1 I-Cache timing "\
            "annotation latency for cache maintenance operations given in "\
            "total ticks. This is only used when icache-state_modelled=true.")
    icache_miss_latency = Param.UInt64(0, "L1 I-Cache timing annotation "\
            "latency for miss.  Intended to model the time for failed "\
            "tag-lookup and allocation of intermediate buffers.  This is "\
            "only used when icache-state_modelled=true.")
    icache_prefetch_enabled = Param.Bool(False, "Enable simulation of "\
            "instruction cache prefetching. This is only used when "\
            "icache-state_modelled=true.")
    icache_read_access_latency = Param.UInt64(0, "L1 I-Cache timing "\
            "annotation latency for read accesses given in ticks per access "\
            "(of size icache-read_bus_width_in_bytes).  If this parameter is "\
            "non-zero, per-access latencies will be used instead of per-byte "\
            "even if icache-read_latency is set. This is in addition to the "\
            "hit or miss latency, and intended to correspond to the time "\
            "taken to transfer across the cache upstream bus, this is only "\
            "used when icache-state_modelled=true.")
    icache_read_latency = Param.UInt64(0, "L1 I-Cache timing annotation "\
            "latency for read accesses given in ticks per byte "\
            "accessed.icache-read_access_latency must be set to 0 for "\
            "per-byte latencies to be applied.  This is in addition to the "\
            "hit or miss latency, and intended to correspond to the time "\
            "taken to transfer across the cache upstream bus. This is only "\
            "used when icache-state_modelled=true.")
    icache_state_modelled = Param.Bool(False,
            "Set whether I-cache has stateful implementation")
    l3cache_hit_latency = Param.UInt64(0, "L3 Cache timing annotation "\
            "latency for hit.  Intended to model the tag-lookup time.  This "\
            "is only used when l3cache-state_modelled=true.")
    l3cache_maintenance_latency = Param.UInt64(0, "L3 Cache timing "\
            "annotation latency for cache maintenance operations given in "\
            "total ticks. This is only used when dcache-state_modelled=true.")
    l3cache_miss_latency = Param.UInt64(0, "L3 Cache timing annotation "\
            "latency for miss.  Intended to model the time for failed "\
            "tag-lookup and allocation of intermediate buffers.  This is "\
            "only used when l3cache-state_modelled=true.")
    l3cache_read_access_latency = Param.UInt64(0, "L3 Cache timing "\
            "annotation latency for read accesses given in ticks per access "\
            "(of size l3cache-read_bus_width_in_bytes).  If this parameter "\
            "is non-zero, per-access latencies will be used instead of "\
            "per-byte even if l3cache-read_latency is set. This is in "\
            "addition to the hit or miss latency, and intended to correspond "\
            "to the time taken to transfer across the cache upstream bus, "\
            "this is only used when l3cache-state_modelled=true.")
    l3cache_read_latency = Param.UInt64(0, "L3 Cache timing annotation "\
            "latency for read accesses given in ticks per byte "\
            "accessed.l3cache-read_access_latency must be set to 0 for "\
            "per-byte latencies to be applied.  This is in addition to the "\
            "hit or miss latency, and intended to correspond to the time "\
            "taken to transfer across the cache upstream bus. This is only "\
            "used when l3cache-state_modelled=true.")
    l3cache_size = Param.MemorySize('0x100000', "L3 Cache size in bytes.")
    l3cache_snoop_data_transfer_latency = Param.UInt64(0, "L3 Cache timing "\
            "annotation latency for received snoop accesses that perform a "\
            "data transfer given in ticks per byte accessed. This is only "\
            "used when dcache-state_modelled=true.")
    l3cache_snoop_issue_latency = Param.UInt64(0, "L3 Cache timing "\
            "annotation latency for snoop accesses issued by this cache in "\
            "total ticks. This is only used when dcache-state_modelled=true.")
    l3cache_write_access_latency = Param.UInt64(0, "L3 Cache timing "\
            "annotation latency for write accesses given in ticks per access "\
            "(of size l3cache-write_bus_width_in_bytes). If this parameter "\
            "is non-zero, per-access latencies will be used instead of "\
            "per-byte even if l3cache-write_latency is set. This is only "\
            "used when l3cache-state_modelled=true.")
    l3cache_write_latency = Param.UInt64(0, "L3 Cache timing annotation "\
            "latency for write accesses given in ticks per byte accessed. "\
            "l3cache-write_access_latency must be set to 0 for per-byte "\
            "latencies to be applied. This is only used when "\
            "l3cache-state_modelled=true.")
    pchannel_treat_simreset_as_poreset = Param.Bool(False,
            "Register core as ON state to cluster with simulation reset.")
    periph_address_end = Param.Addr(0x0, "End address for peripheral port "\
            "address range exclusive(corresponds to AENDMP input signal).")
    periph_address_start = Param.Addr(0x0, "Start address for peripheral "\
            "port address range inclusive(corresponds to ASTARTMP input "\
            "signal).")
    ptw_latency = Param.UInt64(0, "Page table walker latency for TA "\
            "(Timing Annotation), expressed in simulation ticks")
    tlb_latency = Param.UInt64(0, "TLB latency for TA (Timing Annotation), "\
            "expressed in simulation ticks")
    treat_dcache_cmos_to_pou_as_nop = Param.Bool(False, "Whether dcache "\
            "invalidation to the point of unification is required for "\
            "instruction to data coherence. true - Invalidate operations not "\
            "required")
    walk_cache_latency = Param.UInt64(0, "Walk cache latency for TA (Timing "\
            "Annotation), expressed in simulation ticks")

    def generateDeviceTree(self, state):
        node = FdtNode("timer")

        node.appendCompatible(["arm,cortex-a15-timer",
                               "arm,armv7-timer",
                               "arm,armv8-timer"])
        node.append(FdtPropertyWords("interrupts", [
            1, int(self.cntpsirq.num), 0xf08,
            1, int(self.cntpnsirq.num), 0xf08,
            1, int(self.cntvirq.num), 0xf08,
            1, int(self.cnthpirq.num), 0xf08,
        ]))

        yield node

class FastModelScxEvsCortexA76x1(SystemC_ScModule):
    type = 'FastModelScxEvsCortexA76x1'
    cxx_class = \
        'gem5::fastmodel::ScxEvsCortexA76<fastmodel::ScxEvsCortexA76x1Types>'
    cxx_template_params = [ 'class Types' ]
    cxx_header = 'arch/arm/fastmodel/CortexA76/evs.hh'

class FastModelCortexA76x1(FastModelCortexA76Cluster):
    cores = [ FastModelCortexA76(thread_paths=[ 'core.cpu0' ]) ]

    evs = FastModelScxEvsCortexA76x1()

class FastModelScxEvsCortexA76x2(SystemC_ScModule):
    type = 'FastModelScxEvsCortexA76x2'
    cxx_class = \
        'gem5::fastmodel::ScxEvsCortexA76<fastmodel::ScxEvsCortexA76x2Types>'
    cxx_template_params = [ 'class Types' ]
    cxx_header = 'arch/arm/fastmodel/CortexA76/evs.hh'

class FastModelCortexA76x2(FastModelCortexA76Cluster):
    cores = [ FastModelCortexA76(thread_paths=[ 'core.cpu0' ]),
              FastModelCortexA76(thread_paths=[ 'core.cpu1' ]) ]

    evs = FastModelScxEvsCortexA76x2()

class FastModelScxEvsCortexA76x3(SystemC_ScModule):
    type = 'FastModelScxEvsCortexA76x3'
    cxx_class = \
        'gem5::fastmodel::ScxEvsCortexA76<fastmodel::ScxEvsCortexA76x3Types>'
    cxx_template_params = [ 'class Types' ]
    cxx_header = 'arch/arm/fastmodel/CortexA76/evs.hh'

class FastModelCortexA76x3(FastModelCortexA76Cluster):
    cores = [ FastModelCortexA76(thread_paths=[ 'core.cpu0' ]),
              FastModelCortexA76(thread_paths=[ 'core.cpu1' ]),
              FastModelCortexA76(thread_paths=[ 'core.cpu2' ]) ]

    evs = FastModelScxEvsCortexA76x3()

class FastModelScxEvsCortexA76x4(SystemC_ScModule):
    type = 'FastModelScxEvsCortexA76x4'
    cxx_class = \
        'gem5::fastmodel::ScxEvsCortexA76<fastmodel::ScxEvsCortexA76x4Types>'
    cxx_template_params = [ 'class Types' ]
    cxx_header = 'arch/arm/fastmodel/CortexA76/evs.hh'

class FastModelCortexA76x4(FastModelCortexA76Cluster):
    cores = [ FastModelCortexA76(thread_paths=[ 'core.cpu0' ]),
              FastModelCortexA76(thread_paths=[ 'core.cpu1' ]),
              FastModelCortexA76(thread_paths=[ 'core.cpu2' ]),
              FastModelCortexA76(thread_paths=[ 'core.cpu3' ]) ]

    evs = FastModelScxEvsCortexA76x4()
