// 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) // , , , , ..., // // Short streams (dataBit is specified): // - [1, 8] bits // , , // // 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. // , , 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. // // , , 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. // // , , gpioSetD byte = 0x80 gpioSetC byte = 0x82 // , returns 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 // // , , 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. // , clockOnShort byte = 0x8E // Enables the clock between [8, 524288] pulses in 8 multiples. // , , clockOnLong byte = 0x8F // Enables clock until D5 is high or low. Used with JTAG. clockUntilHigh byte = 0x94 clockUntilLow byte = 0x95 // , , 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. // // , cpuReadShort byte = 0x90 // , , cpuReadFar byte = 0x91 // , , cpuWriteShort byte = 0x92 // , , , 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 , . 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) }