1+ #!/usr/bin/env python3
2+
3+ import argparse
4+ import os
5+ import sys
6+ import logging
7+ from pathlib import Path
8+
9+ # Add parent directory to path for imports
10+ sys .path .insert (0 , os .path .dirname (os .path .dirname (os .path .abspath (__file__ ))))
11+
12+ from generator import load_instructions , load_csrs
13+
14+
15+ def format_instruction_name (name ):
16+ """Format instruction name for SystemVerilog (uppercase with underscores)."""
17+ # Handle compressed instructions
18+ if name .startswith ("c." ):
19+ name = "C_" + name [2 :]
20+ # Replace dots with underscores and convert to uppercase
21+ return name .replace ("." , "_" ).upper ()
22+
23+
24+ def format_csr_name (name ):
25+ """Format CSR name for SystemVerilog (uppercase with underscores)."""
26+ return "CSR_" + name .replace ("." , "_" ).upper ()
27+
28+
29+ def match_to_sverilog_bits (match_str , is_compressed = False ):
30+ """Convert a match string to SystemVerilog bit pattern."""
31+ if not match_str :
32+ return "32'b" + "?" * 32
33+
34+ # For compressed instructions (16-bit), we need to handle them differently
35+ # The riscv-opcodes format puts the 16-bit pattern in the lower 16 bits
36+ # with the upper 16 bits as wildcards
37+ if is_compressed or len (match_str ) == 16 :
38+ # Pad with wildcards on the left for 16-bit instructions
39+ match_str = "?" * 16 + match_str
40+ elif len (match_str ) < 32 :
41+ # For other cases, pad on the right
42+ match_str = match_str + "-" * (32 - len (match_str ))
43+
44+ # Convert to SystemVerilog format (0, 1, or ?)
45+ result = []
46+ for bit in match_str :
47+ if bit == "0" :
48+ result .append ("0" )
49+ elif bit == "1" :
50+ result .append ("1" )
51+ else : # '-' or any other character
52+ result .append ("?" )
53+
54+ return "32'b" + "" .join (result )
55+
56+
57+ def generate_sverilog (instructions , csrs , output_file ):
58+ """Generate SystemVerilog package file."""
59+ with open (output_file , "w" ) as f :
60+ # Write header
61+ f .write ("\n /* Automatically generated by parse_opcodes */\n " )
62+ f .write ("package riscv_instr;\n " )
63+
64+ # Find the maximum name length for alignment
65+ max_instr_len = max ((len (format_instruction_name (name )) for name in instructions .keys ()), default = 0 )
66+ max_csr_len = max ((len (format_csr_name (csrs [addr ])) for addr in csrs .keys ()), default = 0 )
67+ max_len = max (max_instr_len , max_csr_len )
68+
69+ # Write instruction parameters
70+ for name in sorted (instructions .keys ()):
71+ encoding = instructions [name ]
72+ sv_name = format_instruction_name (name )
73+ # Pad the name for alignment
74+ padded_name = sv_name .ljust (max_len )
75+
76+ # Get the match pattern
77+ if isinstance (encoding , dict ) and "match" in encoding :
78+ match = encoding ["match" ]
79+ else :
80+ # If no match field, use all wildcards
81+ match = "-" * 32
82+
83+ # Check if this is a compressed instruction
84+ is_compressed = name .startswith ("c." )
85+ sv_bits = match_to_sverilog_bits (match , is_compressed )
86+ f .write (f" localparam [31:0] { padded_name } = { sv_bits } ;\n " )
87+
88+ # Write CSR parameters
89+ # CSRs are returned as {address: name} by load_csrs
90+ for addr in sorted (csrs .keys ()):
91+ csr_name = csrs [addr ]
92+ sv_name = format_csr_name (csr_name )
93+ # Pad the name for alignment
94+ padded_name = sv_name .ljust (max_len )
95+
96+ # Format CSR address as 12-bit hex
97+ f .write (f" localparam logic [11:0] { padded_name } = 12'h{ addr :03x} ;\n " )
98+
99+ # Write footer
100+ f .write ("\n endpackage\n " )
101+
102+
103+ def parse_args ():
104+ parser = argparse .ArgumentParser (
105+ description = "Generate SystemVerilog package from RISC-V instruction definitions"
106+ )
107+ parser .add_argument (
108+ "--inst-dir" ,
109+ default = "../../../gen/resolved_spec/_/inst/" ,
110+ help = "Directory containing instruction YAML files" ,
111+ )
112+ parser .add_argument (
113+ "--csr-dir" ,
114+ default = "../../../gen/resolved_spec/_/csr/" ,
115+ help = "Directory containing CSR YAML files" ,
116+ )
117+ parser .add_argument (
118+ "--output" ,
119+ default = "inst.sverilog" ,
120+ help = "Output SystemVerilog file name"
121+ )
122+ parser .add_argument (
123+ "--extensions" ,
124+ default = "A,D,F,I,M,Q,Zba,Zbb,Zbs,S,System,V,Zicsr,Smpmp,Sm,H,U,Zicntr,Zihpm,Smhpm" ,
125+ help = "Comma-separated list of enabled extensions. Default includes standard extensions." ,
126+ )
127+ parser .add_argument (
128+ "--arch" ,
129+ default = "RV64" ,
130+ choices = ["RV32" , "RV64" , "BOTH" ],
131+ help = "Target architecture (RV32, RV64, or BOTH). Default is RV64." ,
132+ )
133+ parser .add_argument (
134+ "--verbose" , "-v" , action = "store_true" , help = "Enable verbose logging"
135+ )
136+ parser .add_argument (
137+ "--include-all" ,
138+ action = "store_true" ,
139+ help = "Include all instructions and CSRs regardless of extensions" ,
140+ )
141+ return parser .parse_args ()
142+
143+
144+ def main ():
145+ args = parse_args ()
146+
147+ # Set up logging
148+ log_level = logging .DEBUG if args .verbose else logging .INFO
149+ logging .basicConfig (level = log_level , format = "%(levelname)s:: %(message)s" )
150+
151+ # Parse extensions
152+ if args .include_all :
153+ enabled_extensions = []
154+ logging .info ("Including all instructions and CSRs (ignoring extension filter)" )
155+ else :
156+ enabled_extensions = [ext .strip () for ext in args .extensions .split ("," )]
157+ logging .info (f"Enabled extensions: { ', ' .join (enabled_extensions )} " )
158+
159+ logging .info (f"Target architecture: { args .arch } " )
160+
161+ # Load instructions
162+ instructions = load_instructions (
163+ args .inst_dir , enabled_extensions , args .include_all , args .arch
164+ )
165+ logging .info (f"Loaded { len (instructions )} instructions" )
166+
167+ # Load CSRs
168+ csrs = load_csrs (args .csr_dir , enabled_extensions , args .include_all , args .arch )
169+ logging .info (f"Loaded { len (csrs )} CSRs" )
170+
171+ # Generate the SystemVerilog file
172+ generate_sverilog (instructions , csrs , args .output )
173+ logging .info (
174+ f"Generated { args .output } with { len (instructions )} instructions and { len (csrs )} CSRs"
175+ )
176+
177+
178+ if __name__ == "__main__" :
179+ main ()
0 commit comments