Skip to content

Commit fb3408d

Browse files
committed
Integrate Brain branch with UDB in ext/ folder. Merge the output of the UDB with riscv-opcodes generation process to generate riscv-opcodes outputs.
Signed-off-by: Afonso Oliveira <[email protected]>
1 parent 3f860c6 commit fb3408d

File tree

4 files changed

+455
-0
lines changed

4 files changed

+455
-0
lines changed

ext/opcodes_maker/Makefile

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Directories
2+
YAML_DIR ?= ../../arch/inst
3+
OPCODES_DIR := ../riscv-opcodes
4+
OUTPUT_DIR := output
5+
6+
# Python scripts
7+
YAML_TO_JSON := yaml_to_json.py
8+
GENERATOR := generator.py
9+
10+
# Output files
11+
INSTR_DICT := $(OUTPUT_DIR)/instr_dict.json
12+
13+
# Generated output files
14+
C_OUT := $(OUTPUT_DIR)/encoding.out.h
15+
CHISEL_OUT := $(OUTPUT_DIR)/inst.chisel
16+
SPINALHDL_OUT := $(OUTPUT_DIR)/inst.spinalhdl
17+
SVERILOG_OUT := $(OUTPUT_DIR)/inst.sverilog
18+
RUST_OUT := $(OUTPUT_DIR)/inst.rs
19+
GO_OUT := $(OUTPUT_DIR)/inst.go
20+
LATEX_OUT := $(OUTPUT_DIR)/instr-table.tex
21+
LATEX_PRIV_OUT := $(OUTPUT_DIR)/priv-instr-table.tex
22+
PROCESSED_DICT := $(OUTPUT_DIR)/processed_instr_dict.json
23+
24+
# Check for required files
25+
REQUIRED_FILES := $(YAML_TO_JSON) $(GENERATOR)
26+
$(foreach file,$(REQUIRED_FILES),\
27+
$(if $(wildcard $(file)),,$(error Required file $(file) not found)))
28+
29+
# Default target
30+
all: generate
31+
32+
# Create output directory
33+
$(OUTPUT_DIR):
34+
mkdir -p $(OUTPUT_DIR)
35+
36+
# Convert YAML to JSON
37+
$(INSTR_DICT): $(YAML_TO_JSON) | $(OUTPUT_DIR)
38+
python3 $(YAML_TO_JSON) $(YAML_DIR) $(OUTPUT_DIR)
39+
40+
# Generate all outputs
41+
generate: $(INSTR_DICT)
42+
python3 $(GENERATOR) $(INSTR_DICT) -c -chisel -rust -go -latex
43+
mv encoding.out.h inst.chisel inst.spinalhdl inst.sverilog inst.rs inst.go \
44+
instr-table.tex priv-instr-table.tex processed_instr_dict.json \
45+
$(OUTPUT_DIR)/ 2>/dev/null || true
46+
47+
# Clean generated files
48+
clean:
49+
rm -rf $(OUTPUT_DIR)
50+
51+
# Help target
52+
help:
53+
@echo "Available targets:"
54+
@echo " all (default) - Run complete pipeline: YAML -> JSON -> generate"
55+
@echo " generate - Generate all output formats from JSON"
56+
@echo " clean - Remove all generated files"
57+
@echo ""
58+
@echo "All output files will be placed in the '$(OUTPUT_DIR)' directory"
59+
@echo "Use YAML_DIR=/path/to/yaml to specify custom YAML input directory"
60+
61+
.PHONY: all generate clean help

