
En artículos anteriores, ya nos reunimos con el bus
Avalon-MM , donde MM significa Memory Mapped, es decir, proyectado en la memoria. Este neumático es bastante versátil. Se pueden conectar varios dispositivos maestros (Master) y varios esclavos. Ya conectamos dos dispositivos líderes a la vez (Instruction Master y Data Master), porque el procesador NIOS II tiene una arquitectura Harvard, por lo que los buses de comando y datos son diferentes, pero muchos autores los conectan al mismo común para simplificar el desarrollo de software desde el exterior al bus
Si un bloque en el bus tiene la funcionalidad de acceso directo a memoria (DMA), también contendrá un maestro para el bus.
En realidad, el principal inconveniente de este neumático se basa en este hecho (muchos líderes, muchos seguidores). Cuando diseñamos nuestro esclavo, tuvimos que decodificar la dirección. Cuando resultó ser mi líder, había mucho más alboroto con el arbitraje. Pero un hilo rojo en toda la serie de artículos es la afirmación de que el desarrollo para Redd es una parte auxiliar del proyecto, no debería requerir demasiado trabajo. Y si podemos liberarnos de la rutina, debemos liberarnos de ella.

Todos los artículos del ciclo:
- Desarrollo del "firmware" más simple para FPGAs instalados en Redd, y depuración utilizando la prueba de memoria como ejemplo
- Desarrollo del "firmware" más simple para FPGAs instalados en Redd. Parte 2. Código del programa
- Desarrollo de su propio núcleo para incrustar en un sistema de procesador basado en FPGA
- Desarrollo de programas para el procesador central Redd sobre el ejemplo de acceso a la FPGA
El documento de
especificaciones de la interfaz de Avalon que ya conocemos (en general, no doy enlaces directos, ya que siempre cambian, por lo que toda la red está llena de artículos con enlaces muertos, es más fácil encontrar la posición actual al ingresar el nombre en el motor de búsqueda) informa que, además del autobús
Avalon-MM , También hay un autobús
Avalon-ST , donde ST significa Stream, es decir, streaming. El hecho es que muy a menudo los datos transmitidos tienen una estructura de flujo. Sí, incluso el sector clásico del disco duro. Tiene un tamaño fijo. Se debe pasar de principio a fin. Incluso si lo consideramos en el área direccionable, entonces las direcciones aumentarán linealmente. Y si usa el bloque FIFO para el almacenamiento, entonces las direcciones dentro de él están completamente ocultas para nosotros. Lo son, pero trabajar con ellos no es nuestra preocupación.
Lo mismo se aplica a muchos otros datos de transmisión: siempre van de principio a fin y se colocan en repositorios secuencialmente. Esto es exactamente lo que los protocolos de transmisión se utilizan para transferir dichos datos. Además de la falta de direccionamiento explícito, el bus
Avalon-ST es interesante porque siempre conecta dos dispositivos: una fuente y un receptor. Siempre hay dos de ellos. Un dispositivo es siempre la fuente, el segundo es siempre el receptor. Por lo tanto, los problemas con el arbitraje de este autobús no se refieren. Así es como se ven los pares de dispositivos típicos conectados a este bus:

Y aquí están las señales típicas de este autobús:

Además, las líneas de
error son opcionales, transmiten códigos de error binarios asignados por nosotros y podemos decir que no hay códigos de error. Y las líneas de número de canal, como vimos anteriormente, solo son necesarias si la demultiplexación se realiza más. Si no, el número de canal no es necesario. Lo haremos sin él por ahora. Quedan tres líneas: de hecho, datos, una señal de disponibilidad y una señal de confirmación de datos (luz estroboscópica). Bueno, otra señal de reloj, ya que el bus está sincronizado.
De la documentación también se deduce que son posibles tres señales más, agregando al bus las propiedades de transmisión de distintos paquetes:

