Register encoding for CSR instructions

1,580 views
Skip to first unread message

Liviu Ionescu

unread,
Jan 12, 2018, 3:02:21 PM1/12/18
to RISC-V ISA Dev
In the current ISA, all CSR instructions are immediate.

This prevents the implementations of plain, non static inline, C/C++ function where the `id` is not known at compile time, like:

```c
reg_t
read_csr(id_t id)
{
reg_t val;

// ???

return val;
}
```

You'll probably argue that the `id` should be known at compile time and the function should be a C++ inline or a C static inline.

Unfortunately, because of the way the compiler is implemented, this does not work either, on `-O0` GCC generates plain functions even for inline functions; only on higher optimisation levels the call with an immediate argument can be generated with immediate instructions.

Please don't think that accessing the CSRs via generic function might be a hypothetic use case, it affected my C++ code from the very first contact with RISC-V, I had to duplicate lots of assembly code to implement accessor and mutators for a handful of CSRs.

C programmers will probably suggest preprocessor macros, but in C++ macros are generally not accepted, for example neither MIRA C++ nor Lockheed Martin JSF coding styles allow them:

- [MISRA C++ Rule 16–0–4, Required] - Function-like macros shall not be defined.
- [JSF AV Rule 29] - The #define pre-processor directive shall not be used to create inline macros. Inline functions shall be used instead.


The solution is to add instructions that take the CSR id also from a register, not only from an immediate value.


Regards,

Liviu



kr...@berkeley.edu

unread,
Jan 12, 2018, 6:09:12 PM1/12/18
to Liviu Ionescu, RISC-V ISA Dev

>>>>> On Fri, 12 Jan 2018 22:02:17 +0200, Liviu Ionescu <i...@livius.net> said:
| The solution is to add instructions that take the CSR id also from a register, not only from an immediate value.

This would cause havoc in any efficient pipeline design, so is not
going to happen.

A sparse switch table in software is fine way of coding this case.

Krste

Liviu Ionescu

unread,
Jan 12, 2018, 6:26:52 PM1/12/18
to Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 01:09, kr...@berkeley.edu wrote:
>
>
>>>>>> On Fri, 12 Jan 2018 22:02:17 +0200, Liviu Ionescu <i...@livius.net> said:
> | The solution is to add instructions that take the CSR id also from a register, not only from an immediate value.
>
> This would cause havoc in any efficient pipeline design, so is not
> going to happen.

in a MMIO implementation, a pointer to the actual register would be used

how would the pipeline behave in this case?

> A sparse switch table in software is fine way of coding this case.

sparse? the reason why such a functionality is needed is exactly because the id is not known, so the full possible table must be implemented.

and even sparse, do you have any idea how long it would take to execute such a call compared to a 'havoc-ed' pipeline?

regards,

Liviu

Michael Chapman

unread,
Jan 12, 2018, 6:32:17 PM1/12/18
to Liviu Ionescu, Krste Asanovic, RISC-V ISA Dev

What is the use case to read from a "variable" register?

The only one I am aware of is a debugger.

If a debugger is running on a RISC-V, then this is not an embedded
application a 4096 entry case statement will do the job and should be
efficient enough.

Why do you want to do that with the CSR registers, but not with the
floating point or general purpose integer registers?

Joel Vandergriendt

unread,
Jan 12, 2018, 6:32:47 PM1/12/18
to Krste Asanovic, Liviu Ionescu, RISC-V ISA Dev

If it is actually known at compile time, you could use templates:

template<int i>
int read_csr()
{
    int tmp;
    asm volatile("csrr %0,%1":"=r"(tmp):"i"(i));
}

int main(){
    read_csr<5>();
}

compiles with -O0 to

00000000 <main>:
   0:    ff010113              addi    sp,sp,-16
   4:    00112623              sw    ra,12(sp)
   8:    00812423              sw    s0,8(sp)
   c:    01010413              addi    s0,sp,16
  10:    00000317              auipc    t1,0x0
  14:    000300e7              jalr    t1
  18:    00000793              li    a5,0
  1c:    00078513              mv    a0,a5
  20:    00c12083              lw    ra,12(sp)
  24:    00812403              lw    s0,8(sp)
  28:    01010113              addi    sp,sp,16
  2c:    00008067              ret

Joel Vandergriendt

Software Engineer

VectorBlox Computing Inc.


--
You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+unsubscribe@groups.riscv.org.
To post to this group, send email to isa...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/23129.16403.170105.855331%40KAMacBookAir2012.local.

Liviu Ionescu

unread,
Jan 12, 2018, 6:38:48 PM1/12/18
to Joel Vandergriendt, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 01:32, Joel Vandergriendt <jo...@vectorblox.com> wrote:
>
> compiles with -O0 to ...

can you help me identify the csrr in the generated code?

thank you,

Liviu


Joel Vandergriendt

unread,
Jan 12, 2018, 6:42:54 PM1/12/18
to Liviu Ionescu, Krste Asanovic, RISC-V ISA Dev

oops I didn’t copy all of the dump:

00000000 <main>:
   0:    ff010113              addi    sp,sp,-16
   4:    00112623              sw    ra,12(sp)
   8:    00812423              sw    s0,8(sp)
   c:    01010413              addi    s0,sp,16
  10:    00000317              auipc    t1,0x0
  14:    000300e7              jalr    t1
  18:    00000793              li    a5,0
  1c:    00078513              mv    a0,a5
  20:    00c12083              lw    ra,12(sp)
  24:    00812403              lw    s0,8(sp)
  28:    01010113              addi    sp,sp,16
  2c:    00008067              ret

00000030 <_Z8read_csrILi5EEiv>:
  30:    fe010113              addi    sp,sp,-32
  34:    00812e23              sw    s0,28(sp)
  38:    02010413              addi    s0,sp,32
  3c:    005027f3              csrr    a5,0x5
  40:    fef42623              sw    a5,-20(s0)
  44:    00000013              nop
  48:    00078513              mv    a0,a5
  4c:    01c12403              lw    s0,28(sp)
  50:    02010113              addi    sp,sp,32
  54:    00008067              ret

Joel Vandergriendt
Software Engineer
VectorBlox Computing Inc.

Liviu Ionescu

unread,
Jan 12, 2018, 6:53:56 PM1/12/18
to Joel Vandergriendt, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 01:42, Joel Vandergriendt <jo...@vectorblox.com> wrote:
>
> oops I didn’t copy all of the dump: ...

thank you.

Liviu


Jacob Bachmeyer

unread,
Jan 12, 2018, 7:34:51 PM1/12/18
to kr...@berkeley.edu, Liviu Ionescu, RISC-V ISA Dev
kr...@berkeley.edu wrote:
> On Fri, 12 Jan 2018 22:02:17 +0200, Liviu Ionescu <i...@livius.net> said:
>
> | The solution is to add instructions that take the CSR id also from a register, not only from an immediate value.
>
> This would cause havoc in any efficient pipeline design, so is not
> going to happen.
>

This also would break an invariant (each CSR access can only affect one
specific CSR) that we currently have that simplifies security audits of
supervisors. In a supervisor, there must be no code paths (including
potential ROP gadgets) that leave SUM set. Similarly, there must be no
code paths (including potential ROP gadgets) that can clear the MODE
field inadvertently. Further, in the current draft of RVH, there must
be no code paths (including potential ROP gadgets) that can result in
the guest being resumed with SPV clear (a VM escape if it happens!).
These critical fields are in sstatus, satp, and hstatus respectively and
the risks can (currently) be evaluated by examining only the CSR access
instructions that specifically apply to sstatus, satp, and hstatus.

If dynamic CSR access is added, that set of potentially abusable
instructions could expand to *all* CSR accesses in the supervisor. We
should not make attacks easier like that.


-- Jacob

Allen J. Baum

unread,
Jan 13, 2018, 3:07:17 AM1/13/18
to Liviu Ionescu, Krste Asanovic, RISC-V ISA Dev
I'm trying to come up with a use case that justifies this, and can't.

Under what circumstances would I not know which CSR that is being accessed,
and further, why do those circumstances care so much about latency (and/or the switch table size, for that matter)?

