diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-10-28 00:00:21 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-10-28 00:00:21 +0300 |
| commit | d96b8c7d12fc61ec609d6138627b9d6d18139a9a (patch) | |
| tree | 82bc9a4c2b22de6c021097fb79a57b6d7756a127 /triscv/src | |
| download | tri-d96b8c7d12fc61ec609d6138627b9d6d18139a9a.tar.gz tri-d96b8c7d12fc61ec609d6138627b9d6d18139a9a.zip | |
move to monorepo
+ Will probably have to make some changes to dir layout to make things
make more sense
Diffstat (limited to 'triscv/src')
| -rw-r--r-- | triscv/src/common.c | 13 | ||||
| -rw-r--r-- | triscv/src/cpu.c | 281 | ||||
| -rw-r--r-- | triscv/src/main.c | 62 | ||||
| -rw-r--r-- | triscv/src/mem.c | 79 | ||||
| -rw-r--r-- | triscv/src/mmu.c | 151 | ||||
| -rw-r--r-- | triscv/src/source.mk | 2 | ||||
| -rw-r--r-- | triscv/src/tmb.c | 29 | ||||
| -rw-r--r-- | triscv/src/uart.c | 44 |
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 + }; +} |
