| # Copyright (c) 2021 The Regents of the University of California. |
| # 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. |
| |
| import m5 |
| from m5.objects import * |
| from m5.util import convert |
| |
| ''' |
| This class creates a bare bones RISCV full system. |
| |
| The targeted system is based on SiFive FU540-C000. |
| Reference: |
| [1] https://sifive.cdn.prismic.io/sifive/b5e7a29c- |
| d3c2-44ea-85fb-acc1df282e21_FU540-C000-v1p3.pdf |
| ''' |
| |
| class RiscvSystem(System): |
| |
| def __init__(self, bbl, disk, cpu_type, num_cpus): |
| super(RiscvSystem, self).__init__() |
| |
| # Set up the clock domain and the voltage domain |
| self.clk_domain = SrcClockDomain() |
| self.clk_domain.clock = '3GHz' |
| self.clk_domain.voltage_domain = VoltageDomain() |
| |
| # DDR memory range starts from base address 0x80000000 |
| # based on [1] |
| self.mem_ranges = [AddrRange(start=0x80000000, size='1GB')] |
| |
| # Create the main memory bus |
| # This connects to main memory |
| self.membus = SystemXBar(width = 64) # 64-byte width |
| |
| # Set up the system port for functional access from the simulator |
| self.system_port = self.membus.cpu_side_ports |
| |
| # Create the CPUs for our system. |
| self.createCPU(cpu_type, num_cpus) |
| |
| # using RISCV bare metal as the base full system workload |
| self.workload = RiscvBareMetal() |
| |
| # this is user passed berkeley boot loader binary |
| # currently the Linux kernel payload is compiled into this |
| # as well |
| self.workload.bootloader = bbl |
| |
| # HiFive platform |
| # This is based on a HiFive RISCV board and has |
| # only a limited number of devices so far i.e. |
| # PLIC, CLINT, UART, VirtIOMMIO |
| self.platform = HiFive() |
| |
| # Next, create and intialize devices |
| # currently supported for RISCV |
| |
| # add a disk image |
| self.attachDisk(disk) |
| |
| # set up core and platform |
| # level interrupt controllers |
| self.setupIntrCtrl() |
| |
| # set up PMA checker |
| self.pmaChecker() |
| |
| # attach off and on chip IO |
| self.attachIO(self.membus) |
| |
| # Create the cache heirarchy for the system. |
| self.createCacheHierarchy() |
| |
| # Create the memory controller |
| self.createMemoryControllerDDR3() |
| |
| self.setupInterrupts() |
| |
| def createCPU(self, cpu_type, num_cpus): |
| if cpu_type == "atomic": |
| self.cpu = [AtomicSimpleCPU(cpu_id = i) |
| for i in range(num_cpus)] |
| self.mem_mode = 'atomic' |
| elif cpu_type == "simple": |
| self.cpu = [TimingSimpleCPU(cpu_id = i) |
| for i in range(num_cpus)] |
| self.mem_mode = 'timing' |
| else: |
| m5.fatal("No CPU type {}".format(cpu_type)) |
| |
| for cpu in self.cpu: |
| cpu.createThreads() |
| |
| |
| def createCacheHierarchy(self): |
| class L1Cache(Cache): |
| """Simple L1 Cache with default values""" |
| |
| assoc = 8 |
| size = '32kB' |
| tag_latency = 1 |
| data_latency = 1 |
| response_latency = 1 |
| mshrs = 16 |
| tgts_per_mshr = 20 |
| writeback_clean = True |
| |
| def __init__(self): |
| super(L1Cache, self).__init__() |
| |
| for cpu in self.cpu: |
| # Create a very simple cache hierarchy |
| |
| # Create an L1 instruction, data and mmu cache |
| cpu.icache = L1Cache() |
| cpu.dcache = L1Cache() |
| cpu.mmucache = L1Cache() |
| |
| # Connecting icache and dcache to memory bus and cpu |
| cpu.icache.mem_side = self.membus.cpu_side_ports |
| cpu.dcache.mem_side = self.membus.cpu_side_ports |
| |
| cpu.icache.cpu_side = cpu.icache_port |
| cpu.dcache.cpu_side = cpu.dcache_port |
| |
| # Need a new crossbar for mmucache |
| |
| cpu.mmucache.mmubus = L2XBar() |
| |
| cpu.mmucache.cpu_side = cpu.mmucache.mmubus.mem_side_ports |
| cpu.mmucache.mem_side = self.membus.cpu_side_ports |
| |
| # Connect the itb and dtb to mmucache |
| cpu.mmu.connectWalkerPorts( |
| cpu.mmucache.mmubus.cpu_side_ports, cpu.mmucache.mmubus.cpu_side_ports) |
| |
| |
| def setupInterrupts(self): |
| for cpu in self.cpu: |
| # create the interrupt controller CPU and connect to the membus |
| cpu.createInterruptController() |
| |
| |
| def createMemoryControllerDDR3(self): |
| self.mem_cntrls = [ |
| MemCtrl(dram = DDR3_1600_8x8(range = self.mem_ranges[0]), |
| port = self.membus.mem_side_ports) |
| ] |
| |
| def attachIO(self, membus): |
| self.iobus = IOXBar() |
| |
| self.bridge = Bridge(delay='50ns') |
| self.bridge.master = self.iobus.slave |
| self.bridge.slave = self.membus.master |
| self.bridge.ranges = self.platform._off_chip_ranges() |
| |
| # Connecting on chip and off chip IO to the mem |
| # and IO bus |
| self.platform.attachOnChipIO(self.membus) |
| self.platform.attachOffChipIO(self.iobus) |
| |
| def setupIntrCtrl(self): |
| # Set the frequency of RTC (real time clock) used by |
| # CLINT (core level interrupt controller). |
| # This frequency is 1MHz in SiFive's U54MC. |
| # Setting it to 100MHz for faster simulation (from riscv/fs_linux.py) |
| self.platform.rtc = RiscvRTC(frequency=Frequency("100MHz")) |
| |
| # RTC sends the clock signal to CLINT via an interrupt pin. |
| self.platform.clint.int_pin = self.platform.rtc.int_pin |
| |
| # Attach the PLIC (platform level interrupt controller) |
| # to the platform. This initializes the PLIC with |
| # interrupt sources coming from off chip devices |
| self.platform.attachPlic() |
| |
| def pmaChecker(self): |
| # From riscv/fs_linux.py |
| uncacheable_range = [ |
| *self.platform._on_chip_ranges(), |
| *self.platform._off_chip_ranges() |
| ] |
| # PMA (physical memory attribute) checker is a hardware structure |
| # that ensures that physical addresses follow the memory permissions |
| |
| # PMA checker can be defined at system-level (system.pma_checker) |
| # or MMU-level (system.cpu[0].mmu.pma_checker). It will be resolved |
| # by RiscvTLB's Parent.any proxy |
| |
| self.pma_checker = PMAChecker(uncacheable=uncacheable_range) |
| |
| def attachDisk(self, disk): |
| # VirtIOMMIO |
| image = CowDiskImage(child=RawDiskImage(read_only=True), read_only=False) |
| image.child.image_file = disk |
| # using reserved memory space |
| self.platform.disk = MmioVirtIO( |
| vio=VirtIOBlock(image=image), |
| interrupt_id=0x8, |
| pio_size = 4096, |
| pio_addr=0x10008000 |
| ) |