Em um
artigo anterior, eu disse que é hora de seguirmos para os protocolos de streaming. Mas, tendo começado a preparar uma história sobre eles, percebi que estou nadando em um tópico muito importante. Como já observamos, meu relacionamento com o Linux é bastante peculiar. Em geral, percebi que eu próprio não posso criar do zero um aplicativo C ++ que satisfizesse todos os princípios de programação do Redd. Você pode pedir a alguém para fazer isso e depois usar o modelo pronto, mas a série de artigos foi projetada para ensinar a todos como se desenvolver no Redd do zero. Por isso, perguntei ao meu chefe (um grande especialista em Linux) e ele me explicou em que clicar. Depois, repensei levemente suas palavras e agora considero necessário consertar todo o conhecimento por escrito. Isso salvará pessoas como eu de pensamentos dolorosos: "Então ... O que ele fez é compreensível, mas como posso repetir isso?" Em geral, qualquer pessoa que trabalha no Linux pode executar as duas seções a seguir na diagonal. É improvável que você encontre algo novo lá (você encontrará mais). E quanto ao resto, ofereço uma escolha de dois métodos de desenvolvimento que correspondem aos princípios declarados de trabalho sob Redd: baixos custos de mão-de-obra para desenvolvimento e depuração remota.

Todos os artigos do ciclo:
- Desenvolvimento do “firmware” mais simples para FPGAs instalados no Redd e depuração usando o teste de memória como exemplo
- Desenvolvimento do “firmware” mais simples para FPGAs instalados em Redd. Parte 2. Código do Programa
- Desenvolvimento de núcleo próprio para incorporação em um sistema de processador baseado em FPGA
Trabalhar com ferramentas do Visual Studio
Acontece que você pode conduzir o desenvolvimento do Linux remoto, sem tê-lo em sua máquina local e sem instalar nenhuma ferramenta de software que não seja da Microsoft. Aqui, é mostrado como ele é colocado no Visual Studio (eu o instalei na versão 2019, mas aparentemente apareceu em 2015)
docs.microsoft.com/ru-ru/cpp/linux/download-install-and-setup- the-linux-development-workload? view = vs-2019E aqui está a
teoria do trabalho .
Bem, você pode andar pelas guias, muita teoria e tudo em russo.
Ótimo! Vamos tentar usar o conhecimento adquirido para acessar o chip FT2232H, através do qual o processador central Redd está conectado ao FPGA. É este canal no futuro que formará a base do nosso trabalho de streaming. Abra o Visual Studio, selecione "Criar Projeto". Nos filtros, selecione "Idioma - C ++", "Plataforma - Linux", "Tipo de projeto - Console". Vou mostrar onde isso já está localizado para o tipo de projeto. Pelo que fomos filtrados, selecionamos o "Aplicativo de console":

Vamos chamá-lo, digamos, SimpleConsole. Criamos um código fonte espartano:
#include <cstdio> int main() { printf("hello from SimpleConsole!\n"); return 0; }
Vamos tentar coletar. E nos fazem uma pergunta muito interessante sobre como estabelecer uma conexão. Esse é um recurso de desenvolvimento no Visual Studio. O ambiente não contém um compilador cruzado e nenhuma biblioteca. Ele simplesmente cria um diretório de origem na máquina remota, após o qual, para cada compilação, copia os arquivos atualizados e inicia a compilação nele. É por isso que a conexão com a máquina remota não deve ser estabelecida para iniciar, mas já para a montagem normal do projeto.
Nós preenchemos os parâmetros para o usuário Redd, em nome de quem o trabalho será realizado no projeto.

Se alguma coisa - os parâmetros podem ser alterados aqui neste local secreto (não tente alterar as propriedades do projeto, isso não levará a nada de bom):


