Skip to content

Commit 40d5b09

Browse files
committed
[ot] scripts/opentitan: swexit.py: rewrite this tool to use DV SIM window
`rv_core_ibex.SW_FATAL_ERR` has been deprecated a long time ago, however this smoke test generator had not been updated. See 7ba08ff Signed-off-by: Emmanuel Blot <eblot@rivosinc.com>
1 parent 5bdd9c2 commit 40d5b09

1 file changed

Lines changed: 126 additions & 26 deletions

File tree

scripts/opentitan/swexit.py

Lines changed: 126 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
#!/usr/bin/env python3
22

3-
# Copyright (c) 2023-2024 Rivos, Inc.
3+
# Copyright (c) 2023-2025 Rivos, Inc.
44
# SPDX-License-Identifier: Apache2
55

66
"""OpenTitan exit code generator for QEMU.
77
8+
Generate a small RV32I instruction bytestream to instruct the Ibex core to
9+
trigger a VM shutdown, using a dedicated HW register.
10+
11+
This generator avoids adding a dependency on a RISC-V toolchain for the sake
12+
of smoke-testing an Ibex/OpenTitan VM.
13+
814
:author: Emmanuel Blot <eblot@rivosinc.com>
915
"""
1016

@@ -14,44 +20,135 @@
1420
from typing import Union
1521
import sys
1622

17-
# pylint: disable=missing-function-docstring
1823

19-
BASE_ADDRESS = {
20-
'earlgrey': 0x411f0000,
21-
'darjeeling': 0x211f0000,
22-
'ibexdemo': 0x20000,
24+
SOC_CONSTANTS = {
25+
'earlgrey': (0x411f0000, 0x80),
26+
'darjeeling': (0x211f0000, 0x440),
27+
'ibexdemo': (0x20000, None)
2328
}
24-
25-
LUI_MASK = (1 << 12) - 1
29+
"""Supported SoCs, with controller base address and register offset."""
30+
31+
TEST_STATUS_PASSED = 0x900d # 'good'
32+
"""Special code to signal a successful completion to DV SIM register."""
33+
34+
OPCODE = {
35+
'addi': 0b000_00000_0010011,
36+
'lui': 0b0110111,
37+
'srli': 0b101_00000_0010011,
38+
'ssli': 0b001_00000_0010011,
39+
'sw': 0b010_00000_0100011,
40+
'wfi': 0x10500073,
41+
}
42+
"""RV32I binary opcodes used in code generator."""
2643

2744

2845
def to_int(value: Union[int, str]) -> int:
46+
"""Argparser helper to parse decimal or hexadecimal integer strings."""
2947
if isinstance(value, int):
3048
return value
3149
return int(value.strip(), value.startswith('0x') and 16 or 10)
3250

3351

34-
def opentitan_code(addr: int) -> bytes:
35-
addr &= ~LUI_MASK
36-
lui = addr | 0x537
37-
return spack('<IIII',
38-
lui, # lui a0,addr # ibex_core_wrapper base
39-
0xc0de05b7, # lui a1,0xc0de0 # exit code (0)
40-
0x00b52423, # sw a1,8(a0) # write to sw_fatal_err
41-
0x10500073) # wfi # stop here
52+
def rd_u(reg: int) -> int:
53+
"""Destination register for U-type instruction."""
54+
return reg << 7
55+
56+
57+
def imm_u(imm: int) -> int:
58+
"""Immediate value for U-type instruction."""
59+
return imm << 12
60+
61+
62+
def mask_u(imm: int) -> int:
63+
"""Mask immediate value for U-type instruction."""
64+
return imm & ~((1 << 12) - 1)
65+
66+
67+
def imm_i(imm: int) -> int:
68+
"""Immediate value for I-type instruction."""
69+
return imm << 20
70+
71+
72+
def rs1_i(reg: int) -> int:
73+
"""Source register for I-type instruction."""
74+
return reg << 15
75+
76+
77+
def rd_i(reg: int) -> int:
78+
"""Destination register for I-type instruction."""
79+
return reg << 7
80+
81+
82+
def shamt_i(imm: int) -> int:
83+
"""Shift ammount for I-type instruction."""
84+
return imm << 20
85+
86+
87+
def rs1_s(reg: int) -> int:
88+
"""First source register for S-type instruction."""
89+
return reg << 15
90+
91+
92+
def rs2_s(reg: int) -> int:
93+
"""Second source register for S-type instruction."""
94+
return reg << 20
95+
96+
97+
def off_s(imm: int) -> int:
98+
"""Offset value for S-type instruction."""
99+
off_hi = (imm >> 5) << 25
100+
off_lo = (imm & 0b11111) << 7
101+
return off_hi | off_lo
102+
103+
104+
def opentitan_code(addr: int, offset: int) -> bytes:
105+
"""Exit code generation using DV SIM status register.
106+
107+
:param addr: Ibex wrapper controller base address
108+
:param offset: Offset of the DV SIM status register.
109+
:return: RV32I instruction stream
110+
"""
111+
assert 0 <= offset < (1 << 11) # 12 bit, signed int
112+
instructions = [
113+
# lui a0,addr # ibex_core_wrapper base
114+
mask_u(addr) | rd_u(10) | OPCODE['lui'],
115+
# lui a1,status # "test" status
116+
imm_u(TEST_STATUS_PASSED) | rd_u(11) | OPCODE['lui'],
117+
# srli a1,a1,12 # shift down status
118+
shamt_i(12) | rs1_i(11) | rd_i(11) | OPCODE['srli'],
119+
# sw a1,offset(a0) # write to DV SIM offset
120+
off_s(offset) | rs2_s(11) | rs1_s(10) | OPCODE['sw'],
121+
# wfi # stop here
122+
OPCODE['wfi'],
123+
# illegal instruction
124+
0x0
125+
]
126+
return spack(f'<{len(instructions)}I', *instructions)
42127

43128

44129
def ibexdemo_code(addr: int) -> bytes:
45-
addr &= ~LUI_MASK
46-
lui = addr | 0x537
47-
return spack('<IIII',
48-
lui, # lui a0,addr # simulator_ctrl base
49-
0x00100593, # li a1,1 # set bit 0 to exit
50-
0x00b52423, # sw a1,8(a0) # write to sim_ctrl
51-
0x10500073) # wfi # stop here
130+
"""Exit code generation for Ibex demo machine.
131+
132+
:param addr: Controller base address
133+
:return: RV32I instruction stream
134+
"""
135+
instructions = [
136+
# lui a0,addr # simulator_ctrl base
137+
mask_u(addr) | rd_u(10) | OPCODE['lui'],
138+
# li a1,1 # set bit 0 to exit
139+
imm_i(1) | rs1_i(0) | rd_i(11) | OPCODE['addi'],
140+
# sw a1,offset(a0) # write to DV SIM offset
141+
off_s(0x8) | rs2_s(11) | rs1_s(10) | OPCODE['sw'],
142+
# wfi # stop here
143+
OPCODE['wfi'],
144+
# illegal instruction
145+
0x0
146+
]
147+
return spack(f'<{len(instructions)}I', *instructions)
52148

53149

54150
def main():
151+
"""Entry point."""
55152
debug = False
56153
try:
57154
desc = sys.modules[__name__].__doc__.split('.', 1)[0].strip()
@@ -62,7 +159,7 @@ def main():
62159
argparser.add_argument('-b', '--base', type=to_int, default=0x80,
63160
help='Offset for the first instruction '
64161
'(default: 0x80)')
65-
argparser.add_argument('-t', '--soc', choices=list(BASE_ADDRESS),
162+
argparser.add_argument('-t', '--soc', choices=list(SOC_CONSTANTS),
66163
help='SoC type', required=True)
67164
argparser.add_argument('-o', '--output', type=FileType('wb'),
68165
help='output file, default to stdout')
@@ -71,11 +168,14 @@ def main():
71168
args = argparser.parse_args()
72169
debug = args.debug
73170

74-
addr = args.address if args.address else BASE_ADDRESS[args.soc]
171+
addr = args.address if args.address else SOC_CONSTANTS[args.soc][0]
75172
if args.soc == 'ibexdemo':
76173
bincode = ibexdemo_code(addr)
77174
else:
78-
bincode = opentitan_code(addr)
175+
offset = SOC_CONSTANTS.get(args.soc)[1]
176+
if offset is None:
177+
argparser.error('Unsupported SoC type')
178+
bincode = opentitan_code(addr, offset)
79179
out = args.output or sys.stdout.buffer
80180
padding = bytes(args.base)
81181
out.write(padding)

0 commit comments

Comments
 (0)