aboutsummaryrefslogtreecommitdiff
/*
 * A wishbone slave testing module (a "mock")
 *
 * It performs a sequence of wishbone writes, reads and waits based on contents
 * of provided .mem file. It prints error messages whenever the value it reads
 * if different from the one it expects.
 *
/*
 * A Wishbone SLAVE testing module (a "mock")
 *
 * | *WISHBONE DATASHEET*                                                      |
 * |---------------------------------------------------------------------------|
 * | *Description*                   | *Specification*                         |
 * |---------------------------------+-----------------------------------------|
 * | General description             | mock MASTER model for test benches      |
 * |---------------------------------+-----------------------------------------|
 * | Supported cycles                | MASTER, pipelined READ/WRITE            |
 * |---------------------------------+-----------------------------------------|
 * | Data port, size                 | parametrizable (WORD_SIZE bytes)        |
 * | Data port, granularity          | parametrizable                          |
 * |                                 |     (WORD_SIZE/SEL_LINES bytes)         |
 * | Data port, maximum operand size | same as data port size                  |
 * | Data transfer ordering          | Little endian                           |
 * | Data transfer ordering          | Undefined                               |
 * | Address port, size              | parametrizable (ADR_BITS bits)          |
 * |---------------------------------+-----------------------------------------|
 * | Clock frequency constraints     | NONE                                    |
 * |---------------------------------+-----------------------------------------|
 * |                                 | *Signal name*    | *WISHBONE Equiv.*    |
 * |                                 |------------------+----------------------|
 * |                                 | ACK_I            | ACK_I                |
 * |                                 | ADR_O            | ADR_O()              |
 * | Supported signal list and cross | CLK_I            | CLK_I                |
 * |     reference to equivalent     | DAT_I            | DAT_I()              |
 * |     WISHBONE signals            | DAT_O            | DAT_O()              |
 * |                                 | SEL_O            | SEL_O                |
 * |                                 | STB_O            | STB_O                |
 * |                                 | CYC_O            | CYC_O                |
 * |                                 | WE_O             | WE_O                 |
 * |                                 | RST_I            | RST_I                |
 * |                                 | STALL_I          | STALL_I              |
 * |---------------------------------+-----------------------------------------|
 * | Special requirements            | Should only be used in simulation,      |
 * |                                 | not synthesizable.                      |
 */

`default_nettype none

`include "messages.vh"

`ifndef SIMULATION
 `error_SIMULATION_not_defined
