From d5ffd0f68976b5f0e3a4cd314d40067a107a48b0 Mon Sep 17 00:00:00 2001 From: Tim Hutt Date: Fri, 4 Oct 2024 21:46:51 +0100 Subject: [PATCH 1/2] Add basic pre-commit setup To avoid an enormouse commit, pylint and pyright are commented out for now. --- .github/workflows/python-app.yml | 9 +++++--- .pre-commit-config.yaml | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 86d5f0a4..4aac843c 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -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 @@ -27,4 +31,3 @@ jobs: run: coverage xml - name: Upload coverage uses: codecov/codecov-action@v2 - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..cca4b3b9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -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" From 3ba18d3b772f66caabc94c47273d8b42d9db5d31 Mon Sep 17 00:00:00 2001 From: Tim Hutt Date: Fri, 4 Oct 2024 21:47:46 +0100 Subject: [PATCH 2/2] Apply pre-commit fixes --- .github/workflows/tests.yml | 1 - README.md | 43 +- constants.py | 406 ++++++++++-------- parse.py | 822 +++++++++++++++++++++--------------- rv32_c | 4 +- rv32_c_f | 1 - rv32_zbkb | 4 +- rv32_zbs | 1 - rv32_zicntr | 1 - rv32_zk | 5 +- rv32_zkn | 5 +- rv32_zknd | 1 - rv32_zkne | 1 - rv32_zknh | 1 - rv32_zks | 5 +- rv64_f | 1 - rv64_h | 1 - rv64_i | 2 +- rv64_q | 1 - rv64_zbb | 2 +- rv64_zbs | 1 - rv64_zk | 1 - rv64_zkn | 1 - rv64_zknd | 1 - rv64_zknh | 1 - rv_c | 4 - rv_c_d | 1 - rv_d | 2 - rv_f | 1 - rv_h | 1 - rv_q | 1 - rv_s | 1 - rv_svinval | 1 - rv_system | 1 - rv_zbc | 1 - rv_zbs | 1 - rv_zfh | 1 - rv_zicntr | 1 - rv_zicond | 1 - rv_zifencei | 1 - rv_zks | 1 - rv_zksed | 1 - rv_zksh | 1 - rv_zvkg | 1 - test.py | 34 +- unratified/rv_zicfiss | 1 - 46 files changed, 759 insertions(+), 612 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 93572931..a30716ac 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,4 +25,3 @@ jobs: run: coverage xml - name: Upload coverage uses: codecov/codecov-action@v2 - diff --git a/README.md b/README.md index f1a71d3e..e6702ed6 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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: ``` @@ -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 `=`. 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: `..=`. 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: `..=`. 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 :: ``` - Here the `` specifies the extension which contains the 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 `` specifies the extension which contains the 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 @@ -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 @@ -96,15 +96,15 @@ 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 @@ -112,14 +112,14 @@ Once the above checks are passed for a regular instruction, we then create a dic - 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 @@ -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: @@ -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 @@ -223,4 +223,3 @@ You can use `grep "^\s*" 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. - diff --git a/constants.py b/constants.py index 18b0613b..94e8f2d2 100644 --- a/constants.py +++ b/constants.py @@ -1,51 +1,56 @@ -import re import csv - +import re overlapping_extensions = { - 'rv_zcmt': {'rv_c_d'}, - 'rv_zcmp': {'rv_c_d'}, - 'rv_c': {'rv_zcmop'}, + "rv_zcmt": {"rv_c_d"}, + "rv_zcmp": {"rv_c_d"}, + "rv_c": {"rv_zcmop"}, } overlapping_instructions = { - 'c_addi': {'c_nop'}, - 'c_lui': {'c_addi16sp'}, - 'c_mv': {'c_jr'}, - 'c_jalr': {'c_ebreak'}, - 'c_add': {'c_ebreak', 'c_jalr'}, + "c_addi": {"c_nop"}, + "c_lui": {"c_addi16sp"}, + "c_mv": {"c_jr"}, + "c_jalr": {"c_ebreak"}, + "c_add": {"c_ebreak", "c_jalr"}, } -isa_regex = \ -re.compile("^RV(32|64|128)[IE]+[ABCDEFGHJKLMNPQSTUVX]*(Zicsr|Zifencei|Zihintpause|Zam|Ztso|Zkne|Zknd|Zknh|Zkse|Zksh|Zkg|Zkb|Zkr|Zks|Zkn|Zba|Zbc|Zbb|Zbp|Zbr|Zbm|Zbs|Zbe|Zbf|Zbt|Zmmul|Zbpbo|Zca|Zcf|Zcd|Zcb|Zcmp|Zcmt){,1}(_Zicsr){,1}(_Zifencei){,1}(_Zihintpause){,1}(_Zmmul){,1}(_Zam){,1}(_Zba){,1}(_Zbb){,1}(_Zbc){,1}(_Zbe){,1}(_Zbf){,1}(_Zbm){,1}(_Zbp){,1}(_Zbpbo){,1}(_Zbr){,1}(_Zbs){,1}(_Zbt){,1}(_Zkb){,1}(_Zkg){,1}(_Zkr){,1}(_Zks){,1}(_Zkn){,1}(_Zknd){,1}(_Zkne){,1}(_Zknh){,1}(_Zkse){,1}(_Zksh){,1}(_Ztso){,1}(_Zca){,1}(_Zcf){,1}(_Zcd){,1}(_Zcb){,1}(_Zcmp){,1}(_Zcmt){,1}$") +isa_regex = re.compile( + "^RV(32|64|128)[IE]+[ABCDEFGHJKLMNPQSTUVX]*(Zicsr|Zifencei|Zihintpause|Zam|Ztso|Zkne|Zknd|Zknh|Zkse|Zksh|Zkg|Zkb|Zkr|Zks|Zkn|Zba|Zbc|Zbb|Zbp|Zbr|Zbm|Zbs|Zbe|Zbf|Zbt|Zmmul|Zbpbo|Zca|Zcf|Zcd|Zcb|Zcmp|Zcmt){,1}(_Zicsr){,1}(_Zifencei){,1}(_Zihintpause){,1}(_Zmmul){,1}(_Zam){,1}(_Zba){,1}(_Zbb){,1}(_Zbc){,1}(_Zbe){,1}(_Zbf){,1}(_Zbm){,1}(_Zbp){,1}(_Zbpbo){,1}(_Zbr){,1}(_Zbs){,1}(_Zbt){,1}(_Zkb){,1}(_Zkg){,1}(_Zkr){,1}(_Zks){,1}(_Zkn){,1}(_Zknd){,1}(_Zkne){,1}(_Zknh){,1}(_Zkse){,1}(_Zksh){,1}(_Ztso){,1}(_Zca){,1}(_Zcf){,1}(_Zcd){,1}(_Zcb){,1}(_Zcmp){,1}(_Zcmt){,1}$" +) # regex to find ..= patterns in instruction fixed_ranges = re.compile( - '\s*(?P\d+.?)\.\.(?P\d+.?)\s*=\s*(?P\d[\w]*)[\s$]*', re.M) + "\s*(?P\d+.?)\.\.(?P\d+.?)\s*=\s*(?P\d[\w]*)[\s$]*", re.M +) # regex to find = patterns in instructions -#single_fixed = re.compile('\s+(?P\d+)=(?P[\w\d]*)[\s$]*', re.M) -single_fixed = re.compile('(?:^|[\s])(?P\d+)=(?P[\w]*)((?=\s|$))', re.M) +# single_fixed = re.compile('\s+(?P\d+)=(?P[\w\d]*)[\s$]*', re.M) +single_fixed = re.compile("(?:^|[\s])(?P\d+)=(?P[\w]*)((?=\s|$))", re.M) # regex to find the overloading condition variable -var_regex = re.compile('(?P[a-zA-Z][\w\d]*)\s*=\s*.*?[\s$]*', re.M) +var_regex = re.compile("(?P[a-zA-Z][\w\d]*)\s*=\s*.*?[\s$]*", re.M) # regex for pseudo op instructions returns the dependent filename, dependent # instruction, the pseudo op name and the encoding string pseudo_regex = re.compile( - '^\$pseudo_op\s+(?Prv[\d]*_[\w].*)::\s*(?P.*?)\s+(?P.*?)\s+(?P.*)$' -, re.M) + "^\$pseudo_op\s+(?Prv[\d]*_[\w].*)::\s*(?P.*?)\s+(?P.*?)\s+(?P.*)$", + re.M, +) + +imported_regex = re.compile( + "^\s*\$import\s*(?P.*)\s*::\s*(?P.*)", re.M +) -imported_regex = re.compile('^\s*\$import\s*(?P.*)\s*::\s*(?P.*)', re.M) 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. """ @@ -53,6 +58,7 @@ def read_csv(filename): csv_reader = csv.reader(f, skipinitialspace=True) return [(int(row[0], 0), row[1]) for row in csv_reader] + causes = read_csv("causes.csv") csrs = read_csv("csrs.csv") csrs32 = read_csv("csrs32.csv") @@ -66,181 +72,213 @@ def read_csv(filename): arg_lut[k] = v # for mop -arg_lut['mop_r_t_30'] = (30,30) -arg_lut['mop_r_t_27_26'] = (27,26) -arg_lut['mop_r_t_21_20'] = (21, 20) -arg_lut['mop_rr_t_30'] = (30,30) -arg_lut['mop_rr_t_27_26'] = (27, 26) -arg_lut['c_mop_t'] = (10,8) +arg_lut["mop_r_t_30"] = (30, 30) +arg_lut["mop_r_t_27_26"] = (27, 26) +arg_lut["mop_r_t_21_20"] = (21, 20) +arg_lut["mop_rr_t_30"] = (30, 30) +arg_lut["mop_rr_t_27_26"] = (27, 26) +arg_lut["c_mop_t"] = (10, 8) # dictionary containing the mapping of the argument to the what the fields in # the latex table should be latex_mapping = {} -latex_mapping['imm12'] = 'imm[11:0]' -latex_mapping['rs1'] = 'rs1' -latex_mapping['rs2'] = 'rs2' -latex_mapping['rd'] = 'rd' -latex_mapping['imm20'] = 'imm[31:12]' -latex_mapping['bimm12hi'] = 'imm[12$\\vert$10:5]' -latex_mapping['bimm12lo'] = 'imm[4:1$\\vert$11]' -latex_mapping['imm12hi'] = 'imm[11:5]' -latex_mapping['imm12lo'] = 'imm[4:0]' -latex_mapping['jimm20'] = 'imm[20$\\vert$10:1$\\vert$11$\\vert$19:12]' -latex_mapping['zimm'] = 'uimm' -latex_mapping['shamtw'] = 'shamt' -latex_mapping['shamtd'] = 'shamt' -latex_mapping['shamtq'] = 'shamt' -latex_mapping['rd_p'] = "rd\\,$'$" -latex_mapping['rs1_p'] = "rs1\\,$'$" -latex_mapping['rs2_p'] = "rs2\\,$'$" -latex_mapping['rd_rs1_n0'] = 'rd/rs$\\neq$0' -latex_mapping['rd_rs1_p'] = "rs1\\,$'$/rs2\\,$'$" -latex_mapping['c_rs2'] = 'rs2' -latex_mapping['c_rs2_n0'] = 'rs2$\\neq$0' -latex_mapping['rd_n0'] = 'rd$\\neq$0' -latex_mapping['rs1_n0'] = 'rs1$\\neq$0' -latex_mapping['c_rs1_n0'] = 'rs1$\\neq$0' -latex_mapping['rd_rs1'] = 'rd/rs1' -latex_mapping['zimm6hi'] = 'uimm[5]' -latex_mapping['zimm6lo'] = 'uimm[4:0]' -latex_mapping['c_nzuimm10'] = "nzuimm[5:4$\\vert$9:6$\\vert$2$\\vert$3]" -latex_mapping['c_uimm7lo'] = 'uimm[2$\\vert$6]' -latex_mapping['c_uimm7hi'] = 'uimm[5:3]' -latex_mapping['c_uimm8lo'] = 'uimm[7:6]' -latex_mapping['c_uimm8hi'] = 'uimm[5:3]' -latex_mapping['c_uimm9lo'] = 'uimm[7:6]' -latex_mapping['c_uimm9hi'] = 'uimm[5:4$\\vert$8]' -latex_mapping['c_nzimm6lo'] = 'nzimm[4:0]' -latex_mapping['c_nzimm6hi'] = 'nzimm[5]' -latex_mapping['c_imm6lo'] = 'imm[4:0]' -latex_mapping['c_imm6hi'] = 'imm[5]' -latex_mapping['c_nzimm10hi'] = 'nzimm[9]' -latex_mapping['c_nzimm10lo'] = 'nzimm[4$\\vert$6$\\vert$8:7$\\vert$5]' -latex_mapping['c_nzimm18hi'] = 'nzimm[17]' -latex_mapping['c_nzimm18lo'] = 'nzimm[16:12]' -latex_mapping['c_imm12'] = 'imm[11$\\vert$4$\\vert$9:8$\\vert$10$\\vert$6$\\vert$7$\\vert$3:1$\\vert$5]' -latex_mapping['c_bimm9lo'] = 'imm[7:6$\\vert$2:1$\\vert$5]' -latex_mapping['c_bimm9hi'] = 'imm[8$\\vert$4:3]' -latex_mapping['c_nzuimm5'] = 'nzuimm[4:0]' -latex_mapping['c_nzuimm6lo'] = 'nzuimm[4:0]' -latex_mapping['c_nzuimm6hi'] = 'nzuimm[5]' -latex_mapping['c_uimm8splo'] = 'uimm[4:2$\\vert$7:6]' -latex_mapping['c_uimm8sphi'] = 'uimm[5]' -latex_mapping['c_uimm8sp_s'] = 'uimm[5:2$\\vert$7:6]' -latex_mapping['c_uimm10splo'] = 'uimm[4$\\vert$9:6]' -latex_mapping['c_uimm10sphi'] = 'uimm[5]' -latex_mapping['c_uimm9splo'] = 'uimm[4:3$\\vert$8:6]' -latex_mapping['c_uimm9sphi'] = 'uimm[5]' -latex_mapping['c_uimm10sp_s'] = 'uimm[5:4$\\vert$9:6]' -latex_mapping['c_uimm9sp_s'] = 'uimm[5:3$\\vert$8:6]' +latex_mapping["imm12"] = "imm[11:0]" +latex_mapping["rs1"] = "rs1" +latex_mapping["rs2"] = "rs2" +latex_mapping["rd"] = "rd" +latex_mapping["imm20"] = "imm[31:12]" +latex_mapping["bimm12hi"] = "imm[12$\\vert$10:5]" +latex_mapping["bimm12lo"] = "imm[4:1$\\vert$11]" +latex_mapping["imm12hi"] = "imm[11:5]" +latex_mapping["imm12lo"] = "imm[4:0]" +latex_mapping["jimm20"] = "imm[20$\\vert$10:1$\\vert$11$\\vert$19:12]" +latex_mapping["zimm"] = "uimm" +latex_mapping["shamtw"] = "shamt" +latex_mapping["shamtd"] = "shamt" +latex_mapping["shamtq"] = "shamt" +latex_mapping["rd_p"] = "rd\\,$'$" +latex_mapping["rs1_p"] = "rs1\\,$'$" +latex_mapping["rs2_p"] = "rs2\\,$'$" +latex_mapping["rd_rs1_n0"] = "rd/rs$\\neq$0" +latex_mapping["rd_rs1_p"] = "rs1\\,$'$/rs2\\,$'$" +latex_mapping["c_rs2"] = "rs2" +latex_mapping["c_rs2_n0"] = "rs2$\\neq$0" +latex_mapping["rd_n0"] = "rd$\\neq$0" +latex_mapping["rs1_n0"] = "rs1$\\neq$0" +latex_mapping["c_rs1_n0"] = "rs1$\\neq$0" +latex_mapping["rd_rs1"] = "rd/rs1" +latex_mapping["zimm6hi"] = "uimm[5]" +latex_mapping["zimm6lo"] = "uimm[4:0]" +latex_mapping["c_nzuimm10"] = "nzuimm[5:4$\\vert$9:6$\\vert$2$\\vert$3]" +latex_mapping["c_uimm7lo"] = "uimm[2$\\vert$6]" +latex_mapping["c_uimm7hi"] = "uimm[5:3]" +latex_mapping["c_uimm8lo"] = "uimm[7:6]" +latex_mapping["c_uimm8hi"] = "uimm[5:3]" +latex_mapping["c_uimm9lo"] = "uimm[7:6]" +latex_mapping["c_uimm9hi"] = "uimm[5:4$\\vert$8]" +latex_mapping["c_nzimm6lo"] = "nzimm[4:0]" +latex_mapping["c_nzimm6hi"] = "nzimm[5]" +latex_mapping["c_imm6lo"] = "imm[4:0]" +latex_mapping["c_imm6hi"] = "imm[5]" +latex_mapping["c_nzimm10hi"] = "nzimm[9]" +latex_mapping["c_nzimm10lo"] = "nzimm[4$\\vert$6$\\vert$8:7$\\vert$5]" +latex_mapping["c_nzimm18hi"] = "nzimm[17]" +latex_mapping["c_nzimm18lo"] = "nzimm[16:12]" +latex_mapping["c_imm12"] = ( + "imm[11$\\vert$4$\\vert$9:8$\\vert$10$\\vert$6$\\vert$7$\\vert$3:1$\\vert$5]" +) +latex_mapping["c_bimm9lo"] = "imm[7:6$\\vert$2:1$\\vert$5]" +latex_mapping["c_bimm9hi"] = "imm[8$\\vert$4:3]" +latex_mapping["c_nzuimm5"] = "nzuimm[4:0]" +latex_mapping["c_nzuimm6lo"] = "nzuimm[4:0]" +latex_mapping["c_nzuimm6hi"] = "nzuimm[5]" +latex_mapping["c_uimm8splo"] = "uimm[4:2$\\vert$7:6]" +latex_mapping["c_uimm8sphi"] = "uimm[5]" +latex_mapping["c_uimm8sp_s"] = "uimm[5:2$\\vert$7:6]" +latex_mapping["c_uimm10splo"] = "uimm[4$\\vert$9:6]" +latex_mapping["c_uimm10sphi"] = "uimm[5]" +latex_mapping["c_uimm9splo"] = "uimm[4:3$\\vert$8:6]" +latex_mapping["c_uimm9sphi"] = "uimm[5]" +latex_mapping["c_uimm10sp_s"] = "uimm[5:4$\\vert$9:6]" +latex_mapping["c_uimm9sp_s"] = "uimm[5:3$\\vert$8:6]" # created a dummy instruction-dictionary like dictionary for all the instruction # types so that the same logic can be used to create their tables latex_inst_type = {} -latex_inst_type['R-type'] = {} -latex_inst_type['R-type']['variable_fields'] = ['opcode', 'rd', 'funct3', \ - 'rs1', 'rs2', 'funct7'] -latex_inst_type['R4-type'] = {} -latex_inst_type['R4-type']['variable_fields'] = ['opcode', 'rd', 'funct3', \ - 'rs1', 'rs2', 'funct2', 'rs3'] -latex_inst_type['I-type'] = {} -latex_inst_type['I-type']['variable_fields'] = ['opcode', 'rd', 'funct3', \ - 'rs1', 'imm12'] -latex_inst_type['S-type'] = {} -latex_inst_type['S-type']['variable_fields'] = ['opcode', 'imm12lo', 'funct3', \ - 'rs1', 'rs2', 'imm12hi'] -latex_inst_type['B-type'] = {} -latex_inst_type['B-type']['variable_fields'] = ['opcode', 'bimm12lo', 'funct3', \ - 'rs1', 'rs2', 'bimm12hi'] -latex_inst_type['U-type'] = {} -latex_inst_type['U-type']['variable_fields'] = ['opcode', 'rd', 'imm20'] -latex_inst_type['J-type'] = {} -latex_inst_type['J-type']['variable_fields'] = ['opcode', 'rd', 'jimm20'] +latex_inst_type["R-type"] = {} +latex_inst_type["R-type"]["variable_fields"] = [ + "opcode", + "rd", + "funct3", + "rs1", + "rs2", + "funct7", +] +latex_inst_type["R4-type"] = {} +latex_inst_type["R4-type"]["variable_fields"] = [ + "opcode", + "rd", + "funct3", + "rs1", + "rs2", + "funct2", + "rs3", +] +latex_inst_type["I-type"] = {} +latex_inst_type["I-type"]["variable_fields"] = [ + "opcode", + "rd", + "funct3", + "rs1", + "imm12", +] +latex_inst_type["S-type"] = {} +latex_inst_type["S-type"]["variable_fields"] = [ + "opcode", + "imm12lo", + "funct3", + "rs1", + "rs2", + "imm12hi", +] +latex_inst_type["B-type"] = {} +latex_inst_type["B-type"]["variable_fields"] = [ + "opcode", + "bimm12lo", + "funct3", + "rs1", + "rs2", + "bimm12hi", +] +latex_inst_type["U-type"] = {} +latex_inst_type["U-type"]["variable_fields"] = ["opcode", "rd", "imm20"] +latex_inst_type["J-type"] = {} +latex_inst_type["J-type"]["variable_fields"] = ["opcode", "rd", "jimm20"] latex_fixed_fields = [] -latex_fixed_fields.append((31,25)) -latex_fixed_fields.append((24,20)) -latex_fixed_fields.append((19,15)) -latex_fixed_fields.append((14,12)) -latex_fixed_fields.append((11,7)) -latex_fixed_fields.append((6,0)) +latex_fixed_fields.append((31, 25)) +latex_fixed_fields.append((24, 20)) +latex_fixed_fields.append((19, 15)) +latex_fixed_fields.append((14, 12)) +latex_fixed_fields.append((11, 7)) +latex_fixed_fields.append((6, 0)) # Pseudo-ops present in the generated encodings. # By default pseudo-ops are not listed as they are considered aliases # of their base instruction. emitted_pseudo_ops = [ - 'pause', - 'prefetch_i', - 'prefetch_r', - 'prefetch_w', - 'rstsa16', - 'rstsa32', - 'srli32_u', - 'slli_rv32', - 'srai_rv32', - 'srli_rv32', - 'umax32', - 'c_mop_1', - 'c_sspush_x1', - 'c_mop_3', - 'c_mop_5', - 'c_sspopchk_x5', - 'c_mop_7', - 'c_mop_9', - 'c_mop_11', - 'c_mop_13', - 'c_mop_15', - 'mop_r_0', - 'mop_r_1', - 'mop_r_2', - 'mop_r_3', - 'mop_r_4', - 'mop_r_5', - 'mop_r_6', - 'mop_r_7', - 'mop_r_8', - 'mop_r_9', - 'mop_r_10', - 'mop_r_11', - 'mop_r_12', - 'mop_r_13', - 'mop_r_14', - 'mop_r_15', - 'mop_r_16', - 'mop_r_17', - 'mop_r_18', - 'mop_r_19', - 'mop_r_20', - 'mop_r_21', - 'mop_r_22', - 'mop_r_23', - 'mop_r_24', - 'mop_r_25', - 'mop_r_26', - 'mop_r_27', - 'mop_r_28', - 'sspopchk_x1', - 'sspopchk_x5', - 'ssrdp', - 'mop_r_29', - 'mop_r_30', - 'mop_r_31', - 'mop_r_32', - 'mop_rr_0', - 'mop_rr_1', - 'mop_rr_2', - 'mop_rr_3', - 'mop_rr_4', - 'mop_rr_5', - 'mop_rr_6', - 'mop_rr_7', - 'sspush_x1', - 'sspush_x5', - 'lpad', - 'bclri.rv32', - 'bexti.rv32', - 'binvi.rv32', - 'bseti.rv32', - 'zext.h.rv32', - 'rev8.h.rv32', - 'rori.rv32', + "pause", + "prefetch_i", + "prefetch_r", + "prefetch_w", + "rstsa16", + "rstsa32", + "srli32_u", + "slli_rv32", + "srai_rv32", + "srli_rv32", + "umax32", + "c_mop_1", + "c_sspush_x1", + "c_mop_3", + "c_mop_5", + "c_sspopchk_x5", + "c_mop_7", + "c_mop_9", + "c_mop_11", + "c_mop_13", + "c_mop_15", + "mop_r_0", + "mop_r_1", + "mop_r_2", + "mop_r_3", + "mop_r_4", + "mop_r_5", + "mop_r_6", + "mop_r_7", + "mop_r_8", + "mop_r_9", + "mop_r_10", + "mop_r_11", + "mop_r_12", + "mop_r_13", + "mop_r_14", + "mop_r_15", + "mop_r_16", + "mop_r_17", + "mop_r_18", + "mop_r_19", + "mop_r_20", + "mop_r_21", + "mop_r_22", + "mop_r_23", + "mop_r_24", + "mop_r_25", + "mop_r_26", + "mop_r_27", + "mop_r_28", + "sspopchk_x1", + "sspopchk_x5", + "ssrdp", + "mop_r_29", + "mop_r_30", + "mop_r_31", + "mop_r_32", + "mop_rr_0", + "mop_rr_1", + "mop_rr_2", + "mop_rr_3", + "mop_rr_4", + "mop_rr_5", + "mop_rr_6", + "mop_rr_7", + "sspush_x1", + "sspush_x5", + "lpad", + "bclri.rv32", + "bexti.rv32", + "binvi.rv32", + "bseti.rv32", + "zext.h.rv32", + "rev8.h.rv32", + "rori.rv32", ] diff --git a/parse.py b/parse.py index 839fd230..8a924e7e 100755 --- a/parse.py +++ b/parse.py @@ -1,21 +1,24 @@ #!/usr/bin/env python3 -from constants import * +import collections import copy -import re import glob +import logging import os import pprint -import logging -import collections -import yaml +import re import sys +import yaml + +from constants import * + pp = pprint.PrettyPrinter(indent=2) -logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + def process_enc_line(line, ext): - ''' + """ This function processes each line of the encoding files (rv*). As part of the processing, the function ensures that the encoding is legal through the following checks:: @@ -40,18 +43,18 @@ def process_enc_line(line, ext): this instruction - mask: hex value representin the bits that need to be masked to extract the value required for matching. - ''' + """ single_dict = {} # fill all bits with don't care. we use '-' to represent don't care # TODO: hardcoded for 32-bits. - encoding = ['-'] * 32 + encoding = ["-"] * 32 # get the name of instruction by splitting based on the first space - [name, remaining] = line.split(' ', 1) + [name, remaining] = line.split(" ", 1) # replace dots with underscores as dot doesn't work with C/Sverilog, etc - name = name.replace('.', '_') + name = name.replace(".", "_") # remove leading whitespaces remaining = remaining.lstrip() @@ -59,7 +62,7 @@ def process_enc_line(line, ext): # check each field for it's length and overlapping bits # ex: 1..0=5 will result in an error --> x overlapping bits - for (s2, s1, entry) in fixed_ranges.findall(remaining): + for s2, s1, entry in fixed_ranges.findall(remaining): msb = int(s2) lsb = int(s1) @@ -80,7 +83,7 @@ def process_enc_line(line, ext): for ind in range(lsb, msb + 1): # overlapping bits - if encoding[31 - ind] != '-': + if encoding[31 - ind] != "-": logging.error( f'{line.split(" ")[0]:<10} has {ind} bit overlapping in it\'s opcodes' ) @@ -89,14 +92,14 @@ def process_enc_line(line, ext): encoding[31 - ind] = bit # extract bit pattern assignments of the form hi..lo=val - remaining = fixed_ranges.sub(' ', remaining) + remaining = fixed_ranges.sub(" ", remaining) # do the same as above but for = pattern. single_fixed is a regex # expression present in constants.py - for (lsb, value, drop) in single_fixed.findall(remaining): + for lsb, value, drop in single_fixed.findall(remaining): lsb = int(lsb, 0) value = int(value, 0) - if encoding[31 - lsb] != '-': + if encoding[31 - lsb] != "-": logging.error( f'{line.split(" ")[0]:<10} has {lsb} bit overlapping in it\'s opcodes' ) @@ -104,110 +107,130 @@ def process_enc_line(line, ext): encoding[31 - lsb] = str(value) # convert the list of encodings into a single string for match and mask - match = "".join(encoding).replace('-','0') - mask = "".join(encoding).replace('0','1').replace('-','0') + match = "".join(encoding).replace("-", "0") + mask = "".join(encoding).replace("0", "1").replace("-", "0") # check if all args of the instruction are present in arg_lut present in # constants.py - args = single_fixed.sub(' ', remaining).split() + args = single_fixed.sub(" ", remaining).split() encoding_args = encoding.copy() for a in args: if a not in arg_lut: - parts = a.split('=') + parts = a.split("=") if len(parts) == 2: 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') + logging.error( + f" Found field {existing_arg} in variable {a} in instruction {name} whose mapping in arg_lut does not exist" + ) raise SystemExit(1) else: - logging.error(f' Found variable {a} in instruction {name} whose mapping in arg_lut does not exist') + logging.error( + f" Found variable {a} in instruction {name} whose mapping in arg_lut does not exist" + ) raise SystemExit(1) (msb, lsb) = arg_lut[a] for ind in range(lsb, msb + 1): # overlapping bits - if encoding_args[31 - ind] != '-': - logging.error(f' Found variable {a} in instruction {name} overlapping {encoding_args[31 - ind]} variable in bit {ind}') + if encoding_args[31 - ind] != "-": + logging.error( + f" Found variable {a} in instruction {name} overlapping {encoding_args[31 - ind]} variable in bit {ind}" + ) raise SystemExit(1) encoding_args[31 - ind] = a - # update the fields of the instruction as a dict and return back along with # the name of the instruction - single_dict['encoding'] = "".join(encoding) - single_dict['variable_fields'] = args - single_dict['extension'] = [os.path.basename(ext)] - single_dict['match']=hex(int(match,2)) - single_dict['mask']=hex(int(mask,2)) + single_dict["encoding"] = "".join(encoding) + single_dict["variable_fields"] = args + single_dict["extension"] = [os.path.basename(ext)] + single_dict["match"] = hex(int(match, 2)) + single_dict["mask"] = hex(int(mask, 2)) return (name, single_dict) + def same_base_isa(ext_name, ext_name_list): type1 = ext_name.split("_")[0] for ext_name1 in ext_name_list: type2 = ext_name1.split("_")[0] # "rv" mean insn for rv32 and rv64 - if (type1 == type2 or - (type2 == "rv" and (type1 == "rv32" or type1 == "rv64")) or - (type1 == "rv" and (type2 == "rv32" or type2 == "rv64"))): + if ( + type1 == type2 + or (type2 == "rv" and (type1 == "rv32" or type1 == "rv64")) + or (type1 == "rv" and (type2 == "rv32" or type2 == "rv64")) + ): return True return False + def overlaps(x, y): - x = x.rjust(len(y), '-') - y = y.rjust(len(x), '-') + x = x.rjust(len(y), "-") + y = y.rjust(len(x), "-") for i in range(0, len(x)): - if not (x[i] == '-' or y[i] == '-' or x[i] == y[i]): + if not (x[i] == "-" or y[i] == "-" or x[i] == y[i]): return False return True + def overlap_allowed(a, x, y): - return x in a and y in a[x] or \ - y in a and x in a[y] + return x in a and y in a[x] or y in a and x in a[y] + def extension_overlap_allowed(x, y): return overlap_allowed(overlapping_extensions, x, y) + def instruction_overlap_allowed(x, y): return overlap_allowed(overlapping_instructions, x, y) + def add_segmented_vls_insn(instr_dict): updated_dict = {} for k, v in instr_dict.items(): - if "nf" in v['variable_fields']: - for new_key, new_value in expand_nf_field(k,v): + if "nf" in v["variable_fields"]: + for new_key, new_value in expand_nf_field(k, v): updated_dict[new_key] = new_value else: updated_dict[k] = v return updated_dict + def expand_nf_field(name, single_dict): - if "nf" not in single_dict['variable_fields']: + if "nf" not in single_dict["variable_fields"]: logging.error(f"Cannot expand nf field for instruction {name}") raise SystemExit(1) # nf no longer a variable field - single_dict['variable_fields'].remove("nf") + single_dict["variable_fields"].remove("nf") # include nf in mask - single_dict['mask'] = hex(int(single_dict['mask'],16) | 0b111 << 29) + single_dict["mask"] = hex(int(single_dict["mask"], 16) | 0b111 << 29) - name_expand_index = name.find('e') + name_expand_index = name.find("e") expanded_instructions = [] - for nf in range(0,8): + for nf in range(0, 8): new_single_dict = copy.deepcopy(single_dict) - new_single_dict['match'] = hex(int(single_dict['match'],16) | nf << 29) - new_single_dict['encoding'] = format(nf, '03b') + single_dict['encoding'][3:] - new_name = name if nf == 0 else name[:name_expand_index] + "seg" + str(nf+1) + name[name_expand_index:] + new_single_dict["match"] = hex(int(single_dict["match"], 16) | nf << 29) + new_single_dict["encoding"] = format(nf, "03b") + single_dict["encoding"][3:] + new_name = ( + name + if nf == 0 + else name[:name_expand_index] + + "seg" + + str(nf + 1) + + name[name_expand_index:] + ) expanded_instructions.append((new_name, new_single_dict)) return expanded_instructions def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): - ''' + """ This function return a dictionary containing all instructions associated with an extension defined by the file_filter input. The file_filter input needs to be rv* file name with out the 'rv' prefix i.e. '_i', '32_i', etc. @@ -241,26 +264,25 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): skipped. - ''' + """ 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}') + file_names += glob.glob(f"{opcodes_dir}/{fil}") file_names.sort(reverse=True) # first pass if for standard/regular instructions - logging.debug('Collecting standard instructions first') + logging.debug("Collecting standard instructions first") for f in file_names: - logging.debug(f'Parsing File: {f} for standard instructions') + logging.debug(f"Parsing File: {f} for standard instructions") with open(f) as fp: - lines = (line.rstrip() - for line in fp) # All lines including the blank ones + 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 lines = list( - line for line in lines - if not line.startswith("#")) # remove comment lines + line for line in lines if not line.startswith("#") + ) # remove comment lines # go through each line of the file for line in lines: @@ -270,9 +292,9 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): # imported file # ignore all lines starting with $import and $pseudo - if '$import' in line or '$pseudo' in line: + if "$import" in line or "$pseudo" in line: continue - logging.debug(f' Processing line: {line}') + logging.debug(f" Processing line: {line}") # call process_enc_line to get the data about the current # instruction @@ -286,69 +308,74 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): var = instr_dict[name]["extension"] if same_base_isa(ext_name, var): # disable same names on the same base ISA - err_msg = f'instruction : {name} from ' - err_msg += f'{ext_name} is already ' - err_msg += f'added from {var} in same base ISA' + err_msg = f"instruction : {name} from " + err_msg += f"{ext_name} is already " + err_msg += f"added from {var} in same base ISA" logging.error(err_msg) raise SystemExit(1) - elif instr_dict[name]['encoding'] != single_dict['encoding']: + elif instr_dict[name]["encoding"] != single_dict["encoding"]: # disable same names with different encodings on different base ISAs - err_msg = f'instruction : {name} from ' - err_msg += f'{ext_name} is already ' - err_msg += f'added from {var} but each have different encodings in different base ISAs' + err_msg = f"instruction : {name} from " + err_msg += f"{ext_name} is already " + err_msg += f"added from {var} but each have different encodings in different base ISAs" logging.error(err_msg) raise SystemExit(1) - instr_dict[name]['extension'].extend(single_dict['extension']) + instr_dict[name]["extension"].extend(single_dict["extension"]) else: - for key in instr_dict: - item = instr_dict[key] - if overlaps(item['encoding'], single_dict['encoding']) and \ - not extension_overlap_allowed(ext_name, item['extension'][0]) and \ - not instruction_overlap_allowed(name, key) and \ - same_base_isa(ext_name, item['extension']): - # disable different names with overlapping encodings on the same base ISA - err_msg = f'instruction : {name} in extension ' - err_msg += f'{ext_name} overlaps instruction {key} ' - err_msg += f'in extension {item["extension"]}' - logging.error(err_msg) - raise SystemExit(1) + for key in instr_dict: + item = instr_dict[key] + if ( + overlaps(item["encoding"], single_dict["encoding"]) + and not extension_overlap_allowed( + ext_name, item["extension"][0] + ) + and not instruction_overlap_allowed(name, key) + and same_base_isa(ext_name, item["extension"]) + ): + # disable different names with overlapping encodings on the same base ISA + err_msg = f"instruction : {name} in extension " + err_msg += f"{ext_name} overlaps instruction {key} " + err_msg += f'in extension {item["extension"]}' + logging.error(err_msg) + raise SystemExit(1) if name not in instr_dict: # update the final dict with the instruction instr_dict[name] = single_dict # second pass if for pseudo instructions - logging.debug('Collecting pseudo instructions now') + logging.debug("Collecting pseudo instructions now") for f in file_names: - logging.debug(f'Parsing File: {f} for pseudo_ops') + logging.debug(f"Parsing File: {f} for pseudo_ops") with open(f) as fp: - lines = (line.rstrip() - for line in fp) # All lines including the blank ones + 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 lines = list( - line for line in lines - if not line.startswith("#")) # remove comment lines + line for line in lines if not line.startswith("#") + ) # remove comment lines # go through each line of the file for line in lines: # ignore all lines not starting with $pseudo - if '$pseudo' not in line: + if "$pseudo" not in line: continue - logging.debug(f' Processing line: {line}') + logging.debug(f" Processing line: {line}") # use the regex pseudo_regex from constants.py to find the dependent # extension, dependent instruction, the pseudo_op in question and # its encoding (ext, orig_inst, pseudo_inst, line) = pseudo_regex.findall(line)[0] - ext_file = f'{opcodes_dir}/{ext}' + ext_file = f"{opcodes_dir}/{ext}" # check if the file of the dependent extension exist. Throw error if # it doesn't if not os.path.exists(ext_file): - ext1_file = f'{opcodes_dir}/unratified/{ext}' + ext1_file = f"{opcodes_dir}/unratified/{ext}" if not os.path.exists(ext1_file): - logging.error(f'Pseudo op {pseudo_inst} in {f} depends on {ext} which is not available') + logging.error( + f"Pseudo op {pseudo_inst} in {f} depends on {ext} which is not available" + ) raise SystemExit(1) else: ext_file = ext1_file @@ -357,53 +384,59 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): # extension. Else throw error. found = False for oline in open(ext_file): - if not re.findall(f'^\\s*{orig_inst}\\s+',oline): + if not re.findall(f"^\\s*{orig_inst}\\s+", oline): continue else: found = True break if not found: - logging.error(f'Orig instruction {orig_inst} not found in {ext}. Required by pseudo_op {pseudo_inst} present in {f}') + logging.error( + f"Orig instruction {orig_inst} not found in {ext}. Required by pseudo_op {pseudo_inst} present in {f}" + ) raise SystemExit(1) - - (name, single_dict) = process_enc_line(pseudo_inst + ' ' + line, f) + (name, single_dict) = process_enc_line(pseudo_inst + " " + line, f) # add the pseudo_op to the dictionary only if the original # instruction is not already in the dictionary. - if orig_inst.replace('.','_') not in instr_dict \ - or include_pseudo \ - or name in include_pseudo_ops: + if ( + orig_inst.replace(".", "_") not in instr_dict + or include_pseudo + or name in include_pseudo_ops + ): # update the final dict with the instruction if name not in instr_dict: instr_dict[name] = single_dict - logging.debug(f' including pseudo_ops:{name}') + logging.debug(f" including pseudo_ops:{name}") else: - if(single_dict['match'] != instr_dict[name]['match']): - instr_dict[name + '_pseudo'] = single_dict + if single_dict["match"] != instr_dict[name]["match"]: + instr_dict[name + "_pseudo"] = single_dict # if a pseudo instruction has already been added to the filtered # instruction dictionary but the extension is not in the current # list, add it else: - ext_name = single_dict['extension'] + ext_name = single_dict["extension"] - if (ext_name not in instr_dict[name]['extension']) & (name + '_pseudo' not in instr_dict): - instr_dict[name]['extension'].extend(ext_name) + if (ext_name not in instr_dict[name]["extension"]) & ( + name + "_pseudo" not in instr_dict + ): + instr_dict[name]["extension"].extend(ext_name) else: - logging.debug(f' Skipping pseudo_op {pseudo_inst} since original instruction {orig_inst} already selected in list') + logging.debug( + f" Skipping pseudo_op {pseudo_inst} since original instruction {orig_inst} already selected in list" + ) # third pass if for imported instructions - logging.debug('Collecting imported instructions') + logging.debug("Collecting imported instructions") for f in file_names: - logging.debug(f'Parsing File: {f} for imported ops') + logging.debug(f"Parsing File: {f} for imported ops") with open(f) as fp: - lines = (line.rstrip() - for line in fp) # All lines including the blank ones + 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 lines = list( - line for line in lines - if not line.startswith("#")) # remove comment lines + line for line in lines if not line.startswith("#") + ) # remove comment lines # go through each line of the file for line in lines: @@ -413,19 +446,21 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): # imported file # ignore all lines starting with $import and $pseudo - if '$import' not in line : + if "$import" not in line: continue - logging.debug(f' Processing line: {line}') + logging.debug(f" Processing line: {line}") (import_ext, reg_instr) = imported_regex.findall(line)[0] - import_ext_file = f'{opcodes_dir}/{import_ext}' + import_ext_file = f"{opcodes_dir}/{import_ext}" # check if the file of the dependent extension exist. Throw error if # it doesn't if not os.path.exists(import_ext_file): - ext1_file = f'{opcodes_dir}/unratified/{import_ext}' + ext1_file = f"{opcodes_dir}/unratified/{import_ext}" if not os.path.exists(ext1_file): - logging.error(f'Instruction {reg_instr} in {f} cannot be imported from {import_ext}') + logging.error( + f"Instruction {reg_instr} in {f} cannot be imported from {import_ext}" + ) raise SystemExit(1) else: ext_file = ext1_file @@ -436,14 +471,16 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): # extension. Else throw error. found = False for oline in open(ext_file): - if not re.findall(f'^\\s*{reg_instr}\\s+',oline): + if not re.findall(f"^\\s*{reg_instr}\\s+", oline): continue else: found = True break if not found: - logging.error(f'imported instruction {reg_instr} not found in {ext_file}. Required by {line} present in {f}') - logging.error(f'Note: you cannot import pseudo/imported ops.') + logging.error( + f"imported instruction {reg_instr} not found in {ext_file}. Required by {line} present in {f}" + ) + logging.error(f"Note: you cannot import pseudo/imported ops.") raise SystemExit(1) # call process_enc_line to get the data about the current @@ -455,37 +492,92 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): # instruction is already imported and raise SystemExit if name in instr_dict: var = instr_dict[name]["extension"] - if instr_dict[name]['encoding'] != single_dict['encoding']: - err_msg = f'imported instruction : {name} in ' - err_msg += f'{os.path.basename(f)} is already ' - err_msg += f'added from {var} but each have different encodings for the same instruction' + if instr_dict[name]["encoding"] != single_dict["encoding"]: + err_msg = f"imported instruction : {name} in " + err_msg += f"{os.path.basename(f)} is already " + err_msg += f"added from {var} but each have different encodings for the same instruction" logging.error(err_msg) raise SystemExit(1) - instr_dict[name]['extension'].extend(single_dict['extension']) + instr_dict[name]["extension"].extend(single_dict["extension"]) else: # update the final dict with the instruction instr_dict[name] = single_dict return instr_dict + def make_priv_latex_table(): - latex_file = open('priv-instr-table.tex','w') - type_list = ['R-type','I-type'] - system_instr = ['_h','_s','_system','_svinval', '64_h'] - dataset_list = [ (system_instr, 'Trap-Return Instructions',['sret','mret'], False) ] - dataset_list.append((system_instr, 'Interrupt-Management Instructions',['wfi'], False)) - dataset_list.append((system_instr, 'Supervisor Memory-Management Instructions',['sfence_vma'], False)) - dataset_list.append((system_instr, 'Hypervisor Memory-Management Instructions',['hfence_vvma', 'hfence_gvma'], False)) - dataset_list.append((system_instr, 'Hypervisor Virtual-Machine Load and Store Instructions', - ['hlv_b','hlv_bu', 'hlv_h','hlv_hu', 'hlv_w', 'hlvx_hu', 'hlvx_wu', 'hsv_b', 'hsv_h','hsv_w'], False)) - dataset_list.append((system_instr, 'Hypervisor Virtual-Machine Load and Store Instructions, RV64 only', ['hlv_wu','hlv_d','hsv_d'], False)) - dataset_list.append((system_instr, 'Svinval Memory-Management Instructions', ['sinval_vma', 'sfence_w_inval','sfence_inval_ir', 'hinval_vvma','hinval_gvma'], False)) - caption = '\\caption{RISC-V Privileged Instructions}' + latex_file = open("priv-instr-table.tex", "w") + type_list = ["R-type", "I-type"] + system_instr = ["_h", "_s", "_system", "_svinval", "64_h"] + dataset_list = [(system_instr, "Trap-Return Instructions", ["sret", "mret"], False)] + dataset_list.append( + (system_instr, "Interrupt-Management Instructions", ["wfi"], False) + ) + dataset_list.append( + ( + system_instr, + "Supervisor Memory-Management Instructions", + ["sfence_vma"], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Hypervisor Memory-Management Instructions", + ["hfence_vvma", "hfence_gvma"], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Hypervisor Virtual-Machine Load and Store Instructions", + [ + "hlv_b", + "hlv_bu", + "hlv_h", + "hlv_hu", + "hlv_w", + "hlvx_hu", + "hlvx_wu", + "hsv_b", + "hsv_h", + "hsv_w", + ], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Hypervisor Virtual-Machine Load and Store Instructions, RV64 only", + ["hlv_wu", "hlv_d", "hsv_d"], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Svinval Memory-Management Instructions", + [ + "sinval_vma", + "sfence_w_inval", + "sfence_inval_ir", + "hinval_vvma", + "hinval_gvma", + ], + False, + ) + ) + caption = "\\caption{RISC-V Privileged Instructions}" make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) latex_file.close() + def make_latex_table(): - ''' + """ This function is mean to create the instr-table.tex that is meant to be used by the riscv-isa-manual. This function basically creates a single latext file of multiple tables with each table limited to a single page. Only the @@ -503,9 +595,9 @@ def make_latex_table(): The last table only has to be given a caption - as per the policy of the riscv-isa-manual. - ''' + """ # open the file and use it as a pointer for all further dumps - latex_file = open('instr-table.tex','w') + latex_file = open("instr-table.tex", "w") # create the rv32i table first. Here we set the caption to empty. We use the # files rv_i and rv32_i to capture instructions relevant for rv32i @@ -514,58 +606,77 @@ def make_latex_table(): # is empty then it indicates that all instructions of the all the extensions # in list_of_extensions need to be dumped. If not empty, then only the # instructions listed in list_of_instructions will be dumped into latex. - caption = '' - type_list = ['R-type','I-type','S-type','B-type','U-type','J-type'] - dataset_list = [(['_i','32_i'], 'RV32I Base Instruction Set', [], False)] - dataset_list.append((['_i'], '', ['fence_tso','pause'], True)) + caption = "" + type_list = ["R-type", "I-type", "S-type", "B-type", "U-type", "J-type"] + dataset_list = [(["_i", "32_i"], "RV32I Base Instruction Set", [], False)] + dataset_list.append((["_i"], "", ["fence_tso", "pause"], True)) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) - type_list = ['R-type','I-type','S-type'] - dataset_list = [(['64_i'], 'RV64I Base Instruction Set (in addition to RV32I)', [], False)] - dataset_list.append((['_zifencei'], 'RV32/RV64 Zifencei Standard Extension', [], False)) - dataset_list.append((['_zicsr'], 'RV32/RV64 Zicsr Standard Extension', [], False)) - dataset_list.append((['_m','32_m'], 'RV32M Standard Extension', [], False)) - dataset_list.append((['64_m'],'RV64M Standard Extension (in addition to RV32M)', [], False)) + type_list = ["R-type", "I-type", "S-type"] + dataset_list = [ + (["64_i"], "RV64I Base Instruction Set (in addition to RV32I)", [], False) + ] + dataset_list.append( + (["_zifencei"], "RV32/RV64 Zifencei Standard Extension", [], False) + ) + dataset_list.append((["_zicsr"], "RV32/RV64 Zicsr Standard Extension", [], False)) + dataset_list.append((["_m", "32_m"], "RV32M Standard Extension", [], False)) + dataset_list.append( + (["64_m"], "RV64M Standard Extension (in addition to RV32M)", [], False) + ) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) - type_list = ['R-type'] - dataset_list = [(['_a'],'RV32A Standard Extension', [], False)] - dataset_list.append((['64_a'],'RV64A Standard Extension (in addition to RV32A)', [], False)) + type_list = ["R-type"] + dataset_list = [(["_a"], "RV32A Standard Extension", [], False)] + dataset_list.append( + (["64_a"], "RV64A Standard Extension (in addition to RV32A)", [], False) + ) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) - type_list = ['R-type','R4-type','I-type','S-type'] - dataset_list = [(['_f'],'RV32F Standard Extension', [], False)] - dataset_list.append((['64_f'],'RV64F Standard Extension (in addition to RV32F)', [], False)) + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [(["_f"], "RV32F Standard Extension", [], False)] + dataset_list.append( + (["64_f"], "RV64F Standard Extension (in addition to RV32F)", [], False) + ) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) - type_list = ['R-type','R4-type','I-type','S-type'] - dataset_list = [(['_d'],'RV32D Standard Extension', [], False)] - dataset_list.append((['64_d'],'RV64D Standard Extension (in addition to RV32D)', [], False)) + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [(["_d"], "RV32D Standard Extension", [], False)] + dataset_list.append( + (["64_d"], "RV64D Standard Extension (in addition to RV32D)", [], False) + ) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) - type_list = ['R-type','R4-type','I-type','S-type'] - dataset_list = [(['_q'],'RV32Q Standard Extension', [], False)] - dataset_list.append((['64_q'],'RV64Q Standard Extension (in addition to RV32Q)', [], False)) + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [(["_q"], "RV32Q Standard Extension", [], False)] + dataset_list.append( + (["64_q"], "RV64Q Standard Extension (in addition to RV32Q)", [], False) + ) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) - caption = '\\caption{Instruction listing for RISC-V}' - type_list = ['R-type','R4-type','I-type','S-type'] - dataset_list = [(['_zfh', '_d_zfh','_q_zfh'],'RV32Zfh Standard Extension', [], False)] - dataset_list.append((['64_zfh'],'RV64Zfh Standard Extension (in addition to RV32Zfh)', [], False)) + caption = "\\caption{Instruction listing for RISC-V}" + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [ + (["_zfh", "_d_zfh", "_q_zfh"], "RV32Zfh Standard Extension", [], False) + ] + dataset_list.append( + (["64_zfh"], "RV64Zfh Standard Extension (in addition to RV32Zfh)", [], False) + ) make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) ## The following is demo to show that Compressed instructions can also be # dumped in the same manner as above - #type_list = [''] - #dataset_list = [(['_c', '32_c', '32_c_f','_c_d'],'RV32C Standard Extension', [])] - #dataset_list.append((['64_c'],'RV64C Standard Extension (in addition to RV32C)', [])) - #make_ext_latex_table(type_list, dataset_list, latex_file, 16, caption) + # type_list = [''] + # dataset_list = [(['_c', '32_c', '32_c_f','_c_d'],'RV32C Standard Extension', [])] + # dataset_list.append((['64_c'],'RV64C Standard Extension (in addition to RV32C)', [])) + # make_ext_latex_table(type_list, dataset_list, latex_file, 16, caption) latex_file.close() + def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): - ''' + """ For a given collection of extensions this function dumps out a complete latex table which includes the encodings of the instructions. @@ -614,10 +725,11 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): the position and assign the same string as the data of the multicolumn entry in the table. - ''' - column_size = "".join(['p{0.002in}']*(ilen+1)) + """ + column_size = "".join(["p{0.002in}"] * (ilen + 1)) - type_entries = ''' + type_entries = ( + """ \\multicolumn{3}{l}{31} & \\multicolumn{2}{r}{27} & \\multicolumn{1}{c}{26} & @@ -633,7 +745,9 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): \\multicolumn{6}{l}{6} & \\multicolumn{1}{r}{0} \\\\ \\cline{2-33}\n&\n\n -''' if ilen == 32 else ''' +""" + if ilen == 32 + else """ \\multicolumn{1}{c}{15} & \\multicolumn{1}{c}{14} & \\multicolumn{1}{c}{13} & @@ -651,11 +765,14 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): \\multicolumn{1}{c}{1} & \\multicolumn{1}{c}{0} \\\\ \\cline{2-17}\n&\n\n -''' +""" + ) # depending on the type_list input we create a subset dictionary of # latex_inst_type dictionary present in constants.py - type_dict = {key: value for key, value in latex_inst_type.items() if key in type_list} + type_dict = { + key: value for key, value in latex_inst_type.items() if key in type_list + } # iterate ovr each instruction type and create a table entry for t in type_dict: @@ -663,7 +780,7 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): # first capture all "arguments" of the type (funct3, funct7, rd, etc) # and capture their positions using arg_lut. - for f in type_dict[t]['variable_fields']: + for f in type_dict[t]["variable_fields"]: (msb, lsb) = arg_lut[f] name = f if f not in latex_mapping else latex_mapping[f] fields.append((msb, lsb, name)) @@ -673,43 +790,45 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): # information is stored as a 3-element tuple containing the msb, lsb # position of the arugment and the name of the argument. msb = ilen - 1 - y = '' - for r in range(0,ilen): - if y != '': - fields.append((msb,ilen-1-r+1,y)) - y = '' - msb = ilen-1-r-1 + y = "" + for r in range(0, ilen): + if y != "": + fields.append((msb, ilen - 1 - r + 1, y)) + y = "" + msb = ilen - 1 - r - 1 if r == 31: - if y != '': + if y != "": fields.append((msb, 0, y)) - y = '' + y = "" # sort the arguments in decreasing order of msb position fields.sort(key=lambda y: y[0], reverse=True) # for each argument/string of 1s or 0s, create a multicolumn latex table # entry - entry = '' + entry = "" for r in range(len(fields)): (msb, lsb, name) = fields[r] - if r == len(fields)-1: - entry += f'\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} & {t} \\\\\n' + if r == len(fields) - 1: + entry += ( + f"\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} & {t} \\\\\n" + ) elif r == 0: - entry += f'\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} &\n' + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} &\n" else: - entry += f'\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n' - entry += f'\\cline{{2-{ilen+1}}}\n&\n\n' + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n" + entry += f"\\cline{{2-{ilen+1}}}\n&\n\n" type_entries += entry # for each entry in the dataset create a table - content = '' - for (ext_list, title, filter_list, include_pseudo) in dataset: + content = "" + for ext_list, title, filter_list, include_pseudo in dataset: instr_dict = {} # for all extensions list in ext_list, create a dictionary of # instructions associated with those extensions. for e in ext_list: - instr_dict.update(create_inst_dict(['rv'+e], include_pseudo)) + instr_dict.update(create_inst_dict(["rv" + e], include_pseudo)) # if filter_list is not empty then use that as the official set of # instructions that need to be dumped into the latex table @@ -717,64 +836,70 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): # for each instruction create an latex table entry just like how we did # above with the instruction-type table. - instr_entries = '' + instr_entries = "" for inst in inst_list: if inst not in instr_dict: - logging.error(f'in make_ext_latex_table: Instruction: {inst} not found in instr_dict') + logging.error( + f"in make_ext_latex_table: Instruction: {inst} not found in instr_dict" + ) raise SystemExit(1) fields = [] # only if the argument is available in arg_lut we consume it, else # throw error. - for f in instr_dict[inst]['variable_fields']: + for f in instr_dict[inst]["variable_fields"]: if f not in arg_lut: - logging.error(f'Found variable {f} in instruction {inst} whose mapping is not available') + logging.error( + f"Found variable {f} in instruction {inst} whose mapping is not available" + ) raise SystemExit(1) - (msb,lsb) = arg_lut[f] - name = f.replace('_','.') if f not in latex_mapping else latex_mapping[f] + (msb, lsb) = arg_lut[f] + name = ( + f.replace("_", ".") if f not in latex_mapping else latex_mapping[f] + ) fields.append((msb, lsb, name)) - msb = ilen -1 - y = '' + msb = ilen - 1 + y = "" if ilen == 16: - encoding = instr_dict[inst]['encoding'][16:] + encoding = instr_dict[inst]["encoding"][16:] else: - encoding = instr_dict[inst]['encoding'] - for r in range(0,ilen): - x = encoding [r] - if ((msb, ilen-1-r+1)) in latex_fixed_fields: - fields.append((msb,ilen-1-r+1,y)) - msb = ilen-1-r - y = '' - if x == '-': - if y != '': - fields.append((msb,ilen-1-r+1,y)) - y = '' - msb = ilen-1-r-1 + encoding = instr_dict[inst]["encoding"] + for r in range(0, ilen): + x = encoding[r] + if ((msb, ilen - 1 - r + 1)) in latex_fixed_fields: + fields.append((msb, ilen - 1 - r + 1, y)) + msb = ilen - 1 - r + y = "" + if x == "-": + if y != "": + fields.append((msb, ilen - 1 - r + 1, y)) + y = "" + msb = ilen - 1 - r - 1 else: y += str(x) - if r == ilen-1: - if y != '': + if r == ilen - 1: + if y != "": fields.append((msb, 0, y)) - y = '' + y = "" fields.sort(key=lambda y: y[0], reverse=True) - entry = '' + entry = "" for r in range(len(fields)): (msb, lsb, name) = fields[r] - if r == len(fields)-1: + if r == len(fields) - 1: entry += f'\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} & {inst.upper().replace("_",".")} \\\\\n' elif r == 0: - entry += f'\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} &\n' + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} &\n" else: - entry += f'\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n' - entry += f'\\cline{{2-{ilen+1}}}\n&\n\n' + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n" + entry += f"\\cline{{2-{ilen+1}}}\n&\n\n" instr_entries += entry # once an entry of the dataset is completed we create the whole table # with the title of that dataset as sub-heading (sort-of) - if title != '': - content += f''' + if title != "": + content += f""" \\multicolumn{{{ilen}}}{{c}}{{}} & \\\\ \\multicolumn{{{ilen}}}{{c}}{{\\bf {title} }} & \\\\ @@ -782,14 +907,13 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): & {instr_entries} -''' +""" else: - content += f''' + content += f""" {instr_entries} -''' - +""" - header = f''' + header = f""" \\newpage \\begin{{table}}[p] @@ -800,30 +924,32 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): & {type_entries} -''' - endtable=f''' +""" + endtable = f""" \\end{{tabular}} \\end{{center}} \\end{{small}} {caption} \\end{{table}} -''' +""" # dump the contents and return - latex_file.write(header+content+endtable) + latex_file.write(header + content + endtable) + def instr_dict_2_extensions(instr_dict): extensions = [] for item in instr_dict.values(): - if item['extension'][0] not in extensions: - extensions.append(item['extension'][0]) + if item["extension"][0] not in extensions: + extensions.append(item["extension"][0]) return extensions + def make_chisel(instr_dict, spinal_hdl=False): - chisel_names='' - cause_names_str='' - csr_names_str = '' + chisel_names = "" + cause_names_str = "" + csr_names_str = "" for i in instr_dict: if spinal_hdl: chisel_names += f' def {i.upper().replace(".","_"):<18s} = M"b{instr_dict[i]["encoding"].replace("-","-")}"\n' @@ -832,7 +958,7 @@ def make_chisel(instr_dict, spinal_hdl=False): if not spinal_hdl: extensions = instr_dict_2_extensions(instr_dict) for e in extensions: - e_instrs = filter(lambda i: instr_dict[i]['extension'][0] == e, instr_dict) + e_instrs = filter(lambda i: instr_dict[i]["extension"][0] == e, instr_dict) if "rv64_" in e: e_format = e.replace("rv64_", "").upper() + "64" elif "rv32_" in e: @@ -843,42 +969,43 @@ def make_chisel(instr_dict, spinal_hdl=False): e_format = e.upper chisel_names += f' val {e_format+"Type"} = Map(\n' for instr in e_instrs: - tmp_instr_name = '"'+instr.upper().replace(".","_")+'"' + tmp_instr_name = '"' + instr.upper().replace(".", "_") + '"' chisel_names += f' {tmp_instr_name:<18s} -> BitPat("b{instr_dict[instr]["encoding"].replace("-","?")}"),\n' - chisel_names += f' )\n' + chisel_names += f" )\n" for num, name in causes: cause_names_str += f' val {name.lower().replace(" ","_")} = {hex(num)}\n' - cause_names_str += ''' val all = { + cause_names_str += """ val all = { val res = collection.mutable.ArrayBuffer[Int]() -''' +""" for num, name in causes: cause_names_str += f' res += {name.lower().replace(" ","_")}\n' - cause_names_str += ''' res.toArray - }''' + cause_names_str += """ res.toArray + }""" - for num, name in csrs+csrs32: - csr_names_str += f' val {name} = {hex(num)}\n' - csr_names_str += ''' val all = { + for num, name in csrs + csrs32: + csr_names_str += f" val {name} = {hex(num)}\n" + csr_names_str += """ val all = { val res = collection.mutable.ArrayBuffer[Int]() -''' +""" for num, name in csrs: - csr_names_str += f''' res += {name}\n''' - csr_names_str += ''' res.toArray + csr_names_str += f""" res += {name}\n""" + csr_names_str += """ res.toArray } val all32 = { val res = collection.mutable.ArrayBuffer(all:_*) -''' +""" for num, name in csrs32: - csr_names_str += f''' res += {name}\n''' - csr_names_str += ''' res.toArray - }''' + csr_names_str += f""" res += {name}\n""" + csr_names_str += """ res.toArray + }""" if spinal_hdl: - chisel_file = open('inst.spinalhdl','w') + chisel_file = open("inst.spinalhdl", "w") else: - chisel_file = open('inst.chisel','w') - chisel_file.write(f''' + chisel_file = open("inst.chisel", "w") + chisel_file.write( + f""" /* Automatically generated by parse_opcodes */ object Instructions {{ {chisel_names} @@ -889,76 +1016,95 @@ def make_chisel(instr_dict, spinal_hdl=False): object CSRs {{ {csr_names_str} }} -''') +""" + ) chisel_file.close() + def make_rust(instr_dict): - mask_match_str= '' + mask_match_str = "" for i in instr_dict: mask_match_str += f'const MATCH_{i.upper().replace(".","_")}: u32 = {(instr_dict[i]["match"])};\n' mask_match_str += f'const MASK_{i.upper().replace(".","_")}: u32 = {(instr_dict[i]["mask"])};\n' - for num, name in csrs+csrs32: - mask_match_str += f'const CSR_{name.upper()}: u16 = {hex(num)};\n' + for num, name in csrs + csrs32: + mask_match_str += f"const CSR_{name.upper()}: u16 = {hex(num)};\n" for num, name in causes: - mask_match_str += f'const CAUSE_{name.upper().replace(" ","_")}: u8 = {hex(num)};\n' - rust_file = open('inst.rs','w') - rust_file.write(f''' + mask_match_str += ( + f'const CAUSE_{name.upper().replace(" ","_")}: u8 = {hex(num)};\n' + ) + rust_file = open("inst.rs", "w") + rust_file.write( + f""" /* Automatically generated by parse_opcodes */ {mask_match_str} -''') +""" + ) rust_file.close() + def make_sverilog(instr_dict): - names_str = '' + names_str = "" for i in instr_dict: names_str += f" localparam [31:0] {i.upper().replace('.','_'):<18s} = 32'b{instr_dict[i]['encoding'].replace('-','?')};\n" - names_str += ' /* CSR Addresses */\n' - for num, name in csrs+csrs32: - names_str += f" localparam logic [11:0] CSR_{name.upper()} = 12'h{hex(num)[2:]};\n" - - sverilog_file = open('inst.sverilog','w') - sverilog_file.write(f''' + names_str += " /* CSR Addresses */\n" + for num, name in csrs + csrs32: + names_str += ( + f" localparam logic [11:0] CSR_{name.upper()} = 12'h{hex(num)[2:]};\n" + ) + + sverilog_file = open("inst.sverilog", "w") + sverilog_file.write( + f""" /* Automatically generated by parse_opcodes */ package riscv_instr; {names_str} endpackage -''') +""" + ) sverilog_file.close() + + def make_c(instr_dict): - mask_match_str = '' - declare_insn_str = '' + mask_match_str = "" + declare_insn_str = "" for i in instr_dict: - mask_match_str += f'#define MATCH_{i.upper().replace(".","_")} {instr_dict[i]["match"]}\n' - mask_match_str += f'#define MASK_{i.upper().replace(".","_")} {instr_dict[i]["mask"]}\n' + mask_match_str += ( + f'#define MATCH_{i.upper().replace(".","_")} {instr_dict[i]["match"]}\n' + ) + mask_match_str += ( + f'#define MASK_{i.upper().replace(".","_")} {instr_dict[i]["mask"]}\n' + ) declare_insn_str += f'DECLARE_INSN({i.replace(".","_")}, MATCH_{i.upper().replace(".","_")}, MASK_{i.upper().replace(".","_")})\n' - csr_names_str = '' - declare_csr_str = '' - for num, name in csrs+csrs32: - csr_names_str += f'#define CSR_{name.upper()} {hex(num)}\n' - declare_csr_str += f'DECLARE_CSR({name}, CSR_{name.upper()})\n' + csr_names_str = "" + declare_csr_str = "" + for num, name in csrs + csrs32: + csr_names_str += f"#define CSR_{name.upper()} {hex(num)}\n" + declare_csr_str += f"DECLARE_CSR({name}, CSR_{name.upper()})\n" - causes_str= '' - declare_cause_str = '' + causes_str = "" + declare_cause_str = "" for num, name in causes: causes_str += f"#define CAUSE_{name.upper().replace(' ', '_')} {hex(num)}\n" - declare_cause_str += f"DECLARE_CAUSE(\"{name}\", CAUSE_{name.upper().replace(' ','_')})\n" + declare_cause_str += ( + f"DECLARE_CAUSE(\"{name}\", CAUSE_{name.upper().replace(' ','_')})\n" + ) - arg_str = '' + arg_str = "" for name, rng in arg_lut.items(): - sanitized_name = name.replace(' ', '_').replace('=', '_eq_') + sanitized_name = name.replace(" ", "_").replace("=", "_eq_") begin = rng[1] - end = rng[0] + end = rng[0] 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: + with open(f"{os.path.dirname(__file__)}/encoding.h", "r") as file: enc_header = file.read() commit = os.popen('git log -1 --format="format:%h"').read() # Generate the output as a string - output_str = f'''/* SPDX-License-Identifier: BSD-3-Clause */ + output_str = f"""/* SPDX-License-Identifier: BSD-3-Clause */ /* Copyright (c) 2023 RISC-V International */ @@ -981,18 +1127,19 @@ def make_c(instr_dict): {declare_csr_str}#endif #ifdef DECLARE_CAUSE {declare_cause_str}#endif -''' +""" # Write the modified output to the file - with open('encoding.out.h', 'w') as enc_file: + with open("encoding.out.h", "w") as enc_file: enc_file.write(output_str) + def make_go(instr_dict): args = " ".join(sys.argv) - prelude = f'''// Code generated by {args}; DO NOT EDIT.''' + prelude = f"""// Code generated by {args}; DO NOT EDIT.""" - prelude += ''' + prelude += """ package riscv import "cmd/internal/obj" @@ -1008,52 +1155,54 @@ def make_go(instr_dict): func encode(a obj.As) *inst { switch a { -''' +""" - endoffile = ''' } + endoffile = """ } return nil } -''' +""" - instr_str = '' + instr_str = "" for i in instr_dict: - enc_match = int(instr_dict[i]['match'],0) - opcode = (enc_match >> 0) & ((1<<7)-1) - funct3 = (enc_match >> 12) & ((1<<3)-1) - rs1 = (enc_match >> 15) & ((1<<5)-1) - rs2 = (enc_match >> 20) & ((1<<5)-1) - csr = (enc_match >> 20) & ((1<<12)-1) - funct7 = (enc_match >> 25) & ((1<<7)-1) - instr_str += f''' case A{i.upper().replace("_","")}: + enc_match = int(instr_dict[i]["match"], 0) + opcode = (enc_match >> 0) & ((1 << 7) - 1) + funct3 = (enc_match >> 12) & ((1 << 3) - 1) + rs1 = (enc_match >> 15) & ((1 << 5) - 1) + rs2 = (enc_match >> 20) & ((1 << 5) - 1) + csr = (enc_match >> 20) & ((1 << 12) - 1) + funct7 = (enc_match >> 25) & ((1 << 7) - 1) + 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: +""" + + with open("inst.go", "w") as file: file.write(prelude) file.write(instr_str) file.write(endoffile) try: import subprocess + subprocess.run(["go", "fmt", "inst.go"]) except: pass + def signed(value, width): - if 0 <= value < (1<<(width-1)): - return value - else: - return value - (1< mop.rr.7 rd=x0, rs2=x1/x5, rs1=x0 $pseudo_op rv_zimop::mop.rr.N sspush.x1 30=1 27=1 26=1 31=1 29..28=0 25=1 24..20=1 19..15=0 14..12=4 11..7=0 6..2=0x1C 1..0=3 $pseudo_op rv_zimop::mop.rr.N sspush.x5 30=1 27=1 26=1 31=1 29..28=0 25=1 24..20=5 19..15=0 14..12=4 11..7=0 6..2=0x1C 1..0=3 -