利用hls编写好了ip之后,那么驱动代码如何编写?好在hls sdk已经提前考虑到了这一点,在export rtl的时候,除了正常输出verilog和vhdl代码之外,也会提前帮助我们写好驱动代码。驱动代码有两种格式,一种是逻辑系统的驱动,类似于rtos里面用到的驱动;还有一种是linux驱动,但是也是在用户层对设备进行访问,不是真正的kernel module文件。
1、hls代码,主要是一个按键和s_axilite控制的led闪烁功能
#include <ap_cint.h>
extern "C" void led_twinkle(uint1 key, uint32 num, uint2* led)
{
#pragma HLS INTERFACE s_axilite port=num
#pragma HLS INTERFACE ap_none port=key
#pragma HLS INTERFACE ap_none port=led
#pragma HLS INTERFACE ap_ctrl_none port=return
int i;
if(key)
{
for(i = 0; i < num; i++)
{
if(i < num/2)
{
*led = 1;
}
else
{
*led = 2;
}
}
}
else
{
*led = 0;
}
}
2、导出的驱动代码位置
3、驱动代码的内容
4、驱动的组成
如果是裸机系统,需要的文件是
xled_twinkle_sinit.c、xled_twinkle.c
如果是linux系统,需要的文件时
xled_twinkle_linux.c、xled_twinkle.c
5、裸机系统的初始化函数,见xled_twinkle_sinit.c
int XLed_twinkle_Initialize(XLed_twinkle *InstancePtr, u16 DeviceId) {
XLed_twinkle_Config *ConfigPtr;
Xil_AssertNonvoid(InstancePtr != NULL);
ConfigPtr = XLed_twinkle_LookupConfig(DeviceId);
if (ConfigPtr == NULL) {
InstancePtr->IsReady = 0;
return (XST_DEVICE_NOT_FOUND);
}
return XLed_twinkle_CfgInitialize(InstancePtr, ConfigPtr);
}
6、linux驱动并不内核代码,而是通过user layer访问kernel layer,见xled_twinkle_linux.c
int XLed_twinkle_Initialize(XLed_twinkle *InstancePtr, const char* InstanceName) {
XLed_twinkle_uio_info *InfoPtr = &uio_info;
struct dirent **namelist;
int i, n;
char* s;
char file[ MAX_UIO_PATH_SIZE ];
char name[ MAX_UIO_NAME_SIZE ];
int flag = 0;
assert(InstancePtr != NULL);
n = scandir("/sys/class/uio", &namelist, 0, alphasort);
if (n < 0) return XST_DEVICE_NOT_FOUND;
for (i = 0; i < n; i++) {
strcpy(file, "/sys/class/uio/");
strcat(file, namelist[i]->d_name);
strcat(file, "/name");
if ((line_from_file(file, name) == 0) && (strcmp(name, InstanceName) == 0)) {
flag = 1;
s = namelist[i]->d_name;
s += 3; // "uio"
InfoPtr->uio_num = atoi(s);
break;
}
}
if (flag == 0) return XST_DEVICE_NOT_FOUND;
uio_info_read_name(InfoPtr);
uio_info_read_version(InfoPtr);
for (n = 0; n < MAX_UIO_MAPS; ++n) {
uio_info_read_map_addr(InfoPtr, n);
uio_info_read_map_size(InfoPtr, n);
}
sprintf(file, "/dev/uio%d", InfoPtr->uio_num);
if ((InfoPtr->uio_fd = open(file, O_RDWR)) < 0) {
return XST_OPEN_DEVICE_FAILED;
}
// NOTE: slave interface 'Axilites' should be mapped to uioX/map0
InstancePtr->Axilites_BaseAddress = (u32)mmap(NULL, InfoPtr->maps[0].size, PROT_READ|PROT_WRITE, MAP_SHARED, InfoPtr->uio_fd, 0 * getpagesize());
assert(InstancePtr->Axilites_BaseAddress);
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
return XST_SUCCESS;
}
int XLed_twinkle_Release(XLed_twinkle *InstancePtr) {
XLed_twinkle_uio_info *InfoPtr = &uio_info;
assert(InstancePtr != NULL);
assert(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
munmap((void*)InstancePtr->Axilites_BaseAddress, InfoPtr->maps[0].size);
close(InfoPtr->uio_fd);
return XST_SUCCESS;
}
7、寄存器设置代码,见xled_twinkle.c代码
void XLed_twinkle_Set_num(XLed_twinkle *InstancePtr, u32 Data) {
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
XLed_twinkle_WriteReg(InstancePtr->Axilites_BaseAddress, XLED_TWINKLE_AXILITES_ADDR_NUM_DATA, Data);
}
u32 XLed_twinkle_Get_num(XLed_twinkle *InstancePtr) {
u32 Data;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Data = XLed_twinkle_ReadReg(InstancePtr->Axilites_BaseAddress, XLED_TWINKLE_AXILITES_ADDR_NUM_DATA);
return Data;
}
8、测试与验证
在一开始设计ip的时候,可以通过简单的逻辑系统+驱动代码的方式进行验证。等到后期成熟了,再转成linux驱动、或者是linux kernel驱动,都是可以的。
9、使用方法
总体来说,hls驱动的使用还是比较简单的。这里分两种,一种是查询,一种是中断。
首先,不管是查询,还是中断,第一步都是初始化设备,都是调用X***_Initialize函数,
接着,就是设置参数,一般是X**_Set***这种格式,
等参数都设置好了之后,就可以调用X***_Start函数,
ip开始执行之后,如果是查询模式,只需要轮询X***_IsDone函数即可;而中断的话,则等待中断函数被调用;
所有工作完成后,如果还需要继续处理,那么设置参数和处理,如果不需要了调用X***_Release释放设备即可。