diff --git a/process_json.py b/process_json.py index 9d58acc..7921c16 100644 --- a/process_json.py +++ b/process_json.py @@ -1,9 +1,11 @@ -from abc import abstractmethod +#!/usr/bin/env python3 import argparse import json +from abc import abstractmethod from pathlib import Path from typing import Union -from jsonschema import validate, ValidationError + +from jsonschema import ValidationError, validate INSTRUCTION_SCHEMA = { "type": "object", @@ -14,13 +16,13 @@ INSTRUCTION_SCHEMA = { "type": "object", "properties": { "name": {"type": "string"}, - "decoding": {"type": "string"} + "decoding": {"type": "string"}, }, - "required": ["name", "decoding"] - } + "required": ["name", "decoding"], + }, } }, - "required": ["instructions"] + "required": ["instructions"], } @@ -36,29 +38,33 @@ class EncodingPart: class Field(EncodingPart): def get_bounds(self, s: str) -> tuple[int, int]: if ":" in s: - end, start = s[s.index("["):].strip("[").rstrip("]").split(":") + end, start = s[s.index("[") :].strip("[").rstrip("]").split(":") else: - start = end = int(s[s.index("["):].strip("[").rstrip("]")) + start = end = int(s[s.index("[") :].strip("[").rstrip("]")) return int(start), int(end) def __init__(self, s: str) -> None: assert "[" in s and "]" in s assert s.count("[") == 1 and s.count("]") self.start, self.end = self.get_bounds(s) - self.size = self.end-self.start+1 - self.name = s[:s.index("[")] + self.size = self.end - self.start + 1 + self.name = s[: s.index("[")] def __str__(self) -> str: shift = " << " if self.pos >= self.start else ">>" shamt = str(self.pos) - value_str = f"(\\{self.name} & 0b{self.size*'1'})" + value_str = f"(\\{self.name} & 0b{self.size * '1'})" if self.start: - value_str = f"(\\{self.name} & 0b{self.size*'1'}{self.start*'0'})" - shamt = str(abs(self.pos-self.start)) - return value_str + shift + shamt if self.pos-self.start else value_str + value_str = f"(\\{self.name} & 0b{self.size * '1'}{self.start * '0'})" + shamt = str(abs(self.pos - self.start)) + return value_str + shift + shamt if self.pos - self.start else value_str def coredsl(self) -> str: - middle = f"[{self.end}:{self.start}]" if self.size > 1 else f"[{self.end}:{self.end}]" + middle = ( + f"[{self.end}:{self.start}]" + if self.size > 1 + else f"[{self.end}:{self.end}]" + ) return self.name + middle def __repr__(self) -> str: @@ -73,11 +79,11 @@ class Literal(EncodingPart): def __str__(self) -> str: shamt = str(self.pos) - value_str = format(self.value, f'#0{self.size+2}b') + value_str = format(self.value, f"#0{self.size + 2}b") return value_str + " << " + shamt if self.pos else value_str def coredsl(self) -> str: - return format(self.value, f'#0{self.size+2}b') + return format(self.value, f"#0{self.size + 2}b") def __repr__(self) -> str: return "Literal: " + self.coredsl() @@ -102,17 +108,27 @@ class Encoding: self.name = d["name"].upper() def _get_masked_enc(self) -> str: - masked_enc = "".join([bin(elem.value)[2:].zfill(elem.size) if isinstance( - elem, Literal) else elem.size*"x" for elem in self.parts]) + masked_enc = "".join( + [ + bin(elem.value)[2:].zfill(elem.size) + if isinstance(elem, Literal) + else elem.size * "x" + for elem in self.parts + ] + ) return masked_enc def _collapse_literals(self): new_parts: list[EncodingPart] = [] for part in self.parts: - if isinstance(part, Literal) and new_parts and isinstance(new_parts[-1], Literal): + if ( + isinstance(part, Literal) + and new_parts + and isinstance(new_parts[-1], Literal) + ): new_value = (new_parts[-1].value << part.size) + part.value new_size = new_parts[-1].size + part.size - combined_val = format(new_value, f'#0{new_size+2}b') + combined_val = format(new_value, f"#0{new_size + 2}b") collapsed = Literal(combined_val[2:]) new_parts[-1] = collapsed else: @@ -141,21 +157,21 @@ class Encoding: else: return (2, name) - field_names = [ - elem.name for elem in self.parts if isinstance(elem, Field)] + field_names = [elem.name for elem in self.parts if isinstance(elem, Field)] unique_field_names = list(dict.fromkeys(field_names)) - fields_str = ', '.join(sorted(unique_field_names, key=riscv_sort_key)) - header = f".macro {self.name}{',' if len(unique_field_names)> 0 else ''} {fields_str}" + fields_str = ", ".join(sorted(unique_field_names, key=riscv_sort_key)) + header = f".macro {self.name}{',' if len(unique_field_names) > 0 else ''} {fields_str}" indent = " " - comment = "# Encoding parts: " + \ - " ".join([elem.coredsl() for elem in self.parts]) + comment = "# Encoding parts: " + " ".join( + [elem.coredsl() for elem in self.parts] + ) self._collapse_literals() strs = [str(elem) for elem in self.parts] content = ".word " + " | ".join(strs) tail = ".endm" - return "\n".join([header, indent+comment, indent+content, tail]) + return "\n".join([header, indent + comment, indent + content, tail]) def parse_args(): @@ -163,20 +179,18 @@ def parse_args(): description="Generate assembler macros from CoreDSL2JSON output." ) parser.add_argument( - "path", - type=Path, - help="Path to the JSON file generated by CoreDSL2JSON." + "path", type=Path, help="Path to the JSON file generated by CoreDSL2JSON." ) parser.add_argument( "--name", type=str, - help="Name of the instruction to generate the macro for (optional)." + help="Name of the instruction to generate the macro for (optional).", ) parser.add_argument( "--size", type=int, default=-1, - help="Instruction size in bits. If not set, checks lowest bits of the instruction and determines size according to default RISC-V specification." + help="Instruction size in bits. If not set, checks lowest bits of the instruction and determines size according to default RISC-V specification.", ) return parser.parse_args()