嵌入式开发笔记——MCU配置Altera-Cyclone系列FPGA(PS)

作者:zzssdd2

E-mail:zzssdd2@foxmail.com

1、需求描述

FPGA内部是SRAM储存结构,掉电后程序就会丢失,故需要将FPGA程序保存在掉电不丢失的储存介质中(比如FLASH、EMMC、SD卡等),在每次上电时读取程序进行配置。

2、功能分析

项目中使用的FPGA型号是Altera公司(现属于Intel)的Cyclone系列。在Altera的文档[Cyclone Device Handbook, Volume 1]:https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/hb/cyc/cyc_c5v1.pdf 的第13章节讲述了该系列FPGA的几种配置方式。

嵌入式开发笔记——MCU配置Altera-Cyclone系列FPGA(PS)

FPGA'的三种配置模式

模式 描述
AS(Active serial)模式 FPGA主动配置。该模式由FPGA主动从外部储存器读取配置数据
PS(Passive serial)模式 FPGA被动控制。该模式由外部控制器对FPGA进行配置
JTAG模式 通过外部下载器下载到FPGA内部SRAM中

FPGA选择配置模式

通过MSEL0MSEL1引脚不同的电平来选择配置方式(如果使用JTAG配置则可以忽略这些引脚配置)

MSEL1 MSEL0 模式
0 0 AS
0 1 PS
x x JTAG

最终确定的方案是使用PS模式通过MCU来升级、配置FPGA。下面主要讲使用MCU对FPGA进行PS模式下的配置过程。

PS模式配置引脚时序

  • 发起配置请求
    • nCONFIG引脚拉低tCFG时间然后拉高,等待nSTATU拉低响应请求
  • 进行配置
    • FPGA在DCLK引脚的上升沿采集DATA引脚Bit数据,LSB在前传输方式
  • 配置完成
    • 等待CONF_DONE引脚回应一个高电平表示配置完成
      嵌入式开发笔记——MCU配置Altera-Cyclone系列FPGA(PS)

PS配置模式时序参数

嵌入式开发笔记——MCU配置Altera-Cyclone系列FPGA(PS)

3、功能实现

配置FPGA用到的变量和标志

static uint8_t fpga_cfg_buf[W25Q_SECTOR_SIZE]; //储存从FLASH读出数据
static __IO uint8_t fpga_cfg_sta = 0x00;       //记录配置状态
//配置过程用到的标识
enum
{
    FPGA_CFG_ENABLE	= 0x01,	
    FPGA_CFG_START	= 0x02,	
    FPGA_CFG_DONE	= 0x04,
    FPGA_CFG_OVER	= 0x08,
};

MCU与FPGA连接引脚配置

