Хабрахабр

Разработка интерфейсных плат на SoC Xilinx Zynq 7000 для записи речи в аналоговом и цифровом формате

Платы предназначались для записи речевых сигналов в аналоговом и цифровом формате PRI/BRI (ISDN, E1/T1). В этой статье мы поделимся опытом разработки интерфейсных плат блока сопряжения на базе SoC ARM+FPGA Xilinx Zynq 7000. Само конечное устройство будет использоваться для фиксации переговоров в гражданской авиации.

Железо: выбор аппаратной платформы устройства

Выбор аппаратной платформы был обусловлен поддержкой протоколов PRI/BRI, которые можно реализовать только на стороне FPGA. Микроконтроллеры (MCU) и микропроцессоры (MPU) не подходили.

Можно было выбрать два решения этой задачи:

  1. синтез IP-ядра Microblaze,
  2. SoC Zynq-7000.

Мы остановились на системе на кристалле (SoC) Zynq 7000, т.к. она проще в плане написании программных приложений и дает больше функциональных возможностей для текущих и будущих задач.

Итого, в рамках проекта собрался следующий список железа:

Xilinx Zynq 7020 (Mars-ZX3 и Mars EB1)

SOM-модуль Mars ZX3 компании Enclustra

Базовая плата Mars EB1 компании Enclustra 1.

TI TLV320AIC34 (tlv320aic34evm-k и USB motherboard). 2.


Отладочная плата для tlv320aic34(tlv320aic34evm-k)


Плата расширения USB-MODEVM для tlv320aic34evm-k

Микросхемы IDT82P2288 — PRI, XHFC-4SU — BRI, отладочных комплектов не было, поэтому мы заложили только фундамент в качестве ip-ядра для тестирования, а боевое крещение происходило прямо в процессе работы, после изготовления опытных образцов плат. 3.

Работа с системой на кристалле Xilinx Zynq 7000


Внутренняя структура SoC Xilinx Zynq 7000


Этапы формирования загрузочных файлов для Xilinx Zynq

Привычная работа с процессорами Cortex-A — это загрузка u-boot, kernel linux, rootfs. Прошивка/загрузка исполняемых файлов для Zynq отличается от привычной загрузки для MPU. В bitstream содержится описание аппаратных блоков на ПЛИС и внутренняя связь с процессором. А на Zynq появляется bitstream, файл прошивки для ПЛИС (FPGA). Также на стороне linux есть механизм, который позволяет прошивать PL-часть сразу во время работы, такое устройство называется xdevcfg (ZYNQ FPGA manager с 2018. Этот файл загружается при старте системы. 1).

Интерфейсы PRI/BRI


Особенности цифровых сетей PRI/BRI

Интерфейс первичного уровня (Primary Rate Interface, PRI) — стандартный интерфейс сети ISDN, определяющий дисциплину подключения станций ISDN к широкополосным магистралям, которые связывают местные и центральные АТС или сетевые коммутаторы.


Вид передаваемого фрейма для PRI


Вид передаваемого фрейма для BRI


Внутренняя структура PRI-физики — IDT82P2288


Внутренняя структура BRI-физики — XHFC-4SU

Аудиокодек TLV320AIC34

Четырехканальный маломощный аудиокодек TLV320AIC34 для портативного аудио и телефонии — хорошее решение для применения в аналоговой телефонии.


A-часть tlv320aic34, аудиокодек содержит два таких функциональных блока

Данные можно передавать через I2S-интерфейс, а также через DSP, PCM, TDM.

I2S — стандарт интерфейса последовательной шины, он используется для соединения цифровых аудиоустройств и электрически представляет собой 3 проводника, которые идут от активного устройства к пассивному, а также 4 сигнала, которые соответствуют им следующим образом:

  1. Тактовый сигнал битовой синхронизации (BCLK).
  2. Тактовый сигнал фреймовой (по словам) синхронизации (WCLK).
  3. Сигнал данных, который может передавать или принимать 2 разделенных по времени канала (DIN/DOUT).

