Browse Source

all: add emulation support for Cortex-M3 with QEMU

pull/43/merge
Ayke van Laethem 6 years ago
parent
commit
a63af97e86
No known key found for this signature in database GPG Key ID: E97FF5335DFDFDED
  1. 2
      Makefile
  2. 55
      main.go
  3. 63
      src/device/arm/semihosting.go
  4. 45
      src/runtime/runtime_qemu.go
  5. 1
      target.go
  6. 61
      targets/cortex-m.s
  7. 10
      targets/lm3s6965.ld
  8. 20
      targets/qemu.json

2
Makefile

@ -62,7 +62,7 @@ clean:
@rm -rf build
fmt:
@go fmt . ./compiler ./ir ./src/examples/* ./src/machine ./src/runtime ./src/sync
@go fmt . ./compiler ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/runtime ./src/sync
@go fmt ./testdata/*.go
test:

55
main.go

@ -330,6 +330,32 @@ func Run(pkgName string) error {
return nil
}
// Compile and run the given program in an emulator.
func Emulate(pkgName, target string) error {
spec, err := LoadTarget(target)
if err != nil {
return err
}
if len(spec.Emulator) == 0 {
return errors.New("no emulator configured for this target")
}
return Compile(pkgName, ".elf", spec, false, false, false, "", func(tmppath string) error {
args := append(spec.Emulator[1:], tmppath)
cmd := exec.Command(spec.Emulator[0], args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
// Workaround for QEMU which always exits with an error.
return nil
}
}
return err
})
}
func usage() {
fmt.Fprintf(os.Stderr, "usage: %s command [-printir] [-target=<target>] -o <output> <input>\n", os.Args[0])
fmt.Fprintln(os.Stderr, "\ncommands:")
@ -395,31 +421,32 @@ func main() {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
case "clean":
// remove cache directory
dir := cacheDir()
err := os.RemoveAll(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "cannot clean cache:", err)
os.Exit(1)
}
case "help":
usage()
case "run":
if flag.NArg() != 1 {
fmt.Fprintln(os.Stderr, "No package specified.")
usage()
os.Exit(1)
}
if *target != llvm.DefaultTargetTriple() {
fmt.Fprintf(os.Stderr, "Cannot run %s: target triple does not match host triple.", *target)
os.Exit(1)
var err error
if *target == llvm.DefaultTargetTriple() {
err = Run(flag.Arg(0))
} else {
err = Emulate(flag.Arg(0), *target)
}
err := Run(flag.Arg(0))
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
case "clean":
// remove cache directory
dir := cacheDir()
err := os.RemoveAll(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "cannot clean cache:", err)
os.Exit(1)
}
case "help":
usage()
default:
fmt.Fprintln(os.Stderr, "Unknown command:", command)
usage()

63
src/device/arm/semihosting.go

@ -0,0 +1,63 @@
package arm
// Semihosting commands.
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471c/Bgbjhiea.html
const (
// Regular semihosting calls
SemihostingClock = 0x10
SemihostingClose = 0x02
SemihostingElapsed = 0x30
SemihostingErrno = 0x13
SemihostingFileLen = 0x0C
SemihostingGetCmdline = 0x15
SemihostingHeapInfo = 0x16
SemihostingIsError = 0x08
SemihostingIsTTY = 0x09
SemihostingOpen = 0x01
SemihostingRead = 0x06
SemihostingReadByte = 0x07
SemihostingRemove = 0x0E
SemihostingRename = 0x0F
SemihostingSeek = 0x0A
SemihostingSystem = 0x12
SemihostingTickFreq = 0x31
SemihostingTime = 0x11
SemihostingTmpName = 0x0D
SemihostingWrite = 0x05
SemihostingWrite0 = 0x04
SemihostingWriteByte = 0x03
// Angel semihosting calls
SemihostingEnterSVC = 0x17
SemihostingReportException = 0x18
)
// Special codes for the Angel Semihosting interface.
const (
// Hardware vector reason codes
SemihostingBranchThroughZero = 20000
SemihostingUndefinedInstr = 20001
SemihostingSoftwareInterrupt = 20002
SemihostingPrefetchAbort = 20003
SemihostingDataAbort = 20004
SemihostingAddressException = 20005
SemihostingIRQ = 20006
SemihostingFIQ = 20007
// Software reason codes
SemihostingBreakPoint = 20020
SemihostingWatchPoint = 20021
SemihostingStepComplete = 20022
SemihostingRunTimeErrorUnknown = 20023
SemihostingInternalError = 20024
SemihostingUserInterruption = 20025
SemihostingApplicationExit = 20026
SemihostingStackOverflow = 20027
SemihostingDivisionByZero = 20028
SemihostingOSSpecific = 20029
)
// Call a semihosting function.
// TODO: implement it here using inline assembly.
//go:linkname SemihostingCall SemihostingCall
func SemihostingCall(num int, arg uintptr) int

45
src/runtime/runtime_qemu.go

@ -0,0 +1,45 @@
// +build qemu
package runtime
// This file implements the Stellaris LM3S6965 Cortex-M3 chip as implemented by
// QEMU.
import (
"device/arm"
"unsafe"
)
type timeUnit int64
const tickMicros = 1
var timestamp timeUnit
//go:export Reset_Handler
func main() {
preinit()
initAll()
mainWrapper()
arm.SemihostingCall(arm.SemihostingReportException, arm.SemihostingApplicationExit)
abort()
}
func sleepTicks(d timeUnit) {
// TODO: actually sleep here for the given time.
timestamp += d
}
func ticks() timeUnit {
return timestamp
}
//go:volatile
type regValue uint32
// UART0 output register.
var stdoutWrite *regValue = (*regValue)(unsafe.Pointer(uintptr(0x4000c000)))
func putchar(c byte) {
*stdoutWrite = regValue(c)
}

1
target.go

@ -21,6 +21,7 @@ type TargetSpec struct {
CompilerRT bool `json:"compiler-rt"`
PreLinkArgs []string `json:"pre-link-args"`
Objcopy string `json:"objcopy"`
Emulator []string `json:"emulator"`
Flasher string `json:"flash"`
OCDDaemon []string `json:"ocd-daemon"`
GDB string `json:"gdb"`

61
targets/cortex-m.s

@ -0,0 +1,61 @@
// Generic Cortex-M interrupt vector.
// This vector is used by the QEMU target.
.syntax unified
// This is a convenience function for QEMU semihosting support.
// At some point, this should be replaced by inline assembly.
.section .text.SemihostingCall
.global SemihostingCall
.type SemihostingCall, %function
SemihostingCall:
bkpt 0xab
bx lr
// This is the default handler for interrupts, if triggered but not defined.
.section .text.Default_Handler
.global Default_Handler
.type Default_Handler, %function
Default_Handler:
wfe
b Default_Handler
// Avoid the need for repeated .weak and .set instructions.
.macro IRQ handler
.weak \handler
.set \handler, Default_Handler
.endm
.section .isr_vector
.global __isr_vector
// Interrupt vector as defined by Cortex-M, starting with the stack top.
// On reset, SP is initialized with *0x0 and PC is loaded with *0x4, loading
// _stack_top and Reset_Handler.
.long _stack_top
.long Reset_Handler
.long NMI_Handler
.long HardFault_Handler
.long MemoryManagement_Handler
.long BusFault_Handler
.long UsageFault_Handler
.long 0
.long 0
.long 0
.long 0
.long SVC_Handler
.long DebugMon_Handler
.long 0
.long PendSV_Handler
.long SysTick_Handler
// Define default implementations for interrupts, redirecting to
// Default_Handler when not implemented.
IRQ NMI_Handler
IRQ HardFault_Handler
IRQ MemoryManagement_Handler
IRQ BusFault_Handler
IRQ UsageFault_Handler
IRQ SVC_Handler
IRQ DebugMon_Handler
IRQ PendSV_Handler
IRQ SysTick_Handler

10
targets/lm3s6965.ld

@ -0,0 +1,10 @@
MEMORY
{
FLASH_TEXT (rw) : ORIGIN = 0x00000000, LENGTH = 256K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}
_stack_size = 4K;
INCLUDE "targets/arm.ld"

20
targets/qemu.json

@ -0,0 +1,20 @@
{
"llvm-target": "armv7m-none-eabi",
"build-tags": ["qemu", "lm3s6965", "arm", "js", "wasm"],
"linker": "arm-none-eabi-gcc",
"compiler-rt": true,
"pre-link-args": [
"-nostdlib",
"-nostartfiles",
"-mcpu=cortex-m0",
"-mthumb",
"-T", "targets/lm3s6965.ld",
"-Wl,--gc-sections",
"-fno-exceptions", "-fno-unwind-tables",
"-ffunction-sections", "-fdata-sections",
"-Os",
"targets/cortex-m.s"
],
"objcopy": "arm-none-eabi-objcopy",
"emulator": ["qemu-system-arm", "-machine", "lm3s6965evb", "-semihosting", "-nographic", "-kernel"]
}
Loading…
Cancel
Save