Source code for adi_lg_plugins.drivers.xilinxjtagdriver

"""Driver to program Xilinx FPGAs via JTAG using xsdb."""

import os

import attr
from labgrid.driver.common import Driver
from labgrid.driver.exception import ExecutionError
from labgrid.factory import target_factory
from labgrid.step import step


[docs] @target_factory.reg_driver @attr.s(eq=False) class XilinxJTAGDriver(Driver): """XilinxJTAGDriver - Driver to program Xilinx FPGAs via JTAG using xsdb. Supports Virtex, Artix, and Kintex FPGAs with Microblaze soft processors. Uses Xilinx xsdb (Xilinx Software Command-Line Tool) for JTAG operations. This driver implements the boot sequence for logic-only FPGAs: 1. Flash bitstream to configure FPGA fabric and instantiate Microblaze 2. Download Linux kernel image to Microblaze memory 3. Start kernel execution 4. Disconnect from JTAG Bindings: xilinxdevicejtag: XilinxDeviceJTAG resource for JTAG configuration xilinxvivado: XilinxVivadoTool resource for xsdb tool access """ bindings = { "xilinxdevicejtag": {"XilinxDeviceJTAG"}, "xilinxvivado": {"XilinxVivadoTool"}, }
[docs] def __attrs_post_init__(self): """Initialize driver and verify xsdb is available.""" super().__attrs_post_init__() self.logger.info("XilinxJTAGDriver initialized") # Verify xsdb is executable if not os.path.exists(self.xilinxvivado.xsdb_path): raise ExecutionError(f"xsdb not found at {self.xilinxvivado.xsdb_path}") self.logger.debug(f"xsdb found at: {self.xilinxvivado.xsdb_path}") self.logger.debug(f"Vivado version: {self.xilinxvivado.version}")
[docs] @Driver.check_active @step() def connect_jtag(self): """Connect to JTAG interface. Raises: ExecutionError: If JTAG connection fails. """ self.logger.info("Connecting to JTAG") tcl_script = """ connect after 1000 puts "JTAG connected" """ stdout, stderr, returncode = self.xilinxvivado.run_xsdb_script(tcl_script) if returncode != 0: raise ExecutionError(f"JTAG connection failed: {stderr}") self.logger.debug(f"JTAG connection output: {stdout}")
[docs] @Driver.check_active @step() def flash_bitstream(self): """Flash the FPGA bitstream via JTAG. Loads the bitstream file to configure the FPGA fabric and instantiate the Microblaze processor. Raises: ExecutionError: If flashing fails or bitstream file not found. """ if not self.xilinxdevicejtag.bitstream_path: raise ExecutionError("Bitstream path not configured in XilinxDeviceJTAG resource") if not os.path.exists(self.xilinxdevicejtag.bitstream_path): raise ExecutionError( f"Bitstream file not found: {self.xilinxdevicejtag.bitstream_path}" ) self.logger.info(f"Flashing bitstream: {self.xilinxdevicejtag.bitstream_path}") tcl_script = f""" connect after 1000 targets {self.xilinxdevicejtag.root_target} after 1000 fpga -f {self.xilinxdevicejtag.bitstream_path} after 2000 puts "Bitstream flashed successfully" """ stdout, stderr, returncode = self.xilinxvivado.run_xsdb_script(tcl_script) if returncode != 0: raise ExecutionError(f"Bitstream flash failed: {stderr}") self.logger.info("Bitstream flashed successfully") self.logger.debug(f"Flash output: {stdout}")
[docs] @Driver.check_active @step() def download_kernel(self): """Download Linux kernel image to Microblaze processor. Uses xsdb 'dow' (download) command to load the kernel into Microblaze memory. The bitstream must be flashed before calling this method. Raises: ExecutionError: If download fails or kernel file not found. """ if not self.xilinxdevicejtag.kernel_path: raise ExecutionError("Kernel path not configured in XilinxDeviceJTAG resource") if not os.path.exists(self.xilinxdevicejtag.kernel_path): raise ExecutionError(f"Kernel file not found: {self.xilinxdevicejtag.kernel_path}") self.logger.info(f"Downloading kernel: {self.xilinxdevicejtag.kernel_path}") tcl_script = f""" connect after 1000 targets {self.xilinxdevicejtag.microblaze_target} after 1000 dow {self.xilinxdevicejtag.kernel_path} after 1000 puts "Kernel downloaded successfully" """ stdout, stderr, returncode = self.xilinxvivado.run_xsdb_script(tcl_script) if returncode != 0: raise ExecutionError(f"Kernel download failed: {stderr}") self.logger.info("Kernel downloaded successfully") self.logger.debug(f"Download output: {stdout}")
[docs] @Driver.check_active @step() def start_execution(self): """Start kernel execution on Microblaze processor. Uses xsdb 'con' (continue) command to begin kernel boot. The kernel must be downloaded before calling this method. Raises: ExecutionError: If execution start fails. """ self.logger.info("Starting kernel execution") tcl_script = f""" connect after 1000 targets {self.xilinxdevicejtag.microblaze_target} after 1000 con after 500 puts "Kernel execution started" """ stdout, stderr, returncode = self.xilinxvivado.run_xsdb_script(tcl_script) if returncode != 0: raise ExecutionError(f"Kernel execution failed: {stderr}") self.logger.info("Kernel execution started") self.logger.debug(f"Execution output: {stdout}")
[docs] @Driver.check_active @step() def load_bitstream_and_kernel_and_start(self): """Load bitstream and kernel in sequence and start execution. This is a convenience method that performs the full sequence: 1. Flash bitstream 2. Download kernel 3. Start execution Raises: ExecutionError: If any step in the sequence fails. """ tcl_script = f""" connect after 1000 targets {self.xilinxdevicejtag.root_target} after 1000 fpga -f {self.xilinxdevicejtag.bitstream_path} after 2000 targets {self.xilinxdevicejtag.microblaze_target} after 1000 dow {self.xilinxdevicejtag.kernel_path} after 1000 con after 500 puts "System started" """ self.logger.debug(f"System start TCL script:\n{tcl_script}") stdout, stderr, returncode = self.xilinxvivado.run_xsdb_script(tcl_script) if returncode != 0: raise ExecutionError(f"System start failed: {stderr}") self.logger.debug(f"System start output: {stdout}")
[docs] @Driver.check_active @step() def disconnect_jtag(self): """Disconnect from JTAG interface.""" self.logger.info("Disconnecting from JTAG") tcl_script = """ disconnect puts "JTAG disconnected" """ stdout, stderr, returncode = self.xilinxvivado.run_xsdb_script(tcl_script) if returncode != 0: self.logger.warning(f"JTAG disconnect warning: {stderr}") self.logger.debug(f"JTAG disconnect output: {stdout}")