diff --git a/Makefile b/Makefile index 4ca17c20..01476b32 100644 --- a/Makefile +++ b/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: diff --git a/main.go b/main.go index 55f412fc..ffb986e2 100644 --- a/main.go +++ b/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=] -o \n", os.Args[0]) fmt.Fprintln(os.Stderr, "\ncommands:") @@ -395,6 +421,22 @@ func main() { fmt.Fprintln(os.Stderr, "error:", err) os.Exit(1) } + case "run": + if flag.NArg() != 1 { + fmt.Fprintln(os.Stderr, "No package specified.") + usage() + os.Exit(1) + } + var err error + if *target == llvm.DefaultTargetTriple() { + err = Run(flag.Arg(0)) + } else { + err = Emulate(flag.Arg(0), *target) + } + if err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + os.Exit(1) + } case "clean": // remove cache directory dir := cacheDir() @@ -405,21 +447,6 @@ func main() { } 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) - } - err := Run(flag.Arg(0)) - if err != nil { - fmt.Fprintln(os.Stderr, "error:", err) - os.Exit(1) - } default: fmt.Fprintln(os.Stderr, "Unknown command:", command) usage() diff --git a/src/device/arm/semihosting.go b/src/device/arm/semihosting.go new file mode 100644 index 00000000..cfece50d --- /dev/null +++ b/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 diff --git a/src/runtime/runtime_qemu.go b/src/runtime/runtime_qemu.go new file mode 100644 index 00000000..5f16b4be --- /dev/null +++ b/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) +} diff --git a/target.go b/target.go index 87678e50..2916912a 100644 --- a/target.go +++ b/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"` diff --git a/targets/cortex-m.s b/targets/cortex-m.s new file mode 100644 index 00000000..62ed28ef --- /dev/null +++ b/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 diff --git a/targets/lm3s6965.ld b/targets/lm3s6965.ld new file mode 100644 index 00000000..7ac5730e --- /dev/null +++ b/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" diff --git a/targets/qemu.json b/targets/qemu.json new file mode 100644 index 00000000..1fc2369c --- /dev/null +++ b/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"] +}