riscv

an RV32I simulator and related experiments
git clone http://frotz.net/git/riscv.git
Log | Files | Refs | README

rvsim.c (10494B)


      1 // Copyright 2019, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 #include <stdio.h>
      5 #include <stdint.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #include "riscv.h"
     10 #include "rvsim.h"
     11 
     12 #define DO_TRACE_INS     0
     13 #define DO_TRACE_TRAPS   0
     14 #define DO_TRACE_MEM_WR  0
     15 #define DO_TRACE_REG_WR  0
     16 
     17 #define DO_ABORT_INVAL   0
     18 
     19 #define RVMEMBASE 0x80000000
     20 #define RVMEMSIZE 0x01000000
     21 #define RVMEMMASK (RVMEMSIZE - 1)
     22 
     23 typedef struct rvstate {
     24 	uint32_t x[32];
     25 	void* memory;
     26 	uint32_t mscratch;
     27 	uint32_t mtvec;
     28 	uint32_t mtval;
     29 	uint32_t mepc;
     30 	uint32_t mcause;
     31 	void* ctx;
     32 } rvstate_t;
     33 
     34 void* rvsim_dma(rvstate_t* s, uint32_t va, uint32_t len) {
     35 	if (va < RVMEMBASE) return NULL;
     36 	va -= RVMEMBASE;
     37 	if (va >= RVMEMSIZE) return NULL;
     38 	if (len > (RVMEMSIZE - va)) return NULL;
     39 	return s->memory + va;
     40 }
     41 
     42 static uint32_t rd32(uint8_t* memory, uint32_t addr) {
     43 	if (addr < RVMEMBASE) {
     44 		return ior32(addr);
     45 	} else {
     46 		addr &= RVMEMMASK;
     47 		return ((uint32_t*) memory)[addr >> 2];
     48 	}
     49 }
     50 static void wr32(uint8_t* memory, uint32_t addr, uint32_t val) {
     51 	if (addr < RVMEMBASE) {
     52 		iow32(addr, val);
     53 	} else {
     54 		addr &= RVMEMMASK;
     55 		((uint32_t*) memory)[addr >> 2] = val;
     56 	}
     57 }
     58 static uint32_t rd16(uint8_t* memory, uint32_t addr) {
     59 	if (addr < RVMEMBASE) {
     60 		return 0xffff;
     61 	} else {
     62 		addr &= RVMEMMASK;
     63 		return ((uint16_t*) memory)[addr >> 1];
     64 	}
     65 }
     66 static void wr16(uint8_t* memory, uint32_t addr, uint32_t val) {
     67 	if (addr >= RVMEMBASE) {
     68 		addr &= RVMEMMASK;
     69 		((uint16_t*) memory)[addr >> 1] = val;
     70 	}
     71 }
     72 static uint32_t rd8(uint8_t* memory, uint32_t addr) {
     73 	if (addr < RVMEMBASE) {
     74 		return 0xff;
     75 	} else {
     76 		addr &= RVMEMMASK;
     77 		return memory[addr];
     78 	}
     79 }
     80 static void wr8(uint8_t* memory, uint32_t addr, uint32_t val) {
     81 	if (addr >= RVMEMBASE) {
     82 		addr &= RVMEMMASK;
     83 		memory[addr] = val;
     84 	}
     85 }
     86 
     87 uint32_t rvsim_rd32(rvstate_t* s, uint32_t addr) {
     88 	return rd32(s->memory, addr);
     89 }
     90 
     91 int rvsim_init(rvstate_t** _s, void* ctx) {
     92 	rvstate_t *s;
     93 	if ((s = malloc(sizeof(rvstate_t))) == NULL) {
     94 		return -1;
     95 	}
     96 	memset(s, 0, sizeof(rvstate_t));
     97 	if ((s->memory = malloc(RVMEMSIZE)) == NULL) {
     98 		free(s);
     99 		return -1;
    100 	}
    101 	memset(s->memory, 0, RVMEMSIZE);
    102 	s->mtvec = 0x80000000;
    103 	s->ctx = ctx ? ctx : s;
    104 	*_s = s;
    105 	return 0;
    106 }
    107 
    108 static inline uint32_t rreg(rvstate_t* s, uint32_t n) {
    109 	return n ? s->x[n] : 0;
    110 }
    111 static inline void wreg(rvstate_t* s, uint32_t n, uint32_t v) {
    112 	s->x[n] = v;
    113 }
    114 
    115 static void put_csr(rvstate_t* s, uint32_t csr, uint32_t v) {
    116 	switch (csr) {
    117 	case CSR_MSCRATCH: s->mscratch = v; break;
    118 	case CSR_MTVEC:    s->mtvec = v & 0xFFFFFFFC; break;
    119 	case CSR_MTVAL:    s->mtval = v; break;
    120 	case CSR_MEPC:     s->mepc = v; break;
    121 	case CSR_MCAUSE:   s->mcause = v; break;
    122 	}
    123 }
    124 static uint32_t get_csr(rvstate_t* s, uint32_t csr) {
    125 	switch (csr) {
    126 	case CSR_MISA:      return 0x40000100; // RV32I
    127 	case CSR_MVENDORID: return 0; // NONE
    128 	case CSR_MARCHID:   return 0; // NONE
    129 	case CSR_MIMPID:    return 0; // NONE
    130 	case CSR_MHARTID:   return 0; // Thread Zero
    131 	case CSR_MSCRATCH:  return s->mscratch;
    132 	case CSR_MTVEC:	    return s->mtvec;
    133 	case CSR_MTVAL:	    return s->mtval;
    134 	case CSR_MEPC:	    return s->mepc;
    135 	case CSR_MCAUSE:    return s->mcause;
    136 	default:
    137 		return 0;
    138 	}
    139 }
    140 
    141 #define RdR1() rreg(s, get_r1(ins))
    142 #define RdR2() rreg(s, get_r2(ins))
    143 #define RdRd() rreg(s, get_rd(ins))
    144 #define WrRd(v) wreg(s, get_rd(ins), v)
    145 
    146 #if DO_TRACE_REG_WR
    147 #define trace_reg_wr(v) do {\
    148 	uint32_t r = get_rd(ins); \
    149 	if (r) { \
    150 	fprintf(stderr, "          (%s = %08x)\n", \
    151 		rvregname(get_rd(ins)), v); \
    152 	}} while (0)
    153 #else
    154 #define trace_reg_wr(v) do {} while (0)
    155 #endif
    156 
    157 #if DO_TRACE_MEM_WR
    158 #define trace_mem_wr(a, v) do {\
    159 	fprintf(stderr, "          ([%08x] = %08x)\n", a, v);\
    160 	} while (0)
    161 #else
    162 #define trace_mem_wr(a, v) do {} while (0)
    163 #endif
    164 
    165 int rvsim_exec(rvstate_t* s, uint32_t _pc) {
    166 	uint32_t pc = _pc;
    167 	uint32_t next = _pc;
    168 	uint32_t ins;
    169 	uint64_t ccount = 0;
    170 	for (;;) {
    171 		ccount++;
    172 		pc = next;
    173 		ins = rd32(s->memory, pc);
    174 #if DO_TRACE_INS
    175 		char dis[128];
    176 		rvdis(pc, ins, dis);
    177 		fprintf(stderr, "%08x: %08x %s\n", pc, ins, dis);
    178 #endif
    179 		next = pc + 4;
    180 		switch (get_oc(ins)) {
    181 		case OC_LOAD: {
    182 			uint32_t a = RdR1() + get_ii(ins);
    183 			uint32_t v;
    184 			switch (get_fn3(ins)) {
    185 			case F3_LW:
    186 				if (a & 3) goto trap_load_align;
    187 				v = rd32(s->memory, a); break;
    188 			case F3_LHU:
    189 				if (a & 1) goto trap_load_align;
    190 				v = rd16(s->memory, a); break;
    191 			case F3_LBU:
    192 				v = rd8(s->memory, a); break;
    193 			case F3_LH:
    194 				if (a & 1) goto trap_load_align;
    195 				v = rd16(s->memory, a);
    196 				if (v & 0x8000) { v |= 0xFFFF0000; } break;
    197 			case F3_LB:
    198 				v = rd8(s->memory, a);
    199 				if (v & 0x80) { v |= 0xFFFFFF00; } break;
    200 			default:
    201 				goto inval;
    202 			}
    203 			WrRd(v);
    204 			trace_reg_wr(v);
    205 			break;
    206 		trap_load_align:
    207 			s->mcause = EC_L_ALIGN;
    208 			s->mtval = a;
    209 			goto trap_common;
    210 		}
    211 		case OC_CUSTOM_0:
    212 			switch (get_fn3(ins)) {
    213 			case 0b000: // _exiti
    214 				fprintf(stderr, "CCOUNT %lu\n", ccount);
    215 				return get_ii(ins);
    216 			case 0b100: // _exit
    217 				fprintf(stderr, "CCOUNT %lu\n", ccount);
    218 				return RdR1();
    219 			case 0b001: // _iocall
    220 				s->x[10] = iocall(s->ctx, get_ii(ins), s->x + 10);
    221 				break;
    222 			default:
    223 				goto inval;
    224 			}
    225 			break;
    226 		case OC_MISC_MEM:
    227 			switch (get_fn3(ins)) {
    228 			case F3_FENCE:
    229 			case F3_FENCE_I:
    230 				break;
    231 			default:
    232 				goto inval;
    233 			}
    234 			break;
    235 		case OC_OP_IMM: {
    236 			uint32_t a = RdR1();
    237 			uint32_t b = get_ii(ins);
    238 			uint32_t n = 0xe1e1e1e1;
    239 			switch (get_fn3(ins)) {
    240 			case F3_ADDI: n = a + b; break;
    241 			case F3_SLLI:
    242 				if (b & 0b111111100000) goto inval;
    243 				n = a << b; break;
    244 			case F3_SLTI: n = ((int32_t)a) < ((int32_t)b); break;
    245 			case F3_SLTIU: n = a < b; break;
    246 			case F3_XORI: n = a ^ b; break;
    247 			case F3_SRLI:
    248 				if (b & 0b101111100000) goto inval;
    249 				if (b & 0b010000000000) {
    250 					n = ((int32_t)a) >> b;
    251 				} else {
    252 					n = a >> b;
    253 				}
    254 				break;
    255 			case F3_ORI: n = a | b; break;
    256 			case F3_ANDI: n = a & b; break;
    257 			}
    258 			WrRd(n);
    259 			trace_reg_wr(n);
    260 			break;
    261 			}
    262 		case OC_AUIPC: {
    263 			uint32_t v = pc + get_iu(ins);
    264 			WrRd(v);
    265 			trace_reg_wr(v);
    266 			break;
    267 			}
    268 		case OC_STORE: {
    269 			uint32_t a = RdR1() + get_is(ins);
    270 			uint32_t v = RdR2();
    271 			switch (get_fn3(ins)) {
    272 			case F3_SW:
    273 				if (a & 3) goto trap_store_align;
    274 				wr32(s->memory, a, v); break;
    275 			case F3_SH:
    276 				if (a & 1) goto trap_store_align;
    277 				wr16(s->memory, a, v); break;
    278 			case F3_SB:
    279 				wr8(s->memory, a, v); break;
    280 			default:
    281 				goto inval;
    282 			}
    283 			trace_mem_wr(a, v);
    284 			break;
    285 		trap_store_align:
    286 			s->mcause = EC_S_ALIGN;
    287 			s->mtval = a;
    288 			goto trap_common;
    289 			}
    290 		case OC_OP: {
    291 			uint32_t a = RdR1();
    292 			uint32_t b = RdR2();
    293 			uint32_t n = get_fn3(ins);
    294 			switch (ins >> 25) {
    295 			case 0b0000000:
    296 				switch (n) {
    297 				case F3_ADD: n = a + b; break;
    298 				case F3_SLL: n = a << (b & 31); break;
    299 				case F3_SLT: n = ((int32_t)a) < ((int32_t)b); break;
    300 				case F3_SLTU: n = a < b; break;
    301 				case F3_XOR: n = a ^ b; break;
    302 				case F3_SRL: n = a >> (b & 31); break;
    303 				case F3_OR: n = a | b; break;
    304 				case F3_AND: n = a & b; break;
    305 				}
    306 				break;
    307 			case 0b0000001:
    308 				switch (n) {
    309 				case F3_MUL: n = a * b; break;
    310 				case F3_MULH: n = ((int64_t)(int32_t)a * (int64_t)(int32_t)b) >> 32; break;
    311 				case F3_MULHSU: n = ((int64_t)(int32_t)a * (uint64_t)b) >> 32; break;
    312 				case F3_MULHU: n = ((uint64_t)a * (uint64_t)b) >> 32; break;
    313 				case F3_DIV:
    314 					if (b == 0) { n = 0xffffffff; }
    315 					else if ((a == 0x80000000) && (b == 0xffffffff)) { n = a; }
    316 					else { n = ((int32_t)a / (int32_t)b); }
    317 					break;
    318 				case F3_DIVU:
    319 					if (b == 0) { n = 0xffffffff; }
    320 					else { n = a / b; }
    321 					break;
    322 				case F3_REM:
    323 					if (b == 0) { n = a; }
    324 					else if ((a == 0x80000000) && (b == 0xffffffff)) { n = 0; }
    325 					else { n = ((int32_t)a % (int32_t)b); }
    326 					break;
    327 				case F3_REMU:
    328 					if (b == 0) { n = a; }
    329 					else { n = a % b; }
    330 					break;
    331 				}
    332 				break;
    333 			case 0b0100000:
    334 				switch (n) {
    335 				case F3_SUB: n = a - b; break;
    336 				case F3_SRA: n = ((int32_t)a) >> (b & 31); break;
    337 				default: goto inval;
    338 				}
    339 				break;
    340 			}
    341 			WrRd(n);
    342 			trace_reg_wr(n);
    343 			break;
    344 			}
    345 		case OC_LUI: {
    346 			uint32_t v = get_iu(ins);
    347 			WrRd(v);
    348 			trace_reg_wr(v);
    349 			break;
    350 			}
    351 		case OC_BRANCH: {
    352 			uint32_t a = RdR1();
    353 			uint32_t b = RdR2();
    354 			int32_t p;
    355 			switch (get_fn3(ins)) {
    356 			case F3_BEQ: p = (a == b); break;
    357 			case F3_BNE: p = (a != b); break;
    358 			case F3_BLT: p = (((int32_t)a) < ((int32_t)b)); break;
    359 			case F3_BGE: p = (((int32_t)a) >= ((int32_t)b)); break;
    360 			case F3_BLTU: p = (a < b); break;
    361 			case F3_BGEU: p = (a >= b); break;
    362 			default:
    363 				goto inval;
    364 			}
    365 			if (p) {
    366 				next = pc + get_ib(ins);
    367 				if (next & 3) goto trap_pc_align;
    368 			}
    369 			break;
    370 			}
    371 		case OC_JALR: {
    372 			uint32_t a = (RdR1() + get_ii(ins)) & 0xFFFFFFFE;
    373 			if (get_fn3(ins) != 0) goto inval;
    374 			WrRd(next);
    375 			trace_reg_wr(next);
    376 			next = a;
    377 			if (next & 3) goto trap_pc_align;
    378 			break;
    379 			}
    380 		case OC_JAL:
    381 			WrRd(next);
    382 			trace_reg_wr(next);
    383 			next = pc + get_ij(ins);
    384 			if (next & 3) goto trap_pc_align;
    385 			break;
    386 		case OC_SYSTEM: {
    387 			uint32_t fn = get_fn3(ins);
    388 			if (fn == 0) {
    389 				switch(ins >> 7) {
    390 				case 0b0000000000000000000000000: // ecall
    391 					s->mcause = EC_ECALL_FROM_M;
    392 					s->mtval = 0;
    393 					goto trap_common;
    394 				case 0b0000000000010000000000000: // ebreak
    395 					s->mcause = EC_BREAKPOINT;
    396 					s->mtval = 0;
    397 					goto trap_common;
    398 				case 0b0011000000100000000000000: // mret
    399 					next = s->mepc;
    400 					break;
    401 				default:
    402 					goto inval;
    403 				}
    404 				break;
    405 			}
    406 			uint32_t c = get_iC(ins);
    407 			uint32_t nv = (fn & 4) ? get_ic(ins) : RdR1();
    408 			uint32_t ov = 0;
    409 			switch (fn & 3) {
    410 			case F3_CSRRW:
    411 				// only reads of Rd != x0
    412 				if (get_rd(ins)) ov = get_csr(s, c);
    413 				put_csr(s, c, nv);
    414 				break;
    415 			case F3_CSRRS:
    416 				// only writes if nv != 0
    417 				ov = get_csr(s, c);
    418 				if (nv) put_csr(s, c, ov | nv);
    419 				WrRd(ov);
    420 				break;
    421 			case F3_CSRRC:
    422 				// only writes if nv != 0
    423 				ov = get_csr(s, c);
    424 				if (nv) put_csr(s, c, ov & (~nv));
    425 				break;
    426 			}
    427 			WrRd(ov);
    428 			break;
    429 			}
    430 trap_pc_align:
    431 			s->mcause = EC_I_ALIGN;
    432 			s->mtval = next;
    433 			goto trap_common;
    434 		default:
    435 		inval:
    436 			s->mcause = EC_I_ILLEGAL;
    437 			s->mtval = ins;
    438 #if DO_ABORT_INVAL
    439 			fprintf(stderr,"          (TRAP ILLEGAL %08x)\n", ins);
    440 			return -1;
    441 #endif
    442 trap_common:
    443 			s->mepc = pc;
    444 			next = s->mtvec & 0xFFFFFFFD;
    445 #if DO_TRACE_TRAPS
    446 			fprintf(stderr, "          (TRAP C=%08x V=%08x)\n", s->mcause, s->mtval);
    447 #endif
    448 			break;
    449 		}
    450 	}
    451 }
    452