Каналы для приёма и передачи данных разделены, то есть существует отдельный канал для приёма данных и канал для передачи. Контроллер принимает данные, которые передает звуковой кодек, но возможна и обратная ситуация.


I2S-фрейм, особенности работы I2S-интерфейса

После выбора всех аппаратных составляющих мы решали задачу по соединению аудиокодека и Xilinx Zynq 7020.

Поиск I2S-ядер

Наверное, самым сложным моментом при работе с аудиопотоком в Xilinx Zynq 7020 было то, что на процессорной части этой системы на кристалле в принципе нет I2S-шины, поэтому нужно было найти I2S-ядра. Эта задача усложнялась тем условием, что ip-ядро должно было быть бесплатным.

Нашли для bare metal ядро I2S Digilent. Мы остановились на нескольких ip-ядрах. Они выпускают ip-ядра для своего оборудования, для взаимодействия ПЛИС/FPGA. Нашли несколько ip-ядер на opencores и, наверное, самый лучший для нас вариант — это ip-ядро Analog Devices.

Нас заинтересовало ip-ядро под названием AXI-I2S-ADI. Сама компания Analog Devices продвигает эти ip-ядра для своих аппаратных платформ.

Итого, список прецедентов для работы:

  1. Bare metal — IP core для I2S (Digilent ZYBO audio)
  2. opencores.org
  3. AXI-I2S-ADI controller (Analog Devices)

IP-ядро AXI-I2S-ADI

Само ip-ядро выглядит следующим образом: у него есть линии bclk, wclk, din, dout. Оно подключается к DMA Xilinx Zynq 7000, в нашем примере используется DMA PS-части. Весь обмен данными происходит через DMA. DMA может быть отдельным блоком или составной частью PS SoC.

При конфигурации данного ip-ядра важно не забыть подать мастер-частоту mclk на сам tlv320aic34, как вариант при использовании отладочного комплекта для tlv320aic34 — подавать внешнюю мастер-частоту.


Функциональный блок с подключенной axi-i2s-adi

После процедуры конфигурирования стояла задача запуска функционала в ОС Linux.

Запуск и настройка device tree для tlv320aic34

Настройка i2c (по данному интерфейсу конфигурируется tlv320aic34):

i2c0: i2c@e0004000 ; ... };

Настройка i2s (по данному интерфейс передаются аудиоданные):

i2s_clk: i2s_clk { #clock-cells = <0>; compatible = "fixed-clock"; clock-frequency = <11289600>; clock-output-names = "i2s_clk"; }; axi_i2s_adi_0: axi_i2s_adi@43C00000 { compatible = "adi,axi-i2s-1.00.a"; reg = <0x43C00000 0x1000>; xlnx,bclk-pol = <0x0>; xlnx,dma-type = <0x1>; xlnx,has-rx = <0x1>; xlnx,has-tx = <0x1>; xlnx,lrclk-pol = <0x0>; xlnx,num-ch = <0x1>; xlnx,s-axi-min-size = <0x000001FF>; xlnx,slot-width = <0x18>; }; &axi_i2s_adi_0 { #sound-dai-cells = <0>; compatible = "adi,axi-i2s-1.00.a"; clocks = <&clkc 15>, <&i2s_clk>; clock-names = "axi", "ref"; dmas = <&dmac_s 0 &dmac_s 1>; dma-names = "tx", "rx";
};

Настройка sound card в device tree (аудиокарты):

sound { compatible = "simple-audio-card"; simple-audio-card,name = "TLV320AIC34"; simple-audio-card,format = "i2s"; simple-audio-card,bitclock-master = <&dailink0_master>; simple-audio-card,frame-master = <&dailink0_master>; simple-audio-card,widgets = ... simple-audio-card,routing = ... dailink0_master: simple-audio-card,cpu { clocks = <&i2s_clk>; sound-dai = <&axi_i2s_adi_0>; }; simple-audio-card,codec { clocks = <&i2s_clk>; sound-dai = <&tlv320aic3x>; }; }; };

