
En este artículo, compartiremos nuestra experiencia en el desarrollo de tarjetas de interfaz de la unidad de interfaz basada en SoC ARM + FPGA Xilinx Zynq 7000. Las placas se diseñaron para grabar señales de voz en formato PRI / BRI analógico y digital (ISDN, E1 / T1). El dispositivo final en sí se utilizará para registrar las negociaciones en la aviación civil.
Hierro: selección de plataforma de hardware del dispositivo
La elección de la plataforma de hardware fue determinada por el soporte de los protocolos PRI / BRI, que solo se pueden implementar en el lado de FPGA. Los microcontroladores (MCU) y los microprocesadores (MPU) no encajaban.
Se podrían elegir dos soluciones a este problema:
- Síntesis del núcleo de Microblaze IP
- SoC Zynq-7000.
Nos instalamos en un sistema en un chip Zynq 7000 (SoC), porque es más fácil escribir aplicaciones de software y proporciona más funcionalidad para tareas actuales y futuras.
En total, se recopiló la siguiente lista de hierro en el marco del proyecto:
1.Xilinx Zynq 7020 (
Mars-ZX3 y
Mars EB1 )
Enclustra Mars ZX3 SOM
Enclustra Mars EB1 Baseboard2. TI TLV320AIC34 (
tlv320aic34evm-k y placa base USB).
Tarjeta de depuración para tlv320aic34 (tlv320aic34evm-k)
Tarjeta de expansión USB-MODEVM para tlv320aic34evm-k3. IDT82P2288 - PRI, XHFC-4SU - Microcircuitos BRI, no había kits de depuración, por lo tanto, solo sentamos las bases como un núcleo ip para las pruebas, y el bautismo de fuego ocurrió justo en el proceso, después de hacer placas prototipo.
Trabaje con el sistema en el chip Xilinx Zynq 7000
La estructura interna del SoC Xilinx Zynq 7000
Pasos para generar archivos de arranque para Xilinx ZynqParpadear / descargar ejecutables para Zynq es diferente de la descarga habitual para MPU. El trabajo habitual con los procesadores Cortex-A es cargar u-boot, kernel linux, rootfs. Y en Zynq, aparece Bitstream, el archivo de firmware para FPGA. El flujo de bits contiene una descripción de los bloques de hardware en el FPGA y la comunicación interna con el procesador. Este archivo se carga al iniciar el sistema. También en el lado de Linux hay un mecanismo que le permite flashear la parte PL inmediatamente durante la operación, dicho dispositivo se llama xdevcfg (
administrador FPGA ZYNQ desde 2018.1 ).
Interfaces PRI / BRI
Características de las redes digitales PRI / BRILa interfaz de velocidad primaria (PRI) es una interfaz de red ISDN estándar que define la disciplina de conectar estaciones ISDN a troncales de banda ancha que conectan centrales locales o centrales o conmutadores de red.
Tipo de trama transmitida para PRI
Vista de la trama transmitida para BRI
La estructura interna de la física del PRI - IDT82P2288
La estructura interna de la física BRI - XHFC-4SUCódec de audio TLV320AIC34
El
códec de audio TLV320AIC34 de baja potencia de
cuatro canales para audio y telefonía portátil es una buena solución para su uso en telefonía analógica.
Tlv320aic34 Parte A, el códec de audio contiene dos bloques de funcionesLos datos pueden transmitirse a través de la interfaz I2S, así como a través de DSP, PCM, TDM.
I2S es un estándar de interfaz de bus serie, se utiliza para conectar dispositivos de audio digital y representa eléctricamente 3 conductores que van de un dispositivo activo a uno pasivo, así como 4 señales que corresponden a ellos de la siguiente manera:
- Bit Clock (BCLK).
- Marco de señal de reloj (según palabras) sincronización (WCLK).
- Señal de datos que puede transmitir o recibir 2 canales divididos en el tiempo (DIN / DOUT).
Los canales para recibir y transmitir datos están divididos, es decir, hay un canal separado para recibir datos y un canal para la transmisión. El controlador recibe los datos transmitidos por el códec de audio, pero también es posible lo contrario.
Marco I2S, características de la interfaz I2SDespués de seleccionar todos los componentes de hardware, resolvimos el problema de conectar el códec de audio y Xilinx Zynq 7020.
Buscar núcleos I2S
Probablemente el momento más difícil al trabajar con la transmisión de audio en el Xilinx Zynq 7020 fue que en la parte del procesador de este sistema básicamente no hay bus I2S en el chip, por lo que tuve que encontrar el núcleo I2S. Esta tarea se complicó por la condición de que el núcleo de ip debería estar libre.
Nos decidimos por varios núcleos ip. Encontrado para el núcleo de metal desnudo I2S
Digilent . Encontramos varios núcleos de IP en núcleos
abiertos y, probablemente, la mejor opción para nosotros es el núcleo de ip de
Analog Devices . Producen núcleos ip para sus equipos, para la interacción FPGA / FPGA.
Estamos interesados en el ip-core llamado
AXI-I2S-ADI. Analog Devices está promoviendo estos núcleos ip para sus plataformas de hardware.
Lista total de casos de uso:
- Metal desnudo: núcleo IP para I2S (audio Digilent ZYBO)
- opencores.org
- Controlador AXI-I2S-ADI (dispositivos analógicos)
Núcleo IP AXI-I2S-ADI
El núcleo de ip en sí se ve así: tiene las líneas bclk, wclk, din, dout. Se conecta al DMA Xilinx Zynq 7000, en nuestro ejemplo, se utiliza la parte DMA PS. Todo el intercambio de datos ocurre a través de DMA. DMA puede ser una unidad independiente o una parte integral de PS SoC.
Al configurar este kernel ip, es importante no olvidar enviar la frecuencia maestra mclk a tlv320aic34, como una opción al usar el kit de depuración para tlv320aic34: enviar una frecuencia maestra externa.
Bloque de funciones con axi-i2s-adi conectadoDespués del procedimiento de configuración, la tarea consistía en iniciar la funcionalidad en el sistema operativo Linux.
Configuración de i2c (tlv320aic34 está configurado en esta interfaz):
i2c0: i2c@e0004000 { ... tlv320aic3x: tlv320aic3x@18 { #sound-dai-cells = <0>; compatible = "ti,tlv320aic3x"; reg = <0x18>; gpio-reset = <&axi_gpio_0 0 0>; ai3x-gpio-func = <&axi_gpio_0 1 0>, /* AIC3X_GPIO1_FUNC_DISABLED */ <&axi_gpio_0 2 0>; /* AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT */ AVDD-supply = <&vmmc2>; DRVDD-supply = <&vmmc2>; IOVDD-supply = <&vmmc2>; DVDD-supply = <&vmmc2>; ai3x-micbias-vg = <1>; }; ... };
Configuración de i2s (los datos de audio se transmiten a través de esta interfaz):
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"; };
Configuración de la tarjeta de sonido en el árbol de dispositivos (tarjetas de audio):
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>; }; }; };
Después de todas las manipulaciones para configurar y configurar el códec en el árbol de dispositivos en Linux, apareció la codiciada tarjeta de audio y pudimos escuchar música (nuestra primera pista de música fue Highway to Hell, AC / DC).
Esto es lo que tuvimos que hacer para esto:
- Generó la frecuencia necesaria usando clk_wiz (asistente de sincronización)
- DTS configurado correctamente para tlv320aic34
- Soporte agregado para el controlador tlv320aic3x
- Se agregaron paquetes de audio a buildroot para reproducir audio (aplay, madplay, etc.)
En el proceso de desarrollo del dispositivo final, nos enfrentamos a la tarea de conectar 4 microcircuitos tlv320aic34. El chip tlv320aic34 descrito anteriormente contiene 2 bloques para trabajar con la transmisión de audio, cada bloque tiene su propia línea i2c para configurar y configurar los parámetros de audio. Un bloque puede tener solo cuatro direcciones, respectivamente, es imposible conectar cuatro microcircuitos tlv320aic34 a una interfaz i2c, debe usar dos interfaces i2c (8 bloques de audio independientes). Para cada bloque, si inicia mclk, blck, wclk, din / dout individualmente, debe agregar 40 líneas de señal en total, lo cual es imposible e irracional desde el punto de vista del circuito para el módulo som que elegimos, porque además de estas señales, tenía que conectar muchas otras líneas y interfaces
Como resultado, decidimos cambiar la tarjeta de audio al
modo TDM , en el que se combinan todas las líneas mclk, bclk, din, dout, lo que reduce el número total de líneas de comunicación. Esta decisión afectó la operación de axi-i2s-adi, debido al hecho de que el núcleo de ip funcionaba en modo maestro. Además, este cambio no nos permitió usar nuestro ip-core en modo TDM, y una decisión decidida fue abandonar el uso del ip-core seleccionado. Tuve que escribir un kernel ip para escuchar el tráfico de i2s y enviarlo a dma, esta solución nos permitió crear una interfaz común para recibir datos que no dependería del tipo de tarjeta para grabar llamadas (tarjetas analógicas y digitales).
La arquitectura inicial para recibir la transmisión de audio y su procesamiento a través de la interfaz i2s:

