From 24ca9532d4c95a82d42ce901ef44f6f0fea9b84c Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Thu, 5 Oct 2023 20:39:50 +0200 Subject: [PATCH] Added A (Atomic) extension --- src/cpu/instruction.h | 17 +++++ src/cpu/rv32cpu.c | 171 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 180 insertions(+), 8 deletions(-) diff --git a/src/cpu/instruction.h b/src/cpu/instruction.h index 479edb9..47b202f 100644 --- a/src/cpu/instruction.h +++ b/src/cpu/instruction.h @@ -14,6 +14,23 @@ #define OPCODE_NOP 0xF #define OPCODE_SYSTEM 0x73 +/* RISC-V RV32 A Extension */ +#define OPCODE_ATOMIC 0x2F + +/* OPCODE_ATOMIC sub functions (func3 + func7) */ +#define FUNC3_ATOMIC 0x2 +#define FUNC75_LRW 0x2 +#define FUNC75_SCW 0x3 +#define FUNC75_AMOSWAPW 0x1 +#define FUNC75_AMOADDW 0x0 +#define FUNC75_AMOXORW 0x4 +#define FUNC75_AMOANDW 0xC +#define FUNC75_AMOORW 0x8 +#define FUNC75_AMOMINW 0x10 +#define FUNC75_AMOMAXW 0x14 +#define FUNC75_AMOMINUW 0x18 +#define FUNC75_AMOMAXUW 0x1C + /* OPCODE_BRANCH sub functions (func3) */ #define FUNC3_BEQ 0x0 #define FUNC3_BNE 0x1 diff --git a/src/cpu/rv32cpu.c b/src/cpu/rv32cpu.c index ab24826..8b0d116 100644 --- a/src/cpu/rv32cpu.c +++ b/src/cpu/rv32cpu.c @@ -93,6 +93,7 @@ static void cpu_decode(raw_instruction_t raw_instruction, instruction_t* output) break; // R-type instructions case OPCODE_ARITHLOG: + case OPCODE_ATOMIC: break; // S-type instructions case OPCODE_STORE: @@ -118,22 +119,19 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) case OPCODE_LUI: { // Load Upper Immediate (load immediate(31:12 bits) in rd) - if(instruction->rd) - cpu->regs.x[instruction->rd] = instruction->immediate; + cpu->regs.x[instruction->rd] = instruction->immediate; break; } case OPCODE_AUIPC: { // Add Upper Immediate to PC - if(instruction->rd) - cpu->regs.x[instruction->rd] = instruction->immediate + cpu->pc; + cpu->regs.x[instruction->rd] = instruction->immediate + cpu->pc; break; } case OPCODE_JAL: { // Jump And Link - if(instruction->rd) - cpu->regs.x[instruction->rd] = cpu->pc + 4; + 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; @@ -142,8 +140,7 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) case OPCODE_JALR: { // Jump And Link Register - if(instruction->rd) - cpu->regs.x[instruction->rd] = cpu->pc + 4; + 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; @@ -460,6 +457,97 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction) 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); @@ -489,6 +577,9 @@ void cpu_loop(rv32_cpu_t* cpu) // Execute cpu_execute(cpu, &instruction); + + // Reset value of zero, in case instruction tried to modify + cpu->regs.zero = 0; cpu->pc += 4; } @@ -799,6 +890,70 @@ static void cpu_print_instruction(instruction_t* instruction) } 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;