После всех манипуляций по настройке и конфигурированию кодека в дереве устройств в ОС Linux появилась заветная аудиокарта и мы смогли услышать музыку (наш первый музыкальных трек — Highway to Hell, AC/DC).

Вот что нам пришлось ради этого сделать:

  • Сформировали необходимую частоту с помощью clk_wiz (clocking wizard)
  • Правильно сконфигурировали DTS для tlv320aic34
  • Добавили поддержку драйвера tlv320aic3x
  • Добавили audio-пакеты в buildroot для воспроизведения audio stream (aplay, madplay и др.)

В процессе разработки конечного устройства перед нами стояла задача подключить 4 микросхемы tlv320aic34. Описанная выше микросхема tlv320aic34 содержит 2 блока по работе с аудиопотоком, каждый блок имеет собственную линию i2c по конфигурированию и настройке аудиопараметров. Блок может иметь только четыре адреса, соответственно, подключить четыре микросхемы tlv320aic34 на один интерфейс i2c невозможно, нужно использовать два интерфейса i2c (8 независимых аудиоблоков). На каждый блок, если заводить индивидуально mclk, blck, wclk, din/dout, суммарно нужно завести 40 линий сигнала, что невозможно и нерационально со схемотехнической точки зрения для выбранного нами som-модуля, ведь кроме этих сигналов нужно было подключить множество других линий и интерфейсов.

Это решение повлияло на работу axi-i2s-adi, из за того что само ip-ядро работало в мастер-режиме. В итоге мы решили перевести аудиоплату на TDM-режим, в котором все линии mclk, bclk, din, dout объединяются, что позволяет сократить общие колличество линий связи. Пришлось писать ip-ядро для слушания i2s-трафика и отправки его в dma, такое решение позволило нам создать общий интерфейс по приему данных, который не зависел бы от типа платы для записи разговоров (аналоговая и цифровая платы). Также это изменение не позволяло использовать наше ip-ядро в TDM-режиме, и волевым решением нам пришлось отказаться от использования выбранного ip-ядра.

Первоначальная архитектура приема аудиопотока и его обработки по интерфейсу i2s:

Окончательная архитектура приема аудиопотока и его обработки по интерфейсу i2s:

Архитектура приема PRI-потока и его обработки:

Архитектура приема BRI-потока и его обработки:

AXI DMA

Это важный элемент системы синхронизации обмена данным для dma.


Окно конфигурации AXI DMA в Xilinx Vivado

У него множество параметров. На принтскрине представлен сам AXI DMA-блок. Данные могут быть выровненными или быть в произвольном формате. Можно настроить шину, сколько данных передавать. Подробное описание работы и взаимодействия с axi dma описано в технической документации (от версии к версии идет дополнение и исправление неточностей в описании, а также доработка ip-ядер).

Проверка передачи данных через AXI DMA, варианты тестирования AXI DMA

При разработке драйвера мы решили найти open source и адаптировать его под нашу задачу. В итоге выбрали исходники github-проекта ezdma (игра слов, читается как easy dma).

До наступления этого момента мы решили взять AXI DMA, AXI DATA FIFO и сделать loopback, чтобы подстраховаться от возможных будущих ошибок. Следующий шаг — разработка тестового драйвера, это был подготовительный этап в ожидании момента момента, когда к нам придет ip-ядро с уже готовой функциональностью из отдела FPGA-разработки (описанный процесс проработки формировали embedded-программисты). Мы немного адаптировали функциональность, довели ее до наших пожеланий по интерфейсу взаимодействия и еще раз проверили работоспособность драйвера и выбранного принципа взаимодействия. Закольцевали отправку и прием данных, так мы проверили результат своей работы и работоспособность нашего драйвера.


Блок-дизайн с look-back, первый способ проверки AXI DMA

