arch-riscv: Added DTB Generation Functionality to RISC-V FS

Changes:
1. RiscvBareMetal
The RiscvBareMetal class and API are preserved for backwards
compatibility, but the base class RiscvFSWorkload is removed
as it inherits from the Workload class. However, most needed
functionalities are already implemented in the KernelWorkload
class

2. RiscvLinux
The RiscvLinux class is added. A dtb filename can be specified
to be loaded to the corresponding memory address.

3. HiFive, Clint, Plic, Uart8250, VirtIOMMIO
Devicetree node generation function is added.

4. tlb, faults
Unnecessary includes of arch/riscv/fs_workload are removed.

Change-Id: Ia239b5614bd93d8e794330ead266f6121a4d13cb
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/42053
Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Ayaz Akram <yazakram@ucdavis.edu>
Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu>
This commit is contained in:
Peter Yuen
2021-03-03 15:30:24 +07:00
committed by Peter Yuen
parent bef4bf08f1
commit 39108150ab
15 changed files with 305 additions and 58 deletions

View File

@@ -40,6 +40,7 @@
import optparse
import sys
from os import path
import m5
from m5.defines import buildEnv
@@ -62,6 +63,43 @@ from common import ObjectList
from common.Caches import *
from common import Options
def generateMemNode(state, mem_range):
node = FdtNode("memory@%x" % int(mem_range.start))
node.append(FdtPropertyStrings("device_type", ["memory"]))
node.append(FdtPropertyWords("reg",
state.addrCells(mem_range.start) +
state.sizeCells(mem_range.size()) ))
return node
def generateDtb(system):
"""
Autogenerate DTB. Arguments are the folder where the DTB
will be stored, and the name of the DTB file.
"""
state = FdtState(addr_cells=2, size_cells=2, cpu_cells=1)
root = FdtNode('/')
root.append(state.addrCellsProperty())
root.append(state.sizeCellsProperty())
root.appendCompatible(["riscv-virtio"])
for mem_range in system.mem_ranges:
root.append(generateMemNode(state, mem_range))
sections = [*system.cpu, system.platform]
for section in sections:
for node in section.generateDeviceTree(state):
if node.get_name() == root.get_name():
root.merge(node)
else:
root.append(node)
fdt = Fdt()
fdt.add_rootnode(root)
fdt.writeDtsFile(path.join(m5.options.outdir, 'device.dts'))
fdt.writeDtbFile(path.join(m5.options.outdir, 'device.dtb'))
# ----------------------------- Add Options ---------------------------- #
parser = optparse.OptionParser()
Options.addCommonOptions(parser)
@@ -92,12 +130,12 @@ mdesc = SysConfig(disks=options.disk_image, rootdev=options.root_device,
system.mem_mode = mem_mode
system.mem_ranges = [AddrRange(start=0x80000000, size=mdesc.mem())]
system.workload = RiscvBareMetal()
system.workload = RiscvLinux()
system.iobus = IOXBar()
system.membus = MemBus()
system.system_port = system.membus.slave
system.system_port = system.membus.cpu_side_ports
system.intrctrl = IntrControl()
@@ -147,7 +185,7 @@ system.cpu_clk_domain = SrcClockDomain(clock = options.cpu_clock,
voltage_domain =
system.cpu_voltage_domain)
system.workload.bootloader = options.kernel
system.workload.object_file = options.kernel
# NOTE: Not yet tested
if options.script is not None:
@@ -161,12 +199,12 @@ system.cpu = [CPUClass(clk_domain=system.cpu_clk_domain, cpu_id=i)
if options.caches or options.l2cache:
# By default the IOCache runs at the system clock
system.iocache = IOCache(addr_ranges = system.mem_ranges)
system.iocache.cpu_side = system.iobus.master
system.iocache.mem_side = system.membus.slave
system.iocache.cpu_side = system.iobus.mem_side_ports
system.iocache.mem_side = system.membus.cpu_side_ports
elif not options.external_memory_system:
system.iobridge = Bridge(delay='50ns', ranges = system.mem_ranges)
system.iobridge.slave = system.iobus.master
system.iobridge.master = system.membus.slave
system.iobridge.cpu_side_ports = system.iobus.mem_side_ports
system.iobridge.mem_side_ports = system.membus.cpu_side_ports
# Sanity check
if options.simpoint_profile:
@@ -197,13 +235,27 @@ uncacheable_range = [
*system.platform._on_chip_ranges(),
*system.platform._off_chip_ranges()
]
pma_checker = PMAChecker(uncacheable=uncacheable_range)
# 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
for cpu in system.cpu:
cpu.mmu.pma_checker = pma_checker
cpu.mmu.pma_checker = PMAChecker(uncacheable=uncacheable_range)
# --------------------------- DTB Generation --------------------------- #
generateDtb(system)
system.workload.dtb_filename = path.join(m5.options.outdir, 'device.dtb')
# Default DTB address if bbl is bulit with --with-dts option
system.workload.dtb_addr = 0x87e00000
# Linux boot command flags
kernel_cmd = [
"console=ttyS0",
"root=/dev/vda",
"ro"
]
system.workload.command_line = " ".join(kernel_cmd)
# ---------------------------- Default Setup --------------------------- #

View File

@@ -30,22 +30,20 @@
from m5.params import *
from m5.objects.System import System
from m5.objects.Workload import Workload
from m5.objects.Workload import Workload, KernelWorkload
class RiscvFsWorkload(Workload):
type = 'RiscvFsWorkload'
cxx_class = 'RiscvISA::FsWorkload'
cxx_header = 'arch/riscv/fs_workload.hh'
abstract = True
bare_metal = Param.Bool(False, "Using Bare Metal Application?")
reset_vect = Param.Addr(0x0, 'Reset vector')
class RiscvBareMetal(RiscvFsWorkload):
class RiscvBareMetal(Workload):
type = 'RiscvBareMetal'
cxx_class = 'RiscvISA::BareMetal'
cxx_header = 'arch/riscv/bare_metal/fs_workload.hh'
bootloader = Param.String("File, that contains the bootloader code")
bare_metal = Param.Bool(True, "Using Bare Metal Application?")
reset_vect = Param.Addr(0x0, 'Reset vector')
bare_metal = True
class RiscvLinux(KernelWorkload):
type = 'RiscvLinux'
cxx_class = 'RiscvISA::FsLinux'
cxx_header = 'arch/riscv/linux/fs_workload.hh'
dtb_filename = Param.String("",
"File that contains the Device Tree Blob. Don't use DTB if empty.")
dtb_addr = Param.Addr(0x87e00000, "DTB address")

View File

@@ -58,6 +58,7 @@ if env['TARGET_ISA'] == 'riscv':
Source('linux/se_workload.cc')
Source('linux/linux.cc')
Source('linux/fs_workload.cc')
Source('bare_metal/fs_workload.cc')

View File

@@ -32,12 +32,14 @@
#include "arch/riscv/faults.hh"
#include "base/loader/object_file.hh"
#include "sim/system.hh"
#include "sim/workload.hh"
namespace RiscvISA
{
BareMetal::BareMetal(const Params &p) : RiscvISA::FsWorkload(p),
bootloader(Loader::createObjectFile(p.bootloader))
BareMetal::BareMetal(const Params &p) : Workload(p),
_isBareMetal(p.bare_metal), _resetVect(p.reset_vect),
bootloader(Loader::createObjectFile(p.bootloader))
{
fatal_if(!bootloader, "Could not load bootloader file %s.", p.bootloader);
_resetVect = bootloader->entryPoint();
@@ -52,7 +54,7 @@ BareMetal::~BareMetal()
void
BareMetal::initState()
{
RiscvISA::FsWorkload::initState();
Workload::initState();
for (auto *tc: system->threads) {
RiscvISA::Reset().invoke(tc);

View File

@@ -29,20 +29,24 @@
#ifndef __ARCH_RISCV_BARE_METAL_SYSTEM_HH__
#define __ARCH_RISCV_BARE_METAL_SYSTEM_HH__
#include "arch/riscv/fs_workload.hh"
#include "params/RiscvBareMetal.hh"
#include "sim/workload.hh"
namespace RiscvISA
{
class BareMetal : public RiscvISA::FsWorkload
class BareMetal : public Workload
{
protected:
// checker for bare metal application
bool _isBareMetal;
// entry point for simulation
Addr _resetVect;
Loader::ObjectFile *bootloader;
Loader::SymbolTable bootloaderSymtab;
public:
typedef RiscvBareMetalParams Params;
PARAMS(RiscvBareMetal);
BareMetal(const Params &p);
~BareMetal();
@@ -54,11 +58,20 @@ class BareMetal : public RiscvISA::FsWorkload
{
return bootloaderSymtab;
}
bool
insertSymbol(const Loader::Symbol &symbol) override
{
return bootloaderSymtab.insert(symbol);
}
// return reset vector
Addr resetVect() const { return _resetVect; }
// return bare metal checker
bool isBareMetal() const { return _isBareMetal; }
Addr getEntry() const override { return _resetVect; }
};
} // namespace RiscvISA

View File

@@ -31,7 +31,6 @@
#include "arch/riscv/faults.hh"
#include "arch/riscv/fs_workload.hh"
#include "arch/riscv/isa.hh"
#include "arch/riscv/registers.hh"
#include "arch/riscv/utility.hh"
@@ -40,6 +39,7 @@
#include "debug/Fault.hh"
#include "sim/debug.hh"
#include "sim/full_system.hh"
#include "sim/workload.hh"
namespace RiscvISA
{
@@ -155,8 +155,8 @@ void Reset::invoke(ThreadContext *tc, const StaticInstPtr &inst)
tc->setMiscReg(MISCREG_MCAUSE, 0);
// Advance the PC to the implementation-defined reset vector
auto workload = dynamic_cast<FsWorkload *>(tc->getSystemPtr()->workload);
PCState pc = workload->resetVect();
auto workload = dynamic_cast<Workload *>(tc->getSystemPtr()->workload);
PCState pc = workload->getEntry();
tc->pcState(pc);
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2021 Huawei International
* 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.
*/
#include "arch/riscv/linux/fs_workload.hh"
#include "arch/riscv/faults.hh"
#include "base/loader/dtb_file.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "sim/kernel_workload.hh"
#include "sim/system.hh"
namespace RiscvISA
{
void
FsLinux::initState()
{
KernelWorkload::initState();
if (params().dtb_filename != "") {
inform("Loading DTB file: %s at address %#x\n", params().dtb_filename,
params().dtb_addr);
auto *dtb_file = new ::Loader::DtbFile(params().dtb_filename);
if (!dtb_file->addBootCmdLine(
commandLine.c_str(), commandLine.size())) {
warn("couldn't append bootargs to DTB file: %s\n",
params().dtb_filename);
}
dtb_file->buildImage().offset(params().dtb_addr)
.write(system->physProxy);
delete dtb_file;
for (auto *tc: system->threads) {
tc->setIntReg(11, params().dtb_addr);
}
} else {
warn("No DTB file specified\n");
}
for (auto *tc: system->threads) {
RiscvISA::Reset().invoke(tc);
tc->activate();
}
}
} // namespace RiscvISA

View File

@@ -1,7 +1,6 @@
/*
* Copyright (c) 2002-2005 The Regents of The University of Michigan
* Copyright (c) 2007 MIPS Technologies, Inc.
* All rights reserved.
* Copyright (c) 2021 Huawei International
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -27,38 +26,24 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __ARCH_RISCV_FS_WORKLOAD_HH__
#define __ARCH_RISCV_FS_WORKLOAD_HH__
#ifndef __ARCH_RISCV_LINUX_SYSTEM_HH__
#define __ARCH_RISCV_LINUX_SYSTEM_HH__
#include "params/RiscvFsWorkload.hh"
#include "sim/sim_object.hh"
#include "sim/workload.hh"
#include "params/RiscvLinux.hh"
#include "sim/kernel_workload.hh"
namespace RiscvISA
{
class FsWorkload : public Workload
class FsLinux : public KernelWorkload
{
protected:
// checker for bare metal application
bool _isBareMetal;
// entry point for simulation
Addr _resetVect;
public:
FsWorkload(const RiscvFsWorkloadParams &p) : Workload(p),
_isBareMetal(p.bare_metal), _resetVect(p.reset_vect)
{}
PARAMS(RiscvLinux);
FsLinux(const Params &p) : KernelWorkload(p) {}
// return reset vector
Addr resetVect() const { return _resetVect; }
// return bare metal checker
bool isBareMetal() const { return _isBareMetal; }
Addr getEntry() const override { return _resetVect; }
void initState() override;
};
} // namespace RiscvISA
#endif // __ARCH_RISCV_FS_WORKLOAD_HH__
#endif // __ARCH_RISCV_LINUX_FS_WORKLOAD_HH__

View File

@@ -35,7 +35,6 @@
#include <vector>
#include "arch/riscv/faults.hh"
#include "arch/riscv/fs_workload.hh"
#include "arch/riscv/mmu.hh"
#include "arch/riscv/pagetable.hh"
#include "arch/riscv/pagetable_walker.hh"

View File

@@ -37,6 +37,7 @@ from m5.objects.Device import BasicPioDevice
from m5.objects.IntPin import IntSinkPin
from m5.params import *
from m5.proxy import *
from m5.util.fdthelper import *
class Clint(BasicPioDevice):
"""
@@ -51,3 +52,21 @@ class Clint(BasicPioDevice):
intrctrl = Param.IntrControl(Parent.any, "interrupt controller")
int_pin = IntSinkPin('Pin to receive RTC signal')
pio_size = Param.Addr(0xC000, "PIO Size")
def generateDeviceTree(self, state):
node = self.generateBasicPioDeviceNode(state, "clint", self.pio_addr,
self.pio_size)
cpus = self.system.unproxy(self).cpu
int_extended = list()
for cpu in cpus:
phandle = state.phandle(cpu)
int_extended.append(phandle)
int_extended.append(0x3)
int_extended.append(phandle)
int_extended.append(0x7)
node.append(FdtPropertyWords("interrupts-extended", int_extended))
node.appendCompatible(["riscv,clint0"])
yield node

View File

@@ -42,6 +42,7 @@ from m5.objects.Uart import Uart8250
from m5.objects.Terminal import Terminal
from m5.params import *
from m5.proxy import *
from m5.util.fdthelper import *
class HiFive(Platform):
"""HiFive Platform
@@ -111,6 +112,9 @@ class HiFive(Platform):
uart_int_id = Param.Int(0xa, "PLIC Uart interrupt ID")
terminal = Terminal()
# Dummy param for generating devicetree
cpu_count = Param.Int(0, "dummy")
def _on_chip_devices(self):
"""Returns a list of on-chip peripherals
"""
@@ -167,3 +171,39 @@ class HiFive(Platform):
"""
for device in self._off_chip_devices():
device.pio = bus.mem_side_ports
def generateDeviceTree(self, state):
cpus_node = FdtNode("cpus")
cpus_node.append(FdtPropertyWords("timebase-frequency", [10000000]))
yield cpus_node
node = FdtNode("soc")
local_state = FdtState(addr_cells=2, size_cells=2)
node.append(local_state.addrCellsProperty())
node.append(local_state.sizeCellsProperty())
node.append(FdtProperty("ranges"))
node.appendCompatible(["simple-bus"])
for subnode in self.recurseDeviceTree(local_state):
node.append(subnode)
yield node
def annotateCpuDeviceNode(self, cpu, state):
cpu.append(FdtPropertyStrings('mmu-type', 'riscv,sv48'))
cpu.append(FdtPropertyStrings('status', 'okay'))
cpu.append(FdtPropertyStrings('riscv,isa', 'rv64imafdcsu'))
cpu.appendCompatible(["riscv"])
int_node = FdtNode("interrupt-controller")
int_state = FdtState(interrupt_cells=1)
int_node.append(int_state.interruptCellsProperty())
int_node.append(FdtProperty("interrupt-controller"))
int_node.appendCompatible("riscv,cpu-intc")
cpus = self.system.unproxy(self).cpu
phandle = int_state.phandle(cpus[self.cpu_count])
self.cpu_count += 1
int_node.append(FdtPropertyWords("phandle", [phandle]))
cpu.append(int_node)

View File

@@ -36,6 +36,7 @@
from m5.objects.Device import BasicPioDevice
from m5.params import *
from m5.proxy import *
from m5.util.fdthelper import *
class Plic(BasicPioDevice):
"""
@@ -50,3 +51,30 @@ class Plic(BasicPioDevice):
intrctrl = Param.IntrControl(Parent.any, "interrupt controller")
pio_size = Param.Addr(0x4000000, "PIO Size")
n_src = Param.Int("Number of interrupt sources")
def generateDeviceTree(self, state):
node = self.generateBasicPioDeviceNode(state, "plic", self.pio_addr,
self.pio_size)
int_state = FdtState(addr_cells=0, interrupt_cells=1)
node.append(int_state.addrCellsProperty())
node.append(int_state.interruptCellsProperty())
phandle = int_state.phandle(self)
node.append(FdtPropertyWords("phandle", [phandle]))
node.append(FdtPropertyWords("riscv,ndev", [self.n_src - 1]))
cpus = self.system.unproxy(self).cpu
int_extended = list()
for cpu in cpus:
phandle = int_state.phandle(cpu)
int_extended.append(phandle)
int_extended.append(0xb)
int_extended.append(phandle)
int_extended.append(0x9)
node.append(FdtPropertyWords("interrupts-extended", int_extended))
node.append(FdtProperty("interrupt-controller"))
node.appendCompatible(["riscv,plic0"])
yield node

View File

@@ -36,6 +36,7 @@
from m5.objects.Device import BasicPioDevice
from m5.params import *
from m5.proxy import *
from m5.util.fdthelper import *
class PlicIntDevice(BasicPioDevice):
type = 'PlicIntDevice'
@@ -44,3 +45,13 @@ class PlicIntDevice(BasicPioDevice):
platform = Param.Platform(Parent.any, "Platform")
pio_size = Param.Addr("PIO Size")
interrupt_id = Param.Int("PLIC Interrupt ID")
def generatePlicDeviceNode(self, state, name):
node = self.generateBasicPioDeviceNode(state, name,
self.pio_addr, self.pio_size)
plic = self.platform.unproxy(self).plic
node.append(FdtPropertyWords("interrupts", [self.interrupt_id]))
node.append(FdtPropertyWords("interrupt-parent", state.phandle(plic)))
return node

View File

@@ -37,6 +37,7 @@
from m5.SimObject import SimObject
from m5.params import *
from m5.proxy import *
from m5.util.fdthelper import *
from m5.objects.PlicDevice import PlicIntDevice
from m5.objects.VirtIO import VirtIODummyDevice
@@ -45,3 +46,9 @@ class MmioVirtIO(PlicIntDevice):
type = 'MmioVirtIO'
cxx_header = 'dev/riscv/vio_mmio.hh'
vio = Param.VirtIODeviceBase(VirtIODummyDevice(), "VirtIO device")
def generateDeviceTree(self, state):
node = self.generatePlicDeviceNode(state, "virtio_mmio")
node.appendCompatible(["virtio,mmio"])
yield node

View File

@@ -38,6 +38,8 @@
from m5.params import *
from m5.proxy import *
from m5.util.fdthelper import *
from m5.defines import buildEnv
from m5.objects.Device import BasicPioDevice
from m5.objects.Serial import SerialDevice
@@ -61,3 +63,18 @@ class Uart8250(Uart):
type = 'Uart8250'
cxx_header = "dev/serial/uart8250.hh"
pio_size = Param.Addr(0x8, "Size of address range")
def generateDeviceTree(self, state):
if buildEnv['TARGET_ISA'] == "riscv":
node = self.generateBasicPioDeviceNode(
state, "uart", self.pio_addr, self.pio_size)
platform = self.platform.unproxy(self)
plic = platform.plic
node.append(
FdtPropertyWords("interrupts", [platform.uart_int_id]))
node.append(
FdtPropertyWords("clock-frequency", [0x384000]))
node.append(
FdtPropertyWords("interrupt-parent", state.phandle(plic)))
node.appendCompatible(["ns8250"])
yield node