From 36c8080a0ebc8619adfdc36fa98381f901173e21 Mon Sep 17 00:00:00 2001 From: "Cheng Ho Ming, Eric" Date: Tue, 27 Aug 2024 16:27:46 +0800 Subject: [PATCH] Not Implemented: save/load RISC-V 16bit programs in EEPROM --- .vscode/settings.json | 5 +- save-rvasm/Makefile | 13 + save-rvasm/Makefile-emulator | 35 ++ save-rvasm/RV-LICENSE.txt | 18 + save-rvasm/RV-README.md | 128 ++++++ save-rvasm/funconfig.h | 17 + save-rvasm/rv.c | 815 +++++++++++++++++++++++++++++++++++ save-rvasm/rv.h | 125 ++++++ save-rvasm/save-rvasm.c | 374 ++++++++++++++++ 9 files changed, 1529 insertions(+), 1 deletion(-) create mode 100644 save-rvasm/Makefile create mode 100644 save-rvasm/Makefile-emulator create mode 100644 save-rvasm/RV-LICENSE.txt create mode 100644 save-rvasm/RV-README.md create mode 100644 save-rvasm/funconfig.h create mode 100644 save-rvasm/rv.c create mode 100644 save-rvasm/rv.h create mode 100644 save-rvasm/save-rvasm.c diff --git a/.vscode/settings.json b/.vscode/settings.json index e8f231f..ab7fd65 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,6 +30,9 @@ "riscv-disas.h": "c", "ch32v003_i2c.h": "c", "i2c_tx.h": "c", - "oled_min.h": "c" + "oled_min.h": "c", + "array": "c", + "string": "c", + "string_view": "c" }, } \ No newline at end of file diff --git a/save-rvasm/Makefile b/save-rvasm/Makefile new file mode 100644 index 0000000..6c741de --- /dev/null +++ b/save-rvasm/Makefile @@ -0,0 +1,13 @@ +TARGET:=save-rvasm +ADDITIONAL_C_FILES:= rv.c +include ../ch32v003fun/ch32v003fun.mk +clean: + rm -f $(TARGET).elf $(TARGET).hex $(TARGET).bin $(TARGET).map $(TARGET).lst \ + $(TARGET).o $(TARGET).d $(TARGET).srec $(TARGET).sym $(TARGET).lss + $(MAKE) -f Makefile-emulator clean + +emulator: + $(MAKE) -f Makefile-emulator clean + $(MAKE) -f Makefile-emulator + +.PHONY: clean emulator diff --git a/save-rvasm/Makefile-emulator b/save-rvasm/Makefile-emulator new file mode 100644 index 0000000..11ce69b --- /dev/null +++ b/save-rvasm/Makefile-emulator @@ -0,0 +1,35 @@ +CC=gcc +INCLUDES=-I../emulator -I../data -I. +CFLAGS=-g -Wall -Wextra -Wshadow -Wswitch -Wfloat-equal $(INCLUDES) +LDFLAGS=$(CFLAGS) + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S), Darwin) + LDFLAGS += -framework CoreFoundation -framework ApplicationServices -pthread +endif + +SRCS=save-rvasm.c rv.c +TARGET=save-rvasm + +SRCS += $(wildcard ../emulator/*.c) +ifneq ($(UNAME_S), Darwin) +SRCS := $(filter-out ../emulator/system_mac.c,$(SRCS)) +endif +OBJS = $(SRCS:.c=.o) +# Default target +all: $(TARGET) + +# Linking +$(TARGET): $(OBJS) + $(CC) $(OBJS) $(LDFLAGS) -o $(TARGET) + +# Compiling +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# Clean up +clean: + rm -f $(OBJS) $(TARGET) *.exe + +# Phony targets +.PHONY: all clean diff --git a/save-rvasm/RV-LICENSE.txt b/save-rvasm/RV-LICENSE.txt new file mode 100644 index 0000000..76837fa --- /dev/null +++ b/save-rvasm/RV-LICENSE.txt @@ -0,0 +1,18 @@ +Copyright (c) 2023-2024 Max Nurzia + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/save-rvasm/RV-README.md b/save-rvasm/RV-README.md new file mode 100644 index 0000000..e22f24b --- /dev/null +++ b/save-rvasm/RV-README.md @@ -0,0 +1,128 @@ +# rv + +RISC-V CPU core written in ANSI C. + +Features: + +- `RV32IMAC_Zicsr` implementation with M-mode and S-mode +- Boots RISCV32 Linux +- Passes all supported tests in [`riscv-tests`](https://github.com/riscv/riscv-tests) +- ~800 lines of code +- Doesn't use any integer types larger than 32 bits, even for multiplication +- Simple API (two required functions, plus one memory callback function that you provide) +- No memory allocations + +## API + +```c +/* Memory access callback: data is input/output, return RV_BAD on fault. */ +typedef rv_res (*rv_bus_cb)(void *user, rv_u32 addr, rv_u8 *data, rv_u32 is_store, rv_u32 width); + +/* Initialize CPU. You can call this again on `cpu` to reset it. */ +void rv_init(rv *cpu, void *user, rv_bus_cb bus_cb); + +/* Single-step CPU. Returns RV_E* on exception. */ +rv_u32 rv_step(rv *cpu); +``` + +## Usage + +```c +#include +#include + +#include "rv.h" + +#define RAM_BASE 0x80000000 +#define RAM_SIZE 0x10000 + +rv_res bus_cb(void *user, rv_u32 addr, rv_u8 *data, rv_u32 is_store, + rv_u32 width) { + rv_u8 *mem = (rv_u8 *)user + addr - RAM_BASE; + if (addr < RAM_BASE || addr + width >= RAM_BASE + RAM_SIZE) + return RV_BAD; + memcpy(is_store ? mem : data, is_store ? data : mem, width); + return RV_OK; +} + +rv_u32 program[2] = { + /* */ /* _start: */ + /* 0x80000000 */ 0x02A88893, /* add a7, a7, 42 */ + /* 0x80000004 */ 0x00000073 /* ecall */ +}; + +int main(void) { + rv_u8 mem[RAM_SIZE]; + rv cpu; + rv_init(&cpu, (void *)mem, &bus_cb); + memcpy((void *)mem, (void *)program, sizeof(program)); + while (rv_step(&cpu) != RV_EMECALL) { + } + printf("Environment call @ %08X: %u\n", cpu.csr.mepc, cpu.r[17]); + return 0; +} +``` + +See [`tools/example/example.c`](tools/example/example.c). + +## Running Linux + +This repository contains a machine emulator that can use `rv` to boot Linux. +See [`tools/linux/README.md`](tools/linux/README.md). + +## Targeting `rv` + +Use [riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain) with [tools/link.ld](tools/link.ld). + +Suggested GCC commandline: + +`riscv64-unknown-elf-gcc example.S -nostdlib -nostartfiles -Tlink.ld -march=rv32imac -mabi=ilp32 -o example.o -e _start -g -no-pie` + +To dump a binary starting at `0x80000000` that can be directly loaded by `rv` as in the above example: + +`riscv64-unknown-elf-objcopy -g -O binary example.o example.bin` + +## Instruction List + +Click an instruction to see its implementation in `rv.c`. + +- [`add`](rv.c#L589)[`addi`](rv.c#L589)[`amoadd.w`](rv.c#L548)[`amoand.w`](rv.c#L560)[`amomax.w`](rv.c#L564)[`amomaxu.w`](rv.c#L568)[`amomin.w`](rv.c#L562)[`amominu.w`](rv.c#L566) +- [`amoor.w`](rv.c#L558)[`amoswap.w`](rv.c#L550)[`amoxor.w`](rv.c#L556)[`and`](rv.c#L606)[`andi`](rv.c#L606)[`auipc`](rv.c#L696)[`beq`](rv.c#L509)[`bge`](rv.c#L512) +- [`bgtu`](rv.c#L514)[`blt`](rv.c#L511)[`bltu`](rv.c#L513)[`bne`](rv.c#L510)[`c.add`](rv.c#L386)[`c.addi`](rv.c#L324)[`c.addi16sp`](rv.c#L331)[`c.and`](rv.c#L352) +- [`c.andi`](rv.c#L343)[`c.beqz`](rv.c#L362)[`c.bnez`](rv.c#L364)[`c.ebreak`](rv.c#L383)[`c.j`](rv.c#L360)[`c.jal`](rv.c#L326)[`c.jalr`](rv.c#L380)[`c.jr`](rv.c#L375) +- [`c.li`](rv.c#L328)[`c.lui`](rv.c#L333)[`c.lw`](rv.c#L316)[`c.lwsp`](rv.c#L372)[`c.mv`](rv.c#L377)[`c.or`](rv.c#L350)[`c.slli`](rv.c#L370)[`c.srai`](rv.c#L341) +- [`c.srli`](rv.c#L339)[`c.sub`](rv.c#L346)[`c.sw`](rv.c#L318)[`c.swsp`](rv.c#L388)[`c.xor`](rv.c#L348)[`csrrc`](rv.c#L649)[`csrrci`](rv.c#L649)[`csrrs`](rv.c#L643) +- [`csrrsi`](rv.c#L643)[`csrrw`](rv.c#L634)[`csrrwi`](rv.c#L634)[`div`](rv.c#L620)[`divu`](rv.c#L622)[`ebreak`](rv.c#L685)[`ecall`](rv.c#L682)[`fence`](rv.c#L531) +- [`fence.i`](rv.c#L535)[`jal`](rv.c#L577)[`jalr`](rv.c#L524)[`lb`](rv.c#L488)[`lbu`](rv.c#L488)[`lh`](rv.c#L488)[`lhu`](rv.c#L488)[`lr.w`](rv.c#L552) +- [`lui`](rv.c#L698)[`lw`](rv.c#L488)[`mret`](rv.c#L658)[`mul`](rv.c#L610)[`mulh`](rv.c#L610)[`mulhsu`](rv.c#L610)[`mulhu`](rv.c#L610)[`or`](rv.c#L604) +- [`ori`](rv.c#L604)[`rem`](rv.c#L624)[`remu`](rv.c#L626)[`sb`](rv.c#L500)[`sc.w`](rv.c#L554)[`sfence.vma`](rv.c#L678)[`sh`](rv.c#L500)[`sll`](rv.c#L594) +- [`slli`](rv.c#L594)[`slt`](rv.c#L596)[`slti`](rv.c#L596)[`sltiu`](rv.c#L598)[`sltu`](rv.c#L598)[`sra`](rv.c#L602)[`srai`](rv.c#L602)[`sret`](rv.c#L658) +- [`srl`](rv.c#L602)[`srli`](rv.c#L602)[`sub`](rv.c#L589)[`sw`](rv.c#L500)[`wfi`](rv.c#L675)[`xor`](rv.c#L600)[`xori`](rv.c#L600) + +## FAQ + +### Spaghetti code + +- `rv` was written in a way that takes maximal advantage of RISCV's instruction orthogonality. +- `rv` also tries to strike a good balance between conciseness and readability. +- Of course, being able to read this code at all requires intimate prior knowledge of the ISA encoding. + +### No switch statements + +- C only allows constant expressions in switch statements. In addition to an abundance of `break` statements using these would result in more bloated code in the author's opinion. As it turns out, you are actually free to reimplement this code with switch statements. See [LICENSE.txt](LICENSE.txt). + +### Not useful + +- [Ok](https://www.google.com/search?q=happy+smiley+thumbs+up+happy+cool+funny+ok&tbm=isch) + +### Slow + +- [Ok](https://www.google.com/search?q=happy+smiley+thumbs+up+happy+cool+funny+ok&tbm=isch) + +## Caveats + +- Written in C89. +- Not actually written in C89, since it uses external names longer than 6 characters. +- Doesn't use any integer types larger than 32 bits, even for multiplication, because it's written in C89. +- Assumes width of integer types in a way that's not completely compliant with C89/99. Fix for this is coming soon, I'm working on a watertight `` for C89. +- Written in C89. diff --git a/save-rvasm/funconfig.h b/save-rvasm/funconfig.h new file mode 100644 index 0000000..b927055 --- /dev/null +++ b/save-rvasm/funconfig.h @@ -0,0 +1,17 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 +#define FUNCONF_USE_DEBUGPRINTF 0 +#define FUNCONF_USE_UARTPRINTF 1 +#define FUNCONF_UART_PRINTF_BAUD 115200 +#define horizontalButtons 8 +#define verticalButtons 8 +#define NUM_LEDS (horizontalButtons * verticalButtons) + +// #define CH32V003J4M6_USE_PD6_AS_UART_TX +#define FUNCONF_SYSTICK_USE_HCLK 1 + +#define INTERNAL_INSPIRE_MATRIX + +#endif diff --git a/save-rvasm/rv.c b/save-rvasm/rv.c new file mode 100644 index 0000000..843b4dd --- /dev/null +++ b/save-rvasm/rv.c @@ -0,0 +1,815 @@ +#include "rv.h" + +#include + +#define RV_RESET_VEC 0x80000000 /* CPU reset vector */ + +#define rv_ext(c) (1 << (rv_u8)((c) - 'A')) /* isa extension bit in misa */ + +void rv_init(rv * cpu, void * user, rv_bus_cb bus_cb) { + memset(cpu, 0, sizeof(*cpu)); + cpu->user = user; + cpu->bus_cb = bus_cb; + cpu->pc = RV_RESET_VEC; + cpu->csr.misa = (1 << 30) /* MXL = 1 [XLEN=32] */ + | rv_ext('M') /* Multiplication and Division */ + | rv_ext('C') /* Compressed Instructions */ + | rv_ext('A') /* Atomics */ + | rv_ext('S') /* Supervisor Mode */ + | rv_ext('U') /* User Mode */; + cpu->priv = RV_PMACH; +} + +/* sign-extend x from h'th bit */ +static rv_u32 rv_signext(rv_u32 x, rv_u32 h) { return (0 - (x >> h)) << h | x; } + +#define RV_SBIT 0x80000000 /* sign bit */ +#define rv_sgn(x) (!!((rv_u32)(x) & RV_SBIT)) /* extract sign */ + +/* compute overflow */ +#define rv_ovf(a, b, y) ((((a) ^ (b)) & RV_SBIT) && (((y) ^ (a)) & RV_SBIT)) + +#define rv_bf(i, h, l) \ + (((i) >> (l)) & ((1 << ((h) - (l) + 1)) - 1)) /* extract bit field */ +#define rv_b(i, l) rv_bf(i, l, l) /* extract bit */ +#define rv_tb(i, l, o) (rv_b(i, l) << (o)) /* translate bit */ +#define rv_tbf(i, h, l, o) (rv_bf(i, h, l) << (o)) /* translate bit field */ + +/* instruction field macros */ +#define rv_ioph(i) rv_bf(i, 6, 5) /* [h]i bits of opcode */ +#define rv_iopl(i) rv_bf(i, 4, 2) /* [l]o bits of opcode */ +#define rv_if3(i) rv_bf(i, 14, 12) /* funct3 */ +#define rv_if5(i) rv_bf(i, 31, 27) /* funct5 */ +#define rv_if7(i) rv_bf(i, 31, 25) /* funct7 */ +#define rv_ird(i) rv_bf(i, 11, 7) /* rd */ +#define rv_irs1(i) rv_bf(i, 19, 15) /* rs1 */ +#define rv_irs2(i) rv_bf(i, 24, 20) /* rs2 */ +#define rv_iimm_i(i) rv_signext(rv_bf(i, 31, 20), 11) /* imm. for I-type */ +#define rv_iimm_iu(i) rv_bf(i, 31, 20) /* zero-ext'd. imm. for I-type */ +#define rv_iimm_s(i) \ + (rv_signext(rv_tbf(i, 31, 25, 5), 11) | rv_tbf(i, 30, 25, 5) | \ + rv_bf(i, 11, 7)) /* imm. for S-type */ +#define rv_iimm_u(i) rv_tbf(i, 31, 12, 12) /* imm. for U-type */ +#define rv_iimm_b(i) \ + (rv_signext(rv_tb(i, 31, 12), 12) | rv_tb(i, 7, 11) | \ + rv_tbf(i, 30, 25, 5) | rv_tbf(i, 11, 8, 1)) /* imm. for B-type */ +#define rv_iimm_j(i) \ + (rv_signext(rv_tb(i, 31, 20), 20) | rv_tbf(i, 19, 12, 12) | \ + rv_tb(i, 20, 11) | rv_tbf(i, 30, 21, 1)) /* imm. for J-type */ +#define rv_isz(i) \ + (rv_bf(i, 1, 0) == 3 ? 4 : 2) /* instruction size \ + */ + +/* load register */ +static rv_u32 rv_lr(rv * cpu, rv_u8 i) { return cpu->r[i]; } + +/* store register */ +static void rv_sr(rv * cpu, rv_u8 i, rv_u32 v) { cpu->r[i] = i ? v : 0; } + +#define RV_CSR(num, r, w, dst) /* check if we are accessing csr `num` */ \ + y = ((csr == (num)) ? (rm = r, wm = w, &cpu->csr.dst) : y) + +/* csr bus access -- we model csrs as an internal memory bus */ +static rv_res rv_csr_bus(rv * cpu, rv_u32 csr, rv_u32 w, rv_u32 * io) { + rv_u32 *y = NULL /* phys. register */, wm /* writable bits */ = -1U, + rm = -1U; + rv_u32 rw = rv_bf(csr, 11, 10), priv = rv_bf(csr, 9, 8); + if ((w && rw == 3) || cpu->priv < priv || + (csr == 0x180 && cpu->priv == RV_PSUPER && rv_b(cpu->csr.mstatus, 20))) + return RV_BAD; /* invalid access OR writing to satp with tvm=1 + */ + /* id read mask write mask phys reg csr name */ + RV_CSR(0x100, 0x800DE762, 0x800DE762, mstatus); /*C sstatus */ + RV_CSR(0x104, 0x00000222, 0x00000222, mie); /*C sie */ + RV_CSR(0x105, 0xFFFFFFFF, 0xFFFFFFFF, stvec); /*C stvec */ + RV_CSR(0x106, 0xFFFFFFFF, 0x00000000, scounteren); /*C scounteren */ + RV_CSR(0x140, 0xFFFFFFFF, 0xFFFFFFFF, sscratch); /*C sscratch */ + RV_CSR(0x141, 0xFFFFFFFF, 0xFFFFFFFF, sepc); /*C sepc */ + RV_CSR(0x142, 0xFFFFFFFF, 0xFFFFFFFF, scause); /*C scause */ + RV_CSR(0x143, 0xFFFFFFFF, 0xFFFFFFFF, stval); /*C stval */ + RV_CSR(0x144, 0x00000222, 0x00000222, sip); /*C sip */ + RV_CSR(0x180, 0xFFFFFFFF, 0xFFFFFFFF, satp); /*C satp */ + RV_CSR(0x300, 0x807FFFEC, 0x807FFFEC, mstatus); /*C mstatus */ + RV_CSR(0x301, 0xFFFFFFFF, 0x00000000, misa); /*C misa */ + RV_CSR(0x302, 0xFFFFFFFF, 0xFFFFFFFF, medeleg); /*C medeleg */ + RV_CSR(0x303, 0xFFFFFFFF, 0xFFFFFFFF, mideleg); /*C mideleg */ + RV_CSR(0x304, 0xFFFFFFFF, 0x00000AAA, mie); /*C mie */ + RV_CSR(0x305, 0xFFFFFFFF, 0xFFFFFFFF, mtvec); /*C mtvec */ + RV_CSR(0x306, 0xFFFFFFFF, 0x00000000, mcounteren); /*C mcounteren */ + RV_CSR(0x310, 0x00000030, 0x00000030, mstatush); /*C mstatush */ + RV_CSR(0x340, 0xFFFFFFFF, 0xFFFFFFFF, mscratch); /*C mscratch */ + RV_CSR(0x341, 0xFFFFFFFF, 0xFFFFFFFF, mepc); /*C mepc */ + RV_CSR(0x342, 0xFFFFFFFF, 0xFFFFFFFF, mcause); /*C mcause */ + RV_CSR(0x343, 0xFFFFFFFF, 0x00000000, mtval); /*C mtval */ + RV_CSR(0x344, 0xFFFFFFFF, 0x00000AAA, mip); /*C mip */ + RV_CSR(0xC00, 0xFFFFFFFF, 0xFFFFFFFF, cycle); /*C cycle */ + RV_CSR(0xC01, 0xFFFFFFFF, 0xFFFFFFFF, mtime); /*C time */ + RV_CSR(0xC02, 0xFFFFFFFF, 0xFFFFFFFF, cycle); /*C instret */ + RV_CSR(0xC80, 0xFFFFFFFF, 0xFFFFFFFF, cycleh); /*C cycleh */ + RV_CSR(0xC81, 0xFFFFFFFF, 0xFFFFFFFF, mtimeh); /*C timeh */ + RV_CSR(0xC82, 0xFFFFFFFF, 0xFFFFFFFF, cycleh); /*C instreth */ + RV_CSR(0xF11, 0xFFFFFFFF, 0x00000000, mvendorid); /*C mvendorid */ + RV_CSR(0xF12, 0xFFFFFFFF, 0x00000000, marchid); /*C marchid */ + RV_CSR(0xF13, 0xFFFFFFFF, 0x00000000, mimpid); /*C mimpid */ + RV_CSR(0xF14, 0xFFFFFFFF, 0xFFFFFFFF, mhartid); /*C mhartid */ + if (!y) + return RV_BAD; /* invalid csr */ + *io = w ? *io : (*y & rm); /* only read allowed bits */ + *y = w ? (*y & ~wm) | (*io & wm) : *y; /* only write allowed bits */ + return RV_OK; +} + +/* trigger a trap */ +static rv_u32 rv_trap(rv * cpu, rv_u32 cause, rv_u32 tval) { + rv_u32 is_interrupt = !!(cause & 0x80000000), rcause = cause & ~0x80000000; + rv_priv xp = /* destination privilege, switch from y = cpu->priv + to this */ + (cpu->priv < RV_PMACH) && + ((is_interrupt ? cpu->csr.mideleg : cpu->csr.medeleg) & + (1 << rcause)) + ? RV_PSUPER + : RV_PMACH; + rv_u32 *xtvec = &cpu->csr.mtvec, *xepc = &cpu->csr.mepc, + *xcause = &cpu->csr.mcause, *xtval = &cpu->csr.mtval; + rv_u32 xie = rv_b(cpu->csr.mstatus, xp); + if (xp == RV_PSUPER) /* select s-mode regs */ + xtvec = &cpu->csr.stvec, xepc = &cpu->csr.sepc, + xcause = &cpu->csr.scause, xtval = &cpu->csr.stval; + cpu->csr.mstatus &= + (xp == RV_PMACH ? 0xFFFFE777 /* {mpp, mie, mpie} <- 0 */ + : 0xFFFFFEDD); /* {spp, sie, spie} <- 0 */ + cpu->csr.mstatus |= (cpu->priv << (xp == RV_PMACH ? 11 : 8)) /* xpp <- y */ + | xie << (4 + xp); /* xpie <- xie */ + *xepc = cpu->pc; /* xepc <- pc */ + *xcause = rcause | (is_interrupt << 31); /* xcause <- cause */ + *xtval = tval; /* xtval <- tval */ + cpu->priv = xp; /* priv <- x */ + /* if tvec[0], return 4 * cause + vec, otherwise just return vec + */ + cpu->pc = (*xtvec & ~3U) + 4 * rcause * ((*xtvec & 1) && is_interrupt); + return cause; +} + +/* bus access trap with tval */ +static rv_u32 rv_trap_bus(rv * cpu, rv_u32 err, rv_u32 tval, rv_access a) { + static const rv_u32 ex[] = {RV_EIFAULT, RV_EIALIGN, RV_EIPAGE, /* RV_AR */ + RV_ELFAULT, RV_ELALIGN, RV_ELPAGE, /* RV_AW */ + RV_ESFAULT, RV_ESALIGN, RV_ESPAGE}; /* RV_AX */ + return rv_trap(cpu, ex[(a == RV_AW ? 2 : a == RV_AR) * 3 + err - 1], tval); +} + +/* sv32 virtual address -> physical address */ +static rv_u32 rv_vmm(rv * cpu, rv_u32 va, rv_u32 * pa, rv_access access) { + rv_u32 epriv = rv_b(cpu->csr.mstatus, 17) && access != RV_AX + ? rv_bf(cpu->csr.mstatus, 12, 11) + : cpu->priv; /* effective privilege mode */ + if (!rv_b(cpu->csr.satp, 31) || epriv > RV_PSUPER) { + *pa = va; /* if !satp.mode, no translation */ + } + else { + rv_u32 ppn /* satp.ppn */ = rv_bf(cpu->csr.satp, 21, 0), + a /* satp.ppn * PAGESIZE */ = ppn << 12, + i /* LEVELS - 1 */ = 1, pte, pte_address, tlb_hit = 0; + if (cpu->tlb_valid && cpu->tlb_va == (va & ~0xFFFU)) + pte = cpu->tlb_pte, tlb_hit = 1, i = cpu->tlb_i; + while (!tlb_hit) { + /* pte_address = a + va.vpn[i] * PTESIZE */ + pte_address = a + (rv_bf(va, 21 + 10 * i, 12 + 10 * i) << 2); + if (cpu->bus_cb(cpu->user, pte_address, (rv_u8 *)&pte, 0, 4)) + return RV_BAD; + rv_endcvt((rv_u8 *)&pte, (rv_u8 *)&pte, 4, 0); + if (!rv_b(pte, 0) || (!rv_b(pte, 1) && rv_b(pte, 2))) + return RV_PAGEFAULT; /* pte.v == 0, or (pte.r == 0 and + pte.w == 1) */ + if (rv_b(pte, 1) || rv_b(pte, 3)) + break; /* if pte.r = 1 or pte.x = 1, this is a leaf + page */ + if (i == 0) + return RV_PAGEFAULT; /* if i - 1 < 0, pagefault */ + i = i - 1; + a = rv_tbf(pte, 31, 10, 12); /* a = pte.ppn[*] * PAGESIZE */ + } + if (!tlb_hit) + cpu->tlb_va = va & ~0xFFFU, cpu->tlb_pte = pte, cpu->tlb_i = i, + cpu->tlb_valid = 1; /* avoid another pte walk on the next access */ + if (rv_b(cpu->csr.mstatus, 19)) + pte |= rv_b(pte, 3) << 2; /* pte.r = pte.x if mxr bit set */ + if ((!rv_b(pte, 4) && epriv == RV_PUSER) /* u-bit not set */ + || (epriv == RV_PSUPER && !rv_b(cpu->csr.mstatus, 18) && + rv_b(pte, 4)) /* mstatus.sum bit wrong */ + || ~rv_bf(pte, 3, 1) & access /* mismatching access type*/ + || (i && rv_bf(pte, 19, 10)) /* misaligned megapage */ + || !rv_b(pte, 6) /* pte.a == 0 */ + || ((access & RV_AW) && !rv_b(pte, 7))) /* writing and pte.d == 0 */ + return RV_PAGEFAULT; + /* pa.ppn[1:i] = pte.ppn[1:i] */ + *pa = rv_tbf(pte, 31, 10 + 10 * i, 12 + 10 * i) | + rv_bf(va, 11 + 10 * i, 0); + } + return RV_OK; +} + +#define rvm_lo(w) ((w) & (rv_u32)0xFFFFU) /* low 16 bits of 32-bit word */ +#define rvm_hi(w) ((w) >> 16) /* high 16 bits of 32-bit word */ + +/* adc 16 bit */ +static rv_u32 rvm_ahh(rv_u32 a, rv_u32 b, rv_u32 cin, rv_u32 * cout) { + rv_u32 sum = a + b + cin /* cin must be less than 2. */; + *cout = rvm_hi(sum); + return rvm_lo(sum); +} + +/* mul 16 bit */ +static rv_u32 rvm_mhh(rv_u32 a, rv_u32 b, rv_u32 * cout) { + rv_u32 prod = a * b; + *cout = rvm_hi(prod); + return rvm_lo(prod); +} + +/* 32 x 32 -> 64 bit multiply */ +static rv_u32 rvm(rv_u32 a, rv_u32 b, rv_u32 * hi) { + rv_u32 al = rvm_lo(a), ah = rvm_hi(a), bl = rvm_lo(b), bh = rvm_hi(b); + rv_u32 qh, ql = rvm_mhh(al, bl, &qh); /* qh, ql = al * bl */ + rv_u32 rh, rl = rvm_mhh(al, bh, &rh); /* rh, rl = al * bh */ + rv_u32 sh, sl = rvm_mhh(ah, bl, &sh); /* sh, sl = ah * bl */ + rv_u32 th, tl = rvm_mhh(ah, bh, &th); /* th, tl = ah * bh */ + rv_u32 mc, m = rvm_ahh(rl, sl, 0, &mc); /* m, nc = rl + sl */ + rv_u32 nc, n = rvm_ahh(rh, sh, mc, &nc); /* n, nc = rh + sh + nc */ + rv_u32 x = ql; /* x, 0 = ql */ + rv_u32 yc, y = rvm_ahh(m, qh, 0, &yc); /* y, yc = qh + m */ + rv_u32 zc, z = rvm_ahh(n, tl, yc, &zc); /* z, zc = tl + n + yc */ + rv_u32 wc, w = rvm_ahh(th, nc, zc, &wc); /* w, 0 = th + nc + zc */ + *hi = z | (w << 16); /* hi = (w, z) */ + return x | (y << 16); /* lo = (y, x) */ +} + +#define rvc_op(c) rv_bf(c, 1, 0) /* c. op */ +#define rvc_f3(c) rv_bf(c, 15, 13) /* c. funct3 */ +#define rvc_rp(r) ((r) + 8) /* c. r' register offsetter */ +#define rvc_ird(c) rv_bf(c, 11, 7) /* c. ci-format rd/rs1 */ +#define rvc_irpl(c) rvc_rp(rv_bf(c, 4, 2)) /* c. rd'/rs2' (bits 4-2) */ +#define rvc_irph(c) rvc_rp(rv_bf(c, 9, 7)) /* c. rd'/rs1' (bits 9-7) */ +#define rvc_imm_ciw(c) /* CIW imm. for c.addi4spn */ \ + (rv_tbf(c, 10, 7, 6) | rv_tbf(c, 12, 11, 4) | rv_tb(c, 6, 2) | \ + rv_tb(c, 5, 3)) +#define rvc_imm_cl(c) /* CL imm. for c.lw/c.sw */ \ + (rv_tb(c, 5, 6) | rv_tbf(c, 12, 10, 3) | rv_tb(c, 6, 2)) +#define rvc_imm_ci(c) /* CI imm. for c.addi/c.li/c.lui */ \ + (rv_signext(rv_tb(c, 12, 5), 5) | rv_bf(c, 6, 2)) +#define rvc_imm_ci_b(c) /* CI imm. for c.addi16sp */ \ + (rv_signext(rv_tb(c, 12, 9), 9) | rv_tbf(c, 4, 3, 7) | rv_tb(c, 5, 6) | \ + rv_tb(c, 2, 5) | rv_tb(c, 6, 4)) +#define rvc_imm_ci_c(c) /* CI imm. for c.lwsp */ \ + (rv_tbf(c, 3, 2, 6) | rv_tb(c, 12, 5) | rv_tbf(c, 6, 4, 2)) +#define rvc_imm_cj(c) /* CJ imm. for c.jalr/c.j */ \ + (rv_signext(rv_tb(c, 12, 11), 11) | rv_tb(c, 11, 4) | \ + rv_tbf(c, 10, 9, 8) | rv_tb(c, 8, 10) | rv_tb(c, 7, 6) | \ + rv_tb(c, 6, 7) | rv_tbf(c, 5, 3, 1) | rv_tb(c, 2, 5)) +#define rvc_imm_cb(c) /* CB imm. for c.beqz/c.bnez */ \ + (rv_signext(rv_tb(c, 12, 8), 8) | rv_tbf(c, 6, 5, 6) | rv_tb(c, 2, 5) | \ + rv_tbf(c, 11, 10, 3) | rv_tbf(c, 4, 3, 1)) +#define rvc_imm_css(c) /* CSS imm. for c.swsp */ \ + (rv_tbf(c, 8, 7, 6) | rv_tbf(c, 12, 9, 2)) + +/* macros to assemble all uncompressed instruction types */ +#define rv_i_i(op, f3, rd, rs1, imm) /* I-type */ \ + ((imm) << 20 | (rs1) << 15 | (f3) << 12 | (rd) << 7 | (op) << 2 | 3) +#define rv_i_s(op, f3, rs1, rs2, imm) /* S-type */ \ + (rv_bf(imm, 11, 5) << 25 | (rs2) << 20 | (rs1) << 15 | (f3) << 12 | \ + rv_bf(imm, 4, 0) << 7 | (op) << 2 | 3) +#define rv_i_u(op, rd, imm) /* U-type */ \ + ((imm) << 12 | (rd) << 7 | (op) << 2 | 3) +#define rv_i_r(op, f3, rd, rs1, rs2, f7) /* R-type */ \ + ((f7) << 25 | (rs2) << 20 | (rs1) << 15 | (f3) << 12 | (rd) << 7 | \ + (op) << 2 | 3) +#define rv_i_j(op, rd, imm) /* J-type */ \ + (rv_b(imm, 20) << 31 | rv_bf(imm, 10, 1) << 21 | rv_b(imm, 11) << 20 | \ + rv_bf(imm, 19, 12) << 12 | (rd) << 7 | (op) << 2 | 3) +#define rv_i_b(op, f3, rs1, rs2, imm) /* B-type */ \ + (rv_b(imm, 12) << 31 | rv_bf(imm, 10, 5) << 25 | (rs2) << 20 | \ + (rs1) << 15 | (f3) << 12 | rv_bf(imm, 4, 1) << 8 | \ + rv_b(imm, 11) << 7 | (op) << 2 | 3) + +/* decompress instruction */ +static rv_u32 rvc(rv_u32 c) { + if (rvc_op(c) == 0) { + if (rvc_f3(c) == 0 && c != 0) { /* c.addi4spn -> addi rd', x2, nzuimm */ + return rv_i_i(4, 0, rvc_irpl(c), 2, rvc_imm_ciw(c)); + } + else if (c == 0) { /* illegal */ + return 0; + } + else if (rvc_f3(c) == 2) { /*I c.lw -> lw rd', offset(rs1') */ + return rv_i_i(0, 2, rvc_irpl(c), rvc_irph(c), rvc_imm_cl(c)); + } + else if (rvc_f3(c) == 6) { /*I c.sw -> sw rs2', offset(rs1') */ + return rv_i_s(8, 2, rvc_irph(c), rvc_irpl(c), rvc_imm_cl(c)); + } + else { /* illegal */ + return 0; + } + } + else if (rvc_op(c) == 1) { + if (rvc_f3(c) == 0) { /*I c.addi -> addi rd, rd, nzimm */ + return rv_i_i(4, 0, rvc_ird(c), rvc_ird(c), rvc_imm_ci(c)); + } + else if (rvc_f3(c) == 1) { /*I c.jal -> jal x1, offset */ + return rv_i_j(27, 1, rvc_imm_cj(c)); + } + else if (rvc_f3(c) == 2) { /*I c.li -> addi rd, x0, imm */ + return rv_i_i(4, 0, rvc_ird(c), 0, rvc_imm_ci(c)); + } + else if (rvc_f3(c) == 3) { /* 01/011: LUI/ADDI16SP */ + if (rvc_ird(c) == 2) { /*I c.addi16sp -> addi x2, x2, nzimm */ + return rv_i_i(4, 0, 2, 2, rvc_imm_ci_b(c)); + } + else if (rvc_ird(c) != 0) { /*I c.lui -> lui rd, nzimm */ + return rv_i_u(13, rvc_ird(c), rvc_imm_ci(c)); + } + else { /* illegal */ + return 0; + } + } + else if (rvc_f3(c) == 4) { /* 01/100: MISC-ALU */ + if (rv_bf(c, 11, 10) == 0) { /*I c.srli -> srli rd', rd', shamt */ + return rv_i_r( + 4, 5, rvc_irph(c), rvc_irph(c), rvc_imm_ci(c) & 0x1F, 0); + } + else if (rv_bf(c, 11, 10) == + 1) { /*I c.srai -> srai rd', rd', shamt */ + return rv_i_r( + 4, 5, rvc_irph(c), rvc_irph(c), rvc_imm_ci(c) & 0x1F, 32); + } + else if (rv_bf(c, 11, 10) == + 2) { /*I c.andi -> andi rd', rd', imm */ + return rv_i_i(4, 7, rvc_irph(c), rvc_irph(c), rvc_imm_ci(c)); + } + else if (rv_bf(c, 11, 10) == 3) { + if (rv_bf(c, 6, 5) == 0) { /*I c.sub -> sub rd', rd', rs2' */ + return rv_i_r( + 12, 0, rvc_irph(c), rvc_irph(c), rvc_irpl(c), 32); + } + else if (rv_bf(c, 6, 5) == + 1) { /*I c.xor -> xor rd', rd', rs2' */ + return rv_i_r( + 12, 4, rvc_irph(c), rvc_irph(c), rvc_irpl(c), 0); + } + else if (rv_bf(c, 6, 5) == 2) { /*I c.or -> or rd', rd', rs2' */ + return rv_i_r( + 12, 6, rvc_irph(c), rvc_irph(c), rvc_irpl(c), 0); + } + else if (rv_bf(c, 6, 5) == + 3) { /*I c.and -> and rd', rd', rs2' */ + return rv_i_r( + 12, 7, rvc_irph(c), rvc_irph(c), rvc_irpl(c), 0); + } + else { /* illegal */ + return 0; + } + } + else { /* illegal */ + return 0; + } + } + else if (rvc_f3(c) == 5) { /*I c.j -> jal x0, offset */ + return rv_i_j(27, 0, rvc_imm_cj(c)); + } + else if (rvc_f3(c) == 6) { /*I c.beqz -> beq rs1' x0, offset */ + return rv_i_b(24, 0, rvc_irph(c), 0, rvc_imm_cb(c)); + } + else if (rvc_f3(c) == 7) { /*I c.bnez -> bne rs1' x0, offset */ + return rv_i_b(24, 1, rvc_irph(c), 0, rvc_imm_cb(c)); + } + else { /* illegal */ + return 0; + } + } + else if (rvc_op(c) == 2) { + if (rvc_f3(c) == 0) { /*I c.slli -> slli rd, rd, shamt */ + return rv_i_r( + 4, 1, rvc_ird(c), rvc_ird(c), rvc_imm_ci(c) & 0x1F, 0); + } + else if (rvc_f3(c) == 2) { /*I c.lwsp -> lw rd, offset(x2) */ + return rv_i_i(0, 2, rvc_ird(c), 2, rvc_imm_ci_c(c)); + } + else if (rvc_f3(c) == 4 && !rv_b(c, 12) && !rv_bf(c, 6, 2)) { + /*I c.jr -> jalr x0, 0(rs1) */ + return rv_i_i(25, 0, 0, rvc_ird(c), 0); + } + else if (rvc_f3(c) == 4 && + !rv_b(c, 12)) { /*I c.mv -> add rd, x0, rs2 */ + return rv_i_r(12, 0, rvc_ird(c), 0, rv_bf(c, 6, 2), 0); + } + else if (rvc_f3(c) == 4 && rv_b(c, 12) && rvc_ird(c) && + !rv_bf(c, 6, 2)) { /*I c.jalr -> jalr x1, 0(rs1) */ + return rv_i_i(25, 0, 1, rvc_ird(c), 0); + } + else if (rvc_f3(c) == 4 && rv_b(c, 12) && !rvc_ird(c) && + !rv_bf(c, 6, 2)) { /*I c.ebreak -> ebreak */ + return rv_i_i(28, 0, 0, 0, 1); + } + else if (rvc_f3(c) == 4 && rv_b(c, 12) && rvc_ird(c) && + rv_bf(c, 6, 2)) { /*I c.add -> add rd, rd, rs2 */ + return rv_i_r(12, 0, rvc_ird(c), rvc_ird(c), rv_bf(c, 6, 2), 0); + } + else if (rvc_f3(c) == 6) { /*I c.swsp -> sw rs2, offset(x2) */ + return rv_i_s(8, 2, 2, rv_bf(c, 6, 2), rvc_imm_css(c)); + } + else { /* illegal */ + return 0; + } + } + else { /* illegal */ + return 0; + } +} + +void rv_endcvt(rv_u8 * in, rv_u8 * out, rv_u32 width, rv_u32 is_store) { + if (!is_store && width == 1) + *out = in[0]; + else if (!is_store && width == 2) + *((rv_u16 *)out) = (rv_u16)(in[0] << 0) | (rv_u16)(in[1] << 8); + else if (!is_store && width == 4) + *((rv_u32 *)out) = (rv_u32)(in[0] << 0) | (rv_u32)(in[1] << 8) | + (rv_u32)(in[2] << 16) | (rv_u32)(in[3] << 24); + else if (width == 1) + out[0] = *in; + else if (width == 2) + out[0] = *(rv_u16 *)in >> 0 & 0xFF, out[1] = (*(rv_u16 *)in >> 8); + else + out[0] = *(rv_u32 *)in >> 0 & 0xFF, out[1] = *(rv_u32 *)in >> 8 & 0xFF, + out[2] = *(rv_u32 *)in >> 16 & 0xFF, + out[3] = *(rv_u32 *)in >> 24 & 0xFF; +} + +/* perform a bus access. access == RV_AW stores data. */ +static rv_u32 rv_bus( + rv * cpu, rv_u32 * va, rv_u8 * data, rv_u32 width, rv_access access) { + rv_u32 err, pa /* physical address */; + rv_u8 ledata[4]; + rv_endcvt(data, ledata, width, 1); + if (*va & (width - 1)) + return RV_BAD_ALIGN; + if ((err = rv_vmm(cpu, *va, &pa, access))) + return err; /* page or access fault */ + if (((pa + width - 1) ^ pa) & ~0xFFFU) /* page bound overrun */ + { + rv_u32 w0 /* load this many bytes from 1st page */ = + 0x1000 - (*va & 0xFFF); + if ((err = cpu->bus_cb(cpu->user, pa, ledata, access == RV_AW, w0))) + return err; + width -= w0, *va += w0, data += w0; + if ((err = rv_vmm(cpu, *va, &pa, RV_AW))) + return err; + } + if ((err = cpu->bus_cb(cpu->user, pa, ledata, access == RV_AW, width))) + return err; + rv_endcvt(ledata, data, width, 0); + return 0; +} + +/* instruction fetch */ +static rv_u32 rv_if(rv * cpu, rv_u32 * i, rv_u32 * tval) { + rv_u32 err, page = (cpu->pc ^ (cpu->pc + 3)) & ~0xFFFU, pc = cpu->pc; + if (cpu->pc & 2 || page) { /* perform fetch in two 2-byte fetches */ + rv_u32 ia /* first half of instruction */ = 0, ib /* second half */ = 0; + if ((err = rv_bus( + cpu, &pc, (rv_u8 *)&ia, 2, RV_AX))) /* fetch 1st half */ + goto error; + if (rv_isz(ia) == 4 && + (pc += 2, 1) && /* if instruction is 4 byte wide */ + (err = rv_bus( + cpu, &pc, (rv_u8 *)&ib, 2, RV_AX))) /* fetch 2nd half */ + goto error; /* need pc += 2 above for accurate {page}fault + traps */ + *i = (rv_u32)ia | (rv_u32)ib << 16U; + } + else if ((err = rv_bus(cpu, &pc, (rv_u8 *)i, 4, RV_AX))) /* 4-byte fetch */ + goto error; + cpu->next_pc = cpu->pc + rv_isz(*i); + *tval = *i; /* tval is original inst for illegal instruction traps */ + if (rv_isz(*i) < 4) + *i = rvc(*i & 0xFFFF); + return RV_OK; +error: + *tval = pc; /* tval is pc for instruction {page}fault traps */ + return err; +} + +/* service interrupts */ +static rv_u32 rv_service(rv * cpu) { + rv_u32 iidx /* interrupt number */, d /* delegated privilege */; + for (iidx = 12; iidx > 0; iidx--) { /* highest -> lowest priority */ + if (!(cpu->csr.mip & cpu->csr.mie & (1 << iidx))) + continue; /* interrupt not triggered or not enabled */ + d = (cpu->csr.mideleg & (1 << iidx)) ? RV_PSUPER : RV_PMACH; + if (d == cpu->priv ? rv_b(cpu->csr.mstatus, d) : (d > cpu->priv)) + return rv_trap(cpu, 0x80000000U + iidx, cpu->pc); + } + return RV_TRAP_NONE; +} + +/* single step */ +rv_u32 rv_step(rv * cpu) { + rv_u32 i, tval, err = rv_if(cpu, &i, &tval); /* fetch instruction into i */ + if (!++cpu->csr.cycle) + cpu->csr.cycleh++; /* add to cycle,cycleh with carry */ + if (err) + return rv_trap_bus(cpu, err, tval, RV_AX); /* instruction fetch error */ + if (rv_isz(i) != 4) + return rv_trap(cpu, RV_EILL, tval); /* instruction length invalid */ + if (rv_iopl(i) == 0) { + if (rv_ioph(i) == 0) { /*Q 00/000: LOAD */ + rv_u32 va /* virtual address */ = + rv_lr(cpu, rv_irs1(i)) + rv_iimm_i(i); + rv_u32 v /* loaded value */ = 0, w /* value width */, + sx /* sign ext. */; + w = 1 << (rv_if3(i) & 3), + sx = ~rv_if3(i) & 4; /*I lb, lh, lw, lbu, lhu */ + if ((err = rv_bus(cpu, &va, (rv_u8 *)&v, w, RV_AR))) + return rv_trap_bus(cpu, err, va, RV_AR); + if ((rv_if3(i) & 3) == 3) + return rv_trap( + cpu, RV_EILL, tval); /* ld instruction not supported */ + if (sx) + v = rv_signext(v, (w * 8 - 1)); + rv_sr(cpu, rv_ird(i), v); + } + else if (rv_ioph(i) == 1) { /*Q 01/000: STORE */ + rv_u32 va /* virtual address */ = + rv_lr(cpu, rv_irs1(i)) + rv_iimm_s(i); + rv_u32 w /* value width */ = 1 << (rv_if3(i) & 3); + rv_u32 y /* stored value */ = rv_lr(cpu, rv_irs2(i)); + if (rv_if3(i) > 2) /*I sb, sh, sw */ + return rv_trap( + cpu, RV_EILL, tval); /* sd instruction not supported */ + if ((err = rv_bus(cpu, &va, (rv_u8 *)&y, w, RV_AW))) + return rv_trap_bus(cpu, err, va, RV_AW); + } + else if (rv_ioph(i) == 3) { /*Q 11/000: BRANCH */ + rv_u32 a = rv_lr(cpu, rv_irs1(i)), b = rv_lr(cpu, rv_irs2(i)); + rv_u32 y /* comparison value */ = a - b; + rv_u32 zero = !y, sgn = rv_sgn(y), ovf = rv_ovf(a, b, y), + carry = y > a; + rv_u32 targ = cpu->pc + rv_iimm_b(i); /* computed branch target */ + if ((rv_if3(i) == 0 && zero) || /*I beq */ + (rv_if3(i) == 1 && !zero) || /*I bne */ + (rv_if3(i) == 4 && (sgn != ovf)) || /*I blt */ + (rv_if3(i) == 5 && (sgn == ovf)) || /*I bge */ + (rv_if3(i) == 6 && carry) || /*I bltu */ + (rv_if3(i) == 7 && !carry) /*I bgtu */ + ) { + cpu->next_pc = targ; /* take branch */ + } + else if (rv_if3(i) == 2 || rv_if3(i) == 3) + return rv_trap(cpu, RV_EILL, tval); + /* default: don't take branch [fall through here] */ + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else if (rv_iopl(i) == 1) { + if (rv_ioph(i) == 3 && rv_if3(i) == 0) { /*Q 11/001: JALR */ + rv_u32 target = (rv_lr(cpu, rv_irs1(i)) + rv_iimm_i(i)); /*I jalr */ + rv_sr(cpu, rv_ird(i), cpu->next_pc); + cpu->next_pc = target & ~1U; /* target is two-byte aligned */ + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else if (rv_iopl(i) == 3) { + if (rv_ioph(i) == 0) { /*Q 00/011: MISC-MEM */ + if (rv_if3(i) == 0) { /*I fence */ + rv_u32 fm = rv_bf(i, 31, 28); /* extract fm field */ + if (fm && fm != 8) + return rv_trap(cpu, RV_EILL, tval); + } + else if (rv_if3(i) == 1) { /*I fence.i */ + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else if (rv_ioph(i) == 1) { /*Q 01/011: AMO */ + rv_u32 va /* address */ = rv_lr(cpu, rv_irs1(i)); + rv_u32 b /* argument */ = rv_lr(cpu, rv_irs2(i)); + rv_u32 x /* loaded value */ = 0, y /* stored value */ = b; + rv_u32 l /* should load? */ = rv_if5(i) != 3, + s /* should store? */ = 1; + if (rv_bf(i, 14, 12) != 2) { /* width must be 2 */ + return rv_trap(cpu, RV_EILL, tval); + } + else { + if (l && (err = rv_bus(cpu, &va, (rv_u8 *)&x, 4, RV_AR))) + return rv_trap_bus(cpu, err, va, RV_AR); + if (rv_if5(i) == 0) /*I amoadd.w */ + y = x + b; + else if (rv_if5(i) == 1) /*I amoswap.w */ + y = b; + else if (rv_if5(i) == 2 && !b) /*I lr.w */ + cpu->res = va, cpu->res_valid = 1, s = 0; + else if (rv_if5(i) == 3) /*I sc.w */ + x = !(cpu->res_valid && cpu->res_valid-- && cpu->res == va), + s = !x; + else if (rv_if5(i) == 4) /*I amoxor.w */ + y = x ^ b; + else if (rv_if5(i) == 8) /*I amoor.w */ + y = x | b; + else if (rv_if5(i) == 12) /*I amoand.w */ + y = x & b; + else if (rv_if5(i) == 16) /*I amomin.w */ + y = rv_sgn(x - b) != rv_ovf(x, b, x - b) ? x : b; + else if (rv_if5(i) == 20) /*I amomax.w */ + y = rv_sgn(x - b) == rv_ovf(x, b, x - b) ? x : b; + else if (rv_if5(i) == 24) /*I amominu.w */ + y = (x - b) > x ? x : b; + else if (rv_if5(i) == 28) /*I amomaxu.w */ + y = (x - b) <= x ? x : b; + else + return rv_trap(cpu, RV_EILL, tval); + if (s && (err = rv_bus(cpu, &va, (rv_u8 *)&y, 4, RV_AW))) + return rv_trap_bus(cpu, err, va, RV_AW); + } + rv_sr(cpu, rv_ird(i), x); + } + else if (rv_ioph(i) == 3) { /*Q 11/011: JAL */ + rv_sr(cpu, rv_ird(i), cpu->next_pc); /*I jal */ + cpu->next_pc = cpu->pc + rv_iimm_j(i); + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else if (rv_iopl(i) == 4) { /* ALU section */ + if (rv_ioph(i) == 0 || /*Q 00/100: OP-IMM */ + rv_ioph(i) == 1) { /*Q 01/100: OP */ + rv_u32 a = rv_lr(cpu, rv_irs1(i)), + b = rv_ioph(i) ? rv_lr(cpu, rv_irs2(i)) : rv_iimm_i(i), + s /* alt. ALU op */ = + (rv_ioph(i) || rv_if3(i)) ? rv_b(i, 30) : 0, + y /* result */, sh /* shift amount */ = b & 0x1F; + if (!rv_ioph(i) || !rv_b(i, 25)) { + if (rv_if3(i) == 0) /*I add, addi, sub */ + y = s ? a - b : a + b; /* subtract if alt. op, + otherwise add */ + else if ((rv_if3(i) == 5 || rv_if3(i) == 1) && b >> 5 & 0x5F && + !rv_ioph(i)) + return rv_trap(cpu, RV_EILL, tval); /* shift too big! */ + else if (rv_if3(i) == 1) /*I sll, slli */ + y = a << sh; + else if (rv_if3(i) == 2) /*I slt, slti */ + y = rv_ovf(a, b, a - b) != rv_sgn(a - b); + else if (rv_if3(i) == 3) /*I sltu, sltiu */ + y = (a - b) > a; + else if (rv_if3(i) == 4) /*I xor, xori */ + y = a ^ b; + else if (rv_if3(i) == 5) /*I srl, srli, sra, srai */ + y = a >> (sh & 31) | (0U - (s && rv_sgn(a))) + << (31 - (sh & 31)); + else if (rv_if3(i) == 6) /*I or, ori */ + y = a | b; + else /*I and, andi */ + y = a & b; + } + else { + rv_u32 as /* sgn(a) */ = 0, bs /* sgn(b) */ = 0, ylo, + yhi /* result */; + if (rv_if3(i) < 4) { /*I mul, mulh, mulhsu, mulhu */ + if (rv_if3(i) < 3 && + rv_sgn(a)) /* a is signed iff f3 in {0, 1, 2} */ + a = ~a + 1, as = 1; /* two's complement */ + if (rv_if3(i) < 2 && + rv_sgn(b)) /* b is signed iff f3 in {0, 1} */ + b = ~b + 1, bs = 1; /* two's complement */ + ylo = rvm(a, b, &yhi); /* perform multiply */ + if (as != bs) /* invert output quantity if result <0 */ + ylo = ~ylo + 1, + yhi = ~yhi + !ylo; /* two's complement */ + y = rv_if3(i) ? yhi : ylo; /* return hi word if mulh, + otherwise lo */ + } + else { + if (rv_if3(i) == 4) /*I div */ + y = b ? (rv_u32)((rv_s32)a / (rv_s32)b) : (rv_u32)(-1); + else if (rv_if3(i) == 5) /*I divu */ + y = b ? (a / b) : (rv_u32)(-1); + else if (rv_if3(i) == 6) /*I rem */ + y = (rv_u32)((rv_s32)a % (rv_s32)b); + else /* if (rv_if3(i) == 8) */ /*I remu */ + y = a % b; + } /* all this because we don't have 64bits. worth it? + probably not B) */ + } + rv_sr(cpu, rv_ird(i), y); /* set register to ALU output */ + } + else if (rv_ioph(i) == 3) { /*Q 11/100: SYSTEM */ + rv_u32 csr /* CSR number */ = rv_iimm_iu(i), y /* result */; + rv_u32 s /* uimm */ = + rv_if3(i) & 4 ? rv_irs1(i) : rv_lr(cpu, rv_irs1(i)); + if ((rv_if3(i) & 3) == 1) { /*I csrrw, csrrwi */ + if (rv_irs1(i)) { /* perform CSR load */ + if (rv_csr_bus(cpu, csr, 0, &y)) /* load CSR into y */ + return rv_trap(cpu, RV_EILL, tval); + if (rv_ird(i)) + rv_sr(cpu, rv_ird(i), y); /* store y into rd */ + } + if (rv_csr_bus(cpu, csr, 1, &s)) /* set CSR to s */ + return rv_trap(cpu, RV_EILL, tval); + } + else if ((rv_if3(i) & 3) == 2) { /*I csrrs, csrrsi */ + if (rv_csr_bus(cpu, csr, 0, &y)) /* load CSR into y */ + return rv_trap(cpu, RV_EILL, tval); + rv_sr(cpu, rv_ird(i), y), y |= s; /* store y into rd */ + if (rv_irs1(i) && + rv_csr_bus(cpu, csr, 1, &y)) /* s|y into CSR */ + return rv_trap(cpu, RV_EILL, tval); + } + else if ((rv_if3(i) & 3) == 3) { /*I csrrc, csrrci */ + if (rv_csr_bus(cpu, csr, 0, &y)) /* load CSR into y */ + return rv_trap(cpu, RV_EILL, tval); + rv_sr(cpu, rv_ird(i), y), y &= ~s; /* store y into rd */ + if (rv_irs1(i) && + rv_csr_bus(cpu, csr, 1, &y)) /* ~s&y into CSR */ + return rv_trap(cpu, RV_EILL, tval); + } + else if (!rv_if3(i)) { + if (!rv_ird(i)) { + if (!rv_irs1(i) && rv_irs2(i) == 2 && + (rv_if7(i) == 8 || rv_if7(i) == 24)) { /*I mret, sret */ + rv_u32 xp /* instruction privilege */ = rv_if7(i) >> 3; + rv_u32 yp /* previous (incoming) privilege + [either mpp or spp] */ + = cpu->csr.mstatus >> (xp == RV_PMACH ? 11 : 8) & + xp; + rv_u32 xpie /* previous ie bit */ = + rv_b(cpu->csr.mstatus, 4 + xp); + rv_u32 mprv /* modify privilege */ = + rv_b(cpu->csr.mstatus, 17); + if (rv_b(cpu->csr.mstatus, 22) && xp == RV_PSUPER) + return rv_trap( + cpu, RV_EILL, tval); /* exception if tsr=1 */ + mprv *= yp == RV_PMACH; /* if y != m, mprv' = 0 */ + cpu->csr.mstatus &= + xp == RV_PMACH + ? 0xFFFDE777 /* {mpp, mie, mpie, mprv} + <- 0 */ + : 0xFFFDFEDD; /* {spp, sie, spie, + mprv} <- 0 */ + cpu->csr.mstatus |= xpie << xp /* xie <- xpie */ + | 1 << (4 + xp) /* xpie <- 1 */ + | mprv << 17; /* mprv <- mprv' */ + cpu->priv = yp; /* priv <- y */ + cpu->next_pc = + xp == RV_PMACH ? cpu->csr.mepc : cpu->csr.sepc; + } + else if (rv_irs2(i) == 5 && rv_if7(i) == 8) { /*I wfi */ + cpu->pc = cpu->next_pc; + return (err = rv_service(cpu)) == RV_TRAP_NONE + ? RV_TRAP_WFI + : err; + } + else if (rv_if7(i) == 9) { /*I sfence.vma */ + if (cpu->priv == RV_PSUPER && + (cpu->csr.mstatus & (1 << 20))) + return rv_trap(cpu, RV_EILL, tval); + cpu->tlb_valid = 0; + } + else if (!rv_irs1(i) && !rv_irs2(i) && + !rv_if7(i)) { /*I ecall */ + return rv_trap(cpu, RV_EUECALL + cpu->priv, cpu->pc); + } + else if (!rv_irs1(i) && rv_irs2(i) == 1 && !rv_if7(i)) { + return rv_trap(cpu, RV_EBP, cpu->pc); /*I ebreak */ + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else if (rv_iopl(i) == 5) { + if (rv_ioph(i) == 0) { /*Q 00/101: AUIPC */ + rv_sr(cpu, rv_ird(i), rv_iimm_u(i) + cpu->pc); /*I auipc */ + } + else if (rv_ioph(i) == 1) { /*Q 01/101: LUI */ + rv_sr(cpu, rv_ird(i), rv_iimm_u(i)); /*I lui */ + } + else + return rv_trap(cpu, RV_EILL, tval); + } + else + return rv_trap(cpu, RV_EILL, tval); + cpu->pc = cpu->next_pc; + if (cpu->csr.mip && (err = rv_service(cpu)) != RV_TRAP_NONE) + return err; + return RV_TRAP_NONE; /* reserved code -- no exception */ +} + +void rv_irq(rv * cpu, rv_cause cause) { + cpu->csr.mip &= ~(rv_u32)(RV_CSI | RV_CTI | RV_CEI); + cpu->csr.mip |= cause; +} diff --git a/save-rvasm/rv.h b/save-rvasm/rv.h new file mode 100644 index 0000000..52778b7 --- /dev/null +++ b/save-rvasm/rv.h @@ -0,0 +1,125 @@ +/* RV32I[MAC] emulator. + * see: https://github.com/riscv/riscv-isa-manual */ +#ifndef MN_RV_H +#define MN_RV_H + +#define RV_VERSION "1.0.1" + +/* Exception list. */ +#define RV_EIALIGN 0 /* Instruction alignment exception. */ +#define RV_EIFAULT 1 /* Instruction fault exception. */ +#define RV_EILL 2 /* Illegal instruction exception. */ +#define RV_EBP 3 /* Breakpoint. */ +#define RV_ELALIGN 4 /* Load alignment exception. */ +#define RV_ELFAULT 5 /* Load fault. */ +#define RV_ESALIGN 6 /* Store alignment exception. */ +#define RV_ESFAULT 7 /* Store fault. */ +#define RV_EUECALL 8 /* Environment call from U-mode. */ +#define RV_ESECALL 9 /* Environment call from S-mode. */ +#define RV_EMECALL 11 /* Environment call from M-mode. */ +#define RV_EIPAGE 12 /* Instruction page fault. */ +#define RV_ELPAGE 13 /* Load page fault. */ +#define RV_ESPAGE 15 /* Store page fault. */ + +#if __STDC__ && __STDC_VERSION__ >= 199901L /* Attempt to load stdint.h. */ +#include +#define RV_U8_TYPE uint8_t /* Yes, I know that these types are optional. */ +#define RV_U16_TYPE uint16_t /* They *usually* exist. Regardless, rv isn't */ +#define RV_S32_TYPE int32_t /* meant to be run on systems with */ +#define RV_U32_TYPE uint32_t /* CHAR_BIT != 8 or other weird integer specs. */ +#else +#ifdef __UINT8_TYPE__ /* If these are here, we might as well use \ + them. */ +#define RV_U8_TYPE __UINT8_TYPE__ +#define RV_U16_TYPE __UINT16_TYPE__ +#define RV_S32_TYPE __INT32_TYPE__ +#define RV_U32_TYPE __UINT32_TYPE__ +#else +#define RV_U8_TYPE unsigned char /* Assumption: CHAR_BIT == 8 */ +#define RV_U16_TYPE unsigned short /* Assumption: sizeof(ushort) == 2 */ +#define RV_S32_TYPE signed int /* Assumption: sizeof(sint) == 4 */ +#define RV_U32_TYPE unsigned int /* Assumption: sizeof(uint) == 4 */ +#endif /* (slight) deviations from c89. Sorry {TI, Cray, DEC, et. \ + al.} */ +#endif /* All I want for Christmas is C89 with stdint.h */ + +typedef RV_U8_TYPE rv_u8; +typedef RV_U16_TYPE rv_u16; +typedef RV_S32_TYPE rv_s32; +typedef RV_U32_TYPE rv_u32; + +/* Result type: one of {RV_OK, RV_BAD, RV_PAGEFAULT, RV_BAD_ALIGN} */ +typedef rv_u32 rv_res; + +#define RV_OK 0 +#define RV_BAD 1 +#define RV_BAD_ALIGN 2 +#define RV_PAGEFAULT 3 +#define RV_TRAP_NONE 0x80000010 +#define RV_TRAP_WFI 0x80000011 + +typedef struct rv_csr { + rv_u32 /* sstatus, */ sie, stvec, scounteren, sscratch, sepc, scause, stval, + sip, satp; + rv_u32 mstatus, misa, medeleg, mideleg, mie, mtvec, mcounteren, mstatush, + mscratch, mepc, mcause, mtval, mip, mtime, mtimeh, mvendorid, marchid, + mimpid, mhartid; + rv_u32 cycle, cycleh; +} rv_csr; + +typedef enum rv_priv { RV_PUSER = 0, RV_PSUPER = 1, RV_PMACH = 3 } rv_priv; +typedef enum rv_access { RV_AR = 1, RV_AW = 2, RV_AX = 4 } rv_access; +typedef enum rv_cause { RV_CSI = 8, RV_CTI = 128, RV_CEI = 512 } rv_cause; + +/* Memory access callback: data is input/output, return RV_BAD on + * fault. Accesses are always aligned to `width`. */ +typedef rv_res (*rv_bus_cb)( + void * user, rv_u32 addr, rv_u8 * data, rv_u32 is_store, rv_u32 width); + +typedef struct rv { + rv_bus_cb bus_cb; + void * user; + rv_u32 r[32]; /* registers */ + rv_u32 pc; /* program counter */ + rv_u32 next_pc; /* program counter for next cycle */ + rv_csr csr; /* csr state */ + rv_u32 priv; /* current privilege level*/ + rv_u32 res, res_valid; /* lr/sc reservation set */ + rv_u32 tlb_va, tlb_pte, tlb_valid, tlb_i; +} rv; + +/* Initialize CPU. You can call this again on `cpu` to reset it. */ +void rv_init(rv * cpu, void * user, rv_bus_cb bus_cb); + +/* Single-step CPU. Returns trap cause if trap occurred, else + * `RV_TRAP_NONE` */ +rv_u32 rv_step(rv * cpu); + +/* Trigger interrupt(s). */ +void rv_irq(rv * cpu, rv_cause cause); + +/* Utility function to convert between host<->LE. */ +void rv_endcvt(rv_u8 * in, rv_u8 * out, rv_u32 width, rv_u32 is_store); + +#endif + +/* Copyright (c) 2023 Max Nurzia + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +“Software”), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ diff --git a/save-rvasm/save-rvasm.c b/save-rvasm/save-rvasm.c new file mode 100644 index 0000000..094a647 --- /dev/null +++ b/save-rvasm/save-rvasm.c @@ -0,0 +1,374 @@ +#define CH32V003_I2C_IMPLEMENTATION +#define WS2812BSIMPLE_IMPLEMENTATION +#include "colors.h" +#include "driver.h" +#include "rv.h" +#include "ws2812b_simple.h" +#include "ch32v003_i2c.h" + +#include +#include + +// obtained from i2c_scan(), before shifting by 1 bit +#define EEPROM_ADDR 0x51 + +#define RAM_BASE 0x80000000 +#define RAM_SIZE 0x400 + +#define LED_PINS GPIOA, 2 + +rv_res bus_cb(void * user, rv_u32 addr, rv_u8 * data, rv_u32 is_store, rv_u32 width); + +// Just a printf wrapper +void display_all_registers(rv * cpu); + +#define num_of_instructions 32 +#define led_page_size_show 2 +#define num_of_led_page_show (num_of_instructions / led_page_size_show) + +// rv16 bit +#define size_of_one_saveprogram (num_of_instructions * sizeof(rv_u16)) + +#define page_size 64 +// range of byte that stores status of page[x] +#define init_status_addr_begin 0 +#define init_status_addr_end 7 +#define init_status_reg_size (init_status_addr_end - init_status_addr_begin + 1) + +#define init_status_format " %c " +#define init_status_data (uint8_t *)"IL000001" + +#define page_status_addr_begin 8 +#define page_status_addr_end 511 +#define page_status_reg_size (page_status_addr_end - page_status_addr_begin + 1) + +#define delay 1000 +#define matrix_hori 16 +#define program_store_page_no + +void rv_asm_routine(void); +rv_res bus_cb(void * user, rv_u32 addr, rv_u8 * data, rv_u32 is_store, rv_u32 width); +void display_all_registers(rv * cpu); +void init_storage(void); +void reset_storage(void); +uint8_t is_storage_initialized(void); + +void choose_save_program_page(void); +void choose_load_program_page(void); +void led_display_program_page_status(void); +void save_program(uint16_t program_no, rv_u16 * program); +void load_program(uint16_t program_no, rv_u16 * program); +void erase_all_program_saves(void); + +// print storage data to console +void print_status_storage(void); + +void print_status_storage(void) { + printf("Status storage data:\n"); + for (uint16_t addr = init_status_addr_begin; + addr < init_status_addr_begin + init_status_reg_size; addr++) { + uint8_t data = 0; + i2c_read(EEPROM_ADDR, addr, I2C_REGADDR_2B, &data, sizeof(data)); + printf(init_status_format, data); + } + for (uint16_t addr = page_status_addr_begin; + addr < page_status_addr_begin + page_status_reg_size; addr++) { + uint8_t data = 0; + i2c_read(EEPROM_ADDR, addr, I2C_REGADDR_2B, &data, sizeof(data)); + if (data) { + printf("%d ", addr); + } + else { + printf(" "); + } + if ((addr + 1) % matrix_hori == 0) { + printf("\n"); + } + } + printf("\n"); +} + +void add_no_page_ontop(uint16_t current_no_page) { + if (current_no_page < 0 || current_no_page > num_of_led_page_show) { + return; + } + for (uint16_t i = NUM_LEDS - 1; i >= NUM_LEDS - 1 - current_no_page; i--) { + set_color(i, (color_t){30, 30, 30}); + } +} + +rv_u16 program[num_of_instructions] = { + // smile mouth + // c.addi x5, 4 + 0x0291, + // c.slli x5, 4 + 0x0292, + // c.addi x5, 2 + 0x0289, + // c.slli x5, 4 + 0x0292, + // c.addi x5, 7 + 0x029d, + // c.slli x5, 4 + 0x0292, + // c.addi x5, 14 + 0x02b9, + // eyes + // c.addi x6, 4 + 0x0311, + // c.slli x6, 4 + 0x0312, + // c.addi x6, 2 + 0x0309, + // c.slli x6, 8 + 0x0322, + // ecall + 0x0073, + // 13-20: filler with zeros. + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + // 21-30: + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + // 31-32 + 0x0000, + 0x0000, +}; + +#define instruction_size 16 +typedef enum _which_focus { + focus_instruction_0 = 0, + focus_instruction_1 = 1, + none = 2, +} which_focus; + + +int main(void) { + SystemInit(); + ADC_init(); + printf("Hello, World!\n"); + + clear(); + WS2812BSimpleSend(LED_PINS, (uint8_t *)led_array, NUM_LEDS * 3); + Delay_Ms(delay); + i2c_init(); + printf("I2C Initialized\n"); + init_storage(); + + print_status_storage(); + + // display saved instructions + + // choose which save to load + + rv_asm_routine(); +} + +void instructionDisplay(int16_t index, which_focus focus) { + if (index < 0) { + return; + } + const uint16_t starting_index = index * led_page_size_show; + +#define default_code_color (color_t){.g = 255, .r = 0, .b = 0} + color_t code1_color = default_code_color; + if (focus == focus_instruction_0) { + code1_color = (color_t){.b = 255, .r = 0, .g = 0}; + } + color_t code2_color = default_code_color; + if (focus == focus_instruction_1) { + code2_color = (color_t){.b = 255, .r = 0, .g = 0}; + } + + // display 2 16bit instructions in led 0-15, 16-31 + for (int8_t bit = instruction_size - 1; bit >= 0; bit--) { + if (program[starting_index] & (1 << bit)) { + set_color(bit, code1_color); + } + } + for (int8_t bit = instruction_size - 1; bit >= 0; bit--) { + if (program[starting_index + 1] & (1 << bit)) { + set_color(bit + instruction_size, code2_color); + } + } +} + +const color_t white = {255, 255, 255}; + +void inputProgram(void) { + // input 2 16bit instructions in led 0-15, 16-31 + int16_t page_of_instructions = 0; + clear(); + instructionDisplay(page_of_instructions, none); + add_no_page_ontop(page_of_instructions); + WS2812BSimpleSend(LED_PINS, (uint8_t *)led_array, NUM_LEDS * 3); + while (1) { + int8_t input = matrix_pressed_two(); + Delay_Ms(120); + if (input == no_button_pressed) { + if (JOY_down_pressed()) { + page_of_instructions++; + if (page_of_instructions > num_of_led_page_show) { + page_of_instructions = 0; + } + printf("page: %d\n", page_of_instructions); + } + else if (JOY_up_pressed()) { + page_of_instructions--; + if (page_of_instructions < 0) { + page_of_instructions = num_of_led_page_show; + } + printf("page: %d\n", page_of_instructions); + } + else if (JOY_Y_pressed()) { + break; + } + else { + continue; + } + } + else { + int16_t instruction_index = (led_page_size_show * page_of_instructions) + + (input / instruction_size); + int8_t bit_index = input % instruction_size; + printf("instruction: %d, ", instruction_index); + printf("bit: %d, ", bit_index); + printf("program: %04x, ", program[instruction_index]); + program[instruction_index] ^= (1 << bit_index); + printf("new program: %04X\n", program[instruction_index]); + } + + + clear(); + instructionDisplay(page_of_instructions, none); + add_no_page_ontop(page_of_instructions); + WS2812BSimpleSend(LED_PINS, (uint8_t *)led_array, NUM_LEDS * 3); + + } +} + +#define register_r5_cpu_r_index 5 +#define register_r6_cpu_r_index 6 + +#define register_r5_color (color_t){0, 255, 0} +#define register_r6_color (color_t){0, 0, 255} + +void rv_asm_routine(void) { + inputProgram(); + + printf("Matrix Pressed\n"); + + rv_u8 mem[RAM_SIZE]; + rv cpu; + rv_init(&cpu, (void *)mem, &bus_cb); + memcpy((void *)mem, (void *)program, sizeof(program)); + + while (1) { + rv_u32 trap = rv_step(&cpu); + + clear(); + for (int8_t bit = instruction_size - 1; bit >= 0; bit--) { + // display register r10, r11 value at led 32-47, 48-63 + if (cpu.r[register_r5_cpu_r_index] & (1 << bit)) { + set_color(bit + 32, register_r5_color); + } + if (cpu.r[register_r6_cpu_r_index] & (1 << bit)) { + set_color(bit + 48, register_r6_color); + } + } + if (trap == RV_EMECALL) { + break; + } + uint8_t next_pos = (cpu.pc - RAM_BASE) / sizeof(program[0]); + + instructionDisplay((next_pos - 1) / led_page_size_show, + ((next_pos - 1) % led_page_size_show) == 0 ? focus_instruction_0 : focus_instruction_1); + WS2812BSimpleSend(LED_PINS, (uint8_t *)led_array, NUM_LEDS * 3); + + printf("PC: %lX, ", cpu.pc); + printf("Trap: %lX\n", trap); + printf("Opcode: %X, ", program[next_pos]); + printf("Next_pos: %d\n", next_pos); + + display_all_registers(&cpu); + + Delay_Ms(900); + + while (JOY_Y_pressed()) { + Delay_Ms(100); + printf("Waiting for Y to be released\n"); + } + } + + printf("Environment call @ %lX\n", cpu.csr.mepc); + display_all_registers(&cpu); + + while (!JOY_Y_pressed()) + ; + Delay_Ms(1000); + NVIC_SystemReset(); +} + +rv_res bus_cb(void * user, rv_u32 addr, rv_u8 * data, rv_u32 is_store, rv_u32 width) { + rv_u8 * mem = (rv_u8 *)user + addr - RAM_BASE; + if (addr < RAM_BASE || addr + width >= RAM_BASE + RAM_SIZE) + return RV_BAD; + memcpy(is_store ? mem : data, is_store ? data : mem, width); + return RV_OK; +} + +void display_all_registers(rv * cpu) { + for (int i = 0; i < 32; i++) { + if (cpu->r[i] != 0) + printf("r%d: %ld ", i, cpu->r[i]); + } + printf("\n"); +} + +void init_storage(void) { + if (!is_storage_initialized()) { + reset_storage(); + printf("Storage initialized\n"); + } + else { + printf("Storage already initialized\n"); + } +} + +uint8_t is_storage_initialized(void) { + uint8_t data[init_status_reg_size]; + i2c_read( + EEPROM_ADDR, init_status_addr_begin, I2C_REGADDR_2B, data, init_status_reg_size); + for (uint8_t i = 0; i < init_status_reg_size; i++) { + if (data[i] != *(init_status_data + i)) { + return 0; + } + } + return 1; +} + +void reset_storage(void) { + i2c_write(EEPROM_ADDR, init_status_addr_begin, I2C_REGADDR_2B, init_status_data, + init_status_reg_size); + Delay_Ms(3); + for (uint16_t addr = page_status_addr_begin; + addr < page_status_addr_begin + page_status_reg_size; addr++) { + i2c_write(EEPROM_ADDR, addr, I2C_REGADDR_2B, (uint8_t[]){0}, sizeof(uint8_t)); + Delay_Ms(3); + } + printf("Storage reset\n"); +}