[v2] sim: riscv: Fix Zicsr and fence instructions

Message ID AS8P193MB128547BE7FF7547A5CFB35CAE41B2@AS8P193MB1285.EURP193.PROD.OUTLOOK.COM
State New
Headers
Series [v2] sim: riscv: Fix Zicsr and fence instructions |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Bernd Edlinger April 29, 2024, 1:57 p.m. UTC
  The Zicsr instructions were totally broken, and
some instructions like fence.tso were missing.

Since the test coverage is not very good, add some
basic tests for fence and csrrw instructions.
---
 sim/riscv/sim-main.c        | 82 ++++++++++++++++++++++++++++++++-----
 sim/testsuite/riscv/fence.s | 17 ++++++++
 sim/testsuite/riscv/zicsr.s | 24 +++++++++++
 3 files changed, 112 insertions(+), 11 deletions(-)
 create mode 100644 sim/testsuite/riscv/fence.s
 create mode 100644 sim/testsuite/riscv/zicsr.s

v2: address review comments, make store_csr execution
only executed when rs1 != 0.
  

Comments

Andrew Burgess May 1, 2024, 3:39 p.m. UTC | #1
Bernd Edlinger <bernd.edlinger@hotmail.de> writes:

> The Zicsr instructions were totally broken, and
> some instructions like fence.tso were missing.
>
> Since the test coverage is not very good, add some
> basic tests for fence and csrrw instructions.
> ---
>  sim/riscv/sim-main.c        | 82 ++++++++++++++++++++++++++++++++-----
>  sim/testsuite/riscv/fence.s | 17 ++++++++
>  sim/testsuite/riscv/zicsr.s | 24 +++++++++++
>  3 files changed, 112 insertions(+), 11 deletions(-)
>  create mode 100644 sim/testsuite/riscv/fence.s
>  create mode 100644 sim/testsuite/riscv/zicsr.s
>
> v2: address review comments, make store_csr execution
> only executed when rs1 != 0.
>  
> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
> index 1815d7f2a6c..d556e6f5bdd 100644
> --- a/sim/riscv/sim-main.c
> +++ b/sim/riscv/sim-main.c
> @@ -535,45 +535,100 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
>        break;
>  
>      case MATCH_CSRRC:
> -      TRACE_INSN (cpu, "csrrc");
> +      TRACE_INSN (cpu, "csrrc %s, %#x, %s;",
> +		  rd_name, csr, rs1_name);
> +      switch (csr)
> +	{
> +#define DECLARE_CSR(name, num, ...) \
> +	case num: \
> +	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
> +	  if (rs1) \

Could you be explicit about the comparisons added in this commit please,
e.g.:

  if (rs1 != 0)

this is the GDB style, and given sim/ is maintained on the same list I
figure the rules should carry over.


> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> +		       riscv_cpu->csr.name & ~riscv_cpu->regs[rs1]); \
> +	  store_rd (cpu, rd, tmp); \
> +	  break;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +	}
> +      break;
> +    case MATCH_CSRRS:
> +      TRACE_INSN (cpu, "csrrs %s, %#x, %s;",
> +		  rd_name, csr, rs1_name);
> +      switch (csr)
> +	{
> +#define DECLARE_CSR(name, num, ...) \
> +	case num: \
> +	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
> +	  if (rs1) \
> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> +		       riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \
> +	  store_rd (cpu, rd, tmp); \
> +	  break;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +	}
> +      break;
> +    case MATCH_CSRRW:
> +      TRACE_INSN (cpu, "csrrw %s, %#x, %s;",
> +		  rd_name, csr, rs1_name);
> +      switch (csr)
> +	{
> +#define DECLARE_CSR(name, num, ...) \
> +	case num: \
> +	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \

For CSRRW the check is on the CSR read.  If 'rd == 0' then no CSR read
is performed.

> +	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> +		     riscv_cpu->regs[rs1]); \
> +	  store_rd (cpu, rd, tmp); \
> +	  break;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +	}
> +      break;
> +
> +    case MATCH_CSRRCI:
> +      TRACE_INSN (cpu, "csrrci %s, %#x, %#x;",
> +		  rd_name, csr, rs1);
>        switch (csr)
>  	{
>  #define DECLARE_CSR(name, num, ...) \
>  	case num: \
>  	  store_rd (cpu, rd, \
>  		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
> -	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> -		     riscv_cpu->csr.name & !riscv_cpu->regs[rs1]); \
> +	  if (rs1) \
> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> +		       riscv_cpu->csr.name & ~rs1); \
>  	  break;
>  #include "opcode/riscv-opc.h"
>  #undef DECLARE_CSR
>  	}
>        break;
> -    case MATCH_CSRRS:
> -      TRACE_INSN (cpu, "csrrs");
> +    case MATCH_CSRRSI:
> +      TRACE_INSN (cpu, "csrrsi %s, %#x, %#x;",
> +		  rd_name, csr, rs1);
>        switch (csr)
>  	{
>  #define DECLARE_CSR(name, num, ...) \
>  	case num: \
>  	  store_rd (cpu, rd, \
>  		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
> -	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> -		     riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \
> +	  if (rs1) \
> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> +		       riscv_cpu->csr.name | rs1); \
>  	  break;
>  #include "opcode/riscv-opc.h"
>  #undef DECLARE_CSR
>  	}
>        break;
> -    case MATCH_CSRRW:
> -      TRACE_INSN (cpu, "csrrw");
> +    case MATCH_CSRRWI:
> +      TRACE_INSN (cpu, "csrrwi %s, %#x, %#x;",
> +		  rd_name, csr, rs1);
>        switch (csr)
>  	{
>  #define DECLARE_CSR(name, num, ...) \
>  	case num: \
>  	  store_rd (cpu, rd, \
>  		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \

Here too the fetch_csr call should not be performed if 'rd == 0'.

Thanks,
Andrew


> -	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
> -		     riscv_cpu->regs[rs1]); \
> +	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, rs1); \
>  	  break;
>  #include "opcode/riscv-opc.h"
>  #undef DECLARE_CSR
> @@ -622,6 +677,9 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
>      case MATCH_FENCE_I:
>        TRACE_INSN (cpu, "fence.i;");
>        break;
> +    case MATCH_FENCE_TSO:
> +      TRACE_INSN (cpu, "fence.tso;");
> +      break;
>      case MATCH_EBREAK:
>        TRACE_INSN (cpu, "ebreak;");
>        sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped, SIM_SIGTRAP);
> @@ -1349,6 +1407,8 @@ execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
>  			   SIM_SIGILL);
>  	}
>      case INSN_CLASS_I:
> +    case INSN_CLASS_ZICSR:
> +    case INSN_CLASS_ZIFENCEI:
>        return execute_i (cpu, iw, op);
>      case INSN_CLASS_M:
>      case INSN_CLASS_ZMMUL:
> diff --git a/sim/testsuite/riscv/fence.s b/sim/testsuite/riscv/fence.s
> new file mode 100644
> index 00000000000..25200891161
> --- /dev/null
> +++ b/sim/testsuite/riscv/fence.s
> @@ -0,0 +1,17 @@
> +# Check that various fence instructions run without any faults.
> +# mach: riscv32 riscv64
> +
> +.include "testutils.inc"
> +
> +	start
> +
> +	fence
> +	fence	rw,rw
> +	fence	rw,w
> +	fence	r,r
> +	fence	w,w
> +	fence	r,rw
> +	fence.i
> +	fence.tso
> +
> +	pass
> diff --git a/sim/testsuite/riscv/zicsr.s b/sim/testsuite/riscv/zicsr.s
> new file mode 100644
> index 00000000000..7f1bd740230
> --- /dev/null
> +++ b/sim/testsuite/riscv/zicsr.s
> @@ -0,0 +1,24 @@
> +# Check that the Zicsr instructions run without any faults.
> +# mach: riscv32 riscv64
> +
> +.include "testutils.inc"
> +
> +	start
> +
> +	csrrs	a0,frm,x0
> +	csrrw	a1,frm,a0
> +	bne	a1,a0,bad
> +	csrrc	a2,frm,a1
> +	bne	a2,x0,bad
> +	csrrsi	a0,frm,1
> +	bne	a0,x0,bad
> +	csrrci	a0,frm,1
> +	li	a1,1
> +	bne	a0,a1,bad
> +	csrrwi	a0,frm,1
> +	bne	a0,x0,bad
> +
> +	pass
> +
> +bad:
> +	fail
> -- 
> 2.39.2
  
