| # Copyright (c) 2009-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. |
| # |
| # Copyright (c) 2006-2007 The Regents of The University of Michigan |
| # All rights reserved. |
| # |
| # 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.defines import buildEnv |
| from m5.params import * |
| from m5.proxy import * |
| from m5.util.fdthelper import * |
| from m5.objects.ArmSystem import ArmExtension |
| from m5.objects.ClockDomain import ClockDomain, SrcClockDomain |
| from m5.objects.VoltageDomain import VoltageDomain |
| from m5.objects.Device import \ |
| BasicPioDevice, PioDevice, IsaFake, BadAddr, DmaDevice |
| from m5.objects.PciHost import * |
| from m5.objects.Ethernet import NSGigE, IGbE_igb, IGbE_e1000 |
| from m5.objects.Ide import * |
| from m5.objects.Platform import Platform |
| from m5.objects.Terminal import Terminal |
| from m5.objects.Uart import Uart |
| from m5.objects.SimpleMemory import SimpleMemory |
| from m5.objects.GenericTimer import * |
| from m5.objects.Gic import * |
| from m5.objects.MHU import MHU, Scp2ApDoorbell, Ap2ScpDoorbell |
| from m5.objects.EnergyCtrl import EnergyCtrl |
| from m5.objects.ClockedObject import ClockedObject |
| from m5.objects.SubSystem import SubSystem |
| from m5.objects.Graphics import ImageFormat |
| from m5.objects.ClockedObject import ClockedObject |
| from m5.objects.PS2 import * |
| from m5.objects.VirtIOMMIO import MmioVirtIO |
| from m5.objects.Display import Display, Display1080p |
| from m5.objects.Scmi import * |
| from m5.objects.SMMUv3 import SMMUv3 |
| from m5.objects.PciDevice import PciLegacyIoBar, PciIoBar |
| |
| from m5.objects.CfiMemory import CfiMemory |
| |
| # Platforms with KVM support should generally use in-kernel GIC |
| # emulation. Use a GIC model that automatically switches between |
| # gem5's GIC model and KVM's GIC model if KVM is available. |
| try: |
| from m5.objects.KvmGic import MuxingKvmGic |
| kvm_gicv2_class = MuxingKvmGic |
| except ImportError: |
| # KVM support wasn't compiled into gem5. Fallback to a |
| # software-only GIC. |
| kvm_gicv2_class = Gic400 |
| pass |
| |
| class AmbaPioDevice(BasicPioDevice): |
| type = 'AmbaPioDevice' |
| abstract = True |
| cxx_header = "dev/arm/amba_device.hh" |
| cxx_class = 'gem5::AmbaPioDevice' |
| amba_id = Param.UInt32("ID of AMBA device for kernel detection") |
| |
| class AmbaIntDevice(AmbaPioDevice): |
| type = 'AmbaIntDevice' |
| abstract = True |
| cxx_header = "dev/arm/amba_device.hh" |
| cxx_class = 'gem5::AmbaIntDevice' |
| interrupt = Param.ArmInterruptPin("Interrupt that connects to GIC") |
| int_delay = Param.Latency("100ns", |
| "Time between action and interrupt generation by device") |
| |
| class AmbaDmaDevice(DmaDevice): |
| type = 'AmbaDmaDevice' |
| abstract = True |
| cxx_header = "dev/arm/amba_device.hh" |
| cxx_class = 'gem5::AmbaDmaDevice' |
| pio_addr = Param.Addr("Address for AMBA responder interface") |
| pio_latency = Param.Latency("10ns", "Time between action and write/read" |
| "result by AMBA DMA Device") |
| interrupt = Param.ArmInterruptPin("Interrupt that connects to GIC") |
| amba_id = Param.UInt32("ID of AMBA device for kernel detection") |
| |
| class A9SCU(BasicPioDevice): |
| type = 'A9SCU' |
| cxx_header = "dev/arm/a9scu.hh" |
| cxx_class = 'gem5::A9SCU' |
| |
| class ArmPciIntRouting(Enum): vals = [ |
| 'ARM_PCI_INT_STATIC', |
| 'ARM_PCI_INT_DEV', |
| 'ARM_PCI_INT_PIN', |
| ] |
| |
| class GenericArmPciHost(GenericPciHost): |
| type = 'GenericArmPciHost' |
| cxx_header = "dev/arm/pci_host.hh" |
| cxx_class = 'gem5::GenericArmPciHost' |
| |
| int_policy = Param.ArmPciIntRouting("PCI interrupt routing policy") |
| int_base = Param.Unsigned("PCI interrupt base") |
| int_count = Param.Unsigned("Maximum number of interrupts used by this host") |
| |
| # This python parameter can be used in configuration scripts to turn |
| # on/off the fdt dma-coherent flag when doing dtb autogeneration |
| _dma_coherent = True |
| |
| def generateDeviceTree(self, state): |
| local_state = FdtState( |
| addr_cells=3, size_cells=2, |
| cpu_cells=1, interrupt_cells=1) |
| |
| node = FdtNode("pci") |
| |
| if int(self.conf_device_bits) == 8: |
| node.appendCompatible("pci-host-cam-generic") |
| elif int(self.conf_device_bits) == 12: |
| node.appendCompatible("pci-host-ecam-generic") |
| else: |
| m5.fatal("No compatibility string for the set conf_device_width") |
| |
| node.append(FdtPropertyStrings("device_type", ["pci"])) |
| |
| # Cell sizes of child nodes/peripherals |
| node.append(local_state.addrCellsProperty()) |
| node.append(local_state.sizeCellsProperty()) |
| node.append(local_state.interruptCellsProperty()) |
| # PCI address for CPU |
| node.append(FdtPropertyWords("reg", |
| state.addrCells(self.conf_base) + |
| state.sizeCells(self.conf_size) )) |
| |
| # Ranges mapping |
| # For now some of this is hard coded, because the PCI module does not |
| # have a proper full understanding of the memory map, but adapting the |
| # PCI module is beyond the scope of what I'm trying to do here. |
| # Values are taken from the VExpress_GEM5_V1 platform. |
| ranges = [] |
| # Pio address range |
| ranges += self.pciFdtAddr(space=1, addr=0) |
| ranges += state.addrCells(self.pci_pio_base) |
| ranges += local_state.sizeCells(0x10000) # Fixed size |
| |
| # AXI memory address range |
| ranges += self.pciFdtAddr(space=2, addr=0) |
| ranges += state.addrCells(self.pci_mem_base) |
| ranges += local_state.sizeCells(0x40000000) # Fixed size |
| node.append(FdtPropertyWords("ranges", ranges)) |
| |
| if str(self.int_policy) == 'ARM_PCI_INT_DEV': |
| gic = self._parent.unproxy(self).gic |
| int_phandle = state.phandle(gic) |
| # Interrupt mapping |
| interrupts = [] |
| |
| # child interrupt specifier |
| child_interrupt = local_state.interruptCells(0x0) |
| |
| # parent unit address |
| parent_addr = gic._state.addrCells(0x0) |
| |
| for i in range(int(self.int_count)): |
| parent_interrupt = gic.interruptCells(0, |
| int(self.int_base) - 32 + i, 1) |
| |
| interrupts += self.pciFdtAddr(device=i, addr=0) + \ |
| child_interrupt + [int_phandle] + parent_addr + \ |
| parent_interrupt |
| |
| node.append(FdtPropertyWords("interrupt-map", interrupts)) |
| |
| int_count = int(self.int_count) |
| if int_count & (int_count - 1): |
| fatal("PCI interrupt count should be power of 2") |
| |
| intmask = self.pciFdtAddr(device=int_count - 1, addr=0) + [0x0] |
| node.append(FdtPropertyWords("interrupt-map-mask", intmask)) |
| else: |
| m5.fatal("Unsupported PCI interrupt policy " + |
| "for Device Tree generation") |
| |
| if self._dma_coherent: |
| node.append(FdtProperty("dma-coherent")) |
| |
| yield node |
| |
| class RealViewCtrl(BasicPioDevice): |
| type = 'RealViewCtrl' |
| cxx_header = "dev/arm/rv_ctrl.hh" |
| cxx_class = 'gem5::RealViewCtrl' |
| proc_id0 = Param.UInt32(0x0C000000, "Processor ID, SYS_PROCID") |
| proc_id1 = Param.UInt32(0x0C000222, "Processor ID, SYS_PROCID1") |
| idreg = Param.UInt32(0x00000000, "ID Register, SYS_ID") |
| |
| def generateDeviceTree(self, state): |
| node = FdtNode("sysreg@%x" % int(self.pio_addr)) |
| node.appendCompatible("arm,vexpress-sysreg") |
| node.append(FdtPropertyWords("reg", |
| state.addrCells(self.pio_addr) + |
| state.sizeCells(0x1000) )) |
| node.append(FdtProperty("gpio-controller")) |
| node.append(FdtPropertyWords("#gpio-cells", [2])) |
| node.appendPhandle(self) |
| |
| yield node |
| |
| class RealViewOsc(ClockDomain): |
| type = 'RealViewOsc' |
| cxx_header = "dev/arm/rv_ctrl.hh" |
| cxx_class = 'gem5::RealViewOsc' |
| |
| parent = Param.RealViewCtrl(Parent.any, "RealView controller") |
| |
| # TODO: We currently don't have the notion of a clock source, |
| # which means we have to associate oscillators with a voltage |
| # source. |
| voltage_domain = Param.VoltageDomain(Parent.voltage_domain, |
| "Voltage domain") |
| |
| # See ARM DUI 0447J (ARM Motherboard Express uATX -- V2M-P1) and |
| # the individual core/logic tile reference manuals for details |
| # about the site/position/dcc/device allocation. |
| site = Param.UInt8("Board Site") |
| position = Param.UInt8("Position in device stack") |
| dcc = Param.UInt8("Daughterboard Configuration Controller") |
| device = Param.UInt8("Device ID") |
| |
| freq = Param.Clock("Default frequency") |
| |
| # These are currently only used for the device tree. |
| min_freq = Param.Clock("0t", "Minimum frequency") |
| max_freq = Param.Clock("0t", "Maximum frequency") |
| |
| def generateDeviceTree(self, state): |
| phandle = state.phandle(self) |
| node = FdtNode("osc@" + format(int(phandle), 'x')) |
| node.appendCompatible("arm,vexpress-osc") |
| node.append(FdtPropertyWords("arm,vexpress-sysreg,func", |
| [0x1, int(self.device)])) |
| node.append(FdtPropertyWords("#clock-cells", [0])) |
| |
| minf = self.min_freq if self.min_freq.value else self.freq |
| maxf = self.max_freq if self.max_freq.value else self.freq |
| |
| # Values are stored as a clock period. |
| def to_freq(prop): |
| return int(1.0 / prop.value) |
| |
| node.append(FdtPropertyWords("freq-range", |
| [to_freq(minf), to_freq(maxf)])) |
| node.append(FdtPropertyStrings("clock-output-names", |
| ["oscclk" + str(phandle)])) |
| node.appendPhandle(self) |
| yield node |
| |
| class RealViewTemperatureSensor(SimObject): |
| type = 'RealViewTemperatureSensor' |
| cxx_header = "dev/arm/rv_ctrl.hh" |
| cxx_class = 'gem5::RealViewTemperatureSensor' |
| |
| parent = Param.RealViewCtrl(Parent.any, "RealView controller") |
| |
| system = Param.System(Parent.any, "system") |
| |
| # See ARM DUI 0447J (ARM Motherboard Express uATX -- V2M-P1) and |
| # the individual core/logic tile reference manuals for details |
| # about the site/position/dcc/device allocation. |
| site = Param.UInt8("Board Site") |
| position = Param.UInt8("Position in device stack") |
| dcc = Param.UInt8("Daughterboard Configuration Controller") |
| device = Param.UInt8("Device ID") |
| |
| class VExpressMCC(SubSystem): |
| """ARM V2M-P1 Motherboard Configuration Controller |
| |
| This subsystem describes a subset of the devices that sit behind the |
| motherboard configuration controller on the the ARM Motherboard |
| Express (V2M-P1) motherboard. See ARM DUI 0447J for details. |
| """ |
| |
| class Osc(RealViewOsc): |
| site, position, dcc = (0, 0, 0) |
| |
| class Temperature(RealViewTemperatureSensor): |
| site, position, dcc = (0, 0, 0) |
| |
| osc_mcc = Osc(device=0, min_freq="25MHz", max_freq="60MHz", freq="50MHz") |
| osc_clcd = Osc(device=1, min_freq="23.75MHz", max_freq="63.5MHz", |
| freq="23.75MHz") |
| osc_peripheral = Osc(device=2, freq="24MHz") |
| osc_system_bus = Osc(device=4, min_freq="2MHz", max_freq="230MHz", |
| freq="24MHz") |
| |
| # See Table 4.19 in ARM DUI 0447J (Motherboard Express uATX TRM). |
| temp_crtl = Temperature(device=0) |
| |
| def generateDeviceTree(self, state): |
| node = FdtNode("mcc") |
| node.appendCompatible("arm,vexpress,config-bus") |
| node.append(FdtPropertyWords("arm,vexpress,site", [0])) |
| |
| for obj in self._children.values(): |
| if issubclass(type(obj), SimObject): |
| node.append(obj.generateDeviceTree(state)) |
| |
| io_phandle = state.phandle(self.osc_mcc.parent.unproxy(self)) |
| node.append(FdtPropertyWords("arm,vexpress,config-bridge", io_phandle)) |
| |
| yield node |
| |
| class CoreTile2A15DCC(SubSystem): |
| """ARM CoreTile Express A15x2 Daughterboard Configuration Controller |
| |
| This subsystem describes a subset of the devices that sit behind the |
| daughterboard configuration controller on a CoreTile Express A15x2. See |
| ARM DUI 0604E for details. |
| """ |
| |
| class Osc(RealViewOsc): |
| site, position, dcc = (1, 0, 0) |
| |
| # See Table 2.8 in ARM DUI 0604E (CoreTile Express A15x2 TRM) |
| osc_cpu = Osc(device=0, min_freq="20MHz", max_freq="60MHz", freq="60MHz") |
| osc_hsbm = Osc(device=4, min_freq="20MHz", max_freq="40MHz", freq="40MHz") |
| osc_pxl = Osc(device=5, min_freq="23.76MHz", max_freq="165MHz", |
| freq="23.75MHz") |
| osc_smb = Osc(device=6, min_freq="20MHz", max_freq="50MHz", freq="50MHz") |
| osc_sys = Osc(device=7, min_freq="20MHz", max_freq="60MHz", freq="60MHz") |
| osc_ddr = Osc(device=8, freq="40MHz") |
| |
| def generateDeviceTree(self, state): |
| node = FdtNode("dcc") |
| node.appendCompatible("arm,vexpress,config-bus") |
| |
| for obj in self._children.values(): |
| if isinstance(obj, SimObject): |
| node.append(obj.generateDeviceTree(state)) |
| |
| io_phandle = state.phandle(self.osc_cpu.parent.unproxy(self)) |
| node.append(FdtPropertyWords("arm,vexpress,config-bridge", io_phandle)) |
| |
| yield node |
| |
| class AmbaFake(AmbaPioDevice): |
| type = 'AmbaFake' |
| cxx_header = "dev/arm/amba_fake.hh" |
| cxx_class = 'gem5::AmbaFake' |
| ignore_access = Param.Bool(False, |
| "Ignore reads/writes to this device, (e.g. IsaFake + AMBA)") |
| amba_id = 0; |
| |
| # Simple fixed-rate clock source. Intended to be instantiated in Platform |
| # instances for definition of clock bindings on DTB auto-generation |
| class FixedClock(SrcClockDomain): |
| # Keep track of the number of FixedClock instances in the system |
| # to provide unique names |
| _index = 0 |
| |
| def generateDeviceTree(self, state): |
| if len(self.clock) > 1: |
| fatal('FixedClock configured with multiple frequencies') |
| node = FdtNode('clock{}'.format(FixedClock._index)) |
| node.appendCompatible('fixed-clock') |
| node.append(FdtPropertyWords('#clock-cells', 0)) |
| node.append(FdtPropertyWords('clock-frequency', |
| self.clock[0].frequency)) |
| node.appendPhandle(self) |
| FixedClock._index += 1 |
| |
| yield node |
| |
| class Pl011(Uart): |
| type = 'Pl011' |
| cxx_header = "dev/arm/pl011.hh" |
| cxx_class = 'gem5::Pl011' |
| interrupt = Param.ArmInterruptPin("Interrupt that connects to GIC") |
| end_on_eot = Param.Bool(False, |
| "End the simulation when a EOT is received on the UART") |
| int_delay = Param.Latency("100ns", |
| "Time between action and interrupt generation by UART") |
| |
| def generateDeviceTree(self, state): |
| node = self.generateBasicPioDeviceNode(state, 'uart', self.pio_addr, |
| 0x1000, [ self.interrupt ]) |
| node.appendCompatible(["arm,pl011", "arm,primecell"]) |
| |
| # Hardcoded reference to the realview platform clocks, because the |
| # clk_domain can only store one clock (i.e. it is not a VectorParam) |
| realview = self._parent.unproxy(self) |
| node.append(FdtPropertyWords("clocks", |
| [state.phandle(realview.mcc.osc_peripheral), |
| state.phandle(realview.dcc.osc_smb)])) |
| node.append(FdtPropertyStrings("clock-names", ["uartclk", "apb_pclk"])) |
| yield node |
| |
| class Sp804(AmbaPioDevice): |
| type = 'Sp804' |
| cxx_header = "dev/arm/timer_sp804.hh" |
| cxx_class = 'gem5::Sp804' |
| int0 = Param.ArmSPI("Interrupt that connects to GIC") |
| clock0 = Param.Clock('1MHz', "Clock speed of the input") |
| int1 = Param.ArmSPI("Interrupt that connects to GIC") |
| clock1 = Param.Clock('1MHz', "Clock speed of the input") |
| amba_id = 0x00141804 |
| |
| class Sp805(AmbaIntDevice): |
| """ |
| Arm Watchdog Module (SP805) |
| Reference: |
| Arm Watchdog Module (SP805) - Technical Reference Manual - rev. r1p0 |
| Doc. ID: ARM DDI 0270B |
| """ |
| |
| type = 'Sp805' |
| cxx_header = 'dev/arm/watchdog_sp805.hh' |
| cxx_class = 'gem5::Sp805' |
| |
| amba_id = 0x00141805 |
| |
| def generateDeviceTree(self, state): |
| node = self.generateBasicPioDeviceNode(state, 'watchdog', |
| self.pio_addr, 0x1000, [ self.interrupt ]) |
| node.appendCompatible(['arm,sp805', 'arm,primecell']) |
| clocks = [state.phandle(self.clk_domain.unproxy(self))] |
| clock_names = ['wdogclk'] |
| platform = self._parent.unproxy(self) |
| if self in platform._off_chip_devices(): |
| clocks.append(state.phandle(platform.dcc.osc_smb)) |
| clock_names.append('apb_pclk') |
| node.append(FdtPropertyWords('clocks', clocks)) |
| node.append(FdtPropertyStrings('clock-names', clock_names)) |
| |
| yield node |
| |
| class GenericWatchdog(PioDevice): |
| type = 'GenericWatchdog' |
| cxx_header = 'dev/arm/watchdog_generic.hh' |
| cxx_class = 'gem5::GenericWatchdog' |
| |
| refresh_start = Param.Addr("Start address for the refresh frame") |
| control_start = Param.Addr("Start address for the control frame") |
| pio_latency = Param.Latency('10ns', "Delay for PIO r/w") |
| |
| ws0 = Param.ArmInterruptPin("WS0 Signal") |
| ws1 = Param.ArmInterruptPin("WS1 Signal") |
| |
| system_counter = Param.SystemCounter(Parent.any, |
| "The Watchdog uses the Generic Timer system counter as the timebase " |
| "against which the decision to trigger an interrupt is made.") |
| |
| class CpuLocalTimer(BasicPioDevice): |
| type = 'CpuLocalTimer' |
| cxx_header = "dev/arm/timer_cpulocal.hh" |
| cxx_class = 'gem5::CpuLocalTimer' |
| int_timer = Param.ArmPPI("Interrrupt used per-cpu to GIC") |
| int_watchdog = Param.ArmPPI("Interrupt for per-cpu watchdog to GIC") |
| |
| class PL031(AmbaIntDevice): |
| type = 'PL031' |
| cxx_header = "dev/arm/rtc_pl031.hh" |
| cxx_class = 'gem5::PL031' |
| time = Param.Time('01/01/2009', |
| "System time to use ('Now' for actual time)") |
| amba_id = 0x00041031 |
| |
| def generateDeviceTree(self, state): |
| node = self.generateBasicPioDeviceNode(state, 'rtc', self.pio_addr, |
| 0x1000, [ self.interrupt ]) |
| |
| node.appendCompatible(["arm,pl031", "arm,primecell"]) |
| clock = state.phandle(self.clk_domain.unproxy(self)) |
| node.append(FdtPropertyWords("clocks", clock)) |
| node.append(FdtPropertyStrings("clock-names", ["apb_pclk"])) |
| |
| yield node |
| |
| class Pl050(AmbaIntDevice): |
| type = 'Pl050' |
| cxx_header = "dev/arm/kmi.hh" |
| cxx_class = 'gem5::Pl050' |
| amba_id = 0x00141050 |
| |
| ps2 = Param.PS2Device("PS/2 device") |
| |
| def generateDeviceTree(self, state): |
| node = self.generateBasicPioDeviceNode(state, 'kmi', self.pio_addr, |
| 0x1000, [ self.interrupt ]) |
| |
| node.appendCompatible(["arm,pl050", "arm,primecell"]) |
| clock = state.phandle(self.clk_domain.unproxy(self)) |
| node.append(FdtPropertyWords("clocks", clock)) |
| |
| yield node |
| |
| class Pl111(AmbaDmaDevice): |
| type = 'Pl111' |
| cxx_header = "dev/arm/pl111.hh" |
| cxx_class = 'gem5::Pl111' |
| pixel_clock = Param.Clock('24MHz', "Pixel clock") |
| vnc = Param.VncInput(Parent.any, |
| "Vnc server for remote frame buffer display") |
| amba_id = 0x00141111 |
| enable_capture = Param.Bool(True, |
| "capture frame to system.framebuffer.bmp") |
| |
| class HDLcd(AmbaDmaDevice): |
| type = 'HDLcd' |
| cxx_header = "dev/arm/hdlcd.hh" |
| cxx_class = 'gem5::HDLcd' |
| vnc = Param.VncInput(Parent.any, "Vnc server for remote frame buffer " |
| "display") |
| amba_id = 0x00141000 |
| workaround_swap_rb = Param.Bool(False, "Workaround incorrect color " |
| "selector order in some kernels") |
| workaround_dma_line_count = Param.Bool(True, "Workaround incorrect " |
| "DMA line count (off by 1)") |
| enable_capture = Param.Bool(True, "capture frame to " |
| "system.framebuffer.{extension}") |
| frame_format = Param.ImageFormat("Auto", |
| "image format of the captured frame") |
| |
| pixel_buffer_size = Param.MemorySize32("2KiB", "Size of address range") |
| |
| pxl_clk = Param.ClockDomain("Pixel clock source") |
| pixel_chunk = Param.Unsigned(32, "Number of pixels to handle in one batch") |
| virt_refresh_rate = Param.Frequency("20Hz", "Frame refresh rate " |
| "in KVM mode") |
| _status = "ok" |
| |
| encoder = Param.Display(Display1080p(), "Display encoder") |
| |
| def endpointPhandle(self): |
| return "hdlcd_endpoint" |
| |
| def generateDeviceTree(self, state): |
| endpoint_node = FdtNode("endpoint") |
| endpoint_node.appendPhandle(self.endpointPhandle()) |
| |
| for encoder_node in self.encoder.generateDeviceTree(state): |
| encoder_endpoint = self.encoder.endpointNode() |
| |
| # Endpoint subnode |
| endpoint_node.append(FdtPropertyWords("remote-endpoint", |
| [ state.phandle(self.encoder.endpointPhandle()) ])) |
| encoder_endpoint.append(FdtPropertyWords("remote-endpoint", |
| [ state.phandle(self.endpointPhandle()) ])) |
| |
| yield encoder_node |
| |
| port_node = FdtNode("port") |
| port_node.append(endpoint_node) |
| |
| node = self.generateBasicPioDeviceNode(state, 'hdlcd', |
| self.pio_addr, 0x1000, [ self.interrupt ]) |
| |
| node.appendCompatible(["arm,hdlcd"]) |
| node.append(FdtPropertyWords("clocks", state.phandle(self.pxl_clk))) |
| node.append(FdtPropertyStrings("clock-names", ["pxlclk"])) |
| |
| node.append(FdtPropertyStrings("status", [ self._status ])) |
| |
| self.addIommuProperty(state, node) |
| |
| node.append(port_node) |
| |
| yield node |
| |
| class ParentMem(SimpleMemory): |
| """ |
| This is a base abstract class for child node generation |
| A memory willing to autogenerate child nodes can do that |
| directly in the generateDeviceTree method. |
| However sometimes portions of memory (child nodes) are tagged |
| for specific applications. Hardcoding the child node in the |
| parent memory class is not flexible, so we delegate this |
| to the application model, which is registering the generator |
| helper via the ParentMem interface. |
| """ |
| def __init__(self, *args, **kwargs): |
| super().__init__(*args, **kwargs) |
| self._generators = [] |
| |
| def addSubnodeGenerator(self, gen): |
| """ |
| This is the method a client application would use to |
| register a child generator in the memory object. |
| """ |
| self._generators.append(gen) |
| |
| def generateSubnodes(self, node, state): |
| """ |
| This is the method the memory would use to instantiate |
| the child nodes via the previously registered generators. |
| """ |
| for subnode_gen in self._generators: |
| node.append(subnode_gen(state)) |
| |
| class MmioSRAM(ParentMem): |
| def __init__(self, *args, **kwargs): |
| super().__init__(**kwargs) |
| |
| def generateDeviceTree(self, state): |
| node = FdtNode("sram@%x" % int(self.range.start)) |
| node.appendCompatible(["mmio-sram"]) |
| node.append(FdtPropertyWords("reg", |
| state.addrCells(self.range.start) + |
| state.sizeCells(self.range.size()) )) |
| |
| local_state = FdtState(addr_cells=2, size_cells=2, cpu_cells=1) |
| node.append(local_state.addrCellsProperty()) |
| node.append(local_state.sizeCellsProperty()) |
| node.append(FdtPropertyWords("ranges", |
| local_state.addrCells(0) + |
| state.addrCells(self.range.start) + |
| state.sizeCells(self.range.size()) )) |
| |
| self.generateSubnodes(node, state) |
| |
| yield node |
| |
| class FVPBasePwrCtrl(BasicPioDevice): |
| """ |
| Based on Fast Models Base_PowerController v11.8 |
| Reference: |
| Fast Models Reference Manual - Section 7.7.2 - Version 11.8 |
| Document ID: 100964_1180_00_en |
| """ |
| |
| type = 'FVPBasePwrCtrl' |
| cxx_header = 'dev/arm/fvp_base_pwr_ctrl.hh' |
| cxx_class = 'gem5::FVPBasePwrCtrl' |
| |
| class GenericMHU(MHU): |
| lowp_scp2ap = Scp2ApDoorbell( |
| set_address=0x10020008, clear_address=0x10020010, |
| interrupt=ArmSPI(num=68)) |
| highp_scp2ap = Scp2ApDoorbell( |
| set_address=0x10020028, clear_address=0x10020030, |
| interrupt=ArmSPI(num=67)) |
| sec_scp2ap = Scp2ApDoorbell( |
| set_address=0x10020208, clear_address=0x10020210, |
| interrupt=ArmSPI(num=69)) |
| lowp_ap2scp = Ap2ScpDoorbell( |
| set_address=0x10020108, clear_address=0x10020110) |
| highp_ap2scp = Ap2ScpDoorbell( |
| set_address=0x10020128, clear_address=0x10020130) |
| sec_ap2scp = Ap2ScpDoorbell( |
| set_address=0x10020308, clear_address=0x10020310) |
| |
| class RealView(Platform): |
| type = 'RealView' |
| cxx_header = "dev/arm/realview.hh" |
| cxx_class = 'gem5::RealView' |
| _mem_regions = [ AddrRange(0, size='256MiB') ] |
| _num_pci_dev = 0 |
| |
| def _on_chip_devices(self): |
| return [] |
| |
| def _off_chip_devices(self): |
| return [] |
| |
| def _on_chip_memory(self): |
| return [] |
| |
| def _off_chip_memory(self): |
| return [] |
| |
| _off_chip_ranges = [] |
| |
| def _attach_memory(self, mem, bus, mem_ports=None): |
| if hasattr(mem, "port"): |
| if mem_ports is None: |
| mem.port = bus.mem_side_ports |
| else: |
| mem_ports.append(mem.port) |
| |
| def _attach_device(self, device, bus, dma_ports=None): |
| if hasattr(device, "pio"): |
| device.pio = bus.mem_side_ports |
| if hasattr(device, "dma"): |
| if dma_ports is None: |
| device.dma = bus.cpu_side_ports |
| else: |
| dma_ports.append(device.dma) |
| |
| def _attach_io(self, devices, *args, **kwargs): |
| for d in devices: |
| self._attach_device(d, *args, **kwargs) |
| |
| def _attach_mem(self, memories, *args, **kwargs): |
| for mem in memories: |
| self._attach_memory(mem, *args, **kwargs) |
| |
| def _attach_clk(self, devices, clkdomain): |
| for d in devices: |
| if hasattr(d, "clk_domain"): |
| d.clk_domain = clkdomain |
| |
| def attachPciDevices(self): |
| pass |
| |
| def enableMSIX(self): |
| pass |
| |
| def onChipIOClkDomain(self, clkdomain): |
| self._attach_clk(self._on_chip_devices(), clkdomain) |
| |
| def offChipIOClkDomain(self, clkdomain): |
| self._attach_clk(self._off_chip_devices(), clkdomain) |
| |
| def attachOnChipIO(self, bus, bridge=None, dma_ports=None, mem_ports=None): |
| self._attach_mem(self._on_chip_memory(), bus, mem_ports) |
| self._attach_io(self._on_chip_devices(), bus, dma_ports) |
| if bridge: |
| bridge.ranges = self._off_chip_ranges |
| |
| def attachIO(self, bus, dma_ports=None, mem_ports=None): |
| self._attach_mem(self._off_chip_memory(), bus, mem_ports) |
| self._attach_io(self._off_chip_devices(), bus, dma_ports) |
| |
| def setupBootLoader(self, cur_sys, boot_loader, dtb_addr, load_offset): |
| cur_sys.workload.boot_loader = boot_loader |
| cur_sys.workload.load_addr_offset = load_offset |
| cur_sys.workload.dtb_addr = load_offset + dtb_addr |
| cur_sys.workload.cpu_release_addr = cur_sys.workload.dtb_addr - 8 |
| |
| def generateDeviceTree(self, state): |
| node = FdtNode("/") # Things in this module need to end up in the root |
| node.append(FdtPropertyWords("interrupt-parent", |
| state.phandle(self.gic))) |
| |
| for subnode in self.recurseDeviceTree(state): |
| node.append(subnode) |
| |
| yield node |
| |
| def annotateCpuDeviceNode(self, cpu, state): |
| system = self.system.unproxy(self) |
| if system._have_psci: |
| cpu.append(FdtPropertyStrings('enable-method', 'psci')) |
| else: |
| cpu.append(FdtPropertyStrings("enable-method", "spin-table")) |
| # The kernel writes the entry addres of secondary CPUs to this |
| # address before waking up secondary CPUs. |
| # The gem5 bootloader then makes secondary CPUs jump to it. |
| cpu.append(FdtPropertyWords("cpu-release-addr", \ |
| state.addrCells(system.workload.cpu_release_addr))) |
| |
| class VExpress_EMM(RealView): |
| _mem_regions = [ AddrRange('2GiB', size='2GiB') ] |
| |
| # Ranges based on excluding what is part of on-chip I/O (gic, |
| # a9scu) |
| _off_chip_ranges = [AddrRange(0x2F000000, size='16MiB'), |
| AddrRange(0x30000000, size='256MiB'), |
| AddrRange(0x40000000, size='512MiB'), |
| AddrRange(0x18000000, size='64MiB'), |
| AddrRange(0x1C000000, size='64MiB')] |
| |
| # Platform control device (off-chip) |
| realview_io = RealViewCtrl(proc_id0=0x14000000, proc_id1=0x14000000, |
| idreg=0x02250000, pio_addr=0x1C010000) |
| |
| mcc = VExpressMCC() |
| dcc = CoreTile2A15DCC() |
| |
| ### On-chip devices ### |
| gic = Gic400(dist_addr=0x2C001000, cpu_addr=0x2C002000) |
| vgic = VGic(vcpu_addr=0x2c006000, hv_addr=0x2c004000, maint_int=25) |
| |
| local_cpu_timer = CpuLocalTimer(int_timer=ArmPPI(num=29), |
| int_watchdog=ArmPPI(num=30), |
| pio_addr=0x2C080000) |
| |
| hdlcd = HDLcd(pxl_clk=dcc.osc_pxl, |
| pio_addr=0x2b000000, interrupt=ArmSPI(num=117), |
| workaround_swap_rb=True) |
| |
| def _on_chip_devices(self): |
| devices = [ |
| self.gic, self.vgic, |
| self.local_cpu_timer |
| ] |
| if hasattr(self, "gicv2m"): |
| devices.append(self.gicv2m) |
| devices.append(self.hdlcd) |
| return devices |
| |
| def _on_chip_memory(self): |
| memories = [ |
| self.bootmem, |
| ] |
| return memories |
| |
| ### Off-chip devices ### |
| uart = Pl011(pio_addr=0x1c090000, interrupt=ArmSPI(num=37)) |
| pci_host = GenericPciHost( |
| conf_base=0x30000000, conf_size='256MiB', conf_device_bits=16, |
| pci_pio_base=0) |
| |
| sys_counter = SystemCounter() |
| generic_timer = GenericTimer( |
| int_phys_s=ArmPPI(num=29, int_type='IRQ_TYPE_LEVEL_LOW'), |
| int_phys_ns=ArmPPI(num=30, int_type='IRQ_TYPE_LEVEL_LOW'), |
| int_virt=ArmPPI(num=27, int_type='IRQ_TYPE_LEVEL_LOW'), |
| int_hyp=ArmPPI(num=26, int_type='IRQ_TYPE_LEVEL_LOW')) |
| |
| timer0 = Sp804(int0=ArmSPI(num=34), int1=ArmSPI(num=34), |
| pio_addr=0x1C110000, clock0='1MHz', clock1='1MHz') |
| timer1 = Sp804(int0=ArmSPI(num=35), int1=ArmSPI(num=35), |
| pio_addr=0x1C120000, clock0='1MHz', clock1='1MHz') |
| clcd = Pl111(pio_addr=0x1c1f0000, interrupt=ArmSPI(num=46)) |
| kmi0 = Pl050(pio_addr=0x1c060000, interrupt=ArmSPI(num=44), |
| ps2=PS2Keyboard()) |
| kmi1 = Pl050(pio_addr=0x1c070000, interrupt=ArmSPI(num=45), |
| ps2=PS2TouchKit()) |
| cf_ctrl = IdeController(disks=[], pci_func=0, pci_dev=0, pci_bus=2, |
| io_shift = 2, ctrl_offset = 2, Command = 0x1) |
| cf_ctrl.BAR0 = PciLegacyIoBar(addr='0x1C1A0000', size='256B') |
| cf_ctrl.BAR1 = PciLegacyIoBar(addr='0x1C1A0100', size='4096B') |
| |
| bootmem = SimpleMemory(range = AddrRange('64MiB'), |
| conf_table_reported = False) |
| vram = SimpleMemory(range = AddrRange(0x18000000, size='32MiB'), |
| conf_table_reported = False) |
| rtc = PL031(pio_addr=0x1C170000, interrupt=ArmSPI(num=36)) |
| |
| l2x0_fake = IsaFake(pio_addr=0x2C100000, pio_size=0xfff) |
| uart1_fake = AmbaFake(pio_addr=0x1C0A0000) |
| uart2_fake = AmbaFake(pio_addr=0x1C0B0000) |
| uart3_fake = AmbaFake(pio_addr=0x1C0C0000) |
| sp810_fake = AmbaFake(pio_addr=0x1C020000, ignore_access=True) |
| watchdog_fake = AmbaFake(pio_addr=0x1C0F0000) |
| aaci_fake = AmbaFake(pio_addr=0x1C040000) |
| lan_fake = IsaFake(pio_addr=0x1A000000, pio_size=0xffff) |
| usb_fake = IsaFake(pio_addr=0x1B000000, pio_size=0x1ffff) |
| mmc_fake = AmbaFake(pio_addr=0x1c050000) |
| energy_ctrl = EnergyCtrl(pio_addr=0x1c080000) |
| |
| def _off_chip_devices(self): |
| devices = [ |
| self.uart, |
| self.realview_io, |
| self.pci_host, |
| self.timer0, |
| self.timer1, |
| self.clcd, |
| self.kmi0, |
| self.kmi1, |
| self.cf_ctrl, |
| self.rtc, |
| self.vram, |
| self.l2x0_fake, |
| self.uart1_fake, |
| self.uart2_fake, |
| self.uart3_fake, |
| self.sp810_fake, |
| self.watchdog_fake, |
| self.aaci_fake, |
| self.lan_fake, |
| self.usb_fake, |
| self.mmc_fake, |
| self.energy_ctrl, |
| ] |
| # Try to attach the I/O if it exists |
| if hasattr(self, "ide"): |
| devices.append(self.ide) |
| if hasattr(self, "ethernet"): |
| devices.append(self.ethernet) |
| return devices |
| |
| # Attach any PCI devices that are supported |
| def attachPciDevices(self): |
| self.ethernet = IGbE_e1000(pci_bus=0, pci_dev=0, pci_func=0, |
| InterruptLine=1, InterruptPin=1) |
| self.ide = IdeController(disks = [], pci_bus=0, pci_dev=1, pci_func=0, |
| InterruptLine=2, InterruptPin=2) |
| |
| def enableMSIX(self): |
| self.gic = Gic400(dist_addr=0x2C001000, cpu_addr=0x2C002000, |
| it_lines=512) |
| self.gicv2m = Gicv2m() |
| self.gicv2m.frames = [Gicv2mFrame(spi_base=256, spi_len=64, addr=0x2C1C0000)] |
| |
| def setupBootLoader(self, cur_sys, loc, boot_loader=None): |
| if boot_loader is None: |
| boot_loader = loc('boot_emm.arm') |
| super().setupBootLoader(cur_sys, boot_loader, 0x8000000, 0x80000000) |
| |
| class VExpress_EMM64(VExpress_EMM): |
| # Three memory regions are specified totalling 512GiB |
| _mem_regions = [ AddrRange('2GiB', size='2GiB'), |
| AddrRange('34GiB', size='30GiB'), |
| AddrRange('512GiB', size='480GiB') ] |
| pci_host = GenericPciHost( |
| conf_base=0x30000000, conf_size='256MiB', conf_device_bits=12, |
| pci_pio_base=0x2f000000) |
| |
| def setupBootLoader(self, cur_sys, loc, boot_loader=None): |
| if boot_loader is None: |
| boot_loader = loc('boot_emm.arm64') |
| RealView.setupBootLoader(self, cur_sys, boot_loader, |
| 0x8000000, 0x80000000) |
| |
| class VExpress_GEM5_Base(RealView): |
| """ |
| The VExpress gem5 memory map is loosely based on a modified |
| Versatile Express RS1 memory map. |
| |
| The gem5 platform has been designed to implement a subset of the |
| original Versatile Express RS1 memory map. Off-chip peripherals should, |
| when possible, adhere to the Versatile Express memory map. Non-PCI |
| off-chip devices that are gem5-specific should live in the CS5 memory |
| space to avoid conflicts with existing devices that we might want to |
| model in the future. Such devices should normally have interrupts in |
| the gem5-specific SPI range. |
| |
| On-chip peripherals are loosely modeled after the ARM CoreTile Express |
| A15x2 memory and interrupt map. In particular, the GIC and |
| Generic Timer have the same interrupt lines and base addresses. Other |
| on-chip devices are gem5 specific. |
| |
| Unlike the original Versatile Express RS2 extended platform, gem5 implements a |
| large contigious DRAM space, without aliases or holes, starting at the |
| 2GiB boundary. This means that PCI memory is limited to 1GiB. |
| |
| References: |
| |
| Technical Reference Manuals: |
| Arm Motherboard Express uATX (V2M-P1) - ARM DUI 0447J |
| Arm CoreTile Express A15x2 (V2P-CA15) - ARM DUI 0604E |
| |
| Official Linux device tree specifications: |
| V2M-P1 - arch/arm/boot/dts/vexpress-v2m-rs1.dtsi |
| V2P-CA15 - arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts |
| |
| Memory map: |
| Arm CoreTile Express A15x2 (V2P-CA15) - ARM DUI 0604E |
| Daughterboard (global) |
| Section 3.2.1 - Table 3-1 - Daughterboard memory map |
| On-chip |
| Section 3.2.3 - Table 3-2 - Cortex-A15 MPCore on-chip peripheral |
| memory map |
| |
| Interrupts: |
| Arm CoreTile Express A15x2 (V2P-CA15) - ARM DUI 0604E |
| Section 2.8.2 - Test chip interrupts |
| |
| Memory map: |
| 0x00000000-0x03ffffff: Boot memory (CS0) |
| 0x04000000-0x07ffffff: Trusted Memory/Reserved |
| 0x04000000-0x0403FFFF: 256kB Trusted SRAM |
| 0x06000000-0x07ffffff: 32MB Trusted DRAM |
| 0x08000000-0x0bffffff: NOR FLASH0 (CS0 alias) |
| 0x0c000000-0x0fffffff: NOR FLASH1 (Off-chip, CS4) |
| 0x10000000-0x13ffffff: gem5-specific peripherals (Off-chip, CS5) |
| 0x10000000-0x1000ffff: gem5 energy controller |
| 0x10010000-0x1001ffff: gem5 pseudo-ops |
| 0x10020000-0x1002ffff: gem5 MHU |
| |
| 0x14000000-0x17ffffff: Reserved (Off-chip, PSRAM, CS1) |
| |
| 0x18000000-0x1bffffff: Off-chip, Peripherals, CS2 |
| 0x18000000-0x19ffffff: VRAM |
| 0x1a000000-0x1bffffff: Reserved |
| |
| 0x1c000000-0x1fffffff: Peripheral block 1 (Off-chip, CS3): |
| 0x1c010000-0x1c01ffff: realview_io (VE system control regs.) |
| 0x1c060000-0x1c06ffff: KMI0 (keyboard) |
| 0x1c070000-0x1c07ffff: KMI1 (mouse) |
| 0x1c090000-0x1c09ffff: UART0 |
| 0x1c0a0000-0x1c0affff: UART1 |
| 0x1c0b0000-0x1c0bffff: UART2 |
| 0x1c0c0000-0x1c0cffff: UART3 |
| 0x1c0f0000-0x1c0fffff: Watchdog (SP805) |
| 0x1c130000-0x1c13ffff: VirtIO (gem5/FM extension) |
| 0x1c140000-0x1c14ffff: VirtIO (gem5/FM extension) |
| 0x1c170000-0x1c17ffff: RTC |
| |
| 0x20000000-0x3fffffff: On-chip peripherals: |
| 0x2a430000-0x2a43ffff: System Counter (control) |
| 0x2a490000-0x2a49ffff: Trusted Watchdog (SP805) |
| 0x2a800000-0x2a800fff: System Counter (read) |
| 0x2a810000-0x2a810fff: System Timer (control) |
| |
| 0x2a820000-0x2a820fff: System Timer (frame 0) |
| 0x2a830000-0x2a830fff: System Timer (frame 1) |
| |
| 0x2b000000-0x2b00ffff: HDLCD |
| |
| 0x2b060000-0x2b060fff: System Watchdog (SP805) |
| |
| 0x2b400000-0x2b41ffff: SMMUv3 |
| |
| 0x2c001000-0x2c001fff: GIC (distributor) |
| 0x2c002000-0x2c003fff: GIC (CPU interface) |
| 0x2c004000-0x2c005fff: vGIC (HV) |
| 0x2c006000-0x2c007fff: vGIC (VCPU) |
| 0x2c1c0000-0x2c1cffff: GICv2m MSI frame 0 |
| |
| 0x2d000000-0x2d00ffff: GPU (reserved) |
| |
| 0x2e000000-0x2e007fff: Non-trusted SRAM |
| |
| 0x2f000000-0x2fffffff: PCI IO space |
| 0x30000000-0x3fffffff: PCI config space |
| |
| 0x40000000-0x7fffffff: Ext. AXI: Used as PCI memory |
| |
| 0x80000000-X: DRAM |
| |
| Interrupts: |
| 0- 15: Software generated interrupts (SGIs) |
| 16- 31: On-chip private peripherals (PPIs) |
| 25 : vgic |
| 26 : generic_timer (hyp) |
| 27 : generic_timer (virt) |
| 28 : Reserved (Legacy FIQ) |
| 29 : generic_timer (phys, sec) |
| 30 : generic_timer (phys, non-sec) |
| 31 : Reserved (Legacy IRQ) |
| 32- 95: Mother board peripherals (SPIs) |
| 32 : Watchdog (SP805) |
| 33 : Reserved (IOFPGA SW int) |
| 34-35: Reserved (SP804) |
| 36 : RTC |
| 37-40: uart0-uart3 |
| 41-42: Reserved (PL180) |
| 43 : Reserved (AACI) |
| 44-45: kmi0-kmi1 |
| 46 : Reserved (CLCD) |
| 47 : Reserved (Ethernet) |
| 48 : Reserved (USB) |
| 56 : Trusted Watchdog (SP805) |
| 57 : System timer0 (phys) |
| 58 : System timer1 (phys) |
| 95-255: On-chip interrupt sources (we use these for |
| gem5-specific devices, SPIs) |
| 74 : VirtIO (gem5/FM extension) |
| 75 : VirtIO (gem5/FM extension) |
| 95 : HDLCD |
| 96- 98: GPU (reserved) |
| 100-103: PCI |
| 130 : System Watchdog (SP805) |
| 256-319: MSI frame 0 (gem5-specific, SPIs) |
| 320-511: Unused |
| |
| """ |
| |
| # Everything above 2GiB is memory |
| _mem_regions = [ AddrRange('2GiB', size='510GiB') ] |
| |
| _off_chip_ranges = [ |
| # CS1-CS5 |
| AddrRange(0x0c000000, 0x20000000), |
| # External AXI interface (PCI) |
| AddrRange(0x2f000000, 0x80000000), |
| ] |
| |
| bootmem = SimpleMemory(range=AddrRange(0, size='64MiB'), |
| conf_table_reported=False) |
| |
| # NOR flash, flash0 |
| flash0 = SimpleMemory(range=AddrRange(0x08000000, size='64MiB'), |
| conf_table_reported=False) |
| |
| # Trusted SRAM |
| trusted_sram = SimpleMemory(range=AddrRange(0x04000000, size='256KiB'), |
| conf_table_reported=False) |
| # Trusted DRAM |
| # TODO: preventing access from unsecure world to the trusted RAM |
| trusted_dram=SimpleMemory(range=AddrRange(0x06000000, size='32MB'), |
| conf_table_reported=False) |
| # Non-Trusted SRAM |
| non_trusted_sram = MmioSRAM(range=AddrRange(0x2e000000, size=0x8000), |
| conf_table_reported=False) |
| |
| # Platform control device (off-chip) |
| realview_io = RealViewCtrl(proc_id0=0x14000000, proc_id1=0x14000000, |
| idreg=0x30101100, pio_addr=0x1c010000) |
| mcc = VExpressMCC() |
| dcc = CoreTile2A15DCC() |
| |
| ### On-chip devices ### |
| |
| el2_watchdog = GenericWatchdog( |
| control_start=0x2a440000, |
| refresh_start=0x2a450000, |
| ws0=ArmSPI(num=59), ws1=ArmSPI(num=60)) |
| |
| # Trusted Watchdog, SP805 |
| trusted_watchdog = Sp805(pio_addr=0x2a490000, interrupt=ArmSPI(num=56)) |
| |
| sys_counter = SystemCounter() |
| generic_timer = GenericTimer( |
| int_phys_s=ArmPPI(num=29, int_type='IRQ_TYPE_LEVEL_LOW'), |
| int_phys_ns=ArmPPI(num=30, int_type='IRQ_TYPE_LEVEL_LOW'), |
| int_virt=ArmPPI(num=27, int_type='IRQ_TYPE_LEVEL_LOW'), |
| int_hyp=ArmPPI(num=26, int_type='IRQ_TYPE_LEVEL_LOW')) |
| generic_timer_mem = GenericTimerMem(cnt_control_base=0x2a430000, |
| cnt_read_base=0x2a800000, |
| cnt_ctl_base=0x2a810000, |
| frames=[ |
| GenericTimerFrame(cnt_base=0x2a820000, |
| int_phys=ArmSPI(num=57), int_virt=ArmSPI(num=133)), |
| GenericTimerFrame(cnt_base=0x2a830000, |
| int_phys=ArmSPI(num=58), int_virt=ArmSPI(num=134)) |
| ]) |
| |
| system_watchdog = Sp805(pio_addr=0x2b060000, interrupt=ArmSPI(num=130)) |
| |
| def _on_chip_devices(self): |
| return [ |
| self.generic_timer_mem, |
| self.el2_watchdog, |
| self.trusted_watchdog, |
| self.system_watchdog |
| ] + self.generic_timer_mem.frames |
| |
| def _on_chip_memory(self): |
| memories = [ |
| self.bootmem, |
| self.trusted_sram, |
| self.trusted_dram, |
| self.non_trusted_sram, |
| self.flash0, |
| ] |
| return memories |
| |
| ### Off-chip devices ### |
| io_voltage = VoltageDomain(voltage="3.3V") |
| clock32KHz = SrcClockDomain(clock="32kHz") |
| clock24MHz = SrcClockDomain(clock="24MHz") |
| |
| uart = [ |
| Pl011(pio_addr=0x1c090000, |
| interrupt=ArmSPI(num=37)), |
| Pl011(pio_addr=0x1c0a0000, |
| interrupt=ArmSPI(num=38), device=Terminal()), |
| Pl011(pio_addr=0x1c0b0000, |
| interrupt=ArmSPI(num=39), device=Terminal()), |
| Pl011(pio_addr=0x1c0c0000, |
| interrupt=ArmSPI(num=40), device=Terminal()) |
| ] |
| |
| kmi0 = Pl050(pio_addr=0x1c060000, interrupt=ArmSPI(num=44), |
| ps2=PS2Keyboard()) |
| kmi1 = Pl050(pio_addr=0x1c070000, interrupt=ArmSPI(num=45), |
| ps2=PS2TouchKit()) |
| |
| watchdog = Sp805(pio_addr=0x1c0f0000, interrupt=ArmSPI(num=32)) |
| |
| rtc = PL031(pio_addr=0x1C170000, interrupt=ArmSPI(num=36)) |
| |
| ### gem5-specific off-chip devices ### |
| pci_host = GenericArmPciHost( |
| conf_base=0x30000000, conf_size='256MiB', conf_device_bits=12, |
| pci_pio_base=0x2f000000, |
| pci_mem_base=0x40000000, |
| int_policy="ARM_PCI_INT_DEV", int_base=100, int_count=4) |
| |
| energy_ctrl = EnergyCtrl(pio_addr=0x10000000) |
| |
| pwr_ctrl = FVPBasePwrCtrl(pio_addr=0x1c100000) |
| |
| vio = [ |
| MmioVirtIO(pio_addr=0x1c130000, pio_size=0x1000, |
| interrupt=ArmSPI(num=74)), |
| MmioVirtIO(pio_addr=0x1c140000, pio_size=0x1000, |
| interrupt=ArmSPI(num=75)), |
| ] |
| |
| # NOR flash, flash1 |
| flash1 = CfiMemory(range=AddrRange(0x0c000000, 0x10000000), |
| conf_table_reported=False) |
| |
| # VRAM |
| vram = SimpleMemory(range=AddrRange(0x18000000, size='32MB'), |
| conf_table_reported=False) |
| |
| def _off_chip_devices(self): |
| return [ |
| self.realview_io, |
| self.kmi0, |
| self.kmi1, |
| self.watchdog, |
| self.rtc, |
| self.pci_host, |
| self.energy_ctrl, |
| self.pwr_ctrl, |
| self.clock32KHz, |
| self.clock24MHz, |
| self.vio[0], |
| self.vio[1], |
| ] + self.uart |
| |
| def _off_chip_memory(self): |
| return [ |
| self.flash1, |
| self.vram, |
| ] |
| |
| def __init__(self, **kwargs): |
| super().__init__(**kwargs) |
| self.clock32KHz.voltage_domain = self.io_voltage |
| self.clock24MHz.voltage_domain = self.io_voltage |
| self.system_watchdog.clk_domain = self.dcc.osc_sys |
| self.watchdog.clk_domain = self.clock32KHz |
| |
| def attachPciDevice(self, device, *args, **kwargs): |
| device.host = self.pci_host |
| self._num_pci_dev += 1 |
| device.pci_bus = 0 |
| device.pci_dev = self._num_pci_dev |
| device.pci_func = 0 |
| self._attach_device(device, *args, **kwargs) |
| |
| def attachSmmu(self, devices, bus): |
| """ |
| Instantiate a single SMMU and attach a group of client devices to it. |
| The devices' dma port is wired to the SMMU and the SMMU's dma port |
| is attached to the bus. In order to make it work, the list of clients |
| shouldn't contain any device part of the _off_chip_devices or |
| _on_chip_devices. |
| This method should be called only once. |
| |
| Parameters: |
| devices (list): List of devices which will be using the SMMU |
| bus (Bus): The bus downstream of the SMMU. Its response port will |
| receive memory requests from the SMMU, and its request |
| port will forward accesses to the memory mapped devices |
| """ |
| if hasattr(self, 'smmu'): |
| m5.fatal("A SMMU has already been instantiated\n") |
| |
| self.smmu = SMMUv3(reg_map=AddrRange(0x2b400000, size=0x00020000)) |
| |
| self.smmu.request = bus.cpu_side_ports |
| self.smmu.control = bus.mem_side_ports |
| |
| dma_ports = [] |
| for dev in devices: |
| self._attach_device(dev, bus, dma_ports) |
| self.smmu.connect(dev) |
| |
| def setupBootLoader(self, cur_sys, boot_loader): |
| super().setupBootLoader(cur_sys, boot_loader, 0x8000000, 0x80000000) |
| |
| # Setup m5ops. It's technically not a part of the boot |
| # loader, but this is the only place we can configure the |
| # system. |
| cur_sys.m5ops_base = 0x10010000 |
| |
| def attachScmi(self, bus): |
| # Generate and attach the mailbox |
| self.mailbox = GenericMHU(pio_addr=0x10020000) |
| self._attach_device(self.mailbox, bus) |
| |
| # Generate and attach the SCMI platform |
| _scmi_comm = ScmiCommunication( |
| agent_channel = ScmiAgentChannel( |
| shmem=self.non_trusted_sram, |
| shmem_range=AddrRange(0x2e000000, size=0x200), |
| doorbell=self.mailbox.highp_ap2scp), |
| platform_channel = ScmiPlatformChannel( |
| shmem=self.non_trusted_sram, |
| shmem_range=AddrRange(0x2e000000, size=0x200), |
| doorbell=self.mailbox.highp_scp2ap)) |
| |
| self.scmi = ScmiPlatform(comms=[ _scmi_comm ]) |
| self._attach_device(self.scmi, bus) |
| |
| def generateDeviceTree(self, state): |
| # Generate using standard RealView function |
| dt = list(super().generateDeviceTree(state)) |
| if len(dt) > 1: |
| raise Exception("System returned too many DT nodes") |
| node = dt[0] |
| |
| node.appendCompatible(["arm,vexpress"]) |
| node.append(FdtPropertyStrings("model", ["V2P-CA15"])) |
| node.append(FdtPropertyWords("arm,hbi", [0x0])) |
| node.append(FdtPropertyWords("arm,vexpress,site", [0xf])) |
| |
| system = self.system.unproxy(self) |
| if system._have_psci: |
| # PSCI functions exposed to the kernel |
| if not system.release.has(ArmExtension('SECURITY')): |
| raise AssertionError("PSCI requires EL3 (have_security)") |
| |
| psci_node = FdtNode('psci') |
| psci_node.appendCompatible(['arm,psci-1.0', 'arm,psci-0.2', |
| 'arm,psci']) |
| method = 'smc' |
| psci_node.append(FdtPropertyStrings('method', method)) |
| psci_node.append(FdtPropertyWords('cpu_suspend', 0xc4000001)) |
| psci_node.append(FdtPropertyWords('cpu_off', 0x84000002)) |
| psci_node.append(FdtPropertyWords('cpu_on', 0xc4000003)) |
| psci_node.append(FdtPropertyWords('sys_poweroff', 0x84000008)) |
| psci_node.append(FdtPropertyWords('sys_reset', 0x84000009)) |
| node.append(psci_node) |
| |
| yield node |
| |
| class VExpress_GEM5_V1_Base(VExpress_GEM5_Base): |
| gic = kvm_gicv2_class(dist_addr=0x2c001000, cpu_addr=0x2c002000, |
| it_lines=512) |
| vgic = VGic(vcpu_addr=0x2c006000, hv_addr=0x2c004000, maint_int=25) |
| gicv2m = Gicv2m() |
| gicv2m.frames = [ |
| Gicv2mFrame(spi_base=256, spi_len=64, addr=0x2c1c0000), |
| ] |
| |
| def setupBootLoader(self, cur_sys, loc, boot_loader=None): |
| if boot_loader is None: |
| boot_loader = [ loc('boot.arm64'), loc('boot.arm') ] |
| super().setupBootLoader(cur_sys, boot_loader) |
| |
| def _on_chip_devices(self): |
| return super()._on_chip_devices() + [ |
| self.gic, self.vgic, self.gicv2m, |
| ] |
| |
| class VExpress_GEM5_V1(VExpress_GEM5_V1_Base): |
| """ |
| We subclass VExpress_GEM5_V1_Base in order to alias it to |
| VExpress_GEM5_V1, which is what gem5 scripts are currently using |
| """ |
| pass |
| |
| class VExpress_GEM5_V1_HDLcd(VExpress_GEM5_V1_Base): |
| hdlcd = HDLcd(pxl_clk=VExpress_GEM5_V1_Base.dcc.osc_pxl, |
| pio_addr=0x2b000000, interrupt=ArmSPI(num=95)) |
| |
| def _on_chip_devices(self): |
| return super()._on_chip_devices() + [self.hdlcd,] |
| |
| class VExpress_GEM5_V2_Base(VExpress_GEM5_Base): |
| gic = Gicv3(dist_addr=0x2c000000, redist_addr=0x2c010000, |
| maint_int=ArmPPI(num=25), |
| its=Gicv3Its(pio_addr=0x2e010000)) |
| |
| # Limiting to 128 since it will otherwise overlap with PCI space |
| gic.cpu_max = 128 |
| |
| def _on_chip_devices(self): |
| return super()._on_chip_devices() + [self.gic, self.gic.its] |
| |
| def setupBootLoader(self, cur_sys, loc, boot_loader=None): |
| if boot_loader is None: |
| boot_loader = [ loc('boot_v2.arm64') ] |
| super().setupBootLoader(cur_sys, boot_loader) |
| |
| class VExpress_GEM5_V2(VExpress_GEM5_V2_Base): |
| """ |
| We subclass VExpress_GEM5_V2_Base in order to alias it to |
| VExpress_GEM5_V2, which is what gem5 scripts are currently using |
| """ |
| pass |
| |
| class VExpress_GEM5_V2_HDLcd(VExpress_GEM5_V2_Base): |
| hdlcd = HDLcd(pxl_clk=VExpress_GEM5_V2_Base.dcc.osc_pxl, |
| pio_addr=0x2b000000, interrupt=ArmSPI(num=95)) |
| |
| def _on_chip_devices(self): |
| return super()._on_chip_devices() + [self.hdlcd,] |
| |
| class VExpress_GEM5_Foundation(VExpress_GEM5_Base): |
| """ |
| Based on Armv8-A FVP Foundation platform v11.8 |
| Reference for memory and interrupt map: |
| Armv8-A Foundation Platform - User Guide - Version 11.8 |
| Document ID: 100961_1180_00_en |
| |
| We are adding PCI capabilities to the Armv8-A FVP Foundation |
| Platform. We are enabling it by using the PCI memory map |
| of the Armv8-A FVP Base Platform: |
| Fast Models - Reference Manual - Version 11.8 |
| Document ID: 100964_1108_00_en |
| """ |
| _off_chip_ranges = [ |
| # CS1-CS5 |
| AddrRange(0x0c000000, 0x20000000), |
| # External AXI interface (PCI) |
| AddrRange(0x40000000, 0x80000000), |
| AddrRange(0x4000000000, 0x800000000), |
| ] |
| |
| sp810_fake = AmbaFake(pio_addr=0x1C020000, ignore_access=True) |
| |
| clcd = Pl111(pio_addr=0x1c1f0000, interrupt=ArmSPI(num=46)) |
| |
| gic = Gicv3(dist_addr=0x2f000000, redist_addr=0x2f100000, |
| maint_int=ArmPPI(num=25), gicv4=False, |
| its=NULL) |
| |
| pci_host = GenericArmPciHost( |
| conf_base=0x40000000, conf_size='256MiB', conf_device_bits=12, |
| pci_pio_base=0x50000000, |
| pci_mem_base=0x4000000000, |
| int_policy="ARM_PCI_INT_DEV", int_base=100, int_count=4) |
| |
| def _on_chip_devices(self): |
| return super()._on_chip_devices() + [self.gic] |
| |
| def _off_chip_devices(self): |
| return super()._off_chip_devices() + [self.clcd, self.sp810_fake,] |
| |
| def setupBootLoader(self, cur_sys, loc, boot_loader=None): |
| if boot_loader is None: |
| boot_loader = [ loc('boot_v2.arm64') ] |
| super().setupBootLoader(cur_sys, boot_loader) |