453 lines
12 KiB
Go
453 lines
12 KiB
Go
|
// Copyright 2017 The Periph Authors. All rights reserved.
|
||
|
// Use of this source code is governed under the Apache License, Version 2.0
|
||
|
// that can be found in the LICENSE file.
|
||
|
|
||
|
// MPSSE is Multi-Protocol Synchronous Serial Engine
|
||
|
//
|
||
|
// MPSSE basics:
|
||
|
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_135_MPSSE_Basics.pdf
|
||
|
//
|
||
|
// MPSSE and MCU emulation modes:
|
||
|
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf
|
||
|
|
||
|
package ftdi
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
|
||
|
"periph.io/x/conn/v3/gpio"
|
||
|
"periph.io/x/conn/v3/physic"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// TDI/TDO serial operation synchronised on clock edges.
|
||
|
//
|
||
|
// Long streams (default):
|
||
|
// - [1, 65536] bytes (length is sent minus one, requires 8 bits multiple)
|
||
|
// <op>, <LengthLow-1>, <LengthHigh-1>, <byte0>, ..., <byteN>
|
||
|
//
|
||
|
// Short streams (dataBit is specified):
|
||
|
// - [1, 8] bits
|
||
|
// <op>, <Length-1>, <byte>
|
||
|
//
|
||
|
// When both dataOut and dataIn are specified, one of dataOutFall or
|
||
|
// dataInFall should be specified, at least for most sane protocols.
|
||
|
//
|
||
|
// Flags:
|
||
|
dataOut byte = 0x10 // Enable output, default on +VE (Rise)
|
||
|
dataIn byte = 0x20 // Enable input, default on +VE (Rise)
|
||
|
dataOutFall byte = 0x01 // instead of Rise
|
||
|
dataInFall byte = 0x04 // instead of Rise
|
||
|
dataLSBF byte = 0x08 // instead of MSBF
|
||
|
dataBit byte = 0x02 // instead of Byte
|
||
|
|
||
|
// Data line drives low when the data is 0 and tristates on data 1. This is
|
||
|
// used with I²C.
|
||
|
// <op>, <ADBus pins>, <ACBus pins>
|
||
|
dataTristate byte = 0x9E
|
||
|
|
||
|
// TSM operation (for JTAG).
|
||
|
//
|
||
|
// - Send bits 6 to 0 to the TMS pin using LSB or MSB.
|
||
|
// - Bit 7 is passed to TDI/DO before the first clock of TMS and is held
|
||
|
// static for the duration of TMS clocking.
|
||
|
//
|
||
|
// <op>, <Length>, <byte>
|
||
|
tmsOutLSBFRise byte = 0x4A
|
||
|
tmsOutLSBFFall byte = 0x4B
|
||
|
tmsIOLSBInRise byte = 0x6A
|
||
|
tmsIOLSBInFall byte = 0x6B
|
||
|
// Unclear: 0x6E and 0x6F
|
||
|
|
||
|
// GPIO operation.
|
||
|
//
|
||
|
// - Operates on 8 GPIOs at a time, e.g. C0~C7 or D0~D7.
|
||
|
// - Direction 1 means output, 0 means input.
|
||
|
//
|
||
|
// <op>, <value>, <direction>
|
||
|
gpioSetD byte = 0x80
|
||
|
gpioSetC byte = 0x82
|
||
|
// <op>, returns <value>
|
||
|
gpioReadD byte = 0x81
|
||
|
gpioReadC byte = 0x83
|
||
|
|
||
|
// Internal loopback.
|
||
|
//
|
||
|
// Connects TDI and TDO together.
|
||
|
internalLoopbackEnable byte = 0x84
|
||
|
internalLoopbackDisable byte = 0x85
|
||
|
|
||
|
// Clock.
|
||
|
//
|
||
|
// The TCK/SK has a 50% duty cycle.
|
||
|
//
|
||
|
// The inactive clock state can be set via the gpioSetD command and control
|
||
|
// bit 0.
|
||
|
//
|
||
|
// By default, the base clock is 6MHz via a 5x divisor. On
|
||
|
// FT232H/FT2232H/FT4232H, the 5x divisor can be disabled.
|
||
|
clock30MHz byte = 0x8A
|
||
|
clock6MHz byte = 0x8B
|
||
|
// Sets clock divisor.
|
||
|
//
|
||
|
// The effective value depends if clock30MHz was sent or not.
|
||
|
//
|
||
|
// - 0(1) 6MHz / 30MHz
|
||
|
// - 1(2) 3MHz / 15MHz
|
||
|
// - 2(3) 2MHz / 10MHz
|
||
|
// - 3(4) 1.5MHz / 7.5MHz
|
||
|
// - 4(5) 1.25MHz / 6MHz
|
||
|
// - ...
|
||
|
// - 0xFFFF(65536) 91.553Hz / 457.763Hz
|
||
|
//
|
||
|
// <op>, <valueL-1>, <valueH-1>
|
||
|
clockSetDivisor byte = 0x86
|
||
|
// Uses 3 phases data clocking: data is valid on both clock edges. Needed
|
||
|
// for I²C.
|
||
|
clock3Phase byte = 0x8C
|
||
|
// Uses normal 2 phases data clocking.
|
||
|
clock2Phase byte = 0x8D
|
||
|
// Enables clock even while not doing any operation. Used with JTAG.
|
||
|
// Enables the clock between [1, 8] pulses.
|
||
|
// <op>, <length-1>
|
||
|
clockOnShort byte = 0x8E
|
||
|
// Enables the clock between [8, 524288] pulses in 8 multiples.
|
||
|
// <op>, <lengthL-1>, <lengthH-1>
|
||
|
clockOnLong byte = 0x8F
|
||
|
// Enables clock until D5 is high or low. Used with JTAG.
|
||
|
clockUntilHigh byte = 0x94
|
||
|
clockUntilLow byte = 0x95
|
||
|
// <op>, <lengthL-1>, <lengthH-1> in 8 multiples.
|
||
|
clockUntilHighLong byte = 0x9C
|
||
|
clockUntilLowLong byte = 0x9D
|
||
|
// Enables adaptive clocking. Used with JTAG.
|
||
|
//
|
||
|
// This causes the controller to wait for D7 signal state as an ACK.
|
||
|
clockAdaptive byte = 0x96
|
||
|
// Disables adaptive clocking.
|
||
|
clockNormal byte = 0x97
|
||
|
|
||
|
// CPU mode.
|
||
|
//
|
||
|
// Access the device registers like a memory mapped device.
|
||
|
//
|
||
|
// <op>, <addrLow>
|
||
|
cpuReadShort byte = 0x90
|
||
|
// <op>, <addrHi>, <addrLow>
|
||
|
cpuReadFar byte = 0x91
|
||
|
// <op>, <addrLow>, <data>
|
||
|
cpuWriteShort byte = 0x92
|
||
|
// <op>, <addrHi>, <addrLow>, <data>
|
||
|
cpuWriteFar byte = 0x91
|
||
|
|
||
|
// Buffer operations.
|
||
|
//
|
||
|
// Flush the buffer back to the host.
|
||
|
flush byte = 0x87
|
||
|
// Wait until D5 (JTAG) or I/O1 (CPU) is high. Once it is detected as
|
||
|
// high, the MPSSE engine moves on to process the next instruction.
|
||
|
waitHigh byte = 0x88
|
||
|
waitLow byte = 0x89
|
||
|
)
|
||
|
|
||
|
// InitMPSSE sets the device into MPSSE mode.
|
||
|
//
|
||
|
// This requires a f232h, ft2232, ft2232h or a ft4232h.
|
||
|
//
|
||
|
// Use only one of Init or InitMPSSE.
|
||
|
func (h *handle) InitMPSSE() error {
|
||
|
// http://www.ftdichip.com/Support/Documents/AppNotes/AN_255_USB%20to%20I2C%20Example%20using%20the%20FT232H%20and%20FT201X%20devices.pdf
|
||
|
// Pre-state:
|
||
|
// - Write EEPROM i.IsFifo = true so the device DBus is started in tristate.
|
||
|
|
||
|
// Try to verify the MPSSE controller without initializing it first. This is
|
||
|
// the 'happy path', which enables reusing the device is its current state
|
||
|
// without affecting current GPIO state.
|
||
|
if h.mpsseVerify() != nil {
|
||
|
// Do a full reset. Just trying to set the MPSSE controller will
|
||
|
// likely not work. That's a layering violation (since the retry with reset
|
||
|
// is done in driver.go) but we've survived worse things...
|
||
|
//
|
||
|
// TODO(maruel): This is not helping in practice, this need to be fine
|
||
|
// tuned.
|
||
|
if err := h.Reset(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := h.Init(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := h.SetBitMode(0, bitModeMpsse); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := h.mpsseVerify(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Initialize MPSSE to a known state.
|
||
|
// Reset the clock since it is impossible to read back the current clock rate.
|
||
|
// Reset all the GPIOs are inputs since it is impossible to read back the
|
||
|
// state of each GPIO (if they are input or output).
|
||
|
cmd := []byte{
|
||
|
clock30MHz, clockNormal, clock2Phase, internalLoopbackDisable,
|
||
|
gpioSetC, 0x00, 0x00,
|
||
|
gpioSetD, 0x00, 0x00,
|
||
|
}
|
||
|
if _, err := h.Write(cmd); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// Success!!
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// mpsseVerify sends an invalid MPSSE command and verifies the returned value
|
||
|
// is incorrect.
|
||
|
//
|
||
|
// In practice this takes around 2ms.
|
||
|
func (h *handle) mpsseVerify() error {
|
||
|
var b [16]byte
|
||
|
for _, v := range []byte{0xAA, 0xAB} {
|
||
|
// Write a bad command and ensure it returned correctly.
|
||
|
// Unlike what the application note proposes, include a flush op right
|
||
|
// after. Without the flush, the device will only flush after the delay
|
||
|
// specified to SetLatencyTimer. The flush removes this unneeded wait,
|
||
|
// which enables increasing the delay specified to SetLatencyTimer.
|
||
|
b[0] = v
|
||
|
b[1] = flush
|
||
|
if _, err := h.Write(b[:2]); err != nil {
|
||
|
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
|
||
|
}
|
||
|
// Sometimes, especially right after a reset, the device spews a few bytes.
|
||
|
// Discard them. This significantly increases the odds of a successful
|
||
|
// initialization.
|
||
|
p, e := h.h.GetQueueStatus()
|
||
|
if e != 0 {
|
||
|
return toErr("Read/GetQueueStatus", e)
|
||
|
}
|
||
|
for p > 2 {
|
||
|
l := int(p) - 2
|
||
|
if l > len(b) {
|
||
|
l = len(b)
|
||
|
}
|
||
|
// Discard the overflow bytes.
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
if _, err := h.ReadAll(ctx, b[:l]); err != nil {
|
||
|
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
|
||
|
}
|
||
|
p -= uint32(l)
|
||
|
}
|
||
|
// Custom implementation, as we want to flush any stray byte.
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
if _, err := h.ReadAll(ctx, b[:2]); err != nil {
|
||
|
return fmt.Errorf("d2xx: mpsseVerify: %v", err)
|
||
|
}
|
||
|
// 0xFA means invalid command, 0xAA is the command echoed back.
|
||
|
if b[0] != 0xFA || b[1] != v {
|
||
|
return fmt.Errorf("d2xx: mpsseVerify: failed test for byte %#x: %#x", v, b)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
// MPSSERegRead reads the memory mapped registers from the device.
|
||
|
func (h *handle) MPSSERegRead(addr uint16) (byte, error) {
|
||
|
// Unlike most other operations, the uint16 byte order is <hi>, <lo>.
|
||
|
b := [...]byte{cpuReadFar, byte(addr >> 8), byte(addr), flush}
|
||
|
if _, err := h.Write(b[:]); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
_, err := h.ReadAll(ctx, b[:1])
|
||
|
return b[0], err
|
||
|
}
|
||
|
|
||
|
// MPSSEClock sets the clock at the closest value and returns it.
|
||
|
func (h *handle) MPSSEClock(f physic.Frequency) (physic.Frequency, error) {
|
||
|
// TODO(maruel): Memory clock and skip if the same value.
|
||
|
clk := clock30MHz
|
||
|
base := 30 * physic.MegaHertz
|
||
|
div := base / f
|
||
|
if div >= 65536 {
|
||
|
clk = clock6MHz
|
||
|
base /= 5
|
||
|
div = base / f
|
||
|
if div >= 65536 {
|
||
|
return 0, errors.New("d2xx: clock frequency is too low")
|
||
|
}
|
||
|
}
|
||
|
b := [...]byte{clk, clockSetDivisor, byte(div - 1), byte((div - 1) >> 8)}
|
||
|
_, err := h.Write(b[:])
|
||
|
return base / div, err
|
||
|
}
|
||
|
|
||
|
// mpsseTxOp returns the right MPSSE command byte for the stream.
|
||
|
func mpsseTxOp(w, r bool, ew, er gpio.Edge, lsbf bool) byte {
|
||
|
op := byte(0)
|
||
|
if lsbf {
|
||
|
op |= dataLSBF
|
||
|
}
|
||
|
if w {
|
||
|
op |= dataOut
|
||
|
if ew == gpio.FallingEdge {
|
||
|
op |= dataOutFall
|
||
|
}
|
||
|
}
|
||
|
if r {
|
||
|
op |= dataIn
|
||
|
if er == gpio.FallingEdge {
|
||
|
op |= dataInFall
|
||
|
}
|
||
|
}
|
||
|
return op
|
||
|
}
|
||
|
|
||
|
// MPSSETx runs a transaction on the clock on pins D0, D1 and D2.
|
||
|
//
|
||
|
// It can only do it on a multiple of 8 bits.
|
||
|
func (h *handle) MPSSETx(w, r []byte, ew, er gpio.Edge, lsbf bool) error {
|
||
|
l := len(w)
|
||
|
if len(w) != 0 {
|
||
|
// TODO(maruel): This is easy to fix by daisy chaining operations.
|
||
|
if len(w) > 65536 {
|
||
|
return errors.New("d2xx: write buffer too long; max 65536")
|
||
|
}
|
||
|
}
|
||
|
if len(r) != 0 {
|
||
|
if len(r) > 65536 {
|
||
|
return errors.New("d2xx: read buffer too long; max 65536")
|
||
|
}
|
||
|
if l != 0 && len(r) != l {
|
||
|
return errors.New("d2xx: mismatched buffer lengths")
|
||
|
}
|
||
|
l = len(r)
|
||
|
}
|
||
|
// The FT232H has 1Kb Tx and Rx buffers. So partial writes should be done.
|
||
|
// TODO(maruel): Test.
|
||
|
|
||
|
// Flush can be useful if rbits != 0.
|
||
|
op := mpsseTxOp(len(w) != 0, len(r) != 0, ew, er, lsbf)
|
||
|
cmd := []byte{op, byte(l - 1), byte((l - 1) >> 8)}
|
||
|
cmd = append(cmd, w...)
|
||
|
if len(r) != 0 {
|
||
|
cmd = append(cmd, flush)
|
||
|
}
|
||
|
if _, err := h.Write(cmd); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if len(r) != 0 {
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
_, err := h.ReadAll(ctx, r)
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// MPSSETxShort runs a transaction on the clock pins D0, D1 and D2 for a byte
|
||
|
// or less: between 1 and 8 bits.
|
||
|
func (h *handle) MPSSETxShort(w byte, wbits, rbits int, ew, er gpio.Edge, lsbf bool) (byte, error) {
|
||
|
op := byte(dataBit)
|
||
|
if lsbf {
|
||
|
op |= dataLSBF
|
||
|
}
|
||
|
l := wbits
|
||
|
if wbits != 0 {
|
||
|
if wbits > 8 {
|
||
|
return 0, errors.New("d2xx: write buffer too long; max 8")
|
||
|
}
|
||
|
op |= dataOut
|
||
|
if ew == gpio.FallingEdge {
|
||
|
op |= dataOutFall
|
||
|
}
|
||
|
}
|
||
|
if rbits != 0 {
|
||
|
if rbits > 8 {
|
||
|
return 0, errors.New("d2xx: read buffer too long; max 8")
|
||
|
}
|
||
|
op |= dataIn
|
||
|
if er == gpio.FallingEdge {
|
||
|
op |= dataInFall
|
||
|
}
|
||
|
if l != 0 && rbits != l {
|
||
|
return 0, errors.New("d2xx: mismatched buffer lengths")
|
||
|
}
|
||
|
l = rbits
|
||
|
}
|
||
|
b := [3]byte{op, byte(l - 1)}
|
||
|
cmd := b[:2]
|
||
|
if wbits != 0 {
|
||
|
cmd = append(cmd, w)
|
||
|
}
|
||
|
if rbits != 0 {
|
||
|
cmd = append(cmd, flush)
|
||
|
}
|
||
|
if _, err := h.Write(cmd); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
if rbits != 0 {
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
_, err := h.ReadAll(ctx, b[:1])
|
||
|
return b[0], err
|
||
|
}
|
||
|
return 0, nil
|
||
|
}
|
||
|
|
||
|
// MPSSECBus operates on 8 GPIOs at a time C0~C7.
|
||
|
//
|
||
|
// Direction 1 means output, 0 means input.
|
||
|
func (h *handle) MPSSECBus(mask, value byte) error {
|
||
|
b := [...]byte{gpioSetC, value, mask}
|
||
|
_, err := h.Write(b[:])
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// MPSSEDBus operates on 8 GPIOs at a time D0~D7.
|
||
|
//
|
||
|
// Direction 1 means output, 0 means input.
|
||
|
func (h *handle) MPSSEDBus(mask, value byte) error {
|
||
|
b := [...]byte{gpioSetD, value, mask}
|
||
|
_, err := h.Write(b[:])
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// MPSSECBusRead reads all the CBus pins C0~C7.
|
||
|
func (h *handle) MPSSECBusRead() (byte, error) {
|
||
|
b := [...]byte{gpioReadC, flush}
|
||
|
if _, err := h.Write(b[:]); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
if _, err := h.ReadAll(ctx, b[:1]); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
return b[0], nil
|
||
|
}
|
||
|
|
||
|
// MPSSEDBusRead reads all the DBus pins D0~D7.
|
||
|
func (h *handle) MPSSEDBusRead() (byte, error) {
|
||
|
b := [...]byte{gpioReadD, flush}
|
||
|
if _, err := h.Write(b[:]); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
ctx, cancel := context200ms()
|
||
|
defer cancel()
|
||
|
if _, err := h.ReadAll(ctx, b[:1]); err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
return b[0], nil
|
||
|
}
|
||
|
|
||
|
func context200ms() (context.Context, func()) {
|
||
|
return context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||
|
}
|