Esta pergunta é sobre a implementação de um filtro IIR em um FPGA com fatias DSP, com critérios muito específicos.
Digamos que você esteja criando um filtro sem toques para a frente e apenas um toque para trás, com esta equação:
(veja a imagem)
Tome a fatia DSP48A1 do Xilinx como exemplo - a maioria das fatias IP DSP rígidas são semelhantes.
Digamos que você receba dados analógicos em 1 amostra por relógio. Gostaria de criar um filtro IIR que seja executado de forma síncrona no relógio de amostra.
O problema é que, para executar a fatia DSP na taxa máxima, você não pode multiplicar E adicionar no mesmo ciclo. Você precisa ter um registro de pipeline entre esses componentes.
Portanto, se você tiver 1 nova amostra a cada relógio, precisará produzir 1 saída por relógio. No entanto, você precisa dos relógios de saída 2 anteriores para poder produzir um novo neste design.
A solução óbvia é processar os dados a uma taxa de clock dupla ou desativar o registro do pipeline para que você possa multiplicar e adicionar no mesmo ciclo.
Infelizmente, se você estiver amostrando com a taxa de clock máxima da fatia DSP totalmente em pipeline, nenhuma dessas soluções será possível. Existe alguma outra maneira de construir isso?
(Pontos de bônus se você pode criar um filtro IIR que opere na metade da taxa de amostragem, usando qualquer número de fatias DSP)
O objetivo seria executar um filtro de compensação para um 1 GSPS ADC em um Xilinx Artix FPGA. Suas fatias DSP podem rodar pouco mais de 500 MHz quando totalmente em pipeline. Se houver uma solução para 1 amostra por relógio, eu gostaria de tentar escalar a solução para 2 amostras por relógio. Tudo isso é muito fácil com um filtro FIR.
Respostas:
Ainda não trabalhei com filtros IIR, mas se você precisar apenas calcular a equação fornecida
uma vez por ciclo de CPU, você pode usar pipelining.
Em um ciclo, você faz a multiplicação e, em um ciclo, precisa fazer a soma para cada amostra de entrada. Isso significa que seu FPGA deve ser capaz de fazer a multiplicação em um ciclo quando cronometrado na taxa de amostragem especificada! Então você só precisará fazer a multiplicação da amostra atual E o somatório do resultado da multiplicação da última amostra em paralelo. Isso causa um atraso de processamento constante de 2 ciclos.
Ok, vamos dar uma olhada na fórmula e criar um pipeline:
O código do seu pipeline pode ficar assim:
Observe que todos os três comandos precisam ser executados em paralelo e que a "saída" na segunda linha usa a saída do último ciclo do relógio!
Como não trabalhei muito com o Verilog, a sintaxe desse código pode estar errada (por exemplo, falta de largura de bit dos sinais de entrada / saída; sintaxe de execução para multiplicação). No entanto, você deve ter a ideia:
PS: Talvez algum programador experiente da Verilog possa editar esse código e remover esse comentário e o comentário acima do código posteriormente. Obrigado!
PPS: Caso seu fator "b1" seja uma constante fixa, você poderá otimizar o design implementando um multiplicador especial que apenas recebe uma entrada escalar e calcula apenas "tempos b1".
Resposta a: "Infelizmente, isso é realmente equivalente a y [n] = y [n-2] * b1 + x [n]. Isso ocorre devido ao estágio extra do pipeline." como comentário para a versão antiga da resposta
Sim, isso foi realmente adequado para a seguinte versão antiga (INCORRETA !!!):
Espero corrigir esse bug agora, atrasando os valores de entrada também em um segundo registro:
Para garantir que funcione corretamente dessa vez, vejamos o que acontece nos primeiros ciclos. Observe que os 2 primeiros ciclos produzem mais ou menos lixo (definido), pois nenhum valor de saída anterior (por exemplo, y [-1] == ??) está disponível. O registro y é inicializado com 0, o que equivale a assumir que y [-1] == 0.
Primeiro ciclo (n = 0):
Segundo ciclo (n = 1):
Terceiro ciclo (n = 2):
Quarto ciclo (n = 3):
Podemos ver que, começando com cylce n = 2, obtemos a seguinte saída:
que é equivalente a
Como mencionado acima, introduzimos um atraso adicional de l = 1 ciclos. Isso significa que sua saída y [n] está atrasada por lag l = 1. Isso significa que os dados de saída são equivalentes, mas estão atrasados em um "índice". Para ser mais claro: os dados de saída atrasados são 2 ciclos, pois é necessário um ciclo de relógio (normal) e 1 ciclo de relógio adicional (atraso l = 1) é adicionado para o estágio intermediário.
Aqui está um esboço para representar graficamente como os dados fluem:
PS: Obrigado por dar uma olhada no meu código. Então eu aprendi algo também! ;-) Deixe-me saber se esta versão está correta ou se você encontrar mais problemas.
fonte
y[n+l] = y[n-1] * b + x[n]
com um valor fixo para o atrasol
que pode ser reescrito paray[n] = y[n-1-l] * b + x[n-l]
e para l = 1 é essey[n] = y[n-2] * b + x[n-1]
.y[n+l] = x[n] * b0 + x[n-1] * b1 - y[n-1] * a1 - y[n-2] * a2
=>y[n] = x[n-l]*b0 + x[n-1-l] * b1 - y[n-1-l] * a1 - y[n-2-l]*a2
. Supondo que você possa fazer todas as três multiplicações em paralelo (1. estágio / 1 ciclo) e precisar adicionar os produtos, você precisa de 2 ciclos (1 ciclo: adicionar / sub primeiros dois resultados do produto, 1 ciclo: adicionar / sub o resultado desses dois add / subs), você precisará de 2 ciclos adicionais. Então l = (3-1) = 2 dando a vocêy[n]=x[n-2]*b0+x[n-1-2]*b1-y[n-1-2]*a1-y[n-2-2]*a2
=>y[n]=x[n-2]*b0+x[n-3]*b1-y[n-3]*a1-y[n-4]*a2
Sim, você pode cronometrar na frequência da amostra.
Uma solução para esse problema é manipular a expressão original para que os registros de pipeline possam ser inseridos, mantendo a sequência de saída desejada.
Dado: y [n] = y [n-1] * b1 + x [n];
isso pode ser manipulado em: y [n] = y [n-2] * b1 * b1 + x [n-1] * b1 + x [n].
Para verificar se essa é a mesma sequência, considere o que acontece com as primeiras amostras x [0], x [1], x [2] etc., onde antes de x [0] todas as amostras x, y eram zero.
Para a expressão original, a sequência é:
É claro que é necessário que b1 <1, caso contrário, este irá crescer sem limites.
Agora considere a expressão manipulada:
Esta é a mesma sequência.
Uma solução de hardware nas primitivas da biblioteca Xilinx precisaria de dois DSP48E em cascata. Consulte a figura 1-1 no UG193 v3.6 para obter os nomes de porta e registro abaixo. O primeiro primitivo está multiplicando por b1 e adicionando um relógio mais tarde; o segundo é multiplicado por b1 * b1 e adicionando um relógio mais tarde. Há uma latência de pipeline de 4 relógios para essa lógica.
- DSP48E # 1
a_port1: = b1; - coeficiente constante, defina AREG = 1
b_port1: = x; - definir atributo BREG = 1
c_port1: = x; - defina CREG = 1
- interno ao DSP48E # 1
reg_a1 <= a_port1;
reg_b1 <= b_port1;
reg_c1 <= c_port1;
reg_m1 <= reg_a1 * reg_b1;
reg_p1 <= reg_m1 + reg_c1; - saída do 1º DSP48E
- fim do DSP48E # 1
- DSP48E # 2
a_port2: = reg_p2; - definir atributo AREG = 0
b_port2: = b1 * b1; - constante, defina BREG = 1
c_port2: = reg_p1; - defina CREG = 1
- interno ao DSP48E # 2
reg_b2 <= b_port2;
reg_c2 <= c_port2;
reg_m2 <= a_port2 * reg_b2;
reg_p2 <= reg_m2 + reg_c2;
- fim do DSP48E # 2
A sequência em reg_p1:
x [0],
x [1] + x [0] * b1,
x [2] + x [1] * b1,
x [3] + x [2] * b1,
etc.
A sequência em reg_p2 é o resultado desejado. Interno ao 2º DSP48E, o registro reg_m2 possui uma sequência:
x [0] * b1 * b1,
x [1] * b1 * b1 + x [0] * b1 * b1 * b1,
x [2] * b1 * b1 + x [1] * b1 * b1 * b1 + x [0] * b1 * b1 * b1 * b1
Há uma boa elegância nesse resultado. Claramente, o DSP48E não se multiplica e adiciona no mesmo relógio, mas é isso que a equação da diferença está exigindo. A equação da diferença manipulada nos permite tolerar os registros M e P no DSP48E e o relógio em velocidade máxima.
fonte