Код, который отправляет данные о пикселях по строкам на PC через ethernet. В ПЛИС есть две двухпортовые DRAM памяти. Пока в одну память пишутся данные с ПЛИС, с другой Linux читает данные и передает данные по ethernet. Для синхронизации их используется регистр. Когда ПЛИС закончила писать данные в одну память и начинает в другую, байт в этом регистре меняет свое значение на противоположное, и служит сигналом для Linux о начале чтения. Linux читает намного быстрее чем пишет FPGA.
При создании проекта в Quartus`е мы создаем память, двунаправленную, которая с одной стороны подключается к процессору через мост h2p_lw по определенному адресу этой шины, а с другой стороны к FPGA. Чтобы обратиться к этой памяти, нам сначала надо определить её виртуальный адрес. Для этого, мы открываем файл /dev/mem (40) , и с помощью функции mmap() (44), получаем интересующий нас адрес. Далее в нем прибавляем адрес моста h2p_lw и адрес памяти в нем с маскировкой нужных битов (51-53).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
#include <sys/types.h> #include <sys/socket.h> #include <sys/mman.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <time.h> #include <stdbool.h> #include "hwlib.h" #include "soc_cv_av/socal/socal.h" #include "soc_cv_av/socal/hps.h" #include "soc_cv_av/socal/alt_gpio.h" #define soc_cv_av #define HW_REGS_BASE ( ALT_STM_OFST ) #define HW_REGS_SPAN ( 0x04000000 ) #define HW_REGS_MASK ( HW_REGS_SPAN - 1 ) #define ALT_LWFPGASLVS_OFST (0xFF200000) #define LED_PIO_BASE ( 0x00010080 ) #define DATA_ON_CHIP_MEMORY ( 0x00030000 ) #define DATA_ON_CHIP_MEMORY2 ( 0x00040000 ) #define DATA_ON_SW_REG ( 0x00010080 ) volatile unsigned long *h2p_lw_led_addr =NULL; volatile uint32_t *h2p_lw_data_addr =NULL; volatile unsigned long *h2p_lw_data_addr2 =NULL; int main(int argc, char **argv) { void *virtual_base; int fd; 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); } h2p_lw_led_addr =virtual_base + ( ( unsigned long )(ALT_LWFPGASLVS_OFST + LED_PIO_BASE ) & ( unsigned long)( HW_REGS_MASK ) ); h2p_lw_data_addr =virtual_base + ( ( unsigned long )(ALT_LWFPGASLVS_OFST + DATA_ON_CHIP_MEMORY ) & ( unsigned long)( HW_REGS_MASK ) ); h2p_lw_data_addr2 =virtual_base + ( ( unsigned long )(ALT_LWFPGASLVS_OFST + DATA_ON_CHIP_MEMORY2 ) & ( unsigned long)( HW_REGS_MASK ) ); int value; int adress; int sockfd; /* Дескриптор сокета */ int n, len; /* Переменные для различных длин и количества символов */ // для отсылаемой и принятой строки */ struct sockaddr_in servaddr, cliaddr; /* Структуры для адресов сервера и клиента */ /* Сначала проверяем наличие второго аргумента в командной строке. При его отсутствии ругаемся и прекращаем работу */ if(argc != 2){ printf("Usage: a.out <IP address>\n"); exit(1); } /* Создаем UDP сокет */ if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0){ perror(NULL); /* Печатаем сообщение об ошибке */ exit(1); } /* Заполняем структуру для адреса клиента: семейство протоколов TCP/IP, сетевой интерфейс – любой, номер порта по усмотрению операционной системы. Поскольку в структуре содержится дополнительное не нужное нам поле, которое должно быть нулевым, перед заполнением обнуляем ее всю */ bzero(&cliaddr, sizeof(cliaddr)); cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(0); cliaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Настраиваем адрес сокета */ if(bind(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)) < 0){ perror(NULL); close(sockfd); /* По окончании работы закрываем дескриптор сокета */ exit(1); } /* Заполняем структуру для адреса сервера: семейство протоколов TCP/IP, сетевой интерфейс – из аргумента командной строки, номер порта 7. Поскольку в структуре содержится дополнительное не нужное нам поле, которое должно быть нулевым, перед заполнением обнуляем ее всю */ bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(2221); if(inet_aton(argv[1], &servaddr.sin_addr) == 0){ printf("Invalid IP address\n"); close(sockfd); /* По окончании работы закрываем дескриптор сокета */ exit(1); } volatile uint32_t *reg2 = NULL; volatile uint32_t reg1 , oldreg1; while(1){ reg2 = (virtual_base + ( ( unsigned long )(ALT_LWFPGASLVS_OFST + DATA_ON_SW_REG ) & ( unsigned long)( HW_REGS_MASK ))); reg1 = * reg2; if (reg1 != oldreg1){ if (reg1 == 2){ if(sendto(sockfd, h2p_lw_data_addr , 1000, 0, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){ perror(NULL); close(sockfd); exit(1); } } else { if(sendto(sockfd, h2p_lw_data_addr2 , 1000, 0, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){ perror(NULL); close(sockfd); exit(1); } } } oldreg1 = reg1; } close(sockfd); return 0; } |
Программа запускается из консоли, через SSH. При запуске параметром вводиться IP адрес, куда будут посылаться данные. После заполнения структур отправителя и получателя, запускается цикл while, который проверяет значение регистра reg1. Если в нем изменяется значение начинается отсылка пакета, и данные для этого берутся по адресам вычисленным в строках 52,53. Параметр 1000 означает, что оттуда читается по 1000 байт.

