main: add -serial=rtt support
Этот коммит содержится в:
родитель
ffe6dfd21b
коммит
8d2a07b927
5 изменённых файлов: 294 добавлений и 35 удалений
|
@ -777,6 +777,8 @@ endif
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=rtt examples/echo
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||||
@$(MD5SUM) test.nro
|
@$(MD5SUM) test.nro
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
var (
|
var (
|
||||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||||
validSerialOptions = []string{"none", "uart", "usb"}
|
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||||
|
|
6
main.go
6
main.go
|
@ -532,7 +532,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||||
}
|
}
|
||||||
if options.Monitor {
|
if options.Monitor {
|
||||||
return Monitor(result.Executable, "", options)
|
return Monitor(result.Executable, "", config)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1740,7 +1740,9 @@ func main() {
|
||||||
fmt.Printf("%s %4s %4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
fmt.Printf("%s %4s %4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := Monitor("", *port, options)
|
config, err := builder.NewConfig(options)
|
||||||
|
handleCompilerError(err)
|
||||||
|
err = Monitor("", *port, config)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
}
|
}
|
||||||
case "targets":
|
case "targets":
|
||||||
|
|
175
monitor.go
175
monitor.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
|
@ -9,6 +10,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -17,7 +19,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattn/go-tty"
|
"github.com/mattn/go-tty"
|
||||||
"github.com/tinygo-org/tinygo/builder"
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
|
@ -25,44 +26,151 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Monitor connects to the given port and reads/writes the serial port.
|
// Monitor connects to the given port and reads/writes the serial port.
|
||||||
func Monitor(executable, port string, options *compileopts.Options) error {
|
func Monitor(executable, port string, config *compileopts.Config) error {
|
||||||
config, err := builder.NewConfig(options)
|
const timeout = time.Second * 3
|
||||||
if err != nil {
|
var exit func() // function to be called before exiting
|
||||||
return err
|
var serialConn io.ReadWriter
|
||||||
}
|
|
||||||
|
|
||||||
wait := 300
|
if config.Options.Serial == "rtt" {
|
||||||
for i := 0; i <= wait; i++ {
|
// Use the RTT interface, which is documented (in part) here:
|
||||||
port, err = getDefaultPort(port, config.Target.SerialPort)
|
// https://wiki.segger.com/RTT
|
||||||
|
|
||||||
|
// Try to find the "machine.rttSerialInstance" symbol, which is the RTT
|
||||||
|
// control block.
|
||||||
|
file, err := elf.Open(executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if i < wait {
|
return fmt.Errorf("could not open ELF file to determine RTT control block: %w", err)
|
||||||
time.Sleep(10 * time.Millisecond)
|
}
|
||||||
continue
|
defer file.Close()
|
||||||
|
symbols, err := file.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read ELF symbol table to determine RTT control block: %w", err)
|
||||||
|
}
|
||||||
|
var address uint64
|
||||||
|
for _, symbol := range symbols {
|
||||||
|
if symbol.Name == "machine.rttSerialInstance" {
|
||||||
|
address = symbol.Value
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if address == 0 {
|
||||||
|
return fmt.Errorf("could not find RTT control block in ELF file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start an openocd process in the background.
|
||||||
|
args, err := config.OpenOCDConfiguration()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
break
|
args = append(args,
|
||||||
}
|
"-c", fmt.Sprintf("rtt setup 0x%x 16 \"SEGGER RTT\"", address),
|
||||||
|
"-c", "init",
|
||||||
br := options.BaudRate
|
"-c", "rtt server start 0 0")
|
||||||
if br <= 0 {
|
cmd := executeCommand(config.Options, "openocd", args...)
|
||||||
br = 115200
|
stderr, err := cmd.StderrPipe()
|
||||||
}
|
|
||||||
|
|
||||||
wait = 300
|
|
||||||
var p serial.Port
|
|
||||||
for i := 0; i <= wait; i++ {
|
|
||||||
p, err = serial.Open(port, &serial.Mode{BaudRate: br})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if i < wait {
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
break
|
cmd.Stdout = os.Stdout
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
exit = func() {
|
||||||
|
// Make sure the openocd process is terminated at exit.
|
||||||
|
// This does not happen through the defer above when exiting through
|
||||||
|
// os.Exit.
|
||||||
|
cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the stderr, which logs various important messages we need.
|
||||||
|
r := bufio.NewReader(stderr)
|
||||||
|
var telnet net.Conn
|
||||||
|
var timeoutAt time.Time
|
||||||
|
for {
|
||||||
|
// Read the next line from the openocd process.
|
||||||
|
lineBytes, err := r.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line := string(lineBytes)
|
||||||
|
|
||||||
|
if line == "Info : rtt: No control block found\n" {
|
||||||
|
// Message that is sent back when OpenOCD can't find the control
|
||||||
|
// block after a 'rtt start' message.
|
||||||
|
if time.Now().After(timeoutAt) {
|
||||||
|
return fmt.Errorf("RTT timeout (could not locate RTT control block at 0x%08x)", address)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
telnet.Write([]byte("rtt start\r\n"))
|
||||||
|
} else if strings.HasPrefix(line, "Info : Listening on port") {
|
||||||
|
// We need two different ports for controlling OpenOCD
|
||||||
|
// (typically port 4444) and the RTT channel 0 socket (arbitrary
|
||||||
|
// port).
|
||||||
|
var port int
|
||||||
|
var protocol string
|
||||||
|
fmt.Sscanf(line, "Info : Listening on port %d for %s connections\n", &port, &protocol)
|
||||||
|
if protocol == "telnet" && telnet == nil {
|
||||||
|
// Connect to the "telnet" command line interface.
|
||||||
|
telnet, err = net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Tell OpenOCD to start scanning for the RTT control block.
|
||||||
|
telnet.Write([]byte("rtt start\r\n"))
|
||||||
|
// Also make sure we will time out if the control block just
|
||||||
|
// can't be found.
|
||||||
|
timeoutAt = time.Now().Add(timeout)
|
||||||
|
} else if protocol == "rtt" {
|
||||||
|
// Connect to the RTT channel, for both stdin and stdout.
|
||||||
|
conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serialConn = conn
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "Info : rtt: Control block found at") {
|
||||||
|
// Connection established!
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // -serial=uart or -serial=usb
|
||||||
|
var err error
|
||||||
|
wait := 300
|
||||||
|
for i := 0; i <= wait; i++ {
|
||||||
|
port, err = getDefaultPort(port, config.Target.SerialPort)
|
||||||
|
if err != nil {
|
||||||
|
if i < wait {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
br := config.Options.BaudRate
|
||||||
|
if br <= 0 {
|
||||||
|
br = 115200
|
||||||
|
}
|
||||||
|
|
||||||
|
wait = 300
|
||||||
|
var p serial.Port
|
||||||
|
for i := 0; i <= wait; i++ {
|
||||||
|
p, err = serial.Open(port, &serial.Mode{BaudRate: br})
|
||||||
|
if err != nil {
|
||||||
|
if i < wait {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serialConn = p
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer p.Close()
|
||||||
}
|
}
|
||||||
defer p.Close()
|
|
||||||
|
|
||||||
tty, err := tty.Open()
|
tty, err := tty.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,6 +185,9 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||||
go func() {
|
go func() {
|
||||||
<-sig
|
<-sig
|
||||||
tty.Close()
|
tty.Close()
|
||||||
|
if exit != nil {
|
||||||
|
exit()
|
||||||
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -88,7 +199,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||||
buf := make([]byte, 100*1024)
|
buf := make([]byte, 100*1024)
|
||||||
var line []byte
|
var line []byte
|
||||||
for {
|
for {
|
||||||
n, err := p.Read(buf)
|
n, err := serialConn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCh <- fmt.Errorf("read error: %w", err)
|
errCh <- fmt.Errorf("read error: %w", err)
|
||||||
return
|
return
|
||||||
|
@ -124,7 +235,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p.Write([]byte(string(r)))
|
serialConn.Write([]byte(string(r)))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
144
src/machine/serial-rtt.go
Обычный файл
144
src/machine/serial-rtt.go
Обычный файл
|
@ -0,0 +1,144 @@
|
||||||
|
//go:build baremetal && serial.rtt
|
||||||
|
|
||||||
|
// Implement Segger RTT support.
|
||||||
|
// This is mostly useful for targets that only have a debug connection
|
||||||
|
// available, and no serial output (or input). It is somewhat like semihosting,
|
||||||
|
// but not unusably slow.
|
||||||
|
// It was originally specified by Segger, but support is available in OpenOCD
|
||||||
|
// for at least the DAPLink debuggers so I assume it works on any SWD debugger.
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime/interrupt"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This symbol name is known by the compiler, see monitor.go.
|
||||||
|
var rttSerialInstance rttSerial
|
||||||
|
|
||||||
|
var Serial = &rttSerialInstance
|
||||||
|
|
||||||
|
func InitSerial() {
|
||||||
|
Serial.Configure(UARTConfig{})
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Some constants, see:
|
||||||
|
// https://github.com/SEGGERMicro/RTT/blob/master/RTT/SEGGER_RTT.h
|
||||||
|
|
||||||
|
rttMaxNumUpBuffers = 1
|
||||||
|
rttMaxNumDownBuffers = 1
|
||||||
|
rttBufferSizeUp = 1024
|
||||||
|
rttBufferSizeDown = 16
|
||||||
|
|
||||||
|
rttModeNoBlockSkip = 0
|
||||||
|
rttModeNoBlockTrim = 1
|
||||||
|
rttModeBlockIfFifoFull = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// The debugger knows about the layout of this struct, so it must not change.
|
||||||
|
// This is SEGGER_RTT_CB.
|
||||||
|
type rttControlBlock struct {
|
||||||
|
id [16]volatile.Register8
|
||||||
|
maxNumUpBuffers int32
|
||||||
|
maxNumDownBuffers int32
|
||||||
|
buffersUp [rttMaxNumUpBuffers]rttBuffer
|
||||||
|
buffersDown [rttMaxNumDownBuffers]rttBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up or down buffer.
|
||||||
|
// This is SEGGER_RTT_BUFFER_UP and SEGGER_RTT_BUFFER_DOWN.
|
||||||
|
type rttBuffer struct {
|
||||||
|
name *byte
|
||||||
|
buffer *volatile.Register8
|
||||||
|
bufferSize uint32
|
||||||
|
writeOffset volatile.Register32
|
||||||
|
readOffset volatile.Register32
|
||||||
|
flags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static buffers, for the default up and down buffer.
|
||||||
|
var (
|
||||||
|
rttBufferUpData [rttBufferSizeUp]volatile.Register8
|
||||||
|
rttBufferDownData [rttBufferSizeDown]volatile.Register8
|
||||||
|
)
|
||||||
|
|
||||||
|
type rttSerial struct {
|
||||||
|
rttControlBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rttSerial) Configure(config UARTConfig) error {
|
||||||
|
s.maxNumUpBuffers = rttMaxNumUpBuffers
|
||||||
|
s.maxNumDownBuffers = rttMaxNumDownBuffers
|
||||||
|
|
||||||
|
s.buffersUp[0].name = &[]byte("Terminal\x00")[0]
|
||||||
|
s.buffersUp[0].buffer = &rttBufferUpData[0]
|
||||||
|
s.buffersUp[0].bufferSize = rttBufferSizeUp
|
||||||
|
s.buffersUp[0].flags = rttModeNoBlockSkip
|
||||||
|
|
||||||
|
s.buffersDown[0].name = &[]byte("Terminal\x00")[0]
|
||||||
|
s.buffersDown[0].buffer = &rttBufferDownData[0]
|
||||||
|
s.buffersDown[0].bufferSize = rttBufferSizeDown
|
||||||
|
s.buffersDown[0].flags = rttModeNoBlockSkip
|
||||||
|
|
||||||
|
id := "SEGGER RTT"
|
||||||
|
for i := 0; i < len(id); i++ {
|
||||||
|
s.id[i].Set(id[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *rttBuffer) writeByte(c byte) {
|
||||||
|
state := interrupt.Disable()
|
||||||
|
readOffset := b.readOffset.Get()
|
||||||
|
writeOffset := b.writeOffset.Get()
|
||||||
|
newWriteOffset := writeOffset + 1
|
||||||
|
if newWriteOffset == b.bufferSize {
|
||||||
|
newWriteOffset = 0
|
||||||
|
}
|
||||||
|
if newWriteOffset != readOffset {
|
||||||
|
unsafe.Slice(b.buffer, b.bufferSize)[writeOffset].Set(c)
|
||||||
|
b.writeOffset.Set(newWriteOffset)
|
||||||
|
}
|
||||||
|
interrupt.Restore(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *rttBuffer) readByte() byte {
|
||||||
|
readOffset := b.readOffset.Get()
|
||||||
|
writeOffset := b.writeOffset.Get()
|
||||||
|
for readOffset == writeOffset {
|
||||||
|
readOffset = b.readOffset.Get()
|
||||||
|
}
|
||||||
|
c := unsafe.Slice(b.buffer, b.bufferSize)[readOffset].Get()
|
||||||
|
b.readOffset.Set(readOffset + 1)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *rttBuffer) buffered() int {
|
||||||
|
readOffset := b.readOffset.Get()
|
||||||
|
writeOffset := b.writeOffset.Get()
|
||||||
|
return int((writeOffset - readOffset) % rttBufferSizeDown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rttSerial) WriteByte(b byte) error {
|
||||||
|
s.buffersUp[0].writeByte(b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rttSerial) ReadByte() (byte, error) {
|
||||||
|
return s.buffersDown[0].readByte(), errNoByte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rttSerial) Buffered() int {
|
||||||
|
return s.buffersDown[0].buffered()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rttSerial) Write(data []byte) (n int, err error) {
|
||||||
|
for _, v := range data {
|
||||||
|
s.WriteByte(v)
|
||||||
|
}
|
||||||
|
return len(data), nil
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче