diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml
index 5e8fced9a..193dc6a06 100644
--- a/.github/workflows/Documentation.yml
+++ b/.github/workflows/Documentation.yml
@@ -51,11 +51,8 @@ jobs:
with:
fetch-depth: 0
- - name: '🐍 Install doit'
- run: pip install doit
-
- name: '📚 Build Datasheet and User Guide (PDF and HTML)'
- run: ./do.py Documentation container
+ run: make -C docs container
- name: '📤 Upload Artifact: HTML'
uses: actions/upload-artifact@v4
@@ -80,7 +77,7 @@ jobs:
- name: '📥 Download Artifacts'
uses: actions/download-artifact@v4
- - name: '🛠️ Organise public subdir and create a tarball'
+ - name: '🛠️ Organize public subdir and create a tarball'
run: |
mv NEORV32 public
mv public/pdf ./
@@ -98,8 +95,15 @@ jobs:
run: |
gh release upload nightly NEORV32-SITE-nightly.tar.gz pdf/NEORV32*nightly.pdf --clobber
- - name: '🐍 Install doit'
- run: pip install doit
-
- name: '🚀 Deploy to GitHub-Pages'
- run: ./do.py DeployToGitHubPages "update ${{ github.sha }}"
+ run: |
+ ls -al
+ cd public
+ git init
+ cp ../.git/config ./.git/config
+ touch .nojekyll
+ git add .
+ git config --local user.email "push@gha"
+ git config --local user.name "GHA"
+ git commit -am 'update ${{ github.sha }}'
+ git push -u origin +HEAD:gh-pages
diff --git a/.github/workflows/Processor.yml b/.github/workflows/Processor.yml
index 7728608fa..ec47a46f6 100644
--- a/.github/workflows/Processor.yml
+++ b/.github/workflows/Processor.yml
@@ -19,6 +19,7 @@ jobs:
Software:
runs-on: ubuntu-latest
+ container: ghcr.io/stnolting/neorv32/sim
steps:
@@ -26,9 +27,10 @@ jobs:
uses: actions/checkout@v4
- name: '⚙️ Build Software Framework Tests'
- uses: docker://ghcr.io/stnolting/neorv32/sim
- with:
- args: ./do.py SoftwareFrameworkTests
+ run: |
+ make -C sw/example/processor_check check
+ make -C sw/example clean_all exe
+ make -C sw/bootloader clean_all info bootloader
Simple:
diff --git a/.gitignore b/.gitignore
index f2b46f035..633ad7921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,3 @@
-# doit databases
-/.doit.db.*
-
-# python
-__pycache__
-
# generated app files
*.bin
*.coe
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e034e7b3..ff1cdc94a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
+| 11.10.2024 | 1.10.5.5 | :sparkles: :lock: add optional support for on-chip debugger authentication; :warning: rename OCD-related top generics | [#](https://github.com/stnolting/neorv32/pull/1053) |
| 06.10.2024 | 1.10.5.4 | :warning: rework PWM module | [#1049](https://github.com/stnolting/neorv32/pull/1049) |
| 05.10.2024 | 1.10.5.3 | upgrade neoTRNG to version 3.2 | [#1048](https://github.com/stnolting/neorv32/pull/1048) |
| 03.10.2024 | 1.10.5.2 | :warning: remove `A` ISA extensions; replaced by new `Zalrsc` ISA extension | [#1047](https://github.com/stnolting/neorv32/pull/1047) |
diff --git a/README.md b/README.md
index fad3963df..51ab5150a 100644
--- a/README.md
+++ b/README.md
@@ -198,6 +198,7 @@ data integrity (CRC8/16/32)
* compatible to the "Minimal RISC-V Debug Specification Version 1.0"
* compatible with **OpenOCD**, **GDB** and **Segger Embedded Studio**
* RISC-V [trigger module](https://stnolting.github.io/neorv32/#_trigger_module) for hardware-assisted breakpoints
+* optional authentication module to implement custom security mechanisms
## 3. FPGA Implementation Results
diff --git a/do.py b/do.py
deleted file mode 100755
index 16d66468a..000000000
--- a/do.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-
-# doit
-
-from sys import executable, argv as sys_argv, exit as sys_exit
-from os import environ
-from pathlib import Path
-
-from doit.action import CmdAction
-from doit.cmd_base import ModuleTaskLoader
-from doit.doit_cmd import DoitMain
-
-DOIT_CONFIG = {"verbosity": 2, "action_string_formatting": "both"}
-
-ROOT = Path(__file__).parent
-
-
-def task_SoftwareFrameworkTests():
- return {
- "actions": [
- # Check toolchain
- "make -C sw/example/processor_check check",
- # Generate executables for all example projects
- "make -C sw/example clean_all exe",
- # Compile and install bootloader
- "make -C sw/bootloader clean_all info bootloader",
- ],
- "doc": "Build all sw/example/*; install bootloader",
- }
-
-
-def task_Documentation():
- return {
- "actions": ["make -C docs {posargs}"],
- "doc": "Run a target in subdir 'doc'",
- "uptodate": [False],
- "pos_arg": "posargs",
- }
-
-
-def task_DeployToGitHubPages():
- cwd = str(ROOT / "public")
- return {
- "actions": [
- CmdAction(cmd, cwd=cwd)
- for cmd in [
- "git init",
- "cp ../.git/config ./.git/config",
- "touch .nojekyll",
- "git add .",
- 'git config --local user.email "push@gha"',
- 'git config --local user.name "GHA"',
- "git commit -am '{posargs}'",
- "git push -u origin +HEAD:gh-pages",
- ]
- ],
- "doc": "Create a clean branch in subdir 'public' and push to branch 'gh-pages'",
- "pos_arg": "posargs",
- }
-
-
-if __name__ == '__main__':
- sys_exit(DoitMain(ModuleTaskLoader(globals())).run(sys_argv[1:]))
diff --git a/docs/datasheet/cpu.adoc b/docs/datasheet/cpu.adoc
index 5f094f2f8..7ddcfd43e 100644
--- a/docs/datasheet/cpu.adoc
+++ b/docs/datasheet/cpu.adoc
@@ -455,8 +455,8 @@ This chapter gives a brief overview of all available ISA extensions.
| <<_zmmul_isa_extension,`Zmmul`>> | Integer multiplication-only instructions | <<_processor_top_entity_generics, `RISCV_ISA_Zmmul`>>
| <<_zxcfu_isa_extension,`Zcfu`>> | Custom / user-defined instructions | <<_processor_top_entity_generics, `RISCV_ISA_Zxcfu`>>
| <<_smpmp_isa_extension,`Smpmp`>> | Physical memory protection (PMP) extension | <<_processor_top_entity_generics, `RISCV_ISA_Smpmp`>>
-| <<_sdext_isa_extension,`Sdext`>> | External debug support extension | <<_processor_top_entity_generics, `ON_CHIP_DEBUGGER_EN`>>
-| <<_sdtrig_isa_extension,`Sdtrig`>> | Trigger module extension | <<_processor_top_entity_generics, `ON_CHIP_DEBUGGER_EN`>>
+| <<_sdext_isa_extension,`Sdext`>> | External debug support extension | <<_processor_top_entity_generics, `OCD_EN`>>
+| <<_sdtrig_isa_extension,`Sdtrig`>> | Trigger module extension | <<_processor_top_entity_generics, `OCD_EN`>>
|=======================
.RISC-V ISA Specification
diff --git a/docs/datasheet/on_chip_debugger.adoc b/docs/datasheet/on_chip_debugger.adoc
index d13f07233..b6c719cf8 100644
--- a/docs/datasheet/on_chip_debugger.adoc
+++ b/docs/datasheet/on_chip_debugger.adoc
@@ -3,69 +3,64 @@
== On-Chip Debugger (OCD)
The NEORV32 Processor features an _on-chip debugger_ (OCD) implementing the **execution-based debugging** scheme
-compatible to the **Minimal RISC-V Debug Specification**. A copy of the specification is
-available in `docs/references`.
+compatible to the **Minimal RISC-V Debug Specification**. A copy of the specification is available in `docs/references`.
+The on-chip debugger is implemented via the <<_processor_top_entity_generics, `OCD_EN`>> processor top generic.
**Key Features**
-* standard JTAG access port
+* standard 4-wire JTAG access port
* full control of the CPU: halting, single-stepping and resuming
-* indirect access to all core registers (via program buffer)
-* indirect access to the whole processor address space (via program buffer)
-* trigger module for hardware breakpoints
+* indirect access to all core registers and the entire processor address space (via program buffer)
* compatible with upstream OpenOCD and GDB
+* optional trigger module for hardware breakpoints
+* optional authentication for increased security
-**Section Structure**
+.Hands-On Tutorial
+[TIP]
+A simple example on how to use NEORV32 on-chip debugger in combination with OpenOCD and the GNU debugger is shown in
+section https://stnolting.github.io/neorv32/ug/#_debugging_using_the_on_chip_debugger[Debugging using the On-Chip Debugger]
+of the User Guide.
-This chapter is separated into four sections:
+**Section Structure**
* <<_debug_transport_module_dtm>>
* <<_debug_module_dm>>
+* <<_debug_authentication>>
* <<_cpu_debug_mode>>
* <<_trigger_module>>
-.GDB + SVD
-[TIP]
-Together with a third-party plugin the processor's SVD file can be imported right into GDB to allow comfortable
-debugging of peripheral/IO devices (see https://github.com/stnolting/neorv32/discussions/656).
-
-.Hands-On Tutorial
-[TIP]
-A simple example on how to use NEORV32 on-chip debugger in combination with OpenOCD and the GNU debugger is shown in
-section https://stnolting.github.io/neorv32/ug/#_debugging_using_the_on_chip_debugger[Debugging using the On-Chip Debugger]
-of the User Guide.
-
-The NEORV32 on-chip debugger is based on four hardware modules:
+The NEORV32 on-chip debugger is based on five hardware modules:
.NEORV32 on-chip debugger complex
image::neorv32_ocd_complex.png[align=center]
[start=1]
-. <<_debug_transport_module_dtm>> (`rtl/core/neorv32_debug_dtm.vhd`): JTAG access tap to allow an external
-adapter to interface with the _debug module (DM)_ using the _debug module interface (dmi)_.
-. <<_debug_module_dm>> (`rtl/core/neorv32_debug_tm.vhd`): RISC-V debug module that is configured by the DTM via the dmi.
-From the CPU's "point of view" this module behaves as another memory-mapped peripheral that can be accessed via the
-processor-internal bus. The memory-mapped registers provide an internal _data buffer_ for data transfer from/to the DM, a
-_code ROM_ containing the "park loop" code, a _program buffer_ to allow the debugger to execute small programs defined by the
-DM and a _status register_ that is used to communicate _exception_, _halt_, _resume_ and _execute_ requests/acknowledges from/to the DM.
-. <<_cpu_debug_mode>> extension (part of `rtl/core/neorv32_cpu_control.vhd`): This extension provides the "debug execution mode"
-as another operation mode, which is used to execute the park loop code from the DM. This mode also provides additional CSRs and instructions.
-. CPU <<_trigger_module>> (also part of `rtl/core/neorv32_cpu_control.vhd`): This module provides a single _hardware_ breakpoint.
+. <<_debug_transport_module_dtm>>: JTAG access tap to allow an external adapter to interface with the _debug module (DM)_.
+. <<_debug_module_dm>>: RISC-V debug module that is configured by the DTM. From the CPU's perspective this module behaves as
+another memory-mapped peripheral that can be accessed via the processor-internal bus. The memory-mapped registers provide an
+internal _data buffer_ for data transfer from/to the DM, a _code ROM_ containing the "park loop" code, a _program buffer_ to
+allow the debugger to execute small programs defined by the DM and a _status register_ that is used to communicate _exception_,
+_halt_, _resume_ and _execute_ requests/acknowledges from/to the DM.
+. <<_debug_authentication>>: Authenticator module to secure on-chip debugger access. This module implements a very simple
+authentication mechanism as example. Users can modify/replace this default logic to implement arbitrary authentication mechanism.
+. <<_cpu_debug_mode>> ISA extension: This ISA extension provides the "debug execution mode" as another operation mode that is
+used to execute the park loop code from the DM. This mode also provides additional CSRs and instructions.
+. CPU <<_trigger_module>>: This module provides a single _hardware_ breakpoint.
**Theory of Operation**
-When debugging the system using the OCD, the debugger issues a halt request to the CPU (via the CPU's
-`db_halt_req_i` signal) to make the CPU enter _debug mode_. In this mode, the application-defined architectural
-state of the system/CPU is "frozen" so the debugger can monitor if without interfering with the actual application.
-However, the OCD can also modify the entire architectural state at any time. While in debug mode, the debugger has
-full control over the entire CPU and processor operating at highest-privileged mode.
+When debugging the system using the OCD, the debugger (like GDB) issues a halt request to the CPU to make the it enter
+_debug mode_. In this mode the application-defined architectural state of the system/CPU is "frozen" so the debugger
+can monitor it without interfering with the actual application. However, the OCD can also modify the entire architectural
+state at any time. While in debug mode, the debugger has full control over the entire CPU and processor operating at
+highest-privileged mode.
While in debug mode, the CPU executes the "park loop" code from the code ROM of the debug module (DM).
This park loop implements an endless loop, where the CPU polls a memory-mapped <<_status_register>> that is
-controlled by the DM. The flags in this register are used to communicate requests from
-the DM and to acknowledge them by the CPU: trigger execution of the program buffer or resume the halted
-application. Furthermore, the CPU uses this register to signal that the CPU has halted after a halt request
-and to signal that an exception has been triggered while being in debug mode.
+controlled by the DM. The flags in this register are used to communicate requests from the DM and to acknowledge
+them by the CPU: trigger execution of the program buffer or resume the halted application. Furthermore, the CPU
+uses this register to signal that the CPU has halted after a halt request or to signal that an exception has been
+raised while being in debug mode.
<<<
@@ -73,8 +68,8 @@ and to signal that an exception has been triggered while being in debug mode.
:sectnums:
=== Debug Transport Module (DTM)
-The debug transport module "DTM" (VHDL module: `rtl/core/neorv32_debug_dtm.vhd`) provides a JTAG test access port ("tap").
-External JTAG access is provided by the following top-level ports:
+The debug transport module "DTM" (VHDL module: `rtl/core/neorv32_debug_dtm.vhd`) provides a standard 4-wire JTAG test
+access port ("tap") via the following top-level ports:
.JTAG top level signals
[cols="^2,^2,^2,<8"]
@@ -103,7 +98,7 @@ since JTAG-level resets can be triggered using with TMS signaling.
If the on-chip debugger is disabled the JTAG serial input `jtag_tdi_i` is directly
connected to the JTAG serial output `jtag_tdo_o` to maintain the JTAG chain.
-JTAG accesses are based on a single _instruction register_ `IR`, which is 5 bit wide, and several _data registers_ `DR`
+JTAG accesses are based on a single 5-bit _instruction register_ `IR` and several _data registers_ `DR`
with different sizes. The individual data registers are accessed by writing the according address to the instruction
register. The following table shows the available data registers and their addresses:
@@ -112,9 +107,9 @@ register. The following table shows the available data registers and their addre
[options="header",grid="rows"]
|=======================
| Address (via `IR`) | Name | Size (bits) | Description
-| `00001` | `IDCODE` | 32 | identifier, version and part ID fields are hardwired to zero, manufacturer ID is assigned via the `JEDEC_ID` top generic (<<_processor_top_entity_generics>>)
-| `10000` | `DTMCS` | 32 | debug transport module control and status register
-| `10001` | `DMI` | 41 | debug module interface (_dmi_); 7-bit address, 32-bit read/write data, 2-bit operation (`00` = NOP; `10` = write; `01` = read)
+| `00001` | `IDCODE` | 32 | identifier, version and part ID fields are hardwired to zero, manufacturer ID is assigned via the <<_processor_top_entity_generics, `JEDEC_ID`>> generic
+| `10000` | `DTMCS` | 32 | debug transport module control and status register (see below)
+| `10001` | `DMI` | 41 | debug module interface: 7-bit address, 32-bit read/write data, 2-bit operation (`00` = NOP; `10` = write; `01` = read)
| others | `BYPASS` | 1 | default JTAG bypass register
|=======================
@@ -130,7 +125,7 @@ register. The following table shows the available data registers and their addre
| 14:12 | `idle` | r/- | recommended idle states (= 0, no idle states required)
| 11:10 | `dmistat` | r/- | DMI status: `00` = no error, `01` = reserved, `10` = operation failed, `11` = failed operation during pending DMI operation
| 9:4 | `abits` | r/- | number of address bits in `DMI` register (= 6)
-| 3:0 | `version` | r/- | `0001` = DTM is compatible to spec. versions v0.13 and v1.0
+| 3:0 | `version` | r/- | `0001` = DTM is compatible to RISC-V debug spec. versions v0.13 and v1.0
|=======================
@@ -145,38 +140,38 @@ It supports the following features:
* Gives the debugger necessary information about the implementation.
* Allows the hart to be halted/resumed/reset and provides the current status.
-* Provides abstract read and write access to the halted hart's generap purpose registers.
+* Provides abstract read and write access to the halted hart's general purpose registers.
* Provides access to a reset signal that allows debugging from the very first instruction after reset.
-* Provides a _Program Buffer_ to force the hart to execute arbitrary instructions.
+* Provides a _program buffer_ to force the hart to execute arbitrary instructions.
* Allows memory access from a hart's point of view.
+* Optionally implements an authentication mechanism to secure on-chip debugger access.
The NEORV32 DM follows the "Minimal RISC-V External Debug Specification" to provide full debugging capabilities while
keeping resource/area requirements at a minimum. It implements the **execution based debugging scheme** for a
-single hart and provides the following core features:
+single hart and provides the following architectural core features:
* program buffer with 2 entries and an implicit `ebreak` instruction
* indirect bus access via the CPU using the program buffer
* abstract commands: "access register" plus auto-execution
* halt-on-reset capability
+* optional authentication
.DM Spec. Version
[TIP]
-By default, the OCD's debug module supports version 1.0 of the RISC-V debug spec. For backwards compatibility, the DM
-can be "downgraded" back to version 0.13 via the `DM_LEGACY_MODE` generic (see <<_processor_top_entity_generics>>).
+By default, the OCD's debug module supports version 1.0 of the RISC-V debug spec. However, for backwards compatibility the
+DM can be downgraded back to version 0.13 via the see <<_processor_top_entity_generics, `OCD_DM_LEGACY_MODE`>> top generic.
-The DM provides two access "point of views": accesses from the DTM via the _debug module interface (dmi)_ and
-accesses from the CPU via the processor-internal bus system. From the DTM's point of view, the DM implements a set of
-<<_dm_registers>> that are used to control and monitor the debugging session. From the CPU's point of view, the
-DM implements several memory-mapped registers (within the _normal_ address space) that are used for communicating
-debugging control and status (<<_dm_cpu_access>>).
+From the DTM's point of view, the DM implements a set of <<_dm_registers>> that are used to control and monitor the
+debugging session. From the CPU's point of view, the DM implements several memory-mapped registers that are used for
+communicating debugging control and status (<<_dm_cpu_access>>).
:sectnums:
==== DM Registers
-The DM is controlled via a set of registers that are accessed via the DTM's _debug module interface_ (dmi).
-The following registers are implemented:
+The DM is controlled via a set of registers that are accessed via the DTM. The following registers are implemented:
+.Unimplemented Registers
[NOTE]
Write accesses to registers that are not implemented are simply ignored and read accesses
to these registers will always return zero.
@@ -186,18 +181,19 @@ to these registers will always return zero.
[options="header",grid="rows"]
|=======================
| Address | Name | Description
-| `0x04` | <<_data0>> | Abstract data 0, used for data transfer between debugger and processor
-| `0x10` | <<_dmcontrol>> | Debug module control
-| `0x11` | <<_dmstatus>> | Debug module status
-| `0x12` | <<_hartinfo>> | Hart information
-| `0x16` | <<_abstracts>> | Abstract control and status
-| `0x17` | <<_command>> | Abstract command
-| `0x18` | <<_abstractauto>> | Abstract command auto-execution
-| `0x1d` | `nextdm` | Base address of next DM; reads as zero to indicate there is only one DM
-| `0x20` | <<_progbuf, `progbuf0`>> | Program buffer 0
-| `0x21` | <<_progbuf, `progbuf1`>> | Program buffer 1
-| `0x38` | `sbcs` | System bus access control and status; reads as zero to indicate there is **no** direct system bus access
-| `0x40` | <<_haltsum0>> | Halted harts
+| 0x04 | <<_data0>> | Abstract data 0, used for data transfer between debugger and processor
+| 0x10 | <<_dmcontrol>> | Debug module control
+| 0x11 | <<_dmstatus>> | Debug module status
+| 0x12 | <<_hartinfo>> | Hart information
+| 0x16 | <<_abstracts>> | Abstract control and status
+| 0x17 | <<_command>> | Abstract command
+| 0x18 | <<_abstractauto>> | Abstract command auto-execution
+| 0x1d | `nextdm` | Base address of next DM; reads as zero to indicate there is only one DM
+| 0x20 | <<_progbuf, `progbuf0`>> | Program buffer 0
+| 0x21 | <<_progbuf, `progbuf1`>> | Program buffer 1
+| 0x30 | <<_authdata>> | Data to/from the authentication module
+| 0x38 | `sbcs` | System bus access control and status; reads as zero to indicate there is **no** direct system bus access
+| 0x40 | <<_haltsum0>> | Halted harts
|=======================
@@ -254,14 +250,14 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | Description
-| 31:23 | _reserved_ | reserved; always zero
-| 22 | `impebreak` | always `1`; indicates an implicit `ebreak` instruction after the last program buffer entry
-| 21:20 | _reserved_ | reserved; always zero
+| 31:23 | _reserved_ | reserved; zero
+| 22 | `impebreak` | `1`: indicates an implicit `ebreak` instruction after the last program buffer entry
+| 21:20 | _reserved_ | reserved; zero
| 19 | `allhavereset` .2+| `1` when the hart is in reset
| 18 | `anyhavereset`
| 17 | `allresumeack` .2+| `1` when the hart has acknowledged a resume request
| 16 | `anyresumeack`
-| 15 | `allnonexistent` .2+| always zero to indicate the hart is always existent
+| 15 | `allnonexistent` .2+| zero to indicate the hart is always existent
| 14 | `anynonexistent`
| 13 | `allunavail` .2+| `1` when the DM is disabled to indicate the hart is unavailable
| 12 | `anyunavail`
@@ -269,18 +265,13 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
| 10 | `anyrunning`
| 9 | `allhalted` .2+| `1` when the hart is halted
| 8 | `anyhalted`
-| 7 | `authenticated` | always `1`; there is no authentication
-| 6 | `authbusy` | always `0`; there is no authentication
-| 5 | `hasresethaltreq` | always `0`; halt-on-reset is not supported (directly)
-| 4 | `confstrptrvalid` | always `0`; no configuration string available
-| 3:0 | `version` | debug spec. version; `0011` (v1.0) or `0010` (v0.13); configured via the `DM_LEGACY_MODE` <<_processor_top_entity_generics>>
+| 7 | `authenticated` | set if authentication passed; see <<_debug_authentication>>
+| 6 | `authbusy` | set if authentication is busy, see <<_debug_authentication>>
+| 5 | `hasresethaltreq` | `0`: halt-on-reset is not supported (directly)
+| 4 | `confstrptrvalid` | `0`: no configuration string available
+| 3:0 | `version` | debug spec. version; `0011` (v1.0) or `0010` (v0.13); configured via the <<_processor_top_entity_generics, `OCD_DM_LEGACY_MODE`>> top generic
|=======================
-.OCD Security
-[WARNING]
-JTAG access via the OCD is **always authenticated** (`dmstatus.authenticated = 1`). Hence, the entire system can always
-be accessed via the on-chip debugger.
-
:sectnums!:
===== **`hartinfo`**
@@ -298,11 +289,11 @@ be accessed via the on-chip debugger.
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | Description
-| 31:24 | _reserved_ | reserved; always zero
-| 23:20 | `nscratch` | `0001`, number of `dscratch*` CPU registers = 1
-| 19:17 | _reserved_ | reserved; always zero
-| 16 | `dataccess` | `0`, the `data` registers are shadowed in the hart's address space
-| 15:12 | `datasize` | `0001`, number of 32-bit words in the address space dedicated to shadowing the `data` registers (1 register)
+| 31:24 | _reserved_ | reserved; zero
+| 23:20 | `nscratch` | `0001`: number of `dscratch*` CPU registers = 1
+| 19:17 | _reserved_ | reserved; zero
+| 16 | `dataccess` | `0`: the `data` registers are shadowed in the hart's address space
+| 15:12 | `datasize` | `0001`: number of 32-bit words in the address space dedicated to shadowing the `data` registers (1 register)
| 11:0 | `dataaddr` | = `dm_data_base_c(11:0)`, signed base address of `data` words (see address map in <<_dm_cpu_access>>)
|=======================
@@ -323,14 +314,14 @@ be accessed via the on-chip debugger.
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
-| 31:29 | _reserved_ | r/- | reserved; always zero
-| 28:24 | `progbufsize` | r/- | always `0010`: size of the program buffer (`progbuf`) = 2 entries
-| 23:11 | _reserved_ | r/- | reserved; always zero
-| 12 | `busy` | r/- | `1` when a command is being executed
-| 11 | `relaxedpriv` | r/- | always `1`: PMP rules are ignored when in debug mode
+| 31:29 | _reserved_ | r/- | reserved; zero
+| 28:24 | `progbufsize` | r/- | `0010`: size of the program buffer (`progbuf`) = 2 entries
+| 23:11 | _reserved_ | r/- | reserved; zero
+| 12 | `busy` | r/- | set when a command is being executed
+| 11 | `relaxedpriv` | r/- | `1`: PMP rules are ignored when in debug mode
| 10:8 | `cmderr` | r/w | error during command execution (see below); has to be cleared by writing `111`
-| 7:4 | _reserved_ | r/- | reserved; always zero
-| 3:0 | `datacount` | r/- | always `0001`: number of implemented `data` registers for abstract commands = 1
+| 7:4 | _reserved_ | r/- | reserved; zero
+| 3:0 | `datacount` | r/- | `0001`: number of implemented `data` registers for abstract commands = 1
|=======================
Error codes in `cmderr` (highest priority first):
@@ -363,12 +354,12 @@ hart's GPRs x0 - x15/31 (abstract command register index `0x1000` - `0x101f`).
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description / required value
-| 31:24 | `cmdtype` | -/w | `00000000` to indicate "access register" command
+| 31:24 | `cmdtype` | -/w | `00000000`: indicates "access register" command
| 23 | _reserved_ | -/w | reserved, has to be `0` when writing
-| 22:20 | `aarsize` | -/w | `010` to indicate 32-bit accesses
-| 21 | `aarpostincrement` | -/w | `0`, post-increment is not supported
-| 18 | `postexec` | -/w | if set the program buffer is executed _after_ the command
-| 17 | `transfer` | -/w | if set the operation in `write` is conducted
+| 22:20 | `aarsize` | -/w | `010`: indicates 32-bit accesses
+| 21 | `aarpostincrement` | -/w | `0`: post-increment is not supported
+| 18 | `postexec` | -/w | set if the program buffer is executed _after_ the command
+| 17 | `transfer` | -/w | set if the operation in `write` is conducted
| 16 | `write` | -/w | `1`: copy `data0` to `[regno]`, `0`: copy `[regno]` to `data0`
| 15:0 | `regno` | -/w | GPR-access only; has to be `0x1000` - `0x101f`
|=======================
@@ -409,6 +400,19 @@ hart's GPRs x0 - x15/31 (abstract command register index `0x1000` - `0x101f`).
|======
+:sectnums!:
+===== **`authdata`**
+
+[cols="4,27,>7"]
+[frame="topbot",grid="none"]
+|======
+| 0x30 | **Authentication data** | `authdata`
+3+| Reset value: _user-defined_
+3+| This register serves as a 32-bit serial port to/from the authentication module. See <<_debug_authentication>>.
+|======
+
+
+
:sectnums!:
===== **`haltsum0`**
@@ -432,40 +436,39 @@ hart's GPRs x0 - x15/31 (abstract command register index `0x1000` - `0x101f`).
:sectnums:
==== DM CPU Access
-From the CPU's perspective, the DM acts like another memory-mapped peripheral. It occupies 256 bytes of the CPU's address
-space starting at address `base_io_dm_c` (see table below). This address space is divided into four sections of 64 bytes
-each to provide access to the _park loop code ROM_, the _program buffer_, the _data buffer_ and the _status register_.
-The program buffer, the data buffer and the status register do not fully occupy the 64-byte-wide sections and are
-mirrored several times to fill the entire section.
+From the CPU's perspective the DM acts like another memory-mapped peripheral. It occupies 256 bytes of the CPU's address
+space starting at address `base_io_dm_c`. This address space is divided into four sections of 64 bytes each to provide
+access to the _park loop code ROM_, the _program buffer_, the _data buffer_ and the _status register_. The program buffer,
+the data buffer and the status register do not fully occupy the 64-byte-wide sections and are mirrored several times to fill
+the entire section.
.DM CPU Access - Address Map
-[cols="^2,^2,<5"]
+[cols="^2,^2,<4"]
[options="header",grid="rows"]
|=======================
-| Base address | Actual size | Description
-| `0xffffff00` | 64 bytes | ROM for the "park loop" code
-| `0xffffff40` | 16 bytes | Program buffer (<<_progbuf>>)
-| `0xffffff80` | 4 bytes | Data buffer (<<_data0>>)
-| `0xffffffc0` | 4 bytes | Control and <<_status_register>>
+| Base address | Physical size | Description
+| `0xffffff00` | 64 bytes | ROM for the "park loop" code
+| `0xffffff40` | 16 bytes | Program buffer (<<_progbuf>>)
+| `0xffffff80` | 4 bytes | Data buffer (<<_data0>>)
+| `0xffffffc0` | 4 bytes | Control and <<_status_register>>
|=======================
.DM Register Access
[IMPORTANT]
-All memory-mapped registers of the DM can only be accessed by the CPU if it is actually in debug mode.
-Hence, the DM registers are not "visible" for normal CPU operations.
-Any CPU access outside of debug mode will raise a bus access fault exception.
+All memory-mapped registers of the DM can only be accessed by the CPU if it is in debug mode. Hence, the DM registers are not
+visible nor accessible for normal CPU operations. Any CPU access outside of debug mode will raise a bus access fault exception.
.Park Loop Code Sources ("OCD Firmware")
[NOTE]
-The assembly sources of the **park loop code** are available in `sw/ocd-firmware/park_loop.S`.
+The assembly sources of the park loop code are available in `sw/ocd-firmware/park_loop.S`.
:sectnums:
===== Code ROM Entry Points
-The park loop code provides two entry points where the actual code execution can start. These are used to enter
-the park loop either when an explicit debug-entry request has been issued (for example a halt request) or when an exception
-has occurred while executing code _inside_ debug mode.
+The park loop code provides two entry points where code execution can start. These are used to enter the park loop either when
+an explicit debug-entry/halt request has been issued (for example a halt request) or when an exception has occurred while executing
+code in debug mode.
.Park Loop Entry Points
[cols="^6,<4"]
@@ -473,60 +476,113 @@ has occurred while executing code _inside_ debug mode.
|=======================
| Address | Description
| `dm_exc_entry_c` (`base_io_dm_c` + 0) | Exception entry address
-| `dm_park_entry_c` (`base_io_dm_c` + 8) | Normal entry address
+| `dm_park_entry_c` (`base_io_dm_c` + 8) | Normal entry address (halt request)
|=======================
-When the CPU enters or re-enters debug mode (for example via an `ebreak` in the DM's program buffer), it jumps to
-the _normal entry point_ that is configured via the `CPU_DEBUG_PARK_ADDR` generic
-(<<_cpu_top_entity_generics>>). By default, this generic is set to `dm_park_entry_c`, which is defined in main
-package file. If an exception is encountered during debug mode, the CPU jumps to the address of the _exception
-entry point_ configured via the `CPU_DEBUG_EXC_ADDR` generic (<<_cpu_top_entity_generics>>). By default, this generic
-is set to `dm_exc_entry_c`, which is also defined in main package file.
+When the CPU enters (via an explicit halt request from the dubber) or re-enters debug mode (for example via an `ebreak` in the
+DM's program buffer), it jumps to the _normal entry point_ that is configured via the <<_cpu_top_entity_generics, `CPU_DEBUG_PARK_ADDR`>>
+CPU generic. By default, this address is set to `dm_park_entry_c`, which is defined in the main
+package file. If an exception is encountered during debug mode, the CPU jumps to the address of the _exception entry point_
+configured via the <<_cpu_top_entity_generics, `CPU_DEBUG_EXC_ADDR`>> CPU generic. By default, this address
+is set to `dm_exc_entry_c`, which is also defined in the main package file.
:sectnums:
===== Status Register
-The status register provides a direct communication channel between the CPU's debug mode executing the park loop
-and the debugger-controlled debug module. This register is used to communicate _requests_, which are issued by the
-DM and the according _acknowledges_, which are generated by the CPU.
+The status register provides a direct communication channel between the CPU's debug-mode executing the park loop
+and the debugger-controlled DM. This register is used to communicate requests, which are issued by the
+DM, and the according acknowledges, which are generated by the CPU.
-There are only 4 bits in this register that are used to implement the requests/acknowledges. Each bit is left-aligned
-in one sub-byte of the entire 32-bit register. Thus, the CPU can access each bit individually using _store-byte_ and
-_load-byte_ instructions. This eliminates the need to perform bit-masking in the park loop code leading to less code size
-and faster execution.
+There are only 4 bits in this register that are used to implement requests/acknowledges. Each bit is left-aligned
+in one sub-byte of the entire 32-bit register. Thus, the CPU can access each bit individually using store-byte (`sb`) and
+load-byte (`lb`) instructions. This eliminates the need to perform bit-masking in the park loop code resulting in less code
+size and faster execution.
.DM Status Register - CPU Access
-[cols="^1,^3,^2,<8"]
+[cols="^1,^3,^3,<8"]
[options="header",grid="rows"]
|=======================
-| Bit | Name | CPU access <| Description
-.2+| 0 | `sreg_halt_ack` | read <| -
- | - | write <| Set by the CPU while it is halted (and executing the park loop).
-.2+| 8 | `sreg_resume_req` | read <| Set by the DM to request the CPU to resume normal operation.
- | `sreg_resume_ack` | write <| Set by the CPU before it starts resuming.
-.2+| 16 | `sreg_execute_req` | read <| Set by the DM to request execution of the program buffer.
- | `sreg_execute_ack` | write <| Set by the CPU before it starts executing the program buffer.
-.2+| 24 | - | read <| -
- | `sreg_execute_ack` | write <| Set by the CPU if an exception occurs while being in debug mode.
+| Bit | Name | CPU/DM access <| Description
+| 0 | `sreg_halt_ack` | CPU write, DM read <| Set by the CPU when halting.
+.2+| 8 | `sreg_resume_req` | DM write, CPU read <| Set by the DM to request the CPU to resume normal operation.
+ | `sreg_resume_ack` | CPU write, DM read <| Set by the CPU before it starts resuming.
+.2+| 16 | `sreg_execute_req` | DM write, CPU read <| Set by the DM to request execution of the program buffer.
+ | `sreg_execute_ack` | CPU write, DM read <| Set by the CPU before it starts executing the program buffer.
+| 24 | `sreg_execute_ack` | CPU write, DM read <| Set by the CPU if an exception occurs while being in debug mode.
|=======================
+<<<
+// ####################################################################################################################
+:sectnums:
+=== Debug Authentication
+
+Optionally, the on-chip debugger's DM can be equipped with an _authenticator module_ to secure debugger access. This authentication
+is enabled by the <<_processor_top_entity_generics, `OCD_AUTHENTICATION`>> top generic. When disabled, the debugger is always
+authorized and has unlimited access. When enabled, the debugger is required to authenticate in order to gain access.
+
+The authenticator module is implemented as individual RTL module (`rtl/core/neorv32_debug_auth.vhd`). By default, it implements
+a very simple authentication mechanism. Note that this default mechanism is not secure in any way - it is intended as example
+logic to illustrate the interface and authentication process. Users can modify the default logic or replace the entire module
+to implement a more sophisticated custom authentication mechanism.
+
+The authentication interface is compliant to the RISC-V debug spec and is based on a single CSR and two additional status bits:
+
+* <<_authdata>> CSR: this 32-bit register is used to read/write data from/to the authentication module. It is hardwired to
+all-zero if authentication is not implemented.
+* <<_dmstatus>> CSR:
+** The `authenticated` bit (read-only) is set if authentication was successful. The debugger can access the processor only
+if this bit is set. It is automatically hardwired to `1` (always authenticated) if the authentication module is not implemented.
+** The `authbusy` bit (read-only) indicates if the authentication module is busy. When set, no data should be written/read to/from
+<<_authdata>>. This bit is automatically hardwired to `0` (never busy) if the authentication module is not implemented.
+
+openOCD provides dedicated commands to exchange data with the authenticator module:
+
+.openOCD RISC-V Authentication Commands
+[source,tcl]
+----
+riscv authdata_read // read 32-bit from authdata CSR
+riscv authdata_write value // write 32-bit value to authdata CSR
+----
+
+Based on these two primitives arbitrary complex authentication mechanism can be implemented.
+
+
+:sectnums:
+==== Default Authentication Mechanism
+
+[IMPORTANT]
+The default authentication mechanism is not secure at all. Replace it by a custom design.
+
+The default authenticator hardware implements a very simple authentication mechanism: a single read/write bit is implemented
+that directly corresponds to the `authenticated` bit in <<_dmstatus>>. This bit can be read/written as bit zero (LSB) of the
+<<_authdata>> CSR. Writing 1 to this register will result in a successful authentication. The default openOCD configuration
+script for the NEORV32 implements this basic authentication mechanism:
+
+.Default authentication process (`sw/openocd/openocd_neorv32.cfg`)
+[source,tcl]
+----
+set challenge [riscv authdata_read] # read authdata; not required, just an example
+riscv authdata_write [expr {$challenge | 1}] # set LSB to authenticate
+----
+
+
<<<
// ####################################################################################################################
:sectnums:
=== CPU Debug Mode
-The NEORV32 CPU Debug Mode is compatible to the **Minimal RISC-V Debug Specification 1.0**
-`Sdext` (external debug) ISA extension. When enabled via the CPU's <<_sdext_isa_extension>> generic and/or
-the processor's `ON_CHIP_DEBUGGER_EN` it adds a new CPU operation mode ("debug mode"), three additional
-<<_cpu_debug_mode_csrs>> and one additional instruction (`dret`) to the core.
+The NEORV32 CPU Debug Mode is compatible to the **Minimal RISC-V Debug Specification 1.0** `Sdext` (external debug)
+ISA extension. When enabled via the CPU's <<_sdext_isa_extension>> generic and/or the processor's `OCD_EN` it adds
+a new CPU operation mode ("debug mode"), three additional <<_cpu_debug_mode_csrs>> and one additional instruction
+(`dret`) to the core.
Debug-mode is entered on any of the following events:
[start=1]
. The CPU executes an `ebreak` instruction (when in machine-mode and `ebreakm` in <<_dcsr>> is set OR when in user-mode and `ebreaku` in <<_dcsr>> is set).
-. A debug halt request is issued by the DM (via CPU signal `db_halt_req_i`, high-active).
+. A debug halt request is issued by the DM (via CPU `db_halt_req_i` signal, high-active).
. The CPU completes executing of a single instruction while being in single-step debugging mode (`step` in <<_dcsr>> is set).
. A hardware trigger from the <<_trigger_module>> fires (`exe` and `action` in <<_tdata1>> / `mcontrol` are set).
@@ -542,17 +598,19 @@ asynchronous interrupts) that are handled transparently by the control logic.
* copy the hart's current privilege level to the `prv` flags in <<_dcsr>>
* set `cause` in <<_dcsr>> according to the cause why debug mode is entered
* **no update** of `mtval`, `mcause`, `mtval` and `mstatus` CSRs
-* load the address configured via the CPU's `CPU_DEBUG_PARK_ADDR` (<<_cpu_top_entity_generics>>) generic to the program counter jumping to the
+* load the address configured via the CPU's (<<_cpu_top_entity_generics, `CPU_DEBUG_PARK_ADDR`>>) generic to the program counter jumping to the
"debugger park loop" code stored in the debug module (DM)
-**When the CPU is in debug-mode the following things are important:**
+**When the CPU is in debug-mode:**
* while in debug mode, the CPU executes the parking loop and - if requested by the DM - the program buffer
* effective CPU privilege level is `machine` mode; any active physical memory protection (PMP) configuration is bypassed
* the `wfi` instruction acts as a `nop` (also during single-stepping)
* if an exception occurs while being in debug mode:
-** if the exception was caused by any debug-mode entry action the CPU jumps to the normal entry point (defined by `CPU_DEBUG_PARK_ADDR` generic of the <<_cpu_top_entity_generics>>) of the park loop again (for example when executing `ebreak` while in debug-mode)
-** for all other exception sources the CPU jumps to the exception entry point (defined by `CPU_DEBUG_EXC_ADDR` generic of the <<_cpu_top_entity_generics>>) to signal an exception to the DM; the CPU restarts the park loop again afterwards
+** if the exception was caused by any debug-mode entry action the CPU jumps to the normal entry point (defined by the
+<<_cpu_top_entity_generics, `CPU_DEBUG_PARK_ADDR`>> generic) of the park loop again (for example when executing `ebreak` while in debug-mode)
+** for all other exception sources the CPU jumps to the exception entry point (defined by the <<_cpu_top_entity_generics, `CPU_DEBUG_EXC_ADDR`>> generic)
+to signal an exception to the DM; the CPU restarts the park loop again afterwards
* interrupts are disabled; however, they will remain pending and will get executed after the CPU has left debug mode and is not being single-stepped
* if the DM makes a resume request, the park loop exits and the CPU leaves debug mode (executing `dret`)
* the standard counters <<_machine_counter_and_timer_csrs>> `[m]cycle[h]` and `[m]instret[h]` are stopped
@@ -570,11 +628,10 @@ Executing `dret` outside of debug mode will raise an illegal instruction excepti
:sectnums:
==== CPU Debug Mode CSRs
-Two additional CSRs are required by the _Minimal RISC-V Debug Specification_: the debug mode control and status register
+Two additional CSRs are required by the "Minimal RISC-V Debug Specification": the debug mode control and status register
`dcsr` and the debug program counter `dpc`. An additional general purpose scratch register for debug-mode-only
-(`dscratch0`) allows faster execution by having a fast-accessible backup register.
-These CSRs are only accessible when the CPU is _in_ debug mode. If these CSRs are accessed outside of debug mode
-an illegal instruction exception is raised.
+(`dscratch0`) allows faster execution by having a fast-accessible backup register. These CSRs are only accessible if the CPU
+is in debug mode. If these CSRs are accessed outside of debug mode an illegal instruction exception is raised.
:sectnums!:
@@ -595,19 +652,19 @@ an illegal instruction exception is raised.
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
-| 31:28 | `xdebugver` | r/- | `0100` - CPU debug mode is compatible to spec. version 1.0
-| 27:16 | - | r/- | `000000000000` - _reserved_
+| 31:28 | `xdebugver` | r/- | `0100`: CPU debug mode is compatible to spec. version 1.0
+| 27:16 | - | r/- | `000000000000`: _reserved_
| 15 | `ebereakm` | r/w | `ebreak` instructions in `machine` mode will _enter_ debug mode when set
-| 14 | `ebereakh` | r/- | `0` - hypervisor mode not supported
-| 13 | `ebereaks` | r/- | `0` - supervisor mode not supported
+| 14 | `ebereakh` | r/- | `0`: hypervisor mode not supported
+| 13 | `ebereaks` | r/- | `0`: supervisor mode not supported
| 12 | `ebereaku` | r/w | `ebreak` instructions in `user` mode will _enter_ debug mode when set
-| 11 | `stepie` | r/- | `0` - IRQs are disabled during single-stepping
-| 10 | `stopcount` | r/- | `1` - standard counters and HPMs are stopped when in debug mode
-| 9 | `stoptime` | r/- | `0` - timers increment as usual
-| 8:6 | `cause` | r/- | cause identifier - why debug mode was entered (see below)
-| 5 | - | r/- | `0` - _reserved_
-| 4 | `mprven` | r/- | `1` - `mprv` in <<_mstatus>> is also evaluated when in debug mode
-| 3 | `nmip` | r/- | `0` - non-maskable interrupt is pending
+| 11 | `stepie` | r/- | `0`: IRQs are disabled during single-stepping
+| 10 | `stopcount` | r/- | `1`: standard counters and HPMs are stopped when in debug mode
+| 9 | `stoptime` | r/- | `0`: timers increment as usual
+| 8:6 | `cause` | r/- | cause identifier: why debug mode was entered (see below)
+| 5 | - | r/- | `0`: _reserved_
+| 4 | `mprven` | r/- | `1`: `mprv` in <<_mstatus>> is also evaluated when in debug mode
+| 3 | `nmip` | r/- | `0`: non-maskable interrupt is pending
| 2 | `step` | r/w | enable single-stepping when set
| 1:0 | `prv` | r/w | CPU privilege level before/after debug mode
|=======================
@@ -657,7 +714,7 @@ return to the address stored in `dpc` by automatically moving `dpc` to the progr
:sectnums:
=== Trigger Module
-"Normal" _software_ breakpoints (using GDB's `b`/`break` command) are implemented by temporarily replacing the according
+"Normal" software breakpoints (using GDB's `b`/`break` command) are implemented by temporarily replacing the according
instruction word by an `[c.]ebreak` instruction. However, this is not possible when debugging code that is executed from
read-only memory (for example when debugging programs that are executed via the <<_execute_in_place_module_xip>>).
To circumvent this limitation a hardware trigger logic allows to (re-)enter debug-mode when instruction execution
@@ -716,7 +773,7 @@ if it uses the trigger module for implementing a "hardware breakpoint"
| Address | `0x7a1`
| Reset value | `0x60000048`
| ISA | `Zicsr` & `Sdtrig`
-| Description | This CSR is used to configure the address match trigger using the "type 6" format.
+| Description | This CSR is used to configure the address match trigger using "type 6" format.
|=======================
.Match Control CSR (`tdata1`) Bits
@@ -724,26 +781,26 @@ if it uses the trigger module for implementing a "hardware breakpoint"
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
-| 31:28 | `type` | r/- | `0100` - address match trigger type 6
+| 31:28 | `type` | r/- | `0100`: address match trigger type 6
| 27 | `dmode` | r/w | set to ignore write accesses to <<_tdata1>> and <<_tdata2>> from machine-mode; writable from debug-mode only
-| 26 | `uncertain` | r/- | `0` - trigger satisfies the configured conditions
-| 25 | `hit1` | r/- | `0` - hardwired to zero, only `hit0` is used
-| 24 | `vs` | r/- | `0` - VS-mode not supported
-| 23 | `vu` | r/- | `0` - VU-mode not supported
+| 26 | `uncertain` | r/- | `0`: trigger satisfies the configured conditions
+| 25 | `hit1` | r/- | `0`: hardwired to zero, only `hit0` is used
+| 24 | `vs` | r/- | `0`: VS-mode not supported
+| 23 | `vu` | r/- | `0`: VU-mode not supported
| 22 | `hit0` | r/c | set when trigger has fired (**BEFORE** executing the triggering address); must be explicitly cleared by writing zero; writing 1 has no effect
-| 21 | `select` | r/- | `0` - only address matching is supported
-| 20:19 | reserved | r/- | `00` - hardwired to zero
-| 18:16 | `size` | r/- | `000` - match accesses of any size
+| 21 | `select` | r/- | `0`: only address matching is supported
+| 20:19 | reserved | r/- | `00`: hardwired to zero
+| 18:16 | `size` | r/- | `000`: match accesses of any size
| 15:12 | `action` | r/w | `0000` = breakpoint exception on trigger match, `0001` = enter debug-mode on trigger match
-| 11 | `chain` | r/- | `0` - chaining is not supported as there is only one trigger
-| 10:6 | `match` | r/- | `0000` - equal-match only
-| 6 | `m` | r/- | `1` - trigger enabled when in machine-mode
-| 5 | `uncertainen` | r/- | `0` - feature not supported, hardwired to zero
-| 4 | `s` | r/- | `0` - supervisor-mode not supported
-| 3 | `u` | r/- | `0`/`1` - trigger enabled when in user-mode, set if `U` ISA extension is enabled
+| 11 | `chain` | r/- | `0`: chaining is not supported as there is only one trigger
+| 10:6 | `match` | r/- | `0000`: equal-match only
+| 6 | `m` | r/- | `1`: trigger enabled when in machine-mode
+| 5 | `uncertainen` | r/- | `0`: feature not supported, hardwired to zero
+| 4 | `s` | r/- | `0`: supervisor-mode not supported
+| 3 | `u` | r/- | `0`/`1`: trigger enabled when in user-mode, set if `U` ISA extension is enabled
| 2 | `execute` | r/w | set to enable trigger matching on instruction address
-| 1 | `store` | r/- | `0` - store address/data matching not supported
-| 0 | `load` | r/- | `0` - load address/data matching not supported
+| 1 | `store` | r/- | `0`: store address/data matching not supported
+| 0 | `load` | r/- | `0`: load address/data matching not supported
|=======================
@@ -780,7 +837,7 @@ Note that the trigger module will fire **before** the instruction at the program
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
-| 31:24 | `version` | r/- | `0x01` - compatible to spec. version v1.0
-| 23:15 | reserved | r/- | `0x00` - hardwired to zero
-| 15:0 | `info` | r/- | `0x0006` - only the "type 6 trigger" is supported
+| 31:24 | `version` | r/- | `0x01`: compatible to spec. version v1.0
+| 23:15 | reserved | r/- | `0x00`: hardwired to zero
+| 15:0 | `info` | r/- | `0x0006`: only "type 6 trigger" is supported
|=======================
diff --git a/docs/datasheet/overview.adoc b/docs/datasheet/overview.adoc
index a56abcc4e..374b24348 100644
--- a/docs/datasheet/overview.adoc
+++ b/docs/datasheet/overview.adoc
@@ -94,7 +94,7 @@ include::rationale.adoc[]
* optional execute in-place (XIP) module to execute code directly form an external SPI flash
* optional DMA controller for CPU-independent data transfers
* optional CRC module to check data integrity
-* on-chip debugger compatible with OpenOCD and gdb including hardware trigger module
+* on-chip debugger compatible with OpenOCD and GDB including hardware trigger module and optional authentication
**Software framework**
@@ -206,6 +206,7 @@ neorv32_top.vhd - NEORV32 PROCESSOR/SOC TOP ENTITY
├neorv32_clockgate.vhd - Generic clock gating switch
├neorv32_crc.vhd - Cyclic redundancy check unit
├neorv32_debug_dm.vhd - on-chip debugger: debug module
+├neorv32_debug_auth.vhd - on-chip debugger: authentication module
├neorv32_debug_dtm.vhd - on-chip debugger: debug transfer module
├neorv32_dma.vhd - Direct memory access controller
├neorv32_dmem.vhd - Generic processor-internal data memory
diff --git a/docs/datasheet/soc.adoc b/docs/datasheet/soc.adoc
index 8a3f75fe9..8a5010a61 100644
--- a/docs/datasheet/soc.adoc
+++ b/docs/datasheet/soc.adoc
@@ -209,8 +209,9 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
| `HART_ID` | suv(31:0) | 0x00000000 | The hart thread ID of the CPU (passed to <<_mhartid>> CSR).
| `JEDEC_ID` | suv(10:0) | 0b00000000000 | JEDEC ID; continuation codes plus vendor ID (passed to <<_mvendorid>> CSR and to the <<_debug_transport_module_dtm>>).
4+^| **<<_on_chip_debugger_ocd>>**
-| `ON_CHIP_DEBUGGER_EN` | boolean | false | Implement the on-chip debugger and the CPU debug mode.
-| `DM_LEGACY_MODE` | boolean | false | Debug module spec. version: `false` = v1.0, `true` = v0.13 (legacy mode).
+| `OCD_EN` | boolean | false | Implement the on-chip debugger and the CPU debug mode.
+| `OCD_DM_LEGACY_MODE` | boolean | false | Debug module spec. version: `false` = v1.0, `true` = v0.13 (legacy mode).
+| `OCD_AUTHENTICATION` | boolean | false | Implement <<_debug_authentication>> module.
4+^| **CPU <<_instruction_sets_and_extensions>>**
| `RISCV_ISA_C` | boolean | false | Enable <<_c_isa_extension>> (compressed instructions).
| `RISCV_ISA_E` | boolean | false | Enable <<_e_isa_extension>> (reduced register file size).
diff --git a/docs/datasheet/soc_sysinfo.adoc b/docs/datasheet/soc_sysinfo.adoc
index 2c08687c1..5fe374fec 100644
--- a/docs/datasheet/soc_sysinfo.adoc
+++ b/docs/datasheet/soc_sysinfo.adoc
@@ -77,14 +77,15 @@ Bit fields in this register are set to all-zero if the according memory system i
| `1` | `SYSINFO_SOC_XBUS` | set if external Wishbone bus interface is implemented (via top's `XBUS_EN` generic)
| `2` | `SYSINFO_SOC_MEM_INT_IMEM` | set if processor-internal DMEM implemented (via top's `MEM_INT_DMEM_EN` generic)
| `3` | `SYSINFO_SOC_MEM_INT_DMEM` | set if processor-internal IMEM is implemented (via top's `MEM_INT_IMEM_EN` generic)
-| `4` | `SYSINFO_SOC_OCD` | set if on-chip debugger is implemented (via top's `ON_CHIP_DEBUGGER_EN` generic)
+| `4` | `SYSINFO_SOC_OCD` | set if on-chip debugger is implemented (via top's `OCD_EN` generic)
| `5` | `SYSINFO_SOC_ICACHE` | set if processor-internal instruction cache is implemented (via top's `ICACHE_EN` generic)
| `6` | `SYSINFO_SOC_DCACHE` | set if processor-internal data cache is implemented (via top's `DCACHE_EN` generic)
| `7` | `SYSINFO_SOC_CLOCK_GATING` | set if CPU clock gating is implemented (via top's `CLOCK_GATING_EN` generic)
| `8` | `SYSINFO_SOC_XBUS_CACHE` | set if external bus interface cache is implemented (via top's `XBUS_CACHE_EN` generic)
| `9` | `SYSINFO_SOC_XIP` | set if XIP module is implemented (via top's `XIP_EN` generic)
| `10` | `SYSINFO_SOC_XIP_CACHE` | set if XIP cache is implemented (via top's `XIP_CACHE_EN` generic)
-| `13:11` | - | _reserved_, read as zero
+| `11` | `SYSINFO_SOC_OCD_AUTH` | set if on-chip debugger authentication is implemented (via top's `OCD_AUTHENTICATION` generic)
+| `13:12` | - | _reserved_, read as zero
| `14` | `SYSINFO_SOC_IO_DMA` | set if direct memory access controller is implemented (via top's `IO_DMA_EN` generic)
| `15` | `SYSINFO_SOC_IO_GPIO` | set if GPIO is implemented (via top's `IO_GPIO_EN` generic)
| `16` | `SYSINFO_SOC_IO_MTIME` | set if MTIME is implemented (via top's `IO_MTIME_EN` generic)
diff --git a/docs/figures/neorv32_ocd_complex.png b/docs/figures/neorv32_ocd_complex.png
index 2d8a08ffe..7b5459a75 100644
Binary files a/docs/figures/neorv32_ocd_complex.png and b/docs/figures/neorv32_ocd_complex.png differ
diff --git a/docs/figures/neorv32_processor.png b/docs/figures/neorv32_processor.png
index 59e012418..058d30967 100644
Binary files a/docs/figures/neorv32_processor.png and b/docs/figures/neorv32_processor.png differ
diff --git a/rtl/core/neorv32_debug_auth.vhd b/rtl/core/neorv32_debug_auth.vhd
new file mode 100644
index 000000000..1e3e21b25
--- /dev/null
+++ b/rtl/core/neorv32_debug_auth.vhd
@@ -0,0 +1,75 @@
+-- ================================================================================ --
+-- NEORV32 SoC - RISC-V-Compatible Authentication Module for the On-Chip Debugger --
+-- -------------------------------------------------------------------------------- --
+-- Note that this module (in its default state) just provides a very simple and --
+-- UNSECUR authentication mechanism that is meant as an example to showcase the --
+-- interface. Users should replace this module to implement a custom authentication --
+-- (and SECURE) mechanism. --
+-- -------------------------------------------------------------------------------- --
+-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
+-- Copyright (c) NEORV32 contributors. --
+-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
+-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
+-- SPDX-License-Identifier: BSD-3-Clause --
+-- ================================================================================ --
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+library neorv32;
+use neorv32.neorv32_package.all;
+
+entity neorv32_debug_auth is
+ port (
+ -- global control --
+ clk_i : in std_ulogic; -- global clock
+ rstn_i : in std_ulogic; -- global reset, low-active, asynchronous
+ -- register interface --
+ we_i : in std_ulogic; -- write data when high
+ re_i : in std_ulogic; -- read data has been consumed by the debugger when high
+ wdata_i : in std_ulogic_vector(31 downto 0); -- write data (from debugger)
+ rdata_o : out std_ulogic_vector(31 downto 0); -- read data (to debugger)
+ -- status --
+ enable_i : in std_ulogic; -- authenticator enabled when high; reset & clear authentication when low
+ busy_o : out std_ulogic; -- authenticator is busy when high; no further read/write accesses
+ valid_o : out std_ulogic -- high when authentication passed; unlocks the on-chip debugger
+ );
+end neorv32_debug_auth;
+
+architecture neorv32_debug_auth_rtl of neorv32_debug_auth is
+
+ signal authenticated : std_ulogic;
+
+begin
+
+ -- Warn about Default Authenticator -------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ assert false report "[NEORV32] OCD: using DEFAULT authenticator. Replace by custom module." severity warning;
+
+
+ -- Exemplary Authentication Mechanism -----------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ dm_controller: process(rstn_i, clk_i)
+ begin
+ if (rstn_i = '0') then
+ authenticated <= '0';
+ elsif rising_edge(clk_i) then
+ if (enable_i = '0') then
+ authenticated <= '0'; -- clear authentication when disabled
+ elsif (we_i = '1') then
+ authenticated <= wdata_i(0); -- just write a 1 to authenticate
+ end if;
+ end if;
+ end process dm_controller;
+
+ -- authenticator busy --
+ busy_o <= '0'; -- this simple authenticator is always ready
+
+ -- authentication passed --
+ valid_o <= authenticated;
+
+ -- read data --
+ rdata_o <= (others => '0'); -- there is nothing to read here
+
+
+end neorv32_debug_auth_rtl;
diff --git a/rtl/core/neorv32_debug_dm.vhd b/rtl/core/neorv32_debug_dm.vhd
index 1a66de2a6..d2fd3ad73 100644
--- a/rtl/core/neorv32_debug_dm.vhd
+++ b/rtl/core/neorv32_debug_dm.vhd
@@ -1,7 +1,7 @@
-- ================================================================================ --
-- NEORV32 SoC - RISC-V-Compatible Debug Module (DM) --
-- -------------------------------------------------------------------------------- --
--- Execution-based debugging for a single hart only. --
+-- Execution-based debugger compatible to the "Minimal RISC-V Debug Specification". --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@@ -19,8 +19,9 @@ use neorv32.neorv32_package.all;
entity neorv32_debug_dm is
generic (
- CPU_BASE_ADDR : std_ulogic_vector(31 downto 0);
- LEGACY_MODE : boolean -- false = spec. v1.0, true = spec. v0.13
+ CPU_BASE_ADDR : std_ulogic_vector(31 downto 0); -- base address for the memory-mapped CPU interface registers
+ LEGACY_MODE : boolean; -- false = spec. v1.0, true = spec. v0.13
+ AUTHENTICATOR : boolean -- implement authentication module when true
);
port (
-- global control --
@@ -41,17 +42,21 @@ end neorv32_debug_dm;
architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
- -- **********************************************************
- -- DM Memory Layout
- -- **********************************************************
- constant dm_code_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"00"); -- base address of code ROM (park loop)
- constant dm_pbuf_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"40"); -- base address of program buffer (PBUF)
- constant dm_data_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"80"); -- base address of abstract data buffer (DATA)
- constant dm_sreg_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"C0"); -- base address of status register (SREG)
+ -- memory map --
+ constant dm_code_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"00"); -- code ROM (park loop)
+ constant dm_pbuf_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"40"); -- program buffer (PBUF)
+ constant dm_data_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"80"); -- abstract data buffer (DATA)
+ constant dm_sreg_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"C0"); -- status register (SREG)
- -- **********************************************************
+ -- rv32i instruction prototypes --
+ constant instr_nop_c : std_ulogic_vector(31 downto 0) := x"00000013"; -- nop
+ constant instr_lw_c : std_ulogic_vector(31 downto 0) := x"00002003"; -- lw zero, 0(zero)
+ constant instr_sw_c : std_ulogic_vector(31 downto 0) := x"00002023"; -- sw zero, 0(zero)
+ constant instr_ebreak_c : std_ulogic_vector(31 downto 0) := x"00100073"; -- ebreak
+
+ -- ----------------------------------------------------------
-- DMI Access
- -- **********************************************************
+ -- ----------------------------------------------------------
-- available DMI registers --
constant addr_data0_c : std_ulogic_vector(6 downto 0) := "0000100";
@@ -64,18 +69,12 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
constant addr_nextdm_c : std_ulogic_vector(6 downto 0) := "0011101";
constant addr_progbuf0_c : std_ulogic_vector(6 downto 0) := "0100000";
constant addr_progbuf1_c : std_ulogic_vector(6 downto 0) := "0100001";
+ constant addr_authdata_c : std_ulogic_vector(6 downto 0) := "0110000";
constant addr_sbcs_c : std_ulogic_vector(6 downto 0) := "0111000";
constant addr_haltsum0_c : std_ulogic_vector(6 downto 0) := "1000000";
- -- RISC-V 32-bit instruction prototypes --
- constant instr_nop_c : std_ulogic_vector(31 downto 0) := x"00000013"; -- nop
- constant instr_lw_c : std_ulogic_vector(31 downto 0) := x"00002003"; -- lw zero, 0(zero)
- constant instr_sw_c : std_ulogic_vector(31 downto 0) := x"00002023"; -- sw zero, 0(zero)
- constant instr_ebreak_c : std_ulogic_vector(31 downto 0) := x"00100073"; -- ebreak
-
-- DMI access --
- signal dmi_wren : std_ulogic;
- signal dmi_rden : std_ulogic;
+ signal dmi_wren, dmi_wren_auth, dmi_rden, dmi_rden_auth : std_ulogic;
-- debug module DMI registers / access --
type progbuf_t is array (0 to 1) of std_ulogic_vector(31 downto 0);
@@ -102,15 +101,12 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
type cpu_progbuf_t is array (0 to 3) of std_ulogic_vector(31 downto 0);
signal cpu_progbuf : cpu_progbuf_t;
- -- **********************************************************
+ -- ----------------------------------------------------------
-- DM Control
- -- **********************************************************
+ -- ----------------------------------------------------------
-- DM configuration --
- constant nscratch_c : std_ulogic_vector(3 downto 0) := "0001"; -- number of dscratch registers in CPU (=1)
- constant datasize_c : std_ulogic_vector(3 downto 0) := "0001"; -- number of data registers in memory/CSR space (=1)
constant dataaddr_c : std_ulogic_vector(11 downto 0) := dm_data_base_c(11 downto 0); -- signed base address of data registers in memory/CSR space
- constant dataaccess_c : std_ulogic := '1'; -- 1: abstract data is memory-mapped, 0: abstract data is CSR-mapped
constant dm_version_c : std_ulogic_vector(3 downto 0) := cond_sel_suv_f(LEGACY_MODE, "0010", "0011"); -- version: v0.13 / v1.0
-- debug module controller --
@@ -133,9 +129,19 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
end record;
signal dm_ctrl : dm_ctrl_t;
- -- **********************************************************
+ -- authentication --
+ type auth_t is record
+ busy : std_ulogic; -- authenticator is busy when set
+ valid : std_ulogic; -- authentication successful
+ reset : std_ulogic; -- reset authentication (sync, high-active)
+ re, we : std_ulogic; -- data interface read/write enable
+ rdata : std_ulogic_vector(31 downto 0); -- read data
+ end record;
+ signal auth : auth_t;
+
+ -- ----------------------------------------------------------
-- CPU Bus and Debug Interfaces
- -- **********************************************************
+ -- ----------------------------------------------------------
-- status and control register - bits --
-- for write access we only care about the actual BYTE WRITE ACCESSES! --
@@ -186,16 +192,21 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
begin
- -- Info -----------------------------------------------------------------------------------
+ -- Configuration Info ---------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
- assert not (LEGACY_MODE = true) report "[NEORV32] OCD DM compatible to debug spec. version 0.13" severity note;
- assert not (LEGACY_MODE = false) report "[NEORV32] OCD DM compatible to debug spec. version 1.0" severity note;
+ assert not (LEGACY_MODE = true) report "[NEORV32] OCD: DM compatible to debug spec. version 0.13" severity note;
+ assert not (LEGACY_MODE = false) report "[NEORV32] OCD: DM compatible to debug spec. version 1.0" severity note;
+ assert not (AUTHENTICATOR = false) report "[NEORV32] OCD: authentication enabled." severity note;
-- DMI Access -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
+ -- any access --
dmi_wren <= '1' when (dmi_req_i.op = dmi_req_wr_c) else '0';
dmi_rden <= '1' when (dmi_req_i.op = dmi_req_rd_c) else '0';
+ -- authenticated access --
+ dmi_wren_auth <= dmi_wren when (not AUTHENTICATOR) or (auth.valid = '1') else '0';
+ dmi_rden_auth <= dmi_rden when (not AUTHENTICATOR) or (auth.valid = '1') else '0';
-- Debug Module Command Controller --------------------------------------------------------
@@ -232,7 +243,7 @@ begin
when CMD_IDLE => -- wait for new abstract command
-- ------------------------------------------------------------
- if (dmi_wren = '1') then -- valid DM write access
+ if (dmi_wren_auth = '1') then -- valid and authenticated DM write access
if (dmi_req_i.addr = addr_command_c) then
if (dm_ctrl.cmderr = "000") then -- only execute if no error
dm_ctrl.state <= CMD_CHECK;
@@ -404,72 +415,63 @@ begin
dm_reg.clr_acc_err <= '0';
dm_reg.autoexec_wr <= '0';
- -- DMI access --
- if (dmi_wren = '1') then -- valid DMI write request
-
- -- debug module control --
- if (dmi_req_i.addr = addr_dmcontrol_c) then
+ -- debug module control --
+ if (dmi_req_i.addr = addr_dmcontrol_c) then
+ if (dmi_wren_auth = '1') then -- valid and authenticated DM write access
dm_reg.halt_req <= dmi_req_i.data(31); -- haltreq (-/w): write 1 to request halt; has to be cleared again by debugger
dm_reg.resume_req <= dmi_req_i.data(30); -- resumereq (-/w1): write 1 to request resume; auto-clears
dm_reg.reset_ack <= dmi_req_i.data(28); -- ackhavereset (-/w1): write 1 to ACK reset; auto-clears
dm_reg.dmcontrol_ndmreset <= dmi_req_i.data(1); -- ndmreset (r/w): SoC reset when high
+ end if;
+ if (dmi_wren = '1') then -- valid DM write access (may be unauthenticated)
dm_reg.dmcontrol_dmactive <= dmi_req_i.data(0); -- dmactive (r/w): DM reset when low
end if;
+ end if;
- -- write abstract command --
- if (dmi_req_i.addr = addr_command_c) then
- if (dm_ctrl.busy = '0') and (dm_ctrl.cmderr = "000") then -- idle and no errors yet
- dm_reg.command <= dmi_req_i.data;
- end if;
- end if;
+ -- write abstract command (only when idle and no error yet) --
+ if (dmi_req_i.addr = addr_command_c) and (dmi_wren_auth = '1') and (dm_ctrl.busy = '0') and (dm_ctrl.cmderr = "000") then
+ dm_reg.command <= dmi_req_i.data;
+ end if;
- -- write abstract command autoexec --
- if (dmi_req_i.addr = addr_abstractauto_c) then
- if (dm_ctrl.busy = '0') then -- idle and no errors yet
- dm_reg.abstractauto_autoexecdata <= dmi_req_i.data(0);
- dm_reg.abstractauto_autoexecprogbuf(0) <= dmi_req_i.data(16);
- dm_reg.abstractauto_autoexecprogbuf(1) <= dmi_req_i.data(17);
- end if;
- end if;
+ -- write abstract command autoexec (only when idle) --
+ if (dmi_req_i.addr = addr_abstractauto_c) and (dmi_wren_auth = '1') and (dm_ctrl.busy = '0') then
+ dm_reg.abstractauto_autoexecdata <= dmi_req_i.data(0);
+ dm_reg.abstractauto_autoexecprogbuf(0) <= dmi_req_i.data(16);
+ dm_reg.abstractauto_autoexecprogbuf(1) <= dmi_req_i.data(17);
+ end if;
- -- auto execution trigger --
- if ((dmi_req_i.addr = addr_data0_c) and (dm_reg.abstractauto_autoexecdata = '1')) or
- ((dmi_req_i.addr = addr_progbuf0_c) and (dm_reg.abstractauto_autoexecprogbuf(0) = '1')) or
- ((dmi_req_i.addr = addr_progbuf1_c) and (dm_reg.abstractauto_autoexecprogbuf(1) = '1')) then
+ -- auto execution trigger --
+ if ((dmi_req_i.addr = addr_data0_c) and (dm_reg.abstractauto_autoexecdata = '1')) or
+ ((dmi_req_i.addr = addr_progbuf0_c) and (dm_reg.abstractauto_autoexecprogbuf(0) = '1')) or
+ ((dmi_req_i.addr = addr_progbuf1_c) and (dm_reg.abstractauto_autoexecprogbuf(1) = '1')) then
+ if (dmi_wren_auth = '1') then -- valid and authenticated DM write access
dm_reg.autoexec_wr <= '1';
end if;
+ end if;
- -- acknowledge command error --
- if (dmi_req_i.addr = addr_abstractcs_c) then
- if (dmi_req_i.data(10 downto 8) = "111") then
- dm_reg.clr_acc_err <= '1';
- end if;
- end if;
+ -- acknowledge command error --
+ if (dmi_req_i.addr = addr_abstractcs_c) and (dmi_wren_auth = '1') and (dmi_req_i.data(10 downto 8) = "111") then
+ dm_reg.clr_acc_err <= '1';
+ end if;
- -- write program buffer --
- if (dmi_req_i.addr(dmi_req_i.addr'left downto 1) = addr_progbuf0_c(dmi_req_i.addr'left downto 1)) then
- if (dm_ctrl.busy = '0') then -- idle
- if (dmi_req_i.addr(0) = addr_progbuf0_c(0)) then
- dm_reg.progbuf(0) <= dmi_req_i.data;
- else
- dm_reg.progbuf(1) <= dmi_req_i.data;
- end if;
- end if;
- end if;
+ -- write program buffer 0 (only when idle) --
+ if (dmi_req_i.addr = addr_progbuf0_c) and (dmi_wren_auth = '1') and (dm_ctrl.busy = '0') then
+ dm_reg.progbuf(0) <= dmi_req_i.data;
+ end if;
- -- invalid access while command is executing --
- if (dm_ctrl.busy = '1') then -- busy
- if (dmi_req_i.addr = addr_abstractcs_c) or
- (dmi_req_i.addr = addr_command_c) or
- (dmi_req_i.addr = addr_abstractauto_c) or
- (dmi_req_i.addr = addr_data0_c) or
- (dmi_req_i.addr = addr_progbuf0_c) or
- (dmi_req_i.addr = addr_progbuf1_c) then
- dm_reg.wr_acc_err <= '1';
- end if;
- end if;
+ -- write program buffer 1 (only when idle) --
+ if (dmi_req_i.addr = addr_progbuf1_c) and (dmi_wren_auth = '1') and (dm_ctrl.busy = '0') then
+ dm_reg.progbuf(1) <= dmi_req_i.data;
+ end if;
+ -- invalid access while command is executing --
+ if (dmi_wren_auth = '1') and (dm_ctrl.busy = '1') and -- write access while busy
+ ((dmi_req_i.addr = addr_abstractcs_c) or (dmi_req_i.addr = addr_command_c) or
+ (dmi_req_i.addr = addr_abstractauto_c) or (dmi_req_i.addr = addr_data0_c) or
+ (dmi_req_i.addr = addr_progbuf0_c) or (dmi_req_i.addr = addr_progbuf1_c)) then
+ dm_reg.wr_acc_err <= '1';
end if;
+
end if;
end process dmi_write_access;
@@ -477,14 +479,14 @@ begin
-- Direct Control -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- write to abstract data register --
- dci.data_we <= '1' when (dmi_wren = '1') and (dmi_req_i.addr = addr_data0_c) and (dm_ctrl.busy = '0') else '0';
+ dci.data_we <= '1' when (dmi_wren_auth = '1') and (dmi_req_i.addr = addr_data0_c) and (dm_ctrl.busy = '0') else '0';
-- CPU halt/resume request --
- cpu_halt_req_o <= dm_reg.halt_req and dm_reg.dmcontrol_dmactive;
+ cpu_halt_req_o <= dm_reg.halt_req and dm_reg.dmcontrol_dmactive when ((not AUTHENTICATOR) or (auth.valid = '1')) else '0';
dci.resume_req <= dm_ctrl.hart_resume_req; -- active until explicitly cleared
-- SoC reset --
- cpu_ndmrstn_o <= '0' when (dm_reg.dmcontrol_ndmreset = '1') and (dm_reg.dmcontrol_dmactive = '1') else '1'; -- to processor's reset generator
+ cpu_ndmrstn_o <= '0' when (dm_reg.dmcontrol_ndmreset = '1') and (dm_reg.dmcontrol_dmactive = '1') and ((not AUTHENTICATOR) or (auth.valid = '1')) else '1';
-- construct program buffer array for CPU access --
cpu_progbuf(0) <= dm_ctrl.ldsw_progbuf; -- pseudo program buffer for GPR access
@@ -503,136 +505,147 @@ begin
dm_reg.rd_acc_err <= '0';
dm_reg.autoexec_rd <= '0';
elsif rising_edge(clk_i) then
- dmi_rsp_o.ack <= dmi_wren or dmi_rden; -- always ACK any request
- dmi_rsp_o.data <= (others => '0'); -- default
- dm_reg.rd_acc_err <= '0';
- dm_reg.autoexec_rd <= '0';
-
+ dmi_rsp_o.ack <= dmi_wren or dmi_rden; -- always ACK any request
+ dmi_rsp_o.data <= (others => '0'); -- default
case dmi_req_i.addr is
-- debug module status register --
when addr_dmstatus_c =>
- dmi_rsp_o.data(31 downto 23) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(22) <= '1'; -- impebreak (r/-): there is an implicit ebreak instruction after the visible program buffer
- dmi_rsp_o.data(21 downto 20) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(19) <= dm_ctrl.hart_reset; -- allhavereset (r/-): there is only one hart that can be reset
- dmi_rsp_o.data(18) <= dm_ctrl.hart_reset; -- anyhavereset (r/-): there is only one hart that can be reset
- dmi_rsp_o.data(17) <= dm_ctrl.hart_resume_ack; -- allresumeack (r/-): there is only one hart that can acknowledge resume request
- dmi_rsp_o.data(16) <= dm_ctrl.hart_resume_ack; -- anyresumeack (r/-): there is only one hart that can acknowledge resume request
- dmi_rsp_o.data(15) <= '0'; -- allnonexistent (r/-): there is only one hart that is always existent
- dmi_rsp_o.data(14) <= '0'; -- anynonexistent (r/-): there is only one hart that is always existent
- dmi_rsp_o.data(13) <= dm_reg.dmcontrol_ndmreset; -- allunavail (r/-): there is only one hart that is unavailable during reset
- dmi_rsp_o.data(12) <= dm_reg.dmcontrol_ndmreset; -- anyunavail (r/-): there is only one hart that is unavailable during reset
- dmi_rsp_o.data(11) <= not dm_ctrl.hart_halted; -- allrunning (r/-): there is only one hart that can be RUNNING or HALTED
- dmi_rsp_o.data(10) <= not dm_ctrl.hart_halted; -- anyrunning (r/-): there is only one hart that can be RUNNING or HALTED
- dmi_rsp_o.data(9) <= dm_ctrl.hart_halted; -- allhalted (r/-): there is only one hart that can be RUNNING or HALTED
- dmi_rsp_o.data(8) <= dm_ctrl.hart_halted; -- anyhalted (r/-): there is only one hart that can be RUNNING or HALTED
- dmi_rsp_o.data(7) <= '1'; -- authenticated (r/-): authentication passed since there is no authentication
- dmi_rsp_o.data(6) <= '0'; -- authbusy (r/-): always ready since there is no authentication
- dmi_rsp_o.data(5) <= '0'; -- hasresethaltreq (r/-): halt-on-reset not implemented
- dmi_rsp_o.data(4) <= '0'; -- confstrptrvalid (r/-): no configuration string available
- dmi_rsp_o.data(3 downto 0) <= dm_version_c; -- version (r/-): DM spec. version
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data(31 downto 23) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(22) <= '1'; -- impebreak (r/-): there is an implicit ebreak instruction after the visible program buffer
+ dmi_rsp_o.data(21 downto 20) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(19) <= dm_ctrl.hart_reset; -- allhavereset (r/-): there is only one hart that can be reset
+ dmi_rsp_o.data(18) <= dm_ctrl.hart_reset; -- anyhavereset (r/-): there is only one hart that can be reset
+ dmi_rsp_o.data(17) <= dm_ctrl.hart_resume_ack; -- allresumeack (r/-): there is only one hart that can acknowledge resume request
+ dmi_rsp_o.data(16) <= dm_ctrl.hart_resume_ack; -- anyresumeack (r/-): there is only one hart that can acknowledge resume request
+ dmi_rsp_o.data(15) <= '0'; -- allnonexistent (r/-): there is only one hart that is always existent
+ dmi_rsp_o.data(14) <= '0'; -- anynonexistent (r/-): there is only one hart that is always existent
+ dmi_rsp_o.data(13) <= dm_reg.dmcontrol_ndmreset; -- allunavail (r/-): there is only one hart that is unavailable during reset
+ dmi_rsp_o.data(12) <= dm_reg.dmcontrol_ndmreset; -- anyunavail (r/-): there is only one hart that is unavailable during reset
+ dmi_rsp_o.data(11) <= not dm_ctrl.hart_halted; -- allrunning (r/-): there is only one hart that can be RUNNING or HALTED
+ dmi_rsp_o.data(10) <= not dm_ctrl.hart_halted; -- anyrunning (r/-): there is only one hart that can be RUNNING or HALTED
+ dmi_rsp_o.data(9) <= dm_ctrl.hart_halted; -- allhalted (r/-): there is only one hart that can be RUNNING or HALTED
+ dmi_rsp_o.data(8) <= dm_ctrl.hart_halted; -- anyhalted (r/-): there is only one hart that can be RUNNING or HALTED
+ dmi_rsp_o.data(5) <= '0'; -- hasresethaltreq (r/-): halt-on-reset not implemented
+ dmi_rsp_o.data(4) <= '0'; -- confstrptrvalid (r/-): no configuration string available
+ end if;
+ dmi_rsp_o.data(7) <= auth.valid; -- authenticated (r/-): authentication successful when set
+ dmi_rsp_o.data(6) <= auth.busy; -- authbusy (r/-): wait for authenticator operation when set
+ dmi_rsp_o.data(3 downto 0) <= dm_version_c; -- version (r/-): DM spec. version
-- debug module control --
when addr_dmcontrol_c =>
- dmi_rsp_o.data(31) <= '0'; -- haltreq (-/w): write-only
- dmi_rsp_o.data(30) <= '0'; -- resumereq (-/w1): write-only
- dmi_rsp_o.data(29) <= '0'; -- hartreset (r/w): not supported
- dmi_rsp_o.data(28) <= '0'; -- ackhavereset (-/w1): write-only
- dmi_rsp_o.data(27) <= '0'; -- reserved (r/-)
- dmi_rsp_o.data(26) <= '0'; -- hasel (r/-) - only a single hart can be selected at once
- dmi_rsp_o.data(25 downto 16) <= (others => '0'); -- hartsello (r/-) - there is only one hart
- dmi_rsp_o.data(15 downto 6) <= (others => '0'); -- hartselhi (r/-) - there is only one hart
- dmi_rsp_o.data(5 downto 4) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(3) <= '0'; -- setresethaltreq (-/w1): halt-on-reset request - halt-on-reset not implemented
- dmi_rsp_o.data(2) <= '0'; -- clrresethaltreq (-/w1): halt-on-reset ack - halt-on-reset not implemented
- dmi_rsp_o.data(1) <= dm_reg.dmcontrol_ndmreset; -- ndmreset (r/w): soc reset
- dmi_rsp_o.data(0) <= dm_reg.dmcontrol_dmactive; -- dmactive (r/w): DM reset
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data(31) <= '0'; -- haltreq (-/w): write-only
+ dmi_rsp_o.data(30) <= '0'; -- resumereq (-/w1): write-only
+ dmi_rsp_o.data(29) <= '0'; -- hartreset (r/w): not supported
+ dmi_rsp_o.data(28) <= '0'; -- ackhavereset (-/w1): write-only
+ dmi_rsp_o.data(27) <= '0'; -- reserved (r/-)
+ dmi_rsp_o.data(26) <= '0'; -- hasel (r/-) - only a single hart can be selected at once
+ dmi_rsp_o.data(25 downto 16) <= (others => '0'); -- hartsello (r/-) - there is only one hart
+ dmi_rsp_o.data(15 downto 6) <= (others => '0'); -- hartselhi (r/-) - there is only one hart
+ dmi_rsp_o.data(5 downto 4) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(3) <= '0'; -- setresethaltreq (-/w1): halt-on-reset request - halt-on-reset not implemented
+ dmi_rsp_o.data(2) <= '0'; -- clrresethaltreq (-/w1): halt-on-reset ack - halt-on-reset not implemented
+ dmi_rsp_o.data(1) <= dm_reg.dmcontrol_ndmreset; -- ndmreset (r/w): soc reset
+ end if;
+ dmi_rsp_o.data(0) <= dm_reg.dmcontrol_dmactive; -- dmactive (r/w): DM reset
-- hart info --
when addr_hartinfo_c =>
- dmi_rsp_o.data(31 downto 24) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(23 downto 20) <= nscratch_c; -- nscratch (r/-): number of dscratch CSRs
- dmi_rsp_o.data(19 downto 17) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(16) <= dataaccess_c; -- dataaccess (r/-): 1: data registers are memory-mapped, 0: data registers are CSR-mapped
- dmi_rsp_o.data(15 downto 12) <= datasize_c; -- datasize (r/-): number data registers in memory/CSR space
- dmi_rsp_o.data(11 downto 0) <= dataaddr_c(11 downto 0); -- dataaddr (r/-): data registers base address (memory/CSR)
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data(31 downto 24) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(23 downto 20) <= "0001"; -- nscratch (r/-): number of dscratch CSRs = 1
+ dmi_rsp_o.data(19 downto 17) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(16) <= '1'; -- dataaccess (r/-): data registers are memory-mapped
+ dmi_rsp_o.data(15 downto 12) <= "0001"; -- datasize (r/-): number data registers in memory/CSR space = 1
+ dmi_rsp_o.data(11 downto 0) <= dataaddr_c(11 downto 0); -- dataaddr (r/-): data registers base address (memory/CSR)
+ end if;
-- abstract control and status --
when addr_abstractcs_c =>
- dmi_rsp_o.data(31 downto 24) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(28 downto 24) <= "00010"; -- progbufsize (r/-): number of words in program buffer = 2
- dmi_rsp_o.data(12) <= dm_ctrl.busy; -- busy (r/-): abstract command in progress (1) / idle (0)
- dmi_rsp_o.data(11) <= '1'; -- relaxedpriv (r/-): PMP rules are ignored when in debug-mode
- dmi_rsp_o.data(10 downto 8) <= dm_ctrl.cmderr; -- cmderr (r/w1c): any error during execution?
- dmi_rsp_o.data(7 downto 4) <= (others => '0'); -- reserved (r/-)
- dmi_rsp_o.data(3 downto 0) <= "0001"; -- datacount (r/-): number of implemented data registers = 1
-
--- -- abstract command (-/w) --
--- when addr_command_c =>
--- dmi_rsp_o.data <= (others => '0'); -- register is write-only
-
- -- abstract command autoexec (r/w) --
- when addr_abstractauto_c =>
- dmi_rsp_o.data(0) <= dm_reg.abstractauto_autoexecdata; -- autoexecdata(0): read/write access to data0 triggers execution of program buffer
- dmi_rsp_o.data(16) <= dm_reg.abstractauto_autoexecprogbuf(0); -- autoexecprogbuf(0): read/write access to progbuf0 triggers execution of program buffer
- dmi_rsp_o.data(17) <= dm_reg.abstractauto_autoexecprogbuf(1); -- autoexecprogbuf(1): read/write access to progbuf1 triggers execution of program buffer
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data(31 downto 24) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(28 downto 24) <= "00010"; -- progbufsize (r/-): number of words in program buffer = 2
+ dmi_rsp_o.data(12) <= dm_ctrl.busy; -- busy (r/-): abstract command in progress (1) / idle (0)
+ dmi_rsp_o.data(11) <= '1'; -- relaxedpriv (r/-): PMP rules are ignored when in debug-mode
+ dmi_rsp_o.data(10 downto 8) <= dm_ctrl.cmderr; -- cmderr (r/w1c): any error during execution?
+ dmi_rsp_o.data(7 downto 4) <= (others => '0'); -- reserved (r/-)
+ dmi_rsp_o.data(3 downto 0) <= "0001"; -- datacount (r/-): number of implemented data registers = 1
+ end if;
--- -- next debug module (r/-) --
--- when addr_nextdm_c =>
--- dmi_rsp_o.data <= (others => '0'); -- this is the only DM
+ -- abstract command autoexec --
+ when addr_abstractauto_c =>
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data(0) <= dm_reg.abstractauto_autoexecdata; -- autoexecdata(0): read/write access to data0 triggers execution of program buffer
+ dmi_rsp_o.data(16) <= dm_reg.abstractauto_autoexecprogbuf(0); -- autoexecprogbuf(0): read/write access to progbuf0 triggers execution of program buffer
+ dmi_rsp_o.data(17) <= dm_reg.abstractauto_autoexecprogbuf(1); -- autoexecprogbuf(1): read/write access to progbuf1 triggers execution of program buffer
+ end if;
- -- abstract data 0 (r/w) --
+ -- abstract data 0 --
when addr_data0_c =>
- dmi_rsp_o.data <= dci.data_reg;
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data <= dci.data_reg;
+ end if;
- -- program buffer (r/w) --
+ -- program buffer 0 --
when addr_progbuf0_c =>
- if (LEGACY_MODE = true) then dmi_rsp_o.data <= dm_reg.progbuf(0); else dmi_rsp_o.data <= (others => '0'); end if; -- program buffer 0
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ if LEGACY_MODE then
+ dmi_rsp_o.data <= dm_reg.progbuf(0);
+ end if;
+ end if;
+
+ -- program buffer 1 --
when addr_progbuf1_c =>
- if (LEGACY_MODE = true) then dmi_rsp_o.data <= dm_reg.progbuf(1); else dmi_rsp_o.data <= (others => '0'); end if; -- program buffer 1
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ if LEGACY_MODE then
+ dmi_rsp_o.data <= dm_reg.progbuf(1);
+ end if;
+ end if;
--- -- system bus access control and status (r/-) --
--- when addr_sbcs_c =>
--- dmi_rsp_o.data <= (others => '0'); -- system bus access not implemented
+ -- authentication --
+ when addr_authdata_c =>
+ dmi_rsp_o.data <= auth.rdata;
- -- halt summary 0 (r/-) --
+ -- halt summary 0 --
when addr_haltsum0_c =>
- dmi_rsp_o.data(0) <= dm_ctrl.hart_halted; -- hart 0 is halted
+ if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
+ dmi_rsp_o.data(0) <= dm_ctrl.hart_halted; -- hart 0 is halted
+ end if;
- -- not implemented (r/-) --
- when others =>
+ -- not implemented or read-only-zero --
+ when others => -- addr_sbcs_c, addr_nextdm_c, addr_command_c
dmi_rsp_o.data <= (others => '0');
end case;
-- invalid read access while command is executing --
-- ------------------------------------------------------------
- if (dmi_rden = '1') then -- valid DMI read request
- if (dm_ctrl.busy = '1') then -- busy
- if (dmi_req_i.addr = addr_data0_c) or
- (dmi_req_i.addr = addr_progbuf0_c) or
- (dmi_req_i.addr = addr_progbuf1_c) then
- dm_reg.rd_acc_err <= '1';
- end if;
- end if;
+ if (dmi_rden_auth = '1') and (dm_ctrl.busy = '1') and -- write while busy
+ ((dmi_req_i.addr = addr_data0_c) or (dmi_req_i.addr = addr_progbuf0_c) or (dmi_req_i.addr = addr_progbuf1_c)) then
+ dm_reg.rd_acc_err <= '1';
+ else
+ dm_reg.rd_acc_err <= '0';
end if;
-- auto execution trigger --
-- ------------------------------------------------------------
- if (dmi_rden = '1') then -- valid DMI read request
- if ((dmi_req_i.addr = addr_data0_c) and (dm_reg.abstractauto_autoexecdata = '1')) or
- ((dmi_req_i.addr = addr_progbuf0_c) and (dm_reg.abstractauto_autoexecprogbuf(0) = '1')) or
- ((dmi_req_i.addr = addr_progbuf1_c) and (dm_reg.abstractauto_autoexecprogbuf(1) = '1')) then
- dm_reg.autoexec_rd <= '1';
- end if;
+ if (dmi_rden_auth = '1') and
+ (((dmi_req_i.addr = addr_data0_c) and (dm_reg.abstractauto_autoexecdata = '1')) or
+ ((dmi_req_i.addr = addr_progbuf0_c) and (dm_reg.abstractauto_autoexecprogbuf(0) = '1')) or
+ ((dmi_req_i.addr = addr_progbuf1_c) and (dm_reg.abstractauto_autoexecprogbuf(1) = '1'))) then
+ dm_reg.autoexec_rd <= '1';
+ else
+ dm_reg.autoexec_rd <= '0';
end if;
end if;
end process dmi_read_access;
- -- Bus Access ----------------------------------------------------------------------------
+ -- Bus Access (from CPU) ------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_access: process(rstn_i, clk_i)
begin
@@ -662,7 +675,7 @@ begin
dci.execute_ack <= '0';
dci.exception_ack <= '0';
if (bus_req_i.addr(7 downto 6) = dm_sreg_base_c(7 downto 6)) and (wren = '1') then
- dci.halt_ack <= bus_req_i.ben(sreg_halt_ack_c/8); -- [NOTE] use the individual BYTE ENABLES and not the actual write data
+ dci.halt_ack <= bus_req_i.ben(sreg_halt_ack_c/8); -- [NOTE] use individual BYTE ENABLES and not the actual write data
dci.resume_ack <= bus_req_i.ben(sreg_resume_ack_c/8);
dci.execute_ack <= bus_req_i.ben(sreg_execute_ack_c/8);
dci.exception_ack <= bus_req_i.ben(sreg_exception_ack_c/8);
@@ -691,4 +704,37 @@ begin
wren <= accen and ( bus_req_i.rw);
+ -- Authentication Module ------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ authenticator_enabled:
+ if AUTHENTICATOR generate
+ neorv32_debug_auth_inst: entity neorv32.neorv32_debug_auth
+ port map (
+ -- global control --
+ clk_i => clk_i,
+ rstn_i => rstn_i,
+ -- register interface --
+ we_i => auth.we,
+ re_i => auth.re,
+ wdata_i => dmi_req_i.data,
+ rdata_o => auth.rdata,
+ -- status --
+ enable_i => dm_reg.dmcontrol_dmactive, -- disable and reset authentication when DM gets disabled
+ busy_o => auth.busy,
+ valid_o => auth.valid
+ );
+ auth.re <= '1' when (dmi_rden = '1') and (dmi_req_i.addr = addr_authdata_c) else '0';
+ auth.we <= '1' when (dmi_wren = '1') and (dmi_req_i.addr = addr_authdata_c) else '0';
+ end generate;
+
+ authenticator_disabled:
+ if not AUTHENTICATOR generate
+ auth.busy <= '0';
+ auth.valid <= '1'; -- always authenticated
+ auth.re <= '0';
+ auth.we <= '0';
+ auth.rdata <= (others => '0');
+ end generate;
+
+
end neorv32_debug_dm_rtl;
diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd
index bbd4de943..9e40860ed 100644
--- a/rtl/core/neorv32_package.vhd
+++ b/rtl/core/neorv32_package.vhd
@@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
- constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100504"; -- hardware version
+ constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100505"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width
@@ -123,6 +123,7 @@ package neorv32_package is
-- -------------------------------------------------------------------------------------------
-- bus request --
type bus_req_t is record
+ -- in-band signals --
addr : std_ulogic_vector(31 downto 0); -- access address
data : std_ulogic_vector(31 downto 0); -- write data
ben : std_ulogic_vector(3 downto 0); -- byte enable
@@ -131,7 +132,8 @@ package neorv32_package is
src : std_ulogic; -- access source (1=instruction fetch, 0=data access)
priv : std_ulogic; -- set if privileged (machine-mode) access
rvso : std_ulogic; -- set if reservation set operation (atomic LR/SC)
- fence : std_ulogic; -- set if fence(.i) operation, single-shot, independent of STB
+ -- out-of-band signals --
+ fence : std_ulogic; -- set if fence(.i) operation, single-shot
end record;
-- bus response --
@@ -674,8 +676,9 @@ package neorv32_package is
JEDEC_ID : std_ulogic_vector(10 downto 0) := "00000000000";
INT_BOOTLOADER_EN : boolean := false;
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN : boolean := false;
- DM_LEGACY_MODE : boolean := false;
+ OCD_EN : boolean := false;
+ OCD_DM_LEGACY_MODE : boolean := false;
+ OCD_AUTHENTICATION : boolean := false;
-- RISC-V CPU Extensions --
RISCV_ISA_C : boolean := false;
RISCV_ISA_E : boolean := false;
@@ -777,7 +780,7 @@ package neorv32_package is
-- Global control --
clk_i : in std_ulogic;
rstn_i : in std_ulogic;
- -- JTAG on-chip debugger interface --
+ -- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i : in std_ulogic := 'L';
jtag_tdi_i : in std_ulogic := 'L';
jtag_tdo_o : out std_ulogic;
diff --git a/rtl/core/neorv32_sysinfo.vhd b/rtl/core/neorv32_sysinfo.vhd
index 5ceaea77e..951d31e69 100644
--- a/rtl/core/neorv32_sysinfo.vhd
+++ b/rtl/core/neorv32_sysinfo.vhd
@@ -39,6 +39,7 @@ entity neorv32_sysinfo is
XIP_CACHE_NUM_BLOCKS : natural; -- number of blocks (min 1), has to be a power of 2
XIP_CACHE_BLOCK_SIZE : natural; -- block size in bytes (min 4), has to be a power of 2
ON_CHIP_DEBUGGER_EN : boolean; -- implement OCD?
+ OCD_AUTHENTICATION : boolean; -- implement OCD authenticator?
IO_GPIO_EN : boolean; -- implement general purpose IO port (GPIO)?
IO_MTIME_EN : boolean; -- implement machine system timer (MTIME)?
IO_UART0_EN : boolean; -- implement primary universal asynchronous receiver/transmitter (UART0)?
@@ -116,7 +117,7 @@ begin
sysinfo(2)(8) <= '1' when xcache_en_c else '0'; -- external bus interface cache implemented?
sysinfo(2)(9) <= '1' when XIP_EN else '0'; -- execute in-place module implemented?
sysinfo(2)(10) <= '1' when xip_cache_en_c else '0'; -- execute in-place cache implemented?
- sysinfo(2)(11) <= '0'; -- reserved
+ sysinfo(2)(11) <= '1' when OCD_AUTHENTICATION else '0'; -- on-chip debugger authentication implemented?
sysinfo(2)(12) <= '0'; -- reserved
sysinfo(2)(13) <= '0'; -- reserved
sysinfo(2)(14) <= '1' when IO_DMA_EN else '0'; -- direct memory access controller (DMA) implemented?
diff --git a/rtl/core/neorv32_top.vhd b/rtl/core/neorv32_top.vhd
index 77aa14e64..9db4d1b30 100644
--- a/rtl/core/neorv32_top.vhd
+++ b/rtl/core/neorv32_top.vhd
@@ -29,8 +29,9 @@ entity neorv32_top is
INT_BOOTLOADER_EN : boolean := false; -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN : boolean := false; -- implement on-chip debugger
- DM_LEGACY_MODE : boolean := false; -- debug module spec version: false = v1.0, true = v0.13
+ OCD_EN : boolean := false; -- implement on-chip debugger
+ OCD_DM_LEGACY_MODE : boolean := false; -- debug module spec version: false = v1.0, true = v0.13
+ OCD_AUTHENTICATION : boolean := false; -- implement on-chip debugger authentication
-- RISC-V CPU Extensions --
RISCV_ISA_C : boolean := false; -- implement compressed extension
@@ -145,7 +146,7 @@ entity neorv32_top is
clk_i : in std_ulogic; -- global clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
- -- JTAG on-chip debugger interface (available if ON_CHIP_DEBUGGER_EN = true) --
+ -- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i : in std_ulogic := 'L'; -- serial clock
jtag_tdi_i : in std_ulogic := 'L'; -- serial data input
jtag_tdo_o : out std_ulogic; -- serial data output
@@ -252,6 +253,7 @@ architecture neorv32_top_rtl of neorv32_top is
constant io_pwm_en_c : boolean := boolean(IO_PWM_NUM_CH > 0);
constant cpu_smpmp_c : boolean := boolean(PMP_NUM_REGIONS > 0);
constant io_sysinfo_en_c : boolean := not IO_DISABLE_SYSINFO;
+ constant ocd_auth_en_c : boolean := OCD_EN and OCD_AUTHENTICATION;
-- convert JEDEC ID to mvendorid CSR --
constant vendorid_c : std_ulogic_vector(31 downto 0) := x"00000" & "0" & JEDEC_ID;
@@ -358,7 +360,8 @@ begin
cond_sel_string_f(IO_SLINK_EN, "SLINK ", "") &
cond_sel_string_f(IO_CRC_EN, "CRC ", "") &
cond_sel_string_f(io_sysinfo_en_c, "SYSINFO ", "") &
- cond_sel_string_f(ON_CHIP_DEBUGGER_EN, "OCD ", "") &
+ cond_sel_string_f(OCD_EN, "OCD ", "") &
+ cond_sel_string_f(ocd_auth_en_c, "OCD-AUTH ", "") &
""
severity note;
@@ -479,8 +482,8 @@ begin
RISCV_ISA_Zksh => RISCV_ISA_Zksh,
RISCV_ISA_Zmmul => RISCV_ISA_Zmmul,
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu,
- RISCV_ISA_Sdext => ON_CHIP_DEBUGGER_EN,
- RISCV_ISA_Sdtrig => ON_CHIP_DEBUGGER_EN,
+ RISCV_ISA_Sdext => OCD_EN,
+ RISCV_ISA_Sdtrig => OCD_EN,
RISCV_ISA_Smpmp => cpu_smpmp_c,
-- Tuning Options --
FAST_MUL_EN => FAST_MUL_EN,
@@ -975,38 +978,38 @@ begin
neorv32_bus_io_switch_inst: entity neorv32.neorv32_bus_io_switch
generic map (
DEV_SIZE => iodev_size_c, -- size of a single IO device
- DEV_00_EN => ON_CHIP_DEBUGGER_EN, DEV_00_BASE => base_io_dm_c,
- DEV_01_EN => io_sysinfo_en_c, DEV_01_BASE => base_io_sysinfo_c,
- DEV_02_EN => IO_NEOLED_EN, DEV_02_BASE => base_io_neoled_c,
- DEV_03_EN => io_gpio_en_c, DEV_03_BASE => base_io_gpio_c,
- DEV_04_EN => IO_WDT_EN, DEV_04_BASE => base_io_wdt_c,
- DEV_05_EN => IO_TRNG_EN, DEV_05_BASE => base_io_trng_c,
- DEV_06_EN => IO_TWI_EN, DEV_06_BASE => base_io_twi_c,
- DEV_07_EN => IO_SPI_EN, DEV_07_BASE => base_io_spi_c,
- DEV_08_EN => IO_SDI_EN, DEV_08_BASE => base_io_sdi_c,
- DEV_09_EN => IO_UART1_EN, DEV_09_BASE => base_io_uart1_c,
- DEV_10_EN => IO_UART0_EN, DEV_10_BASE => base_io_uart0_c,
- DEV_11_EN => IO_MTIME_EN, DEV_11_BASE => base_io_mtime_c,
- DEV_12_EN => io_xirq_en_c, DEV_12_BASE => base_io_xirq_c,
- DEV_13_EN => IO_ONEWIRE_EN, DEV_13_BASE => base_io_onewire_c,
- DEV_14_EN => IO_GPTMR_EN, DEV_14_BASE => base_io_gptmr_c,
- DEV_15_EN => io_pwm_en_c, DEV_15_BASE => base_io_pwm_c,
- DEV_16_EN => XIP_EN, DEV_16_BASE => base_io_xip_c,
- DEV_17_EN => IO_CRC_EN, DEV_17_BASE => base_io_crc_c,
- DEV_18_EN => IO_DMA_EN, DEV_18_BASE => base_io_dma_c,
- DEV_19_EN => IO_SLINK_EN, DEV_19_BASE => base_io_slink_c,
- DEV_20_EN => IO_CFS_EN, DEV_20_BASE => base_io_cfs_c,
- DEV_21_EN => false, DEV_31_BASE => (others => '0'), -- reserved
- DEV_22_EN => false, DEV_30_BASE => (others => '0'), -- reserved
- DEV_23_EN => false, DEV_29_BASE => (others => '0'), -- reserved
- DEV_24_EN => false, DEV_28_BASE => (others => '0'), -- reserved
- DEV_25_EN => false, DEV_27_BASE => (others => '0'), -- reserved
- DEV_26_EN => false, DEV_26_BASE => (others => '0'), -- reserved
- DEV_27_EN => false, DEV_25_BASE => (others => '0'), -- reserved
- DEV_28_EN => false, DEV_24_BASE => (others => '0'), -- reserved
- DEV_29_EN => false, DEV_23_BASE => (others => '0'), -- reserved
- DEV_30_EN => false, DEV_22_BASE => (others => '0'), -- reserved
- DEV_31_EN => false, DEV_21_BASE => (others => '0') -- reserved
+ DEV_00_EN => OCD_EN, DEV_00_BASE => base_io_dm_c,
+ DEV_01_EN => io_sysinfo_en_c, DEV_01_BASE => base_io_sysinfo_c,
+ DEV_02_EN => IO_NEOLED_EN, DEV_02_BASE => base_io_neoled_c,
+ DEV_03_EN => io_gpio_en_c, DEV_03_BASE => base_io_gpio_c,
+ DEV_04_EN => IO_WDT_EN, DEV_04_BASE => base_io_wdt_c,
+ DEV_05_EN => IO_TRNG_EN, DEV_05_BASE => base_io_trng_c,
+ DEV_06_EN => IO_TWI_EN, DEV_06_BASE => base_io_twi_c,
+ DEV_07_EN => IO_SPI_EN, DEV_07_BASE => base_io_spi_c,
+ DEV_08_EN => IO_SDI_EN, DEV_08_BASE => base_io_sdi_c,
+ DEV_09_EN => IO_UART1_EN, DEV_09_BASE => base_io_uart1_c,
+ DEV_10_EN => IO_UART0_EN, DEV_10_BASE => base_io_uart0_c,
+ DEV_11_EN => IO_MTIME_EN, DEV_11_BASE => base_io_mtime_c,
+ DEV_12_EN => io_xirq_en_c, DEV_12_BASE => base_io_xirq_c,
+ DEV_13_EN => IO_ONEWIRE_EN, DEV_13_BASE => base_io_onewire_c,
+ DEV_14_EN => IO_GPTMR_EN, DEV_14_BASE => base_io_gptmr_c,
+ DEV_15_EN => io_pwm_en_c, DEV_15_BASE => base_io_pwm_c,
+ DEV_16_EN => XIP_EN, DEV_16_BASE => base_io_xip_c,
+ DEV_17_EN => IO_CRC_EN, DEV_17_BASE => base_io_crc_c,
+ DEV_18_EN => IO_DMA_EN, DEV_18_BASE => base_io_dma_c,
+ DEV_19_EN => IO_SLINK_EN, DEV_19_BASE => base_io_slink_c,
+ DEV_20_EN => IO_CFS_EN, DEV_20_BASE => base_io_cfs_c,
+ DEV_21_EN => false, DEV_31_BASE => (others => '0'), -- reserved
+ DEV_22_EN => false, DEV_30_BASE => (others => '0'), -- reserved
+ DEV_23_EN => false, DEV_29_BASE => (others => '0'), -- reserved
+ DEV_24_EN => false, DEV_28_BASE => (others => '0'), -- reserved
+ DEV_25_EN => false, DEV_27_BASE => (others => '0'), -- reserved
+ DEV_26_EN => false, DEV_26_BASE => (others => '0'), -- reserved
+ DEV_27_EN => false, DEV_25_BASE => (others => '0'), -- reserved
+ DEV_28_EN => false, DEV_24_BASE => (others => '0'), -- reserved
+ DEV_29_EN => false, DEV_23_BASE => (others => '0'), -- reserved
+ DEV_30_EN => false, DEV_22_BASE => (others => '0'), -- reserved
+ DEV_31_EN => false, DEV_21_BASE => (others => '0') -- reserved
)
port map (
clk_i => clk_i,
@@ -1570,7 +1573,8 @@ begin
XIP_CACHE_EN => XIP_CACHE_EN,
XIP_CACHE_NUM_BLOCKS => XIP_CACHE_NUM_BLOCKS,
XIP_CACHE_BLOCK_SIZE => XIP_CACHE_BLOCK_SIZE,
- ON_CHIP_DEBUGGER_EN => ON_CHIP_DEBUGGER_EN,
+ ON_CHIP_DEBUGGER_EN => OCD_EN,
+ OCD_AUTHENTICATION => ocd_auth_en_c,
IO_GPIO_EN => io_gpio_en_c,
IO_MTIME_EN => IO_MTIME_EN,
IO_UART0_EN => IO_UART0_EN,
@@ -1611,7 +1615,7 @@ begin
-- On-Chip Debugger Complex
-- **************************************************************************************************************************
neorv32_ocd_inst_true:
- if ON_CHIP_DEBUGGER_EN generate
+ if OCD_EN generate
-- On-Chip Debugger - Debug Transport Module (DTM) ----------------------------------------
-- -------------------------------------------------------------------------------------------
@@ -1637,7 +1641,8 @@ begin
neorv32_debug_dm_inst: entity neorv32.neorv32_debug_dm
generic map (
CPU_BASE_ADDR => base_io_dm_c,
- LEGACY_MODE => DM_LEGACY_MODE
+ LEGACY_MODE => OCD_DM_LEGACY_MODE,
+ AUTHENTICATOR => OCD_AUTHENTICATION
)
port map (
clk_i => clk_i,
@@ -1654,7 +1659,7 @@ begin
end generate;
neorv32_debug_ocd_inst_false:
- if not ON_CHIP_DEBUGGER_EN generate
+ if not OCD_EN generate
iodev_rsp(IODEV_OCD) <= rsp_terminate_c;
jtag_tdo_o <= jtag_tdi_i; -- JTAG pass-through
dci_ndmrstn <= '1';
diff --git a/rtl/file_list_soc.f b/rtl/file_list_soc.f
index 08b0bfd87..88cf13f91 100644
--- a/rtl/file_list_soc.f
+++ b/rtl/file_list_soc.f
@@ -42,6 +42,7 @@
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_crc.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_sysinfo.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_debug_dtm.vhd
+NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_debug_auth.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_debug_dm.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_top.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_application_image.vhd
diff --git a/rtl/system_integration/neorv32_litex_core_complex.vhd b/rtl/system_integration/neorv32_litex_core_complex.vhd
index 1f978da26..a038addca 100644
--- a/rtl/system_integration/neorv32_litex_core_complex.vhd
+++ b/rtl/system_integration/neorv32_litex_core_complex.vhd
@@ -122,7 +122,7 @@ begin
HART_ID => hart_id_c, -- hardware thread ID
JEDEC_ID => jedec_id_c, -- vendor's JEDEC ID
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN => DEBUG, -- implement on-chip debugger
+ OCD_EN => DEBUG, -- implement on-chip debugger
-- RISC-V CPU Extensions --
RISCV_ISA_C => configs_c.riscv_c(CONFIG), -- implement compressed extension?
RISCV_ISA_M => configs_c.riscv_m(CONFIG), -- implement mul/div extension?
diff --git a/rtl/system_integration/neorv32_vivado_ip.tcl b/rtl/system_integration/neorv32_vivado_ip.tcl
index 065cba873..7ba27e0ab 100644
--- a/rtl/system_integration/neorv32_vivado_ip.tcl
+++ b/rtl/system_integration/neorv32_vivado_ip.tcl
@@ -70,25 +70,23 @@ set_property description "The NEORV32 RISC-V Processor" [ipx::current_core]
# **************************************************************
# Set configuration dependencies: Interfaces
# **************************************************************
-set_property enablement_dependency {$axi4_stream_en = true} [ipx::get_ports s0_axis_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$axi4_stream_en = true} [ipx::get_ports s1_axis_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$on_chip_debugger_en = true} [ipx::get_ports jtag_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$xip_en = true} [ipx::get_ports xip_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_gpio_en = true} [ipx::get_ports gpio_i -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_gpio_en = true} [ipx::get_ports gpio_o -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_uart0_en = true} [ipx::get_ports uart0_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_uart1_en = true} [ipx::get_ports uart1_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_spi_en = true} [ipx::get_ports spi_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_sdi_en = true} [ipx::get_ports sdi_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_twi_en = true} [ipx::get_ports twi_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_onewire_en = true} [ipx::get_ports onewire_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_pwm_en = true} [ipx::get_ports pwm_o -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_cfs_en = true} [ipx::get_ports cfs_* -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_neoled_en = true} [ipx::get_ports neoled_o -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_mtime_en = true} [ipx::get_ports mtime_time_o -of_objects [ipx::current_core]]
-set_property enablement_dependency {$xirq_en = true} [ipx::get_ports xirq_i -of_objects [ipx::current_core]]
-set_property enablement_dependency {$io_mtime_en = false} [ipx::get_ports mtime_irq_i -of_objects [ipx::current_core]]
-
+set_property enablement_dependency {$axi4_stream_en = true} [ipx::get_ports s0_axis_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$axi4_stream_en = true} [ipx::get_ports s1_axis_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$ocd_en = true} [ipx::get_ports jtag_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$xip_en = true} [ipx::get_ports xip_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_gpio_en = true} [ipx::get_ports gpio_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_uart0_en = true} [ipx::get_ports uart0_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_uart1_en = true} [ipx::get_ports uart1_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_spi_en = true} [ipx::get_ports spi_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_sdi_en = true} [ipx::get_ports sdi_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_twi_en = true} [ipx::get_ports twi_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_onewire_en = true} [ipx::get_ports onewire_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_pwm_en = true} [ipx::get_ports pwm_o -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_cfs_en = true} [ipx::get_ports cfs_* -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_neoled_en = true} [ipx::get_ports neoled_o -of_objects [ipx::current_core]]
+set_property enablement_dependency {$xirq_en = true} [ipx::get_ports xirq_i -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_mtime_en = true} [ipx::get_ports mtime_time_o -of_objects [ipx::current_core]]
+set_property enablement_dependency {$io_mtime_en = false} [ipx::get_ports mtime_irq_i -of_objects [ipx::current_core]]
# **************************************************************
# Configuration GUI: General
@@ -99,17 +97,18 @@ set_property display_name {HART ID}
set_property tooltip {For mhartid CSR} [ipgui::get_guiparamspec -name "HART_ID" -component [ipx::current_core]]
set_property display_name {JEDEC ID} [ipgui::get_guiparamspec -name "JEDEC_ID" -component [ipx::current_core]]
set_property tooltip {For JTAG tap identification and mvendorid CSR} [ipgui::get_guiparamspec -name "JEDEC_ID" -component [ipx::current_core]]
-set_property display_name {On-Chip Debugger} [ipgui::get_guiparamspec -name "ON_CHIP_DEBUGGER_EN" -component [ipx::current_core]]
+set_property display_name {RISC-V on-chip debugger} [ipgui::get_guiparamspec -name "OCD_EN" -component [ipx::current_core]]
+set_property display_name {RISC-V on-chip debugger authentication} [ipgui::get_guiparamspec -name "OCD_AUTHENTICATION" -component [ipx::current_core]]
set_property display_name {AXI4-Lite (XBUS) timeout} [ipgui::get_guiparamspec -name "XBUS_TIMEOUT" -component [ipx::current_core]]
set_property tooltip {Max number of clock cycles before AXI access times out} [ipgui::get_guiparamspec -name "XBUS_TIMEOUT" -component [ipx::current_core]]
-set_property display_name {AXI4-Lite cache (XBUS)} [ipgui::get_guiparamspec -name "XBUS_CACHE_EN" -component [ipx::current_core]]
-set_property display_name {AXI4-Lite cache (XBUS) number of blocks} [ipgui::get_guiparamspec -name "XBUS_CACHE_NUM_BLOCKS" -component [ipx::current_core]]
-set_property display_name {AXI4-Lite cache (XBUS) block size} [ipgui::get_guiparamspec -name "XBUS_CACHE_BLOCK_SIZE" -component [ipx::current_core]]
+set_property display_name {AXI4-Lite (XBUS) cache} [ipgui::get_guiparamspec -name "XBUS_CACHE_EN" -component [ipx::current_core]]
+set_property display_name {AXI4-Lite (XBUS) cache number of blocks} [ipgui::get_guiparamspec -name "XBUS_CACHE_NUM_BLOCKS" -component [ipx::current_core]]
+set_property display_name {AXI4-Lite (XBUS) cache block size} [ipgui::get_guiparamspec -name "XBUS_CACHE_BLOCK_SIZE" -component [ipx::current_core]]
set_property tooltip {In bytes (use a power of two)} [ipgui::get_guiparamspec -name "XBUS_CACHE_BLOCK_SIZE" -component [ipx::current_core]]
-set_property display_name {AXI4-Stream source and sink} [ipgui::get_guiparamspec -name "AXI4_STREAM_EN" -component [ipx::current_core]]
-set_property display_name {AXI4-Stream input FIFO depth} [ipgui::get_guiparamspec -name "IO_SLINK_RX_FIFO" -component [ipx::current_core]]
+set_property display_name {AXI4-Stream (SLINK) source and sink} [ipgui::get_guiparamspec -name "AXI4_STREAM_EN" -component [ipx::current_core]]
+set_property display_name {AXI4-Stream (SLINK) input FIFO depth} [ipgui::get_guiparamspec -name "IO_SLINK_RX_FIFO" -component [ipx::current_core]]
set_property tooltip {Number of entries (use a power of two)} [ipgui::get_guiparamspec -name "IO_SLINK_RX_FIFO" -component [ipx::current_core]]
-set_property display_name {AXI4-Stream output FIFO depth} [ipgui::get_guiparamspec -name "IO_SLINK_TX_FIFO" -component [ipx::current_core]]
+set_property display_name {AXI4-Stream (SLINK) output FIFO depth} [ipgui::get_guiparamspec -name "IO_SLINK_TX_FIFO" -component [ipx::current_core]]
set_property tooltip {Number of entries (use a power of two)} [ipgui::get_guiparamspec -name "IO_SLINK_TX_FIFO" -component [ipx::current_core]]
ipgui::add_group -name {General} -component [ipx::current_core] -parent [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]] -display_name {General}
@@ -117,14 +116,15 @@ ipgui::move_group -component [ipx::current_core] -order 0 [ipgui::get_groupspec
ipgui::move_param -component [ipx::current_core] -order 0 [ipgui::get_guiparamspec -name "CLOCK_FREQUENCY" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
ipgui::move_param -component [ipx::current_core] -order 1 [ipgui::get_guiparamspec -name "HART_ID" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
ipgui::move_param -component [ipx::current_core] -order 2 [ipgui::get_guiparamspec -name "JEDEC_ID" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 3 [ipgui::get_guiparamspec -name "ON_CHIP_DEBUGGER_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 4 [ipgui::get_guiparamspec -name "XBUS_TIMEOUT" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 5 [ipgui::get_guiparamspec -name "XBUS_CACHE_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 6 [ipgui::get_guiparamspec -name "XBUS_CACHE_NUM_BLOCKS" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 7 [ipgui::get_guiparamspec -name "XBUS_CACHE_BLOCK_SIZE" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 8 [ipgui::get_guiparamspec -name "AXI4_STREAM_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 9 [ipgui::get_guiparamspec -name "IO_SLINK_RX_FIFO" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
-ipgui::move_param -component [ipx::current_core] -order 10 [ipgui::get_guiparamspec -name "IO_SLINK_TX_FIFO" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 3 [ipgui::get_guiparamspec -name "OCD_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 4 [ipgui::get_guiparamspec -name "OCD_AUTHENTICATION" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 5 [ipgui::get_guiparamspec -name "XBUS_TIMEOUT" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 6 [ipgui::get_guiparamspec -name "XBUS_CACHE_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 7 [ipgui::get_guiparamspec -name "XBUS_CACHE_NUM_BLOCKS" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 8 [ipgui::get_guiparamspec -name "XBUS_CACHE_BLOCK_SIZE" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 9 [ipgui::get_guiparamspec -name "AXI4_STREAM_EN" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 10 [ipgui::get_guiparamspec -name "IO_SLINK_RX_FIFO" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
+ipgui::move_param -component [ipx::current_core] -order 11 [ipgui::get_guiparamspec -name "IO_SLINK_TX_FIFO" -component [ipx::current_core]] -parent [ipgui::get_groupspec -name "General" -component [ipx::current_core]]
# **************************************************************
diff --git a/rtl/system_integration/neorv32_vivado_ip.vhd b/rtl/system_integration/neorv32_vivado_ip.vhd
index d0a94e9e3..39bfdfd04 100644
--- a/rtl/system_integration/neorv32_vivado_ip.vhd
+++ b/rtl/system_integration/neorv32_vivado_ip.vhd
@@ -33,7 +33,8 @@ entity neorv32_vivado_ip is
JEDEC_ID : std_logic_vector(10 downto 0) := "00000000000";
INT_BOOTLOADER_EN : boolean := false;
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN : boolean := false;
+ OCD_EN : boolean := false;
+ OCD_AUTHENTICATION : boolean := false;
-- RISC-V CPU Extensions --
RISCV_ISA_C : boolean := false;
RISCV_ISA_E : boolean := false;
@@ -185,7 +186,7 @@ entity neorv32_vivado_ip is
s1_axis_tdata : in std_logic_vector(31 downto 0) := x"00000000";
s1_axis_tlast : in std_logic := '0';
-- ------------------------------------------------------------
- -- JTAG on-chip debugger interface (available if ON_CHIP_DEBUGGER_EN = true)
+ -- JTAG on-chip debugger interface (available if OCD_EN = true)
-- ------------------------------------------------------------
jtag_tck_i : in std_logic := '0';
jtag_tdi_i : in std_logic := '0';
@@ -307,8 +308,9 @@ begin
JEDEC_ID => std_ulogic_vector(JEDEC_ID),
INT_BOOTLOADER_EN => INT_BOOTLOADER_EN,
-- On-Chip Debugger --
- ON_CHIP_DEBUGGER_EN => ON_CHIP_DEBUGGER_EN,
- DM_LEGACY_MODE => false,
+ OCD_EN => OCD_EN,
+ OCD_DM_LEGACY_MODE => false,
+ OCD_AUTHENTICATION => OCD_AUTHENTICATION,
-- RISC-V CPU Extensions --
RISCV_ISA_C => RISCV_ISA_C,
RISCV_ISA_E => RISCV_ISA_E,
@@ -409,7 +411,7 @@ begin
-- Global control --
clk_i => std_ulogic(clk),
rstn_i => std_ulogic(resetn),
- -- JTAG on-chip debugger interface (available if ON_CHIP_DEBUGGER_EN = true) --
+ -- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i => std_ulogic(jtag_tck_i),
jtag_tdi_i => std_ulogic(jtag_tdi_i),
jtag_tdo_o => jtag_tdo_aux,
diff --git a/rtl/test_setups/neorv32_test_setup_on_chip_debugger.vhd b/rtl/test_setups/neorv32_test_setup_on_chip_debugger.vhd
index 96c62d8e4..8bf4c2999 100644
--- a/rtl/test_setups/neorv32_test_setup_on_chip_debugger.vhd
+++ b/rtl/test_setups/neorv32_test_setup_on_chip_debugger.vhd
@@ -53,7 +53,7 @@ begin
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => true, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN => true, -- implement on-chip debugger
+ OCD_EN => true, -- implement on-chip debugger
-- RISC-V CPU Extensions --
RISCV_ISA_C => true, -- implement compressed extension?
RISCV_ISA_M => true, -- implement mul/div extension?
diff --git a/sim/neorv32_tb.vhd b/sim/neorv32_tb.vhd
index 54aad0aa7..1df4e6e5b 100644
--- a/sim/neorv32_tb.vhd
+++ b/sim/neorv32_tb.vhd
@@ -198,7 +198,8 @@ begin
JEDEC_ID => "00000000000", -- vendor's JEDEC ID
INT_BOOTLOADER_EN => false, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN => true, -- implement on-chip debugger
+ OCD_EN => true, -- implement on-chip debugger
+ OCD_AUTHENTICATION => true, -- implement on-chip debugger authentication
-- RISC-V CPU Extensions --
RISCV_ISA_C => true, -- implement compressed extension?
RISCV_ISA_E => false, -- implement embedded RF extension?
@@ -295,7 +296,7 @@ begin
-- Global control --
clk_i => clk_gen, -- global clock, rising edge
rstn_i => rst_gen, -- global reset, low-active, async
- -- JTAG on-chip debugger interface (available if ON_CHIP_DEBUGGER_EN = true) --
+ -- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i => '0', -- serial clock
jtag_tdi_i => '0', -- serial data input
jtag_tdo_o => open, -- serial data output
diff --git a/sim/simple/neorv32_tb.simple.vhd b/sim/simple/neorv32_tb.simple.vhd
index c1c83e7f3..af687239f 100644
--- a/sim/simple/neorv32_tb.simple.vhd
+++ b/sim/simple/neorv32_tb.simple.vhd
@@ -170,7 +170,8 @@ begin
JEDEC_ID => "00000000000", -- vendor's JEDEC ID
INT_BOOTLOADER_EN => false, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- On-Chip Debugger (OCD) --
- ON_CHIP_DEBUGGER_EN => true, -- implement on-chip debugger
+ OCD_EN => true, -- implement on-chip debugger
+ OCD_AUTHENTICATION => true, -- implement on-chip debugger authentication
-- RISC-V CPU Extensions --
RISCV_ISA_C => false, -- implement compressed extension?
RISCV_ISA_E => false, -- implement embedded RF extension?
@@ -271,7 +272,7 @@ begin
-- Global control --
clk_i => clk_gen, -- global clock, rising edge
rstn_i => rst_gen, -- global reset, low-active, async
- -- JTAG on-chip debugger interface (available if ON_CHIP_DEBUGGER_EN = true) --
+ -- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i => '0', -- serial clock
jtag_tdi_i => '0', -- serial data input
jtag_tdo_o => open, -- serial data output
diff --git a/sw/common/common.mk b/sw/common/common.mk
index 18915fd53..0e0d89ea4 100644
--- a/sw/common/common.mk
+++ b/sw/common/common.mk
@@ -313,7 +313,7 @@ elf_sections: $(APP_ELF)
# -----------------------------------------------------------------------------
# Run GDB
# -----------------------------------------------------------------------------
-gdb:
+gdb: $(APP_ELF)
@$(GDB) $(APP_ELF) $(GDB_ARGS)
# -----------------------------------------------------------------------------
diff --git a/sw/lib/include/neorv32_sysinfo.h b/sw/lib/include/neorv32_sysinfo.h
index 8742e8ab5..2ff1ccc97 100644
--- a/sw/lib/include/neorv32_sysinfo.h
+++ b/sw/lib/include/neorv32_sysinfo.h
@@ -46,13 +46,14 @@ enum NEORV32_SYSINFO_SOC_enum {
SYSINFO_SOC_XBUS = 1, /**< SYSINFO_SOC (1) (r/-): External bus interface implemented when 1 (via XBUS_EN generic) */
SYSINFO_SOC_MEM_INT_IMEM = 2, /**< SYSINFO_SOC (2) (r/-): Processor-internal instruction memory implemented when 1 (via MEM_INT_IMEM_EN generic) */
SYSINFO_SOC_MEM_INT_DMEM = 3, /**< SYSINFO_SOC (3) (r/-): Processor-internal data memory implemented when 1 (via MEM_INT_DMEM_EN generic) */
- SYSINFO_SOC_OCD = 4, /**< SYSINFO_SOC (4) (r/-): On-chip debugger implemented when 1 (via ON_CHIP_DEBUGGER_EN generic) */
+ SYSINFO_SOC_OCD = 4, /**< SYSINFO_SOC (4) (r/-): On-chip debugger implemented when 1 (via OCD_EN generic) */
SYSINFO_SOC_ICACHE = 5, /**< SYSINFO_SOC (5) (r/-): Processor-internal instruction cache implemented when 1 (via ICACHE_EN generic) */
SYSINFO_SOC_DCACHE = 6, /**< SYSINFO_SOC (6) (r/-): Processor-internal instruction cache implemented when 1 (via DCACHE_EN generic) */
SYSINFO_SOC_CLOCK_GATING = 7, /**< SYSINFO_SOC (7) (r/-): Clock gating implemented when 1 (via CLOCK_GATING_EN generic) */
SYSINFO_SOC_XBUS_CACHE = 8, /**< SYSINFO_SOC (8) (r/-): External bus cache implemented when 1 (via XBUS_CACHE_EN generic) */
SYSINFO_SOC_XIP = 9, /**< SYSINFO_SOC (9) (r/-): Execute in-place module implemented when 1 (via XIP_EN generic) */
- SYSINFO_SOC_XIP_CACHE = 10, /**< SYSINFO_S C (10) (r/-): Execute in-place cache implemented when 1 (via XIP_CACHE_EN generic) */
+ SYSINFO_SOC_XIP_CACHE = 10, /**< SYSINFO_SOC (10) (r/-): Execute in-place cache implemented when 1 (via XIP_CACHE_EN generic) */
+ SYSINFO_SOC_OCD_AUTH = 11, /**< SYSINFO_SOC (11) (r/-): On-chip debugger authentication implemented when 1 (via OCD_AUTHENTICATION generic) */
SYSINFO_SOC_IO_DMA = 14, /**< SYSINFO_SOC (14) (r/-): Direct memory access controller implemented when 1 (via IO_DMA_EN generic) */
SYSINFO_SOC_IO_GPIO = 15, /**< SYSINFO_SOC (15) (r/-): General purpose input/output port unit implemented when 1 (via IO_GPIO_EN generic) */
diff --git a/sw/lib/source/neorv32_rte.c b/sw/lib/source/neorv32_rte.c
index fecf02056..cba206115 100644
--- a/sw/lib/source/neorv32_rte.c
+++ b/sw/lib/source/neorv32_rte.c
@@ -407,8 +407,18 @@ void neorv32_rte_print_hw_config(void) {
else { neorv32_uart0_printf("disabled\n"); }
neorv32_uart0_printf("On-chip debugger: ");
- if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_OCD)) { neorv32_uart0_printf("enabled\n"); }
- else { neorv32_uart0_printf("disabled\n"); }
+ if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_OCD)) {
+ neorv32_uart0_printf("enabled");
+ }
+ else {
+ neorv32_uart0_printf("disabled");
+ }
+ if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_OCD_AUTH)) {
+ neorv32_uart0_printf(" + authentication\n");
+ }
+ else {
+ neorv32_uart0_printf("\n");
+ }
// IDs
neorv32_uart0_printf("Hart ID: 0x%x\n"
diff --git a/sw/openocd/openocd_neorv32.cfg b/sw/openocd/openocd_neorv32.cfg
index f580249f0..cc805eae8 100644
--- a/sw/openocd/openocd_neorv32.cfg
+++ b/sw/openocd/openocd_neorv32.cfg
@@ -1,19 +1,19 @@
-# NEORV32 on-chip debugger OpenOCD configuration file
+# The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32
+# OpenOCD on-chip debugger configuration file
-# ----------------------------------------------
-# Physical interface configuration
-# ----------------------------------------------
+# -------------------------------------------------------------------
+# Physical interface configuration; ADJUST THIS FOR YOUR ADAPTER
+# -------------------------------------------------------------------
adapter driver ftdi
ftdi vid_pid 0x0403 0x6010
ftdi channel 0
ftdi layout_init 0x0038 0x003b
-
adapter speed 4000
transport select jtag
-# ----------------------------------------------
+# -------------------------------------------------------------------
# Target configuration
-# ----------------------------------------------
+# -------------------------------------------------------------------
set _CHIPNAME neorv32
jtag newtap $_CHIPNAME cpu -irlen 5
set _TARGETNAME $_CHIPNAME.cpu
@@ -26,15 +26,35 @@ riscv expose_csrs 2050=cfureg2
riscv expose_csrs 2051=cfureg3
riscv expose_csrs 4032=mxisa
-# ----------------------------------------------
+# -------------------------------------------------------------------
# Server configuration
-# ----------------------------------------------
+# -------------------------------------------------------------------
gdb report_data_abort enable
-# ----------------------------------------------
-# Start session and halt
-# ----------------------------------------------
+# -------------------------------------------------------------------
+# Initialize target
+# -------------------------------------------------------------------
init
-halt
+# -------------------------------------------------------------------
+# Authenticate; ADJUST THIS FOR YOUR AUTHENTICATOR
+# -------------------------------------------------------------------
+set challenge [riscv authdata_read]
+riscv authdata_write [expr {$challenge | 1}]
+
+# -------------------------------------------------------------------
+# Check if device is authenticated (bit 7 in dmstatus)
+# -------------------------------------------------------------------
+set dmstatus [riscv dmi_read 0x11]
+if { [expr {$dmstatus & (1<<7)}] } {
+ echo "Authentication passed."
+} else {
+ echo "AUTHENTICATION FAILED!"
+ exit
+}
+
+# -------------------------------------------------------------------
+# Target should be halted and ready now
+# -------------------------------------------------------------------
+halt
echo "Target HALTED. Ready for remote connections."
diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd
index 76e11af61..52b90a2ce 100644
--- a/sw/svd/neorv32.svd
+++ b/sw/svd/neorv32.svd
@@ -1406,7 +1406,7 @@
WDT_CTRL_RCAUSE
[6:5]
read-only
- Cause of last system reset: 0=external reset, 1=OCD reset, 2=WDT reset
+ Cause of last system reset: 0=external reset, 1=OCD reset, 2=WDT reset, 3=WDT access violation
WDT_CTRL_TIMEOUT
@@ -1619,6 +1619,7 @@
SYSINFO_SOC_XBUS_CACHE[8:8]External bus cache implemented
SYSINFO_SOC_XIP[9:9]Execute in place module implemented
SYSINFO_SOC_XIP_CACHE[10:10]Execute in place cache implemented
+ SYSINFO_SOC_OCD_AUTH[11:11]On-chip debugger authentication implemented
SYSINFO_SOC_IO_DMA[14:14]Direct memory access controller implemented
SYSINFO_SOC_IO_GPIO[15:15]General purpose input/output port unit implemented
SYSINFO_SOC_IO_MTIME[16:16]Machine system timer implemented