Skip to content

Commit 9800685

Browse files
aykevldeadprogram
authored andcommitted
riscv: implement VirtIO target
This allows running RISC-V tests in CI using QEMU, which should help catch bugs.
1 parent c4fd19b commit 9800685

File tree

6 files changed

+132
-33
lines changed

6 files changed

+132
-33
lines changed

main_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ func TestCompiler(t *testing.T) {
5454
runPlatTests("cortex-m-qemu", matches, t)
5555
})
5656

57+
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
58+
// Note: running only on Windows and macOS because Linux (as of 2020)
59+
// usually has an outdated QEMU version that doesn't support RISC-V yet.
60+
t.Run("EmulatedRISCV", func(t *testing.T) {
61+
runPlatTests("riscv-qemu", matches, t)
62+
})
63+
}
64+
5765
if runtime.GOOS == "linux" {
5866
t.Run("ARMLinux", func(t *testing.T) {
5967
runPlatTests("arm--linux-gnueabihf", matches, t)

src/runtime/runtime_fe310.go

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,6 @@ import (
1616

1717
type timeUnit int64
1818

19-
//go:extern _sbss
20-
var _sbss [0]byte
21-
22-
//go:extern _ebss
23-
var _ebss [0]byte
24-
25-
//go:extern _sdata
26-
var _sdata [0]byte
27-
28-
//go:extern _sidata
29-
var _sidata [0]byte
30-
31-
//go:extern _edata
32-
var _edata [0]byte
33-
3419
func postinit() {}
3520

3621
//go:export main
@@ -108,24 +93,6 @@ func initPeripherals() {
10893
machine.UART0.Configure(machine.UARTConfig{})
10994
}
11095

111-
func preinit() {
112-
// Initialize .bss: zero-initialized global variables.
113-
ptr := unsafe.Pointer(&_sbss)
114-
for ptr != unsafe.Pointer(&_ebss) {
115-
*(*uint32)(ptr) = 0
116-
ptr = unsafe.Pointer(uintptr(ptr) + 4)
117-
}
118-
119-
// Initialize .data: global variables initialized from flash.
120-
src := unsafe.Pointer(&_sidata)
121-
dst := unsafe.Pointer(&_sdata)
122-
for dst != unsafe.Pointer(&_edata) {
123-
*(*uint32)(dst) = *(*uint32)(src)
124-
dst = unsafe.Pointer(uintptr(dst) + 4)
125-
src = unsafe.Pointer(uintptr(src) + 4)
126-
}
127-
}
128-
12996
func putchar(c byte) {
13097
machine.UART0.WriteByte(c)
13198
}

src/runtime/runtime_tinygoriscv.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// +build tinygo.riscv
2+
3+
package runtime
4+
5+
import "unsafe"
6+
7+
//go:extern _sbss
8+
var _sbss [0]byte
9+
10+
//go:extern _ebss
11+
var _ebss [0]byte
12+
13+
//go:extern _sdata
14+
var _sdata [0]byte
15+
16+
//go:extern _sidata
17+
var _sidata [0]byte
18+
19+
//go:extern _edata
20+
var _edata [0]byte
21+
22+
func preinit() {
23+
// Initialize .bss: zero-initialized global variables.
24+
ptr := unsafe.Pointer(&_sbss)
25+
for ptr != unsafe.Pointer(&_ebss) {
26+
*(*uint32)(ptr) = 0
27+
ptr = unsafe.Pointer(uintptr(ptr) + 4)
28+
}
29+
30+
// Initialize .data: global variables initialized from flash.
31+
src := unsafe.Pointer(&_sidata)
32+
dst := unsafe.Pointer(&_sdata)
33+
for dst != unsafe.Pointer(&_edata) {
34+
*(*uint32)(dst) = *(*uint32)(src)
35+
dst = unsafe.Pointer(uintptr(dst) + 4)
36+
src = unsafe.Pointer(uintptr(src) + 4)
37+
}
38+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// +build tinygo.riscv,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+
const tickMicros = 1
17+
18+
var timestamp timeUnit
19+
20+
func postinit() {}
21+
22+
//go:export main
23+
func main() {
24+
preinit()
25+
run()
26+
abort()
27+
}
28+
29+
const asyncScheduler = false
30+
31+
func sleepTicks(d timeUnit) {
32+
// TODO: actually sleep here for the given time.
33+
timestamp += d
34+
}
35+
36+
func ticks() timeUnit {
37+
return timestamp
38+
}
39+
40+
// Memory-mapped I/O as defined by QEMU.
41+
// Source: https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c
42+
// Technically this is an implementation detail but hopefully they won't change
43+
// the memory-mapped I/O registers.
44+
var (
45+
// UART0 output register.
46+
stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x10000000)))
47+
// SiFive test finisher
48+
testFinisher = (*volatile.Register16)(unsafe.Pointer(uintptr(0x100000)))
49+
)
50+
51+
func putchar(c byte) {
52+
stdoutWrite.Set(uint8(c))
53+
}
54+
55+
func abort() {
56+
// Make sure the QEMU process exits.
57+
testFinisher.Set(0x5555) // FINISHER_PASS
58+
59+
// Lock up forever (as a fallback).
60+
for {
61+
riscv.Asm("wfi")
62+
}
63+
}

targets/riscv-qemu.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"inherits": ["riscv"],
3+
"features": ["+a", "+c", "+m"],
4+
"build-tags": ["virt", "qemu"],
5+
"linkerscript": "targets/riscv-qemu.ld",
6+
"emulator": ["qemu-system-riscv32", "-machine", "virt", "-nographic", "-bios", "none", "-kernel"]
7+
}

targets/riscv-qemu.ld

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
/* Memory map:
3+
* https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c
4+
* RAM and flash are set to 1MB each. That should be enough for the foreseeable
5+
* future. QEMU does not seem to limit the flash/RAM size and in fact doesn't
6+
* seem to differentiate between it.
7+
*/
8+
MEMORY
9+
{
10+
FLASH_TEXT (rw) : ORIGIN = 0x80000000, LENGTH = 0x100000
11+
RAM (xrw) : ORIGIN = 0x80100000, LENGTH = 0x100000
12+
}
13+
14+
_stack_size = 2K;
15+
16+
INCLUDE "targets/riscv.ld"

0 commit comments

Comments
 (0)