Bernd Edlinger May 3, 2024, 11:51 a.m. UTC | #2
On 5/1/24 17:39, Andrew Burgess wrote:
> Bernd Edlinger <bernd.edlinger@hotmail.de> writes:
> 
>> The Zicsr instructions were totally broken, and
>> some instructions like fence.tso were missing.
>>
>> Since the test coverage is not very good, add some
>> basic tests for fence and csrrw instructions.
>> ---
>>  sim/riscv/sim-main.c        | 82 ++++++++++++++++++++++++++++++++-----
>>  sim/testsuite/riscv/fence.s | 17 ++++++++
>>  sim/testsuite/riscv/zicsr.s | 24 +++++++++++
>>  3 files changed, 112 insertions(+), 11 deletions(-)
>>  create mode 100644 sim/testsuite/riscv/fence.s
>>  create mode 100644 sim/testsuite/riscv/zicsr.s
>>
>> v2: address review comments, make store_csr execution
>> only executed when rs1 != 0.
>>  
>> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
>> index 1815d7f2a6c..d556e6f5bdd 100644
>> --- a/sim/riscv/sim-main.c
>> +++ b/sim/riscv/sim-main.c
>> @@ -535,45 +535,100 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
>>        break;
>>  
>>      case MATCH_CSRRC:
>> -      TRACE_INSN (cpu, "csrrc");
>> +      TRACE_INSN (cpu, "csrrc %s, %#x, %s;",
>> +		  rd_name, csr, rs1_name);
>> +      switch (csr)
>> +	{
>> +#define DECLARE_CSR(name, num, ...) \
>> +	case num: \
>> +	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
>> +	  if (rs1) \
> 
> Could you be explicit about the comparisons added in this commit please,
> e.g.:
> 
>   if (rs1 != 0)
> 
> this is the GDB style, and given sim/ is maintained on the same list I
> figure the rules should carry over.
> 
> 

I wanted to follow the style in store_rd where we have this:

static INLINE void
store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
{
  struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);

  if (rd)
    {
      riscv_cpu->regs[rd] = val;
      TRACE_REG (cpu, rd);
    }
}



>> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> +		       riscv_cpu->csr.name & ~riscv_cpu->regs[rs1]); \
>> +	  store_rd (cpu, rd, tmp); \
>> +	  break;
>> +#include "opcode/riscv-opc.h"
>> +#undef DECLARE_CSR
>> +	}
>> +      break;
>> +    case MATCH_CSRRS:
>> +      TRACE_INSN (cpu, "csrrs %s, %#x, %s;",
>> +		  rd_name, csr, rs1_name);
>> +      switch (csr)
>> +	{
>> +#define DECLARE_CSR(name, num, ...) \
>> +	case num: \
>> +	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
>> +	  if (rs1) \
>> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> +		       riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \
>> +	  store_rd (cpu, rd, tmp); \
>> +	  break;
>> +#include "opcode/riscv-opc.h"
>> +#undef DECLARE_CSR
>> +	}
>> +      break;
>> +    case MATCH_CSRRW:
>> +      TRACE_INSN (cpu, "csrrw %s, %#x, %s;",
>> +		  rd_name, csr, rs1_name);
>> +      switch (csr)
>> +	{
>> +#define DECLARE_CSR(name, num, ...) \
>> +	case num: \
>> +	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
> 
> For CSRRW the check is on the CSR read.  If 'rd == 0' then no CSR read
> is performed.
> 

That is a bit overkill, especially here.  If I add an if (rd != 0) here,
I need yet another if around the store_rd below, because the uninitalized
tmp will certainly generate warnings.  And here is also the reason why this
code got my attention in the first place: because previously a "csrrw a1, csr, a1"
could not work correctly, since rd == rs1 overwrote the rs1 register too early,
therefore I added the "tmp = ", and then I realized that the whole
instruction was not executable at all, due to the introduction of
INSN_CLASS_ZICSR.

But the spec says, there are no side effects except possible disallowed accesses:
"The CSRs defined so far do not have any architectural side effects on reads beyond raising illegal
instruction exceptions on disallowed accesses. Custom extensions might add CSRs with side
effects on reads. "

So since the write is unconditional that will create the exception
anyway, and custom extension CSRs are out of scope for the riscv simulator.


>> +	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> +		     riscv_cpu->regs[rs1]); \
>> +	  store_rd (cpu, rd, tmp); \
>> +	  break;
>> +#include "opcode/riscv-opc.h"
>> +#undef DECLARE_CSR
>> +	}
>> +      break;
>> +
>> +    case MATCH_CSRRCI:
>> +      TRACE_INSN (cpu, "csrrci %s, %#x, %#x;",
>> +		  rd_name, csr, rs1);
>>        switch (csr)
>>  	{
>>  #define DECLARE_CSR(name, num, ...) \
>>  	case num: \
>>  	  store_rd (cpu, rd, \
>>  		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
>> -	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> -		     riscv_cpu->csr.name & !riscv_cpu->regs[rs1]); \
>> +	  if (rs1) \
>> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> +		       riscv_cpu->csr.name & ~rs1); \
>>  	  break;
>>  #include "opcode/riscv-opc.h"
>>  #undef DECLARE_CSR
>>  	}
>>        break;
>> -    case MATCH_CSRRS:
>> -      TRACE_INSN (cpu, "csrrs");
>> +    case MATCH_CSRRSI:
>> +      TRACE_INSN (cpu, "csrrsi %s, %#x, %#x;",
>> +		  rd_name, csr, rs1);
>>        switch (csr)
>>  	{
>>  #define DECLARE_CSR(name, num, ...) \
>>  	case num: \
>>  	  store_rd (cpu, rd, \
>>  		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
>> -	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> -		     riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \
>> +	  if (rs1) \
>> +	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> +		       riscv_cpu->csr.name | rs1); \
>>  	  break;
>>  #include "opcode/riscv-opc.h"
>>  #undef DECLARE_CSR
>>  	}
>>        break;
>> -    case MATCH_CSRRW:
>> -      TRACE_INSN (cpu, "csrrw");
>> +    case MATCH_CSRRWI:
>> +      TRACE_INSN (cpu, "csrrwi %s, %#x, %#x;",
>> +		  rd_name, csr, rs1);
>>        switch (csr)
>>  	{
>>  #define DECLARE_CSR(name, num, ...) \
>>  	case num: \
>>  	  store_rd (cpu, rd, \
>>  		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
> 
> Here too the fetch_csr call should not be performed if 'rd == 0'.
> 

Same here.  I do not think that there will be any side effects that may be
worth this complication.


Thanks
Bernd.


> Thanks,
> Andrew
> 
> 
>> -	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
>> -		     riscv_cpu->regs[rs1]); \
>> +	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, rs1); \
>>  	  break;
>>  #include "opcode/riscv-opc.h"
>>  #undef DECLARE_CSR
>> @@ -622,6 +677,9 @@ execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
>>      case MATCH_FENCE_I:
>>        TRACE_INSN (cpu, "fence.i;");
>>        break;
>> +    case MATCH_FENCE_TSO:
>> +      TRACE_INSN (cpu, "fence.tso;");
>> +      break;
>>      case MATCH_EBREAK:
>>        TRACE_INSN (cpu, "ebreak;");
>>        sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped, SIM_SIGTRAP);
>> @@ -1349,6 +1407,8 @@ execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
>>  			   SIM_SIGILL);
>>  	}
>>      case INSN_CLASS_I:
>> +    case INSN_CLASS_ZICSR:
>> +    case INSN_CLASS_ZIFENCEI:
>>        return execute_i (cpu, iw, op);
>>      case INSN_CLASS_M:
>>      case INSN_CLASS_ZMMUL:
>> diff --git a/sim/testsuite/riscv/fence.s b/sim/testsuite/riscv/fence.s
>> new file mode 100644
>> index 00000000000..25200891161
>> --- /dev/null
>> +++ b/sim/testsuite/riscv/fence.s
>> @@ -0,0 +1,17 @@
>> +# Check that various fence instructions run without any faults.
>> +# mach: riscv32 riscv64
>> +
>> +.include "testutils.inc"
>> +
>> +	start
>> +
>> +	fence
>> +	fence	rw,rw
>> +	fence	rw,w
>> +	fence	r,r
>> +	fence	w,w
>> +	fence	r,rw
>> +	fence.i
>> +	fence.tso
>> +
>> +	pass
>> diff --git a/sim/testsuite/riscv/zicsr.s b/sim/testsuite/riscv/zicsr.s
>> new file mode 100644
>> index 00000000000..7f1bd740230
>> --- /dev/null
>> +++ b/sim/testsuite/riscv/zicsr.s
>> @@ -0,0 +1,24 @@
>> +# Check that the Zicsr instructions run without any faults.
>> +# mach: riscv32 riscv64
>> +
>> +.include "testutils.inc"
>> +
>> +	start
>> +
>> +	csrrs	a0,frm,x0
>> +	csrrw	a1,frm,a0
>> +	bne	a1,a0,bad
>> +	csrrc	a2,frm,a1
>> +	bne	a2,x0,bad
>> +	csrrsi	a0,frm,1
>> +	bne	a0,x0,bad
>> +	csrrci	a0,frm,1
>> +	li	a1,1
>> +	bne	a0,a1,bad
>> +	csrrwi	a0,frm,1
>> +	bne	a0,x0,bad
>> +
>> +	pass
>> +
>> +bad:
>> +	fail
>> -- 
>> 2.39.2
>
  