/*
**********************************************************************
*    函 数: fpga_config_init
*    功 能: 配置FPGA引脚
*    输 入: 无
*    输 出: 无
**********************************************************************
*/
void fpga_config_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /* 引脚时钟使能 */
    FPGA_PIN_CLK_ENABLE();
    /* nCFG、DAT、CLK配置为输出 */
    GPIO_InitStruct.Pin 	= FPGA_nCFG_PIN;
    GPIO_InitStruct.Mode 	= GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull 	= GPIO_NOPULL;
    GPIO_InitStruct.Speed 	= GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(FPGA_nCFG_PORT, &GPIO_InitStruct);
    GPIO_InitStruct.Pin 	= FPGA_DAT_PIN;
    HAL_GPIO_Init(FPGA_DAT_PORT, &GPIO_InitStruct);
    GPIO_InitStruct.Pin 	= FPGA_CLK_PIN;
    HAL_GPIO_Init(FPGA_CLK_PORT, &GPIO_InitStruct);
    /* nSTA、CFG_DONE配置为输入 */
    GPIO_InitStruct.Pin 	= FPGA_nSTA_PIN;
    GPIO_InitStruct.Mode 	= GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull 	= GPIO_NOPULL;
    HAL_GPIO_Init(FPGA_nSTA_PORT, &GPIO_InitStruct);
    GPIO_InitStruct.Pin 	= FPGA_CFG_DONE_PIN;
    HAL_GPIO_Init(FPGA_CFG_DONE_PORT, &GPIO_InitStruct);
    /* 配置引脚默认状态 */
    HAL_GPIO_WritePin(FPGA_nCFG_PORT, FPGA_nCFG_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(FPGA_DAT_PORT, FPGA_DAT_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(FPGA_CLK_PORT, FPGA_CLK_PIN, GPIO_PIN_RESET);
}

MCU对FPGA配置过程

/*
**********************************************************************
*    函 数: fpga_config_process
*    功 能: FPGA程序配置
*    输 入: _uiDataSize:FPGA配置文件大小
*          _uiStartAddr:FLASH储存FPGA配置文件地址
*    输 出: 失败:< 0; 成功:0
**********************************************************************
*/
int fpga_config_process(uint32_t _uiDataSize, uint32_t _uiStartAddr)
{
    UINT interrupt_save;
    uint16_t i, j;
    uint32_t uiTout, uiRdAddr, uiCnt = 0;
	
    fpga_cfg_sta = 0;
    uiRdAddr = _uiStartAddr; 
    
    /*############## 第一阶段:发起配置请求 ########################*/
    FPGA_PinWrite(FPGA_nCFG_PORT,FPGA_nCFG_PIN,GPIO_PIN_RESET);
    dwt_delay_us(100);
    FPGA_PinWrite(FPGA_nCFG_PORT,FPGA_nCFG_PIN,GPIO_PIN_SET);
    dwt_delay_us(40);
    /* 等待FPGA回应:100ms超时 */
    for (uiTout = 0; uiTout < 10000; uiTout++)
    {
        if (GPIO_PIN_RESET == FPGA_PinRead(FPGA_nSTA_PORT,FPGA_nSTA_PIN))
        {
            SET_BIT(fpga_cfg_sta, FPGA_CFG_START);
            break;
        }
        dwt_delay_us(10);
    }
    /* 是否响应? */
    if (!READ_BIT(fpga_cfg_sta, FPGA_CFG_START))
    {
        return -1;
    }
    
    /*############## 第二阶段:进行配置 ########################*/
    do{
        W25Q_ReadBuffer(fpga_cfg_buf, uiRdAddr, W25Q_SECTOR_SIZE);
        uiRdAddr += W25Q_SECTOR_SIZE;
        for (i = 0; i < W25Q_SECTOR_SIZE; i++)
        {
            /* 按bit写入,LSB在前 */
            DISABLE_IRQ();
            for (j = 0; j < 8; j++)
            {
                if (fpga_cfg_buf[i] & 0x01) 
                {
                    FPGA_PinWrite(FPGA_DAT_PORT,FPGA_DAT_PIN,GPIO_PIN_SET);
                } 
                else 
                {
                    FPGA_PinWrite(FPGA_DAT_PORT,FPGA_DAT_PIN,GPIO_PIN_RESET);
                }
                FPGA_PinWrite(FPGA_CLK_PORT,FPGA_CLK_PIN,GPIO_PIN_RESET); Delay(2);
                FPGA_PinWrite(FPGA_CLK_PORT,FPGA_CLK_PIN,GPIO_PIN_SET);	  Delay(2);
                FPGA_PinWrite(FPGA_CLK_PORT,FPGA_CLK_PIN,GPIO_PIN_RESET); Delay(2);

                fpga_cfg_buf[i] >>= 1;
            }
            ENABLE_IRQ();

            /* 数据写入完毕退出 */
            if (++uiCnt >= _uiDataSize) 
            {
                SET_BIT(fpga_cfg_sta, FPGA_CFG_OVER);
                break;
            }
        }
    }while(RESET == READ_BIT(fpga_cfg_sta, FPGA_CFG_OVER));
	
    /*############## 第三阶段:等待配置完成回应 ########################*/
    for (i = 0, uiTout = 0; uiTout < 20000; uiTout++)
    {
        dwt_delay_us(100);
        if (GPIO_PIN_SET == FPGA_PinRead(FPGA_CFG_DONE_PORT,FPGA_CFG_DONE_PIN))
        {
            if (++i >= 10)
            {
                SET_BIT(fpga_cfg_sta, FPGA_CFG_DONE);
                break;
            }
        }
        else
        {
            i = 0;
        }
    }

    if (READ_BIT(fpga_cfg_sta, FPGA_CFG_DONE))
    {
        return 0;
    }
    else
    {
        return -1;
    }
}
上一篇:奋斗的小孩系列 FPGA学习altera系列: 第七篇 添加激励及功能仿真操作


下一篇:Altera FPGA芯片的管脚快速锁定的一种方法