main: add -serial=rtt support
Этот коммит содержится в:
родитель
ffe6dfd21b
коммит
8d2a07b927
5 изменённых файлов: 294 добавлений и 35 удалений
|
@ -777,6 +777,8 @@ endif
|
|||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||
@$(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
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
var (
|
||||
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
|
||||
validSerialOptions = []string{"none", "uart", "usb"}
|
||||
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
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)
|
||||
}
|
||||
if options.Monitor {
|
||||
return Monitor(result.Executable, "", options)
|
||||
return Monitor(result.Executable, "", config)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1740,7 +1740,9 @@ func main() {
|
|||
fmt.Printf("%s %4s %4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
||||
}
|
||||
} else {
|
||||
err := Monitor("", *port, options)
|
||||
config, err := builder.NewConfig(options)
|
||||
handleCompilerError(err)
|
||||
err = Monitor("", *port, config)
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "targets":
|
||||
|
|
123
monitor.go
123
monitor.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
|
@ -17,7 +19,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/mattn/go-tty"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
|
||||
"go.bug.st/serial"
|
||||
|
@ -25,12 +26,117 @@ import (
|
|||
)
|
||||
|
||||
// Monitor connects to the given port and reads/writes the serial port.
|
||||
func Monitor(executable, port string, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
func Monitor(executable, port string, config *compileopts.Config) error {
|
||||
const timeout = time.Second * 3
|
||||
var exit func() // function to be called before exiting
|
||||
var serialConn io.ReadWriter
|
||||
|
||||
if config.Options.Serial == "rtt" {
|
||||
// Use the RTT interface, which is documented (in part) here:
|
||||
// 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 {
|
||||
return fmt.Errorf("could not open ELF file to determine RTT control block: %w", err)
|
||||
}
|
||||
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
|
||||
}
|
||||
args = append(args,
|
||||
"-c", fmt.Sprintf("rtt setup 0x%x 16 \"SEGGER RTT\"", address),
|
||||
"-c", "init",
|
||||
"-c", "rtt server start 0 0")
|
||||
cmd := executeCommand(config.Options, "openocd", args...)
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
|
@ -44,7 +150,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||
break
|
||||
}
|
||||
|
||||
br := options.BaudRate
|
||||
br := config.Options.BaudRate
|
||||
if br <= 0 {
|
||||
br = 115200
|
||||
}
|
||||
|
@ -60,9 +166,11 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
serialConn = p
|
||||
break
|
||||
}
|
||||
defer p.Close()
|
||||
}
|
||||
|
||||
tty, err := tty.Open()
|
||||
if err != nil {
|
||||
|
@ -77,6 +185,9 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||
go func() {
|
||||
<-sig
|
||||
tty.Close()
|
||||
if exit != nil {
|
||||
exit()
|
||||
}
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
|
@ -88,7 +199,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||
buf := make([]byte, 100*1024)
|
||||
var line []byte
|
||||
for {
|
||||
n, err := p.Read(buf)
|
||||
n, err := serialConn.Read(buf)
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("read error: %w", err)
|
||||
return
|
||||
|
@ -124,7 +235,7 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
|||
if r == 0 {
|
||||
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
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче