aboutsummaryrefslogtreecommitdiff
path: root/triscv/src
diff options
context:
space:
mode:
Diffstat (limited to 'triscv/src')
-rw-r--r--triscv/src/common.c13
-rw-r--r--triscv/src/cpu.c281
-rw-r--r--triscv/src/main.c62
-rw-r--r--triscv/src/mem.c79
-rw-r--r--triscv/src/mmu.c151
-rw-r--r--triscv/src/source.mk2
-rw-r--r--triscv/src/tmb.c29
-rw-r--r--triscv/src/uart.c44
8 files changed, 661 insertions, 0 deletions
diff --git a/triscv/src/common.c b/triscv/src/common.c
new file mode 100644
index 0000000..e73d76e
--- /dev/null
+++ b/triscv/src/common.c
@@ -0,0 +1,13 @@
+#include <triscv/common.h>
+
+static const char *err_strs[] = {
+ [OK] = "OK",
+ [ERR_FULL] = "full",
+ [ERR_EXISTS] = "exists",
+ [ERR_OOB] = "out of bounds"
+};
+
+const char *err_str(stat_t s)
+{
+ return err_strs[s];
+}
diff --git a/triscv/src/cpu.c b/triscv/src/cpu.c
new file mode 100644
index 0000000..54e892c
--- /dev/null
+++ b/triscv/src/cpu.c
@@ -0,0 +1,281 @@
+#include <triscv/cpu.h>
+#include <triscv/mmu.h>
+#include <triscv/csr.h>
+
+struct cpu {
+ vm_t pc;
+ struct mmu *mmu;
+ struct tmb *tmb;
+
+ enum mode {
+ MMODE = 0, SMODE = 1, UMODE = 2
+ } mode;
+
+ /* we only have 81 registers, but 3^4 can be encoded in 2^8, and this
+ * way we can directly use the BCT as an index, speeding things up a bit
+ *
+ * csrs are too big, probably not worth it */
+ tri_t gpr[256];
+
+ /* 6561 M-mode, 6561 S-mode, 6561 U-mode, maybe? so ixxxxxxxx is
+ * reserved for M, 0xxxxxx for S, 1xxxx... for U. Just have to map them
+ * to decimal/binary in a somewhat sensible manner */
+ /* value */
+ tri_t csr[19683];
+ /* write trit, all csrs are readable? */
+ tri_t csrw[19683];
+};
+
+static size_t get_csr_num(tri_t csr)
+{
+ return tri_to(csr) + 9841;
+}
+
+static tri_t get_csr(struct cpu *cpu, size_t i)
+{
+ size_t max_csr = cpu->mode * 6561;
+ if (i < max_csr)
+ return 0; /* illegal access, should maybe also raise an interrupt? */
+
+ return cpu->csr[i];
+}
+
+static void set_csr(struct cpu *cpu, size_t i, tri_t t)
+{
+ size_t max_csr = cpu->mode * 6561;
+ if (i < max_csr)
+ return;
+
+ tri_t csr = cpu->csr[i];
+ tri_t w = cpu->csrw[i];
+
+ /* clear out trits we don't have write permission for*/
+ t &= w;
+ csr &= ~w;
+ csr |= t;
+
+ cpu->csr[i] = csr;
+}
+
+static tri_t get_gpr(struct cpu *cpu, tri_t i)
+{
+ /* could also add in check that i is in range, though that would
+ * probably add a fair bit of overhead */
+ if (i == 0)
+ return 0;
+
+ return cpu->gpr[i];
+}
+
+static void set_gpr(struct cpu *cpu, tri_t i, tri_t t)
+{
+ if (i == 0)
+ return;
+
+ cpu->gpr[i] = t;
+}
+
+static void csr_init(struct cpu *cpu)
+{
+ cpu->csr[CSR_MPOWER] = 0;
+ cpu->csrw[CSR_MPOWER] = RW_MPOWER;
+}
+
+struct cpu *cpu_create(struct mmu *mmu, struct tmb *tmb)
+{
+ struct cpu *cpu = calloc(1, sizeof(struct cpu));
+ cpu->mmu = mmu;
+ cpu->tmb = tmb;
+ csr_init(cpu);
+ return cpu;
+}
+
+void cpu_destroy(struct cpu *cpu)
+{
+ free(cpu);
+}
+
+static tri_t get_rd(tri_t i)
+{
+ i = tri_sr(i, 5);
+ return tri_mask(i, 4);
+}
+
+static tri_t get_rs1(tri_t i)
+{
+ i = tri_sr(i, 14);
+ return tri_mask(i, 4);
+}
+
+static tri_t get_rs2(tri_t i)
+{
+ i = tri_sr(i, 18);
+ return tri_mask(i, 4);
+}
+
+static tri_t get_imm4(tri_t i)
+{
+ i = tri_sr(i, 5);
+ return tri_mask(i, 4);
+}
+
+static tri_t get_imm5(tri_t i)
+{
+ i = tri_sr(i, 22);
+ return tri_mask(i, 5);
+}
+
+static tri_t get_imm9(tri_t i)
+{
+ i = tri_sr(i, 18);
+ return tri_mask(i, 9);
+}
+
+static tri_t get_imm18(tri_t i)
+{
+ i = tri_sr(i, 9);
+ return tri_mask(i, 18);
+}
+
+static tri_t get_fn0(tri_t i)
+{
+ i = tri_sr(i, 9);
+ return tri_mask(i, 5);
+}
+
+/** @todo these macros should be ripped out into some common repository for
+ * reuse in an assembler */
+/* ALL HARDCODED VALUES ARE PLACEHOLDERS FOR NOW */
+
+/* 0 */
+#define OP_IMM 0b0000000000
+static void do_op_imm(struct cpu *cpu, tri_t i)
+{
+ tri_t rs1 = get_rs1(i);
+ tri_t rd = get_rd(i);
+
+ tri_t src = get_gpr(cpu, rs1);
+ tri_t imm9 = get_imm9(i);
+ tri_t fn0 = get_fn0(i);
+
+#define ADDI 0
+ switch (fn0) {
+ case ADDI: {
+ tri_t r = tri_add(src, imm9);
+ set_gpr(cpu, rd, r);
+ break;
+ }
+ default: fprintf(stderr, "illegal/unimplemented OP_IMM at %lx,"
+ "aborting\n", cpu->pc);
+ abort();
+ }
+
+ cpu->pc += 3;
+}
+
+/* 1 */
+#define SYSTEM 0b0000000001
+static void do_system(struct cpu *cpu, tri_t i)
+{
+ size_t rs1 = get_rs1(i);
+ size_t rd = get_rd(i);
+
+ tri_t src = get_gpr(cpu, rs1);
+ tri_t csr = get_imm9(i);
+ tri_t fn0 = get_fn0(i);
+
+
+#define CSRRW 0
+ switch (fn0) {
+ case CSRRW: {
+ /* read existing value and replace it with rs1 */
+ size_t n = get_csr_num(csr);
+ tri_t c = get_csr(cpu, n);
+ set_csr(cpu, n, src);
+ set_gpr(cpu, rd, c);
+ break;
+ }
+
+ default: fprintf(stderr, "illegal/unimplemented SYSTEM at %lx,"
+ "aborting\n", cpu->pc);
+ abort();
+ }
+
+ cpu->pc += 3;
+}
+
+/* -1 */
+#define STORE 0b0000000010
+static void do_store(struct cpu *cpu, tri_t i)
+{
+ size_t rs1 = get_rs1(i);
+ size_t rs2 = get_rs2(i);
+
+ tri_t src = get_gpr(cpu, rs2);
+ tri_t base = get_gpr(cpu, rs1);
+ tri_t imm4 = get_imm4(i);
+ tri_t imm5 = get_imm5(i);
+ tri_t fn0 = get_fn0(i);
+
+ tri_t imm = tri_sl(imm5, 4) | imm4;
+
+ /* should maybe check that there's zeroes in other trits in fn0? */
+ int w = tri_get_trit(fn0, 4);
+
+ /* somewhat unsure if mmu should function in trinary or binary at this
+ * point */
+ tri_t addr = tri_add(base, imm);
+ vm_t a = tri_to(addr);
+
+ switch (w) {
+ case 0: mmu_write1(cpu, cpu->mmu, a, src); break;
+ case 1: mmu_write3(cpu, cpu->mmu, a, src); break;
+ default:
+ /* illegal value */
+ abort();
+ }
+
+ cpu->pc += 3;
+}
+
+/* 3 */
+#define LUI 0b0000000100
+static void do_lui(struct cpu *cpu, tri_t i)
+{
+ size_t rd = get_rd(i);
+ tri_t imm18 = get_imm18(i);
+ set_gpr(cpu, rd, tri_sl(imm18, 9));
+ cpu->pc += 3;
+}
+
+void cpu_reset(struct cpu *cpu)
+{
+ csr_init(cpu);
+}
+
+void cpu_run(struct cpu *cpu, vm_t start)
+{
+ cpu->pc = start;
+
+ /* run while the shutdown trit is zero */
+ while (!(cpu->csr[CSR_MPOWER] & 0b1100)) {
+ tri_t i = mmu_read3(cpu, cpu->mmu, cpu->pc);
+ /** @todo check for raised interrupts, illegal addr etc. */
+
+ /* check lowest five trits to determine opcode */
+ switch (tri_mask(i, 5)) {
+ case LUI: do_lui(cpu, i); break;
+ case STORE: do_store(cpu, i); break;
+ case SYSTEM: do_system(cpu, i); break;
+ case OP_IMM: do_op_imm(cpu, i); break;
+ default: /** @todo raise illegal instruction exception */
+ fprintf(stderr, "illegal/unimplemented "
+ "instruction at %lx, aborting\n",
+ cpu->pc);
+ abort();
+ break;
+ }
+
+ /* do_* is responsible for updating pc */
+ }
+}
diff --git a/triscv/src/main.c b/triscv/src/main.c
new file mode 100644
index 0000000..7acc958
--- /dev/null
+++ b/triscv/src/main.c
@@ -0,0 +1,62 @@
+#include <triscv/uart.h>
+#include <triscv/cpu.h>
+#include <triscv/mmu.h>
+#include <triscv/tmb.h>
+#include <triscv/mem.h>
+
+#define OPCODE(t) (t)
+#define RD(t) ((t) << 10)
+#define RS1(t) ((t) << 28)
+#define RS2(t) ((t) << 36)
+#define IMM4(t) ((t) << 10)
+#define IMM5(t) ((t) << 44)
+#define IMM9(t) ((t) << 36)
+#define IMM18(t) ((t) << 18)
+
+static const tri_t prog[] = {
+ /* li x1, '0' (48) */
+ /* or in this case, lui x1, 0, addi x1, 48 */
+ OPCODE(0b0100ULL) | RD(0b01ULL) | IMM18(0b0ULL),
+ OPCODE(0b0000ULL) | RD(0b01ULL) | RS1(0b01ULL) | IMM9(0b0110100100ULL),
+ /* li x2, 19683 (uart base reg) */
+ OPCODE(0b0100ULL) | RD(0b10ULL) | IMM18(0b1ULL),
+ OPCODE(0b0000ULL) | RD(0b10ULL) | RS1(0b10ULL) | IMM9(0ULL),
+ /* sw x1, 0(x2) */
+ OPCODE(0b0010ULL) | RS2(0b01ULL) | RS1(0b10ULL),
+ /* li x1, 3 (mpower)*/
+ OPCODE(0b0100ULL) | RD(0b01ULL),
+ OPCODE(0b0000ULL) | RD(0b01ULL) | RS1(0b01ULL) | IMM9(0b0100ULL),
+ /* csrrw mpower, x0, x1 */
+ /* mpower = -9841, which is then internally converted to 0 */
+ /* NOT FINAL CSR VALUE */
+ OPCODE(0b0001ULL) | RD(0b00ULL) | RS1(0b01ULL) | IMM9(0b101010101010101010UL)
+};
+
+int main()
+{
+ /* add memory regions etc. */
+ struct mmu *mmu = mmu_create();
+
+ /* map one trinary page */
+ struct mem *mem = mem_create(19683);
+ mem_init(mem, prog, 8);
+ mmu_map_dev(mmu, 0, mem_dev(mem));
+
+ struct uart *uart = uart_create();
+ mmu_map_dev(mmu, 19683, uart_dev(uart));
+
+ struct tmb *tmb = tmb_create();
+
+ /* one cpu for each core we want */
+ struct cpu *cpu = cpu_create(mmu, tmb);
+
+ cpu_run(cpu, 0);
+
+ uart_destroy(uart);
+ cpu_destroy(cpu);
+ tmb_destroy(tmb);
+ mmu_destroy(mmu);
+ mem_destroy(mem);
+
+ return 0;
+}
diff --git a/triscv/src/mem.c b/triscv/src/mem.c
new file mode 100644
index 0000000..5d70896
--- /dev/null
+++ b/triscv/src/mem.c
@@ -0,0 +1,79 @@
+#include <triscv/mem.h>
+#include <string.h>
+
+struct mem {
+ size_t size;
+ uint32_t buf[];
+};
+
+struct mem *mem_create(size_t size)
+{
+ struct mem *mem = calloc(1, sizeof(struct mem) + size * sizeof(mem->buf[0]));
+ mem->size = size;
+ return mem;
+}
+
+void mem_destroy(struct mem *mem)
+{
+ free(mem);
+}
+
+void mem_init(struct mem *mem, const void *buf, size_t len)
+{
+ assert((len * 3) < mem->size);
+ for (size_t i = 0; i < len * 3; i += 3) {
+ tri_t t = ((tri_t *)buf)[i / 3];
+ mem->buf[i + 0] = tri_mask(t, 9);
+ t = tri_sr(t, 9);
+
+ mem->buf[i + 1] = tri_mask(t, 9);
+ t = tri_sr(t, 9);
+
+ mem->buf[i + 2] = tri_mask(t, 9);
+ }
+}
+
+static void mem_write1(struct cpu *cpu, struct mem *mem, pm_t addr, tri_t t)
+{
+ (void)cpu;
+ (void)mem;
+ (void)addr;
+ (void)t;
+}
+
+static void mem_write3(struct cpu *cpu, struct mem *mem, pm_t addr, tri_t t)
+{
+ (void)cpu;
+ (void)mem;
+ (void)addr;
+ (void)t;
+}
+
+static tri_t mem_read1(struct cpu *cpu, struct mem *mem, pm_t addr)
+{
+ (void)cpu;
+ tri_t t = (tri_t)mem->buf[addr];
+ return tri_mask(t, 9);
+}
+
+static tri_t mem_read3(struct cpu *cpu, struct mem *mem, pm_t addr)
+{
+ (void)cpu;
+ /* not necessarily ideal, but I guess it works for now */
+ tri_t t0 = mem_read1(cpu, mem, addr + 0);
+ tri_t t1 = mem_read1(cpu, mem, addr + 1);
+ tri_t t2 = mem_read1(cpu, mem, addr + 2);
+ return t0 | tri_sl(t1, 9) | tri_sl(t2, 18);
+}
+
+struct dev mem_dev(struct mem *mem)
+{
+ return (struct dev){
+ .dev = mem,
+ .size = mem->size,
+ .write1 = (dev_write1_t)mem_write1,
+ .write3 = (dev_write3_t)mem_write3,
+ .read1 = (dev_read1_t)mem_read1,
+ .read3 = (dev_read3_t)mem_read3
+ };
+}
diff --git a/triscv/src/mmu.c b/triscv/src/mmu.c
new file mode 100644
index 0000000..c73f91d
--- /dev/null
+++ b/triscv/src/mmu.c
@@ -0,0 +1,151 @@
+#include <triscv/mmu.h>
+
+struct tlb_map {
+ vm_t from;
+ pm_t to;
+ bool valid;
+};
+
+struct dev_map {
+ pm_t start;
+ struct dev dev;
+};
+
+/* completely arbitrary value */
+#define MMU_SIZE 1024
+#define MAX_DEVS 16
+struct mmu {
+ /* virtual -> physical */
+ struct tlb_map tlb[MMU_SIZE];
+
+ /* physical to dev */
+ struct dev_map dev[MAX_DEVS];
+ size_t devs;
+
+ /* whether we should treat incoming addresses as virtual or physical */
+ bool translate;
+};
+
+struct mmu *mmu_create()
+{
+ /** @todo map some backing memory as well */
+ return calloc(1, sizeof(struct mmu));
+}
+
+void mmu_destroy(struct mmu *mmu)
+{
+ free(mmu);
+}
+
+static struct dev_map *mmu_find_dev_map(struct mmu *mmu, pm_t addr)
+{
+ for (size_t i = 0; i < mmu->devs; ++i) {
+ struct dev_map *d = &mmu->dev[i];
+ if (d->start > addr)
+ continue;
+
+ /* slightly tricky off by one here */
+ if (d->dev.size <= (addr - d->start))
+ continue;
+
+ return d;
+ }
+
+ return NULL;
+}
+
+stat_t mmu_map_dev(struct mmu *mmu, pm_t start, struct dev dev)
+{
+ /** @todo return status or something */
+ if (mmu->devs == MAX_DEVS)
+ return ERR_FULL;
+
+ if (mmu_find_dev_map(mmu, start))
+ return ERR_EXISTS;
+
+ struct dev_map *d = &mmu->dev[mmu->devs++];
+ d->dev = dev;
+ d->start = start;
+ return OK;
+}
+
+void mmu_enable(struct mmu *mmu)
+{
+ mmu->translate = true;
+}
+
+void mmu_disable(struct mmu *mmu)
+{
+ mmu->translate = false;
+}
+
+static bool mmu_translate(struct cpu *cpu, struct mmu *mmu, vm_t *addr)
+{
+ (void)cpu;
+ (void)mmu;
+ (void)addr;
+ /** @todo raise segfault if addr is not allowed */
+ return true;
+}
+
+void mmu_write1(struct cpu *cpu, struct mmu *mmu, vm_t addr, tri_t t)
+{
+ if (mmu->translate) {
+ if (!mmu_translate(cpu, mmu, &addr))
+ return;
+ }
+
+ struct dev_map *d = mmu_find_dev_map(mmu, addr);
+ if (!d || !d->dev.write1) {
+ /** @todo raise bus (?) error */
+ }
+
+ d->dev.write1(cpu, d->dev.dev, addr - d->start, t);
+}
+
+void mmu_write3(struct cpu *cpu, struct mmu *mmu, vm_t addr, tri_t t)
+{
+ if (mmu->translate) {
+ if (!mmu_translate(cpu, mmu, &addr))
+ return;
+ }
+
+ struct dev_map *d = mmu_find_dev_map(mmu, addr);
+ if (!d || !d->dev.write3) {
+ /** @todo raise bus error */
+ }
+
+ d->dev.write3(cpu, d->dev.dev, addr - d->start, t);
+}
+
+tri_t mmu_read1(struct cpu *cpu, struct mmu *mmu, vm_t addr)
+{
+ if (mmu->translate) {
+ if (!mmu_translate(cpu, mmu, &addr))
+ return 0;
+ }
+
+ struct dev_map *d = mmu_find_dev_map(mmu, addr);
+ if (!d || !d->dev.read1) {
+ /** @todo raise bus error */
+ return 0;
+ }
+
+ return d->dev.read1(cpu, d->dev.dev, addr - d->start);
+}
+
+tri_t mmu_read3(struct cpu *cpu, struct mmu *mmu, vm_t addr)
+{
+ if (mmu->translate) {
+ if (!mmu_translate(cpu, mmu, &addr))
+ return 0;
+ }
+
+ struct dev_map *d = mmu_find_dev_map(mmu, addr);
+ if (!d || !d->dev.read3) {
+ /** @todo raise bus error */
+ return 0;
+ }
+
+ return d->dev.read3(cpu, d->dev.dev, addr - d->start);
+}
diff --git a/triscv/src/source.mk b/triscv/src/source.mk
new file mode 100644
index 0000000..3b8724f
--- /dev/null
+++ b/triscv/src/source.mk
@@ -0,0 +1,2 @@
+SRC_LOCAL != echo src/*.c
+TRISCV_SOURCES += $(SRC_LOCAL)
diff --git a/triscv/src/tmb.c b/triscv/src/tmb.c
new file mode 100644
index 0000000..3f6c037
--- /dev/null
+++ b/triscv/src/tmb.c
@@ -0,0 +1,29 @@
+#include <triscv/tmb.h>
+
+struct map {
+ vm_t to;
+ tri_t val;
+ size_t width;
+ bool valid;
+};
+
+#define TMB_SIZE 3
+struct tmb {
+ struct map map[TMB_SIZE];
+};
+
+struct tmb *tmb_create()
+{
+ return calloc(1, sizeof(struct tmb));
+}
+
+void tmb_destroy(struct tmb *tmb)
+{
+ free(tmb);
+}
+
+size_t tmb_size(struct cpu *cpu)
+{
+ (void)cpu;
+ return TMB_SIZE;
+}
diff --git a/triscv/src/uart.c b/triscv/src/uart.c
new file mode 100644
index 0000000..492f0dd
--- /dev/null
+++ b/triscv/src/uart.c
@@ -0,0 +1,44 @@
+#include <triscv/uart.h>
+#include <utf9.h>
+
+struct uart {
+ /* guess this doesn't need much for now, at least until I add some state
+ * stuff to it */
+};
+
+struct uart *uart_create()
+{
+ return calloc(1, sizeof(struct uart));
+}
+
+void uart_destroy(struct uart *uart)
+{
+ free(uart);
+}
+
+static void uart_write1(struct cpu *cpu, struct uart *uart, pm_t addr, tri_t t)
+{
+ (void)cpu;
+ (void)uart;
+ (void)addr;
+
+#define U8_LEN 16
+ uint8_t u8[U8_LEN] = {0};
+ size_t u9_read = 0;
+ size_t l = utf9_to_utf8(u8, U8_LEN, &t, 1, &u9_read);
+
+ for (size_t i = 0; i < l; ++i)
+ putchar(u8[i]);
+}
+
+struct dev uart_dev(struct uart *uart)
+{
+ return (struct dev){
+ .dev = uart,
+ .size = 1,
+ .write1 = (dev_write1_t)uart_write1,
+ .write3 = NULL, /* illegal */
+ .read1 = NULL, /* not supported for now */
+ .read3 = NULL
+ };
+}