/****************************************************************************** | |
* | |
* 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, ¤t); | |
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; | |
} |