mirror of
https://github.com/ringtailsoftware/uvm32.git
synced 2026-06-05 22:43:39 +00:00
auto-generate Arduino example, to keep in sync
This commit is contained in:
parent
7a1656ab10
commit
9ca720d35c
9 changed files with 22 additions and 980 deletions
15
host-arduino/Makefile
Normal file
15
host-arduino/Makefile
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = (rs2<rval)?rs2:rval; break; //AMOMINU.W (0b11000)
|
||||
case 28: rs2 = (rs2>rval)?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
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
#ifndef UVM32_H
|
||||
#define UVM32_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#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
|
||||
Loading…
Add table
Add a link
Reference in a new issue