xvc_server/
lib.rs

1//! # XVC Server Library
2//!
3//! This crate provides a foundation for implementing Xilinx Virtual Cable (XVC) servers
4//! that handle JTAG communication with FPGA devices over network connections.
5//!
6//! ## Overview
7//!
8//! XVC is a protocol used by Xilinx design tools to interact with FPGA devices remotely.
9//! This library abstracts the protocol handling and provides a server implementation that
10//! can work with different backend device drivers.
11//!
12//! ## Architecture
13//!
14//! The crate is built around two main components:
15//!
16//! - **[`XvcServer`] Trait**: Defines the interface that backend drivers must implement
17//!   to handle low-level JTAG operations (TCK configuration and vector shifting)
18//! - **[`server::Server`]**: A generic server that handles XVC protocol communication,
19//!   message parsing, and client connections
20//!
21//! ## How It Works
22//!
23//! 1. A backend driver (e.g., kernel driver, UIO device) implements the [`XvcServer`] trait
24//! 2. The driver is wrapped in a [`server::Server`] instance
25//! 3. The server listens for TCP connections and processes XVC protocol messages
26//! 4. Each message is dispatched to the backend driver for actual JTAG operations
27//! 5. Results are serialized and sent back to the client
28//!
29//! ## Protocol Support
30//!
31//! This implementation supports the XVC 1.0 protocol with the following operations:
32//!
33//! - **GetInfo**: Query server capabilities (version, max vector size)
34//! - **SetTck**: Configure the JTAG Test Clock (TCK) period
35//! - **Shift**: Perform JTAG vector shifting (TMS/TDI/TDO)
36//!
37//! For detailed protocol information, see the [`xvc_protocol`](https://docs.rs/xvc-protocol/) crate.
38//!
39//! ## Basic Usage
40//!
41//! ### Implementing a Backend Driver
42//!
43//! Create a struct that implements the [`XvcServer`] trait:
44//!
45//! ```ignore
46//! use xvc_server::XvcServer;
47//!
48//! struct MyDriver {
49//!     // device-specific fields
50//! }
51//!
52//! impl XvcServer for MyDriver {
53//!     fn set_tck(&self, period_ns: u32) -> u32 {
54//!         // Configure hardware TCK period
55//!         period_ns
56//!     }
57//!
58//!     fn shift(&self, num_bits: u32, tms: Box<[u8]>, tdi: Box<[u8]>) -> Box<[u8]> {
59//!         // Perform JTAG shifting and return TDO data
60//!         Box::default()
61//!     }
62//! }
63//! ```
64//!
65//! ### Starting the Server
66//!
67//! ```ignore
68//! use xvc_server::server::{Server, Config};
69//! use std::net::{IpAddr, Ipv4Addr, SocketAddr};
70//!
71//! let driver = MyDriver::new()?;
72//! let config = Config::default();
73//! let server = Server::new(driver, config);
74//!
75//! let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 2542);
76//! server.listen(addr)?;
77//! ```
78//!
79//! ## Error Handling
80//!
81//! The XVC 1.0 protocol specification does not support error reporting in the Shift operation.
82//! If a shift operation fails, an empty result is returned to the client.
83//! For other operations, standard I/O errors are propagated as appropriate.
84//!
85//! ## Configuration
86//!
87//! Server behavior can be customized via [`server::Config`]:
88//!
89//! - **max_vector_size**: Maximum size of JTAG vectors (default: 10 MiB)
90//! - **read_write_timeout**: Socket I/O timeout duration (default: 30 seconds)
91//!
92//! ## Logging
93//!
94//! This crate uses the `log` crate for diagnostics. Enable logging to see:
95//! - Client connections and disconnections
96//! - Protocol messages being processed
97//! - Configuration details and error conditions
98//!
99//! Configure logging with an implementation like `env_logger`:
100//!
101//! ```ignore
102//! env_logger::init();
103//! ```
104//!
105//! ## Thread Model
106//!
107//! The server processes each client connection sequentially in a single thread.
108//! For multi-client support, wrap the server in a multi-threaded framework or
109//! run multiple server instances.
110pub mod server;
111
112/// Trait that backend drivers must implement to provide JTAG functionality.
113///
114/// This trait defines the interface between the XVC protocol server and the actual
115/// hardware debug bridge driver. Implementors are responsible for translating
116/// high-level JTAG operations into hardware-specific commands.
117///
118/// See the [`xvc-server-debugbridge`](https://docs.rs/xvc-server-debugbridge/) crate for examples.
119pub trait XvcServer {
120    /// Set the TCK (Test Clock) period.
121    ///
122    /// Configures the frequency of the JTAG Test Clock (TCK). The server attempts to set
123    /// the requested period. If the hardware cannot achieve the exact period, it returns
124    /// the closest achievable period.
125    ///
126    /// # Arguments
127    ///
128    /// * `period_ns` - The desired TCK period in nanoseconds
129    ///
130    /// # Returns
131    ///
132    /// The actual TCK period set by the hardware (in nanoseconds). This may differ from
133    /// the requested value if the hardware has limited frequency resolution.
134    fn set_tck(&self, period_ns: u32) -> u32;
135
136    /// Shift JTAG TMS and TDI vectors into the device and return TDO data.
137    ///
138    /// Performs a JTAG shift operation by:
139    /// 1. Shifting `tms` and `tdi` data into the JTAG chain
140    /// 2. Capturing the corresponding `tdo` (Test Data Out) data
141    /// 3. Returning the captured `tdo` data
142    ///
143    /// The operation is atomic with respect to the JTAG state machine.
144    ///
145    /// # Arguments
146    ///
147    /// * `num_bits` - Number of TCK cycles to perform
148    /// * `tms` - Test Mode Select vector (must be ⌈num_bits / 8⌉ bytes)
149    /// * `tdi` - Test Data In vector (must be ⌈num_bits / 8⌉ bytes)
150    ///
151    /// # Returns
152    ///
153    /// Test Data Out vector of the same size as `tms` and `tdi`. On error,
154    /// an empty vector should be returned.
155    ///
156    /// # Error Handling
157    ///
158    /// The XVC 1.0 protocol does not support error reporting for shift operations.
159    /// Implementations should return an empty box on error rather than propagating errors.
160    fn shift(&self, num_bits: u32, tms: Box<[u8]>, tdi: Box<[u8]>) -> Box<[u8]>;
161}