debugger: implement burst reading
This commit is contained in:
parent
789d8d002b
commit
0d4855fc21
|
@ -1,15 +1,23 @@
|
||||||
package serial
|
package serial
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
)
|
)
|
||||||
|
|
||||||
const portDev = "/dev/ttyUSB0"
|
const (
|
||||||
const maxAddr = 1 << 17
|
portDev = "/dev/ttyUSB0"
|
||||||
|
portSpeed = 115_200
|
||||||
|
maxAddr = 1 << 17
|
||||||
|
pingResponse = 0xEA
|
||||||
|
readTimeout = 2 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
type Serial struct {
|
type Serial struct {
|
||||||
portMu sync.Mutex
|
portMu sync.Mutex
|
||||||
|
@ -25,6 +33,21 @@ func Open() (*Serial, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packet := encode(cmdPing, 0, 0)
|
||||||
|
if _, err := port.Write(packet[:]); err != nil {
|
||||||
|
port.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
port.SetReadTimeout(readTimeout)
|
||||||
|
if _, err := io.ReadFull(port, packet[:1]); err != nil {
|
||||||
|
port.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if packet[0] != pingResponse {
|
||||||
|
port.Close()
|
||||||
|
return nil, fmt.Errorf("wrong ping response: got %x, want %x", packet[0], pingResponse)
|
||||||
|
}
|
||||||
|
|
||||||
return &Serial{port: port}, nil
|
return &Serial{port: port}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,42 +57,71 @@ func (d *Serial) Close() error {
|
||||||
return d.port.Close()
|
return d.port.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode(addr int, write bool, data byte) [4]byte {
|
const (
|
||||||
writeVal := uint8(0)
|
cmdReadByte = 1
|
||||||
if write {
|
cmdWriteByte = 2
|
||||||
writeVal = 1
|
cmdReadRange = 3
|
||||||
}
|
cmdPing = 0x7F
|
||||||
|
)
|
||||||
|
|
||||||
|
func encode(cmd int, addr int, data byte) [4]byte {
|
||||||
|
cmd = cmd & (1<<7 - 1)
|
||||||
|
addr = addr & (1<<17 - 1)
|
||||||
|
raw := (uint32(cmd) << 25) + (uint32(addr) << 8) + uint32(data)
|
||||||
var ret [4]byte
|
var ret [4]byte
|
||||||
ret[3] = data
|
binary.BigEndian.PutUint32(ret[:], raw)
|
||||||
ret[2] = byte(addr<<1) | writeVal
|
|
||||||
ret[1] = byte(addr >> 7)
|
|
||||||
ret[0] = byte(addr >> 15)
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Serial) ReadAt(bs []byte, off int64) (int, error) {
|
// readSlow reads memory one byte at a time, and is 10x slower than
|
||||||
|
// burst reading. The code is preserved here just because it's a
|
||||||
|
// simpler implementation in the hardware, and is thus sometimes
|
||||||
|
// useful for debugging.
|
||||||
|
func (d *Serial) readSlow(bs []byte, off int64) (int, error) {
|
||||||
d.portMu.Lock()
|
d.portMu.Lock()
|
||||||
defer d.portMu.Unlock()
|
defer d.portMu.Unlock()
|
||||||
if off+int64(len(bs)) >= maxAddr {
|
if off+int64(len(bs)) >= maxAddr {
|
||||||
return 0, errors.New("OOB read")
|
return 0, errors.New("OOB read")
|
||||||
}
|
}
|
||||||
for i := range bs {
|
for i := range bs {
|
||||||
packet := encode(int(off)+i, false, 0)
|
packet := encode(cmdReadByte, int(off)+i, 0)
|
||||||
if _, err := d.port.Write(packet[:]); err != nil {
|
if _, err := d.port.Write(packet[:]); err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
d.port.SetReadTimeout(2 * time.Second)
|
d.port.SetReadTimeout(readTimeout)
|
||||||
n, err := d.port.Read(packet[:1])
|
n, err := io.ReadFull(d.port, bs[i:i+1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i + n, err
|
||||||
} else if n == 0 {
|
|
||||||
return i, errors.New("short read")
|
|
||||||
}
|
}
|
||||||
bs[i] = packet[0]
|
|
||||||
}
|
}
|
||||||
return len(bs), nil
|
return len(bs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Serial) readBurst(bs []byte, off int64) (int, error) {
|
||||||
|
d.portMu.Lock()
|
||||||
|
defer d.portMu.Unlock()
|
||||||
|
if off+int64(len(bs)) >= maxAddr {
|
||||||
|
return 0, errors.New("OOB read")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(bs); i += 255 {
|
||||||
|
burstLen := min(len(bs)-i, 255)
|
||||||
|
packet := encode(cmdReadRange, int(off)+i, byte(burstLen))
|
||||||
|
if _, err := d.port.Write(packet[:]); err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
d.port.SetReadTimeout(readTimeout)
|
||||||
|
n, err := io.ReadFull(d.port, bs[i:i+burstLen])
|
||||||
|
if err != nil {
|
||||||
|
return i + n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(bs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Serial) ReadAt(bs []byte, off int64) (int, error) {
|
||||||
|
return d.readBurst(bs, off)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Serial) WriteAt(bs []byte, off int64) (int, error) {
|
func (d *Serial) WriteAt(bs []byte, off int64) (int, error) {
|
||||||
d.portMu.Lock()
|
d.portMu.Lock()
|
||||||
defer d.portMu.Unlock()
|
defer d.portMu.Unlock()
|
||||||
|
@ -77,7 +129,7 @@ func (d *Serial) WriteAt(bs []byte, off int64) (int, error) {
|
||||||
return 0, errors.New("OOB write")
|
return 0, errors.New("OOB write")
|
||||||
}
|
}
|
||||||
for i, v := range bs {
|
for i, v := range bs {
|
||||||
packet := encode(int(off)+i, true, v)
|
packet := encode(cmdWriteByte, int(off)+i, v)
|
||||||
if _, err := d.port.Write(packet[:]); err != nil {
|
if _, err := d.port.Write(packet[:]); err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (m debugger) readFullMemoryCmds() []tea.Cmd {
|
||||||
|
|
||||||
func (m debugger) readMemory(start, count int) tea.Cmd {
|
func (m debugger) readMemory(start, count int) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
if err := m.mem.Load(start, count); err != nil {
|
if err := m.mem.Refresh(start, count); err != nil {
|
||||||
return msgErr{fmt.Errorf("loading region 0x%x+%x: %w", start, count, err)}
|
return msgErr{fmt.Errorf("loading region 0x%x+%x: %w", start, count, err)}
|
||||||
}
|
}
|
||||||
return msgMemoryChanged{start, count}
|
return msgMemoryChanged{start, count}
|
||||||
|
@ -131,6 +131,8 @@ func (m debugger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
case "r":
|
case "r":
|
||||||
start, end := m.hex.VisibleBytes()
|
start, end := m.hex.VisibleBytes()
|
||||||
return m, m.readMemory(start, end-start)
|
return m, m.readMemory(start, end-start)
|
||||||
|
case "R":
|
||||||
|
return m, m.readFullMemory()
|
||||||
}
|
}
|
||||||
case msgErr:
|
case msgErr:
|
||||||
m.lastErr = msg.err
|
m.lastErr = msg.err
|
||||||
|
|
Loading…
Reference in New Issue