/*
* 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