aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-09-22 22:45:18 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-09-22 22:45:18 +0300
commitb1d67364b4b4832b8efed112f7b0ead1a0b3cd3b (patch)
tree7926bfb428703515438c08b98c61cbffd34b3cbb
parent32f9719e29762001f73460acd31f2a6da8fd6298 (diff)
downloadgran-b1d67364b4b4832b8efed112f7b0ead1a0b3cd3b.tar.gz
gran-b1d67364b4b4832b8efed112f7b0ead1a0b3cd3b.zip
start experimenting with processor grids
-rw-r--r--Makefile2
-rw-r--r--README.md50
-rw-r--r--include/gran/bus/simple_bus.h2
-rw-r--r--include/gran/cpu/riscv/simple_riscv64.h (renamed from include/gran/cpu/riscv/simple_riscv32.h)8
-rw-r--r--include/gran/grid/node.h14
-rw-r--r--include/gran/grid/router.h10
-rw-r--r--include/gran/packet.h2
-rw-r--r--include/gran/uart/simple_uart.h8
-rw-r--r--include/gran/vec.h46
-rw-r--r--src/clock_domain.c27
-rw-r--r--src/components/bus/simple_bus.c2
-rw-r--r--src/components/cpu/riscv/simple_riscv64.c (renamed from src/components/cpu/riscv/simple_riscv32.c)178
-rw-r--r--src/components/cpu/riscv/source.mk2
-rw-r--r--src/components/grid/node.c106
-rw-r--r--src/components/grid/router.c114
-rw-r--r--src/components/grid/source.mk1
-rw-r--r--src/components/uart/simple_uart.c28
-rw-r--r--src/components/uart/source.mk1
-rw-r--r--src/vec.c67
-rw-r--r--tests/simple_grid/sim.c114
-rw-r--r--tests/simple_grid/source.mk6
-rw-r--r--tests/simple_grid/test.c43
-rw-r--r--tests/simple_riscv32/source.mk6
-rw-r--r--tests/simple_riscv64/s.asm (renamed from tests/simple_riscv32/s.asm)0
-rw-r--r--tests/simple_riscv64/sim.c (renamed from tests/simple_riscv32/sim.c)6
-rw-r--r--tests/simple_riscv64/source.mk6
-rw-r--r--tests/simple_uart/hello.c18
-rw-r--r--tests/simple_uart/sim.c45
-rw-r--r--tests/simple_uart/source.mk6
29 files changed, 819 insertions, 99 deletions
diff --git a/Makefile b/Makefile
index 88855fa..108c182 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
DO != echo -n > deps.mk
DEBUGFLAGS != [ $(RELEASE) ] && echo "-flto=auto -O2 -DNODEBUG" || echo "-O0 -g -DDEBUG"
-CFLAGS = -Wall -Wextra -fopenmp -g
+CFLAGS = -Wall -Wextra -g
DEPFLAGS = -MT $@ -MMD -MP -MF $@.d
LINTFLAGS = -fsyntax-only
INCLUDEFLAGS = -Iinclude
diff --git a/README.md b/README.md
index 6bf8707..a50938b 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,53 @@
# gran
-Gran is a toy granular full system simulator,
-taking heavy inspiration from [gem5](https://www.gem5.org/).
+Gran is a toy granular full system simulator, taking heavy inspiration from
+[gem5](https://www.gem5.org/).
-Currently there are a couple example components, including
-`simple_mem` for atomic memory accesses, `simple_bus` for non-coherent
-atomic transfers between components and `simple_riscv32`, a simple
-rv32i processor.
+Currently there are a couple example components, including `simple_mem` for
+atomic memory accesses, `simple_bus` for non-coherent atomic transfers between
+components and `simple_riscv32`, a simple rv32i processor.
# Building
+ `make`
Add `RELEASE=1` to enable optimizations.
+
+# manycore
+
+Idea: Have a grid of x/y width, where each node has three 'axes', i.e. if the
+address matches the node, pass the message to whatever is at that node (think of
+it like a miniature system I suppose?), otherwise check x, send it in that
+direction, otherwise check y, send it in that direction, otherwise check if
+message should be transcended, i.e. sent to some other cluster.
+
+Each address is 64 bits, split in half. The lower 32 bits is the offset within
+the node, and the upper 32 bits are split (again) in twine, something like x1 y1
+x2 y2 where each x or y is eight bits, specifying a node in an x/y grid. x2/y2
+specifies the lower cluster, with x1/y1 upper cluster. The idea is that a
+complete system would consist of cards that contain one cluster, clustered
+together. So a 'supercomputer' could be up to 65536 cards, with each card having
+a cluster of 65536 nodes. Each node is a compute unit, probably a single core,
+possibly with associated memory or devices like UART, whatever.
+
+Currently I'm imagining that each node also has a control region that can be
+written to with commands like 'drop all cache entries that containt address xyz'
+or 'wake up' (to enable userspace mutexes, that would be massively cool)
+
+The current focus would be to enable lightweight userspaces, full-blown
+hardware-assisted virtual memory is costly to implement (as in takes up too much
+space in hardware), but I'd still want to be able to run a command line and
+generic programs on the system. One idea would be a capability-based system,
+sort of like CHERIoT, though it has some drawbacks like having to keep track of
+the valid capabilities in separate memory or as part of ECC.
+
+Another approach that I'm currently thinking of is having a *very* simple
+region-based memory protection scheme, where instead of paging we have a TLB of,
+dunno, 64 regions that the core is allowed to access. If the core tries to
+access a region outside of those regions, the kernel is trapped to and can then
+insert the requested area into the regions or kill the process or whatever. This
+is *similar* to virtual memory in other systems (particularly MIPS), but would
+be range-based rather than page-based. If I wanted to use OpenASIP, I probably
+wouldn't be able to use interrupts, and we would either need a separate
+coprocessor to handle these kinds of exceptions or implement a tree walker in
+hardware (unsure how much extra area it would take)
diff --git a/include/gran/bus/simple_bus.h b/include/gran/bus/simple_bus.h
index 41a8d2e..62dd7d2 100644
--- a/include/gran/bus/simple_bus.h
+++ b/include/gran/bus/simple_bus.h
@@ -8,6 +8,6 @@
struct component *create_simple_bus();
stat simple_bus_add(struct component *bus, struct component *component,
- uintptr_t addr, size_t size);
+ uint64_t addr, uint64_t size);
#endif /* GRAN_SIMPLE_BUS_H */
diff --git a/include/gran/cpu/riscv/simple_riscv32.h b/include/gran/cpu/riscv/simple_riscv64.h
index 3cbd842..8f66013 100644
--- a/include/gran/cpu/riscv/simple_riscv32.h
+++ b/include/gran/cpu/riscv/simple_riscv64.h
@@ -1,13 +1,15 @@
/* SPDX-License-Identifier: copyleft-next-0.3.1 */
/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-#ifndef GRAN_SIMPLE_RISCV32
-#define GRAN_SIMPLE_RISCV32
+#ifndef GRAN_SIMPLE_RISCV64
+#define GRAN_SIMPLE_RISCV64
#include <gran/component.h>
-struct component *create_simple_riscv32(uint32_t start_pc,
+struct component *create_simple_riscv64(uint32_t start_pc,
struct component *imem,
struct component *dmem);
+void simple_riscv64_set_reg(struct component *cpu, size_t reg, uint64_t val);
+
#endif /* GRAN_SIMPLE_RISCV */
diff --git a/include/gran/grid/node.h b/include/gran/grid/node.h
new file mode 100644
index 0000000..637ac91
--- /dev/null
+++ b/include/gran/grid/node.h
@@ -0,0 +1,14 @@
+#ifndef GRAN_GRID_NODE_H
+#define GRAN_GRID_NODE_H
+
+#include <gran/component.h>
+#include <stdint.h>
+
+struct component *create_grid_node(uint8_t u, uint8_t v, uint8_t x, uint8_t y);
+
+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);
+
+#endif /* GRAN_GRID_NODE_H */
diff --git a/include/gran/grid/router.h b/include/gran/grid/router.h
new file mode 100644
index 0000000..cff0fa1
--- /dev/null
+++ b/include/gran/grid/router.h
@@ -0,0 +1,10 @@
+#ifndef GRAN_NODE_ROUTER_H
+#define GRAN_NODE_ROUTER_H
+
+#include <gran/component.h>
+
+struct component *create_node_router(uint8_t u, uint8_t v, uint8_t x, uint8_t y);
+stat node_router_add(struct component *router, struct component *component, uint32_t addr, uint32_t size);
+stat node_router_ascend(struct component *router, struct component *node);
+
+#endif /* GRAN_NODE_ROUTER_H */
diff --git a/include/gran/packet.h b/include/gran/packet.h
index d18dfd6..ab07b56 100644
--- a/include/gran/packet.h
+++ b/include/gran/packet.h
@@ -39,7 +39,7 @@ 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);
+uint64_t packet_addr(struct packet *pkt);
void *packet_data(struct packet *pkt);
void destroy_packet(struct packet *pkt);
diff --git a/include/gran/uart/simple_uart.h b/include/gran/uart/simple_uart.h
new file mode 100644
index 0000000..75906fa
--- /dev/null
+++ b/include/gran/uart/simple_uart.h
@@ -0,0 +1,8 @@
+#ifndef GRAN_SIMPLE_UART_H
+#define GRAN_SIMPLE_UART_H
+
+#include <gran/component.h>
+
+struct component *create_simple_uart();
+
+#endif /* GRAN_SIMPLE_UART_H */
diff --git a/include/gran/vec.h b/include/gran/vec.h
new file mode 100644
index 0000000..08a608a
--- /dev/null
+++ b/include/gran/vec.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+
+#ifndef VEC_H
+#define VEC_H
+
+#include <stddef.h>
+
+struct vec {
+ size_t n;
+ size_t s;
+ size_t ns;
+ void *buf;
+};
+
+struct vec vec_create(size_t s);
+void vec_destroy(struct vec *v);
+void vec_reset(struct vec *v);
+
+size_t vec_len(struct vec *v);
+void *vec_at(struct vec *v, size_t i);
+void *vec_back(struct vec *v);
+void *vec_pop(struct vec *v);
+void vec_append(struct vec *v, void *n);
+
+typedef int (*vec_comp_t)(const void *, const void *);
+void vec_sort(struct vec *v, vec_comp_t comp);
+
+#define foreach_vec(iter, v) \
+ for (size_t iter = 0; iter < vec_len(&v); ++iter)
+
+#define vect_at(type, v, i) \
+ *(type *)vec_at(&v, i)
+
+#define vect_append(type, v, e) \
+ vec_append(&v, (type *)(e))
+
+#define vect_back(type, v) \
+ *(type *)vec_back(&v)
+
+#define vect_pop(type, v) \
+ *(type *)vec_pop(&v)
+
+#define vec_uninit(v) \
+ (v.buf == NULL)
+
+#endif /* VEC_H */
diff --git a/src/clock_domain.c b/src/clock_domain.c
index 65dc72a..6610d8f 100644
--- a/src/clock_domain.c
+++ b/src/clock_domain.c
@@ -12,12 +12,10 @@
#include <assert.h>
#include <gran/clock_domain.h>
-
-#define MAX_COMPONENTS 512
+#include <gran/vec.h>
struct clock_domain {
- size_t num_components;
- struct component *components[MAX_COMPONENTS];
+ struct vec components;
struct clock_time time;
tick period;
@@ -44,21 +42,23 @@ struct clock_domain *create_clock_domain(tick period)
clk->period = period;
mtx_init(&clk->mtx, mtx_plain);
+ clk->components = vec_create(sizeof(struct component *));
return clk;
}
void destroy_clock_domain(struct clock_domain *clk)
{
- for (size_t i = 0; i < clk->num_components; ++i)
- destroy(clk->components[i]);
+ for (size_t i = 0; i < vec_len(&clk->components); ++i)
+ destroy(vect_at(struct component *, clk->components, i));
+ vec_destroy(&clk->components);
free(clk);
}
stat clock_domain_add(struct clock_domain *clk, struct component *component)
{
- clk->components[clk->num_components++] = component;
+ vect_append(struct component *, clk->components, &component);
return OK;
}
@@ -80,18 +80,11 @@ static void clocked_component_tick(struct clock_domain *clk,
stat clock_domain_tick(struct clock_domain *clk)
{
- /* openmp on gcc apparently doesn't really handle if conditionals
- * particularly efficiently, so it's faster to do it manually */
- if (clk->num_components == 1) {
- clocked_component_tick(clk, clk->components[0]);
- advance_clock(clk);
- return clk->ret;
+ for (size_t i = 0; i < vec_len(&clk->components); ++i) {
+ clocked_component_tick(clk,
+ vect_at(struct component *, clk->components, i));
}
-#pragma omp parallel for
- for (size_t i = 0; i < clk->num_components; ++i)
- clocked_component_tick(clk, clk->components[i]);
-
advance_clock(clk);
return clk->ret;
}
diff --git a/src/components/bus/simple_bus.c b/src/components/bus/simple_bus.c
index 440ff8a..218e930 100644
--- a/src/components/bus/simple_bus.c
+++ b/src/components/bus/simple_bus.c
@@ -172,7 +172,7 @@ struct component *create_simple_bus()
}
stat simple_bus_add(struct component *bus, struct component *component,
- uintptr_t addr, size_t size)
+ uint64_t addr, uint64_t size)
{
struct mem_region *new = calloc(1, sizeof(struct mem_region));
if (!new)
diff --git a/src/components/cpu/riscv/simple_riscv32.c b/src/components/cpu/riscv/simple_riscv64.c
index 51b2a42..be96e60 100644
--- a/src/components/cpu/riscv/simple_riscv32.c
+++ b/src/components/cpu/riscv/simple_riscv64.c
@@ -2,30 +2,31 @@
/* 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_riscv32.h>
+#include <gran/cpu/riscv/simple_riscv64.h>
-struct simple_rv32_ldst {
+struct simple_rv64_ldst {
struct packet *pkt;
uint32_t reg;
bool u;
};
-struct simple_riscv32 {
+struct simple_riscv64 {
struct component component;
struct component *imem;
struct component *dmem;
- struct simple_rv32_ldst dls;
- struct simple_rv32_ldst ils;
+ struct simple_rv64_ldst dls;
+ struct simple_rv64_ldst ils;
/* have to be careful with x0 */
- uint32_t regs[32];
- uint32_t pc;
+ uint64_t regs[32];
+ uint64_t pc;
};
/* big endian format, going from smallest to highest address.
@@ -119,7 +120,7 @@ union rv_insn {
uint32_t val;
};
-static uint32_t get_reg(struct simple_riscv32 *cpu, size_t i)
+static uint64_t get_reg(struct simple_riscv64 *cpu, size_t i)
{
assert(i < 32);
@@ -129,7 +130,7 @@ static uint32_t get_reg(struct simple_riscv32 *cpu, size_t i)
return cpu->regs[i];
}
-static void set_reg(struct simple_riscv32 *cpu, size_t i, uint32_t v)
+static void set_reg(struct simple_riscv64 *cpu, size_t i, uint64_t v)
{
assert(i < 32);
@@ -143,18 +144,18 @@ static void set_reg(struct simple_riscv32 *cpu, size_t i, uint32_t v)
#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)
+static stat op_imm(struct simple_riscv64 *cpu, union rv_insn insn)
{
- uint32_t dst = 0;
- uint32_t src = get_reg(cpu, insn.itype.rs1);
+ uint64_t dst = 0;
+ uint64_t src = get_reg(cpu, insn.itype.rs1);
- uint32_t imm = EXTEND_IMM12(insn.itype.imm);
+ uint64_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;
+ case 0b010: dst = (int64_t)src < (int64_t)imm; break;
/* SLTIU */
case 0b011: dst = src < imm; break;
/* ANDI */
@@ -171,7 +172,7 @@ static stat op_imm(struct simple_riscv32 *cpu, union rv_insn insn)
if (imm & ~0b11111) /* SRLI */
dst = src >> SHAMT(imm);
else /* SRAI */
- dst = (int32_t)src >> SHAMT(imm);
+ dst = (int64_t)src >> SHAMT(imm);
break;
default:
@@ -184,26 +185,67 @@ static stat op_imm(struct simple_riscv32 *cpu, union rv_insn insn)
return OK;
}
-static stat lui(struct simple_riscv32 *cpu, union rv_insn insn)
+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_riscv32 *cpu, union rv_insn insn)
+static stat auipc(struct simple_riscv64 *cpu, union rv_insn insn)
{
- uint32_t res = cpu->pc + (insn.utype.imm << 12);
+ 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_riscv32 *cpu, union rv_insn insn)
+static stat op(struct simple_riscv64 *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);
+ 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 */
@@ -215,7 +257,7 @@ static stat op(struct simple_riscv32 *cpu, union rv_insn insn)
break;
/* SLT */
- case 0b010: dst = (int32_t)src1 < (int32_t)src2; break;
+ case 0b010: dst = (int64_t)src1 < (int64_t)src2; break;
/* SLTU */
case 0b011: dst = src1 < src2; break;
/* AND */
@@ -231,7 +273,7 @@ static stat op(struct simple_riscv32 *cpu, union rv_insn insn)
if (insn.rtype.funct7) /* SRL */
dst = src1 >> src2;
else /* SRA */
- dst = (int32_t)src1 >> src2;
+ dst = (int64_t)src1 >> src2;
break;
default:
@@ -250,18 +292,18 @@ static stat op(struct simple_riscv32 *cpu, union rv_insn insn)
| (insn.jtype.imm1 << 11) \
| (insn.jtype.imm0 << 12))
-static stat jal(struct simple_riscv32 *cpu, union rv_insn insn)
+static stat jal(struct simple_riscv64 *cpu, union rv_insn insn)
{
/** @todo generate exception on unaligned jumps */
- int32_t imm = JTYPE_IMM(insn);
+ 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_riscv32 *cpu, union rv_insn insn)
+static stat jalr(struct simple_riscv64 *cpu, union rv_insn insn)
{
- int32_t src = get_reg(cpu, insn.itype.rs1);
+ 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;
@@ -273,11 +315,11 @@ static stat jalr(struct simple_riscv32 *cpu, union rv_insn insn)
| (insn.btype.imm1 << 1) \
| (insn.btype.imm0 << 11))
-static stat branch(struct simple_riscv32 *cpu, union rv_insn insn)
+static stat branch(struct simple_riscv64 *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);
+ 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 */
@@ -298,7 +340,7 @@ static stat branch(struct simple_riscv32 *cpu, union rv_insn insn)
/* BLT */
case 0b100:
- if ((int32_t)src1 < (int32_t)src2) {
+ if ((int64_t)src1 < (int64_t)src2) {
cpu->pc += offset;
return OK;
}
@@ -314,7 +356,7 @@ static stat branch(struct simple_riscv32 *cpu, union rv_insn insn)
/* BGE */
case 0b101:
- if ((int32_t)src1 >= (int32_t)src2) {
+ if ((int64_t)src1 >= (int64_t)src2) {
cpu->pc += offset;
return OK;
}
@@ -337,13 +379,13 @@ static stat branch(struct simple_riscv32 *cpu, union rv_insn insn)
return OK;
}
-static stat load(struct simple_riscv32 *cpu, union rv_insn insn)
+static stat load(struct simple_riscv64 *cpu, union rv_insn insn)
{
- int32_t imm = EXTEND_IMM12(insn.itype.imm);
- int32_t base = get_reg(cpu, insn.itype.rs1);
+ int64_t imm = EXTEND_IMM12(insn.itype.imm);
+ int64_t base = get_reg(cpu, insn.itype.rs1);
- size_t addr = base + imm;
- size_t size = 0;
+ int64_t addr = base + imm;
+ int64_t size = 0;
bool u = false;
// assume little endian for now
@@ -356,6 +398,7 @@ static stat load(struct simple_riscv32 *cpu, union rv_insn insn)
case 0b001: size = 2; break;
/* LW */
case 0b010: size = 4; break;
+ case 0b011: size = 8; break;
default:
error("unknown LOAD width %x", insn.btype.funct3);
return ENOSUCH;
@@ -365,7 +408,7 @@ static stat load(struct simple_riscv32 *cpu, union rv_insn insn)
if (!pkt)
return EMEM;
- cpu->dls = (struct simple_rv32_ldst){pkt, insn.itype.rd, u};
+ cpu->dls = (struct simple_rv64_ldst){pkt, insn.itype.rd, u};
stat ret = read(cpu->dmem, pkt);
if (ret)
return ret;
@@ -378,13 +421,13 @@ static stat load(struct simple_riscv32 *cpu, union rv_insn insn)
EXTEND_IMM12((insn.stype.imm1 << 5) \
| (insn.stype.imm0))
-static stat store(struct simple_riscv32 *cpu, union rv_insn insn)
+static stat store(struct simple_riscv64 *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;
+ int64_t imm = STYPE_IMM(insn);
+ int64_t base = get_reg(cpu, insn.stype.rs1);
+ int64_t addr = base + imm;
- uint32_t src = get_reg(cpu, insn.stype.rs2);
+ uint64_t src = get_reg(cpu, insn.stype.rs2);
switch (insn.stype.funct3) {
/* SB */
@@ -405,6 +448,12 @@ static stat store(struct simple_riscv32 *cpu, union rv_insn insn)
*(uint32_t *)packet_data(cpu->dls.pkt) = src;
break;
}
+ /* SD */
+ case 0b011: {
+ cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 8);
+ *(uint64_t *)packet_data(cpu->dls.pkt) = src;
+ break;
+ }
default:
error("unknown width of STORE %x", insn.stype.funct3);
return ENOSUCH;
@@ -414,11 +463,11 @@ static stat store(struct simple_riscv32 *cpu, union rv_insn insn)
return write(cpu->dmem, cpu->dls.pkt);
}
-static void finalize_ld(struct simple_riscv32 *cpu)
+static void finalize_ld(struct simple_riscv64 *cpu)
{
- struct simple_rv32_ldst ld = cpu->dls;
+ struct simple_rv64_ldst ld = cpu->dls;
- uint32_t val = 0;
+ uint64_t val = 0;
void *data = packet_data(ld.pkt);
switch (packet_size(ld.pkt)) {
case 1:
@@ -430,7 +479,11 @@ static void finalize_ld(struct simple_riscv32 *cpu)
else val = *(int16_t *)data;
break;
- case 4: val = *(uint32_t *)data;
+ case 4: if (ld.u) val = *(uint32_t *)data;
+ else val = *(int32_t *)data;
+ break;
+
+ case 8: val = *(uint64_t *)data;
break;
default:
@@ -440,13 +493,13 @@ static void finalize_ld(struct simple_riscv32 *cpu)
set_reg(cpu, ld.reg, val);
}
-static void finalize_st(struct simple_riscv32 *cpu)
+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_riscv32 *cpu)
+static void finalize_dls(struct simple_riscv64 *cpu)
{
struct packet *pkt = cpu->dls.pkt;
@@ -455,13 +508,13 @@ static void finalize_dls(struct simple_riscv32 *cpu)
else if (packet_type(pkt) == PACKET_WRITE)
finalize_st(cpu);
else
- error("unsupported packet type for simple_riscv32");
+ error("unsupported packet type for simple_riscv64");
destroy_packet(pkt);
cpu->dls.pkt = NULL;
}
-static uint32_t finalize_ils(struct simple_riscv32 *cpu)
+static uint32_t finalize_ils(struct simple_riscv64 *cpu)
{
uint32_t insn = *(uint32_t *)packet_data(cpu->ils.pkt);
destroy_packet(cpu->ils.pkt);
@@ -469,7 +522,7 @@ static uint32_t finalize_ils(struct simple_riscv32 *cpu)
return insn;
}
-static stat simple_riscv32_clock(struct simple_riscv32 *cpu)
+static stat simple_riscv64_clock(struct simple_riscv64 *cpu)
{
/* there's an active data transfer we should handle */
if (cpu->dls.pkt) {
@@ -509,6 +562,7 @@ static stat simple_riscv32_clock(struct simple_riscv32 *cpu)
// 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;
@@ -530,7 +584,7 @@ static stat simple_riscv32_clock(struct simple_riscv32 *cpu)
return ret;
}
-static void simple_riscv32_destroy(struct simple_riscv32 *cpu)
+static void simple_riscv64_destroy(struct simple_riscv64 *cpu)
{
/* oh yeah, will have to think about the name stuff,
* i.e. how and where to free it, and where to assign it */
@@ -548,19 +602,25 @@ static void simple_riscv32_destroy(struct simple_riscv32 *cpu)
free(cpu);
}
-struct component *create_simple_riscv32(uint32_t start_pc,
+struct component *create_simple_riscv64(uint32_t start_pc,
struct component *imem,
struct component *dmem)
{
- struct simple_riscv32 *new = calloc(1, sizeof(struct simple_riscv32));
+ struct simple_riscv64 *new = calloc(1, sizeof(struct simple_riscv64));
if (!new)
return NULL;
- new->component.clock = (clock_callback)simple_riscv32_clock;
- new->component.destroy = (destroy_callback)simple_riscv32_destroy;
+ new->component.clock = (clock_callback)simple_riscv64_clock;
+ new->component.destroy = (destroy_callback)simple_riscv64_destroy;
new->pc = start_pc;
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
index ea41f62..da1cef6 100644
--- a/src/components/cpu/riscv/source.mk
+++ b/src/components/cpu/riscv/source.mk
@@ -1 +1 @@
-SOURCES += src/components/cpu/riscv/simple_riscv32.c
+SOURCES += src/components/cpu/riscv/simple_riscv64.c
diff --git a/src/components/grid/node.c b/src/components/grid/node.c
new file mode 100644
index 0000000..0aed768
--- /dev/null
+++ b/src/components/grid/node.c
@@ -0,0 +1,106 @@
+/* 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 <gran/grid/node.h>
+
+struct grid_node {
+ struct component component;
+ uint8_t u, v, x, y;
+ struct component *left, *right, *up, *down, *ascend, *lower;
+};
+
+typedef read_callback callback;
+
+static stat grid_route(struct grid_node *grid, struct packet *pkt, callback op)
+{
+ uint64_t addr = packet_addr(pkt);
+ uint8_t u = (addr >> 56) & 0xff;
+ uint8_t v = (addr >> 48) & 0xff;
+ uint8_t x = (addr >> 40) & 0xff;
+ uint8_t y = (addr >> 32) & 0xff;
+
+ if (grid->u == u && grid->v == v && grid->x == x && grid->y == y)
+ return op(grid->lower, pkt);
+
+ if (grid->u != u || grid->v != v) {
+ if (!grid->ascend)
+ return EBUS;
+
+ return op(grid->ascend, pkt);
+ }
+
+ if (y < grid->y) {
+ if (!grid->down)
+ return EBUS;
+
+ return op(grid->down, pkt);
+ }
+
+ if (y > grid->y) {
+ if (!grid->up)
+ return EBUS;
+
+ return op(grid->up, pkt);
+ }
+
+ if (x < grid->x) {
+ if (!grid->left)
+ return EBUS;
+
+ return op(grid->left, pkt);
+ }
+
+ if (x > grid->x) {
+ if (!grid->right)
+ return EBUS;
+
+ return op(grid->right, pkt);
+ }
+
+ return EBUS;
+}
+
+static stat grid_write(struct grid_node *node, struct packet *pkt)
+{
+ return grid_route(node, pkt, write);
+}
+
+static stat grid_read(struct grid_node *node, struct packet *pkt)
+{
+ return grid_route(node, pkt, read);
+}
+
+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->u = u;
+ node->v = v;
+ node->x = x;
+ node->y = y;
+ node->component.write = (write_callback)grid_write;
+ node->component.read = (read_callback)grid_read;
+
+ 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
new file mode 100644
index 0000000..bdb05c8
--- /dev/null
+++ b/src/components/grid/router.c
@@ -0,0 +1,114 @@
+#include <gran/grid/router.h>
+#include <gran/vec.h>
+
+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;
+};
+
+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_write(struct node_router *router, struct packet *pkt)
+{
+ uint64_t addr = packet_addr(pkt);
+ uint8_t u = (addr >> 56) & 0xff;
+ uint8_t v = (addr >> 48) & 0xff;
+ uint8_t x = (addr >> 40) & 0xff;
+ uint8_t y = (addr >> 32) & 0xff;
+
+ if (router->u != u || router->v != v || router->x != x || router->y != y)
+ return write(router->ascend, pkt);
+
+ struct router_region *region = find_region(router, addr);
+ if (!region)
+ return write(router->ascend, pkt);
+
+ return write(region->component, pkt);
+}
+
+static stat router_read(struct node_router *router, struct packet *pkt)
+{
+ uint64_t addr = packet_addr(pkt);
+ uint8_t u = (addr >> 56) & 0xff;
+ uint8_t v = (addr >> 48) & 0xff;
+ uint8_t x = (addr >> 40) & 0xff;
+ uint8_t y = (addr >> 32) & 0xff;
+
+ if (router->u != u || router->v != v || router->x != x || router->y != y)
+ return read(router->ascend, pkt);
+
+ struct router_region *region = find_region(router, addr);
+ if (!region)
+ return read(router->ascend, pkt);
+
+ return read(region->component, pkt);
+}
+
+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.write = (write_callback)router_write;
+ router->component.read = (read_callback)router_read;
+ 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
new file mode 100644
index 0000000..4fb44ec
--- /dev/null
+++ b/src/components/grid/source.mk
@@ -0,0 +1 @@
+SOURCES += src/components/grid/node.c src/components/grid/router.c
diff --git a/src/components/uart/simple_uart.c b/src/components/uart/simple_uart.c
new file mode 100644
index 0000000..5c97ccd
--- /dev/null
+++ b/src/components/uart/simple_uart.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <gran/uart/simple_uart.h>
+
+struct simple_uart {
+ struct component component;
+};
+
+static stat simple_uart_write(struct simple_uart *uart, struct packet *pkt)
+{
+ (void)uart;
+ size_t size = packet_size(pkt);
+ if (size != 1)
+ return EBUS;
+
+ putchar(*(uint8_t *)packet_data(pkt));
+ packet_set_state(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.write = (write_callback)simple_uart_write;
+ return (struct component *)uart;
+}
diff --git a/src/components/uart/source.mk b/src/components/uart/source.mk
new file mode 100644
index 0000000..af26289
--- /dev/null
+++ b/src/components/uart/source.mk
@@ -0,0 +1 @@
+SOURCES += src/components/uart/simple_uart.c
diff --git a/src/vec.c b/src/vec.c
new file mode 100644
index 0000000..e56ac41
--- /dev/null
+++ b/src/vec.c
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <gran/vec.h>
+
+struct vec vec_create(size_t ns)
+{
+ return (struct vec) {
+ .n = 0,
+ .s = 1,
+ .ns = ns,
+ .buf = malloc(ns),
+ };
+}
+
+size_t vec_len(struct vec *v)
+{
+ return v->n;
+}
+
+void *vec_at(struct vec *v, size_t i)
+{
+ assert(i < v->n && "out of vector bounds");
+ return v->buf + i * v->ns;
+}
+
+void *vec_back(struct vec *v)
+{
+ assert(v->n);
+ return v->buf + (v->n - 1) * v->ns;
+}
+
+void *vec_pop(struct vec *v)
+{
+ assert(v->n && "attempting to pop empty vector");
+ v->n--;
+ return v->buf + v->n * v->ns;
+}
+
+void vec_append(struct vec *v, void *n)
+{
+ v->n++;
+ if (v->n >= v->s) {
+ v->s *= 2;
+ v->buf = realloc(v->buf, v->s * v->ns);
+ }
+
+ void *p = vec_at(v, v->n - 1);
+ memcpy(p, n, v->ns);
+}
+
+void vec_reset(struct vec *v)
+{
+ v->n = 0;
+}
+
+void vec_destroy(struct vec *v) {
+ free(v->buf);
+}
+
+void vec_sort(struct vec *v, vec_comp_t comp)
+{
+ qsort(v->buf, v->n, v->ns, comp);
+}
diff --git a/tests/simple_grid/sim.c b/tests/simple_grid/sim.c
new file mode 100644
index 0000000..e0c485b
--- /dev/null
+++ b/tests/simple_grid/sim.c
@@ -0,0 +1,114 @@
+/* testcase for a 64x64 grid of processors, that all just spam the first memory
+ * region due to there being a 'lock' variable there, more or less the worst
+ * possible program for performance. */
+#include <assert.h>
+
+#include <gran/root.h>
+#include <gran/mem/simple_mem.h>
+#include <gran/bus/simple_bus.h>
+#include <gran/uart/simple_uart.h>
+#include <gran/grid/node.h>
+#include <gran/grid/router.h>
+#include <gran/cpu/riscv/simple_riscv64.h>
+
+unsigned char test_grid[] = {
+ 0xb3, 0x67, 0xb5, 0x00, 0x63, 0x9a, 0x07, 0x0a, 0xb7, 0x17, 0x00, 0x00,
+ 0x23, 0xb0, 0x07, 0x00, 0x1b, 0x57, 0x35, 0x00, 0xb7, 0x27, 0x00, 0x00,
+ 0x93, 0x06, 0x80, 0x02, 0x13, 0x77, 0x77, 0x00, 0x23, 0x80, 0xd7, 0x00,
+ 0x13, 0x07, 0x07, 0x03, 0x93, 0x76, 0x75, 0x00, 0x23, 0x80, 0xe7, 0x00,
+ 0x13, 0x87, 0x06, 0x03, 0x23, 0x80, 0xe7, 0x00, 0x93, 0x06, 0xc0, 0x02,
+ 0x1b, 0xd7, 0x35, 0x00, 0x23, 0x80, 0xd7, 0x00, 0x13, 0x77, 0x77, 0x00,
+ 0x93, 0x06, 0x00, 0x02, 0x23, 0x80, 0xd7, 0x00, 0x13, 0x07, 0x07, 0x03,
+ 0x93, 0xf6, 0x75, 0x00, 0x23, 0x80, 0xe7, 0x00, 0x13, 0x87, 0x06, 0x03,
+ 0x23, 0x80, 0xe7, 0x00, 0x13, 0x07, 0x90, 0x02, 0x23, 0x80, 0xe7, 0x00,
+ 0x93, 0x06, 0xa0, 0x00, 0x23, 0x80, 0xd7, 0x00, 0x1b, 0x87, 0x15, 0x00,
+ 0x93, 0x06, 0xf0, 0x03, 0x93, 0x77, 0xf7, 0x0f, 0x63, 0x98, 0xd5, 0x00,
+ 0x1b, 0x05, 0x15, 0x00, 0x13, 0x75, 0xf5, 0x0f, 0x93, 0x07, 0x00, 0x00,
+ 0x13, 0x15, 0x85, 0x00, 0x33, 0x65, 0xf5, 0x00, 0xb7, 0x17, 0x00, 0x00,
+ 0x23, 0xb0, 0xa7, 0x00, 0xb7, 0x16, 0x00, 0x00, 0x37, 0x47, 0x00, 0x00,
+ 0x83, 0xb7, 0x06, 0x00, 0xe3, 0x9e, 0xe7, 0xfe, 0x73, 0x00, 0x10, 0x00,
+ 0x67, 0x80, 0x00, 0x00, 0x13, 0x17, 0x85, 0x00, 0x33, 0x67, 0xb7, 0x00,
+ 0xb7, 0x16, 0x00, 0x00, 0x83, 0xb7, 0x06, 0x00, 0xe3, 0x9e, 0xe7, 0xfe,
+ 0x6f, 0xf0, 0x5f, 0xf4
+};
+unsigned int test_grid_len = 208;
+
+static struct component *get_grid(struct component **grid, int i, int j, uint8_t x, uint8_t y)
+{
+ if (i < 0)
+ return NULL;
+
+ if (j < 0)
+ return NULL;
+
+ if (i >= x)
+ return NULL;
+
+ if (j >= y)
+ return NULL;
+
+ return grid[i * x + j];
+}
+
+static stat build_grid(struct clock_domain *clk, uint8_t x, uint8_t y)
+{
+ struct component **grid = calloc(x * y, sizeof(struct component *));
+ assert(grid);
+
+ struct component **routers = calloc(x * y, sizeof(struct component *));
+ assert(routers);
+
+ for (size_t i = 0; i < x; ++i)
+ for (size_t j = 0; j < y; ++j) {
+ struct component *imem = create_simple_mem(4096);
+ init_simple_mem(imem, 0, test_grid_len, test_grid);
+
+ struct component *dmem = create_simple_mem(4096);
+ struct component *router = create_node_router(0, 0, i, j);
+ node_router_add(router, dmem, 4096, 4096);
+
+ struct component *rv64 = create_simple_riscv64(0, imem, router);
+ simple_riscv64_set_reg(rv64, 10, i); /* a0 */
+ simple_riscv64_set_reg(rv64, 11, j); /* a1 */
+
+ clock_domain_add(clk, rv64);
+
+ routers[i * x + j] = router;
+ grid[i * x + j] = create_grid_node(0, 0, i, j);
+ }
+
+ struct component *uart = create_simple_uart();
+ node_router_add(routers[0], uart, 8192, 1);
+
+ for (int i = 0; i < x; ++i)
+ for (int j = 0; j < y; ++j) {
+ struct component *node = grid[i * x + j];
+ struct component *lower = routers[i * x + j];
+ struct component *left = get_grid(grid, i - 1, j , x, y);
+ struct component *right = get_grid(grid, i + 1, j , x, y);
+ struct component *up = get_grid(grid, i , j + 1, x, y);
+ struct component *down = get_grid(grid, i , j - 1, x, y);
+ grid_node_connect(node, left, right, up, down, lower, NULL);
+ node_router_ascend(lower, node);
+ }
+
+ free(routers);
+ free(grid);
+ return OK;
+}
+
+int main()
+{
+ struct clock_domain *clk = create_clock_domain(NS(1));
+
+ stat r = build_grid(clk, 64, 64);
+ assert(r == OK);
+
+ struct gran_root *root = create_root();
+ root_add_clock(root, clk);
+
+ r = root_run(root);
+ assert(r == OK);
+
+ destroy_root(root);
+}
diff --git a/tests/simple_grid/source.mk b/tests/simple_grid/source.mk
new file mode 100644
index 0000000..e2dd442
--- /dev/null
+++ b/tests/simple_grid/source.mk
@@ -0,0 +1,6 @@
+GRID_TEST_OBJ != ./scripts/gen-deps --sources "tests/simple_grid/sim.c"
+
+TEST_PROGS += build/tests/simple_grid/sim
+
+build/tests/simple_grid/sim: $(GRID_TEST_OBJ) $(OBJS)
+ $(COMPILE) $(GRID_TEST_OBJ) $(OBJS) -o $@
diff --git a/tests/simple_grid/test.c b/tests/simple_grid/test.c
new file mode 100644
index 0000000..782062d
--- /dev/null
+++ b/tests/simple_grid/test.c
@@ -0,0 +1,43 @@
+#define X 64
+#define Y 64
+
+void _start(unsigned char x, unsigned char y)
+{
+ volatile unsigned long *counter = (unsigned long *)4096;
+ volatile char *uart = (char *)8192;
+
+ /* very hacky, not recommended but good enough for testing */
+ if (x == 0 && y == 0)
+ *counter = 0;
+ else
+ while (*counter != ((x << 8) | y))
+ ;
+
+ *uart = '(';
+ /* [0 - 63] as two octal numbers */
+ *uart = ((x >> 3) & 0x7) + '0';
+ *uart = ((x >> 0) & 0x7) + '0';
+
+ *uart = ',';
+ *uart = ' ';
+
+ *uart = ((y >> 3) & 0x7) + '0';
+ *uart = ((y >> 0) & 0x7) + '0';
+
+ *uart = ')';
+ *uart = '\n';
+
+ if (y == 63) {
+ x++;
+ y = 0;
+ }
+ else
+ y++;
+
+ *counter = (x << 8) | y;
+
+ while (*counter != (64 << 8))
+ ;
+
+ asm ("ebreak\n");
+}
diff --git a/tests/simple_riscv32/source.mk b/tests/simple_riscv32/source.mk
deleted file mode 100644
index 736c111..0000000
--- a/tests/simple_riscv32/source.mk
+++ /dev/null
@@ -1,6 +0,0 @@
-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 $@
diff --git a/tests/simple_riscv32/s.asm b/tests/simple_riscv64/s.asm
index d423dd9..d423dd9 100644
--- a/tests/simple_riscv32/s.asm
+++ b/tests/simple_riscv64/s.asm
diff --git a/tests/simple_riscv32/sim.c b/tests/simple_riscv64/sim.c
index fcf19a1..ae3cad4 100644
--- a/tests/simple_riscv32/sim.c
+++ b/tests/simple_riscv64/sim.c
@@ -5,7 +5,7 @@
#include <gran/root.h>
#include <gran/mem/simple_mem.h>
-#include <gran/cpu/riscv/simple_riscv32.h>
+#include <gran/cpu/riscv/simple_riscv64.h>
unsigned char simple_sum[] = {
0x13, 0x05, 0x00, 0x00, 0xb7, 0x45, 0x0f, 0x00, 0x93, 0x85, 0x05, 0x24,
@@ -24,10 +24,10 @@ int main()
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 component *rv64 = create_simple_riscv64(0, imem, dmem);
struct clock_domain *clk = create_clock_domain(NS(1));
- clock_domain_add(clk, rv32);
+ clock_domain_add(clk, rv64);
struct gran_root *root = create_root();
root_add_clock(root, clk);
diff --git a/tests/simple_riscv64/source.mk b/tests/simple_riscv64/source.mk
new file mode 100644
index 0000000..fbc541d
--- /dev/null
+++ b/tests/simple_riscv64/source.mk
@@ -0,0 +1,6 @@
+RV64_TEST_OBJ != ./scripts/gen-deps --sources "tests/simple_riscv64/sim.c"
+
+TEST_PROGS += build/tests/simple_riscv64/sim
+
+build/tests/simple_riscv64/sim: $(RV64_TEST_OBJ) $(TEST_OBJS) $(OBJS)
+ $(COMPILE) $(RV64_TEST_OBJ) $(TEST_OBJS) $(OBJS) -o $@
diff --git a/tests/simple_uart/hello.c b/tests/simple_uart/hello.c
new file mode 100644
index 0000000..30719e0
--- /dev/null
+++ b/tests/simple_uart/hello.c
@@ -0,0 +1,18 @@
+/* compile with
+ * riscv64-unknown-elf-gcc -ffreestanding -nostdlib -Wl,-Ttext=0 -march=rv64i -mabi=lp64 -O2 hello.c -o simple_hello
+ * xxd --include simple_hello
+ */
+void _start()
+{
+ /* relies on hard-coded system, good enough for this simple test */
+ volatile char *uart = (char *)4096;
+ *uart = 'H';
+ *uart = 'e';
+ *uart = 'l';
+ *uart = 'l';
+ *uart = 'o';
+ *uart = '!';
+ *uart = '\n';
+
+ asm ("ebreak\n");
+}
diff --git a/tests/simple_uart/sim.c b/tests/simple_uart/sim.c
new file mode 100644
index 0000000..762b591
--- /dev/null
+++ b/tests/simple_uart/sim.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+
+#include <assert.h>
+
+#include <gran/root.h>
+#include <gran/mem/simple_mem.h>
+#include <gran/bus/simple_bus.h>
+#include <gran/uart/simple_uart.h>
+#include <gran/cpu/riscv/simple_riscv64.h>
+
+unsigned char simple_hello[] = {
+ 0xb7, 0x17, 0x00, 0x00, 0x13, 0x07, 0x80, 0x04, 0x23, 0x80, 0xe7, 0x00,
+ 0x13, 0x07, 0x50, 0x06, 0x23, 0x80, 0xe7, 0x00, 0x13, 0x07, 0xc0, 0x06,
+ 0x23, 0x80, 0xe7, 0x00, 0x23, 0x80, 0xe7, 0x00, 0x13, 0x07, 0xf0, 0x06,
+ 0x23, 0x80, 0xe7, 0x00, 0x13, 0x07, 0x10, 0x02, 0x23, 0x80, 0xe7, 0x00,
+ 0x13, 0x07, 0xa0, 0x00, 0x23, 0x80, 0xe7, 0x00, 0x73, 0x00, 0x10, 0x00,
+ 0x67, 0x80, 0x00, 0x00
+};
+unsigned int simple_hello_len = 64;
+
+int main()
+{
+ const size_t size = 4096;
+ struct component *imem = create_simple_mem(size);
+ init_simple_mem(imem, 0, simple_hello_len, simple_hello);
+
+ struct component *dmem = create_simple_mem(size);
+ struct component *uart = create_simple_uart();
+ struct component *bus = create_simple_bus();
+ simple_bus_add(bus, dmem, 0, size);
+ simple_bus_add(bus, uart, size, 1);
+
+ struct component *rv64 = create_simple_riscv64(0, imem, bus);
+
+ struct clock_domain *clk = create_clock_domain(NS(1));
+ clock_domain_add(clk, rv64);
+
+ struct gran_root *root = create_root();
+ root_add_clock(root, clk);
+
+ assert(root_run(root) == OK);
+
+ destroy_root(root);
+}
diff --git a/tests/simple_uart/source.mk b/tests/simple_uart/source.mk
new file mode 100644
index 0000000..a42a447
--- /dev/null
+++ b/tests/simple_uart/source.mk
@@ -0,0 +1,6 @@
+UART_TEST_OBJ != ./scripts/gen-deps --sources "tests/simple_uart/sim.c"
+
+TEST_PROGS += build/tests/simple_uart/sim
+
+build/tests/simple_uart/sim: $(UART_TEST_OBJ) $(OBJS)
+ $(COMPILE) $(UART_TEST_OBJ) $(TEST_OBJS) $(OBJS) -o $@