Skip to content

Commit

Permalink
Merge pull request #292 from Timmmm/user/timh/precommit
Browse files Browse the repository at this point in the history
Add basic pre-commit config, apply fixes and run it in CI
  • Loading branch information
aswaterman authored Oct 7, 2024
2 parents 9653f6a + 3ba18d3 commit 50e2986
Show file tree
Hide file tree
Showing 48 changed files with 803 additions and 615 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ jobs:
python-version: 3.8
- name: Install PyYAML
run: |
pip3 install -r requirements.txt
pip3 install coverage
pip3 install -r requirements.txt
pip3 install coverage
- name: Run pre-commit
run: |
python3 -m pip install pre-commit
pre-commit run --all-files
- name: Generate
run: coverage run ./parse.py -c -chisel -sverilog -rust -latex -spinalhdl -go "rv*" "unratified/rv*"
- name: Check C output
Expand All @@ -27,4 +31,3 @@ jobs:
run: coverage xml
- name: Upload coverage
uses: codecov/codecov-action@v2

1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,3 @@ jobs:
run: coverage xml
- name: Upload coverage
uses: codecov/codecov-action@v2

38 changes: 38 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-merge-conflict
- id: check-symlinks
- id: trailing-whitespace
- id: end-of-file-fixer

- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
# Configure isort to use Black's import style to prevent formatting
# conflicts. As isort and Black may repeatedly reformat the same code,
# causing pre-commit to fail.
args: ["--profile", "black"]

- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
- id: black

# TODO: Enable this when lints are fixed.
# - repo: https://github.com/PyCQA/pylint
# rev: v3.3.1
# hooks:
# - id: pylint
# additional_dependencies:
# - "pyyaml==6.0.2"

# TODO: Enable this when types are added.
# - repo: https://github.com/RobertCraigie/pyright-python
# rev: v1.1.383
# hooks:
# - id: pyright
# additional_dependencies:
# - "pyyaml==6.0.2"
43 changes: 21 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ This repo enumerates standard RISC-V instruction opcodes and control and
status registers. It also contains a script to convert them into several
formats (C, Scala, LaTeX).

Artifacts (encoding.h, latex-tables, etc) from this repo are used in other
tools and projects like Spike, PK, RISC-V Manual, etc.
Artifacts (encoding.h, latex-tables, etc) from this repo are used in other
tools and projects like Spike, PK, RISC-V Manual, etc.

## Project Structure

Expand Down Expand Up @@ -43,7 +43,7 @@ The encoding syntax uses `$` to indicate keywords. As of now 2 keywords have bee

Instruction syntaxes used in this project are broadly categorized into three:

- **regular instructions** :- these are instructions which hold a unique opcode in the encoding space. A very generic syntax guideline
- **regular instructions** :- these are instructions which hold a unique opcode in the encoding space. A very generic syntax guideline
for these instructions is as follows:
```
<instruction name> <arguments>
Expand All @@ -55,19 +55,19 @@ Instruction syntaxes used in this project are broadly categorized into three:
lui rd imm20 6..2=0x0D 1..0=3
beq bimm12hi rs1 rs2 bimm12lo 14..12=0 6..2=0x18 1..0=3
```
The bit encodings are usually of 2 types:
The bit encodings are usually of 2 types:
- *single bit assignment* : here the value of a single bit is assigned using syntax `<bit-position>=<value>`. For e.g. `6=1` means bit 6 should be 1. Here the value must be 1 or 0.
- *range assignment*: here a range of bits is assigned a value using syntax: `<msb>..<lsb>=<val>`. For e.g. `31..24=0xab`. The value here can be either unsigned integer, hex (0x) or binary (0b).
- *range assignment*: here a range of bits is assigned a value using syntax: `<msb>..<lsb>=<val>`. For e.g. `31..24=0xab`. The value here can be either unsigned integer, hex (0x) or binary (0b).

- **pseudo_instructions** (a.k.a pseudo\_ops) - These are instructions which are aliases of regular instructions. Their encodings force
- **pseudo_instructions** (a.k.a pseudo\_ops) - These are instructions which are aliases of regular instructions. Their encodings force
certain restrictions over the regular instruction. The syntax for such instructions uses the `$pseudo_op` keyword as follows:
```
$pseudo_op <extension>::<base-instruction> <instruction name> <instruction args> <bit-encodings>
```
Here the `<extension>` specifies the extension which contains the base instruction. `<base-instruction>` indicates the name of the instruction
this pseudo-instruction is an alias of. The remaining fields are the same as the regular instruction syntax, where all the args and the fields
Here the `<extension>` specifies the extension which contains the base instruction. `<base-instruction>` indicates the name of the instruction
this pseudo-instruction is an alias of. The remaining fields are the same as the regular instruction syntax, where all the args and the fields
of the pseudo instruction are specified.

