--- 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 <gau...@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 <gau...@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 <gau...@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 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel