xvc-server 0.3.0

Library for implementing Xilinx Virtual Cable (XVC) servers that handle JTAG communication with FPGA devices over network connections
Documentation
//! # XVC Server Library
//!
//! This crate provides a foundation for implementing Xilinx Virtual Cable (XVC) servers
//! that handle JTAG communication with FPGA devices over network connections.
//!
//! ## Overview
//!
//! XVC is a protocol used by Xilinx design tools to interact with FPGA devices remotely.
//! This library abstracts the protocol handling and provides a server implementation that
//! can work with different backend device drivers.
//!
//! ## Architecture
//!
//! The crate is built around two main components:
//!
//! - **[`XvcServer`] Trait**: Defines the interface that backend drivers must implement
//!   to handle low-level JTAG operations (TCK configuration and vector shifting)
//! - **[`server::Server`]**: A generic server that handles XVC protocol communication,
//!   message parsing, and client connections
//!
//! ## How It Works
//!
//! 1. A backend driver (e.g., kernel driver, UIO device) implements the [`XvcServer`] trait
//! 2. The driver is wrapped in a [`server::Server`] instance
//! 3. The server listens for TCP connections and processes XVC protocol messages
//! 4. Each message is dispatched to the backend driver for actual JTAG operations
//! 5. Results are serialized and sent back to the client
//!
//! ## Protocol Support
//!
//! This implementation supports the XVC 1.0 protocol with the following operations:
//!
//! - **GetInfo**: Query server capabilities (version, max vector size)
//! - **SetTck**: Configure the JTAG Test Clock (TCK) period
//! - **Shift**: Perform JTAG vector shifting (TMS/TDI/TDO)
//!
//! For detailed protocol information, see the [`xvc_protocol`](https://docs.rs/xvc-protocol/) crate.
//!
//! ## Basic Usage
//!
//! ### Implementing a Backend Driver
//!
//! Create a struct that implements the [`XvcServer`] trait:
//!
//! ```no_run
//! use xvc_server::XvcServer;
//!
//! struct MyDriver {
//!     // device-specific fields
//! }
//!
//! impl XvcServer for MyDriver {
//!     type Err = std::io::Error; // device-specific error
//!
//!     fn set_tck(&self, period_ns: u32) -> Result<u32, Self::Err> {
//!         // Configure hardware TCK period
//!         Ok(period_ns)
//!     }
//!
//!     fn shift(&self, num_bits: u32, tms: &[u8], tdi: &[u8], tdo: &mut [u8]) -> Result<(), Self::Err> {
//!         // Perform JTAG shifting and write the captured TDO data to `tdo`
//!         Ok(())
//!     }
//! }
//! ```
//!
//! ### Starting the Server
//!
//! ```ignore
//! use xvc_server::server::{Server, Config};
//! use std::net::{IpAddr, Ipv4Addr, SocketAddr};
//!
//! let driver = MyDriver::new()?;
//! let config = Config::default();
//! let server = Server::new(driver, config);
//!
//! let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 2542);
//! server.listen(addr).await?;
//! ```
//!
//! ## Error Handling
//!
//! The XVC 1.0 protocol specification does not support error reporting in the Shift operation.
//!
//! ## Configuration
//!
//! Server behavior can be customized via [`server::Config`]:
//!
//! - **max_vector_size**: Maximum size of JTAG vectors (default: 10 MiB)
//! - **read_write_timeout**: Socket I/O timeout duration (default: 30 seconds)
//!
//! ## Logging
//!
//! This crate uses the `log` crate for diagnostics. Enable logging to see:
//! - Client connections and disconnections
//! - Protocol messages being processed
//! - Configuration details and error conditions
//!
//! Configure logging with an implementation like `env_logger`:
//!
//! ```ignore
//! env_logger::init();
//! ```
//!
//! ## Thread Model
//!
//! The server is async (tokio) and accepts connections concurrently, but enforces
//! **at-most-one active client** at a time. A second connection attempt while a client
//! is active is immediately rejected. This matches the XVC protocol assumption of a
//! single JTAG session and prevents interleaved access to the hardware state machine.
//!
//! Backend methods (`set_tck`, `shift`) are called via `block_in_place`, so the server
//! requires a multi-thread tokio runtime.
pub mod server;

/// Trait that backend drivers must implement to provide JTAG functionality.
///
/// This trait defines the interface between the XVC protocol server and the actual
/// hardware debug bridge driver. Implementors are responsible for translating
/// high-level JTAG operations into hardware-specific commands.
///
/// See the [`xvc-server-debugbridge`](https://docs.rs/xvc-server-debugbridge/) crate for examples.
pub trait XvcServer {
    type Err: std::error::Error;
    /// Set the TCK (Test Clock) period.
    ///
    /// Configures the frequency of the JTAG Test Clock (TCK). The server attempts to set
    /// the requested period. If the hardware cannot achieve the exact period, it returns
    /// the closest achievable period.
    ///
    /// # Arguments
    ///
    /// * `period_ns` - The desired TCK period in nanoseconds
    ///
    /// # Returns
    ///
    /// The actual TCK period set by the hardware (in nanoseconds). This may differ from
    /// the requested value if the hardware has limited frequency resolution.
    ///
    /// # Errors
    ///
    /// Returns [`Self::Err`] if the period cannot be configured. The XVC 1.0 protocol has
    /// no error channel, so the server logs the error and echoes the requested period back
    /// to the client to keep the reply framing intact.
    fn set_tck(&self, period_ns: u32) -> Result<u32, Self::Err>;

    /// Shift JTAG TMS and TDI vectors into the device and capture TDO data.
    ///
    /// Performs a JTAG shift operation by:
    /// 1. Shifting `tms` and `tdi` data into the JTAG chain
    /// 2. Capturing and the corresponding TDO data to `tdo`
    ///
    /// The operation is atomic with respect to the JTAG state machine.
    ///
    /// # Arguments
    ///
    /// * `num_bits` - Number of TCK cycles to perform
    /// * `tms` - Test Mode Select vector (⌈num_bits / 8⌉ bytes)
    /// * `tdi` - Test Data In vector (⌈num_bits / 8⌉ bytes)
    /// * `tdo` - Output buffer for the Test Data Out vector. The caller passes
    ///   a buffer of ⌈num_bits / 8⌉ bytes; implementations must fill it completely
    ///   with the captured TDO data.
    ///
    /// # Errors
    ///
    /// Returns [`Self::Err`] if the hardware shift fails. The XVC 1.0 protocol has no
    /// error channel, so the server cannot report the failure to the client: it logs
    /// the error and sends the current contents of `tdo` (zeroed by the caller) as the
    /// TDO response. Implementations should leave `tdo` as-is on error.
    fn shift(&self, num_bits: u32, tms: &[u8], tdi: &[u8], tdo: &mut [u8])
    -> Result<(), Self::Err>;
}