//  -----------------------------------------------------------------------------------
// | Lookahead Multiport Memory
//  -----------------------------------------------------------------------------------
// | Module Name     : data_format_adapter_state_ram
// | Author          : kjo
// | Created On      : 2006 Jun 22
// | Description     :
// |   Standard multi-port memory, where a read on cycle n responds with data on 
// |   cycle n+2.  The lookahead protection means that if you write data on cycle n or 
// |   cycle n+1, the data returned on cycle n+2 is the written data.
//  -----------------------------------------------------------------------------------

`timescale 1ns / 100ps
module data_format_adapter_state_ram (
    
      // Interface: clock
      input              clk,
      input              reset_n,
      // Interface: write
      input      [ 0: 0] wr_address,
      input      [ 1: 0] wr_writedata,
      input              wr_write,
      output reg         wr_waitrequest,
      // Interface: read0
      input      [ 0: 0] rd0_address,
      output reg [ 1: 0] rd0_readdata
);

   // ---------------------------------------------------------------------
   //| Internal Parameters
   // ---------------------------------------------------------------------
   localparam  DEPTH           = 1;
   localparam  DATA_WIDTH      = 2;
   localparam  ADDRESS_WIDTH   = 1;
   localparam  CLEAR_ON_RESET  = 1;

   // ---------------------------------------------------------------------
   //| Signals
   // ---------------------------------------------------------------------
   reg [DATA_WIDTH-1:0]        wr_writedata1;
   reg [ADDRESS_WIDTH-1:0]     reset_count;
   reg [DATA_WIDTH-1:0]        mem_wr_writedata;
   reg [ADDRESS_WIDTH-1:0]     mem_wr_address;
   reg 			       mem_wr_write;
   reg [DATA_WIDTH-1:0]        mem [DEPTH-1:0];
   
   reg 			      rd0_bypass;
   reg [DATA_WIDTH-1:0]       rd0_mem_readdata;
   
   // ---------------------------------------------------------------------
   // Synchronous Stuff
   // ---------------------------------------------------------------------
   always @(posedge clk or negedge reset_n) begin
      if (!reset_n) begin
	 wr_writedata1  <= 0;
	 wr_waitrequest <= 1;
	 if (CLEAR_ON_RESET) begin
	    reset_count    <= DEPTH-1'b1;
	 end else begin
	    reset_count    <= 0;
	 end
	 rd0_bypass     <= 0;
      end else begin
  rd0_bypass   <= (rd0_address == wr_address) && wr_write;
	 wr_writedata1  <= wr_writedata;
	 
	 // Memory reset state machine
	 if (reset_count > 0) begin
	    reset_count <= reset_count - 1'b1;
	 end else begin
	    wr_waitrequest <= 0;
	 end
      end
   end
   
   // ---------------------------------------------------------------------
   // Combinatorial Memory Control
   // --------------------------------------------------------------------- 
   always @* begin
      rd0_readdata     = rd0_mem_readdata;
      mem_wr_write     = wr_write;
      mem_wr_address   = wr_address;
      mem_wr_writedata = wr_writedata;
      
      // Lookahead
      if (rd0_bypass) begin
	 rd0_readdata   = wr_writedata1;
      end
      // Memory Reset
      if (CLEAR_ON_RESET) begin
	 if ( wr_waitrequest ) begin
	    mem_wr_write  = 1;
	    mem_wr_address = reset_count;
	    mem_wr_writedata    = 0;
	 end
      end
   end
   
   // --------------------------------------------------------------------- 
   // Infer Memory
   // --------------------------------------------------------------------- 
   always @(posedge clk) begin
      if (mem_wr_write)
         mem[mem_wr_address] <= mem_wr_writedata;
      rd0_mem_readdata    <= mem[rd0_address];
   end
   
endmodule

// synthesis translate_off

//  --------------------------------------------------------------------------------
// | test bench
//  --------------------------------------------------------------------------------

module test_data_format_adapter_state_ram;

   // ---------------------------------------------------------------------
   //| Internal Parameters
   // ---------------------------------------------------------------------
   localparam  DEPTH                   = 1;
   localparam  DATA_WIDTH              = 2;
   localparam  ADDRESS_WIDTH           = 1;
   localparam  CLEAR_ON_RESET          = 1;
   localparam  CLOCK_HALF_PERIOD       = 10;
   localparam  CLOCK_PERIOD            = 2*CLOCK_HALF_PERIOD;
   localparam  RESET_TIME              = 25;
   
   // ---------------------------------------------------------------------
   //| Signals
   // ---------------------------------------------------------------------
   reg 	                     clk            = 0;
   reg 			     reset_n        = 0;
   reg 			     test_success = 1;
   reg 			     success      = 1;
   reg [ADDRESS_WIDTH-1:0]   wr_address;
   reg [DATA_WIDTH-1:0]      wr_writedata;
   reg 			     wr_write;
   wire 		     wr_waitrequest;
   reg [ADDRESS_WIDTH-1:0]   rd0_address ;
   wire [DATA_WIDTH-1:0]     rd0_readdata;
   reg [DATA_WIDTH-1:0]      mem_mirror [DEPTH-1:0];
			 
   // ---------------------------------------------------------------------
   //| DUT
   // ---------------------------------------------------------------------
   data_format_adapter_state_ram dut ( 
		 .clk              (clk)               ,
		 .reset_n          (reset_n)           ,
		 .rd0_address   (rd0_address)    ,
		 .rd0_readdata  (rd0_readdata)   ,
		 .wr_address       (wr_address)        ,
		 .wr_writedata     (wr_writedata)      ,
		 .wr_write         (wr_write)          ,
		 .wr_waitrequest   (wr_waitrequest)          
		 );

   // ---------------------------------------------------------------------
   //| Clock & Reset
   // ---------------------------------------------------------------------
   initial begin
      reset_n = 0;
      #RESET_TIME;
      reset_n = 1;
   end
   
   always begin
      #CLOCK_HALF_PERIOD;
      clk <= ~clk;
   end

   // ---------------------------------------------------------------------
   //| Main Test
   // ---------------------------------------------------------------------
   initial begin
      test_reset();
      test_random();
      test_single_channel();
      
      $finish;
   end
   
   // ---------------------------------------------------------------------
   //| Test reset
   // ---------------------------------------------------------------------
   task test_reset;
      integer i;
      begin
	 test_success = 1;

	 if (CLEAR_ON_RESET) begin
	    @(posedge clk);
	    test_assert ("Memory's wr_waitrequest should be test_asserted while held in reset.", wr_waitrequest);

	    wait (reset_n == 1);
	    test_assert ("Memory should still be in reset immediately after clearing the in_reset signal.", wr_waitrequest);

	    wait (wr_waitrequest == 0);
	    
	    for (i=0; i<DEPTH; i=i+1) begin
	       rd0_address <= i;
	       @(posedge clk);
	       test_assert ("Memory location should be 0.", rd0_readdata==0);
	    end
	 end else begin
	    @(posedge clk);
	    test_assert ("Memory's in_reset should be asserted while held in reset.", wr_waitrequest);

	    wait (reset_n == 1);
	    @(posedge clk);
	    #1 test_assert ("Memory should have in_reset=0 immediately after reset!!.", wr_waitrequest==0);
	 end // else: !if(CLEAR_ON_RESET)
	 endtest("test_reset");
      end	 
   endtask

   // ---------------------------------------------------------------------
   //| Test random in & out data
   // ---------------------------------------------------------------------
   task test_random;
      integer i;
      begin


	 // Initialize mem_mirror
	 for (i=0; i<DEPTH; i=i+1) begin
	    if (CLEAR_ON_RESET) begin
	       mem_mirror[i] = 0;
	    end else begin
	       mem_mirror[i] = 256'bX;
	    end
	 end

	 wait (reset_n == 1);
	 wait (wr_waitrequest == 0);
	 
	 fork
	    begin
	       repeat (20*DEPTH) begin // do 20x Depth writes.
		  #1;
		  wr_write = $random % 2;
		  wr_address = ($random & 31'hFFFFFFFF) % DEPTH;
		  wr_writedata = $random;
		  #1;
		  if (wr_write)
		    mem_mirror[wr_address] = wr_writedata;
		  @(posedge clk);
	       end
	    end
	    
	    begin
	       repeat (80*DEPTH) begin // do 80x Depth reads.
		  rd0_address = ($random & 31'hFFFFFFFF) % DEPTH;
		  @(posedge clk);
		  #1;
		  test_assert("Data Mismatch (port 0)", mem_mirror[rd0_address] == rd0_readdata);
	       end
	    end
	    
	 join
	 endtest("test_random");
      end
   endtask // test_random

   // ---------------------------------------------------------------------
   //| Test a single port
   // ---------------------------------------------------------------------
   task test_single_channel;
      integer i;
      begin

	 reset_n <= 0;
	 @(posedge clk);
	 reset_n <= 1;
	 
	 // Initialize mem_mirror
	 for (i=0; i<DEPTH; i=i+1) begin
	    if (CLEAR_ON_RESET) begin
	       mem_mirror[i] = 0;
	    end else begin
	       mem_mirror[i] = 256'bX;
	    end
	 end

	 wait (reset_n == 1);
	 wait (wr_waitrequest == 0);
	 
	 wr_write  = 0;
	 wr_address = ($random & 31'hFFFFFFFF) % DEPTH;
	 rd0_address  = wr_address;
	 
	 repeat (200) begin // do 200 times
	    wr_write  = ($random & 1'b1);
	    wr_writedata    = $dist_uniform(23,0,256);
	    #1;
	    mem_mirror[wr_address] = wr_writedata;
	    @(posedge clk);
	    #1;
	    test_assert("Data Mismatch", mem_mirror[rd0_address] == rd0_readdata);
	 end
	 endtest("test_single_channel");
      end
   endtask // test_random
   
   
   // ---------------------------------------------------------------------
   //| Test_assert
   // ---------------------------------------------------------------------
   task test_assert;
      input [256:0] message;
      input         condition;
      begin
	 if (! condition) begin
	    $display("%t: %s ",$time, message);
	    success = 0;
	    test_success = 0;
	 end
      end
   endtask

   // ---------------------------------------------------------------------
   //| End Test
   // ---------------------------------------------------------------------
   task endtest;
      input [256:0] message;
      begin
	 if (test_success) begin
	    $display("(sim)%t: %-40s: Pass",$time, message);
	 end else begin
	    $display("(sim)%t: %-40s: Fail",$time, message);
	 end
	 success = success & test_success;
      end
   endtask

endmodule

// synthesis translate_on