Patch

diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index 1815d7f2a6c..d556e6f5bdd 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -535,45 +535,100 @@  execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
       break;
 
     case MATCH_CSRRC:
-      TRACE_INSN (cpu, "csrrc");
+      TRACE_INSN (cpu, "csrrc %s, %#x, %s;",
+		  rd_name, csr, rs1_name);
+      switch (csr)
+	{
+#define DECLARE_CSR(name, num, ...) \
+	case num: \
+	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
+	  if (rs1) \
+	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
+		       riscv_cpu->csr.name & ~riscv_cpu->regs[rs1]); \
+	  store_rd (cpu, rd, tmp); \
+	  break;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+	}
+      break;
+    case MATCH_CSRRS:
+      TRACE_INSN (cpu, "csrrs %s, %#x, %s;",
+		  rd_name, csr, rs1_name);
+      switch (csr)
+	{
+#define DECLARE_CSR(name, num, ...) \
+	case num: \
+	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
+	  if (rs1) \
+	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
+		       riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \
+	  store_rd (cpu, rd, tmp); \
+	  break;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+	}
+      break;
+    case MATCH_CSRRW:
+      TRACE_INSN (cpu, "csrrw %s, %#x, %s;",
+		  rd_name, csr, rs1_name);
+      switch (csr)
+	{
+#define DECLARE_CSR(name, num, ...) \
+	case num: \
+	  tmp = fetch_csr (cpu, #name, num, &riscv_cpu->csr.name); \
+	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
+		     riscv_cpu->regs[rs1]); \
+	  store_rd (cpu, rd, tmp); \
+	  break;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+	}
+      break;
+
+    case MATCH_CSRRCI:
+      TRACE_INSN (cpu, "csrrci %s, %#x, %#x;",
+		  rd_name, csr, rs1);
       switch (csr)
 	{
 #define DECLARE_CSR(name, num, ...) \
 	case num: \
 	  store_rd (cpu, rd, \
 		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
-	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
-		     riscv_cpu->csr.name & !riscv_cpu->regs[rs1]); \
+	  if (rs1) \
+	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
+		       riscv_cpu->csr.name & ~rs1); \
 	  break;
 #include "opcode/riscv-opc.h"
 #undef DECLARE_CSR
 	}
       break;
