57 / 63 · Conceito
Ponteiros e políticas de fronteira de uma FIFO de clock único
Implemente os ponteiros e a ocupação de uma FIFO de profundidade 4 e defina quando push e pop simultâneos são aceitos com a fila vazia ou cheia.
As aulas podem ser lidas gratuitamente. Inscreva-se para salvar o progresso.
Manter a ordem sem deslocar todos os dados
Uma FIFOFIFO Fila na qual o primeiro dado a entrar é o primeiro a sair. As condições de cheio e vazio e as regras de leitura e escrita simultâneas fazem parte do contrato. entrega primeiro o dado que entrou primeiro. Em uma FIFO de clock único, leituras e escritas são decididas na mesma borda de subida. Aqui fixamos a profundidade em 4 e a largura dos dados em 8 bits.
rptr aponta para a próxima posição a ser lida e wptr para a próxima posição a ser escrita. Com 2 bits, os ponteiros voltam naturalmente de 3 para 0. A ocupação precisa representar de 0 a 4, portanto exige 3 bits.
rptr=wptr pode ocorrer tanto com a FIFO vazia quanto cheia. A igualdade dos ponteiros não basta para distingui-las. Sendo C a ocupação, empty=(C=0) e full=(C=4).
Separar solicitações de operações aceitas
push e pop são solicitações. Chamaremos p de push aceito e q de pop aceito. Sem reset, eles são calculados a partir do estado anterior à borda:
Se a FIFO estiver vazia e receber as duas solicitações, somente push será aceito: o dado recém-escrito não aparece imediatamente na saída. Se estiver cheia e receber ambas, a cabeça anterior sai e o novo dado é armazenado. full significa que existem quatro elementos armazenados; por si só, não proíbe todo push.
| Ocupação anterior | Somente push | Somente pop | Push e pop |
|---|---|---|---|
C=0 | Armazena; C=1 | Rejeitado | Somente push; C=1 |
0<C<4 | C+1 | C−1 | Mantém C |
C=4 | Rejeitado | C=3 | Aceita ambos; C=4 |
Testar a política pelos controles
Primeiro ative push e pop com a FIFO vazia: somente push deve ser aceito. Desative pop e preencha as quatro posições. Depois ative as duas solicitações com um novo dado: a ocupação deve continuar em 4, a cabeça anterior deve sair e o novo dado deve ser armazenado no final.
FIFO de quatro posições, inicialmente vazia e com os ponteiros de leitura e escrita em 0. Aceita pop se não estiver vazia. Aceita push se houver espaço ou se um pop for aceito na mesma borda.
O modelo exibe o estado após a borda de subidaBorda de subida Instante em que o clock passa de 0 para 1. É diferente do nível alto, que abrange todo o intervalo com CLK=1. Saiba mais. Um pop aceito produz uma saída com valid=1. Se nenhum pop for aceito na borda seguinte, o registradorRegistrador Circuito que armazena vários bits de estado. Os registradores síncronos deste curso capturam a entrada especificada na borda de clock. Saiba mais de dados mantém seus bits, mas valid volta a 0. Os bits antigos de posições vazias ficam ocultos. Reiniciar a demonstração restaura todo o seu estado; isso não significa que o resetReset Controle que retorna o estado a um valor inicial especificado. É preciso definir se é síncrono ou assíncrono e sua prioridade em relação a outros controles. Saiba mais do RTL apague a memória.
RTL com saída registrada
module fifo4 (
input logic clk, rst, push, pop,
input logic [7:0] din,
output logic [7:0] dout,
output logic dout_valid, full, empty,
output logic [2:0] occupancy
);
timeunit 1ns;
timeprecision 1ps;
logic [7:0] mem [0:3];
logic [1:0] rptr, wptr;
logic take_push, take_pop;
assign empty = (occupancy == 3'd0);
assign full = (occupancy == 3'd4);
assign take_pop = !rst && pop && !empty;
assign take_push = !rst && push && (!full || take_pop);
always_ff @(posedge clk) begin
if (rst) begin
rptr <= 2'd0;
wptr <= 2'd0;
occupancy <= 3'd0;
dout_valid <= 1'b0;
end else begin
dout_valid <= take_pop;
if (take_push) begin
mem[wptr] <= din;
wptr <= wptr + 2'd1;
end
if (take_pop) begin
dout <= mem[rptr];
rptr <= rptr + 2'd1;
end
case ({take_push, take_pop})
2'b10: occupancy <= occupancy + 3'd1;
2'b01: occupancy <= occupancy - 3'd1;
default: occupancy <= occupancy;
endcase
end
end
endmoduleNão atualize count com dois if independentes que incrementem e decrementem o valor. Se ambos executarem atribuições não bloqueantesAtribuição não bloqueante Atribuição escrita com <= em RTL sequencial. Avalia o lado direito e agenda a atualização, permitindo que os registradores da mesma borda calculem a partir do estado anterior. Saiba mais ao mesmo registrador, prevalece a última. Os ponteiros avançam conforme a aceitação correspondente, enquanto count resolve conjuntamente as quatro combinações. As condições de aceitação impedem decrementar 0 ou incrementar 4.
Ler e escrever o mesmo endereço quando a fila está cheia
Após armazenar A, B, C e D, temos rptr=wptr=0 e C=4. Com push e pop simultâneos e din=E, o resultado deve ser dout=A, valid=1, fila [B,C,D,E], os dois ponteiros em 1 e ocupação 4. A posição mem[0] termina com E, mas a leitura não bloqueante obtém o A anterior. A semântica read-first é essencial neste caso.
O próximo pop sem push entrega B e reduz a ocupação para 3. Já com a FIFO vazia, push e pop simultâneos de Z deixam C=1 e valid=0; é preciso aceitar outro pop em uma borda posterior para obter Z. Permitir passagem direta quando vazia e permitir substituição quando cheia são decisões independentes.
rst é síncrono e ativo em nível alto: reinicia ponteiros, ocupação e valid, mas não apaga a memória nem dout. Os bits antigos não importam, pois a FIFO vazia rejeita pop e cada push escreve antes de uma leitura posterior. Saídas inválidas não são consumidas. valid identifica a resposta de um pop aceito; não existe ready na saída, portanto o consumidor deve poder recebê-la.
Para uma profundidade que não seja potência de dois, o retorno do ponteiro deve ser explícito: com profundidade 3, o endereço 2 deve voltar a 0. Se houver inferência de RAM, confira o comportamento de dado antigo nas colisões. Este projeto usa um único clock; ele não resolve a sincronização dos ponteiros de uma FIFO assíncrona.
Experimente
Após o reset, aplique em bordas sucessivas: (1) push de A e pop simultâneos, (2) somente push de B, (3) somente push de C, (4) somente push de D, (5) push de E e pop simultâneos e (6) somente pop. Indique, após cada borda, a fila, a ocupação, dout_valid e, quando válido, o dado de saída. Por que a quinta borda exige comportamento read-first?
Ler a explicação
(1) [A], ocupação 1, valid=0; (2) [A,B], 2, valid=0; (3) [A,B,C], 3, valid=0; (4) [A,B,C,D], 4, valid=0; (5) [B,C,D,E], 4, valid=1, dout=A; (6) [C,D,E], 3, valid=1, dout=B. Na primeira borda não havia elemento armazenado, portanto pop é rejeitado. Na quinta, rptr=wptr=0: leitura e escrita usam a mesma posição e a saída deve ser a cabeça anterior A. Retornar E por write-first altera o conteúdo e a ordem da FIFO. Não se avalia dout quando valid=0.