aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--include/gran/component.h68
-rw-r--r--include/gran/mem/simple_mem.h1
-rw-r--r--include/gran/packet.h38
-rw-r--r--include/gran/snoop.h20
-rw-r--r--src/components/bus/simple_bus.c64
-rw-r--r--src/components/cpu/riscv/simple_riscv32.c522
-rw-r--r--src/components/cpu/riscv/source.mk1
-rw-r--r--src/components/cpu/source.mk1
-rw-r--r--src/components/mem/simple_mem.c26
-rw-r--r--src/packet.c75
-rw-r--r--src/snoop.c39
-rw-r--r--tests/simple_mem/bus.c3
-rw-r--r--tests/simple_mem/traffic_gen.c39
-rw-r--r--tests/simple_riscv32/s.asm16
-rw-r--r--tests/simple_riscv32/sim.c35
-rw-r--r--tests/simple_riscv32/source.mk6
17 files changed, 878 insertions, 80 deletions
diff --git a/Makefile b/Makefile
index 2db9806..d46978d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
DO != echo -n > deps.mk
-DEBUGFLAGS != [ $(RELEASE) ] && echo "-flto=auto -O2 -g -DNODEBUG" || echo "-O0 -g -DDEBUG"
-CFLAGS = -Wall -Wextra -fopenmp
+DEBUGFLAGS != [ $(RELEASE) ] && echo "-flto=auto -O2 -DNODEBUG" || echo "-O0 -g -DDEBUG"
+CFLAGS = -Wall -Wextra -fopenmp -g
DEPFLAGS = -MT $@ -MMD -MP -MF $@.d
LINTFLAGS = -fsyntax-only
INCLUDEFLAGS = -Iinclude
diff --git a/include/gran/component.h b/include/gran/component.h
index 15dc875..4ef94ad 100644
--- a/include/gran/component.h
+++ b/include/gran/component.h
@@ -6,20 +6,25 @@
#include <stdint.h>
#include <stdio.h>
+#include <assert.h>
#include <stdlib.h>
#include <inttypes.h>
#include <gran/common.h>
+#include <gran/packet.h>
+#include <gran/snoop.h>
struct component;
-typedef stat (*write_callback)(struct component *, uintptr_t, size_t, void *);
-typedef stat (*read_callback)(struct component *, uintptr_t, size_t, void *);
-typedef stat (*swap_callback)(struct component *, uintptr_t, size_t, void *,
- size_t, void *);
+typedef stat (*read_callback)(struct component *, struct packet *pkt);
+typedef stat (*write_callback)(struct component *, struct packet *pkt);
+typedef stat (*swap_callback)(struct component *, struct packet *pkt);
+
+typedef stat (*snoop_callback)(struct component *, struct snoop *snoop);
+typedef stat (*ctrl_callback)(struct component *, struct packet *pkt);
+
typedef stat (*irq_callback)(struct component *, int);
typedef stat (*clock_callback)(struct component *);
-
typedef stat (*stat_callback)(struct component *, FILE *);
typedef stat (*dts_callback)(struct component *, FILE *);
@@ -29,10 +34,13 @@ struct component {
/* optional */
char *name;
- write_callback write;
read_callback read;
+ write_callback write;
swap_callback swap;
+ snoop_callback snoop;
+ ctrl_callback ctrl;
+
irq_callback irq;
clock_callback clock;
@@ -42,69 +50,57 @@ struct component {
destroy_callback destroy;
};
-static inline stat write(struct component *component, uintptr_t addr,
- size_t size, void *buf)
+static inline stat write(struct component *component, struct packet *pkt)
{
+ assert(packet_type(pkt) == PACKET_WRITE);
if (!component->write) {
+ /* printf formatted asserts would maybe be preferable? */
error(
"tried writing %p:%" PRIuPTR " but it doesn't support writing",
- component->name, addr);
+ component->name, packet_addr(pkt));
return ENOSUCH;
}
- return component->write(component, addr, size, buf);
+ packet_set_state(pkt, PACKET_SENT);
+ return component->write(component, pkt);
}
-static inline stat read(struct component *component, uintptr_t addr,
- size_t size, void *buf)
+static inline stat read(struct component *component, struct packet *pkt)
{
+ assert(packet_type(pkt) == PACKET_READ);
if (!component->read) {
error(
"tried reading %p:%" PRIuPTR " but it doesn't support reading",
- component->name, addr);
+ component->name, packet_addr(pkt));
return ENOSUCH;
}
- return component->read(component, addr, size, buf);
+ packet_set_state(pkt, PACKET_SENT);
+ return component->read(component, pkt);
}
-static inline stat swap(struct component *component, uintptr_t addr,
- size_t wsize, void *wbuf, size_t rsize, void *rbuf)
+static inline stat swap(struct component *component, struct packet *pkt)
{
+ assert(packet_type(pkt) == PACKET_SWAP);
if (!component->swap) {
error(
"tried swapping %p:%" PRIuPTR " but it doesn't support swapping",
- component->name, addr);
+ component->name, packet_addr(pkt));
return ENOSUCH;
}
- return component->swap(component, addr, wsize, wbuf, rsize, rbuf);
+ packet_set_state(pkt, PACKET_SENT);
+ return component->swap(component, pkt);
}
static inline void destroy(struct component *component)
{
+ free(component->name);
+
if (component->destroy)
component->destroy(component);
else
free(component);
}
-static inline stat write_u8(struct component *component, uintptr_t addr,
- uint8_t c)
-{
- return write(component, addr, sizeof(uint8_t), &c);
-}
-
-static inline stat read_u8(struct component *component, uintptr_t addr,
- uint8_t *c)
-{
- return read(component, addr, sizeof(uint8_t), c);
-}
-
-static inline stat swap_u8(struct component *component, uintptr_t addr,
- uint8_t *c)
-{
- return swap(component, addr, sizeof(uint8_t), c, sizeof(uint8_t), c);
-}
-
#endif /* GRAN_COMPONENT_H */
diff --git a/include/gran/mem/simple_mem.h b/include/gran/mem/simple_mem.h
index 8d6ce75..23bcacb 100644
--- a/include/gran/mem/simple_mem.h
+++ b/include/gran/mem/simple_mem.h
@@ -7,5 +7,6 @@
#include <gran/component.h>
struct component *create_simple_mem(size_t size);
+void init_simple_mem(struct component *mem, uintptr_t addr, size_t size, void *data);
#endif /* GRAN_SIMPLE_MEM_H */
diff --git a/include/gran/packet.h b/include/gran/packet.h
new file mode 100644
index 0000000..3c9dda9
--- /dev/null
+++ b/include/gran/packet.h
@@ -0,0 +1,38 @@
+#ifndef GRAN_PACKET_H
+#define GRAN_PACKET_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+enum packet_state {
+ PACKET_INIT,
+ PACKET_FAILED,
+ PACKET_SENT,
+ PACKET_WAITING,
+ PACKET_DONE,
+};
+
+enum packet_type {
+ PACKET_READ,
+ PACKET_WRITE,
+ PACKET_SWAP,
+ PACKET_SNOOP,
+ PACKET_CTRL,
+};
+
+struct packet;
+
+struct packet *create_packet(enum packet_type type, uintptr_t addr, size_t size);
+struct packet *create_packet_with(enum packet_type type, uintptr_t addr, size_t size, void *data);
+
+enum packet_type packet_type(struct packet *pkt);
+enum packet_state packet_state(struct packet *pkt);
+void packet_set_state(struct packet *pkt, enum packet_state state);
+
+size_t packet_size(struct packet *pkt);
+uintptr_t packet_addr(struct packet *pkt);
+void *packet_data(struct packet *pkt);
+
+void destroy_packet(struct packet *pkt);
+
+#endif /* GRAN_PACKET_H */
diff --git a/include/gran/snoop.h b/include/gran/snoop.h
new file mode 100644
index 0000000..39c0e33
--- /dev/null
+++ b/include/gran/snoop.h
@@ -0,0 +1,20 @@
+#ifndef GRAN_SNOOP_H
+#define GRAN_SNOOP_H
+
+#include <stdint.h>
+
+enum snoop_state {
+ SNOOP_UNANSWERED,
+ SNOOP_ANSWERED,
+};
+
+struct snoop;
+
+struct snoop *create_snoop(uintptr_t addr, size_t size);
+void destroy_snoop(struct snoop *snoop);
+
+enum snoop_state snoop_state(struct snoop *snoop);
+uintptr_t snoop_addr(struct snoop *snoop);
+size_t snoop_size(struct snoop *snoop);
+
+#endif /* GRAN_SNOOP_H */
diff --git a/src/components/bus/simple_bus.c b/src/components/bus/simple_bus.c
index 3629ecd..440ff8a 100644
--- a/src/components/bus/simple_bus.c
+++ b/src/components/bus/simple_bus.c
@@ -64,65 +64,94 @@ static stat add_mem_region(struct simple_bus *bus, struct mem_region *new)
return OK;
}
-static stat simple_bus_write(struct simple_bus *bus, uintptr_t addr,
- size_t size, char *buf)
+static stat simple_bus_write(struct simple_bus *bus, struct packet *pkt)
{
/* only one device can drive the bus at one time */
if (mtx_trylock(&bus->lock) != thrd_success)
return EBUSY;
- struct mem_region *mem_region = find_mem_region(bus, addr);
+ struct mem_region *mem_region = find_mem_region(bus, packet_addr(pkt));
if (!mem_region) {
warn("nothing to write on bus %s at %" PRIuPTR,
- bus->component.name, addr);
+ bus->component.name, packet_addr(pkt));
return EBUS;
}
- stat ret = write(mem_region->component, addr, size, buf);
+ stat ret = write(mem_region->component, pkt);
mtx_unlock(&bus->lock);
return ret;
}
-static stat simple_bus_read(struct simple_bus *bus, uintptr_t addr, size_t size,
- char *buf)
+static stat simple_bus_read(struct simple_bus *bus, struct packet *pkt)
{
/* only one device can drive the bus at one time */
if (mtx_trylock(&bus->lock) != thrd_success)
return EBUSY;
- struct mem_region *mem_region = find_mem_region(bus, addr);
+ struct mem_region *mem_region = find_mem_region(bus, packet_addr(pkt));
if (!mem_region) {
warn("nothing to read on bus %s at %" PRIuPTR,
- bus->component.name, addr);
+ bus->component.name, packet_addr(pkt));
return EBUS;
}
- stat ret = read(mem_region->component, addr, size, buf);
+ stat ret = read(mem_region->component, pkt);
mtx_unlock(&bus->lock);
return ret;
}
-static stat simple_bus_swap(struct simple_bus *bus, uintptr_t addr,
- size_t wsize, char *wbuf, size_t rsize, char *rbuf)
+static stat simple_bus_swap(struct simple_bus *bus, struct packet *pkt)
{
if (mtx_trylock(&bus->lock) != thrd_success)
return EBUSY;
- struct mem_region *mem_region = find_mem_region(bus, addr);
+ struct mem_region *mem_region = find_mem_region(bus, packet_addr(pkt));
if (!mem_region) {
warn("nothing to swap on bus %s at %" PRIuPTR,
- bus->component.name, addr);
+ bus->component.name, packet_addr(pkt));
return EBUS;
}
- stat ret = swap(mem_region->component, addr, wsize, wbuf, rsize, rbuf);
+ stat ret = swap(mem_region->component, pkt);
mtx_unlock(&bus->lock);
return ret;
}
+static stat simple_bus_snoop(struct simple_bus *bus, struct snoop *snoop)
+{
+ struct mem_region *cur = bus->mem_regions;
+ while (cur) {
+ if (cur->component->snoop) {
+ stat ret = cur->component->snoop(cur->component, snoop);
+ if (ret)
+ return ret;
+
+ if (snoop_state(snoop) == SNOOP_ANSWERED)
+ return OK;
+ }
+
+ cur = cur->next;
+ }
+
+ return OK;
+}
+
+static void simple_bus_destroy(struct simple_bus *bus)
+{
+ struct mem_region *cur = bus->mem_regions, *next = NULL;
+ if (cur)
+ do {
+ next = cur->next;
+ destroy(cur->component);
+ free(cur);
+ } while ((cur = next));
+
+ free(bus);
+}
+
struct component *create_simple_bus()
{
struct simple_bus *bus = calloc(1, sizeof(struct simple_bus));
@@ -132,6 +161,11 @@ struct component *create_simple_bus()
bus->component.write = (write_callback)simple_bus_write;
bus->component.read = (read_callback)simple_bus_read;
bus->component.swap = (swap_callback)simple_bus_swap;
+ bus->component.snoop = (snoop_callback)simple_bus_snoop;
+ // actually, still not sure about what API I want to use for controls
+ // bus->component.ctrl = (ctrl_callback)simple_bus_ctrl;
+
+ bus->component.destroy = (destroy_callback)simple_bus_destroy;
mtx_init(&bus->lock, mtx_plain);
return (struct component *)bus;
diff --git a/src/components/cpu/riscv/simple_riscv32.c b/src/components/cpu/riscv/simple_riscv32.c
index 63eba4d..b000bb1 100644
--- a/src/components/cpu/riscv/simple_riscv32.c
+++ b/src/components/cpu/riscv/simple_riscv32.c
@@ -1,19 +1,124 @@
/* SPDX-License-Identifier: copyleft-next-0.3.1 */
/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+#include <byteswap.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
#include <gran/cpu/riscv/simple_riscv32.h>
+struct simple_rv32_ldst {
+ struct packet *pkt;
+ uint32_t reg;
+ bool u;
+};
+
struct simple_riscv32 {
struct component component;
struct component *imem;
struct component *dmem;
+ struct simple_rv32_ldst dls;
+ struct simple_rv32_ldst ils;
+
/* have to be careful with x0 */
uint32_t regs[32];
uint32_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 uint32_t get_reg(struct simple_riscv32 *cpu, size_t i)
{
assert(i < 32);
@@ -34,30 +139,427 @@ static void set_reg(struct simple_riscv32 *cpu, size_t i, uint32_t v)
cpu->regs[i] = v;
}
-static stat simple_riscv32_clock(struct simple_riscv32 *cpu)
+#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_riscv32 *cpu, union rv_insn insn)
{
- uint32_t insn = 0;
- stat ret = read(cpu->imem, cpu->pc, sizeof(insn), &insn);
+ uint32_t dst = 0;
+ uint32_t src = get_reg(cpu, insn.itype.rs1);
+
+ uint32_t imm = EXTEND_IMM12(insn.itype.imm);
+
+ switch (insn.rtype.funct3) {
+ /* ADDI */
+ case 0b000: dst = src + imm; break;
+ /* SLTI */
+ case 0b010: dst = (int32_t)src < (int32_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 = (int32_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_riscv32 *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_riscv32 *cpu, union rv_insn insn)
+{
+ uint32_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_riscv32 *cpu, union rv_insn insn)
+{
+ uint32_t dst = 0;
+ uint32_t src1 = get_reg(cpu, insn.rtype.rs1);
+ uint32_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 = (int32_t)src1 < (int32_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 = (int32_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_riscv32 *cpu, union rv_insn insn)
+{
+ /** @todo generate exception on unaligned jumps */
+ int32_t imm = JTYPE_IMM(insn);
+ set_reg(cpu, insn.jtype.rd, cpu->pc + 4);
+ cpu->pc += imm;
+ return OK;
+}
+
+static stat jalr(struct simple_riscv32 *cpu, union rv_insn insn)
+{
+ int32_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_riscv32 *cpu, union rv_insn insn)
+{
+ uint32_t src1 = get_reg(cpu, insn.btype.rs1);
+ uint32_t src2 = get_reg(cpu, insn.btype.rs2);
+ int32_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 ((int32_t)src1 < (int32_t)src2) {
+ cpu->pc += offset;
+ return OK;
+ }
+ break;
+
+ /* BLTU */
+ case 0b110:
+ if (src1 < src2) {
+ cpu->pc += offset;
+ return OK;
+ }
+ break;
+
+ /* BGE */
+ case 0b101:
+ if ((int32_t)src1 >= (int32_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_riscv32 *cpu, union rv_insn insn)
+{
+ int32_t imm = EXTEND_IMM12(insn.itype.imm);
+ int32_t base = get_reg(cpu, insn.itype.rs1);
+
+ size_t addr = base + imm;
+ size_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 */
+ case 0b010: size = 4; break;
+ default:
+ error("unknown LOAD width %x", insn.btype.funct3);
+ return ENOSUCH;
+ }
+
+ struct packet *pkt = create_packet(PACKET_READ, addr, size);
+ if (!pkt)
+ return EMEM;
+
+ cpu->dls = (struct simple_rv32_ldst){pkt, insn.itype.rd, u};
+ stat ret = read(cpu->dmem, pkt);
if (ret)
return ret;
- /* @todo instruction decode and execution, not sure if this is too early
- * to start thinking about how to modularise stuff */
+ cpu->pc += 4;
return OK;
}
-struct componen *create_simple_riscv32(uint32_t start_pc,
- struct component *imem,
- struct component *dmem)
+#define STYPE_IMM(insn) \
+ EXTEND_IMM12((insn.stype.imm1 << 5) \
+ | (insn.stype.imm0))
+
+static stat store(struct simple_riscv32 *cpu, union rv_insn insn)
+{
+ int32_t imm = STYPE_IMM(insn);
+ int32_t base = get_reg(cpu, insn.stype.rs1);
+ size_t addr = base + imm;
+
+ uint32_t src = get_reg(cpu, insn.stype.rs2);
+
+ switch (insn.stype.funct3) {
+ /* SB */
+ case 0b000: {
+ cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 1);
+ *(uint8_t *)packet_data(cpu->dls.pkt) = src;
+ break;
+ }
+ /* SH */
+ case 0b001: {
+ cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 2);
+ *(uint16_t *)packet_data(cpu->dls.pkt) = src;
+ break;
+ }
+ /* SW */
+ case 0b010: {
+ cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 4);
+ *(uint32_t *)packet_data(cpu->dls.pkt) = src;
+ break;
+ }
+ default:
+ error("unknown width of STORE %x", insn.stype.funct3);
+ return ENOSUCH;
+ }
+
+ cpu->pc += 4;
+ return write(cpu->dmem, cpu->dls.pkt);
+}
+
+static void finalize_ld(struct simple_riscv32 *cpu)
+{
+ struct simple_rv32_ldst ld = cpu->dls;
+
+ uint32_t val = 0;
+ void *data = packet_data(ld.pkt);
+ switch (packet_size(ld.pkt)) {
+ case 1:
+ if (ld.u) val = *(uint8_t *)data;
+ else val = *(int8_t *)data;
+ break;
+
+ case 2: if (ld.u) val = *(uint16_t *)data;
+ else val = *(int16_t *)data;
+ break;
+
+ case 4: val = *(uint32_t *)data;
+ break;
+
+ default:
+ error("unknown load size %zu", packet_size(ld.pkt));
+ }
+
+ set_reg(cpu, ld.reg, val);
+}
+
+static void finalize_st(struct simple_riscv32 *cpu)
+{
+ (void)cpu;
+ /* nothing really to do, this is here mostly for vibe */
+}
+
+static void finalize_dls(struct simple_riscv32 *cpu)
+{
+ struct packet *pkt = cpu->dls.pkt;
+
+ if (packet_type(pkt) == PACKET_READ)
+ finalize_ld(cpu);
+ else if (packet_type(pkt) == PACKET_WRITE)
+ finalize_st(cpu);
+ else
+ error("unsupported packet type for simple_riscv32");
+
+ destroy_packet(pkt);
+ cpu->dls.pkt = NULL;
+}
+
+static uint32_t finalize_ils(struct simple_riscv32 *cpu)
+{
+ uint32_t insn = *(uint32_t *)packet_data(cpu->ils.pkt);
+ destroy_packet(cpu->ils.pkt);
+ cpu->ils.pkt = NULL;
+ return insn;
+}
+
+static stat simple_riscv32_clock(struct simple_riscv32 *cpu)
+{
+ /* there's an active data transfer we should handle */
+ if (cpu->dls.pkt) {
+ assert(packet_state(cpu->dls.pkt) != PACKET_FAILED);
+
+ if (packet_state(cpu->dls.pkt) == PACKET_DONE)
+ finalize_dls(cpu);
+ else /* wait for data */
+ return OK;
+ }
+
+ if (!cpu->ils.pkt) {
+ cpu->ils.pkt = create_packet(PACKET_READ, cpu->pc, sizeof(uint32_t));
+ if (!cpu->ils.pkt)
+ return EMEM;
+
+ stat ret = read(cpu->imem, cpu->ils.pkt);
+ if (ret)
+ return ret;
+ }
+
+ uint32_t insn = 0;
+ if (cpu->ils.pkt) {
+ assert(packet_state(cpu->ils.pkt) != PACKET_FAILED);
+
+ if (packet_state(cpu->ils.pkt) == PACKET_DONE)
+ insn = finalize_ils(cpu);
+ else /* wait for instruction */
+ return OK;
+ }
+
+ // 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 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_riscv32_destroy(struct simple_riscv32 *cpu)
+{
+ /* oh yeah, will have to think about the name stuff,
+ * i.e. how and where to free it, and where to assign it */
+ destroy(cpu->imem);
+
+ if (cpu->imem != cpu->dmem)
+ destroy(cpu->dmem);
+
+ if (cpu->ils.pkt)
+ destroy_packet(cpu->ils.pkt);
+
+ if (cpu->dls.pkt)
+ destroy_packet(cpu->dls.pkt);
+
+ free(cpu);
+}
+
+struct component *create_simple_riscv32(uint32_t start_pc,
+ struct component *imem,
+ struct component *dmem)
{
- struct component *new = calloc(1, sizeof(simple_riscv32));
+ struct simple_riscv32 *new = calloc(1, sizeof(struct simple_riscv32));
if (!new)
return NULL;
new->component.clock = (clock_callback)simple_riscv32_clock;
+ new->component.destroy = (destroy_callback)simple_riscv32_destroy;
new->pc = start_pc;
new->imem = imem;
new->dmem = dmem;
- return new;
+ return (struct component *)new;
}
diff --git a/src/components/cpu/riscv/source.mk b/src/components/cpu/riscv/source.mk
new file mode 100644
index 0000000..ea41f62
--- /dev/null
+++ b/src/components/cpu/riscv/source.mk
@@ -0,0 +1 @@
+SOURCES += src/components/cpu/riscv/simple_riscv32.c
diff --git a/src/components/cpu/source.mk b/src/components/cpu/source.mk
new file mode 100644
index 0000000..ef808f6
--- /dev/null
+++ b/src/components/cpu/source.mk
@@ -0,0 +1 @@
+include src/components/cpu/*/source.mk
diff --git a/src/components/mem/simple_mem.c b/src/components/mem/simple_mem.c
index db2632a..80bddf3 100644
--- a/src/components/mem/simple_mem.c
+++ b/src/components/mem/simple_mem.c
@@ -12,29 +12,29 @@ struct simple_mem {
char buf[];
};
-static stat simple_mem_write(struct simple_mem *mem, uintptr_t addr,
- size_t size, char *buf)
+static stat simple_mem_write(struct simple_mem *mem, struct packet *pkt)
{
- uintptr_t offset = addr % mem->size;
- if (offset + size > mem->size) {
+ uintptr_t offset = packet_addr(pkt) % mem->size;
+ if (offset + packet_size(pkt) > mem->size) {
error("write outside memory");
return ESIZE;
}
- memcpy(mem->buf + offset, buf, size);
+ memcpy(mem->buf + offset, packet_data(pkt), packet_size(pkt));
+ packet_set_state(pkt, PACKET_DONE);
return OK;
}
-static stat simple_mem_read(struct simple_mem *mem, uintptr_t addr, size_t size,
- char *buf)
+static stat simple_mem_read(struct simple_mem *mem, struct packet *pkt)
{
- uintptr_t offset = addr % mem->size;
- if (offset + size > mem->size) {
+ uintptr_t offset = packet_addr(pkt) % mem->size;
+ if (offset + packet_size(pkt) > mem->size) {
error("read outside memory");
return ESIZE;
}
- memcpy(buf, mem->buf + offset, size);
+ memcpy(packet_data(pkt), mem->buf + offset, packet_size(pkt));
+ packet_set_state(pkt, PACKET_DONE);
return OK;
}
@@ -49,3 +49,9 @@ struct component *create_simple_mem(size_t size)
new->component.read = (read_callback)simple_mem_read;
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/packet.c b/src/packet.c
new file mode 100644
index 0000000..3582020
--- /dev/null
+++ b/src/packet.c
@@ -0,0 +1,75 @@
+#include <gran/packet.h>
+#include <stdlib.h>
+
+struct packet {
+ enum packet_state state;
+ enum packet_type type;
+
+ uintptr_t addr;
+ size_t size;
+ void *data;
+};
+
+
+struct packet *create_packet(enum packet_type type, uintptr_t addr, size_t size)
+{
+ struct packet *pkt = calloc(1, sizeof(struct packet) + size);
+ if (!pkt)
+ return NULL;
+
+ pkt->state = PACKET_INIT;
+ pkt->type = type;
+ pkt->addr = addr;
+ pkt->size = size;
+ pkt->data = pkt + 1;
+ return pkt;
+}
+
+struct packet *create_packet_with(enum packet_type type, uintptr_t addr, size_t size, void *data)
+{
+ struct packet *pkt = calloc(1, sizeof(struct packet));
+ if (!pkt)
+ return NULL;
+
+ pkt->state = PACKET_INIT;
+ pkt->type = type;
+ pkt->addr = addr;
+ pkt->size = size;
+ pkt->data = data;
+ return pkt;
+}
+
+enum packet_type packet_type(struct packet *pkt)
+{
+ return pkt->type;
+}
+
+enum packet_state packet_state(struct packet *pkt)
+{
+ return pkt->state;
+}
+
+void packet_set_state(struct packet *pkt, enum packet_state state)
+{
+ pkt->state = state;
+}
+
+size_t packet_size(struct packet *pkt)
+{
+ return pkt->size;
+}
+
+uintptr_t packet_addr(struct packet *pkt)
+{
+ return pkt->addr;
+}
+
+void *packet_data(struct packet *pkt)
+{
+ return pkt->data;
+}
+
+void destroy_packet(struct packet *pkt)
+{
+ free(pkt);
+}
diff --git a/src/snoop.c b/src/snoop.c
new file mode 100644
index 0000000..0caae8e
--- /dev/null
+++ b/src/snoop.c
@@ -0,0 +1,39 @@
+#include <stdlib.h>
+
+#include <gran/snoop.h>
+
+struct snoop {
+ enum snoop_state state;
+ uintptr_t addr;
+ size_t size;
+};
+
+struct snoop *create_snoop(uintptr_t addr, size_t size)
+{
+ struct snoop *snoop = calloc(1, sizeof(struct snoop));
+
+ snoop->state = SNOOP_UNANSWERED;
+ snoop->addr = addr;
+ snoop->size = size;
+ return snoop;
+}
+
+enum snoop_state snoop_state(struct snoop *snoop)
+{
+ return snoop->state;
+}
+
+uintptr_t snoop_addr(struct snoop *snoop)
+{
+ return snoop->addr;
+}
+
+size_t snoop_size(struct snoop *snoop)
+{
+ return snoop->size;
+}
+
+destroy_snoop(struct snoop *snoop)
+{
+ free(snoop);
+}
diff --git a/tests/simple_mem/bus.c b/tests/simple_mem/bus.c
index 9fa7a47..4a6be40 100644
--- a/tests/simple_mem/bus.c
+++ b/tests/simple_mem/bus.c
@@ -18,8 +18,7 @@ int main()
struct component *simple_bus = create_simple_bus();
simple_bus_add(simple_bus, simple_mem, addr, size);
- struct component *traffic_gen = create_traffic_gen(simple_mem, addr,
- size);
+ struct component *traffic_gen = create_traffic_gen(simple_bus, addr, size);
struct clock_domain *clk = create_clock_domain(NS(1));
clock_domain_add(clk, traffic_gen);
diff --git a/tests/simple_mem/traffic_gen.c b/tests/simple_mem/traffic_gen.c
index 2de8f6e..192e2c4 100644
--- a/tests/simple_mem/traffic_gen.c
+++ b/tests/simple_mem/traffic_gen.c
@@ -11,6 +11,9 @@
struct traffic_gen {
struct component component;
struct component *stress;
+
+ struct packet *pkt;
+
uintptr_t addr;
uintptr_t end;
@@ -23,16 +26,39 @@ static stat traffic_gen_clock(struct traffic_gen *tg)
return DONE;
uint8_t c = 0;
+ if (tg->pkt) {
+ assert(packet_state(tg->pkt) != PACKET_FAILED);
+ /* wait for packet */
+ if (packet_state(tg->pkt) != PACKET_DONE)
+ return OK;
+ }
+
switch (tg->counter) {
case 0:
- assert(write_u8(tg->stress, tg->addr, 13) == OK);
- tg->counter = 1;
+ c = 13;
+ tg->pkt = create_packet(PACKET_WRITE, tg->addr, sizeof(c));
+ *(uint8_t *)packet_data(tg->pkt) = c;
+ assert(write(tg->stress, tg->pkt) == OK);
+ tg->counter++;
break;
case 1:
- c = 0;
- assert(read_u8(tg->stress, tg->addr, &c) == OK);
- assert(c == 13);
+ /* destroy write packet now that the write is done */
+ destroy_packet(tg->pkt);
+ tg->pkt = NULL;
+ tg->counter++;
+ break;
+
+ case 2:
+ tg->pkt = create_packet(PACKET_READ, tg->addr, sizeof(c));
+ assert(read(tg->stress, tg->pkt) == OK);
+ tg->counter++;
+ break;
+
+ case 3:
+ c = *(uint8_t *)packet_data(tg->pkt);
+ destroy_packet(tg->pkt);
+ tg->pkt = NULL;
tg->counter = 0;
tg->addr++;
break;
@@ -43,6 +69,9 @@ static stat traffic_gen_clock(struct traffic_gen *tg)
void traffic_gen_destroy(struct traffic_gen *tg)
{
+ if (tg->pkt)
+ destroy_packet(tg->pkt);
+
destroy(tg->stress);
free(tg);
}
diff --git a/tests/simple_riscv32/s.asm b/tests/simple_riscv32/s.asm
new file mode 100644
index 0000000..d423dd9
--- /dev/null
+++ b/tests/simple_riscv32/s.asm
@@ -0,0 +1,16 @@
+.global _start
+_start:
+/* sum from 0 to 1000000 */
+
+li a0, 0 /* index */
+li a1, 1000000 /* top */
+li a2, 0 /* sum */
+
+top:
+beq a0, a1, done
+add a2, a2, a0
+addi a0, a0, 1
+j top
+
+done:
+ebreak
diff --git a/tests/simple_riscv32/sim.c b/tests/simple_riscv32/sim.c
new file mode 100644
index 0000000..df2127d
--- /dev/null
+++ b/tests/simple_riscv32/sim.c
@@ -0,0 +1,35 @@
+#include <assert.h>
+
+#include <gran/root.h>
+#include <gran/mem/simple_mem.h>
+#include <gran/cpu/riscv/simple_riscv32.h>
+
+unsigned char simple_sum[] = {
+ 0x13, 0x05, 0x00, 0x00, 0xb7, 0x45, 0x0f, 0x00, 0x93, 0x85, 0x05, 0x24,
+ 0x13, 0x06, 0x00, 0x00, 0x63, 0x08, 0xb5, 0x00, 0x33, 0x06, 0xa6, 0x00,
+ 0x13, 0x05, 0x15, 0x00, 0x6f, 0xf0, 0x5f, 0xff, 0x73, 0x00, 0x10, 0x00
+};
+
+unsigned int simple_sum_len = 36;
+
+int main()
+{
+ const size_t size = 100000;
+ struct component *imem = create_simple_mem(size);
+
+ /* This works for simple memory, but feels kind of hacky */
+ init_simple_mem(imem, 0, simple_sum_len, simple_sum);
+
+ struct component *dmem = create_simple_mem(size);
+ struct component *rv32 = create_simple_riscv32(0, imem, dmem);
+
+ struct clock_domain *clk = create_clock_domain(NS(1));
+ clock_domain_add(clk, rv32);
+
+ struct gran_root *root = create_root();
+ root_add_clock(root, clk);
+
+ assert(root_run(root) == OK);
+
+ destroy_root(root);
+}
diff --git a/tests/simple_riscv32/source.mk b/tests/simple_riscv32/source.mk
new file mode 100644
index 0000000..736c111
--- /dev/null
+++ b/tests/simple_riscv32/source.mk
@@ -0,0 +1,6 @@
+RV32_TEST_OBJ != ./scripts/gen-deps --sources "tests/simple_riscv32/sim.c"
+
+TEST_PROGS += build/tests/simple_riscv32/sim
+
+build/tests/simple_riscv32/sim: $(RV32_TEST_OBJ) $(TEST_OBJS) $(OBJS)
+ $(COMPILE) $(RV32_TEST_OBJ) $(TEST_OBJS) $(OBJS) -o $@