rvemu

RISC-V RV32I execution engine
git clone git://mcdim.xyz/rvemu.git
Log | Files | Refs | README

exec.c (11080B)


      1 #include <stdint.h>
      2 #include <inttypes.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 
      7 #include "exec.h"
      8 #include "mem.h"
      9 #include "decode.h"
     10 #include "trap.h"
     11 
     12 static inline void print_bin16(uint16_t x) {
     13 	for (int i = 15; i >= 0; i--) {
     14 		putchar((x >> i) & 1 ? '1' : '0');
     15 		/*if (i % 4 == 0) putchar(' '); grouping */
     16 	}
     17 }
     18 
     19 static inline void print_bin32(uint32_t x) {
     20 	for (int i = 31; i >= 0; i--) {
     21 		putchar((x >> i) & 1 ? '1' : '0');
     22 	}
     23 }
     24 
     25 static inline void print_binless(uint32_t x, int a) {
     26 	for (int i = a-1; i >= 0; i--) {
     27 		putchar((x >> i) & 1 ? '1' : '0');
     28 	}
     29 }
     30 
     31 static void
     32 raise_trap(cpu_state_t *cpu, trap_cause_t trapinfo, uint32_t tval) 
     33 {
     34 	cpu->status = CPU_TRAPPED;
     35 	cpu->trap.cause = trapinfo;
     36 	cpu->trap.pc = cpu->pc;
     37 	cpu->trap.tval = tval;
     38 	return;
     39 }
     40 	
     41 static inline trap_cause_t
     42 validate_instruction_address(int64_t addr)
     43 {
     44 	if (addr < 0 || addr > (int64_t)MEMSIZE - INT64_C(4))
     45 		return TRAP_INSTRUCTION_ACCESS_FAULT;
     46 	
     47 	if ((addr & INT64_C(3)) != 0)
     48 		return TRAP_INSTRUCTION_MISALIGNED;
     49 	
     50 	return TRAP_NONE;
     51 }
     52 
     53 static trap_cause_t
     54 get_valid_branch_target(uint32_t pc, int32_t offset, uint32_t *target)
     55 {
     56 	int64_t candidate = (int64_t)pc + (int64_t)offset;
     57 
     58 	trap_cause_t valid = validate_instruction_address(candidate);
     59 	if (valid != TRAP_NONE)
     60 		return valid;
     61 
     62 	*target = (uint32_t)candidate;
     63 	return valid;
     64 }
     65 
     66 static trap_cause_t
     67 get_valid_jalr_target(uint32_t base, int32_t offset, uint32_t *target)
     68 {
     69 	int64_t candidate = ((int64_t)base + (int64_t)offset)
     70 			    & ~INT64_C(1);
     71 	trap_cause_t valid = validate_instruction_address(candidate);
     72 	if (valid != TRAP_NONE)
     73 		return valid;
     74 
     75 	*target = (uint32_t)candidate;
     76 	return valid;
     77 }
     78 
     79 static void exec_itype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
     80 	(void)mem; /* suppressing unused parameters warning */
     81 	itype_t i;
     82 	decode_itype(&i, inst);
     83 
     84 	uint32_t x; uint32_t shamt;
     85 	uint32_t uimm = (uint32_t)i.imm;
     86 	switch(i.funct3) {
     87 	case 0x0:
     88 		x = cpu->x[i.rs1]+uimm;
     89 		break;
     90 	case 0x1:
     91 		if (i.funct7 == 0x00) {
     92 			shamt = i.imm & 31;
     93 			x = cpu->x[i.rs1] << shamt;
     94 		} else {
     95 			raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
     96 			return;
     97 		}
     98 		break;
     99 	case 0x2:
    100 		x = ((int32_t)cpu->x[i.rs1] < (int32_t)i.imm) ? 1 : 0;
    101 		break;
    102 	case 0x3:
    103 		x = (cpu->x[i.rs1] < uimm) ? 1 : 0;
    104 		break;
    105 	case 0x4:
    106 		x = cpu->x[i.rs1]^uimm;
    107 		break;
    108 	case 0x5:
    109 		shamt = i.imm & 31;
    110 		switch(i.funct7) {
    111 		case 0x00:
    112 			x = cpu->x[i.rs1] >> shamt;
    113 			break;
    114 		case 0x20:
    115 			x = (uint32_t)((int32_t)cpu->x[i.rs1] >> shamt);
    116 			break;
    117 		default:
    118 			raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    119 			return;
    120 		}
    121 		break;
    122 	case 0x6:
    123 		x = cpu->x[i.rs1]|uimm;
    124 		break;
    125 	case 0x7:
    126 		x = cpu->x[i.rs1]&uimm;
    127 		break;
    128 	default:
    129 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    130 		return;
    131 	}
    132 
    133 	if (i.rd != 0) {
    134 		cpu->x[i.rd] = x;
    135 	}
    136 
    137 	cpu->pc += 4;
    138 }
    139 
    140 static void exec_rtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
    141 	(void)mem; /* suppressing unused parameters warning */
    142 	rtype_t r;
    143 	decode_rtype(&r, inst);
    144 
    145 	uint32_t x;
    146 	uint32_t shamt;
    147 	switch(r.funct3) {
    148 	case 0x0:
    149 		switch(r.funct7) {
    150 		case 0x00:
    151 			x = cpu->x[r.rs1]+cpu->x[r.rs2];
    152 			break;
    153 		case 0x20:
    154 			x = cpu->x[r.rs1]-cpu->x[r.rs2];
    155 			break;
    156 		default:
    157 			raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    158 			return;
    159 		}
    160 		break;
    161 	case 0x1:
    162 		shamt = cpu->x[r.rs2] & 31;
    163 		x = cpu->x[r.rs1] << shamt;
    164 		break;
    165 	case 0x2:
    166 		x = ((int32_t)cpu->x[r.rs1] < (int32_t)cpu->x[r.rs2]) ? 1 : 0;
    167 		break;
    168 	case 0x3:
    169 		x = (cpu->x[r.rs1] < cpu->x[r.rs2]) ? 1 : 0;
    170 		break;
    171 	case 0x4:
    172 		x = cpu->x[r.rs1]^cpu->x[r.rs2];
    173 		break;
    174 	case 0x5:
    175 		switch(r.funct7) {
    176 		case 0x00:
    177 			shamt = cpu->x[r.rs2] & 31;
    178 			x = cpu->x[r.rs1] >> shamt;
    179 			break;
    180 		case 0x20:
    181 			shamt = cpu->x[r.rs2] & 31;
    182 			x = (uint32_t)((int32_t)cpu->x[r.rs1] >> shamt);
    183 			break;
    184 		default:
    185 			raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    186 			return;
    187 		}
    188 		break;
    189 	case 0x6:
    190 		x = cpu->x[r.rs1]|cpu->x[r.rs2];
    191 		break;
    192 	case 0x7:
    193 		x = cpu->x[r.rs1]&cpu->x[r.rs2];
    194 		break;
    195 	default:
    196 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    197 		return;
    198 	}
    199 
    200 	if (r.rd != 0) {
    201 		cpu->x[r.rd] = x;
    202 	}
    203 
    204 	cpu->pc += 4;
    205 }
    206 
    207 static void exec_itype_loads(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
    208 	itype_t i;
    209 	decode_itype(&i, inst);
    210 
    211 	uint32_t addr = (uint32_t)i.imm + cpu->x[i.rs1];
    212 
    213 	uint32_t x;
    214 	switch(i.funct3) {
    215 	case 0x0:
    216 		if (addr > MEMSIZE - 1) {
    217 			raise_trap(cpu, TRAP_LOAD_ACCESS_FAULT, inst);
    218 			return;
    219 		}
    220 
    221 		x = (uint32_t)(int32_t)(int8_t)mem[addr];
    222 		break;
    223 	case 0x1:
    224 		if ((addr & UINT32_C(0x1)) != 0) {
    225 			raise_trap(cpu, TRAP_LOAD_MISALIGNED, inst);
    226 			return;
    227 		}
    228 
    229 		if (addr + 1 > MEMSIZE - 1) {
    230 			raise_trap(cpu, TRAP_LOAD_ACCESS_FAULT, inst);
    231 			return;
    232 		}
    233 
    234 		uint16_t half = (uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8);
    235 		x = (uint32_t)(int32_t)(int16_t)half;
    236 		break;
    237 	case 0x2:
    238 		if ((addr & UINT32_C(0x3)) != 0) {
    239 			raise_trap(cpu, TRAP_LOAD_MISALIGNED, inst);
    240 			return;
    241 		}
    242 
    243 		if (addr + 3 > MEMSIZE - 1) {
    244 			raise_trap(cpu, TRAP_LOAD_ACCESS_FAULT, inst);
    245 			return;
    246 		}
    247 
    248 		/* TODO: clean up */
    249 		x = (uint32_t)(int32_t)(
    250 				((uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8))
    251 		    		|
    252 				(((uint16_t)mem[addr+2] | ((uint16_t)mem[addr+3] << 8))
    253 				  << 16)
    254 		    );
    255 		break;
    256 	case 0x4:
    257 		if (addr > MEMSIZE - 1) {
    258 			raise_trap(cpu, TRAP_LOAD_ACCESS_FAULT, inst);
    259 			return;
    260 		}
    261 
    262 		x = (uint32_t)(uint8_t)mem[addr];
    263 		break;
    264 	case 0x5:
    265 		if ((addr & UINT32_C(0x1)) != 0) {
    266 			raise_trap(cpu, TRAP_LOAD_MISALIGNED, inst);
    267 			return;
    268 		}
    269 
    270 		if (addr + 1 > MEMSIZE - 1) {
    271 			raise_trap(cpu, TRAP_LOAD_ACCESS_FAULT, inst);
    272 			return;
    273 		}
    274 
    275 		uint16_t uhalf = (uint16_t)mem[addr] | ((uint16_t)mem[addr+1] << 8);
    276 		x = (uint32_t)uhalf;
    277 		break;
    278 	default:
    279 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    280 		return;
    281 	}
    282 
    283 	if (i.rd != 0) {
    284 		cpu->x[i.rd] = x;
    285 	}
    286 	cpu->pc += 4;
    287 }
    288 
    289 static void exec_stype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
    290 	stype_t s;
    291 	decode_stype(&s, inst);
    292 
    293 	uint32_t addr = (uint32_t)s.imm + cpu->x[s.rs1];
    294 
    295 	/* TODO: helpers, better checks */
    296 	switch(s.funct3) {
    297 	case 0x0:
    298 		if (addr > MEMSIZE - 1) {
    299 			raise_trap(cpu, TRAP_STORE_ACCESS_FAULT, inst);
    300 			return;
    301 		}
    302 
    303 		mem[addr] = cpu->x[s.rs2] & 255;
    304 		break;
    305 	case 0x1:
    306 		if ((addr & UINT32_C(0x1)) != 0) {
    307 			raise_trap(cpu, TRAP_STORE_MISALIGNED, inst);
    308 			return;
    309 		}
    310 
    311 		if (addr + 1 > MEMSIZE - 1) {
    312 			raise_trap(cpu, TRAP_STORE_ACCESS_FAULT, inst);
    313 			return;
    314 		}
    315 
    316 		if (addr % 2 == 0) {
    317 			mem[addr] = (uint8_t)cpu->x[s.rs2];
    318 			mem[addr+1] = (uint8_t)(cpu->x[s.rs2] >> 8);
    319 		}
    320 		break;
    321 	case 0x2:
    322 		if ((addr & UINT32_C(0x3)) != 0) {
    323 			raise_trap(cpu, TRAP_STORE_MISALIGNED, inst);
    324 			return;
    325 		}
    326 
    327 		if (addr + 3 > MEMSIZE - 1) {
    328 			raise_trap(cpu, TRAP_STORE_ACCESS_FAULT, inst);
    329 			return;
    330 		}
    331 		if (addr % 4 == 0) {
    332 			mem[addr] =   (uint8_t)cpu->x[s.rs2];
    333 			mem[addr+1] = (uint8_t)(cpu->x[s.rs2] >> 8);
    334 			mem[addr+2] = (uint8_t)(cpu->x[s.rs2] >> 16);
    335 			mem[addr+3] = (uint8_t)(cpu->x[s.rs2] >> 24);
    336 		}
    337 		break;
    338 	default:
    339 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    340 		return;
    341 	}
    342 
    343 	cpu->pc += 4;
    344 }
    345 
    346 static void exec_btype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
    347 	(void)mem; /* suppressing unused parameters warning */
    348 	btype_t b;
    349 	decode_btype(&b, inst);
    350 
    351 	int jump = 0;
    352 	switch (b.funct3) {
    353 	case 0x0:
    354 		if (cpu->x[b.rs1] == cpu->x[b.rs2])
    355 			jump = 1;
    356 		break;
    357 	case 0x1:
    358 		if (cpu->x[b.rs1] != cpu->x[b.rs2])
    359 			jump = 1;
    360 		break;
    361 	case 0x4:
    362 		if ((int32_t)cpu->x[b.rs1] < (int32_t)cpu->x[b.rs2])
    363 			jump = 1;
    364 		break;
    365 	case 0x5:
    366 		if ((int32_t)cpu->x[b.rs1] >= (int32_t)cpu->x[b.rs2])
    367 			jump = 1;
    368 		break;
    369 	case 0x6:
    370 		if (cpu->x[b.rs1] < cpu->x[b.rs2])
    371 			jump = 1;
    372 		break;
    373 	case 0x7:
    374 		if (cpu->x[b.rs1] >= cpu->x[b.rs2])
    375 			jump = 1;
    376 		break;
    377 	default:
    378 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    379 		return;
    380 	}
    381 
    382 	if (jump == 1) {
    383 		uint32_t target;
    384 		trap_cause_t cause = get_valid_branch_target(cpu->pc,
    385 							     b.imm,
    386 							     &target);
    387 		if (cause != TRAP_NONE) {
    388 			raise_trap(cpu, cause, inst);
    389 			return;
    390 		}
    391 		cpu->pc = target;
    392 	} else {
    393 		cpu->pc += 4;
    394 	}
    395 }
    396 
    397 static void exec_itype_jalr(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
    398 	(void)mem; /* suppressing unused parameters warning */
    399 	itype_t i;
    400 	decode_itype(&i, inst);
    401 
    402 	uint32_t x;
    403 	if (i.funct3 != 0x0) {
    404 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    405 		return;
    406 	}
    407 
    408 	x = cpu->pc + 4;
    409 
    410 	uint32_t target;
    411 	trap_cause_t cause = get_valid_jalr_target(cpu->x[i.rs1], i.imm,
    412 							     &target);
    413 	if (cause != TRAP_NONE) {
    414 		raise_trap(cpu, cause, inst);
    415 		return;
    416 	}
    417 
    418 	cpu->pc = target;
    419 
    420 	if (i.rd != 0) {
    421 		cpu->x[i.rd] = x;
    422 	}
    423 }
    424 
    425 static void exec_jtype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem) {
    426 	(void)mem; /* suppressing unused parameters warning */
    427 	jtype_t j;
    428 	decode_jtype(&j, inst);
    429 
    430 	uint32_t target;
    431 	trap_cause_t cause = get_valid_branch_target(cpu->pc,
    432 						     (int32_t)j.imm,
    433 						     &target);
    434 	if (cause != TRAP_NONE) {
    435 		raise_trap(cpu, cause, inst);
    436 		return;
    437 	}
    438 
    439 	if (j.rd != 0) {
    440 		uint32_t x = cpu->pc + 4;
    441 		cpu->x[j.rd] = x;
    442 	}
    443 	cpu->pc = target;
    444 }
    445 
    446 static void
    447 exec_utype(cpu_state_t *cpu, uint32_t inst, uint8_t *mem)
    448 {
    449 	(void)mem; /* suppressing unused parameters warning */
    450 	utype_t u;
    451 	decode_utype(&u, inst);
    452 
    453 	if (u.rd != 0)
    454 		cpu->x[u.rd] = u.imm << 12;
    455 
    456 	cpu->pc += 4;
    457 }
    458 
    459 static void
    460 exec_utype_auipc(cpu_state_t *cpu, uint32_t inst, uint8_t *mem)
    461 {
    462 	(void)mem; /* suppressing unused parameters warning */
    463 	utype_t u;
    464 	decode_utype(&u, inst);
    465 
    466 	if (u.rd != 0)
    467 		cpu->x[u.rd] = cpu->pc + (u.imm << 12);
    468 
    469 	cpu->pc += 4;
    470 }
    471 
    472 static void
    473 exec_itype_control(cpu_state_t *cpu, uint32_t inst, uint8_t *mem)
    474 {
    475 	(void)mem; /* suppressing unused parameters warning */
    476 	itype_t i;
    477 	decode_itype(&i, inst);
    478 
    479 	if (i.funct3 != 0x0) {
    480 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    481 		return;
    482 	}
    483 
    484 	if (i.imm == 0x0) {
    485 		raise_trap(cpu, TRAP_ECALL, inst);
    486 		return;
    487 	} else if (i.imm == 0x1) {
    488 		raise_trap(cpu, TRAP_EBREAK, inst);
    489 		return;
    490 	} else {
    491 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    492 		return;
    493 	}
    494 
    495 }
    496 
    497 static void
    498 exec_itype_fence(cpu_state_t *cpu, uint32_t inst, uint8_t *mem)
    499 {
    500 	(void)mem; /* suppressing unused parameters warning */
    501 	itype_t i;
    502 	decode_itype(&i, inst);
    503 
    504 	if (i.funct3 != 0x0) {
    505 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    506 		return;
    507 	}
    508 
    509 	cpu->pc += 4;
    510 }
    511 
    512 /* Function pointer dispatch */
    513 typedef void (*exec_fn)(cpu_state_t *, uint32_t inst, uint8_t *mem);
    514 
    515 static exec_fn opcode_table[128] = {
    516         [0x33] = exec_rtype,
    517         [0x13] = exec_itype,
    518         [0x03] = exec_itype_loads,
    519         [0x67] = exec_itype_jalr,
    520         [0x73] = exec_itype_control,
    521         [0x0f] = exec_itype_fence,
    522         [0x23] = exec_stype,
    523         [0x63] = exec_btype,
    524         [0x6f] = exec_jtype,
    525 	[0x37] = exec_utype,
    526 	[0x17] = exec_utype_auipc
    527 };
    528 /***/
    529 
    530 void
    531 cpu_step(cpu_state_t *cpu, uint8_t *mem)
    532 {
    533 	uint32_t inst = load_u32_le(mem, cpu->pc); /* load little endian */
    534 
    535 	/* decode opcode */
    536 	uint16_t opcode = inst & 127;
    537 
    538 	exec_fn fn = opcode_table[opcode];
    539 
    540 	if (!fn) {
    541 		raise_trap(cpu, TRAP_ILLEGAL_INSTRUCTION, inst);
    542 		return;
    543 	}
    544 
    545 	fn(cpu,inst,mem);
    546 }