En general, el neumático es muy interesante, y hoy comenzaremos a experimentar con él. Como ya sabemos, el FPGA está conectado al bus USB del complejo Redd a través del puente
FT2232H que funciona en modo
FT245-SYNC . Estrictamente hablando, los datos que pasan a través de esta interfaz están transmitiendo datos por completo. Hoy aprenderemos cómo transferir estos datos a nuestro sistema de procesador basado en NIOS II. Es una pena que el protocolo
FT245-SYNC , a pesar de la transmisión, no cumpla totalmente con el bus
Avalon-ST . Para salvar las patas del chip, tiene un bus de datos bidireccional, y el bus
Avalon-ST es unidireccional. Entonces, tenemos que hacer un bloque que coordine los protocolos cercanos pero no coincidentes.
Ya nos familiarizamos con el protocolo
FT245-SYNC en uno
de los artículos anteriores . Permítame recordarle que su descripción se puede encontrar en el documento
AN_130 FT2232H utilizado en un modo FIFO síncrono de estilo FT245 . Aquí hay un diagrama de tiempo típico de transmisión desde un puente a un FPGA

En general, como programador, estoy muy interesado en el hecho de que el paquete transmitido habría marcado claramente el inicio y el final. Bueno, para que sea más lógico en lógica al protocolo UDP, ya que si la transferencia es en el estilo TCP, tendrá que agregar datos de referencia especiales a la transmisión, que se gastarán en mi programación, esfuerzos y ciclos de procesador ... Parece que la línea RXF nos puede ayudar con esto. Verificamos ... Completamos el "firmware" en el FPGA para medir el rendimiento, realizado en el
artículo anterior , y conectamos la sonda del osciloscopio a la línea RXF. Como programa de prueba para el procesador central Redd, utilizamos la base, también utilizada para medir el rendimiento, en lugar de enviar grandes cantidades de datos, enviamos un bloque monolítico de 0x400 bytes.
uint8_t temp [maxBlockSize]; memset (temp,0,sizeof (temp)); uint32_t dwWritten; FT_Write(ftHandle0, temp, 0x400, &dwWritten);
Obtenemos la siguiente imagen en la línea RXF:

Está claro que el microcircuito recibe 0x200 bytes de búfer (es decir, cuánto puede venir en un paquete USB2.0 HS), luego los envía al canal. En general, esto es extraño, ya que la documentación establece que se usan dos memorias intermedias en cada dirección. Durante la transmisión, el segundo búfer debería haber tenido tiempo de llenarse. Por desgracia El final de su llenado es claramente tarde. En realidad, esto muestra por qué el rendimiento no alcanza los 52 megabytes teóricos por segundo: un gran porcentaje del tiempo (aunque no el 50%) simplemente no se transmite.
Pero de una forma u otra, descubrimos que es posible detectar el comienzo de un paquete en un borde RXF negativo solo si el tamaño del paquete no supera los 0x200 bytes. Si enviamos solo comandos con una pequeña cantidad de datos al dispositivo, esto es bastante factible. Pero si enviamos grandes flujos de datos, tendremos que usar un canal continuo, similar en lógica a UART (o, digamos, al canal TCP), destacando los límites de los paquetes de manera puramente programática.
En general, para simplificar la presentación, tomamos como base la versión de transmisión. No consideraremos paquetes hoy. Bueno, qué versión del autobús
Avalon-ST tomamos como base es clara. Comenzamos a diseñar nuestro bloque. Como se señaló anteriormente, tenemos que hacer no solo un puente, sino un interruptor, porque el bus
FT245FIFO es bidireccional y el bus
Avalon-ST es unidireccional. Es decir, es necesario hacer dos buses
Avalon-ST a la vez: salida y entrada.

Estamos comenzando a desarrollar lentamente un autómata que implementará la lógica que necesitamos. Por supuesto, en el artículo esta lógica se simplificará al máximo. Comencemos con la transferencia de datos desde el FPGA a la PC, ya que este proceso es un poco más simple (no necesita cambiar el estado de la línea OE, de la que hablamos en el
último artículo ). Es decir, estamos implementando el puerto Sink.
Del lado del bus
Avalon-ST , elegí el siguiente modo de operación (hay muchos de ellos en el documento, pero este es el más cercano a la interfaz con el
FT245-SYNC ):

