Files
iverilog/ivtest/contrib/fifo.v
T
Stephen Williams cea237b407 Add ivtest to the iverilog source tree
By adding ivtest to the iverilog source tree, it is easier to keep
the regression test synchronized with the source that is being tested.
This should be especially helpful for PRs that add a new feature, and
have a matching ivtest PR with the regression test for that feature.
2022-01-15 10:18:50 -08:00

376 lines
7.5 KiB
Verilog

`begin_keywords "1364-2005"
//
// Copyright (c) 1999 Thomas Coonan ([email protected])
//
// This source code is free software; you can redistribute it
// and/or modify it in source code form under the terms of the GNU
// General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
//
// Synchronous FIFO. 4 x 16 bit words.
//
// Modified by SDW to print out PASSED only if DEBUG not defined.
// Also changed TEST1 so that it is "self checking" by adding a
// passed in value to read_word.
//
module fifo (clk, rstp, din, writep, readp, dout, emptyp, fullp);
input clk;
input rstp;
input [15:0] din;
input readp;
input writep;
output [15:0] dout;
output emptyp;
output fullp;
// Defines sizes in terms of bits.
//
parameter DEPTH = 3, // 2 bits, e.g. 4 words in the FIFO.
MAX_COUNT = 3'b111; // topmost address in FIFO.
reg emptyp;
reg fullp;
// Registered output.
reg [15:0] dout;
// Define the FIFO pointers. A FIFO is essentially a circular queue.
//
reg [(DEPTH-1):0] tail;
reg [(DEPTH-1):0] head;
// Define the FIFO counter. Counts the number of entries in the FIFO which
// is how we figure out things like Empty and Full.
//
reg [(DEPTH-1):0] count;
// Define our regsiter bank. This is actually synthesizable!
//
reg [15:0] fifomem[0:MAX_COUNT];
// Dout is registered and gets the value that tail points to RIGHT NOW.
//
always @(posedge clk)
begin
if (rstp == 1)
dout <= 16'h0000;
else
dout <= fifomem[tail];
end
// Update FIFO memory.
always @(posedge clk) begin
if (rstp == 1'b0 && writep == 1'b1 && fullp == 1'b0) begin
fifomem[head] <= din;
end
end
// Update the head register.
//
always @(posedge clk) begin
if (rstp == 1'b1) begin
head <= 2'b00;
end
else begin
if (writep == 1'b1 && fullp == 1'b0) begin
// WRITE
head <= head + 1;
end
end
end
// Update the tail register.
//
always @(posedge clk) begin
if (rstp == 1'b1) begin
tail <= 2'b00;
end
else begin
if (readp == 1'b1 && emptyp == 1'b0) begin
// READ
tail <= tail + 1;
end
end
end
// Update the count regsiter.
//
always @(posedge clk) begin
if (rstp == 1'b1) begin
count <= 2'b00;
end
else begin
case ({readp, writep})
2'b00: count <= count;
2'b01:
// WRITE
if (count != MAX_COUNT)
count <= count + 1;
2'b10:
// READ
if (count != 2'b00)
count <= count - 1;
2'b11:
// Concurrent read and write.. no change in count
count <= count;
endcase
end
end
// *** Update the flags
//
// First, update the empty flag.
//
always @(count) begin
if (count == 2'b00)
emptyp <= 1'b1;
else
emptyp <= 1'b0;
end
// Update the full flag
//
always @(count) begin
if (count == MAX_COUNT)
fullp <= 1'b1;
else
fullp <= 1'b0;
end
endmodule
// synopsys translate_off
`define TEST_FIFO
// synopsys translate_off
`ifdef TEST_FIFO
module test_fifo;
reg clk;
reg rstp;
reg [15:0] din;
reg readp;
reg writep;
wire [15:0] dout;
wire emptyp;
wire fullp;
reg error ;
reg [15:0] value;
fifo U1 (
.clk (clk),
.rstp (rstp),
.din (din),
.readp (readp),
.writep (writep),
.dout (dout),
.emptyp (emptyp),
.fullp (fullp)
);
//
// SDW Added self testing aspect here..
//
task read_word;
input [15:0] expect;
begin
@(negedge clk);
readp = 1;
@(posedge clk) #5;
`ifdef DEBUG
$display ("Expect %0h, Read %0h from FIFO",
`endif // DEBUG
if(expect !== dout)
begin
$display ("FAILED - Expect %0h, Read %0h from FIFO",
expect,dout);
error = 1;
end
readp = 0;
end
endtask
task write_word;
input [15:0] value;
begin
@(negedge clk);
din = value;
writep = 1;
@(posedge clk);
`ifdef DEBUG
$display ("Write %0h to FIFO", din);
`endif // DEBUG
#5;
din = 16'hzzzz;
writep = 0;
end
endtask
initial begin
clk = 0;
forever begin
#10 clk = 1;
#10 clk = 0;
end
end
initial begin
error = 0; // Set error to zero here.
`ifdef DEBUG
$dumpfile("test.vcd");
$dumpvars(0,test_fifo);
`endif // DEBUG
test1;
//test2;
if(error == 0)
$display("PASSED");
$finish;
end
task test1;
begin
din = 16'hzzzz;
writep = 0;
readp = 0;
// Reset
rstp = 1;
#50;
rstp = 0;
#50;
// ** Write 3 values.
write_word (16'h1111);
write_word (16'h2222);
write_word (16'h3333);
// ** Read 2 values
read_word(16'h1111);
read_word(16'h2222);
// ** Write one more
write_word (16'h4444);
// ** Read a bunch of values
read_word(16'h3333);
// *** Write a bunch more values
write_word (16'h0001);
write_word (16'h0002);
write_word (16'h0003);
write_word (16'h0004);
write_word (16'h0005);
write_word (16'h0006);
write_word (16'h0007);
write_word (16'h0008);
// ** Read a bunch of values
read_word(16'h4444);
read_word(16'h0001);
read_word(16'h0002);
read_word(16'h0003);
read_word(16'h0004);
read_word(16'h0005);
read_word(16'h0006);
end
endtask
`ifdef TEST2
// TEST2
//
// This test will operate the FIFO in an orderly manner the way it normally works.
// 2 threads are forked; a reader and a writer. The writer writes a counter to
// the FIFO and obeys the fullp flag and delays randomly. The reader likewise
// obeys the emptyp flag and reads at random intervals. The result should be that
// the reader reads the incrementing counter out of the FIFO. The empty/full flags
// should bounce around depending on the random delays. The writer repeats some
// fixed number of times and then terminates both threads and kills the sim.
//
task test2;
reg [15:0] writer_counter;
begin
writer_counter = 16'h0001;
din = 16'hzzzz;
writep = 0;
readp = 0;
// Reset
rstp = 1;
#50;
rstp = 0;
#50;
fork
// Writer
begin
repeat (500) begin
@(negedge clk);
if (fullp == 1'b0) begin
write_word (writer_counter);
#5;
writer_counter = writer_counter + 1;
end
else begin
$display ("WRITER is waiting..");
end
// Delay a random amount of time between 0ns and 100ns
#22 ;
end
$display ("Done with WRITER fork..");
$finish;
end
// Reader
begin
forever begin
@(negedge clk);
if (emptyp == 1'b0) begin
read_word;
end
else begin
$display ("READER is waiting..");
end
// Delay a random amount of time between 0ns and 100ns
#50;
end
end
join
end
endtask
/*
always @(fullp)
$display ("fullp = %0b", fullp);
always @(emptyp)
$display ("emptyp = %0b", emptyp);
always @(U1.head)
$display ("head = %0h", U1.head);
always @(U1.tail)
$display ("tail = %0h", U1.tail);
*/
`endif // TEST2
endmodule
`endif
`end_keywords