You can find use cases for register-indirect CSR reads (and for just about anything else really) - but not one that would justify the amount of complexity this would take.

There is no guarantee that any implementation will have MMIO addresses that shadow CSRs, ( with the possible exception of some of the debug DMI registers - and even that is optional)

The bar to add a new instruction (especially one that mucks with the pipeline) is high, and a switch statement will perform the function you want.

If it's that important to some application you care about
>--
>You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
>To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.
>To post to this group, send email to isa...@groups.riscv.org.
>Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
>To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/588A8C06-EA81-427F-A8C3-C8D57269205B%40livius.net.


--
**************************************************
* Allen Baum tel. (908)BIT-BAUM *
* 248-2286 *
**************************************************

Liviu Ionescu

unread,
Jan 13, 2018, 4:43:01 AM1/13/18
to Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 10:07, Allen J. Baum <allen...@esperantotech.com> wrote:
>
> I'm trying to come up with a use case that justifies this, and can't.

-O0


Liviu


Andrew Waterman

unread,
Jan 13, 2018, 4:47:58 AM1/13/18
to Liviu Ionescu, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
Amusingly, the P&H textbook describes making architectural decisions on the basis of -O0 code as a “pitfall.”

--
You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.
To post to this group, send email to isa...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/.

Liviu Ionescu

unread,
Jan 13, 2018, 6:42:09 AM1/13/18
to Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 11:47, Andrew Waterman <wate...@eecs.berkeley.edu> wrote:
>
> Amusingly, the P&H textbook describes making architectural decisions on the basis of -O0 code as a “pitfall.”

I guess the textbook is generally right.

Unfortunately the very tight RISC-V ISA does not leave too much room to manoeuvre.

It might be perfect for privileged mode machines, no questions about it, but for bare-metal embedded, by enforcing the same privileged specs instead of accepting a separate embedded profile, it is far from C/C++ friendly.

Not being able to conveniently implement modern C/C++ access to system registers is only one example.


Regards,

Liviu


Cesar Eduardo Barros

unread,
Jan 13, 2018, 8:08:11 AM1/13/18
to Allen J. Baum, Liviu Ionescu, Krste Asanovic, RISC-V ISA Dev
Em 13-01-2018 06:07, Allen J. Baum escreveu:
> I'm trying to come up with a use case that justifies this, and can't.
>
> Under what circumstances would I not know which CSR that is being accessed,
> and further, why do those circumstances care so much about latency (and/or the switch table size, for that matter)?
>
> You can find use cases for register-indirect CSR reads (and for just about anything else really) - but not one that would justify the amount of complexity this would take.
>
> There is no guarantee that any implementation will have MMIO addresses that shadow CSRs, ( with the possible exception of some of the debug DMI registers - and even that is optional)
>
> The bar to add a new instruction (especially one that mucks with the pipeline) is high, and a switch statement will perform the function you want.

From what I understand, the main issue is that the CSRs are not some
well-organized register file or memory which can be indexed into and
acessed. The CSRs are probably spread around the core, some in separate
pieces, and some might even have direct side-effects.

An example would be fflags/frm/fcsr. They might live next to the
floating point unit, or they might be a special renamed register in an
out-of-order implementation. The decoder has to know which CSR is being
talked about, and it can't get that information from a register since
register reads come later in the pipeline.

In my opinion, the correct way to think about CSR instructions is not
"instructions acessing a separate register file which has the control
registers". The correct way to think about CSR instructions is "special
control instructions which happen to have a common encoding schema".

And even if you think of them as registers, consider the following
alternative rationale: the RISC-V ISA has no indirect register access.
The number of the register(s) being acessed is always fixed within the
instruction. This applies equally to integer registers, floating-point
registers, and control registers.