-    case MATCH_CSRRS:
-      TRACE_INSN (cpu, "csrrs");
+    case MATCH_CSRRSI:
+      TRACE_INSN (cpu, "csrrsi %s, %#x, %#x;",
+		  rd_name, csr, rs1);
       switch (csr)
 	{
 #define DECLARE_CSR(name, num, ...) \
 	case num: \
 	  store_rd (cpu, rd, \
 		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
-	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
-		     riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \
+	  if (rs1) \
+	    store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
+		       riscv_cpu->csr.name | rs1); \
 	  break;
 #include "opcode/riscv-opc.h"
 #undef DECLARE_CSR
 	}
       break;
-    case MATCH_CSRRW:
-      TRACE_INSN (cpu, "csrrw");
+    case MATCH_CSRRWI:
+      TRACE_INSN (cpu, "csrrwi %s, %#x, %#x;",
+		  rd_name, csr, rs1);
       switch (csr)
 	{
 #define DECLARE_CSR(name, num, ...) \
 	case num: \
 	  store_rd (cpu, rd, \
 		    fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \
-	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
-		     riscv_cpu->regs[rs1]); \
+	  store_csr (cpu, #name, num, &riscv_cpu->csr.name, rs1); \
 	  break;
 #include "opcode/riscv-opc.h"
 #undef DECLARE_CSR
@@ -622,6 +677,9 @@  execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
     case MATCH_FENCE_I:
       TRACE_INSN (cpu, "fence.i;");
       break;
+    case MATCH_FENCE_TSO:
+      TRACE_INSN (cpu, "fence.tso;");
+      break;
     case MATCH_EBREAK:
       TRACE_INSN (cpu, "ebreak;");
       sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped, SIM_SIGTRAP);
@@ -1349,6 +1407,8 @@  execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
 			   SIM_SIGILL);
 	}
     case INSN_CLASS_I:
