all: add emulation support for Cortex-M3 with QEMU
Этот коммит содержится в:
родитель
8982b8df83
коммит
a63af97e86
8 изменённых файлов: 243 добавлений и 16 удалений
2
Makefile
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:
|
||||
|
|
57
main.go
57
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,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()
|
||||
|
|
63
src/device/arm/semihosting.go
Обычный файл
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
Обычный файл
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)
|
||||
}
|
|
@ -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
Обычный файл
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
Обычный файл
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
Обычный файл
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"]
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче