作者: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的几种配置方式。
FPGA'的三种配置模式
模式 | 描述 |
---|---|
AS(Active serial)模式 | FPGA主动配置。该模式由FPGA主动从外部储存器读取配置数据 |
PS(Passive serial)模式 | FPGA被动控制。该模式由外部控制器对FPGA进行配置 |
JTAG模式 | 通过外部下载器下载到FPGA内部SRAM中 |
FPGA选择配置模式
通过MSEL0
和MSEL1
引脚不同的电平来选择配置方式(如果使用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引脚回应一个高电平表示配置完成
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;
}
}