Skip to content

Commit 3ece684

Browse files
committed
feat: add exception code handling to SystemVerilog generator and update task script
1 parent 5a69acc commit 3ece684

File tree

5 files changed

+216
-153
lines changed

5 files changed

+216
-153
lines changed

.github/workflows/regress.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,17 @@ jobs:
190190
uses: ./.github/actions/singularity-setup
191191
- name: Generate c_header code
192192
run: ./do gen:c_header
193+
regress-gen-sverilog:
194+
runs-on: ubuntu-latest
195+
env:
196+
SINGULARITY: 1
197+
steps:
198+
- name: Clone Github Repo Action
199+
uses: actions/checkout@v4
200+
- name: singularity setup
201+
uses: ./.github/actions/singularity-setup
202+
- name: Generate sverilog_header code
203+
run: ./do gen:sverilog
193204
regress-cpp-unit:
194205
runs-on: ubuntu-latest
195206
env:

backends/generators/c_header/generate_encoding.py

Lines changed: 1 addition & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import logging
99
import argparse
1010
import yaml
11-
import json
1211

1312
# Add parent directory to path to import generator.py
1413
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -18,6 +17,7 @@
1817
from generator import (
1918
load_instructions,
2019
load_csrs,
20+
load_exception_codes,
2121
parse_match,
2222
parse_extension_requirements,
2323
)
@@ -30,125 +30,6 @@ def calculate_mask(match_str):
3030
return int("".join("0" if c == "-" else "1" for c in match_str), 2)
3131

3232

33-
def load_exception_codes(
34-
ext_dir, enabled_extensions=None, include_all=False, resolved_codes_file=None
35-
):
36-
"""Load exception codes from extension YAML files or pre-resolved JSON file."""
37-
exception_codes = []
38-
found_extensions = 0
39-
found_files = 0
40-
41-
if enabled_extensions is None:
42-
enabled_extensions = []
43-
44-
# If we have a resolved codes file, use it instead of processing YAML files
45-
if resolved_codes_file and os.path.exists(resolved_codes_file):
46-
try:
47-
with open(resolved_codes_file, encoding="utf-8") as f:
48-
resolved_codes = json.load(f)
49-
50-
for code in resolved_codes:
51-
num = code.get("num")
52-
name = code.get("name")
53-
if num is not None and name is not None:
54-
sanitized_name = (
55-
name.lower()
56-
.replace(" ", "_")
57-
.replace("/", "_")
58-
.replace("-", "_")
59-
)
60-
exception_codes.append((num, sanitized_name))
61-
62-
logging.info(
63-
f"Loaded {len(exception_codes)} pre-resolved exception codes from {resolved_codes_file}"
64-
)
65-
66-
# Sort by exception code number and deduplicate
67-
seen_nums = set()
68-
unique_codes = []
69-
for num, name in sorted(exception_codes, key=lambda x: x[0]):
70-
if num not in seen_nums:
71-
seen_nums.add(num)
72-
unique_codes.append((num, name))
73-
74-
return unique_codes
75-
76-
except Exception as e:
77-
logging.error(
78-
f"Error loading resolved codes file {resolved_codes_file}: {e}"
79-
)
80-
# Fall back to processing YAML files
81-
82-
for dirpath, _, filenames in os.walk(ext_dir):
83-
for fname in filenames:
84-
if not fname.endswith(".yaml"):
85-
continue
86-
87-
found_files += 1
88-
path = os.path.join(dirpath, fname)
89-
90-
try:
91-
with open(path, encoding="utf-8") as f:
92-
data = yaml.safe_load(f)
93-
94-
if not isinstance(data, dict) or data.get("kind") != "extension":
95-
continue
96-
97-
found_extensions += 1
98-
ext_name = data.get("name", "unnamed")
99-
100-
# Skip extension filtering if include_all is True
101-
if not include_all:
102-
# Filter by extension requirements
103-
definedBy = data.get("definedBy")
104-
if definedBy:
105-
meets_req = parse_extension_requirements(definedBy)
106-
if not meets_req(enabled_extensions):
107-
continue
108-
109-
# Check if excluded
110-
excludedBy = data.get("excludedBy")
111-
if excludedBy:
112-
exclusion_check = parse_extension_requirements(excludedBy)
113-
if exclusion_check(enabled_extensions):
114-
continue
115-
116-
# Get exception codes
117-
for code in data.get("exception_codes", []):
118-
num = code.get("num")
119-
name = code.get("name")
120-
121-
if num is not None and name is not None:
122-
sanitized_name = (
123-
name.lower()
124-
.replace(" ", "_")
125-
.replace("/", "_")
126-
.replace("-", "_")
127-
)
128-
exception_codes.append((num, sanitized_name))
129-
130-
except Exception as e:
131-
logging.error(f"Error processing file {path}: {e}")
132-
133-
if found_extensions > 0:
134-
logging.info(
135-
f"Found {found_extensions} extension definitions in {found_files} files"
136-
)
137-
logging.info(f"Added {len(exception_codes)} exception codes to the output")
138-
else:
139-
logging.warning(f"No extension definitions found in {ext_dir}")
140-
141-
# Sort by exception code number and deduplicate
142-
seen_nums = set()
143-
unique_codes = []
144-
for num, name in sorted(exception_codes, key=lambda x: x[0]):
145-
if num not in seen_nums:
146-
seen_nums.add(num)
147-
unique_codes.append((num, name))
148-
149-
return unique_codes
150-
151-
15233
def extract_instruction_fields(instructions):
15334
"""Extract field names and their positions from instruction definitions."""
15435
field_dict = {}

backends/generators/generator.py

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import yaml
44
import logging
55
import pprint
6+
import json
67

