Skip to content

Commit 9ca31a5

Browse files
committed
test: run tests for AVR
This runs the currently working compiler tests for AVR using simavr. I picked the atmega1284p target as it seems like the most suitable one (most importantly, with a lot of RAM) so that tests are less likely to hit resource limitations. One extra change this required is using a different implementation of the runtime.abort() function. The new one is smaller and will prevent interrupts from running on exit, which should at least in theory be fine. This pattern is required as it signals to simavr the program is finished.
1 parent 9c2d2b6 commit 9ca31a5

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ commands:
277277
curl https://dl.google.com/go/go1.15.5.darwin-amd64.tar.gz -o go1.15.5.darwin-amd64.tar.gz
278278
sudo tar -C /usr/local -xzf go1.15.5.darwin-amd64.tar.gz
279279
ln -s /usr/local/go/bin/go /usr/local/bin/go
280-
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
280+
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu simavr
281281
- install-xtensa-toolchain:
282282
variant: "macos"
283283
- restore_cache:

main_test.go

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
"os"
1313
"os/exec"
1414
"path/filepath"
15+
"regexp"
1516
"runtime"
1617
"sort"
18+
"strconv"
1719
"strings"
1820
"sync"
1921
"testing"
@@ -22,12 +24,31 @@ import (
2224
"github.com/tinygo-org/tinygo/builder"
2325
"github.com/tinygo-org/tinygo/compileopts"
2426
"github.com/tinygo-org/tinygo/goenv"
27+
"tinygo.org/x/go-llvm"
2528
)
2629

2730
const TESTDATA = "testdata"
2831

2932
var testTarget = flag.String("target", "", "override test target")
3033

34+
// There are a lot of tests that don't yet pass on AVR, often because avr-libc
35+
// doesn't provide the required functions (for float64 for example).
36+
var skipOnAVR = map[string]struct{}{
37+
"testdata/atomic.go": {},
38+
"testdata/cgo/": {},
39+
"testdata/channel.go": {},
40+
"testdata/coroutines.go": {},
41+
"testdata/float.go": {},
42+
"testdata/gc.go": {},
43+
"testdata/interface.go": {},
44+
"testdata/map.go": {},
45+
"testdata/math.go": {},
46+
"testdata/print.go": {},
47+
"testdata/reflect.go": {},
48+
"testdata/stdlib.go": {},
49+
"testdata/structs.go": {},
50+
}
51+
3152
func TestCompiler(t *testing.T) {
3253
matches, err := filepath.Glob(filepath.Join(TESTDATA, "*.go"))
3354
if err != nil {
@@ -77,6 +98,19 @@ func TestCompiler(t *testing.T) {
7798
})
7899
}
79100

101+
if runtime.GOOS == "darwin" {
102+
// Running AVR tests only on Darwin as it has an easily installed
103+
// homebrew simavr package.
104+
llvmMajorVersion, _ := strconv.ParseInt(strings.Split(llvm.Version, ".")[0], 10, 32)
105+
if llvmMajorVersion >= 11 {
106+
// The AVR backend in LLVM 11 has been significantly improved and is
107+
// able to correctly compile a lot more tests than before.
108+
t.Run("AVR", func(t *testing.T) {
109+
runPlatTests("atmega1284p", matches, t)
110+
})
111+
}
112+
}
113+
80114
if runtime.GOOS == "linux" {
81115
t.Run("X86Linux", func(t *testing.T) {
82116
runPlatTests("i386--linux-gnu", matches, t)
@@ -114,6 +148,13 @@ func runPlatTests(target string, matches []string, t *testing.T) {
114148
for _, path := range matches {
115149
path := path // redefine to avoid race condition
116150

151+
if target == "atmega1284p" {
152+
if _, ok := skipOnAVR[path]; ok {
153+
// Some tests don't work on AVR yet, so skip them.
154+
continue
155+
}
156+
}
157+
117158
t.Run(filepath.Base(path), func(t *testing.T) {
118159
t.Parallel()
119160
runTest(path, target, t)
@@ -180,6 +221,7 @@ func runTest(path, target string, t *testing.T) {
180221
runComplete := make(chan struct{})
181222
var cmd *exec.Cmd
182223
ranTooLong := false
224+
var emulator = ""
183225
if target == "" {
184226
cmd = exec.Command(binary)
185227
} else {
@@ -190,13 +232,19 @@ func runTest(path, target string, t *testing.T) {
190232
if len(spec.Emulator) == 0 {
191233
cmd = exec.Command(binary)
192234
} else {
235+
emulator = spec.Emulator[0]
193236
args := append(spec.Emulator[1:], binary)
194237
cmd = exec.Command(spec.Emulator[0], args...)
195238
}
196239
}
197240
stdout := &bytes.Buffer{}
198-
cmd.Stdout = stdout
199-
cmd.Stderr = os.Stderr
241+
if emulator == "simavr" {
242+
cmd.Stdout = nil
243+
cmd.Stderr = stdout
244+
} else {
245+
cmd.Stdout = stdout
246+
cmd.Stderr = os.Stderr
247+
}
200248
err = cmd.Start()
201249
if err != nil {
202250
t.Fatal("failed to start:", err)
@@ -230,6 +278,13 @@ func runTest(path, target string, t *testing.T) {
230278
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
231279
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
232280

281+
if emulator == "simavr" {
282+
// Munge the data a bit to remove escape characters and the two dots
283+
// simavr likes to put at the end of each line.
284+
actual = regexp.MustCompile("\x1b.*?m").ReplaceAll(actual, nil)
285+
actual = regexp.MustCompile("\\.\\.\n").ReplaceAll(actual, []byte{'\n'})
286+
}
287+
233288
// Check whether the command ran successfully.
234289
fail := false
235290
if err != nil {

src/runtime/runtime_avr.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,13 @@ func ticks() timeUnit {
9696
}
9797

9898
func abort() {
99+
avr.Asm("cli")
99100
for {
100-
sleepWDT(WDT_PERIOD_2S)
101+
// Sleeping with interrupts disabled will mean the MCU sleeps forever.
102+
// To be extra sure, put it in a loop.
103+
// This mechanism is used by simavr to detect a program exit, the
104+
// emulator will stop when it detects a sleep instruction with
105+
// interrupts disabled.
106+
avr.Asm("sleep")
101107
}
102108
}

0 commit comments

Comments
 (0)