From 6d0e7c5eba49efc171e63ff2631722a5c7f95460 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 25 Sep 2024 21:00:16 +0300 Subject: technically speaking deadlock free, but starves --- src/bus/simple_bus.c | 117 ++++++ src/bus/source.mk | 1 + src/components/bus/simple_bus.c | 117 ------ src/components/bus/source.mk | 1 - src/components/cpu/riscv/simple_riscv64.c | 661 ----------------------------- src/components/cpu/riscv/source.mk | 1 - src/components/cpu/source.mk | 1 - src/components/grid/node.c | 133 ------ src/components/grid/router.c | 136 ------ src/components/grid/source.mk | 1 - src/components/mem/simple_mem.c | 77 ---- src/components/mem/source.mk | 1 - src/components/source.mk | 1 - src/components/uart/simple_uart.c | 54 --- src/components/uart/source.mk | 1 - src/cpu/riscv/simple_riscv64.c | 669 ++++++++++++++++++++++++++++++ src/cpu/riscv/source.mk | 1 + src/cpu/source.mk | 1 + src/grid/node.c | 145 +++++++ src/grid/source.mk | 1 + src/mem/simple_mem.c | 78 ++++ src/mem/source.mk | 1 + src/source.mk | 8 +- src/uart/simple_uart.c | 55 +++ src/uart/source.mk | 1 + 25 files changed, 1073 insertions(+), 1190 deletions(-) create mode 100644 src/bus/simple_bus.c create mode 100644 src/bus/source.mk delete mode 100644 src/components/bus/simple_bus.c delete mode 100644 src/components/bus/source.mk delete mode 100644 src/components/cpu/riscv/simple_riscv64.c delete mode 100644 src/components/cpu/riscv/source.mk delete mode 100644 src/components/cpu/source.mk delete mode 100644 src/components/grid/node.c delete mode 100644 src/components/grid/router.c delete mode 100644 src/components/grid/source.mk delete mode 100644 src/components/mem/simple_mem.c delete mode 100644 src/components/mem/source.mk delete mode 100644 src/components/source.mk delete mode 100644 src/components/uart/simple_uart.c delete mode 100644 src/components/uart/source.mk create mode 100644 src/cpu/riscv/simple_riscv64.c create mode 100644 src/cpu/riscv/source.mk create mode 100644 src/cpu/source.mk create mode 100644 src/grid/node.c create mode 100644 src/grid/source.mk create mode 100644 src/mem/simple_mem.c create mode 100644 src/mem/source.mk create mode 100644 src/uart/simple_uart.c create mode 100644 src/uart/source.mk (limited to 'src') diff --git a/src/bus/simple_bus.c b/src/bus/simple_bus.c new file mode 100644 index 0000000..1932419 --- /dev/null +++ b/src/bus/simple_bus.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: copyleft-next-0.3.1 */ +/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ +#include + +#include +#include + +struct bus_region { + uint64_t addr; + uint64_t size; + struct component *component; +}; + +struct simple_bus { + struct component component; + struct vec regions; + + struct component *send; + struct packet pkt; + bool busy; +}; + +static struct bus_region *find_bus_region(struct simple_bus *bus, uint64_t addr) +{ + foreach_vec(i, bus->regions) { + struct bus_region *r = vec_at(&bus->regions, i); + if (addr >= r->addr && addr < (r->addr + r->size)) + return r; + } + + return NULL; +} + +static stat simple_bus_clock(struct simple_bus *bus) +{ + if (bus->busy) { + stat r = SEND(bus, bus->send, bus->pkt); + if (r == EBUSY) + return OK; + + bus->busy = false; + return r; + } + + return OK; +} + +static stat simple_bus_receive(struct simple_bus *bus, struct component *from, struct packet pkt) +{ + if (bus->busy) + return EBUSY; + + bus->busy = true; + + struct bus_region *region = find_bus_region(bus, pkt.to); + if (!region) { + warn("illegal address on bus %s at %" PRIuPTR, + bus->component.name, pkt.to); + + bus->send = from; + bus->pkt = response(pkt); + set_flags(&bus->pkt, PACKET_ERROR); + return OK; + } + + bus->send = region->component; + bus->pkt = pkt; + return OK; +} + +static void simple_bus_destroy(struct simple_bus *bus) +{ + vec_destroy(&bus->regions); + free(bus); +} + +struct component *create_simple_bus() +{ + struct simple_bus *bus = calloc(1, sizeof(struct simple_bus)); + if (!bus) + return NULL; + + bus->component.receive = (receive_callback)simple_bus_receive; + bus->component.clock = (clock_callback)simple_bus_clock; + + bus->component.destroy = (destroy_callback)simple_bus_destroy; + bus->regions = vec_create(sizeof(struct bus_region)); + + return (struct component *)bus; +} + +stat simple_bus_add(struct component *bus, struct component *component, + uint64_t addr, uint64_t size) +{ + struct simple_bus *b = (struct simple_bus *)bus; + struct bus_region *found = find_bus_region(b, addr); + if (!found) found = find_bus_region(b, addr + size); + + if (found) { + error("%s overlaps with %s at %" PRIuPTR, + found->component->name, + component->name, + found->addr + ); + + return EEXISTS; + } + + struct bus_region new = (struct bus_region){ + .component = component, + .addr = addr, + .size = size + }; + + vect_append(struct bus_region, b->regions, &new); + return OK; +} diff --git a/src/bus/source.mk b/src/bus/source.mk new file mode 100644 index 0000000..e812785 --- /dev/null +++ b/src/bus/source.mk @@ -0,0 +1 @@ +SOURCES += src/bus/simple_bus.c diff --git a/src/components/bus/simple_bus.c b/src/components/bus/simple_bus.c deleted file mode 100644 index 1932419..0000000 --- a/src/components/bus/simple_bus.c +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ -#include - -#include -#include - -struct bus_region { - uint64_t addr; - uint64_t size; - struct component *component; -}; - -struct simple_bus { - struct component component; - struct vec regions; - - struct component *send; - struct packet pkt; - bool busy; -}; - -static struct bus_region *find_bus_region(struct simple_bus *bus, uint64_t addr) -{ - foreach_vec(i, bus->regions) { - struct bus_region *r = vec_at(&bus->regions, i); - if (addr >= r->addr && addr < (r->addr + r->size)) - return r; - } - - return NULL; -} - -static stat simple_bus_clock(struct simple_bus *bus) -{ - if (bus->busy) { - stat r = SEND(bus, bus->send, bus->pkt); - if (r == EBUSY) - return OK; - - bus->busy = false; - return r; - } - - return OK; -} - -static stat simple_bus_receive(struct simple_bus *bus, struct component *from, struct packet pkt) -{ - if (bus->busy) - return EBUSY; - - bus->busy = true; - - struct bus_region *region = find_bus_region(bus, pkt.to); - if (!region) { - warn("illegal address on bus %s at %" PRIuPTR, - bus->component.name, pkt.to); - - bus->send = from; - bus->pkt = response(pkt); - set_flags(&bus->pkt, PACKET_ERROR); - return OK; - } - - bus->send = region->component; - bus->pkt = pkt; - return OK; -} - -static void simple_bus_destroy(struct simple_bus *bus) -{ - vec_destroy(&bus->regions); - free(bus); -} - -struct component *create_simple_bus() -{ - struct simple_bus *bus = calloc(1, sizeof(struct simple_bus)); - if (!bus) - return NULL; - - bus->component.receive = (receive_callback)simple_bus_receive; - bus->component.clock = (clock_callback)simple_bus_clock; - - bus->component.destroy = (destroy_callback)simple_bus_destroy; - bus->regions = vec_create(sizeof(struct bus_region)); - - return (struct component *)bus; -} - -stat simple_bus_add(struct component *bus, struct component *component, - uint64_t addr, uint64_t size) -{ - struct simple_bus *b = (struct simple_bus *)bus; - struct bus_region *found = find_bus_region(b, addr); - if (!found) found = find_bus_region(b, addr + size); - - if (found) { - error("%s overlaps with %s at %" PRIuPTR, - found->component->name, - component->name, - found->addr - ); - - return EEXISTS; - } - - struct bus_region new = (struct bus_region){ - .component = component, - .addr = addr, - .size = size - }; - - vect_append(struct bus_region, b->regions, &new); - return OK; -} diff --git a/src/components/bus/source.mk b/src/components/bus/source.mk deleted file mode 100644 index 98a10b6..0000000 --- a/src/components/bus/source.mk +++ /dev/null @@ -1 +0,0 @@ -SOURCES += src/components/bus/simple_bus.c 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 -#include -#include -#include -#include - -#include - -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 diff --git a/src/components/cpu/source.mk b/src/components/cpu/source.mk deleted file mode 100644 index ef808f6..0000000 --- a/src/components/cpu/source.mk +++ /dev/null @@ -1 +0,0 @@ -include src/components/cpu/*/source.mk diff --git a/src/components/grid/node.c b/src/components/grid/node.c deleted file mode 100644 index 57ef7ed..0000000 --- a/src/components/grid/node.c +++ /dev/null @@ -1,133 +0,0 @@ -/* very simple grid node with 32bit private region, does not currently signal - * being busy or anything. I think I might have to refine the message passing - * interface I have, but this is good enough. - * - * Each node should have a router beneath it, just to simplify my life. A router - * is basically a bus with a fallback ascension path. - */ -#include - -#include - -struct grid_node { - struct component component; - uint8_t u, v, x, y; - struct component *left, *right, *up, *down, *ascend, *lower; - - struct component *send; - struct packet pkt; - bool busy; -}; - -static stat grid_clock(struct grid_node *grid) -{ - if (!grid->busy) - return OK; - - stat r = SEND(grid, grid->send, grid->pkt); - if (r == EBUSY) - return OK; - - grid->busy = false; - return r; -} - -static stat grid_receive(struct grid_node *grid, struct component *from, struct packet pkt) -{ - if (grid->busy) - return EBUSY; - - uint8_t u; - uint8_t v; - uint8_t x; - uint8_t y; - uint64_t addr = pkt.to; - addr_grid(addr, NULL, &x, &y, &u, &v); - - grid->busy = true; - grid->pkt = pkt; - - if (grid->u == u && grid->v == v && grid->x == x && grid->y == y) { - if (!grid->lower) - goto nosuch; - - grid->send = grid->lower; - return OK; - } - - if (grid->u != u || grid->v != v) { - if (!grid->ascend) - goto nosuch; - - grid->send = grid->ascend; - return OK; - } - - if (y < grid->y) { - if (!grid->down) - goto nosuch; - - grid->send = grid->down; - return OK; - } - - if (y > grid->y) { - if (!grid->up) - goto nosuch; - - grid->send = grid->up; - return OK; - } - - if (x < grid->x) { - if (!grid->left) - goto nosuch; - - grid->send = grid->left; - return OK; - } - - if (x > grid->x) { - if (!grid->right) - return EBUS; - - grid->send = grid->right; - return OK; - } - -nosuch: - grid->send = from; - grid->pkt = response(pkt); - set_flags(&grid->pkt, PACKET_ERROR); - return OK; -} - -struct component *create_grid_node(uint8_t u, uint8_t v, uint8_t x, uint8_t y) -{ - struct grid_node *node = calloc(1, sizeof(struct grid_node)); - if (!node) - return NULL; - - node->component.receive = (receive_callback)grid_receive; - node->component.clock = (clock_callback)grid_clock; - node->u = u; - node->v = v; - node->x = x; - node->y = y; - return (struct component *)node; -} - -stat grid_node_connect(struct component *node, - struct component *left, struct component *right, - struct component *up, struct component *down, - struct component *lower, struct component *ascend) -{ - struct grid_node *n = (struct grid_node *)node; - n->left = left; - n->right = right; - n->up = up; - n->down = down; - n->lower = lower; - n->ascend = ascend; - return OK; -} diff --git a/src/components/grid/router.c b/src/components/grid/router.c deleted file mode 100644 index 63c4011..0000000 --- a/src/components/grid/router.c +++ /dev/null @@ -1,136 +0,0 @@ -#include - -#include -#include -#include - -struct router_region { - uint32_t addr; - uint32_t size; - struct component *component; -}; - -struct node_router { - struct component component; - struct component *ascend; - struct vec regions; - uint8_t u, v, x, y; - - struct component *send; - struct packet pkt; - bool busy; -}; - -static struct router_region *find_region(struct node_router *router, uint32_t addr) -{ - for (size_t i = 0; i < vec_len(&router->regions); ++i) { - struct router_region *region = vec_at(&router->regions, i); - - if (addr >= region->addr && addr < region->addr + region->size) - return region; - } - - return NULL; -} - -static stat router_clock(struct node_router *router) -{ - if (!router->busy) - return OK; - - stat r = SEND(router, router->send, router->pkt); - if (r == EBUSY) - return OK; - - router->busy = false; - return OK; -} - -static stat router_receive(struct node_router *router, struct component *from, struct packet pkt) -{ - if (router->busy) - return EBUSY; - - router->busy = true; - - uint64_t addr = pkt.to; - uint8_t u; - uint8_t v; - uint8_t x; - uint8_t y; - addr_grid(addr, NULL, &x, &y, &u, &v); - - router->pkt = pkt; - - if (router->u != u || router->v != v || router->x != x || router->y != y) { - if (!router->ascend) - goto nosuch; - - router->send = router->ascend; - return OK; - } - - struct router_region *region = find_region(router, addr); - if (!region) - goto nosuch; - - router->send = region->component; - return OK; - -nosuch: - router->send = from; - router->pkt = response(pkt); - set_flags(&router->pkt, PACKET_ERROR); - return OK; -} - -struct component *create_node_router(uint8_t u, uint8_t v, uint8_t x, uint8_t y) -{ - struct node_router *router = calloc(1, sizeof(struct node_router)); - if (!router) - return NULL; - - router->u = u; - router->v = v; - router->x = x; - router->y = y; - - router->component.receive = (receive_callback)router_receive; - router->component.clock = (clock_callback)router_clock; - router->regions = vec_create(sizeof(struct router_region)); - - return (struct component *)router; -} - -stat node_router_add(struct component *router, struct component *component, uint32_t addr, uint32_t size) -{ - struct node_router *nr = (struct node_router *)router; - struct router_region *found = find_region(nr, addr); - if (!found) found = find_region(nr, addr + size); - - if (found) { - error("%s overlaps with %s at %x", - found->component->name, - component->name, - found->addr - ); - - return EEXISTS; - } - - struct router_region r = (struct router_region){ - .addr = addr, - .size = size, - .component = component - }; - vect_append(struct node_region, nr->regions, &r); - - return OK; -} - -stat node_router_ascend(struct component *router, struct component *node) -{ - struct node_router *nr = (struct node_router *)router; - nr->ascend = node; - return OK; -} diff --git a/src/components/grid/source.mk b/src/components/grid/source.mk deleted file mode 100644 index 4fb44ec..0000000 --- a/src/components/grid/source.mk +++ /dev/null @@ -1 +0,0 @@ -SOURCES += src/components/grid/node.c src/components/grid/router.c diff --git a/src/components/mem/simple_mem.c b/src/components/mem/simple_mem.c deleted file mode 100644 index e435ea5..0000000 --- a/src/components/mem/simple_mem.c +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -#include -#include -#include - -#include - -struct simple_mem { - struct component component; - struct component *send; - struct packet pkt; - bool busy; - - size_t size; - uint8_t buf[]; -}; - -static stat simple_mem_clock(struct simple_mem *mem) -{ - if (mem->busy) - return OK; - - stat r = SEND(mem, mem->send, mem->pkt); - if (r == EBUSY) - return OK; - - mem->busy = false; - return OK; -} - -static stat simple_mem_receive(struct simple_mem *mem, struct component *from, struct packet pkt) -{ - if (mem->busy) - return EBUSY; - - mem->send = from; - - uint64_t offset = pkt.to % mem->size; - if (offset >= mem->size) { - error("read outside memory"); - mem->pkt = response(pkt); - set_flags(&mem->pkt, PACKET_ERROR); - return OK; - } - - if (is_set(&pkt, PACKET_READ)) - checked_copyto(&pkt, mem->buf + offset); - else if (is_set(&pkt, PACKET_WRITE)) - checked_copyfrom(&pkt, mem->buf + offset); - else - abort(); - - mem->pkt = response(pkt); - set_flags(&mem->pkt, PACKET_DONE); - return OK; -} - -struct component *create_simple_mem(size_t size) -{ - struct simple_mem *new = calloc(1, sizeof(struct simple_mem) + size); - if (!new) - return NULL; - - new->size = size; - new->component.receive = (receive_callback)simple_mem_receive; - new->component.clock = (clock_callback)simple_mem_clock; - return (struct component *)new; -} - -void init_simple_mem(struct component *m, uintptr_t addr, size_t size, - void *data) -{ - struct simple_mem *mem = (struct simple_mem *)m; - memcpy(mem->buf + addr, data, size); -} diff --git a/src/components/mem/source.mk b/src/components/mem/source.mk deleted file mode 100644 index 9c339ba..0000000 --- a/src/components/mem/source.mk +++ /dev/null @@ -1 +0,0 @@ -SOURCES += src/components/mem/simple_mem.c diff --git a/src/components/source.mk b/src/components/source.mk deleted file mode 100644 index c45f391..0000000 --- a/src/components/source.mk +++ /dev/null @@ -1 +0,0 @@ -include src/components/*/source.mk diff --git a/src/components/uart/simple_uart.c b/src/components/uart/simple_uart.c deleted file mode 100644 index 1528a70..0000000 --- a/src/components/uart/simple_uart.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include - -struct simple_uart { - struct component component; - - struct component *send; - struct packet pkt; - bool busy; -}; - -static stat simple_uart_clock(struct simple_uart *uart) -{ - if (!uart->busy) - return OK; - - stat r = SEND(uart, uart->send, uart->pkt); - if (r == EBUSY) - return OK; - - uart->busy = false; - return OK; -} - -static stat simple_uart_receive(struct simple_uart *uart, struct component *from, struct packet pkt) -{ - if (uart->busy) - return EBUSY; - - uart->busy = true; - uart->send = from; - uart->pkt = response(pkt); - - size_t size = packet_convsize(&pkt); - if (size != 1) { - set_flags(&uart->pkt, PACKET_ERROR); - return OK; - } - - putchar(packet_convu8(&pkt)); - set_flags(&uart->pkt, PACKET_DONE); - return OK; -} - -struct component *create_simple_uart() -{ - struct simple_uart *uart = calloc(1, sizeof(struct simple_uart)); - if (!uart) - return NULL; - - uart->component.receive = (receive_callback)simple_uart_receive; - uart->component.clock = (clock_callback)simple_uart_clock; - return (struct component *)uart; -} diff --git a/src/components/uart/source.mk b/src/components/uart/source.mk deleted file mode 100644 index af26289..0000000 --- a/src/components/uart/source.mk +++ /dev/null @@ -1 +0,0 @@ -SOURCES += src/components/uart/simple_uart.c diff --git a/src/cpu/riscv/simple_riscv64.c b/src/cpu/riscv/simple_riscv64.c new file mode 100644 index 0000000..1c4e810 --- /dev/null +++ b/src/cpu/riscv/simple_riscv64.c @@ -0,0 +1,669 @@ +/* SPDX-License-Identifier: copyleft-next-0.3.1 */ +/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ + +#include +#include +#include +#include +#include + +#include + +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) & 0b111111) + +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) +{ + stat ret = OK; + + /* 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) + goto send; + + if (cpu->ils.state == LDST_SENT) + return OK; + + /* this effectively encodes a NOP */ + uint32_t insn = OP_IMM; + + if (cpu->ils.state == LDST_DONE) { + assert(!is_set(&cpu->dls.pkt, PACKET_ERROR)); + + insn = finalize_ils(cpu); + cpu->ils.state = LDST_IDLE; + } + + // for now assume little endian emulated and host cpu + union rv_insn i = {.val = insn}; + + // 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; + } + + 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_BLOCKED; + } + +send: + if (cpu->ils.state == LDST_BLOCKED) { + stat ret = SEND(cpu, cpu->imem, cpu->ils.pkt); + if (ret == EBUSY) { + cpu->ils.state = LDST_BLOCKED; + return ret; + } + + cpu->ils.state = LDST_SENT; + } + + return ret; +} + +struct component *create_simple_riscv64(uint64_t rcv, uint64_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->pc = start_pc; + new->rcv = rcv; + new->imem = imem; + new->dmem = dmem; + + /* fetch new instruction at start */ + new->ils.state = LDST_BLOCKED; + new->ils.pkt = create_packet(new->rcv + 64, + new->pc, + sizeof(uint32_t), + NULL, + PACKET_READ); + + 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/cpu/riscv/source.mk b/src/cpu/riscv/source.mk new file mode 100644 index 0000000..aa11801 --- /dev/null +++ b/src/cpu/riscv/source.mk @@ -0,0 +1 @@ +SOURCES += src/cpu/riscv/simple_riscv64.c diff --git a/src/cpu/source.mk b/src/cpu/source.mk new file mode 100644 index 0000000..47dcc3e --- /dev/null +++ b/src/cpu/source.mk @@ -0,0 +1 @@ +include src/cpu/*/source.mk diff --git a/src/grid/node.c b/src/grid/node.c new file mode 100644 index 0000000..16cce1b --- /dev/null +++ b/src/grid/node.c @@ -0,0 +1,145 @@ +/* very simple grid node with 32bit private region, does not currently signal + * being busy or anything. I think I might have to refine the message passing + * interface I have, but this is good enough. + * + * Each node should have a router beneath it, just to simplify my life. A router + * is basically a bus with a fallback ascension path. + */ +#include + +#include + +struct port { + struct component *send; + struct packet pkt; + bool busy; +}; + +struct grid_node { + struct component component; + uint16_t x, y; + + struct port left, right, up, down, lower; +}; + +static void port_clock(struct grid_node *grid, struct port *port) +{ + if (!port->busy) + return; + + stat r = SEND(grid, port->send, port->pkt); + if (r == EBUSY) + return; + + port->busy = false; +} + +static stat grid_clock(struct grid_node *grid) +{ + port_clock(grid, &grid->left); + port_clock(grid, &grid->right); + port_clock(grid, &grid->up); + port_clock(grid, &grid->down); + port_clock(grid, &grid->lower); + return OK; +} + +static stat port_receive(struct port *port, struct packet pkt) +{ + if (port->busy) + return EBUSY; + + port->pkt = pkt; + port->busy = true; + return OK; +} + +static stat grid_receive(struct grid_node *grid, struct component *from, struct packet pkt) +{ + uint16_t x; + uint16_t y; + uint64_t addr = pkt.to; + addr_grid(addr, NULL, &x, &y); + + if (grid->x == x && grid->y == y) { + if (!grid->lower.send) + goto nosuch; + + return port_receive(&grid->lower, pkt); + } + + if (y < grid->y) { + if (!grid->down.send) + goto nosuch; + + return port_receive(&grid->down, pkt); + } + + if (y > grid->y) { + if (!grid->up.send) + goto nosuch; + + return port_receive(&grid->up, pkt); + } + + if (x < grid->x) { + if (!grid->left.send) + goto nosuch; + + return port_receive(&grid->left, pkt); + } + + if (x > grid->x) { + if (!grid->right.send) + goto nosuch; + + return port_receive(&grid->right, pkt); + } + +nosuch: + set_flags(&pkt, PACKET_ERROR); + if (from == grid->lower.send) + return port_receive(&grid->lower, pkt); + + if (from == grid->left.send) + return port_receive(&grid->left, pkt); + + if (from == grid->right.send) + return port_receive(&grid->right, pkt); + + if (from == grid->down.send) + return port_receive(&grid->down, pkt); + + if (from == grid->up.send) + return port_receive(&grid->up, pkt); + + abort(); + return OK; +} + +struct component *create_grid_node(uint16_t x, uint16_t y) +{ + struct grid_node *node = calloc(1, sizeof(struct grid_node)); + if (!node) + return NULL; + + node->component.receive = (receive_callback)grid_receive; + node->component.clock = (clock_callback)grid_clock; + node->x = x; + node->y = y; + return (struct component *)node; +} + +stat grid_node_connect(struct component *node, + struct component *left, struct component *right, + struct component *up, struct component *down, + struct component *lower) +{ + struct grid_node *n = (struct grid_node *)node; + n->left.send = left; + n->right.send = right; + n->up.send = up; + n->down.send = down; + n->lower.send = lower; + return OK; +} diff --git a/src/grid/source.mk b/src/grid/source.mk new file mode 100644 index 0000000..e23d7db --- /dev/null +++ b/src/grid/source.mk @@ -0,0 +1 @@ +SOURCES += src/grid/node.c diff --git a/src/mem/simple_mem.c b/src/mem/simple_mem.c new file mode 100644 index 0000000..0a3a3ce --- /dev/null +++ b/src/mem/simple_mem.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: copyleft-next-0.3.1 */ +/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ + +#include +#include +#include + +#include + +struct simple_mem { + struct component component; + struct component *send; + struct packet pkt; + bool busy; + + size_t size; + uint8_t buf[]; +}; + +static stat simple_mem_clock(struct simple_mem *mem) +{ + if (!mem->busy) + return OK; + + stat r = SEND(mem, mem->send, mem->pkt); + if (r == EBUSY) + return OK; + + mem->busy = false; + return OK; +} + +static stat simple_mem_receive(struct simple_mem *mem, struct component *from, struct packet pkt) +{ + if (mem->busy) + return EBUSY; + + mem->busy = true; + mem->send = from; + + uint64_t offset = pkt.to % mem->size; + if (offset >= mem->size) { + error("read outside memory"); + mem->pkt = response(pkt); + set_flags(&mem->pkt, PACKET_ERROR); + return OK; + } + + if (is_set(&pkt, PACKET_READ)) + checked_copyto(&pkt, mem->buf + offset); + else if (is_set(&pkt, PACKET_WRITE)) + checked_copyfrom(&pkt, mem->buf + offset); + else + abort(); + + mem->pkt = response(pkt); + set_flags(&mem->pkt, PACKET_DONE); + return OK; +} + +struct component *create_simple_mem(size_t size) +{ + struct simple_mem *new = calloc(1, sizeof(struct simple_mem) + size); + if (!new) + return NULL; + + new->size = size; + new->component.receive = (receive_callback)simple_mem_receive; + new->component.clock = (clock_callback)simple_mem_clock; + return (struct component *)new; +} + +void init_simple_mem(struct component *m, uintptr_t addr, size_t size, + void *data) +{ + struct simple_mem *mem = (struct simple_mem *)m; + memcpy(mem->buf + addr, data, size); +} diff --git a/src/mem/source.mk b/src/mem/source.mk new file mode 100644 index 0000000..7d1a717 --- /dev/null +++ b/src/mem/source.mk @@ -0,0 +1 @@ +SOURCES += src/mem/simple_mem.c diff --git a/src/source.mk b/src/source.mk index db585ee..dd92722 100644 --- a/src/source.mk +++ b/src/source.mk @@ -1,6 +1,4 @@ -include src/components/source.mk +include src/*/source.mk -# everything except main -SRC_LOCAL != echo src/*.c | sed 's|src/main.c||g' -SOURCES += $(SRC_LOCAL) -MAIN_SRC = src/main.c +SOURCES += src/root.c src/clock_domain.c src/vec.c +MAIN_SRC += src/main.c diff --git a/src/uart/simple_uart.c b/src/uart/simple_uart.c new file mode 100644 index 0000000..e640ae1 --- /dev/null +++ b/src/uart/simple_uart.c @@ -0,0 +1,55 @@ +#include +#include + +struct simple_uart { + struct component component; + + struct component *send; + struct packet pkt; + bool busy; +}; + +static stat simple_uart_clock(struct simple_uart *uart) +{ + if (!uart->busy) + return OK; + + stat r = SEND(uart, uart->send, uart->pkt); + if (r == EBUSY) + return OK; + + uart->busy = false; + return OK; +} + +static stat simple_uart_receive(struct simple_uart *uart, struct component *from, struct packet pkt) +{ + if (uart->busy) + return EBUSY; + + uart->busy = true; + uart->send = from; + uart->pkt = response(pkt); + + size_t size = packet_convsize(&pkt); + if (size != 1) { + set_flags(&uart->pkt, PACKET_ERROR); + return OK; + } + + putchar(packet_convu8(&pkt)); + fflush(stdout); + set_flags(&uart->pkt, PACKET_DONE); + return OK; +} + +struct component *create_simple_uart() +{ + struct simple_uart *uart = calloc(1, sizeof(struct simple_uart)); + if (!uart) + return NULL; + + uart->component.receive = (receive_callback)simple_uart_receive; + uart->component.clock = (clock_callback)simple_uart_clock; + return (struct component *)uart; +} diff --git a/src/uart/source.mk b/src/uart/source.mk new file mode 100644 index 0000000..cdc0bc9 --- /dev/null +++ b/src/uart/source.mk @@ -0,0 +1 @@ +SOURCES += src/uart/simple_uart.c -- cgit v1.3