// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Copyright (c) 2005 by Lattice Semiconductor Corporation
// --------------------------------------------------------------------
//
// Permission:
//
//   Lattice Semiconductor grants permission to use this code for use
//   in synthesis for any Lattice programmable logic product.  Other
//   use of this code, including the selling or duplication of any
//   portion is strictly prohibited.
//
// Disclaimer:
//
//   This VHDL or Verilog source code is intended as a design reference
//   which illustrates how these types of functions can be implemented.
//   It is the user's responsibility to verify their design for
//   consistency and functionality through the use of formal
//   verification methods.  Lattice Semiconductor provides no warranty
//   regarding the use or functionality of this code.
//
// --------------------------------------------------------------------
//
//                     Lattice Semiconductor Corporation
//                     5555 NE Moore Court
//                     Hillsboro, OR 97214
//                     U.S.A
//
//                     TEL: 1-800-Lattice (USA and Canada)
//                          408-826-6000 (other locations)
//
//                     web: http://www.latticesemi.com/
//                     email: techsupport@latticesemi.com
//
// --------------------------------------------------------------------
//
//  Project:           DDR memory test
//  File:              ddr_test.v
//  Title:             ddr_test
//  Description:       This module implements state machines that
//                     perform the DDR memory test through Lattice IP
//                     core DDRCT-GEN-E2-N1.
//
// --------------------------------------------------------------------
//
// Revision History :
// --------------------------------------------------------------------
// $Log: ddr_test.v,v $
// Revision 1.0  2005-06-23 10:56:27-07  jhsin
// Initial revision
//
// Revision 1.2  2005-05-09 16:07:44-07  jhsin
// Fixed two major bugs related to the generations of data_mask and datacheck signals.
//
// Revision 1.1  2005-02-02 17:05:08-08  jhsin
// 1. Modified code so that the last few reads/writes are covered in the test.
// 2. Reduced wait_count size from 8-bit to 7-bit.
//
// Revision 1.0  2005-02-01 16:42:01-08  jhsin
// Initial revision
//
//
// --------------------------------------------------------------------

`include "ddrct_gen_e2_1_001_params.v"
`timescale 100ps / 10ps

module ddr_test (
   //-------INPUTS
   waited_200us,
   init_done,
   pll_clk,
   rst_n,
   cmd_rdy,
   data_rdy,
   read_data_valid,
   read_data,

   //-------OUTPUTS
   init_start,
   test_data,
   fsm2,
   datacheck,
   latched_data,
   number_of_tests_passed,
   number_of_tests_failed,
   cmd,
   cmd_valid,
   data_mask,
   addr,
   write_data
);

input                    waited_200us;
input                    init_done;
input                    pll_clk;
input                    rst_n;
input                    cmd_rdy;
input                    data_rdy;
input                    read_data_valid;
input  [`DSIZE-1:0]      read_data;

output                   init_start;
output [7:0]             test_data;
output                   fsm2;
output [1:0]             datacheck;
output [15:0]            latched_data;
output [3:0]             number_of_tests_passed;
output [3:0]             number_of_tests_failed;
output [3:0]             cmd;
output                   cmd_valid;
output [`USER_DM-1:0]    data_mask;
output [`ADDR_WIDTH-1:0] addr;
output [`DSIZE-1:0]      write_data;

reg                      init_start;
reg [3:0]                cmd;
reg                      cmd_valid;
reg [`ADDR_WIDTH-1:0]    addr;
reg [`DSIZE-1:0]         write_data;
reg [15:0]               latched_data;

