github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_tinygoriscv_qemu.go (about) 1 //go:build tinygo.riscv && virt && qemu 2 3 package runtime 4 5 import ( 6 "device/riscv" 7 "runtime/volatile" 8 "unsafe" 9 ) 10 11 // This file implements the VirtIO RISC-V interface implemented in QEMU, which 12 // is an interface designed for emulation. 13 14 type timeUnit int64 15 16 var timestamp timeUnit 17 18 //export main 19 func main() { 20 preinit() 21 run() 22 exit(0) 23 } 24 25 func ticksToNanoseconds(ticks timeUnit) int64 { 26 return int64(ticks) 27 } 28 29 func nanosecondsToTicks(ns int64) timeUnit { 30 return timeUnit(ns) 31 } 32 33 func sleepTicks(d timeUnit) { 34 // TODO: actually sleep here for the given time. 35 timestamp += d 36 } 37 38 func ticks() timeUnit { 39 return timestamp 40 } 41 42 // Memory-mapped I/O as defined by QEMU. 43 // Source: https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c 44 // Technically this is an implementation detail but hopefully they won't change 45 // the memory-mapped I/O registers. 46 var ( 47 // UART0 output register. 48 stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x10000000))) 49 // SiFive test finisher 50 testFinisher = (*volatile.Register32)(unsafe.Pointer(uintptr(0x100000))) 51 ) 52 53 func putchar(c byte) { 54 stdoutWrite.Set(uint8(c)) 55 } 56 57 func getchar() byte { 58 // dummy, TODO 59 return 0 60 } 61 62 func buffered() int { 63 // dummy, TODO 64 return 0 65 } 66 67 func abort() { 68 exit(1) 69 } 70 71 func exit(code int) { 72 // Make sure the QEMU process exits. 73 if code == 0 { 74 testFinisher.Set(0x5555) // FINISHER_PASS 75 } else { 76 // Exit code is stored in the upper 16 bits of the 32 bit value. 77 testFinisher.Set(uint32(code)<<16 | 0x3333) // FINISHER_FAIL 78 } 79 80 // Lock up forever (as a fallback). 81 for { 82 riscv.Asm("wfi") 83 } 84 }