diff --git a/lifo.sv b/lifo.sv index 353c6d9..28f8fd0 100644 --- a/lifo.sv +++ b/lifo.sv @@ -4,19 +4,29 @@ //------------------------------------------------------------------------------ // INFO ------------------------------------------------------------------------ -// Simple single-clock LIFO buffer implementation, also known as "stack" -// Features one write- and one read- port in FWFT mode -// See also "fifo.sv" module for similar FIFO buffer implementation +// Single-clock LIFO buffer implementation, also known as "stack" +// +// Features: +// - single clock operation +// - configurable depth and data width +// - one write- and one read- port in "FWFT" or "normal" mode +// - protected against overflow and underflow +// - simultaneous read and write operations supported if not full and not empty +// - only read operation is performed when (full && r_req && w_req) +// - only write operation is performed when (empty && r_req && w_req) +// +// See also "fifo_Single_clock_reg_*.sv" modules for similar FIFO buffer implementation /* --- INSTANTIATION TEMPLATE BEGIN --- lifo #( + .FWFT_MODE( "TRUE" ), .DEPTH( 8 ), .DATA_W( 32 ) -) FF1 ( +) LF1 ( .clk( clk ), - .rst( 1'b0 ), + .nrst( 1'b1 ), .w_req( ), .w_data( ), @@ -33,7 +43,10 @@ lifo #( module lifo #( parameter - DEPTH = 4, // max elements count == DEPTH, DEPTH MUST be power of 2 + FWFT_MODE = "TRUE", // "TRUE" - first word fall-trrough" mode + // "FALSE" - normal fifo mode + + DEPTH = 8, // max elements count == DEPTH, DEPTH MUST be power of 2 DEPTH_W = $clog2(DEPTH)+1, // elements counter width, extra bit to store // "fifo full" state, see cnt[] variable comments @@ -41,7 +54,7 @@ module lifo #( parameter )( input clk, - input rst, // non-inverted reset + input nrst, // inverted reset // input port input w_req, @@ -52,7 +65,7 @@ module lifo #( parameter output logic [DATA_W-1:0] r_data, // helper ports - output logic [DATA_W-1:0] cnt = 0, + output logic [DEPTH_W-1:0] cnt = '0, output logic empty, output logic full, @@ -60,51 +73,71 @@ module lifo #( parameter ); // lifo data -logic [DEPTH-1:0][DATA_W-1:0] data = 0; +logic [DEPTH-1:0][DATA_W-1:0] data = '0; + +// data output buffer for normal fifo mode +logic [DATA_W-1:0] data_buf = '0; // cnt[] vector always holds lifo elements count // data[cnt[]] points to the first empty lifo slot // when lifo is full data[cnt[]] points "outside" of data[] -// please take attention to the case when cnt[]==0 && r_req==1'b1 && w_req==1'b1 -// this case makes no read/write to the lifo and should be handled externally +// filtered requests +logic w_req_f; +assign w_req_f = w_req && ~full; +logic r_req_f; +assign r_req_f = r_req && ~empty; + + +integer i; always_ff @(posedge clk) begin - if ( rst ) begin - data <= 0; - cnt <= 0; + if ( ~nrst ) begin + data <= '0; + cnt[DEPTH_W-1:0] <= '0; + data_buf[DATA_W-1:0] <= '0; end else begin - case ({w_req, r_req}) + unique case ({w_req_f, r_req_f}) + 2'b00: ; // nothing + 2'b01 : begin // reading out - if ( cnt[DATA_W-1:0] > 1'b0 ) begin - cnt[DATA_W-1:0] <= cnt[DATA_W-1:0] - 1'b1; + for ( i = (DEPTH-1); i > 0; i-- ) begin + data[i-1] <= data[i]; end + cnt[DEPTH_W-1:0] <= cnt[DEPTH_W-1:0] - 1'b1; end + 2'b10 : begin // writing in - if ( ~full ) begin - data[cnt[DATA_W-1:0]] <= w_data; - cnt[DATA_W-1:0] <= cnt[DATA_W-1:0] + 1'b1; - end + data[cnt[DEPTH_W-1:0]] <= w_data[DATA_W-1:0]; + cnt[DEPTH_W-1:0] <= cnt[DEPTH_W-1:0] + 1'b1; end + 2'b11 : begin // simultaneously reading and writing - if ( cnt[DATA_W-1:0] > 1'b0 ) begin - data[cnt[DATA_W-1:0]-1] <= w_data; - end - // cnt[DATA_W-1:0] <= cnt[DATA_W-1:0]; // data counter does not change + data[cnt[DEPTH_W-1:0]-1] <= w_data[DATA_W-1:0]; + // data counter does not change here end - default: ; endcase + + // data buffer works only for normal lifo mode + if( r_req_f ) begin + data_buf[DATA_W-1:0] <= data[0]; + end end end -always_comb begin - empty = ( cnt[DATA_W-1:0] == 0 ); - full = ( cnt[DATA_W-1:0] == DEPTH ); - if (~empty) begin - r_data[DATA_W-1:0] = data[cnt[DATA_W-1:0]]; // first-word fall-through mode +always_comb begin + empty = ( cnt[DEPTH_W-1:0] == '0 ); + full = ( cnt[DEPTH_W-1:0] == DEPTH ); + + if( FWFT_MODE == "TRUE" ) begin + if (~empty) begin + r_data[DATA_W-1:0] = data[0]; // first-word fall-through mode + end else begin + r_data[DATA_W-1:0] = '0; + end end else begin - r_data[DATA_W-1:0] = 0; + r_data[DATA_W-1:0] = data_buf[DATA_W-1:0]; // normal mode end fail = ( empty && r_req ) || diff --git a/lifo_tb.sv b/lifo_tb.sv index 15b270e..96b6280 100644 --- a/lifo_tb.sv +++ b/lifo_tb.sv @@ -13,15 +13,24 @@ module lifo_tb(); logic clk200; initial begin - #0 clk200 = 1; + #0 clk200 = 1'b0; forever #2.5 clk200 = ~clk200; end +// external device "asynchronous" clock +logic clk33; +initial begin + #0 clk33 = 1'b0; + forever + #15.151 clk33 = ~clk33; +end + logic rst; initial begin - #10.2 rst = 1; - #5 rst = 0; + #0 rst = 1'b0; + #10.2 rst = 1'b1; + #5 rst = 1'b0; //#10000; forever begin #9985 rst = ~rst; @@ -33,29 +42,27 @@ logic nrst; assign nrst = ~rst; logic rst_once; -initial begin // initializing non-X data before PLL starts - #10.2 rst_once = 1; - #5 rst_once = 0; -end initial begin - #510.2 rst_once = 1; // PLL starts at 500ns, clock appears, so doing the reset for modules - #5 rst_once = 0; + #0 rst_once = 1'b0; + #10.2 rst_once = 1'b1; + #5 rst_once = 1'b0; end logic nrst_once; assign nrst_once = ~rst_once; logic [31:0] DerivedClocks; -ClkDivider #( +clk_divider #( .WIDTH( 32 ) -) CD1 ( +) cd1 ( .clk( clk200 ), .nrst( nrst_once ), + .ena( 1'b1 ), .out( DerivedClocks[31:0] ) ); logic [31:0] E_DerivedClocks; -EdgeDetect ED1[31:0] ( +edge_detect ed1[31:0] ( .clk( {32{clk200}} ), .nrst( {32{nrst_once}} ), .in( DerivedClocks[31:0] ), @@ -65,44 +72,84 @@ EdgeDetect ED1[31:0] ( ); logic [15:0] RandomNumber1; -c_rand RNG1 ( +c_rand rng1 ( .clk(clk200), .rst(rst_once), .reseed(1'b0), .seed_val(DerivedClocks[31:0]), - .out(RandomNumber1[15:0])); + .out( RandomNumber1[15:0] ) +); -/*logic start; +logic start; initial begin #0 start = 1'b0; - #100.2 start = 1'b1; - #5 start = 1'b0; -end*/ - -logic read; -initial begin - #0 read = 1'b0; - #1000.2 read = 1'b1; + #100 start = 1'b1; + #20 start = 1'b0; end +// Module under test ========================================================== +// comment or uncomment to test FWFT and normal fifo modes +//`define TEST_FWFT yes + +// comment or uncomment to sweep-test or random test +`define TEST_SWEEP yes + +logic full1, empty1; +logic full1_d1, empty1_d1; + +logic direction1 = 1'b0; +always_ff @(posedge clk200) begin + if( ~nrst ) begin + direction1 <= 1'b0; + end else begin + // sweep logic + if( full1_d1 ) begin + direction1 <= 1'b1; + end else if( empty1_d1 ) begin + direction1 <= 1'b0; +end + + // these signals allow "erroring" requests testing: + // - reads from the empty fifo + // - writes to the filled fifo + full1_d1 <= full1; + empty1_d1 <= empty1; + end +end + +logic [3:0] cnt1; +logic [15:0] data_out1; lifo #( +`ifdef TEST_FWFT + .FWFT_MODE( "TRUE" ), +`else + .FWFT_MODE( "FALSE" ), +`endif .DEPTH( 8 ), .DATA_W( 16 ) ) LF1 ( .clk( clk200 ), - .rst( rst_once ), + .nrst( nrst_once ), +`ifdef TEST_SWEEP + .w_req( ~direction1 && &RandomNumber1[10] ), + .w_data( RandomNumber1[15:0] ), + + .r_req( direction1 && &RandomNumber1[10] ), + .r_data( data_out1[15:0] ), +`else .w_req( &RandomNumber1[10:9] ), .w_data( RandomNumber1[15:0] ), - .r_req( read ), - .r_data( ), + .r_req( &RandomNumber1[8:7] ), + .r_data( data_out1[15:0] ), +`endif - .cnt( ), - .empty( ), - .full( ) + .cnt( cnt1[3:0] ), + .empty( empty1 ), + .full( full1 ) );