/* * A Wishbone SLAVE testing module (a "mock") * * | *WISHBONE DATASHEET* | * |---------------------------------------------------------------------------| * | *Description* | *Specification* | * |---------------------------------+-----------------------------------------| * | General description | mock memory model for test benches | * |---------------------------------+-----------------------------------------| * | | SLAVE, pipelined READ/WRITE | * | Supported cycles | (WRITE ability can be disabled | * | | through module parameter) | * |---------------------------------+-----------------------------------------| * | 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_O | ACK_O | * | | ADR_I | ADR_I() | * | Supported signal list and cross | CLK_I | CLK_I | * | reference to equivalent | DAT_I | DAT_I() | * | WISHBONE signals | DAT_O | DAT_O() | * | | STB_I | STB_I | * | | WE_I | WE_I | * | | RST_I | RST_I | * | | STALL_O | STALL_O | * |---------------------------------+-----------------------------------------| * | 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 memory_slave_model #( parameter SLAVE_NR = -1, parameter WORD_SIZE = 2, /* in bytes */ parameter SEL_LINES = 1, parameter ADR_BITS = 18, /* Changing the following 3 allows us to make it function as ROM */ parameter WRITABLE = 1, parameter WORDS_TO_INITIALIZE = 0, parameter INITIAL_CONTENTS_FILE = "some_file.mem" ) ( output wire ACK_O, /* output wire ERR_O, */ /* we might use it one day */ input wire CLK_I, input wire [ADR_BITS - 1 : 0] ADR_I, input wire [8*WORD_SIZE - 1 : 0] DAT_I, output wire [8*WORD_SIZE - 1 : 0] DAT_O, input wire [SEL_LINES - 1 : 0] SEL_I, input wire RST_I, input wire STB_I, input wire WE_I, output wire STALL_O ); parameter GRANULARITY = 8 * WORD_SIZE / SEL_LINES; /* in bits */ /* * A simple memory slave should be most useful for testing purposes; * WARNING! The 'memory' variable might be referenced from outside the module * by testbench code - be careful when changing or removing it */ reg [GRANULARITY - 1 : 0] memory [2**ADR_BITS - 1 : 0]; /* For random stall times and acknowledge times */ integer seed; parameter MAX_STALL_TIME = 7; parameter MAX_ACK_WAIT_TIME = 7; reg [2:0] stall_time_left; reg [2:0] acknowledge_time_left; /* incoming command pipeline */ reg WE_I_pipeline [15:0]; reg [ADR_BITS - 1 : 0] ADR_I_pipeline [15:0]; reg [8*WORD_SIZE - 1 : 0] DAT_I_pipeline [15:0]; reg [SEL_LINES - 1 : 0] SEL_I_pipeline [15:0]; reg [3:0] pipeline_oldest_element; reg [4:0] pipeline_elements_count; wire [3:0] pipeline_index_to_insert; assign pipeline_index_to_insert = (pipeline_oldest_element + pipeline_elements_count) % 16; reg stall; reg WE_I_to_process; reg [ADR_BITS - 1 : 0] ADR_I_to_process; reg [8*WORD_SIZE - 1 : 0] DAT_I_to_process; reg [SEL_LINES - 1 : 0] SEL_I_to_process; always @* begin if (pipeline_elements_count) begin WE_I_to_process = WE_I_pipeline[pipeline_oldest_element]; ADR_I_to_process = ADR_I_pipeline[pipeline_oldest_element]; DAT_I_to_process = DAT_I_pipeline[pipeline_oldest_element]; SEL_I_to_process = SEL_I_pipeline[pipeline_oldest_element]; end else begin WE_I_to_process = WE_I; ADR_I_to_process = ADR_I; DAT_I_to_process = DAT_I; SEL_I_to_process = SEL_I; end end // always @ * /* Those are irrelevant if RST_I is high */ wire pipeline_space_left; wire can_accept; wire new_command_accepted; wire command_available_for_acknowledging; wire command_acknowledged; assign pipeline_space_left = pipeline_elements_count < 16; assign can_accept = pipeline_space_left && stall_time_left == 0; assign new_command_accepted = STB_I && can_accept; assign command_available_for_acknowledging = pipeline_elements_count || new_command_accepted; assign command_acknowledged = acknowledge_time_left == 0 && command_available_for_acknowledging; integer i; /* loop counter */ reg [8*WORD_SIZE - 1 : 0] masked_out_data; always @ (SEL_I_to_process or WE_I_to_process or ADR_I_to_process) begin for (i = SEL_LINES - 1; i >= 0; i--) begin if (SEL_LINES > 1) begin masked_out_data[8*WORD_SIZE - 1 : GRANULARITY] = masked_out_data[8*WORD_SIZE - GRANULARITY - 1 : 0]; end masked_out_data[GRANULARITY - 1 : 0] = SEL_I_to_process[i] ? memory[ADR_I_to_process + i] : {GRANULARITY{1'bx}}; end end // always @ (SEL_I_to_process or WE_I_to_process or ADR_I_to_process) /* Finally, drive the outputs */ /* only drive data outputs for read commands */ assign DAT_O = (command_acknowledged && !WE_I_to_process) ? masked_out_data : {(8 * WORD_SIZE - 1){1'bx}}; assign STALL_O = !can_accept; assign ACK_O = command_acknowledged; initial begin pipeline_oldest_element <= 0; pipeline_elements_count <= 0; seed <= SLAVE_NR; stall_time_left <= 0; acknowledge_time_left <= 0; if (WORDS_TO_INITIALIZE) $readmemb(INITIAL_CONTENTS_FILE, memory, 0, WORDS_TO_INITIALIZE - 1); end integer k; /* loop counter */ reg [8*WORD_SIZE - 1 : 0] masked_in_data; always @ (posedge CLK_I) begin if (RST_I) begin pipeline_oldest_element <= 0; pipeline_elements_count <= 0; end else begin if (new_command_accepted) begin stall_time_left <= $urandom(seed) % (MAX_STALL_TIME + 1); WE_I_pipeline[pipeline_index_to_insert] <= WE_I; ADR_I_pipeline[pipeline_index_to_insert] <= ADR_I; DAT_I_pipeline[pipeline_index_to_insert] <= DAT_I; SEL_I_pipeline[pipeline_index_to_insert] <= SEL_I; end else begin if (stall_time_left) stall_time_left <= stall_time_left - 1; end if (command_acknowledged) begin acknowledge_time_left <= $urandom(seed) % (MAX_ACK_WAIT_TIME + 1); pipeline_oldest_element <= pipeline_oldest_element + 1; if (WE_I_to_process) begin /* Write command */ masked_in_data = DAT_I_to_process; for (k = 0; k < SEL_LINES; k++) begin if (SEL_I_to_process[k]) begin if (WRITABLE) begin memory[ADR_I_to_process + k] <= masked_in_data[GRANULARITY - 1 : 0]; end end else begin masked_in_data[GRANULARITY - 1 : 0] = {GRANULARITY{1'bx}}; end if (SEL_LINES > 1) begin masked_in_data = {masked_in_data[GRANULARITY - 1 : 0], masked_in_data[8*WORD_SIZE - 1 : GRANULARITY]}; end end // for (k = 0; k < SEL_LINES; k++) if (WRITABLE) begin `DBG(("Slave %0d: write of h%x (%b) at h%x", SLAVE_NR, masked_in_data, SEL_I_to_process, ADR_I_to_process)); end else begin `DBG(({"Slave %0d: error: write of h%x (%b) at h%x ", "(read-only) memory"}, SLAVE_NR, DAT_I_to_process, SEL_I_to_process, ADR_I_to_process)); end end else begin /* Read command */ `DBG(("Slave %0d: read of h%x (%b) at h%x", SLAVE_NR, DAT_O, SEL_I_to_process, ADR_I_to_process)); end end else if (command_available_for_acknowledging) begin acknowledge_time_left <= acknowledge_time_left - 1; end pipeline_elements_count <= pipeline_elements_count + new_command_accepted - command_acknowledged; end end endmodule // memory_slave_model