作者:zzssdd2
E-mail:zzssdd2@foxmail.com
1、需求描述
FPGA
內部是SRAM
儲存結構,掉電后程序就會丟失,故需要將FPGA程序保存在掉電不丟失的儲存介質中(比如FLASH、EMMC、SD卡等),在每次上電時讀取程序進行配置。
2、功能分析
項目中使用的FPGA型號是Altera
公司(現屬於Intel
)的Cyclone
系列。在Altera的文檔《Cyclone Device Handbook,Volume1》的第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;
}
}