Have a look at the RARS simulator as an example of a simple RISC V implementation. It implements sufficient CSRs (e.g. the exception cause, processor status, exception pc, vector table address, etc..) that you can program an interrupt handler.
You'll need:
utvec
— sets the exception handler address
ustatus
— to enable/disable interrupts,
uscratch
— needed by software exception handler,
ucause
— tells the reason for exception
uepc
— tells the address of uncompleted instruction at exception
And some others. In RARS, you can see the registers implemented in the register display, Control and Status tab.
I believe RARS supports the timer, so has some
CSRs for that. It also provides a floating point unit, so some CSRs
for exceptions for that as well as rounding configuration. For
handling memory access exceptions, it has utval
. And then it
offers some counters. See also table 2.2 in Document Version
20190608-Priv-MSU-Ratified
I would think that your usage of CSRs would be restricted to standalone application configuration, e.g. initial bootup, and interrupt handling, both of which would be written in assembly.
Hard to imagine that compiled C code (object files, .o's) would touch the CSRs in any way. If you have an example of that, please share it.
In some environments, the C implementation allows for standalone (e.g. unhosted) programs. It is possible that such a program created by some compiler includes startup configuration and an exception handler though more likely that these would be user supplied. See, for example, http://cs107e.github.io/guides/gcc/