reg                      inc_test_addr;
reg [`ADDR_WIDTH-1:0]    test_addr;
reg [7:0]                test_data;
reg [1:0]                datacheck;
reg [3:0]                number_of_tests_passed;
reg [3:0]                number_of_tests_failed;
reg                      end_of_test_addr;

reg                      stop_init;
reg                      read_data_valid_delay;
reg                      data_rdy_delay;
reg                      data_rdy_delay2;
reg [6:0]                wait_count;

//
// Wait 200µs after the power is applied and the clocks are running before
// asserting the "init_start" signal.
// Signal "init_start" should be kept asserted until the controller signaling
// the completion of the initialization endprocess asserts "init_done".
//
always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      init_start <= 1'b0;
      stop_init <= 1'b0;
   end
   else if (waited_200us & ~init_start & ~stop_init) begin
      init_start <= 1'b1;
      stop_init <= 1'b1;
   end
   else if (init_done) begin
      init_start <= 1'b0;
   end
end

//
// Delays singnals
//    (1) read_data_valid (used in the READs), and
//    (2) data_rdy (used in the WRITEs)
//
always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      read_data_valid_delay <= 1'b0;
      data_rdy_delay <= 1'b0;
      data_rdy_delay2 <= 1'b0;
   end
   else begin
      read_data_valid_delay <= read_data_valid;
      data_rdy_delay <= data_rdy;
      data_rdy_delay2 <= data_rdy_delay;
   end
end

//
// Enable all bytes
//
assign data_mask = {(`USER_DM){1'b0}};

//
// FSM1 : main state machine for DDR memory testing
//
reg [4:0] fsm1;
parameter fsm1_idle             = 5'b00001,
          fsm1_write            = 5'b00010,
          fsm1_verify           = 5'b00100,
          fsm1_change_test_data = 5'b01000,
          fsm1_wait_for_cmd_rdy = 5'b10000;

`define CMD_READ     4'b0001
`define CMD_WRITE    4'b0010
`define CMD_READA    4'b0011
`define CMD_WRITEA   4'b0100
`define CMD_PDOWN    4'b0101
`define CMD_LOAD_MR  4'b0110
`define CMD_SELF_REF 4'b0111

always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      cmd              <= 4'b0;
      cmd_valid        <= 1'b0;
      addr             <= {(`ADDR_WIDTH){1'b0}};
      write_data       <= {(`DSIZE){1'b0}};
      inc_test_addr    <= 1'b0;
      datacheck        <= 2'b0;
      latched_data     <= 16'b0;
      wait_count       <= 7'h0;
      fsm1             <= fsm1_idle;
   end
   else begin
      case (fsm1)
         fsm1_idle:
            if (init_done) begin
               fsm1 <= fsm1_wait_for_cmd_rdy;
            end
         fsm1_wait_for_cmd_rdy:
            if (cmd_rdy) begin
               addr <= test_addr;
               write_data <= { 48'h0, test_data, (test_data ^ 8'hff) };
               datacheck <= 2'b0;
               cmd <= `CMD_WRITE;
               cmd_valid <= 1'b1;
               fsm1 <= fsm1_write; // Switch to fsm1_write to start a new test
               inc_test_addr <= 1'b1;
            end
         fsm1_write:
            begin
               addr <= test_addr;
               write_data <= { 48'h0, test_data, (test_data ^ 8'hff) };
               datacheck <= 2'b0;
               if (cmd_rdy & end_of_test_addr) begin
                  cmd <= `CMD_READ; // Switch to fsm1_verify to read data back and check if is correct
                  fsm1 <= fsm1_verify;
               end
            end
         fsm1_verify:
            begin
               addr <= test_addr;
               write_data <= { 48'h0, test_data, (test_data ^ 8'hff) }; // Even after switched to read, write_data still
                                                                        // needs to be updated for the last few writes
               if (read_data_valid_delay & ~read_data_valid) begin // Checking readback data
                  latched_data <= read_data[15:0];
                  datacheck <= {|(read_data[15: 8]^(test_data)),
                                |(read_data[ 7: 0]^(test_data ^ 8'hff))};
               end
               if (cmd_rdy & end_of_test_addr) begin
                  cmd <= `CMD_WRITE;
                  fsm1 <= fsm1_change_test_data; // Switch to fsm1_change_test_data to change test_data for the next test
                  cmd_valid <= 1'b0;
               end
            end
         fsm1_change_test_data:
            begin
               write_data <= { 48'h0, test_data, (test_data ^ 8'hff) };
               if (cmd_rdy & (wait_count[6] == 1'b1)) begin
                  datacheck <= 2'b0;
                  fsm1 <= fsm1_write; // Switch to fsm1_write to start another new test
                  cmd_valid <= 1'b1;
                  wait_count <= 7'h0;
               end
               else begin
                  if (read_data_valid_delay & ~read_data_valid) begin // Even after switched to this state, we still need
                                                                      // to check data for the last few reads
                     latched_data <= read_data[15:0];
                     datacheck <= {|(read_data[15: 8]^(test_data)),
                                   |(read_data[ 7: 0]^(test_data ^ 8'hff))};
                  end
                  wait_count <= wait_count + 1;
               end
            end
      endcase
   end
end

//
// Creating test_addr
//
always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      test_addr <= {(`ADDR_WIDTH){1'b0}};
   end
   else if (inc_test_addr & cmd_rdy & cmd_valid) begin
      // Advance test_addr for the next READ command or WRITE command
      // Default burst length = 4, therefore increase test_addr by 4
      test_addr <= test_addr + 4;
   end
end

//
// Detecting end_of_test_addr
//
always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      end_of_test_addr <= 1'b0;
   end
   else if (test_addr == {{(`ADDR_WIDTH-2){1'b1}}, {(2){1'b0}}}) end_of_test_addr <= 1'b1;
   else end_of_test_addr <= 1'b0;
end

//
// Creating test_data
//
always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      test_data <= 8'h01;
   end
   else begin
      if ((data_rdy_delay2 & ~data_rdy_delay) || (read_data_valid_delay & ~read_data_valid)) begin
         // Rotate test_data for every read command and write command
         // During the same test, test_data will be rotated whenever
         //    (1) test_data is sampled by data_rdy durring the write, or
         //    (2) read_data_valid indicates data is read back (rotate test_data for data checking)
         test_data <= { test_data[6:0], test_data[7] };
      end
      else if (wait_count == 7'h20) begin // Change "test_data" for the next test
         //
         // test #                =>  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 , 17 18 19 20 21 22 23 24 ...
         // test_data(at addr  0) => 01 fd 04 f7 10 df 40 7f fe 02 fb 08 ef 20 bf 80 , 01 fd 04 f7 10 df 40 7f ...
         // test_data(at addr  4) => 02 fb 08 ef 20 bf 80 fe fd 04 f7 10 df 40 7f 01 , 02 fb 08 ef 20 bf 80 fe ...
         // test_data(at addr  8) => 04 f7 10 df 40 7f 01 fd fb 08 ef 20 bf 80 fe 02 , 04 f7 10 df 40 7f 01 fd ...
         // test_data(at addr  c) => 08 ef 20 bf 80 fe 02 fb f7 10 df 40 7f 01 fd 04 , 08 ef 20 bf 80 fe 02 fb ...
         // test_data(at addr 10) => 10 df 40 7f 01 fd 04 f7 ef 20 bf 80 fe 02 fb 08 , 10 df 40 7f 01 fd 04 f7 ...
         // test_data(at addr 14) => 20 bf 80 fe 02 fb 08 ef df 40 7f 01 fd 04 f7 10 , 20 bf 80 fe 02 fb 08 ef ...
         // test_data(at addr 18) => 40 7f 01 fd 04 f7 10 df bf 80 fe 02 fb 08 ef 20 , 40 7f 01 fd 04 f7 10 df ...
         // test_data(at addr 1c) => 80 fe 02 fb 08 ef 20 bf 7f 01 fd 04 f7 10 df 40 , 80 fe 02 fb 08 ef 20 bf ...
         // test_data(at addr 20) => 01 fd 04 f7 10 df 40 7f fe 02 fb 08 ef 20 bf 80 , 01 fd 04 f7 10 df 40 7f ...
         //         :
         //         :
         if ((test_data == 8'h80) || (test_data == 8'h7f)) begin
            test_data <= { test_data[6:0], test_data[7] }; // Rotated test_data
         end
         else begin
            test_data <= { test_data[6:0], test_data[7] } ^ 8'hff; // Rotate test_data and then invert it
         end
      end
   end
end


//
// FSM2 for increaing success/failure counters during the data checking
//
reg fsm2;
parameter fsm2_wait_for_data_check = 1'b0,
          fsm2_check_readback_data = 1'b1;

always @(posedge pll_clk or negedge rst_n)
begin
   if (~rst_n) begin
      number_of_tests_passed <= 4'b0;
      number_of_tests_failed <= 4'b0;
      fsm2 <= fsm2_wait_for_data_check;
   end
   else begin
      case (fsm2)
         fsm2_wait_for_data_check:
            if (cmd_rdy && (fsm1 == fsm1_write) && (end_of_test_addr == 1'b1)) begin
               fsm2 <= fsm2_check_readback_data;
            end
         fsm2_check_readback_data:
            if (wait_count[5]) begin
               // test_data will be changed whenever (wait_count == 7'h20), therefore,
               // if no error found when wait_count[5] goes high (i.e. wait_count >= 7'h20),
               // then increase correct counter, go back to fsm2_wait_for_data_check
               // and wait for next test
               number_of_tests_passed <= number_of_tests_passed + 1;
               fsm2 <= fsm2_wait_for_data_check;
            end
            else if (datacheck[1] | datacheck[0]) begin
               // If there is any error (even just 1 error), increase error counter
               // and go to fsm2_wait_for_data_check for next test
               number_of_tests_failed <= number_of_tests_failed + 1;
               fsm2 <= fsm2_wait_for_data_check;
            end
      endcase
   end
end

endmodule