Home

Awesome

riscv-opcodes

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.

Project Structure

├── constants.py    # contains variables, constants and data-structures used in parse.py
├── encoding.h      # the template encoding.h file
├── LICENSE         # license file
├── Makefile        # makefile to generate artifacts
├── parse.py        # python file to perform checks on the instructions and generate artifacts
├── README.md       # this file
├── rv*             # instruction opcode files
└── unratified      # contains unratified instruction opcode files

File Naming Policy

This project follows a very specific file structure to define the instruction encodings. All files containing instruction encodings start with the prefix rv. These files can either be present in the root directory (if the instructions have been ratified) or the unratified directory. The exact file-naming policy and location is as mentioned below:

  1. rv_x - contains instructions common within the 32-bit and 64-bit modes of extension X.
  2. rv32_x - contains instructions present in rv32x only (absent in rv64x e.g.. brev8)
  3. rv64_x - contains instructions present in rv64x only (absent in rv32x, e.g. addw)
  4. rv_x_y - contains instructions when both extension X and Y are available/enabled. It is recommended to follow canonical ordering for such file names as specified by the spec.
  5. unratified - this directory will also contain files similar to the above policies, but will correspond to instructions which have not yet been ratified.

When an instruction is present in multiple extensions and the spec is vague in defining the extension which owns the instruction, the instruction encoding must be placed in the first canonically ordered extension and should be imported(via the $import keyword) in the remaining extensions.

Encoding Syntax

The encoding syntax uses $ to indicate keywords. As of now 2 keywords have been identified : $import and $pseudo_op (described below). The syntax also uses :: as a means to define the relationship between extension and instruction. .. is used to defined bit ranges. We use # to define comments in the files. All comments must be in a separate line. In-line comments are not supported.

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

RESTRICTIONS

Following are the restrictions one should keep in mind while defining $pseudo_ops and $imported_ops

Flow for parse.py

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. For each regular instruction, the following checks are performed :

Once the above checks are passed for a regular instruction, we then create a dictionary for this instruction which contains the following fields:

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.

Artifact Generation and Usage

The following artifacts can be generated using parse.py:

To generate all the above artifacts for all instructions currently checked in, simply run make from the root-directory. This should print the following log on the command-line:

Running with args : ['./parse.py', '-c', '-go', '-chisel', '-sverilog', '-rust', '-latex', '-spinalhdl', 'rv*', 'unratified/rv*']
Extensions selected : ['rv*', 'unratified/rv*']
INFO:: encoding.out.h generated successfully
INFO:: inst.chisel generated successfully
INFO:: inst.spinalhdl generated successfully
INFO:: inst.sverilog generated successfully
INFO:: inst.rs generated successfully
INFO:: inst.go generated successfully
INFO:: instr-table.tex generated successfully
INFO:: priv-instr-table.tex generated successfully

By default all extensions are enabled. To select only a subset of extensions you can change the EXTENSIONS variable of the makefile to contains only the file names of interest. For example if you want only the I and M extensions you can do the following:

make EXTENSIONS='rv*_i rv*_m'

Which will print the following log:

Running with args : ['./parse.py', '-c', '-go', '-chisel', '-sverilog', '-rust', '-latex', '-spinalhdl', 'rv32_i', 'rv64_i', 'rv_i', 'rv64_m', 'rv_m']
Extensions selected : ['rv32_i', 'rv64_i', 'rv_i', 'rv64_m', 'rv_m']
INFO:: encoding.out.h generated successfully
INFO:: inst.chisel generated successfully
INFO:: inst.spinalhdl generated successfully
INFO:: inst.sverilog generated successfully
INFO:: inst.rs generated successfully
INFO:: inst.go generated successfully
INFO:: instr-table.tex generated successfully
INFO:: priv-instr-table.tex generated successfully

If you only want a specific artifact you can use one or more of the following targets : c, rust, chisel, sverilog, latex. For example, if you want to generate the c based artifact with extensions as shown earlier, you can use the following command:

./parse.py -c  EXTENSIONS='rv*_i rv*_m'

Which will print the following log:

Running with args : ['./parse.py', '-c', 'EXTENSIONS=rv*_i rv*_m']
Extensions selected : ['EXTENSIONS=rv*_i rv*_m']
INFO:: encoding.out.h generated successfully

or you can also use the make command as:

make encoding.out.h  EXTENSIONS='rv*_i rv*_m'

You can use the clean target to remove all artifacts.

Adding a new extension

To add a new extension of instructions, create an appropriate rv* file based on the policy defined in File Structure. Run make from the root directory to ensure that all checks pass and all artifacts are created correctly. A successful run should print the following log on the terminal:

Running with args : ['./parse.py', '-c', '-chisel', '-sverilog', '-rust', '-latex', 'rv*', 'unratified/rv*']
Extensions selected : ['rv*', 'unratified/rv*']
INFO:: encoding.out.h generated successfully
INFO:: inst.chisel generated successfully
INFO:: inst.sverilog generated successfully
INFO:: inst.rs generated successfully
INFO:: instr-table.tex generated successfully
INFO:: priv-instr-table.tex generated successfully

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 the terminal like below:

DEBUG:: Collecting standard instructions first
DEBUG:: Parsing File: ./rv_i
DEBUG::      Processing line: lui     rd imm20 6..2=0x0D 1..0=3
DEBUG::      Processing line: auipc   rd imm20 6..2=0x05 1..0=3
DEBUG::      Processing line: jal     rd jimm20                          6..2=0x1b 1..0=3
DEBUG::      Processing line: jalr    rd rs1 imm12              14..12=0 6..2=0x19 1..0=3
DEBUG::      Processing line: beq     bimm12hi rs1 rs2 bimm12lo 14..12=0 6..2=0x18 1..0=3
DEBUG::      Processing line: bne     bimm12hi rs1 rs2 bimm12lo 14..12=1 6..2=0x18 1..0=3

How do I find where an instruction is defined?

You can use grep "^\s*<instr-name>" rv* unratified/rv* OR run make and open instr_dict.json and search for the instruction you are looking for. Within that instruction the extension field will indicate which file the instruction was picked from.