本实验是为学习HPS(ARM)如何和FPGA进行交互。
演示 HPS(ARM)如何控制FPGA端的LED。(FPGA端的LED作为外设,从而实现HPS对外设的控制)
本实验主要分别两个部分,硬件设计部分和软件设计部分。(掌握Quartus硬件设计和C程序设计)
HPS和FPGA之间的协议通信主要是通过AXI高速总线,其包括 HPS-to-FPGA AXI、FPGA-to-HPS AXI以及Light-weight HPS-to-FPGA AXI。
当HPS作为主端(master)时,它可以访问FPGA端Avalon MM slave接口的所有组件,HPS作为主端时的AXI-Bridge包括:
FPGA作为主端时的AXI-Bridge包括:
下面来描述到底如何基于ARM(HPS)的Linux应用程序 来控制FPGA端的PIO控制器,另外控制器是连接在HPS lw h2f 总线上的,因此可获得其在HPS总线上的物理地址空间。基于此物理地址空间,我们就可以采用Linux内核内存映射设备(memory-mapped device)驱动访问PIO控制器寄存器,从而即可控制LED的行为。应用程序采用DS-5编写编译。
在软件实验二中,已经讲解了如何生成hps_0.h头文件。那该头文件的用处是什么呢?
在该头文件中,包含了 qsys进行配置的一些信息。
例如:
包含了PIO控制器在Qsys中分配的相对于lwh2fAXI的基地址
它表现为一个宏定义PIO_LED_BASE
PIO控制器位宽信息表示为宏定义PIO_LED_DATA_WIDTH
这两个参数在驱动访问寄存器的时候要用到
#define PIO_LED_BASE 0x0
#define PIO_LED_DATA_WIDTH 10
打开SOCEDS,输入elcipse&,跳转打开DS-5软件,在该软件中新建C工程,并在工程上建source文件。
编写如下C程序,并进行全编译,未出现错误,即可得到可执行文件。
具体步骤和实验一相同。
#include
#include
#include
#include
#define soc_cv_av
#include "hwlib.h"
#include "socal/socal.h"
#include "socal/hps.h"
#include "socal/alt_gpio.h"
#include "hps_0.h"
#define HW_REGS_BASE ( ALT_STM_OFST )
#define HW_REGS_SPAN ( 0x04000000 )
#define HW_REGS_MASK ( HW_REGS_SPAN - 1 )
int main() {
void *virtual_base;
int fd;
int loop_count;
int led_direction;
int led_mask;
void *h2p_lw_led_addr;
// map the address space for the LED registers into user space so we can interact with them.
// we'll actually map in the entire CSR span of the HPS since we want to access various registers within that span
//打开内存映射设备
if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) {
printf( "ERROR: could not open \"/dev/mem\"...\n" );
return( 1 );
}
//将寄存器物理地址空间映射到用户空间
virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE );
if( virtual_base == MAP_FAILED ) {
printf( "ERROR: mmap() failed...\n" );
close( fd );
return( 1 );
}
//PIO的虚拟地址被定义为空指针h2p_lw_led_addr,后可用该指针变量访问PIO控制器的寄存器
h2p_lw_led_addr=virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + PIO_LED_BASE ) & ( unsigned long)( HW_REGS_MASK ) );
// toggle the LEDs a bit
//流水灯的切换
loop_count = 0; //初始化,并循环60次
led_mask = 0x01;
led_direction = 0; // 0: left to right direction
while( loop_count < 60 ) {
// control led, add ~ because the led is low-active
//led灯低电平有效,因此加~
*(uint32_t *)h2p_lw_led_addr = ~led_mask;
// 把进程挂起一段时间 wait 100ms
usleep( 100*1000 );
// update led mask
if (led_direction == 0){
led_mask <<= 1;
if (led_mask == (0x01 << (PIO_LED_DATA_WIDTH-1)))
led_direction = 1; //从右到左
}else{
led_mask >>= 1;
if (led_mask == 0x01){
led_direction = 0; //从左到右
loop_count++;
}
}
} // while
// clean up our memory mapping and exit
if (munmap(virtual_base, HW_REGS_SPAN) != 0) {
printf("ERROR: munmap() failed...\n");
close(fd);
return(1);
}
close( fd );
return( 0 );
}
my_hps_led为生成的可执行文件,将其右键复制到SD卡中即可。
由于此LED灯采用的是开发板FPGA部分的LED灯,因此我们需要将硬件设计的工程全编译,得到sof文件,将sof文件下载到开发板中。
打开putty以root用户登录终端
找到我们复制到SD卡中的文件,my_hps_led,并将该可执行程序复制到根目录
采用命令修改可执行文件的权限
chmod 777 my_hps_led
采用命令运行 可执行文件
./my_hps_led
总结:
本实验分为两个大的部分,硬件设计和软件设计。
最终我们将quartus生成的sof文件下载到开发板,采用串口终端调试软件putty 以实现在开发板上运行Linux系统,并通过命令找到我们所需要的可执行文件,修改权限并执行,至此,即可在开发板上看到实验效果。