debugger: implement burst reading

This commit is contained in:
David Anderson 2024-09-18 21:31:34 -07:00
parent 789d8d002b
commit 0d4855fc21
2 changed files with 75 additions and 21 deletions

View File

@ -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
} }

View File

@ -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