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
1420from typing import Union
1521import 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
2845def 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
44129def 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
54150def 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