From ee1f6c47e1eff920068f4bceaf604f9535a2e8a9 Mon Sep 17 00:00:00 2001 From: Wojciech Kosior Date: Tue, 1 Sep 2020 10:54:59 +0200 Subject: start anew --- models/master.v | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++ models/slave.v | 146 +++++++++++++++++++++++++++++++++ models/vga_display.v | 203 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 572 insertions(+) create mode 100644 models/master.v create mode 100644 models/slave.v create mode 100644 models/vga_display.v (limited to 'models') diff --git a/models/master.v b/models/master.v new file mode 100644 index 0000000..b728bda --- /dev/null +++ b/models/master.v @@ -0,0 +1,223 @@ +/* A wishbone slave testing module (a "mock") */ +`default_nettype none + +`include "messages.vh" + +`ifndef SIMULATION + `error_SIMULATION_not_defined +; /* Cause syntax error */ +`else + +module master_model + #( + parameter MASTER_NR = -1, + parameter OPERATIONS_FILE = "master_operations.mem", + parameter OPERATIONS_COUNT = 10 + ) + ( + input wire ACK_I, + input wire CLK_I, + output wire [19:0] ADR_O, + input wire [15:0] DAT_I, + output wire [15:0] DAT_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 + 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 */ + + /* + * Bits 39:36 specify type of the operation; bits 35:16 contain the + * address for read wnd write operations (may contain garbage otherwise); + * bits 15:0 contain data to write (for write operation) or expected data + * (for read operation) or are irrelevant (for other operations) + */ + reg [39:0] operations [OPERATIONS_COUNT - 1 : 0]; + /* + * This will allow us to check whether read operation returned proper data; + * bit 16 tells us whether the corresponding command was a read; if not, + * bits 15:0 don't matter; if yes bits 15:0 contain the data we should + * receive from that read operation + */ + reg [16:0] expected_results [OPERATIONS_COUNT - 1 : 0]; + + integer i, j; + + initial begin + $readmemh(OPERATIONS_FILE, operations, 0, OPERATIONS_COUNT - 1); + + j = 0; + for (i = 0; i < OPERATIONS_COUNT; i++) begin + if (operations[i][39:36] == OP_READ || + operations[i][39:36] == OP_WRITE) begin + if (operations[i][39:36] == OP_READ) + expected_results[j] <= {1'b1, operations[i][15:0]}; + else + expected_results[j] <= {1'b0, 16'bx}; + + j++; + end + end + end + + 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 [2:0] current_operation_type; + wire operation_successful; + wire [31:0] operations_performed_next_tick; + wire [39:0] operation_to_perform; + 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_operation_type = operations[operations_performed][39:36]; + assign operation_successful + = operations_performed < OPERATIONS_COUNT && + ((current_operation_type == OP_READ && command_successful) || + (current_operation_type == OP_WRITE && command_successful) || + (current_operation_type == OP_WAIT && wait_successful) || + (current_operation_type == OP_DESELECT && deselect_successful)); + assign operations_performed_next_tick + = operations_performed + operation_successful; + assign operation_to_perform = operations[operations_performed_next_tick]; + + 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; + + reg strobe; + assign STB_O = strobe; + + reg cycle; + assign CYC_O = cycle; + + reg write_enable; + assign WE_O = write_enable; + + reg [15:0] output_data; + assign DAT_O = output_data; + + reg [19:0] addr; + assign ADR_O = addr; + + reg done; + assign finished = done; + + 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_operation_type) + OP_READ : begin + `DBG(("Master %0d: sent read command at h%x", MASTER_NR, + operations[operations_performed][35:16])); + end + OP_WRITE : begin + `DBG(("Master %0d: sent write command of h%x at h%x", + MASTER_NR, operations[operations_performed][15:0], + operations[operations_performed][35:16])); + end + default : + `MSG(("Master %0d: error: bug in test bench", MASTER_NR)); + endcase // case (current_operation_type) + end // if (command_successful) + + if (acknowledgement_successful) begin + if (expected_results[commands_acknowledged][16]) + `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 (operation_to_perform[39:36]) + OP_READ : begin + cycle <= 1; + strobe <= 1; + write_enable <= 0; + addr <= operation_to_perform[35:16]; + end + OP_WRITE : begin + cycle <= 1; + strobe <= 1; + write_enable <= 1; + addr <= operation_to_perform[35:16]; + output_data <= operation_to_perform[15:0]; + end + OP_WAIT : begin + cycle <= 1; + strobe <= 0; + end + OP_DESELECT : begin + cycle <= acknowledgements_needed > 0; + strobe <= 0; + end + endcase // case (operation_to_perform[39:36]) + end // else: !if(operations_performed_next_tick == OPERATIONS_COUNT) + + if (acknowledgement_successful) begin + if (expected_results[commands_acknowledged][15:0] !== DAT_I && + expected_results[commands_acknowledged][16]) begin + `MSG(("Master %0d: error: read h%x instead of h%x", + MASTER_NR, DAT_I, + expected_results[commands_acknowledged][15:0])); + end + end + end // else: !if(RST_I) + end // always @ (posedge CLK_I) +endmodule // master_model + +`endif // !`ifndef SIMULATION diff --git a/models/slave.v b/models/slave.v new file mode 100644 index 0000000..bae3485 --- /dev/null +++ b/models/slave.v @@ -0,0 +1,146 @@ +/* A wishbone slave testing module (a "mock") */ +`default_nettype none + +`include "messages.vh" + +`ifndef SIMULATION + `error_SIMULATION_not_defined +; /* Cause syntax error */ +`else + +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 + +`endif // !`ifndef SIMULATION diff --git a/models/vga_display.v b/models/vga_display.v new file mode 100644 index 0000000..bb683e2 --- /dev/null +++ b/models/vga_display.v @@ -0,0 +1,203 @@ +`default_nettype none +`timescale 1ns/1ns + +`include "messages.vh" + +`ifndef SIMULATION + `error_SIMULATION_not_defined +; /* Cause syntax error */ +`endif + +module VGA_640_480_60Hz( + input wire horizontal_sync, + input wire vertical_sync, + + input wire [2:0] red, + input wire [2:0] green, + input wire [2:0] blue, + + output reg [9:0] image_writes + ); + initial + image_writes <= 0; + + parameter FRONT_PORCH = 0; + parameter SYNC_PULSE = 1; + parameter BACK_PORCH = 2; + parameter ACTIVE_VIDEO = 3; + parameter UNKNOWN_STATE = 4; + + integer h_state; + integer v_state; + initial begin + h_state <= UNKNOWN_STATE; + v_state <= UNKNOWN_STATE; + end + + parameter H_SYNC_POLARITY = 1'b0; + parameter H_FRONT_PORCH_LENGTH = 16; + parameter H_SYNC_PULSE_LENGTH = 96; + parameter H_BACK_PORCH_LENGTH = 48; + parameter H_ACTIVE_VIDEO_LENGTH = 640; + + parameter V_SYNC_POLARITY = 1'b1; + parameter V_FRONT_PORCH_LENGTH = 10; + parameter V_SYNC_PULSE_LENGTH = 2; + parameter V_BACK_PORCH_LENGTH = 33; + parameter V_ACTIVE_VIDEO_LENGTH = 480; + + reg [8:0] picture + [V_ACTIVE_VIDEO_LENGTH - 1 : 0] + [H_ACTIVE_VIDEO_LENGTH - 1 : 0]; + + integer h_counter, v_counter; + + reg error; + + wire unexpected_h_sync_pulse; + wire missing_h_sync_pulse; + assign unexpected_h_sync_pulse = horizontal_sync == H_SYNC_POLARITY && + h_state != SYNC_PULSE; + assign missing_h_sync_pulse = horizontal_sync != H_SYNC_POLARITY && + h_state == SYNC_PULSE; + + wire unexpected_v_sync_pulse; + wire missing_v_sync_pulse; + assign unexpected_v_sync_pulse = vertical_sync == V_SYNC_POLARITY && + v_state != SYNC_PULSE; + assign missing_v_sync_pulse = vertical_sync != V_SYNC_POLARITY && + v_state == SYNC_PULSE; + + wire new_line; + assign new_line = h_state == BACK_PORCH && + h_counter + 1 == H_BACK_PORCH_LENGTH; + + wire new_screen; + assign new_screen = v_state == BACK_PORCH && + v_counter + 1 == V_BACK_PORCH_LENGTH && new_line; + + always #40 begin + /* First, horizontal stuff */ + if (unexpected_h_sync_pulse) begin + h_state <= SYNC_PULSE; + h_counter <= 1; + + if (h_state != UNKNOWN_STATE) begin + `MSG(({"VGA: error: unexpected horizontal sync pulse signal ", + "(tick %0d; state %0d)"}, h_counter, h_state)); + end + end else if (missing_h_sync_pulse) begin + h_state <= BACK_PORCH; + h_counter <= 1; + + `MSG(({"VGA: error: missing expected horizontal sync pulse signal ", + "(tick %0d of sync pulse)"}, h_counter)); + end else begin + case (h_state) + FRONT_PORCH : begin + if (h_counter + 1 != H_FRONT_PORCH_LENGTH) begin + h_counter <= h_counter + 1; + end else begin + h_state <= SYNC_PULSE; + h_counter <= 0; + end + end + SYNC_PULSE : begin + if (h_counter + 1 != H_SYNC_PULSE_LENGTH) begin + h_counter <= h_counter + 1; + end else begin + h_state <= BACK_PORCH; + h_counter <= 0; + end + end + BACK_PORCH : begin + if (h_counter + 1 != H_BACK_PORCH_LENGTH) begin + h_counter <= h_counter + 1; + end else begin + h_state <= ACTIVE_VIDEO; + h_counter <= 0; + end + end + ACTIVE_VIDEO : begin + if (v_state == ACTIVE_VIDEO) begin + picture[v_counter][h_counter] <= {red, green, blue}; + end + + if (h_counter + 1 != H_ACTIVE_VIDEO_LENGTH) begin + h_counter <= h_counter + 1; + end else begin + h_state <= FRONT_PORCH; + h_counter <= 0; + end + end // case: ACTIVE_VIDEO + endcase // case (h_state) + end // else: !if(missing_h_sync_pulse) + + + /* Vertical stuff is almost entirely the same as horizontal */ + if (new_line) begin + if (unexpected_v_sync_pulse) begin + v_state <= SYNC_PULSE; + v_counter <= 1; + + if (v_state != UNKNOWN_STATE) begin + `MSG(({"VGA: error: unexpected vertical sync pulse signal ", + "(line %0d; state %0d)"}, v_counter, v_state)); + end + end else if (missing_v_sync_pulse) begin + v_state <= BACK_PORCH; + v_counter <= 1; + + `MSG(({"VGA: error: missing expected vertical sync pulse signal ", + "(line %0d of sync pulse)"}, v_counter)); + end else begin + case (v_state) + FRONT_PORCH : begin + if (v_counter + 1 != V_FRONT_PORCH_LENGTH) begin + v_counter <= v_counter + 1; + end else begin + v_state <= SYNC_PULSE; + v_counter <= 0; + end + end + SYNC_PULSE : begin + if (v_counter + 1 != V_SYNC_PULSE_LENGTH) begin + v_counter <= v_counter + 1; + end else begin + v_state <= BACK_PORCH; + v_counter <= 0; + end + end + BACK_PORCH : begin + if (v_counter + 1 != V_BACK_PORCH_LENGTH) begin + v_counter <= v_counter + 1; + end else begin + v_state <= ACTIVE_VIDEO; + v_counter <= 0; + end + end + ACTIVE_VIDEO : begin + if (v_counter + 1 != V_ACTIVE_VIDEO_LENGTH) begin + v_counter <= v_counter + 1; + end else begin + v_state <= FRONT_PORCH; + v_counter <= 0; + + if (!error) begin + $writememb("VGAdump.mem", picture); + `DBG(("VGA: written VGAdump.mem")); + image_writes <= image_writes + 1; + end + end // else: !if(v_counter + 1 != V_ACTIVE_VIDEO_LENGTH) + end // case: ACTIVE_VIDEO + endcase // case (v_state) + end // else: !if(missing_v_sync_pulse) + end // if (new_line) + + if (unexpected_h_sync_pulse || unexpected_h_sync_pulse || + ((unexpected_v_sync_pulse || missing_v_sync_pulse) && new_line)) + error <= 1; + else if (new_screen) + error <= 0; + end // always #40 +endmodule // VGA_640_480_60Hz -- cgit v1.2.3