[PATCH v2 2/2] bsp/xilinx-zynq: Add device configuration driver

Patrick Gauvin pggauvin at gmail.com
Wed May 3 19:51:19 UTC 2017


---
 c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am       |   5 +
 .../libbsp/arm/xilinx-zynq/devcfg/zynq-devcfg.c    | 896 +++++++++++++++++++++
 .../arm/xilinx-zynq/include/zynq-devcfg-regs.h     | 197 +++++
 .../libbsp/arm/xilinx-zynq/include/zynq-devcfg.h   | 152 ++++
 c/src/lib/libbsp/arm/xilinx-zynq/preinstall.am     |   8 +
 5 files changed, 1258 insertions(+)
 create mode 100644 c/src/lib/libbsp/arm/xilinx-zynq/devcfg/zynq-devcfg.c
 create mode 100644 c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg-regs.h
 create mode 100644 c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg.h

diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am b/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am
index 08024b9..439399b 100644
--- a/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am
+++ b/c/src/lib/libbsp/arm/xilinx-zynq/Makefile.am
@@ -49,6 +49,8 @@ include_bsp_HEADERS += include/i2c.h
 include_bsp_HEADERS += include/irq.h
 include_bsp_HEADERS += include/zynq-uart.h
 include_bsp_HEADERS += include/zynq-uart-regs.h
+include_bsp_HEADERS += include/zynq-devcfg.h
+include_bsp_HEADERS += include/zynq-devcfg-regs.h
 include_bsp_HEADERS += include/zynq-slcr.h
 include_bsp_HEADERS += include/zynq-slcr-regs.h
 
@@ -120,6 +122,9 @@ libbsp_a_SOURCES += ../shared/arm-a9mpcore-clock-config.c
 # I2C
 libbsp_a_SOURCES += i2c/cadence-i2c.c
 
+# Device Config
+libbsp_a_SOURCES += devcfg/zynq-devcfg.c
+
 # System Level Control Registers
 libbsp_a_SOURCES += slcr/zynq-slcr.c
 
diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/devcfg/zynq-devcfg.c b/c/src/lib/libbsp/arm/xilinx-zynq/devcfg/zynq-devcfg.c
new file mode 100644
index 0000000..df59609
--- /dev/null
+++ b/c/src/lib/libbsp/arm/xilinx-zynq/devcfg/zynq-devcfg.c
@@ -0,0 +1,896 @@
+/*
+ * Xilinx Zynq7000 Device Configuration Driver Implementation
+ *
+ * Notes:
+ * - There will only ever be 1 of these controllers in the Zynq, so this driver
+ *   is designed to be run as a single instance.
+ * - Even if an interrupt bit is already asserted, unmasking it will lead to
+ *   triggering the interrupt. In several areas operations are started before
+ *   unmasking an interrupt which could be triggered by those operations; this
+ *   interrupt behavior allows for such code to not be racy.
+ * - Secure loading is not supported.
+ *
+ * Copyright (c) 2016
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),
+ *  University of Florida.  All rights reserved.
+ * Copyright (c) 2017
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),
+ *  University of Pittsburgh.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of CHREC.
+ *
+ * Author: Patrick Gauvin <gauvin at hcs.ufl.edu>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/rtems/sem.h>
+#include <rtems/irq-extension.h>
+#include <bsp/zynq-slcr.h>
+#include <bsp/zynq-slcr-regs.h>
+#include <bsp/zynq-devcfg.h>
+#include <bsp/zynq-devcfg-regs.h>
+
+#define WARN( msg ) printf( "%s:%s: %s", __FILE__, __func__, msg )
+/* Timeout for interrupt waits, 2 seconds should be enough for any operation.
+ */
+#define INT_TIMEOUT ( 2 * rtems_clock_get_ticks_per_second() )
+#define ZYNQ_DEVCFG_EVENT_SET RTEMS_EVENT_0
+
+typedef struct {
+  volatile zynq_devcfg_regs *regs;
+  /* Used to restrict the device to being opened once at a time. */
+  rtems_id                   sem_id_open;
+  /* Used for mutual exclusion between read/write/ioctl. */
+  rtems_id                   sem_id_internal;
+  /* Indicates if the PCAP will be used for a secure bitstream. Secure
+   * bitstreams are untested with this driver. Defaults to false.
+   */
+  bool                       secure;
+  /* If true, write data is not assumed to be a complete bitstream and no
+   * checks are performed on it.
+   */
+  bool                       write_mode_restricted;
+  rtems_id                   current_task;
+} driver_data;
+
+typedef struct {
+  uint8_t *buf;
+  uint8_t *buf_orig;
+} dma_buf;
+
+static driver_data data;
+
+/**
+ * @brief Check if bit is set in reg (and also not masked by mask), and if it
+ * is, write that bit to reg.
+ *
+ * @retval true The bit was set and not masked, and was written to.
+ * @retval false The bit was not written to.
+ */
+static inline bool check_and_set(
+  volatile uint32_t *reg,
+  uint32_t           mask,
+  uint32_t           bit
+)
+{
+  if ( *reg & bit & ~mask )
+  {
+    *reg = bit;
+    return true;
+  }
+  return false;
+}
+
+/* Only one event is used since only one interrupt is unmasked at a time.  The
+ * interrupt is cleared and masked after it is caught here.
+ */
+static void zynq_devcfg_isr(
+  void *args
+)
+{
+  const uint32_t intrs[] = {
+    ZYNQ_DEVCFG_INT_DMA_DONE_INT,
+    ZYNQ_DEVCFG_INT_PCFG_INIT_PE_INT,
+    ZYNQ_DEVCFG_INT_PCFG_INIT_NE_INT,
+    ZYNQ_DEVCFG_INT_PCFG_DONE_INT,
+    ZYNQ_DEVCFG_INT_PSS_CFG_RESET_B_INT
+  };
+  volatile uint32_t *int_sts = &data.regs->int_sts;
+  volatile uint32_t *int_mask = &data.regs->int_mask;
+
+  (void) args;
+
+  for ( size_t i = 0; i < RTEMS_ARRAY_SIZE( intrs ); ++i )
+    if ( check_and_set( int_sts, *int_mask, intrs[i] ) )
+    {
+      *int_mask |= intrs[i];
+      if ( RTEMS_INVALID_ID != data.current_task )
+        rtems_event_system_send( data.current_task, ZYNQ_DEVCFG_EVENT_SET );
+      return;
+    }
+}
+
+static inline bool ptr_is_pcap_dma_aligned(
+  void *ptr
+)
+{
+  return 0 == (uintptr_t)ptr % ZYNQ_DEVCFG_PCAP_DMA_ALIGN;
+}
+
+/**
+ * @brief Create an aligned buffer for the bitstream.
+ *
+ * @param len Desired length of the buffer in bytes.
+ *
+ * @return dma_buf members are NULL if malloc failed.
+ */
+static dma_buf dma_buf_get(
+  size_t len
+)
+{
+  dma_buf dbuf;
+
+  dbuf.buf_orig = malloc( len + ZYNQ_DEVCFG_PCAP_DMA_ALIGN );
+  if ( NULL == dbuf.buf_orig )
+  {
+    dbuf.buf = NULL;
+    return dbuf;
+  }
+
+  if ( !ptr_is_pcap_dma_aligned( dbuf.buf_orig ) )
+  {
+    dbuf.buf = dbuf.buf_orig + ZYNQ_DEVCFG_PCAP_DMA_ALIGN
+        - ( (size_t)dbuf.buf_orig % ZYNQ_DEVCFG_PCAP_DMA_ALIGN );
+  }
+  else
+    dbuf.buf = dbuf.buf_orig;
+  return dbuf;
+}
+
+/**
+ * @brief Frees the originally allocated area for dbuf.
+ */
+static void dma_buf_release(
+  dma_buf dbuf
+)
+{
+  free( dbuf.buf_orig );
+}
+
+/**
+ * @brief Initiates a PCAP DMA transfer.
+ *
+ * @param src[in] For programming the FPGA, this is the location of the
+ * bitstream data. For readback, it is the location of the PL readback command
+ * sequence.
+ * @param src_len Typically the length of bitstream in dwords, or the number of
+ * PL commands. The user must check this value for correctness.
+ * @param dst[in,out] For programming the FPGA use ZYNQ_DEVCFG_BITSTREAM_ADDR,
+ * for readback this is where the readback data is stored.
+ * @param dst_len Typically the Length of bitstream in dwords, or the number of
+ * readback words expected. The user must check this value for correctness.
+ * @param pcap_wait If true, interrupt assertion waits for both the AXI
+ * transfer and the PCAP to finish.
+ *
+ * @retval 0 Transfer was started.
+ * @retval -1 src_len or dst_len invalid.
+ * @retval -2 The DMA queue was full.
+ */
+static int pcap_dma_xfer(
+  uint32_t                  *src,
+  size_t                     src_len,
+  uint32_t                  *dst,
+  size_t                     dst_len,
+  bool                       pcap_wait
+)
+{
+  if ( ZYNQ_DEVCFG_DMA_SRC_LEN_LEN( src_len ) != src_len )
+    return -1;
+  if ( ZYNQ_DEVCFG_DMA_DEST_LEN_LEN( dst_len ) != dst_len )
+    return -1;
+
+  if ( pcap_wait )
+  {
+    src = (uint32_t *)
+        ( (uintptr_t)src | ZYNQ_DEVCFG_DMA_SRC_ADDR_DMA_DONE_INT_WAIT_PCAP );
+    dst = (uint32_t *)
+        ( (uintptr_t)dst | ZYNQ_DEVCFG_DMA_DST_ADDR_DMA_DONE_INT_WAIT_PCAP );
+  }
+
+#ifdef ZYNQ_DEVCFG_DEBUG
+  printf( "DMA TRANSFER REQUESTED:\n" );
+  printf( "Source: %p\n", src );
+  printf( "Source length: %zu\n", src_len );
+  printf( "Destination: %p\n", dst );
+  printf( "Destination length: %zu\n", dst_len );
+#endif /* ZYNQ_DEVCFG_DEBUG */
+
+  /* Check if the command queue is full */
+  if ( ZYNQ_DEVCFG_STATUS_DMA_CMD_Q_F( data.regs->status ) )
+  {
+    WARN( "Zynq DMA queue full\n" );
+    return -2;
+  }
+
+  /* Order is important */
+  data.regs->dma_src_addr = (uint32_t)src;
+  data.regs->dma_dst_addr = (uint32_t)dst;
+  data.regs->dma_src_len = ZYNQ_DEVCFG_DMA_SRC_LEN_LEN( src_len );
+  data.regs->dma_dest_len = ZYNQ_DEVCFG_DMA_DEST_LEN_LEN( dst_len );
+
+  return 0;
+}
+
+/**
+ * @brief Unmasks and waits for the interrupt in int_bit.
+ *
+ * @param int_bit The interrupt bit in int_sts (NOT a bit number).
+ * @return The result of rtems_event_system_receive.
+ */
+static rtems_status_code int_enable_and_wait(
+  uint32_t int_bit
+)
+{
+  rtems_event_set   ev;
+  rtems_status_code status;
+
+  data.current_task = rtems_task_self();
+  /* data.current_task must be updated before an interrupt is handled. */
+  RTEMS_COMPILER_MEMORY_BARRIER();
+  data.regs->int_mask &= ~int_bit;
+  status = rtems_event_system_receive(
+    ZYNQ_DEVCFG_EVENT_SET,
+    RTEMS_WAIT,
+    INT_TIMEOUT,
+    &ev
+  );
+  /* Re-mask interrupt if not received. */
+  if ( RTEMS_SUCCESSFUL != status )
+    data.regs->int_mask |= int_bit;
+  return status;
+}
+
+/**
+ * @brief Wait for a DMA transfer to finish and check for failure.
+ *
+ * @retval 0 Success.
+ * @retval -1 DMA timeout.
+ * @retval -2 AXI transfer error.
+ * @retval -3 Receive FIFO overflow.
+ * @retval -4 DMA command error or command queue overflow.
+ * @retval -5 Mismatch between PCAP output length and DMA transfer length.
+ * @retval -6 HMAC error.
+ */
+static int pcap_dma_xfer_wait_and_check( void )
+{
+  uint32_t          int_sts;
+  rtems_status_code status;
+
+  /* NOTE: The ISR will handle acknowledging the transfer. */
+  status = int_enable_and_wait( ZYNQ_DEVCFG_INT_DMA_DONE_INT );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    WARN( "DMA timed out\n" );
+    return -1;
+  }
+
+  int_sts = data.regs->int_sts;
+  if (
+    ZYNQ_DEVCFG_INT_AXI_WERR_INT_GET( int_sts )
+    || ZYNQ_DEVCFG_INT_AXI_RTO_INT_GET( int_sts )
+    || ZYNQ_DEVCFG_INT_AXI_RERR_INT_GET( int_sts )
+  )
+    return -2;
+  if ( ZYNQ_DEVCFG_INT_RX_FIFO_OV_INT_GET( int_sts ) )
+    return -3;
+  if (
+    ZYNQ_DEVCFG_INT_DMA_CMD_ERR_INT_GET( int_sts )
+    || ZYNQ_DEVCFG_INT_DMA_Q_OV_INT_GET( int_sts )
+  )
+    return -4;
+  if ( ZYNQ_DEVCFG_INT_P2D_LEN_ERR_INT_GET( int_sts ) )
+    return -5;
+  if ( ZYNQ_DEVCFG_INT_PCFG_HMAC_ERR_INT_GET( int_sts ) )
+    return -6;
+
+  return 0;
+}
+
+/**
+ * @brief Configure the PCAP controller.
+ */
+static void pl_init( void )
+{
+  data.regs->ctrl = ZYNQ_DEVCFG_CTRL_PCAP_MODE( 1 )
+      | ZYNQ_DEVCFG_CTRL_PCAP_PR( ZYNQ_DEVCFG_CTRL_PCAP_PR_PCAP )
+      | ZYNQ_DEVCFG_CTRL_RESERVED_BITS | data.regs->ctrl;
+  /* Disable loopback */
+  data.regs->mctrl = ZYNQ_DEVCFG_MCTRL_SET(
+    data.regs->mctrl,
+    ~ZYNQ_DEVCFG_MCTRL_INT_PCAP_LPBK( 1 ) & data.regs->mctrl
+  );
+  /* Clear all interrupts */
+  data.regs->int_sts = ZYNQ_DEVCFG_INT_ALL;
+
+  if ( !data.secure )
+  {
+    if ( ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_RATE_EN( data.regs->ctrl ) )
+      data.regs->ctrl =
+          ( ~ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_RATE_EN( 1 ) & data.regs->ctrl )
+          | ZYNQ_DEVCFG_CTRL_RESERVED_BITS;
+  }
+  else
+  {
+    if ( !ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_RATE_EN( data.regs->ctrl ) )
+      data.regs->ctrl =
+          ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_RATE_EN( 1 ) | data.regs->ctrl
+          | ZYNQ_DEVCFG_CTRL_RESERVED_BITS;
+  }
+}
+
+/**
+ * @brief Initialize the PCAP and clear the FPGA's configuration.
+ *
+ * @retval 0 Success.
+ * @retval -1 PCAP intialization timeout.
+ * @retval -2 PCAP deinitialization timeout.
+ * @retval -3 PCAP reinitialization timeout.
+ * @retval -4 Device reset timeout.
+ */
+static int pl_clear( void )
+{
+  /* TODO: Check that controller is available */
+  rtems_status_code status;
+
+  if ( !ZYNQ_DEVCFG_CTRL_PCFG_PROG_B_GET( data.regs->ctrl ) )
+    data.regs->ctrl = ZYNQ_DEVCFG_CTRL_PCFG_PROG_B( 1 )
+        | ZYNQ_DEVCFG_CTRL_RESERVED_BITS | data.regs->ctrl;
+  if ( !ZYNQ_DEVCFG_STATUS_PCFG_INIT_GET( data.regs->status ) )
+  {
+    status = int_enable_and_wait( ZYNQ_DEVCFG_INT_PCFG_INIT_PE_INT );
+    if ( RTEMS_SUCCESSFUL != status )
+    {
+      WARN( "PCAP init timed out\n" );
+      return -1;
+    }
+  }
+
+  data.regs->ctrl = ( ~ZYNQ_DEVCFG_CTRL_PCFG_PROG_B( 1 ) & data.regs->ctrl )
+      | ZYNQ_DEVCFG_CTRL_RESERVED_BITS;
+  status = int_enable_and_wait( ZYNQ_DEVCFG_INT_PCFG_INIT_NE_INT );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    WARN( "PCAP deinit timed out\n" );
+    return -2;
+  }
+
+  data.regs->ctrl = ZYNQ_DEVCFG_CTRL_PCFG_PROG_B( 1 )
+      | ZYNQ_DEVCFG_CTRL_RESERVED_BITS | data.regs->ctrl;
+  status = int_enable_and_wait( ZYNQ_DEVCFG_INT_PCFG_INIT_PE_INT );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    WARN( "PCAP reinit timed out\n" );
+    return -3;
+  }
+
+  status = int_enable_and_wait( ZYNQ_DEVCFG_INT_PSS_CFG_RESET_B_INT );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    WARN( "PSS_CFG_RESET_B_INT timed out\n" );
+    return -4;
+  }
+
+  return 0;
+}
+
+static int pl_prog_pre( void )
+{
+  int status;
+
+  pl_init();
+  /* Hold FPGA clocks in reset */
+  zynq_slcr_fpga_clk_rst( 0xf );
+  /* Enable PS to PL level shifters */
+  zynq_slcr_level_shifter_enable( ZYNQ_SLCR_LVL_SHFTR_EN_DISABLE );
+  zynq_slcr_level_shifter_enable( ZYNQ_SLCR_LVL_SHFTR_EN_PS_TO_PL );
+  status = pl_clear();
+  return status;
+}
+
+static int pl_prog_post( void )
+{
+  /* Enable all PS-PL level shifters */
+  zynq_slcr_level_shifter_enable( ZYNQ_SLCR_LVL_SHFTR_EN_ALL );
+  /* Release FPGA clocks from reset */
+  zynq_slcr_fpga_clk_rst( 0 );
+  return 0;
+}
+
+static int pl_prog_done_wait( void )
+{
+  rtems_status_code status;
+
+  status = int_enable_and_wait( ZYNQ_DEVCFG_INT_PCFG_DONE_INT );
+  if ( RTEMS_SUCCESSFUL != status )
+    return -1;
+  return 0;
+}
+
+static bool hdr_check_bin(
+  const uint32_t *bitstream,
+  size_t len
+)
+{
+  const uint32_t valid_header[] = {
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_BUS_WIDTH_SYNC,
+    ZYNQ_DEVCFG_CFG_BUS_WIDTH_DETECT,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_DUMMY,
+    ZYNQ_DEVCFG_CFG_SYNC
+  };
+
+  if ( len < RTEMS_ARRAY_SIZE( valid_header ) )
+    return false;
+  for ( size_t i = 0; i < RTEMS_ARRAY_SIZE( valid_header ); ++i )
+    if ( valid_header[i] != bitstream[i] )
+      return false;
+  return true;
+}
+
+/* TODO: Check that PL power is on.
+ * TODO: Check for configuration differences between silicon revisions.
+ */
+rtems_device_driver zynq_devcfg_init(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+)
+{
+  rtems_status_code status;
+
+  (void) args;
+
+  data.regs = (zynq_devcfg_regs *)ZYNQ_DEVCFG_BASE_ADDR;
+  data.secure = false;
+  data.current_task = RTEMS_INVALID_ID;
+  data.write_mode_restricted = true;
+  status = rtems_semaphore_create(
+    rtems_build_name( 'D', 'e', 'v', 'C' ),
+    1,
+    RTEMS_LOCAL | RTEMS_SIMPLE_BINARY_SEMAPHORE,
+    RTEMS_NO_PRIORITY,
+    &data.sem_id_open
+  );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    status = RTEMS_UNSATISFIED;
+    goto err;
+  }
+  status = rtems_semaphore_create(
+    rtems_build_name( 'D', 'v', 'C', 'i' ),
+    1,
+    RTEMS_LOCAL | RTEMS_SIMPLE_BINARY_SEMAPHORE,
+    RTEMS_NO_PRIORITY,
+    &data.sem_id_internal
+  );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    status = RTEMS_UNSATISFIED;
+    goto err_sem_internal;
+  }
+
+  /* Mask and clear all interrupts and install handler */
+  data.regs->int_mask |= ZYNQ_DEVCFG_INT_ALL;
+  data.regs->int_sts = ZYNQ_DEVCFG_INT_ALL;
+  status = rtems_interrupt_handler_install(
+    ZYNQ_DEVCFG_INTERRUPT_VECTOR,
+    "DevC ISR",
+    RTEMS_INTERRUPT_UNIQUE,
+    zynq_devcfg_isr,
+    NULL
+  );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    WARN( "Failed to assign interrupt handler\n" );
+    status = RTEMS_INTERNAL_ERROR;
+    goto err_intr;
+  }
+
+  status = rtems_io_register_name( ZYNQ_DEVCFG_NAME, major, minor );
+  if ( RTEMS_SUCCESSFUL != status )
+  {
+    status = RTEMS_INVALID_NAME;
+    goto err_register;
+  }
+
+  return RTEMS_SUCCESSFUL;
+err_register:
+  rtems_interrupt_handler_remove(
+    ZYNQ_DEVCFG_INTERRUPT_VECTOR,
+    zynq_devcfg_isr,
+    NULL
+  );
+err_intr:
+  rtems_semaphore_delete( data.sem_id_internal );
+err_sem_internal:
+  rtems_semaphore_delete( data.sem_id_open );
+err:
+  return status;
+}
+
+rtems_device_driver zynq_devcfg_open(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+)
+{
+  rtems_status_code rstatus;
+
+  (void) major;
+  (void) minor;
+  (void) args;
+
+  rstatus = rtems_semaphore_obtain(
+    data.sem_id_open,
+    RTEMS_NO_WAIT,
+    0
+  );
+  if ( RTEMS_SUCCESSFUL == rstatus )
+    return RTEMS_SUCCESSFUL;
+  else if ( RTEMS_UNSATISFIED == rstatus )
+    return RTEMS_RESOURCE_IN_USE;
+  else
+    return RTEMS_INTERNAL_ERROR;
+}
+
+rtems_device_driver zynq_devcfg_close(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+)
+{
+  rtems_status_code rstatus;
+
+  (void) major;
+  (void) minor;
+  (void) args;
+
+  rstatus = rtems_semaphore_release( data.sem_id_open );
+  if ( RTEMS_SUCCESSFUL != rstatus )
+  {
+    WARN( rtems_status_text( rstatus ) );
+    return RTEMS_INTERNAL_ERROR;
+  }
+  else
+    return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver zynq_devcfg_read(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+)
+{
+  rtems_libio_rw_args_t *rw_args;
+  int                    status;
+  rtems_status_code      rstatus;
+  rtems_status_code      final_status;
+  dma_buf                data_buf;
+
+  (void) major;
+  (void) minor;
+  rw_args = args;
+  rw_args->bytes_moved = 0;
+
+  rstatus = rtems_semaphore_obtain( data.sem_id_internal, RTEMS_NO_WAIT, 0 );
+  if ( RTEMS_SUCCESSFUL != rstatus )
+  {
+    final_status = RTEMS_RESOURCE_IN_USE;
+    goto err_obtain;
+  }
+
+  if ( rw_args->count < 4 )
+  {
+    final_status = RTEMS_INVALID_SIZE;
+    goto err_insane;
+  }
+  /* TODO: It might be valid to read configuration registers while the PL is
+   * not programmed.
+   */
+  /* PCFG_DONE must be asserted before readback */
+  if ( !ZYNQ_DEVCFG_INT_PCFG_DONE_INT_GET( data.regs->int_sts ) )
+  {
+    WARN( "read attempted when FPGA configuration not done\n" );
+    final_status = RTEMS_IO_ERROR;
+    goto err_insane;
+  }
+
+  if ( !ptr_is_pcap_dma_aligned ( rw_args->buffer ) )
+  {
+    data_buf = dma_buf_get( rw_args->count );
+    if ( NULL == data_buf.buf )
+    {
+      final_status = RTEMS_NO_MEMORY;
+      goto err_insane;
+    }
+  }
+  else
+    data_buf.buf = (uint8_t *)rw_args->buffer;
+
+  status = pcap_dma_xfer(
+    (uint32_t *)ZYNQ_DEVCFG_BITSTREAM_ADDR,
+    rw_args->count / 4,
+    (uint32_t *)data_buf.buf,
+    rw_args->count / 4,
+    true
+  );
+  if ( status )
+  {
+    WARN( "DMA setup FAILED\n" );
+    final_status = RTEMS_IO_ERROR;
+    goto err_dma;
+  }
+  else
+  {
+    status = pcap_dma_xfer_wait_and_check();
+    if ( status )
+    {
+      WARN( "DMA FAILED\n" );
+      final_status = RTEMS_IO_ERROR;
+      goto err_dma;
+    }
+  }
+
+  /* Ensure stale data is not read */
+  rtems_cache_invalidate_multiple_data_lines( data_buf.buf, rw_args->count );
+
+  final_status = RTEMS_SUCCESSFUL;
+  rw_args->bytes_moved = rw_args->count;
+  if ( data_buf.buf != (uint8_t *)rw_args->buffer )
+    memcpy( rw_args->buffer, data_buf.buf, rw_args->count );
+err_dma:
+  if ( data_buf.buf != (uint8_t *)rw_args->buffer )
+    dma_buf_release( data_buf );
+  rstatus = rtems_semaphore_release( data.sem_id_internal );
+err_insane:
+  if ( RTEMS_SUCCESSFUL != rstatus )
+    final_status = RTEMS_INTERNAL_ERROR;
+err_obtain:
+  return final_status;
+}
+
+rtems_device_driver zynq_devcfg_write(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+)
+{
+  rtems_libio_rw_args_t *rw_args;
+  int                    status;
+  rtems_status_code      rstatus;
+  rtems_status_code      final_status;
+  dma_buf                data_buf;
+
+  (void) major;
+  (void) minor;
+  rw_args = args;
+  rw_args->bytes_moved = 0;
+
+  rstatus = rtems_semaphore_obtain( data.sem_id_internal, RTEMS_NO_WAIT, 0 );
+  if ( RTEMS_SUCCESSFUL != rstatus )
+  {
+    final_status = RTEMS_RESOURCE_IN_USE;
+    goto err_obtain;
+  }
+
+  /* TODO: Check byte order. */
+  if ( data.write_mode_restricted )
+  {
+    /* Only BIN files in restricted mode. */
+    if ( !hdr_check_bin ( (uint32_t *)rw_args->buffer, rw_args->count / 4 ) )
+    {
+      /* Closest status to invalid argument I could find. */
+      final_status = RTEMS_INVALID_NUMBER;
+      goto err_obtain;
+    }
+    status = pl_prog_pre();
+    if ( 0 != status )
+    {
+      final_status = RTEMS_IO_ERROR;
+      goto err_obtain;
+    }
+  }
+
+  if ( !ptr_is_pcap_dma_aligned( rw_args->buffer ) )
+  {
+    data_buf = dma_buf_get( rw_args->count );
+    if ( NULL == data_buf.buf )
+    {
+      final_status = RTEMS_NO_MEMORY;
+      goto err_obtain;
+    }
+    memcpy( data_buf.buf, rw_args->buffer, rw_args->count );
+  }
+  else
+    data_buf.buf = (uint8_t *)rw_args->buffer;
+
+  /* Ensure data is available to the DMA engine */
+  rtems_cache_flush_multiple_data_lines( data_buf.buf, rw_args->count );
+
+  status = pcap_dma_xfer(
+    (uint32_t *)data_buf.buf,
+    rw_args->count / 4,
+    (uint32_t *)ZYNQ_DEVCFG_BITSTREAM_ADDR,
+    rw_args->count / 4,
+    true
+  );
+  if ( status )
+  {
+    final_status = RTEMS_IO_ERROR;
+    goto err_dma;
+  }
+  else
+  {
+    status = pcap_dma_xfer_wait_and_check();
+    if ( status )
+    {
+      final_status = RTEMS_IO_ERROR;
+      goto err_dma;
+    }
+  }
+
+  if ( data.write_mode_restricted )
+  {
+    status = pl_prog_post();
+    if ( 0 != status )
+    {
+      final_status = RTEMS_IO_ERROR;
+      goto err_dma;
+    }
+    status = pl_prog_done_wait();
+    if ( 0 != status )
+    {
+      final_status = RTEMS_TIMEOUT;
+      goto err_dma;
+    }
+  }
+
+  final_status = RTEMS_SUCCESSFUL;
+  rw_args->bytes_moved = rw_args->count;
+err_dma:
+  if ( data_buf.buf != (uint8_t *)rw_args->buffer )
+    dma_buf_release( data_buf );
+  rstatus = rtems_semaphore_release( data.sem_id_internal );
+  if ( RTEMS_SUCCESSFUL != rstatus )
+    final_status = RTEMS_INTERNAL_ERROR;
+err_obtain:
+  return final_status;
+}
+
+rtems_device_driver zynq_devcfg_control(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+)
+{
+  rtems_libio_ioctl_args_t *ioctl_args;
+  char                     *str;
+  int                       status;
+  rtems_status_code         rstatus;
+  rtems_status_code         final_status;
+
+  (void) major;
+  (void) minor;
+  ioctl_args = args;
+
+  rstatus = rtems_semaphore_obtain( data.sem_id_internal, RTEMS_NO_WAIT, 0 );
+  if ( RTEMS_UNSATISFIED == rstatus )
+  {
+    ioctl_args->ioctl_return = -1;
+    return RTEMS_RESOURCE_IN_USE;
+  }
+  else if ( RTEMS_SUCCESSFUL != rstatus )
+  {
+    ioctl_args->ioctl_return = -1;
+    return RTEMS_INTERNAL_ERROR;
+  }
+
+  final_status = RTEMS_SUCCESSFUL;
+  ioctl_args->ioctl_return = 0;
+  switch ( ioctl_args->command ) {
+  case ZYNQ_DEVCFG_IOCTL_VERSION:
+    str = ioctl_args->buffer;
+    switch( ZYNQ_DEVCFG_MCTRL_PS_VERSION_GET( data.regs->mctrl ) ) {
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_1_0:
+        strncpy( str, "1.0", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN );
+        break;
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_2_0:
+        strncpy( str, "2.0", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN );
+        break;
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_3_0:
+        strncpy( str, "3.0", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN );
+        break;
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_3_1:
+        strncpy( str, "3.1", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN );
+        break;
+      default:
+        strncpy( str, "???", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN );
+        break;
+    }
+    break;
+  case ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM_PRE:
+    status = pl_prog_pre();
+    if ( 0 != status )
+    {
+      ioctl_args->ioctl_return = -1;
+      final_status = RTEMS_UNSATISFIED;
+    }
+    break;
+  case ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM_POST:
+    status = pl_prog_post();
+    if ( 0 != status )
+    {
+      ioctl_args->ioctl_return = -1;
+      final_status = RTEMS_UNSATISFIED;
+    }
+    break;
+  case ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM_WAIT_DONE:
+    status = pl_prog_done_wait();
+    if ( 0 != status )
+    {
+      ioctl_args->ioctl_return = -1;
+      final_status = RTEMS_TIMEOUT;
+    }
+    break;
+  case ZYNQ_DEVCFG_IOCTL_SET_SECURE:
+    data.secure = *(bool *)ioctl_args->buffer;
+    break;
+  case ZYNQ_DEVCFG_IOCTL_SET_WRITE_MODE_RESTRICTED:
+    data.write_mode_restricted = *(bool *)ioctl_args->buffer;
+    break;
+  default:
+    ioctl_args->ioctl_return = -1;
+    final_status = RTEMS_INVALID_NAME; /* Maps to EINVAL */
+    break;
+  }
+
+  rstatus = rtems_semaphore_release( data.sem_id_internal );
+  if ( rstatus != RTEMS_SUCCESSFUL )
+    WARN( "Failed to release internal semaphore\n" );
+  return final_status;
+}
diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg-regs.h b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg-regs.h
new file mode 100644
index 0000000..63b9f6a
--- /dev/null
+++ b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg-regs.h
@@ -0,0 +1,197 @@
+/**
+ * @file
+ * @ingroup zynq_devcfg
+ * @brief Device configuration interface register definitions.
+ */
+
+/*
+ * Copyright (c) 2016
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),
+ *  University of Florida.  All rights reserved.
+ * Copyright (c) 2017
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),
+ *  University of Pittsburgh.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of CHREC.
+ *
+ * Author: Patrick Gauvin <gauvin at hcs.ufl.edu>
+ */
+
+/**
+ * @defgroup zynq_devcfg_regs Device Configuration Interface Register Definitions
+ * @ingroup zynq_devcfg
+ * @brief Device Configuration Interface Register Definitions
+ */
+
+#ifndef LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_REGS_H
+#define LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_REGS_H
+
+#include <stdint.h>
+#include <bsp/utility.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Zynq-7000 series devcfg address */
+#define ZYNQ_DEVCFG_BASE_ADDR 0xF8007000UL
+/* For use with the PCAP DMA */
+#define ZYNQ_DEVCFG_BITSTREAM_ADDR 0xFFFFFFFFUL
+/* PCAP DMA transfers must be 64-byte aligned */
+#define ZYNQ_DEVCFG_PCAP_DMA_ALIGN 64
+#define ZYNQ_DEVCFG_INTERRUPT_VECTOR 40
+
+typedef struct {
+  uint32_t ctrl;
+#define ZYNQ_DEVCFG_CTRL_FORCE_RST( val ) BSP_FLD32( val, 31, 31 )
+#define ZYNQ_DEVCFG_CTRL_FORCE_RST_GET( reg ) BSP_FLD32GET( reg, 31, 31 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_PROG_B_GET( reg ) BSP_FLD32GET( reg, 30, 30 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_PROG_B( val ) BSP_FLD32( val, 30, 30 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_POR_CNT_4K_GET( reg ) BSP_FLD32GET( reg, 29, 29 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_POR_CNT_4K( val ) BSP_FLD32( val, 29, 29 )
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR( val ) BSP_FLD32( val, 27, 27 )
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR_GET( reg ) BSP_FLD32GET( reg, 27, 27 )
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR_ICAP ( 0 )
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR_PCAP ( 1 )
+#define ZYNQ_DEVCFG_CTRL_PCAP_MODE( val ) BSP_FLD32( val, 26, 26 )
+#define ZYNQ_DEVCFG_CTRL_PCAP_MODE_GET( reg ) BSP_FLD32GET( reg, 26, 26 )
+#define ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_RATE_EN( val ) BSP_FLD32( val, 25, 25 )
+#define ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_RATE_EN_GET( reg ) \
+  BSP_FLD32GET( reg, 25, 25 )
+#define ZYNQ_DEVCFG_CTRL_MULTIBOOT_EN( val ) BSP_FLD32( val, 24, 24 )
+#define ZYNQ_DEVCFG_CTRL_MULTIBOOT_EN_GET( reg ) BSP_FLD32GET( reg, 24, 24 )
+#define ZYNQ_DEVCFG_CTRL_JTAG_CHAIN_DIS( val ) BSP_FLD32( val, 23, 23 )
+#define ZYNQ_DEVCFG_CTRL_JTAG_CHAIN_DIS_GET( reg ) BSP_FLD32GET( reg, 23, 23 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE( val ) BSP_FLD32( val, 12, 12 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE_GET( reg ) BSP_FLD32GET( reg, 12, 12 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE_BBRAM ( 0 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE_EFUSE ( 1 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN( val ) BSP_FLD32( val, 9, 11 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_GET( reg ) BSP_FLD32GET( reg, 9, 11 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_ENABLE ( 0x3 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_DISABLE ( 0x0 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_LOCKDOWN ( 0x1 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SEU_EN( val ) BSP_FLD32( val, 8, 8 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SEU_EN_GET( reg ) BSP_FLD32GET( reg, 8, 8 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SEC_EN_GET( reg ) BSP_FLD32GET( reg, 7, 7 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPNIDEN( val ) BSP_FLD32( val, 6, 6 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPNIDEN_GET( reg ) BSP_FLD32GET( reg, 6, 6 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPIDEN( val ) BSP_FLD32( val, 5, 5 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPIDEN_GET( reg ) BSP_FLD32GET( reg, 5, 5 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_NIDEN( val ) BSP_FLD32( val, 4, 4 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_NIDEN_GET( reg ) BSP_FLD32GET( reg, 4, 4 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_DBGEN( val ) BSP_FLD32( val, 3, 3 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_DBGEN_GET( reg ) BSP_FLD32GET( reg, 3, 3 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN( val ) BSP_FLD32( val, 0, 2 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN_GET( reg ) BSP_FLD32GET( reg, 0, 2 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN_ENABLE ( 0x3 )
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN_BYPASS ( 0x0 )
+#define ZYNQ_DEVCFG_CTRL_RESERVED_BITS ( 0x6000 )
+  uint32_t lock;
+  uint32_t cfg;
+  /* int_sts and int_mask directly overlap, so they share the ZYNQ_DEVCFG_INT_*
+   * macros */
+  uint32_t int_sts;
+  uint32_t int_mask;
+#define ZYNQ_DEVCFG_INT_PSS_CFG_RESET_B_INT BSP_BIT32( 27 )
+#define ZYNQ_DEVCFG_INT_PSS_CFG_RESET_B_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 27, 27 )
+#define ZYNQ_DEVCFG_INT_AXI_WERR_INT_GET( reg ) BSP_FLD32GET( reg, 22, 22 )
+#define ZYNQ_DEVCFG_INT_AXI_RTO_INT_GET( reg ) BSP_FLD32GET( reg, 21, 21 )
+#define ZYNQ_DEVCFG_INT_AXI_RERR_INT_GET( reg ) BSP_FLD32GET( reg, 20, 20 )
+#define ZYNQ_DEVCFG_INT_RX_FIFO_OV_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 18, 18 )
+#define ZYNQ_DEVCFG_INT_DMA_CMD_ERR_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 15, 15 )
+#define ZYNQ_DEVCFG_INT_DMA_Q_OV_INT_GET( reg ) BSP_FLD32GET( reg, 14, 14 )
+#define ZYNQ_DEVCFG_INT_DMA_DONE_INT BSP_BIT32( 13 )
+#define ZYNQ_DEVCFG_INT_DMA_DONE_INT_GET( reg ) BSP_FLD32GET( reg, 13, 13 )
+#define ZYNQ_DEVCFG_INT_D_P_DONE_INT BSP_BIT32( 12 )
+#define ZYNQ_DEVCFG_INT_D_P_DONE_INT_GET( reg ) BSP_FLD32GET( reg, 12, 12 )
+#define ZYNQ_DEVCFG_INT_P2D_LEN_ERR_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 11, 11 )
+#define ZYNQ_DEVCFG_INT_PCFG_HMAC_ERR_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 6, 6 )
+#define ZYNQ_DEVCFG_INT_PCFG_SEU_ERR_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 5, 5 )
+#define ZYNQ_DEVCFG_INT_PCFG_POR_B_INT_GET( reg ) BSP_FLD32GET( reg, 4, 4 )
+#define ZYNQ_DEVCFG_INT_PCFG_CFG_RST_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 3, 3 )
+#define ZYNQ_DEVCFG_INT_PCFG_DONE_INT BSP_BIT32( 2 )
+#define ZYNQ_DEVCFG_INT_PCFG_DONE_INT_GET( reg ) BSP_FLD32GET( reg, 2, 2 )
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_PE_INT BSP_BIT32( 1 )
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_PE_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 1, 1 )
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_NE_INT BSP_BIT32( 0 )
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_NE_INT_GET( reg ) \
+  BSP_FLD32GET( reg, 0, 0 )
+#define ZYNQ_DEVCFG_INT_ALL ( 0xf8f7f87f )
+  uint32_t status;
+#define ZYNQ_DEVCFG_STATUS_DMA_CMD_Q_F( val ) BSP_FLD32( val, 31, 31 )
+#define ZYNQ_DEVCFG_STATUS_DMA_CMD_Q_F_GET( reg ) BSP_FLD32GET( reg, 31, 31 )
+#define ZYNQ_DEVCFG_STATUS_PCFG_INIT_GET( reg ) BSP_FLD32GET( reg, 4, 4 )
+  uint32_t dma_src_addr;
+#define ZYNQ_DEVCFG_DMA_SRC_ADDR_DMA_DONE_INT_WAIT_PCAP ( 0x1 )
+  uint32_t dma_dst_addr;
+#define ZYNQ_DEVCFG_DMA_DST_ADDR_DMA_DONE_INT_WAIT_PCAP ( 0x1 )
+  uint32_t dma_src_len;
+#define ZYNQ_DEVCFG_DMA_SRC_LEN_LEN( val ) BSP_FLD32( val, 0, 26 )
+  uint32_t dma_dest_len; /* (sic) */
+#define ZYNQ_DEVCFG_DMA_DEST_LEN_LEN( val ) BSP_FLD32( val, 0, 26 )
+  uint32_t reserved0;
+  uint32_t multiboot_addr;
+  uint32_t reserved1;
+  uint32_t unlock;
+  uint32_t reserved2[18];
+  uint32_t mctrl;
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_GET( reg ) BSP_FLD32GET( reg, 28, 31 )
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_1_0 0x0
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_2_0 0x1
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_3_0 0x2
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_3_1 0x3
+#define ZYNQ_DEVCFG_MCTRL_PCFG_POR_B_GET( reg ) BSP_FLD32GET( reg, 8, 8 )
+#define ZYNQ_DEVCFG_MCTRL_INT_PCAP_LPBK_GET( reg ) BSP_FLD32GET( reg, 4, 4 )
+#define ZYNQ_DEVCFG_MCTRL_INT_PCAP_LPBK( val ) BSP_FLD32( val, 4, 4 )
+#define ZYNQ_DEVCFG_MCTRL_RESERVED_SET_BITS ( 0x800000 )
+#define ZYNQ_DEVCFG_MCTRL_RESERVED_UNSET_BITS ( 0x3 )
+#define ZYNQ_DEVCFG_MCTRL_SET( reg, val ) ( ( ( reg ) & \
+  ~ZYNQ_DEVCFG_MCTRL_RESERVED_UNSET_BITS ) | \
+  ZYNQ_DEVCFG_MCTRL_RESERVED_SET_BITS | ( val ) )
+  uint32_t reserved3[32];
+  uint32_t xadcif_cfg;
+  uint32_t xadcif_int_sts;
+  uint32_t xadcif_int_mask;
+  uint32_t xadcif_msts;
+  uint32_t xadcif_cmdfifo;
+  uint32_t xadcif_rdfifo;
+  uint32_t xadcif_mctrl;
+} zynq_devcfg_regs;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_REGS_H */
diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg.h b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg.h
new file mode 100644
index 0000000..8f73e98
--- /dev/null
+++ b/c/src/lib/libbsp/arm/xilinx-zynq/include/zynq-devcfg.h
@@ -0,0 +1,152 @@
+/**
+ * @file
+ * @ingroup zynq_devcfg
+ * @brief Device configuration support.
+ *
+ * Provides support for the Zynq7000 series device configuration interface
+ * controller. PCAP command sequences are written using the write interface,
+ * and PCAP responses are retrieved with the read interface. The driver can be
+ * used for reconfiguration of the FPGA, and also reading FPGA configuration
+ * data for error checking.
+ */
+
+/*
+ * Copyright (c) 2016
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),
+ *  University of Florida.  All rights reserved.
+ * Copyright (c) 2017
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),
+ *  University of Pittsburgh.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of CHREC.
+ *
+ * Author: Patrick Gauvin <gauvin at hcs.ufl.edu>
+ */
+
+/**
+ * @defgroup zynq_devcfg Device Configuration Interface Support
+ * @ingroup arm_zynq
+ * @brief Device Configuration Interface Support
+ */
+
+#ifndef LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_H
+#define LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_H
+
+#include <rtems/libio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define ZYNQ_DEVCFG_NAME "/dev/devcfg"
+#define ZYNQ_DEVCFG_DRIVER_TABLE_ENTRY \
+  { zynq_devcfg_init, zynq_devcfg_open, zynq_devcfg_close, zynq_devcfg_read, \
+    zynq_devcfg_write, zynq_devcfg_control }
+
+/* Configuration command words. */
+#define ZYNQ_DEVCFG_CFG_DUMMY ( 0xffffffff )
+#define ZYNQ_DEVCFG_CFG_BUS_WIDTH_SYNC ( 0x000000bb )
+#define ZYNQ_DEVCFG_CFG_BUS_WIDTH_DETECT ( 0x11220044 )
+#define ZYNQ_DEVCFG_CFG_SYNC ( 0xaa995566 )
+
+/** @brief Zynq configuration frame length in bytes */
+#define ZYNQ_DEVCFG_CONFIG_FRAME_LEN ( 101 * 4 )
+
+#define ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN 16
+
+enum zynq_devcfg_ioctl {
+  /** @brief Argument: Buffer for character string of at least
+   * ZYNQ_DEVCFG_IOCTL_VERSION_MAX_LEN bytes.
+   */
+  ZYNQ_DEVCFG_IOCTL_VERSION,
+  /** @brief Argument: None. */
+  ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM_PRE,
+  /** @brief Argument: None. */
+  ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM_POST,
+  /** @brief Argument: None. */
+  ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM_WAIT_DONE,
+  /** @brief Argument: bool. */
+  ZYNQ_DEVCFG_IOCTL_SET_SECURE,
+  /** @brief Argument: bool. */
+  ZYNQ_DEVCFG_IOCTL_SET_WRITE_MODE_RESTRICTED
+};
+
+rtems_device_driver zynq_devcfg_init(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+);
+
+rtems_device_driver zynq_devcfg_open(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+);
+
+rtems_device_driver zynq_devcfg_close(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+);
+
+/**
+ * @brief Read from the PCAP controller.
+ *
+ * Readback reads cannot be split into multiple DMA reads, this may cause the
+ * PCAP DMA to exhibit unexpected behavior. Therefore, the read length must
+ * match the preceding command sequence's expected data output length.
+ */
+rtems_device_driver zynq_devcfg_read(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+);
+
+/**
+ * @brief Write to the PCAP controller.
+ *
+ * Data format: dword aligned bistream data or PCAP commands. Bitstream data is
+ * expected to be formatted as Vivado 2016.4 outputs BIN-format bitstreams by
+ * default (not bit-swapped) BUT with the byte order within each dword changed
+ * to little endian. See UG470 for information on data ordering.
+ */
+rtems_device_driver zynq_devcfg_write(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+);
+
+rtems_device_driver zynq_devcfg_control(
+  rtems_device_major_number  major,
+  rtems_device_minor_number  minor,
+  void                      *args
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_H */
diff --git a/c/src/lib/libbsp/arm/xilinx-zynq/preinstall.am b/c/src/lib/libbsp/arm/xilinx-zynq/preinstall.am
index 814095f..4eaab68 100644
--- a/c/src/lib/libbsp/arm/xilinx-zynq/preinstall.am
+++ b/c/src/lib/libbsp/arm/xilinx-zynq/preinstall.am
@@ -150,6 +150,14 @@ $(PROJECT_INCLUDE)/bsp/zynq-uart-regs.h: include/zynq-uart-regs.h $(PROJECT_INCL
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/zynq-uart-regs.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/zynq-uart-regs.h
 
+$(PROJECT_INCLUDE)/bsp/zynq-devcfg.h: include/zynq-devcfg.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/zynq-devcfg.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/zynq-devcfg.h
+
+$(PROJECT_INCLUDE)/bsp/zynq-devcfg-regs.h: include/zynq-devcfg-regs.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/zynq-devcfg-regs.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/zynq-devcfg-regs.h
+
 $(PROJECT_INCLUDE)/bsp/zynq-slcr.h: include/zynq-slcr.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/zynq-slcr.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/zynq-slcr.h
-- 
2.7.4




More information about the devel mailing list