# Rocket RISC-V processor - Adding a custom CSR, software point of view


## Introduction

In the context of a research project related to RIMI [1], we need to add a new CSR (Control & Status Register) in the Rocket processor [2] in order to store new security settings. There are obviously modifications to do in the hardware description of the processor (it will be the topic of a future article). However, we also need to modify a few lines of the software stack.

## Software stack for Rocket Chip

rocket-tools [3] is the reference repository given by Rocket Chip maintainers. It contains:

- The cross-compiler : `riscv-gnu-toolchain`

- The simulator : `riscv-isa-sim` (also known as Spike)

- An on-chip debugger : `riscv-openocd`

- A proxy kernel : `riscv-pk`

- Several tests : `riscv-tests`

## Adding a custom CSR - Use case

### Initial configuration

Let's say we want to add a custom CSR labeled `dmpcfg` at address `0x308` (CSR ID isn't used by any existing register).

### Modifications in the software toolchain

It turns out that we have to modify a few lines in [fsf-binutils-gdb @ ffea142](http://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=include/opcode/riscv-opc.h;h=f09200c073d4b5f05d0a4fed7e1ea3307d7a2f78;hb=ffea142):

```c
// Define CSR ID
#define CSR_DMPCFG 0x308
// Creating the CSR
DECLARE_CSR(dmpcfg, CSR_DMPCFG)
```

This repository contains several tools such as objdump, the linker and **the assembler**. Modifying binutils source code didn't seem trivial at first sight.

### Why is this modification needed?

If we want to access this new CSR with a [very simple C code](https://github.com/pcotret/riscv-tests/blob/master/benchmarks_custom/read_write_dmpcfg/read_write_dmpcfg.c) with a default toolchain, an error appears:

```bash
./read_write_dmpcfg/read_write_dmpcfg.c: Assembler messages:
./read_write_dmpcfg/read_write_dmpcfg.c:14: Error: unknown CSR `dmpcfg'
./read_write_dmpcfg/read_write_dmpcfg.c:15: Error: unknown CSR `dmpcfg'
```

- Yes, the assembler is involved here.

- It seems that the `dmpcfg` is unknown to the software toolchain.

This error comes from a function called [check_absolute_expr](http://sourceware.org/git?p=binutils-gdb.git;a=blob;f=gas/config/tc-riscv.c;h=e50505138e95d386d51c80ca3119b5838b47e608;hb=ffea142#l984) which call graph is as follows:

```goat
      .               .                .               .--- 1          .-- 1     / 1
     / \              |                |           .---+            .-+         +
    /   \         .---+---.         .--+--.        |   '--- 2      |   '-- 2   / \ 2
   +     +        |       |        |       |    ---+            ---+          +
  / \   / \     .-+-.   .-+-.     .+.     .+.      |   .--- 3      |   .-- 3   \ / 3
 /   \ /   \    |   |   |   |    |   |   |   |     '---+            '-+         +
 1   2 3   4    1   2   3   4    1   2   3   4         '--- 4          '-- 4     \ 4

```

The description of `riscv_ip` is interesting:

> This routine assembles an instruction into its binary format.

When looking at the code related to CSR (`csrrw` instruction):

```c
case 'Z':           /* CSRRxI immediate.  */
  my_getExpression (imm_expr, s);
  check_absolute_expr (ip, imm_expr, FALSE);
  if ((unsigned long) imm_expr->X_add_number > 31)
    as_bad (_("Improper CSRxI immediate (%lu)"),
            (unsigned long) imm_expr->X_add_number);
  INSERT_OPERAND (RS1, *ip, imm_expr->X_add_number);
  imm_expr->X_op = O_absent;
  s = expr_end;
  continue;

case 'E':           /* Control register.  */
  if (reg_lookup (&s, RCLASS_CSR, &regno))
    INSERT_OPERAND (CSR, *ip, regno);
  else
    {
      my_getExpression (imm_expr, s);
      check_absolute_expr (ip, imm_expr, TRUE);
      if ((unsigned long) imm_expr->X_add_number > 0xfff)
        as_bad (_("Improper CSR address (%lu)"),
                (unsigned long) imm_expr->X_add_number);
      INSERT_OPERAND (CSR, *ip, imm_expr->X_add_number);
      imm_expr->X_op = O_absent;
      s = expr_end;
    }
  continue;
```

It turns out the GNU assembler is checking the CSR name before assembling the binary!

```c
/* [...] MAYBE_CSR is true if the symbol may be an unrecognized CSR name. <= This comment !  */

static void
check_absolute_expr (struct riscv_cl_insn *ip, expressionS *ex,
                     bfd_boolean maybe_csr)
{
  if (ex->X_op == O_big)
    as_bad (_("unsupported large constant"));
  else if (maybe_csr && ex->X_op == O_symbol)
    as_bad (_("unknown CSR `%s'"),
            S_GET_NAME (ex->X_add_symbol));
  else if (ex->X_op != O_constant)
    as_bad (_("Instruction %s requires absolute expression"),
            ip->insn_mo->name);
  normalize_constant_expr (ex);
}
```

Once the CSR ID has been correctly in the GNU assembler (`fsf-binutils-gdb` submodule of [3]), the C code given in [4] is compiled without error.

## References

1. [RIMI: instruction-level memory isolation for embedded systems on RISC-V](https://dl.acm.org/doi/abs/10.1145/3400302.3415727)
2. [GitHub - chipsalliance/rocket-chip: Rocket Chip Generator](https://github.com/chipsalliance/rocket-chip)
3. [GitHub - chipsalliance/rocket-tools: Software tools that support rocket-chip](https://github.com/chipsalliance/rocket-tools)
4. [Custom C simple C code](https://github.com/pcotret/riscv-tests/blob/master/benchmarks_custom/read_write_dmpcfg/read_write_dmpcfg.c)


