1 硬件接线
可以使用PH1,PH3,PH7,PH17,PH19,PH23。
在sys_config.fex中配置如下:
[FPGA_set_para]
fpga_set_enable = 1
fpga_set_nums = 6
fpga_nCONFIG = port:PH1<1><1><default><default>
fpga_DCLK = port:PH3<1><1><default><default>
fpga_DATA = port:PH7<1><1><default><default>
fpga_STATUS = port:PH17<1><1><default><default>
fpga_CONFIG_DONE = port:PH19<1><1><default><default>
fpga_INIT_DONE = port:PH23<1><1><default><default>
2 驱动代码
将驱动代码命名为:fpga_driver.c,内容如下所示:
/**
* copyright wit_yuan 2017-07-24 北京全景声信息科技有限公司
*
* A20驱动fpga,升级fpga的程序
*
*/
#include "linux/init.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/leds.h>
#include <plat/sys_config.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#define FPGA_DRIVER_DEBUG 0
#if FPGA_DRIVER_DEBUG
#define fpga_loader_debug(fmt,...) printk("%s,%d"fmt,__FILE__,__LINE__,##__VA_ARGS__)
#else
#define fpga_loader_debug(fmt,...)
#endif
struct fpga_io_name{
unsigned char *main_key;
unsigned char *sub_key;
unsigned char status;/*input=0,output=1*/
unsigned int handler; /* gpio handler*/
};
enum {
TYPE_FPGA_START=1,
TYPE_FPGA_BUFFER,
TYPE_FPGA_CONFIG_END,
TYPE_FPGA_INIT_END,
}ID_TYPE;
struct fpga_buffer{
int id;/* 1.start 2.data 3.end*/
unsigned char data;
};
static struct fpga_buffer fpga_buffer_t;
static struct fpga_io_name fpga_io_name_t[6]={
{"FPGA_set_para","fpga_nCONFIG",1,0},
{"FPGA_set_para","fpga_DCLK",1,0},
{"FPGA_set_para","fpga_DATA",1,0},
{"FPGA_set_para","fpga_STATUS",0,0},
{"FPGA_set_para","fpga_CONFIG_DONE",0,0},
{"FPGA_set_para","fpga_INIT_DONE",0,0},
};
static script_gpio_set_t info;
static struct class *fpga_loader_class;
static struct device *fpga_loader_device;
static unsigned int fpga_loader_major;
static ssize_t fpga_loader_read (struct file *, char __user *, size_t, loff_t *);
static int fpga_loader_open(struct inode *inode, struct file *filp);
static ssize_t fpga_loader_write (struct file *filp, const char __user *buf, size_t len, loff_t *off);
static int fpga_loader_close(struct inode *inode, struct file *filp);
struct file_operations fpga_loader_operations = {
.owner = THIS_MODULE,
.open = fpga_loader_open,
.read = fpga_loader_read,
.write = fpga_loader_write,
.release = fpga_loader_close,
};
static int fpga_loader_open(struct inode *inode, struct file *filp)
{
int i = 0;
int err = 0;
int fpga_loader_test_enabled = 0;
int ret = 0;
fpga_loader_debug("-----open---\r\n");
#if 1
err = script_parser_fetch("FPGA_set_para", "fpga_set_enable", &fpga_loader_test_enabled,
sizeof(fpga_loader_test_enabled)/sizeof(int));
if(!err){
fpga_loader_debug("---script.bin led get ok,value:%d----\n",fpga_loader_test_enabled);
}
else
{
fpga_loader_debug("---script.bin led get false----\n");
return -1;
}
for(i = 0 ; i < 6 ; i ++){
err = script_parser_fetch(fpga_io_name_t[i].main_key, fpga_io_name_t[i].sub_key,
(int *)&info,
sizeof(script_gpio_set_t));
if (err) {
fpga_loader_debug("----script.bin get io error----\r\n");
return -1;
}
/* reserve gpio for led */
fpga_io_name_t[i].handler = gpio_request_ex(fpga_io_name_t[i].main_key, fpga_io_name_t[i].sub_key);
if (!fpga_io_name_t[i].handler) {
fpga_loader_debug("----script.bin can't requst nCONFIG handler----\r\n");
return -1;
}
gpio_set_one_pin_io_status(fpga_io_name_t[i].handler, fpga_io_name_t[i].status,
fpga_io_name_t[i].sub_key);
gpio_set_one_pin_pull(fpga_io_name_t[i].handler,1/*pull up*/,
fpga_io_name_t[i].sub_key);
}
#endif
return 0;
}
/*
* copyright wit_yuan 2017-07-24 北京全景声信息科技有限公司
*
* 读取数据状态
*
* STATUS
* CONFIG_DONE
* INIT_DONE
*
*/
static ssize_t fpga_loader_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
int ret_val = 0;
switch(fpga_buffer_t.id){
case TYPE_FPGA_START:
/*status*/
ret_val = -1;
ret_val = gpio_read_one_pin_value(fpga_io_name_t[3].handler,fpga_io_name_t[3].sub_key);
if(ret_val < 0){
fpga_loader_debug("---read status value failed---\r\n");
}
//printk("---len:%d---\r\n",len);
copy_to_user(buf,&ret_val,len);
break;
case TYPE_FPGA_BUFFER:
/*status*/
ret_val = -1;
ret_val = gpio_read_one_pin_value(fpga_io_name_t[3].handler,fpga_io_name_t[3].sub_key);
if(ret_val < 0){
fpga_loader_debug("---read status value failed---\r\n");
}
copy_to_user(buf,&ret_val,len);
break;
case TYPE_FPGA_CONFIG_END:
/*CONFIG_DONE*/
ret_val = gpio_read_one_pin_value(fpga_io_name_t[4].handler,fpga_io_name_t[4].sub_key);
if(ret_val < 0){
fpga_loader_debug("---read CONFIG_DONE value failed---\r\n");
}
copy_to_user(buf,&ret_val,len);
break;
case TYPE_FPGA_INIT_END:
/*INIT_DONE*/
ret_val = gpio_read_one_pin_value(fpga_io_name_t[5].handler,fpga_io_name_t[5].sub_key);
if(ret_val < 0){
fpga_loader_debug("---read INIT_DONE value failed---\r\n");
}
copy_to_user(buf,&ret_val,len);
break;
}
return ret_val;
}
static ssize_t fpga_loader_write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
int val;
int i = 0;
int i_ret = 1;
unsigned char data;
//struct fpga_buffer fpga_buffer_t ;
copy_from_user(&fpga_buffer_t,buf,len);
switch(fpga_buffer_t.id){
case TYPE_FPGA_START:
fpga_loader_debug("----start message----\r\n");
udelay(10);
/*nconfig=1,dclk=0,data=0*/
/*config*/
gpio_write_one_pin_value(fpga_io_name_t[0].handler,
1, fpga_io_name_t[0].sub_key);
/*dclk*/
gpio_write_one_pin_value(fpga_io_name_t[1].handler,
0, fpga_io_name_t[1].sub_key);
/*data*/
gpio_write_one_pin_value(fpga_io_name_t[2].handler,
0, fpga_io_name_t[2].sub_key);
udelay(10);
/*nCONFIG=0,DCLK=0*/
gpio_write_one_pin_value(fpga_io_name_t[0].handler,
0, fpga_io_name_t[0].sub_key);
gpio_write_one_pin_value(fpga_io_name_t[1].handler,
0, fpga_io_name_t[1].sub_key);
udelay(2);
/*nCONFIG=1*/
gpio_write_one_pin_value(fpga_io_name_t[0].handler,
1, fpga_io_name_t[0].sub_key);
udelay(2);
break;
case TYPE_FPGA_BUFFER:
//printk("----buffer message----\r\n");
data = fpga_buffer_t.data;
for(i = 0 ; i < 8 ; i ++){
if(data & (1<<i)){
/*data*/
gpio_write_one_pin_value(fpga_io_name_t[2].handler,
1, fpga_io_name_t[2].sub_key);
}
else
{
/*data*/
gpio_write_one_pin_value(fpga_io_name_t[2].handler,
0, fpga_io_name_t[2].sub_key);
}
udelay(1);
/*dclk=1*/
gpio_write_one_pin_value(fpga_io_name_t[1].handler,
1, fpga_io_name_t[1].sub_key);
udelay(1);
/*dclk=0*/
gpio_write_one_pin_value(fpga_io_name_t[1].handler,
0, fpga_io_name_t[1].sub_key);
udelay(1);
}
break;
case TYPE_FPGA_CONFIG_END:
/*CONFIG_DONE*/
break;
case TYPE_FPGA_INIT_END:
/*INIT_DONE*/
break;
default:
fpga_loader_debug("----default message----\r\n");
break;
}
return 0;
}
static int fpga_loader_close(struct inode *inode, struct file *filp)
{
fpga_loader_debug("----fpga_loader close----\r\n");
#if 0
int i = 0;
//if (gpio_handler)
// gpio_release(gpio_handler, 1);
for(i = 0 ; i < 6 ; i ++){
if(fpga_io_name_t[i].handler){
gpio_release(fpga_io_name_t[i].handler, 1);
}
}
#endif
return 0;
}
static int __init fpga_loader_init(void)
{
fpga_loader_major = register_chrdev(0, "fpga_loader_chrdev", &fpga_loader_operations);
fpga_loader_class = class_create(THIS_MODULE, "fpga_loader_class");
if(!fpga_loader_class){
unregister_chrdev(fpga_loader_major, "fpga_loader_chrdev");
fpga_loader_debug("----leds_chrdev error----\r\n");
return -1;
}
fpga_loader_device = device_create(fpga_loader_class, NULL, MKDEV(fpga_loader_major,0),
NULL, "fpga_loader_device");
if(!fpga_loader_device){
class_destroy(fpga_loader_class);
unregister_chrdev(fpga_loader_major, "fpga_loader_chrdev");
fpga_loader_debug("----fpga_loader_device error----\r\n");
return -1;
}
fpga_loader_debug("----fpga_loader init ok----\r\n");
return 0;
}
static void __exit fpga_loader_exit(void)
{
device_destroy(fpga_loader_class, MKDEV(fpga_loader_major, 0));
class_destroy(fpga_loader_class);
unregister_chrdev(fpga_loader_major, "fpga_loader_chrdev");
fpga_loader_debug("---driver exit---\r\n");
}
module_init(fpga_loader_init);
module_exit(fpga_loader_exit);
MODULE_DESCRIPTION("Driver for fpga loader 2017-07-24");
MODULE_AUTHOR("wit_yuan");
MODULE_LICENSE("GPL");
最终,将代码放到内核目录linux-sunxi/drivers/char下。并且修改Kconfig和Makefile内容:
Kconfig内容如下所示:
config FPGA_LOADR
tristate "(wit_yuan add) loader for fpga slave boot"
depends on ARCH_SUN7I
default y
help
it is a driver for loading code for fpga.it is authorized by wit_yuan 2017-09-25 at QJ
Makefile内容如下:
obj-$(CONFIG_FPGA_LOADR) += fpga_driver.o
最终可以看到设备:
$ ls /dev/fpga_loader_device
3 驱动测试
针对模块驱动程序,相应的Makefile如下所示:
ifeq ($(KERNELRELEASE),)
KERNEL_DIR=/home/wityuan/Downloads/MarsBoard-A20-Linux-SDK-V1.1/linux-sunxi
PWD=$(shell pwd)
modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
arm-linux-gnueabihf-gcc -o fpga_driver fpga_driver.c
modules_install:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
clean:
rm -rf *.ko *.o .tmp_versions *.mod.c modules.order Module.symvers .*.cmd
else
obj-m:=fpga_driver.o
endif
由于我已经将驱动程序放到内核中,所以上面的Makefile实际上用不着。
测试程序,命名为fpga_test.c,内容如下所示:
#include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#define FPGA_DRIVER_DEBUG 0
#if FPGA_DRIVER_DEBUG
#define fpga_loader_debug(fmt,...) printf("%s,%d"fmt,__FILE__,__LINE__,##__VA_ARGS__)
#else
#define fpga_loader_debug(fmt,...)
#endif
#define LOADER_FILE "output_file.rbf"
#define BUFFER_LOADER 1
enum {
TYPE_FPGA_START=1,
TYPE_FPGA_BUFFER,
TYPE_FPGA_CONFIG_END,
TYPE_FPGA_INIT_END,
}ID_TYPE;
struct fpga_buffer{
int id;/* 1.start 2.data 3.end*/
unsigned char data;
};
static struct fpga_buffer fpga_buffer_t;
int main(int argc,char *argv[])
{
int fd;
int val;
int file_fd;
int init_done_times = 0;
int error_times = 0;
int wait_status_error_times = 0;
unsigned char *fileMapP;
struct stat file_stat = {0};
int init_done_flag = 0;
int i = 0;
struct timeval tv1;
struct timeval tv2;
gettimeofday(&tv1, NULL);
file_fd = open(LOADER_FILE,O_RDONLY);
if(file_fd < 0){
printf("fpga loader file error,please make sure it exists\r\n");
return -1;
}
memset(&file_stat, 0, sizeof(struct stat));
if (fstat(file_fd, &file_stat) < 0) {
printf("--file stat Error----\r\n");
close(file_fd);
return -1;
}
fpga_loader_debug("--file len:%d bytes----\r\n",(int)file_stat.st_size);
if(file_stat.st_size == 0){
printf("---file size error,please check it---\r\n");
close(file_fd);
return -1;
}
//Memory Map
if ((fileMapP=mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, file_fd, 0)) == MAP_FAILED) {
fpga_loader_debug("Mmap Error\r\n");
close(file_fd);
return -1;
}
fd = open("/dev/fpga_loader_device",O_RDWR);
if(fd < 0){
fpga_loader_debug("---open file error----\r\n");
close(file_fd);
return -1;
}
label1:
/*we only try 3 times to bootloade the fpga or will warn an error. 2017-07-25 wit_yuan*/
while(error_times < 3){
/*1.send start message*/
fpga_buffer_t.id = TYPE_FPGA_START;
write(fd,&fpga_buffer_t,sizeof(fpga_buffer_t));
read(fd,&val,sizeof(val));
if(val == 0){
fpga_loader_debug("---start data ok---\r\n");
usleep(1000);
}
else
{
fpga_loader_debug("---start data error:%d---\r\n",val);
/*need a delay ,and then start another start signal*/
usleep(1000); // 1ms
error_times ++;//error times count
continue;
}
/*1.1 wait the status to be high*/
do{
read(fd,&val,sizeof(val));
if(val == 1){
//printf("--now status be high--\r\n");
break;
}
else
{
//printf("-- wait status --\r\n");
wait_status_error_times ++;
usleep(1000); // 1ms
if(wait_status_error_times == 3)
{
wait_status_error_times = 0;
error_times ++;//error times count
goto label1;
}
}
}
while(val==0);
/*2.send data*/
for(i = 0 ;i < file_stat.st_size ;i ++){
fpga_buffer_t.id = TYPE_FPGA_BUFFER;
fpga_buffer_t.data = *(fileMapP+i); /*the data need to be send*/
write(fd,&fpga_buffer_t,sizeof(fpga_buffer_t));
/*read status*/
fpga_buffer_t.id = TYPE_FPGA_BUFFER;
read(fd,&val,sizeof(val));
if(val == 0){
fpga_loader_debug("---sending data error---\r\n");
usleep(1000); // 1ms
error_times ++;//error times count
goto label1;
}
}
/*3.wait config_done status*/
fpga_buffer_t.id = TYPE_FPGA_CONFIG_END;
write(fd,&fpga_buffer_t,sizeof(fpga_buffer_t));
read(fd,&val,sizeof(val));
if(val == 0){
fpga_loader_debug("---config_done data error---\r\n");
usleep(1000); // 1ms
error_times ++;//error times count
goto label1;
}
/*4.wait init_done status*/
while(init_done_times<3)
{
fpga_buffer_t.id = TYPE_FPGA_INIT_END;
write(fd,&fpga_buffer_t,sizeof(fpga_buffer_t));
read(fd,&val,sizeof(val));
if(val == 0){
fpga_loader_debug("---init_done data error---\r\n");
init_done_times++;
usleep(1000); // 1ms
error_times ++;//error times count
goto label1;
}
else
{
fpga_loader_debug("----init done ok----\r\n");
init_done_flag = 1;
break;
}
usleep(100);
}
if(init_done_flag == 1)
{
break;
}
}
if(error_times == 3){
fpga_loader_debug("---we bootload the fpga error---\r\n");
}
gettimeofday(&tv2, NULL);
/*we finish bootload the fpga,whenever goes right or wrong. 2017-07-25 wit_yuan*/
munmap(fileMapP, file_stat.st_size);
close(fd);
close(file_fd);
printf("time:%ld ms\r\n",(tv2.tv_sec-tv1.tv_sec)*1000+(tv2.tv_usec-tv1.tv_usec)/1000);
if(init_done_flag == 1)
printf("---sending fpga loader file done----\r\n ");
else
printf("---sending fpga loader file err----\r\n ");
return 0;
}
基本上,就这些内容了。