Table of Contents
The USB interface is an FTDI FT240X (datasheet). While not explicitely supported by libftdi, you can still use it (libftdi will think it is an FT230X).
lsusb
output
Bus 001 Device 061: ID 0403:7fd0 Future Technology Devices International, Ltd ScanaQuad SQ50
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x0403 Future Technology Devices International, Ltd
idProduct 0x7fd0
bcdDevice 10.00
iManufacturer 1 IKALOGIC
iProduct 2 ScanaQuad SQ50
iSerial 3 1003050005482
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0020
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 400mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 2 ScanaQuad SQ50
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status: 0x0000
(Bus Powered)
Note: the serial number matches the one on the sticker on the bottom of the plastic shell.
Protocol
The device operates in two (or three, depending on how you count) operting modes: bootloader mode (and authenticated bootloader mode), and application mode. On startup, the device starts in 'unauthenticated' bootloader mode.
In bootloader mode, you first have to send a magic number for the device (stored in the FT240X' EEPROM) to enable some functionality: when authenticated, in bootloader mode, you can access the Spartan-3AN's internal SPI flash, as well as maybe a few extra bits.
You can switch to application mode (and back to bootloader mode) at any time, however, allegedly (this is the behavior of the ScanaPLUS device, which has a similar setup) capturing samples (in application mode) will not work when not authenticated in bootloader mode.
A log of libftd2xx function calls from ScanaStudio, in various scenarios, is available here.
Commands are typically a one-byte command ID, followed by a variable amount of data bytes. These are sent through the FTDI USB interface (e.g. ftdi_read_data
/ftdi_write_data
using libftdi), they are not raw USB packets.
Mode-independent
0xfd
: Get status/mode- Arguments: fixed
0x00 0x01 0x02 0xfe
- Response:
- Unauthenticated bootloader mode:
0x09 0x09 0x09 0x09
- Authenticated bootloader mode:
0x01 0x01 0x01 0x01
- Application mode:
0x22 0x22 0x22 0x22
- Unauthenticated bootloader mode:
- Arguments: fixed
0x93
: Reset to application mode- No arguments
- No response
0x94
: Reset to bootloader mode- No arguments
- No response
- TODO: does this reset to authenticated or unauthenticated bootloader mode after already having authenticated once? (Doesn't matter that much in the end..)
Bootloader mode
0xf1
: Send unlock/authentication code- Arguments: 3 bytes from FT240X EEPROM word locations
0x12:0x13
(little-endian, so low byte of 0x12 first, then hi byte of 0x12, then lo byte of 0x13), followed by a fixed 23 zero bytes - No response
- Arguments: 3 bytes from FT240X EEPROM word locations
0x90
: Spartan-3AN internal SPI flash chip select- Arguments: fixed
0x00
- No response
- Arguments: fixed
0x91
: Spartan-3AN internal SPI flash chip deselect- Arguments: fixed
0x00
- No response
- Arguments: fixed
0x92
: Spartan-3AN internal SPI flash chip data transfer- Arguments: single byte to send (
0xff
for SPI read operations) - Response: single byte result of the SPI operation (garbage if the byte transferred was meant as a write)
- Yes, SPI transfers are done byte-by-byte this way. For details on how to communicate with the Spartan-3AN internal flash, see Xilinx docs (UG333). See the "Example transfers" section below.
- Arguments: single byte to send (
Application mode
0xf0
: Start transaction- Arguments: one (sometimes two) transaction type byte(s)
0x00
: Cancel ongoing capture/wait-for-trigger0x01
: Start capture/wait for trigger- Response:
- 3-byte "trigger instant" (little-endian): this is the point at which the trigger happened, units are
MS1*16
. (That is, if the trigger happened at the very last sample, this value will beMS1*16
. If it happens right in the middle, it'll be 50% ofMS1*16
.) (ForMS1
: see settings blob.) - 1 status byte:
0xdd
indicates success, no other values seen.
- 3-byte "trigger instant" (little-endian): this is the point at which the trigger happened, units are
- Response:
0x02
: Start generating an uploaded signal indefinitely0x03
: Start capture/generate/wait for trigger in mixed mode- Response: same as
0xf0 0x01
- Response: same as
0x05 0xf3
: Start uploading signal generator pattern to the logic analyzer- This command is immediately followed by the generator pattern data. For the size of this blob, see the description of the Settings blob.
- The extra
0xf3
byte seems to always be sent, so it's probably mandatory.
0x06
: Start downloading captured signal data- Response: capture signal data. For the size of this blob, see the description of the Settings blob.
0x07
: Start generating an uploaded signal once
- No response unless mentioned otherwise in the subtypes
- Arguments: one (sometimes two) transaction type byte(s)
0xf1
: Send settings blob- Arguments: settings blob (see below)
- No response
0xf4
: Send trigger step blobs- Arguments: as many trigger step blobs (32 bits each) as specified in the settings blob (see below for both).
- No response
Settings blob
This blob is 24 bytes in size.
0x00
: Normally0x01
,0x09
means "scale trigger pulse widths to microseconds". No other values observed.0x01..0x02
: Clock frequency (little-endian). Equal to 100000 divided by the frequency in kHz.0x03..0x04
: Trigger pulse width scale (16-bit little-endian). Maximum0x8008
. This value, multiplied by the minimum/maximum pulse width, should be equal to 4 times the product of the configured pulse width (in seconds) and the sample frequency (in Hz). That is:settings.trigger_pulse_width * trigstep.pw_min = 4 * pw_in_s * samplefreq_in_hz
.0x05..0x07
: Memory setting 1 (MS1): For a description, see below.0x08..0x0a
: Memory setting 2 (MS2): For a description, see below.0x0b..0x0d
: Memory setting 3 (MS3): For a description, see below.0x0e
: Unknown, always zero.0x0f
: Number of trigger steps.0x10
: Unknown, always0xf0
.0x11
: Unknown, always0x0f
.0x12
: Channel output settings bitmap, see below.0x13..0x14
: Voltage settings. See below.0x15
: Unknown, always0x32
.0x16
: Capture mode flag.0x01
in capture and mixed mode,0x00
in generate mode.0x17
: Generate mode flag.0x01
in generate and mixed mode,0x00
in capture mode.
"Passive settings" (used in the "Example transfers" section) means settings that retain frequency, voltage, ..., settings, but disable triggers and reset capture/generate bytes.
The sample rate settings' minimum value is 0x0001
, which corresponds to 200 MHz. When given this sample setting, my SQ50 complies and starts sampling at 200 MHz, which it nominally cannot.
Memory settings: These three fields are each 3-byte little-endian values:
MS1
denotes the memory size used for storing captured signal data.MS2
denotes the total memory size used.MS3
configures the pre-trigger sample settings:MS3 = MS1 * (1 - pre_trigger_samples)
, withpre_trigger_samples
a float between 0 and 1 (0% pretrigger and 100% pretrigger).
The most significant nybble of MS3
is the complement of the high nybble of teh channel output settings bitmap (0x12
), and should be masked out before doing memory size calculations.
In capture mode, MS1
and MS2
are equal. In generator mode, MS1
and MS3
are zero while MS2
denotes the size of the memory used for storing generator patterns. In mixed mode, MS2
is the sum of MS1
(the memory size used for captured data) and the memory size used for generator data.
The number of bytes received as capture data or sent as generator patterns, is equal to the numbers here, multiplied by two. That is, the MSx
values are (most likely) in 16-bit units.
In 4-channel capture mode, the maximum memory size for the SQ50 seems to be 0x03d090
. This is equal to 1/4th of the sample frequency (in Hz) multiplied by the capture period (in seconds).
Channel output settings bitmap:
The low nybble of this byte is always 0xf
. For each channel, if the channel is configured as an output, the bit in the high nybble corresponding to the channel is 1, otherwise it is 0.
In other words, the byte value is (chan-x-is-output ? 1 : 0) << (x+4))
ORed together for each channel x
(0..3), ORed together with the constant 0x0f
.
Voltage settings:
Voltage (V) | Before capturing | During capture |
---|---|---|
1.8 | 0x46 0x4b |
0x46 0x1e |
2.8 | 0x6e 0x4b |
0x6e 0x2c |
3.3 | 0x81 0x4b |
0x81 0x46 |
3.6 | 0x8d 0x4b |
0x8d 0x4f |
5.0 | 0xc4 0x4b |
0xc4 0x72 |
The first byte of the voltage settings is equal to floor(max_voltage * 39.2)
. The second byte is the threshold voltage. In idle mode, the second byte is always 0x4b
(1.9V), but during a capture it varies. For some reason, it isn't always half the maximum voltage, sometimes it's above it, sometimes below.
The default settings blob used seems to be 01 04 00 00 00 90 d0 03 90 d0 03 e8 6e f3 00 00 f0 0f 0f 81 4b 32 01 00
.
Trigger step blob
Every trigger step is 32 bits (4 bytes) in size. Unlike the above settings blob, this one does not distinguish different functionalities in different bytes as much. Therefore, we will represent it here as a single 32-bit bitmap. This bitmap is still sent as little-endian over the FTDI serial->FIFO USB protocol!
byte: .---------- 3 -----------.---------- 2 ------------.------------ 1 ------------.-------------------- 0 ----------------------.
bit : .31 .30.29 28 27 26 25 24'23 22 21. 20 .19.18 17 16'15 14 13 12 11 10. 9 . 8 . 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 .
+---+--+-----------------'--------+----+--+--------'-----------------+----+----+----+----+-----+-----+-----+-----+-----+-----+
field: |LVL|0 | pw_max ' |OVRL|0 | ' pw_min |IGN4|IGN3|IGN2|IGN1|NOMAX|NOMIN|HIRI4|HIRI3|HIRI2|HIRI1|
+---+--+-----------------'--------+----+--+--------'-----------------+----+----+----+----+-----+-----+-----+-----+-----+-----+
- 31:
LVL
: edge/level flag: edge-triggered (0) or level-triggered (1). Denotes whether this step is using edge-triggering. If so, only one channel may be marked as enabled/non-ignored. - 30: Unknown, always 0.
- 29..21:
pw_max
: 9-bit (29..21 → 8..0) maximum pulse width setting. Unit configured in settings blob. - 20:
OVRL
: level trigger override: when this bit is 1, the trigger should still be considered a level trigger, even if bit 31 says otherwise. Used in ScanaStudio for "logic triggers" only. - 19: Unknown, always 0.
- 18..10:
pw_min
: 9-bit (18..10 → 8..0) minimum pulse width setting. Unit configured in settings blob. - 9:
IGN4
: set to 1 to ignore channel 4 in this trigger step. - 8:
IGN3
: set to 1 to ignore channel 3 in this trigger step. - 7:
IGN2
: set to 1 to ignore channel 2 in this trigger step. - 6:
IGN1
: set to 1 to ignore channel 1 in this trigger step. - 5:
NOMAX
: set to 1 to disable/ignore the maximum pulse width setting. - 4:
NOMIN
: set to 1 to disable/ignore the minimum pulse width setting. - 3:
HIRI4
: set to 1 trigger on channel 4 high or rising, 0 for low or falling. (Edge or level depends on bits 31 and 20) - 2:
HIRI3
: set to 1 trigger on channel 3 high or rising, 0 for low or falling. (Edge or level depends on bits 31 and 20) - 1:
HIRI2
: set to 1 trigger on channel 2 high or rising, 0 for low or falling. (Edge or level depends on bits 31 and 20) - 0:
HIRI1
: set to 1 trigger on channel 1 high or rising, 0 for low or falling. (Edge or level depends on bits 31 and 20)
Example transfers
Below is what the ScanaStudio software does.
Abbreviations/terms used:
APP
: application mode variant of a commandBL
bootloader mode variant of a commandpassive
: see "Settings blob".SC/WFT
: signal capture/wait-for-trigger- While a status response is technically the same byte repeated 4 byte, the byte is only listed once below.
Device init/reset
- Send settings (
0xf1
APP) (passive) (probably spurious) - Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
)
- Should return either
0x09
(device just started up) or0x22
(already inited).
- Switch to bootloader mode (
0x94
) - Read 3 magic auth bytes from FT240X EEPROM at (little-endian) word addresses 0x12 and 0x13.
- Send the above magic auth bytes (
0xf1
BL) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x01
. - Switch to application mode (
0x93
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Send settings (
0xf1
APP) (passive)
Capture a signal
- Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Send settings (
0xf1
) (passive) (spurious?) - Send settings (
0xf1
) - Send trigger steps (
0xf4
) (optional) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Cancel ongoing SC/WFT (
0xf0 0x00
) - Start SC/WFT (
0xf0 0x01
) - Read 4-byte response (trigger position, result)
- Cancel ongoing SC/WFT (
0xf0 0x00
) - Start capture data download (
0xf0 0x06
) - Read capture data bytes (
MS1*2
bytes) - Cancel ongoing SC/WFT (
0xf0 0x00
) - Send settings (
0xf1
) (passive) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
.
Generate a signal
- Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Send settings (
0xf1
) (passive, but generator mode enabled) - Start generator upload pattern (
0xf0 0x05 0xf3
) - Send generator pattern bytes (
MS2*2
bytes) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Start pattern generation, indefinitely (
0xf0 0x02
) or once (0xf0 0x07
) - Send settings (
0xf1
) - (wait...)
- Stop pattern generation: Cancel ongoing SC/WFT (
0xf0 0x00
) - Send settings (
0xf1
) (passive, but generator mode enabled) - Send settings (
0xf1
) (passive) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
.
Perform a mixed-mode transaction
- Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Send settings (
0xf1
) (passive, but capture and generator modes enabled) - Start generator upload pattern (
0xf0 0x05 0xf3
) - Send generator pattern bytes (
(MS2-MS1)*2
bytes) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Send settings (
0xf1
) - Send trigger steps (
0xf4
) (optional) (?) - Cancel ongoing SC/WFT (
0xf0 0x00
) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Send settings (
0xf1
) - Send settings (
0xf1
) (spurious?) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
. - Cancel ongoing SC/WFT (
0xf0 0x00
) - Start mixed-mode action (
0xf0 0x03
) - Read 4-byte response (trigger position, result)
- Cancel ongoing SC/WFT (
0xf0 0x00
) - Start capture data download (
0xf0 0x06
) - Read capture data bytes (
MS1*2
bytes) - Cancel ongoing SC/WFT (
0xf0 0x00
) - Send settings (
0xf1
) (passive) - Get status (
0xfd 0x00 0x01 0x02 0xfe
), should return0x22
.
Spartan-3AN internal SPI flash: read ID and status
Flash ID:
Send 0x90,0x00, no read data
Send 0x92,0x9f, read 1 byte and discard it
Send 0x92,0xff, read 1 data byte
Send 0x92,0xff, read 1 data byte
Send 0x91,0x00, no read data
Status:
Send 0x90,0x00, no read data
Send 0x92,0xd7, read 1 byte and discard it
Send 0x92,0xff, read 1 data byte
Send 0x91,0x00, no read data
Spartan-3AN internal SPI flash: dump entire flash (including bootloader and application mode bitstreams)
Send 0x90,0x00, no read data
Send 0x92,0x0b, read 1 byte and discard it
Send 0x92,0x00, read 1 byte and discard it
Send 0x92,0x00, read 1 byte and discard it
Send 0x92,0x00, read 1 byte and discard it
Send 0x92,0xff, read 1 byte and discard it
Repeat 135168 times:
Send 0x92,0xff, read 1 data byte
Send 0x91,0x00, no read data