diff --git a/Makefile b/Makefile index 11e3183..f1eeba4 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ all: (cd host && make) (cd host-mini && make) (cd host-parallel && make) + (cd host-arduino && make) (cd apps && make) clean: @@ -12,6 +13,7 @@ clean: (cd host && make clean) (cd host-mini && make clean) (cd host-parallel && make clean) + (cd host-arduino && make clean) (cd apps && make clean) test: diff --git a/host-arduino/Makefile b/host-arduino/Makefile new file mode 100644 index 0000000..ab12a93 --- /dev/null +++ b/host-arduino/Makefile @@ -0,0 +1,15 @@ +# The purpose of this Makefile is to auto-generate the Arduino example +# Arduino cannot set -DUVM32_MEMORY_SIZE, so add a config file +# Arduino expects .cpp files + +make: + echo '#include "config.h"' > uvm32.cpp + cat ../uvm32/uvm32.c >> uvm32.cpp + cp ../uvm32/uvm32.h uvm32.h + cp ../uvm32/mini-rv32ima.h mini-rv32ima.h + cp ../common/uvm32_common_custom.h uvm32_common_custom.h + cp ../common/uvm32_sys.h uvm32_sys.h + xxd -i ../precompiled/mandel.bin | sed -e "s/unsigned char/const unsigned char/" > mandel.h + +clean: + rm -f uvm32.cpp uvm32.h mini-rv32ima.h uvm32_common_custom.h uvm32_sys.h mandel.h diff --git a/host-arduino/common/uvm32_common_custom.h b/host-arduino/common/uvm32_common_custom.h deleted file mode 100644 index 69760cc..0000000 --- a/host-arduino/common/uvm32_common_custom.h +++ /dev/null @@ -1,12 +0,0 @@ -// Definitions needed by both host and target - -// syscalls for exposed host functions, start at 0 -#define UVM32_SYSCALL_PUTC 0x00000000 -#define UVM32_SYSCALL_GETC 0x00000001 -#define UVM32_SYSCALL_PRINT 0x00000002 -#define UVM32_SYSCALL_PRINTLN 0x00000003 -#define UVM32_SYSCALL_PRINTDEC 0x00000004 -#define UVM32_SYSCALL_PRINTHEX 0x00000005 -#define UVM32_SYSCALL_MILLIS 0x00000006 -#define UVM32_SYSCALL_PRINTBUF 0x00000007 - diff --git a/host-arduino/common/uvm32_sys.h b/host-arduino/common/uvm32_sys.h deleted file mode 100644 index 82dec7d..0000000 --- a/host-arduino/common/uvm32_sys.h +++ /dev/null @@ -1,5 +0,0 @@ -// System provided UVM32_SYSCALLs, start at 0x10000000 -#define UVM32_SYSCALL_HALT 0x1000000 -#define UVM32_SYSCALL_YIELD 0x1000001 - -#include "uvm32_common_custom.h" diff --git a/host-arduino/host-arduino.ino b/host-arduino/host-arduino.ino index 9abc23c..20e85c1 100644 --- a/host-arduino/host-arduino.ino +++ b/host-arduino/host-arduino.ino @@ -1,32 +1,8 @@ #include "config.h" #include "uvm32.h" -#include "common/uvm32_common_custom.h" +#include "uvm32_common_custom.h" -uint8_t rom[] = { // mandel.bin - 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, - 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff, - 0x13, 0x07, 0xf0, 0x01, 0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01, - 0x13, 0x08, 0xd5, 0xcc, 0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6, - 0x93, 0x83, 0x35, 0xb3, 0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09, - 0x13, 0x0e, 0x03, 0x00, 0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00, - 0x13, 0x0f, 0x00, 0x00, 0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, - 0x93, 0x06, 0x00, 0x02, 0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04, - 0x33, 0x05, 0xff, 0x01, 0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00, - 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02, - 0x33, 0x0f, 0xff, 0x41, 0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03, - 0x13, 0x5f, 0xc5, 0x00, 0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00, - 0x93, 0x86, 0x16, 0x00, 0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00, - 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, - 0x13, 0x0e, 0x1e, 0x09, 0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, - 0x93, 0x05, 0x00, 0x00, 0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, - 0x13, 0x08, 0x98, 0x19, 0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80, - 0x13, 0x05, 0x05, 0x10, 0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x03, 0x24, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x01, - 0x67, 0x80, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, - 0x72, 0x6c, 0x64, 0x00 -}; +#include "mandel.h" uvm32_state_t vmst; uvm32_evt_t evt; @@ -86,16 +62,13 @@ void loop(void) { Serial.println(evt.data.err.errstr); isrunning = false; break; - case UVM32_EVT_YIELD: - break; } } else { - Serial.println("Starting VM"); + Serial.println("Running VM"); // setup vm uvm32_init(&vmst); - uvm32_load(&vmst, rom, sizeof(rom)); + uvm32_load(&vmst, ___precompiled_mandel_bin, ___precompiled_mandel_bin_len); isrunning = true; - delay(2000); } return; diff --git a/host-arduino/mini-rv32ima.h b/host-arduino/mini-rv32ima.h deleted file mode 100644 index 531648e..0000000 --- a/host-arduino/mini-rv32ima.h +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2022 Charles Lohr, you may use this file or any portions herein under any of the BSD, MIT, or CC0 licenses. - -#ifndef _MINI_RV32IMAH_H -#define _MINI_RV32IMAH_H - -/** - To use mini-rv32ima.h for the bare minimum, the following: - - #define MINI_RV32_RAM_SIZE ram_amt - #define MINIRV32_IMPLEMENTATION - - #include "mini-rv32ima.h" - - Though, that's not _that_ interesting. You probably want I/O! - - - Notes: - * There is a dedicated CLNT at 0x10000000. - * There is free MMIO from there to 0x12000000. - * You can put things like a UART, or whatever there. - * Feel free to override any of the functionality with macros. -*/ - -#ifndef MINIRV32_DECORATE - #define MINIRV32_DECORATE static -#endif - -#ifndef MINIRV32_RAM_IMAGE_OFFSET - #define MINIRV32_RAM_IMAGE_OFFSET 0x80000000 -#endif - -#ifndef MINIRV32_MMIO_RANGE - #define MINIRV32_MMIO_RANGE(n) (0x10000000 <= (n) && (n) < 0x12000000) -#endif - -#ifndef MINIRV32_POSTEXEC - #define MINIRV32_POSTEXEC(...); -#endif - -#ifndef MINIRV32_HANDLE_MEM_STORE_CONTROL - #define MINIRV32_HANDLE_MEM_STORE_CONTROL(...); -#endif - -#ifndef MINIRV32_HANDLE_MEM_LOAD_CONTROL - #define MINIRV32_HANDLE_MEM_LOAD_CONTROL(...); -#endif - -#ifndef MINIRV32_OTHERCSR_WRITE - #define MINIRV32_OTHERCSR_WRITE(...); -#endif - -#ifndef MINIRV32_OTHERCSR_READ - #define MINIRV32_OTHERCSR_READ(...); -#endif - -#ifndef MINIRV32_CUSTOM_MEMORY_BUS - #define MINIRV32_STORE4( ofs, val ) *(uint32_t*)(image + ofs) = val - #define MINIRV32_STORE2( ofs, val ) *(uint16_t*)(image + ofs) = val - #define MINIRV32_STORE1( ofs, val ) *(uint8_t*)(image + ofs) = val - #define MINIRV32_LOAD4( ofs ) *(uint32_t*)(image + ofs) - #define MINIRV32_LOAD2( ofs ) *(uint16_t*)(image + ofs) - #define MINIRV32_LOAD1( ofs ) *(uint8_t*)(image + ofs) - #define MINIRV32_LOAD2_SIGNED( ofs ) *(int16_t*)(image + ofs) - #define MINIRV32_LOAD1_SIGNED( ofs ) *(int8_t*)(image + ofs) -#endif - -// As a note: We quouple-ify these, because in HLSL, we will be operating with -// uint4's. We are going to uint4 data to/from system RAM. -// -// We're going to try to keep the full processor state to 12 x uint4. -struct MiniRV32IMAState -{ - uint32_t regs[32]; - - uint32_t pc; - uint32_t mstatus; - uint32_t cyclel; - uint32_t cycleh; - - uint32_t timerl; - uint32_t timerh; - uint32_t timermatchl; - uint32_t timermatchh; - - uint32_t mscratch; - uint32_t mtvec; - uint32_t mie; - uint32_t mip; - - uint32_t mepc; - uint32_t mtval; - uint32_t mcause; - - // Note: only a few bits are used. (Machine = 3, User = 0) - // Bits 0..1 = privilege. - // Bit 2 = WFI (Wait for interrupt) - // Bit 3+ = Load/Store reservation LSBs. - uint32_t extraflags; -}; - -#ifndef MINIRV32_STEPPROTO -MINIRV32_DECORATE int32_t MiniRV32IMAStep(void *userdata, struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count ); -#endif - -#ifdef MINIRV32_IMPLEMENTATION - -#ifndef MINIRV32_CUSTOM_INTERNALS -#define CSR( x ) state->x -#define SETCSR( x, val ) { state->x = val; } -#define REG( x ) state->regs[x] -#define REGSET( x, val ) { state->regs[x] = val; } -#endif - -#ifndef MINIRV32_STEPPROTO -MINIRV32_DECORATE int32_t MiniRV32IMAStep(void *userdata, struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count ) -#else -MINIRV32_STEPPROTO -#endif -{ - uint32_t new_timer = CSR( timerl ) + elapsedUs; - if( new_timer < CSR( timerl ) ) CSR( timerh )++; - CSR( timerl ) = new_timer; - - // Handle Timer interrupt. - if( ( CSR( timerh ) > CSR( timermatchh ) || ( CSR( timerh ) == CSR( timermatchh ) && CSR( timerl ) > CSR( timermatchl ) ) ) && ( CSR( timermatchh ) || CSR( timermatchl ) ) ) - { - CSR( extraflags ) &= ~4; // Clear WFI - CSR( mip ) |= 1<<7; //MTIP of MIP // https://stackoverflow.com/a/61916199/2926815 Fire interrupt. - } - else - CSR( mip ) &= ~(1<<7); - - // If WFI, don't run processor. - if( CSR( extraflags ) & 4 ) - return 1; - - uint32_t trap = 0; - uint32_t rval = 0; - uint32_t pc = CSR( pc ); - uint32_t cycle = CSR( cyclel ); - - if( ( CSR( mip ) & (1<<7) ) && ( CSR( mie ) & (1<<7) /*mtie*/ ) && ( CSR( mstatus ) & 0x8 /*mie*/) ) - { - // Timer interrupt. - trap = 0x80000007; - pc -= 4; - } - else // No timer interrupt? Execute a bunch of instructions. - for( int icount = 0; icount < count; icount++ ) - { - uint32_t ir = 0; - rval = 0; - cycle++; - uint32_t ofs_pc = pc - MINIRV32_RAM_IMAGE_OFFSET; - - if( ofs_pc >= MINI_RV32_RAM_SIZE ) - { - trap = 1 + 1; // Handle access violation on instruction read. - break; - } - else if( ofs_pc & 3 ) - { - trap = 1 + 0; //Handle PC-misaligned access - break; - } - else - { - ir = MINIRV32_LOAD4( ofs_pc ); - uint32_t rdid = (ir >> 7) & 0x1f; - - switch( ir & 0x7f ) - { - case 0x37: // LUI (0b0110111) - rval = ( ir & 0xfffff000 ); - break; - case 0x17: // AUIPC (0b0010111) - rval = pc + ( ir & 0xfffff000 ); - break; - case 0x6F: // JAL (0b1101111) - { - int32_t reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); - if( reladdy & 0x00100000 ) reladdy |= 0xffe00000; // Sign extension. - rval = pc + 4; - pc = pc + reladdy - 4; - break; - } - case 0x67: // JALR (0b1100111) - { - uint32_t imm = ir >> 20; - int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0); - rval = pc + 4; - pc = ( (REG( (ir >> 15) & 0x1f ) + imm_se) & ~1) - 4; - break; - } - case 0x63: // Branch (0b1100011) - { - uint32_t immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12); - if( immm4 & 0x1000 ) immm4 |= 0xffffe000; - int32_t rs1 = REG((ir >> 15) & 0x1f); - int32_t rs2 = REG((ir >> 20) & 0x1f); - immm4 = pc + immm4 - 4; - rdid = 0; - switch( ( ir >> 12 ) & 0x7 ) - { - // BEQ, BNE, BLT, BGE, BLTU, BGEU - case 0: if( rs1 == rs2 ) pc = immm4; break; - case 1: if( rs1 != rs2 ) pc = immm4; break; - case 4: if( rs1 < rs2 ) pc = immm4; break; - case 5: if( rs1 >= rs2 ) pc = immm4; break; //BGE - case 6: if( (uint32_t)rs1 < (uint32_t)rs2 ) pc = immm4; break; //BLTU - case 7: if( (uint32_t)rs1 >= (uint32_t)rs2 ) pc = immm4; break; //BGEU - default: trap = (2+1); - } - break; - } - case 0x03: // Load (0b0000011) - { - uint32_t rs1 = REG((ir >> 15) & 0x1f); - uint32_t imm = ir >> 20; - int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0); - uint32_t rsval = rs1 + imm_se; - - rsval -= MINIRV32_RAM_IMAGE_OFFSET; - if( rsval >= MINI_RV32_RAM_SIZE-3 ) - { - rsval += MINIRV32_RAM_IMAGE_OFFSET; - if( MINIRV32_MMIO_RANGE( rsval ) ) // UART, CLNT - { - MINIRV32_HANDLE_MEM_LOAD_CONTROL( rsval, rval ); - } - else - { - trap = (5+1); - rval = rsval; - } - } - else - { - switch( ( ir >> 12 ) & 0x7 ) - { - //LB, LH, LW, LBU, LHU - case 0: rval = MINIRV32_LOAD1_SIGNED( rsval ); break; - case 1: rval = MINIRV32_LOAD2_SIGNED( rsval ); break; - case 2: rval = MINIRV32_LOAD4( rsval ); break; - case 4: rval = MINIRV32_LOAD1( rsval ); break; - case 5: rval = MINIRV32_LOAD2( rsval ); break; - default: trap = (2+1); - } - } - break; - } - case 0x23: // Store 0b0100011 - { - uint32_t rs1 = REG((ir >> 15) & 0x1f); - uint32_t rs2 = REG((ir >> 20) & 0x1f); - uint32_t addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 ); - if( addy & 0x800 ) addy |= 0xfffff000; - addy += rs1 - MINIRV32_RAM_IMAGE_OFFSET; - rdid = 0; - - if( addy >= MINI_RV32_RAM_SIZE-3 ) - { - addy += MINIRV32_RAM_IMAGE_OFFSET; - if( MINIRV32_MMIO_RANGE( addy ) ) - { - MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, rs2 ); - } - else - { - trap = (7+1); // Store access fault. - rval = addy; - } - } - else - { - switch( ( ir >> 12 ) & 0x7 ) - { - //SB, SH, SW - case 0: MINIRV32_STORE1( addy, rs2 ); break; - case 1: MINIRV32_STORE2( addy, rs2 ); break; - case 2: MINIRV32_STORE4( addy, rs2 ); break; - default: trap = (2+1); - } - } - break; - } - case 0x13: // Op-immediate 0b0010011 - case 0x33: // Op 0b0110011 - { - uint32_t imm = ir >> 20; - imm = imm | (( imm & 0x800 )?0xfffff000:0); - uint32_t rs1 = REG((ir >> 15) & 0x1f); - uint32_t is_reg = !!( ir & 0x20 ); - uint32_t rs2 = is_reg ? REG(imm & 0x1f) : imm; - - if( is_reg && ( ir & 0x02000000 ) ) - { - switch( (ir>>12)&7 ) //0x02000000 = RV32M - { - case 0: rval = rs1 * rs2; break; // MUL -#ifndef CUSTOM_MULH // If compiling on a system that doesn't natively, or via libgcc support 64-bit math. - case 1: rval = ((int64_t)((int32_t)rs1) * (int64_t)((int32_t)rs2)) >> 32; break; // MULH - case 2: rval = ((int64_t)((int32_t)rs1) * (uint64_t)rs2) >> 32; break; // MULHSU - case 3: rval = ((uint64_t)rs1 * (uint64_t)rs2) >> 32; break; // MULHU -#else - CUSTOM_MULH -#endif - case 4: if( rs2 == 0 ) rval = -1; else rval = ((int32_t)rs1 == INT32_MIN && (int32_t)rs2 == -1) ? rs1 : ((int32_t)rs1 / (int32_t)rs2); break; // DIV - case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU - case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int32_t)rs1 == INT32_MIN && (int32_t)rs2 == -1) ? 0 : ((uint32_t)((int32_t)rs1 % (int32_t)rs2)); break; // REM - case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU - } - } - else - { - switch( (ir>>12)&7 ) // These could be either op-immediate or op commands. Be careful. - { - case 0: rval = (is_reg && (ir & 0x40000000) ) ? ( rs1 - rs2 ) : ( rs1 + rs2 ); break; - case 1: rval = rs1 << (rs2 & 0x1F); break; - case 2: rval = (int32_t)rs1 < (int32_t)rs2; break; - case 3: rval = rs1 < rs2; break; - case 4: rval = rs1 ^ rs2; break; - case 5: rval = (ir & 0x40000000 ) ? ( ((int32_t)rs1) >> (rs2 & 0x1F) ) : ( rs1 >> (rs2 & 0x1F) ); break; - case 6: rval = rs1 | rs2; break; - case 7: rval = rs1 & rs2; break; - } - } - break; - } - case 0x0f: // 0b0001111 - rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl. - break; - case 0x73: // Zifencei+Zicsr (0b1110011) - { - uint32_t csrno = ir >> 20; - uint32_t microop = ( ir >> 12 ) & 0x7; - if( (microop & 3) ) // It's a Zicsr function. - { - int rs1imm = (ir >> 15) & 0x1f; - uint32_t rs1 = REG(rs1imm); - uint32_t writeval = rs1; - - // https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf - // Generally, support for Zicsr - switch( csrno ) - { - case 0x340: rval = CSR( mscratch ); break; - case 0x305: rval = CSR( mtvec ); break; - case 0x304: rval = CSR( mie ); break; - case 0xC00: rval = cycle; break; - case 0x344: rval = CSR( mip ); break; - case 0x341: rval = CSR( mepc ); break; - case 0x300: rval = CSR( mstatus ); break; //mstatus - case 0x342: rval = CSR( mcause ); break; - case 0x343: rval = CSR( mtval ); break; - case 0xf11: rval = 0xff0ff0ff; break; //mvendorid - case 0x301: rval = 0x40401101; break; //misa (XLEN=32, IMA+X) - //case 0x3B0: rval = 0; break; //pmpaddr0 - //case 0x3a0: rval = 0; break; //pmpcfg0 - //case 0xf12: rval = 0x00000000; break; //marchid - //case 0xf13: rval = 0x00000000; break; //mimpid - //case 0xf14: rval = 0x00000000; break; //mhartid - default: - MINIRV32_OTHERCSR_READ( csrno, rval ); - break; - } - - switch( microop ) - { - case 1: writeval = rs1; break; //CSRRW - case 2: writeval = rval | rs1; break; //CSRRS - case 3: writeval = rval & ~rs1; break; //CSRRC - case 5: writeval = rs1imm; break; //CSRRWI - case 6: writeval = rval | rs1imm; break; //CSRRSI - case 7: writeval = rval & ~rs1imm; break; //CSRRCI - } - - switch( csrno ) - { - case 0x340: SETCSR( mscratch, writeval ); break; - case 0x305: SETCSR( mtvec, writeval ); break; - case 0x304: SETCSR( mie, writeval ); break; - case 0x344: SETCSR( mip, writeval ); break; - case 0x341: SETCSR( mepc, writeval ); break; - case 0x300: SETCSR( mstatus, writeval ); break; //mstatus - case 0x342: SETCSR( mcause, writeval ); break; - case 0x343: SETCSR( mtval, writeval ); break; - //case 0x3a0: break; //pmpcfg0 - //case 0x3B0: break; //pmpaddr0 - //case 0xf11: break; //mvendorid - //case 0xf12: break; //marchid - //case 0xf13: break; //mimpid - //case 0xf14: break; //mhartid - //case 0x301: break; //misa - default: - MINIRV32_OTHERCSR_WRITE( csrno, writeval ); - break; - } - } - else if( microop == 0x0 ) // "SYSTEM" 0b000 - { - rdid = 0; - if( ( ( csrno & 0xff ) == 0x02 ) ) // MRET - { - //https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf - //Table 7.6. MRET then in mstatus/mstatush sets MPV=0, MPP=0, MIE=MPIE, and MPIE=1. La - // Should also update mstatus to reflect correct mode. - uint32_t startmstatus = CSR( mstatus ); - uint32_t startextraflags = CSR( extraflags ); - SETCSR( mstatus , (( startmstatus & 0x80) >> 4) | ((startextraflags&3) << 11) | 0x80 ); - SETCSR( extraflags, (startextraflags & ~3) | ((startmstatus >> 11) & 3) ); - pc = CSR( mepc ) -4; - } else { - switch (csrno) { - case 0: - trap = ( CSR( extraflags ) & 3) ? (11+1) : (8+1); // ECALL; 8 = "Environment call from U-mode"; 11 = "Environment call from M-mode" - break; - case 1: - trap = (3+1); break; // EBREAK 3 = "Breakpoint" - case 0x105: //WFI (Wait for interrupts) - CSR( mstatus ) |= 8; //Enable interrupts - CSR( extraflags ) |= 4; //Infor environment we want to go to sleep. - SETCSR( pc, pc + 4 ); - return 1; - default: - trap = (2+1); break; // Illegal opcode. - } - } - } - else - trap = (2+1); // Note micrrop 0b100 == undefined. - break; - } - case 0x2f: // RV32A (0b00101111) - { - uint32_t rs1 = REG((ir >> 15) & 0x1f); - uint32_t rs2 = REG((ir >> 20) & 0x1f); - uint32_t irmid = ( ir>>27 ) & 0x1f; - - rs1 -= MINIRV32_RAM_IMAGE_OFFSET; - - // We don't implement load/store from UART or CLNT with RV32A here. - - if( rs1 >= MINI_RV32_RAM_SIZE-3 ) - { - trap = (7+1); //Store/AMO access fault - rval = rs1 + MINIRV32_RAM_IMAGE_OFFSET; - } - else - { - rval = MINIRV32_LOAD4( rs1 ); - - // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c - uint32_t dowrite = 1; - switch( irmid ) - { - case 2: //LR.W (0b00010) - dowrite = 0; - CSR( extraflags ) = (CSR( extraflags ) & 0x07) | (rs1<<3); - break; - case 3: //SC.W (0b00011) (Make sure we have a slot, and, it's valid) - rval = ( CSR( extraflags ) >> 3 != ( rs1 & 0x1fffffff ) ); // Validate that our reservation slot is OK. - dowrite = !rval; // Only write if slot is valid. - break; - case 1: break; //AMOSWAP.W (0b00001) - case 0: rs2 += rval; break; //AMOADD.W (0b00000) - case 4: rs2 ^= rval; break; //AMOXOR.W (0b00100) - case 12: rs2 &= rval; break; //AMOAND.W (0b01100) - case 8: rs2 |= rval; break; //AMOOR.W (0b01000) - case 16: rs2 = ((int32_t)rs2<(int32_t)rval)?rs2:rval; break; //AMOMIN.W (0b10000) - case 20: rs2 = ((int32_t)rs2>(int32_t)rval)?rs2:rval; break; //AMOMAX.W (0b10100) - case 24: rs2 = (rs2rval)?rs2:rval; break; //AMOMAXU.W (0b11100) - default: trap = (2+1); dowrite = 0; break; //Not supported. - } - if( dowrite ) MINIRV32_STORE4( rs1, rs2 ); - } - break; - } - default: trap = (2+1); // Fault: Invalid opcode. - } - - // If there was a trap, do NOT allow register writeback. - if( trap ) { - SETCSR( pc, pc ); - MINIRV32_POSTEXEC( pc, ir, trap ); - break; - } - - if( rdid ) - { - REGSET( rdid, rval ); // Write back register. - } - } - - MINIRV32_POSTEXEC( pc, ir, trap ); - - pc += 4; - } - - // Handle traps and interrupts. - if( trap ) - { - if( trap & 0x80000000 ) // If prefixed with 1 in MSB, it's an interrupt, not a trap. - { - SETCSR( mcause, trap ); - SETCSR( mtval, 0 ); - pc += 4; // PC needs to point to where the PC will return to. - } - else - { - SETCSR( mcause, trap - 1 ); - SETCSR( mtval, (trap > 5 && trap <= 8)? rval : pc ); - } - SETCSR( mepc, pc ); //TRICKY: The kernel advances mepc automatically. - //CSR( mstatus ) & 8 = MIE, & 0x80 = MPIE - // On an interrupt, the system moves current MIE into MPIE - SETCSR( mstatus, (( CSR( mstatus ) & 0x08) << 4) | (( CSR( extraflags ) & 3 ) << 11) ); - pc = (CSR( mtvec ) - 4); - - // If trapping, always enter machine mode. - CSR( extraflags ) |= 3; - - trap = 0; - pc += 4; - } - - if( CSR( cyclel ) > cycle ) CSR( cycleh )++; - SETCSR( cyclel, cycle ); - SETCSR( pc, pc ); - return 0; -} - -#endif - -#endif - - diff --git a/host-arduino/uvm32.cpp b/host-arduino/uvm32.cpp deleted file mode 100644 index f93ab92..0000000 --- a/host-arduino/uvm32.cpp +++ /dev/null @@ -1,295 +0,0 @@ -#include "config.h" - -#define MINIRV32_IMPLEMENTATION -#include "uvm32.h" - -#ifndef UVM32_MEMORY_SIZE -#error Define UVM32_MEMORY_SIZE -#endif - -#define UVM32_NULL (void *)0 - -// On an invalid operation, an error is set in uvm32_state_t, but a valid pointer still needs to be temporarily used -static uint32_t garbage; - -// magic value for stack canary -#define STACK_CANARY_VALUE 0x42 - -#ifndef UVM32_MEMCPY -#define UVM32_MEMCPY uvm32_memcpy -void uvm32_memcpy(void *dst, const void *src, int len) { - uint8_t *d = (uint8_t *)dst; - const uint8_t *s = (const uint8_t *)src; - while(len--) { - *(d++) = *(s++); - } -} -#endif - -#ifndef UVM32_MEMSET -#define UVM32_MEMSET uvm32_memset -void uvm32_memset(void *buf, int c, int len) { - uint8_t *b = (uint8_t *)buf; - while(len--) { - *(b++) = c; - } -} -#endif - -#include "mini-rv32ima.h" - -#define X(name) #name, -static const char *errNames[] = { - LIST_OF_UVM32_ERRS -}; -#undef X - -static void setup_err_evt(uvm32_state_t *vmst, uvm32_evt_t *evt) { - evt->typ = UVM32_EVT_ERR; - evt->data.err.errcode = vmst->err; - evt->data.err.errstr = errNames[vmst->err]; -} - -static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) { - if (vmst->status == UVM32_STATUS_ERROR) { - // always stay in error state until a uvm32_init() - return; - } else { - vmst->status = newStatus; - } -} - -static void setStatusErr(uvm32_state_t *vmst, uvm32_err_t err) { - // if already in error state, stay in the first error state - if (vmst->status != UVM32_STATUS_ERROR) { - setStatus(vmst, UVM32_STATUS_ERROR); - vmst->err = err; - } -} - -void uvm32_init(uvm32_state_t *vmst) { - UVM32_MEMSET(vmst, 0x00, sizeof(uvm32_state_t)); - vmst->status = UVM32_STATUS_PAUSED; - - vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET; - // https://projectf.io/posts/riscv-cheat-sheet/ - // setup stack pointer - // la sp, _sstack - // addi sp,sp,-16 - vmst->core.regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE) - 16; - vmst->core.regs[10] = 0x00; //hart ID - vmst->core.regs[11] = 0; - vmst->core.extraflags |= 3; // Machine-mode. -} - -bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len) { - if (len > UVM32_MEMORY_SIZE) { - // too big - return false; - } - - UVM32_MEMCPY(vmst->memory, rom, len); - vmst->stack_canary = (uint8_t *)UVM32_NULL; - - return true; -} - -// Read C-string up to terminator and return len,ptr -bool get_safeptr_null_terminated(uvm32_state_t *vmst, uint32_t addr, uvm32_evt_syscall_buf_t *buf) { - uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - uint32_t p = ptrstart; - if (p >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = (uint8_t *)UVM32_NULL; - buf->len = 0; - return false; - } - while(vmst->memory[p] != '\0') { - p++; - if (p >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = (uint8_t *)UVM32_NULL; - buf->len = 0; - return false; - } - } - buf->ptr = &vmst->memory[ptrstart]; - buf->len = p - ptrstart; - return true; -} - -bool get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_syscall_buf_t *buf) { - uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - if ((ptrstart > UVM32_MEMORY_SIZE) || (ptrstart + len >= UVM32_MEMORY_SIZE)) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = (uint8_t *)UVM32_NULL; - buf->len = 0; - return false; - } - buf->ptr = &vmst->memory[ptrstart]; - buf->len = len; - return true; -} - -void uvm32_clearError(uvm32_state_t *vmst) { - if (vmst->status == UVM32_STATUS_ERROR) { - // vm is in an error state, but user wants to continue - // most likely after UVM32_ERR_HUNG - vmst->status = UVM32_STATUS_PAUSED; - } -} - -uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { - uint32_t num_instr = 0; - - if (vmst->stack_canary != UVM32_NULL && *vmst->stack_canary != STACK_CANARY_VALUE) { - setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); - setup_err_evt(vmst, evt); - return num_instr; - } - - if (vmst->status != UVM32_STATUS_PAUSED) { - setStatusErr(vmst, UVM32_ERR_NOTREADY); - setup_err_evt(vmst, evt); - return num_instr; - } - - setStatus(vmst, UVM32_STATUS_RUNNING); - - // run CPU until no longer in running state - while(vmst->status == UVM32_STATUS_RUNNING) { - uint64_t elapsedUs = 1; - uint32_t ret; - ret = MiniRV32IMAStep(vmst, &vmst->core, vmst->memory, 0, elapsedUs, 1); - if (3 == ret) { - // Fetch registers used by syscall - const uint32_t syscall = vmst->core.regs[17]; // a7 - // on exception we should jump to mtvec, but we handle directly - // and skip over the ecall instruction - vmst->core.pc += 4; - switch(syscall) { - // inbuilt syscalls - case UVM32_SYSCALL_HALT: - setStatus(vmst, UVM32_STATUS_ENDED); - break; - case UVM32_SYSCALL_STACKPROTECT: { - // don't allow errant code to change it once set - if (vmst->stack_canary == (uint8_t *)UVM32_NULL) { - uint32_t param0 = vmst->core.regs[10]; // a0 - uint32_t mem_offset = param0 - MINIRV32_RAM_IMAGE_OFFSET; - - // check data fits in ram - if (mem_offset > UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_STACKOVERFLOW); - setup_err_evt(vmst, evt); - } - // check canary is inside valid memory - if (mem_offset < UVM32_MEMORY_SIZE) { - // set canary - vmst->stack_canary = &vmst->memory[mem_offset]; - *vmst->stack_canary = STACK_CANARY_VALUE; - } - } - } break; - default: - // user defined syscalls - vmst->ioevt.typ = UVM32_EVT_SYSCALL; - vmst->ioevt.data.syscall.code = syscall; - vmst->ioevt.data.syscall.ret = &vmst->core.regs[12]; // a2 - vmst->ioevt.data.syscall.params[0] = &vmst->core.regs[10]; // a0 - vmst->ioevt.data.syscall.params[1] = &vmst->core.regs[11]; // a1 - setStatus(vmst, UVM32_STATUS_PAUSED); - break; - } - } else if (ret != 0) { - // unhandled exception - setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); - setup_err_evt(vmst, evt); - } - - num_instr++; - - // check instruction meter, in case of hang/infinite loop - if (instr_meter-- == 0) { - setStatusErr(vmst, UVM32_ERR_HUNG); - setup_err_evt(vmst, evt); - return num_instr; - } - } - - if (vmst->status == UVM32_STATUS_ENDED) { - evt->typ = UVM32_EVT_END; - return num_instr; - } - - // an event is ready - if (vmst->status == UVM32_STATUS_PAUSED) { - // send back the built up event - UVM32_MEMCPY(evt, &vmst->ioevt, sizeof(uvm32_evt_t)); - return num_instr; - } else { - if (vmst->status == UVM32_STATUS_ERROR) { - setup_err_evt(vmst, evt); - } else { - setStatusErr(vmst, UVM32_ERR_INTERNAL_STATE); - setup_err_evt(vmst, evt); - } - return num_instr; - } -} - -bool uvm32_hasEnded(const uvm32_state_t *vmst) { - return vmst->status == UVM32_STATUS_ENDED; -} - -static uint32_t *arg_to_ptr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { - switch(arg) { - case ARG0: - return evt->data.syscall.params[0]; - break; - case ARG1: - return evt->data.syscall.params[1]; - break; - case RET: - return evt->data.syscall.ret; - break; - default: - // if something invalid is passed to arg, we should never crash - // return a pointer to something readable - setStatusErr(vmst, UVM32_ERR_ARGS); - garbage = 0; - return &garbage; - break; - } -} - -void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg, uint32_t val) { - *arg_to_ptr(vmst, evt, arg) = val; -} - -uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { - return *arg_to_ptr(vmst, evt, arg); -} - -const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t arg) { - uvm32_evt_syscall_buf_t scb; - if (get_safeptr_null_terminated(vmst, uvm32_getval(vmst, evt, arg), &scb)) { - return (const char *)scb.ptr; // we know the buffer in cpu memory is null terminated, so safe to pass back - } else { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - garbage = 0; - return (const char *)&garbage; - } -} - -uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen) { - uvm32_evt_syscall_buf_t scb; - if (!get_safeptr(vmst, uvm32_getval(vmst, evt, argPtr), uvm32_getval(vmst, evt, argLen), &scb)) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - garbage = 0; - scb.ptr = (uint8_t *)&garbage; - scb.len = 0; - } - return scb; -} - diff --git a/host-arduino/uvm32.h b/host-arduino/uvm32.h deleted file mode 100644 index f940949..0000000 --- a/host-arduino/uvm32.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef UVM32_H -#define UVM32_H 1 - -#include -#include -#include "common/uvm32_sys.h" - -#define LIST_OF_UVM32_ERRS \ - X(UVM32_ERR_NONE) \ - X(UVM32_ERR_NOTREADY) \ - X(UVM32_ERR_MEM_RD) \ - X(UVM32_ERR_MEM_WR) \ - X(UVM32_ERR_BAD_SYSCALL) \ - X(UVM32_ERR_HUNG) \ - X(UVM32_ERR_INTERNAL_CORE) \ - X(UVM32_ERR_INTERNAL_STATE) \ - X(UVM32_ERR_ARGS) \ - -#define X(name) name, -typedef enum { - LIST_OF_UVM32_ERRS -} uvm32_err_t; -#undef X - -typedef enum { - UVM32_EVT_ERR, - UVM32_EVT_SYSCALL, - UVM32_EVT_YIELD, - UVM32_EVT_END, -} uvm32_evt_typ_t; - -typedef struct { - uvm32_err_t errcode; - const char *errstr; -} uvm32_evt_err_t; - -typedef struct { - uint32_t code; // syscall number - uint32_t *ret; - uint32_t *params[2]; -} uvm32_evt_syscall_t; - -typedef struct { - uvm32_evt_typ_t typ; - union { - uvm32_evt_syscall_t syscall; - uvm32_evt_err_t err; - } data; -} uvm32_evt_t; - -#define MINIRV32_DECORATE static -#define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE -#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} -#ifndef MINIRV32_IMPLEMENTATION -#define MINIRV32_STEPPROTO -#endif -#include "mini-rv32ima.h" - -typedef enum { - UVM32_STATUS_PAUSED, - UVM32_STATUS_RUNNING, - UVM32_STATUS_ERROR, - UVM32_STATUS_ENDED, -} uvm32_status_t; - -typedef struct { - uvm32_status_t status; - uvm32_err_t err; - struct MiniRV32IMAState core; - uint8_t memory[UVM32_MEMORY_SIZE]; - uvm32_evt_t ioevt; // for building up in callbacks -} uvm32_state_t; - -void uvm32_init(uvm32_state_t *vmst); -bool uvm32_load(uvm32_state_t *vmst, const uint8_t *rom, int len); -bool uvm32_hasEnded(const uvm32_state_t *vmst); -uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter); - -// convenience for getptr -typedef struct { - uint8_t *ptr; - uint32_t len; -} uvm32_evt_syscall_buf_t; - -typedef enum { - ARG0, - ARG1, - RET -} uvm32_arg_t; - -uint32_t uvm32_getval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t); -const char *uvm32_getcstr(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t); -void uvm32_setval(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t, uint32_t val); -uvm32_evt_syscall_buf_t uvm32_getbuf(uvm32_state_t *vmst, uvm32_evt_t *evt, uvm32_arg_t argPtr, uvm32_arg_t argLen); -void uvm32_clearError(uvm32_state_t *vmst); - - -#endif diff --git a/uvm32/uvm32.c b/uvm32/uvm32.c index 7a9da97..d20d325 100644 --- a/uvm32/uvm32.c +++ b/uvm32/uvm32.c @@ -70,7 +70,7 @@ void uvm32_init(uvm32_state_t *vmst) { vmst->status = UVM32_STATUS_PAUSED; vmst->extramLen = 0; - vmst->extram = UVM32_NULL; + vmst->extram = (uint32_t *)UVM32_NULL; vmst->extramDirty = false; vmst->core.pc = MINIRV32_RAM_IMAGE_OFFSET;