Lógica sequencial com sempre
Um artigo anterior mostrou diferentes exemplos de uso de um
always
bloco para implementar a lógica combinacional. Um always
bloco também é usado principalmente para implementar sequencial lógica que tem elementos de memória como flip-flops que podem armazenar valores. Flip Flop JK
Um flip-flop JK é um dos muitos tipos de flops usados para armazenar valores e tem duas entradas de dados j e k junto com uma para reset rstn e outra para clock clk. A tabela verdade para um flop JK é mostrada abaixo e normalmente é implementada usando portas NAND.
rstn | j | k | q | Comentários |
---|---|---|---|---|
0 | 0 | 0 | 0 | Quando o reset é ativado, a saída é sempre zero |
1 | 0 | 0 | Reter valor | Quando j e k são 0, a saída permanece a mesma de antes |
1 | 0 | 1 | 1 | Quando k=1, a saída se torna 1 |
1 | 1 | 0 | 0 | Quando k=0, a saída se torna 0 |
1 | 1 | 1 | Alternar valor | Quando j=1,k=1 a saída alterna o valor atual |
O código comportamental Verilog para um flip-flop JK pode ser escrito como mostrado abaixo
module jk_ff ( input j, // Input J
input k, // Input K
input rstn, // Active-low async reset
input clk, // Input clk
output reg q); // Output Q
always @ (posedge clk or negedge rstn) begin
if (!rstn) begin
q <= 0;
end else begin
q <= (j & ~q) | (~k & q);
end
end
endmodule
Banco de teste
Primeiro declare todas as variáveis usadas no testbench e inicie um relógio usando um simples
always
bloco que pode ser direcionado para o projeto. Em seguida, instancie o projeto e conecte suas portas com as variáveis do testbench correspondentes. Observe que q é do tipo wire
porque está conectado a uma saída do projeto que o conduzirá ativamente. Todas as outras entradas do projeto são do tipo reg
para que possam ser conduzidos dentro de um bloco procedural como initial
. O estímulo primeiro inicializa todas as entradas do projeto para zero e, em seguida, desabilita a reinicialização após algum tempo. Um
for
loop é usado para direcionar valores diferentes para j e k que são direcionados em tempos aleatórios. Feito o loop, espere mais um pouco e finalize a simulação.
module tb;
// Declare testbench variables
reg j, k, rstn, clk;
wire q;
integer i;
reg [2:0] dly;
// Start the clock
always #10 clk = ~clk;
// Instantiate the design
jk_ff u0 ( .j(j), .k(k), .clk(clk), .rstn(rstn), .q(q));
// Write the stimulus
initial begin
{j, k, rstn, clk} <= 0;
#10 rstn <= 1;
for (i = 0; i < 10; i = i+1) begin
dly = $random;
#(dly) j <= $random;
#(dly) k <= $random;
end
#20 $finish;
end
endmodule
Observe a partir da onda de simulação que na posição do clock, a saída q muda de valor com base no estado das entradas j e k como dado na tabela verdade.
Contador Módulo-10
Os contadores de módulo (MOD) simplesmente contam até um certo número antes de voltar a zero. Um contador MOD-N contará de 0 a N-1 e depois voltará a zero e começará a contar novamente. Esses contadores geralmente exigem log2 N número de flops para manter o valor de contagem. Abaixo está o código Verilog para um contador MOD-10 que continua contando a cada clk do relógio enquanto reset rstn estiver desabilitado.
Os parâmetros Verilog podem ser usados para fazer um contador MOD-N mais escalável.
module mod10_counter ( input clk,
input rstn,
output reg[3:0] out);
always @ (posedge clk) begin
if (!rstn) begin
out <= 0;
end else begin
if (out == 10)
out <= 0;
else
out <= out + 1;
end
end
endmodule
Banco de teste
O testbench primeiro declara algumas variáveis que podem ser atribuídas a alguns valores e direcionadas às entradas de projeto. O módulo contador é então instanciado e conectado com os sinais da bancada de teste que são posteriormente acionados com alguns valores no estímulo. Como o contador também requer um relógio, o relógio do testbench é modelado com um
always
quadra. O estímulo simplesmente define os valores padrão no tempo 0ns, então as desativações são redefinidas após 10ns e o projeto pode ser executado por algum tempo.
module tb;
reg clk, rstn;
reg [3:0] out;
mod10_counter u0 ( .clk(clk), .rstn(rstn), .out(out));
always #10 clk = ~clk;
initial begin
{clk, rstn} <= 0;
#10 rstn <= 1;
#450 $finish;
end
endmodule
Veja que o módulo contador conta de zero a 9, passa para zero e começa a contar novamente.
Registro de deslocamento esquerdo de 4 bits
Abaixo é mostrado um registrador de deslocamento à esquerda de 4 bits que aceita uma entrada d em LSB e todos os outros bits serão deslocados para a esquerda em 1. Por exemplo, se d for igual a zero e o valor inicial do registrador for 0011, ele se tornará 0110 em a próxima borda do clk do relógio.
module lshift_4b_reg ( input d,
input clk,
input rstn,
output reg [3:0] out
);
always @ (posedge clk) begin
if (!rstn) begin
out <= 0;
end else begin
out <= {out[2:0], d};
end
end
endmodule
Banco de teste
O testbench segue um template similar ao mostrado antes onde algumas variáveis são declaradas, o módulo de design é instanciado e conectado com os sinais do testbench. Em seguida, um relógio é iniciado e o estímulo é direcionado para o design usando um
initial
quadra. Neste exemplo do testbench, diferentes valores de d devem ser exercitados e, portanto, um for
loop é usado para iterar 20 vezes e aplicar valores aleatórios ao projeto.
module tb;
reg clk, rstn, d;
wire [3:0] out;
integer i;
lshift_4b_reg u0 ( .d(d), .clk(clk), .rstn(rstn), .out(out));
always #10 clk = ~clk;
initial begin
{clk, rstn, d} <= 0;
#10 rstn <= 1;
for (i = 0; i < 20; i=i+1) begin
@(posedge clk) d <= $random;
end
#10 $finish;
end
endmodule
Observe que cada bit é deslocado para a esquerda em 1 e o novo valor de d é aplicado ao LSB.
Verilog
- Tutorial - Escrevendo Código Combinacional e Sequencial
- Circuito com interruptor
- Circuitos integrados
- Controladores lógicos programáveis (PLC)
- Introdução à Álgebra Booleana
- Simplificação da lógica com mapas de Karnaugh
- Lógica digital com feedback
- Contador Verilog Mod-N
- Contador Verilog Cinza
- Sempre um acabamento suave com as retificadoras Okamoto