blob: 791012e55b57558a8570b14a4c338d74c9a3c0b4 [file] [log] [blame]
/******************************************************************************
*
* alt_fpga_manager.c - API for the Altera SoC FPGA manager.
*
******************************************************************************/
/******************************************************************************
*
* Copyright 2013 Altera Corporation. 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.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "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 AUTHOR 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.
*
******************************************************************************/
#include "alt_fpga_manager.h"
#include "socal/socal.h"
#include "socal/hps.h"
#include "socal/alt_fpgamgr.h"
#include <string.h>
#include <stdbool.h>
#include "hwlib.h"
// NOTE: To enable debugging output, delete the next line and uncomment the
// line after.
#define dprintf(...)
// #define dprintf(fmt, ...) printf(fmt, __VA_ARGS__)
// This is the timeout used when waiting for a state change in the FPGA monitor.
#define _ALT_FPGA_TMO_STATE 2048
// This is the timeout used when waiting for the DCLK countdown to complete.
// The time to wait a constant + DCLK * multiplier.
#define _ALT_FPGA_TMO_DCLK_CONST 2048
#define _ALT_FPGA_TMO_DCLK_MUL 2
#define _ALT_FPGA_TMO_CONFIG 8192
// This define is used to control whether to use the Configuration with DCLK steps
#ifndef _ALT_FPGA_USE_DCLK
#define _ALT_FPGA_USE_DCLK 0
#endif
/////
// This is used in the FGPA reconfiguration streaming interface. Because FPGA
// images are commonly stored on disk, the chunk size is that of the disk size.
// We cannot choose too large a chunk size because the stack size is fairly
// small.
#define DISK_SECTOR_SIZE 512
#define ISTREAM_CHUNK_SIZE DISK_SECTOR_SIZE
/////
//
// FPGA Data Type identifier enum
//
typedef enum FPGA_DATA_TYPE_e
{
FPGA_DATA_FULL = 1,
FPGA_DATA_ISTREAM = 2
} FPGA_DATA_TYPE_t;
//
// FPGA Data, for Full Stream or IStream configuration
//
typedef struct FPGA_DATA_s
{
FPGA_DATA_TYPE_t type;
union
{
// For FPGA_DATA_FULL
struct
{
const void * buffer;
size_t length;
} full;
// FPGA_DATA_ISTREAM
struct
{
alt_fpga_istream_t stream;
void * data;
} istream;
} mode;
#if ALT_FPGA_ENABLE_DMA_SUPPORT
bool use_dma;
ALT_DMA_CHANNEL_t dma_channel;
#endif
} FPGA_DATA_t;
#if ALT_FPGA_ENABLE_DMA_SUPPORT
static ALT_STATUS_CODE alt_dma_channel_wait_for_state(ALT_DMA_CHANNEL_t channel,
ALT_DMA_CHANNEL_STATE_t state,
uint32_t count)
{
ALT_STATUS_CODE status = ALT_E_SUCCESS;
ALT_DMA_CHANNEL_STATE_t current;
uint32_t i = count;
while (--i)
{
status = alt_dma_channel_state_get(channel, &current);
if (status != ALT_E_SUCCESS)
{
break;
}
if (current == state)
{
break;
}
}
if (i == 0)
{
dprintf("FPGA[AXI]: Timeout [count=%u] waiting for DMA state [%d]. Last state was [%d]",
(unsigned)count,
(int)state, (int)current);
status = ALT_E_TMO;
}
return status;
}
#endif
//
// Helper function which handles writing data to the AXI bus.
//
static ALT_STATUS_CODE alt_fpga_internal_writeaxi(const char * cfg_buf, uint32_t cfg_buf_len
#if ALT_FPGA_ENABLE_DMA_SUPPORT
,
bool use_dma, ALT_DMA_CHANNEL_t dma_channel
#endif
)
{
ALT_STATUS_CODE status = ALT_E_SUCCESS;
#if ALT_FPGA_ENABLE_DMA_SUPPORT
// Use DMA if requested.
if (use_dma)
{
ALT_DMA_PROGRAM_t program;
if (status == ALT_E_SUCCESS)
{
dprintf("FPGA[AXI]: DMA mem-to-reg ...\n");
status = alt_dma_memory_to_register(dma_channel, &program,
ALT_FPGAMGRDATA_ADDR,
cfg_buf,
cfg_buf_len >> 2, // we need the number uint32_t's
32,
false, ALT_DMA_EVENT_0);
}
if (status == ALT_E_SUCCESS)
{
dprintf("FPGA[AXI]: Wait for channel to stop\n");
// NOTE: Polling this register is much better than polling the
// FPGA status register. Thus ensure the DMA is complete here.
status = alt_dma_channel_wait_for_state(dma_channel, ALT_DMA_CHANNEL_STATE_STOPPED, cfg_buf_len);
}
}
else
#endif
{
dprintf("FPGA[AXI]: PIO memcpy() ...\n");
size_t i = 0;
// Write out as many complete 32-bit chunks.
const uint32_t * buffer_32 = (const uint32_t *) cfg_buf;
while (cfg_buf_len >= sizeof(uint32_t))
{
alt_write_word(ALT_FPGAMGRDATA_ADDR, buffer_32[i++]);
cfg_buf_len -= sizeof(uint32_t);
}
}
// Write out remaining non 32-bit chunks.
if ((status == ALT_E_SUCCESS) && (cfg_buf_len & 0x3))
{
dprintf("FPGA[AXI]: Copy non-aligned data ...\n");
const uint32_t * buffer_32 = (const uint32_t *) (cfg_buf + (cfg_buf_len & ~0x3));
switch (cfg_buf_len & 0x3)
{
case 3:
alt_write_word(ALT_FPGAMGRDATA_ADDR, *buffer_32 & 0x00ffffff);
break;
case 2:
alt_write_word(ALT_FPGAMGRDATA_ADDR, *buffer_32 & 0x0000ffff);
break;
case 1:
alt_write_word(ALT_FPGAMGRDATA_ADDR, *buffer_32 & 0x000000ff);
break;
default:
// This will never happen.
break;
}
}
return status;
}
/////
//
// Helper function which sets the DCLKCNT, waits for DCLKSTAT to report the
// count completed, and clear the complete status.
// Returns:
// - ALT_E_SUCCESS if the FPGA DCLKSTAT reports that the DCLK count is done.
// - ALT_E_TMO if the number of polling cycles exceeds the timeout value.
//
static ALT_STATUS_CODE dclk_set_and_wait_clear(uint32_t count, uint32_t timeout)
{
ALT_STATUS_CODE status = ALT_E_TMO;
// Clear any existing DONE status. This can happen if a previous call to
// this function returned timeout. The counter would complete later on but
// never be cleared.
if (alt_read_word(ALT_FPGAMGR_DCLKSTAT_ADDR))
{
alt_write_word(ALT_FPGAMGR_DCLKSTAT_ADDR, ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE);
}
// Issue the DCLK count.
alt_write_word(ALT_FPGAMGR_DCLKCNT_ADDR, count);
// Poll DCLKSTAT to see if it completed in the timeout period specified.
do
{
dprintf(".");
uint32_t done = alt_read_word(ALT_FPGAMGR_DCLKSTAT_ADDR);
if (done == ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE)
{
// Now that it is done, clear the DONE status.
alt_write_word(ALT_FPGAMGR_DCLKSTAT_ADDR, ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE);
status = ALT_E_SUCCESS;
break;
}
}
while (timeout--);
dprintf("\n");
return status;
}
//
// Helper function which waits for the FPGA to enter the specified state.
// Returns:
// - ALT_E_SUCCESS if successful
// - ALT_E_TMO if the number of polling cycles exceeds the timeout value.
//
static ALT_STATUS_CODE wait_for_fpga_state(ALT_FPGA_STATE_t state, uint32_t timeout)
{
ALT_STATUS_CODE status = ALT_E_TMO;
// Poll on the state to see if it matches the requested state within the
// timeout period specified.
do
{
dprintf(".");
ALT_FPGA_STATE_t current = alt_fpga_state_get();
if (current == state)
{
status = ALT_E_SUCCESS;
break;
}
}
while (timeout--);
dprintf("\n");
return status;
}
//
// Waits for the FPGA CB monitor to report the FPGA configuration status by
// polling that both CONF_DONE and nSTATUS or neither flags are set.
//
// Returns:
// - ALT_E_SUCCESS if CB monitor reports configuration successful.
// - ALT_E_FPGA_CFG if CB monitor reports configuration failure.
// - ALT_E_FPGA_CRC if CB monitor reports a CRC error.
// - ALT_E_TMO if CONF_DONE and nSTATUS fails to "settle" in the number
// of polling cycles specified by the timeout value.
//
static ALT_STATUS_CODE wait_for_config_done(uint32_t timeout)
{
ALT_STATUS_CODE retval = ALT_E_TMO;
// Poll on the CONF_DONE and nSTATUS both being set within the timeout
// period specified.
do
{
dprintf(".");
uint32_t status = alt_fpga_mon_status_get();
// Detect CRC problems with the FGPA configuration
if (status & ALT_FPGA_MON_CRC_ERROR)
{
retval = ALT_E_FPGA_CRC;
break;
}
bool conf_done = (status & ALT_FPGA_MON_CONF_DONE) != 0;
bool nstatus = (status & ALT_FPGA_MON_nSTATUS) != 0;
if (conf_done == nstatus)
{
if (conf_done)
{
retval = ALT_E_SUCCESS;
}
else
{
retval = ALT_E_FPGA_CFG;
}
break;
}
}
while (timeout--);
dprintf("\n");
return retval;
}
/////
ALT_STATUS_CODE alt_fpga_init(void)
{
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_fpga_uninit(void)
{
return ALT_E_SUCCESS;
}
/////
ALT_STATUS_CODE alt_fpga_control_enable(void)
{
// Simply set CTRL.EN to allow HPS to control the FPGA control block.
alt_setbits_word(ALT_FPGAMGR_CTL_ADDR, ALT_FPGAMGR_CTL_EN_SET_MSK);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_fpga_control_disable(void)
{
// Simply clear CTRL.EN to allow HPS to control the FPGA control block.
alt_clrbits_word(ALT_FPGAMGR_CTL_ADDR, ALT_FPGAMGR_CTL_EN_SET_MSK);
return ALT_E_SUCCESS;
}
bool alt_fpga_control_is_enabled(void)
{
// Check if CTRL.EN is set or not.
if ((alt_read_word(ALT_FPGAMGR_CTL_ADDR) & ALT_FPGAMGR_CTL_EN_SET_MSK) != 0)
{
return true;
}
else
{
return false;
}
}
ALT_FPGA_STATE_t alt_fpga_state_get(void)
{
// Detect FPGA power status.
// NOTE: Do not use alt_fpga_state_get() to look for ALT_FPGA_STATE_PWR_OFF.
// This is a bit of a misnomer in that the ALT_FPGA_STATE_PWR_OFF really means
// FPGA is powered off or idle (which happens just out of being reset by the
// reset manager).
if ((alt_fpga_mon_status_get() & ALT_FPGA_MON_FPGA_POWER_ON) == 0)
{
return ALT_FPGA_STATE_POWER_OFF;
}
// The fpgamgrreg::stat::mode bits maps to the FPGA state enum.
return (ALT_FPGA_STATE_t) ALT_FPGAMGR_STAT_MOD_GET(alt_read_word(ALT_FPGAMGR_STAT_ADDR));
}
uint32_t alt_fpga_mon_status_get(void)
{
return alt_read_word(ALT_FPGAMGR_MON_GPIO_EXT_PORTA_ADDR) & ((1 << 12) - 1);
}
ALT_STATUS_CODE alt_fgpa_reset_assert(void)
{
// Verify that HPS has control of the FPGA control block.
if (alt_fpga_control_is_enabled() != true)
{
return ALT_E_FPGA_NO_SOC_CTRL;
}
// Detect FPGA power status.
if (alt_fpga_state_get() == ALT_FPGA_STATE_POWER_OFF)
{
return ALT_E_FPGA_PWR_OFF;
}
// Set the nCONFIGPULL to put the FPGA into reset.
alt_setbits_word(ALT_FPGAMGR_CTL_ADDR, ALT_FPGAMGR_CTL_NCFGPULL_SET_MSK);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_fgpa_reset_deassert(void)
{
// Verify that HPS has control of the FPGA control block.
if (alt_fpga_control_is_enabled() != true)
{
return ALT_E_FPGA_NO_SOC_CTRL;
}
// Detect FPGA power status.
if (alt_fpga_state_get() == ALT_FPGA_STATE_POWER_OFF)
{
return ALT_E_FPGA_PWR_OFF;
}
// Clear the nCONFIGPULL to release the FPGA from reset.
alt_clrbits_word(ALT_FPGAMGR_CTL_ADDR, ALT_FPGAMGR_CTL_NCFGPULL_SET_MSK);
return ALT_E_SUCCESS;
}
ALT_FPGA_CFG_MODE_t alt_fpga_cfg_mode_get(void)
{
uint32_t msel = ALT_FPGAMGR_STAT_MSEL_GET(alt_read_word(ALT_FPGAMGR_STAT_ADDR));
switch (msel)
{
case ALT_FPGAMGR_STAT_MSEL_E_PP16_FAST_NOAES_NODC: // SoCAL: 0x0
case ALT_FPGAMGR_STAT_MSEL_E_PP16_FAST_AES_NODC: // SoCAL: 0x1
case ALT_FPGAMGR_STAT_MSEL_E_PP16_FAST_AESOPT_DC: // SoCAL: 0x2
case ALT_FPGAMGR_STAT_MSEL_E_PP16_SLOW_NOAES_NODC: // SoCAL: 0x4
case ALT_FPGAMGR_STAT_MSEL_E_PP16_SLOW_AES_NODC: // SoCAL: 0x5
case ALT_FPGAMGR_STAT_MSEL_E_PP16_SLOW_AESOPT_DC: // SoCAL: 0x6
case ALT_FPGAMGR_STAT_MSEL_E_PP32_FAST_NOAES_NODC: // SoCAL: 0x8
case ALT_FPGAMGR_STAT_MSEL_E_PP32_FAST_AES_NODC: // SoCAL: 0x9
case ALT_FPGAMGR_STAT_MSEL_E_PP32_FAST_AESOPT_DC: // SoCAL: 0xa
case ALT_FPGAMGR_STAT_MSEL_E_PP32_SLOW_NOAES_NODC: // SoCAL: 0xc
case ALT_FPGAMGR_STAT_MSEL_E_PP32_SLOW_AES_NODC: // SoCAL: 0xd
case ALT_FPGAMGR_STAT_MSEL_E_PP32_SLOW_AESOPT_DC: // SoCAL: 0xe
// The definitions for the various msel's match up with the hardware
// definitions, so just cast it to the enum type.
return (ALT_FPGA_CFG_MODE_t) msel;
default:
return ALT_FPGA_CFG_MODE_UNKNOWN;
}
}
ALT_STATUS_CODE alt_fpga_cfg_mode_set(ALT_FPGA_CFG_MODE_t cfg_mode)
{
// This function will always return ERROR. See header for reasons.
return ALT_E_ERROR;
}
//
// This function handles writing data to the FPGA data register and ensuring
// the image was programmed correctly.
//
static ALT_STATUS_CODE alt_fpga_internal_configure_idata(FPGA_DATA_t * fpga_data)
{
ALT_STATUS_CODE status = ALT_E_SUCCESS;
// Step 9:
// - Write configuration image to AXI DATA register in 4 byte chunks.
dprintf("FPGA: === Step 9 ===\n");
// This is the largest configuration image possible for the largest Arria 5
// SoC device with some generous padding added. Given that the Arria 5 SoC
// is larger than the Cyclone 5 SoC, this value will also be sufficient for
// the Cyclone 5 SoC device.
// From the A5 datasheet, it is 186 Mb => ~ 23 MiB. Thus cap the max
// configuration data size to 32 MiB. Anything larger will cause an error
// to be reported to the user. This will also terminate the IStream
// interface should the stream never end.
uint32_t data_limit = 32 * 1024 * 1024;
if (fpga_data->type == FPGA_DATA_FULL)
{
if (fpga_data->mode.full.length > data_limit)
{
status = ALT_E_FPGA_CFG;
}
else
{
status = alt_fpga_internal_writeaxi(fpga_data->mode.full.buffer, fpga_data->mode.full.length
#if ALT_FPGA_ENABLE_DMA_SUPPORT
,
fpga_data->use_dma, fpga_data->dma_channel
#endif
);
}
}
else
{
char buffer[ISTREAM_CHUNK_SIZE];
int32_t cb_status = 0; // Callback status
do
{
cb_status = fpga_data->mode.istream.stream(buffer, sizeof(buffer), fpga_data->mode.istream.data);
if (cb_status > sizeof(buffer))
{
// Callback data overflows buffer space.
status = ALT_E_FPGA_CFG_STM;
}
else if (cb_status < 0)
{
// A problem occurred when streaming data from the source.
status = ALT_E_FPGA_CFG_STM;
}
else if (cb_status == 0)
{
// End of IStream data.
break;
}
else if (cb_status > data_limit)
{
// Limit hit for the largest permissible data stream.
status = ALT_E_FPGA_CFG_STM;
}
else
{
// Copy in configuration data.
status = alt_fpga_internal_writeaxi(buffer, cb_status
#if ALT_FPGA_ENABLE_DMA_SUPPORT
,
fpga_data->use_dma, fpga_data->dma_channel
#endif
);
data_limit -= cb_status;
}
if (status != ALT_E_SUCCESS)
{
break;
}
} while (cb_status > 0);
}
if (status != ALT_E_SUCCESS)
{
dprintf("FPGA: Error in step 9: Problem streaming or writing out AXI data.\n");
return status;
}
// Step 10:
// - Observe CONF_DONE and nSTATUS (active low)
// - if CONF_DONE = 1 and nSTATUS = 1, configuration was successful
// - if CONF_DONE = 0 and nSTATUS = 0, configuration failed
dprintf("FPGA: === Step 10 ===\n");
status = wait_for_config_done(_ALT_FPGA_TMO_CONFIG);
if (status != ALT_E_SUCCESS)
{
if (status == ALT_E_FPGA_CRC)
{
dprintf("FPGA: Error in step 10: CRC error detected.\n");
return ALT_E_FPGA_CRC;
}
else if (status == ALT_E_TMO)
{
dprintf("FPGA: Error in step 10: Timeout waiting for CONF_DONE + nSTATUS.\n");
return ALT_E_FPGA_CFG;
}
else
{
dprintf("FPGA: Error in step 10: Configuration error CONF_DONE, nSTATUS = 0.\n");
return ALT_E_FPGA_CFG;
}
}
return ALT_E_SUCCESS;
}
//
// Helper function which does handles the common steps for Full Buffer or
// IStream FPGA configuration.
//
static ALT_STATUS_CODE alt_fpga_internal_configure(FPGA_DATA_t * fpga_data)
{
// Verify preconditions.
// This is a minor difference from the configure instructions given by the NPP.
// Verify that HPS has control of the FPGA control block.
if (alt_fpga_control_is_enabled() != true)
{
return ALT_E_FPGA_NO_SOC_CTRL;
}
// Detect FPGA power status.
if (alt_fpga_state_get() == ALT_FPGA_STATE_POWER_OFF)
{
return ALT_E_FPGA_PWR_OFF;
}
/////
ALT_STATUS_CODE status = ALT_E_SUCCESS;
dprintf("FPGA: Configure() !!!\n");
// The FPGA CTRL register cache
uint32_t ctrl_reg;
ctrl_reg = alt_read_word(ALT_FPGAMGR_CTL_ADDR);
// Step 1:
// - Set CTRL.CFGWDTH, CTRL.CDRATIO to match cfg mode
// - Set CTRL.NCE to 0
dprintf("FPGA: === Step 1 ===\n");
int cfgwidth = 0;
int cdratio = 0;
switch (alt_fpga_cfg_mode_get())
{
case ALT_FPGA_CFG_MODE_PP16_FAST_NOAES_NODC:
cfgwidth = 16;
cdratio = 1;
break;
case ALT_FPGA_CFG_MODE_PP16_FAST_AES_NODC:
cfgwidth = 16;
cdratio = 2;
break;
case ALT_FPGA_CFG_MODE_PP16_FAST_AESOPT_DC:
cfgwidth = 16;
cdratio = 4;
break;
case ALT_FPGA_CFG_MODE_PP16_SLOW_NOAES_NODC:
cfgwidth = 16;
cdratio = 1;
break;
case ALT_FPGA_CFG_MODE_PP16_SLOW_AES_NODC:
cfgwidth = 16;
cdratio = 2;
break;
case ALT_FPGA_CFG_MODE_PP16_SLOW_AESOPT_DC:
cfgwidth = 16;
cdratio = 4;
break;
case ALT_FPGA_CFG_MODE_PP32_FAST_NOAES_NODC:
cfgwidth = 32;
cdratio = 1;
break;
case ALT_FPGA_CFG_MODE_PP32_FAST_AES_NODC:
cfgwidth = 32;
cdratio = 4;
break;
case ALT_FPGA_CFG_MODE_PP32_FAST_AESOPT_DC:
cfgwidth = 32;
cdratio = 8;
break;
case ALT_FPGA_CFG_MODE_PP32_SLOW_NOAES_NODC:
cfgwidth = 32;
cdratio = 1;
break;
case ALT_FPGA_CFG_MODE_PP32_SLOW_AES_NODC:
cfgwidth = 32;
cdratio = 4;
break;
case ALT_FPGA_CFG_MODE_PP32_SLOW_AESOPT_DC:
cfgwidth = 32;
cdratio = 8;
break;
default:
return ALT_E_ERROR;
}
dprintf("FPGA: CDRATIO = %x\n", cdratio);
dprintf("FPGA: CFGWIDTH = %s\n", cfgwidth == 16 ? "16" : "32");
// Adjust CTRL for the CDRATIO
ctrl_reg &= ALT_FPGAMGR_CTL_CDRATIO_CLR_MSK;
switch (cdratio)
{
case 1:
ctrl_reg |= ALT_FPGAMGR_CTL_CDRATIO_SET(ALT_FPGAMGR_CTL_CDRATIO_E_X1);
break;
case 2: // Unused; included for completeness.
ctrl_reg |= ALT_FPGAMGR_CTL_CDRATIO_SET(ALT_FPGAMGR_CTL_CDRATIO_E_X2);
break;
case 4:
ctrl_reg |= ALT_FPGAMGR_CTL_CDRATIO_SET(ALT_FPGAMGR_CTL_CDRATIO_E_X4);
break;
case 8:
ctrl_reg |= ALT_FPGAMGR_CTL_CDRATIO_SET(ALT_FPGAMGR_CTL_CDRATIO_E_X8);
break;
default:
return ALT_E_ERROR;
}
// Adjust CTRL for CFGWIDTH
switch (cfgwidth)
{
case 16:
ctrl_reg &= ALT_FPGAMGR_CTL_CFGWDTH_CLR_MSK;
break;
case 32:
ctrl_reg |= ALT_FPGAMGR_CTL_CFGWDTH_SET_MSK;
break;
default:
return ALT_E_ERROR;
}
// Set NCE to 0.
ctrl_reg &= ALT_FPGAMGR_CTL_NCE_CLR_MSK;
alt_write_word(ALT_FPGAMGR_CTL_ADDR, ctrl_reg);
// Step 2:
// - Set CTRL.EN to 1
dprintf("FPGA: === Step 2 (skipped due to precondition) ===\n");
// Step 3:
// - Set CTRL.NCONFIGPULL to 1 to put FPGA in reset
dprintf("FPGA: === Step 3 ===\n");
ctrl_reg |= ALT_FPGAMGR_CTL_NCFGPULL_SET_MSK;
alt_write_word(ALT_FPGAMGR_CTL_ADDR, ctrl_reg);
// Step 4:
// - Wait for STATUS.MODE to report FPGA is in reset phase
dprintf("FPGA: === Step 4 ===\n");
status = wait_for_fpga_state(ALT_FPGA_STATE_RESET, _ALT_FPGA_TMO_STATE);
// Handle any error conditions after reset has been unasserted.
// Step 5:
// - Set CONTROL.NCONFIGPULL to 0 to release FPGA from reset
dprintf("FPGA: === Step 5 ===\n");
ctrl_reg &= ALT_FPGAMGR_CTL_NCFGPULL_CLR_MSK;
alt_write_word(ALT_FPGAMGR_CTL_ADDR, ctrl_reg);
if (status != ALT_E_SUCCESS)
{
// This is a failure from Step 4.
dprintf("FPGA: Error in step 4: Wait for RESET timeout.\n");
return ALT_E_FPGA_CFG;
}
// Step 6:
// - Wait for STATUS.MODE to report FPGA is in configuration phase
dprintf("FPGA: === Step 6 ===\n");
status = wait_for_fpga_state(ALT_FPGA_STATE_CFG, _ALT_FPGA_TMO_STATE);
if (status != ALT_E_SUCCESS)
{
dprintf("FPGA: Error in step 6: Wait for CFG timeout.\n");
return ALT_E_FPGA_CFG;
}
// Step 7:
// - Clear nSTATUS interrupt in CB Monitor
dprintf("FPGA: === Step 7 ===\n");
alt_write_word(ALT_FPGAMGR_MON_GPIO_PORTA_EOI_ADDR,
ALT_MON_GPIO_PORTA_EOI_NS_SET(ALT_MON_GPIO_PORTA_EOI_NS_E_CLR));
// Step 8:
// - Set CTRL.AXICFGEN to 1 to enable config data on AXI slave bus
dprintf("FPGA: === Step 8 ===\n");
ctrl_reg |= ALT_FPGAMGR_CTL_AXICFGEN_SET_MSK;
alt_write_word(ALT_FPGAMGR_CTL_ADDR, ctrl_reg);
/////
//
// Helper function to handle steps 9 - 10.
//
ALT_STATUS_CODE data_status;
data_status = alt_fpga_internal_configure_idata(fpga_data);
/////
// Step 11:
// - Set CTRL.AXICFGEN to 0 to disable config data on AXI slave bus
dprintf("FPGA: === Step 11 ===\n");
ctrl_reg &= ALT_FPGAMGR_CTL_AXICFGEN_CLR_MSK;
alt_write_word(ALT_FPGAMGR_CTL_ADDR, ctrl_reg);
// Step 12:
// - Write 4 to DCLKCNT
// - Wait for STATUS.DCNTDONE = 1
// - Clear W1C bit in STATUS.DCNTDONE
dprintf("FPGA: === Step 12 ===\n");
status = dclk_set_and_wait_clear(4, _ALT_FPGA_TMO_DCLK_CONST + 4 * _ALT_FPGA_TMO_DCLK_MUL);
if (status != ALT_E_SUCCESS)
{
dprintf("FPGA: Error in step 12: Wait for dclk(4) timeout.\n");
// The error here is probably a result of an error in the FPGA data.
if (data_status != ALT_E_SUCCESS)
{
return data_status;
}
else
{
return ALT_E_FPGA_CFG;
}
}
#if _ALT_FPGA_USE_DCLK
// Extra steps for Configuration with DCLK for Initialization Phase (4.2.1.2)
// Step 14 (using 4.2.1.2 steps), 15 (using 4.2.1.2 steps)
// - Write 0x5000 to DCLKCNT
// - Poll until STATUS.DCNTDONE = 1, write 1 to clear
dprintf("FPGA: === Step 14 (4.2.1.2) ===\n");
dprintf("FPGA: === Step 15 (4.2.1.2) ===\n");
status = dclk_set_and_wait_clear(0x5000, _ALT_FPGA_TMO_DCLK_CONST + 0x5000 * _ALT_FPGA_TMO_DCLK_MUL);
if (status == ALT_E_TMO)
{
dprintf("FPGA: Error in step 15 (4.2.1.2): Wait for dclk(0x5000) timeout.\n");
// The error here is probably a result of an error in the FPGA data.
if (data_status != ALT_E_SUCCESS)
{
return data_status;
}
else
{
return ALT_E_FPGA_CFG;
}
}
#endif
// Step 13:
// - Wait for STATUS.MODE to report USER MODE
dprintf("FPGA: === Step 13 ===\n");
status = wait_for_fpga_state(ALT_FPGA_STATE_USER_MODE, _ALT_FPGA_TMO_STATE);
if (status == ALT_E_TMO)
{
dprintf("FPGA: Error in step 13: Wait for state = USER_MODE timeout.\n");
// The error here is probably a result of an error in the FPGA data.
if (data_status != ALT_E_SUCCESS)
{
return data_status;
}
else
{
return ALT_E_FPGA_CFG;
}
}
// Step 14:
// - Set CTRL.EN to 0
dprintf("FPGA: === Step 14 (skipped due to precondition) ===\n");
// Return data_status. status is used for the setup of the FPGA parameters
// and should be successful. Any errors in programming the FPGA is
// returned in data_status.
return data_status;
}
ALT_STATUS_CODE alt_fpga_configure(const void* cfg_buf,
size_t cfg_buf_len)
{
FPGA_DATA_t fpga_data;
fpga_data.type = FPGA_DATA_FULL;
fpga_data.mode.full.buffer = cfg_buf;
fpga_data.mode.full.length = cfg_buf_len;
#if ALT_FPGA_ENABLE_DMA_SUPPORT
fpga_data.use_dma = false;
#endif
return alt_fpga_internal_configure(&fpga_data);
}
#if ALT_FPGA_ENABLE_DMA_SUPPORT
ALT_STATUS_CODE alt_fpga_configure_dma(const void* cfg_buf,
size_t cfg_buf_len,
ALT_DMA_CHANNEL_t dma_channel)
{
FPGA_DATA_t fpga_data;
fpga_data.type = FPGA_DATA_FULL;
fpga_data.mode.full.buffer = cfg_buf;
fpga_data.mode.full.length = cfg_buf_len;
fpga_data.use_dma = true;
fpga_data.dma_channel = dma_channel;
return alt_fpga_internal_configure(&fpga_data);
}
#endif
ALT_STATUS_CODE alt_fpga_istream_configure(alt_fpga_istream_t cfg_stream,
void * user_data)
{
FPGA_DATA_t fpga_data;
fpga_data.type = FPGA_DATA_ISTREAM;
fpga_data.mode.istream.stream = cfg_stream;
fpga_data.mode.istream.data = user_data;
#if ALT_FPGA_ENABLE_DMA_SUPPORT
fpga_data.use_dma = false;
#endif
return alt_fpga_internal_configure(&fpga_data);
}
#if ALT_FPGA_ENABLE_DMA_SUPPORT
ALT_STATUS_CODE alt_fpga_istream_configure_dma(alt_fpga_istream_t cfg_stream,
void * user_data,
ALT_DMA_CHANNEL_t dma_channel)
{
FPGA_DATA_t fpga_data;
fpga_data.type = FPGA_DATA_ISTREAM;
fpga_data.mode.istream.stream = cfg_stream;
fpga_data.mode.istream.data = user_data;
fpga_data.use_dma = true;
fpga_data.dma_channel = dma_channel;
return alt_fpga_internal_configure(&fpga_data);
}
#endif
uint32_t alt_fpga_gpi_read(uint32_t mask)
{
if (mask == 0)
{
return 0;
}
return alt_read_word(ALT_FPGAMGR_GPI_ADDR) & mask;
}
ALT_STATUS_CODE alt_fpga_gpo_write(uint32_t mask, uint32_t value)
{
if (mask == 0)
{
return ALT_E_SUCCESS;
}
alt_write_word(ALT_FPGAMGR_GPO_ADDR, (alt_read_word(ALT_FPGAMGR_GPO_ADDR) & ~mask) | (value & mask));
return ALT_E_SUCCESS;
}
/////
ALT_STATUS_CODE alt_fpga_man_irq_disable(ALT_FPGA_MON_STATUS_t mon_stat_mask)
{
// Ensure only bits 11:0 are set.
if (mon_stat_mask & ~((1 << 12) - 1))
{
return ALT_E_BAD_ARG;
}
uint32_t current = alt_read_word(ALT_FPGAMGR_MON_GPIO_INTEN_ADDR);
current &= ~((uint32_t)mon_stat_mask) & ((1 << 12) - 1);
alt_write_word(ALT_FPGAMGR_MON_GPIO_INTEN_ADDR, current);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_fpga_man_irq_enable(ALT_FPGA_MON_STATUS_t mon_stat_mask)
{
// Ensure only bits 11:0 are set.
if (mon_stat_mask & ~((1 << 12) - 1))
{
return ALT_E_BAD_ARG;
}
alt_setbits_word(ALT_FPGAMGR_MON_GPIO_INTEN_ADDR, mon_stat_mask);
return ALT_E_SUCCESS;
}