Search code examples
assemblygccinline-assemblyriscv

Reading RISC-V CSR registers using C asm


I'm trying to read a csr register using function macro

I have a struct array that contains name and address of csr registers

typedef struct csr_lists
{
    int address;
    const cahr* name;
} csr_lists;

csr_lists list[] = 
{
    {0xc00, "CYCLE"},
    ...
};

And the function I made:

#define csr_read(csr)                               \
({                                                  \
    register uint32_t v;                            \
    __asm__ __volatile__ ("csrr %0, %1"             \
                  : "=r" (v)                        \
                  : "n" (csr)                       \
                  : "memory");                      \
    v;                                              \
})

So it is called like uint64_t value = csr_read(list[i].address); or uint64_t value = csr_read(0xc00);

And the compiler gives me following errors

csr.h:68:2: error: asm operand 1 probably doesn’t match constraints [-Werror]
   68 |  __asm__ __volatile__ ("csrr %0, %1"    \
      |  ^~~~~~~
csr.c:127:25: note: in expansion of macro ‘csr_read’
  127 |         value[i] = csr_read(list[i].address);
      |  
csr.h:68:2: error: impossible constraint in ‘asm’
   68 |  __asm__ __volatile__ ("csrr %0, %1"    \
      |  ^~~~~~~
csr.c:127:25: note: in expansion of macro ‘csr_read’
  127 |         value[i] = csr_read(list[i].address);
      |   
cc1: all warnings being treated as errors
/mnt/d/project/riscv32-linux/buildroot-2021.02.10/output/host/lib/gcc/riscv32-buildroot-linux-gnu/9.4.0/../../../../riscv32-buildroot-linux-gnu/bin/ld: ./libhpm.so: undefined reference to `csr_read'

How can I fix this problem?

edit) The problem happens at these lines

#define MAX    7
for (i = 0; i < MAX; i++)
{
    value[i] = csr_read(list[i].address);
}

Solution

  • Maybe this snippet will be helpful to the next person looking at this.

    I'm sure the original question has been well resolved by now. The main problem is you have to use a compile time constant for the 'csr' value, you can not use a register/variable to provide this. Which means you can not put it in a loop and get the CSR register number from a variable in C.

    The specific ASM instruction that is generated is encoded to include the CSR register number, the CSR to work with is never taken from a general purpose register, it is encoded into the instruction itself.

    Only the values (the contents of the CSR register) that are read from written to, always use a general purpose register. Allowing the value to be taken from or returned to, a variable in C.

    The other matter is the use of the "memory" clobber constraint in the question is probably not what you want. This instructs the compiler to perform a barrier around the CSR access, but this contradicts a general claim (I am making here) that CSR access instructions have no side-effect that would alter memory in a way the compiler does not expect. Think of it more like an IO device, you are altering hardware state not memory state.

    Most userspace of use of CSR instruction never need a memory barrier, like timers/counters/FPU-state and doing so will result in the compiler generating less optimal code. Due to excessive invalidation and reloading of memory state.

    There will be specific situations where a CSR access does need a barrier, but they will be the exception and you should perform that explicitly using a separate C language statement. Rather than lower the performance of a macro primitive that has many use cases.

    The macro include the usage information, since the compiler is helpful to provide the code with the compile error message and this will remind the user of this particular quirk of usage.

    #define CSRR_READ(v, csr)                           \
    /* CSRR_READ(v, csr):
     * csr: MUST be a compile time integer 12-bit constant (0-4095)
     */                                             \
    __asm__ __volatile__ ("csrr %0, %1"             \
                  : "=r" (v)                        \
                  : "n" (csr)                       \
                  : /* clobbers: none */ );