Skip to content

Commit

Permalink
Reorganise Python code using pyproject.toml
Browse files Browse the repository at this point in the history
Add pyproject.toml (the modern alternative to requirements.txt), making this a proper Python package that can be installed via pip and potentially uploaded to PyPI.

This required moving all of the data files into the package, and reading them using importlib. This gives a much nicer file structure. It does require Python 3.7 but that is over 6 years old and easy to install on old Linux distros like RHEL 8.
  • Loading branch information
Timmmm committed Oct 4, 2024
1 parent 0033120 commit d980336
Show file tree
Hide file tree
Showing 120 changed files with 81 additions and 26 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ inst.sverilog
instr_dict.yaml

__pycache__/

/.venv
17 changes: 9 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,36 @@ ENV_H := ../riscv-tests/env/encoding.h
OPENOCD_H := ../riscv-openocd/src/target/riscv/encoding.h
INSTALL_HEADER_FILES := $(ISASIM_H) $(PK_H) $(ENV_H) $(OPENOCD_H)

.PHONY : default
default: everything

.PHONY : everything
everything:
@./parse.py -c -go -chisel -sverilog -rust -latex -spinalhdl $(EXTENSIONS)
@./parse.sh -c -go -chisel -sverilog -rust -latex -spinalhdl $(EXTENSIONS)

.PHONY : encoding.out.h
encoding.out.h:
@./parse.py -c rv* unratified/rv_* unratified/rv32* unratified/rv64*
@./parse.sh -c rv* unratified/rv_* unratified/rv32* unratified/rv64*

.PHONY : inst.chisel
inst.chisel:
@./parse.py -chisel $(EXTENSIONS)
@./parse.sh -chisel $(EXTENSIONS)

.PHONY : inst.go
inst.go:
@./parse.py -go $(EXTENSIONS)
@./parse.sh -go $(EXTENSIONS)

.PHONY : latex
latex:
@./parse.py -latex $(EXTENSIONS)
@./parse.sh -latex $(EXTENSIONS)

.PHONY : inst.sverilog
inst.sverilog:
@./parse.py -sverilog $(EXTENSIONS)
@./parse.sh -sverilog $(EXTENSIONS)

.PHONY : inst.rs
inst.rs:
@./parse.py -rust $(EXTENSIONS)
@./parse.sh -rust $(EXTENSIONS)

.PHONY : clean
clean:
Expand All @@ -51,4 +52,4 @@ priv-instr-table.tex: latex

.PHONY: inst.spinalhdl
inst.spinalhdl:
@./parse.py -spinalhdl $(EXTENSIONS)
@./parse.sh -spinalhdl $(EXTENSIONS)
20 changes: 20 additions & 0 deletions parse.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash

# This is a simple script to handle Python's venv nonsense.

set -e

# Create the virtual environment if it doesn't already exist.
if [ ! -e ".venv/bin/activate" ]; then
echo "Creating venv"
python3 -m venv ".venv"
fi

# Activate it
. ".venv/bin/activate"

# See https://github.com/microsoft/pyright/issues/6209#issuecomment-1772924061
# for why editable_mode=compat is required.
python3 -m pip install --config-settings editable_mode=compat --editable .

riscv_opcodes $@
26 changes: 26 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[project]
name = "riscv-opcodes"
version = "0.1.0"
description = "Machine readable information about RISC-V instruction opcodes"
dependencies = [
"PyYAML>=6.0.0, <7"
]
readme = "README.md"
requires-python = ">= 3.7"

[project.scripts]
riscv_opcodes = "riscv_opcodes.parse:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.rye]
managed = true
dev-dependencies = []

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/riscv_opcodes"]
1 change: 0 additions & 1 deletion requirements.txt

This file was deleted.

10 changes: 5 additions & 5 deletions constants.py → src/riscv_opcodes/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import re
import csv

from importlib.resources import open_text

