veriloghdlxilinx-ise

XXX on output ports


I have written an asynchronous FIFO buffer, but when I run it, I get XXX on output ports. I referred to questions on SO which said asserting reset signals should make it work, but despite of doing it, I am still facing the same issue.

Any help will be appreciated.

module fifo 
    #(parameter width =8,
                           addr_width = 4,
                            depth = (1 << addr_width)
     )
     ( // Read port
      output  [width - 1:0] dout,
      output reg                 empty_out,
      input wire                 rd_en,
      input wire              rclk,
      //write port
        input wire [width-1:0]  din,
        output reg                  full,
        input wire                  wr_en,
        input wire                  wclk,
        
        input wire                  rst
);

(* ram_style = "bram" *)
reg [width-1:0] memory_s[depth-1:0];
reg [31:0] push_ptr;
reg [31:0] pop_ptr;

assign dout = memory_s[pop_ptr];  // assign cannot assign values to registers
always @(posedge wclk)
    begin
        if (rst == 1)
            push_ptr <= 0;
        else if(wr_en == 1)
            begin
                memory_s\[push_ptr\] <= din;
                //$display("w: %d", push_ptr);
            if (push_ptr == (depth -1))
                    push_ptr <= 0;
            else 
                push_ptr <= push_ptr + 1;
        end
    end

always @ (posedge rclk)
    if (rst == 1)
        pop_ptr <= 0;
    else if (rd_en ==1)
        begin
                //dout <= memory_s\[pop_ptr\]; 
                //$display("r: %d", pop_ptr);
            if (pop_ptr == depth-1)
                pop_ptr <=0;
            else
                pop_ptr <= pop_ptr+1;
        end

reg full_s;
reg overflow;

always @*
begin
        if (rst == 1)
            full_s <= 0;
        else if (push_ptr <= pop_ptr)
            if (push_ptr + 1 == pop_ptr)
               begin
                full_s <= 1;
                $display("push,pop,full: %d %d %d", push_ptr,pop_ptr,full_s); 
                end
            else 
                full_s <=0;
        else 
            if(push_ptr + 1 == pop_ptr + depth) 
               begin
                full_s <= 1;
                $display("push,pop,full: %d %d %d", push_ptr,pop_ptr,full_s);
                end
            else
                full_s <= 0;
        
        end
endmodule]

Here is a waveform:

waveform
(external link)

Added Testbench module fifoTb;

// Inputs
reg rd_en;
reg rclk;
reg [7:0] din;
reg wr_en;
reg wclk;
reg rst;

// Outputs
wire[7:0] dout;
wire empty_out;
wire full;

// Instantiate the Unit Under Test (UUT)
fifo uut (
    .dout(dout), 
    .empty_out(empty_out), 
    .rd_en(rd_en), 
    .rclk(rclk), 
    .din(din), 
    .full(full), 
    .wr_en(wr_en), 
    .wclk(wclk), 
    .rst(rst)
);
initial begin
    // Initialize Inputs
    rd_en = 0;
    rclk = 0;
    
    wr_en = 0;
    wclk = 0;
    rst = 1;
    din = 8'h0;
    // Wait 100 ns for global reset to finish
    #100;
  rst = 0; 
    wr_en = 1;
    din = 8'h1;
    #101 din = 8'h2;
    rd_en = 1;
    // Add stimulus here

end

always begin #10 wclk = ~wclk; end

always begin #10 rclk = ~rclk; end
endmodule

Solution

  • I would suggest adding additional logic on your output dout signal to avoid having 'bxxx values because memory_s has an initial value of 'bxxx:

    assign dout = (rd_en) ? memory_s[pop_ptr] : 0;
    

    Additional tips in creating your testbench:

    First, it is very important to try to understand how your device works.

    Upon reading your RTL code, I concluded that your fifo works in the following manner:

    Write operation

    always @(posedge wclk)
      begin
         if (rst == 1)
           push_ptr <= 0;
         else if(wr_en == 1)
           begin
              memory_s[push_ptr] <= din;
              if (push_ptr == (depth -1))
                push_ptr <= 0;
              else
                push_ptr <= push_ptr + 1;
           end
      end
    

    When wr_en is high, two operations are performed.

    1. The value from din will be written on memory_s pointed by push_ptr at the next positive edge of wclk.
    2. If push_ptr is equal with (depth -1), 0 will be written to the register push_ptr else register push_ptr is incremented by 1 instead.

      Write operation will not be performed when wr_en is low.

    Read operation

    assign dout = memory_s[pop_ptr];
    
    always @ (posedge rclk)
      if (rst == 1)
        pop_ptr <= 0;
      else if (rd_en ==1)
        begin
           if (pop_ptr == depth-1)
             pop_ptr <=0;
           else
             pop_ptr <= pop_ptr+1;
        end
    

    When rd_en is high, increment the register pop_ptr by 1 if pop_ptr is not equal to depth-1 else write it with 0 instead. dout will all the time hold the value of memory_s pointed by the register pop_ptr.

    Creating tasks for every operation that you are going to perform is usually convenient.

      wr_en = 1;
      din = 8'h1;
      #101 din = 8'h2;
      rd_en = 1;
    

    I created write and read tasks for you as an example and you might want to substitute your code above.

    task write(input [7:0] pdin);
       $display("[ testbench ] writing data: %0x", pdin);
       din <= pdin;
       wr_en <= 1;
       @(posedge wclk);
       din <= 0;
       wr_en <= 0;
    endtask
    
    task read(output [7:0] prdata);
       rd_en <= 1;
       @(posedge rclk);
       prdata = dout;
       rd_en <= 0;
       $display("[ testbench ] reading data: %0x", prdata);
    endtask
    

    Here is how to use the tasks:

      write(8'hAA);
      read(read_data);
    
      write(8'hCC);
      read(read_data);
    
      write(8'hBC);
      read(read_data);
    

    In writing a combinational circuit, it is not recommended to add a reset logic on to it.

     always @*
     begin
             if (rst == 1)
                 full_s <= 0; . . .
    

    Also, most of the EDA tool vendors recommend to use blocking (=) assignment in writing a combinational circuit and non-blocking assignment (<=) in a sequential circuit.

    End you're simulation when you're done by calling $finish.

    initial begin
       #1000; $finish;
    end