+    case INSN_CLASS_ZICSR:
+    case INSN_CLASS_ZIFENCEI:
       return execute_i (cpu, iw, op);
     case INSN_CLASS_M:
     case INSN_CLASS_ZMMUL:
diff --git a/sim/testsuite/riscv/fence.s b/sim/testsuite/riscv/fence.s
new file mode 100644
index 00000000000..25200891161
--- /dev/null
+++ b/sim/testsuite/riscv/fence.s
@@ -0,0 +1,17 @@ 
+# Check that various fence instructions run without any faults.
+# mach: riscv32 riscv64
+
+.include "testutils.inc"
+
+	start
+
+	fence
+	fence	rw,rw
+	fence	rw,w
+	fence	r,r
+	fence	w,w
+	fence	r,rw
+	fence.i
+	fence.tso
+
+	pass
diff --git a/sim/testsuite/riscv/zicsr.s b/sim/testsuite/riscv/zicsr.s
new file mode 100644
index 00000000000..7f1bd740230
--- /dev/null
+++ b/sim/testsuite/riscv/zicsr.s
@@ -0,0 +1,24 @@ 
+# Check that the Zicsr instructions run without any faults.
+# mach: riscv32 riscv64
+
+.include "testutils.inc"
+
+	start
+
+	csrrs	a0,frm,x0
+	csrrw	a1,frm,a0
+	bne	a1,a0,bad
+	csrrc	a2,frm,a1
+	bne	a2,x0,bad
+	csrrsi	a0,frm,1
+	bne	a0,x0,bad
+	csrrci	a0,frm,1
+	li	a1,1
+	bne	a0,a1,bad
+	csrrwi	a0,frm,1
+	bne	a0,x0,bad
+
+	pass
+
+bad:
+	fail