rvemu

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

exec.c (10063B)


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