misc: update fs examples to use ubuntu 24.04 boot workloads
Change-Id: I7e16f69eff3a7ff0ab16c18e6d35e846d07ac829
This commit is contained in:
committed by
Bobby R. Bruce
parent
40ccb8b171
commit
630173a845
@@ -105,39 +105,36 @@ board = ArmBoard(
|
|||||||
release=release,
|
release=release,
|
||||||
platform=platform,
|
platform=platform,
|
||||||
)
|
)
|
||||||
# This is the command to run after the system has booted. The first `m5 exit`
|
|
||||||
# will stop the simulation so we can switch the CPU cores from KVM to timing
|
|
||||||
# and continue the simulation to run the echo command, sleep for a second,
|
|
||||||
# then, again, call `m5 exit` to terminate the simulation. After simulation
|
|
||||||
# has ended you may inspect `m5out/system.pc.com_1.device` to see the echo
|
|
||||||
# output.
|
|
||||||
command = (
|
|
||||||
"m5 --addr=0x10010000 exit;"
|
|
||||||
+ "echo 'This is running on Timing CPU cores.';"
|
|
||||||
+ "m5 exit;"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Here we set a full system workload. The "arm64-ubuntu-20.04-boot" boots
|
# Here we set a full system workload. The "arm-ubuntu-24.04-boot-with-systemd" boots
|
||||||
# Ubuntu 20.04. We use arm64-bootloader (boot.arm64) as the bootloader to use
|
# Ubuntu 24.04.
|
||||||
# ARM KVM.
|
workload = obtain_resource("arm-ubuntu-24.04-boot-with-systemd")
|
||||||
board.set_kernel_disk_workload(
|
board.set_workload(workload)
|
||||||
kernel=obtain_resource(
|
|
||||||
"arm64-linux-kernel-5.4.49", resource_version="1.0.0"
|
|
||||||
),
|
def exit_event_handler():
|
||||||
disk_image=obtain_resource(
|
print("First exit: kernel booted")
|
||||||
"arm64-ubuntu-20.04-img", resource_version="1.0.0"
|
yield False # gem5 is now executing systemd startup
|
||||||
),
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
bootloader=obtain_resource("arm64-bootloader", resource_version="1.0.0"),
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
readfile_contents=command,
|
# booted.
|
||||||
)
|
# Here we switch the CPU type to Timing.
|
||||||
# We define the system with the aforementioned system defined.
|
print("Switching to Timing CPU")
|
||||||
|
processor.switch()
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
simulator = Simulator(
|
simulator = Simulator(
|
||||||
board=board,
|
board=board,
|
||||||
on_exit_event={ExitEvent.EXIT: (func() for func in [processor.switch])},
|
on_exit_event={
|
||||||
|
# Here we want override the default behavior for the first m5 exit
|
||||||
|
# exit event.
|
||||||
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Once the system successfully boots, it encounters an
|
|
||||||
# `m5_exit instruction encountered`. We stop the simulation then. When the
|
|
||||||
# simulation has ended you may inspect `m5out/board.terminal` to see
|
|
||||||
# the stdout.
|
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
@@ -99,20 +99,32 @@ board = ArmBoard(
|
|||||||
platform=platform,
|
platform=platform,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Here we set a full system workload. The "arm64-ubuntu-20.04-boot" boots
|
# Here we set a full system workload. The "arm-ubuntu-24.04-boot-with-systemd" boots
|
||||||
# Ubuntu 20.04.
|
# Ubuntu 24.04.
|
||||||
|
workload = obtain_resource("arm-ubuntu-24.04-boot-with-systemd")
|
||||||
|
board.set_workload(workload)
|
||||||
|
|
||||||
board.set_workload(
|
|
||||||
obtain_resource("arm64-ubuntu-20.04-boot", resource_version="2.0.0")
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
|
simulator = Simulator(
|
||||||
|
board=board,
|
||||||
|
on_exit_event={
|
||||||
|
# Here we want override the default behavior for the first m5 exit
|
||||||
|
# exit event.
|
||||||
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# We define the system with the aforementioned system defined.
|
|
||||||
|
|
||||||
simulator = Simulator(board=board)
|
|
||||||
|
|
||||||
# Once the system successfully boots, it encounters an
|
|
||||||
# `m5_exit instruction encountered`. We stop the simulation then. When the
|
|
||||||
# simulation has ended you may inspect `m5out/board.terminal` to see
|
|
||||||
# the stdout.
|
|
||||||
|
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ from gem5.components.processors.cpu_types import CPUTypes
|
|||||||
from gem5.components.processors.simple_processor import SimpleProcessor
|
from gem5.components.processors.simple_processor import SimpleProcessor
|
||||||
from gem5.isas import ISA
|
from gem5.isas import ISA
|
||||||
from gem5.resources.resource import obtain_resource
|
from gem5.resources.resource import obtain_resource
|
||||||
|
from gem5.simulate.exit_event import ExitEvent
|
||||||
from gem5.simulate.simulator import Simulator
|
from gem5.simulate.simulator import Simulator
|
||||||
from gem5.utils.requires import requires
|
from gem5.utils.requires import requires
|
||||||
|
|
||||||
@@ -89,8 +90,29 @@ board = RiscvBoard(
|
|||||||
# instruction which stops the simulation. When the simulation has ended you may
|
# instruction which stops the simulation. When the simulation has ended you may
|
||||||
# inspect `m5out/system.pc.com_1.device` to see the stdout.
|
# inspect `m5out/system.pc.com_1.device` to see the stdout.
|
||||||
board.set_workload(
|
board.set_workload(
|
||||||
obtain_resource("riscv-ubuntu-20.04-boot", resource_version="3.0.0")
|
obtain_resource("riscv-ubuntu-24.04-boot", resource_version="1.0.0")
|
||||||
)
|
)
|
||||||
|
|
||||||
simulator = Simulator(board=board)
|
|
||||||
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
|
simulator = Simulator(
|
||||||
|
board=board,
|
||||||
|
on_exit_event={
|
||||||
|
# Here we want override the default behavior for the first m5 exit
|
||||||
|
# exit event.
|
||||||
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
|
},
|
||||||
|
)
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import argparse
|
|||||||
from gem5.isas import ISA
|
from gem5.isas import ISA
|
||||||
from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard
|
from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard
|
||||||
from gem5.resources.resource import obtain_resource
|
from gem5.resources.resource import obtain_resource
|
||||||
|
from gem5.simulate.exit_event import ExitEvent
|
||||||
from gem5.simulate.simulator import Simulator
|
from gem5.simulate.simulator import Simulator
|
||||||
from gem5.utils.requires import requires
|
from gem5.utils.requires import requires
|
||||||
|
|
||||||
@@ -76,12 +77,33 @@ board = RISCVMatchedBoard(
|
|||||||
# In the case where the `-i` flag is passed, we add the kernel argument
|
# In the case where the `-i` flag is passed, we add the kernel argument
|
||||||
# `init=/root/exit.sh`. This means the simulation will exit after the Linux
|
# `init=/root/exit.sh`. This means the simulation will exit after the Linux
|
||||||
# Kernel has booted.
|
# Kernel has booted.
|
||||||
workload = obtain_resource("riscv-ubuntu-20.04-boot", resource_version="3.0.0")
|
workload = obtain_resource("riscv-ubuntu-24.04-boot", resource_version="1.0.0")
|
||||||
kernel_args = board.get_default_kernel_args()
|
kernel_args = board.get_default_kernel_args()
|
||||||
if args.to_init:
|
if args.to_init:
|
||||||
kernel_args.append("init=/root/exit.sh")
|
kernel_args.append("init=/root/exit.sh")
|
||||||
workload.set_parameter("kernel_args", kernel_args)
|
workload.set_parameter("kernel_args", kernel_args)
|
||||||
board.set_workload(workload)
|
board.set_workload(workload)
|
||||||
|
|
||||||
simulator = Simulator(board=board)
|
|
||||||
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
|
simulator = Simulator(
|
||||||
|
board=board,
|
||||||
|
on_exit_event={
|
||||||
|
# Here we want override the default behavior for the first m5 exit
|
||||||
|
# exit event.
|
||||||
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
|
},
|
||||||
|
)
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ parser = argparse.ArgumentParser(
|
|||||||
description="An example configuration script to run the npb benchmarks."
|
description="An example configuration script to run the npb benchmarks."
|
||||||
)
|
)
|
||||||
|
|
||||||
npb_suite = obtain_resource("npb-benchmark-suite", resource_version="1.0.0")
|
npb_suite = obtain_resource(
|
||||||
|
"x86-ubuntu-24.04-npb-suite", resource_version="1.0.0"
|
||||||
|
)
|
||||||
# The only positional argument accepted is the benchmark name in this script.
|
# The only positional argument accepted is the benchmark name in this script.
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -109,22 +111,15 @@ parser.add_argument(
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
# The simulation may fail in the case of `mg` with class C as it uses 3.3 GiB
|
# The simulation may fail in the case of using size "c" and size "d" of the
|
||||||
# of memory (more information is available at https://arxiv.org/abs/2010.13216).
|
# benchmarks. This is because the X86Board is currently limited to 3 GB of
|
||||||
|
# memory.
|
||||||
# We warn the user here.
|
# We warn the user here.
|
||||||
|
|
||||||
if args.benchmark == "npb-mg-c":
|
if args.benchmark.endswith("c") or args.benchmark.endswith("d"):
|
||||||
warn(
|
warn(
|
||||||
"mg.C uses 3.3 GiB of memory. Currently we are simulating 3 GiB\
|
f"The X86Board is currently limited to 3 GB of memory. The benchmark "
|
||||||
of main memory in the system."
|
f"{args.benchmark} may fail to run."
|
||||||
)
|
|
||||||
|
|
||||||
# The simulation will fail in the case of `ft` with class C. We warn the user
|
|
||||||
# here.
|
|
||||||
elif args.benchmark == "npb-ft-c":
|
|
||||||
warn(
|
|
||||||
"There is not enough memory for ft.C. Currently we are\
|
|
||||||
simulating 3 GiB of main memory in the system."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Checking for the maximum number of instructions, if provided by the user.
|
# Checking for the maximum number of instructions, if provided by the user.
|
||||||
@@ -221,11 +216,25 @@ def handle_workend():
|
|||||||
yield True
|
yield True
|
||||||
|
|
||||||
|
|
||||||
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
simulator = Simulator(
|
simulator = Simulator(
|
||||||
board=board,
|
board=board,
|
||||||
on_exit_event={
|
on_exit_event={
|
||||||
ExitEvent.WORKBEGIN: handle_workbegin(),
|
ExitEvent.WORKBEGIN: handle_workbegin(),
|
||||||
ExitEvent.WORKEND: handle_workend(),
|
ExitEvent.WORKEND: handle_workend(),
|
||||||
|
ExitEvent.EXIT: exit_event_handler(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -104,35 +104,33 @@ board = X86Board(
|
|||||||
cache_hierarchy=cache_hierarchy,
|
cache_hierarchy=cache_hierarchy,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Here we set the Full System workload.
|
workload = obtain_resource("x86-ubuntu-24.04-boot-with-systemd")
|
||||||
# The `set_kernel_disk_workload` function for the X86Board takes a kernel, a
|
|
||||||
# disk image, and, optionally, a command to run.
|
|
||||||
|
|
||||||
# This is the command to run after the system has booted. The first `m5 exit`
|
|
||||||
# will stop the simulation so we can switch the CPU cores from KVM to timing
|
|
||||||
# and continue the simulation to run the echo command, sleep for a second,
|
|
||||||
# then, again, call `m5 exit` to terminate the simulation. After simulation
|
|
||||||
# has ended you may inspect `m5out/system.pc.com_1.device` to see the echo
|
|
||||||
# output.
|
|
||||||
command = (
|
|
||||||
"m5 exit;"
|
|
||||||
+ "echo 'This is running on Timing CPU cores.';"
|
|
||||||
+ "sleep 1;"
|
|
||||||
+ "m5 exit;"
|
|
||||||
)
|
|
||||||
|
|
||||||
workload = obtain_resource("x86-ubuntu-18.04-boot", resource_version="2.0.0")
|
|
||||||
workload.set_parameter("readfile_contents", command)
|
|
||||||
board.set_workload(workload)
|
board.set_workload(workload)
|
||||||
|
|
||||||
|
|
||||||
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
# Here we switch the CPU type to Timing.
|
||||||
|
print("Switching to Timing CPU")
|
||||||
|
processor.switch()
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
simulator = Simulator(
|
simulator = Simulator(
|
||||||
board=board,
|
board=board,
|
||||||
on_exit_event={
|
on_exit_event={
|
||||||
# Here we want override the default behavior for the first m5 exit
|
# Here we want override the default behavior for the first m5 exit
|
||||||
# exit event. Instead of exiting the simulator, we just want to
|
# exit event.
|
||||||
# switch the processor. The 2nd m5 exit after will revert to using
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
# default behavior where the simulator run will exit.
|
|
||||||
ExitEvent.EXIT: (func() for func in [processor.switch])
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
@@ -100,35 +100,34 @@ board = X86Board(
|
|||||||
cache_hierarchy=cache_hierarchy,
|
cache_hierarchy=cache_hierarchy,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Here we set the Full System workload.
|
|
||||||
# The `set_kernel_disk_workload` function for the X86Board takes a kernel, a
|
|
||||||
# disk image, and, optionally, a command to run.
|
|
||||||
|
|
||||||
# This is the command to run after the system has booted. The first `m5 exit`
|
workload = obtain_resource("x86-ubuntu-24.04-boot-with-systemd")
|
||||||
# will stop the simulation so we can switch the CPU cores from KVM to timing
|
|
||||||
# and continue the simulation to run the echo command, sleep for a second,
|
|
||||||
# then, again, call `m5 exit` to terminate the simulation. After simulation
|
|
||||||
# has ended you may inspect `m5out/system.pc.com_1.device` to see the echo
|
|
||||||
# output.
|
|
||||||
command = (
|
|
||||||
"m5 exit;"
|
|
||||||
+ "echo 'This is running on Timing CPU cores.';"
|
|
||||||
+ "sleep 1;"
|
|
||||||
+ "m5 exit;"
|
|
||||||
)
|
|
||||||
|
|
||||||
workload = obtain_resource("x86-ubuntu-18.04-boot", resource_version="2.0.0")
|
|
||||||
workload.set_parameter("readfile_contents", command)
|
|
||||||
board.set_workload(workload)
|
board.set_workload(workload)
|
||||||
|
|
||||||
|
|
||||||
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
# Here we switch the CPU type to Timing.
|
||||||
|
print("Switching to Timing CPU")
|
||||||
|
processor.switch()
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
simulator = Simulator(
|
simulator = Simulator(
|
||||||
board=board,
|
board=board,
|
||||||
on_exit_event={
|
on_exit_event={
|
||||||
# Here we want override the default behavior for the first m5 exit
|
# Here we want override the default behavior for the first m5 exit
|
||||||
# exit event. Instead of exiting the simulator, we just want to
|
# exit event.
|
||||||
# switch the processor. The 2nd m5 exit after will revert to using
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
# default behavior where the simulator run will exit.
|
|
||||||
ExitEvent.EXIT: (func() for func in [processor.switch])
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2021 The Regents of the University of California
|
# Copyright (c) 2021-24 The Regents of the University of California
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
@@ -46,18 +46,37 @@ scons build/X86/gem5.opt
|
|||||||
|
|
||||||
from gem5.prebuilt.demo.x86_demo_board import X86DemoBoard
|
from gem5.prebuilt.demo.x86_demo_board import X86DemoBoard
|
||||||
from gem5.resources.resource import obtain_resource
|
from gem5.resources.resource import obtain_resource
|
||||||
|
from gem5.simulate.exit_event import ExitEvent
|
||||||
from gem5.simulate.simulator import Simulator
|
from gem5.simulate.simulator import Simulator
|
||||||
|
|
||||||
# Here we setup the board. The prebuilt X86DemoBoard allows for Full-System X86
|
# Here we setup the board. The prebuilt X86DemoBoard allows for Full-System X86
|
||||||
# simulation.
|
# simulation.
|
||||||
board = X86DemoBoard()
|
board = X86DemoBoard()
|
||||||
|
|
||||||
# We then set the workload. Here we use the "x86-ubuntu-18.04-boot" workload.
|
workload = obtain_resource("x86-ubuntu-24.04-boot-with-systemd")
|
||||||
# This boots Ubuntu 18.04 with Linux 5.4.49. If the required resources are not
|
board.set_workload(workload)
|
||||||
# found locally, they will be downloaded.
|
|
||||||
board.set_workload(
|
|
||||||
obtain_resource("x86-ubuntu-18.04-boot", resource_version="2.0.0")
|
def exit_event_handler():
|
||||||
|
print("First exit: kernel booted")
|
||||||
|
yield False # gem5 is now executing systemd startup
|
||||||
|
print("Second exit: Started `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script is executed after the kernel and systemd have
|
||||||
|
# booted.
|
||||||
|
yield False # gem5 is now executing the `after_boot.sh` script
|
||||||
|
print("Third exit: Finished `after_boot.sh` script")
|
||||||
|
# The after_boot.sh script will run a script if it is passed via
|
||||||
|
# m5 readfile. This is the last exit event before the simulation exits.
|
||||||
|
yield True
|
||||||
|
|
||||||
|
|
||||||
|
simulator = Simulator(
|
||||||
|
board=board,
|
||||||
|
on_exit_event={
|
||||||
|
# Here we want override the default behavior for the first m5 exit
|
||||||
|
# exit event.
|
||||||
|
ExitEvent.EXIT: exit_event_handler()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
simulator = Simulator(board=board)
|
|
||||||
simulator.run()
|
simulator.run()
|
||||||
|
|||||||
Reference in New Issue
Block a user