debugger: implement burst reading
This commit is contained in:
parent
789d8d002b
commit
0d4855fc21
|
@ -1,15 +1,23 @@
|
|||
package serial
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.bug.st/serial"
|
||||
)
|
||||
|
||||
const portDev = "/dev/ttyUSB0"
|
||||
const maxAddr = 1 << 17
|
||||
const (
|
||||
portDev = "/dev/ttyUSB0"
|
||||
portSpeed = 115_200
|
||||
maxAddr = 1 << 17
|
||||
pingResponse = 0xEA
|
||||
readTimeout = 2 * time.Second
|
||||
)
|
||||
|
||||
type Serial struct {
|
||||
portMu sync.Mutex
|
||||
|
@ -25,6 +33,21 @@ func Open() (*Serial, error) {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -34,42 +57,71 @@ func (d *Serial) Close() error {
|
|||
return d.port.Close()
|
||||
}
|
||||
|
||||
func encode(addr int, write bool, data byte) [4]byte {
|
||||
writeVal := uint8(0)
|
||||
if write {
|
||||
writeVal = 1
|
||||
}
|
||||
const (
|
||||
cmdReadByte = 1
|
||||
cmdWriteByte = 2
|
||||
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
|
||||
ret[3] = data
|
||||
ret[2] = byte(addr<<1) | writeVal
|
||||
ret[1] = byte(addr >> 7)
|
||||
ret[0] = byte(addr >> 15)
|
||||
binary.BigEndian.PutUint32(ret[:], raw)
|
||||
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()
|
||||
defer d.portMu.Unlock()
|
||||
if off+int64(len(bs)) >= maxAddr {
|
||||
return 0, errors.New("OOB read")
|
||||
}
|
||||
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 {
|
||||
return i, err
|
||||
}
|
||||
d.port.SetReadTimeout(2 * time.Second)
|
||||
n, err := d.port.Read(packet[:1])
|
||||
d.port.SetReadTimeout(readTimeout)
|
||||
n, err := io.ReadFull(d.port, bs[i:i+1])
|
||||
if err != nil {
|
||||
return i, err
|
||||
} else if n == 0 {
|
||||
return i, errors.New("short read")
|
||||
return i + n, err
|
||||
}
|
||||
bs[i] = packet[0]
|
||||
}
|
||||
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) {
|
||||
d.portMu.Lock()
|
||||
defer d.portMu.Unlock()
|
||||
|
@ -77,7 +129,7 @@ func (d *Serial) WriteAt(bs []byte, off int64) (int, error) {
|
|||
return 0, errors.New("OOB write")
|
||||
}
|
||||
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 {
|
||||
return i, err
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ func (m debugger) readFullMemoryCmds() []tea.Cmd {
|
|||
|
||||
func (m debugger) readMemory(start, count int) tea.Cmd {
|
||||
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 msgMemoryChanged{start, count}
|
||||
|
@ -131,6 +131,8 @@ func (m debugger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
case "r":
|
||||
start, end := m.hex.VisibleBytes()
|
||||
return m, m.readMemory(start, end-start)
|
||||
case "R":
|
||||
return m, m.readFullMemory()
|
||||
}
|
||||
case msgErr:
|
||||
m.lastErr = msg.err
|
||||
|
|
Loading…
Reference in New Issue