背景:
片上单片机与PC通过UART相连并且可以通过PC对片上单片机进行相应的指令操作。
片上单片机可以运行相应编译好的c程序,控制相应的IPcore从而用FPGA上的IPcore进行相应的运算。
相关c源码通过编译可以变为可执行文件拷入u盘,然后片上单片机可以通过挂载u盘执行可执行文件。
目的:
读懂程序。程序的目的为:单片机运行简单的卷积操作,单片机控制的IPcore也运行卷积操作,然后两个卷积操作结果数据之间进行对比。结果相同则表示运行无误,输出相应的结果。
目录
生成卷积IPcore时c语言相关内容
1.关于主函数的参数
2.关于文件的操作
3.memcmp函数
4.atoi函数
5.memset
6.函数与IPcore通信的核心语句
6.1 打开DMA通信
6.2 单片机向IPcore写数据
6.3 单片机从IPcore读数据
与之对应的IPcore内容:
生成卷积IPcore时c语言相关内容
PS端代码:
#include
#include
#include
#include
#include
unsigned long tStart, tEnd;
unsigned long data;
float buf_in[27*600] = {};
float buf_out[float buf_out[16*600] = {};
float weights[16*27] = {};
unsigned long getTime(){
struct timeval temp;
gettimeofday(&temp, NULL);
return temp.tv_sec * 1000 * 1000 + temp.tv_usec;
}
void report(char *msg, unsigned long data, unsigned long time, unsigned long dmaUsed){
printf("%s %ld %ld %f %d
", msg, data, time, data * 1.0 / time, dmaUsed);
FILE *f = fopen("report.dat", "a");
fprintf(f, "%s %ld %ld %f %d
", msg, data, time, data * 1.0 / time, dmaUsed);
fclose(f);
}
#define REPORT(f, timeStart, timeEnd, dataPtr, msg, dmaUsed) *timeStart = getTime(); *dataPtr = f; *timeEnd = getTime(); report(msg, *dataPtr, *timeEnd - *timeStart, dmaUsed);
void checkData(float *bufferIn, float *bufferOut, unsigned int elems){
int i;
for (i = 0; i < elems; i++) {
if (bufferIn[i] != bufferOut[i])
printf("[%d] %f->%f
", i, bufferIn[i], bufferOut[i]);
}
}
unsigned long memCpy_ARM(char *bufferIn, char *bufferOut, unsigned long elems, size_t size){
int i;
for(i=0; i FIFO_LEN ? FIFO_LEN : size * elems - byteMoved;
ret = write(fd, &bufferIn[byteMoved], byteToMove);
if (ret != byteToMove)
printf("write DMA error %d!
", ret);
else
printf("write DMA %d
", ret);
byteMoved += byteToMove;
}
printf("Write Total %d
", byteMoved);
byteMoved = 0;
while(byteMoved!=READ_LEN){
byteToMove = READ_LEN - byteMoved > FIFO_LEN ? FIFO_LEN : READ_LEN - byteMoved;
ret = read(fd, &bufferOut[byteMoved], byteToMove);
if (ret != byteToMove)
printf("read DMA error %d
", ret);
else
printf("read DMA %d
", ret);
byteMoved += byteToMove;
}
printf("Read Total %d
", byteMoved);
close(fd);
return elems * size * dmaToUse;
}
void cnn_simulate(float *in, float *out)
{
float *bufferIn, *bufferOut;
int i, j, k;
float result, temp;
bufferIn = (float *) malloc(4*27*600);
bufferOut = (float *) malloc(4*16*600);
for (i = 0; i < 27*600; i++)
bufferIn[i] = in[i];
for (i = 0; i < 16; i++)
{
for (j = 0; j < 600; j++)
{
result = 0;
for (k = 0; k < 27; k++)
{
temp = weights[i*27+k] * bufferIn[k*600+j];
result += temp;
}
bufferOut[i*600+j] = result;
}
}
for (i = 0; i < 16*600; i++)
out[i] = bufferOut[i];
}
int main(int argc, char **argv)
{
float *bufferIn, *bufferOut_ARM, *bufferOut_DMA;
float *weightsIn;
int fd, ret;
int size_float = sizeof(float);
if(argc!=3){
printf("Usage: ./dmaBench DATA_IN DATA_OUT
");
exit(0);
}
unsigned long size_in = atoi(argv[1]);
unsigned long size_out = atoi(argv[2]);
printf("in %d, out %d
", size_in, size_out);
bufferIn = (float *) malloc(size_float * size_in);
bufferOut_DMA = (float *) malloc(size_float * size_out);
bufferOut_ARM = (float *) malloc(size_float * size_out);
int i;
for (i = 0; i < size_in; i++)
bufferIn[i] = buf_in[i];
cnn_simulate(bufferIn, bufferOut_ARM);
fd = open("/dev/axi-dma1", O_RDWR);
if (fd < 0) {
printf("open axi-dma1 failed!
");
return -1;
}
//ret = write(fd, (unsigned char *)weightsIn, size_float*27*16);
//if (ret != size_float*27*16)
// printf("import weights failed!
");
ret = write(fd, (unsigned char *)bufferIn, size_float * size_in);
if (ret != size_float * size_in)
printf("import image data failed %d
", ret);
ret = read(fd, (unsigned char *)bufferOut_DMA, size_float * size_out);
if (ret != size_float * size_out)
printf("read error %d
", ret);
for (i = 0; i < size_out; i++) {
if (bufferOut_ARM[i] != bufferOut_DMA[i])
printf("[%d] %f - > %f
", i, bufferOut_ARM[i], bufferOut_DMA[i]);
}
free(bufferIn);
free(bufferOut_DMA);
free(bufferOut_ARM);
return 0;
}
1.关于主函数的参数
int main(int argc, char* argv[])
argc是argument count的简称,argv是argument variable的简称,也就是,argc指代参数的个数,argv指代每个参数
- argv[0] 指向程序运行的全路径名
- argv[1] 指向在DOS命令行中执行程序名后的第一个字符串
- argv[2] 指向执行程序名后的第二个字符串
- argv[argc]为NULL。
例如:输入"test c:/testPic/01.jpg",就是在启动test.exe程序的同时,给该程序指定一个额外的参数“c:/testPic/01.jpg”。这里,argc也就是参数的个数,就是2个,test为第一个参数,空格之后的“c:/testPic/01.jpg”是第二个参数,换句话说,argc=2, argv[0]="test",argv[1]="c:/testPic/01.jpg"。
针对本程序,我们的输入就是
inSize outSize
2.关于文件的操作
getc 从文件中读取字符 getc(fp),fp为文件指针
gets 从缓冲区中读取字符串
fopen与open的区别,`fopen`是C标准函数,因此拥有良好的移植性;而`open`是UNIX系统调用,移植性有限。open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。
所以fd = open("/dev/axi-dma1", O_RDWR); 就是以打开方式进行相应的AXI总线与DMA操作。
wirte 函数 ssize_t write(int fd, const void *buf, size_t nbyte);
fd:文件描述符;
buf:指定的缓冲区,即指针,指向一段内存单元;
nbyte:要写入文件指定的字节数;
返回值:写入文档的字节数(成功);-1(出错)
write函数把buf中nbyte写入文件描述符handle所指的文档,成功时返回写的字节数,错误时返回-1.
read函数 ssize_t read(int fd, void *buf, size_t count);
read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或无可读取的数据。错误返回-1,
3.memcmp函数
memcmp是比较内存区域buf1和buf2的前count个字节。该函数是按字节比较的。
当buf1buf2时,返回值大于0
4.atoi函数
C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。该函数返回转换后的长整数,如果没有执行有效的转换,则返回零。
unsigned long size_in = atoi(argv[1]);
unsigned long size_out = atoi(argv[2]);
根据主函数中此段程序,我们知道了输入的第一个参数为函数名,第二个参数为输入的size,第三个参数为输出的size。
5.memset
void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
6.函数与IPcore通信的核心语句
6.1 打开DMA通信
fd = open("/dev/axi-dma1", O_RDWR);
if (fd < 0) {
printf("Open axi-dma1 failed!
");
return -1; }
打开与DMA之间的通信,也就是单片机与IPcore之间的通信。
6.2 单片机向IPcore写数据
ret = write(fd, (unsigned char *)bufferIn, size_float * size_in);
if (ret != size_float * size_in)
printf("write image data from bufferIn to DMA failed %d
", ret);
单片机向IPcore写数据,把bufferIn中的数据通过之前open的fd写入size_float * size_in个字节的数据。若写入不成功返回-1,写入成功,返回写入的字节数。
6.3 单片机从IPcore读数据
ret = read(fd, (unsigned char *)bufferOut_DMA, size_float * size_out);
if (ret != size_float * size_out)
printf("read from DMA to bufferOut_DMA failed %d
", ret);
单片机从IPcore读数据,IPcore中传出的数据通过之前open的fd读入size_float * size_out个字节的数据,存入bufferOut_DMA。若读入不成功返回-1,若读成功,返回读入的字节数。
所以程序就是运用open来打开dma的通信,然后用read和write来进行单片机对IP core的读和写。