Macro ftdi_mpsse::mpsse
source · [−]macro_rules! mpsse {
(@replace_expr $_t:tt $sub:expr) => { ... };
(@count_elements $($tts:expr),* $(,)*) => { ... };
(@assert ((let, $_user_passthru:tt), $_read_len:expr), $e:expr, $msg:expr) => { ... };
(@assert ((const, $_user_passthru:tt), $_read_len:expr), $e:expr, $_msg:expr) => { ... };
() => { ... };
(let $id:ident = {$($commands:tt)*}; $($tail:tt)*) => { ... };
(const $id:ident = {$($commands:tt)*}; $($tail:tt)*) => { ... };
(let ($id:ident, $read_len_id:ident) = {$($commands:tt)*}; $($tail:tt)*) => { ... };
(const ($id:ident, $read_len_id:ident) = {$($commands:tt)*}; $($tail:tt)*) => { ... };
($passthru:tt {enable_loopback(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {disable_loopback(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {enable_3phase_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {disable_3phase_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {enable_adaptive_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {disable_adaptive_data_clocking(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {set_gpio_lower($state:expr, $direction:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {set_gpio_upper($state:expr, $direction:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {gpio_lower(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $idx_id:ident = gpio_lower(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {gpio_upper(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $idx_id:ident = gpio_upper(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {send_immediate(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {wait_on_io_high(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {wait_on_io_low(); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {clock_data_out($mode:expr, [$($data:expr),* $(,)*]); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {clock_data_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $range_id:ident = clock_data_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {clock_data($mode:expr, [$($data:expr),* $(,)*]); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $range_id:ident = clock_data($mode:expr, [$($data:expr),* $(,)*]); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {clock_bits_out($mode:expr, $data:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {clock_bits_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $idx_id:ident = clock_bits_in($mode:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {clock_bits($mode:expr, $data:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $idx_id:ident = clock_bits($mode:expr, $data:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
($passthru:tt {clock_tms_out($mode:expr, $data:expr, $tdi:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {clock_tms($mode:expr, $data:expr, $tdi:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
(($passthru:tt, $read_len:tt) {const $idx_id:ident = clock_tms($mode:expr, $data:expr, $tdi:expr, $len:expr); $($tail:tt)*} -> [$($out:tt)*]) => { ... };
((($const_let:tt, ($id:tt, _)), $read_len:expr) {} -> [$($out:tt)*]) => { ... };
((($const_let:tt, ($id:tt, $read_len_id:tt)), $read_len:expr) {} -> [$($out:tt)*]) => { ... };
}
Expand description
Construct an MPSSE command array at compile-time.
Alternative to MpsseCmdBuilder
. Parses a specialized grammar that gathers MPSSE commands
into pseudo-statements contained within zero or more assigned blocks. The pseudo-assignment
syntax of each block creates a fixed-length [u8; N]
array that is bound with let
or
const
1.
Syntax
mpsse! { let command_data = { command1(); command2(); /* ... */ commandN(); }; }
or
mpsse! { let (command_data, READ_LEN) = { command1(); command2(); /* ... */ commandN(); }; }
The second form provides the caller with a constant size value of the expected data length to read after writing the commands to the device.
Commands
enable_loopback()
disable_loopback()
enable_3phase_data_clocking()
disable_3phase_data_clocking()
set_gpio_lower(state: u8, direction: u8)
set_gpio_upper(state: u8, direction: u8)
gpio_lower() -> usize
gpio_upper() -> usize
send_immediate()
wait_on_io_high()
wait_on_io_low()
clock_data_out(mode: ClockDataOut, data: [u8])
clock_data_in(mode: ClockDataIn, len: u16) -> std::ops::Range<usize>
clock_data(mode: ClockData, data: [u8]) -> std::ops::Range<usize>
clock_bits_out(mode: ClockBitsOut, data: u8, len: u8)
clock_bits_in(mode: ClockBitsIn, len: u8) -> usize
clock_bits(mode: ClockBits, data: u8, len: u8) -> usize
clock_tms_out(mode: ClockTMSOut, data: u8, tdi: bool, len: u8)
clock_tms(mode: ClockTMS, data: u8, tdi: bool, len: u8) -> usize
Command pseudo-statements that read data from the device may optionally have the form:
mpsse! {
// command_data and DATA_IN_RANGE are both declared in the scope of the macro expansion.
let command_data = {
const DATA_IN_RANGE = clock_data_in(ClockDataIn::MsbNeg, 3);
};
}
This provides a constant Range
or usize
index value that may be used
to subscript the data read from the device.
clock_data
and clock_data_out
require that the second argument is a fixed-length, square
bracketed list of u8
values. Compile-time limitations make arbitrary array concatenation or
coercion infeasible.
Asserts
For let
bindings, the standard assert
macro is used for validating parameter size inputs.
For const
bindings, const_assert
is used instead.
const_assert
lacks the ability to provide meaningful compile errors, so it may be useful
to temporarily use a let
binding within function scope to diagnose failing macro expansions.
User Abstractions
With macro shadowing, it is possible to extend the macro with additional rules for abstract, device-specific commands.
Comments within the implementation of this macro contain hints on how to implement these rules.
For example, a SPI device typically delineates transfers with the CS line. Fundamental
commands like cs_high
and cs_low
can be implemented this way, along with other
device-specific abstractions.
macro_rules! mpsse {
// Practical abstraction of CS line for SPI devices.
($passthru:tt {cs_low(); $($tail:tt)*} -> [$($out:tt)*]) => {
mpsse!($passthru {
set_gpio_lower(0x0, 0xb);
$($tail)*
} -> [$($out)*]);
};
($passthru:tt {cs_high(); $($tail:tt)*} -> [$($out:tt)*]) => {
mpsse!($passthru {
set_gpio_lower(0x8, 0xb);
$($tail)*
} -> [$($out)*]);
};
// Hypothetical device-specific command. Leverages both user and libftd2xx commands.
($passthru:tt
{const $idx_id:ident = command_42([$($data:expr),* $(,)*]); $($tail:tt)*} ->
[$($out:tt)*]) => {
mpsse!($passthru {
cs_low();
const $idx_id = clock_data(::ftdi_mpsse::ClockData::MsbPosIn, [0x42, $($data,)*]);
cs_high();
$($tail)*
} -> [$($out)*]);
};
// Everything else handled by libftd2xx crate implementation.
($($tokens:tt)*) => {
::ftdi_mpsse::mpsse!($($tokens)*);
};
}
mpsse! {
const (COMMAND_DATA, READ_LEN) = {
wait_on_io_high();
const COMMAND_42_RESULT_RANGE = command_42([11, 22, 33]);
send_immediate();
};
}
Example
use ftdi_mpsse::{mpsse, ClockDataIn, ClockDataOut};
use libftd2xx::{Ft232h, FtdiCommon, FtdiMpsse};
mpsse! {
const (COMMAND_DATA, READ_LEN) = {
set_gpio_lower(0xFA, 0xFB);
set_gpio_lower(0xF2, 0xFB);
clock_data_out(ClockDataOut::MsbNeg, [0x12, 0x34, 0x56]);
const DATA_IN_RANGE = clock_data_in(ClockDataIn::MsbNeg, 3);
set_gpio_lower(0xFA, 0xFB);
send_immediate();
};
}
let mut ft = Ft232h::with_serial_number("FT5AVX6B")?;
ft.initialize_mpsse_default()?;
ft.write_all(&COMMAND_DATA)?;
let mut buf: [u8; READ_LEN] = [0; READ_LEN];
ft.read_all(&mut buf)?;
println!("Data slice in: {:?}", &buf[DATA_IN_RANGE]);
In
const
bindings, all values used as command parameters and data must be const. ↩