If you really need to do indirect register access (for instance, in some
place you don't know whether you want to access x10 or x17), you have to
either do a switch table, or go old-school and use self-modifying code.

> At 1:26 AM +0200 1/13/18, Liviu Ionescu wrote:
>>> On 13 Jan 2018, at 01:09, kr...@berkeley.edu wrote:
>>>
>>>
>>>>>>>> On Fri, 12 Jan 2018 22:02:17 +0200, Liviu Ionescu <i...@livius.net> said:
>>> | The solution is to add instructions that take the CSR id also from a register, not only from an immediate value.
>>>
>>> This would cause havoc in any efficient pipeline design, so is not
>>> going to happen.
>>
>> in a MMIO implementation, a pointer to the actual register would be used
>>
>> how would the pipeline behave in this case?
>>
>>> A sparse switch table in software is fine way of coding this case.
>>
>> sparse? the reason why such a functionality is needed is exactly because the id is not known, so the full possible table must be implemented.
>>
>> and even sparse, do you have any idea how long it would take to execute such a call compared to a 'havoc-ed' pipeline?


--
Cesar Eduardo Barros
ces...@cesarb.eti.br

Stefan O'Rear

unread,
Jan 13, 2018, 8:14:19 AM1/13/18
to Liviu Ionescu, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
This is a toolchain ergonomics issue. You should not need any inline
assembly to access system registers.

My preferred solution (small variations acceptable):

extern volatile uint64_t mcause __attribute__((csr(0x342)));
extern volatile uint64_t mstatus __attribute__((csr(0x300)));

if (mcause == 0x80000007) { /* timer interrupt */ }

There is precedent in both gcc and clang for the above.

atomic_or(&mstatus, 8); /* enable interrupts with csrsi; one can dream */

Likewise there should be a function attribute for automatically
generating trap entry/exit code. There's no good way to handle
interrupt priorities automatically, and latency will probably be worse
than a hand-written wrapper that only saves what's needed, but we can
make undemanding cases easy.

-s

Liviu Ionescu

unread,
Jan 13, 2018, 9:14:43 AM1/13/18
to Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 15:08, Cesar Eduardo Barros <ces...@cesarb.eti.br> wrote:
>
> ... The CSRs are probably spread around the core, some in separate pieces, and some might even have direct side-effects.

you are probably right, but this is not relevant from a software point of view.

> In my opinion, the correct way to think about CSR instructions is not "instructions accessing a separate register file which has the control registers". The correct way to think about CSR instructions is "special control instructions which happen to have a common encoding schema".

for those living in the priv specs world, having thousands of separate assembly instructions to access various strange things might be a fact of life, and they might even enjoy it, but for someone living in the bare-metal embedded world, not having everything mapped in a memory window and available via standard C structs or indirect absolute pointers is a nightmare.

sorry, 'no assembly required' in my world.


regards,

Liviu

Michael Chapman

unread,
Jan 13, 2018, 9:24:05 AM1/13/18
to Liviu Ionescu, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev

In reality, what you want/need to do with a CSR is very specific to that
CSR. I.e. the code is tied to that particular CSR.

This means that the CSR number is a constant for that piece of code.

I.e. the piece of template code proposed by some one earlier is
perfectly adequate (you can also do a inline static function in C with a
constant as parameter although the error message you get if you try to
use a nonconstant as a parameter will not be as clear as in the C++ case).

I have never seen a use case where you want a variable register number -
with the exception of a debugger.

Liviu Ionescu

unread,
Jan 13, 2018, 9:34:30 AM1/13/18
to Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 16:25, Michael Chapman <michael.c...@gmail.com> wrote:
>
> you can also do a inline static function in C with a
> constant as parameter

do you have an example that also works with -O0?

Liviu

Cesar Eduardo Barros

unread,
Jan 13, 2018, 10:07:11 AM1/13/18
to Liviu Ionescu, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
Em 13-01-2018 12:14, Liviu Ionescu escreveu:
>> In my opinion, the correct way to think about CSR instructions is not "instructions accessing a separate register file which has the control registers". The correct way to think about CSR instructions is "special control instructions which happen to have a common encoding schema".
>
> for those living in the priv specs world, having thousands of separate assembly instructions to access various strange things might be a fact of life, and they might even enjoy it, but for someone living in the bare-metal embedded world, not having everything mapped in a memory window and available via standard C structs or indirect absolute pointers is a nightmare.

If it's pointers you want, why not pointers to code?

Something like

#define FCSR 0
#define SSCRATCH 1
#define SEPC 2

typedef int (*readcsr)(void);
typedef void (*writecsr)(int);

static readcsr csrr[3];
static writecsr csrw[3];

void fn(void) {
int x = csrr[SSCRATCH]();
csrw[SSCRATCH](x);
}

And since each of the read/write functions would have a fixed size (8
bytes without RVC, for the CSR operation and the return), you could even
avoid the intermediate array, putting the code for all known CSRs in
sequence and using first+index*8 as the pointer to the function.

Michael Chapman

unread,
Jan 13, 2018, 10:14:47 AM1/13/18
to Liviu Ionescu, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev

No. -O1 is required for using a inline static function. It would be
possible to use pragmas to ensure that this part of the code is compiled
with optimization.

However, I think it would be desirable to add __builtin functions to the
compiler for these kinds of instructions. The compiler code can then
easily check if it has a constant and produce a much better error
message than the static inline case.
> ---
> This email has been checked for viruses by AVG.
> http://www.avg.com
>

Liviu Ionescu

unread,
Jan 13, 2018, 11:27:18 AM1/13/18
to Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 17:16, Michael Chapman <michael.c...@gmail.com> wrote:
>
>
> No. -O1 is required for using a inline static function.

aha.

so, you confirm my findings, with the current compiler and the current ISA, it is not possible in modern C to access the CSRs in a generic way, you need to do it individually, with separate assembly functions for each of them.

and there are not even that many, only several tens to hundreds; each with separate read, write, set, clear, etc... piece of cake... :-(


regards,

Liviu










Liviu Ionescu

unread,
Jan 13, 2018, 11:54:06 AM1/13/18
to Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 17:16, Michael Chapman <michael.c...@gmail.com> wrote:
>
> it would be desirable to add __builtin functions to the
> compiler for these kinds of instructions.

and


> On 13 Jan 2018, at 15:14, Stefan O'Rear <sor...@gmail.com> wrote:
>
> My preferred solution (small variations acceptable):
>
> extern volatile uint64_t mcause __attribute__((csr(0x342)));
> extern volatile uint64_t mstatus __attribute__((csr(0x300)));


Sure, if you have the muscle, you can bend the compiler to overcome any unusual hardware design choices, but this will not help you make many friends, at least not among those maintaining other compilers/languages/development tools...

It took me almost one year of constant pressure to get rid of the long list of 4096 registered always transferred by OpenOCD and GDB...


Regards,

Liviu



Jim Wilson

unread,
Jan 13, 2018, 12:51:19 PM1/13/18
to Liviu Ionescu, Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
On Sat, Jan 13, 2018 at 8:27 AM, Liviu Ionescu <i...@livius.net> wrote:
> so, you confirm my findings, with the current compiler and the current ISA, it is not possible in modern C to access the CSRs in a generic way, you need to do it individually, with separate assembly functions for each of them.

It was already mentioned that you can do this with self modifying
code. Here is a poorly written and poorly tested example that seems
to work:

rohan:2162$ cat tmp2.c
#include <stdio.h>

int __attribute__ ((noinline))
read_csr (short i)
{
volatile short code[3] = { 0x2573, 0x0000, 0x8082 };
int (*func)(void) = &code[0];
code[1] = i;
asm volatile ("fence.i");
return func ();
}

int
main (void)
{
int rdcycle = read_csr (0xC000);
printf ("rdcycle is 0x%x\n", rdcycle);
int rdtime = read_csr (0xC010);
printf ("rdtime is 0x%x\n", rdtime);
return 0;
}
rohan:2162$ riscv32-unknown-elf-gcc -O2 -Wno-incompatible-pointer-types tmp2.c
rohan:2163$ qemu-riscv32 a.out
rdcycle is 0xb572850a
rdtime is 0xb59853c4
rohan:2164$ qemu-riscv32 a.out
rdcycle is 0xdeeb3722
rdtime is 0xdf10559e
rohan:2164$ qemu-riscv32 a.out
rdcycle is 0x479daca8
rdtime is 0x47c41de5
rohan:2164$

Jim

Cesar Eduardo Barros

unread,
Jan 13, 2018, 12:59:42 PM1/13/18
to Liviu Ionescu, Michael Chapman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
Sure, if you handicap yourself:

- No macros (for C) or templates (for C++)
- No compiler optimization allowed
- No compile-time code generation allowed
- No CSR intrinsics in the compiler
- CSR number not known at compile time

I don't quite see the point of this exercise. Most real-world code will
not have these restrictions. It doesn't make sense to force every
implementation of the standard ISA to carry the cost of "indirect CSR"
instructions, just because some people want to artificially limit
themselves.

Liviu Ionescu

unread,
Jan 13, 2018, 1:43:11 PM1/13/18
to Cesar Eduardo Barros, Michael Chapman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 19:59, Cesar Eduardo Barros <ces...@cesarb.eti.br> wrote:
>
> - No macros (for C) or templates (for C++)

templates are fine, function macros are problematic in general, unacceptable in C++

> - No compiler optimization allowed

on the contrary, the code should build with all possible optimisations, including -O0

> - No compile-time code generation allowed

I'm not sure what you mean

> - No CSR intrinsics in the compiler

as far as I know, there are no CSR intrinsics in the compiler, and I do not know of any plans to add them

> - CSR number not known at compile time

Michael confirmed that even if the number is known at compile time, GCC does not accept it on -O0.

> I don't quite see the point of this exercise. Most real-world code will not have these restrictions.

it is not an exercise, it is real-world code, a C and C++ API to the RISC-V core, similar to ARM CMSIS Core. it is fully functional, but unnecessarily complicated and redundant due to these ISA specificities.


regards,

Liviu




Michael Chapman

unread,
Jan 13, 2018, 1:56:27 PM1/13/18
to Liviu Ionescu, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
I really fail to see any use case which makes where we need a function
which takes a parameter which is the csr number/address.
What is wrong with a pair of functions for each CSR? E.g.

    int  csr_mcause(void);
    int  set_csr_mcause(int new_val);
    int  csr_epc(void);
    int  set_csr_epc(int new_val);

etc.
The functions can easily be generated, one per .c file in order to
generated an object file per function as is done for libc/newlib etc.
Then you only pay for what you use - even when compiling with -O0 and
linking without GC, relaxation and link time recompilation.

In any case you can do what you want with a switch. There are not a huge
number of CSRs - most of the 4K space will punt to default anyway.

Liviu Ionescu

unread,
Jan 13, 2018, 1:59:27 PM1/13/18
to Jim Wilson, Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 19:51, Jim Wilson <ji...@sifive.com> wrote:
>
> It was already mentioned that you can do this with self modifying
> code.

sure, this is possible, and your test proved it is functional, but do you have any idea how many cycles would take to read a CSR using this approach compared it to the 'pipeline havoc' created by an instruction with register encoding?

what is basically needed, and both Joel and Michael understood perfectly, is a C static inline function.

when called with a constant argument, it should optimise and generate an immediate csrr instruction, when called with a variable, it should generate something else, currently not available.

using elaborated static inlines is currently the norm in modern C/C++.

unfortunately, with the current compiler and the current ISA, accessing the CSRs is not possible in a generic way and needs to be done individually.


regards,

Liviu

Liviu Ionescu

unread,
Jan 13, 2018, 2:21:28 PM1/13/18
to Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 13 Jan 2018, at 20:57, Michael Chapman <michael.c...@gmail.com> wrote:
>
> What is wrong with a pair of functions for each CSR? E.g. ...

I have already done this, the code is functional, but ugly and redundant. C++ templates might bring a slight improvement to the C++ API, but the C API will probably require macros.

I was just searching for a more elegant solution, which apparently is not possible with the current ISA.

if everyone is happy with using the current assembly instructions for accessing the CSRs, fine, but for bare-metal embedded devices, a MMIO approach would have been easier to use.


regards,

Liviu








Michael Chapman

unread,
Jan 13, 2018, 2:26:32 PM1/13/18
to Liviu Ionescu, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
Why would a MMIO approach be easier to use?
I still fail to see any use for any kind of device - bare-metal or
otherwise.

Where is the inconvenience in the name of the register being part of the
function name? Or indeed any other of the proposed approaches. None of
these registers is general purpose, none of them can be exchanged for
another.

Liviu Ionescu

unread,
Jan 13, 2018, 4:30:46 PM1/13/18
to Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev, Tommy Murphy


> On 13 Jan 2018, at 21:27, Michael Chapman <michael.c...@gmail.com> wrote:
>
> Why would a MMIO approach be easier to use?

Because

- accessing MMIO registers in an application can be done with standard C/C++ constructs, and remain functional in any optimisation level. Accessing current CSRs in C/C++ require custom assembly statements and individual sets of functions for each register.

- even more, mechanisms to access MMIO registers are generally available in most other languages; accessing CSRs in other languages require custom libraries (which in turn would require 4096 separate functions or self modifying code).

- MMIO registers are directly accessible in most standard debuggers, in a graphical manner, down to bitfield level, like any peripheral registers.

(I just added support in Eclipse for the SiFive devices peripheral registers, so I know what I'm talking about).

I don't know of any development environment to conveniently support examining/modifying CSRs neither in character or graphical mode. Eclipse definitely does not. GDB can print CSRs with `p/x name`, but this is tedious.

Up to now, the workaround in debuggers was to group CSRs with regular registers, which is very inefficient, because debuggers fetch all registers at each step, and fetching hundreds/thousands of registers is terrible.

---

In a few words, because MMIO is a standard mechanism, very well supported in programming languages and development tools, while custom `csr` instructions are... custom. And as such more complicated/expensive to use.


I know the general opinion in the Linux community that everything should be Linux based, but in this case the one-size-fits-all approach is really not beneficial.

The current custom `csr` instructions might be perfect for priv mode devices, and I'm not at all suggesting to alter the priv specs if people are happy with them.

What I'm advocating is different: to consider a separate set of specs for bare-metal devices, to better match the needs and tools used by embedded developers, aiming to make development more efficient (and more enjoyable).


Both profiles (privileged and embed) would share the RISC-V instruction set, which is a nice piece of engineering, but would allow to better optimise for each separate needs, instead of enforcing lots of supervisor/hypervisor/etc strict requirements to much simpler machine mode devices.

In addition to 'riscv-privileged', was a 'riscv-embed' profile seriously considered up to now?


Regards,

Liviu








kr...@berkeley.edu

unread,
Jan 13, 2018, 5:20:32 PM1/13/18
to Liviu Ionescu, Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev, Tommy Murphy

I really suggest people cease this thread. It is not productive. I
can safely predict indirect access to CSRs via a register address
won't be added to the standard for all the reasons given in this
thread.

There is no use case given beyond a debugger wanting to index the CSR
space, and the software approach provides this capability. There is
no argument that optimizing this is important to anyone. If you care
about running this operation natively in a very limited environment,
use the self-modifying code variant. When operating over the debugger
link, you can construct the instruction and pass to the program buffer
for execution.

Even if this somehow did magically appeared in the standard, I
guarantee every implementer would use trap and emulate using
self-modifying code in M-mode. So, you can either call that function
directly, or have to install the same code in a trap handler to get
the functionality in M-mode.

There are much more important uses of everyone's time and energy,

Krste

Jacob Bachmeyer

unread,
Jan 13, 2018, 5:57:07 PM1/13/18
to Liviu Ionescu, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
For a bare-metal embedded, M-mode-only implementation, omitting the
SYSTEM opcode entirely and using MMIO for CSRs is reasonable. My
concerns about auditing supervisors do not apply, since there is no
supervisor, or, for that matter, any hardware-enforced security at all,
in an M-mode-only system. RVA provides atomic operations on memory
locations, so the CSR instructions are unneeded in a minimal embedded
RISC-V.


-- Jacob

Jacob Bachmeyer

unread,
Jan 13, 2018, 5:59:49 PM1/13/18
to Stefan O'Rear, Liviu Ionescu, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
These are purely toolchain features and will work fine with the current
ISA. You are proposing a "csr" variable attribute that causes a
variable to actually refer to a CSR; GCC should be able to use the CSR
instructions in this scenario.

Again, this does not require any changes to the ISA, only the RISC-V GCC.


-- Jacob

Jacob Bachmeyer

unread,
Jan 13, 2018, 6:06:58 PM1/13/18
to Liviu Ionescu, Jim Wilson, Michael Chapman, Cesar Eduardo Barros, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
Liviu Ionescu wrote:
> unfortunately, with the current compiler and the current ISA, accessing the CSRs is not possible in a generic way and needs to be done individually.
>

You have hit the nail on the head with "current compiler". Ask for that
"csr" variable attribute on sw-dev; there is no need to add instructions
in the ISA for this.


-- Jacob

Allen Baum

unread,
Jan 14, 2018, 4:53:03 PM1/14/18
to Liviu Ionescu, Michael Chapman, Cesar Eduardo Barros, Krste Asanovic, RISC-V ISA Dev, Tommy Murphy
OK, I’m beginning to understand the pain point- but I don’t think I agree with your solution.
You’re asking for a cost in all future implementations (multiplied by millions -we hope) actual physical cost in the die themselves, instead of a one time cost to fix the tools (well, one per tool)

I’ll go out on a limb and say there will be fewer tools than implementations, and that if a proper Foundation generated, machine readable spec for CSRs exists, that code could be mechanically generated (along with code that generates documentation, RTL, and validation tests, among other things- there are existence proofs).
I will admit that I don’t think the collateral for that kind of spec is up to that point yet, by a long shot, but it should be; that kind of spec does exist internally at least for some commercial HW vendors).

I also don’t understand why you need 4k separate functions when far fewer (by one or two orders of magnitude) CSRs are implemented.

I also don’t understand why the code that supports CSR fields in a graphical manner can’t be adapted from the code that works for MMIO registers (but I’m a crap programmer, so take that and the above statements with a grain of salt).


-Allen

Liviu Ionescu

unread,
Jan 14, 2018, 6:38:21 PM1/14/18
to Allen Baum, Michael Chapman, Cesar Eduardo Barros, Krste Asanovic, RISC-V ISA Dev, Tommy Murphy


> On 14 Jan 2018, at 23:52, Allen Baum <allen...@esperantotech.com> wrote:
>
> OK, I’m beginning to understand the pain point- ...

Hi Allen/Jacob/Michael/Cesar/Jim/Stefan/Joel & others

From respect for the general audience of this group, who does not seem interested in discussions about bare-metal specific issues, I guess we should cease this thread, as suggested.

I remain committed to further explore solutions to improve the RISC-V specs to better match the needs of bare-metal devices, possibly leading to a new 'riscv-embed profile' to complement the 'riscv-privileged' specs.


If there are others in the industry interested in joining such a research path, please let me know, and we'll find a separate group/forum/etc to continue the discussions related to using/improving RISC-V in bare-metal embedded environments.

If not, and everyone is happy with the current solutions, I apologise for wasting your time and energy.


Liviu





Liviu Ionescu

unread,
Jan 17, 2018, 6:13:35 AM1/17/18
to jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 14 Jan 2018, at 00:57, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> For a bare-metal embedded, M-mode-only implementation, omitting the SYSTEM opcode entirely and using MMIO for CSRs is reasonable. My concerns about auditing supervisors do not apply, since there is no supervisor, or, for that matter, any hardware-enforced security at all, in an M-mode-only system. RVA provides atomic operations on memory locations, so the CSR instructions are unneeded in a minimal embedded RISC-V.

Fully agree.

---

Here is another example of issues caused the using CSRs instead of MMIOs:

https://github.com/riscv/riscv-gnu-toolchain/issues/319


Regards,

Liviu

Jacob Bachmeyer

unread,
Jan 17, 2018, 9:18:33 PM1/17/18
to Liviu Ionescu, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
That is just a toolchain "teething problem" and is already being fixed.
(Although the config string would have provided a better solution and I
still want that kind of self-describing hardware using a processor ID
ROM storing FDT.)

You are correct that these problems exist in early embedded
implementations, and I agree that the simplest M-mode-only embedded
systems should use MMIO and RVA instead of CSRs, but I also maintain
that larger systems (1) should use CSRs as they are currently defined
and (2) should *not* make CSRs accessible in MMIO space for security
reasons, with a possible exception to (2) for the monitor. S-mode MMIO
access to CSRs is asking for exploits as I see it.


-- Jacob

Liviu Ionescu

unread,
Jan 18, 2018, 3:58:09 AM1/18/18
to jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 18 Jan 2018, at 04:18, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> ... larger systems (1) should use CSRs as they are currently defined and (2) should *not* make CSRs accessible in MMIO space for security reasons, with a possible exception to (2) for the monitor. S-mode MMIO access to CSRs is asking for exploits as I see it.

I understand the need for security, but are you sure all CSRs can be a security threat?

Even on my small HiFive1, OpenOCD and GDB insist on pushing onto me the following list each time I need to transfer the general registers:

```
231,028 @"===== RISC-V Registers\n"
231,028 @"(0) zero (/32): 0x00000000\n"
231,028 @"(1) ra (/32): 0x80000094\n"
231,028 @"(2) sp (/32): 0x80003F80\n"
231,029 @"(3) gp (/32): 0x80000868\n"
231,029 @"(4) tp (/32): 0x00000000\n"
231,029 @"(5) t0 (/32): 0x80000004\n"
231,029 @"(6) t1 (/32): 0x80000000\n"
231,029 @"(7) t2 (/32): 0x00000000\n"
231,029 @"(8) fp (/32): 0x80003FC0\n"
231,029 @"(9) s1 (/32): 0x00000000\n"
231,029 @"(10) a0 (/32): 0x00000001\n"
231,029 @"(11) a1 (/32): 0x80000068\n"
231,029 @"(12) a2 (/32): 0x00000000\n"
231,029 @"(13) a3 (/32): 0x00000000\n"
231,029 @"(14) a4 (/32): 0x80000068\n"
231,029 @"(15) a5 (/32): 0x00000001\n"
231,029 @"(16) a6 (/32): 0x61727261\n"
231,029 @"(17) a7 (/32): 0x00000001\n"
231,029 @"(18) s2 (/32): 0x00008000\n"
231,030 @"(19) s3 (/32): 0x00000000\n"
231,030 @"(20) s4 (/32): 0xCEB98000\n"
231,030 @"(21) s5 (/32): 0x00000004\n"
231,030 @"(22) s6 (/32): 0x00000000\n"
231,030 @"(23) s7 (/32): 0x00000000\n"
231,030 @"(24) s8 (/32): 0x00000000\n"
231,030 @"(25) s9 (/32): 0x00000000\n"
231,030 @"(26) s10 (/32): 0x00000000\n"
231,030 @"(27) s11 (/32): 0x00000000\n"
231,030 @"(28) t3 (/32): 0x00000003\n"
231,030 @"(29) t4 (/32): 0x00000000\n"
231,030 @"(30) t5 (/32): 0x00000000\n"
231,030 @"(31) t6 (/32): 0x00000000\n"
231,030 @"(32) pc (/32): 0x20400000\n"
231,031 @"(833) mstatus (/32): 0x00001808\n"
231,031 @"(834) misa (/32)\n"
231,031 @"(835) medeleg (/32)\n"
231,031 @"(836) mideleg (/32)\n"
231,031 @"(837) mie (/32)\n"
231,031 @"(838) mtvec (/32)\n"
231,031 @"(839) mcounteren (/32)\n"
231,031 @"(868) mhpmevent3 (/32)\n"
231,031 @"(869) mhpmevent4 (/32)\n"
231,031 @"(870) mhpmevent5 (/32)\n"
231,031 @"(871) mhpmevent6 (/32)\n"
231,031 @"(872) mhpmevent7 (/32)\n"
231,031 @"(873) mhpmevent8 (/32)\n"
231,031 @"(874) mhpmevent9 (/32)\n"
231,031 @"(875) mhpmevent10 (/32)\n"
231,031 @"(876) mhpmevent11 (/32)\n"
231,031 @"(877) mhpmevent12 (/32)\n"
231,031 @"(878) mhpmevent13 (/32)\n"
231,031 @"(879) mhpmevent14 (/32)\n"
231,031 @"(880) mhpmevent15 (/32)\n"
231,031 @"(881) mhpmevent16 (/32)\n"
231,032 @"(882) mhpmevent17 (/32)\n"
231,032 @"(883) mhpmevent18 (/32)\n"
231,032 @"(884) mhpmevent19 (/32)\n"
231,032 @"(885) mhpmevent20 (/32)\n"
231,032 @"(886) mhpmevent21 (/32)\n"
231,032 @"(887) mhpmevent22 (/32)\n"
231,032 @"(888) mhpmevent23 (/32)\n"
231,032 @"(889) mhpmevent24 (/32)\n"
231,032 @"(890) mhpmevent25 (/32)\n"
231,032 @"(891) mhpmevent26 (/32)\n"
231,032 @"(892) mhpmevent27 (/32)\n"
231,032 @"(893) mhpmevent28 (/32)\n"
231,032 @"(894) mhpmevent29 (/32)\n"
231,033 @"(895) mhpmevent30 (/32)\n"
231,033 @"(896) mhpmevent31 (/32)\n"
231,033 @"(897) mscratch (/32)\n"
231,033 @"(898) mepc (/32)\n"
231,033 @"(899) mcause (/32)\n"
231,033 @"(900) mtval (/32)\n"
231,033 @"(901) mip (/32)\n"
231,033 @"(993) pmpcfg0 (/32)\n"
231,033 @"(994) pmpcfg1 (/32)\n"
231,033 @"(995) pmpcfg2 (/32)\n"
231,033 @"(996) pmpcfg3 (/32)\n"
231,033 @"(1009) pmpaddr0 (/32)\n"
231,033 @"(1010) pmpaddr1 (/32)\n"
231,033 @"(1011) pmpaddr2 (/32)\n"
231,033 @"(1012) pmpaddr3 (/32)\n"
231,033 @"(1013) pmpaddr4 (/32)\n"
231,033 @"(1014) pmpaddr5 (/32)\n"
231,033 @"(1015) pmpaddr6 (/32)\n"
231,033 @"(1016) pmpaddr7 (/32)\n"
231,033 @"(1017) pmpaddr8 (/32)\n"
231,033 @"(1018) pmpaddr9 (/32)\n"
231,033 @"(1019) pmpaddr10 (/32)\n"
231,033 @"(1020) pmpaddr11 (/32)\n"
231,034 @"(1021) pmpaddr12 (/32)\n"
231,034 @"(1022) pmpaddr13 (/32)\n"
231,034 @"(1023) pmpaddr14 (/32)\n"
231,034 @"(1024) pmpaddr15 (/32)\n"
231,034 @"(2017) tselect (/32)\n"
231,034 @"(2018) tdata1 (/32)\n"
231,034 @"(2019) tdata2 (/32)\n"
231,034 @"(2020) tdata3 (/32)\n"
231,034 @"(2033) dcsr (/32)\n"
231,034 @"(2034) dpc (/32)\n"
231,034 @"(2035) dscratch (/32)\n"
231,034 @"(2881) mcycle (/32)\n"
231,034 @"(2883) minstret (/32)\n"
231,034 @"(2884) mhpmcounter3 (/32)\n"
231,034 @"(2885) mhpmcounter4 (/32)\n"
231,034 @"(2886) mhpmcounter5 (/32)\n"
231,034 @"(2887) mhpmcounter6 (/32)\n"
231,034 @"(2888) mhpmcounter7 (/32)\n"
231,034 @"(2889) mhpmcounter8 (/32)\n"
231,034 @"(2890) mhpmcounter9 (/32)\n"
231,034 @"(2891) mhpmcounter10 (/32)\n"
231,034 @"(2892) mhpmcounter11 (/32)\n"
231,034 @"(2893) mhpmcounter12 (/32)\n"
231,034 @"(2894) mhpmcounter13 (/32)\n"
231,035 @"(2895) mhpmcounter14 (/32)\n"
231,035 @"(2896) mhpmcounter15 (/32)\n"
231,035 @"(2897) mhpmcounter16 (/32)\n"
231,035 @"(2898) mhpmcounter17 (/32)\n"
231,035 @"(2899) mhpmcounter18 (/32)\n"
231,035 @"(2900) mhpmcounter19 (/32)\n"
231,035 @"(2901) mhpmcounter20 (/32)\n"
231,035 @"(2902) mhpmcounter21 (/32)\n"
231,035 @"(2903) mhpmcounter22 (/32)\n"
231,036 @"(2904) mhpmcounter23 (/32)\n"
231,036 @"(2905) mhpmcounter24 (/32)\n"
231,036 @"(2906) mhpmcounter25 (/32)\n"
231,036 @"(2907) mhpmcounter26 (/32)\n"
231,036 @"(2908) mhpmcounter27 (/32)\n"
231,036 @"(2909) mhpmcounter28 (/32)\n"
231,036 @"(2910) mhpmcounter29 (/32)\n"
231,036 @"(2911) mhpmcounter30 (/32)\n"
231,036 @"(2912) mhpmcounter31 (/32)\n"
231,036 @"(3009) mcycleh (/32)\n"
231,036 @"(3011) minstreth (/32)\n"
231,036 @"(3012) mhpmcounter3h (/32)\n"
231,036 @"(3013) mhpmcounter4h (/32)\n"
231,036 @"(3014) mhpmcounter5h (/32)\n"
231,036 @"(3015) mhpmcounter6h (/32)\n"
231,036 @"(3016) mhpmcounter7h (/32)\n"
231,036 @"(3017) mhpmcounter8h (/32)\n"
231,036 @"(3018) mhpmcounter9h (/32)\n"
231,036 @"(3019) mhpmcounter10h (/32)\n"
231,036 @"(3020) mhpmcounter11h (/32)\n"
231,036 @"(3021) mhpmcounter12h (/32)\n"
231,036 @"(3022) mhpmcounter13h (/32)\n"
231,036 @"(3023) mhpmcounter14h (/32)\n"
231,036 @"(3024) mhpmcounter15h (/32)\n"
231,036 @"(3025) mhpmcounter16h (/32)\n"
231,036 @"(3026) mhpmcounter17h (/32)\n"
231,036 @"(3027) mhpmcounter18h (/32)\n"
231,036 @"(3028) mhpmcounter19h (/32)\n"
231,036 @"(3029) mhpmcounter20h (/32)\n"
231,036 @"(3030) mhpmcounter21h (/32)\n"
231,036 @"(3031) mhpmcounter22h (/32)\n"
231,036 @"(3032) mhpmcounter23h (/32)\n"
231,036 @"(3033) mhpmcounter24h (/32)\n"
231,037 @"(3034) mhpmcounter25h (/32)\n"
231,037 @"(3035) mhpmcounter26h (/32)\n"
231,037 @"(3036) mhpmcounter27h (/32)\n"
231,037 @"(3037) mhpmcounter28h (/32)\n"
231,038 @"(3038) mhpmcounter29h (/32)\n"
231,038 @"(3039) mhpmcounter30h (/32)\n"
231,038 @"(3040) mhpmcounter31h (/32)\n"
231,038 @"(3137) cycle (/32)\n"
231,038 @"(3138) time (/32)\n"
231,038 @"(3139) instret (/32)\n"
231,038 @"(3140) hpmcounter3 (/32)\n"
231,038 @"(3141) hpmcounter4 (/32)\n"
231,038 @"(3142) hpmcounter5 (/32)\n"
231,038 @"(3143) hpmcounter6 (/32)\n"
231,038 @"(3144) hpmcounter7 (/32)\n"
231,038 @"(3145) hpmcounter8 (/32)\n"
231,038 @"(3146) hpmcounter9 (/32)\n"
231,038 @"(3147) hpmcounter10 (/32)\n"
231,038 @"(3148) hpmcounter11 (/32)\n"
231,038 @"(3149) hpmcounter12 (/32)\n"
231,038 @"(3150) hpmcounter13 (/32)\n"
231,038 @"(3151) hpmcounter14 (/32)\n"
231,038 @"(3152) hpmcounter15 (/32)\n"
231,038 @"(3153) hpmcounter16 (/32)\n"
231,038 @"(3154) hpmcounter17 (/32)\n"
231,038 @"(3155) hpmcounter18 (/32)\n"
231,038 @"(3156) hpmcounter19 (/32)\n"
231,038 @"(3157) hpmcounter20 (/32)\n"
231,038 @"(3158) hpmcounter21 (/32)\n"
231,038 @"(3159) hpmcounter22 (/32)\n"
231,038 @"(3160) hpmcounter23 (/32)\n"
231,038 @"(3161) hpmcounter24 (/32)\n"
231,038 @"(3162) hpmcounter25 (/32)\n"
231,038 @"(3163) hpmcounter26 (/32)\n"
231,038 @"(3164) hpmcounter27 (/32)\n"
231,038 @"(3165) hpmcounter28 (/32)\n"
231,038 @"(3166) hpmcounter29 (/32)\n"
231,038 @"(3167) hpmcounter30 (/32)\n"
231,038 @"(3168) hpmcounter31 (/32)\n"
231,038 @"(3265) cycleh (/32)\n"
231,038 @"(3266) timeh (/32)\n"
231,038 @"(3267) instreth (/32)\n"
231,039 @"(3268) hpmcounter3h (/32)\n"
231,039 @"(3269) hpmcounter4h (/32)\n"
231,039 @"(3270) hpmcounter5h (/32)\n"
231,039 @"(3271) hpmcounter6h (/32)\n"
231,039 @"(3272) hpmcounter7h (/32)\n"
231,039 @"(3273) hpmcounter8h (/32)\n"
231,039 @"(3274) hpmcounter9h (/32)\n"
231,039 @"(3275) hpmcounter10h (/32)\n"
231,039 @"(3276) hpmcounter11h (/32)\n"
231,040 @"(3277) hpmcounter12h (/32)\n"
231,040 @"(3278) hpmcounter13h (/32)\n"
231,040 @"(3279) hpmcounter14h (/32)\n"
231,040 @"(3280) hpmcounter15h (/32)\n"
231,040 @"(3281) hpmcounter16h (/32)\n"
231,040 @"(3282) hpmcounter17h (/32)\n"
231,040 @"(3283) hpmcounter18h (/32)\n"
231,040 @"(3284) hpmcounter19h (/32)\n"
231,040 @"(3285) hpmcounter20h (/32)\n"
231,040 @"(3286) hpmcounter21h (/32)\n"
231,040 @"(3287) hpmcounter22h (/32)\n"
231,040 @"(3288) hpmcounter23h (/32)\n"
231,040 @"(3289) hpmcounter24h (/32)\n"
231,040 @"(3290) hpmcounter25h (/32)\n"
231,040 @"(3291) hpmcounter26h (/32)\n"
231,040 @"(3292) hpmcounter27h (/32)\n"
231,040 @"(3293) hpmcounter28h (/32)\n"
231,040 @"(3294) hpmcounter29h (/32)\n"
231,040 @"(3295) hpmcounter30h (/32)\n"
231,040 @"(3296) hpmcounter31h (/32)\n"
231,040 @"(3922) mvendorid (/32)\n"
231,040 @"(3923) marchid (/32)\n"
231,040 @"(3924) mimpid (/32)\n"
231,040 @"(3925) mhartid (/32)\n"
231,040 @"(4161) priv (/8)\n"
231,040 36^done
231,040 (gdb)
```


Do you think that those tens of counters, which are not even implemented, are a big security threat?

I agree that some core registers should be treated carefully, but the bulk of them can be moved to MMIO, as `mtime` was.

And definitely do not encourage users to add their private CSRs if not really needed, MMIO should be fine for most of the use cases.

> I agree that the simplest M-mode-only embedded systems should use MMIO and RVA instead of CSRs,

This can only be achieved by creating a separate riscv-embed profile, with its own specifications.

Large 'application' type devices should implement basic ISA plus the 'priviledged' profile; small (M or M+U, definitely no MMU) devices should implement basic ISA plus the 'embedded' profile.

The current approach, to enforce the privileged specs onto all devices and allow for some features to be optional is not beneficial, and generates only frustration for the embedded developers.

You simply cannot expect to have millions of happy coders if you ask them to write assembly code all over the place, especially when the main competing architecture (Cortex-M) successfully proved that assembly is generally not required (system registers are MMIO, interrupt handlers are plain C functions, the stack pointer is set automatically before reset, etc).


Regards,

Liviu








Rogier Brussee

unread,
Jan 18, 2018, 6:46:01 AM1/18/18
to RISC-V ISA Dev, jcb6...@gmail.com, wate...@eecs.berkeley.edu, allen...@esperantotech.com, kr...@berkeley.edu


Op donderdag 18 januari 2018 09:58:09 UTC+1 schreef Liviu Ionescu:


> On 18 Jan 2018, at 04:18, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> ... larger systems (1) should use CSRs as they are currently defined and (2) should *not* make CSRs accessible in MMIO space for security reasons, with a possible exception to (2) for the monitor.  S-mode MMIO access to CSRs is asking for exploits as I see it.

I understand the need for security, but are you sure all CSRs can be a security threat?

Even on my small HiFive1, OpenOCD and GDB insist on pushing onto me the following list each time I need to transfer the general registers:

```
231,028 @"===== RISC-V Registers\n"
231,028 @"(0) zero (/32): 0x00000000\n"
231,028 @"(1) ra (/32): 0x80000094\n"
231,028 @"(2) sp (/32): 0x80003F80\n"
[[snip]
 
 
231,031 @"(868) mhpmevent3 (/32)\n"
231,031 @"(869) mhpmevent4 (/32)\n"
231,031 @"(870) mhpmevent5 (/32)\n"
231,031 @"(871) mhpmevent6 (/32)\n"
[snip]
 
231,040 36^done
231,040 (gdb)
```


Do you think that those tens of counters, which are not even implemented, are a big security threat?

I agree that some core registers should be treated carefully, but the bulk of them can be moved to MMIO, as `mtime` was.

And definitely do not encourage users to add their private CSRs if not really needed, MMIO should be fine for most of the use cases.

Is there anything in the spec that disallows (some) CSR access to _be_  MMIO (directly in hardware or more likely indirectly through trapping to M mode) 
while also allowing access with ordinary amos and/or load store instructions, so that by redefining CSR access functions  
compilers and programmers can effectively bypass CSR instructions?   

Liviu Ionescu

unread,
Jan 18, 2018, 11:08:44 AM1/18/18
to Rogier Brussee, RISC-V ISA Dev, jcb6...@gmail.com, wate...@eecs.berkeley.edu, allen...@esperantotech.com, kr...@berkeley.edu


> On 18 Jan 2018, at 13:46, Rogier Brussee <rogier....@gmail.com> wrote:
>
> directly in hardware or more likely indirectly through trapping to M mode

please note that trapping to M-mode, although a popular technique in research projects, is generally not acceptable in real life commercial embedded devices.

the device either has the features and the application uses them, or not, and the application does not use them at all.

it's like trying to use a Cortex-M0 hardware and wanting to make it look like a Cortex-M7 by implementing the extra instructions with traps. I never heard of such an idea.


regards,

Liviu







Michael Chapman

unread,
Jan 18, 2018, 11:17:25 AM1/18/18
to Liviu Ionescu, Rogier Brussee, RISC-V ISA Dev, jcb6...@gmail.com, wate...@eecs.berkeley.edu, allen...@esperantotech.com, kr...@berkeley.edu

Exactly the same applies to non-aligned accesses for embedded devices.
Either the HW does them directly, or the SW will not use them at all.

Liviu Ionescu

unread,
Jan 18, 2018, 11:43:57 AM1/18/18
to Michael Chapman, Rogier Brussee, RISC-V ISA Dev, jcb6...@gmail.com, wate...@eecs.berkeley.edu, allen...@esperantotech.com, kr...@berkeley.edu


> On 18 Jan 2018, at 18:18, Michael Chapman <michael.c...@gmail.com> wrote:
>
>
> Exactly the same applies to non-aligned accesses for embedded devices.
> Either the HW does them directly, or the SW will not use them at all.

I confirm this.

The Cortex-M3 and Cortex-M4 processors support unaligned data transfers in normal memory accesses, so generally this is not an issue.

Unaligned accesses are not supported in the Cortex-M0 processors.

On old crippled devices, no sane embedded application will emulate unaligned accesses with traps.

Actually no sane embedded application will emulate anything with traps.

Traps are generally catastrophic. If I remember right, on Cortex-M0 traps are not even resumable.


Regards,

Liviu

Rogier Brussee

unread,
Jan 18, 2018, 12:30:48 PM1/18/18
to RISC-V ISA Dev, rogier....@gmail.com, jcb6...@gmail.com, wate...@eecs.berkeley.edu, allen...@esperantotech.com, kr...@berkeley.edu


Op donderdag 18 januari 2018 17:08:44 UTC+1 schreef Liviu Ionescu:


> On 18 Jan 2018, at 13:46, Rogier Brussee <rogier....@gmail.com> wrote:
>
> directly in hardware or more likely indirectly through trapping to M mode

please note that trapping to M-mode, although a popular technique in research projects, is generally not acceptable in real life commercial embedded devices.



The point of the question was whether the standard allows an implementation that in theorie does CSRs, but in practice never uses them 
( other than as a method of last resort (and arguably, marketing) ) because using AMO's and MMIO directly is easier and cheaper.

Standardising how to do "CSR"s by MMIO so that toolchains can seamlessly use the appropriate MMIO acces seems
like a useful idea that could benefit more than just embedded processors. And I guess that includes discussing whether 
pretending the CSR exists  is needed. 

Just for vanity: I proposed doing CSR's as AMO's and MMIO for the Xcondensed RV-like stand alone isa 
that, like RVC, uses 16 bit wide instructions and the first three quadrants of the encoding, but compresses allmost all of RVIMA with the CSR instructions
as the major missing piece.

Rogier Brussee

unread,
Jan 18, 2018, 12:31:22 PM1/18/18
to RISC-V ISA Dev, rogier....@gmail.com, jcb6...@gmail.com, wate...@eecs.berkeley.edu, allen...@esperantotech.com, kr...@berkeley.edu


Op donderdag 18 januari 2018 17:08:44 UTC+1 schreef Liviu Ionescu:
> On 18 Jan 2018, at 13:46, Rogier Brussee <rogier....@gmail.com> wrote:
>
> directly in hardware or more likely indirectly through trapping to M mode

please note that trapping to M-mode, although a popular technique in research projects, is generally not acceptable in real life commercial embedded devices.


The point of the question was whether the standard allows an implementation that in theorie does CSRs, but in practice never uses them 
( other than as a method of last resort (and arguably, marketing) ) because using AMO's and MMIO directly is easier and cheaper.

Standardising how to do "CSR"s by MMIO so that toolchains can seamlessly use the appropriate MMIO acces seems
like a useful idea that could benefit more than just embedded processors. And I guess that includes discussing whether 
pretending the CSR exists  is needed. 

Just for vanity: I proposed doing CSR's as AMO's and MMIO for the Xcondensed RV-like stand alone isa 
that, like RVC, uses 16 bit wide instructions and the first three quadrants of the encoding, but compresses allmost all of RVIMA with the CSR instructions
as the major missing piece.

 
the device either has the features and the application uses them, or not, and the application does not use them at all.

kr...@berkeley.edu

unread,
Jan 18, 2018, 1:21:38 PM1/18/18
to Liviu Ionescu, jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev

>>>>> On Thu, 18 Jan 2018 10:58:05 +0200, Liviu Ionescu <i...@livius.net> said:
| I understand the need for security, but are you sure all CSRs can be a security threat?

Are you certain any one of these is _not_ a security threat?

Krste

Liviu Ionescu

unread,
Jan 18, 2018, 2:13:40 PM1/18/18
to kr...@berkeley.edu, jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, RISC-V ISA Dev
I'm not a security expert, so I'm not certain of anything. As such, I'm ready to learn what security thread may have reading, for example, `marchid`, from a MMIO.

Or reading some of those countless counters from a MMIO region?

But even without being a security expert, I think that for M or M+U devices only, we can find a reasonable solution to access all the system registers as MMIO.


Regards,

Liviu

ron minnich

unread,
Jan 18, 2018, 2:32:29 PM1/18/18
to Liviu Ionescu, kr...@berkeley.edu, jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, RISC-V ISA Dev
The two most dangerous phrases in computing:

On Thu, Jan 18, 2018 at 11:13 AM Liviu Ionescu <i...@livius.net> wrote:
But even without being a security expert,

followed by:
 
we can find a reasonable solution 

if only :-) 

ron

Cesar Eduardo Barros

unread,
Jan 18, 2018, 5:05:37 PM1/18/18
to Liviu Ionescu, kr...@berkeley.edu, jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, RISC-V ISA Dev
Em 18-01-2018 17:13, Liviu Ionescu escreveu:
>
>
>> On 18 Jan 2018, at 20:21, kr...@berkeley.edu wrote:
>>
>>
>>>>>>> On Thu, 18 Jan 2018 10:58:05 +0200, Liviu Ionescu <i...@livius.net> said:
>> | I understand the need for security, but are you sure all CSRs can be a security threat?
>>
>> Are you certain any one of these is _not_ a security threat?
>
> I'm not a security expert, so I'm not certain of anything. As such, I'm ready to learn what security thread may have reading, for example, `marchid`, from a MMIO.
>
> Or reading some of those countless counters from a MMIO region?

It's not only reading, but also writing, unless you want the MMIO region
to be read-only. Also, I can easily imagine some future cryptographic
extension using a CSR to hold a key, or even someone stashing a key on a
scratch or debug CSR (so it doesn't get outside the core). You can only
treat a CSR as "safe" on a case-by-case basis.

Liviu Ionescu

unread,
Jan 18, 2018, 5:49:55 PM1/18/18
to Cesar Eduardo Barros, kr...@berkeley.edu, jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, RISC-V ISA Dev


> On 19 Jan 2018, at 00:05, Cesar Eduardo Barros <ces...@cesarb.eti.br> wrote:
>
> Also, I can easily imagine some future cryptographic extension using a CSR to hold a key, or even someone stashing a key on a scratch or debug CSR (so it doesn't get outside the core). You can only treat a CSR as "safe" on a case-by-case basis.

on a M-mode device? really?

Liviu


Jacob Bachmeyer

unread,
Jan 18, 2018, 7:21:41 PM1/18/18
to Liviu Ionescu, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev
Liviu Ionescu wrote:
>> On 18 Jan 2018, at 04:18, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>>
>> ... larger systems (1) should use CSRs as they are currently defined and (2) should *not* make CSRs accessible in MMIO space for security reasons, with a possible exception to (2) for the monitor. S-mode MMIO access to CSRs is asking for exploits as I see it.
>>
>
> I understand the need for security, but are you sure all CSRs can be a security threat?
>
> Even on my small HiFive1, OpenOCD and GDB insist on pushing onto me the following list each time I need to transfer the general registers:
>
> ```
> 231,028 @"===== RISC-V Registers\n"
> [...snip main register file...]
> [...snip list of *every* standard CSR...]
> 231,040 @"(4161) priv (/8)\n"
> 231,040 36^done
> 231,040 (gdb)
> ```
>

As I understand it, you are using an old version of GDB and this
particular problem has since been fixed in RISC-V GDB. (It was a more
serious problem than you seem to have noticed, since GDB was also
reading *all* of those registers *every* time the debug target stopped.)

> Do you think that those tens of counters, which are not even implemented, are a big security threat?
>

Security threat? Probably not. Legitimately CSRs? Yes: counters need
to be very efficiently accessed in all modes where they are accessible,
and using CSR space for these avoids complex ABI questions like "where
are the counters mapped?" and simplifies microarchitecture design by
_not_ sending counter accesses through the load/store unit and memory
hierarchy.

> I agree that some core registers should be treated carefully, but the bulk of them can be moved to MMIO, as `mtime` was.
>

I still think that "time" should be in CSR space. The RDTIME opcode
effectively defines its CSR address.

> And definitely do not encourage users to add their private CSRs if not really needed, MMIO should be fine for most of the use cases.
>

Extensions obviously can add CSRs, and there is some expectation of
"promoting" non-standard extensions to standard extensions if they prove
useful, thus requiring the extension's non-standard CSRs be renumbered
into the standard CSR space.

>> I agree that the simplest M-mode-only embedded systems should use MMIO and RVA instead of CSRs,
>>
>
> This can only be achieved by creating a separate riscv-embed profile, with its own specifications.
>

Which I would not oppose. I believe that RISC-V would benefit from a
small-microcontroller profile, perhaps RV32E only.

> Large 'application' type devices should implement basic ISA plus the 'priviledged' profile; small (M or M+U, definitely no MMU) devices should implement basic ISA plus the 'embedded' profile.
>

I would suggest that the "embedded" profile forbid even U-mode and omit
the concept of modes entirely. Privilege levels are less useful on tiny
microcontrollers.

> The current approach, to enforce the privileged specs onto all devices and allow for some features to be optional is not beneficial, and generates only frustration for the embedded developers.
>

I agree, with the qualification that many (higher-end) embedded devices
will *want* "application" type processors, so this does not completely
solve your problem, only create a new class of device that avoids your
problem and is good for some applications. (I still support the idea.)

> You simply cannot expect to have millions of happy coders if you ask them to write assembly code all over the place, especially when the main competing architecture (Cortex-M) successfully proved that assembly is generally not required (system registers are MMIO, interrupt handlers are plain C functions, the stack pointer is set automatically before reset, etc).
>

The plan, as I understand it, is that that assembler should be limited
to a few places, and possibly to system libraries that high-level
programmers can simply link and ignore. Providing CSR access as C
variables is merely a matter of compiler development and I expect it to
be added to GCC sooner rather than later.


-- Jacob

Jacob Bachmeyer

unread,
Jan 18, 2018, 7:30:23 PM1/18/18
to Liviu Ionescu, kr...@berkeley.edu, Andrew Waterman, Allen J. Baum, RISC-V ISA Dev
Liviu Ionescu wrote:
>> On 18 Jan 2018, at 20:21, kr...@berkeley.edu wrote:
>>
>>> On Thu, 18 Jan 2018 10:58:05 +0200, Liviu Ionescu <i...@livius.net> said:
>>>
>> | I understand the need for security, but are you sure all CSRs can be a security threat?
>>
>> Are you certain any one of these is _not_ a security threat?
>>
>
> I'm not a security expert, so I'm not certain of anything. As such, I'm ready to learn what security thread may have reading, for example, `marchid`, from a MMIO.
>
> Or reading some of those countless counters from a MMIO region?

The counters actually need to be CSRs for accuracy, rather than
security. A microarchitecture will need to handle them specially,
particularly instret and cycle, which update on every instruction.
Sending accesses to those through a non-trivial memory subsystem would
make them useless.

For microcontrollers, with a trivial memory susbsystem with on-die SRAM
and MMIO and no external bus, this is not a problem. For "full-size"
systems, the counters *must* be CSRs; MMIO simply will not work. (The
only way MMIO counters _could_ work would be for the ISA to reserve
*virtual* addresses for the counters. I really do not like that idea.)


-- Jacob

Liviu Ionescu

unread,
Jan 19, 2018, 5:21:00 AM1/19/18
to jcb6...@gmail.com, Andrew Waterman, Allen J. Baum, Krste Asanovic, RISC-V ISA Dev


> On 19 Jan 2018, at 02:21, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> As I understand it, you are using an old version of GDB and this particular problem has since been fixed in RISC-V GDB.

nope.

I'm using the latest versions of the toolchain and OpenOCD from the latest commits, I personally built them a few days ago.

the problem was not fixed, was just 'sweetened', instead of always passing 4096 registers, now only a few hundreds are passed, the ones in the list I gave early.

still lot of them, and most of them completely useless on the small HiFive1 board I use for tests.


regards,

Liviu


Reply all
Reply to author
Forward
0 new messages