; /* Cause syntax error */
`endif

module master_model
  #(
    parameter MASTER_NR = -1,
    parameter WORD_SIZE = 2, /* in bytes */
    parameter SEL_LINES = 1,
    parameter ADR_BITS = 20,
    parameter OPERATIONS_FILE = "master_operations.mem",
    parameter OPERATIONS_COUNT = 10
    )
   (
    input wire 			      ACK_I,
    input wire 			      CLK_I,
    output wire [ADR_BITS - 1 : 0]    ADR_O,
    input wire [8*WORD_SIZE - 1 : 0]  DAT_I,
    output wire [8*WORD_SIZE - 1 : 0] DAT_O,
    output wire [SEL_LINES - 1 : 0]   SEL_O,
    input wire 			      RST_I,
    output wire 		      STB_O,
    output wire 		      CYC_O,
    output wire 		      WE_O,
    input wire 			      STALL_I,

    /* Non-wishbone */
    output wire 		      finished
   );

   parameter WORD_BITS = 8 * WORD_SIZE;
   parameter GRANULARITY = WORD_BITS / SEL_LINES; /* in bits */

   parameter
     OP_READ = 0,
     OP_WRITE = 1,
     OP_WAIT = 2, /* Keep CYC_O high, but STB_O low for 1 tick */
     OP_DESELECT = 3; /* Drive CYC_O low for one tick */

   /* See below what we need this for */
   parameter BIGGEST_WIDTH = ADR_BITS > WORD_BITS ? ADR_BITS : WORD_BITS;
   /*
    * Contents of this array determine, what this master will do.
    * Each (4*n)th element denotes type of operation number n.
    * (4*n+1)th element denotes address to use, (4*n+2)th element denotes
    * data to use and (4*n+3)th element denotes SEL_O mask to use.
    */
   reg [BIGGEST_WIDTH - 1 : 0] 	      operations [4*OPERATIONS_COUNT - 1 : 0];

   /* Arrays used for verifying read reaults */
   reg 				      was_read [OPERATIONS_COUNT - 1 : 0];
   reg [SEL_LINES - 1 : 0] 	      SEL_mask [OPERATIONS_COUNT - 1 : 0];
   reg [WORD_BITS - 1 : 0] 	      expected_data [OPERATIONS_COUNT - 1 : 0];

   integer 			      i, j;

   initial begin
      $readmemh(OPERATIONS_FILE, operations, 0, 4*OPERATIONS_COUNT - 1);

      j = 0;
      for (i = 0; i < OPERATIONS_COUNT; i++) begin
	 if (operations[4*i][1:0] == OP_READ) begin
	    was_read[j] <= 1;
	    expected_data[j] <= operations[4*i + 2][WORD_BITS - 1 : 0];
	    SEL_mask[j] <= operations[4*i + 3][SEL_LINES - 1 : 0];

	    j++;
	 end

	 if (operations[4*i][1:0] == OP_WRITE) begin
	    was_read[j] <= 0;

	    j++;
	 end
      end // for (i = 0; i < OPERATIONS_COUNT; i++)
   end // initial begin

   reg [31:0]                         operations_performed;
   reg [31:0] 			      commands_sent;
   reg [31:0] 			      commands_acknowledged;

   /* Those are irrelevant if RST_I is high */
   wire 			      command_successful;
   wire 			      wait_successful;
   wire 			      deselect_successful;
   wire [1:0] 			      current_op_type;
   wire [ADR_BITS - 1 : 0] 	      current_op_adr;
   wire [WORD_BITS - 1 : 0] 	      current_op_data;
   wire [SEL_LINES - 1 : 0] 	      current_op_mask;
   wire 			      operation_successful;
   wire [31:0] 			      operations_performed_next_tick;
   wire 			      acknowledgement_successful;
   wire [31:0] 			      commands_sent_next_tick;
   wire [31:0] 			      commands_acknowledged_next_tick;
   wire [31:0] 			      acknowledgements_needed;

   assign command_successful = CYC_O && STB_O && !STALL_I;
   assign wait_successful = !STB_O && !STALL_I;
   assign deselect_successful = !CYC_O;

   assign current_op_type
     = operations[4*operations_performed][1:0];
   assign current_op_adr
     = operations[4*operations_performed + 1][ADR_BITS - 1 : 0];
   assign current_op_data
     = operations[4*operations_performed + 2][WORD_BITS - 1 : 0];
   assign current_op_mask
     = operations[4*operations_performed + 3][SEL_LINES - 1 : 0];

   assign operation_successful
     = operations_performed < OPERATIONS_COUNT &&
       ((current_op_type == OP_READ && command_successful) ||
	(current_op_type == OP_WRITE && command_successful) ||
	(current_op_type == OP_WAIT && wait_successful) ||
	(current_op_type == OP_DESELECT && deselect_successful));
   assign operations_performed_next_tick
     = operations_performed + operation_successful;

   assign commands_sent_next_tick = commands_sent + command_successful;
   assign acknowledgement_successful
     = CYC_O && ACK_I && commands_sent_next_tick > commands_acknowledged;
   assign commands_acknowledged_next_tick
     = commands_acknowledged + acknowledgement_successful;
   assign acknowledgements_needed
     = commands_sent_next_tick - commands_acknowledged_next_tick;

   wire [31:0] 			      idx_to_use;
   wire [1:0] 			      next_op_type;
   wire [ADR_BITS - 1 : 0] 	      next_op_adr;
   wire [WORD_BITS - 1 : 0] 	      next_op_data;
   wire [SEL_LINES - 1 : 0] 	      next_op_mask;

   assign idx_to_use = operations_performed_next_tick;
   assign next_op_type = operations[4*idx_to_use][1:0];
   assign next_op_adr = operations[4*idx_to_use + 1][ADR_BITS - 1 : 0];
   assign next_op_data = operations[4*idx_to_use + 2][WORD_BITS - 1 : 0];
   assign next_op_mask = operations[4*idx_to_use + 3][SEL_LINES - 1 : 0];

   /* Drive the outputs */

   reg 				      strobe;
   assign STB_O = strobe;

   reg 				      cycle;
   assign CYC_O = cycle;

   reg 				      write_enable;
   assign WE_O = write_enable;

   reg [WORD_BITS - 1 : 0] 	      output_data;
   assign DAT_O = output_data;

   reg [SEL_LINES - 1 : 0] 	      mask;
   assign SEL_O = mask;

   reg [ADR_BITS - 1 : 0] 	      addr;
   assign ADR_O = addr;

   reg 				      done;
   assign finished = done;

   reg 				      error;

   initial begin
      strobe <= 0;
      cycle <= 0;
      operations_performed <= 0;
      commands_sent <= 0;
      commands_acknowledged <= 0;
      done <= 0;
   end

   always @ (posedge CLK_I) begin
      if (RST_I) begin
	 strobe <= 0;
	 cycle <= 0;
	 operations_performed <= 0;
	 commands_sent <= 0;
	 commands_acknowledged <= 0;
	 done <= 0;
      end else begin
	 /* debug messages */
	 if (command_successful) begin
	    case (current_op_type)
	      OP_READ : begin
		 `DBG(("Master %0d: sent read command at h%x", MASTER_NR,
		       current_op_adr));
	      end
	      OP_WRITE : begin
		 `DBG(("Master %0d: sent write command of h%x at h%x",
		       MASTER_NR, current_op_data, current_op_adr));
	      end
	      default :
		`MSG(("Master %0d: error: bug in test bench", MASTER_NR));
	    endcase // case (current_op_type)
	 end // if (command_successful)

	 if (acknowledgement_successful) begin
	    if (was_read[commands_acknowledged])
	      `DBG(("Master %0d: acknowledged read of h%x", MASTER_NR, DAT_I));
	    else
	      `DBG(("Master %0d: acknowledged write", MASTER_NR));
	 end

	 operations_performed <= operations_performed_next_tick;
	 commands_sent <= commands_sent_next_tick;
	 commands_acknowledged <= commands_acknowledged_next_tick;

	 if (operations_performed_next_tick == OPERATIONS_COUNT) begin
	    strobe <= 0;

	    if (acknowledgements_needed == 0) begin
	       cycle <= 0;
	       done <= 1;
	    end
	 end else begin
	    operations_performed <= operations_performed_next_tick;

	    case (next_op_type)
	      OP_READ : begin
		 cycle <= 1;
		 strobe <= 1;
		 write_enable <= 0;
		 mask <= next_op_mask;
		 addr <= next_op_adr;
	      end
	      OP_WRITE : begin
		 cycle <= 1;
		 strobe <= 1;
		 write_enable <= 1;
		 mask <= next_op_mask;
		 addr <= next_op_adr;
		 output_data <= next_op_data;
	      end
	      OP_WAIT : begin
		 cycle <= 1;
		 strobe <= 0;
	      end
	      OP_DESELECT : begin
		 cycle <= acknowledgements_needed > 0;
		 strobe <= 0;
	      end
	    endcase // case (next_op_type)
	 end // else: !if(operations_performed_next_tick == OPERATIONS_COUNT)

	 if (acknowledgement_successful &&
	     was_read[commands_acknowledged]) begin
	    error = 0;

	    for (i = 0; i < WORD_BITS; i++) begin
	       if (SEL_mask[commands_acknowledged][i/GRANULARITY]) begin
		  if (DAT_I[i] != expected_data[commands_acknowledged][i])
		    error = 1;
	       end
	    end

	    if (error) begin
	       `MSG(("Master %0d: error: read h%x instead of h%x (%b)",
		     MASTER_NR, DAT_I,
		     expected_data[commands_acknowledged],
		     SEL_mask[commands_acknowledged]));
	    end
	 end // if (acknowledgement_successful &&...
      end // else: !if(RST_I)
   end // always @ (posedge CLK_I)
endmodule // master_model