Intenté implementar el FIFO simple y su banco de pruebas.
Código FIFO Testbench:
'timescale 1ns / 1ps
module fifo_tb();
localparam B=4, W=2; //4bit sized 4 entry fifo
localparam T=20;
reg clk, reset;
reg rd, wr;
reg [B-1:0] w_data;
wire [B-1:0] r_data;
wire empty, full;
fifo fifo
(.clk(clk), .reset(reset), .rd(rd), .wr(wr),
.w_data(w_data), .empty(empty), .full(full),
.r_data(r_data));
always begin
clk = 1'b1;
#(T/2);
clk = 1'b0;
#(T/2);
end
initial begin
reset = 1'b1;
#(T/2);
reset = 1'b0;
end
initial begin
wr = 1;
rd = 0;
w_data = {B{'b1000}};
@(negedge reset);
@(negedge clk);
w_data = {B{'b0100}};
@(negedge clk);
w_data = {B{'b0010}};
@(negedge clk);
w_data = {B{'b0001}};
@(negedge clk);
$stop;
end
endmodule
Código del módulo FIFO:
module fifo
#(
parameter B=8, // number of bits in a word
W=4 // number of address bits
)
(
input wire clk, reset,
input wire rd, wr,
input wire [B-1:0] w_data,
output wire empty, full,
output wire [B-1:0] r_data
);
reg [B-1:0] array_reg [2**W-1:0];
reg [W-1:0] w_ptr_reg, w_ptr_next, w_ptr_succ; //tail
reg [W-1:0] r_ptr_reg, r_ptr_next, r_ptr_succ; //head
reg full_reg, empty_reg, full_next, empty_next;
wire wr_en;
//register file write operation
always @(posedge clk)
if (wr_en)
array_reg[w_ptr_reg] <= w_data;
//register file read operation
assign r_data = array_reg[r_ptr_reg];
//write enabled only when FIFO is not full
assign wr_en = wr & ~full_reg;
//fifo control logic
//register for read and write pointers
always @(posedge clk, posedge reset)
if (reset)
begin
w_ptr_reg <= 0;
r_ptr_reg <= 0;
full_reg <= 1'b0;
empty_reg <= 1'b1;
end
else
begin
w_ptr_reg <= w_ptr_next;
r_ptr_reg <= r_ptr_next;
full_reg <= full_next;
empty_reg <= empty_next;
end
//next state logic for read and write pointers.
always @*
begin
//successive pointer values
w_ptr_succ = w_ptr_reg + 1;
r_ptr_succ = r_ptr_reg + 1;
//default: keep old values.
w_ptr_next = w_ptr_reg;
r_ptr_next = r_ptr_reg;
full_next = full_reg;
empty_next = empty_reg;
case ({wr, rd})
// 2'b00 no op
2'b01: //read
if (~empty_reg) // not empty
begin
r_ptr_next = r_ptr_succ;
full_next = 1'b0;
if (r_ptr_succ == w_ptr_reg)
empty_next = 1'b1;
end
2'b10: //write
if (~full_reg) // not full
begin
w_ptr_next = w_ptr_succ;
empty_next = 1'b0;
if (w_ptr_succ == r_ptr_reg)
full_next = 1'b1;
end
2'b11: //write and read
begin
w_ptr_next = w_ptr_succ;
r_ptr_next = r_ptr_succ;
end
endcase
end
//output
assign full = full_reg;
assign empty = empty_reg;
endmodule
Por lo que yo sé poniendo @ (negedge clk); hace que el código se actualice
para ser ejecutado en el borde negativo del reloj, y
poniendo @ (negedege clk); @ (negedge claro); back to back hace que el código se ejecute en el borde negativo del reloj que se genera después de que el borrado se convierte en cero.
Por lo tanto, he esperado que el primer bloque de código en el bloque inicial
wr = 1;
rd = 0;
w_data = {B{'b1000}};
@(negedge reset);
@(negedge clk);
se ejecutará después del reinicio < = 0 y clk < = 0 (es decir, 30 ns en la siguiente simulación).
Sin embargo, el resultado de la simulación muestra que los valores wr, rd, w_data se establecen en el primer flanco ascendente del reloj, no en las 30 ns. ¿Qué bloque de código generó este resultado de simulación no deseado?
He intentado depurarlo, pero aún me cuesta encontrarlo ...
Aprecio cualquier ayuda.