For baremetal programming I’ll often need to access CSRs, e.g. mstatus.mie for critical sections, mcause in interrupts handlers, etc. Defining function wrappers for accessing these registers creates easier to understand code, however writing these wrappers is pretty tedious.
The quick reference on this blog is generated from a YAML description (csr.yaml). Using that with a web template engine script (generators/yaml_jinja.py) and a template (templates/riscv-csr.h) code can be easily generated.
The full header is include/riscv-csr.h, and there is an example examples/test_csr.c. The CSRs Acccess quick reference has been updated with these links.
The template below shows the various methods to access CSRs. A few notes:
- There are three patterns for register access: read, writing and atomic read then write.
- There are more patterns for bitfield accesses,
- Functions for accessing any bit: (write, set, clear) x (no return, atomic read then modify).
- Macros are defined for accessing the lower 5 bits with immediate data instructions.
/* Register access functions for RISC-V system registers. SPDX-License-Identifier: Unlicense https://five-embeddev.com/ */ #ifndef RISCV_CSR_H #define RISCV_CSR_H #include#if __riscv_xlen==32 typedef uint32_t uint_xlen_t; typedef uint32_t uint_csr32_t; typedef uint32_t uint_csr64_t; #elif __riscv_xlen==64 typedef uint64_t uint_xlen_t; typedef uint32_t uint_csr32_t; typedef uint64_t uint_csr64_t; #else #error "Unknown XLEN" #endif {%- for reg_name,reg_data in data.regs.items() %} {%- if not reg_data.mmio %} /******************************************* * {{reg_name}} - {{reg_data.priv}} - {{reg_data.desc}} */ {%- set ctype_reg = reg_data|csr_ctype %} {%- set ctype_arg = reg_data|arg_ctype %} {%- if "R" in reg_data.priv %} static inline {{ctype_arg}} csr_read_{{reg_name}}(void) { {{ctype_reg}} value; __asm__ volatile ("csrr %0, {{reg_name}}" : "=r" (value) /* output : register */ : /* input : none */ : /* clobbers: none */); return value; } {%- endif %} {%- if "W" in reg_data.priv %} static inline void csr_write_{{reg_name}}({{ctype_reg}} value) { __asm__ volatile ("csrw {{reg_name}}, %0" : /* output: none */ : "r" (value) /* input : from register */ : /* clobbers: none */); } static inline {{ctype_arg}} csr_read_write_{{reg_name}}({{ctype_arg}} new_value) { {{ctype_reg}} prev_value; __asm__ volatile ("csrrw %0, {{reg_name}}, %1" : "=r" (prev_value) /* output: register %0 */ : "r" (new_value) /* input : register */ : /* clobbers: none */); return prev_value; } {%- endif%} {%- if reg_data.fields %} {%- if "W" in reg_data.priv %} /* Register CSR bit set and clear instructions */ static inline void csr_set_bits_{{reg_name}}({{ctype_arg}} mask) { __asm__ volatile ("csrrs zero, {{reg_name}}, %0" : /* output: none */ : "r" (mask) /* input : register */ : /* clobbers: none */); } static inline void csr_clr_bits_{{reg_name}}({{ctype_arg}} mask) { __asm__ volatile ("csrrc zero, {{reg_name}}, %0" : /* output: none */ : "r" (mask) /* input : register */ : /* clobbers: none */); } static inline {{ctype_arg}} csr_read_set_bits_{{reg_name}}({{ctype_arg}} mask) { {{ctype_reg}} value; __asm__ volatile ("csrrs %0, {{reg_name}}, %1" : "=r" (value) /* output: register %0 */ : "r" (mask) /* input : register */ : /* clobbers: none */); return value; } static inline {{ctype_arg}} csr_read_clr_bits_{{reg_name}}({{ctype_arg}} mask) { {{ctype_reg}} value; __asm__ volatile ("csrrc %0, {{reg_name}}, %1" : "=r" (value) /* output: register %0 */ : "r" (mask) /* input : register */ : /* clobbers: none */); return value; } /* {{reg_name}}, CSR write value via immediate value (only up to 5 bits) */ #define CSR_WRITE_IMM_{{reg_name|upper}}(VALUE) \ __asm__ volatile ("csrrwi zero, {{reg_name}}, %0" \ : /* output: none */ \ : "i" (VALUE) /* input : immediate */ \ : /* clobbers: none */) /* {{reg_name}}, CSR set bits via immediate value mask (only up to 5 bits) */ #define CSR_SET_BITS_IMM_{{reg_name|upper}}(MASK) \ __asm__ volatile ("csrrsi zero, {{reg_name}}, %0" \ : /* output: none */ \ : "i" (MASK) /* input : immediate */ \ : /* clobbers: none */) /* {{reg_name}}, CSR clear bits via immediate value mask (only up to 5 bits) */ #define CSR_CLR_BITS_IMM_{{reg_name|upper}}(MASK) \ __asm__ volatile ("csrrci zero, {{reg_name}}, %0" \ : /* output: none */ \ : "i" (MASK) /* input : immediate */ \ : /* clobbers: none */) {%- endif %} {%-for field_name,field_data in reg_data.fields.items() %} {%- set bit_width = field_data|csr_bit_width %} {%- set bit_offset = field_data|csr_bit_offset %} #define {{reg_name|upper}}_{{field_name|upper}}_BIT_OFFSET {{bit_offset}} #define {{reg_name|upper}}_{{field_name|upper}}_BIT_WIDTH {{bit_width}} #define {{reg_name|upper}}_{{field_name|upper}}_BIT_MASK {{bit_offset|csr_format_mask(bit_width)}} #define {{reg_name|upper}}_{{field_name|upper}}_ALL_SET_MASK {{0|csr_format_mask(bit_width)}} {%- endfor%} {%-endif%} {%- endif%} {%- endfor%} #endif // #define RISCV_CSR_H </pre>