Na verdade, você pode colocar um ponto de interrupção em uma única linha e verificar se o projeto inicia e funciona. Mas isso é trivial. Portanto, passamos a uma tarefa mais interessante - trabalhar com o FT2232. Surge a pergunta: onde obter as bibliotecas necessárias? Para Linux, tudo está incluído no pacote de drivers. Há o próprio driver, bibliotecas e exemplos de aplicativos para trabalhar com eles, e até uma breve instrução. Em geral, dirigimos para o mecanismo de pesquisa:
FTDI D2XX drivers
Ele mostrará onde eles podem ser baixados. Verdade, especificamente, tudo foi ruim para mim. Meu provedor bloqueia o site da FTDI (assim como reprap, 7zip e até osronline), referindo-se ao RosKomNadzor. A ILV, em resposta a declarações, envia cancelamentos de inscrição, dizendo que não estamos bloqueando nada, mas que você mesmo deve lidar com o provedor. Onde somente nessas pessoas que não se inscreveram eles não me ofereceram a ajuda, nem mesmo para a polícia. Eles são muito desinscritos. As tentativas de reclamar sobre a inação do ILV acabam sendo encaminhadas para o ILV, de onde vêm os próximos cancelamentos. Em geral, pode acontecer que o seu provedor também bloqueie o acesso. Não se assuste, basta procurar outras maneiras de baixar, gastando tempo com isso. E então eles ficam surpresos que o desenvolvimento de mísseis esteja atrasado por décadas ... Comecei a correspondência com o ILV em novembro passado, agora é junho, exceto os cancelamentos de inscrição - nenhuma ação ... Mas fiquei distraído.
Como usar os drivers podem ser encontrados no arquivo leia-me, no próprio pacote. Você também pode encontrar o documento
AN_220 FTDI Drivers Installation Guide for Linux . Por fim, você pode encontrar no YouTube um vídeo do
Guia de Instalação do Driver Linux d2xx dos chips FTDI. Um link para ele também está na página de download do driver. Em geral, o FTDI não restringiu as opções para informar os usuários. Na verdade, se você recebeu um pacote Redd pronto, os drivers já estão instalados e configurados nele. E estaremos interessados em arquivos de cabeçalho e exemplos.
Vamos inserir um exemplo de fatia
\ release \ examples \ EEPROM \ read . O início da função
principal , onde o dispositivo é aberto e seu tipo é utilizado. Isso será suficiente para garantir que tudo funcione. Eu odeio rótulos, mas como arrastamos e soltamos rapidamente o código que dura 10 minutos, arrastarei o rótulo que estava no exemplo original para economizar tempo. Acontece assim:
#include <cstdio> #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include "ftd2xx.h" int main(int argc, char* argv[]) { printf("hello from ConsoleApplication1!\n"); FT_STATUS ftStatus; FT_HANDLE ftHandle0; int iport; static FT_PROGRAM_DATA Data; static FT_DEVICE ftDevice; DWORD libraryVersion = 0; int retCode = 0; ftStatus = FT_GetLibraryVersion(&libraryVersion); if (ftStatus == FT_OK) { printf("Library version = 0x%x\n", (unsigned int)libraryVersion); } else { printf("Error reading library version.\n"); return 1; } if (argc > 1) { sscanf(argv[1], "%d", &iport); } else { iport = 0; } printf("Opening port %d\n", iport); ftStatus = FT_Open(iport, &ftHandle0); if (ftStatus != FT_OK) { /* This can fail if the ftdi_sio driver is loaded use lsmod to check this and rmmod ftdi_sio to remove also rmmod usbserial */ printf("FT_Open(%d) failed\n", iport); return 1; } printf("FT_Open succeeded. Handle is %p\n", ftHandle0); ftStatus = FT_GetDeviceInfo(ftHandle0, &ftDevice, NULL, NULL, NULL, NULL); if (ftStatus != FT_OK) { printf("FT_GetDeviceType FAILED!\n"); retCode = 1; goto exit; } printf("FT_GetDeviceInfo succeeded. Device is type %d.\n", (int)ftDevice); exit: return 0; }
Tentando coletar - não indo. Arquivos de cabeçalho insuficientes.
1>main.cpp 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : ftd2xx.h: 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : #include "ftd2xx.h" 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : ^ 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : compilation terminated.
Mas eu os coloco a seguir! Tudo é simples. Localmente, eles estão próximos, mas a montagem está sendo feita remotamente. Para que o Studio os transfira para uma máquina remota, você precisa adicioná-los ao projeto. Além disso, no arquivo
main.cpp eu adiciono apenas
ftd2xx.h , mas ele ainda
puxa o WinTypes.h em trânsito . Você precisa adicionar os dois.


