aboutsummaryrefslogtreecommitdiff
path: root/triscv/src/cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'triscv/src/cpu.c')
-rw-r--r--triscv/src/cpu.c281
1 files changed, 281 insertions, 0 deletions
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 */
+ }
+}