summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-03 14:05:38 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-03 14:05:38 -0400
commit32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5 (patch)
treed1cab6bcc35e461610cd5dfcf3f51ba679a576fc /debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py
parentfa71f1b53b524e5f6c6c1fbff1088d610b9f3236 (diff)
downloadopen-iov-32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5.tar.gz
open-iov-32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5.zip
vgsp: host CUDA bridge + sema release recovery; greeneye simulator wave
- kernel: gsp_cuda_bridge (QMD launch capture, debugfs job/out/done, param+output resolution cached from inline writes), sema drain fixes (never hold/starve a channel), GR ring autopin + counter autoreg, per-page push reads, counter-derive gate; module builds clean - tools/cuda-bridge: CUDA driver-API daemon executing mail/vadd kernels on the physical GPU, CUBB2/CBD2 v2 protocol, layout check + self-tests - tools/guest-diag + tools/cuda-ab: scanner/pin fixes, victory push-only mode - debug/risc-v-gsp-rm-vm: Phase S simulator wave (GSP device model, boot ladder, triage/re-demo harnesses, docs 30-41), GPGPU-sim scaffolding - STATE-CURRENT/docs updates
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py458
1 files changed, 458 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py b/debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py
new file mode 100644
index 0000000..8063ee9
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/tests/test_sim_devices.py
@@ -0,0 +1,458 @@
+"""Device-model tests: registry, per-block semantics, profile and boot ladder.
+
+Every modelled register assertion here is tied to the evidence documented in
+``sim/devices/*.py`` and ``docs/36-GSP-DEVICE-MODEL.md``. The real-firmware
+tests are bounded (<= 20k instructions) and assert *observed* rungs only.
+"""
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from sim.devices import ( # noqa: E402
+ CsbWindow,
+ DmaTranslator,
+ FalconBlock,
+ IrqRouter,
+ PmcBlock,
+ PmuStub,
+ PriscvBlock,
+ PtimerBlock,
+ Wpr2Block,
+ load_profile,
+ registry_from_profile,
+)
+from sim.devices.base import DeviceRegistry # noqa: E402
+from sim.devices import falcon as falcon_mod # noqa: E402
+from sim.machine import Machine # noqa: E402
+
+TOOL_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+DEFAULT_IMAGE = os.path.normpath(
+ os.path.join(TOOL_ROOT, "..", "..", "firmware", "57X-GSP", "570.144",
+ "gsp-570.144.fwimage.bin"))
+DEFAULT_PROFILE = os.path.join(TOOL_ROOT, "sim", "profiles", "tu104-570.144.json")
+
+
+class RegistryTests(unittest.TestCase):
+ def test_containment_and_unhandled_log(self):
+ reg = DeviceRegistry()
+ dev = PmcBlock(base=0x1000, size=0x1000)
+ reg.add(dev)
+ ok, value = reg.read(0x1A00, 4)
+ self.assertTrue(ok)
+ self.assertEqual(value, dev.boot_42)
+ # unmodelled offset inside the window: unhandled but window-matched
+ ok, _ = reg.read(0x1A04, 4)
+ self.assertFalse(ok)
+ self.assertEqual(reg.unhandled, 1)
+ # outside every window
+ ok, _ = reg.read(0x2000, 4)
+ self.assertFalse(ok)
+ self.assertEqual(reg.unhandled, 2)
+ # Only window-matched accesses are recorded; outside-window accesses
+ # are counted but left to the machine's unknown log.
+ self.assertEqual(len(reg.accesses), 2)
+ self.assertFalse(reg.accesses[1]["handled"])
+
+ def test_overlapping_windows_fall_back_on_refusal(self):
+ # A later window shadows an earlier one, but an offset the overlay does
+ # not model (here the PMU stub refuses everything) falls back to the
+ # wider device instead of being logged as unknown.
+ reg = DeviceRegistry()
+ fal = FalconBlock(base=0x1000, size=0x1000)
+ reg.add(fal)
+ reg.add(PmuStub(base=0x1000, size=0x1000))
+ self.assertTrue(reg.write(0x1040, 4, 0xCA)) # MAILBOX0 via fallback
+ self.assertEqual(fal.regs[falcon_mod.MAILBOX0], 0xCA)
+ ok, value = reg.read(0x1040, 4)
+ self.assertTrue(ok)
+ self.assertEqual(value, 0xCA)
+ self.assertEqual(reg.unhandled, 0)
+ self.assertTrue(all(a["handled"] for a in reg.accesses))
+
+ def test_overlapping_windows_all_refuse_records_once(self):
+ reg = DeviceRegistry()
+ reg.add(PmuStub(base=0x1000, size=0x1000))
+ reg.add(PmuStub(base=0x1000, size=0x1000))
+ ok, _ = reg.read(0x1000, 4)
+ self.assertFalse(ok)
+ self.assertEqual(reg.unhandled, 1)
+ self.assertEqual(len(reg.accesses), 1)
+ self.assertFalse(reg.accesses[0]["handled"])
+ self.assertFalse(reg.write(0x1000, 4, 1))
+ self.assertEqual(reg.unhandled, 2)
+ self.assertEqual(len(reg.accesses), 2)
+
+ def test_straddling_access_is_not_served(self):
+ reg = DeviceRegistry()
+ reg.add(PmcBlock(base=0x1000, size=0x10))
+ ok, _ = reg.read(0x1008, 8) # ends at 0x1010, outside the window
+ self.assertFalse(ok)
+
+ def test_state_round_trip(self):
+ reg = DeviceRegistry()
+ fal = FalconBlock(base=0x1000, size=0x1000)
+ reg.add(fal)
+ reg.write(0x1040, 4, 0xCA)
+ snap = reg.state()
+ reg2 = DeviceRegistry()
+ fal2 = FalconBlock(base=0x1000, size=0x1000)
+ reg2.add(fal2)
+ reg2.load_state(snap)
+ self.assertEqual(fal2.regs[falcon_mod.MAILBOX0], 0xCA)
+ self.assertEqual(fal2.state(), fal.state())
+
+
+class PmcTests(unittest.TestCase):
+ def test_tu104_value_fields(self):
+ dev = PmcBlock(chip_id=0x164)
+ value = dev.read(0xA00, 4)
+ self.assertEqual((value >> 20) & 0x1FF, 0x164)
+ self.assertEqual((value >> 24) & 0x1F, 0x16) # ARCHITECTURE TU100
+ self.assertEqual((value >> 20) & 0xF, 0x4) # IMPLEMENTATION
+ self.assertEqual(dev.read(0xA04, 4), None)
+ self.assertFalse(dev.write(0xA00, 4, 0))
+
+ def test_profile_profile_value_override(self):
+ dev = PmcBlock(boot_42=0x00123456)
+ self.assertEqual(dev.read(0xA00, 4), 0x00123456)
+
+ def test_chip_id_constants_are_distinct(self):
+ from sim.devices import pmc as pmc_mod
+ ids = [pmc_mod.TU102, pmc_mod.TU104, pmc_mod.TU105, pmc_mod.TU106,
+ pmc_mod.TU116, pmc_mod.TU117]
+ self.assertEqual(len(ids), len(set(ids)))
+ self.assertEqual(pmc_mod.TU117, 0x167)
+ self.assertFalse(hasattr(pmc_mod, "TU107"))
+
+
+class FalconTests(unittest.TestCase):
+ def test_irq_set_clear_status(self):
+ dev = FalconBlock()
+ dev.write(falcon_mod.IRQSSET, 4, falcon_mod.IRQ_HALT)
+ self.assertEqual(dev.read(falcon_mod.IRQSTAT, 4), falcon_mod.IRQ_HALT)
+ dev.write(falcon_mod.IRQSCLR, 4, falcon_mod.IRQ_HALT)
+ self.assertEqual(dev.read(falcon_mod.IRQSTAT, 4), 0)
+ # write-only set/clear registers have no read-back
+ self.assertIsNone(dev.read(falcon_mod.IRQSSET, 4))
+
+ def test_mailbox_rw(self):
+ dev = FalconBlock()
+ dev.write(falcon_mod.MAILBOX0, 4, 0xCA)
+ dev.write(falcon_mod.MAILBOX1, 4, 0x11DEAD)
+ self.assertEqual(dev.read(falcon_mod.MAILBOX0, 4), 0xCA)
+ self.assertEqual(dev.read(falcon_mod.MAILBOX1, 4), 0x11DEAD)
+
+ def test_debuginfo_storage_without_shim(self):
+ dev = FalconBlock(debuginfo_host_consume=False)
+ dev.write(falcon_mod.DEBUGINFO, 4, 0x11DEAD)
+ self.assertEqual(dev.read(falcon_mod.DEBUGINFO, 4), 0x11DEAD)
+
+ def test_debuginfo_host_consume_shim(self):
+ dev = FalconBlock(debuginfo_host_consume=True)
+ dev.write(falcon_mod.DEBUGINFO, 4, 0x11DEAD)
+ value = dev.read(falcon_mod.DEBUGINFO, 4)
+ self.assertEqual(value, 0x11DEAD) # value preserved for the reader
+ self.assertEqual(dev.read(falcon_mod.DEBUGINFO, 4), 0x1DEAD) # stage cleared
+ self.assertEqual(dev.debuginfo_consumed, 1)
+ # debugger peek never consumes
+ dev.write(falcon_mod.DEBUGINFO, 4, 0x21DEAD)
+ self.assertEqual(dev.peek(falcon_mod.DEBUGINFO, 4), 0x21DEAD)
+ self.assertEqual(dev.debuginfo_consumed, 1)
+
+ def test_dma_transfer_through_translator(self):
+ ram = bytearray(0x10000)
+ ram[0x100:0x108] = b"DEADBEEF"
+ dma = DmaTranslator(ram=ram, ram_base=0, ram_size=len(ram),
+ windows=[{"pa": 0x0, "sim": 0, "size": 0x10000}])
+ dev = FalconBlock(dma=dma)
+ # Program: destination = 0x200, source = IMEM + 0x100. The register
+ # encodes ((base1 << 32) | base) << 8; the profile's DMA windows decide
+ # which aperture that address resolves to (aperture selection itself is
+ # not proven yet and is documented as an unknown).
+ dev.write(falcon_mod.DMATRFBASE1, 4, 0)
+ dev.write(falcon_mod.DMATRFBASE, 4, 0)
+ dev.write(falcon_mod.DMATRFFBOFFS, 4, 0x200)
+ dev.write(falcon_mod.DMATRFMOFFS, 4, 0x100)
+ cmd = (falcon_mod.CMD_IMEM | falcon_mod.CMD_WRITE
+ | (2 << falcon_mod.CMD_SIZE_SHIFT)) # IMEM -> FB, 16 bytes
+ dev.write(falcon_mod.DMATRFCMD, 4, cmd)
+ self.assertEqual(ram[0x200:0x210], b"DEADBEEF" + bytes(8))
+ self.assertTrue(dev.regs[falcon_mod.DMATRFCMD] & falcon_mod.CMD_IDLE)
+ self.assertEqual(dev.dma_transfers, 1)
+
+ def test_dma_refused_when_unmapped(self):
+ ram = bytearray(0x1000)
+ dma = DmaTranslator(ram=ram, ram_base=0, ram_size=len(ram), windows=[])
+ dev = FalconBlock(dma=dma)
+ dev.write(falcon_mod.DMATRFBASE, 4, 0)
+ dev.write(falcon_mod.DMATRFCMD, 4, falcon_mod.CMD_IMEM)
+ self.assertFalse(dev.regs[falcon_mod.DMATRFCMD] & falcon_mod.CMD_IDLE)
+ self.assertTrue(dev.regs[falcon_mod.DMATRFCMD] & falcon_mod.CMD_ERROR)
+ self.assertEqual(dev.dma_refused, 1)
+
+
+class PriscvTests(unittest.TestCase):
+ def test_irq_mask_semantics(self):
+ dev = PriscvBlock()
+ dev.write(0x2AC, 4, 0x5)
+ dev.write(0x2B0, 4, 0x1)
+ self.assertEqual(dev.read(0x2B4, 4), 0x4)
+ self.assertFalse(dev.write(0x2B4, 4, 0xFFFF)) # read-only
+
+ def test_trace_and_error_registers(self):
+ dev = PriscvBlock()
+ dev.write(0x344, 4, 0x1234)
+ self.assertEqual(dev.read(0x344, 4), 0x1234)
+ # observed boot panic reads (R-I4R): read-back is zero, writes refused
+ self.assertEqual(dev.read(0x364, 4), 0)
+ self.assertEqual(dev.read(0x368, 4), 0)
+ self.assertFalse(dev.write(0x368, 4, 0x1))
+
+
+class TimerTests(unittest.TestCase):
+ def test_modeled_time_reads(self):
+ clock = {"insns": 0}
+ dev = PtimerBlock(ns_per_insn=2.0, clock=lambda: clock["insns"])
+ self.assertEqual(dev.read(0x400, 4), 0)
+ self.assertEqual(dev.read(0x410, 4), 0)
+ clock["insns"] = 16
+ # 32 ns -> TIME_0 NSEC field (bits 31:5) = 1
+ self.assertEqual(dev.read(0x400, 4), 1)
+ self.assertEqual(dev.read(0x410, 4), 32)
+
+ def test_profile_window_reaches_time_registers(self):
+ # Regression: the profile window is PRIV+0x9000, so TIME_0/TIME_1 must
+ # be block-relative (0x400/0x410); aperture-absolute 0x9400/0x9410 fall
+ # outside the declared window and serve nothing.
+ profile = {
+ "schema": "greeneye-sim-profile/1",
+ "devices": {
+ "timer": {
+ "ns_per_insn": 1.0,
+ "windows": [{"base": "0x9000", "size": "0x1000"}],
+ }
+ },
+ }
+ machine = Machine(memory_mb=1, ram_base=0)
+ reg = registry_from_profile(profile, machine=machine)
+ machine.insns = 32
+ ok, value = reg.read(0x9000 + 0x400, 4, pc=0)
+ self.assertTrue(ok)
+ self.assertEqual(value, 1) # (32 ns >> 5)
+ ok, value = reg.read(0x9000 + 0x410, 4, pc=0)
+ self.assertTrue(ok)
+ self.assertEqual(value, 32)
+
+ def test_timer_registration_is_opt_in(self):
+ machine = Machine(memory_mb=1, ram_base=0)
+ no_section = {
+ "schema": "greeneye-sim-profile/1",
+ "devices": {"pmc": {"windows": [{"base": "0x1000", "size": "0x1000"}]}},
+ }
+ reg = registry_from_profile(no_section, machine=machine)
+ self.assertIsNone(reg.device_by_name("timer"))
+ disabled = {
+ "schema": "greeneye-sim-profile/1",
+ "devices": {"timer": {"enabled": False,
+ "windows": [{"base": "0x9000", "size": "0x1000"}]}},
+ }
+ reg = registry_from_profile(disabled, machine=machine)
+ self.assertIsNone(reg.device_by_name("timer"))
+ enabled = {
+ "schema": "greeneye-sim-profile/1",
+ "devices": {"timer": {"ns_per_insn": 1.0,
+ "windows": [{"base": "0x9000", "size": "0x1000"}]}},
+ }
+ reg = registry_from_profile(enabled, machine=machine)
+ self.assertIsNotNone(reg.device_by_name("timer"))
+
+
+class CsbTests(unittest.TestCase):
+ def test_undeclared_window_serves_nothing(self):
+ dev = CsbWindow(base=0x1000, size=0x1000)
+ self.assertIsNone(dev.read(0x0, 4))
+ self.assertFalse(dev.write(0x0, 4, 1))
+
+ def test_declared_semantics(self):
+ dev = CsbWindow(base=0x1000, size=0x1000, registers={
+ 0x00: {"width": 4, "value": 0xF, "semantics": "w1c"},
+ 0x04: {"width": 4, "value": 0, "semantics": "rw"},
+ 0x08: {"width": 4, "value": 0, "semantics": "ro"},
+ })
+ dev.write(0x00, 4, 0x3)
+ self.assertEqual(dev.read(0x00, 4), 0xC)
+ dev.write(0x04, 4, 0x77)
+ self.assertEqual(dev.read(0x04, 4), 0x77)
+ self.assertFalse(dev.write(0x08, 4, 1))
+
+
+class Wpr2Tests(unittest.TestCase):
+ def test_range_encoding(self):
+ dev = Wpr2Block()
+ dev.set_range(0x2000000, 0x6000000)
+ lo = dev.read(0x1FA824, 4)
+ hi = dev.read(0x1FA828, 4)
+ self.assertEqual((lo << 12), 0x2000000)
+ self.assertEqual((hi << 12) + 0x20000, 0x6000000)
+ self.assertIsNone(dev.read(0x1FA800, 4))
+
+ def test_state_derives_range_after_writes(self):
+ # Regression: writes update regs only; state() must derive start/end
+ # from the registers rather than reporting a stale set_range range.
+ dev = Wpr2Block()
+ dev.write(0x1FA824, 4, 0x2000000 >> 12)
+ dev.write(0x1FA828, 4, (0x6000000 - 0x20000) >> 12)
+ state = dev.state()
+ self.assertEqual(state["start"], 0x2000000)
+ self.assertEqual(state["end"], 0x6000000)
+ restored = Wpr2Block()
+ restored.load_state(state)
+ self.assertEqual(restored.start, 0x2000000)
+ self.assertEqual(restored.end, 0x6000000)
+ # A stale serialised start/end never wins over the registers.
+ stale = Wpr2Block()
+ stale.load_state({"regs": {str(0x1FA824): 0x2000},
+ "start": 0xDEAD, "end": 0xBEEF})
+ self.assertEqual(stale.start, 0x2000000)
+ self.assertIsNone(stale.end)
+
+
+class PmuTests(unittest.TestCase):
+ def test_stub_never_fakes(self):
+ dev = PmuStub(base=0x1000, size=0x1000)
+ self.assertIsNone(dev.read(0x0, 4))
+ self.assertFalse(dev.write(0x0, 4, 1))
+ self.assertEqual(dev.seen["r:0x0:4"], 1)
+
+
+class IrqRouterTests(unittest.TestCase):
+ def test_aggregation(self):
+ fal = FalconBlock()
+ pri = PriscvBlock()
+ router = IrqRouter(falcon=fal, priscv=pri)
+ fal.write(falcon_mod.IRQSSET, 4, falcon_mod.IRQ_HALT)
+ pri.write(0x2AC, 4, 0x2)
+ state = router.pending()
+ self.assertTrue(state["falcon_halt"])
+ self.assertEqual(state["riscv_irq_mask"], 0x2)
+
+
+class DmaTranslatorTests(unittest.TestCase):
+ def test_translate_and_refuse(self):
+ ram = bytearray(0x100)
+ dma = DmaTranslator(ram=ram, ram_base=0, ram_size=len(ram),
+ windows=[{"pa": 0x1000, "sim": 0, "size": 0x100}])
+ self.assertEqual(dma.translate(0x1000), 0)
+ self.assertEqual(dma.translate(0x10FF), 0xFF)
+ self.assertIsNone(dma.translate(0x2000))
+ self.assertTrue(dma.write(0x1000, b"AB"))
+ self.assertEqual(dma.read(0x1000, 2), b"AB")
+ self.assertIsNone(dma.read(0x2000, 2))
+ self.assertEqual(dma.refused, 1)
+
+ def test_copy_counts_only_completed_transfers_and_exports_events(self):
+ ram = bytearray(0x100)
+ ram[0:4] = b"ABCD"
+ dma = DmaTranslator(ram=ram, ram_base=0, ram_size=len(ram),
+ windows=[{"pa": 0x1000, "sim": 0, "size": 0x100}])
+ # Destination outside every window: refused, not a transfer.
+ self.assertFalse(dma.copy(0x1000, 0x2000, 4))
+ self.assertEqual(dma.transfers, 0)
+ self.assertEqual(dma.refused, 1)
+ # Both mapped: one completed transfer.
+ self.assertTrue(dma.copy(0x1000, 0x1004, 4))
+ self.assertEqual(dma.transfers, 1)
+ self.assertEqual(ram[0x4:0x8], b"ABCD")
+ state = dma.state()
+ self.assertIn("events", state)
+ self.assertEqual(state["events"][0]["kind"], "w")
+ self.assertEqual(len(state["events"]), 1)
+
+
+class MachineDeviceIntegrationTests(unittest.TestCase):
+ def _machine(self):
+ reg = DeviceRegistry()
+ reg.add(PmcBlock(base=0x2000000000000000, size=0x1000))
+ m = Machine(memory_mb=1, ram_base=0, unknown_mmio="lenient", devices=reg)
+ return m, reg
+
+ def test_modeled_device_takes_precedence_over_lenient(self):
+ m, reg = self._machine()
+ self.assertEqual(m._mmio_load(0x2000000000000A00, 4), PmcBlock().boot_42)
+ self.assertEqual(m.mmio_unknown, 0)
+ self.assertEqual(len(reg.accesses), 1)
+
+ def test_unknown_inside_window_is_logged(self):
+ m, reg = self._machine()
+ self.assertEqual(m._mmio_load(0x2000000000000A04, 4), 0)
+ self.assertEqual(m.mmio_unknown, 1)
+ self.assertFalse(reg.accesses[0]["handled"])
+
+ def test_debugger_peek_and_device_state(self):
+ m, reg = self._machine()
+ self.assertEqual(m.read_mem(0x2000000000000A00, 4),
+ PmcBlock().boot_42.to_bytes(4, "little"))
+ self.assertIsInstance(m.device_state(), dict)
+ self.assertEqual(m.device_state()["schema"], "greeneye-sim-devices/1")
+
+ def test_reset_regs_names(self):
+ m = Machine(memory_mb=1, ram_base=0, reset_regs={"a0": 0x1234, "sp": 0x8000})
+ self.assertEqual(m.x[10], 0x1234)
+ self.assertEqual(m.x[2], 0x8000)
+
+
+@unittest.skipUnless(os.path.isfile(DEFAULT_IMAGE), "real GSP firmware absent")
+class BootLadderTests(unittest.TestCase):
+ """Bounded real-firmware runs: observed rungs only."""
+
+ def test_profile_loads_and_builds_windows(self):
+ profile = load_profile(DEFAULT_PROFILE)
+ self.assertEqual(profile["schema"], "greeneye-sim-profile/1")
+ m = Machine(memory_mb=16, ram_base=0, unknown_mmio="lenient")
+ reg = registry_from_profile(profile, machine=m)
+ names = {d.name for d in reg.devices}
+ self.assertIn("pmc", names)
+ self.assertIn("falcon", names)
+ self.assertIn("priscv", names)
+ self.assertIsNotNone(reg.dma)
+
+ def test_no_shim_b1_device_model_serves_boot(self):
+ from sim.gsp_boot import run_boot
+ r = run_boot(image=DEFAULT_IMAGE, profile_path=DEFAULT_PROFILE,
+ instructions=20_000, record=False, use_shim=False)
+ self.assertTrue(r.ran)
+ self.assertEqual(r.rung, "B1")
+ self.assertIn("bootargs_consumer", r.milestones)
+ self.assertEqual(r.mmio_unknown, 0)
+ self.assertEqual(r.device_unhandled, 0)
+
+ def test_shim_reaches_relocation_b2(self):
+ from sim.gsp_boot import run_boot
+ r = run_boot(image=DEFAULT_IMAGE, profile_path=DEFAULT_PROFILE,
+ instructions=20_000, record=False, use_shim=True)
+ self.assertTrue(r.ran)
+ self.assertEqual(r.rung, "B2")
+ self.assertIn("relocation_copy", r.milestones)
+ self.assertEqual(r.mmio_unknown, 0)
+ # Known unknown: the DMEM runspace content the app relocates from is
+ # not reconstructed; the run stops there rather than faking it.
+ self.assertEqual(r.stop_reason, "fault")
+ self.assertEqual(r.stop_pc, 0x400EA0C)
+
+ def test_two_runs_identical(self):
+ from sim.gsp_boot import run_boot
+ kwargs = dict(image=DEFAULT_IMAGE, profile_path=DEFAULT_PROFILE,
+ instructions=20_000, record=False, use_shim=True)
+ a = run_boot(**kwargs)
+ b = run_boot(**kwargs)
+ self.assertEqual(a.instructions, b.instructions)
+ self.assertEqual(a.stop_pc, b.stop_pc)
+ self.assertEqual(a.stop_reason, b.stop_reason)
+ self.assertEqual(a.device_state_sha256, b.device_state_sha256)
+ self.assertEqual(a.registers, b.registers)
+
+
+if __name__ == "__main__":
+ unittest.main()