diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-09-25 21:00:16 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-09-25 21:00:16 +0300 |
| commit | 6d0e7c5eba49efc171e63ff2631722a5c7f95460 (patch) | |
| tree | 9159d22c6ee1ee2949dcf8455998405b031cb351 /src/components/cpu/riscv | |
| parent | fecb86f6093c1e8aed6ab05c29c5af9d0cb93157 (diff) | |
| download | gran-6d0e7c5eba49efc171e63ff2631722a5c7f95460.tar.gz gran-6d0e7c5eba49efc171e63ff2631722a5c7f95460.zip | |
technically speaking deadlock free, but starves
Diffstat (limited to 'src/components/cpu/riscv')
| -rw-r--r-- | src/components/cpu/riscv/simple_riscv64.c | 661 | ||||
| -rw-r--r-- | src/components/cpu/riscv/source.mk | 1 |
2 files changed, 0 insertions, 662 deletions
diff --git a/src/components/cpu/riscv/simple_riscv64.c b/src/components/cpu/riscv/simple_riscv64.c deleted file mode 100644 index 987838e..0000000 --- a/src/components/cpu/riscv/simple_riscv64.c +++ /dev/null @@ -1,661 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -#include <byteswap.h> -#include <string.h> -#include <assert.h> -#include <stdbool.h> -#include <stdint.h> - -#include <gran/cpu/riscv/simple_riscv64.h> - -enum ldst_state { - LDST_IDLE, LDST_BLOCKED, LDST_SENT, LDST_DONE -}; - -struct ldst { - struct packet pkt; - enum ldst_state state; - uint32_t reg; - bool u; -}; - -struct simple_riscv64 { - struct component component; - - struct component *imem; - struct component *dmem; - - struct ldst dls; - struct ldst ils; - - uint64_t rcv; - - /* have to be careful with x0 */ - uint64_t regs[32]; - uint64_t pc; -}; - -/* big endian format, going from smallest to highest address. - * Easy to get confused */ -struct rtype { - uint32_t op : 7; - uint32_t rd : 5; - uint32_t funct3 : 3; - uint32_t rs1 : 5; - uint32_t rs2 : 5; - uint32_t funct7 : 7; -}; - -struct itype { - uint32_t op : 7; - uint32_t rd : 5; - uint32_t funct3 : 3; - uint32_t rs1 : 5; - uint32_t imm : 12; -}; - -struct stype { - uint32_t op : 7; - uint32_t imm0 : 5; - uint32_t funct3 : 3; - uint32_t rs1 : 5; - uint32_t rs2 : 5; - uint32_t imm1 : 7; -}; - -struct btype { - uint32_t op : 7; - uint32_t imm0 : 1; - uint32_t imm1 : 4; - uint32_t funct3 : 3; - uint32_t rs1 : 5; - uint32_t rs2 : 5; - uint32_t imm2 : 6; - uint32_t imm3 : 1; -}; - -struct utype { - uint32_t op : 7; - uint32_t rd : 5; - uint32_t imm : 20; -}; - -struct jtype { - uint32_t op : 7; - uint32_t rd : 5; - uint32_t imm0 : 8; - uint32_t imm1 : 1; - uint32_t imm2 : 10; - uint32_t imm3 : 1; -}; - -enum opcode { - LOAD = 0b0000011, - LOAD_FP = 0b0000111, - MISC_MEM = 0b0001111, - OP_IMM = 0b0010011, - AUIPC = 0b0010111, - OP_IMM_32 = 0b0011011, - - STORE = 0b0100011, - STORE_FP = 0b0100111, - AMO = 0b0101111, - OP = 0b0110011, - LUI = 0b0110111, - OP_32 = 0b0111011, - - MADD = 0b1000011, - MSUB = 0b1000111, - NMSUB = 0b1001011, - NMADD = 0b1001111, - OP_FP = 0b1010011, - - BRANCH = 0b1100011, - JALR = 0b1100111, - JAL = 0b1101111, - SYSTEM = 0b1110011, -}; - -union rv_insn { - struct rtype rtype; - struct itype itype; - struct stype stype; - struct btype btype; - struct utype utype; - struct jtype jtype; - uint32_t val; -}; - -static uint64_t get_reg(struct simple_riscv64 *cpu, size_t i) -{ - assert(i < 32); - - if (i == 0) - return 0; - - return cpu->regs[i]; -} - -static void set_reg(struct simple_riscv64 *cpu, size_t i, uint64_t v) -{ - assert(i < 32); - - if (i == 0) - return; - - cpu->regs[i] = v; -} - -#define EXTEND_IMM12(x) ((int32_t)((x) << 20) >> 20) -#define EXTEND_IMM20(x) ((int32_t)((x) << 12) >> 12) -#define SHAMT(x) ((x) & 0b11111) - -static stat op_imm(struct simple_riscv64 *cpu, union rv_insn insn) -{ - uint64_t dst = 0; - uint64_t src = get_reg(cpu, insn.itype.rs1); - - uint64_t imm = EXTEND_IMM12(insn.itype.imm); - - switch (insn.rtype.funct3) { - /* ADDI */ - case 0b000: dst = src + imm; break; - /* SLTI */ - case 0b010: dst = (int64_t)src < (int64_t)imm; break; - /* SLTIU */ - case 0b011: dst = src < imm; break; - /* ANDI */ - case 0b111: dst = src & imm; break; - /* ORI */ - case 0b110: dst = src | imm; break; - /* XORI */ - case 0b100: dst = src ^ imm; break; - /* SLLI */ - case 0b001: dst = src << SHAMT(imm); break; - - /* SRLI / SRAI */ - case 0b101: - if (imm & ~0b11111) /* SRLI */ - dst = src >> SHAMT(imm); - else /* SRAI */ - dst = (int64_t)src >> SHAMT(imm); - break; - - default: - error("unknown OP-IMM instruction: %x", insn.itype.funct3); - return ENOSUCH; - } - - set_reg(cpu, insn.itype.rd, dst); - cpu->pc += 4; - return OK; -} - -static stat op_imm_32(struct simple_riscv64 *cpu, union rv_insn insn) -{ - uint64_t dst = 0; - uint64_t src = get_reg(cpu, insn.itype.rs1); - - uint64_t imm = EXTEND_IMM12(insn.itype.imm); - - switch (insn.rtype.funct3) { - /* ADDIW */ - case 0b000: dst = src + imm; break; - /* SLTI */ - case 0b010: dst = (int64_t)src < (int64_t)imm; break; - /* SLTIU */ - case 0b011: dst = src < imm; break; - /* ANDI */ - case 0b111: dst = src & imm; break; - /* ORI */ - case 0b110: dst = src | imm; break; - /* XORI */ - case 0b100: dst = src ^ imm; break; - /* SLLIW */ - case 0b001: dst = src << SHAMT(imm); break; - - /* SRLIW / SRAIW */ - case 0b101: - if (imm & ~0b11111) /* SRLI */ - dst = src >> SHAMT(imm); - else /* SRAI */ - dst = (int64_t)src >> SHAMT(imm); - break; - - default: - error("unknown OP-IMM instruction: %x", insn.itype.funct3); - return ENOSUCH; - } - - set_reg(cpu, insn.itype.rd, dst); - cpu->pc += 4; - return OK; -} - -static stat lui(struct simple_riscv64 *cpu, union rv_insn insn) -{ - set_reg(cpu, insn.utype.rd, insn.utype.imm << 12); - cpu->pc += 4; - return OK; -} - -static stat auipc(struct simple_riscv64 *cpu, union rv_insn insn) -{ - uint64_t res = cpu->pc + (insn.utype.imm << 12); - set_reg(cpu, insn.utype.rd, res); - cpu->pc += 4; - return OK; -} - -static stat op(struct simple_riscv64 *cpu, union rv_insn insn) -{ - uint64_t dst = 0; - uint64_t src1 = get_reg(cpu, insn.rtype.rs1); - uint64_t src2 = get_reg(cpu, insn.rtype.rs2); - - switch (insn.rtype.funct3) { - /* ADD/SUB */ - case 0b000: - if (insn.rtype.funct7) /* SUB */ - dst = src1 - src2; - else /* ADD */ - dst = src1 + src2; - break; - - /* SLT */ - case 0b010: dst = (int64_t)src1 < (int64_t)src2; break; - /* SLTU */ - case 0b011: dst = src1 < src2; break; - /* AND */ - case 0b111: dst = src1 & src2; break; - /* OR */ - case 0b110: dst = src1 | src2; break; - /* XOR */ - case 0b100: dst = src1 ^ src2; break; - /* SLL */ - case 0b001: dst = src1 << src2; break; - /* SRL/SRA */ - case 0b101: - if (insn.rtype.funct7) /* SRL */ - dst = src1 >> src2; - else /* SRA */ - dst = (int64_t)src1 >> src2; - break; - - default: - error("unknown OP instruction: %x", insn.rtype.funct3); - return ENOSUCH; - } - - set_reg(cpu, insn.rtype.rd, dst); - cpu->pc += 4; - return OK; -} - -#define JTYPE_IMM(insn) \ - EXTEND_IMM20((insn.jtype.imm3 << 20) \ - | (insn.jtype.imm2 << 1) \ - | (insn.jtype.imm1 << 11) \ - | (insn.jtype.imm0 << 12)) - -static stat jal(struct simple_riscv64 *cpu, union rv_insn insn) -{ - /** @todo generate exception on unaligned jumps */ - int64_t imm = JTYPE_IMM(insn); - set_reg(cpu, insn.jtype.rd, cpu->pc + 4); - cpu->pc += imm; - return OK; -} - -static stat jalr(struct simple_riscv64 *cpu, union rv_insn insn) -{ - int64_t src = get_reg(cpu, insn.itype.rs1); - set_reg(cpu, insn.itype.rd, cpu->pc + 4); - cpu->pc += src + EXTEND_IMM12(insn.itype.imm); - return OK; -} - -#define BTYPE_IMM(insn) \ - EXTEND_IMM12((insn.btype.imm3 << 12) \ - | (insn.btype.imm2 << 5) \ - | (insn.btype.imm1 << 1) \ - | (insn.btype.imm0 << 11)) - -static stat branch(struct simple_riscv64 *cpu, union rv_insn insn) -{ - uint64_t src1 = get_reg(cpu, insn.btype.rs1); - uint64_t src2 = get_reg(cpu, insn.btype.rs2); - int64_t offset = BTYPE_IMM(insn); - - switch (insn.btype.funct3) { - /* BEQ */ - case 0b000: - if (src1 == src2) { - cpu->pc += offset; - return OK; - } - break; - - /* BNE */ - case 0b001: - if (src1 != src2) { - cpu->pc += offset; - return OK; - } - break; - - /* BLT */ - case 0b100: - if ((int64_t)src1 < (int64_t)src2) { - cpu->pc += offset; - return OK; - } - break; - - /* BLTU */ - case 0b110: - if (src1 < src2) { - cpu->pc += offset; - return OK; - } - break; - - /* BGE */ - case 0b101: - if ((int64_t)src1 >= (int64_t)src2) { - cpu->pc += offset; - return OK; - } - break; - - /* BGEU */ - case 0b111: - if (src1 >= src2) { - cpu->pc += offset; - return OK; - } - break; - - default: - error("unknown BRANCH instruction %x", insn.btype.funct3); - return ENOSUCH; - } - - cpu->pc += 4; - return OK; -} - -static stat load(struct simple_riscv64 *cpu, union rv_insn insn) -{ - int64_t imm = EXTEND_IMM12(insn.itype.imm); - int64_t base = get_reg(cpu, insn.itype.rs1); - - int64_t addr = base + imm; - int64_t size = 0; - bool u = false; - - // assume little endian for now - switch (insn.itype.funct3) { - /* LB/LBU */ - case 0b100: u = true; /* fallthrough */ - case 0b000: size = 1; break; - /* LH/LHU */ - case 0b101: u = true; /* fallthrough */ - case 0b001: size = 2; break; - /* LW/LWU */ - case 0b110: u = true; /* fallthrough */ - case 0b010: size = 4; break; - /* LD */ - case 0b011: size = 8; break; - default: - error("unknown LOAD width %x", insn.btype.funct3); - return ENOSUCH; - } - - struct packet pkt = create_packet(cpu->rcv, - addr, - size, - NULL, - PACKET_READ); - - cpu->dls = (struct ldst){pkt, LDST_SENT, insn.itype.rd, u}; - stat ret = SEND(cpu, cpu->dmem, pkt); - if (ret == EBUSY) { - cpu->dls.state = LDST_BLOCKED; - ret = OK; - } - - cpu->pc += 4; - return ret; -} - -#define STYPE_IMM(insn) \ - EXTEND_IMM12((insn.stype.imm1 << 5) \ - | (insn.stype.imm0)) - -static stat store(struct simple_riscv64 *cpu, union rv_insn insn) -{ - int64_t imm = STYPE_IMM(insn); - int64_t base = get_reg(cpu, insn.stype.rs1); - int64_t addr = base + imm; - - uint64_t src = get_reg(cpu, insn.stype.rs2); - uint64_t size = 0; - - switch (insn.stype.funct3) { - /* SB */ - case 0b000: size = 1; break; - /* SH */ - case 0b001: size = 2; break; - /* SW */ - case 0b010: size = 4; break; - /* SD */ - case 0b011: size = 8; break; - default: - error("unknown width of STORE %x", insn.stype.funct3); - return ENOSUCH; - } - - struct packet pkt = create_packet(cpu->rcv, addr, size, &src, PACKET_WRITE); - cpu->dls = (struct ldst){pkt, LDST_SENT, 0, false}; - stat ret = SEND(cpu, cpu->dmem, pkt); - if (ret == EBUSY) { - cpu->dls.state = LDST_BLOCKED; - ret = OK; - } - - cpu->pc += 4; - return ret; -} - -static void finalize_ld(struct simple_riscv64 *cpu) -{ - struct ldst ld = cpu->dls; - - uint64_t val = 0; - switch (packet_convsize(&ld.pkt)) { - case 1: - if (ld.u) val = packet_convu8(&ld.pkt); - else val = packet_convi8(&ld.pkt); - break; - - case 2: if (ld.u) val = packet_convu16(&ld.pkt); - else val = packet_convi16(&ld.pkt); - break; - - case 4: if (ld.u) val = packet_convi32(&ld.pkt); - else val = packet_convu32(&ld.pkt); - break; - - case 8: val = packet_convu64(&ld.pkt); - break; - - default: - error("unknown load size %zu", packet_convsize(&ld.pkt)); - } - - set_reg(cpu, ld.reg, val); -} - -static void finalize_st(struct simple_riscv64 *cpu) -{ - (void)cpu; - /* nothing really to do, this is here mostly for vibe */ -} - -static void finalize_dls(struct simple_riscv64 *cpu) -{ - cpu->dls.state = LDST_IDLE; - struct packet pkt = cpu->dls.pkt; - - if (is_set(&pkt, PACKET_READ)) - finalize_ld(cpu); - else if (is_set(&pkt, PACKET_WRITE)) - finalize_st(cpu); - else - error("unsupported packet type for simple_riscv64"); -} - -static uint32_t finalize_ils(struct simple_riscv64 *cpu) -{ - cpu->ils.state = LDST_IDLE; - return packet_convu32(&cpu->ils.pkt); -} - -static stat simple_riscv64_receive(struct simple_riscv64 *cpu, struct component *from, struct packet pkt) -{ - (void)from; - - if (pkt.to == cpu->rcv) { - cpu->dls.pkt = pkt; - cpu->dls.state = LDST_DONE; - return OK; - } - else if (pkt.to == cpu->rcv + 64) { - cpu->ils.pkt = pkt; - cpu->ils.state = LDST_DONE; - return OK; - } - else { - error("illegal receive on %s", cpu->component.name); - return EBUS; - } -} - -static stat simple_riscv64_clock(struct simple_riscv64 *cpu) -{ - /* there's an active data transfer we should handle */ - if (cpu->dls.state != LDST_IDLE) { - assert(!is_set(&cpu->dls.pkt, PACKET_ERROR)); - - if (cpu->dls.state == LDST_BLOCKED) { - stat r = SEND(cpu, cpu->dmem, cpu->dls.pkt); - if (r == EBUSY) - return OK; - - cpu->dls.state = LDST_SENT; - return OK; - } - - if (cpu->dls.state == LDST_SENT) - return OK; - - if (cpu->dls.state == LDST_DONE) { - finalize_dls(cpu); - cpu->dls.state = LDST_IDLE; - } - else - return OK; - } - - if (cpu->ils.state == LDST_BLOCKED) { - stat ret = SEND(cpu, cpu->imem, cpu->ils.pkt); - if (ret == EBUSY) - return OK; - - return OK; - } - - uint32_t insn = 0; - if (cpu->ils.state == LDST_DONE) { - assert(!is_set(&cpu->dls.pkt, PACKET_ERROR)); - - insn = finalize_ils(cpu); - cpu->ils.state = LDST_IDLE; - } - - if (cpu->ils.state == LDST_IDLE) { - cpu->ils.pkt = create_packet(cpu->rcv + 64, - cpu->pc, - sizeof(uint32_t), - NULL, - PACKET_READ); - cpu->ils.state = LDST_SENT; - stat ret = SEND(cpu, cpu->imem, cpu->ils.pkt); - if (ret == EBUSY) { - cpu->ils.state = LDST_BLOCKED; - return ret; - } - } - - // for now assume little endian emulated and host cpu - union rv_insn i = {.val = insn}; - - stat ret = OK; - // all formats have identical opcodes, use whatever - switch (i.rtype.op) { - case OP_IMM: ret = op_imm(cpu, i); break; - case OP_IMM_32: ret = op_imm_32(cpu, i); break; - case LUI: ret = lui(cpu, i); break; - case AUIPC: ret = auipc(cpu, i); break; - case OP: ret = op(cpu, i); break; - case JAL: ret = jal(cpu, i); break; - case JALR: ret = jalr(cpu, i); break; - case BRANCH: ret = branch(cpu, i); break; - case LOAD: ret = load(cpu, i); break; - case STORE: ret = store(cpu, i); break; - case MISC_MEM: /* nop in this case */ break; - case SYSTEM: - /* we don't support these yet, but we can use them - * to stop the simulation */ - return DONE; - default: - error("unknown opcode %x", i.rtype.op); - return ENOSUCH; - } - - return ret; -} - -static void simple_riscv64_destroy(struct simple_riscv64 *cpu) -{ - destroy(cpu->imem); - destroy(cpu->dmem); - free(cpu); -} - -struct component *create_simple_riscv64(uint64_t rcv, uint32_t start_pc, - struct component *imem, - struct component *dmem) -{ - struct simple_riscv64 *new = calloc(1, sizeof(struct simple_riscv64)); - if (!new) - return NULL; - - new->component.receive = (receive_callback)simple_riscv64_receive; - new->component.clock = (clock_callback)simple_riscv64_clock; - new->component.destroy = (destroy_callback)simple_riscv64_destroy; - - new->pc = start_pc; - new->rcv = rcv; - new->imem = imem; - new->dmem = dmem; - return (struct component *)new; -} - -void simple_riscv64_set_reg(struct component *cpu, size_t reg, uint64_t val) -{ - struct simple_riscv64 *rv64 = (struct simple_riscv64 *)cpu; - set_reg(rv64, reg, val); -} diff --git a/src/components/cpu/riscv/source.mk b/src/components/cpu/riscv/source.mk deleted file mode 100644 index da1cef6..0000000 --- a/src/components/cpu/riscv/source.mk +++ /dev/null @@ -1 +0,0 @@ -SOURCES += src/components/cpu/riscv/simple_riscv64.c |