La arquitectura final para recibir la transmisión de audio y su procesamiento a través de la interfaz i2s:

Arquitectura de recibir un flujo PRI y su procesamiento:

Arquitectura de recepción y procesamiento de secuencias BRI:

Axi dma
Este es un elemento importante del sistema de sincronización de datos para dma.
Ventana de configuración de AXI DMA en Xilinx VivadoEn la pantalla de impresión, se presenta el bloque AXI DMA. Tiene muchos parámetros. Puede configurar en el bus la cantidad de datos a transferir. Los datos se pueden alinear o en cualquier formato. Una descripción detallada de la operación e interacción con axi dma se describe
en la documentación técnica (de una versión a otra hay una adición y corrección de imprecisiones en la descripción, así como el refinamiento de los núcleos ip).
Verifique la transferencia de datos a través de las opciones de prueba AXI DMA, AXI DMA
Al desarrollar el controlador, decidimos buscar código abierto y adaptarlo a nuestra tarea. Como resultado, elegimos las fuentes del
proyecto github ezdma (juego de palabras, leer como dma fácil).
El siguiente paso es el desarrollo de un controlador de prueba, fue una etapa preparatoria antes del momento en que nos llegó un núcleo de ip con funcionalidad preparada del departamento de desarrollo de FPGA (el proceso de desarrollo descrito fue formado por programadores integrados). Antes de este momento, decidimos tomar AXI DMA, AXI DATA FIFO y hacer un loopback para asegurarnos de futuros errores. Enrollamos el envío y la recepción de datos, por lo que verificamos el resultado de nuestro trabajo y el rendimiento de nuestro controlador. Adaptamos un poco la funcionalidad, la adaptamos a nuestros deseos en la interfaz de interacción y una vez más verificamos la operatividad del controlador y el principio de interacción seleccionado.
Diseño de bloque retrospectivo, la primera forma de probar AXI DMAUn ejemplo de una descripción de DMA y ezdma en un árbol de dispositivos:
/ { 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) }; ... }; };
Puede generar fácilmente archivos dts / dtsi utilizando la
herramienta Device Tree Generator .
El segundo paso en nuestro proceso de desarrollo es la creación de un kernel ip de prueba para verificar el rendimiento del controlador, solo que esta vez los datos serán significativos, con la transferencia a través de AXIS a AXI_DMA (como lo estará en la versión final del kernel ip).
Flujo de trabajo de la interfaz AXISImplementamos dos variantes de núcleos ip para la generación de datos, la primera versión probada se implementa a través de verilog, la segunda, en HLS (en este contexto, HLS apareció bajo el lema "estilo-moda-juventud").
El generador de datos verilog (y generalmente en los idiomas de la familia hdl: verilog, vhdl, etc.) es una solución estándar cuando se desarrollan núcleos ip de este tipo. Aquí hay algunos fragmentos de código para el núcleo de IP intermedio:
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
No hay necesidad de una descripción más detallada, ya que esta es una tarea típica de un diseñador de FPGA.
Una "bestia" más interesante aquí es HLS.
Vivado HLS (High Level Synthesis) es el nuevo software Xilinx CAD para crear dispositivos digitales que utilizan lenguajes de alto nivel como OpenCL, C o C ++.
C / C ++ son los lenguajes principales para un ingeniero de software integrado, por lo que resolver un problema usando estos lenguajes es más interesante en términos de implementación y análisis comparativo para proyectos futuros.
Aquí hay dos pequeños ejemplos de trabajo con HLS. El primer ejemplo es un generador de datos para AXI_DMA, el segundo ejemplo es el intercambio de datos entre la parte del procesador y la lógica programable a través de la interfaz s_axilite.
El intercambio de datos a través de la interfaz s_axilite (el segundo ejemplo) se implementó para que en cualquier momento en procfs fuera posible restar qué bitstream estaba cargado, y para que fuera posible rastrear la corrección del trabajo versionando para la parte PL del SoC. Aquí aparece un punto muy interesante con s_axilite: Vivado HLS genera un controlador para Linux (el controlador, a su vez, nos adaptamos para trabajar a través de procesos para preservar la herencia de la escritura). A continuación se muestra un ejemplo del código generado para Linux (la ruta a la solución de origen1 / impl / ip / drivers / name_xxx / src /).
Etapas de síntesis HLS y generación de código rtlGenerador de datos HLS para verificar la operación con 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); }
Un ejemplo de obtención de versiones y tipo de placa de interfaz:
#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; }
Como notó, para el desarrollo en hls es muy importante comprender el trabajo y la aplicación de varios pragmas (pragma HLS), ya que el proceso de síntesis está directamente relacionado con los pragmas.
Controlador generado para s_axilita:
Un archivo importante que le indica la ubicación de las variables (registros) en el espacio de direcciones es el archivo x # your_name # _hw.h. Siempre puede verificar la corrección del núcleo de IP escrito utilizando la herramienta devmem.
El contenido de este archivo:
Este archivo describe las direcciones de los registros, los registros corresponden a la ubicación de los argumentos en la función. Después de la síntesis del proyecto, puede ver cómo se ejecutará el proyecto creado en ciclos.
Proyecto Beat EjemploTrabajar con hls ha demostrado que esta herramienta es adecuada para resolver tareas rápidamente, especialmente se ha demostrado para resolver problemas matemáticos de visión por computadora, que se pueden describir fácilmente en C ++ o C, así como para crear pequeños núcleos de IP para interacciones e intercambios información con interfaces FPGA estándar.
Al mismo tiempo, HLS no es adecuado para implementar interfaces de hardware específicas, por ejemplo, en nuestro caso fue I2S, y el código rtl generado ocupa más espacio en FPGA que el escrito en lenguajes hdl estándar.
El último paso en la prueba de controladores es el desarrollo de un generador de tráfico I2S. Este ip-core repite la funcionalidad de los núcleos ip anteriores, excepto que genera datos incrementales (tráfico) que corresponden a datos I2S reales en modo TDM.
Diseño de bloques para futuras pruebas de núcleo I2S personalizadas y generador de tráfico I2SComo resultado, obtuvimos los resultados de hls, axi dma y s_axilite, verificamos el rendimiento de nuestro software y controladores.
Conclusiones
Logramos desarrollar los tipos necesarios de tarjetas de interfaz, así como los núcleos ip para tdm, pri, bri. Hemos mejorado significativamente el enfoque actual para el desarrollo de dichos dispositivos y hemos creado una solución integral que puede competir con placas de interfaz similares de
Asterick ,
Patton y otros. La ventaja de nuestra solución es que el desarrollador no necesita un enlace intermedio entre la PC y el PCI para la transferencia de datos, podrá transmitir directamente la información recibida a través de Ethernet.