Estou trabalhando em um circuito digital usando componentes discretos para conduzir um monitor VGA de 640x480 no modo de texto 80x30.
Para uma tela de 640x480, o clock do pixel é 25.175MHz, com um período em torno de 40ns. Eu não entendo como devo fornecer um novo pixel para a tela com tanta frequência.
A arquitetura básica para o meu circuito é a seguinte:
O contador binário para pixels horizontais conta de 25,175 MHz a 800 (640 pixels visíveis + 160 para varanda frontal, sincronização, varanda traseira). Em 800, aumente o contador de linhas verticais (e redefina em 525 linhas)
Usando a posição horizontal e vertical, derive as coordenadas x, y do caractere atual.
Usando a coordenada x, y do caractere, indexe na memória de vídeo para recuperar o caractere ASCII.
Use o caractere ASCII para indexar na ROM de caracteres para obter um padrão de bits para o caractere
Use o registro de deslocamento paralelo ao serial para converter a linha de caracteres de 8 pixels em bits individuais na frequência do clock de pixels
Se você seguir a cadeia, ele será: Contador -> RAM -> ROM -> Paralelo ao registro de troca serial
Usando os componentes mais rápidos que posso encontrar, os atrasos de propagação e o tempo de acesso somam cerca de 15ns + 20ns + 70ns + 15ns = 120ns, muito maior que o período de 40ns para 25MHz.
Em resoluções e taxas de atualização ainda mais altas, você pode ter relógios de pixel bem acima de 100MHz, que serão um período de 10ns.
Como é possível fornecer novos pixels para a tela a cada 10ns, quando o tempo de acesso à RAM / ROM já está bem acima, nem mesmo considerando todos os outros sinais em seu sistema?
fonte
Respostas:
Há duas razões principais para você achar esse desafio.
Primeiro, você está usando peças mais antigas e mais discretas (integração em menor escala) do que as que seriam usadas na era do VGA.
Mas a seguir, você os está usando de maneira atípica. Especificamente, sua abordagem não é o
pipelined
que significa que você precisa adicionar vários atrasos ao determinar seu intervalo e, portanto, avaliar.Por outro lado, projetos digitais síncronos que tentam atingir velocidade tentam fazer o mínimo possível entre os registros.
Enquanto os detalhes provavelmente diferem um pouco, falando mal, seria algo parecido com isto:
Quando você divide uma tarefa dessa maneira, você obtém apenas um atraso combinatório mais algum atraso de propagação e registra os tempos de configuração e espera que precisam caber entre os relógios.
Um design criado dessa maneira levará muitos relógios para produzir uma saída - a latência será realmente maior do que um design puramente combinatório. Mas produz uma nova saída correta em cada ciclo de um relógio muito mais rápido.
E ei, é um vídeo, não importa realmente se o CRT está desenhando uma dúzia de pixels atrás do contador de pixels - é claro que você leva isso em consideração no tempo dos sinais de sincronização para que eles estejam corretos quando comparados com os dados realmente sai do DAC.
Na prática, quase todos os sistemas digitais complexos funcionam dessa maneira, como é uma ótima idéia - até que uma CPU em pipeline atinja uma dependência de um resultado computacional anterior ou de uma ramificação condicional ... Então as coisas ficam interessantes, como eles falavam sobre na próxima aula de uma aula de sistemas digitais - mas felizmente sua situação de VGA é muito mais simples, especialmente se você ainda não se preocupa com os efeitos de lacrimejamento, se o buffer de caracteres mudar enquanto a tela está sendo desenhada.
Por uma questão prática, se você deseja criar isso, faça-o em um FPGA. Isso forçará as memórias síncronas se você usar as internas, ou os registros síncronos de E / S se você usar memória externa. Você terá muitos estímulos para um design adequado, o tecido em si será mais rápido do que suas peças discretas e, é claro, se você cometer um erro, precisará apenas mexer os polegares enquanto recompila, em vez de passar um longo dia re-conectando .
fonte
Você esquece que um adaptador gráfico nunca desenharia apenas um único pixel - mas pelo menos uma linha de varredura completa. Assim, este seria um problema completamente pipeleable.
Além disso, não esqueça que há cinco décadas produzindo hardware de vídeo até agora. Seu problema geralmente seria resolvido com um tipo especial de RAM, no qual você renderiza suas cartas em uma porta e é sequencialmente lido em um DAC de sinal de vídeo. Esse hardware é muito, muito mais rápido do que você está vendo.
Não, por que você faria isso? Você simplesmente colocaria o pixel da linha em uma área contígua da memória e o distribuiria linearmente para o seu DAC - se for uma implementação de CPU / MCU, você nem deixaria sua CPU fazer isso, mas uma unidade de DMA programada fazer nada além de pegar um valor após o outro e colocá-lo em uma porta de dados paralela, sem nenhuma interação do núcleo da CPU.
Ah, você quer renderizar rapidamente - boa escolha, mas incomum a custos modernos de RAM. Em vez disso, basta renderizar o personagem em um buffer de quadro antecipadamente, ou se seu dispositivo for extremamente fino, canalize diretamente (veja minha explicação do DMA acima) a linha de caracteres para o DAC.
fonte
Além do pipelining (que é muito o que você deve fazer), você está perdendo algo importante ...
O registro de deslocamento de entrada paralela e saída serial apresenta pontos de 25 Mhz, com certeza, mas se os seus caracteres tiverem 8 pixels de largura, sua entrada será de apenas ~ 3,2 MHz, que é facilmente acessível para a série LS da era VGA, por tudo isso você precisa ter o próximo byte pronto quando o registro de turno terminar com o atual (é aqui que o pipeline entra).
Gere um clock de pixel a ~ 25MHz e um clock de memória a 1/8 para acionar o buffer de texto e a CG ROM e, em seguida, canalize esse acesso à memória e à CG ROM.
Um truque adicional, a saída do buffer de texto será repetida para cada linha em qualquer linha de texto, portanto, talvez você possa registrar os 80 bytes de texto em um buffer de anel e parar de ler o ram nas próximas 7 linhas (assumindo um número 8). caractere de linha), isso permite liberar a memória para a CPU usar, ao custo de precisar de 80 bytes de memória RAM pendurados na lateral da coisa.
fonte
Então, obviamente, isso não funciona; você precisa de um pipeline.
1) Armazene os caracteres contiguamente na memória. Comece no canto superior esquerdo.
2) Busque um personagem durante o intervalo de apagamento. Continue buscando caracteres em ordem de memória.
3) Pipeline cada caractere decodificado mais o índice de linha na ROM.
4) Pipeline a saída da ROM para um buffer.
5) Faça o pipeline do buffer para um registro de turno. Leia os pixels continuamente em intervalos de 40ns.
(Isso implica que você precisa carregar um novo caractere no registrador de turnos a cada 320ns, o que pode ser possível sem a canalização de todo o resto do sistema.)
6) Durante o apagamento horizontal, retorne ao início da linha ou avance para o próximo caractere (ou seja, início da próxima linha).
Recurso de bônus: como você só precisa de um caractere a cada 320ns, você também pode ler um par de caracteres + cor e executar caracteres coloridos no estilo MSDOS ou Spectrum.
fonte