Anotação
Madness All Ages Submissive
Ao projetar qualquer módulo em FPGAs, às vezes vem à mente a idéia de um uso não padrão do próprio ambiente de design e as ferramentas que ele fornece para o design. Neste breve artigo, veremos como, com a ajuda da ferramenta de gerenciamento ambiental implementada no Tcl, podemos literalmente desenhar fotografias, pinturas, retratos e memes no FPGA.
Uma “rota de design” tão incomum foi implementada há um ano e meio, mas só agora surgiu a idéia de organizá-la na forma de uma nota, na qual há pouca prática no uso de scripts Tcl para gerenciar o ambiente de design, neste caso o Vivado. No entanto, com pequenas modificações, tudo pode ser facilmente adaptado a outros ambientes de desenvolvimento, por exemplo, o Quartus II.

1. Introdução
Obviamente, a ideia não veio à mente do nada. Sua aparência foi promovida pelo meu emprego em projetos de processamento de imagem e gerenciamento de fluxo de vídeo no FPGA. Acontece a todos que, ao se debruçarem sobre a solução de um problema, toda heresia vem à mente por que ela não funciona ou funciona exatamente como deveria, mas não como esperamos.
Ao resolver o problema, tive que recorrer a uma das ferramentas do ambiente Vivado, a saber, colorir os componentes e módulos correspondentes no projeto após a colocação, o rastreio e a observação dos intermináveis diagramas de tempo.
Como resultado, eu pintei vários blocos lógicos configuráveis de CLB em várias cores, e ocorreu-me - esses são os pixels da imagem, para que eu possa tentar desenhar qual fio a imagem é, combinando cada pixel com seu próprio CLB colorido? ... bem, ele começou
Como a Tcl nos ajudará?
Suponha que tenhamos uma imagem pequena de tamanho 100x100 pixels. Agora, suponha que, para colorir o CLB, precisamos fazer duas coisas: selecione o CLB e selecione a cor. Na imagem 100x100, temos 10.000 pixels e fazer essa coloração manualmente é bastante tedioso, principalmente porque as ações são iguais e repetitivas. Portanto, pintar manualmente cada CLB não é uma opção e você precisa usar Tcl e scripts. Mas por onde começar?
A primeira coisa que veio à mente foi encontrar a equipe certa responsável por atribuir cores ao elemento selecionado. Felizmente, ao executar ações manualmente, o Vivado exibe os comandos Tcl apropriados no console, e parece que o problema de pesquisa deve ser resolvido o mais rápido possível. No entanto, não estava lá. O Vivado simplesmente ignora a saída do comando para destacar os elementos selecionados, e a única opção para encontrá-lo, e eu tinha certeza absoluta de que deveria, é mergulhar de cabeça no guia Tcl disponível no Vivado, e isso é quase 2000 páginas [1].
Não se desespere, a palavra-chave "destacar" encontrou rapidamente o comando correspondente, chamado de destaque_objetos. Este comando destaca os objetos especificados ou selecionados em uma cor específica, especificada usando as opções. As opções para o comando destacar_objetos são as seguintes:
- color_index - (opcional) o valor válido do argumento da opção deve ser um número de 1 a 20. A cor na qual o objeto selecionado será pintado é determinada pelo número de série da paleta de cores predefinida, que pode ser encontrada em Cores → Destaque na seção do menu Ferramentas → Configurações .
- rgb - (opcional) define a cor do objeto selecionado no formato RGB
- cor - (opcional) destaca o objeto selecionado em uma das seguintes cores: vermelho, verde, azul, magenta, amarelo, ciano e laranja
As opções restantes do comando estão relacionadas às configurações do sistema do próprio comando e não serão úteis para nós. No entanto, ao usar o comando align_objects, lembre-se de que duas ou mais opções de cores não podem ser aplicadas ao mesmo tempo.
Obviamente, uma opção que define uma cor arbitrária no formato RGB é adequada para a nossa tarefa - a opção rgb
Agora não seria ruim obter os valores de pixel da imagem, mas não consegui encontrar a imagem que seria apresentada no formato bitmap. Ao abrir cada arquivo com um editor de texto, não foi possível encontrar linhas com um valor de pixel. Obviamente, eu não escrevi um programa para converter imagens em formato bitmap, mas simplesmente subi na Internet para procurar uma solução pronta. Não demorou muito para pesquisar. Como se viu, a tarefa de converter uma imagem em um formato de bitmap (ou seja, quando vemos os valores de pixel de uma imagem não compactada) é bastante relevante (provavelmente, essa tarefa é dada aos alunos-programadores como uma lição de casa para trabalhos de laboratório). Uma breve pesquisa resultou no github, de onde o programa Image2Bitmap foi baixado [2].
O programa requer uma entrada de imagem e gera valores de pixel na forma de uma matriz s com valores hexadecimais de pixel no formato RGB565. Esse formato diz que a codificação de cores para o componente vermelho usa 5 bits, verde 6 bits e azul 5 bits. Isso acabou sendo suficiente para o trabalho. Agora, tudo o que é necessário é mapear os valores obtidos diretamente para as seções pintadas (fatia).
Seleção FPGA
Quanto maior o FPGA, mais recursos lógicos nele e, portanto, o "campo da criatividade" é maior e a imagem será mais clara. Deve-se notar imediatamente que “decorar” é um processo bastante longo e pode levar uma quantidade decente de tempo, dependendo do tamanho da imagem. Para o teste, vale a pena escolher um FPGA com uma pequena quantidade de recursos. Por exemplo, a família Spartan-7. Após o teste, você pode alterar o FPGA para mais "gordo", por exemplo, da família Ultrascale +.
Começamos o Vivado e criamos o projeto
Selecionamos o cristal xc7s6cpga196-2, no qual desenharemos a imagem de teste

Para exibir o desenho, precisamos abrir a imagem do próprio cristal, no entanto, isso pode ser feito após a etapa de síntese ou elaborado. No Vivado, para isso, precisamos criar um módulo fictício em qualquer idioma.

Adicione um script ao projeto Tcl.
a. Para fazer isso, crie um arquivo com a extensão “.tcl” na pasta com o projeto, por exemplo, “fpga_painter.tcl”

b. Vá para Vivado e adicione este arquivo ao projeto.

c. Após atualizar a hierarquia do projeto, torne o arquivo inativo.

Após a criação do módulo, ele aparecerá na janela da hierarquia do projeto e o botão Open Elaborate Design estará disponível para nós. Empurre.

Após abrir o Elaborate Design, vá para Window → Device. Uma exibição do campo do nosso FPGA será exibida.

A preparação está concluída, começamos a escrever um script.
Definição de parâmetros e procedimentos
Imagem de teste
Primeiro, vamos depurar o algoritmo / código em uma imagem pequena, digamos 5x3, e depois executá-lo "ao máximo".
Abra o Paint, limite o campo da imagem a 5x3 pixels (você pode tirar qualquer cor). Salve o arquivo como "5x3.png"

Abra o programa Image2Bitmap e converta nossa imagem em uma matriz RGB565.

Após a conversão, o programa nos fornecerá uma matriz de 15 pixels
*uint16_t image = { 0xf800, 0x07e0, 0x001f, 0x8410, 0x8010, 0xfbe4, 0xff80, 0x2589, 0x051d, 0x3a59, 0xa254, 0xbbca, 0xfd79, 0xef36, 0x9edd, };*
Preparação de dados
Antes de prosseguir com o processamento de pixels, transformamos os dados gerados pelo Image2Bitmap em uma lista simples na qual os valores hexadecimais de pixels são gravados. Vamos copiar os dados do programa e salvá-los no arquivo pic_array.dat, que deve estar localizado na pasta do projeto.

Ao iniciar o script criado, temos que processar o arquivo "pic_array.dat". Vale ressaltar que o número de elementos na linha retornada pelo Image2Bitmap não corresponde ao número de pixels na linha da imagem convertida; por esse motivo, criaremos uma lista separada de "pixels".

Ao ler um arquivo, você deve ignorar a primeira linha "uint16_t image = {" e a última "};". Para pular a primeira linha ao ler um arquivo, basta colocar a linha antes do ciclo de leitura do arquivo inteiro.

Depois de ler o arquivo inteiro, veremos que a última linha do arquivo “};” se tornou um elemento da lista, que é simplesmente excluída.

Isso completa a formação da lista com valores hexadecimais de pixel. Agora vamos começar a processá-los.
Dimensionamento da imagem
Mais uma vez, dê uma olhada no campo FPGA e na imagem. O campo FPGA é dividido em seções (SLICE), que possuem as coordenadas horizontais correspondentes “X” e vertical “Y”. Por exemplo, SLICE_X6Y36.

A imagem, por sua vez, possui pixels, também com coordenadas horizontais e verticais. Ao sobrepor a imagem no FPGA, devemos combinar o pixel superior esquerdo com a seção superior esquerda do FPGA. Nesse caso, o cristal selecionado possui uma seção superior com a coordenada X0Y49.

O tamanho da imagem será determinado pelo número de seções no FPGA horizontal e verticalmente. Para o cristal selecionado, a coordenada horizontal das seções varia de X0 a X131 e a coordenada vertical de Y49 a Y0. Segue-se que teoricamente podemos desenhar uma imagem do tamanho de 132x50 no cristal selecionado.
Parâmetros iniciais
Resumindo: os parâmetros iniciais do nosso script serão:
X posição inicial da seção: nome da variável start_x

Posição inicial da seção no eixo y: nome da variável start_y

Largura da imagem (a imagem de teste é 5): variável w

Altura da imagem (a imagem de teste é 3): variável h

Ajuste de cor de pixel
O Image2Bitmap fornece uma matriz de pixels no formato RGB565 como um número de 16 bits gravado no formato hexadecimal. Deveríamos:
Converta o valor do pixel em formato binário. Isso pode ser feito usando o procedimento hex2bin, que pode ser encontrado em [3]

Combine os bits com os componentes de cores correspondentes:
• O componente vermelho de R [15:11]

• Componente verde G [10: 5]

• Componente azul B [4: 0]

Explicação: a ordem é alterada devido ao fato de o procedimento hex2bin retornar uma sequência na qual a numeração de elementos começa com 0, ou seja, o 15º bit corresponde ao 0º elemento da sequência e o 0º bit corresponde ao 15º elemento da sequência
Converta o valor do componente de cor de binário em decimal. Isso pode ser feito usando o procedimento bin2dec, que pode ser encontrado [3]:


Converta valores de pixel do formato RGB565 para RGB888, para uma exibição mais suave da imagem. Isso é feito usando duas listas, que podem ser encontradas em [4]. Como funciona:
• A resolução dos componentes de cor R e B é de 5 bits. Tomando o valor decimal do componente, comparamos com a posição do número escrito na lista t5, e o valor do componente muda para o valor da tabela



• Da mesma forma para o componente G e a tabela t6


Seção Disponibilidade
Dentro de alguns FPGAs, existem recursos ou vazios especiais que podem interromper a numeração seqüencial das coordenadas da seção. Por exemplo, na figura abaixo, você pode ver que a numeração da seção está interrompida (cristal xc7s50)

Por esse motivo, antes da coloração, verificamos primeiro a existência da seção. Se existir, pinte, se não existir, vá para o próximo pixel

Coloração de seção
Cor da seção definida, seção verificada. Agora a cor precisa ser atribuída às seções usando o comando align_objects:

Vetor → Matriz bidimensional
No início, convertemos os dados da imagem em uma lista de pixels, que armazena uma varredura progressiva da imagem. Para organizar a imagem, introduzimos duas variáveis, x e y, que corresponderão à posição dos pixels na imagem. Ao ler sequencialmente os elementos da lista de pixels, formaremos uma imagem usando dois para loops: um pelo número de linhas, o segundo pela posição do pixel na linha


Listagem completa de scripts
Listagem Fpga_painter.tcl #https://tcl.tk/ #http://www.tune-it.ru/web/il/home/-/blogs/-rgb888-<->-rgb565----- #https://github.com/FoxExe/Image2Bitmap/releases/tag/0.5 # set start_x 0; # set start_y 49; # set w 5; # ( ) set h 3; # ( ) proc hex2bin hex { set t [list 0 0000 1 0001 2 0010 3 0011 4 0100 \ 5 0101 6 0110 7 0111 8 1000 9 1001 \ a 1010 b 1011 c 1100 d 1101 e 1110 f 1111 \ A 1010 B 1011 C 1100 D 1101 E 1110 F 1111] regsub {^0[xX]} $hex {} hex return [string map -nocase $t $hex] } proc bin2dec bin { #returns integer equivalent of $bin set res 0 if {$bin == 0} { return 0 } elseif {[string match -* $bin]} { set sign - set bin [string range $bin[set bin {}] 1 end] } else { set sign {} } foreach i [split $bin {}] { set res [expr {$res*2+$i}] } return $sign$res } ############### #RGB565 -> RGB888 using tables set t5 [list 0 8 16 25 33 41 49 58 66 74 82 90 99 107 115 123 132\ 140 148 156 165 173 181 189 197 206 214 222 230 239 247 255] set t6 [list 0 4 8 12 16 20 24 28 32 36 40 45 49 53 57 61 65 69\ 73 77 81 85 89 93 97 101 105 109 113 117 121 125 130 134 138\ 142 146 150 154 158 162 166 170 174 178 182 186 190 194 198\ 202 206 210 215 219 223 227 231 235 239 243 247 251 255] ############### # . set script_path [get_property DIRECTORY [get_projects *]] cd $script_path # set fId [open {pic_array.dat} r] # , set pixels [list ] # . gets $fId line # . . while {[gets $fId line] > 0} { set pixels [concat $pixels $line] } # pixels "};", Image2Bitmap set pixels [lrange $pixels 0 end-1] # pixels set pix_num 0; # for {set y 0} { $y < $h } {incr y} { # ( ) set Y [expr {$start_y - $y}] # for {set x 0} { $x < $w } {incr x } { set pix_val [lindex $pixels $pix_num]; incr pix_num # binary set pix_bin [hex2bin $pix_val] ; # set R_bin [string range $pix_bin 0 4] # set R_dec [ bin2dec $R_bin ] # t5 set R [lindex $t5 $R_dec] # set G_bin [string range $pix_bin 5 10] set G [lindex $t6 [ bin2dec $G_bin ]] set B_bin [string range $pix_bin 11 15] set B [lindex $t5 [ bin2dec $B_bin ] ] # set X [expr {$start_x + $x}] #, set cond [get_sites "SLICE_X${X}Y${Y}"] if {$cond ne ""} { # highlight_objects [get_sites "SLICE_X${X}Y${Y}"] -rgb "$R $G $B" } puts "X = $XY = $Y; $R $G $B" } } close $fId puts "Complete::Check FPGA field::Window->Device"
Teste
Para iniciar o teste, verifique se o campo FPGA está disponível para você, ou seja, uma das etapas do projeto está aberta: elaborada, síntese ou implementada. Para exibir o campo FPGA, selecione Janela → Dispositivo

Abra o arquivo pic_array.dat e copie os dados do Image2Bitmap no arquivo. Salvar arquivo

Vamos abrir o script. Defina a coordenada do pixel superior esquerdo 0 e 49, o tamanho da imagem de teste é 5 por 3 e execute o script. Para fazer isso, clique com o botão direito do mouse no campo de script e selecione Executar.

Vá para o console Tcl e verifique se o script foi executado.

Vá para a guia Dispositivo e verifique se as células estão coloridas na cor apropriada.

Agora podemos tirar qualquer imagem, convertê-la no tamanho desejado e preencher o campo FPGA. Abaixo estão alguns exemplos.
PS: a atribuição de cores personalizada substitui a configuração de cores padrão do Vivado

Referências
- UG835. Vivado Design Suite TclCommand Guia de Referência
- https://github.com/FoxExe/Image2Bitmap/releases/tag/0.5
- https://tcl.tk
- http://www.tune-it.ru/web/il/home/-/blogs/conversion-rgb888 - <-> -rgb565-and-protection-mushrooms-from-fading