mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
Add queue manager module and testbench
This commit is contained in:
parent
3d4ba0fa3f
commit
46f653f097
669
fpga/common/rtl/queue_manager.v
Normal file
669
fpga/common/rtl/queue_manager.v
Normal file
@ -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
|
489
fpga/common/tb/test_queue_manager.py
Executable file
489
fpga/common/tb/test_queue_manager.py
Executable file
@ -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('<Q', 0x8877665544332211)) # address
|
||||||
|
axil_master_inst.init_write(0*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(0*32+12, struct.pack('<L', 0x00000001)) # completion queue index
|
||||||
|
axil_master_inst.init_write(0*32+16, struct.pack('<L', 0x00000000)) # head pointer
|
||||||
|
axil_master_inst.init_write(0*32+24, struct.pack('<L', 0x00000000)) # tail pointer
|
||||||
|
axil_master_inst.init_write(0*32+8, struct.pack('<L', 0x80000004)) # active, log size
|
||||||
|
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
yield clk.posedge
|
||||||
|
|
||||||
|
axil_master_inst.init_read(0*32+0, 8)
|
||||||
|
axil_master_inst.init_read(0*32+8, 4)
|
||||||
|
axil_master_inst.init_read(0*32+12, 4)
|
||||||
|
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
yield clk.posedge
|
||||||
|
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
assert struct.unpack('<Q', data[1])[0] == 0x8877665544332211
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
assert struct.unpack('<L', data[1])[0] == 0x80000004
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
assert struct.unpack('<L', data[1])[0] == 0x00000001
|
||||||
|
|
||||||
|
yield delay(100)
|
||||||
|
|
||||||
|
yield clk.posedge
|
||||||
|
print("test 2: enqueue and dequeue")
|
||||||
|
current_test.next = 2
|
||||||
|
|
||||||
|
# increment head pointer
|
||||||
|
axil_master_inst.init_read(0*32+16, 4) # head pointer
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
head_ptr = struct.unpack('<L', data[1])[0]
|
||||||
|
|
||||||
|
axil_master_inst.init_write(0*32+16, struct.pack('<L', head_ptr + 1)) # head pointer
|
||||||
|
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
yield clk.posedge
|
||||||
|
|
||||||
|
# check for doorbell event
|
||||||
|
yield doorbell_sink.wait()
|
||||||
|
db = doorbell_sink.recv()
|
||||||
|
assert db.data[0][0] == 0
|
||||||
|
|
||||||
|
# read tail pointer
|
||||||
|
axil_master_inst.init_read(0*32+24, 4) # tail pointer
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
tail_ptr = struct.unpack('<L', data[1])[0]
|
||||||
|
|
||||||
|
# dequeue request
|
||||||
|
dequeue_req_source.send([(0, 1)])
|
||||||
|
|
||||||
|
yield dequeue_resp_sink.wait()
|
||||||
|
|
||||||
|
resp = dequeue_resp_sink.recv()
|
||||||
|
print(resp)
|
||||||
|
|
||||||
|
# dequeue commit
|
||||||
|
dequeue_commit_source.send([(resp.data[0][4],)])
|
||||||
|
|
||||||
|
yield delay(100)
|
||||||
|
|
||||||
|
# read tail pointer
|
||||||
|
axil_master_inst.init_read(0*32+24, 4) # tail pointer
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
new_tail_ptr = struct.unpack('<L', data[1])[0]
|
||||||
|
|
||||||
|
assert new_tail_ptr - tail_ptr == 1
|
||||||
|
|
||||||
|
yield delay(100)
|
||||||
|
|
||||||
|
yield clk.posedge
|
||||||
|
print("test 3: set up more queues")
|
||||||
|
current_test.next = 3
|
||||||
|
|
||||||
|
axil_master_inst.init_write(0*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(0*32+0, struct.pack('<Q', 0x5555555555000000)) # address
|
||||||
|
axil_master_inst.init_write(0*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(0*32+12, struct.pack('<L', 0x00000000)) # completion queue index
|
||||||
|
axil_master_inst.init_write(0*32+16, struct.pack('<L', 0x0000fff0)) # head pointer
|
||||||
|
axil_master_inst.init_write(0*32+24, struct.pack('<L', 0x0000fff0)) # tail pointer
|
||||||
|
axil_master_inst.init_write(0*32+8, struct.pack('<L', 0x80000004)) # active, log size
|
||||||
|
|
||||||
|
axil_master_inst.init_write(1*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(1*32+0, struct.pack('<Q', 0x5555555555010000)) # address
|
||||||
|
axil_master_inst.init_write(1*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(1*32+12, struct.pack('<L', 0x00000001)) # completion queue index
|
||||||
|
axil_master_inst.init_write(1*32+16, struct.pack('<L', 0x0000fff0)) # head pointer
|
||||||
|
axil_master_inst.init_write(1*32+24, struct.pack('<L', 0x0000fff0)) # tail pointer
|
||||||
|
axil_master_inst.init_write(1*32+8, struct.pack('<L', 0x80000004)) # active, log size
|
||||||
|
|
||||||
|
axil_master_inst.init_write(2*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(2*32+0, struct.pack('<Q', 0x5555555555020000)) # address
|
||||||
|
axil_master_inst.init_write(2*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(2*32+12, struct.pack('<L', 0x00000002)) # completion queue index
|
||||||
|
axil_master_inst.init_write(2*32+16, struct.pack('<L', 0x0000fff0)) # head pointer
|
||||||
|
axil_master_inst.init_write(2*32+24, struct.pack('<L', 0x0000fff0)) # tail pointer
|
||||||
|
axil_master_inst.init_write(2*32+8, struct.pack('<L', 0x80000004)) # active, log size
|
||||||
|
|
||||||
|
axil_master_inst.init_write(3*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(3*32+0, struct.pack('<Q', 0x5555555555030000)) # address
|
||||||
|
axil_master_inst.init_write(3*32+8, struct.pack('<L', 0x00000004)) # active, log size
|
||||||
|
axil_master_inst.init_write(3*32+12, struct.pack('<L', 0x00000003)) # completion queue index
|
||||||
|
axil_master_inst.init_write(3*32+16, struct.pack('<L', 0x0000fff0)) # head pointer
|
||||||
|
axil_master_inst.init_write(3*32+24, struct.pack('<L', 0x0000fff0)) # tail pointer
|
||||||
|
axil_master_inst.init_write(3*32+8, struct.pack('<L', 0x80000004)) # active, log size
|
||||||
|
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
yield clk.posedge
|
||||||
|
|
||||||
|
yield delay(100)
|
||||||
|
|
||||||
|
yield clk.posedge
|
||||||
|
print("test 4: multiple enqueue and dequeue")
|
||||||
|
current_test.next = 4
|
||||||
|
|
||||||
|
current_tag = 1
|
||||||
|
|
||||||
|
queue_head_ptr = [0xfff0]*4
|
||||||
|
queue_tail_ptr = [0xfff0]*4
|
||||||
|
queue_depth = [0]*4
|
||||||
|
queue_uncommit_depth = [0]*4
|
||||||
|
|
||||||
|
commit_list = []
|
||||||
|
|
||||||
|
random.seed(123456)
|
||||||
|
|
||||||
|
for i in range(50):
|
||||||
|
# enqueue
|
||||||
|
for k in range(random.randrange(8)):
|
||||||
|
q = random.randrange(4)
|
||||||
|
|
||||||
|
if queue_depth[q] < 16:
|
||||||
|
print("Enqueue into queue %d" % q)
|
||||||
|
|
||||||
|
# increment head pointer
|
||||||
|
axil_master_inst.init_read(q*32+16, 4) # head pointer
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
|
||||||
|
data = axil_master_inst.get_read_data()
|
||||||
|
head_ptr = struct.unpack('<L', data[1])[0]
|
||||||
|
|
||||||
|
assert head_ptr == queue_head_ptr[q]
|
||||||
|
|
||||||
|
head_ptr = (head_ptr + 1) & 0xffff
|
||||||
|
|
||||||
|
queue_head_ptr[q] = head_ptr
|
||||||
|
queue_depth[q] += 1
|
||||||
|
queue_uncommit_depth[q] += 1
|
||||||
|
|
||||||
|
axil_master_inst.init_write(q*32+16, struct.pack('<L', head_ptr)) # head pointer
|
||||||
|
yield axil_master_inst.wait()
|
||||||
|
|
||||||
|
# check doorbell event
|
||||||
|
yield doorbell_sink.wait()
|
||||||
|
db = doorbell_sink.recv()
|
||||||
|
assert db.data[0][0] == q
|
||||||
|
|
||||||
|
# dequeue
|
||||||
|
for k in range(random.randrange(8)):
|
||||||
|
q = random.randrange(4)
|
||||||
|
|
||||||
|
if len(commit_list) < OP_TABLE_SIZE:
|
||||||
|
print("Try dequeue from queue %d" % q)
|
||||||
|
|
||||||
|
# dequeue request
|
||||||
|
dequeue_req_source.send([(q, current_tag)])
|
||||||
|
|
||||||
|
yield dequeue_resp_sink.wait()
|
||||||
|
|
||||||
|
resp = dequeue_resp_sink.recv()
|
||||||
|
print(resp)
|
||||||
|
|
||||||
|
assert resp.data[0][0] == queue_tail_ptr[q]
|
||||||
|
assert (resp.data[0][1] >> 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()
|
217
fpga/common/tb/test_queue_manager.v
Normal file
217
fpga/common/tb/test_queue_manager.v
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user