Пример описание DMA и ezdma в device tree:

/ { amba_pl: amba_pl { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges ; axi_dma_1: axi_dma { #dma-cells = <1>; compatible = "xlnx,axi-dma-1.00.a"; reg = <0x40400000 0x10000>; clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk"; clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>; interrupt-parent = <&intc>; interrupts = <0 29 4 0 30 4>; xlnx,addrwidth = <0x20>; xlnx,include-sg; dma-channel@40400000 { compatible = "xlnx,axi-dma-mm2s-channel"; dma-channels = <0x1>; interrupts = <0 29 4>; xlnx,datawidth = <0x20>; xlnx,device-id = <0x0>; xlnx,include-dre ; }; dma-channel@40400030 { compatible = "xlnx,axi-dma-s2mm-channel"; dma-channels = <0x1>; interrupts = <0 30 4>; xlnx,datawidth = <0x20>; xlnx,device-id = <0x0>; xlnx,include-dre ; }; }; ezdma0 { compatible = "ezdma"; dmas = <&axi_dma_1 0 &axi_dma_1 1>; dma-names = "loop_tx", "loop_rx"; // used when obtaining reference to above DMA core using dma_request_slave_channel() ezdma,dirs = <2 1>; // direction of DMA channel: 1 = RX (dev->cpu), 2 = TX (cpu->dev) }; ... };
};

Генерацию dts/dtsi-файлов можно с легкость делать с помощью инструмента Device Tree Generator.

Второй шаг в нашем процессе разработки — создание тестового ip-ядра для проверки работоспособности драйвера, только на это раз данные будут осмысленными, с передачей по AXIS в AXI_DMA (как это будет в конечном варианте ip-ядра).


Циклограмма работы AXIS-интерфейса

Мы реализуем два варианта ip-ядер для генерации данных, первый проверенный вариант реализация через verilog, второй — на HLS (HLS в данном контексте появился под лозунгом «стильно- модно-молодежно»).

Вот небольшие выдержки кода промежуточного ip-ядра: Генератор данных на verilog (и вообще на языках семейства hdl — verilog, vhdl и т.д.), является стандартным решением при разработке ip-ядер такого типа.

module GenCnt ( …. assign HandsHake = m_axis_din_tready & m_axis_dout_tvalid; always @(posedge Clk) begin if (Rst) begin smCnt <= sIDLE; end else begin case (smCnt) sIDLE: begin smCnt <= sDATA; end sDATA: begin if (Cnt == cTopCnt - 1) begin smCnt <= sLAST; end end
...
endmodule

В более подробном описании нет нужды, так как это типовая задача FPGA-дизайнера.

Vivado HLS (High Level Synthesis) – новая САПР Xilinx для создания цифровых устройств с применением языков высокого уровня, таких как OpenCL, C или C++. Более интересным «зверьком» здесь является HLS.

С/C++ — основные языки для инженера-программиста встроенного ПО, поэтому решение задачи с использованием этих языков интереснее в плане реализации и сравнительного анализа для будущих проектов.

Первый пример — это генератор данных для AXI_DMA, второй пример — это обмен данными между процессорной частью и программируемой логикой по интерфейсу s_axilite. Приведем два небольших примера по работе с HLS.

Тут появляется очень интересный момент c s_axilite: Vivado HLS генерирует драйвер для Linux (драйвер в свою очередь мы адаптировали для работы через procfs, чтобы сохранить наследственность по написанию). Обмен данными по интерфейсу s_axilite (второй пример) был реализован, чтобы в любой момент времени в procfs можно было вычитать, какой bitstream загружен, и чтобы можно было отслеживать корректность работы по версионности для PL-части SoC. Пример сгенерировано кода для Linux ниже (путь к исходникам solution1/impl/ip/drivers/name_xxx/src/).


Этапы синтеза HLS и формирования rtl-кода

Генератор данных на HLS для проверки работы с AXI_DMA:

#include <ap_axi_sdata.h>
#include <hls_stream.h>
#define SIZE_STREAM 1024 struct axis { int tdata; bool tlast;
};
void data_generation(axis outStream[SIZE_STREAM])
{
#pragma HLS INTERFACE axis port=outStream int i = 0; do{ outStream[i].tdata = i; outStream[i].tlast = (i == (SIZE_STREAM - 1)) ? 1 : 0; i++; }while( i < SIZE_STREAM);
}

Пример получения версионности и типа интерфейсной платы:

#include <stdio.h> void info( int &aVersion, int &bSubVersion, int &cTypeBoard, int version, int subVersion, int typeBoard ){
#pragma HLS INTERFACE s_axilite port=aVersion #pragma HLS INTERFACE s_axilite port=bSubVersion #pragma HLS INTERFACE s_axilite port=cTypeBoard #pragma HLS INTERFACE ap_ctrl_none port=return aVersion = version; bSubVersion = subVersion; cTypeBoard = typeBoard; }

Как вы заметили, для разработки на hls очень важно понимать работу и применение различных прагм (HLS pragma), так как процесс синтеза напрямую завязан на прагмах.

Сгенерированный драйвер для s_axilite:

// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2016.4
// Copyright (C) 1986-2016 Xilinx, Inc. All Rights Reserved.
// // ============================================================== #ifdef __linux__ /***************************** Include Files *********************************/
#include "xinfo.h" /***************** Macros (Inline Functions) Definitions *********************/
#define MAX_UIO_PATH_SIZE 256
#define MAX_UIO_NAME_SIZE 64
#define MAX_UIO_MAPS 5
#define UIO_INVALID_ADDR 0 /**************************** Type Definitions ******************************/
typedef struct { u32 addr; u32 size;
} XInfo_uio_map; typedef struct { int uio_fd; int uio_num; char name[ MAX_UIO_NAME_SIZE ]; char version[ MAX_UIO_NAME_SIZE ]; XInfo_uio_map maps[ MAX_UIO_MAPS ];
} XInfo_uio_info; /***************** Variable Definitions **************************************/
static XInfo_uio_info uio_info; /************************** Function Implementation *************************/
static int line_from_file(char* filename, char* linebuf) { char* s; int i; FILE* fp = fopen(filename, "r"); if (!fp) return -1; s = fgets(linebuf, MAX_UIO_NAME_SIZE, fp); fclose(fp); if (!s) return -2; for (i=0; (*s)&&(i<MAX_UIO_NAME_SIZE); i++) { if (*s == '\n') *s = 0; s++; } return 0;
} static int uio_info_read_name(XInfo_uio_info* info) { char file[ MAX_UIO_PATH_SIZE ]; sprintf(file, "/sys/class/uio/uio%d/name", info->uio_num); return line_from_file(file, info->name);
} static int uio_info_read_version(XInfo_uio_info* info) { char file[ MAX_UIO_PATH_SIZE ]; sprintf(file, "/sys/class/uio/uio%d/version", info->uio_num); return line_from_file(file, info->version);
} static int uio_info_read_map_addr(XInfo_uio_info* info, int n) { int ret; char file[ MAX_UIO_PATH_SIZE ]; info->maps[n].addr = UIO_INVALID_ADDR; sprintf(file, "/sys/class/uio/uio%d/maps/map%d/addr", info->uio_num, n); FILE* fp = fopen(file, "r"); if (!fp) return -1; ret = fscanf(fp, "0x%x", &info->maps[n].addr); fclose(fp); if (ret < 0) return -2; return 0;
} static int uio_info_read_map_size(XInfo_uio_info* info, int n) { int ret; char file[ MAX_UIO_PATH_SIZE ]; sprintf(file, "/sys/class/uio/uio%d/maps/map%d/size", info->uio_num, n); FILE* fp = fopen(file, "r"); if (!fp) return -1; ret = fscanf(fp, "0x%x", &info->maps[n].size); fclose(fp); if (ret < 0) return -2; return 0;
} int XInfo_Initialize(XInfo *InstancePtr, const char* InstanceName) { XInfo_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 XInfo_Release(XInfo *InstancePtr) { XInfo_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;
} #endif

Важный файл, который подскажет расположение переменных (регистров) в адресном пространстве, — файл x#ваше_имя#_hw.h. Всегда можно проверить правильность написанного ip-ядра, используя инструмент devmem.

Содержание данного файла:

// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2016.4
// Copyright (C) 1986-2016 Xilinx, Inc. All Rights Reserved.
// // ============================================================== // AXILiteS
// 0x00 : reserved
// 0x04 : reserved
// 0x08 : reserved
// 0x0c : reserved
// 0x10 : Data signal of aVersion
// bit 31~0 - aVersion[31:0] (Read)
// 0x14 : Control signal of aVersion
// bit 0 - aVersion_ap_vld (Read/COR)
// others - reserved
// 0x18 : Data signal of bSubVersion
// bit 31~0 - bSubVersion[31:0] (Read)
// 0x1c : Control signal of bSubVersion
// bit 0 - bSubVersion_ap_vld (Read/COR)
// others - reserved
// 0x20 : Data signal of cTypeBoard
// bit 31~0 - cTypeBoard[31:0] (Read)
// 0x24 : Control signal of cTypeBoard
// bit 0 - cTypeBoard_ap_vld (Read/COR)
// others - reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake) #define XINFO_AXILITES_ADDR_AVERSION_DATA 0x10
#define XINFO_AXILITES_BITS_AVERSION_DATA 32
#define XINFO_AXILITES_ADDR_AVERSION_CTRL 0x14
#define XINFO_AXILITES_ADDR_BSUBVERSION_DATA 0x18
#define XINFO_AXILITES_BITS_BSUBVERSION_DATA 32
#define XINFO_AXILITES_ADDR_BSUBVERSION_CTRL 0x1c
#define XINFO_AXILITES_ADDR_CTYPEBOARD_DATA 0x20
#define XINFO_AXILITES_BITS_CTYPEBOARD_DATA 32
#define XINFO_AXILITES_ADDR_CTYPEBOARD_CTRL 0x24

В этом файле описаны адреса регистров, регистры соответствуют расположению аргументов в функции. После синтеза проекта можно посмотреть, как созданный проект будет выполняться по тактам.


Пример выполнения проекта по тактам

Работа с hls показала, что этот инструмент подходит для быстрого решения поставленных задач, особенно он хорошо себя зарекомендовал для решения математических задач компьютерного зрения, которые можно легко описать на С++ или С, а также для создания небольших ip-ядер для взаимодействий и обмена информацией со стандартными интерфейсами FPGA.

При этом HLS не подходит для реализации конкретных аппаратных интерфейсов, например, в нашем случае это был I2S, а сгенерированный rtl-код занимает больше места на FPGA, чем написанный на стандартных hdl-языках.

Это ip-ядро повторяет функциональность предыдущих ip-ядер, за исключением того, что на нем формируется инкрементные данные (трафик), которые соответствуют реальным данным I2S в TDM-режиме. Последний этап в тестировании драйвера — разработка генератора I2S-трафика.


Блок-дизайн будущего тестирования custom I2S-ядра и генератор I2S-трафика

В итоге мы получили результаты работы hls, axi dma и s_axilite, проверили работоспособность нашего ПО и драйверов.

Выводы

Нам удалось разработать необходимые виды интерфейсных плат, а также ip-ядра для tdm, pri, bri. Мы значительно улучшили текущий подход при разработке таких устройств и создали комплексное решение, которое сможет конкурировать с аналогичными интерфейсными платам компаний Asterick, patton и других. Преимущество нашего решение в том, что разработчику для передачи данных не понадобится промежуточное звено связи PC с PCI, он сможет напрямую передавать принятую информацию по Ethernet.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть