FPGA有多种不通的加载模式,模式由FPGA_M0、FPGA_M1、FPGA_M2三个引脚决定
通过原理图可以看到当前单板使用的模式是M[2:0]=111,即为从串模式
模式: slave serial (从串模式)
使用从串模式加载fpga用到的管脚如下:
Signal Name | Direction | Description |
---|---|---|
CCLK | Input | Configuration clock. |
PROGRAM_B | Input | Active-Low reset to configuration logic. |
INIT_B | Input/Output | Active-Low FPGA initialization pin. Indicates when the device is ready to receive configuration data. Also indicates any configuration errors. Can be held Low externally to delay configuration. |
DONE | Input/Output | Indicates configuration is complete. Can be held Low externally to delay start-up. |
M[2:0] | Input | Configuration mode selection. |
D01_DIN | Input | Serial configuration data input. |
DOUT | Output | Data output for serial daisy chains. |
具体对应到CPU的GPIO:
FPGA_PROG_B GPIO0_C5
FPGA_INIT_B GPIO0_B7
FPGA_DONE GPIO0_C4
FPGA_CONFIG_DIN GPIO0_C0
FPGA_CONFIG_CLK GPIO0_C6
从手册中可以看到FPGA基本配置流程如下图:
手册中给到的伪代码如下:
/* Global defines
* Define the addresses for the I/O peripherals used to control and
* monitor the target FPGA. Also define the location in memory the
* bitstream is stored and its size. These are system dependent and
* should be adjusted as needed
*/
/* Output GPIO addresses */
CCLK_GPIO_BASEADDR = 0x40020000
PROGRAM_B_GPIO_BASEADDR = 0x40030000
SERIAL_OUT_GPIO_BASEADDR = 0x40040000
/* Input GPIO addresses */
INIT_B_GPIO_BASEADDR = 0x40050000
DONE_GPIO_BASEADDR = 0x40060000
/* Location in memory and size of the target bitstream */
MEMORY_BASEADDR = 0xC0000000
BITSTREAM_START_ADDR = MEMORY_BASEADDR + 0x2000000
BITSTREAM_SIZE_BYTES = 0xAEA68C
/* PROGRAM_B pulse width. Check the target FPGA data sheet for the
* TPROGRAM pulse width. One microsecond is safe for any 7 series FPGA
*/
TPROGRAM = 1 /* Assumes sleep() is microseconds */
/* Serialize a 32-bit word and clock each bit on the target's DIN and
* CCLK pins */
shift_word_out(data32)
{
*cclk = CCLK_GPIO_BASEADDR
*serial_out = SERIAL_OUT_GPIO_BASEADDR
*cclk = 0
*serial_out = 0
for (i = 31; i >= 0; --i){
*serial_out = (data32 & 1 << i) ? 1 : 0
shift_cclk(1)
}
}
/* Assert and Deassert CCLK */
shift_cclk(count)
{
*cclk = CCLK_GPIO_BASEADDR
*cclk = 0
for (i = 0; i < count; --i) {
*cclk = 1
*cclk = 0
}
}
int main()
{
bits_start = BITSTREAM_START_ADDR
bits_size = BITSTREAM_SIZE_BYTES
*program_b = PROGRAM_B_GPIO_BASEADDR
*init_b = INIT_B_GPIO_BASEADDR
*done = DONE_GPIO_BASEADDR
/* Configuration Reset */
*program_b = 0
sleep(TPROGRAM)
*program_b = 1
/* Wait for Device Initialization */
while(*init_b == 0);
/* Configuration (Bitstream) Load */
for (i = 0; i < bits_size; i+=4) {
shift_word_out(bits_start + i)
}
/* Check INIT_B */
if (*init_b_pointer == 0) {
return 1
}
/* Wait for DONE to assert */
while(*done == 0)
;
/* Compensate for Special Startup Conditions */
shift_cclk(8)
return 0
}
参考手册,使用gpio加载
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/gpio.h>
#include <assert.h>
#include "fpga.h"
typedef unsigned int uint32_t;
/* PROGRAM_B pulse width. Check the target FPGA data sheet for the
* TPROGRAM pulse width. One microsecond is safe for any 7 series FPGA
*/
#define TPROGRAM 1 /* Assumes sleep() is microseconds */
#define SWAP_BYTES 1
#define FPGABUFSIZE 4
#define FPGA_INIT_B 15
#define FPGA_PROG_B 21
#define FPGA_DONE 20
#define FPGA_CFG_CLK 22
#define FPGA_CFG_DIN 16
#define FPGA_PATH_NAME "/userdata/fpga.bin"
int g_fdclk, g_fddin;
static const char values_str[] = "01";
/* Assert and Deassert CCLK */
void shift_cclk(uint32_t count)
{
int i;
for (i = 0; i < count; ++i) {
write(g_fdclk, &values_str[1], 1);
write(g_fdclk, &values_str[0], 1);
}
}
/* Serialize a 32-bit word and clock each bit on the target's DIN and
* CCLK pins */
void shift_word_out(uint32_t data32)
{
int i;
for (i = 31; i >= 0; --i){
write(g_fddin, &values_str[(data32 & 1 << i) ? 1 : 0], 1);
shift_cclk(1);
}
}
uint32_t swap_uint32(uint32_t data){
uint32_t swapped;
if (SWAP_BYTES == 1)
swapped = ((data << 24) & 0xFF000000) |
((data << 8) & 0x00FF0000) |
((data >> 8) & 0x0000FF00) |
((data >> 24) & 0x000000FF);
else
swapped = data;
//printf("0x%08X: ", swapped );
return swapped;
}
static void progress(unsigned long count, unsigned long total)
{
unsigned long percent;
percent = count * 100;
if (total)
percent = (unsigned) (percent / total);
if((0 == count%100000) || (100 == percent))
{
printf("\r FpgaLoad: %lu/%lu (%u%%) ", count, total, (unsigned)percent);
}
fflush(NULL);
}
int bsp_fpga_bit_write(void)
{
int fd_f;
char *filename;
struct stat statb;
unsigned long done;
unsigned long rem;
unsigned long count;
unsigned char buffer[FPGABUFSIZE] = {0};
//static const char values_str[] = "01";
char pathclk[64], pathdin[64];
filename = FPGA_PATH_NAME;
fd_f = open(filename, O_RDONLY);
if (fd_f < 0)
{
printf("%s , file not found.\n", FPGA_PATH_NAME);
return -1;
}
fstat(fd_f, &statb);
printf("Fpga size is %d.\n", statb.st_size);
snprintf(pathclk, sizeof(pathclk), "/sys/class/gpio/gpio%d/value", FPGA_CFG_CLK);
snprintf(pathdin, sizeof(pathdin), "/sys/class/gpio/gpio%d/value", FPGA_CFG_DIN);
g_fdclk = open(pathclk, O_WRONLY);
if (g_fdclk < 0) {
printf("Failed to open gpio FPGA_CFG_CLK for writing!\n");
return -1;
}
g_fddin = open(pathdin, O_WRONLY);
if (g_fddin < 0) {
printf("Failed to open gpio FPGA_CFG_DIN for writing!\n");
return -1;
}
done = 0;
count = FPGABUFSIZE;
if (write(g_fdclk, &values_str[0], 1) < 0) {
printf("Failed to write value!\n");
close(g_fdclk);
return -1;
}
while(1)
{
rem = statb.st_size - done;
if (rem == 0)
break;
if (rem < FPGABUFSIZE)
count = rem;
read(fd_f, buffer, count);
if (count < FPGABUFSIZE)
memset((char*)buffer + count, 0, FPGABUFSIZE - count);
shift_word_out( swap_uint32( *(uint32_t *)(buffer) ));
if (gpio_read(FPGA_INIT_B) == 0) {
printf("INIT_B error\n");
close(g_fdclk);
close(g_fddin);
return -1;
}
progress(done, statb.st_size);
done += count;
}
printf("FPGA write finished.\n");
close(g_fdclk);
close(g_fddin);
close(fd_f);
return 0;
}
int bsp_fpga_serial_load(void)
{
uint32_t i = 0;
gpio_export(FPGA_INIT_B);
gpio_export(FPGA_PROG_B);
gpio_export(FPGA_DONE);
gpio_export(FPGA_CFG_CLK);
gpio_export(FPGA_CFG_DIN);
gpio_direction(FPGA_PROG_B,1);
gpio_direction(FPGA_CFG_CLK,1);
gpio_direction(FPGA_CFG_DIN,1);
/* Configuration Reset */
gpio_write(FPGA_PROG_B, 0);
sleep(TPROGRAM);
gpio_write(FPGA_PROG_B, 1);
/* Wait for Device Initialization */
while(gpio_read(FPGA_INIT_B) == 0)
{
++i;
if (i > 0x00010000) {
printf("waiting for INIT_B go high out of time.\n");
return 1;
}
}
printf("Downloading Bitstream to target FPGA.\n");
/* Configuration (Bitstream) Load */
if (bsp_fpga_bit_write() != 0)
{
printf("fpga bit write error.\n");
return 1;
}
/* Check INIT_B */
if (gpio_read(FPGA_INIT_B) == 0) {
printf("INIT_B error\n");
return 1;
}
/* Wait for DONE to assert */
i = 0;
while(gpio_read(FPGA_DONE) == 0)
{
shift_cclk(1);
++i;
if (i > 0x00001000 ) {
printf("DONE has not go high.\n");
return 1;
}
}
printf("FPGA Load successed!\n");
/* Compensate for Special Startup Conditions */
shift_cclk(8);
return 0;
}
使用gpio方式加载需要代码中shift时钟,效率较低,实测整个加载流程耗时8min,严重超时
预留管脚足以支撑spi的单向传输,改为使用spi加载
spi加载fpga代码
int fpga_cfg_init(void)
{
int fd, ret = 0;
char *device = "/dev/spidev0.0";
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
ret = ioctl(fd, SPI_IOC_WR_MODE32, &fmode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &fmode);
if (ret == -1)
pabort("can't get spi mode");
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &fbits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &fbits);
if (ret == -1)
pabort("can't get bits per word");
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &fspeed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &fspeed);
if (ret == -1)
pabort("can't get max speed hz");
return fd;
}
static void progress(unsigned long count, unsigned long total)
{
unsigned long percent;
unsigned long printsize = 1024*1024;
percent = count * 100;
if (total)
percent = (unsigned) (percent / total);
if((0 == count%printsize) || (100 == percent)){
printf("\r FpgaLoad: %lu/%lu (%u%%) ", count, total, (unsigned)percent);
}
fflush(NULL);
}
#define FPGA_BUFFER_SIZE 32*1024
void transfer_file(int fd, char *filename)
{
ssize_t bytes;
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t *rx;
unsigned long done;
unsigned long rem;
unsigned long count;
if (stat(filename, &sb) == -1)
pabort("can't stat input file");
tx_fd = open(filename, O_RDONLY);
if (tx_fd < 0)
pabort("can't open input file");
tx = malloc(FPGA_BUFFER_SIZE);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(FPGA_BUFFER_SIZE);
if (!rx)
pabort("can't allocate rx buffer");
done = 0;
count = FPGA_BUFFER_SIZE;
while(1)
{
rem = sb.st_size - done;
if (rem == 0)
break;
if (rem < FPGA_BUFFER_SIZE)
count = rem;
bytes = read(tx_fd, tx, count);
if (bytes != count)
pabort("failed to read input file");
if (count < FPGA_BUFFER_SIZE)
memset((char*)tx + count, 0, FPGA_BUFFER_SIZE - count);
transfers(fd, tx, rx, count);
progress(done, sb.st_size);
done += count;
}
free(rx);
free(tx);
close(tx_fd);
}
整个加载过程能在10s内完成,满足要求。