Déjame recordarte la dirección de las señales:

Es decir, solo esperamos la confirmación en el bus (
válido ), hacemos clic en los datos y señalamos este hecho con la línea
preparada .
Desde el lado
FT245_FIFO, el protocolo se ve así:

Resulta que debemos esperar la señal TXE y bloquear los datos con la señal WR # (la polaridad es inversa para ambas señales).
TXE # es muy similar en funcionalidad a
listo , y WR # es
válido . Los detalles son un poco diferentes, pero la lógica es similar.
Resulta que podemos seleccionar un solo estado para PC, en el que se realizarán las conmutaciones más simples de algunas líneas. La condición para ingresar a este estado será la preparación de ambas partes para la transmisión, es decir (TXE # == 0) Y (válido == 1). Tan pronto como desaparece parte de la preparación, volvemos a estar inactivos.
El gráfico de transición del autómata sigue siendo simple:

Y la tabla de conmutación es así (donde los nombres de las señales son ambiguos, se les agregan índices, donde los nombres son únicos, no hay índices):
Pasando a una transferencia un poco más compleja de Source a FT245_FIFO. Como vimos
en el artículo anterior , la complicación es cambiar de dirección con la señal OE #:

Para el bus
Avalon_ST, todo es igual que antes, por lo que las imágenes no se muestran por segunda vez, pero ahora estamos en la posición de origen.
Aquí, la línea RXF # corresponde a la línea
válida , y la línea RD # corresponde a la línea
preparada . Bueno, está bien, agrega un par de estados a la máquina:

y la siguiente lógica para señales activas en este estado:
Está claro que el esquema no era el más ideal. Hay varios matices asociados con desbordamientos o desbordamientos del búfer. Pero no debería haber ningún tipo de pérdida de datos, pero en cuanto a la optimización, ¡debes comenzar por algún lado!
Comenzamos a transferir la teoría desarrollada al código SystemVerilog. Es cierto que no podemos usar todas las características de SystemVerilog. Fue el caso, escribí un
artículo grande donde probé la sintetización práctica de las hermosas características de este lenguaje con un entorno de desarrollo real. Aquí solo pedimos el uso de interfaces, porque el sistema tendrá dos instancias del
tipo Avalon-ST . Ay y ah. Aquí está el código de prueba:
interface AvalonST #(parameter width=8)(input clk); logic [width-1:0] data; logic ready; logic valid; modport source (input clk, ready, output data,valid); modport sink (input clk, data, valid, output ready); endinterface module FT245toAvalonST ( AvalonST.source source, AvalonST.sink sink ); //assign source.ready = sink.valid; assign sink.ready = source.valid; endmodule
Está perfectamente sintetizado en el compilador principal (una línea comentada al eliminar un comentario provoca un error para asegurarse de que el sintetizador interprete todo correctamente), pero cuando se marca el botón
Analizar archivos de síntesis para un componente de este código, se
genera un error que desconoce el tipo
AvalonST . Es decir, el análisis no está en SystemVerilog, sino en Verilog puro. Que pena.

Además, el idioma se determina correctamente, solo el analizador no comprende las interfaces entre los puertos.

En general, tienes que usar la sintaxis vieja y fea.
Con esta sintaxis obtenemos la siguiente interfaz de módulo: module FT245toAvalonST ( input clk, input reset, inout [7:0] ft245_data, input logic ft245_rxf, input logic ft245_txe, output logic ft245_rd, output logic ft245_wr, output logic ft245_oe, output logic ft245_siwu, input logic source_ready, output logic source_valid, output logic[7:0] source_data, output logic sink_ready, input logic sink_valid, input logic[7:0] sink_data );
Grosero, vintage, pero ¿qué puedes hacer?
Realizamos el gráfico de transición del autómata sin lujos: // enum {idle, toPC, dropOE, fromPC} state = idle; // always_ff @(posedge clk,posedge reset) begin if (reset == 1) begin state <= idle; end else begin case (state) idle: begin if ((ft245_txe == 0) && (sink_valid == 1)) state <= toPC; else if ((ft245_rxf == 0)&&(source_ready == 1)) state <= dropOE; end toPC: begin if (!((ft245_txe == 0) && (sink_valid == 1))) state <= idle; end dropOE: begin state <= fromPC; end fromPC: begin if (!((ft245_rxf == 0)&&(source_ready == 1))) state <= idle; end endcase end end
El control de las salidas, sin embargo, requiere alguna explicación.
Parte de las instalaciones se realiza "en la frente": // // , , // - . always_comb begin ft245_oe <= 1; ft245_rd <= 1; ft245_wr <= 1; source_valid <= 0; sink_ready <= 0; // , // assign- case (state) idle: begin end toPC: begin ft245_wr <= !(sink_valid); sink_ready <= !(ft245_txe); end dropOE: begin ft245_oe <= 0; end fromPC: begin ft245_oe <= 0; ft245_rd <= !(source_ready); source_valid <= !(ft245_rxf); end endcase end
Pero, digamos, para un bus de datos bidireccional, se debe aplicar una solución típica. Como recordamos, se declara en la parte de la interfaz de la siguiente manera:
inout [7:0] ft245_data,
y leerlo se puede hacer de la manera habitual. Para nuestro caso, simplemente envolvemos todos los datos en los datos del bus
Avalon-ST saliente:
// assign source_data = ft245_data;
Pero en general, siempre puedes leer desde el autobús y de la manera que quieras. Pero deberías escribirle usando el multiplexor. Cuando escribimos datos en el bus, estos datos deben provenir de cualquier otro bus preparado previamente. Por lo general, en un módulo se inicia una variable de tipo
reg (o
lógica nueva). En nuestro caso, ese autobús ya existe. Este es el bus
sink_data . En otros casos, se emite el estado Z. Si está familiarizado con los circuitos, conoce bien un búfer de salida típico. O bien se salta cualquier dato de entrada o pasa al estado Z. En nuestro código, este multiplexor se ve así:
// inout- assign ft245_data = (state == toPC) ? sink_data : 8'hzz;
Y otra señal ft245_siwu. Nunca lo usamos, así que de acuerdo con la documentación en FT2232H, llévelo a la unidad:
// FTDI : // Tie this pin to VCCIO if not used. assign ft245_siwu = 1;
En realidad, eso es todo.
Todo el módulo se ve así: module FT245toAvalonST ( input clk, input reset, inout [7:0] ft245_data, input logic ft245_rxf, input logic ft245_txe, output logic ft245_rd, output logic ft245_wr, output logic ft245_oe, output logic ft245_siwu, input logic source_ready, output logic source_valid, output logic[7:0] source_data, output logic sink_ready, input logic sink_valid, input logic[7:0] sink_data ); // enum {idle, toPC, dropOE, fromPC} state = idle; // always_ff @(posedge clk,posedge reset) begin if (reset == 1) begin state <= idle; end else begin case (state) idle: begin if ((ft245_txe == 0) && (sink_valid == 1)) state <= toPC; else if ((ft245_rxf == 0)&&(source_ready == 1)) state <= dropOE; end toPC: begin if (!((ft245_txe == 0) && (sink_valid == 1))) state <= idle; end dropOE: begin state <= fromPC; end fromPC: begin if (!((ft245_rxf == 0)&&(source_ready == 1))) state <= idle; end endcase end end // // , , // - . always_comb begin ft245_oe <= 1; ft245_rd <= 1; ft245_wr <= 1; source_valid <= 0; sink_ready <= 0; // , // assign- case (state) idle: begin end toPC: begin ft245_wr <= !(sink_valid); sink_ready <= !(ft245_txe); end dropOE: begin ft245_oe <= 0; end fromPC: begin ft245_oe <= 0; ft245_rd <= !(source_ready); source_valid <= !(ft245_rxf); end endcase end // - c , ... // FTDI : // Tie this pin to VCCIO if not used. assign ft245_siwu = 1; // inout- assign ft245_data = (state == toPC) ? sink_data : 8'hzz; // assign source_data = ft245_data; endmodule
Cómo incluir el módulo en la lista de disponibles para usar en el sistema del procesador, lo examinamos en detalle en uno de los
artículos anteriores , así que solo muestro el resultado en la figura. Recuerdo que para lograrlo tuve que agregar dos buses
AVALON-ST , un bus
Conduit , extraer señales de un bus
AVALON-MM erróneamente definido, y cuando no quede una sola señal en ese bus, simplemente elimínelo. En el camino, la figura muestra la configuración que seleccioné para los buses
AVALON-ST (8 bits por símbolo, sin errores, el canal máximo es cero, la latencia es cero).

Con el desarrollo de un módulo para atracar neumáticos, eso es todo. Pero por desgracia, ah. Desarrollar es solo el comienzo del trabajo. La implementación es mucho más difícil. Como se puede ver desde la posición del desplazador en la pantalla, el final del artículo aún está lejos. Entonces, estamos comenzando a crear un proyecto simple que utiliza la
unión de bus
FT245-SYNC con los buses
AVALON-ST . Es lo mas simple. Un proyecto serio no encaja en el marco de un solo artículo de un tamaño razonable. Ahora haré una simplificación tras simplificación simplemente para que la atención de los lectores sea suficiente para el resto del texto y que no dejen de leer en una palabra. La primera simplificación es que los
relojes de 60 MHz para el
FT245_SYNC son generados por el chip
FT2232H . Podría agregar dos líneas de reloj al sistema, pero tan pronto como todos lo vean, tendremos tales telarañas de cables que mi madre no llorará. Si sigo prestando atención a las diferentes líneas de reloj, todos nos confundiremos. Por lo tanto, simplemente anuncio que hoy nuestro sistema de procesador
registrará desde el chip
FT2232H y no desde un generador normal.
¿Por qué no siempre puedes hacer eso? Muy simple: siempre que el
FT2232H no
esté en modo 245_SYNC, no tiene estos pulsos en la salida. Es decir, primero debe ejecutar el programa para el procesador central, y solo luego cargar todo en el FPGA. Si creáramos un sistema para un cliente externo, tal solución crearía muchos problemas. Sé por experiencia que nos llamaban regularmente y decían que nada funciona, que recordamos los bares, pero eso ayudaría por un tiempo. Pero estamos haciendo algo interno, y lo usaremos solo en condiciones de laboratorio. Es decir, dentro del marco de esta tarea, esto es permisible.
Pero esto trae nuevos desafíos. Tenemos una frecuencia de 60 MHz, y el bloque de reloj SDRAM que estamos usando actualmente está estrechamente vinculado a una frecuencia de 50 MHz. Sí, verifiqué, se pueden enviar 60, pero supongamos que intentamos no ir más allá de los modos permitidos. En artículos posteriores intentaré mostrar cómo reemplazar este bloque duro, pero hoy solo decimos que, dado que nuestra unidad de reloj de SDRAM no puede funcionar en la frecuencia utilizada, la excluimos del sistema de procesador SDRAM. El programa y sus datos se ubicarán completamente en la memoria interna de la FPGA. Se descubrió experimentalmente que en la configuración actual, los FPGA pueden ocupar un máximo de 28 kilobytes de RAM para este negocio. Resulta que puedes tomar volúmenes y no múltiples poderes de dos ...
Además, utilizaremos el reloj estándar y la unidad de reinicio. Se restablece un poco diferente al que usamos para SDRAM. Para no complicar el artículo, aprovecharé el hecho de que el sistema en desarrollo siempre funcionará bajo el control de un depurador, por lo que comenzaré a restablecer el subsistema JTAG para la depuración.
En total, obtenemos un esquema del sistema de procesador base (la línea de reinicio más difícil se resalta en este momento, el marcador azul está en la fuente de señal):

donde la frecuencia se ha ajustado para el reloj y el bloque de reinicio:

y para RAM - el volumen:

Hoy necesitamos mostrar el texto en la terminal. Por lo tanto, agregaremos un bloque tan interesante al sistema:

Con este bloque, podremos llamar a funciones similares a printf. Además del bus AVALON_MM, también debe conectar la salida de solicitud de interrupción.

Eso es todo, la adquisición del sistema del procesador se ha completado. Es hora de incrustar nuestra unidad. ¿A dónde enviará los datos? Entre los bloques disponibles para nosotros, hay una memoria FIFO de dos puertos muy interesante. Su encanto radica en el hecho de que un puerto se puede configurar en modo
AVALON-ST y conectarlo a nuestra unidad, y el segundo en modo
AVALON_MM y trabajar con él utilizando el procesador NIOS II. Este maravilloso bloque se encuentra aquí:

Tenemos dos
buses Avalon-ST (uno para leer, el otro para escribir), por lo que también necesitamos dos bloques FIFO. Ahora analizaré uno de ellos con gran detalle, enrollamos un par de kilómetros de web (y un montón de pantallas de texto con imágenes), y alrededor del segundo decimos que "se puede hacer por analogía", indicando solo diferencias. Por lo tanto, por ahora, agregamos solo un bloque al sistema y observamos su configuración. Hay muchas configuraciones. Sería posible simplemente indicar los valores requeridos para que todos se refieran al artículo como referencia, pero de repente alguien entra en una situación que necesita ser configurada, pero no hay acceso a la red (y, por lo tanto, al artículo). Por lo tanto, agregaré configuraciones de forma iterativa. Primero obvio, luego, según lo exija el sistema, ejecute el diálogo una y otra vez. Entonces todos sentirán el proceso y podrán repetirlo en cualquier momento. Entonces Por defecto, nos dieron la siguiente configuración:

Ahora haré FIFO, que recopila datos de
Avalon-ST y los carga en
Avalon-MM . Resulta que la primera edición será así:

Recibí esta interesante advertencia:

Resulta que cuando al menos uno de los puertos se proyecta en la memoria, el ancho del bus
Avalon-ST debe ser estrictamente de 32 bits. Y tenemos un bus de 8 bits. Cómo acordar las profundidades de bits, te diré un poco más abajo, pero por ahora estamos haciendo un bus de 32 bits con un carácter de ocho bits aquí. Bueno, deshabilite el modo por lotes, como se decidió en la parte teórica.

Lo siguiente es la capacidad. Supongamos que pongo en cola 256 palabras (es decir, 1024 bytes):

Ahora el estado. Al principio, no le di ninguna importancia, y el programa se congeló fuertemente. Entonces ahora sé que se necesita el estado. Dado que trabajaremos con el puerto de salida mediante programación, agregaremos el estado correspondiente.

y capta el error:

Pues bien. Añadir doble reloj. Simplemente conecte ambas entradas a la misma línea de reloj, ya que tenemos una.
Uhhhh Total tenemos:

Pero es demasiado pronto para conectar este negocio al sistema común. Como descubrimos, el bus
Avalon-ST de 8 bits deja el bloque que desarrollamos, y esto debería incluir el de 32 bits. Como seamos Remodelar su bloque? No! Todo se ha hecho antes que nosotros. Esto es lo que nos ayudará:

Agréguelo al sistema. Además, dado que es una capa, puramente por belleza, la colocamos entre nuestro bloque y FIFO, usando la flecha correspondiente:

Hacemos los siguientes ajustes: en la entrada tenemos un bus de 8 bits, en la salida de 32 bits. Las señales de paquete no se usan, se usan señales
listas y
válidas .

Es hora de tejer una red. Primero, colocaré las líneas de transmisión (en la figura, ambas están resaltadas, los marcadores están en los receptores de datos):

Es decir, la señal de la Fuente de nuestro bloque va a la entrada del adaptador. Y desde la salida del adaptador hasta la entrada FIFO. Como dije, todas las conexiones en el protocolo de transmisión se realizan punto a punto.
Bueno, ahora colgamos las líneas de reinicio, las líneas del reloj, y también conectamos todo al bus del sistema y a las interrupciones ...

Bueno ... Y ahora, por el mismo principio, agregamos FIFO para emitir datos al
FT245SYNC . Solo allí, los datos ingresan al FIFO desde
Avalon-MM en forma de 32 bits. Pasan a través de un adaptador 32 en 8 y luego llegan a la entrada SINK de nuestro bloque, que no está conectado en el circuito actual ... Obtenemos el siguiente fragmento del circuito final (la memoria allí resultó con un solo reloj):

Otros trámites que ya hemos resuelto bien en los experimentos descritos en artículos anteriores (
en su mayor parte, en este ). Asignamos vectores al procesador. Para el sistema, llamamos a la asignación automática de números de interrupción y direcciones. Guardamos el sistema ... ¿Todos recuerdan que el nombre del sistema guardado debe coincidir con el nombre del proyecto para que el sistema esté en el nivel superior de la jerarquía? Agregue el sistema al proyecto, haga un borrador del proyecto, asigne piernas. Personalmente, hice trampa: copié las asignaciones del archivo * .qsf del borrador del proyecto al final actual (y puede tomar mi proyecto y copiar las líneas * .qsf correspondientes a las suyas, pero simplemente puede asignar todas las patas a través de la GUI). Presto especial atención al hecho de que la señal clk está conectada al tramo 23, no al 25, como en proyectos anteriores. Les recuerdo que aquí estamos haciendo tictac desde la salida FT2232.

Genial El hardware está listo. Pasamos al software. Por donde empezamos Hoy esta pregunta no vale la pena. Si comenzamos con un programa que se ejecuta en el procesador NIOS II, nada funcionará para nosotros. Primero, debemos poner el FT2232 en modo 245-SYNC, solo entonces nuestro sistema de procesador recibirá pulsos de reloj. Por lo tanto, comenzamos con el código para el procesador central.
Obtenemos algo como esto: #include <cstdio> #include <sys/time.h> #include <unistd.h> #include "ftd2xx.h" FT_HANDLE OpenFT2232H() { FT_HANDLE ftHandle0; static FT_DEVICE ftDevice; // int nDevice = 0; while (true) { // if (FT_Open(nDevice, &ftHandle0) != FT_OK) { printf("No FT2232 found\n"); // , return 0; } // ? if (FT_GetDeviceInfo(ftHandle0, &ftDevice, NULL, NULL, NULL, NULL) == FT_OK) { // , if (ftDevice == FT_DEVICE_2232H) { // , AN130 FT_SetBitMode(ftHandle0, 0xff, 0x00); usleep(1000000); //Sync FIFO mode FT_SetBitMode(ftHandle0, 0xff, 0x40); FT_SetLatencyTimer(ftHandle0, 2); FT_SetUSBParameters(ftHandle0, 0x10000, 0x10000); return ftHandle0; } } // FT_Close(ftHandle0); // nDevice += 1; } printf("No FT2232 found\n"); } int main() { FT_HANDLE ftHandle0 = OpenFT2232H(); if (ftHandle0 == 0) { printf("Cannot open device\n"); return -1; } int item; bool bWork = true; while (bWork) { printf("1 - Send 16 bytes\n"); printf("2 - Send 256 bytes\n"); printf("3 - Receive loop\n"); printf("0 - Exit\n"); scanf("%d", &item); switch (item) { case 0: bWork = false; break; case 1: { static const unsigned char data[0x10] = { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }; DWORD dwWritten; FT_Write(ftHandle0, (void*)data, sizeof(data), &dwWritten); } break; case 2: { unsigned char data[0x100]; for (size_t i = 0; i < sizeof(data); i++) { data[i] = (unsigned char)i; } DWORD dwWritten; FT_Write(ftHandle0, (void*)data, sizeof(data), &dwWritten); } break; case 3: { DWORD dwRxBytes; DWORD dwRead; DWORD buf[0x100]; while (true) { FT_GetQueueStatus(ftHandle0, &dwRxBytes); if (dwRxBytes != 0) { printf("Received %d bytes (%d DWORDs)\n", dwRxBytes, dwRxBytes / sizeof(buf[0])); if (dwRxBytes > sizeof(buf)) { dwRxBytes = sizeof(buf); } FT_Read(ftHandle0, buf, dwRxBytes, &dwRead); for (DWORD i = 0; i < dwRxBytes / sizeof(buf[0]);i++) { printf("0x%X, ",buf[i]); } printf("\n"); } } } break; } } // , FT_Close(ftHandle0); return 0; }
OpenFT2232H() . FT2232 . , NIOS II. , , . (1), (2), (3). , , . , 8--32. . — .
NIOS II BSP. , Hello World Small. BSP ( BSP,
). , , , Settings, .

Generate BSP , ,
hello_world_small.c hello_world_small.cpp , , .
( , FIFO, — , , ). . — NIOS II. :
extern "C" { #include "sys/alt_stdio.h" #include <system.h> #include <altera_avalon_fifo_util.h> } #include <stdint.h> int main() { while (1) { int level = IORD_ALTERA_AVALON_FIFO_LEVEL(FIFO_0_OUT_CSR_BASE); if (level != 0) { alt_printf("0x%x words received:\n",level); for (int i=0;i<level;i++) { alt_printf("0x%x,",IORD_ALTERA_AVALON_FIFO_DATA (FIFO_0_OUT_BASE)); } alt_printf("\n"); } } /* Event loop never exits. */ while (1); return 0; }
FIFO. , .
. , . Redd «» , NIOS II. :

, . , . , , . FT2232, , . . :
FT245-SYNC .
1. , :
0x2 words received:
0x3020100,0x7060504,
0x2 words received:
0xb0a0908,0xf0e0d0c,:
0x3 words received:
0x3020100,0x7060504,0xb0a0908,
0x1 words received:
0xf0e0d0c,, 1, 3 , . , , . , , JTAG — . , . , ( , ? ), (FIFO — , , ).
Little Endian. , :
static const unsigned char data[0x10] = { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };
Eso es correcto 2, ( , ):
0x3 words received: 0x3020100,0x7060504,0xb0a0908, 0x3d words received: 0xf0e0d0c, 0x13121110,0x17161514,0x1b1a1918,0x1f1e1d1c, 0x23222120,0x27262524,0x2b2a2928,0x2f2e2d2c, 0x33323130,0x37363534,0x3b3a3938,0x3f3e3d3c, 0x43424140,0x47464544,0x4b4a4948,0x4f4e4d4c, 0x53525150,0x57565554,0x5b5a5958,0x5f5e5d5c, 0x63626160,0x67666564,0x6b6a6968,0x6f6e6d6c, 0x73727170,0x77767574,0x7b7a7978,0x7f7e7d7c, 0x83828180,0x87868584,0x8b8a8988,0x8f8e8d8c, 0x93929190,0x97969594,0x9b9a9998,0x9f9e9d9c, 0xa3a2a1a0,0xa7a6a5a4,0xabaaa9a8,0xafaeadac, 0xb3b2b1b0,0xb7b6b5b4,0xbbbab9b8,0xbfbebdbc, 0xc3c2c1c0,0xc7c6c5c4,0xcbcac9c8,0xcfcecdcc, 0xd3d2d1d0,0xd7d6d5d4,0xdbdad9d8,0xdfdedddc, 0xe3e2e1e0,0xe7e6e5e4,0xebeae9e8,0xefeeedec, 0xf3f2f1f0,0xf7f6f5f4,0xfbfaf9f8,0xfffefdfc,
. . NIOS II :
/* - 2 */ uint32_t buf[] = {0x11223344,0x55667788,0x99aabbcc,0xddeeff00}; for (uint32_t i=0;i<sizeof(buf)/sizeof(buf[0]);i++) { IOWR_ALTERA_AVALON_FIFO_DATA (FIFO_1_IN_BASE,buf[i]); }
3 NIOS II. :
Received 16 bytes (4 DWORDs)
0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00,. - .
Conclusión
Avalon-ST . Redd , . .
.
. , , Redd.