ext/opcodes_maker/generator.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import json
5+
import logging
6+
import pprint
7+
import os
8+
import sys
9+
import shutil
10+
from contextlib import contextmanager
11+
from pathlib import Path
12+
from typing import Dict, List, Any
13+
14+
# Add riscv-opcodes directory to Python path
15+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
16+
RISCV_OPCODES_DIR = os.path.join(SCRIPT_DIR, "..", "riscv-opcodes")
17+
sys.path.insert(0, RISCV_OPCODES_DIR)
18+
19+
20+
@contextmanager
21+
def working_directory(path):
22+
"""Context manager for changing the current working directory"""
23+
prev_cwd = os.getcwd()
24+
os.chdir(path)
25+
try:
26+
yield prev_cwd
27+
finally:
28+
os.chdir(prev_cwd)
29+
30+
31+
# Change to riscv-opcodes directory when importing to ensure relative paths work
32+
with working_directory(RISCV_OPCODES_DIR):
33+
from c_utils import make_c
34+
from chisel_utils import make_chisel
35+
from constants import emitted_pseudo_ops
36+
from go_utils import make_go
37+
from latex_utils import make_latex_table, make_priv_latex_table
38+
from rust_utils import make_rust
39+
from sverilog_utils import make_sverilog
40+
41+
LOG_FORMAT = "%(levelname)s:: %(message)s"
42+
LOG_LEVEL = logging.INFO
43+
44+
pretty_printer = pprint.PrettyPrinter(indent=2)
45+
logging.basicConfig(level=LOG_LEVEL, format=LOG_FORMAT)
46+
47+
48+
def load_instruction_dict(json_path: str) -> Dict[str, Any]:
49+
"""
50+
Load instruction dictionary from a JSON file.
51+
"""
52+
try:
53+
with open(json_path, encoding="utf-8") as f:
54+
return json.load(f)
55+
except FileNotFoundError:
56+
logging.error(f"Input JSON file not found: {json_path}")
57+
raise
58+
except json.JSONDecodeError:
59+
logging.error(f"Invalid JSON format in file: {json_path}")
60+
raise
61+
62+
63+
def move_file(src: str, dest_dir: str):
64+
"""
65+
Move a file to the destination directory if it exists.
66+
"""
67+
if os.path.exists(src):
68+
dest = os.path.join(dest_dir, os.path.basename(src))
69+
shutil.move(src, dest)
70+
71+
72+
def generate_outputs(
73+
instr_dict: Dict[str, Any],
74+
include_pseudo: bool,
75+
c: bool,
76+
chisel: bool,
77+
spinalhdl: bool,
78+
sverilog: bool,
79+
rust: bool,
80+
go: bool,
81+
latex: bool,
82+
):
83+
"""
84+
Generate output files based on the instruction dictionary.
85+
"""
86+
# Sort the dictionary for consistent output
87+
instr_dict = dict(sorted(instr_dict.items()))
88+
89+
# Save the processed dictionary in current directory
90+
with open("processed_instr_dict.json", "w", encoding="utf-8") as outfile:
91+
json.dump(instr_dict, outfile, indent=2)
92+
93+
# Generate files in riscv-opcodes directory and move them to current directory
94+
with working_directory(RISCV_OPCODES_DIR) as orig_dir:
95+
if c:
96+
# For C output, filter pseudo-ops if needed
97+
if not include_pseudo:
98+
c_dict = {
99+
k: v for k, v in instr_dict.items() if k not in emitted_pseudo_ops
100+
}
101+
else:
102+
c_dict = instr_dict
103+
make_c(c_dict)
104+
move_file("encoding.out.h", orig_dir)
105+
logging.info("encoding.out.h generated successfully")
106+
107+
if chisel:
108+
make_chisel(instr_dict)
109+
move_file("inst.chisel", orig_dir)
110+
logging.info("inst.chisel generated successfully")
111+
112+
if spinalhdl:
113+
make_chisel(instr_dict, True)
114+
move_file("inst.spinalhdl", orig_dir)
115+
logging.info("inst.spinalhdl generated successfully")
116+
117+
if sverilog:
118+
make_sverilog(instr_dict)
119+
move_file("inst.sverilog", orig_dir)
120+
logging.info("inst.sverilog generated successfully")
121+
122+
if rust:
123+
make_rust(instr_dict)
124+
move_file("inst.rs", orig_dir)
125+
logging.info("inst.rs generated successfully")
126+
127+
if go:
128+
make_go(instr_dict)
129+
move_file("inst.go", orig_dir)
130+
logging.info("inst.go generated successfully")
131+
132+
if latex:
133+
make_latex_table()
134+
make_priv_latex_table()
135+
move_file("instr-table.tex", orig_dir)
136+
move_file("priv-instr-table.tex", orig_dir)
137+
logging.info("LaTeX files generated successfully")
138+
139+
140+
def main():
141+
parser = argparse.ArgumentParser(
142+
description="Generate RISC-V constants from JSON input"
143+
)
144+
parser.add_argument(
145+
"input_json", help="Path to JSON file containing instruction definitions"
146+
)
147+
parser.add_argument(
148+
"-pseudo", action="store_true", help="Include pseudo-instructions"
149+
)
150+
parser.add_argument("-c", action="store_true", help="Generate output for C")
151+
parser.add_argument(
152+
"-chisel", action="store_true", help="Generate output for Chisel"
153+
)
154+
parser.add_argument(
155+
"-spinalhdl", action="store_true", help="Generate output for SpinalHDL"
156+
)
157+
parser.add_argument(
158+
"-sverilog", action="store_true", help="Generate output for SystemVerilog"
159+
)
160+
parser.add_argument("-rust", action="store_true", help="Generate output for Rust")
161+
parser.add_argument("-go", action="store_true", help="Generate output for Go")
162+
parser.add_argument("-latex", action="store_true", help="Generate output for Latex")
163+
164+
args = parser.parse_args()
165+
166+
# Load instruction dictionary from JSON
167+
instr_dict = load_instruction_dict(args.input_json)
168+
169+
print(f"Loaded instruction dictionary from: {args.input_json}")
170+
171+
# Generate outputs based on the loaded dictionary
172+
generate_outputs(
173+
instr_dict,
174+
args.pseudo,
175+
args.c,
176+
args.chisel,
177+
args.spinalhdl,
178+
args.sverilog,
179+
args.rust,
180+
args.go,
181+
args.latex,
182+
)
183+
184+
185+
if __name__ == "__main__":
186+
main()

ext/opcodes_maker/sorter.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import json
2+
3+
4+
def sort_instr_json(dir_name, outname):
5+
with open(dir_name) as file:
6+
data = json.load(file)
7+
8+
sorted_data = {}
9+
for key in sorted(data):
10+
entry = data[key]
11+
if "variable_fields" in entry:
12+
entry["variable_fields"] = sorted(entry["variable_fields"])
13+
if "extension" in entry:
14+
entry["extension"] = sorted(entry["extension"])
15+
16+
# Add the processed entry to the sorted data
17+
sorted_data[key] = entry
18+
19+
with open(outname, "w") as file:
20+
json.dump(sorted_data, file, indent=4)
21+
22+
print(json.dumps(sorted_data, indent=4))
23+
24+
25+
def main():
26+
sort_instr_json("instr_dict.json", "udb_sorted_data.json")
27+
sort_instr_json("instr_dict.json", "opcodes_sorted_data.json")
28+
29+
30+
main()

0 commit comments

Comments
 (0)