vriscv/src/cpu/rv32cpu.c

962 lines
31 KiB
C

#include "rv32cpu.h"
#include "instruction.h"
#include "memory/memory.h"
#include "memory/mmu/mmu.h"
#include "vriscv.h"
#include <stdlib.h>
#include <stdio.h>
rv32_cpu_t* cpu0;
typedef union RAW_INSTRUCTION
{
uint32_t data;
struct
{
uint8_t opcode : 7;
uint16_t rd : 5;
uint16_t func3 : 3;
uint16_t rs1 : 5;
uint16_t rs2 : 5;
uint16_t func7 : 7;
} __attribute__((packed));
} __attribute__((packed)) raw_instruction_t;
typedef struct INSTRUCTION
{
uint8_t opcode;
uint32_t immediate;
uint8_t func3;
uint8_t func7;
uint8_t rd;
uint8_t rs1;
uint8_t rs2;
} instruction_t;
static void cpu_print_instruction(instruction_t* instruction);
void cpu_init()
{
cpu0 = malloc(sizeof(rv32_cpu_t));
cpu0->regs.zero = 0;
}
static void cpu_decode(raw_instruction_t raw_instruction, instruction_t* output)
{
output->opcode = raw_instruction.opcode;
output->immediate = 0;
output->func3 = raw_instruction.func3;
output->func7 = raw_instruction.func7;
output->rd = raw_instruction.rd;
output->rs1 = raw_instruction.rs1;
output->rs2 = raw_instruction.rs2;
// Decode immediate, and make sure opcode is correct
switch(raw_instruction.opcode)
{
// U-type instructions
case OPCODE_LUI:
case OPCODE_AUIPC:
output->immediate = raw_instruction.data & 0xFFFFF000;
break;
// J-type instructions
case OPCODE_JAL:
// Last bit (31) of data is immediate bit 20
output->immediate = (raw_instruction.data & 0x80000000) >> 11;
// Then following 10 bits (30-21) are immediate bits 10-1
output->immediate |= (raw_instruction.data & 0x7FE00000) >> 20;
// Following bit (20) is immediate bit 11
output->immediate |= (raw_instruction.data & 0x200000) >> 10;
// Last bits (19-12) are immediate bits 19-12
output->immediate |= (raw_instruction.data & 0xFF000);
break;
// I-type instructions
case OPCODE_JALR:
case OPCODE_LOAD:
case OPCODE_ARITHLOG_IMM:
case OPCODE_SYSTEM:
// Bits 31-20 are immediate bits 11-0
output->immediate = (raw_instruction.data & 0xFFF00000) >> 20;
break;
// B-type instructions
case OPCODE_BRANCH:
// Last bit (31) of data is immediate bit 12
output->immediate = (raw_instruction.data & 0x80000000) >> 19;
// Then following 6 bits (30-25) are immediate bits 10-5
output->immediate |= (raw_instruction.data & 0x7E000000) >> 20;
// On rd field, last 4 bits (4:1) are immediate bits 4:1
output->immediate |= (raw_instruction.rd & 0x1E);
// On rd field, first bit (0) is immediate bit 11
output->immediate |= (raw_instruction.rd & 0x01) << 11;
break;
// R-type instructions
case OPCODE_ARITHLOG:
case OPCODE_ATOMIC:
break;
// S-type instructions
case OPCODE_STORE:
// Bits 31-25 (func7) are immediate bits 11:5
output->immediate = raw_instruction.func7 << 5;
// Bits of rd are immediate bits 4:0
output->immediate |= raw_instruction.rd;
break;
case OPCODE_NOP:
// TODO : Decode NOP instructions
break;
default:
fprintf(stderr, "Error: Unknown instruction opcode 0x%x, could not decode\n", raw_instruction.opcode);
exit(EXIT_FAILURE);
break;
}
}
static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
{
switch(instruction->opcode)
{
case OPCODE_LUI:
{
// Load Upper Immediate (load immediate(31:12 bits) in rd)
cpu->regs.x[instruction->rd] = instruction->immediate;
break;
}
case OPCODE_AUIPC:
{
// Add Upper Immediate to PC
cpu->regs.x[instruction->rd] = instruction->immediate + cpu->pc;
break;
}
case OPCODE_JAL:
{
// Jump And Link
cpu->regs.x[instruction->rd] = cpu->pc + 4;
// Sign extend immediate from 21 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0x1FFFFF) | (instruction->immediate & 0x100000 ? 0xFFE00000 : 0);
cpu->pc += immediate - 4;
break;
}
case OPCODE_JALR:
{
// Jump And Link Register
cpu->regs.x[instruction->rd] = cpu->pc + 4;
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
cpu->pc = ((cpu->regs.x[instruction->rs1] + immediate) & 0xFFFFFFFE) - 4;
break;
}
case OPCODE_BRANCH:
{
// Branches ; to know which one, we must analyse func3
// Sign extend immediate from 13 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x1000 ? 0xFFFFE000 : 0);
immediate -= 4;
switch(instruction->func3)
{
case FUNC3_BEQ:
// Branch EQual
if(cpu->regs.x[instruction->rs1] == cpu->regs.x[instruction->rs2])
cpu->pc = immediate;
break;
case FUNC3_BNE:
// Branch Not Equal
if(cpu->regs.x[instruction->rs1] != cpu->regs.x[instruction->rs2])
cpu->pc = immediate;
break;
case FUNC3_BLT:
// Branch Less Than
if(((int32_t) cpu->regs.x[instruction->rs1]) < ((int32_t) cpu->regs.x[instruction->rs2]))
cpu->pc = immediate;
break;
case FUNC3_BLTU:
// Branch Less Than Unsigned
if(cpu->regs.x[instruction->rs1] < cpu->regs.x[instruction->rs2])
cpu->pc = immediate;
break;
case FUNC3_BGE:
// Branch Greater Equal
if(((int32_t) cpu->regs.x[instruction->rs1]) >= ((int32_t) cpu->regs.x[instruction->rs2]))
cpu->pc = immediate;
break;
case FUNC3_BGEU:
// Branch Greater Equal Unsigned
if(cpu->regs.x[instruction->rs1] >= cpu->regs.x[instruction->rs2])
cpu->pc = immediate;
break;
default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for branch instruction, could not execute\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_LOAD:
{
// Loads ; to know which one, we must analyse func3
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
uint32_t address = cpu->regs.x[instruction->rs1] + immediate;
switch(instruction->func3)
{
case FUNC3_LB:
// Load Byte (8-bits)
cpu->regs.x[instruction->rd] = memory[mmu_translate(address)];
// Sign extend from 8 bits to 32 bits
cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x80 ? 0xFFFFFF00 : 0);
break;
case FUNC3_LH:
// Load Halfword (16-bits)
cpu->regs.x[instruction->rd] = *((uint16_t*) &memory[mmu_translate(address)]);
// Sign extend from 16 bits to 32 bits
cpu->regs.x[instruction->rd] |= (cpu->regs.x[instruction->rd] & 0x8000 ? 0xFFFF0000 : 0);
break;
case FUNC3_LW:
// Load Word (32-bits)
cpu->regs.x[instruction->rd] = *((uint32_t*) &memory[mmu_translate(address)]);
break;
case FUNC3_LBU:
// Load Byte Unsigned (8-bits)
cpu->regs.x[instruction->rd] = memory[mmu_translate(address)];
break;
case FUNC3_LHU:
// Load Halfword Unsigned (16-bits)
cpu->regs.x[instruction->rd] = *((uint16_t*) &memory[mmu_translate(address)]);
break;
default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for load instruction, could not execute\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_STORE:
{
// Store ; to know which one, we must analyse func3
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
uint32_t address = cpu->regs.x[instruction->rs1] + immediate;
switch(instruction->func3)
{
case FUNC3_SB:
// Store Byte (8-bits)
memory[mmu_translate(address)] = cpu->regs.x[instruction->rs2] & 0xFF;
break;
case FUNC3_SH:
// Store Halfword (16-bits)
*((uint16_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2] & 0xFFFF;
break;
case FUNC3_SW:
// Store Word (32-bits)
*((uint32_t*) &memory[mmu_translate(address)]) = cpu->regs.x[instruction->rs2];
break;
default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for store instruction, could not execute\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_ARITHLOG_IMM:
{
// Arithmetic and logic instructions on immediate values
// To find out which operation, we must analyse func3
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
switch(instruction->func3)
{
case FUNC3_ADDI:
// ADD Immediate
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] + immediate;
break;
case FUNC3_SLTI:
// Set Less Than Immediate
if(((int32_t) cpu->regs.x[instruction->rs1]) < ((int32_t) immediate))
cpu->regs.x[instruction->rd] = 1;
else
cpu->regs.x[instruction->rd] = 0;
break;
case FUNC3_SLTIU:
// Set Less Than Immediate Unsigned
if(cpu->regs.x[instruction->rs1] < immediate)
cpu->regs.x[instruction->rd] = 1;
else
cpu->regs.x[instruction->rd] = 0;
break;
case FUNC3_XORI:
// XOR Immediate
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] ^ immediate;
break;
case FUNC3_ORI:
// OR Immediate
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] | immediate;
break;
case FUNC3_ANDI:
// AND Immediate
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] & immediate;
break;
case FUNC3_SLLI:
// Sign-extend immediate in rs2 from 5 bits to 32 bits
immediate = (instruction->rs2 & 0x1F) | (instruction->rs2 & 0x10 ? 0xFFFFFFE0 : 0);
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] << immediate;
break;
case FUNC3_SRLI_SRAI:
// Sign-extend immediate in rs2 from 5 bits to 32 bits
immediate = (instruction->rs2 & 0x1F) | (instruction->rs2 & 0x10 ? 0xFFFFFFE0 : 0);
// Analyse func7 to know which is it
switch(instruction->func7)
{
case FUNC7_SRLI:
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] >> immediate;
break;
case FUNC7_SRAI:
// Arithmetic slide
cpu->regs.x[instruction->rd] = ((int32_t) cpu->regs.x[instruction->rs1]) >> immediate;
break;
default:
fprintf(stderr, "FATAL: Unknown func7 0x%x for arithlog immediate SRLI/SRAI instruction, could not execute\n", instruction->func7);
exit(EXIT_FAILURE);
break;
}
break;
default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for arithlog immediate instruction, could not execute\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_ARITHLOG:
{
// Arithmetic and logic instructions
// To find out which operation, we must analyse func3 and func7
switch(instruction->func3)
{
case FUNC3_ADD_SUB:
switch(instruction->func7)
{
case FUNC7_ADD:
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] + cpu->regs.x[instruction->rs2];
break;
case FUNC7_SUB:
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] - cpu->regs.x[instruction->rs2];
break;
default:
fprintf(stderr, "FATAL: Unknown func7 0x%x for arithlog ADD/SUB instruction, could not execute\n", instruction->func7);
exit(EXIT_FAILURE);
break;
}
break;
case FUNC3_SLL:
// Slide Left Logical
uint32_t sll_value = cpu->regs.x[instruction->rs2] & 0x1F;
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] << sll_value;
break;
case FUNC3_SLT:
// Set Less Than
if(((int32_t) cpu->regs.x[instruction->rs1]) < ((int32_t) cpu->regs.x[instruction->rs2]))
cpu->regs.x[instruction->rd] = 1;
else
cpu->regs.x[instruction->rd] = 0;
break;
case FUNC3_SLTIU:
// Set Less Than Unsigned
if(cpu->regs.x[instruction->rs1] < cpu->regs.x[instruction->rs2])
cpu->regs.x[instruction->rd] = 1;
else
cpu->regs.x[instruction->rd] = 0;
break;
case FUNC3_XOR:
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] ^ cpu->regs.x[instruction->rs2];
break;
case FUNC3_OR:
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] | cpu->regs.x[instruction->rs2];
break;
case FUNC3_AND:
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] & cpu->regs.x[instruction->rs2];
break;
case FUNC3_SRL_SRA:
switch(instruction->func7)
{
case FUNC7_SRL:
// Slide Right Logical
uint32_t srl_value = cpu->regs.x[instruction->rs2] & 0x1F;
cpu->regs.x[instruction->rd] = cpu->regs.x[instruction->rs1] >> srl_value;
break;
case FUNC7_SRA:
// Slide Right Arithmetical
uint32_t sra_value = cpu->regs.x[instruction->rs2] & 0x1F;
cpu->regs.x[instruction->rd] = ((int32_t) cpu->regs.x[instruction->rs1]) >> sra_value;
break;
default:
fprintf(stderr, "FATAL: Unknown func7 0x%x for arithlog SRL/SRA instruction, could not execute\n", instruction->func7);
exit(EXIT_FAILURE);
break;
}
break;
default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for arithlog instruction, could not execute\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_NOP:
{
// TODO : Implement PAUSE, FENCE, FENCE.TSO
fprintf(stderr, "Warning: Unsupported NOP instruction\n");
break;
}
case OPCODE_SYSTEM:
{
switch(instruction->func3)
{
case FUNC3_ECALL_EBREAK:
switch(instruction->immediate)
{
case IMM_ECALL:
fprintf(stderr, "ECALL: a7(sbi ext id) = %d, a6(sbi funct id) = %d\n", cpu->regs.a7, cpu->regs.a6);
break;
case IMM_EBREAK:
fprintf(stderr, "EBREAK\n");
break;
default:
fprintf(stderr, "FATAL: Unknown IMM for ECALL/EBREAK instruction while executing (IMM=0x%x)\n", instruction->immediate);
exit(EXIT_FAILURE);
break;
}
break;
case FUNC3_CSRRW:
fprintf(stderr, "CSRRW\n");
break;
case FUNC3_CSRRS:
fprintf(stderr, "CSRRS\n");
break;
case FUNC3_CSRRC:
fprintf(stderr, "CSRRC\n");
break;
case FUNC3_CSRRWI:
fprintf(stderr, "CSRRWI\n");
break;
case FUNC3_CSRRSI:
fprintf(stderr, "CSRRSI\n");
break;
case FUNC3_CSRRCI:
fprintf(stderr, "CSRRCI\n");
break;
default:
fprintf(stderr, "FATAL: Unknown func3 0x%x for SYSTEM instruction while executing\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_ATOMIC:
{
// Atomic instructions (A extension)
// To find out which operation, we must analyse func7 (func3 == 0x2)
if(instruction->func3 != FUNC3_ATOMIC)
{
fprintf(stderr, "FATAL: Unknown func3 0x%x for ATOMIC instruction, could not execute\n", instruction->func3);
exit(EXIT_FAILURE);
}
// FUNC7 contains 2 flag bits in lower part ; ignore them, we look for func7_5
uint32_t address = cpu->regs.x[instruction->rs1];
uint32_t* ptr = ((uint32_t*) &memory[mmu_translate(address)]);
switch(instruction->func7 >> 2)
{
case FUNC75_LRW:
// Load-Reserved Word
cpu->regs.x[instruction->rd] = *ptr;
// TODO register reservation set that subsumes the bytes in word
fprintf(stderr, "LR.W\n");
break;
case FUNC75_SCW:
// Store-Conditional Word
// TODO succeed only if the reservation is still valid and the reservation set contains the bytes written
*ptr = cpu->regs.x[instruction->rs2];
cpu->regs.x[instruction->rd] = 0; // TODO write 1 in rd on failure
fprintf(stderr, "SC.W\n");
break;
case FUNC75_AMOSWAPW:
// Atomic Memory Operation SWAP Word
cpu->regs.x[instruction->rd] = *ptr;
// Put in RS1 addr the value of RS2
*ptr = cpu->regs.x[instruction->rs2];
// Put in RS2 the value of RS1 addr (which is in RD)
cpu->regs.x[instruction->rs2] = cpu->regs.x[instruction->rd];
break;
case FUNC75_AMOADDW:
// Atomic Memory Operation ADD Word
cpu->regs.x[instruction->rd] = *ptr;
// Add rs1 addr and value of rs2
*ptr = cpu->regs.x[instruction->rd] + cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOXORW:
// Atomic Memory Operation XOR Word
cpu->regs.x[instruction->rd] = *ptr;
// Xor rs1 addr and value of rs2
*ptr = cpu->regs.x[instruction->rd] ^ cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOANDW:
// Atomic Memory Operation AND Word
cpu->regs.x[instruction->rd] = *ptr;
// AND rs1 addr and value of rs2
*ptr = cpu->regs.x[instruction->rd] & cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOORW:
// Atomic Memory Operation OR Word
cpu->regs.x[instruction->rd] = *ptr;
// Or rs1 addr and value of rs2
*ptr = cpu->regs.x[instruction->rd] | cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOMINW:
// Atomic Memory Operation MIN Word
cpu->regs.x[instruction->rd] = *ptr;
// Min rs1 addr and value of rs2
*ptr = ((int32_t) cpu->regs.x[instruction->rd]) < ((int32_t) cpu->regs.x[instruction->rs2]) ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOMAXW:
// Atomic Memory Operation MAX Word
cpu->regs.x[instruction->rd] = *ptr;
// Max rs1 addr and value of rs2
*ptr = ((int32_t) cpu->regs.x[instruction->rd]) > ((int32_t) cpu->regs.x[instruction->rs2]) ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOMINUW:
// Atomic Memory Operation MIN Unsigned Word
cpu->regs.x[instruction->rd] = *ptr;
// Min rs1 addr and value of rs2
*ptr = cpu->regs.x[instruction->rd] < cpu->regs.x[instruction->rs2] ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2];
break;
case FUNC75_AMOMAXUW:
// Atomic Memory Operation MAX Unsigned Word
cpu->regs.x[instruction->rd] = *ptr;
// Max rs1 addr and value of rs2
*ptr = cpu->regs.x[instruction->rd] > cpu->regs.x[instruction->rs2] ? cpu->regs.x[instruction->rd] : cpu->regs.x[instruction->rs2];
break;
default:
fprintf(stderr, "FATAL: Unknown func7 0x%x for ATOMIC/0x2 instruction, could not execute\n", instruction->func7);
exit(EXIT_FAILURE);
break;
}
break;
}
default:
fprintf(stderr, "FATAL: Unknown instruction opcode 0x%x while executing; how could this decode ?\n", instruction->opcode);
exit(EXIT_FAILURE);
break;
}
}
void cpu_loop(rv32_cpu_t* cpu)
{
while(1)
{
// Fetch
raw_instruction_t raw_instruction;
if(cpu->pc > memory_size - 4)
{
fprintf(stderr, "Error: instruction fetch: pc is out of addressable memory\n");
exit(EXIT_FAILURE);
}
raw_instruction.data = *((uint32_t*) (&memory[cpu->pc]));
// Decode
instruction_t instruction;
cpu_decode(raw_instruction, &instruction);
printf("0x%x: ", cpu->pc);
cpu_print_instruction(&instruction);
// Execute
cpu_execute(cpu, &instruction);
// Reset value of zero, in case instruction tried to modify
cpu->regs.zero = 0;
cpu->pc += 4;
}
}
static void cpu_print_instruction(instruction_t* instruction)
{
switch(instruction->opcode)
{
case OPCODE_LUI:
{
// Load Upper Immediate (load immediate(31:12 bits) in rd)
printf("lui x%u, 0x%x\n", instruction->rd, instruction->immediate);
break;
}
case OPCODE_AUIPC:
{
// Add Upper Immediate to PC
printf("auipc x%u, 0x%x\n", instruction->rd, instruction->immediate);
break;
}
case OPCODE_JAL:
{
// Jump And Link
// Sign extend immediate from 21 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0x1FFFFF) | (instruction->immediate & 0x100000 ? 0xFFE00000 : 0);
printf("jal x%u, 0x%x\n", instruction->rd, immediate);
break;
}
case OPCODE_JALR:
{
// Jump And Link Register
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
printf("jalr x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
}
case OPCODE_BRANCH:
{
// Branches ; to know which one, we must analyse func3
// Sign extend immediate from 13 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x1000 ? 0xFFFFE000 : 0);
immediate -= 4;
switch(instruction->func3)
{
case FUNC3_BEQ:
// Branch EQual
printf("beq x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
case FUNC3_BNE:
// Branch Not Equal
printf("bne x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
case FUNC3_BLT:
// Branch Less Than
printf("blt x%u, x%u, %d\n", instruction->rs1, instruction->rs2, (int32_t) immediate);
break;
case FUNC3_BLTU:
// Branch Less Than Unsigned
printf("bltu x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
case FUNC3_BGE:
// Branch Greater Equal
printf("bge x%u, x%u, %d\n", instruction->rs1, instruction->rs2, (int32_t) immediate);
break;
case FUNC3_BGEU:
// Branch Greater Equal Unsigned
printf("bgeu x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
default:
fprintf(stderr, "Warning: Unknown func3 0x%x for branch instruction\n", instruction->func3);
break;
}
break;
}
case OPCODE_LOAD:
{
// Loads ; to know which one, we must analyse func3
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
switch(instruction->func3)
{
case FUNC3_LB:
printf("lb x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_LH:
// Load Halfword (16-bits)
printf("lh x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_LW:
// Load Word (32-bits)
printf("lw x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_LBU:
// Load Byte Unsigned (8-bits)
printf("lbu x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_LHU:
// Load Halfword Unsigned (16-bits)
printf("lhu x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
default:
fprintf(stderr, "Warning: Unknown func3 0x%x for load instruction\n", instruction->func3);
break;
}
break;
}
case OPCODE_STORE:
{
// Store ; to know which one, we must analyse func3
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
switch(instruction->func3)
{
case FUNC3_SB:
// Store Byte (8-bits)
printf("sb x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
case FUNC3_SH:
// Store Halfword (16-bits)
printf("sh x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
case FUNC3_SW:
// Store Word (32-bits)
printf("sw x%u, x%u, 0x%x\n", instruction->rs1, instruction->rs2, immediate);
break;
default:
fprintf(stderr, "Warning: Unknown func3 0x%x for store instruction\n", instruction->func3);
break;
}
break;
}
case OPCODE_ARITHLOG_IMM:
{
// Arithmetic and logic instructions on immediate values
// To find out which operation, we must analyse func3
// Sign extend immediate from 12 bits to 32 bits
uint32_t immediate = (instruction->immediate & 0xFFF) | (instruction->immediate & 0x800 ? 0xFFFFF000 : 0);
switch(instruction->func3)
{
case FUNC3_ADDI:
// ADD Immediate
printf("addi x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_SLTI:
// Set Less Than Immediate
printf("slti x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_SLTIU:
printf("sltiu x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_XORI:
// XOR Immediate
printf("xori x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_ORI:
// OR Immediate
printf("ori x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_ANDI:
// AND Immediate
printf("andi x%u, x%u, 0x%x\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_SLLI:
// Sign-extend immediate in rs2 from 5 bits to 32 bits
immediate = (instruction->rs2 & 0x1F) | (instruction->rs2 & 0x10 ? 0xFFFFFFE0 : 0);
printf("slli x%u, x%u, %u\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC3_SRLI_SRAI:
// Sign-extend immediate in rs2 from 5 bits to 32 bits
immediate = (instruction->rs2 & 0x1F) | (instruction->rs2 & 0x10 ? 0xFFFFFFE0 : 0);
// Analyse func7 to know which is it
switch(instruction->func7)
{
case FUNC7_SRLI:
printf("srli x%u, x%u, %u\n", instruction->rd, instruction->rs1, immediate);
break;
case FUNC7_SRAI:
// Arithmetic slide
printf("srai x%u, x%u, %u\n", instruction->rd, instruction->rs1, immediate);
break;
default:
fprintf(stderr, "Warning: Unknown func7 0x%x for arithlog immediate SRLI/SRAI instruction\n", instruction->func7);
break;
}
break;
default:
fprintf(stderr, "Warning: Unknown func3 0x%x for arithlog immediate instruction, could not execute\n", instruction->func3);
break;
}
break;
}
case OPCODE_ARITHLOG:
{
// Arithmetic and logic instructions
// To find out which operation, we must analyse func3 and func7
switch(instruction->func3)
{
case FUNC3_ADD_SUB:
switch(instruction->func7)
{
case FUNC7_ADD:
printf("add x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC7_SUB:
printf("sub x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
default:
fprintf(stderr, "Warning: Unknown func7 0x%x for arithlog ADD/SUB instruction, could not execute\n", instruction->func7);
break;
}
break;
case FUNC3_SLL:
// Slide Left Logical
printf("sll x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC3_SLT:
// Set Less Than
printf("slt x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC3_SLTIU:
// Set Less Than Unsigned
printf("sltu x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC3_XOR:
printf("xor x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC3_OR:
printf("or x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC3_AND:
printf("and x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC3_SRL_SRA:
switch(instruction->func7)
{
case FUNC7_SRL:
// Slide Right Logical
printf("srl x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC7_SRA:
// Slide Right Arithmetical
printf("sra x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
default:
fprintf(stderr, "Warning: Unknown func7 0x%x for arithlog SRL/SRA instruction\n", instruction->func7);
exit(EXIT_FAILURE);
break;
}
break;
default:
fprintf(stderr, "Warning: Unknown func3 0x%x for arithlog instruction\n", instruction->func3);
exit(EXIT_FAILURE);
break;
}
break;
}
case OPCODE_NOP:
{
// TODO : Implement PAUSE, FENCE, FENCE.TSO
fprintf(stderr, "Warning: Unsupported NOP instruction\n");
break;
}
case OPCODE_SYSTEM:
{
switch(instruction->func3)
{
case FUNC3_ECALL_EBREAK:
switch(instruction->immediate)
{
case IMM_ECALL:
printf("ecall\n");
break;
case IMM_EBREAK:
printf("ebreak\n");
break;
default:
fprintf(stderr, "Warning: Unknown IMM for ECALL/EBREAK instruction (IMM=0x%x)\n", instruction->immediate);
break;
}
break;
case FUNC3_CSRRW:
printf("csrrw csr[0x%x], x%u, x%u\n", instruction->immediate, instruction->rd, instruction->rs1);
break;
case FUNC3_CSRRS:
printf("csrrs csr[0x%x], x%u, x%u\n", instruction->immediate, instruction->rd, instruction->rs1);
break;
case FUNC3_CSRRC:
printf("csrrc csr[0x%x], x%u, x%u\n", instruction->immediate, instruction->rd, instruction->rs1);
break;
case FUNC3_CSRRWI:
printf("csrrwi csr[0x%x], x%u, 0x%x\n", instruction->immediate, instruction->rd, instruction->rs1);
break;
case FUNC3_CSRRSI:
printf("csrrsi csr[0x%x], x%u, 0x%x\n", instruction->immediate, instruction->rd, instruction->rs1);
break;
case FUNC3_CSRRCI:
printf("csrrci csr[0x%x], x%u, 0x%x\n", instruction->immediate, instruction->rd, instruction->rs1);
break;
default:
fprintf(stderr, "Warning: Unknown func3 0x%x for SYSTEM instruction\n", instruction->func3);
break;
}
break;
}
case OPCODE_ATOMIC:
{
// Atomic instructions (A extension)
// To find out which operation, we must analyse func7 (func3 == 0x2)
if(instruction->func3 != FUNC3_ATOMIC)
{
fprintf(stderr, "Warning: Unknown func3 0x%x for ATOMIC instruction\n", instruction->func3);
break;
}
// FUNC7 contains 2 flag bits in lower part ; ignore them, we look for func7_5
switch(instruction->func7 >> 2)
{
case FUNC75_LRW:
// Load-Reserved Word
printf("lr.w x%u, x%u\n", instruction->rd, instruction->rs1);
break;
case FUNC75_SCW:
// Store-Conditional Word
printf("sc.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOSWAPW:
// Atomic Memory Operation SWAP Word
printf("amoswap.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOADDW:
// Atomic Memory Operation ADD Word
printf("amoadd.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOXORW:
// Atomic Memory Operation XOR Word
printf("amoxor.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOANDW:
// Atomic Memory Operation AND Word
printf("amoand.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOORW:
// Atomic Memory Operation OR Word
printf("amoor.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOMINW:
// Atomic Memory Operation MIN Word
printf("amomin.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOMAXW:
// Atomic Memory Operation MAX Word
printf("amomax.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOMINUW:
// Atomic Memory Operation MIN Unsigned Word
printf("amominu.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
case FUNC75_AMOMAXUW:
// Atomic Memory Operation MAX Unsigned Word
printf("amomaxu.w x%u, x%u, x%u\n", instruction->rd, instruction->rs1, instruction->rs2);
break;
default:
fprintf(stderr, "FATAL: Unknown func7 0x%x for ATOMIC/0x2 instruction, could not execute\n", instruction->func7);
exit(EXIT_FAILURE);
break;
}
break;
}
default:
fprintf(stderr, "Warning: Unknown instruction opcode 0x%x while printing\n", instruction->opcode);
break;
}
}