78
pp = pprint.PrettyPrinter(indent=2)
89
logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s")
@@ -276,14 +277,15 @@ def load_instructions(
276277

277278
# Process RV64 encoding
278279
rv64_match = rv64_encoding.get("match")
280+
rv32_match = rv32_encoding.get("match")
281+
279282
if rv64_match:
280283
instr_dict[name] = {
281284
"match": rv64_match
282285
} # RV64 gets the default name
283286

284-
# Process RV32 encoding with a _rv32 suffix
285-
rv32_match = rv32_encoding.get("match")
286-
if rv32_match:
287+
if rv32_match and rv32_match != rv64_match:
288+
# Process RV32 encoding with a _rv32 suffix
287289
instr_dict[f"{name}_rv32"] = {"match": rv32_match}
288290

289291
continue # Skip the rest of the loop as we've already added the encodings
@@ -441,11 +443,7 @@ def load_csrs(csr_root, enabled_extensions, include_all=False, target_arch="RV64
441443
else:
442444
addr_int = int(addr_to_use, 0)
443445

444-
# For BOTH architecture, add suffix to RV32-specific CSRs
445-
if target_arch == "BOTH" and base == 32:
446-
csrs[addr_int] = f"{name.upper()}.RV32"
447-
else:
448-
csrs[addr_int] = name.upper()
446+
csrs[addr_int] = name.upper()
449447
except Exception as e:
450448
logging.error(f"Error parsing address {addr_to_use} in {path}: {e}")
451449
address_errors += 1
@@ -468,6 +466,124 @@ def load_csrs(csr_root, enabled_extensions, include_all=False, target_arch="RV64
468466
return csrs
469467

470468

469+
def load_exception_codes(
470+
ext_dir, enabled_extensions=None, include_all=False, resolved_codes_file=None
471+
):
472+
"""Load exception codes from extension YAML files or pre-resolved JSON file."""
473+
exception_codes = []
474+
found_extensions = 0
475+
found_files = 0
476+
477+
if enabled_extensions is None:
478+
enabled_extensions = []
479+
# If we have a resolved codes file, use it instead of processing YAML files
480+
if resolved_codes_file and os.path.exists(resolved_codes_file):
481+
try:
482+
with open(resolved_codes_file, encoding="utf-8") as f:
483+
resolved_codes = json.load(f)
484+
485+
for code in resolved_codes:
486+
num = code.get("num")
487+
name = code.get("name")
488+
if num is not None and name is not None:
489+
sanitized_name = (
490+
name.lower()
491+
.replace(" ", "_")
492+
.replace("/", "_")
493+
.replace("-", "_")
494+
)
495+
exception_codes.append((num, sanitized_name))
496+
497+
logging.info(
498+
f"Loaded {len(exception_codes)} pre-resolved exception codes from {resolved_codes_file}"
499+
)
500+
501+
# Sort by exception code number and deduplicate
502+
seen_nums = set()
503+
unique_codes = []
504+
for num, name in sorted(exception_codes, key=lambda x: x[0]):
505+
if num not in seen_nums:
506+
seen_nums.add(num)
507+
unique_codes.append((num, name))
508+
509+
return unique_codes
510+
511+
except Exception as e:
512+
logging.error(
513+
f"Error loading resolved codes file {resolved_codes_file}: {e}"
514+
)
515+
# Fall back to processing YAML files
516+
517+
for dirpath, _, filenames in os.walk(ext_dir):
518+
for fname in filenames:
519+
if not fname.endswith(".yaml"):
520+
continue
521+
522+
found_files += 1
523+
path = os.path.join(dirpath, fname)
524+
525+
try:
526+
with open(path, encoding="utf-8") as f:
527+
data = yaml.safe_load(f)
528+
529+
if not isinstance(data, dict) or data.get("kind") != "extension":
530+
continue
531+
532+
found_extensions += 1
533+
ext_name = data.get("name", "unnamed")
534+
535+
# Skip extension filtering if include_all is True
536+
if not include_all:
537+
# Filter by extension requirements
538+
definedBy = data.get("definedBy")
539+
if definedBy:
540+
meets_req = parse_extension_requirements(definedBy)
541+
if not meets_req(enabled_extensions):
542+
continue
543+
544+
# Check if excluded
545+
excludedBy = data.get("excludedBy")
546+
if excludedBy:
547+
exclusion_check = parse_extension_requirements(excludedBy)
548+
if exclusion_check(enabled_extensions):
549+
continue
550+
551+
# Get exception codes
552+
for code in data.get("exception_codes", []):
553+
num = code.get("num")
554+
name = code.get("name")
555+
556+
if num is not None and name is not None:
557+
sanitized_name = (
558+
name.lower()
559+
.replace(" ", "_")
560+
.replace("/", "_")
561+
.replace("-", "_")
562+
)
563+
exception_codes.append((num, sanitized_name))
564+
565+
except Exception as e:
566+
logging.error(f"Error processing file {path}: {e}")
567+
568+
if found_extensions > 0:
569+
logging.info(
570+
f"Found {found_extensions} extension definitions in {found_files} files"
571+
)
572+
logging.info(f"Added {len(exception_codes)} exception codes to the output")
573+
else:
574+
logging.warning(f"No extension definitions found in {ext_dir}")
575+
576+
# Sort by exception code number and deduplicate
577+
seen_nums = set()
578+
unique_codes = []
579+
for num, name in sorted(exception_codes, key=lambda x: x[0]):
580+
if num not in seen_nums:
581+
seen_nums.add(num)
582+
unique_codes.append((num, name))
583+
584+
return unique_codes
585+
586+
471587
def parse_match(match_str):
472588
"""
473589
Convert the bit pattern string to an integer.

0 commit comments

Comments
 (0)