Agora o vinculador jura.
1> 1>D:\Work\SimpleConsole\SimpleConsole\obj\x64\Debug\main.o : error : In function `main': 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(18): error : undefined reference to `FT_GetLibraryVersion' 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(37): error : undefined reference to `FT_Open' 1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(55): error : undefined reference to `FT_GetDeviceInfo' 1>collect2 : error : ld returned 1 exit status
É claro que não há biblioteca suficiente. Inspecionando os
makefiles do exemplo, percebi que precisava adicionar alguns parâmetros às configurações do vinculador:


Agora o projeto está indo com sucesso. Colocamos um ponto de interrupção na última linha, tentamos correr. Vemos o seguinte texto:
hello from ConsoleApplication1! Library version = 0x10408 Opening port 0 FT_Open succeeded. Handle is 0x555555768540 FT_GetDeviceInfo succeeded. Device is type 10.
No geral - não é ruim. Algo abriu, até algum dispositivo foi encontrado. O que é o tipo 10? No arquivo de cabeçalho da FTDI, encontramos:
enum { FT_DEVICE_BM, FT_DEVICE_AM, FT_DEVICE_100AX, FT_DEVICE_UNKNOWN, FT_DEVICE_2232C, FT_DEVICE_232R, FT_DEVICE_2232H, FT_DEVICE_4232H, FT_DEVICE_232H, FT_DEVICE_X_SERIES, FT_DEVICE_4222H_0, FT_DEVICE_4222H_1_2, FT_DEVICE_4222H_3, FT_DEVICE_4222_PROG, };
Contamos o dedo de cima para baixo - à nossa frente está o FT4222H. Sim, Redd tem muitos dispositivos FTDI. Agora vou dizer brevemente que precisamos classificar o número do dispositivo passado para a função
FT_Open () , esse número é passado para a função
main () como argumento. Pode ser definido nas propriedades de depuração do projeto.
É útil colocar um prato com problemas típicos. Geralmente, ele diz "configure Redd" em qualquer lugar, sem detalhes. O fato é que os complexos serão distribuídos de forma personalizada, para que todos os leitores não precisem de regras de configuração. Em caso de problemas, os administradores geralmente lidam com a configuração. Portanto, é claro que você pode descrever as regras de configuração, mas isso ocupará muito espaço. Faz sentido gastar energia nisso apenas se alguém realmente precisar. Então, aqui vou me limitar apenas a indicar problemas, e como corrigi-los, descreverei se existem aplicativos no feedback.
Ótimo. Estamos prontos para conquistas usando exclusivamente ferramentas da Microsoft. Em geral, isso pode ser suficiente, mas, por via das dúvidas, falarei sobre uma alternativa que meu chefe me ensinou.
Trabalhando com Linux executando em uma máquina virtual
A principal desvantagem do método anterior é a montagem remota. Você pode pensar em mil e uma razões pelas quais a montagem deve ser feita localmente, e apenas o arquivo binário final será transferido para a máquina remota. Existem todos os tipos de restrições paranóicas (embora os arquivos sejam transferidos usando um protocolo seguro e você possa separar seu armazenamento de outros usuários com políticas de segurança), este é apenas o cuidado para que a unidade Redd não transborde de todos os tipos de bibliotecas, também é a relutância em registrar cada arquivo de cabeçalho, se houver milhares deles ... Bem, e muito, muito mais. Em geral, a técnica de montagem local pode ser útil, então considere-a.
Primeiro, colocamos o programa VirtualBox da Oracle na máquina local. Se houver algum problema de licenciamento (eu o uso gratuitamente, como indivíduo, mas não sei exatamente o que está acontecendo com entidades legais), selecione uma máquina física separada e coloque o Linux lá. Qual? É mais fácil para mim, eu entendo todos eles da mesma forma. Quero dizer, eu quase não entendo um. Portanto, o chefe me disse que você precisa usar o Debian, eu instalei o Debian. Você pode seguir o mesmo caminho (use o princípio "Por que não?"). Pelo menos no futuro, vou confiar em trabalhar com ele.
Ao trabalhar com Linux, você deve aderir a duas regras que facilitam muito a vida:
- Se, em resposta a um comando, eles nos dizem que não há direitos suficientes, vale a pena repeti-lo, adicionando o feitiço sudo ao início.
- Se em resposta a um comando nos é dito que não existe, vale a pena tentar instalá-lo lançando o feitiço apt-get install <falta de coisa> .
Então Acabamos de instalar o sistema operacional. Adicione imediatamente o suporte ao C ++ instalando o compilador g ++ e o depurador gdb. Como Então, usando a regra 2:
apt-get install g ++
apt-get install gdbNão deu? Ótimo! Repita usando a regra 1:
sudo apt-get install g + +
sudo apt-get install gdbAgora vamos para a Internet, no mecanismo de busca, digite:
IDE do EclipseEncontramos um link para eclipse.org, onde as primeiras opções são para Java, encontramos e baixamos uma opção para C / C ++:

Faça o download e descompacte, digamos, em casa.
Na verdade, nenhuma instalação é necessária. Vá para o diretório em que tudo acabou de ser descompactado e execute o arquivo eclipse:

Estamos em um ambiente de desenvolvimento. Bem, se você já trabalhou com microcontroladores e até núcleos de processador para FPGAs, provavelmente já sabe o que é o Eclipse. Coisas tão familiares estão quase completas. Começamos a procurar coisas mais ou menos familiares. Criamos o projeto C ++. Devo dizer imediatamente que existem duas maneiras. Um levará ao sucesso, o segundo a um beco sem saída. Portanto, siga atentamente o caminho que trilho:



Criamos um projeto que está indo bem. Para configurar sua depuração, vá para as propriedades do GDB:

Crie uma configuração do Aplicativo Remoto do tipo C / C ++, no grupo Conexão, clique em Novo:

Escolha uma conexão como SSH:

Preenchemos as propriedades da conexão alternando o botão de opção do tipo de autorização para autorização de senha:

Na verdade, o sistema está pronto para depuração. Depois de garantir que o texto de boas-vindas seja realmente exibido, tentamos transferir o código do exemplo anterior (que estava no Visual Studio) aqui. Para registrar bibliotecas adicionais, selecione as propriedades do projeto:

Além disso, como o assembly está acontecendo localmente, as bibliotecas também devem estar no diretório local usr / local / lib. Lembro que as bibliotecas foram baixadas com o driver, e como instalá-las é leia-me, bem como vídeos AN220 e YouTube, para obter detalhes, consulte a seção no Visual Studio.
Depois de toda essa preparação, temos as linhas familiares. Ou seja, o código completamente idêntico ao considerado na seção anterior é executado não menos de forma idêntica.
Só isso. Agora, dependendo da situação, podemos executar o código pelo Visual Studio e por uma máquina virtual. Como você pode ver, puramente em termos de configurações, o Visual Studio é mais simples, portanto, ceteris paribus, eu o escolherei. Mas é melhor possuir as duas tecnologias, pois o trabalho no Studio tem suas desvantagens relacionadas ao fato de que não apenas a depuração, mas também a montagem é remota.
Medição de velocidade de gravação FPGA via FT2232H
Bem o que. Vamos corrigir as habilidades adquiridas em um projeto mais ou menos real. É claro que começar algo muito sério não é mais possível; todos já estão cansados. Mas obtemos algum resultado mais ou menos prático. Por exemplo, medimos com que velocidade máxima podemos transferir dados para o FPGA através do chip FT2232H. O protocolo não é o mais fácil, por isso não o transmitiremos pelos dois lados, mas nos restringiremos a transmitir de nós para o canal, na outra extremidade da qual o FPGA está instalado. O documento
AN_130 FT2232H usado no modo FIFO síncrono estilo FT245 nos ajudará com isso, pois no complexo o controlador é ativado precisamente neste modo (FIFO síncrono). Este documento também contém uma descrição das conclusões, na forma em que são usadas nesse modo, diagramas de tempo e até exemplos de código, dos quais nos inspiraremos.
Então Queremos gravar no FIFO usando o controlador. O que virá de nós? Eu tentei, eu sei. Serão necessários 1 kilobyte de dados, após o que o controlador travará. Ele se recusará a aceitar dados adicionais. A questão é que um kilobyte é do tamanho de seu buffer interno. Embora seja possível, os dados serão obtidos do USB e armazenados neste espaço. Mas, para que eles acessem o canal síncrono, ele deve informar sobre a disponibilidade para recebê-los. Nós olhamos para o improvisado correspondente.

Então Quando o controlador possui dados em FIFO, ele baixa o sinal RXF. Em resposta a isso, devemos primeiro descartar o sinal de OE e mantê-lo em zero por pelo menos um ciclo de clock (isso segue mais da descrição para o diagrama do que do próprio diagrama). Seremos dados no barramento, devemos confirmar sua recepção com um baixo nível de sinal RD. E assim - para todo o quadro. Quando o controlador eleva a linha RXF, devemos remover o OE e o RD. Não usaremos os dados hoje. Para medir a velocidade, apenas simule o recebimento de dados no FPGA do FT2232H. Bem então. Para uma operação tão simples, nenhum sistema de processador é necessário. Basta fazer um autômato degenerado, cujo desenvolvimento levará muito menos tempo do que apenas mexer na preparação do processador e no programa para ele. Portanto, criamos um projeto que contém apenas um arquivo SystemVerilog com o seguinte conteúdo:
module JustRead( input logic clk, input logic rxf_n, output logic oe_n, output logic rd_n ); enum {IDLE,TRANSFER} state = IDLE; always @ (posedge clk) begin oe_n <= 1; rd_n <= 1; case (state) IDLE: begin if (rxf_n == 0) begin oe_n <= 0; state <= TRANSFER; end end TRANSFER: begin if (rxf_n == 0) begin oe_n <= 0; rd_n <= 0; end else begin state <= IDLE; end end endcase end endmodule
A máquina possui dois estados. Nesse caso, a duração do sinal OE é determinada pelo fato de que ele é engatilhado imediatamente após o pulso do relógio e é mantido até o próximo. Isso pode ser verificado usando o seguinte modelo:
module JustReadTB( output logic clk, output logic rxf_n, input logic oe_n, input logic rd_n ); JustRead dut ( .clk, .rxf_n, .oe_n, .rd_n ); always begin clk = 1; #16; clk = 0; #16; end initial begin rxf_n = 1; #120; rxf_n = 0; #120; rxf_n = 1; end endmodule
O tempo que levei foi o primeiro disponível, é a sequência de comutação que é anexada ao sinal do relógio que é importante. Temos o seguinte diagrama de tempo:

Numa primeira aproximação, isso corresponde ao que o documento exige. Mas não processaremos os dados reais de qualquer maneira.
A atribuição das pernas do FPGA neste projeto também não causa dificuldades, mesmo para editar através de uma tabela (levará mais tempo para transferir atribuições do arquivo * .QSF, e enfatizo constantemente que ao desenvolver sistemas de um dia no Redd, economizar tempo é uma coisa prioritária).

Nós coletamos, preenchemos, antes de desligar a energia do complexo, você pode trabalhar com o programa, ele não será mais interrompido após um estouro de buffer.
No programa, eu fiz duas funções. A primeira pesquisa e abre o dispositivo. Peguei algo do último teste, peguei emprestado algo do
AN130 :
FT_HANDLE OpenFT2232H() { FT_HANDLE ftHandle0; static FT_DEVICE ftDevice; // int nDevice = 0; while (true) { // if (FT_Open(nDevice, &ftHandle0) != FT_OK) { // , 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, maxBlockSize, maxBlockSize); return ftHandle0; } } // FT_Close(ftHandle0); // nDevice += 1; } }
Como fã do Windows, tive que escrever a função de medição de velocidade, verificando constantemente a Internet, pois normalmente uso os temporizadores clássicos de alta resolução da API do WIN32. Talvez você possa escrever com mais eficiência, mas este é um programa de um dia.
const int maxBlockSize = 0x100000; uint8_t buf[maxBlockSize]; … // BlockSize , 1 double TestSpeed(FT_HANDLE ftHandle0,int totalSize, int blockSize) { if (blockSize > maxBlockSize) { return -1; } DWORD dwWrittenTotal = 0; timespec before; clock_gettime(CLOCK_REALTIME, &before); for (int i = 0; i < totalSize; i += blockSize) { DWORD dwWritten; FT_Write(ftHandle0, buf, blockSize, &dwWritten); // dwWrittenTotal += dwWritten; } timespec after; clock_gettime(CLOCK_REALTIME, &after); if (dwWrittenTotal < (DWORD)totalSize) { return -2; } // uint64_t nsBefore = before.tv_nsec; uint64_t nsAfter = after.tv_nsec; // nsAfter += (after.tv_sec - before.tv_sec) * 1000000000; // nsAfter -= nsBefore; // double res = ((double)nsAfter)/((double)1000000000); // - return ((double)dwWrittenTotal) / res; }
Bem, o código que executa a funcionalidade básica ficou assim:
int main(int argc, char* argv[]) { FT_HANDLE ftHandle0 = OpenFT2232H(); if (ftHandle0 == 0) { printf("Cannot open device\n"); return -1; } const int totalSize = 0x100000; static const int blockSizes[] = { 0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000, 0x4000,0x8000,0x10000,0x20000,0x40000,0x80000,0 }; for (int i = 0; blockSizes[i] != 0; i++) { double speed = TestSpeed(ftHandle0, totalSize, blockSizes[i]); printf("%d,%d\n", blockSizes[i], (int)(speed/1000.)); int stop = 0; } // , FT_Close(ftHandle0); return 0; }
Eu sei que em USB, a velocidade depende muito do tamanho do bloco que está sendo enviado, então eu verifico a velocidade para várias opções. A enumeração linear quase não é necessária. Acabei de apresentar uma lista de tamanhos típicos. Eu produzo os dados em kilobytes por segundo. Bytes são desconfortáveis para os olhos, megabytes têm uma baixa resolução com um tamanho de bloco pequeno. Kilobytes por segundo é um compromisso razoável. Obtemos os seguintes resultados:
16,59 32,110 64,237 128,490 256,932 512,1974 1024,3760 2048,5594 4096,10729 8192,16109 16384,20170 32768,24248 65536,26664 131072,28583 262144,29370 524288,29832
Nós os salvamos em um arquivo * .csv, os carregamos no Excel, construímos um gráfico da velocidade versus tamanho do bloco.

O limite é de 30 megabytes por segundo. Até um máximo teórico de 52 MB / s agora. Talvez você possa de alguma forma acelerar, mas deixe para os leitores na forma de trabalho prático. O principal é que dominamos todas as etapas do trabalho com o canal e estamos prontos para conectar o FPGA ao processador central em um único sistema.
Enquanto o artigo estava sendo
compilado , foi encontrado o documento
AN_165 , que dizia que a velocidade máxima no modo FIFO síncrono é de 35 MB / s. Ou seja, espaço para crescimento - até um determinado tamanho. Mas ainda está lá.
Conclusão
Nós nos familiarizamos com duas estratégias para desenvolver e depurar o código do programa executado no processador central do complexo Redd (usando as ferramentas do Microsoft Visual Studio e em uma máquina virtual com o Linux OS). Também adquirimos habilidades práticas ao trabalhar com o canal através do qual o processador central do complexo se comunica com o FPGA.
Aparentemente, agora nada nos impede de transmitir dados significativos da CPU para o FPGA e vice-versa (embora no último artigo eu tenha escrito essas palavras).
Um exemplo que contém o "firmware" mais simples para FPGAs e um programa que mede a velocidade de gravação no USB pode ser baixado aqui .