From 46f653f097d07b73e05492b2dc7042e06d0d47be Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 16 Jul 2019 00:15:50 -0700 Subject: [PATCH] Add queue manager module and testbench --- fpga/common/rtl/queue_manager.v | 669 +++++++++++++++++++++++++++ fpga/common/tb/test_queue_manager.py | 489 ++++++++++++++++++++ fpga/common/tb/test_queue_manager.v | 217 +++++++++ 3 files changed, 1375 insertions(+) create mode 100644 fpga/common/rtl/queue_manager.v create mode 100755 fpga/common/tb/test_queue_manager.py create mode 100644 fpga/common/tb/test_queue_manager.v diff --git a/fpga/common/rtl/queue_manager.v b/fpga/common/rtl/queue_manager.v new file mode 100644 index 000000000..6b9a6f0e2 --- /dev/null +++ b/fpga/common/rtl/queue_manager.v @@ -0,0 +1,669 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Queue manager + */ +module queue_manager # +( + parameter ADDR_WIDTH = 64, + parameter REQ_TAG_WIDTH = 8, + parameter OP_TABLE_SIZE = 16, + parameter OP_TAG_WIDTH = 8, + parameter QUEUE_INDEX_WIDTH = 8, + parameter CPL_INDEX_WIDTH = 8, + parameter QUEUE_PTR_WIDTH = 16, + parameter QUEUE_LOG_SIZE_WIDTH = $clog2(QUEUE_PTR_WIDTH), + parameter DESC_SIZE = 16, + parameter READ_PIPELINE = 2, + parameter WRITE_PIPELINE = 1, + parameter AXIL_DATA_WIDTH = 32, + parameter AXIL_ADDR_WIDTH = 16, + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * Dequeue request input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_dequeue_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_dequeue_req_tag, + input wire s_axis_dequeue_req_valid, + output wire s_axis_dequeue_req_ready, + + /* + * Dequeue response output + */ + output wire [QUEUE_PTR_WIDTH-1:0] m_axis_dequeue_resp_ptr, + output wire [ADDR_WIDTH-1:0] m_axis_dequeue_resp_addr, + output wire [CPL_INDEX_WIDTH-1:0] m_axis_dequeue_resp_cpl, + output wire [REQ_TAG_WIDTH-1:0] m_axis_dequeue_resp_tag, + output wire [OP_TAG_WIDTH-1:0] m_axis_dequeue_resp_op_tag, + output wire m_axis_dequeue_resp_empty, + output wire m_axis_dequeue_resp_error, + output wire m_axis_dequeue_resp_valid, + input wire m_axis_dequeue_resp_ready, + + /* + * Dequeue commit input + */ + input wire [OP_TAG_WIDTH-1:0] s_axis_dequeue_commit_op_tag, + input wire s_axis_dequeue_commit_valid, + output wire s_axis_dequeue_commit_ready, + + /* + * Doorbell output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_doorbell_queue, + output wire m_axis_doorbell_valid, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * Configuration + */ + input wire enable +); + +parameter QUEUE_COUNT = 2**QUEUE_INDEX_WIDTH; + +parameter CL_OP_TABLE_SIZE = $clog2(OP_TABLE_SIZE); + +parameter CL_DESC_SIZE = $clog2(DESC_SIZE); + +parameter PIPELINE = READ_PIPELINE + WRITE_PIPELINE; + +// bus width assertions +initial begin + if (OP_TAG_WIDTH < CL_OP_TABLE_SIZE) begin + $error("Error: OP_TAG_WDITH insufficient for OP_TABLE_SIZE"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI lite interface width must be 32"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: AXI lite interface requires byte (8-bit) granularity"); + $finish; + end + + if (AXIL_ADDR_WIDTH < QUEUE_INDEX_WIDTH+5) begin + $error("Error: AXI lite address width too narrow"); + $finish; + end + + if (2**$clog2(DESC_SIZE) != DESC_SIZE) begin + $error("Error: Descriptor size must be even power of two"); + $finish; + end + + if (READ_PIPELINE < 1) begin + $error("Error: READ_PIPELINE must be at least 1"); + $finish; + end + + if (WRITE_PIPELINE < 1) begin + $error("Error: WRITE_PIPELINE must be at least 1"); + $finish; + end +end + +reg op_axil_write_pipe_hazard; +reg op_axil_read_pipe_hazard; +reg op_req_pipe_hazard; +reg op_commit_pipe_hazard; +reg stage_active; + +reg [PIPELINE-1:0] op_axil_write_pipe_reg = {PIPELINE{1'b0}}, op_axil_write_pipe_next; +reg [PIPELINE-1:0] op_axil_read_pipe_reg = {PIPELINE{1'b0}}, op_axil_read_pipe_next; +reg [PIPELINE-1:0] op_req_pipe_reg = {PIPELINE{1'b0}}, op_req_pipe_next; +reg [PIPELINE-1:0] op_commit_pipe_reg = {PIPELINE{1'b0}}, op_commit_pipe_next; + +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_addr_pipeline_reg[PIPELINE-1:0], queue_ram_addr_pipeline_next[PIPELINE-1:0]; +reg [127:0] queue_ram_read_data_pipeline_reg[READ_PIPELINE-1:0]; +reg [2:0] axil_reg_pipeline_reg[READ_PIPELINE-1:0], axil_reg_pipeline_next[READ_PIPELINE-1:0]; +reg [AXIL_DATA_WIDTH-1:0] write_data_pipeline_reg[READ_PIPELINE-1:0], write_data_pipeline_next[READ_PIPELINE-1:0]; +reg [AXIL_STRB_WIDTH-1:0] write_strobe_pipeline_reg[READ_PIPELINE-1:0], write_strobe_pipeline_next[READ_PIPELINE-1:0]; +reg [REQ_TAG_WIDTH-1:0] req_tag_pipeline_reg[READ_PIPELINE-1:0], req_tag_pipeline_next[READ_PIPELINE-1:0]; + +reg s_axis_dequeue_req_ready_reg = 1'b0, s_axis_dequeue_req_ready_next; + +reg [QUEUE_PTR_WIDTH-1:0] m_axis_dequeue_resp_ptr_reg = 0, m_axis_dequeue_resp_ptr_next; +reg [ADDR_WIDTH-1:0] m_axis_dequeue_resp_addr_reg = 0, m_axis_dequeue_resp_addr_next; +reg [CPL_INDEX_WIDTH-1:0] m_axis_dequeue_resp_cpl_reg = 0, m_axis_dequeue_resp_cpl_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_dequeue_resp_tag_reg = 0, m_axis_dequeue_resp_tag_next; +reg [OP_TAG_WIDTH-1:0] m_axis_dequeue_resp_op_tag_reg = 0, m_axis_dequeue_resp_op_tag_next; +reg m_axis_dequeue_resp_empty_reg = 1'b0, m_axis_dequeue_resp_empty_next; +reg m_axis_dequeue_resp_error_reg = 1'b0, m_axis_dequeue_resp_error_next; +reg m_axis_dequeue_resp_valid_reg = 1'b0, m_axis_dequeue_resp_valid_next; + +reg s_axis_dequeue_commit_ready_reg = 1'b0, s_axis_dequeue_commit_ready_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_doorbell_queue_reg = 0, m_axis_doorbell_queue_next; +reg m_axis_doorbell_valid_reg = 0, m_axis_doorbell_valid_next; + +reg s_axil_awready_reg = 0, s_axil_awready_next; +reg s_axil_wready_reg = 0, s_axil_wready_next; +reg [1:0] s_axil_bresp_reg = 0, s_axil_bresp_next; +reg s_axil_bvalid_reg = 0, s_axil_bvalid_next; +reg s_axil_arready_reg = 0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = 0, s_axil_rdata_next; +reg [1:0] s_axil_rresp_reg = 0, s_axil_rresp_next; +reg s_axil_rvalid_reg = 0, s_axil_rvalid_next; + +reg [127:0] queue_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_read_ptr; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_write_ptr; +reg [127:0] queue_ram_write_data; +reg queue_ram_wr_en; +reg [15:0] queue_ram_be; + +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_data_head_ptr = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][15:0]; +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_data_tail_ptr = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][31:16]; +wire [CPL_INDEX_WIDTH-1:0] queue_ram_read_data_cpl_queue = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][47:32]; +wire [QUEUE_LOG_SIZE_WIDTH-1:0] queue_ram_read_data_log_size = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][51:48]; +wire queue_ram_read_data_active = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][55]; +wire [CL_OP_TABLE_SIZE-1:0] queue_ram_read_data_op_index = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][63:56]; +wire [ADDR_WIDTH-1:0] queue_ram_read_data_base_addr = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][127:64]; + +reg [OP_TABLE_SIZE-1:0] op_table_active = 0; +reg [OP_TABLE_SIZE-1:0] op_table_commit = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_queue[OP_TABLE_SIZE-1:0]; +reg [QUEUE_PTR_WIDTH-1:0] op_table_queue_ptr[OP_TABLE_SIZE-1:0]; +reg [CL_OP_TABLE_SIZE-1:0] op_table_start_ptr_reg = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_start_queue; +reg [QUEUE_PTR_WIDTH-1:0] op_table_start_queue_ptr; +reg op_table_start_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_commit_ptr; +reg op_table_commit_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_finish_ptr_reg = 0; +reg op_table_finish_en; + +assign s_axis_dequeue_req_ready = s_axis_dequeue_req_ready_reg; + +assign m_axis_dequeue_resp_ptr = m_axis_dequeue_resp_ptr_reg; +assign m_axis_dequeue_resp_addr = m_axis_dequeue_resp_addr_reg; +assign m_axis_dequeue_resp_cpl = m_axis_dequeue_resp_cpl_reg; +assign m_axis_dequeue_resp_tag = m_axis_dequeue_resp_tag_reg; +assign m_axis_dequeue_resp_op_tag = m_axis_dequeue_resp_op_tag_reg; +assign m_axis_dequeue_resp_empty = m_axis_dequeue_resp_empty_reg; +assign m_axis_dequeue_resp_error = m_axis_dequeue_resp_error_reg; +assign m_axis_dequeue_resp_valid = m_axis_dequeue_resp_valid_reg; + +assign s_axis_dequeue_commit_ready = s_axis_dequeue_commit_ready_reg; + +assign m_axis_doorbell_queue = m_axis_doorbell_queue_reg; +assign m_axis_doorbell_valid = m_axis_doorbell_valid_reg; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = s_axil_bresp_reg; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = s_axil_rresp_reg; +assign s_axil_rvalid = s_axil_rvalid_reg; + +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_awaddr_queue = s_axil_awaddr >> 5; +wire [2:0] s_axil_awaddr_reg = s_axil_awaddr >> 2; +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_araddr_queue = s_axil_araddr >> 5; +wire [2:0] s_axil_araddr_reg = s_axil_araddr >> 2; + +wire queue_active = op_table_active[queue_ram_read_data_op_index] && op_table_queue[queue_ram_read_data_op_index] == queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; +wire queue_empty_idle = queue_ram_read_data_head_ptr == queue_ram_read_data_tail_ptr; +wire queue_empty_active = queue_ram_read_data_head_ptr == op_table_queue_ptr[queue_ram_read_data_op_index]; +wire queue_empty = queue_active ? queue_empty_active : queue_empty_idle; +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_active_tail_ptr = queue_active ? op_table_queue_ptr[queue_ram_read_data_op_index] : queue_ram_read_data_tail_ptr; + +integer i; + +initial begin + for (i = 0; i < QUEUE_COUNT; i = i + 1) begin + queue_ram[i] = 0; + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] = 0; + end + + for (i = 0; i < READ_PIPELINE; i = i + 1) begin + axil_reg_pipeline_reg[i] = 0; + write_data_pipeline_reg[i] = 0; + write_strobe_pipeline_reg[i] = 0; + req_tag_pipeline_reg[i] = 0; + end +end + +integer j; + +always @* begin + op_axil_write_pipe_next = {op_axil_write_pipe_reg, 1'b0}; + op_axil_read_pipe_next = {op_axil_read_pipe_reg, 1'b0}; + op_req_pipe_next = {op_req_pipe_reg, 1'b0}; + op_commit_pipe_next = {op_commit_pipe_reg, 1'b0}; + + queue_ram_addr_pipeline_next[0] = 0; + axil_reg_pipeline_next[0] = 0; + write_data_pipeline_next[0] = 0; + write_strobe_pipeline_next[0] = 0; + req_tag_pipeline_next[0] = 0; + for (j = 1; j < PIPELINE; j = j + 1) begin + queue_ram_addr_pipeline_next[j] = queue_ram_addr_pipeline_reg[j-1]; + end + for (j = 1; j < READ_PIPELINE; j = j + 1) begin + axil_reg_pipeline_next[j] = axil_reg_pipeline_reg[j-1]; + write_data_pipeline_next[j] = write_data_pipeline_reg[j-1]; + write_strobe_pipeline_next[j] = write_strobe_pipeline_reg[j-1]; + req_tag_pipeline_next[j] = req_tag_pipeline_reg[j-1]; + end + + s_axis_dequeue_req_ready_next = 1'b0; + + m_axis_dequeue_resp_ptr_next = m_axis_dequeue_resp_ptr_reg; + m_axis_dequeue_resp_addr_next = m_axis_dequeue_resp_addr_reg; + m_axis_dequeue_resp_cpl_next = m_axis_dequeue_resp_cpl_reg; + m_axis_dequeue_resp_tag_next = m_axis_dequeue_resp_tag_reg; + m_axis_dequeue_resp_op_tag_next = m_axis_dequeue_resp_op_tag_reg; + m_axis_dequeue_resp_empty_next = m_axis_dequeue_resp_empty_reg; + m_axis_dequeue_resp_error_next = m_axis_dequeue_resp_error_reg; + m_axis_dequeue_resp_valid_next = m_axis_dequeue_resp_valid_reg && !m_axis_dequeue_resp_ready; + + s_axis_dequeue_commit_ready_next = 1'b0; + + m_axis_doorbell_queue_next = m_axis_doorbell_queue_reg; + m_axis_doorbell_valid_next = 1'b0; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bresp_next = s_axil_bresp_reg; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rresp_next = s_axil_rresp_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + + queue_ram_read_ptr = 0; + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + queue_ram_write_data = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_wr_en = 0; + queue_ram_be = 0; + + op_table_start_queue = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + op_table_start_queue_ptr = queue_ram_read_active_tail_ptr + 1; + op_table_start_en = 1'b0; + op_table_commit_ptr = s_axis_dequeue_commit_op_tag; + op_table_commit_en = 1'b0; + op_table_finish_en = 1'b0; + + op_axil_write_pipe_hazard = 1'b0; + op_axil_read_pipe_hazard = 1'b0; + op_req_pipe_hazard = 1'b0; + op_commit_pipe_hazard = 1'b0; + stage_active = 1'b0; + + for (j = 0; j < PIPELINE; j = j + 1) begin + stage_active = op_axil_write_pipe_reg[j] || op_axil_read_pipe_reg[j] || op_req_pipe_reg[j] || op_commit_pipe_reg[j]; + op_axil_write_pipe_hazard = op_axil_write_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_awaddr_queue); + op_axil_read_pipe_hazard = op_axil_read_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_araddr_queue); + op_req_pipe_hazard = op_req_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axis_dequeue_req_queue); + op_commit_pipe_hazard = op_commit_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == op_table_queue[op_table_finish_ptr_reg]); + end + + // pipeline stage 0 - receive request + if (s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && !op_axil_write_pipe_reg[0] && !op_axil_write_pipe_hazard) begin + // AXIL write + op_axil_write_pipe_next[0] = 1'b1; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + + write_data_pipeline_next[0] = s_axil_wdata; + write_strobe_pipeline_next[0] = s_axil_wstrb; + + queue_ram_read_ptr = s_axil_awaddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_awaddr_queue; + axil_reg_pipeline_next[0] = s_axil_awaddr_reg; + end else if (s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && !op_axil_read_pipe_reg[0] && !op_axil_read_pipe_hazard) begin + // AXIL read + op_axil_read_pipe_next[0] = 1'b1; + + s_axil_arready_next = 1'b1; + + queue_ram_read_ptr = s_axil_araddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_araddr_queue; + axil_reg_pipeline_next[0] = s_axil_araddr_reg; + end else if (op_table_active[op_table_finish_ptr_reg] && op_table_commit[op_table_finish_ptr_reg] && !op_commit_pipe_reg[0] && !op_commit_pipe_hazard) begin + // dequeue commit finalize (update pointer) + op_commit_pipe_next[0] = 1'b1; + + op_table_finish_en = 1'b1; + + write_data_pipeline_next[0] = op_table_queue_ptr[op_table_finish_ptr_reg]; + + queue_ram_read_ptr = op_table_queue[op_table_finish_ptr_reg]; + queue_ram_addr_pipeline_next[0] = op_table_queue[op_table_finish_ptr_reg]; + end else if (enable && !op_table_active[op_table_start_ptr_reg] && s_axis_dequeue_req_valid && (!m_axis_dequeue_resp_valid || m_axis_dequeue_resp_ready) && !op_req_pipe_reg[0] && !op_req_pipe_hazard) begin + // dequeue request + op_req_pipe_next[0] = 1'b1; + + s_axis_dequeue_req_ready_next = 1'b1; + + req_tag_pipeline_next[0] = s_axis_dequeue_req_tag; + + queue_ram_read_ptr = s_axis_dequeue_req_queue; + queue_ram_addr_pipeline_next[0] = s_axis_dequeue_req_queue; + end + + // read complete, perform operation + if (op_req_pipe_reg[READ_PIPELINE-1]) begin + // request + m_axis_dequeue_resp_ptr_next = queue_ram_read_active_tail_ptr; + m_axis_dequeue_resp_addr_next = queue_ram_read_data_base_addr + ((queue_ram_read_active_tail_ptr & ({QUEUE_PTR_WIDTH{1'b1}} >> (QUEUE_PTR_WIDTH - queue_ram_read_data_log_size))) * DESC_SIZE); + m_axis_dequeue_resp_cpl_next = queue_ram_read_data_cpl_queue; + m_axis_dequeue_resp_tag_next = req_tag_pipeline_reg[READ_PIPELINE-1]; + m_axis_dequeue_resp_op_tag_next = op_table_start_ptr_reg; + m_axis_dequeue_resp_empty_next = 1'b0; + m_axis_dequeue_resp_error_next = 1'b0; + + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + queue_ram_write_data[63:56] = op_table_start_ptr_reg; + queue_ram_wr_en = 1'b1; + + op_table_start_queue = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + op_table_start_queue_ptr = queue_ram_read_active_tail_ptr + 1; + + if (!queue_ram_read_data_active) begin + // queue inactive + m_axis_dequeue_resp_error_next = 1'b1; + m_axis_dequeue_resp_valid_next = 1'b1; + end else if (queue_empty) begin + // queue empty + m_axis_dequeue_resp_empty_next = 1'b1; + m_axis_dequeue_resp_valid_next = 1'b1; + end else begin + // start dequeue + m_axis_dequeue_resp_valid_next = 1'b1; + + queue_ram_be[7] = 1'b1; + + op_table_start_en = 1'b1; + end + end else if (op_commit_pipe_reg[READ_PIPELINE-1]) begin + // commit + + // update tail pointer + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + queue_ram_write_data[31:16] = write_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_be[3:2] = 2'b11; + queue_ram_wr_en = 1'b1; + end else if (op_axil_write_pipe_reg[READ_PIPELINE-1]) begin + // AXIL write + s_axil_bvalid_next = 1'b1; + s_axil_bresp_next = 2'b00; + + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + queue_ram_wr_en = 1'b1; + + // TODO parametrize + case (axil_reg_pipeline_reg[READ_PIPELINE-1]) + 3'd0: begin + // base address lower 32 + // base address is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[95:64] = write_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_be[11:8] = write_strobe_pipeline_reg[READ_PIPELINE-1]; + end + end + 3'd1: begin + // base address upper 32 + // base address is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[127:96] = write_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_be[15:12] = write_strobe_pipeline_reg[READ_PIPELINE-1]; + end + end + 3'd2: begin + queue_ram_write_data[55:48] = queue_ram_read_data_pipeline_reg[READ_PIPELINE-1][55:48]; + // log size + // log size is read-only when queue is active + if (!queue_ram_read_data_active) begin + if (write_strobe_pipeline_reg[READ_PIPELINE-1][0]) begin + queue_ram_write_data[54:48] = write_data_pipeline_reg[READ_PIPELINE-1][3:0]; + queue_ram_be[6] = 1'b1; + end + end + // active + if (write_strobe_pipeline_reg[READ_PIPELINE-1][3]) begin + queue_ram_write_data[55] = write_data_pipeline_reg[READ_PIPELINE-1][31]; + queue_ram_be[6] = 1'b1; + end + end + 3'd3: begin + // completion queue index + // completion queue index is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[47:32] = write_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_be[5:4] = write_strobe_pipeline_reg[READ_PIPELINE-1]; + end + end + 3'd4: begin + // head pointer + queue_ram_write_data[15:0] = write_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_be[1:0] = write_strobe_pipeline_reg[READ_PIPELINE-1]; + + // generate doorbell on queue head pointer update + m_axis_doorbell_queue_next = queue_ram_addr_pipeline_reg[READ_PIPELINE-1]; + if (queue_ram_read_data_active) begin + m_axis_doorbell_valid_next = 1'b1; + end + end + 3'd6: begin + // tail pointer + // tail pointer is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[31:16] = write_data_pipeline_reg[READ_PIPELINE-1]; + queue_ram_be[3:2] = write_strobe_pipeline_reg[READ_PIPELINE-1]; + end + end + endcase + end else if (op_axil_read_pipe_reg[READ_PIPELINE-1]) begin + // AXIL read + s_axil_rvalid_next = 1'b1; + s_axil_rdata_next = 0; + + // TODO parametrize + case (axil_reg_pipeline_reg[READ_PIPELINE-1]) + 3'd0: begin + // base address lower 32 + s_axil_rdata_next = queue_ram_read_data_base_addr[31:0]; + end + 3'd1: begin + // base address upper 32 + s_axil_rdata_next = queue_ram_read_data_base_addr[63:32]; + end + 3'd2: begin + // log size + s_axil_rdata_next[3:0] = queue_ram_read_data_log_size; + // active + s_axil_rdata_next[31] = queue_ram_read_data_active; + end + 3'd3: begin + // completion queue index + s_axil_rdata_next = queue_ram_read_data_cpl_queue; + end + 3'd4: begin + // head pointer + s_axil_rdata_next = queue_ram_read_data_head_ptr; + end + 3'd6: begin + // tail pointer + s_axil_rdata_next = queue_ram_read_data_tail_ptr; + end + endcase + end + + // dequeue commit (record in table) + s_axis_dequeue_commit_ready_next = enable; + if (s_axis_dequeue_commit_ready && s_axis_dequeue_commit_valid) begin + op_table_commit_ptr = s_axis_dequeue_commit_op_tag; + op_table_commit_en = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + op_axil_write_pipe_reg <= {PIPELINE{1'b0}}; + op_axil_read_pipe_reg <= {PIPELINE{1'b0}}; + op_req_pipe_reg <= {PIPELINE{1'b0}}; + op_commit_pipe_reg <= {PIPELINE{1'b0}}; + + s_axis_dequeue_req_ready_reg <= 1'b0; + m_axis_dequeue_resp_valid_reg <= 1'b0; + s_axis_dequeue_commit_ready_reg <= 1'b0; + m_axis_doorbell_valid_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + + op_table_active <= 0; + + op_table_start_ptr_reg <= 0; + op_table_finish_ptr_reg <= 0; + end else begin + op_axil_write_pipe_reg <= op_axil_write_pipe_next; + op_axil_read_pipe_reg <= op_axil_read_pipe_next; + op_req_pipe_reg <= op_req_pipe_next; + op_commit_pipe_reg <= op_commit_pipe_next; + + s_axis_dequeue_req_ready_reg <= s_axis_dequeue_req_ready_next; + m_axis_dequeue_resp_valid_reg <= m_axis_dequeue_resp_valid_next; + s_axis_dequeue_commit_ready_reg <= s_axis_dequeue_commit_ready_next; + m_axis_doorbell_valid_reg <= m_axis_doorbell_valid_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + if (op_table_start_en) begin + op_table_start_ptr_reg <= op_table_start_ptr_reg + 1; + op_table_active[op_table_start_ptr_reg] <= 1'b1; + end + if (op_table_finish_en) begin + op_table_finish_ptr_reg <= op_table_finish_ptr_reg + 1; + op_table_active[op_table_finish_ptr_reg] <= 1'b0; + end + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] <= queue_ram_addr_pipeline_next[i]; + end + for (i = 0; i < READ_PIPELINE; i = i + 1) begin + axil_reg_pipeline_reg[i] <= axil_reg_pipeline_next[i]; + write_data_pipeline_reg[i] <= write_data_pipeline_next[i]; + write_strobe_pipeline_reg[i] <= write_strobe_pipeline_next[i]; + req_tag_pipeline_reg[i] <= req_tag_pipeline_next[i]; + end + + m_axis_dequeue_resp_ptr_reg <= m_axis_dequeue_resp_ptr_next; + m_axis_dequeue_resp_addr_reg <= m_axis_dequeue_resp_addr_next; + m_axis_dequeue_resp_cpl_reg <= m_axis_dequeue_resp_cpl_next; + m_axis_dequeue_resp_tag_reg <= m_axis_dequeue_resp_tag_next; + m_axis_dequeue_resp_op_tag_reg <= m_axis_dequeue_resp_op_tag_next; + m_axis_dequeue_resp_empty_reg <= m_axis_dequeue_resp_empty_next; + m_axis_dequeue_resp_error_reg <= m_axis_dequeue_resp_error_next; + + m_axis_doorbell_queue_reg <= m_axis_doorbell_queue_next; + + s_axil_bresp_reg <= s_axil_bresp_next; + s_axil_rdata_reg <= s_axil_rdata_next; + s_axil_rresp_reg <= s_axil_rresp_next; + + if (queue_ram_wr_en) begin + for (i = 0; i < 16; i = i + 1) begin + if (queue_ram_be[i]) begin + queue_ram[queue_ram_write_ptr][i*8 +: 8] <= queue_ram_write_data[i*8 +: 8]; + end + end + end + queue_ram_read_data_pipeline_reg[0] <= queue_ram[queue_ram_read_ptr]; + for (i = 1; i < READ_PIPELINE; i = i + 1) begin + queue_ram_read_data_pipeline_reg[i] <= queue_ram_read_data_pipeline_reg[i-1]; + end + + if (op_table_start_en) begin + op_table_commit[op_table_start_ptr_reg] <= 1'b0; + op_table_queue[op_table_start_ptr_reg] <= op_table_start_queue; + op_table_queue_ptr[op_table_start_ptr_reg] <= op_table_start_queue_ptr; + end + if (op_table_commit_en) begin + op_table_commit[op_table_commit_ptr] <= 1'b1; + end +end + +endmodule diff --git a/fpga/common/tb/test_queue_manager.py b/fpga/common/tb/test_queue_manager.py new file mode 100755 index 000000000..b95b7a901 --- /dev/null +++ b/fpga/common/tb/test_queue_manager.py @@ -0,0 +1,489 @@ +#!/usr/bin/env python +""" + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +""" + +from myhdl import * +import os + +import axil +import axis_ep + +import random +import struct + +module = 'queue_manager' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + ADDR_WIDTH = 64 + REQ_TAG_WIDTH = 8 + OP_TABLE_SIZE = 16 + OP_TAG_WIDTH = 8 + QUEUE_INDEX_WIDTH = 8 + CPL_INDEX_WIDTH = 8 + QUEUE_PTR_WIDTH = 16 + QUEUE_LOG_SIZE_WIDTH = 4 + DESC_SIZE = 16 + READ_PIPELINE = 2 + WRITE_PIPELINE = 1 + AXIL_DATA_WIDTH = 32 + AXIL_ADDR_WIDTH = 16 + AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + s_axis_dequeue_req_queue = Signal(intbv(0)[QUEUE_INDEX_WIDTH:]) + s_axis_dequeue_req_tag = Signal(intbv(0)[REQ_TAG_WIDTH:]) + s_axis_dequeue_req_valid = Signal(bool(0)) + m_axis_dequeue_resp_ready = Signal(bool(0)) + s_axis_dequeue_commit_op_tag = Signal(intbv(0)[OP_TAG_WIDTH:]) + s_axis_dequeue_commit_valid = Signal(bool(0)) + s_axil_awaddr = Signal(intbv(0)[AXIL_ADDR_WIDTH:]) + s_axil_awprot = Signal(intbv(0)[3:]) + s_axil_awvalid = Signal(bool(0)) + s_axil_wdata = Signal(intbv(0)[AXIL_DATA_WIDTH:]) + s_axil_wstrb = Signal(intbv(0)[AXIL_STRB_WIDTH:]) + s_axil_wvalid = Signal(bool(0)) + s_axil_bready = Signal(bool(0)) + s_axil_araddr = Signal(intbv(0)[AXIL_ADDR_WIDTH:]) + s_axil_arprot = Signal(intbv(0)[3:]) + s_axil_arvalid = Signal(bool(0)) + s_axil_rready = Signal(bool(0)) + enable = Signal(bool(0)) + + # Outputs + s_axis_dequeue_req_ready = Signal(bool(0)) + m_axis_dequeue_resp_ptr = Signal(intbv(0)[QUEUE_PTR_WIDTH:]) + m_axis_dequeue_resp_addr = Signal(intbv(0)[ADDR_WIDTH:]) + m_axis_dequeue_resp_cpl = Signal(intbv(0)[CPL_INDEX_WIDTH:]) + m_axis_dequeue_resp_tag = Signal(intbv(0)[REQ_TAG_WIDTH:]) + m_axis_dequeue_resp_op_tag = Signal(intbv(0)[OP_TAG_WIDTH:]) + m_axis_dequeue_resp_empty = Signal(bool(0)) + m_axis_dequeue_resp_error = Signal(bool(0)) + m_axis_dequeue_resp_valid = Signal(bool(0)) + s_axis_dequeue_commit_ready = Signal(bool(0)) + m_axis_doorbell_queue = Signal(intbv(0)[QUEUE_INDEX_WIDTH:]) + m_axis_doorbell_valid = Signal(bool(0)) + s_axil_awready = Signal(bool(0)) + s_axil_wready = Signal(bool(0)) + s_axil_bresp = Signal(intbv(0)[2:]) + s_axil_bvalid = Signal(bool(0)) + s_axil_arready = Signal(bool(0)) + s_axil_rdata = Signal(intbv(0)[AXIL_DATA_WIDTH:]) + s_axil_rresp = Signal(intbv(0)[2:]) + s_axil_rvalid = Signal(bool(0)) + + # sources and sinks + dequeue_req_source = axis_ep.AXIStreamSource() + + dequeue_req_source_logic = dequeue_req_source.create_logic( + clk, + rst, + tdata=(s_axis_dequeue_req_queue, s_axis_dequeue_req_tag), + tvalid=s_axis_dequeue_req_valid, + tready=s_axis_dequeue_req_ready, + name='dequeue_req_source' + ) + + dequeue_resp_sink = axis_ep.AXIStreamSink() + + dequeue_resp_sink_logic = dequeue_resp_sink.create_logic( + clk, + rst, + tdata=(m_axis_dequeue_resp_ptr, m_axis_dequeue_resp_addr, m_axis_dequeue_resp_cpl, m_axis_dequeue_resp_tag, m_axis_dequeue_resp_op_tag, m_axis_dequeue_resp_empty, m_axis_dequeue_resp_error), + tvalid=m_axis_dequeue_resp_valid, + tready=m_axis_dequeue_resp_ready, + name='dequeue_resp_sink' + ) + + dequeue_commit_source = axis_ep.AXIStreamSource() + + dequeue_commit_source_logic = dequeue_commit_source.create_logic( + clk, + rst, + tdata=(s_axis_dequeue_commit_op_tag,), + tvalid=s_axis_dequeue_commit_valid, + tready=s_axis_dequeue_commit_ready, + name='dequeue_commit_source' + ) + + doorbell_sink = axis_ep.AXIStreamSink() + + doorbell_sink_logic = doorbell_sink.create_logic( + clk, + rst, + tdata=(m_axis_doorbell_queue,), + tvalid=m_axis_doorbell_valid, + name='doorbell_sink' + ) + + # AXI4-Lite master + axil_master_inst = axil.AXILiteMaster() + axil_master_pause = Signal(bool(False)) + + axil_master_logic = axil_master_inst.create_logic( + clk, + rst, + m_axil_awaddr=s_axil_awaddr, + m_axil_awprot=s_axil_awprot, + m_axil_awvalid=s_axil_awvalid, + m_axil_awready=s_axil_awready, + m_axil_wdata=s_axil_wdata, + m_axil_wstrb=s_axil_wstrb, + m_axil_wvalid=s_axil_wvalid, + m_axil_wready=s_axil_wready, + m_axil_bresp=s_axil_bresp, + m_axil_bvalid=s_axil_bvalid, + m_axil_bready=s_axil_bready, + m_axil_araddr=s_axil_araddr, + m_axil_arprot=s_axil_arprot, + m_axil_arvalid=s_axil_arvalid, + m_axil_arready=s_axil_arready, + m_axil_rdata=s_axil_rdata, + m_axil_rresp=s_axil_rresp, + m_axil_rvalid=s_axil_rvalid, + m_axil_rready=s_axil_rready, + pause=axil_master_pause, + name='master' + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + s_axis_dequeue_req_queue=s_axis_dequeue_req_queue, + s_axis_dequeue_req_tag=s_axis_dequeue_req_tag, + s_axis_dequeue_req_valid=s_axis_dequeue_req_valid, + s_axis_dequeue_req_ready=s_axis_dequeue_req_ready, + m_axis_dequeue_resp_ptr=m_axis_dequeue_resp_ptr, + m_axis_dequeue_resp_addr=m_axis_dequeue_resp_addr, + m_axis_dequeue_resp_cpl=m_axis_dequeue_resp_cpl, + m_axis_dequeue_resp_tag=m_axis_dequeue_resp_tag, + m_axis_dequeue_resp_op_tag=m_axis_dequeue_resp_op_tag, + m_axis_dequeue_resp_empty=m_axis_dequeue_resp_empty, + m_axis_dequeue_resp_error=m_axis_dequeue_resp_error, + m_axis_dequeue_resp_valid=m_axis_dequeue_resp_valid, + m_axis_dequeue_resp_ready=m_axis_dequeue_resp_ready, + s_axis_dequeue_commit_op_tag=s_axis_dequeue_commit_op_tag, + s_axis_dequeue_commit_valid=s_axis_dequeue_commit_valid, + s_axis_dequeue_commit_ready=s_axis_dequeue_commit_ready, + m_axis_doorbell_queue=m_axis_doorbell_queue, + m_axis_doorbell_valid=m_axis_doorbell_valid, + s_axil_awaddr=s_axil_awaddr, + s_axil_awprot=s_axil_awprot, + s_axil_awvalid=s_axil_awvalid, + s_axil_awready=s_axil_awready, + s_axil_wdata=s_axil_wdata, + s_axil_wstrb=s_axil_wstrb, + s_axil_wvalid=s_axil_wvalid, + s_axil_wready=s_axil_wready, + s_axil_bresp=s_axil_bresp, + s_axil_bvalid=s_axil_bvalid, + s_axil_bready=s_axil_bready, + s_axil_araddr=s_axil_araddr, + s_axil_arprot=s_axil_arprot, + s_axil_arvalid=s_axil_arvalid, + s_axil_arready=s_axil_arready, + s_axil_rdata=s_axil_rdata, + s_axil_rresp=s_axil_rresp, + s_axil_rvalid=s_axil_rvalid, + s_axil_rready=s_axil_rready, + enable=enable + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + enable.next = 1 + + yield clk.posedge + print("test 1: read and write queue configuration registers") + current_test.next = 1 + + axil_master_inst.init_write(0*32+0, struct.pack('> 16) & 0xf == q + assert (resp.data[0][1] >> 4) & 0xf == queue_tail_ptr[q] & 0xf + assert resp.data[0][2] == q + + assert resp.data[0][3] == current_tag # tag + assert not resp.data[0][6] # error + + if queue_uncommit_depth[q]: + commit_list.append((q, resp.data[0][4])) + queue_tail_ptr[q] = (queue_tail_ptr[q] + 1) & 0xffff + queue_uncommit_depth[q] -= 1 + else: + print("Queue was empty") + assert resp.data[0][5] # empty + + current_tag = (current_tag + 1) % 256 + + # commit + #random.shuffle(commit_list) + for k in range(random.randrange(8)): + if commit_list: + q, t = commit_list.pop(0) + + print("Commit dequeue from queue %d" % q) + + # dequeue commit + dequeue_commit_source.send([(t,)]) + + queue_depth[q] -= 1 + + yield delay(100) + + raise StopSimulation + + return instances() + +def test_bench(): + sim = Simulation(bench()) + sim.run() + +if __name__ == '__main__': + print("Running test...") + test_bench() diff --git a/fpga/common/tb/test_queue_manager.v b/fpga/common/tb/test_queue_manager.v new file mode 100644 index 000000000..54942a4d5 --- /dev/null +++ b/fpga/common/tb/test_queue_manager.v @@ -0,0 +1,217 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Testbench for queue_manager + */ +module test_queue_manager; + +// Parameters +parameter ADDR_WIDTH = 64; +parameter REQ_TAG_WIDTH = 8; +parameter OP_TABLE_SIZE = 16; +parameter OP_TAG_WIDTH = 8; +parameter QUEUE_INDEX_WIDTH = 8; +parameter CPL_INDEX_WIDTH = 8; +parameter QUEUE_PTR_WIDTH = 16; +parameter QUEUE_LOG_SIZE_WIDTH = 4; +parameter DESC_SIZE = 16; +parameter READ_PIPELINE = 2; +parameter WRITE_PIPELINE = 1; +parameter AXIL_DATA_WIDTH = 32; +parameter AXIL_ADDR_WIDTH = 16; +parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8); + +// Inputs +reg clk = 0; +reg rst = 0; +reg [7:0] current_test = 0; + +reg [QUEUE_INDEX_WIDTH-1:0] s_axis_dequeue_req_queue = 0; +reg [REQ_TAG_WIDTH-1:0] s_axis_dequeue_req_tag = 0; +reg s_axis_dequeue_req_valid = 0; +reg m_axis_dequeue_resp_ready = 0; +reg [OP_TAG_WIDTH-1:0] s_axis_dequeue_commit_op_tag = 0; +reg s_axis_dequeue_commit_valid = 0; +reg [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr = 0; +reg [2:0] s_axil_awprot = 0; +reg s_axil_awvalid = 0; +reg [AXIL_DATA_WIDTH-1:0] s_axil_wdata = 0; +reg [AXIL_STRB_WIDTH-1:0] s_axil_wstrb = 0; +reg s_axil_wvalid = 0; +reg s_axil_bready = 0; +reg [AXIL_ADDR_WIDTH-1:0] s_axil_araddr = 0; +reg [2:0] s_axil_arprot = 0; +reg s_axil_arvalid = 0; +reg s_axil_rready = 0; +reg enable = 0; + +// Outputs +wire s_axis_dequeue_req_ready; +wire [QUEUE_PTR_WIDTH-1:0] m_axis_dequeue_resp_ptr; +wire [ADDR_WIDTH-1:0] m_axis_dequeue_resp_addr; +wire [CPL_INDEX_WIDTH-1:0] m_axis_dequeue_resp_cpl; +wire [REQ_TAG_WIDTH-1:0] m_axis_dequeue_resp_tag; +wire [OP_TAG_WIDTH-1:0] m_axis_dequeue_resp_op_tag; +wire m_axis_dequeue_resp_empty; +wire m_axis_dequeue_resp_error; +wire m_axis_dequeue_resp_valid; +wire s_axis_dequeue_commit_ready; +wire [QUEUE_INDEX_WIDTH-1:0] m_axis_doorbell_queue; +wire m_axis_doorbell_valid; +wire s_axil_awready; +wire s_axil_wready; +wire [1:0] s_axil_bresp; +wire s_axil_bvalid; +wire s_axil_arready; +wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata; +wire [1:0] s_axil_rresp; +wire s_axil_rvalid; + +initial begin + // myhdl integration + $from_myhdl( + clk, + rst, + current_test, + s_axis_dequeue_req_queue, + s_axis_dequeue_req_tag, + s_axis_dequeue_req_valid, + m_axis_dequeue_resp_ready, + s_axis_dequeue_commit_op_tag, + s_axis_dequeue_commit_valid, + s_axil_awaddr, + s_axil_awprot, + s_axil_awvalid, + s_axil_wdata, + s_axil_wstrb, + s_axil_wvalid, + s_axil_bready, + s_axil_araddr, + s_axil_arprot, + s_axil_arvalid, + s_axil_rready, + enable + ); + $to_myhdl( + s_axis_dequeue_req_ready, + m_axis_dequeue_resp_ptr, + m_axis_dequeue_resp_addr, + m_axis_dequeue_resp_cpl, + m_axis_dequeue_resp_tag, + m_axis_dequeue_resp_op_tag, + m_axis_dequeue_resp_empty, + m_axis_dequeue_resp_error, + m_axis_dequeue_resp_valid, + s_axis_dequeue_commit_ready, + m_axis_doorbell_queue, + m_axis_doorbell_valid, + s_axil_awready, + s_axil_wready, + s_axil_bresp, + s_axil_bvalid, + s_axil_arready, + s_axil_rdata, + s_axil_rresp, + s_axil_rvalid + ); + + // dump file + $dumpfile("test_queue_manager.lxt"); + $dumpvars(0, test_queue_manager); +end + +queue_manager #( + .ADDR_WIDTH(ADDR_WIDTH), + .REQ_TAG_WIDTH(REQ_TAG_WIDTH), + .OP_TABLE_SIZE(OP_TABLE_SIZE), + .OP_TAG_WIDTH(OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .CPL_INDEX_WIDTH(CPL_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .QUEUE_LOG_SIZE_WIDTH(QUEUE_LOG_SIZE_WIDTH), + .DESC_SIZE(DESC_SIZE), + .READ_PIPELINE(READ_PIPELINE), + .WRITE_PIPELINE(WRITE_PIPELINE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) +) +UUT ( + .clk(clk), + .rst(rst), + .s_axis_dequeue_req_queue(s_axis_dequeue_req_queue), + .s_axis_dequeue_req_tag(s_axis_dequeue_req_tag), + .s_axis_dequeue_req_valid(s_axis_dequeue_req_valid), + .s_axis_dequeue_req_ready(s_axis_dequeue_req_ready), + .m_axis_dequeue_resp_ptr(m_axis_dequeue_resp_ptr), + .m_axis_dequeue_resp_addr(m_axis_dequeue_resp_addr), + .m_axis_dequeue_resp_cpl(m_axis_dequeue_resp_cpl), + .m_axis_dequeue_resp_tag(m_axis_dequeue_resp_tag), + .m_axis_dequeue_resp_op_tag(m_axis_dequeue_resp_op_tag), + .m_axis_dequeue_resp_empty(m_axis_dequeue_resp_empty), + .m_axis_dequeue_resp_error(m_axis_dequeue_resp_error), + .m_axis_dequeue_resp_valid(m_axis_dequeue_resp_valid), + .m_axis_dequeue_resp_ready(m_axis_dequeue_resp_ready), + .s_axis_dequeue_commit_op_tag(s_axis_dequeue_commit_op_tag), + .s_axis_dequeue_commit_valid(s_axis_dequeue_commit_valid), + .s_axis_dequeue_commit_ready(s_axis_dequeue_commit_ready), + .m_axis_doorbell_queue(m_axis_doorbell_queue), + .m_axis_doorbell_valid(m_axis_doorbell_valid), + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + .enable(enable) +); + +endmodule