commit f9d029a85313d1a909a73dcdb2ef9079cee7998a
parent 78f5bde3560fc41e88ceee3c4e46d9fa8cb3d3e1
Author: Brian Swetland <swetland@frotz.net>
Date: Sun, 20 Oct 2019 14:51:20 -0700
rvsim: start actually simulating instructions
- trivial test program works
- needs a lot more testing
Diffstat:
M | instab.txt | | | 4 | ++-- |
M | riscv.h | | | 71 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
M | rvdis.c | | | 1 | + |
M | rvsim.c | | | 167 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
M | start.S | | | 28 | ++++++++++++++++++++++++---- |
5 files changed, 252 insertions(+), 19 deletions(-)
diff --git a/instab.txt b/instab.txt
@@ -1,8 +1,8 @@
# Copyright 2018, Brian Swetland <swetland@frotz.net>
# Licensed under the Apache License, Version 2.0.
--------------------------0110111 lui %d, %u
--------------------------0010111 auipc %d, %u
+-------------------------0110111 lui %d, %U
+-------------------------0010111 auipc %d, %U
--------------------000001101111 j %J
--------------------000011101111 jal %J
-------------------------1101111 jal %d, %J
diff --git a/riscv.h b/riscv.h
@@ -3,6 +3,9 @@
#include <stdint.h>
// extract instruction fields
+static inline uint32_t get_oc(uint32_t ins) {
+ return ins & 0x7f;
+}
static inline uint32_t get_rd(uint32_t ins) {
return (ins >> 7) & 0x1f;
}
@@ -33,11 +36,69 @@ static inline uint32_t get_ij(uint32_t ins) {
((ins >> 9) & 0x800) |
((ins >> 20) & 0x7fe);
}
+static inline uint32_t get_fn3(uint32_t ins) {
+ return (ins >> 12) & 7;
+}
+static inline uint32_t get_fn7(uint32_t ins) {
+ return ins >> 25;
+}
+
+// opcode constants (6:0)
+#define OC_LOAD 0b0000011
+#define OC_CUSTOM_0 0b0001011
+#define OC_MISC_MEM 0b0001111
+#define OC_OP_IMM 0b0010011
+#define OC_AUIPC 0b0010111
+#define OC_STORE 0b0100011
+#define OC_OP 0b0110011
+#define OC_LUI 0b0110111
+#define OC_BRANCH 0b1100011
+#define OC_JALR 0b1100111
+#define OC_JAL 0b1101111
+#define OC_SYSTEM 0b1110011
+
+// further discrimination of OC_OP_IMM (14:12)
+#define F3_ADDI 0b000
+#define F3_SLLI 0b001 // 0b0000000
+#define F3_SLTI 0b010
+#define F3_SLTIU 0b011
+#define F3_XORI 0b100
+#define F3_SRLI 0b101 // 0b0000000
+#define F3_SRAI 0b101 // 0b0100000
+#define F3_ORI 0b110
+#define F3_ANDI 0b111
+
+// further discrimination of OC_OP_LOAD (14:12)
+#define F3_LB 0b000
+#define F3_LH 0b001
+#define F3_LW 0b010
+#define F3_LBU 0b100
+#define F3_LHU 0b101
+
+// further discrimination of OC_OP_STORE (14:12)
+#define F3_SB 0b000
+#define F3_SH 0b001
+#define F3_SW 0b010
+
+// further discrimination of OC_OP (14:12) (fn7==0)
+#define F3_ADD 0b0000
+#define F3_SLL 0b0001
+#define F3_SLT 0b0010
+#define F3_SLTU 0b0011
+#define F3_XOR 0b0100
+#define F3_SRL 0b0101
+#define F3_OR 0b0110
+#define F3_AND 0b0111
+// OC_OP (14:12) (fn7==0b0100000)
+#define F3_SUB 0b1000
+#define F3_SRA 0b1101
-#define OP_LUI 0b0110111
-#define OP_AUIPC 0b0010111
-#define OP_JAL 0b1101111
-#define OP_JALR 0b1100111
-#define OP_B
+// further discrimination of OP_BRANCH
+#define F3_BEQ 0b000
+#define F3_BNE 0b001
+#define F3_BLT 0b100
+#define F3_BGE 0b101
+#define F3_BLTU 0b110
+#define F3_BGEU 0b111
void rvdis(uint32_t pc, uint32_t ins, char *out);
diff --git a/rvdis.c b/rvdis.c
@@ -64,6 +64,7 @@ void rvdis(uint32_t pc, uint32_t ins, char *out) {
case 'B': out = append_u32(out, pc + (2 * get_ib(ins))); break;
case 's': out = append_i32(out, get_is(ins)); break;
case 'u': out = append_i32(out, get_iu(ins)); break;
+ case 'U': out = append_u32(out, get_iu(ins)); break;
case 'x': out = append_i32(out, get_r2(ins)); break;
}
}
diff --git a/rvsim.c b/rvsim.c
@@ -11,6 +11,13 @@
#include "riscv.h"
+static const char* regname[32] = {
+ "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
+ "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
+ "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
+ "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6",
+};
+
uint8_t memory[32768];
int load_image(const char* fn, uint8_t* ptr, size_t sz) {
@@ -62,23 +69,167 @@ void wr32(uint32_t addr, uint32_t val) {
typedef struct {
uint32_t x[32];
uint32_t pc;
-} rvstate;
+} rvstate_t;
+
+static inline uint32_t rreg(rvstate_t* s, uint32_t n) {
+ return n ? s->x[n] : 0;
+}
+static inline void wreg(rvstate_t* s, uint32_t n, uint32_t v) {
+ s->x[n] = v;
+}
+
-void rvsim(rvstate* s) {
- char dis[128];
- uint32_t pc, ins;
- pc = s->pc;
- while (pc < 0x80000100) {
+#define RdR1() rreg(s, get_r1(ins))
+#define RdR2() rreg(s, get_r2(ins))
+#define WrRd(v) wreg(s, get_rd(ins), v)
+
+#define DO_DISASM 1
+#define DO_TRACK 1
+
+void rvsim(rvstate_t* s) {
+#if DO_TRACK
+ uint32_t last[32];
+#endif
+ uint32_t pc = s->pc;
+ uint32_t next = pc;
+ uint32_t ccount = 0;
+ uint32_t ins;
+ for (;;) {
+ pc = next;
+#if DO_TRACK
+ memcpy(last, &s->x, sizeof(last));
+#endif
ins = rd32(pc);
+#if DO_DISASM
+ char dis[128];
rvdis(pc, ins, dis);
printf("%08x: %08x %s\n", pc, ins, dis);
- pc += 4;
+#endif
+ next = pc + 4;
+ ccount++;
+ switch (get_oc(ins)) {
+ case OC_LOAD:
+ switch (get_fn3(ins)) {
+ case F3_LW:
+ WrRd(rd32(RdR1() + get_ii(ins)));
+ break;
+ default:
+ goto inval;
+ }
+ break;
+ case OC_CUSTOM_0:
+ goto inval;
+ case OC_MISC_MEM:
+ if (get_fn3(ins) != 0) goto inval;
+ // fence -- do nothing
+ break;
+ case OC_OP_IMM: {
+ uint32_t a = RdR1();
+ uint32_t b = get_ii(ins);
+ uint32_t n;
+ switch (get_fn3(ins)) {
+ case F3_ADDI: n = a + b; break;
+ case F3_SLLI:
+ if (b & 0b111111100000) goto inval;
+ n = a << b; break;
+ case F3_SLTI: n = ((int32_t)a) < ((int32_t)b); break;
+ case F3_SLTIU: n = a < b; break;
+ case F3_XORI: n = a ^ b; break;
+ case F3_SRLI:
+ if (b & 0b101111100000) goto inval;
+ if (b & 0b010000000000) {
+ n = ((int32_t)a) >> b;
+ } else {
+ n = a >> b;
+ }
+ break;
+ case F3_ORI: n = a | b; break;
+ case F3_ANDI: n = a & b; break;
+ }
+ WrRd(n);
+ break;
+ }
+ case OC_AUIPC:
+ WrRd(pc + get_iu(ins));
+ break;
+ case OC_STORE:
+ switch (get_fn3(ins)) {
+ case F3_SW:
+ wr32(RdR2() + get_is(ins), RdR1());
+ break;
+ default:
+ goto inval;
+ }
+ case OC_OP: {
+ uint32_t a = RdR1();
+ uint32_t b = RdR1();
+ uint32_t n;
+ if (ins & 0xDE000000) goto inval;
+ switch (get_fn3(ins) | (ins >> 27)) {
+ case F3_ADD: n = a + b; break;
+ case F3_SLL: n = a << (b & 31); break;
+ case F3_SLT: n = ((int32_t)a) < ((int32_t)b); break;
+ case F3_SLTU: n = a < b; break;
+ case F3_XOR: n = a ^ b; break;
+ case F3_SRL: n = a >> (b & 31); break;
+ case F3_OR: n = a | b; break;
+ case F3_AND: n = a & b; break;
+ case F3_SUB: n = a - b; break;
+ case F3_SRA: n = ((int32_t)a) >> (b & 31); break;
+ default: goto inval;
+ }
+ WrRd(n);
+ break;
+ }
+ case OC_LUI:
+ WrRd(get_iu(ins));
+ break;
+ case OC_BRANCH: {
+ uint32_t a = RdR1();
+ uint32_t b = RdR2();
+ int32_t p;
+ switch (get_fn3(ins)) {
+ case F3_BEQ: p = (a == b); break;
+ case F3_BNE: p = (a != b); break;
+ case F3_BLT: p = (((int32_t)a) == ((int32_t)b)); break;
+ case F3_BGE: p = (((int32_t)a) == ((int32_t)b)); break;
+ case F3_BLTU: p = (a <= b); break;
+ case F3_BGEU: p = (a >= b); break;
+ default:
+ goto inval;
+ }
+ if (p) next = pc + (get_ib(ins) << 1);
+ break;
+ }
+ case OC_JALR:
+ if (get_fn3(ins) != 0) goto inval;
+ WrRd(next);
+ next = RdR1() + (get_ii(ins) << 1);
+ break;
+ case OC_JAL:
+ WrRd(next);
+ next = pc + get_ij(ins);
+ break;
+ case OC_SYSTEM:
+ goto inval;
+ default:
+ inval:
+ return;
+ }
+#if DO_TRACK
+ for (unsigned n = 1; n < 32; n++) {
+ if (s->x[n] != last[n]) {
+ printf(" (%s = 0x%08x)\n",
+ regname[n], s->x[n]);
+ }
+ }
+#endif
}
}
int main(int argc, char** argv) {
- rvstate s;
+ rvstate_t s;
if (load_image("out/hello.bin", memory, sizeof(memory)) < 0) return -1;
memset(&s, 0, sizeof(s));
s.pc = 0x80000000;
diff --git a/start.S b/start.S
@@ -1,9 +1,29 @@
+#define PASS .long 0x0000000b // _exiti 0
+#define FAIL .long 0xFFF0000b // _exiti -1
+#define PUTC .long 0x0005500b // _putc a0
+
.globl _start
_start:
- jal ra, main
+#if 0
+ li t1, 0x87654FFF
+ PASS
+ FAIL
+ PUTC
+ beqz t1, main0
+ bnez t1, main0
+ blez t1, main0
+ bgez t1, main0
+ bltz t1, main0
+ bgtz t1, main0
+ sltz s1, s2
+ sgtz s1, s2
+ snez s1, s2
+ seqz s1, s2
+ not s3, s5
+ neg s5, s6
+#endif
+main0:
jal main
- nop
- mv x3, x4
- j .
+ PASS