├── .gitignore ├── .travis.yml ├── Bender.yml ├── CHANGELOG.md ├── LICENSE ├── LICENSE.SiFive ├── README.md ├── ci ├── download-pulp-gcc.sh ├── get-openocd.sh ├── install-verilator.sh ├── make-tmp.sh ├── openocd-to-junit.py └── run-openocd-compliance.sh ├── debug_rom ├── .gitignore ├── Makefile ├── debug_rom.S ├── debug_rom.h ├── debug_rom.sv ├── debug_rom_one_scratch.h ├── debug_rom_one_scratch.sv ├── encoding.h ├── gen_rom.py └── link.ld ├── doc ├── debug-system.md ├── debugsys_schematic.svg ├── dmi_protocol.json └── dmi_protocol.svg ├── src ├── dm_csrs.sv ├── dm_mem.sv ├── dm_obi_top.sv ├── dm_pkg.sv ├── dm_sba.sv ├── dm_top.sv ├── dmi_bscane_tap.sv ├── dmi_cdc.sv ├── dmi_intf.sv ├── dmi_jtag.sv ├── dmi_jtag_tap.sv └── dmi_test.sv ├── src_files.yml ├── sva ├── dm_csrs_sva.sv ├── dm_sba_sva.sv └── dm_top_sva.sv └── tb ├── .clang-format ├── .gitignore ├── LICENSE.Berkeley ├── LICENSE.SiFive ├── Makefile ├── README.md ├── SimJTAG.sv ├── boot_rom.sv ├── dm_compliance_test.cfg ├── dm_debug.cfg ├── dm_tb_pkg.sv ├── dp_ram.sv ├── jtag_dmi ├── .gitignore ├── jtag_intf.sv ├── jtag_test.sv ├── run_vsim.sh ├── run_vsim_xilinx.sh └── tb_jtag_dmi.sv ├── mm_ram.sv ├── prog ├── crt0.S ├── link.ld ├── syscalls.c ├── test.c └── vectors.S ├── remote_bitbang ├── .gitignore ├── Makefile ├── rbs_test.c ├── remote_bitbang.c ├── remote_bitbang.h └── sim_jtag.c ├── tb_test_env.sv ├── tb_top.sv ├── tb_top_verilator.cpp ├── tb_top_verilator.sv ├── unused └── SimDTM.sv ├── veri-run-openocd.py ├── vsim_batch.tcl ├── vsim_gui.tcl └── waves.tcl /.gitignore: -------------------------------------------------------------------------------- 1 | .bender/ 2 | Bender.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | # run on new infrastructure 3 | dist: xenial 4 | sudo: false 5 | cache: 6 | apt: true 7 | directories: 8 | $RISCV 9 | $VERILATOR_ROOT 10 | timeout: 1000 11 | 12 | # required packages to install 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - gcc-7 19 | - g++-7 20 | - gperf 21 | - autoconf 22 | - automake 23 | - autotools-dev 24 | - libmpc-dev 25 | - libmpfr-dev 26 | - libgmp-dev 27 | - gawk 28 | - build-essential 29 | - bison 30 | - flex 31 | - texinfo 32 | - python-pexpect 33 | - libusb-1.0-0-dev 34 | - default-jdk 35 | - zlib1g-dev 36 | - valgrind 37 | env: 38 | global: 39 | - RISCV="/home/travis/riscv_install" 40 | - VERILATOR_ROOT="/home/travis/verilator-4.018" 41 | 42 | 43 | before_install: 44 | - export CXX=g++-7 CC=gcc-7 45 | # setup dependent paths 46 | - export PATH=$RISCV/bin:$VERILATOR_ROOT/bin:$PATH 47 | - export LIBRARY_PATH=$RISCV/lib 48 | - export LD_LIBRARY_PATH=$RISCV/lib 49 | - export C_INCLUDE_PATH=$RISCV/include:$VERILATOR_ROOT/share/verilator/include 50 | - export CPLUS_INCLUDE_PATH=$RISCV/include:$VERILATOR_ROOT/share/verilator/include 51 | - export PKG_CONFIG_PATH=$VERILATOR_ROOT/share/pkgconfig 52 | # number of parallel jobs to use for make commands and simulation 53 | - export NUM_JOBS=4 54 | - ci/make-tmp.sh 55 | - git submodule update --init --recursive 56 | 57 | stages: 58 | - download 59 | - compile1 60 | - compile2 61 | - test 62 | 63 | jobs: 64 | include: 65 | - stage: download 66 | name: download pulp gcc 67 | script: 68 | - ci/download-pulp-gcc.sh 69 | 70 | - stage: compile2 71 | name: build verilator 72 | script: 73 | - ci/install-verilator.sh 74 | - stage: compile2 75 | name: build openocd 76 | script: 77 | - ci/get-openocd.sh 78 | 79 | - stage: test 80 | name: run openocd debug module tests 81 | script: 82 | - ci/veri-run-openocd-compliance.sh 83 | 84 | # extra time during long builds 85 | install: travis_wait 86 | -------------------------------------------------------------------------------- /Bender.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2020-2021 ETH Zurich and University of Bologna. 2 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | # SPDX-License-Identifier: SHL-0.51 4 | 5 | package: 6 | name: riscv-dbg 7 | authors: 8 | - Florian Zaruba 9 | - Robert Balas 10 | 11 | dependencies: 12 | tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.3 } 13 | common_cells: {git: "https://github.com/pulp-platform/common_cells.git", version: 1.24.0} 14 | 15 | sources: 16 | files: 17 | # Level 1: 18 | - src/dm_pkg.sv 19 | - debug_rom/debug_rom.sv 20 | - debug_rom/debug_rom_one_scratch.sv 21 | # Level 2: 22 | - src/dm_csrs.sv 23 | - src/dm_mem.sv 24 | - src/dmi_cdc.sv 25 | - target: not(all(xilinx, bscane)) 26 | files: 27 | - src/dmi_jtag_tap.sv 28 | - target: all(xilinx, bscane) 29 | files: 30 | - src/dmi_bscane_tap.sv 31 | # Level 3: 32 | - src/dm_sba.sv 33 | - src/dm_top.sv 34 | - src/dmi_jtag.sv 35 | # Level 4: 36 | - src/dm_obi_top.sv 37 | 38 | - target: simulation 39 | files: 40 | - src/dmi_test.sv 41 | 42 | - target: test 43 | files: 44 | # Level 1 45 | - src/dmi_intf.sv 46 | - tb/jtag_dmi/jtag_intf.sv 47 | - tb/jtag_dmi/jtag_test.sv 48 | # Level 3 49 | - tb/jtag_dmi/tb_jtag_dmi.sv 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | ## [0.8.1] - 2023-10-01 10 | ### Changed 11 | - debug_rom: Add rst 12 | 13 | ## [0.8.0] - 2023-04-05 14 | ### Fixed 15 | - dm_csrs: Fix W1C behavior of `sberror` (#155) [@andreaskurth](https://github.com/andreaskurth) 16 | - gen_rom.py: Port to python3 (#152) [@andreaskurth](https://github.com/andreaskurth) 17 | 18 | ### Added 19 | - `xprop_off` to Xprop-incompatible processes (#151) [@andreaskurth](https://github.com/andreaskurth) 20 | 21 | ## [0.7.0] - 2022-11-02 22 | ### Fixed 23 | - 64-bit unaligned accesses (#145) [@creinwar](https://github.com/creinwar) 24 | - Various minor testbench fixes (#146) 25 | 26 | ### Changed 27 | - Halted, Resume and Exception addresses are now aligned to 8 bytes 28 | 29 | ## [0.6.0] - 2022-10-11 30 | ### Fixed 31 | - Testbench build (#141, #142) 32 | - remote_bitbang tb build for newer GCC versions (#133) [@epsilon537](https://github.com/noytzach) 33 | - 32-bit access to abstract data (#27) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 34 | - `dm_mem`: Clear state of hart upon ndmreset (#140) [@andreaskurth](https://github.com/andreaskurth) 35 | - `dmi_jtag_tap`: Bring all state to initial value in test-logic-reset (#139) [@andreaskurth](https://github.com/andreaskurth) 36 | - Fix DMI response when command or SBA are busy (#138) [@andreaskurth](https://github.com/andreaskurth) 37 | 38 | ### Changed 39 | - Add expontential backoff to read_dmi in tb (#134) [@colluca](https://github.com/colluca) 40 | 41 | ## [0.5.1] - 2022-04-12 42 | ### Fixed 43 | - Fixed dmi_bscane_tap top-level signals 44 | 45 | ## [0.5.0] - 2022-04-04 46 | ### Added 47 | - Add sbaccess8 and sbaccess16 support (#106) [@noytzach](https://github.com/noytzach) 48 | - Implement SBA bad address error (#12) [@msfchaffner](https://github.com/msfschaffner) 49 | - Added random reset tests to dmi testbench. 50 | ### Changed 51 | - Implement `dmihardreset` functionaliy in `dtmcs` register. 52 | - `dmi_rst_ni` of `dm_top` is now a synchronous signal. However, `dmi_rst_no` of 53 | `dmi_jtag` is glitch-free and asserted during all forms (functional or POR) of 54 | resets. 55 | ### Fixed 56 | - Fixed documentation (csr) 57 | - Fixed reset value of sbcs register (#127) [@msfchaffner](https://github.com/msfschaffner) 58 | - Fixed various ascent lint warnings [@msfchaffner](https://github.com/msfschaffner) 59 | - Implement proper CDC flushing behavior on functional resets and JTAG resets (asynchronous or TestLogicReset driven). 60 | - Fix JTAG non-compliance in TestLogicReset state (IR should reset to IDCODE). 61 | 62 | ## [0.4.1] - 2021-05-04 63 | ### Added 64 | ### Changed 65 | ### Fixed 66 | - Remove superfluous helper variable in dm_csrs.sv 67 | - Synchronized Bender.yml entries 68 | - Various Lint warnings 69 | 70 | ## [0.4.0] - 2020-11-06 71 | ### Added 72 | - Added parameter ReadByteEnable that may be disabled to revert SBA _be_ behavior to 0 on reads 73 | - Optional wrapper `dm_obi_top.sv` that wraps `dm_top` providing an OBI compliant interface 74 | - `tb` that runs dm in conjunction with ri5cy and OpenOCD 75 | - `.travis-ci.yml` running `tb` with verilator 76 | 77 | ### Changed 78 | - Made second scratch register optional (default is two) from [@zarubaf](https://github.com/zarubaf) 79 | - Use latest version of CV32E40P in testbench (#82) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 80 | - Move assertions into separate module (#82) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 81 | 82 | ### Fixed 83 | - Fix for SBA _be_ when reading to match the request size from (#70) [@jm4rtin](https://github.com/jm4rtin) 84 | - Off-by-one error in data and progbuf end address from [@pbing](https://github.com/pbing) 85 | - Haltsum1-3 calculation 86 | - A DMI read of SBAddress0 andSBAddress0 will (wrongly) trigger SBBUSYERROR when 87 | the system bus master is busy (#93) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 88 | - When the two scratch mode is being used, a0 was loaded instead of saved into 89 | scratch1 (#90) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 90 | - dmireset can be accidentally triggered (#89) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 91 | - enumeration lint issue in ProgBuf (#84) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 92 | - Fix faulty assertion because of bad `hartinfo_i` in testbench (#82) 93 | [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) 94 | - Return `CmdErrBusy` if accessing data or progbuf while command was executing 95 | (#79) [@noytzach](https://github.com/noytzach) 96 | 97 | ## [0.3.0] - 2020-01-23 98 | 99 | ### Added 100 | - Documentation in `doc/` from [@imphil](https://github.com/imphil) 101 | 102 | ### Changed 103 | - Various linting issues and cleanups from [@msfschaffner](https://github.com/msfschaffner) 104 | 105 | ### Fixed 106 | - Corruption on debug exception entry [@tomroberts-lowrisc](https://github.com/tomroberts-lowrisc) 107 | - truncation of `selected_hart` 108 | 109 | ## [0.2.0] - 2019-08-16 110 | 111 | ## Added 112 | - Add Bender.yml 113 | 114 | ### Fixed 115 | - Fix haltsum1, haltsum2 and haltsum3 116 | - Fix minor linter issues 117 | 118 | ## [0.1.0] - 2019-05-18 119 | 120 | ### Added 121 | - Parametrize buswidth to support 32-bit and 64-bit cores 122 | - Support arbitrary base addresses in debug ROM 123 | - Add misc helper functions to facilitate code generation 124 | - Add README 125 | - Fork from Ariane 126 | 127 | ### Changed 128 | - Allow generic number of data registers 129 | - Make JTAG IDCODE parametrizable 130 | 131 | ### Removed 132 | - Remove ariane specific packages 133 | 134 | ### Fixed 135 | - Fix resumeack and resumereq behaviour to be cleared and set according to debug 136 | specification 137 | - Add missing JTAG test logic reset handling 138 | - Fix resume logic in multihart situations 139 | - Fix missing else(s) in system bus access 140 | - Fix bad transitions into program buffer 141 | - Fix error handling when using unsupported abstract commands 142 | - Prevent harts from being in multiple states 143 | - Fix various style issues 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SOLDERPAD HARDWARE LICENSE version 0.51 2 | 3 | This license is based closely on the Apache License Version 2.0, but is not 4 | approved or endorsed by the Apache Foundation. A copy of the non-modified 5 | Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0. 6 | 7 | As this license is not currently OSI or FSF approved, the Licensor permits any 8 | Work licensed under this License, at the option of the Licensee, to be treated 9 | as licensed under the Apache License Version 2.0 (which is so approved). 10 | 11 | This License is licensed under the terms of this License and in particular 12 | clause 7 below (Disclaimer of Warranties) applies in relation to its use. 13 | 14 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 15 | 16 | 1. Definitions. 17 | 18 | “License” shall mean the terms and conditions for use, reproduction, and 19 | distribution as defined by Sections 1 through 9 of this document. 20 | 21 | “Licensor” shall mean the Rights owner or entity authorized by the Rights owner 22 | that is granting the License. 23 | 24 | “Legal Entity” shall mean the union of the acting entity and all other entities 25 | that control, are controlled by, or are under common control with that entity. 26 | For the purposes of this definition, “control” means (i) the power, direct or 27 | indirect, to cause the direction or management of such entity, whether by 28 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 29 | outstanding shares, or (iii) beneficial ownership of such entity. 30 | 31 | “You” (or “Your”) shall mean an individual or Legal Entity exercising 32 | permissions granted by this License. 33 | 34 | “Rights” means copyright and any similar right including design right (whether 35 | registered or unregistered), semiconductor topography (mask) rights and 36 | database rights (but excluding Patents and Trademarks). 37 | 38 | “Source” form shall mean the preferred form for making modifications, including 39 | but not limited to source code, net lists, board layouts, CAD files, 40 | documentation source, and configuration files. 41 | 42 | “Object” form shall mean any form resulting from mechanical transformation or 43 | translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, the instantiation of a hardware design and 45 | conversions to other media types, including intermediate forms such as 46 | bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask 47 | works). 48 | 49 | “Work” shall mean the work of authorship, whether in Source form or other 50 | Object form, made available under the License, as indicated by a Rights notice 51 | that is included in or attached to the work (an example is provided in the 52 | Appendix below). 53 | 54 | “Derivative Works” shall mean any work, whether in Source or Object form, that 55 | is based on (or derived from) the Work and for which the editorial revisions, 56 | annotations, elaborations, or other modifications represent, as a whole, an 57 | original work of authorship. For the purposes of this License, Derivative Works 58 | shall not include works that remain separable from, or merely link (or bind by 59 | name) or physically connect to or interoperate with the interfaces of, the Work 60 | and Derivative Works thereof. 61 | 62 | “Contribution” shall mean any design or work of authorship, including the 63 | original version of the Work and any modifications or additions to that Work or 64 | Derivative Works thereof, that is intentionally submitted to Licensor for 65 | inclusion in the Work by the Rights owner or by an individual or Legal Entity 66 | authorized to submit on behalf of the Rights owner. For the purposes of this 67 | definition, “submitted” means any form of electronic, verbal, or written 68 | communication sent to the Licensor or its representatives, including but not 69 | limited to communication on electronic mailing lists, source code control 70 | systems, and issue tracking systems that are managed by, or on behalf of, the 71 | Licensor for the purpose of discussing and improving the Work, but excluding 72 | communication that is conspicuously marked or otherwise designated in writing 73 | by the Rights owner as “Not a Contribution.” 74 | 75 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf 76 | of whom a Contribution has been received by Licensor and subsequently 77 | incorporated within the Work. 78 | 79 | 2. Grant of License. Subject to the terms and conditions of this License, each 80 | Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 81 | no-charge, royalty-free, irrevocable license under the Rights to reproduce, 82 | prepare Derivative Works of, publicly display, publicly perform, sublicense, 83 | and distribute the Work and such Derivative Works in Source or Object form and 84 | do anything in relation to the Work as if the Rights did not exist. 85 | 86 | 3. Grant of Patent License. Subject to the terms and conditions of this 87 | License, each Contributor hereby grants to You a perpetual, worldwide, 88 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this 89 | section) patent license to make, have made, use, offer to sell, sell, import, 90 | and otherwise transfer the Work, where such license applies only to those 91 | patent claims licensable by such Contributor that are necessarily infringed by 92 | their Contribution(s) alone or by combination of their Contribution(s) with the 93 | Work to which such Contribution(s) was submitted. If You institute patent 94 | litigation against any entity (including a cross-claim or counterclaim in a 95 | lawsuit) alleging that the Work or a Contribution incorporated within the Work 96 | constitutes direct or contributory patent infringement, then any patent 97 | licenses granted to You under this License for that Work shall terminate as of 98 | the date such litigation is filed. 99 | 100 | 4. Redistribution. You may reproduce and distribute copies of the Work or 101 | Derivative Works thereof in any medium, with or without modifications, and in 102 | Source or Object form, provided that You meet the following conditions: 103 | 104 | You must give any other recipients of the Work or Derivative Works a copy 105 | of this License; and 106 | 107 | You must cause any modified files to carry prominent notices stating that 108 | You changed the files; and 109 | 110 | You must retain, in the Source form of any Derivative Works that You 111 | distribute, all copyright, patent, trademark, and attribution notices from 112 | the Source form of the Work, excluding those notices that do not pertain to 113 | any part of the Derivative Works; and 114 | 115 | If the Work includes a “NOTICE” text file as part of its distribution, then 116 | any Derivative Works that You distribute must include a readable copy of 117 | the attribution notices contained within such NOTICE file, excluding those 118 | notices that do not pertain to any part of the Derivative Works, in at 119 | least one of the following places: within a NOTICE text file distributed as 120 | part of the Derivative Works; within the Source form or documentation, if 121 | provided along with the Derivative Works; or, within a display generated by 122 | the Derivative Works, if and wherever such third-party notices normally 123 | appear. The contents of the NOTICE file are for informational purposes only 124 | and do not modify the License. You may add Your own attribution notices 125 | within Derivative Works that You distribute, alongside or as an addendum to 126 | the NOTICE text from the Work, provided that such additional attribution 127 | notices cannot be construed as modifying the License. You may add Your own 128 | copyright statement to Your modifications and may provide additional or 129 | different license terms and conditions for use, reproduction, or 130 | distribution of Your modifications, or for any such Derivative Works as a 131 | whole, provided Your use, reproduction, and distribution of the Work 132 | otherwise complies with the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 135 | Contribution intentionally submitted for inclusion in the Work by You to the 136 | Licensor shall be under the terms and conditions of this License, without any 137 | additional terms or conditions. Notwithstanding the above, nothing herein shall 138 | supersede or modify the terms of any separate license agreement you may have 139 | executed with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade names, 142 | trademarks, service marks, or product names of the Licensor, except as required 143 | for reasonable and customary use in describing the origin of the Work and 144 | reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in 147 | writing, Licensor provides the Work (and each Contributor provides its 148 | Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 149 | KIND, either express or implied, including, without limitation, any warranties 150 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any risks 153 | associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, whether in 156 | tort (including negligence), contract, or otherwise, unless required by 157 | applicable law (such as deliberate and grossly negligent acts) or agreed to in 158 | writing, shall any Contributor be liable to You for damages, including any 159 | direct, indirect, special, incidental, or consequential damages of any 160 | character arising as a result of this License or out of the use or inability to 161 | use the Work (including but not limited to damages for loss of goodwill, work 162 | stoppage, computer failure or malfunction, or any and all other commercial 163 | damages or losses), even if such Contributor has been advised of the 164 | possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or 167 | Derivative Works thereof, You may choose to offer, and charge a fee for, 168 | acceptance of support, warranty, indemnity, or other liability obligations 169 | and/or rights consistent with this License. However, in accepting such 170 | obligations, You may act only on Your own behalf and on Your sole 171 | responsibility, not on behalf of any other Contributor, and only if You agree 172 | to indemnify, defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason of your 174 | accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RISC-V Debug Support for various Cores 2 | 3 | This module is an implementation of a debug unit compliant with the [RISC-V 4 | debug specification](https://github.com/riscv/riscv-debug-spec) v0.13.1. It is 5 | used in the [cva6](https://github.com/pulp-platform/cva6), 6 | [cv32e40p](https://github.com/pulp-platform/cv32e40p) and 7 | [ibex](https://github.com/lowRISC/ibex) cores. 8 | 9 | ## Implementation 10 | We use an execution-based technique, also described in the specification, where 11 | the core is running in a "park loop". Depending on the request made to the debug 12 | unit via JTAG over the Debug Transport Module (DTM), the code that is being 13 | executed is changed dynamically. This approach simplifies the implementation 14 | side of the core, but means that the core is in fact always busy looping while 15 | debugging. 16 | 17 | ## Features 18 | The following features are currently supported 19 | 20 | * Parametrizable buswidth for `XLEN=32` `XLEN=64` cores 21 | * Accessing registers over abstract command 22 | * Program buffer 23 | * System bus access (only `XLEN`) 24 | * DTM with JTAG interface 25 | 26 | These are not implemented (yet) 27 | 28 | * Trigger module 29 | * Quick access using abstract commands 30 | * Accessing memory using abstract commands 31 | * Authentication 32 | 33 | ## Limitations 34 | * The JTAG clock frequency needs to be lower than the system's clock frequency (see also https://github.com/pulp-platform/riscv-dbg/issues/163). 35 | 36 | ## Tests 37 | 38 | We use OpenOCD's [RISC-V compliance 39 | tests](https://github.com/riscv/riscv-openocd/blob/riscv/src/target/riscv/riscv-013.c), 40 | our custom testbench in `tb/` and 41 | [riscv-tests/debug](https://github.com/riscv/riscv-tests/tree/master/debug). 42 | -------------------------------------------------------------------------------- /ci/download-pulp-gcc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o pipefail 3 | set -e 4 | 5 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 6 | VERSION="v1.0.16" 7 | 8 | # mkdir -p $RISCV 9 | 10 | wget https://github.com/pulp-platform/pulp-riscv-gnu-toolchain/releases/download/$VERSION/$VERSION-pulp-riscv-gcc-ubuntu-16.tar.bz2 11 | echo "unpacking pulp gcc and installing to $RISCV" 12 | tar -xvf $VERSION-pulp-riscv-gcc-ubuntu-16.tar.bz2 -C "$RISCV" --strip 1 13 | -------------------------------------------------------------------------------- /ci/get-openocd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | VERSION="af3a034b57279d2a400d87e7508c9a92254ec165" 6 | 7 | mkdir -p $RISCV/ 8 | cd $RISCV 9 | 10 | check_version() { 11 | $1 --version | awk "NR==1 {if (\$NF>$2) {exit 0} exit 1}" || ( 12 | echo $3 requires at least version $2 of $1. Aborting. 13 | exit 1 14 | ) 15 | } 16 | 17 | 18 | if [ -z ${NUM_JOBS} ]; then 19 | NUM_JOBS=1 20 | fi 21 | 22 | if ! [ -e $RISCV/bin/openocd ]; then 23 | if ! [ -e $RISCV/riscv-openocd ]; then 24 | git clone https://github.com/riscv/riscv-openocd.git 25 | fi 26 | check_version automake 1.14 "OpenOCD build" 27 | check_version autoconf 2.64 "OpenOCD build" 28 | 29 | cd riscv-openocd 30 | git checkout $VERSION 31 | git submodule update --init --recursive 32 | 33 | echo "Compiling OpenOCD" 34 | ./bootstrap 35 | ./configure --prefix=$RISCV --disable-werror --disable-wextra --enable-remote-bitbang 36 | make -j${NUM_JOBS} 37 | make install 38 | echo "Compilation Finished" 39 | fi 40 | 41 | -------------------------------------------------------------------------------- /ci/install-verilator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 4 | cd $ROOT/tmp 5 | 6 | if [ -z ${NUM_JOBS} ]; then 7 | NUM_JOBS=1 8 | fi 9 | 10 | if [ ! -e "$VERILATOR_ROOT/bin/verilator" ]; then 11 | echo "Installing Verilator" 12 | rm -f verilator*.tgz 13 | wget https://www.veripool.org/ftp/verilator-4.018.tgz 14 | tar xzf verilator*.tgz 15 | rm -f verilator*.tgz 16 | cd verilator-4.018 17 | mkdir -p $VERILATOR_ROOT 18 | # copy scripts 19 | autoconf && ./configure --prefix="$VERILATOR_ROOT" && make -j${NUM_JOBS} 20 | make install 21 | # not obvious to me why these symlinks are missing 22 | ln -s $VERILATOR_ROOT/share/verilator/include $VERILATOR_ROOT/include 23 | ln -s $VERILATOR_ROOT/share/verilator/bin/verilator_includer \ 24 | $VERILATOR_ROOT/bin/verilator_includer 25 | make test 26 | else 27 | echo "Using Verilator from cached directory." 28 | fi 29 | -------------------------------------------------------------------------------- /ci/make-tmp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cd "$(dirname "${BASH_SOURCE[0]}")/.." 4 | [ -d tmp ] || rm -rf tmp 5 | mkdir -p tmp 6 | -------------------------------------------------------------------------------- /ci/openocd-to-junit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys, getopt 3 | from junit_xml import * 4 | 5 | 6 | def main(argv): 7 | inputfile = '' 8 | outputfile = '' 9 | 10 | try: 11 | opts, args = getopt.getopt(argv,"hi:o:",["ifile=","ofile="]) 12 | except getopt.GetoptError: 13 | print ('openocd-to-junit.py -i -o ') 14 | sys.exit(2) 15 | for opt, arg in opts: 16 | if opt == '-h': 17 | print ('openocd-to-junit.py -i -o ') 18 | sys.exit() 19 | elif opt in ("-i", "--ifile"): 20 | inputfile = arg 21 | elif opt in ("-o", "--ofile"): 22 | outputfile = arg 23 | 24 | test_strings = defaultdict(list) 25 | test_timestamps = {} 26 | current_testname = '' 27 | 28 | test_cases = [] 29 | current_test_case = None 30 | 31 | ocd_stdout = '' 32 | 33 | with open(inputfile, 'r') as infile: 34 | for line in infile: 35 | if 'Info' in line and 'riscv013_test_compliance()' in line: 36 | print(line.split(' ')) 37 | current_testname = ' '.join(line.split(' ')[7:]) 38 | test_strings[current_testname].append(line) 39 | test_timestamps[current_testname] = line.split(' ')[3] 40 | 41 | ocd_stdout += line 42 | 43 | for k,v in test_strings.items(): 44 | current_test_case = TestCase(k, stdout=''.join(v), 45 | timestamp=test_timestamps[k]) 46 | error_msg = "" 47 | for line in v: 48 | if 'FAILED' in line: 49 | error_msg += line; 50 | 51 | if error_msg: 52 | current_test_case.add_error_info(error_msg) 53 | 54 | test_cases.append(current_test_case) 55 | 56 | ts = TestSuite("openocd-compliance", test_cases, stdout=ocd_stdout) 57 | # pretty printing is on by default but can be disabled using prettyprint=False 58 | with open(outputfile, 'w') as outfile: 59 | TestSuite.to_file(outfile, [ts]) 60 | 61 | if __name__ == "__main__": 62 | main(sys.argv[1:]) 63 | -------------------------------------------------------------------------------- /ci/run-openocd-compliance.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 6 | 7 | if [ -z "${RISCV}" ] 8 | then 9 | echo "RISCV is empty" 10 | exit 1 11 | fi 12 | 13 | function cleanup { 14 | echo "cleaning up processes and tmp files" 15 | sleep 2 16 | echo "vsim pid is:${vsim_pid} pgid:${vsim_pgid}" 17 | if ps -p "${vsim_pid}" > /dev/null 18 | then 19 | echo "vsim pid exists, killing it" 20 | kill -- -"${vsim_pgid}" 21 | fi 22 | rm "${vsim_out}" 23 | } 24 | 25 | trap cleanup EXIT 26 | 27 | vsim_out=$(mktemp) 28 | openocd_out=openocd.log 29 | 30 | make -C "${ROOT}"/tb/dm vsim-run &> "${vsim_out}"& 31 | # record vsim pid/pgid to kill it if it survives this script 32 | vsim_pid=$! 33 | vsim_pgid=$(ps -o pgid= ${vsim_pid} | grep -o [0-9]*) 34 | 35 | # block until we get "Listening on port" so that we are safe to connect openocd 36 | coproc grep -m 1 "Listening on port" 37 | tail -f -n0 "${vsim_out}" --pid "$COPROC_PID" >&"${COPROC[1]}" 38 | 39 | echo "Starting openocd" 40 | "${RISCV}"/bin/openocd -f "${ROOT}"/tb/dm/dm_compliance_test.cfg |& tee "${openocd_out}" 41 | 42 | 43 | if grep -q "ALL TESTS PASSED" "${openocd_out}"; then 44 | exit 0 45 | fi 46 | exit 1 47 | 48 | -------------------------------------------------------------------------------- /debug_rom/.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.elf 3 | debug_rom.img 4 | -------------------------------------------------------------------------------- /debug_rom/Makefile: -------------------------------------------------------------------------------- 1 | # See LICENSE.SiFive for license details 2 | 3 | debug_rom = debug_rom.sv debug_rom_one_scratch.sv 4 | 5 | GCC?=riscv64-unknown-elf-gcc 6 | OBJCOPY?=riscv64-unknown-elf-objcopy 7 | OBJDUMP?=riscv64-unknown-elf-objdump 8 | PYTHON?=python3 9 | 10 | all: $(debug_rom) 11 | 12 | %.sv: %.img 13 | $(PYTHON) gen_rom.py $< 14 | 15 | %.img: %.bin 16 | dd if=$< of=$@ bs=256 count=1 17 | 18 | %.bin: %.elf 19 | $(OBJCOPY) -O binary $< $@ 20 | 21 | %.elf: $(findstring debug_rom, $(debug_rom)).S link.ld 22 | $(GCC) $(if $(findstring one_scratch,$@),,-DSND_SCRATCH=1) -I$(RISCV)/include -Tlink.ld $< -nostdlib -fPIC -static -Wl,--no-gc-sections -o $@ 23 | 24 | %.dump: %.elf 25 | $(OBJDUMP) -d $< --disassemble-all --disassemble-zeroes --section=.text --section=.text.startup --section=.text.init --section=.data > $@ 26 | 27 | clean: 28 | rm -f *.img *.dump *.bin *.sv 29 | -------------------------------------------------------------------------------- /debug_rom/debug_rom.S: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | 3 | #include "encoding.h" 4 | 5 | // The debugger can assume as second scratch register. 6 | // # define SND_SCRATCH 1 7 | // These are implementation-specific addresses in the Debug Module 8 | #define HALTED 0x100 9 | #define GOING 0x108 10 | #define RESUMING 0x110 11 | #define EXCEPTION 0x118 12 | 13 | // Region of memory where each hart has 1 14 | // byte to read. 15 | #define FLAGS 0x400 16 | #define FLAG_GO 0 17 | #define FLAG_RESUME 1 18 | 19 | .option norvc 20 | .global entry 21 | .global exception 22 | 23 | // Entry location on ebreak, Halt, or Breakpoint 24 | // It is the same for all harts. They branch when 25 | // their GO or RESUME bit is set. 26 | 27 | entry: 28 | jal zero, _entry 29 | nop 30 | resume: 31 | jal zero, _resume 32 | nop 33 | exception: 34 | jal zero, _exception 35 | nop 36 | 37 | 38 | 39 | _entry: 40 | // This fence is required because the execution may have written something 41 | // into the Abstract Data or Program Buffer registers. 42 | fence 43 | csrw CSR_DSCRATCH0, s0 // Save s0 to allow signaling MHARTID 44 | #ifdef SND_SCRATCH 45 | csrw CSR_DSCRATCH1, a0 // Save a0 to allow loading arbitrary DM base 46 | auipc a0, 0 // Get PC 47 | srli a0, a0, 12 // And throw away lower 12 bits to get the DM base 48 | slli a0, a0, 12 49 | #endif 50 | // We continue to let the hart know that we are halted in order that 51 | // a DM which was reset is still made aware that a hart is halted. 52 | // We keep checking both whether there is something the debugger wants 53 | // us to do, or whether we should resume. 54 | entry_loop: 55 | csrr s0, CSR_MHARTID 56 | #ifdef SND_SCRATCH 57 | sw s0, HALTED(a0) 58 | add s0, s0, a0 59 | #else 60 | sw s0, HALTED(zero) 61 | #endif 62 | lbu s0, FLAGS(s0) // 1 byte flag per hart. Only one hart advances here. 63 | andi s0, s0, (1 << FLAG_GO) 64 | bnez s0, going 65 | csrr s0, CSR_MHARTID 66 | #ifdef SND_SCRATCH 67 | add s0, s0, a0 68 | #endif 69 | lbu s0, FLAGS(s0) // multiple harts can resume here 70 | andi s0, s0, (1 << FLAG_RESUME) 71 | bnez s0, resume 72 | jal zero, entry_loop 73 | 74 | _exception: 75 | // We can only get here due to an exception while in debug mode. Hence, 76 | // we do not need to save a0 to a scratch register as it has already 77 | // been saved on debug entry. 78 | #ifdef SND_SCRATCH 79 | auipc a0, 0 // Get POC 80 | srli a0, a0, 12 // And throw away lower 12 bits to get the DM base 81 | slli a0, a0, 12 82 | sw zero, EXCEPTION(a0) // Let debug module know you got an exception. 83 | // It is safe to always restore the scratch registers here as they must 84 | // have been saved on debug entry. Restoring them here avoids issues 85 | // with registers being overwritten by exceptions occuring during 86 | // program buffer execution. 87 | csrr a0, CSR_DSCRATCH1 // Restore a0 here 88 | #else 89 | sw zero, EXCEPTION(zero) // Let debug module know you got an exception. 90 | #endif 91 | csrr s0, CSR_DSCRATCH0 // Restore s0 here 92 | ebreak 93 | 94 | going: 95 | #ifdef SND_SCRATCH 96 | sw zero, GOING(a0) // When debug module sees this write, the GO flag is reset. 97 | csrr a0, CSR_DSCRATCH1 // Restore a0 here 98 | #else 99 | sw zero, GOING(zero) // When debug module sees this write, the GO flag is reset. 100 | #endif 101 | csrr s0, CSR_DSCRATCH0 // Restore s0 here 102 | jal zero, whereto 103 | _resume: 104 | csrr s0, CSR_MHARTID 105 | #ifdef SND_SCRATCH 106 | sw s0, RESUMING(a0) // When Debug Module sees this write, the RESUME flag is reset. 107 | csrr a0, CSR_DSCRATCH1 // Restore a0 here 108 | #else 109 | sw s0, RESUMING(zero) // When Debug Module sees this write, the RESUME flag is reset. 110 | #endif 111 | csrr s0, CSR_DSCRATCH0 // Restore s0 here 112 | dret 113 | 114 | // END OF ACTUAL "ROM" CONTENTS. BELOW IS JUST FOR LINKER SCRIPT. 115 | 116 | .section .whereto 117 | whereto: 118 | nop 119 | // Variable "ROM" This is : jal x0 abstract, jal x0 program_buffer, 120 | // or jal x0 resume, as desired. 121 | // Debug Module state machine tracks what is 'desired'. 122 | // We don't need/want to use jalr here because all of the 123 | // Variable ROM contents are set by 124 | // Debug Module before setting the OK_GO byte. 125 | -------------------------------------------------------------------------------- /debug_rom/debug_rom.h: -------------------------------------------------------------------------------- 1 | // Auto-generated code 2 | 3 | const int reset_vec_size = 40; 4 | 5 | uint32_t reset_vec[reset_vec_size] = { 6 | 0x0180006f, 7 | 0x00000013, 8 | 0x0840006f, 9 | 0x00000013, 10 | 0x0500006f, 11 | 0x00000013, 12 | 0x0ff0000f, 13 | 0x7b241073, 14 | 0x7b351073, 15 | 0x00000517, 16 | 0x00c55513, 17 | 0x00c51513, 18 | 0xf1402473, 19 | 0x10852023, 20 | 0x00a40433, 21 | 0x40044403, 22 | 0x00147413, 23 | 0x02041c63, 24 | 0xf1402473, 25 | 0x00a40433, 26 | 0x40044403, 27 | 0x00247413, 28 | 0xfa0418e3, 29 | 0xfd5ff06f, 30 | 0x00000517, 31 | 0x00c55513, 32 | 0x00c51513, 33 | 0x10052c23, 34 | 0x7b302573, 35 | 0x7b202473, 36 | 0x00100073, 37 | 0x10052423, 38 | 0x7b302573, 39 | 0x7b202473, 40 | 0xa79ff06f, 41 | 0xf1402473, 42 | 0x10852823, 43 | 0x7b302573, 44 | 0x7b202473, 45 | 0x7b200073 46 | }; 47 | -------------------------------------------------------------------------------- /debug_rom/debug_rom.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the "License"); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: $filename.v 12 | * 13 | * Description: Auto-generated bootrom 14 | */ 15 | 16 | // Auto-generated code 17 | module debug_rom ( 18 | input logic clk_i, 19 | input logic rst_ni, 20 | input logic req_i, 21 | input logic [63:0] addr_i, 22 | output logic [63:0] rdata_o 23 | ); 24 | 25 | localparam int unsigned RomSize = 20; 26 | 27 | logic [RomSize-1:0][63:0] mem; 28 | assign mem = { 29 | 64'h7b200073_7b202473, 30 | 64'h7b302573_10852823, 31 | 64'hf1402473_a79ff06f, 32 | 64'h7b202473_7b302573, 33 | 64'h10052423_00100073, 34 | 64'h7b202473_7b302573, 35 | 64'h10052c23_00c51513, 36 | 64'h00c55513_00000517, 37 | 64'hfd5ff06f_fa0418e3, 38 | 64'h00247413_40044403, 39 | 64'h00a40433_f1402473, 40 | 64'h02041c63_00147413, 41 | 64'h40044403_00a40433, 42 | 64'h10852023_f1402473, 43 | 64'h00c51513_00c55513, 44 | 64'h00000517_7b351073, 45 | 64'h7b241073_0ff0000f, 46 | 64'h00000013_0500006f, 47 | 64'h00000013_0840006f, 48 | 64'h00000013_0180006f 49 | }; 50 | 51 | logic [$clog2(RomSize)-1:0] addr_d, addr_q; 52 | 53 | assign addr_d = req_i ? addr_i[$clog2(RomSize)-1+3:3] : addr_q; 54 | 55 | always_ff @(posedge clk_i or negedge rst_ni) begin 56 | if (!rst_ni) begin 57 | addr_q <= '0; 58 | end else begin 59 | addr_q <= addr_d; 60 | end 61 | end 62 | 63 | // this prevents spurious Xes from propagating into 64 | // the speculative fetch stage of the core 65 | always_comb begin : p_outmux 66 | rdata_o = '0; 67 | if (addr_q < $clog2(RomSize)'(RomSize)) begin 68 | rdata_o = mem[addr_q]; 69 | end 70 | end 71 | 72 | endmodule 73 | -------------------------------------------------------------------------------- /debug_rom/debug_rom_one_scratch.h: -------------------------------------------------------------------------------- 1 | // Auto-generated code 2 | 3 | const int reset_vec_size = 28; 4 | 5 | uint32_t reset_vec[reset_vec_size] = { 6 | 0x0180006f, 7 | 0x00000013, 8 | 0x0580006f, 9 | 0x00000013, 10 | 0x0380006f, 11 | 0x00000013, 12 | 0x0ff0000f, 13 | 0x7b241073, 14 | 0xf1402473, 15 | 0x10802023, 16 | 0x40044403, 17 | 0x00147413, 18 | 0x02041263, 19 | 0xf1402473, 20 | 0x40044403, 21 | 0x00247413, 22 | 0xfc0414e3, 23 | 0xfddff06f, 24 | 0x10002c23, 25 | 0x7b202473, 26 | 0x00100073, 27 | 0x10002423, 28 | 0x7b202473, 29 | 0xaa5ff06f, 30 | 0xf1402473, 31 | 0x10802823, 32 | 0x7b202473, 33 | 0x7b200073 34 | }; 35 | -------------------------------------------------------------------------------- /debug_rom/debug_rom_one_scratch.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the "License"); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: $filename.v 12 | * 13 | * Description: Auto-generated bootrom 14 | */ 15 | 16 | // Auto-generated code 17 | module debug_rom_one_scratch ( 18 | input logic clk_i, 19 | input logic rst_ni, 20 | input logic req_i, 21 | input logic [63:0] addr_i, 22 | output logic [63:0] rdata_o 23 | ); 24 | 25 | localparam int unsigned RomSize = 14; 26 | 27 | logic [RomSize-1:0][63:0] mem; 28 | assign mem = { 29 | 64'h7b200073_7b202473, 30 | 64'h10802823_f1402473, 31 | 64'haa5ff06f_7b202473, 32 | 64'h10002423_00100073, 33 | 64'h7b202473_10002c23, 34 | 64'hfddff06f_fc0414e3, 35 | 64'h00247413_40044403, 36 | 64'hf1402473_02041263, 37 | 64'h00147413_40044403, 38 | 64'h10802023_f1402473, 39 | 64'h7b241073_0ff0000f, 40 | 64'h00000013_0380006f, 41 | 64'h00000013_0580006f, 42 | 64'h00000013_0180006f 43 | }; 44 | 45 | logic [$clog2(RomSize)-1:0] addr_d, addr_q; 46 | 47 | assign addr_d = req_i ? addr_i[$clog2(RomSize)-1+3:3] : addr_q; 48 | 49 | always_ff @(posedge clk_i or negedge rst_ni) begin 50 | if (!rst_ni) begin 51 | addr_q <= '0; 52 | end else begin 53 | addr_q <= addr_d; 54 | end 55 | end 56 | 57 | // this prevents spurious Xes from propagating into 58 | // the speculative fetch stage of the core 59 | always_comb begin : p_outmux 60 | rdata_o = '0; 61 | if (addr_q < $clog2(RomSize)'(RomSize)) begin 62 | rdata_o = mem[addr_q]; 63 | end 64 | end 65 | 66 | endmodule 67 | -------------------------------------------------------------------------------- /debug_rom/gen_rom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from string import Template 4 | import argparse 5 | import os.path 6 | import sys 7 | import binascii 8 | 9 | 10 | parser = argparse.ArgumentParser(description='Convert binary file to verilog rom') 11 | parser.add_argument('filename', metavar='filename', nargs=1, 12 | help='filename of input binary') 13 | 14 | args = parser.parse_args() 15 | file = args.filename[0]; 16 | 17 | # check that file exists 18 | if not os.path.isfile(file): 19 | print("File {} does not exist.".format(filename)) 20 | sys.exit(1) 21 | 22 | filename = os.path.splitext(file)[0] 23 | 24 | license = """\ 25 | /* Copyright 2018 ETH Zurich and University of Bologna. 26 | * Copyright and related rights are licensed under the Solderpad Hardware 27 | * License, Version 0.51 (the "License"); you may not use this file except in 28 | * compliance with the License. You may obtain a copy of the License at 29 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 30 | * or agreed to in writing, software, hardware and materials distributed under 31 | * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 32 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 33 | * specific language governing permissions and limitations under the License. 34 | * 35 | * File: $filename.v 36 | * 37 | * Description: Auto-generated bootrom 38 | */ 39 | 40 | // Auto-generated code 41 | """ 42 | 43 | module = """\ 44 | module $filename ( 45 | input logic clk_i, 46 | input logic rst_ni, 47 | input logic req_i, 48 | input logic [63:0] addr_i, 49 | output logic [63:0] rdata_o 50 | ); 51 | 52 | localparam int unsigned RomSize = $size; 53 | 54 | logic [RomSize-1:0][63:0] mem; 55 | assign mem = { 56 | $content 57 | }; 58 | 59 | logic [$$clog2(RomSize)-1:0] addr_d, addr_q; 60 | 61 | assign addr_d = req_i ? addr_i[$$clog2(RomSize)-1+3:3] : addr_q; 62 | 63 | always_ff @(posedge clk_i or negedge rst_ni) begin 64 | if (!rst_ni) begin 65 | addr_q <= '0; 66 | end else begin 67 | addr_q <= addr_d; 68 | end 69 | end 70 | 71 | // this prevents spurious Xes from propagating into 72 | // the speculative fetch stage of the core 73 | always_comb begin : p_outmux 74 | rdata_o = '0; 75 | if (addr_q < $$clog2(RomSize)'(RomSize)) begin 76 | rdata_o = mem[addr_q]; 77 | end 78 | end 79 | 80 | endmodule 81 | """ 82 | 83 | c_var = """\ 84 | // Auto-generated code 85 | 86 | const int reset_vec_size = $size; 87 | 88 | uint32_t reset_vec[reset_vec_size] = { 89 | $content 90 | }; 91 | """ 92 | 93 | def read_bin(): 94 | 95 | with open(filename + ".img", 'rb') as f: 96 | rom = bytes.hex(f.read()) 97 | rom = list(map(''.join, zip(rom[::2], rom[1::2]))) 98 | 99 | # align to 64 bit 100 | align = (int((len(rom) + 7) / 8 )) * 8; 101 | 102 | for i in range(len(rom), align): 103 | rom.append("00") 104 | 105 | return rom 106 | 107 | rom = read_bin() 108 | 109 | """ Generate C header file for simulator 110 | """ 111 | with open(filename + ".h", "w") as f: 112 | rom_str = "" 113 | # process in junks of 32 bit (4 byte) 114 | for i in range(0, int(len(rom)/4)): 115 | rom_str += " 0x" + "".join(rom[i*4:i*4+4][::-1]) + ",\n" 116 | 117 | # remove the trailing comma 118 | rom_str = rom_str[:-2] 119 | 120 | s = Template(c_var) 121 | f.write(s.substitute(filename=filename, size=int(len(rom)/4), content=rom_str)) 122 | 123 | f.close() 124 | 125 | """ Generate SystemVerilog bootcode for FPGA and ASIC 126 | """ 127 | with open(filename + ".sv", "w") as f: 128 | rom_str = "" 129 | # process in junks of 64 bit (8 byte) 130 | for i in reversed(range(int(len(rom)/8))): 131 | rom_str += " 64'h" + "".join(rom[i*8+4:i*8+8][::-1]) + "_" + "".join(rom[i*8:i*8+4][::-1]) + ",\n" 132 | 133 | # remove the trailing comma 134 | rom_str = rom_str[:-2] 135 | 136 | f.write(license) 137 | s = Template(module) 138 | f.write(s.substitute(filename=filename, size=int(len(rom)/8), content=rom_str)) 139 | -------------------------------------------------------------------------------- /debug_rom/link.ld: -------------------------------------------------------------------------------- 1 | /* See LICENSE.SiFive for license details. */ 2 | OUTPUT_ARCH( "riscv" ) 3 | ENTRY( entry ) 4 | SECTIONS 5 | { 6 | .whereto 0x300 : 7 | { 8 | *(.whereto) 9 | } 10 | . = 0x800; 11 | .text : 12 | { 13 | *(.text) 14 | } 15 | _end = .; 16 | } 17 | -------------------------------------------------------------------------------- /doc/dmi_protocol.json: -------------------------------------------------------------------------------- 1 | {signal: [ 2 | {name: 'clk', wave: 'p...|....|....|....'}, 3 | {name: 'dmi_req.op', wave: 'x=.x|....|x=.x|....', data: ['READ', 'WRITE']}, 4 | {name: 'dmi_req.addr', wave: 'x=.x|....|x=.x|....', data: ['0x12', '0x34']}, 5 | {name: 'dmi_req.data', wave: 'x...|....|x=.x|....', data: ['0xabcd']}, 6 | {name: 'dmi_req_valid', wave: '01.0|....|.1.0|....'}, 7 | {name: 'dmi_req_ready', wave: '0.1.|..0.|..1.|....'}, 8 | {}, 9 | {name: 'dmi_resp.resp',wave: 'x...|x=.x|....|x=.x', data: ['SUCCESS', 'SUCCESS']}, 10 | {name: 'dmi_resp.data',wave: 'x...|x=.x|....|....', data: ['0x1234']}, 11 | {name: 'dmi_resp_valid', wave: '0...|.1.0|....|..10'}, 12 | {name: 'dmi_resp_ready', wave: '1...|0.1.|....|....'}, 13 | ]} 14 | -------------------------------------------------------------------------------- /src/dm_obi_top.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Silicon Labs, Inc. 2 | // 3 | // This file, and derivatives thereof are licensed under the 4 | // Solderpad License, Version 2.0 (the "License"). 5 | // 6 | // Use of this file means you agree to the terms and conditions 7 | // of the license and are in full compliance with the License. 8 | // 9 | // You may obtain a copy of the License at: 10 | // 11 | // https://solderpad.org/licenses/SHL-2.0/ 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // and hardware implementations thereof distributed under the License 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 16 | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED. 17 | // 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // Engineer: Arjan Bink - arjan.bink@silabs.com // 23 | // // 24 | // Design Name: OBI Wrapper for Debug Module (dm_top) // 25 | // Project Name: CV32E40P // 26 | // Language: SystemVerilog // 27 | // // 28 | // Description: Wrapper for the Debug Module (dm_top) which gives it an // 29 | // OBI (Open Bus Interface) compatible interfaces so that it // 30 | // can be integrated without further glue logic (other than // 31 | // tie offs in an OBI compliant system. // 32 | // // 33 | // This wrapper is only intended for OBI compliant systems; // 34 | // in other systems the existing dm_top can be used as before // 35 | // and this wrapper can be ignored. // 36 | // // 37 | // The OBI spec is available at: // 38 | // // 39 | // - https://github.com/openhwgroup/core-v-docs/blob/master/ // 40 | // cores/cv32e40p/ // 41 | // // 42 | // Compared to 'logint' interfaces of dm_top the following // 43 | // signals are added: // 44 | // // 45 | // - slave_* OBI interface: // 46 | // // 47 | // - slave_gnt_o // 48 | // - slave_rvalid_o // 49 | // - slave_aid_i // 50 | // - slave_rid_o // 51 | // // 52 | // Compared to 'logint' interfaces of dm_top the following // 53 | // signals have been renamed: // 54 | // // 55 | // - master_* OBI interface: // 56 | // // 57 | // - Renamed master_add_o to master_addr_o // 58 | // - Renamed master_r_valid_i to master_rvalid_i // 59 | // - Renamed master_r_rdata_i to master_rdata_i // 60 | // // 61 | //////////////////////////////////////////////////////////////////////////////// 62 | 63 | module dm_obi_top #( 64 | parameter int unsigned IdWidth = 1, // Width of aid/rid 65 | parameter int unsigned NrHarts = 1, 66 | parameter int unsigned BusWidth = 32, 67 | parameter int unsigned DmBaseAddress = 'h1000, // default to non-zero page 68 | // Bitmask to select physically available harts for systems 69 | // that don't use hart numbers in a contiguous fashion. 70 | parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}} 71 | ) ( 72 | input logic clk_i, // clock 73 | // asynchronous reset active low, connect PoR here, not the system reset 74 | input logic rst_ni, 75 | input logic testmode_i, 76 | output logic ndmreset_o, // non-debug module reset 77 | output logic dmactive_o, // debug module is active 78 | output logic [NrHarts-1:0] debug_req_o, // async debug request 79 | // communicate whether the hart is unavailable (e.g.: power down) 80 | input logic [NrHarts-1:0] unavailable_i, 81 | input dm::hartinfo_t [NrHarts-1:0] hartinfo_i, 82 | 83 | input logic slave_req_i, 84 | // OBI grant for slave_req_i (not present on dm_top) 85 | output logic slave_gnt_o, 86 | input logic slave_we_i, 87 | input logic [BusWidth-1:0] slave_addr_i, 88 | input logic [BusWidth/8-1:0] slave_be_i, 89 | input logic [BusWidth-1:0] slave_wdata_i, 90 | // Address phase transaction identifier (not present on dm_top) 91 | input logic [IdWidth-1:0] slave_aid_i, 92 | // OBI rvalid signal (end of response phase for reads/writes) (not present on dm_top) 93 | output logic slave_rvalid_o, 94 | output logic [BusWidth-1:0] slave_rdata_o, 95 | // Response phase transaction identifier (not present on dm_top) 96 | output logic [IdWidth-1:0] slave_rid_o, 97 | 98 | output logic master_req_o, 99 | output logic [BusWidth-1:0] master_addr_o, // Renamed according to OBI spec 100 | output logic master_we_o, 101 | output logic [BusWidth-1:0] master_wdata_o, 102 | output logic [BusWidth/8-1:0] master_be_o, 103 | input logic master_gnt_i, 104 | input logic master_rvalid_i, // Renamed according to OBI spec 105 | input logic master_err_i, 106 | input logic master_other_err_i, // *other_err_i has priority over *err_i 107 | input logic [BusWidth-1:0] master_rdata_i, // Renamed according to OBI spec 108 | 109 | // Connection to DTM - compatible to RocketChip Debug Module 110 | input logic dmi_rst_ni, 111 | input logic dmi_req_valid_i, 112 | output logic dmi_req_ready_o, 113 | input dm::dmi_req_t dmi_req_i, 114 | 115 | output logic dmi_resp_valid_o, 116 | input logic dmi_resp_ready_i, 117 | output dm::dmi_resp_t dmi_resp_o 118 | ); 119 | 120 | // Slave response phase (rvalid and identifier) 121 | logic slave_rvalid_q; 122 | logic [IdWidth-1:0] slave_rid_q; 123 | 124 | // dm_top instance 125 | dm_top #( 126 | .NrHarts ( NrHarts ), 127 | .BusWidth ( BusWidth ), 128 | .DmBaseAddress ( DmBaseAddress ), 129 | .SelectableHarts ( SelectableHarts ) 130 | ) i_dm_top ( 131 | .clk_i ( clk_i ), 132 | .rst_ni ( rst_ni ), 133 | .next_dm_addr_i ( '0 ), // Note exposed at the top yet. 134 | .testmode_i ( testmode_i ), 135 | .ndmreset_o ( ndmreset_o ), 136 | .ndmreset_ack_i ( ndmreset_o ), // This is currently not exposed yet. 137 | .dmactive_o ( dmactive_o ), 138 | .debug_req_o ( debug_req_o ), 139 | .unavailable_i ( unavailable_i ), 140 | .hartinfo_i ( hartinfo_i ), 141 | 142 | .slave_req_i ( slave_req_i ), 143 | .slave_we_i ( slave_we_i ), 144 | .slave_addr_i ( slave_addr_i ), 145 | .slave_be_i ( slave_be_i ), 146 | .slave_wdata_i ( slave_wdata_i ), 147 | .slave_rdata_o ( slave_rdata_o ), 148 | 149 | .master_req_o ( master_req_o ), 150 | .master_add_o ( master_addr_o ), // Renamed according to OBI spec 151 | .master_we_o ( master_we_o ), 152 | .master_wdata_o ( master_wdata_o ), 153 | .master_be_o ( master_be_o ), 154 | .master_gnt_i ( master_gnt_i ), 155 | .master_r_valid_i ( master_rvalid_i ), // Renamed according to OBI spec 156 | .master_r_err_i ( master_err_i ), 157 | .master_r_other_err_i ( master_other_err_i ), // *other_err_i has priority over *err_i 158 | .master_r_rdata_i ( master_rdata_i ), // Renamed according to OBI spec 159 | 160 | .dmi_rst_ni ( dmi_rst_ni ), 161 | .dmi_req_valid_i ( dmi_req_valid_i ), 162 | .dmi_req_ready_o ( dmi_req_ready_o ), 163 | .dmi_req_i ( dmi_req_i ), 164 | 165 | .dmi_resp_valid_o ( dmi_resp_valid_o ), 166 | .dmi_resp_ready_i ( dmi_resp_ready_i ), 167 | .dmi_resp_o ( dmi_resp_o ) 168 | ); 169 | 170 | // Extension to wrap dm_top as an OBI-compliant module 171 | // 172 | // dm_top has an implied rvalid pulse the cycle after its granted request. 173 | 174 | // Registers 175 | always_ff @(posedge clk_i or negedge rst_ni) begin : obi_regs 176 | if (!rst_ni) begin 177 | slave_rvalid_q <= 1'b0; 178 | slave_rid_q <= 'b0; 179 | end else begin 180 | if (slave_req_i && slave_gnt_o) begin // 1 cycle pulse on rvalid for every granted request 181 | slave_rvalid_q <= 1'b1; 182 | slave_rid_q <= slave_aid_i; // Mirror aid to rid 183 | end else begin 184 | slave_rvalid_q <= 1'b0; // rid is don't care if rvalid = 0 185 | end 186 | end 187 | end 188 | 189 | assign slave_gnt_o = 1'b1; // Always receptive to request (slave_req_i) 190 | assign slave_rvalid_o = slave_rvalid_q; 191 | assign slave_rid_o = slave_rid_q; 192 | 193 | endmodule : dm_obi_top 194 | -------------------------------------------------------------------------------- /src/dm_sba.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the “License”); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: dm_sba.sv 12 | * Author: Florian Zaruba 13 | * Date: 1.8.2018 14 | * 15 | * Description: System Bus Access Module 16 | * 17 | */ 18 | module dm_sba #( 19 | parameter int unsigned BusWidth = 32, 20 | parameter bit ReadByteEnable = 1 21 | ) ( 22 | input logic clk_i, // Clock 23 | input logic rst_ni, 24 | input logic dmactive_i, // synchronous reset active low 25 | 26 | output logic master_req_o, 27 | output logic [BusWidth-1:0] master_add_o, 28 | output logic master_we_o, 29 | output logic [BusWidth-1:0] master_wdata_o, 30 | output logic [BusWidth/8-1:0] master_be_o, 31 | input logic master_gnt_i, 32 | input logic master_r_valid_i, 33 | input logic master_r_err_i, 34 | input logic master_r_other_err_i, // *other_err_i has priority over *err_i 35 | input logic [BusWidth-1:0] master_r_rdata_i, 36 | 37 | input logic [BusWidth-1:0] sbaddress_i, 38 | input logic sbaddress_write_valid_i, 39 | // control signals in 40 | input logic sbreadonaddr_i, 41 | output logic [BusWidth-1:0] sbaddress_o, 42 | input logic sbautoincrement_i, 43 | input logic [2:0] sbaccess_i, 44 | // data in 45 | input logic sbreadondata_i, 46 | input logic [BusWidth-1:0] sbdata_i, 47 | input logic sbdata_read_valid_i, 48 | input logic sbdata_write_valid_i, 49 | // read data out 50 | output logic [BusWidth-1:0] sbdata_o, 51 | output logic sbdata_valid_o, 52 | // control signals 53 | output logic sbbusy_o, 54 | output logic sberror_valid_o, // bus error occurred 55 | output logic [2:0] sberror_o // bus error occurred 56 | ); 57 | 58 | localparam int BeIdxWidth = $clog2(BusWidth/8); 59 | dm::sba_state_e state_d, state_q; 60 | 61 | logic [BusWidth-1:0] address; 62 | logic req; 63 | logic gnt; 64 | logic we; 65 | logic [BusWidth/8-1:0] be; 66 | logic [BusWidth/8-1:0] be_mask; 67 | logic [BeIdxWidth-1:0] be_idx; 68 | 69 | assign sbbusy_o = logic'(state_q != dm::Idle); 70 | 71 | always_comb begin : p_be_mask 72 | be_mask = '0; 73 | 74 | // generate byte enable mask 75 | unique case (sbaccess_i) 76 | 3'b000: begin 77 | be_mask[be_idx] = '1; 78 | end 79 | 3'b001: begin 80 | be_mask[int'({be_idx[$high(be_idx):1], 1'b0}) +: 2] = '1; 81 | end 82 | 3'b010: begin 83 | if (BusWidth == 32'd64) be_mask[int'({be_idx[$high(be_idx)], 2'h0}) +: 4] = '1; 84 | else be_mask = '1; 85 | end 86 | 3'b011: be_mask = '1; 87 | default: ; 88 | endcase 89 | end 90 | 91 | logic [BusWidth-1:0] sbaccess_mask; 92 | assign sbaccess_mask = {BusWidth{1'b1}} << sbaccess_i; 93 | 94 | logic addr_incr_en; 95 | logic [BusWidth-1:0] addr_incr; 96 | assign addr_incr = (addr_incr_en) ? (BusWidth'(1'b1) << sbaccess_i) : '0; 97 | assign sbaddress_o = sbaddress_i + addr_incr; 98 | 99 | 100 | always_comb begin : p_fsm 101 | req = 1'b0; 102 | address = sbaddress_i; 103 | we = 1'b0; 104 | be = '0; 105 | be_idx = sbaddress_i[BeIdxWidth-1:0]; 106 | 107 | sberror_o = '0; 108 | sberror_valid_o = 1'b0; 109 | 110 | addr_incr_en = 1'b0; 111 | 112 | state_d = state_q; 113 | 114 | unique case (state_q) 115 | dm::Idle: begin 116 | // debugger requested a read 117 | if (sbaddress_write_valid_i && sbreadonaddr_i) state_d = dm::Read; 118 | // debugger requested a write 119 | if (sbdata_write_valid_i) state_d = dm::Write; 120 | // perform another read 121 | if (sbdata_read_valid_i && sbreadondata_i) state_d = dm::Read; 122 | end 123 | 124 | dm::Read: begin 125 | req = 1'b1; 126 | if (ReadByteEnable) be = be_mask; 127 | if (gnt) state_d = dm::WaitRead; 128 | end 129 | 130 | dm::Write: begin 131 | req = 1'b1; 132 | we = 1'b1; 133 | be = be_mask; 134 | if (gnt) state_d = dm::WaitWrite; 135 | end 136 | 137 | dm::WaitRead: begin 138 | if (sbdata_valid_o) begin 139 | state_d = dm::Idle; 140 | // auto-increment address 141 | addr_incr_en = sbautoincrement_i; 142 | // check whether an "other" error has been encountered. 143 | if (master_r_other_err_i) begin 144 | sberror_valid_o = 1'b1; 145 | sberror_o = 3'd7; 146 | // check whether there was a bus error (== bad address). 147 | end else if (master_r_err_i) begin 148 | sberror_valid_o = 1'b1; 149 | sberror_o = 3'd2; 150 | end 151 | end 152 | end 153 | 154 | dm::WaitWrite: begin 155 | if (sbdata_valid_o) begin 156 | state_d = dm::Idle; 157 | // auto-increment address 158 | addr_incr_en = sbautoincrement_i; 159 | // check whether an "other" error has been encountered. 160 | if (master_r_other_err_i) begin 161 | sberror_valid_o = 1'b1; 162 | sberror_o = 3'd7; 163 | // check whether there was a bus error (== bad address). 164 | end else if (master_r_err_i) begin 165 | sberror_valid_o = 1'b1; 166 | sberror_o = 3'd2; 167 | end 168 | end 169 | end 170 | 171 | default: state_d = dm::Idle; // catch parasitic state 172 | endcase 173 | 174 | // handle error case 175 | if (32'(sbaccess_i) > BeIdxWidth && state_q != dm::Idle) begin 176 | req = 1'b0; 177 | state_d = dm::Idle; 178 | sberror_valid_o = 1'b1; 179 | sberror_o = 3'd4; // unsupported size was requested 180 | end 181 | 182 | //if sbaccess_i lsbs of address are not 0 - report misalignment error 183 | if (|(sbaddress_i & ~sbaccess_mask) && state_q != dm::Idle) begin 184 | req = 1'b0; 185 | state_d = dm::Idle; 186 | sberror_valid_o = 1'b1; 187 | sberror_o = 3'd3; // alignment error 188 | end 189 | // further error handling should go here ... 190 | end 191 | 192 | always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs 193 | if (!rst_ni) begin 194 | state_q <= dm::Idle; 195 | end else begin 196 | state_q <= state_d; 197 | end 198 | end 199 | 200 | logic [BeIdxWidth-1:0] be_idx_masked; 201 | assign be_idx_masked = be_idx & BeIdxWidth'(sbaccess_mask); 202 | assign master_req_o = req; 203 | assign master_add_o = address[BusWidth-1:0]; 204 | assign master_we_o = we; 205 | assign master_wdata_o = sbdata_i[BusWidth-1:0] << (8 * be_idx_masked); 206 | assign master_be_o = be[BusWidth/8-1:0]; 207 | assign gnt = master_gnt_i; 208 | assign sbdata_valid_o = master_r_valid_i; 209 | assign sbdata_o = master_r_rdata_i[BusWidth-1:0] >> (8 * be_idx_masked); 210 | 211 | endmodule : dm_sba 212 | -------------------------------------------------------------------------------- /src/dm_top.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the “License”); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: dm_top.sv 12 | * Author: Florian Zaruba 13 | * Date: 30.6.2018 14 | * 15 | * Description: Top-level of debug module (DM). This is an AXI-Slave. 16 | * DTM protocol is equal to SiFives debug protocol to leverage 17 | * SW infrastructure re-use. As of version 0.13 18 | */ 19 | 20 | module dm_top #( 21 | parameter int unsigned NrHarts = 1, 22 | parameter int unsigned BusWidth = 32, 23 | parameter int unsigned DmBaseAddress = 'h1000, // default to non-zero page 24 | // Bitmask to select physically available harts for systems 25 | // that don't use hart numbers in a contiguous fashion. 26 | parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}}, 27 | // toggle new behavior to drive master_be_o during a read 28 | parameter bit ReadByteEnable = 1 29 | ) ( 30 | input logic clk_i, // clock 31 | // asynchronous reset active low, connect PoR here, not the system reset 32 | input logic rst_ni, 33 | // Subsequent debug modules can be chained by setting the nextdm register value to the offset of 34 | // the next debug module. The RISC-V debug spec mandates that the first debug module located at 35 | // 0x0, and that the last debug module in the chain sets the nextdm register to 0x0. The nextdm 36 | // register is a word address and not a byte address. This value is passed in as a static signal 37 | // so that it becomes possible to assign this value with chiplet tie-offs or straps, if needed. 38 | input logic [31:0] next_dm_addr_i, 39 | input logic testmode_i, 40 | output logic ndmreset_o, // non-debug module reset 41 | input logic ndmreset_ack_i, // non-debug module reset acknowledgement pulse 42 | output logic dmactive_o, // debug module is active 43 | output logic [NrHarts-1:0] debug_req_o, // async debug request 44 | // communicate whether the hart is unavailable (e.g.: power down) 45 | input logic [NrHarts-1:0] unavailable_i, 46 | input dm::hartinfo_t [NrHarts-1:0] hartinfo_i, 47 | 48 | input logic slave_req_i, 49 | input logic slave_we_i, 50 | input logic [BusWidth-1:0] slave_addr_i, 51 | input logic [BusWidth/8-1:0] slave_be_i, 52 | input logic [BusWidth-1:0] slave_wdata_i, 53 | output logic [BusWidth-1:0] slave_rdata_o, 54 | 55 | output logic master_req_o, 56 | output logic [BusWidth-1:0] master_add_o, 57 | output logic master_we_o, 58 | output logic [BusWidth-1:0] master_wdata_o, 59 | output logic [BusWidth/8-1:0] master_be_o, 60 | input logic master_gnt_i, 61 | input logic master_r_valid_i, 62 | input logic master_r_err_i, 63 | input logic master_r_other_err_i, // *other_err_i has priority over *err_i 64 | input logic [BusWidth-1:0] master_r_rdata_i, 65 | 66 | // Connection to DTM - compatible to RocketChip Debug Module 67 | input logic dmi_rst_ni, // Synchronous clear request from 68 | // the DTM to clear the DMI response 69 | // FIFO. 70 | input logic dmi_req_valid_i, 71 | output logic dmi_req_ready_o, 72 | input dm::dmi_req_t dmi_req_i, 73 | 74 | output logic dmi_resp_valid_o, 75 | input logic dmi_resp_ready_i, 76 | output dm::dmi_resp_t dmi_resp_o 77 | ); 78 | 79 | // Debug CSRs 80 | logic [NrHarts-1:0] halted; 81 | // logic [NrHarts-1:0] running; 82 | logic [NrHarts-1:0] resumeack; 83 | logic [NrHarts-1:0] haltreq; 84 | logic [NrHarts-1:0] resumereq; 85 | logic clear_resumeack; 86 | logic cmd_valid; 87 | dm::command_t cmd; 88 | 89 | logic cmderror_valid; 90 | dm::cmderr_e cmderror; 91 | logic cmdbusy; 92 | logic [dm::ProgBufSize-1:0][31:0] progbuf; 93 | logic [dm::DataCount-1:0][31:0] data_csrs_mem; 94 | logic [dm::DataCount-1:0][31:0] data_mem_csrs; 95 | logic data_valid; 96 | logic ndmreset; 97 | logic [19:0] hartsel; 98 | // System Bus Access Module 99 | logic [BusWidth-1:0] sbaddress_csrs_sba; 100 | logic [BusWidth-1:0] sbaddress_sba_csrs; 101 | logic sbaddress_write_valid; 102 | logic sbreadonaddr; 103 | logic sbautoincrement; 104 | logic [2:0] sbaccess; 105 | logic sbreadondata; 106 | logic [BusWidth-1:0] sbdata_write; 107 | logic sbdata_read_valid; 108 | logic sbdata_write_valid; 109 | logic [BusWidth-1:0] sbdata_read; 110 | logic sbdata_valid; 111 | logic sbbusy; 112 | logic sberror_valid; 113 | logic [2:0] sberror; 114 | 115 | assign ndmreset_o = ndmreset; 116 | 117 | dm_csrs #( 118 | .NrHarts(NrHarts), 119 | .BusWidth(BusWidth), 120 | .SelectableHarts(SelectableHarts) 121 | ) i_dm_csrs ( 122 | .clk_i, 123 | .rst_ni, 124 | .next_dm_addr_i, 125 | .testmode_i, 126 | .dmi_rst_ni, 127 | .dmi_req_valid_i, 128 | .dmi_req_ready_o, 129 | .dmi_req_i, 130 | .dmi_resp_valid_o, 131 | .dmi_resp_ready_i, 132 | .dmi_resp_o, 133 | .ndmreset_o ( ndmreset ), 134 | .ndmreset_ack_i ( ndmreset_ack_i ), 135 | .dmactive_o, 136 | .hartsel_o ( hartsel ), 137 | .hartinfo_i, 138 | .halted_i ( halted ), 139 | .unavailable_i, 140 | .resumeack_i ( resumeack ), 141 | .haltreq_o ( haltreq ), 142 | .resumereq_o ( resumereq ), 143 | .clear_resumeack_o ( clear_resumeack ), 144 | .cmd_valid_o ( cmd_valid ), 145 | .cmd_o ( cmd ), 146 | .cmderror_valid_i ( cmderror_valid ), 147 | .cmderror_i ( cmderror ), 148 | .cmdbusy_i ( cmdbusy ), 149 | .progbuf_o ( progbuf ), 150 | .data_i ( data_mem_csrs ), 151 | .data_valid_i ( data_valid ), 152 | .data_o ( data_csrs_mem ), 153 | .sbaddress_o ( sbaddress_csrs_sba ), 154 | .sbaddress_i ( sbaddress_sba_csrs ), 155 | .sbaddress_write_valid_o ( sbaddress_write_valid ), 156 | .sbreadonaddr_o ( sbreadonaddr ), 157 | .sbautoincrement_o ( sbautoincrement ), 158 | .sbaccess_o ( sbaccess ), 159 | .sbreadondata_o ( sbreadondata ), 160 | .sbdata_o ( sbdata_write ), 161 | .sbdata_read_valid_o ( sbdata_read_valid ), 162 | .sbdata_write_valid_o ( sbdata_write_valid ), 163 | .sbdata_i ( sbdata_read ), 164 | .sbdata_valid_i ( sbdata_valid ), 165 | .sbbusy_i ( sbbusy ), 166 | .sberror_valid_i ( sberror_valid ), 167 | .sberror_i ( sberror ) 168 | ); 169 | 170 | dm_sba #( 171 | .BusWidth(BusWidth), 172 | .ReadByteEnable(ReadByteEnable) 173 | ) i_dm_sba ( 174 | .clk_i, 175 | .rst_ni, 176 | .dmactive_i ( dmactive_o ), 177 | 178 | .master_req_o, 179 | .master_add_o, 180 | .master_we_o, 181 | .master_wdata_o, 182 | .master_be_o, 183 | .master_gnt_i, 184 | .master_r_valid_i, 185 | .master_r_err_i, 186 | .master_r_other_err_i, 187 | .master_r_rdata_i, 188 | 189 | .sbaddress_i ( sbaddress_csrs_sba ), 190 | .sbaddress_o ( sbaddress_sba_csrs ), 191 | .sbaddress_write_valid_i ( sbaddress_write_valid ), 192 | .sbreadonaddr_i ( sbreadonaddr ), 193 | .sbautoincrement_i ( sbautoincrement ), 194 | .sbaccess_i ( sbaccess ), 195 | .sbreadondata_i ( sbreadondata ), 196 | .sbdata_i ( sbdata_write ), 197 | .sbdata_read_valid_i ( sbdata_read_valid ), 198 | .sbdata_write_valid_i ( sbdata_write_valid ), 199 | .sbdata_o ( sbdata_read ), 200 | .sbdata_valid_o ( sbdata_valid ), 201 | .sbbusy_o ( sbbusy ), 202 | .sberror_valid_o ( sberror_valid ), 203 | .sberror_o ( sberror ) 204 | ); 205 | 206 | dm_mem #( 207 | .NrHarts(NrHarts), 208 | .BusWidth(BusWidth), 209 | .SelectableHarts(SelectableHarts), 210 | .DmBaseAddress(DmBaseAddress) 211 | ) i_dm_mem ( 212 | .clk_i, 213 | .rst_ni, 214 | .debug_req_o, 215 | .ndmreset_i ( ndmreset ), 216 | .hartsel_i ( hartsel ), 217 | .haltreq_i ( haltreq ), 218 | .resumereq_i ( resumereq ), 219 | .clear_resumeack_i ( clear_resumeack ), 220 | .halted_o ( halted ), 221 | .resuming_o ( resumeack ), 222 | .cmd_valid_i ( cmd_valid ), 223 | .cmd_i ( cmd ), 224 | .cmderror_valid_o ( cmderror_valid ), 225 | .cmderror_o ( cmderror ), 226 | .cmdbusy_o ( cmdbusy ), 227 | .progbuf_i ( progbuf ), 228 | .data_i ( data_csrs_mem ), 229 | .data_o ( data_mem_csrs ), 230 | .data_valid_o ( data_valid ), 231 | .req_i ( slave_req_i ), 232 | .we_i ( slave_we_i ), 233 | .addr_i ( slave_addr_i ), 234 | .wdata_i ( slave_wdata_i ), 235 | .be_i ( slave_be_i ), 236 | .rdata_o ( slave_rdata_o ) 237 | ); 238 | 239 | endmodule : dm_top 240 | -------------------------------------------------------------------------------- /src/dmi_bscane_tap.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Florian Zaruba 6 | 7 | /// Replacement for the full JTAG tap with `BSCANE2` Xilinx elements which hook 8 | /// into the FPGA native scan chain. Meant for FPGA boards which do not expose a 9 | /// usable pin-header or a separate, programmable FTDI chip. 10 | 11 | /// They replace the functionality of `dmi_jtag_tap.sv`. The file is 12 | /// pin-compatible so that by selecting the appropriate file for the target it 13 | /// can be transparently managed without relying on tick defines. 14 | module dmi_jtag_tap #( 15 | // Ignored, defined by the FPGA model. 16 | parameter int unsigned IrLength = 5, 17 | // JTAG IDCODE Value 18 | parameter logic [31:0] IdcodeValue = 32'h00000001 19 | // xxxx version 20 | // xxxxxxxxxxxxxxxx part number 21 | // xxxxxxxxxxx manufacturer id 22 | // 1 required by standard 23 | ) ( 24 | /// Unused. Here to maintain pin compatibility with `dmi_jtag_tap` so that it 25 | /// can be used as a drop-in replacement. 26 | input logic tck_i, 27 | input logic tms_i, 28 | input logic trst_ni, 29 | input logic td_i, 30 | output logic td_o, 31 | output logic tdo_oe_o, 32 | input logic testmode_i, 33 | 34 | output logic tck_o, 35 | output logic dmi_clear_o, 36 | output logic update_o, 37 | output logic capture_o, 38 | output logic shift_o, 39 | output logic tdi_o, 40 | output logic dtmcs_select_o, 41 | input logic dtmcs_tdo_i, 42 | // we want to access DMI register 43 | output logic dmi_select_o, 44 | input logic dmi_tdo_i 45 | ); 46 | 47 | BSCANE2 #( 48 | .JTAG_CHAIN (3) 49 | ) i_tap_dtmcs ( 50 | .CAPTURE (capture_o), 51 | .DRCK (), 52 | .RESET (dmi_clear_o), 53 | .RUNTEST (), 54 | .SEL (dtmcs_select_o), 55 | .SHIFT (shift_o), 56 | .TCK (tck_o), 57 | .TDI (tdi_o), 58 | .TMS (), 59 | .TDO (dtmcs_tdo_i), 60 | .UPDATE (update_o) 61 | ); 62 | 63 | /// DMI Register 64 | BSCANE2 #( 65 | .JTAG_CHAIN (4) 66 | ) i_tap_dmi ( 67 | .CAPTURE (), 68 | .DRCK (), 69 | .RESET (), 70 | .RUNTEST (), 71 | .SEL (dmi_select_o), 72 | .SHIFT (), 73 | .TCK (), 74 | .TDI (), 75 | .TMS (), 76 | .TDO (dmi_tdo_i), 77 | .UPDATE () 78 | ); 79 | 80 | endmodule 81 | -------------------------------------------------------------------------------- /src/dmi_cdc.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the “License”); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: axi_riscv_debug_module.sv 12 | * Author: Andreas Traber 13 | * Author: Florian Zaruba 14 | * 15 | * Description: Clock domain crossings for JTAG to DMI very heavily based 16 | * on previous work by Andreas Traber for the PULP project. 17 | * This is mainly a wrapper around the existing CDCs. 18 | */ 19 | module dmi_cdc ( 20 | // JTAG side (master side) 21 | input logic tck_i, 22 | input logic trst_ni, 23 | input dm::dmi_req_t jtag_dmi_req_i, 24 | output logic jtag_dmi_ready_o, 25 | input logic jtag_dmi_valid_i, 26 | input logic jtag_dmi_cdc_clear_i, // Synchronous clear signal. 27 | // Triggers reset sequencing 28 | // accross CDC 29 | 30 | output dm::dmi_resp_t jtag_dmi_resp_o, 31 | output logic jtag_dmi_valid_o, 32 | input logic jtag_dmi_ready_i, 33 | 34 | // core side (slave side) 35 | input logic clk_i, 36 | input logic rst_ni, 37 | 38 | output logic core_dmi_rst_no, 39 | output dm::dmi_req_t core_dmi_req_o, 40 | output logic core_dmi_valid_o, 41 | input logic core_dmi_ready_i, 42 | 43 | input dm::dmi_resp_t core_dmi_resp_i, 44 | output logic core_dmi_ready_o, 45 | input logic core_dmi_valid_i 46 | ); 47 | 48 | logic core_clear_pending; 49 | 50 | cdc_2phase_clearable #(.T(dm::dmi_req_t)) i_cdc_req ( 51 | .src_rst_ni ( trst_ni ), 52 | .src_clear_i ( jtag_dmi_cdc_clear_i ), 53 | .src_clk_i ( tck_i ), 54 | .src_clear_pending_o(), // Not used 55 | .src_data_i ( jtag_dmi_req_i ), 56 | .src_valid_i ( jtag_dmi_valid_i ), 57 | .src_ready_o ( jtag_dmi_ready_o ), 58 | 59 | .dst_rst_ni ( rst_ni ), 60 | .dst_clear_i ( 1'b0 ), // No functional reset from core side 61 | // used (only async). 62 | .dst_clear_pending_o( core_clear_pending ), // use the clear pending signal 63 | // to synchronously clear the 64 | // response FIFO in the dm_top 65 | // csrs 66 | .dst_clk_i ( clk_i ), 67 | .dst_data_o ( core_dmi_req_o ), 68 | .dst_valid_o ( core_dmi_valid_o ), 69 | .dst_ready_i ( core_dmi_ready_i ) 70 | ); 71 | 72 | cdc_2phase_clearable #(.T(dm::dmi_resp_t)) i_cdc_resp ( 73 | .src_rst_ni ( rst_ni ), 74 | .src_clear_i ( 1'b0 ), // No functional reset from core side 75 | // used (only async ). 76 | .src_clear_pending_o(), // Not used 77 | .src_clk_i ( clk_i ), 78 | .src_data_i ( core_dmi_resp_i ), 79 | .src_valid_i ( core_dmi_valid_i ), 80 | .src_ready_o ( core_dmi_ready_o ), 81 | 82 | .dst_rst_ni ( trst_ni ), 83 | .dst_clear_i ( jtag_dmi_cdc_clear_i ), 84 | .dst_clear_pending_o(), //Not used 85 | .dst_clk_i ( tck_i ), 86 | .dst_data_o ( jtag_dmi_resp_o ), 87 | .dst_valid_o ( jtag_dmi_valid_o ), 88 | .dst_ready_i ( jtag_dmi_ready_i ) 89 | ); 90 | 91 | // We need to flush the DMI response FIFO in DM top using the core clock 92 | // synchronous clear signal core_dmi_rst_no. We repurpose the clear 93 | // pending signal in the core clock domain by generating a 1 cycle pulse from 94 | // it. 95 | 96 | logic core_clear_pending_q; 97 | logic core_dmi_rst_nq; 98 | logic clear_pending_rise_edge_detect; 99 | 100 | assign clear_pending_rise_edge_detect = !core_clear_pending_q && core_clear_pending; 101 | 102 | always_ff @(posedge clk_i, negedge rst_ni) begin 103 | if (!rst_ni) begin 104 | core_dmi_rst_nq <= 1'b1; 105 | core_clear_pending_q <= 1'b0; 106 | end else begin 107 | core_dmi_rst_nq <= ~clear_pending_rise_edge_detect; // active-low! 108 | core_clear_pending_q <= core_clear_pending; 109 | end 110 | end 111 | 112 | assign core_dmi_rst_no = core_dmi_rst_nq; 113 | 114 | endmodule : dmi_cdc 115 | -------------------------------------------------------------------------------- /src/dmi_intf.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Florian Zaruba 6 | 7 | 8 | // The DV interface additionally carries a clock signal. 9 | interface DMI_BUS_DV #( 10 | /// The width of the address. 11 | parameter int ADDR_WIDTH = -1 12 | ) ( 13 | input logic clk_i 14 | ); 15 | 16 | import dm::*; 17 | 18 | typedef logic [ADDR_WIDTH-1:0] addr_t; 19 | typedef logic [31:0] data_t; 20 | /// The request channel (Q). 21 | addr_t q_addr; 22 | dtm_op_e q_op; 23 | data_t q_data; 24 | logic q_valid; 25 | logic q_ready; 26 | 27 | /// The response channel (P). 28 | data_t p_data; 29 | logic p_resp; 30 | logic p_valid; 31 | logic p_ready; 32 | 33 | modport in ( 34 | input q_addr, q_op, q_data, q_valid, p_ready, 35 | output q_ready, p_data, p_resp, p_valid 36 | ); 37 | modport out ( 38 | output q_addr, q_op, q_data, q_valid, p_ready, 39 | input q_ready, p_data, p_resp, p_valid 40 | ); 41 | modport monitor ( 42 | input q_addr, q_op, q_data, q_valid, p_ready, 43 | q_ready, p_data, p_resp, p_valid 44 | ); 45 | 46 | // pragma translate_off 47 | `ifndef VERILATOR 48 | assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_addr))); 49 | assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_op))); 50 | assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_data))); 51 | assert property (@(posedge clk_i) (q_valid && !q_ready |=> q_valid)); 52 | 53 | assert property (@(posedge clk_i) (p_valid && !p_ready |=> $stable(p_data))); 54 | assert property (@(posedge clk_i) (p_valid && !p_ready |=> $stable(p_resp))); 55 | assert property (@(posedge clk_i) (p_valid && !p_ready |=> p_valid)); 56 | `endif 57 | // pragma translate_on 58 | 59 | endinterface 60 | -------------------------------------------------------------------------------- /src/dmi_jtag.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the “License”); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: axi_riscv_debug_module.sv 12 | * Author: Florian Zaruba 13 | * Date: 19.7.2018 14 | * 15 | * Description: JTAG DMI (debug module interface) 16 | * 17 | */ 18 | 19 | module dmi_jtag #( 20 | parameter logic [31:0] IdcodeValue = 32'h00000DB3 21 | ) ( 22 | input logic clk_i, // DMI Clock 23 | input logic rst_ni, // Asynchronous reset active low 24 | input logic testmode_i, 25 | 26 | // active-low glitch free reset signal. Is asserted for one dmi clock cycle 27 | // (clk_i) whenever the dmi_jtag is reset (POR or functional reset). 28 | output logic dmi_rst_no, 29 | output dm::dmi_req_t dmi_req_o, 30 | output logic dmi_req_valid_o, 31 | input logic dmi_req_ready_i, 32 | 33 | input dm::dmi_resp_t dmi_resp_i, 34 | output logic dmi_resp_ready_o, 35 | input logic dmi_resp_valid_i, 36 | 37 | input logic tck_i, // JTAG test clock pad 38 | input logic tms_i, // JTAG test mode select pad 39 | input logic trst_ni, // JTAG test reset pad 40 | input logic td_i, // JTAG test data input pad 41 | output logic td_o, // JTAG test data output pad 42 | output logic tdo_oe_o // Data out output enable 43 | ); 44 | 45 | typedef enum logic [1:0] { 46 | DMINoError = 2'h0, DMIReservedError = 2'h1, 47 | DMIOPFailed = 2'h2, DMIBusy = 2'h3 48 | } dmi_error_e; 49 | dmi_error_e error_d, error_q; 50 | 51 | logic tck; 52 | logic jtag_dmi_clear; // Synchronous reset of DMI triggered by TestLogicReset in 53 | // jtag TAP 54 | logic dmi_clear; // Functional (warm) reset of the entire DMI 55 | logic update; 56 | logic capture; 57 | logic shift; 58 | logic tdi; 59 | 60 | logic dtmcs_select; 61 | 62 | assign dmi_clear = jtag_dmi_clear || (dtmcs_select && update && dtmcs_q.dmihardreset); 63 | 64 | // ------------------------------- 65 | // Debug Module Control and Status 66 | // ------------------------------- 67 | 68 | dm::dtmcs_t dtmcs_d, dtmcs_q; 69 | 70 | always_comb begin 71 | dtmcs_d = dtmcs_q; 72 | if (capture) begin 73 | if (dtmcs_select) begin 74 | dtmcs_d = '{ 75 | zero1 : '0, 76 | dmihardreset : 1'b0, 77 | dmireset : 1'b0, 78 | zero0 : '0, 79 | idle : 3'd1, // 1: Enter Run-Test/Idle and leave it immediately 80 | dmistat : error_q, // 0: No error, 2: Op failed, 3: too fast 81 | abits : 6'd7, // The size of address in dmi 82 | version : 4'd1 // Version described in spec version 0.13 (and later?) 83 | }; 84 | end 85 | end 86 | 87 | if (shift) begin 88 | if (dtmcs_select) dtmcs_d = {tdi, 31'(dtmcs_q >> 1)}; 89 | end 90 | end 91 | 92 | always_ff @(posedge tck or negedge trst_ni) begin 93 | if (!trst_ni) begin 94 | dtmcs_q <= '0; 95 | end else begin 96 | dtmcs_q <= dtmcs_d; 97 | end 98 | end 99 | 100 | // ---------------------------- 101 | // DMI (Debug Module Interface) 102 | // ---------------------------- 103 | 104 | logic dmi_select; 105 | logic dmi_tdo; 106 | 107 | dm::dmi_req_t dmi_req; 108 | logic dmi_req_ready; 109 | logic dmi_req_valid; 110 | 111 | dm::dmi_resp_t dmi_resp; 112 | logic dmi_resp_valid; 113 | logic dmi_resp_ready; 114 | 115 | typedef struct packed { 116 | logic [6:0] address; 117 | logic [31:0] data; 118 | logic [1:0] op; 119 | } dmi_t; 120 | 121 | typedef enum logic [2:0] { Idle, Read, WaitReadValid, Write, WaitWriteValid } state_e; 122 | state_e state_d, state_q; 123 | 124 | logic [$bits(dmi_t)-1:0] dr_d, dr_q; 125 | logic [6:0] address_d, address_q; 126 | logic [31:0] data_d, data_q; 127 | 128 | dmi_t dmi; 129 | assign dmi = dmi_t'(dr_q); 130 | assign dmi_req.addr = address_q; 131 | assign dmi_req.data = data_q; 132 | assign dmi_req.op = (state_q == Write) ? dm::DTM_WRITE : dm::DTM_READ; 133 | // We will always be ready to accept the data we requested. 134 | assign dmi_resp_ready = 1'b1; 135 | 136 | logic error_dmi_busy; 137 | logic error_dmi_op_failed; 138 | 139 | always_comb begin : p_fsm 140 | error_dmi_busy = 1'b0; 141 | error_dmi_op_failed = 1'b0; 142 | // default assignments 143 | state_d = state_q; 144 | address_d = address_q; 145 | data_d = data_q; 146 | error_d = error_q; 147 | 148 | dmi_req_valid = 1'b0; 149 | 150 | if (dmi_clear) begin 151 | state_d = Idle; 152 | data_d = '0; 153 | error_d = DMINoError; 154 | address_d = '0; 155 | end else begin 156 | unique case (state_q) 157 | Idle: begin 158 | // make sure that no error is sticky 159 | if (dmi_select && update && (error_q == DMINoError)) begin 160 | // save address and value 161 | address_d = dmi.address; 162 | data_d = dmi.data; 163 | if (dm::dtm_op_e'(dmi.op) == dm::DTM_READ) begin 164 | state_d = Read; 165 | end else if (dm::dtm_op_e'(dmi.op) == dm::DTM_WRITE) begin 166 | state_d = Write; 167 | end 168 | // else this is a nop and we can stay here 169 | end 170 | end 171 | 172 | Read: begin 173 | dmi_req_valid = 1'b1; 174 | if (dmi_req_ready) begin 175 | state_d = WaitReadValid; 176 | end 177 | end 178 | 179 | WaitReadValid: begin 180 | // load data into register and shift out 181 | if (dmi_resp_valid) begin 182 | unique case (dmi_resp.resp) 183 | dm::DTM_SUCCESS: begin 184 | data_d = dmi_resp.data; 185 | end 186 | dm::DTM_ERR: begin 187 | data_d = 32'hDEAD_BEEF; 188 | error_dmi_op_failed = 1'b1; 189 | end 190 | dm::DTM_BUSY: begin 191 | data_d = 32'hB051_B051; 192 | error_dmi_busy = 1'b1; 193 | end 194 | default: begin 195 | data_d = 32'hBAAD_C0DE; 196 | end 197 | endcase 198 | state_d = Idle; 199 | end 200 | end 201 | 202 | Write: begin 203 | dmi_req_valid = 1'b1; 204 | // request sent, wait for response before going back to idle 205 | if (dmi_req_ready) begin 206 | state_d = WaitWriteValid; 207 | end 208 | end 209 | 210 | WaitWriteValid: begin 211 | // got a valid answer go back to idle 212 | if (dmi_resp_valid) begin 213 | unique case (dmi_resp.resp) 214 | dm::DTM_ERR: error_dmi_op_failed = 1'b1; 215 | dm::DTM_BUSY: error_dmi_busy = 1'b1; 216 | default: ; 217 | endcase 218 | state_d = Idle; 219 | end 220 | end 221 | 222 | default: begin 223 | // just wait for idle here 224 | if (dmi_resp_valid) begin 225 | state_d = Idle; 226 | end 227 | end 228 | endcase 229 | 230 | // update means we got another request but we didn't finish 231 | // the one in progress, this state is sticky 232 | if (update && state_q != Idle) begin 233 | error_dmi_busy = 1'b1; 234 | end 235 | 236 | // if capture goes high while we are in the read state 237 | // or in the corresponding wait state we are not giving back a valid word 238 | // -> throw an error 239 | if (capture && state_q inside {Read, WaitReadValid}) begin 240 | error_dmi_busy = 1'b1; 241 | end 242 | 243 | if (error_dmi_busy && error_q == DMINoError) begin 244 | error_d = DMIBusy; 245 | end 246 | 247 | if (error_dmi_op_failed && error_q == DMINoError) begin 248 | error_d = DMIOPFailed; 249 | end 250 | 251 | // clear sticky error flag 252 | if (update && dtmcs_q.dmireset && dtmcs_select) begin 253 | error_d = DMINoError; 254 | end 255 | end 256 | end 257 | 258 | // shift register 259 | assign dmi_tdo = dr_q[0]; 260 | 261 | always_comb begin : p_shift 262 | dr_d = dr_q; 263 | if (dmi_clear) begin 264 | dr_d = '0; 265 | end else begin 266 | if (capture) begin 267 | if (dmi_select) begin 268 | if (error_q == DMINoError && !error_dmi_busy) begin 269 | dr_d = {address_q, data_q, DMINoError}; 270 | // DMI was busy, report an error 271 | end else if (error_q == DMIBusy || error_dmi_busy) begin 272 | dr_d = {address_q, data_q, DMIBusy}; 273 | end 274 | end 275 | end 276 | 277 | if (shift) begin 278 | if (dmi_select) begin 279 | dr_d = {tdi, dr_q[$bits(dr_q)-1:1]}; 280 | end 281 | end 282 | end 283 | end 284 | 285 | always_ff @(posedge tck or negedge trst_ni) begin 286 | if (!trst_ni) begin 287 | dr_q <= '0; 288 | state_q <= Idle; 289 | address_q <= '0; 290 | data_q <= '0; 291 | error_q <= DMINoError; 292 | end else begin 293 | dr_q <= dr_d; 294 | state_q <= state_d; 295 | address_q <= address_d; 296 | data_q <= data_d; 297 | error_q <= error_d; 298 | end 299 | end 300 | 301 | // --------- 302 | // TAP 303 | // --------- 304 | dmi_jtag_tap #( 305 | .IrLength (5), 306 | .IdcodeValue(IdcodeValue) 307 | ) i_dmi_jtag_tap ( 308 | .tck_i, 309 | .tms_i, 310 | .trst_ni, 311 | .td_i, 312 | .td_o, 313 | .tdo_oe_o, 314 | .testmode_i, 315 | .tck_o ( tck ), 316 | .dmi_clear_o ( jtag_dmi_clear ), 317 | .update_o ( update ), 318 | .capture_o ( capture ), 319 | .shift_o ( shift ), 320 | .tdi_o ( tdi ), 321 | .dtmcs_select_o ( dtmcs_select ), 322 | .dtmcs_tdo_i ( dtmcs_q[0] ), 323 | .dmi_select_o ( dmi_select ), 324 | .dmi_tdo_i ( dmi_tdo ) 325 | ); 326 | 327 | // --------- 328 | // CDC 329 | // --------- 330 | dmi_cdc i_dmi_cdc ( 331 | // JTAG side (master side) 332 | .tck_i ( tck ), 333 | .trst_ni ( trst_ni ), 334 | .jtag_dmi_cdc_clear_i ( dmi_clear ), 335 | .jtag_dmi_req_i ( dmi_req ), 336 | .jtag_dmi_ready_o ( dmi_req_ready ), 337 | .jtag_dmi_valid_i ( dmi_req_valid ), 338 | .jtag_dmi_resp_o ( dmi_resp ), 339 | .jtag_dmi_valid_o ( dmi_resp_valid ), 340 | .jtag_dmi_ready_i ( dmi_resp_ready ), 341 | // core side 342 | .clk_i, 343 | .rst_ni, 344 | .core_dmi_rst_no ( dmi_rst_no ), 345 | .core_dmi_req_o ( dmi_req_o ), 346 | .core_dmi_valid_o ( dmi_req_valid_o ), 347 | .core_dmi_ready_i ( dmi_req_ready_i ), 348 | .core_dmi_resp_i ( dmi_resp_i ), 349 | .core_dmi_ready_o ( dmi_resp_ready_o ), 350 | .core_dmi_valid_i ( dmi_resp_valid_i ) 351 | ); 352 | 353 | endmodule : dmi_jtag 354 | -------------------------------------------------------------------------------- /src/dmi_jtag_tap.sv: -------------------------------------------------------------------------------- 1 | /* Copyright 2018 ETH Zurich and University of Bologna. 2 | * Copyright and related rights are licensed under the Solderpad Hardware 3 | * License, Version 0.51 (the “License”); you may not use this file except in 4 | * compliance with the License. You may obtain a copy of the License at 5 | * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | * or agreed to in writing, software, hardware and materials distributed under 7 | * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | * specific language governing permissions and limitations under the License. 10 | * 11 | * File: dmi_jtag_tap.sv 12 | * Author: Florian Zaruba 13 | * Date: 19.7.2018 14 | * 15 | * Description: JTAG TAP for DMI (according to debug spec 0.13) 16 | * 17 | */ 18 | 19 | module dmi_jtag_tap #( 20 | parameter int unsigned IrLength = 5, 21 | // JTAG IDCODE Value 22 | parameter logic [31:0] IdcodeValue = 32'h00000001 23 | // xxxx version 24 | // xxxxxxxxxxxxxxxx part number 25 | // xxxxxxxxxxx manufacturer id 26 | // 1 required by standard 27 | ) ( 28 | input logic tck_i, // JTAG test clock pad 29 | input logic tms_i, // JTAG test mode select pad 30 | input logic trst_ni, // JTAG test reset pad 31 | input logic td_i, // JTAG test data input pad 32 | output logic td_o, // JTAG test data output pad 33 | output logic tdo_oe_o, // Data out output enable 34 | input logic testmode_i, 35 | // JTAG is interested in writing the DTM CSR register 36 | output logic tck_o, 37 | // Synchronous reset of the dmi module triggered by JTAG TAP 38 | output logic dmi_clear_o, 39 | output logic update_o, 40 | output logic capture_o, 41 | output logic shift_o, 42 | output logic tdi_o, 43 | output logic dtmcs_select_o, 44 | input logic dtmcs_tdo_i, 45 | // we want to access DMI register 46 | output logic dmi_select_o, 47 | input logic dmi_tdo_i 48 | ); 49 | 50 | typedef enum logic [3:0] { 51 | TestLogicReset, RunTestIdle, SelectDrScan, 52 | CaptureDr, ShiftDr, Exit1Dr, PauseDr, Exit2Dr, 53 | UpdateDr, SelectIrScan, CaptureIr, ShiftIr, 54 | Exit1Ir, PauseIr, Exit2Ir, UpdateIr 55 | } tap_state_e; 56 | 57 | tap_state_e tap_state_q, tap_state_d; 58 | logic update_dr, shift_dr, capture_dr; 59 | 60 | typedef enum logic [IrLength-1:0] { 61 | BYPASS0 = 'h0, 62 | IDCODE = 'h1, 63 | DTMCSR = 'h10, 64 | DMIACCESS = 'h11, 65 | BYPASS1 = 'h1f 66 | } ir_reg_e; 67 | 68 | // ---------------- 69 | // IR logic 70 | // ---------------- 71 | 72 | // shift register 73 | logic [IrLength-1:0] jtag_ir_shift_d, jtag_ir_shift_q; 74 | // IR register -> this gets captured from shift register upon update_ir 75 | ir_reg_e jtag_ir_d, jtag_ir_q; 76 | logic capture_ir, shift_ir, update_ir, test_logic_reset; // pause_ir 77 | 78 | always_comb begin : p_jtag 79 | jtag_ir_shift_d = jtag_ir_shift_q; 80 | jtag_ir_d = jtag_ir_q; 81 | 82 | // IR shift register 83 | if (shift_ir) begin 84 | jtag_ir_shift_d = {td_i, jtag_ir_shift_q[IrLength-1:1]}; 85 | end 86 | 87 | // capture IR register 88 | if (capture_ir) begin 89 | jtag_ir_shift_d = IrLength'(4'b0101); 90 | end 91 | 92 | // update IR register 93 | if (update_ir) begin 94 | jtag_ir_d = ir_reg_e'(jtag_ir_shift_q); 95 | end 96 | 97 | if (test_logic_reset) begin 98 | // Bring all TAP state to the initial value. 99 | jtag_ir_shift_d = '0; 100 | jtag_ir_d = IDCODE; 101 | end 102 | end 103 | 104 | always_ff @(posedge tck_i, negedge trst_ni) begin : p_jtag_ir_reg 105 | if (!trst_ni) begin 106 | jtag_ir_shift_q <= '0; 107 | jtag_ir_q <= IDCODE; 108 | end else begin 109 | jtag_ir_shift_q <= jtag_ir_shift_d; 110 | jtag_ir_q <= jtag_ir_d; 111 | end 112 | end 113 | 114 | // ---------------- 115 | // TAP DR Regs 116 | // ---------------- 117 | // - Bypass 118 | // - IDCODE 119 | // - DTM CS 120 | logic [31:0] idcode_d, idcode_q; 121 | logic idcode_select; 122 | logic bypass_select; 123 | 124 | logic bypass_d, bypass_q; // this is a 1-bit register 125 | 126 | always_comb begin 127 | idcode_d = idcode_q; 128 | bypass_d = bypass_q; 129 | 130 | if (capture_dr) begin 131 | if (idcode_select) idcode_d = IdcodeValue; 132 | if (bypass_select) bypass_d = 1'b0; 133 | end 134 | 135 | if (shift_dr) begin 136 | if (idcode_select) idcode_d = {td_i, 31'(idcode_q >> 1)}; 137 | if (bypass_select) bypass_d = td_i; 138 | end 139 | 140 | if (test_logic_reset) begin 141 | // Bring all TAP state to the initial value. 142 | idcode_d = IdcodeValue; 143 | bypass_d = 1'b0; 144 | end 145 | end 146 | 147 | // ---------------- 148 | // Data reg select 149 | // ---------------- 150 | always_comb begin : p_data_reg_sel 151 | dmi_select_o = 1'b0; 152 | dtmcs_select_o = 1'b0; 153 | idcode_select = 1'b0; 154 | bypass_select = 1'b0; 155 | unique case (jtag_ir_q) 156 | BYPASS0: bypass_select = 1'b1; 157 | IDCODE: idcode_select = 1'b1; 158 | DTMCSR: dtmcs_select_o = 1'b1; 159 | DMIACCESS: dmi_select_o = 1'b1; 160 | BYPASS1: bypass_select = 1'b1; 161 | default: bypass_select = 1'b1; 162 | endcase 163 | end 164 | 165 | // ---------------- 166 | // Output select 167 | // ---------------- 168 | logic tdo_mux; 169 | 170 | always_comb begin : p_out_sel 171 | // we are shifting out the IR register 172 | if (shift_ir) begin 173 | tdo_mux = jtag_ir_shift_q[0]; 174 | // here we are shifting the DR register 175 | end else begin 176 | unique case (jtag_ir_q) 177 | IDCODE: tdo_mux = idcode_q[0]; // Reading ID code 178 | DTMCSR: tdo_mux = dtmcs_tdo_i; // Read from DTMCS TDO 179 | DMIACCESS: tdo_mux = dmi_tdo_i; // Read from DMI TDO 180 | default: tdo_mux = bypass_q; // BYPASS instruction 181 | endcase 182 | end 183 | end 184 | 185 | // ---------------- 186 | // DFT 187 | // ---------------- 188 | logic tck_n, tck_ni; 189 | 190 | tc_clk_inverter i_tck_inv ( 191 | .clk_i ( tck_i ), 192 | .clk_o ( tck_ni ) 193 | ); 194 | 195 | tc_clk_mux2 i_dft_tck_mux ( 196 | .clk0_i ( tck_ni ), 197 | .clk1_i ( tck_i ), // bypass the inverted clock for testing 198 | .clk_sel_i ( testmode_i ), 199 | .clk_o ( tck_n ) 200 | ); 201 | 202 | // TDO changes state at negative edge of TCK 203 | always_ff @(posedge tck_n, negedge trst_ni) begin : p_tdo_regs 204 | if (!trst_ni) begin 205 | td_o <= 1'b0; 206 | tdo_oe_o <= 1'b0; 207 | end else begin 208 | td_o <= tdo_mux; 209 | tdo_oe_o <= (shift_ir | shift_dr); 210 | end 211 | end 212 | // ---------------- 213 | // TAP FSM 214 | // ---------------- 215 | // Determination of next state; purely combinatorial 216 | always_comb begin : p_tap_fsm 217 | 218 | test_logic_reset = 1'b0; 219 | 220 | capture_dr = 1'b0; 221 | shift_dr = 1'b0; 222 | update_dr = 1'b0; 223 | 224 | capture_ir = 1'b0; 225 | shift_ir = 1'b0; 226 | // pause_ir = 1'b0; unused 227 | update_ir = 1'b0; 228 | 229 | unique case (tap_state_q) 230 | TestLogicReset: begin 231 | tap_state_d = (tms_i) ? TestLogicReset : RunTestIdle; 232 | test_logic_reset = 1'b1; 233 | end 234 | RunTestIdle: begin 235 | tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; 236 | end 237 | // DR Path 238 | SelectDrScan: begin 239 | tap_state_d = (tms_i) ? SelectIrScan : CaptureDr; 240 | end 241 | CaptureDr: begin 242 | capture_dr = 1'b1; 243 | tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; 244 | end 245 | ShiftDr: begin 246 | shift_dr = 1'b1; 247 | tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; 248 | end 249 | Exit1Dr: begin 250 | tap_state_d = (tms_i) ? UpdateDr : PauseDr; 251 | end 252 | PauseDr: begin 253 | tap_state_d = (tms_i) ? Exit2Dr : PauseDr; 254 | end 255 | Exit2Dr: begin 256 | tap_state_d = (tms_i) ? UpdateDr : ShiftDr; 257 | end 258 | UpdateDr: begin 259 | update_dr = 1'b1; 260 | tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; 261 | end 262 | // IR Path 263 | SelectIrScan: begin 264 | tap_state_d = (tms_i) ? TestLogicReset : CaptureIr; 265 | end 266 | // In this controller state, the shift register bank in the 267 | // Instruction Register parallel loads a pattern of fixed values on 268 | // the rising edge of TCK. The last two significant bits must always 269 | // be "01". 270 | CaptureIr: begin 271 | capture_ir = 1'b1; 272 | tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; 273 | end 274 | // In this controller state, the instruction register gets connected 275 | // between TDI and TDO, and the captured pattern gets shifted on 276 | // each rising edge of TCK. The instruction available on the TDI 277 | // pin is also shifted in to the instruction register. 278 | ShiftIr: begin 279 | shift_ir = 1'b1; 280 | tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; 281 | end 282 | Exit1Ir: begin 283 | tap_state_d = (tms_i) ? UpdateIr : PauseIr; 284 | end 285 | PauseIr: begin 286 | // pause_ir = 1'b1; // unused 287 | tap_state_d = (tms_i) ? Exit2Ir : PauseIr; 288 | end 289 | Exit2Ir: begin 290 | tap_state_d = (tms_i) ? UpdateIr : ShiftIr; 291 | end 292 | // In this controller state, the instruction in the instruction 293 | // shift register is latched to the latch bank of the Instruction 294 | // Register on every falling edge of TCK. This instruction becomes 295 | // the current instruction once it is latched. 296 | UpdateIr: begin 297 | update_ir = 1'b1; 298 | tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; 299 | end 300 | default: ; // can't actually happen since case is full 301 | endcase 302 | end 303 | 304 | always_ff @(posedge tck_i or negedge trst_ni) begin : p_regs 305 | if (!trst_ni) begin 306 | tap_state_q <= TestLogicReset; 307 | idcode_q <= IdcodeValue; 308 | bypass_q <= 1'b0; 309 | end else begin 310 | tap_state_q <= tap_state_d; 311 | idcode_q <= idcode_d; 312 | bypass_q <= bypass_d; 313 | end 314 | end 315 | 316 | // Pass through JTAG signals to debug custom DR logic. 317 | // In case of a single TAP those are just feed-through. 318 | assign tck_o = tck_i; 319 | assign tdi_o = td_i; 320 | assign update_o = update_dr; 321 | assign shift_o = shift_dr; 322 | assign capture_o = capture_dr; 323 | assign dmi_clear_o = test_logic_reset; 324 | 325 | 326 | endmodule : dmi_jtag_tap 327 | -------------------------------------------------------------------------------- /src/dmi_test.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Florian Zaruba 6 | 7 | /// A set of testbench utilities for the DMI interfaces. 8 | package dmi_test; 9 | 10 | import dm::*; 11 | 12 | class req_t #( 13 | parameter int AW = 7 14 | ); 15 | rand logic [AW-1:0] addr; 16 | rand dtm_op_e op; 17 | rand logic [31:0] data; 18 | 19 | /// Compare objects of same type. 20 | function do_compare(req_t rhs); 21 | return addr == rhs.addr & 22 | op == rhs.op & 23 | data == rhs.data; 24 | endfunction 25 | 26 | endclass 27 | 28 | class rsp_t; 29 | rand logic [31:0] data; 30 | rand logic [1:0] resp; 31 | 32 | /// Compare objects of same type. 33 | function do_compare(rsp_t rhs); 34 | return data == rhs.data & 35 | resp == rhs.resp; 36 | endfunction 37 | 38 | endclass 39 | 40 | /// A driver for the DMI interface. 41 | class dmi_driver #( 42 | parameter int AW = -1, 43 | parameter time TA = 0 , // stimuli application time 44 | parameter time TT = 0 // stimuli test time 45 | ); 46 | virtual DMI_BUS_DV #( 47 | .ADDR_WIDTH(AW) 48 | ) bus; 49 | 50 | function new( 51 | virtual DMI_BUS_DV #( 52 | .ADDR_WIDTH(AW) 53 | ) bus 54 | ); 55 | this.bus = bus; 56 | endfunction 57 | 58 | task reset_master; 59 | bus.q_addr <= '0; 60 | bus.q_op <= DTM_NOP; 61 | bus.q_data <= '0; 62 | bus.q_valid <= '0; 63 | bus.p_ready <= '0; 64 | endtask 65 | 66 | task reset_slave; 67 | bus.q_ready <= '0; 68 | bus.p_data <= '0; 69 | bus.p_resp <= '0; 70 | bus.p_valid <= '0; 71 | endtask 72 | 73 | task cycle_start; 74 | #TT; 75 | endtask 76 | 77 | task cycle_end; 78 | @(posedge bus.clk_i); 79 | endtask 80 | 81 | /// Send a request. 82 | task send_req (input req_t req); 83 | bus.q_addr <= #TA req.addr; 84 | bus.q_op <= #TA req.op; 85 | bus.q_data <= #TA req.data; 86 | bus.q_valid <= #TA 1; 87 | cycle_start(); 88 | while (bus.q_ready != 1) begin cycle_end(); cycle_start(); end 89 | cycle_end(); 90 | bus.q_addr <= #TA '0; 91 | bus.q_op <= #TA DTM_NOP; 92 | bus.q_data <= #TA '0; 93 | bus.q_valid <= #TA 0; 94 | endtask 95 | 96 | /// Send a response. 97 | task send_rsp (input rsp_t rsp); 98 | bus.p_data <= #TA rsp.data; 99 | bus.p_resp <= #TA rsp.resp; 100 | bus.p_valid <= #TA 1; 101 | cycle_start(); 102 | while (bus.p_ready != 1) begin cycle_end(); cycle_start(); end 103 | cycle_end(); 104 | bus.p_data <= #TA '0; 105 | bus.p_resp <= #TA '0; 106 | bus.p_valid <= #TA 0; 107 | endtask 108 | 109 | /// Receive a request. 110 | task recv_req (output req_t req); 111 | bus.q_ready <= #TA 1; 112 | cycle_start(); 113 | while (bus.q_valid != 1) begin cycle_end(); cycle_start(); end 114 | req = new; 115 | req.addr = bus.q_addr; 116 | req.op = bus.q_op; 117 | req.data = bus.q_data; 118 | cycle_end(); 119 | bus.q_ready <= #TA 0; 120 | endtask 121 | 122 | /// Receive a response. 123 | task recv_rsp (output rsp_t rsp); 124 | bus.p_ready <= #TA 1; 125 | cycle_start(); 126 | while (bus.p_valid != 1) begin cycle_end(); cycle_start(); end 127 | rsp = new; 128 | rsp.data = bus.p_data; 129 | rsp.resp = bus.p_resp; 130 | cycle_end(); 131 | bus.p_ready <= #TA 0; 132 | endtask 133 | 134 | /// Monitor request. 135 | task mon_req (output req_t req); 136 | cycle_start(); 137 | while (!(bus.q_valid && bus.q_ready)) begin cycle_end(); cycle_start(); end 138 | req = new; 139 | req.addr = bus.q_addr; 140 | req.op = bus.q_op; 141 | req.data = bus.q_data; 142 | cycle_end(); 143 | endtask 144 | 145 | /// Monitor response. 146 | task mon_rsp (output rsp_t rsp); 147 | cycle_start(); 148 | while (!(bus.p_valid && bus.p_ready)) begin cycle_end(); cycle_start(); end 149 | rsp = new; 150 | rsp.data = bus.p_data; 151 | rsp.resp = bus.p_resp; 152 | cycle_end(); 153 | endtask 154 | 155 | endclass 156 | 157 | // Super class for random dmi drivers. 158 | virtual class rand_dmi #( 159 | // dmi interface parameters 160 | parameter int AW = 32, 161 | // Stimuli application and test time 162 | parameter time TA = 0ps, 163 | parameter time TT = 0ps 164 | ); 165 | 166 | typedef dmi_test::dmi_driver #( 167 | // dmi bus interface parameters; 168 | .AW ( AW ), 169 | // Stimuli application and test time 170 | .TA ( TA ), 171 | .TT ( TT ) 172 | ) dmi_driver_t; 173 | 174 | dmi_driver_t drv; 175 | 176 | function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); 177 | this.drv = new (bus); 178 | endfunction 179 | 180 | task automatic rand_wait(input int unsigned min, input int unsigned max); 181 | int unsigned rand_success, cycles; 182 | rand_success = std::randomize(cycles) with { 183 | cycles >= min; 184 | cycles <= max; 185 | // Weigh the distribution so that the minimum cycle time is the common 186 | // case. 187 | cycles dist {min := 10, [min+1:max] := 1}; 188 | }; 189 | assert (rand_success) else $error("Failed to randomize wait cycles!"); 190 | repeat (cycles) @(posedge this.drv.bus.clk_i); 191 | endtask 192 | 193 | endclass 194 | 195 | /// Generate random requests as a master device. 196 | class rand_dmi_master #( 197 | // dmi interface parameters 198 | parameter int AW = 32, 199 | // Stimuli application and test time 200 | parameter time TA = 0ps, 201 | parameter time TT = 0ps, 202 | parameter int unsigned REQ_MIN_WAIT_CYCLES = 1, 203 | parameter int unsigned REQ_MAX_WAIT_CYCLES = 20, 204 | parameter int unsigned RSP_MIN_WAIT_CYCLES = 1, 205 | parameter int unsigned RSP_MAX_WAIT_CYCLES = 20 206 | ) extends rand_dmi #(.AW(AW), .TA(TA), .TT(TT)); 207 | 208 | int unsigned cnt = 0; 209 | bit req_done = 0; 210 | 211 | /// Reset the driver. 212 | task reset(); 213 | drv.reset_master(); 214 | endtask 215 | 216 | /// Constructor. 217 | function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); 218 | super.new(bus); 219 | endfunction 220 | 221 | task run(input int n); 222 | fork 223 | send_requests(n); 224 | recv_response(); 225 | join 226 | endtask 227 | 228 | /// Send random requests. 229 | task send_requests (input int n); 230 | automatic req_t r = new; 231 | 232 | repeat (n) begin 233 | this.cnt++; 234 | assert(r.randomize()); 235 | rand_wait(REQ_MIN_WAIT_CYCLES, REQ_MAX_WAIT_CYCLES); 236 | this.drv.send_req(r); 237 | end 238 | this.req_done = 1; 239 | endtask 240 | 241 | /// Receive random responses. 242 | task recv_response; 243 | while (!this.req_done || this.cnt > 0) begin 244 | automatic rsp_t rsp; 245 | this.cnt--; 246 | rand_wait(RSP_MIN_WAIT_CYCLES, RSP_MAX_WAIT_CYCLES); 247 | this.drv.recv_rsp(rsp); 248 | end 249 | endtask 250 | endclass 251 | 252 | class rand_dmi_slave #( 253 | // dmi interface parameters 254 | parameter int AW = 32, 255 | // Stimuli application and test time 256 | parameter time TA = 0ps, 257 | parameter time TT = 0ps, 258 | parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, 259 | parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, 260 | parameter int unsigned RSP_MIN_WAIT_CYCLES = 0, 261 | parameter int unsigned RSP_MAX_WAIT_CYCLES = 10 262 | ) extends rand_dmi #(.AW(AW), .TA(TA), .TT(TT)); 263 | 264 | mailbox req_mbx = new(); 265 | 266 | /// Reset the driver. 267 | task reset(); 268 | drv.reset_slave(); 269 | endtask 270 | 271 | task run(); 272 | fork 273 | recv_requests(); 274 | send_responses(); 275 | join 276 | endtask 277 | 278 | /// Constructor. 279 | function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); 280 | super.new(bus); 281 | endfunction 282 | 283 | task recv_requests(); 284 | forever begin 285 | automatic req_t req; 286 | rand_wait(REQ_MIN_WAIT_CYCLES, REQ_MAX_WAIT_CYCLES); 287 | this.drv.recv_req(req); 288 | req_mbx.put(req); 289 | end 290 | endtask 291 | 292 | task send_responses(); 293 | automatic rsp_t rsp = new; 294 | automatic req_t req; 295 | forever begin 296 | req_mbx.get(req); 297 | assert(rsp.randomize()); 298 | @(posedge this.drv.bus.clk_i); 299 | rand_wait(RSP_MIN_WAIT_CYCLES, RSP_MAX_WAIT_CYCLES); 300 | this.drv.send_rsp(rsp); 301 | end 302 | endtask 303 | endclass 304 | 305 | class dmi_monitor #( 306 | // dmi interface parameters 307 | parameter int AW = 32, 308 | // Stimuli application and test time 309 | parameter time TA = 0ps, 310 | parameter time TT = 0ps 311 | ) extends rand_dmi #(.AW(AW), .TA(TA), .TT(TT)); 312 | 313 | mailbox req_mbx = new, rsp_mbx = new; 314 | 315 | /// Constructor. 316 | function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); 317 | super.new(bus); 318 | endfunction 319 | 320 | // dmi Monitor. 321 | task monitor; 322 | fork 323 | forever begin 324 | automatic dmi_test::req_t req; 325 | this.drv.mon_req(req); 326 | req_mbx.put(req); 327 | end 328 | forever begin 329 | automatic dmi_test::rsp_t rsp; 330 | this.drv.mon_rsp(rsp); 331 | rsp_mbx.put(rsp); 332 | end 333 | join 334 | endtask 335 | endclass 336 | 337 | endpackage -------------------------------------------------------------------------------- /src_files.yml: -------------------------------------------------------------------------------- 1 | riscv-dbg: 2 | files: [ 3 | src/dm_pkg.sv, 4 | debug_rom/debug_rom.sv, 5 | debug_rom/debug_rom_one_scratch.sv, 6 | src/dm_csrs.sv, 7 | src/dm_mem.sv, 8 | src/dm_top.sv, 9 | src/dm_obi_top.sv, 10 | src/dmi_cdc.sv, 11 | src/dmi_jtag.sv, 12 | src/dmi_jtag_tap.sv, 13 | src/dm_sba.sv, 14 | ] 15 | -------------------------------------------------------------------------------- /sva/dm_csrs_sva.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Silicon Labs, Inc. 2 | // 3 | // This file, and derivatives thereof are licensed under the 4 | // Solderpad License, Version 2.0 (the "License"). 5 | // 6 | // Use of this file means you agree to the terms and conditions 7 | // of the license and are in full compliance with the License. 8 | // 9 | // You may obtain a copy of the License at: 10 | // 11 | // https://solderpad.org/licenses/SHL-2.0/ 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // and hardware implementations thereof distributed under the License 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 16 | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED. 17 | // 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // Engineer: Arjan Bink - arjan.bink@silabs.com // 23 | // // 24 | // Design Name: dm_csrs_sva // 25 | // Language: SystemVerilog // 26 | // // 27 | // Description: SystemVerilog assertions for dm_csrs // 28 | // // 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | module dm_csrs_sva #( 32 | parameter int unsigned NrHarts = 1, 33 | parameter int unsigned BusWidth = 32, 34 | parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}} 35 | )( 36 | input logic clk_i, 37 | input logic rst_ni, 38 | input logic dmi_req_valid_i, 39 | input logic dmi_req_ready_o, 40 | input dm::dmi_req_t dmi_req_i, 41 | input dm::dtm_op_e dtm_op 42 | ); 43 | 44 | /////////////////////////////////////////////////////// 45 | // Assertions 46 | /////////////////////////////////////////////////////// 47 | 48 | haltsum: assert property ( 49 | @(posedge clk_i) disable iff (!rst_ni) 50 | (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) |-> 51 | !({1'b0, dmi_req_i.addr} inside 52 | {dm::HaltSum0, dm::HaltSum1, dm::HaltSum2, dm::HaltSum3})) 53 | else $warning("Haltsums have not been properly tested yet."); 54 | 55 | endmodule: dm_csrs_sva 56 | -------------------------------------------------------------------------------- /sva/dm_sba_sva.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Silicon Labs, Inc. 2 | // 3 | // This file, and derivatives thereof are licensed under the 4 | // Solderpad License, Version 2.0 (the "License"). 5 | // 6 | // Use of this file means you agree to the terms and conditions 7 | // of the license and are in full compliance with the License. 8 | // 9 | // You may obtain a copy of the License at: 10 | // 11 | // https://solderpad.org/licenses/SHL-2.0/ 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // and hardware implementations thereof distributed under the License 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 16 | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED. 17 | // 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // Engineer: Arjan Bink - arjan.bink@silabs.com // 23 | // // 24 | // Design Name: dm_sba_sva // 25 | // Language: SystemVerilog // 26 | // // 27 | // Description: SystemVerilog assertions for dm_sba // 28 | // // 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | module dm_sba_sva #( 32 | parameter int unsigned BusWidth = 32, 33 | parameter bit ReadByteEnable = 1 34 | )( 35 | input logic clk_i, 36 | input logic dmactive_i, 37 | input logic [2:0] sbaccess_i, 38 | input dm::sba_state_e state_d 39 | ); 40 | 41 | /////////////////////////////////////////////////////// 42 | // Assertions 43 | /////////////////////////////////////////////////////// 44 | 45 | // maybe bump severity to $error if not handled at runtime 46 | dm_sba_access_size: assert property(@(posedge clk_i) disable iff (dmactive_i !== 1'b0) 47 | (state_d != dm::Idle) |-> (sbaccess_i < 4)) 48 | else $warning ("accesses > 8 byte not supported at the moment"); 49 | 50 | endmodule: dm_sba_sva 51 | -------------------------------------------------------------------------------- /sva/dm_top_sva.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Silicon Labs, Inc. 2 | // 3 | // This file, and derivatives thereof are licensed under the 4 | // Solderpad License, Version 2.0 (the "License"). 5 | // 6 | // Use of this file means you agree to the terms and conditions 7 | // of the license and are in full compliance with the License. 8 | // 9 | // You may obtain a copy of the License at: 10 | // 11 | // https://solderpad.org/licenses/SHL-2.0/ 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // and hardware implementations thereof distributed under the License 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 16 | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED. 17 | // 18 | // See the License for the specific language governing permissions and 19 | // limitations under the License. 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | // Engineer: Arjan Bink - arjan.bink@silabs.com // 23 | // // 24 | // Design Name: dm_top_sva // 25 | // Language: SystemVerilog // 26 | // // 27 | // Description: SystemVerilog assertions for dm_top // 28 | // // 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | module dm_top_sva 32 | #( 33 | parameter int unsigned NrHarts = 1, 34 | parameter int unsigned BusWidth = 32, 35 | parameter int unsigned DmBaseAddress = 'h1000, 36 | parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}}, 37 | parameter bit ReadByteEnable = 1 38 | )( 39 | input logic clk_i, 40 | input logic rst_ni, 41 | input dm::hartinfo_t [NrHarts-1:0] hartinfo_i 42 | ); 43 | 44 | /////////////////////////////////////////////////////// 45 | // Assertions 46 | /////////////////////////////////////////////////////// 47 | 48 | // Check that BusWidth is supported 49 | bus_width: assert property ( 50 | @(posedge clk_i) disable iff (!rst_ni) 51 | (1'b1) |-> (BusWidth == 32 || BusWidth == 64)) 52 | else $fatal(1, "DM needs a bus width of either 32 or 64 bits"); 53 | 54 | // Fail if the DM is not located on the zero page and one hart doesn't have two scratch registers. 55 | genvar i; 56 | generate for (i = 0; i < NrHarts; i++) 57 | begin 58 | hart_info: assert property ( 59 | @(posedge clk_i) disable iff (!rst_ni) 60 | (1'b1) |-> ((DmBaseAddress > 0 && hartinfo_i[i].nscratch >= 2) || (DmBaseAddress == 0 && hartinfo_i[i].nscratch >= 1))) 61 | else $fatal(1, "If the DM is not located at the zero page each hart needs at least two scratch registers %d %d",i, hartinfo_i[i].nscratch); 62 | end 63 | endgenerate 64 | 65 | endmodule: dm_top_sva 66 | -------------------------------------------------------------------------------- /tb/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | UseTab: Never 5 | BreakBeforeBraces: Linux 6 | AlwaysBreakBeforeMultilineStrings: true 7 | AllowShortIfStatementsOnASingleLine: false 8 | AllowShortLoopsOnASingleLine: false 9 | AllowShortFunctionsOnASingleLine: false 10 | IndentCaseLabels: false 11 | AlignEscapedNewlinesLeft: false 12 | AlignTrailingComments: true 13 | AlignOperands: true 14 | AllowAllParametersOfDeclarationOnNextLine: false 15 | AlignAfterOpenBracket: true 16 | SpaceAfterCStyleCast: false 17 | MaxEmptyLinesToKeep: 2 18 | BreakBeforeBinaryOperators: NonAssignment 19 | BreakStringLiterals: false 20 | SortIncludes: false 21 | ContinuationIndentWidth: 4 22 | ColumnLimit: 80 23 | IndentPPDirectives: AfterHash 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | ForEachMacros: 27 | - 'TAILQ_FOREACH' 28 | - 'TAILQ_FOREACH_REVERSE' 29 | BreakBeforeBinaryOperators: None 30 | MaxEmptyLinesToKeep: 1 31 | AlwaysBreakAfterDefinitionReturnType: None 32 | AlwaysBreakAfterReturnType: None 33 | AlwaysBreakBeforeMultilineStrings: false 34 | AlignConsecutiveAssignments: true 35 | ... 36 | -------------------------------------------------------------------------------- /tb/.gitignore: -------------------------------------------------------------------------------- 1 | TAGS 2 | memory_dump.bin 3 | modelsim.ini 4 | *.o 5 | work/* 6 | *.vstf 7 | *.wlf 8 | *.log 9 | objdump 10 | .build-rtl 11 | .lib-rtl 12 | .opt-rtl 13 | *.elf 14 | *.hex 15 | riscv 16 | common_cells 17 | tech_cells_generic 18 | fpnew 19 | transcript 20 | .nfs* 21 | simv* 22 | ucli.key 23 | DVEfiles 24 | cobj_dir 25 | obj_dir 26 | testbench_verilator 27 | -------------------------------------------------------------------------------- /tb/LICENSE.Berkeley: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2016, The Regents of the University of California 2 | (Regents). All Rights Reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 3. Neither the name of the Regents nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 16 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 17 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 18 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | 20 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 23 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 24 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 25 | -------------------------------------------------------------------------------- /tb/README.md: -------------------------------------------------------------------------------- 1 | Debug Unit plus RI5CY Testbench 2 | ===================== 3 | 4 | This testbench tests RI5CY together with a v0.13.1 compliant [debug 5 | unit](https://www.github.com/pulp-platform/riscv-dbg). There are several tests 6 | that can be run, but for now it is just `riscv test_compliance` of 7 | [riscv-openocd](https://www.github.com/riscv/riscv-openocd) (see in 8 | `pulpissimo.cfg`) and a not yet scripted run of gdb connecting to openocd, 9 | loading and running a hello world program (see `prog/test.c`). 10 | 11 | You need `riscv-openocd`. 12 | 13 | Running the testbench with vsim 14 | ---------------------- 15 | Point you environment variable `RISCV` to your RISC-V toolchain. Call `make 16 | vsim-run` to build the testbench and the program, and run it with vsim. Use 17 | `VSIM_FLAGS` to configure the simulator e.g. `make vsim-run VSIM_FLAGS="-gui 18 | -debugdb"`. 19 | 20 | Running the testbench with vcs 21 | ---------------------- 22 | Point you environment variable `RISCV` to your RISC-V toolchain. Call `make 23 | vcs-run`. Use `VCS_FLAGS` and `SIMV_FLAGS` to configure vcs e.g. `make vcs-run 24 | VCS_FLAGS="-debug_all"`. 25 | 26 | 27 | Running the testbench with [verilator](https://www.veripool.org/wiki/verilator) 28 | ---------------------- 29 | Point you environment variable `RISCV` to your RISC-V toolchain. Call `make 30 | veri-run`. Use `VERI_FLAGS` to configure verilator e.g. `make firmware-veri-run 31 | VERI_FLAGS="+firmware=path_to_firmware +vcd"` to use a custom firmware and dump 32 | to a vcd file. 33 | 34 | 35 | Options 36 | ---------------------- 37 | A few plusarg options are supported. 38 | * `+verbose` to show all memory read and writes and other miscellaneous information. 39 | 40 | * `+vcd` to produce a vcd file called `riscy_tb.vcd`. Verilator always produces 41 | a vcd file called `verilator_tb.vcd`. 42 | 43 | * `+firmware=path_to_firmware` to load a specific firmware. It is a bit tricky to 44 | build and link your own program. Look into the `prog` folder for an example. 45 | 46 | Example Run 47 | ----------------------- 48 | 1. `make veri-run` 49 | 3. (in new terminal) `export JTAG_VPI_PORT=port_name_from 1.` 50 | 2. (in new terminal) `openocd -f dm_compliance_test.cfg` 51 | 4. Now you can connect with gdb and interact with the testbench 52 | -------------------------------------------------------------------------------- /tb/SimJTAG.sv: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | //VCS coverage exclude_file 3 | import "DPI-C" function int jtag_tick 4 | ( 5 | input int port, 6 | output bit jtag_TCK, 7 | output bit jtag_TMS, 8 | output bit jtag_TDI, 9 | output bit jtag_TRSTn, 10 | 11 | input bit jtag_TDO 12 | ); 13 | 14 | module SimJTAG #( 15 | parameter TICK_DELAY = 50, 16 | parameter PORT = 0 17 | )( 18 | 19 | input clock, 20 | input reset, 21 | 22 | input enable, 23 | input init_done, 24 | 25 | output jtag_TCK, 26 | output jtag_TMS, 27 | output jtag_TDI, 28 | output jtag_TRSTn, 29 | 30 | input jtag_TDO_data, 31 | input jtag_TDO_driven, 32 | 33 | output [31:0] exit 34 | ); 35 | 36 | reg [31:0] tickCounterReg; 37 | wire [31:0] tickCounterNxt; 38 | 39 | assign tickCounterNxt = (tickCounterReg == 0) ? TICK_DELAY : (tickCounterReg - 1); 40 | 41 | bit r_reset; 42 | 43 | wire [31:0] random_bits = $random; 44 | 45 | wire #0.1 __jtag_TDO = jtag_TDO_driven ? 46 | jtag_TDO_data : random_bits[0]; 47 | 48 | bit __jtag_TCK; 49 | bit __jtag_TMS; 50 | bit __jtag_TDI; 51 | bit __jtag_TRSTn; 52 | int __exit; 53 | 54 | reg init_done_sticky; 55 | 56 | assign #0.1 jtag_TCK = __jtag_TCK; 57 | assign #0.1 jtag_TMS = __jtag_TMS; 58 | assign #0.1 jtag_TDI = __jtag_TDI; 59 | assign #0.1 jtag_TRSTn = __jtag_TRSTn; 60 | 61 | assign #0.1 exit = __exit; 62 | 63 | always @(posedge clock) begin 64 | r_reset <= reset; 65 | if (reset || r_reset) begin 66 | __exit = 0; 67 | tickCounterReg <= TICK_DELAY; 68 | init_done_sticky <= 1'b0; 69 | end else begin 70 | init_done_sticky <= init_done | init_done_sticky; 71 | if (enable && init_done_sticky) begin 72 | tickCounterReg <= tickCounterNxt; 73 | if (tickCounterReg == 0) begin 74 | __exit = jtag_tick(PORT, 75 | __jtag_TCK, 76 | __jtag_TMS, 77 | __jtag_TDI, 78 | __jtag_TRSTn, 79 | __jtag_TDO); 80 | end 81 | end // if (enable && init_done_sticky) 82 | end // else: !if(reset || r_reset) 83 | end // always @ (posedge clock) 84 | 85 | endmodule 86 | -------------------------------------------------------------------------------- /tb/boot_rom.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | // Author: Robert Balas 12 | // Description: Bootrom for firmware booting 13 | 14 | module boot_rom ( 15 | input logic clk_i, 16 | input logic req_i, 17 | input logic [31:0] addr_i, 18 | output logic [31:0] rdata_o 19 | ); 20 | localparam int RomSize = 2; 21 | localparam logic [31:0] entry_addr = 32'h1c00_0080; 22 | 23 | logic [RomSize-1:0][31:0] mem; 24 | assign mem = { 25 | dm_tb_pkg::jalr(5'h0, 5'h1, entry_addr[11:0]), 26 | dm_tb_pkg::lui(5'h1, entry_addr[31:12]) 27 | }; 28 | 29 | logic [$clog2(RomSize)-1:0] addr_q; 30 | 31 | 32 | assign rdata_o = (addr_q < RomSize) ? mem[addr_q] : '0; 33 | 34 | always_ff @(posedge clk_i) begin 35 | if (req_i) begin 36 | addr_q <= addr_i[$clog2(RomSize)-1+3:2]; 37 | end 38 | end 39 | 40 | endmodule 41 | -------------------------------------------------------------------------------- /tb/dm_compliance_test.cfg: -------------------------------------------------------------------------------- 1 | debug_level 4 2 | adapter_khz 10000 3 | 4 | interface remote_bitbang 5 | remote_bitbang_host localhost 6 | 7 | remote_bitbang_port 9999 8 | 9 | set _CHIPNAME riscv 10 | jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x249511C3 11 | 12 | foreach t [jtag names] { 13 | puts [format "TAP: %s\n" $t] 14 | } 15 | 16 | set _TARGETNAME $_CHIPNAME.cpu 17 | #target create $_TARGETNAME riscv -chain-position $_TARGETNAME -coreid 0x3e0 18 | target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv 19 | 20 | riscv set_reset_timeout_sec 2000 21 | riscv set_command_timeout_sec 2000 22 | 23 | # prefer to use sba for system bus access 24 | riscv set_prefer_sba on 25 | 26 | # dump jtag chain 27 | scan_chain 28 | 29 | init 30 | riscv test_compliance 31 | shutdown 32 | -------------------------------------------------------------------------------- /tb/dm_debug.cfg: -------------------------------------------------------------------------------- 1 | debug_level 4 2 | adapter_khz 10000 3 | 4 | interface remote_bitbang 5 | remote_bitbang_host localhost 6 | 7 | remote_bitbang_port $::env(JTAG_VPI_PORT) 8 | 9 | set _CHIPNAME riscv 10 | jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x249511C3 11 | 12 | foreach t [jtag names] { 13 | puts [format "TAP: %s\n" $t] 14 | } 15 | 16 | set _TARGETNAME $_CHIPNAME.cpu 17 | #target create $_TARGETNAME riscv -chain-position $_TARGETNAME -coreid 0x3e0 18 | target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv 19 | 20 | riscv set_reset_timeout_sec 2000 21 | riscv set_command_timeout_sec 2000 22 | 23 | # prefer to use sba for system bus access 24 | riscv set_prefer_sba on 25 | 26 | # dump jtag chain 27 | scan_chain 28 | 29 | init 30 | 31 | halt 32 | echo "Ready for Remote Connections" 33 | -------------------------------------------------------------------------------- /tb/dm_tb_pkg.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the "License"); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | // 11 | // Contributor: Robert Balas 12 | 13 | 14 | package dm_tb_pkg; 15 | 16 | // PULPissimo-like memory map 17 | typedef enum logic [31:0] { 18 | ROM_BASE = 32'h1A00_0000, 19 | FLL_BASE = 32'h1A10_0000, 20 | GPIO_BASE = 32'h1A10_1000, 21 | UDMA_BASE = 32'h1A10_2000, 22 | CNTRL_BASE = 32'h1A10_4000, 23 | ADVTIMER_BASE = 32'h1A10_5000, 24 | EVENT_BASE = 32'h1A10_6000, 25 | TIMER_BASE = 32'h1A10_B000, 26 | HWPE_BASE = 32'h1A10_C000, 27 | STDOUT_BASE = 32'h1A10_F000, 28 | DEBUG_BASE = 32'h1A11_0000, 29 | SRAM_BASE = 32'h1C00_0000 30 | } mmap_base_t; 31 | 32 | localparam logic [31:0] ROM_LEN = 32'h0010_0000; 33 | localparam logic [31:0] FLL_LEN = 32'h0000_1000; 34 | localparam logic [31:0] GPIO_LEN = 32'h0000_1000; 35 | localparam logic [31:0] UDMA_LEN = 32'h0000_2000; 36 | localparam logic [31:0] CNTRL_LEN = 32'h0000_1000; 37 | localparam logic [31:0] ADVTIMER_LEN = 32'h0000_1000; 38 | localparam logic [31:0] EVENT_LEN = 32'h0000_5000; 39 | localparam logic [31:0] TIMER_LEN = 32'h0000_1000; 40 | localparam logic [31:0] HWPE_LEN = 32'h0000_3000; 41 | localparam logic [31:0] STDOUT_LEN = 32'h0000_1000; 42 | localparam logic [31:0] DEBUG_LEN = 32'h0000_1000; 43 | localparam logic [31:0] SRAM_LEN = 32'h000f_C000; 44 | 45 | // helper functions 46 | function automatic logic [31:0] jal (logic[4:0] rd, logic [20:0] imm); 47 | return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; 48 | endfunction 49 | 50 | function automatic logic [31:0] jalr (logic[4:0] rd, logic[4:0] rs1, logic [11:0] offset); 51 | return {offset[11:0], rs1, 3'b0, rd, 7'h67}; 52 | endfunction 53 | 54 | function automatic logic [31:0] lui (logic[4:0] rd, logic [19:0] uimm); 55 | return {uimm, rd, 7'b0110111}; 56 | endfunction 57 | 58 | endpackage // riscv_tb_pkg 59 | -------------------------------------------------------------------------------- /tb/dp_ram.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Copyright 2017 Embecosm Limited 3 | // Copyright and related rights are licensed under the Solderpad Hardware 4 | // License, Version 0.51 (the "License"); you may not use this file except in 5 | // compliance with the License. You may obtain a copy of the License at 6 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 7 | // or agreed to in writing, software, hardware and materials distributed under 8 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | // specific language governing permissions and limitations under the License. 11 | 12 | module dp_ram #( 13 | parameter int unsigned ADDR_WIDTH = 8, 14 | parameter int unsigned INSTR_RDATA_WIDTH = 128 15 | ) ( 16 | input logic clk_i, 17 | 18 | input logic en_a_i, 19 | input logic [ADDR_WIDTH-1:0] addr_a_i, 20 | input logic [31:0] wdata_a_i, 21 | output logic [INSTR_RDATA_WIDTH-1:0] rdata_a_o, 22 | input logic we_a_i, 23 | input logic [3:0] be_a_i, 24 | 25 | input logic en_b_i, 26 | input logic [ADDR_WIDTH-1:0] addr_b_i, 27 | input logic [31:0] wdata_b_i, 28 | output logic [31:0] rdata_b_o, 29 | input logic we_b_i, 30 | input logic [3:0] be_b_i 31 | ); 32 | 33 | localparam bytes = 2**ADDR_WIDTH; 34 | 35 | logic [7:0] mem[bytes]; 36 | logic [ADDR_WIDTH-1:0] addr_a_int; 37 | logic [ADDR_WIDTH-1:0] addr_b_int; 38 | 39 | always_comb addr_a_int = {addr_a_i[ADDR_WIDTH-1:2], 2'b0}; 40 | always_comb addr_b_int = {addr_b_i[ADDR_WIDTH-1:2], 2'b0}; 41 | 42 | always @(posedge clk_i) begin 43 | for (int i = 0; i < INSTR_RDATA_WIDTH/8; i++) begin 44 | rdata_a_o[(i*8)+: 8] <= mem[addr_a_int + i]; 45 | end 46 | 47 | /* addr_b_i is the actual memory address referenced */ 48 | if (en_b_i) begin 49 | /* handle writes */ 50 | if (we_b_i) begin 51 | if (be_b_i[0]) mem[addr_b_int ] <= wdata_b_i[ 0+:8]; 52 | if (be_b_i[1]) mem[addr_b_int + 1] <= wdata_b_i[ 8+:8]; 53 | if (be_b_i[2]) mem[addr_b_int + 2] <= wdata_b_i[16+:8]; 54 | if (be_b_i[3]) mem[addr_b_int + 3] <= wdata_b_i[24+:8]; 55 | end 56 | /* handle reads */ 57 | else begin 58 | if ($test$plusargs("verbose")) 59 | $display("read addr=0x%08x: data=0x%08x", addr_b_int, 60 | {mem[addr_b_int + 3], mem[addr_b_int + 2], 61 | mem[addr_b_int + 1], mem[addr_b_int + 0]}); 62 | 63 | rdata_b_o[ 7: 0] <= mem[addr_b_int ]; 64 | rdata_b_o[15: 8] <= mem[addr_b_int + 1]; 65 | rdata_b_o[23:16] <= mem[addr_b_int + 2]; 66 | rdata_b_o[31:24] <= mem[addr_b_int + 3]; 67 | end 68 | end 69 | end 70 | 71 | export "DPI-C" function read_byte; 72 | export "DPI-C" task write_byte; 73 | 74 | function int read_byte(input logic [ADDR_WIDTH-1:0] byte_addr); 75 | read_byte = mem[byte_addr]; 76 | endfunction 77 | 78 | task write_byte(input integer byte_addr, logic [7:0] val, output logic [7:0] other); 79 | mem[byte_addr] = val; 80 | other = mem[byte_addr]; 81 | 82 | endtask 83 | 84 | endmodule // dp_ram 85 | -------------------------------------------------------------------------------- /tb/jtag_dmi/.gitignore: -------------------------------------------------------------------------------- 1 | compile.tcl 2 | wlf* 3 | work/ 4 | -------------------------------------------------------------------------------- /tb/jtag_dmi/jtag_intf.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | interface JTAG_DV ( 6 | input logic clk_i 7 | ); 8 | logic tdi; 9 | logic tdo; 10 | logic tms; 11 | logic trst_n; 12 | 13 | modport in (input tdi, tms, trst_n, output tdo); 14 | modport out (output tdi, tms, trst_n, input tdo); 15 | endinterface 16 | -------------------------------------------------------------------------------- /tb/jtag_dmi/jtag_test.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | // Author: Andreas Traber 6 | // Author: Florian Zaruba 7 | // Author: Fabian Schuiki 8 | 9 | package jtag_test; 10 | 11 | class jtag_driver #( 12 | parameter int IrLength = 0, 13 | parameter IDCODE = 'h1, 14 | parameter time TA = 0ns , // stimuli application time 15 | parameter time TT = 0ns // stimuli test time 16 | ); 17 | 18 | virtual JTAG_DV jtag; 19 | 20 | // last IR register select 21 | protected logic [IrLength-1:0] ir_select; 22 | 23 | function new ( virtual JTAG_DV jtag); 24 | this.jtag = jtag; 25 | // Per JTAG specification the IDCODE should be at 'h1 and is selected by 26 | // default after reset. 27 | this.ir_select = 'h1; 28 | endfunction 29 | 30 | task reset_master; 31 | jtag.tms <= #TA 1; 32 | jtag.tdi <= #TA 0; 33 | jtag.trst_n <= #TA 0; 34 | repeat (2) clock(); 35 | jtag.trst_n <= #TA 1; 36 | this.ir_select = 'h1; 37 | clock(); 38 | endtask 39 | 40 | task soft_reset(); 41 | jtag.tms <= #TA 1; 42 | jtag.tdi <= #TA 0; 43 | repeat (6) clock(); 44 | jtag.tms <= #TA 0; 45 | clock(); 46 | // After softreset the IR should be reset to IDCODE so we have to mirror 47 | // this in our internal state. 48 | this.ir_select = 'h1; 49 | endtask 50 | 51 | // Set IR, but only if it needs to be set. 52 | task set_ir(input logic [IrLength-1:0] opcode); 53 | logic opcode_unpacked [IrLength]; 54 | // check whether IR is already set to the right value 55 | if (this.ir_select == opcode) return; 56 | {<<{opcode_unpacked}} = opcode; 57 | write_tms(1); // select DR scan 58 | write_tms(1); // select IR scan 59 | write_tms(0); // capture IR 60 | write_tms(0); // shift IR 61 | write_bits(opcode_unpacked, 1); 62 | write_tms(1); // update IR 63 | write_tms(0); // run test idle 64 | this.ir_select = opcode; 65 | endtask 66 | // Go from `run_test_idle` to `shift_dr` 67 | task shift_dr(); 68 | write_tms(1); // select DR scan 69 | write_tms(0); // capture DR 70 | write_tms(0); // shift DR 71 | endtask 72 | 73 | // Go to `run_test_idle` 74 | task update_dr(bit exit_1_dr); 75 | // depending on the state `exit_1_dr` is already reached when shifting data (`tms_on_last`). 76 | if (exit_1_dr) write_tms(1); // exi 1 DR 77 | write_tms(1); // update DR 78 | write_tms(0); // run test idle 79 | endtask 80 | 81 | task write_bits(input logic wdata [$], input logic tms_last); 82 | for (int i = 0; i < $size(wdata); i++) begin 83 | jtag.tdi <= #TA wdata[i]; 84 | if (i == ($size(wdata) - 1)) jtag.tms <= #TA tms_last; 85 | clock(); 86 | end 87 | jtag.tms <= #TA 0; 88 | endtask 89 | 90 | // Assumes JTAG FSM is already in shift DR state 91 | task readwrite_bits(output logic rdata [$], input logic wdata [$], input logic tms_last); 92 | for (int i = 0; i < wdata.size(); i++) begin 93 | jtag.tdi <= #TA wdata[i]; 94 | if (i == (wdata.size() - 1)) jtag.tms <= #TA tms_last; // tms_last ? exit1 DR : shift DR 95 | cycle_start(); 96 | rdata[i] = jtag.tdo; 97 | cycle_end(); 98 | end 99 | jtag.tms <= #TA 0; // tms_last ? pause DR : shift DR 100 | endtask 101 | 102 | task wait_idle(int cycles); 103 | repeat(cycles) clock(); 104 | endtask 105 | 106 | // Protected methods 107 | task write_tms(input logic tms_val); 108 | jtag.tms <= #TA tms_val; 109 | clock(); 110 | endtask 111 | 112 | protected task clock(); 113 | cycle_start(); cycle_end(); 114 | endtask 115 | 116 | protected task cycle_start; 117 | #TT; 118 | endtask 119 | 120 | // TODO(zarubaf): I am not sure on which clock edge to trigger 121 | protected task cycle_end; 122 | @(posedge jtag.clk_i); 123 | endtask 124 | endclass 125 | 126 | // abstracts the debug module 127 | class riscv_dbg #( 128 | parameter int IrLength = 5, 129 | parameter IDCODE = 'h1, 130 | parameter DTMCSR = 'h10, 131 | parameter DMIACCESS = 'h11, 132 | parameter time TA = 0ns, // stimuli application time 133 | parameter time TT = 0ns // stimuli test time 134 | ); 135 | 136 | typedef jtag_test::jtag_driver#(.IrLength(IrLength), .IDCODE(IDCODE), .TA(TA), .TT(TT)) jtag_driver_t; 137 | jtag_driver_t jtag; 138 | 139 | localparam DMIWidth = $bits(dm::dmi_req_t); 140 | 141 | function new (jtag_driver_t jtag); 142 | this.jtag = jtag; 143 | endfunction 144 | 145 | task reset_master(); 146 | jtag.reset_master(); 147 | jtag.soft_reset(); 148 | endtask 149 | 150 | task wait_idle(int cycles); 151 | jtag.wait_idle(cycles); 152 | endtask 153 | 154 | task get_idcode(output logic [31:0] idcode); 155 | logic read_data [32], write_data [32]; 156 | write_data = '{default: 1'b0}; 157 | jtag.set_ir(IDCODE); 158 | jtag.shift_dr(); 159 | jtag.readwrite_bits(read_data, write_data, 1'b0); 160 | jtag.update_dr(1'b1); 161 | idcode = {<<{read_data}}; 162 | endtask 163 | 164 | task write_dtmcs(input logic [31:0] data); 165 | logic write_data [32]; 166 | logic [31:0] write_data_packed = {data}; 167 | {<<{write_data}} = write_data_packed; 168 | jtag.set_ir(DTMCSR); 169 | jtag.shift_dr(); 170 | jtag.write_bits(write_data, 1'b1); 171 | jtag.update_dr(1'b0); 172 | endtask 173 | 174 | task read_dtmcs(output dm::dtmcs_t data, input int wait_cycles = 10); 175 | logic read_data [32], write_data [32]; 176 | jtag.set_ir(DTMCSR); 177 | jtag.shift_dr(); 178 | // shift out read data 179 | {<<{write_data}} = 32'b0; 180 | jtag.readwrite_bits(read_data, write_data, 1'b1); 181 | jtag.update_dr(1'b0); 182 | data = dm::dtmcs_t'({<<{read_data}}); 183 | endtask 184 | 185 | task reset_dmi(); 186 | logic [31:0] dmireset = 1 << 16; 187 | write_dtmcs(dmireset); 188 | endtask 189 | 190 | task write_dmi(input dm::dm_csr_e address, input logic [31:0] data); 191 | logic write_data [DMIWidth]; 192 | logic [DMIWidth-1:0] write_data_packed = {address, data, dm::DTM_WRITE}; 193 | {<<{write_data}} = write_data_packed; 194 | jtag.set_ir(DMIACCESS); 195 | jtag.shift_dr(); 196 | jtag.write_bits(write_data, 1'b1); 197 | jtag.update_dr(1'b0); 198 | endtask 199 | 200 | task read_dmi(input dm::dm_csr_e address, output logic [31:0] data, input int wait_cycles = 10, 201 | output dm::dtm_op_status_e op); 202 | logic read_data [DMIWidth], write_data [DMIWidth]; 203 | logic [DMIWidth-1:0] data_out = 0; 204 | automatic logic [DMIWidth-1:0] write_data_packed = {address, 32'b0, dm::DTM_READ}; 205 | {<<{write_data}} = write_data_packed; 206 | jtag.set_ir(DMIACCESS); 207 | // send read command 208 | jtag.shift_dr(); 209 | jtag.write_bits(write_data, 1'b1); 210 | jtag.update_dr(1'b0); 211 | jtag.wait_idle(wait_cycles); 212 | // shift out read data 213 | jtag.shift_dr(); 214 | write_data_packed = {address, 32'b0, dm::DTM_NOP}; 215 | {<<{write_data}} = write_data_packed; 216 | jtag.readwrite_bits(read_data, write_data, 1'b1); 217 | jtag.update_dr(1'b0); 218 | data_out = {<<{read_data}}; 219 | op = dm::dtm_op_status_e'(data_out[1:0]); 220 | data = data_out[33:2]; 221 | endtask 222 | 223 | // Repeatedly read DMI until we get a valid response. 224 | // The delay between Update-DR and Capture-DR of 225 | // successive operations is automatically adjusted through 226 | // an exponential backoff scheme. 227 | // Note: read operations which have side-effects (e.g. 228 | // reading SBData0) should not use this function 229 | task read_dmi_exp_backoff(input dm::dm_csr_e address, output logic [31:0] data); 230 | logic read_data [DMIWidth], write_data [DMIWidth]; 231 | logic [DMIWidth-1:0] write_data_packed; 232 | logic [DMIWidth-1:0] data_out = 0; 233 | dm::dtm_op_status_e op = dm::DTM_SUCCESS; 234 | int trial_idx = 0; 235 | int wait_cycles = 8; 236 | 237 | do begin 238 | if (trial_idx != 0) begin 239 | // Not entered upon first iteration, resets the 240 | // sticky error state if previous read was unsuccessful 241 | reset_dmi(); 242 | end 243 | read_dmi(address, data, wait_cycles, op); 244 | wait_cycles *= 2; 245 | trial_idx++; 246 | end while (op == dm::DTM_BUSY); 247 | endtask 248 | 249 | task sba_read_double(input logic [31:0] address, output logic [63:0] data); 250 | // Attempt the access sequence. Two timing violations may 251 | // occur: 252 | // 1) an operation is attempted while a DMI request is still 253 | // in progress; 254 | // 2) a SB read is attempted while a read is still in progress 255 | // or a SB access is attempted while one is in progress 256 | // In either case the whole sequence must be re-attempted with 257 | // increased delays. 258 | // Case 1) is intercepted when the op returned by a read is == DTM_BUSY, 259 | // the sequence can be interrupted early and the delay to be adjusted is 260 | // that between the update phase and the capture phase of a successive op. 261 | // Case 2) is intercepted at the end of the sequence by reading the 262 | // SBCS register, and checking sbbusyerror. In this case the delay to be 263 | // adjusted is that before the SBData read operations. 264 | dm::dtm_op_status_e op; 265 | automatic int dmi_wait_cycles = 2; 266 | automatic int sba_wait_cycles = 2; 267 | automatic dm::sbcs_t sbcs = '{sbreadonaddr: 1, sbaccess: 3, default: '0}; 268 | dm::sbcs_t read_sbcs; 269 | // Check address is 64b aligned 270 | assert (address[2:0] == '0) else $error("[JTAG] 64b-unaligned accesses not supported"); 271 | // Start SBA sequence attempts 272 | while (1) begin 273 | automatic bit failed = 0; 274 | write_dmi(dm::SBCS, sbcs); 275 | write_dmi(dm::SBAddress0, address); 276 | wait_idle(sba_wait_cycles); 277 | read_dmi(dm::SBData1, data[63:32], dmi_wait_cycles, op); 278 | // Skip second read if we already have a DTM busy error 279 | // else we can override op 280 | if (op != dm::DTM_BUSY) begin 281 | read_dmi(dm::SBData0, data[31:0], dmi_wait_cycles, op); 282 | end 283 | // If we had a DTM_BUSY error, increase dmi_wait_cycles and clear error 284 | if (op == dm::DTM_BUSY) begin 285 | dmi_wait_cycles *= 2; 286 | failed = 1'b1; 287 | reset_dmi(); 288 | end 289 | // Test sbbusyerror and wait for sbbusy == 0 290 | // Error is cleared in next iteration when writing SBCS 291 | do begin 292 | sbcs.sbbusyerror = 1'b0; 293 | read_dmi_exp_backoff(dm::SBCS, read_sbcs); 294 | if (read_sbcs.sbbusyerror) begin 295 | sbcs.sbbusyerror = 1'b1; // set 1 to clear 296 | sba_wait_cycles *= 2; 297 | failed = 1'b1; 298 | end 299 | if (read_sbcs.sbbusy) wait_idle(sba_wait_cycles); 300 | end while (read_sbcs.sbbusy); 301 | // Exit loop if sequence was successful 302 | if (!failed) break; 303 | end 304 | endtask 305 | 306 | endclass 307 | endpackage 308 | -------------------------------------------------------------------------------- /tb/jtag_dmi/run_vsim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020-2021 ETH Zurich and University of Bologna. 3 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 4 | # SPDX-License-Identifier: SHL-0.51 5 | # 6 | # Fabian Schuiki 7 | # Andreas Kurth 8 | 9 | set -e 10 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 11 | 12 | [ ! -z "$VSIM" ] || VSIM=vsim 13 | 14 | bender script vsim -t test \ 15 | --vlog-arg="-svinputport=compat" \ 16 | --vlog-arg="-override_timescale 1ns/1ps" \ 17 | --vlog-arg="-suppress 2583" \ 18 | --vlog-arg="+cover=sbecft" \ 19 | > compile.tcl 20 | echo 'return 0' >> compile.tcl 21 | $VSIM -c -do 'exit -code [source compile.tcl]' 22 | 23 | call_vsim() { 24 | echo "log -r /*; run -all" | $VSIM -c -coverage -voptargs='+acc +cover=sbecft' "$@" | tee vsim.log 2>&1 25 | grep "Errors: 0," vsim.log 26 | } 27 | 28 | call_vsim tb_jtag_dmi 29 | -------------------------------------------------------------------------------- /tb/jtag_dmi/run_vsim_xilinx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020-2021 ETH Zurich and University of Bologna. 3 | # Solderpad Hardware License, Version 0.51, see LICENSE for details. 4 | # SPDX-License-Identifier: SHL-0.51 5 | # 6 | # Fabian Schuiki 7 | # Andreas Kurth 8 | 9 | set -e 10 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) 11 | 12 | if [ -z "$VIVADO_HOME" ]; then 13 | echo "Please set \$VIVADO_HOME to point to your Vivado installation."; exit 1; 14 | fi 15 | 16 | [ ! -z "$VSIM" ] || VSIM=vsim 17 | 18 | bender script vsim -t test -t xilinx -t bscane \ 19 | --vlog-arg="-svinputport=compat" \ 20 | --vlog-arg="-override_timescale 1ns/1ps" \ 21 | --vlog-arg="-suppress 2583" \ 22 | --vlog-arg="+cover=sbecft" \ 23 | > compile.tcl 24 | echo 'return 0' >> compile.tcl 25 | $VSIM -c -do 'exit -code [source compile.tcl]' 26 | 27 | call_vsim() { 28 | vlog $VIVADO_HOME/data/verilog/src/glbl.v 29 | vlog $VIVADO_HOME/data/verilog/src/unisims/BSCANE2.v 30 | vlog $VIVADO_HOME/data/verilog/src/unisims/JTAG_SIME2.v 31 | echo "log -r /*; run -all" | $VSIM -c -coverage -voptargs='+acc +cover=sbecft' "$@" | tee vsim.log 2>&1 32 | grep "Errors: 0," vsim.log 33 | } 34 | 35 | call_vsim work.tb_jtag_dmi work.glbl 36 | -------------------------------------------------------------------------------- /tb/jtag_dmi/tb_jtag_dmi.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2020 ETH Zurich and University of Bologna. 2 | // Solderpad Hardware License, Version 0.51, see LICENSE for details. 3 | // SPDX-License-Identifier: SHL-0.51 4 | 5 | /// Selectively test the JTAG DMI. 6 | module tb_jtag_dmi; 7 | 8 | logic clk, rst_n; 9 | 10 | localparam time ClkPeriod = 10ns; 11 | localparam time ApplTime = 2ns; 12 | localparam time TestTime = 8ns; 13 | 14 | localparam time JTAGPeriod = 50ns; 15 | 16 | localparam int unsigned AW = 7; 17 | localparam IDCode = 32'hdeadbeef | 32'b1; 18 | 19 | // ---------------- 20 | // Clock generation 21 | // ---------------- 22 | initial begin 23 | rst_n = 0; 24 | repeat (3) begin 25 | #(ClkPeriod/2) clk = 0; 26 | #(ClkPeriod/2) clk = 1; 27 | end 28 | rst_n = 1; 29 | forever begin 30 | #(ClkPeriod/2) clk = 0; 31 | #(ClkPeriod/2) clk = 1; 32 | end 33 | end 34 | 35 | logic tck; 36 | 37 | JTAG_DV jtag_mst (tck); 38 | 39 | initial begin 40 | #100ns; 41 | forever begin 42 | tck = 1; 43 | #(JTAGPeriod/2); 44 | tck = 0; 45 | #(JTAGPeriod/2); 46 | end 47 | end 48 | 49 | // --- 50 | // DUT 51 | // --- 52 | DMI_BUS_DV #( 53 | .ADDR_WIDTH ( AW ) 54 | ) slave_dv (clk); 55 | 56 | logic dut_tck, dut_tms, dut_trstn, dut_tdi, dut_tdo; 57 | logic start_rand; 58 | 59 | dm::dmi_req_t dmi_req; 60 | dm::dmi_resp_t dmi_resp; 61 | 62 | assign slave_dv.q_addr = dmi_req.addr; 63 | assign slave_dv.q_op = dmi_req.op; 64 | assign slave_dv.q_data = dmi_req.data; 65 | assign dmi_resp = '{ 66 | data: slave_dv.p_data, 67 | resp: slave_dv.p_resp 68 | }; 69 | 70 | dmi_jtag #( 71 | .IdcodeValue (IDCode) 72 | ) dut ( 73 | .clk_i (clk), 74 | .rst_ni (rst_n), 75 | .testmode_i (1'b0), 76 | 77 | .dmi_rst_no (), 78 | .dmi_req_o (dmi_req), 79 | .dmi_req_valid_o (slave_dv.q_valid), 80 | .dmi_req_ready_i (slave_dv.q_ready), 81 | 82 | .dmi_resp_i (dmi_resp), 83 | .dmi_resp_ready_o (slave_dv.p_ready), 84 | .dmi_resp_valid_i (slave_dv.p_valid), 85 | 86 | .tck_i (dut_tck), 87 | .tms_i (dut_tms), 88 | .trst_ni (dut_trstn), 89 | .td_i (dut_tdi), 90 | .td_o (dut_tdo), 91 | .tdo_oe_o () 92 | ); 93 | 94 | // ------- 95 | // Monitor 96 | // ------- 97 | typedef dmi_test::dmi_monitor #( 98 | .AW ( AW ), 99 | .TA ( ApplTime ), 100 | .TT ( TestTime ) 101 | ) dmi_monitor_t; 102 | 103 | dmi_monitor_t dmi_monitor = new (slave_dv); 104 | 105 | initial begin 106 | @(posedge start_rand); 107 | dmi_monitor.monitor(); 108 | end 109 | 110 | // ------ 111 | // Driver 112 | // ------ 113 | typedef dmi_test::rand_dmi_slave #( 114 | // dmi bus interface paramaters; 115 | .AW ( AW ), 116 | // Stimuli application and test time 117 | .TA ( ApplTime ), 118 | .TT ( TestTime ) 119 | ) dmi_rand_slave_t; 120 | 121 | dmi_rand_slave_t rand_dmi_slave = new (slave_dv); 122 | 123 | // dmi Slave. 124 | initial begin 125 | rand_dmi_slave.reset(); 126 | @(posedge start_rand); 127 | rand_dmi_slave.run(); 128 | end 129 | 130 | `ifdef TARGET_BSCANE 131 | 132 | JTAG_SIME2 #( 133 | .PART_NAME ("7K325T") 134 | ) i_jtag_sime2 ( 135 | .TDO (jtag_mst.tdo), 136 | .TCK (tck), 137 | .TDI (jtag_mst.tdi), 138 | .TMS (jtag_mst.tms) 139 | ); 140 | 141 | // Default part `7K325T` has an IrLength of 6. 142 | localparam IRLength = i_jtag_sime2.IRLength; 143 | localparam logic [23:0] IR_CAPTURE_VAL = 24'b010001010001010001010001, 144 | BYPASS_INSTR = 24'b111111111111111111111111, 145 | IDCODE_INSTR = 24'b001001001001001001001001, 146 | USER1_INSTR = 24'b000010100100100100100100, 147 | USER2_INSTR = 24'b000011100100100100100100, 148 | USER3_INSTR = 24'b100010100100100100100100, 149 | USER4_INSTR = 24'b100011100100100100100100; 150 | 151 | typedef jtag_test::riscv_dbg #( 152 | .IDCODE (IDCODE_INSTR[23:(24-IRLength)]), 153 | .DTMCSR (USER3_INSTR[23:(24-IRLength)]), 154 | .DMIACCESS (USER4_INSTR[23:(24-IRLength)]), 155 | .IrLength (IRLength), 156 | .TA (JTAGPeriod*0.1), 157 | .TT (JTAGPeriod*0.9) 158 | ) riscv_dbg_t; 159 | 160 | /// Helper function to bring instruction register values. 161 | // initial begin 162 | // for (int i = 6; i < 25; i++) begin 163 | // $display("IRLength %d: %h %h %h\n", i, 164 | // (IDCODE_INSTR >> (24-i)) & (2**i-1), 165 | // (USER3_INSTR >> (24-i)) & (2**i-1), 166 | // (USER4_INSTR >> (24-i)) & (2**i-1)); 167 | // end 168 | // end 169 | `else 170 | 171 | assign dut_tck = tck; 172 | assign dut_tms = jtag_mst.tms; 173 | assign dut_trstn = jtag_mst.trst_n; 174 | assign dut_tdi = jtag_mst.tdi; 175 | assign jtag_mst.tdo = dut_tdo; 176 | 177 | typedef jtag_test::riscv_dbg #( 178 | .IrLength (5), 179 | .TA (JTAGPeriod*0.1), 180 | .TT (JTAGPeriod*0.9) 181 | ) riscv_dbg_t; 182 | `endif 183 | 184 | riscv_dbg_t::jtag_driver_t jtag_in = new (jtag_mst); 185 | riscv_dbg_t riscv_dbg = new (jtag_in); 186 | 187 | mailbox req_mbx = new, rsp_mbx = new; 188 | localparam NrRandomTransactions = 200; 189 | int unsigned nr_transactions = 0; 190 | 191 | initial begin 192 | logic [31:0] idcode; 193 | dm::dtmcs_t dtmcs; 194 | logic [4:0] opocode; 195 | start_rand = 0; 196 | 197 | $display("Resetting"); 198 | riscv_dbg.reset_master(); 199 | 200 | /// Test ID Code. 201 | $display("Reading ID Code"); 202 | riscv_dbg.get_idcode(idcode); 203 | 204 | $display("Got ID Code %h", idcode); 205 | 206 | // Check ID Code. 207 | `ifdef TARGET_BSCANE 208 | assert(idcode == i_jtag_sime2.IDCODEval_sig); 209 | `else 210 | assert(idcode == IDCode); 211 | `endif 212 | 213 | /// Test DTMCs 214 | riscv_dbg.read_dtmcs(dtmcs); 215 | $display("DTMCS: %p", dtmcs); 216 | assert(dtmcs.version == 1); 217 | assert(dtmcs.abits == 7); 218 | 219 | riscv_dbg.write_dtmcs(32'hdeadbeef); 220 | 221 | riscv_dbg.read_dtmcs(dtmcs); 222 | $display("DTMCS: %p", dtmcs); 223 | assert(dtmcs.version == 1); 224 | assert(dtmcs.abits == 7); 225 | 226 | /// Random DMI transactions. 227 | // Generate a number of random transactions and drive them on the JTAG 228 | // interface. 229 | start_rand = 1; 230 | for (int i = 0; i < NrRandomTransactions; i++) begin 231 | automatic dmi_test::req_t transaction = new; 232 | assert(transaction.randomize() with { 233 | op inside {dm::DTM_WRITE, dm::DTM_READ}; 234 | }); 235 | if (transaction.op == dm::DTM_WRITE) begin 236 | req_mbx.put(transaction); 237 | riscv_dbg.write_dmi(dm::dm_csr_e'(transaction.addr), transaction.data); 238 | rsp_mbx.put('h0); 239 | end else if (transaction.op == dm::DTM_READ) begin 240 | automatic logic [31:0] rdata; 241 | req_mbx.put(transaction); 242 | riscv_dbg.read_dmi(dm::dm_csr_e'(transaction.addr), rdata); 243 | rsp_mbx.put(rdata); 244 | end 245 | // Randomly reset the dmi using either hard jtag trst_ni, JTAG 246 | // TestLogicReset or the dtmcs.dmihardreset bit. 247 | if ($urandom_range(0,100) < 5) begin 248 | riscv_dbg.wait_idle(30); 249 | case ($urandom_range(0,3)) 250 | 0: begin 251 | $info("Resetting JTAG DMI using asynchronous JTAG reset signal..."); 252 | riscv_dbg.reset_master(); 253 | end 254 | 255 | 1: begin 256 | $info("Resetting JTAG DMI using JTAG softreset (TestLogicReset TAP state)."); 257 | riscv_dbg.jtag.soft_reset(); 258 | end 259 | 260 | 2: begin 261 | dm::dtmcs_t dtmcs_value; 262 | dtmcs_value = '0; 263 | dtmcs_value.dmihardreset = 1; 264 | $info("Resetting JTAG DMI using DMI dtmcs registers' dmihardreset control bit."); 265 | riscv_dbg.write_dtmcs(dtmcs_value); 266 | end 267 | endcase 268 | end 269 | end 270 | #1000; 271 | $finish(); 272 | end 273 | 274 | // ---------- 275 | // Scoreboard 276 | // ---------- 277 | initial begin 278 | forever begin 279 | automatic dmi_test::req_t req, req_mon; 280 | automatic dmi_test::rsp_t rsp_mon; 281 | automatic logic [31:0] rsp; 282 | 283 | dmi_monitor.req_mbx.get(req_mon); 284 | dmi_monitor.rsp_mbx.get(rsp_mon); 285 | req_mbx.get(req); 286 | rsp_mbx.get(rsp); 287 | nr_transactions++; 288 | assert(req.addr == req_mon.addr) else 289 | $error("Invalid dmi request. Got address %0x instead of %0x.", req_mon.addr, req.addr); 290 | assert(req.op == req_mon.op) else 291 | $error("Invalid dmi request. Got op %0x instead of %0x.", req_mon.op, req.op);; 292 | if (req.op == dm::DTM_READ) begin 293 | assert(rsp_mon.data == rsp); 294 | end else begin 295 | assert(req.data == req_mon.data); 296 | end 297 | end 298 | end 299 | 300 | final begin 301 | assert(NrRandomTransactions == nr_transactions) else $error("Remaining transactions."); 302 | end 303 | 304 | endmodule 305 | -------------------------------------------------------------------------------- /tb/prog/crt0.S: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017 SiFive Inc. All rights reserved. 2 | * Copyright (c) 2019 ETH Zürich and University of Bologna 3 | * This copyrighted material is made available to anyone wishing to use, 4 | * modify, copy, or redistribute it subject to the terms and conditions 5 | * of the FreeBSD License. This program is distributed in the hope that 6 | * it will be useful, but WITHOUT ANY WARRANTY expressed or implied, 7 | * including the implied warranties of MERCHANTABILITY or FITNESS FOR 8 | * A PARTICULAR PURPOSE. A copy of this license is available at 9 | * http://www.opensource.org/licenses. 10 | */ 11 | 12 | /* Entry point for bare metal programs */ 13 | .section .text.start 14 | .global _start 15 | .type _start, @function 16 | 17 | _start: 18 | /* initialize global pointer */ 19 | .option push 20 | .option norelax 21 | 1: auipc gp, %pcrel_hi(__global_pointer$) 22 | addi gp, gp, %pcrel_lo(1b) 23 | .option pop 24 | 25 | /* initialize stack pointer */ 26 | la sp, _sp 27 | 28 | /* set vector table address */ 29 | la a0, __vector_start 30 | csrw mtvec, a0 31 | /* set vector table address for CLINTx */ 32 | la a0, __vector_x_start 33 | csrw 0x307, a0 34 | 35 | /* clear the bss segment */ 36 | la a0, __bss_start 37 | la a2, __bss_end 38 | sub a2, a2, a0 39 | li a1, 0 40 | call memset 41 | 42 | /* new-style constructors and destructors */ 43 | la a0, __libc_fini_array 44 | call atexit 45 | call __libc_init_array 46 | 47 | /* call main */ 48 | lw a0, 0(sp) /* a0 = argc */ 49 | addi a1, sp, __SIZEOF_POINTER__ /* a1 = argv */ 50 | li a2, 0 /* a2 = envp = NULL */ 51 | call main 52 | tail exit 53 | 54 | .size _start, .-_start 55 | 56 | .global _init 57 | .type _init, @function 58 | .global _fini 59 | .type _fini, @function 60 | _init: 61 | _fini: 62 | /* These don't have to do anything since we use init_array/fini_array. Prevent 63 | missing symbol error */ 64 | ret 65 | .size _init, .-_init 66 | .size _fini, .-_fini 67 | -------------------------------------------------------------------------------- /tb/prog/syscalls.c: -------------------------------------------------------------------------------- 1 | /* An extremely minimalist syscalls.c for newlib 2 | * Based on riscv newlib libgloss/riscv/sys_*.c 3 | * 4 | * Copyright 2019 Clifford Wolf 5 | * Copyright 2019 ETH Zürich and University of Bologna 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 12 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 14 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 16 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 | * PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #undef errno 25 | extern int errno; 26 | 27 | /* write to this reg for outputting strings */ 28 | #define STDOUT_REG 0x10000000 29 | /* write test result of program to this reg */ 30 | #define RESULT_REG 0x20000000 31 | /* write exit value of program to this reg */ 32 | #define EXIT_REG 0x20000004 33 | 34 | #define STDOUT_FILENO 1 35 | 36 | /* It turns out that older newlib versions use different symbol names which goes 37 | * against newlib recommendations. Anyway this is fixed in later version. 38 | */ 39 | #if __NEWLIB__ <= 2 && __NEWLIB_MINOR__ <= 5 40 | # define _sbrk sbrk 41 | # define _write write 42 | # define _close close 43 | # define _lseek lseek 44 | # define _read read 45 | # define _fstat fstat 46 | # define _isatty isatty 47 | #endif 48 | 49 | void unimplemented_syscall() 50 | { 51 | const char *p = "Unimplemented system call called!\n"; 52 | while (*p) 53 | *(volatile int *)STDOUT_REG = *(p++); 54 | } 55 | 56 | int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) 57 | { 58 | errno = ENOSYS; 59 | return -1; 60 | } 61 | 62 | int _access(const char *file, int mode) 63 | { 64 | errno = ENOSYS; 65 | return -1; 66 | } 67 | 68 | int _chdir(const char *path) 69 | { 70 | errno = ENOSYS; 71 | return -1; 72 | } 73 | 74 | int _chmod(const char *path, mode_t mode) 75 | { 76 | errno = ENOSYS; 77 | return -1; 78 | } 79 | 80 | int _chown(const char *path, uid_t owner, gid_t group) 81 | { 82 | errno = ENOSYS; 83 | return -1; 84 | } 85 | 86 | int _close(int file) 87 | { 88 | return -1; 89 | } 90 | 91 | int _execve(const char *name, char *const argv[], char *const env[]) 92 | { 93 | errno = ENOMEM; 94 | return -1; 95 | } 96 | 97 | void _exit(int exit_status) 98 | { 99 | *(volatile int *)EXIT_REG = exit_status; 100 | asm volatile("wfi"); 101 | } 102 | 103 | int _faccessat(int dirfd, const char *file, int mode, int flags) 104 | { 105 | errno = ENOSYS; 106 | return -1; 107 | } 108 | 109 | int _fork(void) 110 | { 111 | errno = EAGAIN; 112 | return -1; 113 | } 114 | 115 | int _fstat(int file, struct stat *st) 116 | { 117 | st->st_mode = S_IFCHR; 118 | return 0; 119 | // errno = -ENOSYS; 120 | // return -1; 121 | } 122 | 123 | int _fstatat(int dirfd, const char *file, struct stat *st, int flags) 124 | { 125 | errno = ENOSYS; 126 | return -1; 127 | } 128 | 129 | int _ftime(struct timeb *tp) 130 | { 131 | errno = ENOSYS; 132 | return -1; 133 | } 134 | 135 | char *_getcwd(char *buf, size_t size) 136 | { 137 | errno = -ENOSYS; 138 | return NULL; 139 | } 140 | 141 | int _getpid() 142 | { 143 | return 1; 144 | } 145 | 146 | int _gettimeofday(struct timeval *tp, void *tzp) 147 | { 148 | errno = -ENOSYS; 149 | return -1; 150 | } 151 | 152 | int _isatty(int file) 153 | { 154 | return (file == STDOUT_FILENO); 155 | } 156 | 157 | int _kill(int pid, int sig) 158 | { 159 | errno = EINVAL; 160 | return -1; 161 | } 162 | 163 | int _link(const char *old_name, const char *new_name) 164 | { 165 | errno = EMLINK; 166 | return -1; 167 | } 168 | 169 | off_t _lseek(int file, off_t ptr, int dir) 170 | { 171 | return 0; 172 | } 173 | 174 | int _lstat(const char *file, struct stat *st) 175 | { 176 | errno = ENOSYS; 177 | return -1; 178 | } 179 | 180 | int _open(const char *name, int flags, int mode) 181 | { 182 | return -1; 183 | } 184 | 185 | int _openat(int dirfd, const char *name, int flags, int mode) 186 | { 187 | errno = ENOSYS; 188 | return -1; 189 | } 190 | 191 | ssize_t _read(int file, void *ptr, size_t len) 192 | { 193 | return 0; 194 | } 195 | 196 | int _stat(const char *file, struct stat *st) 197 | { 198 | st->st_mode = S_IFCHR; 199 | return 0; 200 | // errno = ENOSYS; 201 | // return -1; 202 | } 203 | 204 | long _sysconf(int name) 205 | { 206 | 207 | return -1; 208 | } 209 | 210 | clock_t _times(struct tms *buf) 211 | { 212 | return -1; 213 | } 214 | 215 | int _unlink(const char *name) 216 | { 217 | errno = ENOENT; 218 | return -1; 219 | } 220 | 221 | int _utime(const char *path, const struct utimbuf *times) 222 | { 223 | errno = ENOSYS; 224 | return -1; 225 | } 226 | 227 | int _wait(int *status) 228 | { 229 | errno = ECHILD; 230 | return -1; 231 | } 232 | 233 | ssize_t _write(int file, const void *ptr, size_t len) 234 | { 235 | if (file != STDOUT_FILENO) { 236 | errno = ENOSYS; 237 | return -1; 238 | } 239 | 240 | const void *eptr = ptr + len; 241 | while (ptr != eptr) 242 | *(volatile int *)STDOUT_REG = *(char *)(ptr++); 243 | return len; 244 | } 245 | 246 | extern char __heap_start[]; 247 | extern char __heap_end[]; 248 | static char *brk = __heap_start; 249 | 250 | int _brk(void *addr) 251 | { 252 | brk = addr; 253 | return 0; 254 | } 255 | 256 | void *_sbrk(ptrdiff_t incr) 257 | { 258 | char *old_brk = brk; 259 | 260 | if (__heap_start == __heap_end) { 261 | return NULL; 262 | } 263 | 264 | if ((brk += incr) < __heap_end) { 265 | brk += incr; 266 | } else { 267 | brk = __heap_end; 268 | } 269 | return old_brk; 270 | } 271 | -------------------------------------------------------------------------------- /tb/prog/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(){ 4 | printf("hello world!\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tb/prog/vectors.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 ETH Zürich and University of Bologna 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .section .vectors, "ax" 18 | .option norvc 19 | vector_table: 20 | j sw_irq_handler 21 | j __no_irq_handler 22 | j __no_irq_handler 23 | j __no_irq_handler 24 | j __no_irq_handler 25 | j __no_irq_handler 26 | j __no_irq_handler 27 | j __no_irq_handler 28 | j __no_irq_handler 29 | j __no_irq_handler 30 | j __no_irq_handler 31 | j __no_irq_handler 32 | j __no_irq_handler 33 | j __no_irq_handler 34 | j __no_irq_handler 35 | j __no_irq_handler 36 | j __no_irq_handler 37 | j __no_irq_handler 38 | j __no_irq_handler 39 | j __no_irq_handler 40 | j __no_irq_handler 41 | j __no_irq_handler 42 | j __no_irq_handler 43 | j __no_irq_handler 44 | j __no_irq_handler 45 | j __no_irq_handler 46 | j __no_irq_handler 47 | j __no_irq_handler 48 | j __no_irq_handler 49 | j __no_irq_handler 50 | j __no_irq_handler 51 | j verification_irq_handler 52 | j __no_irq_handler 53 | j __no_irq_handler 54 | j __no_irq_handler 55 | j __no_irq_handler 56 | j __no_irq_handler 57 | j __no_irq_handler 58 | j __no_irq_handler 59 | j __no_irq_handler 60 | j __no_irq_handler 61 | j __no_irq_handler 62 | j __no_irq_handler 63 | j __no_irq_handler 64 | j __no_irq_handler 65 | j __no_irq_handler 66 | j __no_irq_handler 67 | j __no_irq_handler 68 | j __no_irq_handler 69 | j __no_irq_handler 70 | j __no_irq_handler 71 | j __no_irq_handler 72 | j __no_irq_handler 73 | j __no_irq_handler 74 | j __no_irq_handler 75 | j __no_irq_handler 76 | j __no_irq_handler 77 | j __no_irq_handler 78 | j __no_irq_handler 79 | j __no_irq_handler 80 | j __no_irq_handler 81 | j __no_irq_handler 82 | j __no_irq_handler 83 | j __no_irq_handler 84 | 85 | /* this is fixed to 0x8000, used for PULP_SECURE=0. We redirect this entry to the 86 | new vector table (which is at mtvec) */ 87 | /* .section .legacy_irq, "ax" */ 88 | /* j vector_table */ 89 | /* j __no_irq_handler */ 90 | /* j __no_irq_handler */ 91 | /* j __no_irq_handler */ 92 | 93 | .section .text.vecs 94 | /* exception handling */ 95 | __no_irq_handler: 96 | la a0, no_exception_handler_msg 97 | jal ra, puts 98 | j __no_irq_handler 99 | 100 | 101 | sw_irq_handler: 102 | csrr t0, mcause 103 | slli t0, t0, 1 /* shift off the high bit */ 104 | srli t0, t0, 1 105 | li t1, 2 106 | beq t0, t1, handle_illegal_insn 107 | li t1, 11 108 | beq t0, t1, handle_ecall 109 | li t1, 3 110 | beq t0, t1, handle_ebreak 111 | j handle_unknown 112 | 113 | handle_ecall: 114 | la a0, ecall_msg 115 | jal ra, puts 116 | j end_handler 117 | 118 | handle_ebreak: 119 | la a0, ebreak_msg 120 | jal ra, puts 121 | j end_handler 122 | 123 | handle_illegal_insn: 124 | la a0, illegal_insn_msg 125 | jal ra, puts 126 | j end_handler 127 | 128 | handle_unknown: 129 | la a0, unknown_msg 130 | jal ra, puts 131 | j end_handler 132 | 133 | end_handler: 134 | csrr a0, mepc 135 | addi a0, a0, 4 136 | csrw mepc, a0 137 | mret 138 | /* this interrupt can be generated for verification purposes, random or when the PC is equal to a given value*/ 139 | verification_irq_handler: 140 | mret 141 | 142 | .section .rodata 143 | illegal_insn_msg: 144 | .string "illegal instruction exception handler entered\n" 145 | ecall_msg: 146 | .string "ecall exception handler entered\n" 147 | ebreak_msg: 148 | .string "ebreak exception handler entered\n" 149 | unknown_msg: 150 | .string "unknown exception handler entered\n" 151 | no_exception_handler_msg: 152 | .string "no exception handler installed\n" 153 | -------------------------------------------------------------------------------- /tb/remote_bitbang/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.so -------------------------------------------------------------------------------- /tb/remote_bitbang/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 ETH Zurich and University of Bologna 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # 16 | # Author: Robert Balas (balasr@iis.ee.ethz.ch) 17 | 18 | CFLAGS = -Wall -Wextra -Wno-missing-field-initializers \ 19 | -Wno-unused-function -Wno-missing-braces \ 20 | -O2 -g -march=native \ 21 | -DENABLE_LOGGING -DNDEBUG 22 | CFLAGS_DBG = 23 | # we need gnu11 and no-strict-aliasing 24 | ALL_CFLAGS = -std=gnu11 -fno-strict-aliasing $(CFLAGS) 25 | ALL_CFLAGS_DBG = -std=gnu11 -Wall -Wextra -Wno-missing-field-initializers \ 26 | -Wno-unused-function -Wno-missing-braces \ 27 | -O0 -g -fno-strict-aliasing \ 28 | -fsanitize=address -fno-omit-frame-pointer \ 29 | -DENABLE_LOGGING -DENABLE_DEBUG $(CFLAGS_DBG)\ 30 | # -fsanitize=undefined \ 31 | # -fsanitize=leak \ 32 | 33 | 34 | # TODO: better path? 35 | LIB_DIRS = 36 | LIBS = 37 | INCLUDE_DIRS = ./ 38 | 39 | 40 | LDFLAGS = $(addprefix -L, $(LIB_DIRS)) 41 | LDLIBS = $(addprefix -l, $(LIBS)) 42 | 43 | SRCS = remote_bitbang.c sim_jtag.c 44 | OBJS = $(SRCS:.c=.o) 45 | INCLUDES = $(addprefix -I, $(INCLUDE_DIRS)) 46 | 47 | HEADERS = $(wildcard *.h) 48 | 49 | # libs 50 | SV_LIB = librbs.so 51 | 52 | # header file dependency generation 53 | DEPDIR := .d 54 | DEPDIRS := $(addsuffix /$(DEPDIR),.) 55 | # goal: make gcc put a dependency file called obj.Td (derived from subdir/obj.o) 56 | # in subdir/.d/ 57 | DEPFLAGS = -MT $@ -MMD -MP -MF $(@D)/$(DEPDIR)/$(patsubst %.o,%.Td,$(@F)) 58 | # move gcc generated header dependencies to DEPDIR 59 | # this rename step is here to make the header dependency generation "atomic" 60 | POSTCOMPILE = @mv -f $(@D)/$(DEPDIR)/$(patsubst %.o,%.Td,$(@F)) \ 61 | $(@D)/$(DEPDIR)/$(patsubst %.o,%.d,$(@F)) && touch $@ 62 | 63 | # GNU recommendations for install targets 64 | prefix = /usr/local 65 | exec_prefix = $(prefix) 66 | bindir = $(exec_prefix)/bin 67 | libdir = $(exec_prefix)/lib 68 | includedir = $(prefix)/include 69 | 70 | INSTALL = install 71 | INSTALL_PROGRAM = $(INSTALL) 72 | INSTALL_DATA = ${INSTALL} -m 644 73 | 74 | CTAGS = ctags 75 | 76 | # compilation targets 77 | all: sv-lib 78 | 79 | debug: ALL_CFLAGS = $(ALL_CFLAGS_DBG) 80 | debug: all 81 | 82 | sv-lib: ALL_CFLAGS += -fPIC 83 | sv-lib: $(SV_LIB) 84 | 85 | #compilation boilerplate 86 | $(SV_LIB): $(OBJS) 87 | $(LD) -shared -E --exclude-libs ALL -o $(SV_LIB) $(LDFLAGS) \ 88 | $(OBJS) $(LDLIBS) 89 | 90 | # $@ = name of target 91 | # $< = first dependency 92 | %.o: %.c 93 | %.o: %.c $(DEPDIR)/%.d $(DEPDIRS) 94 | $(CC) $(DEPFLAGS) $(ALL_CFLAGS) $(INCLUDES) $(LDFLAGS) \ 95 | -c $(CPPFLAGS) $< -o $@ $(LDLIBS) 96 | $(POSTCOMPILE) 97 | 98 | # check if we need to create the dependencies folders (gcc doesn't) 99 | $(DEPDIRS): 100 | $(shell mkdir -p $(DEPDIRS) > /dev/null) 101 | # make won't fail if the dependency file doesn't exist 102 | $(addsuffix /$(DEPDIR)/%.d,. main benchmark test dpi): ; 103 | 104 | # prevent automatic deletion as intermediate file 105 | .PRECIOUS: $(addsuffix /$(DEPDIR)/%.d,. main benchmark test dpi) 106 | 107 | # emacs tag generation 108 | .PHONY: TAGS 109 | TAGS: 110 | $(CTAGS) -R -e -h=".c.h" --tag-relative=always \ 111 | . $(LIB_DIRS) $(INCLUDE_DIRS) $(BINUTILS_PATH)/bfd 112 | 113 | # TODO: missing install targets 114 | # cleanup 115 | .PHONY: clean 116 | clean: 117 | rm -rf $(SV_LIB) $(OBJS) $(DEPDIRS) 118 | 119 | .PHONY: distclean 120 | distclean: clean 121 | rm -f TAGS 122 | 123 | # include auto generated header dependency information 124 | include $(wildcard $(addsuffix /*.d,$(DEPDIRS))) 125 | -------------------------------------------------------------------------------- /tb/remote_bitbang/rbs_test.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2020 ETH Zurich and University of Bologna 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | * 15 | * 16 | * Author: Robert Balas (balasr@iis.ee.ethz.ch) 17 | */ 18 | 19 | #include 20 | #include "remote_bitbang.h" 21 | 22 | int main() 23 | { 24 | unsigned char jtag_TCK, jtag_TMS, jtag_TDI, jtag_TRSTn; 25 | unsigned char jtag_TDO = 0; 26 | 27 | printf("calling rbs_init\n"); 28 | int v = rbs_init(0); 29 | 30 | printf("tick 1\n"); 31 | rbs_tick(&jtag_TCK, &jtag_TMS, &jtag_TDI, &jtag_TRSTn, jtag_TDO); 32 | printf("jtag exit is %d\n", rbs_done()); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tb/remote_bitbang/remote_bitbang.c: -------------------------------------------------------------------------------- 1 | // See LICENSE.Berkeley for license details. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "remote_bitbang.h" 15 | 16 | //Public globals, declared in remote_bitbang.h 17 | 18 | int rbs_err; 19 | 20 | unsigned char tck; 21 | unsigned char tms; 22 | unsigned char tdi; 23 | unsigned char trstn; 24 | unsigned char tdo; 25 | unsigned char quit; 26 | 27 | int socket_fd; 28 | int client_fd; 29 | 30 | //static const ssize_t buf_size = 64 * 1024; 31 | char recv_buf[64 * 1024]; 32 | ssize_t recv_start, recv_end; 33 | 34 | int rbs_init(uint16_t port) 35 | { 36 | socket_fd = 0; 37 | client_fd = 0; 38 | recv_start = 0; 39 | recv_end = 0; 40 | rbs_err = 0; 41 | 42 | socket_fd = socket(AF_INET, SOCK_STREAM, 0); 43 | if (socket_fd == -1) { 44 | fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n", 45 | strerror(errno), errno); 46 | abort(); 47 | } 48 | 49 | fcntl(socket_fd, F_SETFL, O_NONBLOCK); 50 | int reuseaddr = 1; 51 | if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, 52 | sizeof(int)) == -1) { 53 | fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n", 54 | strerror(errno), errno); 55 | abort(); 56 | } 57 | 58 | struct sockaddr_in addr; 59 | memset(&addr, 0, sizeof(addr)); 60 | addr.sin_family = AF_INET; 61 | addr.sin_addr.s_addr = INADDR_ANY; 62 | addr.sin_port = htons(port); 63 | 64 | if (bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 65 | fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n", 66 | strerror(errno), errno); 67 | abort(); 68 | } 69 | 70 | if (listen(socket_fd, 1) == -1) { 71 | fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n", 72 | strerror(errno), errno); 73 | abort(); 74 | } 75 | 76 | socklen_t addrlen = sizeof(addr); 77 | if (getsockname(socket_fd, (struct sockaddr *)&addr, &addrlen) == -1) { 78 | fprintf(stderr, "remote_bitbang getsockname failed: %s (%d)\n", 79 | strerror(errno), errno); 80 | abort(); 81 | } 82 | 83 | tck = 1; 84 | tms = 1; 85 | tdi = 1; 86 | trstn = 1; 87 | quit = 0; 88 | 89 | fprintf(stderr, "JTAG remote bitbang server is ready\n"); 90 | fprintf(stderr, "Listening on port %d\n", ntohs(addr.sin_port)); 91 | return 1; 92 | } 93 | 94 | void rbs_accept() 95 | { 96 | fprintf(stderr, "Attempting to accept client socket\n"); 97 | int again = 1; 98 | while (again != 0) { 99 | client_fd = accept(socket_fd, NULL, NULL); 100 | if (client_fd == -1) { 101 | if (errno == EAGAIN) { 102 | // No client waiting to connect right now. 103 | } else { 104 | fprintf(stderr, "failed to accept on socket: %s (%d)\n", 105 | strerror(errno), errno); 106 | again = 0; 107 | abort(); 108 | } 109 | } else { 110 | fcntl(client_fd, F_SETFL, O_NONBLOCK); 111 | fprintf(stderr, "Accepted successfully."); 112 | again = 0; 113 | } 114 | } 115 | } 116 | 117 | void rbs_tick(unsigned char *jtag_tck, unsigned char *jtag_tms, 118 | unsigned char *jtag_tdi, unsigned char *jtag_trstn, 119 | unsigned char jtag_tdo) 120 | { 121 | if (client_fd > 0) { 122 | tdo = jtag_tdo; 123 | rbs_execute_command(); 124 | } else { 125 | rbs_accept(); 126 | } 127 | 128 | *jtag_tck = tck; 129 | *jtag_tms = tms; 130 | *jtag_tdi = tdi; 131 | *jtag_trstn = trstn; 132 | } 133 | 134 | void rbs_reset() 135 | { 136 | trstn = 0; 137 | } 138 | 139 | void rbs_set() 140 | { 141 | trstn = 1; 142 | } 143 | 144 | void rbs_set_pins(char _tck, char _tms, char _tdi) 145 | { 146 | tck = _tck; 147 | tms = _tms; 148 | tdi = _tdi; 149 | } 150 | 151 | void rbs_execute_command() 152 | { 153 | char command; 154 | int again = 1; 155 | while (again) { 156 | ssize_t num_read = read(client_fd, &command, sizeof(command)); 157 | if (num_read == -1) { 158 | if (errno == EAGAIN) { 159 | // We'll try again the next call. 160 | if (VERBOSE) 161 | fprintf( 162 | stderr, 163 | "Received no command. Will try again on the next call\n"); 164 | } else { 165 | fprintf(stderr, 166 | "remote_bitbang failed to read on socket: %s (%d)\n", 167 | strerror(errno), errno); 168 | again = 0; 169 | abort(); 170 | } 171 | } else if (num_read == 0) { 172 | fprintf(stderr, "No command received. Stopping further reads.\n"); 173 | // again = 1; 174 | return; 175 | } else { 176 | again = 0; 177 | } 178 | } 179 | 180 | int dosend = 0; 181 | 182 | char tosend = '?'; 183 | 184 | switch (command) { 185 | case 'B': 186 | if (VERBOSE) 187 | fprintf(stderr, "*BLINK*\n"); 188 | break; 189 | case 'b': 190 | if (VERBOSE) 191 | fprintf(stderr, "blink off\n"); 192 | break; 193 | case 'r': 194 | if (VERBOSE) 195 | fprintf(stderr, "r-reset\n"); 196 | rbs_set(); //r-reset command deasserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt 197 | break; 198 | case 's': 199 | if (VERBOSE) 200 | fprintf(stderr, "s-reset\n"); 201 | rbs_set(); //s-reset command deasserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt 202 | break; 203 | case 't': 204 | if (VERBOSE) 205 | fprintf(stderr, "t-reset\n"); 206 | rbs_reset(); //t-reset command asserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt 207 | break; 208 | case 'u': 209 | if (VERBOSE) 210 | fprintf(stderr, "u-reset\n"); 211 | rbs_reset(); //u-reset command asserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt 212 | break; 213 | case '0': 214 | if (VERBOSE) 215 | fprintf(stderr, "Write 0 0 0\n"); 216 | rbs_set_pins(0, 0, 0); 217 | break; 218 | case '1': 219 | if (VERBOSE) 220 | fprintf(stderr, "Write 0 0 1\n"); 221 | rbs_set_pins(0, 0, 1); 222 | break; 223 | case '2': 224 | if (VERBOSE) 225 | fprintf(stderr, "Write 0 1 0\n"); 226 | rbs_set_pins(0, 1, 0); 227 | break; 228 | case '3': 229 | if (VERBOSE) 230 | fprintf(stderr, "Write 0 1 1\n"); 231 | rbs_set_pins(0, 1, 1); 232 | break; 233 | case '4': 234 | if (VERBOSE) 235 | fprintf(stderr, "Write 1 0 0\n"); 236 | rbs_set_pins(1, 0, 0); 237 | break; 238 | case '5': 239 | if (VERBOSE) 240 | fprintf(stderr, "Write 1 0 1\n"); 241 | rbs_set_pins(1, 0, 1); 242 | break; 243 | case '6': 244 | if (VERBOSE) 245 | fprintf(stderr, "Write 1 1 0\n"); 246 | rbs_set_pins(1, 1, 0); 247 | break; 248 | case '7': 249 | if (VERBOSE) 250 | fprintf(stderr, "Write 1 1 1\n"); 251 | rbs_set_pins(1, 1, 1); 252 | break; 253 | case 'R': 254 | if (VERBOSE) 255 | fprintf(stderr, "Read req\n"); 256 | dosend = 1; 257 | tosend = tdo ? '1' : '0'; 258 | break; 259 | case 'Q': 260 | if (VERBOSE) 261 | fprintf(stderr, "Quit req\n"); 262 | quit = 1; 263 | break; 264 | default: 265 | fprintf(stderr, "remote_bitbang got unsupported command '%c'\n", 266 | command); 267 | } 268 | if (dosend) { 269 | while (1) { 270 | ssize_t bytes = write(client_fd, &tosend, sizeof(tosend)); 271 | if (bytes == -1) { 272 | fprintf(stderr, "failed to write to socket: %s (%d)\n", 273 | strerror(errno), errno); 274 | abort(); 275 | } 276 | if (bytes > 0) { 277 | break; 278 | } 279 | } 280 | } 281 | 282 | if (quit) { 283 | fprintf(stderr, "Remote end disconnected\n"); 284 | close(client_fd); 285 | client_fd = 0; 286 | } 287 | } 288 | 289 | unsigned char rbs_done() 290 | { 291 | return quit; 292 | } 293 | 294 | int rbs_exit_code() 295 | { 296 | return rbs_err; 297 | } 298 | -------------------------------------------------------------------------------- /tb/remote_bitbang/remote_bitbang.h: -------------------------------------------------------------------------------- 1 | // See LICENSE.Berkeley for license details. 2 | 3 | #ifndef REMOTE_BITBANG_H 4 | #define REMOTE_BITBANG_H 5 | 6 | #include 7 | #include 8 | 9 | #define VERBOSE 0 10 | 11 | extern int rbs_err; 12 | 13 | extern unsigned char tck; 14 | extern unsigned char tms; 15 | extern unsigned char tdi; 16 | extern unsigned char trstn; 17 | extern unsigned char tdo; 18 | extern unsigned char quit; 19 | 20 | extern int socket_fd; 21 | extern int client_fd; 22 | 23 | //static const ssize_t buf_size = 64 * 1024; 24 | extern char recv_buf[64 * 1024]; 25 | extern ssize_t recv_start, recv_end; 26 | 27 | // Create a new server, listening for connections from localhost on the given 28 | // port. 29 | int rbs_init(uint16_t port); 30 | 31 | // Do a bit of work. 32 | void rbs_tick(unsigned char *jtag_tck, unsigned char *jtag_tms, 33 | unsigned char *jtag_tdi, unsigned char *jtag_trstn, 34 | unsigned char jtag_tdo); 35 | 36 | unsigned char rbs_done(); 37 | 38 | int rbs_exit_code(); 39 | 40 | // Check for a client connecting, and accept if there is one. 41 | void rbs_accept(); 42 | // Execute any commands the client has for us. 43 | // But we only execute 1 because we need time for the 44 | // simulation to run. 45 | void rbs_execute_command(); 46 | 47 | void rbs_reset(); //Assert TRST 48 | void rbs_set(); //Deassert TRST 49 | 50 | void rbs_set_pins(char _tck, char _tms, char _tdi); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /tb/remote_bitbang/sim_jtag.c: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | 3 | #include 4 | #include 5 | #include 6 | #include "remote_bitbang.h" 7 | 8 | int init = 0; 9 | 10 | int jtag_tick(int port, unsigned char *jtag_TCK, unsigned char *jtag_TMS, 11 | unsigned char *jtag_TDI, unsigned char *jtag_TRSTn, 12 | unsigned char jtag_TDO) 13 | 14 | { 15 | if (!init) { 16 | if (port < 0 || port > UINT16_MAX) 17 | fprintf(stderr, "Port number of out range: %d\n", port); 18 | init = rbs_init(port); 19 | } 20 | 21 | rbs_tick(jtag_TCK, jtag_TMS, jtag_TDI, jtag_TRSTn, jtag_TDO); 22 | if (VERBOSE) 23 | fprintf( 24 | stderr, 25 | "Tick with: TCK=%hhd TMS=%hhd TDI=%hhd TRSTn=%hhd --> TDO=%hhd\n", 26 | *jtag_TCK, *jtag_TMS, *jtag_TDI, *jtag_TRSTn, jtag_TDO); 27 | 28 | return rbs_done() ? (rbs_exit_code() << 1 | 1) : 0; 29 | } 30 | -------------------------------------------------------------------------------- /tb/tb_top.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Embecosm Limited 2 | // Copyright 2018 Robert Balas 3 | // Copyright 2020 ETH Zurich and University of Bologna. 4 | // Copyright and related rights are licensed under the Solderpad Hardware 5 | // License, Version 0.51 (the "License"); you may not use this file except in 6 | // compliance with the License. You may obtain a copy of the License at 7 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 8 | // or agreed to in writing, software, hardware and materials distributed under 9 | // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | // specific language governing permissions and limitations under the License. 12 | 13 | // Top level wrapper for a RI5CY testbench 14 | // Contributor: Robert Balas 15 | // Jeremy Bennett 16 | 17 | module tb_top #( 18 | parameter int unsigned INSTR_RDATA_WIDTH = 32, 19 | parameter int unsigned RAM_ADDR_WIDTH = 22, 20 | parameter logic [31:0] BOOT_ADDR = 'h1A00_0180, 21 | parameter bit JTAG_BOOT = 1, 22 | parameter int unsigned OPENOCD_PORT = 9999 23 | ); 24 | 25 | // comment to record execution trace 26 | //`define TRACE_EXECUTION 27 | 28 | const time CLK_PHASE_HI = 5ns; 29 | const time CLK_PHASE_LO = 5ns; 30 | const time CLK_PERIOD = CLK_PHASE_HI + CLK_PHASE_LO; 31 | const time STIM_APPLICATION_DEL = CLK_PERIOD * 0.1; 32 | const time RESP_ACQUISITION_DEL = CLK_PERIOD * 0.9; 33 | const time RESET_DEL = STIM_APPLICATION_DEL; 34 | const int RESET_WAIT_CYCLES = 4; 35 | 36 | // clock and reset for tb 37 | logic clk = 'b1; 38 | logic rst_n = 'b0; 39 | 40 | // testbench result 41 | logic tests_passed; 42 | logic tests_failed; 43 | 44 | // signals for ri5cy 45 | logic fetch_enable; 46 | 47 | 48 | // make the core start fetching instruction immediately 49 | assign fetch_enable = '1; 50 | 51 | // allow vcd dump 52 | initial begin: dump_vars 53 | if ($test$plusargs("vcd")) begin 54 | $dumpfile("riscy_tb.vcd"); 55 | $dumpvars(0, tb_top); 56 | end 57 | `ifdef QUESTA 58 | if ($test$plusargs("wlfdump")) begin 59 | $wlfdumpvars(0, tb_top); 60 | end 61 | `endif 62 | end 63 | 64 | // we either load the provided firmware or execute a small test program that 65 | // doesn't do more than an infinite loop with some I/O 66 | initial begin: load_prog 67 | automatic logic [1023:0] firmware; 68 | automatic int prog_size = 6; 69 | 70 | if($value$plusargs("firmware=%s", firmware)) begin 71 | if($test$plusargs("verbose")) 72 | $display("[TESTBENCH] %t: loading firmware %0s ...", 73 | $time, firmware); 74 | $readmemh(firmware, tb_test_env_i.mm_ram_i.dp_ram_i.mem); 75 | 76 | end else begin 77 | $display("No firmware specified"); 78 | end 79 | end 80 | 81 | // clock generation 82 | initial begin: clock_gen 83 | forever begin 84 | #CLK_PHASE_HI clk = 1'b0; 85 | #CLK_PHASE_LO clk = 1'b1; 86 | end 87 | end: clock_gen 88 | 89 | // reset generation 90 | initial begin: reset_gen 91 | rst_n = 1'b0; 92 | 93 | // wait a few cycles 94 | repeat (RESET_WAIT_CYCLES) begin 95 | @(posedge clk); //TODO: was posedge, see below 96 | end 97 | 98 | // start running 99 | #RESET_DEL rst_n = 1'b1; 100 | if($test$plusargs("verbose")) 101 | $display("reset deasserted", $time); 102 | 103 | end: reset_gen 104 | 105 | // set timing format 106 | initial begin: timing_format 107 | $timeformat(-9, 0, "ns", 9); 108 | end: timing_format 109 | 110 | // check if we succeded 111 | always_ff @(posedge clk, negedge rst_n) begin 112 | if (tests_passed) begin 113 | $display("Exit Success"); 114 | $finish; 115 | end 116 | if (tests_failed) begin 117 | $display("Exit FAILURE"); 118 | $finish; 119 | end 120 | end 121 | 122 | // wrapper for riscv, the memory system and stdout peripheral 123 | tb_test_env #( 124 | .INSTR_RDATA_WIDTH (INSTR_RDATA_WIDTH), 125 | .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH), 126 | .BOOT_ADDR (BOOT_ADDR), 127 | .JTAG_BOOT (JTAG_BOOT), 128 | .OPENOCD_PORT (OPENOCD_PORT) 129 | ) tb_test_env_i( 130 | .clk_i ( clk ), 131 | .rst_ni ( rst_n ), 132 | .fetch_enable_i ( fetch_enable ), 133 | .tests_passed_o ( tests_passed ), 134 | .tests_failed_o ( tests_failed ) 135 | ); 136 | 137 | endmodule // tb_top 138 | -------------------------------------------------------------------------------- /tb/tb_top_verilator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Robert Balas 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | // Top level wrapper for a verilator RI5CY testbench 17 | // Contributor: Robert Balas 18 | 19 | #include "svdpi.h" 20 | #include "Vtb_top_verilator__Dpi.h" 21 | #include "Vtb_top_verilator.h" 22 | #include "verilated_vcd_c.h" 23 | #include "verilated.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | void dump_memory(); 34 | double sc_time_stamp(); 35 | 36 | static vluint64_t t = 0; 37 | Vtb_top_verilator *top; 38 | 39 | int main(int argc, char **argv, char **env) 40 | { 41 | Verilated::commandArgs(argc, argv); 42 | Verilated::traceEverOn(true); 43 | top = new Vtb_top_verilator(); 44 | 45 | svSetScope(svGetScopeFromName( 46 | "TOP.tb_top_verilator.mm_ram_i.dp_ram_i")); 47 | Verilated::scopesDump(); 48 | 49 | #ifdef VCD_TRACE 50 | VerilatedVcdC *tfp = new VerilatedVcdC; 51 | top->trace(tfp, 99); 52 | tfp->open("verilator_tb.vcd"); 53 | #endif 54 | top->fetch_enable_i = 1; 55 | top->clk_i = 0; 56 | top->rst_ni = 0; 57 | 58 | top->eval(); 59 | dump_memory(); 60 | 61 | while (!Verilated::gotFinish()) { 62 | if (t > 40) 63 | top->rst_ni = 1; 64 | top->clk_i = !top->clk_i; 65 | top->eval(); 66 | #ifdef VCD_TRACE 67 | tfp->dump(t); 68 | #endif 69 | t += 5; 70 | } 71 | #ifdef VCD_TRACE 72 | tfp->close(); 73 | #endif 74 | delete top; 75 | exit(0); 76 | } 77 | 78 | double sc_time_stamp() 79 | { 80 | return t; 81 | } 82 | 83 | void dump_memory() 84 | { 85 | errno = 0; 86 | std::ofstream mem_file; 87 | svLogicVecVal addr = {0}; 88 | 89 | mem_file.exceptions(std::ofstream::failbit | std::ofstream::badbit); 90 | try { 91 | mem_file.open("memory_dump.bin"); 92 | for (size_t i = 0; i < 1048576; i++) { 93 | addr.aval = i; 94 | uint32_t val = read_byte(&addr); 95 | mem_file << std::setfill('0') << std::setw(2) << std::hex << val 96 | << std::endl; 97 | } 98 | mem_file.close(); 99 | 100 | std::cout << "finished dumping memory" << std::endl; 101 | 102 | } catch (std::ofstream::failure e) { 103 | std::cerr << "exception opening/reading/closing file memory_dump.bin\n"; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /tb/unused/SimDTM.sv: -------------------------------------------------------------------------------- 1 | // See LICENSE.SiFive for license details. 2 | //VCS coverage exclude_file 3 | 4 | import "DPI-C" function int debug_tick 5 | ( 6 | output bit debug_req_valid, 7 | input bit debug_req_ready, 8 | output int debug_req_bits_addr, 9 | output int debug_req_bits_op, 10 | output int debug_req_bits_data, 11 | 12 | input bit debug_resp_valid, 13 | output bit debug_resp_ready, 14 | input int debug_resp_bits_resp, 15 | input int debug_resp_bits_data 16 | ); 17 | 18 | module SimDTM( 19 | input clk, 20 | input reset, 21 | 22 | output debug_req_valid, 23 | input debug_req_ready, 24 | output [ 6:0] debug_req_bits_addr, 25 | output [ 1:0] debug_req_bits_op, 26 | output [31:0] debug_req_bits_data, 27 | 28 | input debug_resp_valid, 29 | output debug_resp_ready, 30 | input [ 1:0] debug_resp_bits_resp, 31 | input [31:0] debug_resp_bits_data, 32 | 33 | output [31:0] exit 34 | ); 35 | 36 | bit r_reset; 37 | 38 | wire #0.1 __debug_req_ready = debug_req_ready; 39 | wire #0.1 __debug_resp_valid = debug_resp_valid; 40 | wire [31:0] #0.1 __debug_resp_bits_resp = {30'b0, debug_resp_bits_resp}; 41 | wire [31:0] #0.1 __debug_resp_bits_data = debug_resp_bits_data; 42 | 43 | bit __debug_req_valid; 44 | int __debug_req_bits_addr; 45 | int __debug_req_bits_op; 46 | int __debug_req_bits_data; 47 | bit __debug_resp_ready; 48 | int __exit; 49 | 50 | assign #0.1 debug_req_valid = __debug_req_valid; 51 | assign #0.1 debug_req_bits_addr = __debug_req_bits_addr[6:0]; 52 | assign #0.1 debug_req_bits_op = __debug_req_bits_op[1:0]; 53 | assign #0.1 debug_req_bits_data = __debug_req_bits_data[31:0]; 54 | assign #0.1 debug_resp_ready = __debug_resp_ready; 55 | assign #0.1 exit = __exit; 56 | 57 | always @(posedge clk) 58 | begin 59 | r_reset <= reset; 60 | if (reset || r_reset) 61 | begin 62 | __debug_req_valid = 0; 63 | __debug_resp_ready = 0; 64 | __exit = 0; 65 | end 66 | else 67 | begin 68 | __exit = debug_tick( 69 | __debug_req_valid, 70 | __debug_req_ready, 71 | __debug_req_bits_addr, 72 | __debug_req_bits_op, 73 | __debug_req_bits_data, 74 | __debug_resp_valid, 75 | __debug_resp_ready, 76 | __debug_resp_bits_resp, 77 | __debug_resp_bits_data 78 | ); 79 | end 80 | end 81 | endmodule 82 | -------------------------------------------------------------------------------- /tb/veri-run-openocd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """Launch riscv-dbg testbench and connect to openocd""" 3 | 4 | import sys 5 | from subprocess import Popen 6 | from subprocess import PIPE, STDOUT 7 | from os import getenv 8 | import shlex 9 | 10 | if __name__ == '__main__': 11 | veri_proc = Popen(shlex.split('make veri-run'), 12 | stdin=PIPE, stdout=PIPE, stderr=STDOUT, 13 | universal_newlines=True) 14 | for line in veri_proc.stdout: 15 | print(line, end='') 16 | if 'Listening on port' in line: 17 | print('Starting OpenOCD') 18 | break 19 | elif 'failed to bind socket' in line: 20 | print("Try 'killall testbench_verilator'", file=sys.stderr) 21 | exit(1) 22 | 23 | # try a few paths where openocd could be 24 | openocd = getenv('OPENOCD') 25 | if not openocd: 26 | openocd = getenv('RISCV') 27 | if openocd: 28 | openocd += '/bin/openocd' 29 | if not openocd: 30 | openocd = 'openocd' 31 | 32 | openocd_script = getenv('OPENOCD_SCRIPT') 33 | if not openocd_script: 34 | openocd_script = 'dm_compliance_test.cfg' 35 | 36 | print("Using '" + openocd) 37 | openocd_proc = Popen(shlex.split(openocd + ' -f ' + openocd_script), 38 | stdin=PIPE, stdout=PIPE, stderr=STDOUT, 39 | universal_newlines=True) 40 | print('Launched OpenOCD') 41 | 42 | ret = 1 43 | for line in openocd_proc.stdout: 44 | print(line, end='') 45 | if 'ALL TESTS PASSED' in line: 46 | ret = 0 47 | 48 | # Our spawned processes should have terminated by now. If not, we have to go after 49 | # them with the hammer (openocd likes to ignore sigterms when it gets stuck) 50 | if not openocd_proc.poll(): 51 | openocd_proc.kill() 52 | 53 | if not veri_proc.poll(): 54 | veri_proc.kill() 55 | 56 | exit(ret) 57 | -------------------------------------------------------------------------------- /tb/vsim_batch.tcl: -------------------------------------------------------------------------------- 1 | # Copyright 2020 ETH Zurich and University of Bologna. 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | # Author: Robert Balas (balasr@student.ethz.ch) 12 | # Description: TCL scripts to facilitate simulations 13 | 14 | set NoQuitOnFinish 1 15 | run -all 16 | -------------------------------------------------------------------------------- /tb/vsim_gui.tcl: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich and University of Bologna. 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | # Author: Robert Balas (balasr@student.ethz.ch) 12 | # Description: TCL scripts to facilitate simulations 13 | 14 | set NoQuitOnFinish 1 15 | source waves.tcl 16 | run -all 17 | -------------------------------------------------------------------------------- /tb/waves.tcl: -------------------------------------------------------------------------------- 1 | # Copyright 2020 ETH Zurich and University of Bologna. 2 | # Copyright and related rights are licensed under the Solderpad Hardware 3 | # License, Version 0.51 (the "License"); you may not use this file except in 4 | # compliance with the License. You may obtain a copy of the License at 5 | # http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | # or agreed to in writing, software, hardware and materials distributed under 7 | # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | # specific language governing permissions and limitations under the License. 10 | 11 | # Author: Robert Balas (balasr@iis.ee.ethz.ch) 12 | # Description: TCL scripts to facilitate simulations 13 | # catch { 14 | # if {$trdb_all ne ""} { 15 | # foreach inst $trdb_all { 16 | # add wave -group [file tail $inst] $inst/* 17 | # } 18 | # } 19 | # } err 20 | 21 | # if {$err ne ""} { 22 | # puts "\[TCL\]: Suppressed error: $err" 23 | # } 24 | 25 | # add fc 26 | set rvcores [find instances -recursive -bydu cv32e40p_core -nodu] 27 | set tb_top [find instances -recursive -bydu tb_top -nod] 28 | set mm_ram [find instances -recursive -bydu mm_ram -nod] 29 | set dp_ram [find instances -recursive -bydu dp_ram -nod] 30 | 31 | if {$tb_top ne ""} { 32 | foreach inst $tb_top { 33 | add wave -group [file tail $inst] $inst/* 34 | } 35 | } 36 | 37 | if {$mm_ram ne ""} { 38 | foreach inst $mm_ram { 39 | add wave -group [file tail $inst] $inst/* 40 | } 41 | } 42 | 43 | if {$dp_ram ne ""} { 44 | foreach inst $dp_ram { 45 | add wave -group [file tail $inst] $inst/* 46 | } 47 | } 48 | 49 | if {$rvcores ne ""} { 50 | add wave -group "Core" $rvcores/* 51 | add wave -group "IF Stage" -group "Prefetch" $rvcores/if_stage_i/prefetch_buffer_i/* 52 | add wave -group "IF Stage" -group "Prefetch" -group "FIFO" $rvcores/if_stage_i/prefetch_buffer_i/fifo_i/* 53 | add wave -group "IF Stage" -group "Prefetch" -group "OBI" $rvcores/if_stage_i/prefetch_buffer_i/instruction_obi_i/* 54 | add wave -group "IF Stage" $rvcores/if_stage_i/* 55 | add wave -group "Aligner" $rvcores/if_stage_i/aligner_i/* 56 | add wave -group "RVCDecoder" $rvcores/if_stage_i/compressed_decoder_i/* 57 | add wave -group "ID Stage" $rvcores/id_stage_i/* 58 | add wave -group "RF" $rvcores/id_stage_i/register_file_i/mem 59 | add wave -group "RF_FP" $rvcores/id_stage_i/register_file_i/mem_fp 60 | add wave -group "Decoder" $rvcores/id_stage_i/decoder_i/* 61 | add wave -group "Controller" $rvcores/id_stage_i/controller_i/* 62 | add wave -group "Int Ctrl" $rvcores/id_stage_i/int_controller_i/* 63 | add wave -group "EX Stage" -group "ALU" $rvcores/ex_stage_i/alu_i/* 64 | add wave -group "EX Stage" -group "ALU_DIV" $rvcores/ex_stage_i/alu_i/alu_div_i/* 65 | add wave -group "EX Stage" -group "MUL" $rvcores/ex_stage_i/mult_i/* 66 | add wave -group "EX Stage" $rvcores/ex_stage_i/* 67 | add wave -group "LSU" $rvcores/load_store_unit_i/* 68 | add wave -group "CSR" $rvcores/cs_registers_i/* 69 | } 70 | 71 | # add dm 72 | set dm [find instances -recursive -bydu dm_top -nodu] 73 | set dm_mem [find instances -recursive -bydu dm_mem -nodu] 74 | set dm_csrs [find instances -recursive -bydu dm_csrs -nodu] 75 | set dm_sba [find instances -recursive -bydu dm_sba -nodu] 76 | 77 | if {$dm ne ""} { 78 | add wave -group "DM" $dm/* 79 | } 80 | if {$dm_mem ne ""} { 81 | add wave -group "DM" -group "dm_mem" $dm_mem/* 82 | } 83 | if {$dm_csrs ne ""} { 84 | add wave -group "DM" -group "dm_csrs" $dm_csrs/* 85 | } 86 | if {$dm_sba ne ""} { 87 | add wave -group "DM" -group "dm_sba" $dm_sba/* 88 | } 89 | 90 | # add dmi_jtag 91 | set dmi [find instances -recursive -bydu dmi_jtag -nodu] 92 | set dmi_tap [find instances -recursive -bydu dmi_jtag_tap -nodu] 93 | 94 | if {$dmi ne ""} { 95 | add wave -group "DMI" $dmi/* 96 | } 97 | if {$dmi_tap ne ""} { 98 | add wave -group "DMI" -group "dmi_tap" $dmi_tap/* 99 | } 100 | 101 | 102 | configure wave -namecolwidth 250 103 | configure wave -valuecolwidth 100 104 | configure wave -justifyvalue left 105 | configure wave -signalnamewidth 1 106 | configure wave -timelineunits ns 107 | --------------------------------------------------------------------------------