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//! ```no_run
46//! use xvc_server::XvcServer;
47//!
48//! struct MyDriver {
49//! // device-specific fields
50//! }
51//!
52//! impl XvcServer for MyDriver {
53//! type Err = std::io::Error; // device-specific error
54//!
55//! fn set_tck(&self, period_ns: u32) -> Result<u32, Self::Err> {
56//! // Configure hardware TCK period
57//! Ok(period_ns)
58//! }
59//!
60//! fn shift(&self, num_bits: u32, tms: &[u8], tdi: &[u8], tdo: &mut [u8]) -> Result<(), Self::Err> {
61//! // Perform JTAG shifting and write the captured TDO data to `tdo`
62//! Ok(())
63//! }
64//! }
65//! ```
66//!
67//! ### Starting the Server
68//!
69//! ```ignore
70//! use xvc_server::server::{Server, Config};
71//! use std::net::{IpAddr, Ipv4Addr, SocketAddr};
72//!
73//! let driver = MyDriver::new()?;
74//! let config = Config::default();
75//! let server = Server::new(driver, config);
76//!
77//! let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 2542);
78//! server.listen(addr).await?;
79//! ```
80//!
81//! ## Error Handling
82//!
83//! The XVC 1.0 protocol specification does not support error reporting in the Shift operation.
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 is async (tokio) and accepts connections concurrently, but enforces
108//! **at-most-one active client** at a time. A second connection attempt while a client
109//! is active is immediately rejected. This matches the XVC protocol assumption of a
110//! single JTAG session and prevents interleaved access to the hardware state machine.
111//!
112//! Backend methods (`set_tck`, `shift`) are called via `block_in_place`, so the server
113//! requires a multi-thread tokio runtime.
114pub mod server;
115
116/// Trait that backend drivers must implement to provide JTAG functionality.
117///
118/// This trait defines the interface between the XVC protocol server and the actual
119/// hardware debug bridge driver. Implementors are responsible for translating
120/// high-level JTAG operations into hardware-specific commands.
121///
122/// See the [`xvc-server-debugbridge`](https://docs.rs/xvc-server-debugbridge/) crate for examples.
123pub trait XvcServer {
124 type Err: std::error::Error;
125 /// Set the TCK (Test Clock) period.
126 ///
127 /// Configures the frequency of the JTAG Test Clock (TCK). The server attempts to set
128 /// the requested period. If the hardware cannot achieve the exact period, it returns
129 /// the closest achievable period.
130 ///
131 /// # Arguments
132 ///
133 /// * `period_ns` - The desired TCK period in nanoseconds
134 ///
135 /// # Returns
136 ///
137 /// The actual TCK period set by the hardware (in nanoseconds). This may differ from
138 /// the requested value if the hardware has limited frequency resolution.
139 ///
140 /// # Errors
141 ///
142 /// Returns [`Self::Err`] if the period cannot be configured. The XVC 1.0 protocol has
143 /// no error channel, so the server logs the error and echoes the requested period back
144 /// to the client to keep the reply framing intact.
145 fn set_tck(&self, period_ns: u32) -> Result<u32, Self::Err>;
146
147 /// Shift JTAG TMS and TDI vectors into the device and capture TDO data.
148 ///
149 /// Performs a JTAG shift operation by:
150 /// 1. Shifting `tms` and `tdi` data into the JTAG chain
151 /// 2. Capturing and the corresponding TDO data to `tdo`
152 ///
153 /// The operation is atomic with respect to the JTAG state machine.
154 ///
155 /// # Arguments
156 ///
157 /// * `num_bits` - Number of TCK cycles to perform
158 /// * `tms` - Test Mode Select vector (⌈num_bits / 8⌉ bytes)
159 /// * `tdi` - Test Data In vector (⌈num_bits / 8⌉ bytes)
160 /// * `tdo` - Output buffer for the Test Data Out vector. The caller passes
161 /// a buffer of ⌈num_bits / 8⌉ bytes; implementations must fill it completely
162 /// with the captured TDO data.
163 ///
164 /// # Errors
165 ///
166 /// Returns [`Self::Err`] if the hardware shift fails. The XVC 1.0 protocol has no
167 /// error channel, so the server cannot report the failure to the client: it logs
168 /// the error and sends the current contents of `tdo` (zeroed by the caller) as the
169 /// TDO response. Implementations should leave `tdo` as-is on error.
170 fn shift(&self, num_bits: u32, tms: &[u8], tdi: &[u8], tdo: &mut [u8])
171 -> Result<(), Self::Err>;
172}