<div dir="ltr">Chris,<br><div class="gmail_extra"><br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
+<br>
+static drvdata data;<br>
+/* TODO: Abstract DMA buffer retrieval better */<br>
</blockquote>
<br></div></div>
Agreed.<div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+static uint8_t *dma_buf = NULL;<br>
+<br>
+/* Check if bit is set in reg (and not masked by mask), and if it is, write<br>
+ * that bit to reg, returning true. Otherwise return false.<br>
+ */<br>
+static inline bool check_and_set(<br>
+  volatile uint32_t *reg,<br>
+  uint32_t           mask,<br>
+  uint32_t           bit<br>
+)<br>
+{<br>
+  if ( *reg & bit & ~mask )<br>
+  {<br>
+    *reg = bit;<br>
+    return true;<br>
+  }<br>
+  return false;<br>
+}<br>
+<br>
+/* Only one semaphore is used since only one interrupt is unmasked at a time.<br>
+ * The interrupt is cleared and masked after it is caught here.<br>
+ */<br>
+static void zynq_devcfg_isr(<br>
+  void *args<br>
+)<br>
</blockquote>
<br></div></div>
Why are you wanting to use interrupts rather than a simple sleep and poll? I am ok with a sychronous read/write driver interface here.<br>
<br>
Do we need interrupts and the complexity they have here when loading the fabric is so critical to any other part of a system and must be done early?</blockquote><div><br></div><div>I moved to interrupts since I wanted a way to implement timeouts that allowed the driver to continue as soon as an operation completes. This is mostly to benefit applications that will read from and write to the PCAP during operation, after the bitstream is initially loaded, like an FPGA scrubber.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+{<br>
+  uint32_t intrs[] = {<br>
+    ZYNQ_DEVCFG_INT_DMA_DONE_INT,<br>
+    ZYNQ_DEVCFG_INT_PCFG_INIT_PE_I<wbr>NT,<br>
+    ZYNQ_DEVCFG_INT_PCFG_INIT_NE_I<wbr>NT,<br>
+    ZYNQ_DEVCFG_INT_PCFG_DONE_INT,<br>
+    ZYNQ_DEVCFG_INT_PSS_CFG_RESET_<wbr>B_INT<br>
+  };<br>
+  volatile uint32_t *int_sts = &data.regs->int_sts;<br>
+  volatile uint32_t *int_mask = &data.regs->int_mask;<br>
+<br>
+  (void) args;<br>
+<br>
+  for ( size_t i = 0; i < RTEMS_ARRAY_SIZE( intrs ); ++i )<br>
+    if ( check_and_set( int_sts, *int_mask, intrs[i] ) )<br>
+    {<br>
+      *int_mask |= intrs[i];<br>
+      rtems_semaphore_release( data.sem_id_int_wait );<br>
+      return;<br>
+    }<br>
+}<br>
+<br>
+static size_t get_bitstream_len( void )<br>
+{<br>
+  switch ( ZYNQ_SLCR_PSS_IDCODE_DEVICE_GE<wbr>T( zynq_slcr_pss_idcode_get() ) )<br>
+  {<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>007s:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z00<wbr>7s;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>012s:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>2s;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>014s:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>4s;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>010:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>0;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>015:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>5;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>020:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z02<wbr>0;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>030:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z03<wbr>0;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>035:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z03<wbr>5;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>045:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z04<wbr>5;<br>
+  case ZYNQ_SLCR_PSS_IDCODE_DEVICE_7z<wbr>100:<br>
+    return ZYNQ_DEVCFG_BITSTREAM_LEN_7z10<wbr>0;<br>
+  default:<br>
+    return 0;<br>
+  }<br>
+}<br>
</blockquote>
<br></div></div>
See below.<span class="m_-5010368240898671984m_561004328460053956gmail-"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+/**<br>
+ * @brief Create an aligned buffer for the bitstream.<br>
+ *<br>
+ * @param Desired length of the buffer in bytes.<br>
+ *<br>
+ * @retval NULL malloc failed.<br>
+ */<br>
+static uint8_t *dma_buf_get(<br>
+  size_t len<br>
+)<br>
+{<br>
+  dma_buf = malloc( len + ZYNQ_DEVCFG_PCAP_DMA_ALIGN );<br>
+  if ( NULL == dma_buf )<br>
+    return NULL;<br>
+  if ( (size_t)dma_buf % ZYNQ_DEVCFG_PCAP_DMA_ALIGN != 0 )<br>
+  {<br>
+    return dma_buf + ZYNQ_DEVCFG_PCAP_DMA_ALIGN<br>
+        - ( (size_t)dma_buf % ZYNQ_DEVCFG_PCAP_DMA_ALIGN );<br>
+  }<br>
+  return dma_buf;<br>
</blockquote>
<br></span>
This and the next function need to be fixed to remove the static global.<div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br></div></div></blockquote><div><br></div><div>OK.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5">
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+}<br>
+<br>
+static void dma_buf_release( void )<br>
+{<br>
+  free( dma_buf );<br>
+}<br>
+<br>
+/**<br>
+ * @brief Initiates a PCAP DMA transfer.<br>
+ *<br>
+ * @param src[in] For programming the FPGA, this is the location of the<br>
+ * bitstream data. For readback, it is the location of the PL readback command<br>
+ * sequence.<br>
+ * @param src_len Typically the length of bitstream in dwords, or the number of<br>
+ * PL commands. The user must check this value for correctness.<br>
+ * @param dst[in,out] For programming the FPGA use ZYNQ_DEVCFG_BITSTREAM_ADDR,<br>
+ * for readback this is where the readback data is stored.<br>
+ * @param dst_len Typically the Length of bitstream in dwords, or the number of<br>
+ * readback words expected. The user must check this value for correctness.<br>
+ *<br>
+ * @retval 0 Transfer was started.<br>
+ * @retval -1 src_len or dst_len invalid.<br>
+ * @retval -2 The DMA queue was full.<br>
+ */<br>
+static int pcap_dma_xfer(<br>
+  volatile zynq_devcfg_regs *regs,<br>
</blockquote>
<br></div></div>
There is only instance of these registers and they fixed in the address space, so why have the argument? It is not like we have a 1..n instances and need to vary the address.</blockquote><div><br></div><div>Good point, I'll go through and remove zynq_devcfg_regs as arguments.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+  uint32_t                  *src,<br>
+  size_t                     src_len,<br>
+  uint32_t                  *dst,<br>
+  size_t                     dst_len<br>
+)<br>
+{<br>
+#ifdef ZYNQ_DEVCFG_DEBUG<br>
+  printf( "DMA TRANSFER REQUESTED:\n" );<br>
+  printf( "Source: %p\n", src );<br>
+  printf( "Source length: %zu\n", src_len );<br>
+  printf( "Destination: %p\n", dst );<br>
+  printf( "Destination length: %zu\n", dst_len );<br>
+#endif /* ZYNQ_DEVCFG_DEBUG */<br>
+<br>
+  if ( ZYNQ_DEVCFG_DMA_SRC_LEN_LEN( src_len ) != src_len )<br>
+    return -1;<br>
+  if ( ZYNQ_DEVCFG_DMA_DEST_LEN_LEN( dst_len ) != dst_len )<br>
+    return -1;<br>
+<br>
+  /* Check if the command queue is full */<br>
+  if ( ZYNQ_DEVCFG_STATUS_DMA_CMD_Q_F<wbr>( regs->status ) )<br>
+  {<br>
+    WARN( "Zynq DMA queue full\n" );<br>
+    return -2;<br>
+  }<br>
+<br>
+  /* Order is important */<br>
+  regs->dma_src_addr = (uint32_t)src;<br>
+  regs->dma_dst_addr = (uint32_t)dst;<br>
+  regs->dma_src_len = ZYNQ_DEVCFG_DMA_SRC_LEN_LEN( src_len );<br>
+  regs->dma_dest_len = ZYNQ_DEVCFG_DMA_DEST_LEN_LEN( dst_len );<br>
+<br>
+  return 0;<br>
+}<br>
+<br>
+static int pcap_dma_xfer_wait_and_check(<br>
+  volatile zynq_devcfg_regs *regs<br>
</blockquote>
<br></div></div>
Same.<span class="m_-5010368240898671984m_561004328460053956gmail-"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+)<br>
+{<br>
+  uint32_t          int_sts;<br>
+  rtems_status_code status;<br>
+<br>
+  /* NOTE: The ISR will handle acknowledging the transfer. */<br>
+  regs->int_mask &= ~ZYNQ_DEVCFG_INT_DMA_DONE_INT;<br>
+  status = rtems_semaphore_obtain(<br>
+    data.sem_id_int_wait,<br>
+    RTEMS_WAIT,<br>
+    INT_TIMEOUT<br>
+  );<br>
+  if ( RTEMS_SUCCESSFUL != status )<br>
+  {<br>
+    regs->int_mask |= ZYNQ_DEVCFG_INT_DMA_DONE_INT;<br>
+    WARN( "DMA timed out\n" );<br>
+    return -1;<br>
+  }<br>
+<br>
+  /* TODO: More descriptive error handling */<br>
</blockquote>
<br></span>
Yes please. We need more than -1 and -2.<div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+  int_sts = regs->int_sts;<br>
+  if (<br>
+    ZYNQ_DEVCFG_INT_AXI_WERR_INT_G<wbr>ET( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_AXI_RTO_INT_GE<wbr>T( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_AXI_RERR_INT_G<wbr>ET( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_RX_FIFO_OV_INT<wbr>_GET( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_DMA_CMD_ERR_IN<wbr>T_GET( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_DMA_Q_OV_INT_G<wbr>ET( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_P2D_LEN_ERR_IN<wbr>T_GET( int_sts )<br>
+    || ZYNQ_DEVCFG_INT_PCFG_HMAC_ERR_<wbr>INT_GET( int_sts )<br>
+  )<br>
+    return -2;<br>
+<br>
+  return 0;<br>
+}<br>
+<br>
+/**<br>
+ * @brief Configure the PCAP controller.<br>
+ */<br>
+static void pl_init(<br>
+  volatile zynq_devcfg_regs *regs<br>
+)<br>
+{<br>
+  regs->ctrl = ZYNQ_DEVCFG_CTRL_PCAP_MODE( 1 ) |<br>
+      ZYNQ_DEVCFG_CTRL_PCAP_PR( ZYNQ_DEVCFG_CTRL_PCAP_PR_PCAP ) |<br>
+      ZYNQ_DEVCFG_CTRL_RESERVED_BITS | regs->ctrl;<br>
+  /* Disable loopback */<br>
+  regs->mctrl = ZYNQ_DEVCFG_MCTRL_SET(<br>
+    regs->mctrl,<br>
+    ~ZYNQ_DEVCFG_MCTRL_INT_PCAP_LP<wbr>BK( 1 ) & regs->mctrl<br>
+  );<br>
+  /* Clear all interrupts */<br>
+  regs->int_sts = ZYNQ_DEVCFG_INT_ALL;<br>
+<br>
+  if ( !data.secure )<br>
+  {<br>
+    if ( ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_<wbr>RATE_EN( regs->ctrl ) )<br>
+      regs->ctrl = ( ~ZYNQ_DEVCFG_CTRL_QUARTER_PCAP<wbr>_RATE_EN( 1 ) & regs->ctrl )<br>
+          | ZYNQ_DEVCFG_CTRL_RESERVED_BITS<wbr>;<br>
+  }<br>
+  else<br>
+  {<br>
+    if ( !ZYNQ_DEVCFG_CTRL_QUARTER_PCAP<wbr>_RATE_EN( regs->ctrl ) )<br>
+      regs->ctrl = ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_<wbr>RATE_EN( 1 ) | regs->ctrl<br>
+          | ZYNQ_DEVCFG_CTRL_RESERVED_BITS<wbr>;<br>
</blockquote>
<br></div></div>
Have you tested secure loading?</blockquote><div><br></div><div>No, I have not yet.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
+rtems_device_driver zynq_devcfg_read(<br>
+  rtems_device_major_number  major,<br>
+  rtems_device_minor_number  minor,<br>
+  void                      *args<br>
+)<br>
+{<br>
+  rtems_libio_rw_args_t *rw_args;<br>
+  int                    status;<br>
+  rtems_status_code      rstatus;<br>
+  rtems_status_code      final_status;<br>
+  uint8_t               *data_buf;<br>
+<br>
+  (void) major;<br>
+  (void) minor;<br>
+  rw_args = args;<br>
+  rw_args->bytes_moved = 0;<br>
+<br>
+  rstatus = rtems_semaphore_obtain( data.sem_id_internal, RTEMS_NO_WAIT, 0 );<br>
+  if ( RTEMS_SUCCESSFUL != rstatus )<br>
+  {<br>
+    final_status = RTEMS_RESOURCE_IN_USE;<br>
+    goto err_obtain;<br>
+  }<br>
+<br>
+  if ( rw_args->count < 4 )<br>
+  {<br>
+    final_status = RTEMS_INVALID_SIZE;<br>
+    goto err_insane;<br>
+  }<br>
+  /* TODO: It might be valid to read configuration registers while the PL is<br>
+   * not programmed.<br>
+   */<br>
+  /* PCFG_DONE must be asserted before readback */<br>
+  if ( !ZYNQ_DEVCFG_INT_PCFG_DONE_INT<wbr>_GET( data.regs->int_sts ) )<br>
+  {<br>
+    WARN( "read attempted when FPGA configuration not done\n" );<br>
+    final_status = RTEMS_IO_ERROR;<br>
+    goto err_insane;<br>
+  }<br>
+<br>
+  if ( 0 != (size_t)rw_args->buffer % ZYNQ_DEVCFG_PCAP_DMA_ALIGN )<br>
+    data_buf = dma_buf_get( rw_args->count );<br>
+  else<br>
+    data_buf = (uint8_t *)rw_args->buffer;<br>
+<br>
+  status = pcap_dma_xfer(<br>
+    data.regs,<br>
+    (uint32_t *)ZYNQ_DEVCFG_BITSTREAM_ADDR,<br>
+    rw_args->count / 4,<br>
+    (uint32_t *)( (uint32_t)data_buf | 1 ),<br>
+    rw_args->count / 4<br>
+  );<br>
</blockquote>
<br></div></div>
What happens if the fabric is loaded with a secured bitfile? Is the hardware nice about you wanting to read a secured image or does it just stop and you need a reset?<br>
<br>
What is the use case for a read? I only ask because you must have loaded the fabric in the first place so your application should have that file somewhere.<div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br></div></div></blockquote><div><br></div><div>Now that you mention it, I remember reading something about a lockdown related to this, I'll have to check the TRM.</div><div><br></div><div>The primary use case for reads that I see is an FPGA scrubber which checks and corrects configuration errors. At least in our lab, this is the primary use.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5">
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+  if ( status )<br>
+  {<br>
+    WARN( "DMA setup FAILED\n" );<br>
+    final_status = RTEMS_IO_ERROR;<br>
+    goto err_dma;<br>
+  }<br>
+  else<br>
+  {<br>
+    status = pcap_dma_xfer_wait_and_check( data.regs );<br>
+    if ( status )<br>
+    {<br>
+      WARN( "DMA FAILED\n" );<br>
+      final_status = RTEMS_IO_ERROR;<br>
+      goto err_dma;<br>
+    }<br>
+  }<br>
+<br>
+  /* Ensure stale data is not read */<br>
+  rtems_cache_invalidate_multipl<wbr>e_data_lines( data_buf, rw_args->count );<br>
+<br>
+  final_status = RTEMS_SUCCESSFUL;<br>
+  rw_args->bytes_moved = rw_args->count;<br>
+  if ( data_buf != (uint8_t *)rw_args->buffer )<br>
+    memcpy( rw_args->buffer, data_buf, rw_args->count );<br>
+err_dma:<br>
+  if ( data_buf != (uint8_t *)rw_args->buffer )<br>
+    dma_buf_release();<br>
</blockquote>
<br></div></div>
dma_buf_release(data_buf) ?</blockquote><div><br></div><div>:) In retrospect, I'm not sure why I made the DMA buffer pointer global.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+  rstatus = rtems_semaphore_release( data.sem_id_internal );<br>
+err_insane:<br>
+  if ( RTEMS_SUCCESSFUL != rstatus )<br>
+    final_status = RTEMS_INTERNAL_ERROR;<br>
+err_obtain:<br>
+  return final_status;<br>
+}<br>
+<br>
+rtems_device_driver zynq_devcfg_write(<br>
+  rtems_device_major_number  major,<br>
+  rtems_device_minor_number  minor,<br>
+  void                      *args<br>
+)<br>
+{<br>
+  rtems_libio_rw_args_t *rw_args;<br>
+  int                    status;<br>
+  rtems_status_code      rstatus;<br>
+  rtems_status_code      final_status;<br>
+  uint8_t               *data_buf;<br>
+<br>
+  (void) major;<br>
+  (void) minor;<br>
+  rw_args = args;<br>
+  rw_args->bytes_moved = 0;<br>
+<br>
+  rstatus = rtems_semaphore_obtain( data.sem_id_internal, RTEMS_NO_WAIT, 0 );<br>
+  if ( RTEMS_SUCCESSFUL != rstatus )<br>
+  {<br>
+    final_status = RTEMS_RESOURCE_IN_USE;<br>
+    goto err_obtain;<br>
+  }<br>
</blockquote>
<br></div></div>
I suggest a check of the data for a valid series of dummy words, the bus width auto detect and a sync word. This sequence is used by the PCAP loader to sync and align the bit stream to be processed.<br>
<br>
For example here is a function that determines the endian format of the image before it is loaded:<br>
<br>
static bool image_check_endian(const uint32_t* image)<br>
{<br>
  /* Xilinx binary format header */<br>
  const uint32_t bin_format[] = {<br>
    0xffffffff, /* Dummy words */<br>
    0xffffffff,<br>
    0x000000bb, /* Bus width auto detect, word 1 */<br>
    0x11220044, /* Bus width auto detect, word 2 */<br>
    0xffffffff,<br>
    0xffffffff,<br>
    0xaa995566, /* Sync word */<br>
  };<br>
  size_t i;<br>
<br>
  for (i = 0; i < (sizeof(bin_format) / sizeof(uint32_t)); i++) {<br>
    if (image[i] != bin_format[i]) {<br>
      return false;<br>
    }<br>
  }<br>
<br>
  return true;<div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
}<br></div></div></blockquote><div><br></div><div>This is a good check to have, however I'll need to check that the commands used with the PCAP after a full programming have a similar header. </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
+rtems_device_driver zynq_devcfg_control(<br>
+  rtems_device_major_number  major,<br>
+  rtems_device_minor_number  minor,<br>
+  void                      *args<br>
+)<br>
+{<br>
+  rtems_libio_ioctl_args_t *ioctl_args;<br>
+  char                     *str;<br>
+  int                       status;<br>
+  rtems_status_code         rstatus;<br>
+  rtems_status_code         final_status;<br>
+<br>
+  (void) major;<br>
+  (void) minor;<br>
+  ioctl_args = args;<br>
+<br>
+  rstatus = rtems_semaphore_obtain( data.sem_id_internal, RTEMS_NO_WAIT, 0 );<br>
+  if ( RTEMS_UNSATISFIED == rstatus )<br>
+  {<br>
+    ioctl_args->ioctl_return = -1;<br>
+    return RTEMS_RESOURCE_IN_USE;<br>
+  }<br>
+  else if ( RTEMS_SUCCESSFUL != rstatus )<br>
+  {<br>
+    ioctl_args->ioctl_return = -1;<br>
+    return RTEMS_INTERNAL_ERROR;<br>
+  }<br>
+<br>
+  final_status = RTEMS_SUCCESSFUL;<br>
+  ioctl_args->ioctl_return = 0;<br>
+  switch ( ioctl_args->command ) {<br>
+  case ZYNQ_DEVCFG_IOCTL_VERSION:<br>
+    str = ioctl_args->buffer;<br>
+    switch( ZYNQ_DEVCFG_MCTRL_PS_VERSION_G<wbr>ET( data.regs->mctrl ) ) {<br>
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_1<wbr>_0:<br>
+        strncpy( str, "1.0", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_<wbr>LEN );<br>
+        break;<br>
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_2<wbr>_0:<br>
+        strncpy( str, "2.0", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_<wbr>LEN );<br>
+        break;<br>
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_3<wbr>_0:<br>
+        strncpy( str, "3.0", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_<wbr>LEN );<br>
+        break;<br>
+      case ZYNQ_DEVCFG_MCTRL_PS_VERSION_3<wbr>_1:<br>
+        strncpy( str, "3.1", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_<wbr>LEN );<br>
+        break;<br>
+      default:<br>
+        strncpy( str, "???", ZYNQ_DEVCFG_IOCTL_VERSION_MAX_<wbr>LEN );<br>
+        break;<br>
+    }<br>
+    break;<br>
+  case ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM<wbr>_PRE:<br>
+    pl_init( data.regs );<br>
+    /* Hold FPGA clocks in reset */<br>
+    zynq_slcr_fpga_clk_rst( 0xf );<br>
+    /* Enable PS to PL level shifters */<br>
+    zynq_slcr_level_shifter_enable<wbr>( ZYNQ_SLCR_LVL_SHFTR_EN_DISABLE );<br>
+    zynq_slcr_level_shifter_enable<wbr>( ZYNQ_SLCR_LVL_SHFTR_EN_PS_TO_P<wbr>L );<br>
+    status = pl_clear( data.regs );<br>
+    if ( 0 != status )<br>
+    {<br>
+      ioctl_args->ioctl_return = -1;<br>
+      final_status = RTEMS_UNSATISFIED;<br>
+    }<br>
</blockquote>
<br></div></div>
Why are the load phases broken out like this? A use case type of example would be nice.</blockquote><div><br></div><div>The load phases are broken out to keep the write function generic enough to be used after the initial configuration of the FPGA. Here is an example of an initial load, minus error checking:</div><div><br></div><div>ioctl ( fd, ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM<wbr>_PRE );<br></div><div>write( fd, BITSTREAM_DATA, BITSTREAM_LEN );<br></div><div>ioctl ( fd, ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM<wbr>_WAIT_DONE );<br></div><div>ioctl ( fd, ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM<wbr>_POST );<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="m_-5010368240898671984m_561004328460053956gmail-"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    break;<br>
+  case ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM<wbr>_POST:<br>
+    /* Enable all PS-PL level shifters */<br>
+    zynq_slcr_level_shifter_enable<wbr>( ZYNQ_SLCR_LVL_SHFTR_EN_ALL );<br>
+    /* Release FPGA clocks from reset */<br>
+    zynq_slcr_fpga_clk_rst( 0 );<br>
+    break;<br>
+  case ZYNQ_DEVCFG_IOCTL_FPGA_PROGRAM<wbr>_WAIT_DONE:<br>
+    data.regs->int_mask &= ~ZYNQ_DEVCFG_INT_PCFG_DONE_INT<wbr>;<br>
+    status = rtems_semaphore_obtain(<br>
+      data.sem_id_int_wait,<br>
+      RTEMS_WAIT,<br>
+      INT_TIMEOUT<br>
+    );<br>
+    if ( RTEMS_SUCCESSFUL != status )<br>
+    {<br>
+      ioctl_args->ioctl_return = -1;<br>
+      data.regs->int_mask |= ZYNQ_DEVCFG_INT_PCFG_DONE_INT;<br>
+      final_status = RTEMS_TIMEOUT;<br>
+    }<br>
</blockquote>
<br></span>
I am still not seeing the need for asychronous operation.<span class="m_-5010368240898671984m_561004328460053956gmail-"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    break;<br>
+  case ZYNQ_DEVCFG_IOCTL_SET_SECURE:<br>
+    data.secure = *(bool *)ioctl_args->buffer;<br>
+    break;<br>
</blockquote>
<br></span>
We can take this off line, but it is easier to detect the secure bit in the bitfile and so this call is not needed. We can also not bother to load a secure bitfile if the Zynq is not secure.<br>
<br>
While on the topic of secure modes, data can be decrypt by passing it in and then out of the PCAN. When I tested this a few years ago I found it only worked with the OCM. We should support doing this as it is a requirement on some system that data be secured with the same AES key as the bitfile. I am not sure how this would fit into the device model being used here, I suppose the returning data is buffered until read or the device is closed.</blockquote><div><br></div><div>OK, I'm open to talking more about secure mode offline. It sounds like you've used it extensively.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="m_-5010368240898671984m_561004328460053956gmail-"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+  case ZYNQ_DEVCFG_IOCTL_BITSTREAM_LE<wbr>N_GET:<br>
+    *(size_t *)ioctl_args->buffer = get_bitstream_len();<br>
+    break;<br>
</blockquote>
<br></span>
What is purpose of this?</blockquote><div><br></div><div>I made that function originally when I was checking the length of the input data. After I realized I needed to support more than just full-bitstream programming, and that the input data was just commands, it really isn't that useful anymore, since the user will already know the length of their commands anyway. I'll take this out, along with the get_bitstream_len function.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+  default:<br>
+    ioctl_args->ioctl_return = -1;<br>
+    final_status = RTEMS_INVALID_NAME; /* Maps to EINVAL */<br>
+    break;<br>
+  }<br>
+<br>
+  rstatus = rtems_semaphore_release( data.sem_id_internal );<br>
+  if ( rstatus != RTEMS_SUCCESSFUL )<br>
+    WARN( "Failed to release internal semaphore\n" );<br>
+  return final_status;<br>
+}<br>
diff --git a/c/src/lib/libbsp/arm/xilinx-<wbr>zynq/include/zynq-devcfg-regs.<wbr>h b/c/src/lib/libbsp/arm/xilinx-<wbr>zynq/include/zynq-devcfg-regs.<wbr>h<br>
new file mode 100644<br>
index 0000000..d3601d6<br>
--- /dev/null<br>
+++ b/c/src/lib/libbsp/arm/xilinx-<wbr>zynq/include/zynq-devcfg-regs.<wbr>h<br>
@@ -0,0 +1,194 @@<br>
+/**<br>
+ * @file<br>
+ * @ingroup zynq_devcfg<br>
+ * @brief Device configuration interface register definitions.<br>
+ */<br>
+<br>
+/*<br>
+ * Copyright (c) 2016<br>
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),<br>
+ *  University of Florida.  All rights reserved.<br>
+ * Copyright (c) 2017<br>
+ *  NSF Center for High-Performance Reconfigurable Computing (CHREC),<br>
+ *  University of Pittsburgh.  All rights reserved.<br>
+ *<br>
+ * Redistribution and use in source and binary forms, with or without<br>
+ * modification, are permitted provided that the following conditions are<br>
+ * met:<br>
+ * 1. Redistributions of source code must retain the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer.<br>
+ * 2. Redistributions in binary form must reproduce the above copyright<br>
+ *    notice, this list of conditions and the following disclaimer in the<br>
+ *    documentation and/or other materials provided with the distribution.<br>
+ *<br>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS<br>
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED<br>
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A<br>
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER<br>
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,<br>
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,<br>
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR<br>
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF<br>
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br>
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS<br>
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br>
+ *<br>
+ * The views and conclusions contained in the software and documentation<br>
+ * are those of the authors and should not be interpreted as representing<br>
+ * official policies, either expressed or implied, of CHREC.<br>
+ *<br>
+ * Author: Patrick Gauvin <<a href="mailto:gauvin@hcs.ufl.edu" target="_blank">gauvin@hcs.ufl.edu</a>><br>
+ */<br>
+<br>
+/**<br>
+ * @defgroup zynq_devcfg_regs Device Configuration Interface Register Definitions<br>
+ * @ingroup zynq_devcfg<br>
+ * @brief Device Configuration Interface Register Definitions<br>
+ */<br>
+<br>
+#ifndef LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_<wbr>REGS_H<br>
+#define LIBBSP_ARM_XILINX_ZYNQ_DEVCFG_<wbr>REGS_H<br>
+<br>
+#include <bsp/utility.h><br>
+<br>
+#ifdef __cplusplus<br>
+extern "C" {<br>
+#endif /* __cplusplus */<br>
+<br>
+/* Zynq-7000 series devcfg address */<br>
+#define ZYNQ_DEVCFG_BASE_ADDR 0xF8007000UL<br>
+/* For use with the PCAP DMA */<br>
+#define ZYNQ_DEVCFG_BITSTREAM_ADDR 0xFFFFFFFFUL<br>
+/* PCAP DMA transfers must be 64-byte aligned */<br>
+#define ZYNQ_DEVCFG_PCAP_DMA_ALIGN 64<br>
+#define ZYNQ_DEVCFG_INTERRUPT_VECTOR 40<br>
+<br>
+typedef struct {<br>
+  uint32_t ctrl;<br>
+#define ZYNQ_DEVCFG_CTRL_FORCE_RST( val ) BSP_FLD32( val, 31, 31 )<br>
+#define ZYNQ_DEVCFG_CTRL_FORCE_RST_GET<wbr>( reg ) BSP_FLD32GET( reg, 31, 31 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_PROG_B_G<wbr>ET( reg ) BSP_FLD32GET( reg, 30, 30 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_PROG_B( val ) BSP_FLD32( val, 30, 30 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_POR_CNT_<wbr>4K_GET( reg ) BSP_FLD32GET( reg, 29, 29 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_POR_CNT_<wbr>4K( val ) BSP_FLD32( val, 29, 29 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR( val ) BSP_FLD32( val, 27, 27 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR_GET( reg ) BSP_FLD32GET( reg, 27, 27 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR_ICAP ( 0 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCAP_PR_PCAP ( 1 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCAP_MODE( val ) BSP_FLD32( val, 26, 26 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCAP_MODE_GET<wbr>( reg ) BSP_FLD32GET( reg, 26, 26 )<br>
+#define ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_<wbr>RATE_EN( val ) BSP_FLD32( val, 25, 25 )<br>
+#define ZYNQ_DEVCFG_CTRL_QUARTER_PCAP_<wbr>RATE_EN_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 25, 25 )<br>
+#define ZYNQ_DEVCFG_CTRL_MULTIBOOT_EN( val ) BSP_FLD32( val, 24, 24 )<br>
+#define ZYNQ_DEVCFG_CTRL_MULTIBOOT_EN_<wbr>GET( reg ) BSP_FLD32GET( reg, 24, 24 )<br>
+#define ZYNQ_DEVCFG_CTRL_JTAG_CHAIN_DI<wbr>S( val ) BSP_FLD32( val, 23, 23 )<br>
+#define ZYNQ_DEVCFG_CTRL_JTAG_CHAIN_DI<wbr>S_GET( reg ) BSP_FLD32GET( reg, 23, 23 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE<wbr>( val ) BSP_FLD32( val, 12, 12 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE<wbr>_GET( reg ) BSP_FLD32GET( reg, 12, 12 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE<wbr>_BBRAM ( 0 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_FUSE<wbr>_EFUSE ( 1 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN( val ) BSP_FLD32( val, 9, 11 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_G<wbr>ET( reg ) BSP_FLD32GET( reg, 9, 11 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_E<wbr>NABLE ( 0x3 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_D<wbr>ISABLE ( 0x0 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_AES_EN_L<wbr>OCKDOWN ( 0x1 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SEU_EN( val ) BSP_FLD32( val, 8, 8 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SEU_EN_G<wbr>ET( reg ) BSP_FLD32GET( reg, 8, 8 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SEC_EN_G<wbr>ET( reg ) BSP_FLD32GET( reg, 7, 7 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPNIDEN( val ) BSP_FLD32( val, 6, 6 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPNIDEN_<wbr>GET( reg ) BSP_FLD32GET( reg, 6, 6 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPIDEN( val ) BSP_FLD32( val, 5, 5 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_SPIDEN_G<wbr>ET( reg ) BSP_FLD32GET( reg, 5, 5 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_NIDEN( val ) BSP_FLD32( val, 4, 4 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_NIDEN_GE<wbr>T( reg ) BSP_FLD32GET( reg, 4, 4 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_DBGEN( val ) BSP_FLD32( val, 3, 3 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_DBGEN_GE<wbr>T( reg ) BSP_FLD32GET( reg, 3, 3 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN( val ) BSP_FLD32( val, 0, 2 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN_G<wbr>ET( reg ) BSP_FLD32GET( reg, 0, 2 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN_E<wbr>NABLE ( 0x3 )<br>
+#define ZYNQ_DEVCFG_CTRL_PCFG_DAP_EN_B<wbr>YPASS ( 0x0 )<br>
+#define ZYNQ_DEVCFG_CTRL_RESERVED_BITS ( 0x6000 )<br>
+  uint32_t lock;<br>
+  uint32_t cfg;<br>
+  /* int_sts and int_mask directly overlap, so they share the ZYNQ_DEVCFG_INT_*<br>
+   * macros */<br>
+  uint32_t int_sts;<br>
+  uint32_t int_mask;<br>
+#define ZYNQ_DEVCFG_INT_PSS_CFG_RESET_<wbr>B_INT BSP_BIT32( 27 )<br>
+#define ZYNQ_DEVCFG_INT_PSS_CFG_RESET_<wbr>B_INT_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 27, 27 )<br>
+#define ZYNQ_DEVCFG_INT_AXI_WERR_INT_G<wbr>ET( reg ) BSP_FLD32GET( reg, 22, 22 )<br>
+#define ZYNQ_DEVCFG_INT_AXI_RTO_INT_GE<wbr>T( reg ) BSP_FLD32GET( reg, 21, 21 )<br>
+#define ZYNQ_DEVCFG_INT_AXI_RERR_INT_G<wbr>ET( reg ) BSP_FLD32GET( reg, 20, 20 )<br>
+#define ZYNQ_DEVCFG_INT_RX_FIFO_OV_INT<wbr>_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 18, 18 )<br>
+#define ZYNQ_DEVCFG_INT_DMA_CMD_ERR_IN<wbr>T_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 15, 15 )<br>
+#define ZYNQ_DEVCFG_INT_DMA_Q_OV_INT_G<wbr>ET( reg ) BSP_FLD32GET( reg, 14, 14 )<br>
+#define ZYNQ_DEVCFG_INT_DMA_DONE_INT BSP_BIT32( 13 )<br>
+#define ZYNQ_DEVCFG_INT_DMA_DONE_INT_G<wbr>ET( reg ) BSP_FLD32GET( reg, 13, 13 )<br>
+#define ZYNQ_DEVCFG_INT_D_P_DONE_INT BSP_BIT32( 12 )<br>
+#define ZYNQ_DEVCFG_INT_D_P_DONE_INT_G<wbr>ET( reg ) BSP_FLD32GET( reg, 12, 12 )<br>
+#define ZYNQ_DEVCFG_INT_P2D_LEN_ERR_IN<wbr>T_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 11, 11 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_HMAC_ERR_<wbr>INT_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 6, 6 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_SEU_ERR_I<wbr>NT_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 5, 5 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_POR_B_INT<wbr>_GET( reg ) BSP_FLD32GET( reg, 4, 4 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_CFG_RST_I<wbr>NT_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 3, 3 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_DONE_INT BSP_BIT32( 2 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_DONE_INT_<wbr>GET( reg ) BSP_FLD32GET( reg, 2, 2 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_PE_I<wbr>NT BSP_BIT32( 1 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_PE_I<wbr>NT_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 1, 1 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_NE_I<wbr>NT BSP_BIT32( 0 )<br>
+#define ZYNQ_DEVCFG_INT_PCFG_INIT_NE_I<wbr>NT_GET( reg ) \<br>
+  BSP_FLD32GET( reg, 0, 0 )<br>
+#define ZYNQ_DEVCFG_INT_ALL ( 0xf8f7f87f )<br>
+  uint32_t status;<br>
+#define ZYNQ_DEVCFG_STATUS_DMA_CMD_Q_F<wbr>( val ) BSP_FLD32( val, 31, 31 )<br>
+#define ZYNQ_DEVCFG_STATUS_DMA_CMD_Q_F<wbr>_GET( reg ) BSP_FLD32GET( reg, 31, 31 )<br>
+#define ZYNQ_DEVCFG_STATUS_PCFG_INIT_G<wbr>ET( reg ) BSP_FLD32GET( reg, 4, 4 )<br>
+  uint32_t dma_src_addr;<br>
+  uint32_t dma_dst_addr;<br>
+  uint32_t dma_src_len;<br>
+#define ZYNQ_DEVCFG_DMA_SRC_LEN_LEN( val ) BSP_FLD32( val, 0, 26 )<br>
+  uint32_t dma_dest_len; /* (sic) */<br>
+#define ZYNQ_DEVCFG_DMA_DEST_LEN_LEN( val ) BSP_FLD32( val, 0, 26 )<br>
+  uint32_t reserved0;<br>
+  uint32_t multiboot_addr;<br>
+  uint32_t reserved1;<br>
+  uint32_t unlock;<br>
+  uint32_t reserved2[18];<br>
+  uint32_t mctrl;<br>
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_G<wbr>ET( reg ) BSP_FLD32GET( reg, 28, 31 )<br>
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_1<wbr>_0 0x0<br>
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_2<wbr>_0 0x1<br>
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_3<wbr>_0 0x2<br>
+#define ZYNQ_DEVCFG_MCTRL_PS_VERSION_3<wbr>_1 0x3<br>
+#define ZYNQ_DEVCFG_MCTRL_PCFG_POR_B_G<wbr>ET( reg ) BSP_FLD32GET( reg, 8, 8 )<br>
+#define ZYNQ_DEVCFG_MCTRL_INT_PCAP_LPB<wbr>K_GET( reg ) BSP_FLD32GET( reg, 4, 4 )<br>
+#define ZYNQ_DEVCFG_MCTRL_INT_PCAP_LPB<wbr>K( val ) BSP_FLD32( val, 4, 4 )<br>
+#define ZYNQ_DEVCFG_MCTRL_RESERVED_SET<wbr>_BITS ( 0x800000 )<br>
+#define ZYNQ_DEVCFG_MCTRL_RESERVED_UNS<wbr>ET_BITS ( 0x3 )<br>
+#define ZYNQ_DEVCFG_MCTRL_SET( reg, val ) ( ( ( reg ) & \<br>
+  ~ZYNQ_DEVCFG_MCTRL_RESERVED_UN<wbr>SET_BITS ) | \<br>
+  ZYNQ_DEVCFG_MCTRL_RESERVED_SET<wbr>_BITS | ( val ) )<br>
+  uint32_t reserved3[32];<br>
+  uint32_t xadcif_cfg;<br>
+  uint32_t xadcif_int_sts;<br>
+  uint32_t xadcif_int_mask;<br>
+  uint32_t xadcif_msts;<br>
+  uint32_t xadcif_cmdfifo;<br>
+  uint32_t xadcif_rdfifo;<br>
+  uint32_t xadcif_mctrl;<br>
+} zynq_devcfg_regs;<br>
</blockquote>
<br></div></div>
<burb> that is a lot of work for accessing a subset of the avaliable registers (I do not like struct mapping to hardware, too many scars). I can only hope these values are correct given the TRM is defined in absolute addresses and offsets.<div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br></div></div></blockquote><div><br></div><div>I'll consider moving to the get/set method you proposed for the SLCR patch. The struct mapping bit me a couple times on this project alone.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
+/** @brief Bitstream lengths in bytes */<br>
+enum zynq_devcfg_bitstream_length {<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z00<wbr>7s = 16669920UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>2s = 28085344UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>4s = 32364512UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>0 = 16669920UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z01<wbr>5 = 28085344UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z02<wbr>0 = 32364512UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z03<wbr>0 = 47839328UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z03<wbr>5 = 106571232UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z04<wbr>5 = 106571232UL / 8,<br>
+  ZYNQ_DEVCFG_BITSTREAM_LEN_7z10<wbr>0 = 139330784UL / 8<br>
</blockquote>
<br></div></div>
Where did these figures come from?<br>
<br>
I would be careful here. A bitfile is a sequence of words that are interpreted. The hardware has registers and op codes and so the length of data can vary including the number of dummy values, or sync bytes that appear in the bit sequence. For example a secure bitfile has a bit set in one of the internal register that selects the secure mode.<br>
<br>
I will send you some code offline I have that decodes the format. There is a Xilinx UG on the topic, I forget the number.<div class="m_-5010368240898671984m_561004328460053956gmail-HOEnZb"><div class="m_-5010368240898671984m_561004328460053956gmail-h5"><br></div></div></blockquote><div><br></div><div>I pulled those numbers from the TRM. They end up being equivalent to the length of the BIN-format bitstream that Vivado outputs. I'll be removing this as I mentioned above since it no longer has a use.</div><div><br></div><div>I'll plan on sending out a v2 patch early next week.</div><div><br></div><div>Thanks again for all the feedback,</div><div><br></div><div>Patrick</div></div><br></div></div>