overlapping_extensions = {
'rv_zcmt': {'rv_c_d'},
Expand Down Expand Up @@ -42,14 +42,14 @@ def read_csv(filename):
"""
Reads a CSV file and returns a list of tuples.
Each tuple contains an integer value (from the first column) and a string (from the second column).
Args:
filename (str): The name of the CSV file to read.
Returns:
list of tuple: A list of (int, str) tuples extracted from the CSV file.
"""
with open(filename) as f:
with open_text("riscv_opcodes.opcodes", filename) as f:
csv_reader = csv.reader(f, skipinitialspace=True)
return [(int(row[0], 0), row[1]) for row in csv_reader]

Expand All @@ -58,7 +58,7 @@ def read_csv(filename):
csrs32 = read_csv("csrs32.csv")

arg_lut = {}
with open("arg_lut.csv") as f:
with open_text("riscv_opcodes.opcodes", "arg_lut.csv") as f:
csv_reader = csv.reader(f, skipinitialspace=True)
for row in csv_reader:
k = row[0]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
29 changes: 18 additions & 11 deletions parse.py → src/riscv_opcodes/parse.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

from constants import *
from .constants import *
import copy
import re
import glob
Expand All @@ -11,6 +11,9 @@
import yaml
import sys

from importlib.resources import open_text, read_text, contents
from fnmatch import fnmatch

pp = pprint.PrettyPrinter(indent=2)
logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s')

Expand Down Expand Up @@ -118,7 +121,7 @@ def process_enc_line(line, ext):
existing_arg, new_arg = parts
if existing_arg in arg_lut:
arg_lut[a] = arg_lut[existing_arg]

else:
logging.error(f' Found field {existing_arg} in variable {a} in instruction {name} whose mapping in arg_lut does not exist')
raise SystemExit(1)
Expand Down Expand Up @@ -242,19 +245,20 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]):
'''
opcodes_dir = os.path.dirname(os.path.realpath(__file__))
instr_dict = {}

# file_names contains all files to be parsed in the riscv-opcodes directory
file_names = []
for fil in file_filter:
file_names += glob.glob(f'{opcodes_dir}/{fil}')
for package in ["riscv_opcodes.opcodes.ratified", "riscv_opcodes.opcodes.unratified"]:
for file in contents(package):
if any(fnmatch(file, fil) for fil in file_filter):
file_names += file
file_names.sort(reverse=True)
# first pass if for standard/regular instructions
logging.debug('Collecting standard instructions first')
for f in file_names:
logging.debug(f'Parsing File: {f} for standard instructions')
with open(f) as fp:
with open_text(f) as fp:
lines = (line.rstrip()
for line in fp) # All lines including the blank ones
lines = list(line for line in lines if line) # Non-blank lines
Expand Down Expand Up @@ -952,8 +956,7 @@ def make_c(instr_dict):
mask = ((1 << (end - begin + 1)) - 1) << begin
arg_str += f"#define INSN_FIELD_{sanitized_name.upper()} {hex(mask)}\n"

with open(f'{os.path.dirname(__file__)}/encoding.h', 'r') as file:
enc_header = file.read()
enc_header = read_text("riscv_opcodes", "encoding.h")

commit = os.popen('git log -1 --format="format:%h"').read()

Expand Down Expand Up @@ -1027,7 +1030,7 @@ def make_go(instr_dict):
instr_str += f''' case A{i.upper().replace("_","")}:
return &inst{{ {hex(opcode)}, {hex(funct3)}, {hex(rs1)}, {hex(rs2)}, {signed(csr,12)}, {hex(funct7)} }}
'''

with open('inst.go','w') as file:
file.write(prelude)
file.write(instr_str)
Expand All @@ -1046,7 +1049,7 @@ def signed(value, width):
return value - (1<<width)


if __name__ == "__main__":
def main():
print(f'Running with args : {sys.argv}')

extensions = sys.argv[1:]
Expand All @@ -1066,7 +1069,7 @@ def signed(value, width):
instr_dict = collections.OrderedDict(sorted(instr_dict.items()))

if '-c' in sys.argv[1:]:
instr_dict_c = create_inst_dict(extensions, False,
instr_dict_c = create_inst_dict(extensions, False,
include_pseudo_ops=emitted_pseudo_ops)
instr_dict_c = collections.OrderedDict(sorted(instr_dict_c.items()))
make_c(instr_dict_c)
Expand Down Expand Up @@ -1097,3 +1100,7 @@ def signed(value, width):
logging.info('instr-table.tex generated successfully')
make_priv_latex_table()
logging.info('priv-instr-table.tex generated successfully')


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion test.py → src/riscv_opcodes/test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

from parse import *
from .parse import *
import logging
import unittest

Expand Down

0 comments on commit d980336

Please sign in to comment.