/* A wishbone slave testing module (a "mock") */ `default_nettype none `include "messages.vh" `ifndef SIMULATION `error_SIMULATION_not_defined ; /* Cause syntax error */ `endif module memory_slave_model #( parameter SLAVE_NR = -1, /* 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, input wire CLK_I, input wire [17:0] ADR_I, input wire [15:0] DAT_I, output wire [15:0] DAT_O, input wire RST_I, input wire STB_I, input wire WE_I, output wire STALL_O ); /* * 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 [15:0] memory [2**18 - 1 : 0]; integer seed; /* For random stall times and acknowledge times */ parameter MAX_STALL_TIME = 7; parameter MAX_ACK_WAIT_TIME = 7; reg [2:0] stall_time_left; reg [2:0] acknowledge_time_left; /* * Bit 34 contains latched WE_I; bits 33:16 contain latched ADR_I; * bits 15:0 contain latched DAT_I */ reg [34:0] command_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; /* 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; wire [34:0] incoming_command; wire [34:0] command_to_process; 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; assign incoming_command = STB_I ? {WE_I, ADR_I, DAT_I} : 35'bx; assign command_to_process = pipeline_elements_count ? command_pipeline[pipeline_oldest_element] : incoming_command; /* Finally, drive the outputs */ /* * command_to_process[34] is low when it's a read command; only drive data * outputs for read commands */ assign DAT_O = (command_acknowledged && !command_to_process[34]) ? memory[command_to_process[33:16]] : 16'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 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); command_pipeline[pipeline_index_to_insert] = incoming_command; 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 (command_to_process[34]) begin /* Write command */ if (WRITABLE) begin memory[command_to_process[33:16]] <= command_to_process[15:0]; `DBG(("Slave %0d: write of h%x at h%x", SLAVE_NR, command_to_process[15:0], command_to_process[31:16])); end else begin `DBG(({"Slave %0d: error: write of h%x at h%x ", "(read-only) memory"}, SLAVE_NR, command_to_process[15:0], command_to_process[31:16])); end end else begin /* Read command */ `DBG(("Slave %0d: read of h%x at h%x", SLAVE_NR, DAT_O, command_to_process[31:16])); 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