Example:
```
$pseudo_op rv_zicsr::csrrs frflags rd 19..15=0 31..20=0x001 14..12=2 6..2=0x1C 1..0=3
Expand All @@ -78,7 +78,7 @@ Instruction syntaxes used in this project are broadly categorized into three:
define the new instruction as a pseudo\_op of the unratified regular
instruction, as this avoids existence of overlapping opcodes for users who are
experimenting with unratified extensions as well.

- **imported_instructions** - these are instructions which are borrowed from an extension into a new/different extension/sub-extension. Only regular instructions can be imported. Pseudo-op or already imported instructions cannot be imported. Example:
```
$import rv32_zkne::aes32esmi
Expand All @@ -96,30 +96,30 @@ Following are the restrictions one should keep in mind while defining $pseudo\_o

The `parse.py` python file is used to perform checks on the current set of instruction encodings and also generates multiple artifacts : latex tables, encoding.h header file, etc. This section will provide a brief overview of the flow within the python file.

To start with, `parse.py` creates a list of all `rv*` files currently checked into the repo (including those inside the `unratified` directory as well).
It then starts parsing each file line by line. In the first pass, we only capture regular instructions and ignore the imported or pseudo instructions.
To start with, `parse.py` creates a list of all `rv*` files currently checked into the repo (including those inside the `unratified` directory as well).
It then starts parsing each file line by line. In the first pass, we only capture regular instructions and ignore the imported or pseudo instructions.
For each regular instruction, the following checks are performed :

- for range-assignment syntax, the *msb* position must be higher than the *lsb* position
- for range-assignment syntax, the value of the range must representable in the space identified by *msb* and *lsb*
- values for the same bit positions should not be defined multiple times.
- All bit positions must be accounted for (either as args or constant value fields)

Once the above checks are passed for a regular instruction, we then create a dictionary for this instruction which contains the following fields:
- encoding : contains a 32-bit string defining the encoding of the instruction. Here `-` is used to represent instruction argument fields
- extension : string indicating which extension/filename this instruction was picked from
- mask : a 32-bit hex value indicating the bits of the encodings that must be checked for legality of that instruction
- match : a 32-bit hex value indicating the values the encoding must take for the bits which are set as 1 in the mask above
- variable_fields : This is list of args required by the instruction

The above dictionary elements are added to a main `instr_dict` dictionary under the instruction node. This process continues until all regular
instructions have been processed. In the second pass, we now process the `$pseudo_op` instructions. Here, we first check if the *base-instruction* of
this pseudo instruction exists in the relevant extension/filename or not. If it is present, the the remaining part of the syntax undergoes the same
checks as above. Once the checks pass and if the *base-instruction* is not already added to the main `instr_dict` then the pseudo-instruction is added to
The above dictionary elements are added to a main `instr_dict` dictionary under the instruction node. This process continues until all regular
instructions have been processed. In the second pass, we now process the `$pseudo_op` instructions. Here, we first check if the *base-instruction* of
this pseudo instruction exists in the relevant extension/filename or not. If it is present, the the remaining part of the syntax undergoes the same
checks as above. Once the checks pass and if the *base-instruction* is not already added to the main `instr_dict` then the pseudo-instruction is added to
the list. In the third, and final, pass we process the imported instructions.

The case where the *base-instruction* for a pseudo-instruction may not be present in the main `instr_dict` after the first pass is if the only a subset
of extensions are being processed such that the *base-instruction* is not included.
The case where the *base-instruction* for a pseudo-instruction may not be present in the main `instr_dict` after the first pass is if the only a subset
of extensions are being processed such that the *base-instruction* is not included.


## Artifact Generation and Usage
Expand Down Expand Up @@ -165,7 +165,7 @@ By default all extensions are enabled. To select only a subset of extensions you
For example if you want only the I and M extensions you can do the following:

```bash
make EXTENSIONS='rv*_i rv*_m'
make EXTENSIONS='rv*_i rv*_m'
```

Which will print the following log:
Expand Down Expand Up @@ -204,7 +204,7 @@ Create a PR for review.

## Enabling Debug logs in parse.py

To enable debug logs in parse.py change `level=logging.INFO` to `level=logging.DEBUG` and run the python command. You will now see debug statements on
To enable debug logs in parse.py change `level=logging.INFO` to `level=logging.DEBUG` and run the python command. You will now see debug statements on
the terminal like below:
```
DEBUG:: Collecting standard instructions first
Expand All @@ -223,4 +223,3 @@ You can use `grep "^\s*<instr-name>" rv* unratified/rv*` OR run `make` and open
`instr_dict.yaml` and search of the instruction you are looking for. Within that
instruction the `extension` field will indicate which file the instruction was
picked from.

Loading

0 comments on commit 50e2986

Please sign in to comment.