mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: add USB2 support and related tests
- Introduced a new USB2 transaction engine testbench to validate the functionality of the USB2 transaction engine. - Updated clock and reset domain tests to include USB2 ULPI crossings. - Enhanced memory map tests to verify USB2 base addresses and performance offsets. - Modified pin map tests to ensure USB2 ULPI pins are correctly generated. - Updated SOC topology tests to account for USB2 peripherals and IRQ mappings. - Added static timing analysis constraints for USB2 clock domains. - Created dedicated USB2 tests for various components including packet RAM, descriptor validation, and transaction engine. - Implemented a register parity test to ensure ABI consistency between RTL and C header definitions.
This commit is contained in:
86 files changed
+11305
-48
No files matched your search
@@ -4,7 +4,7 @@
|
||||
`include "rib_defs.svh"
|
||||
|
||||
module axi4_interconnect_tb;
|
||||
localparam int NumMasters = 6;
|
||||
localparam int NumMasters = 7;
|
||||
localparam int NumTargets = 9;
|
||||
|
||||
logic clk_i = 1'b0;
|
||||
@@ -64,6 +64,7 @@ module axi4_interconnect_tb;
|
||||
.perf_dma_wait_o (),
|
||||
.perf_sdio0_wait_o (),
|
||||
.perf_sdio1_wait_o (),
|
||||
.perf_usb2_wait_o (),
|
||||
.perf_apb4_periph_wait_o(),
|
||||
.perf_apb4_system_wait_o(),
|
||||
.perf_sdram_wait_o (),
|
||||
@@ -194,7 +195,8 @@ module axi4_interconnect_tb;
|
||||
while (!masters[0].rvalid && cycles < 100) begin
|
||||
if (masters[3].arvalid || masters[3].awvalid || masters[3].wvalid ||
|
||||
masters[4].arvalid || masters[4].awvalid || masters[4].wvalid ||
|
||||
masters[5].arvalid || masters[5].awvalid || masters[5].wvalid) begin
|
||||
masters[5].arvalid || masters[5].awvalid || masters[5].wvalid ||
|
||||
masters[6].arvalid || masters[6].awvalid || masters[6].wvalid) begin
|
||||
$fatal(1, "inactive storage master drove a request during management boot traffic");
|
||||
end
|
||||
@(negedge clk_i);
|
||||
|
||||
@@ -39,6 +39,7 @@ module soc_irq_topology_tb;
|
||||
16: expect_irq(32'd1 << 10);
|
||||
17: expect_irq(32'd1 << 21);
|
||||
18: expect_irq(32'd1 << 23);
|
||||
19: expect_irq(32'd1 << 24);
|
||||
default: expect_irq(32'd1 << (bit_index + 7));
|
||||
endcase
|
||||
end
|
||||
@@ -63,7 +64,7 @@ module soc_irq_topology_tb;
|
||||
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
|
||||
s_irq[10], s_irq[21]);
|
||||
end
|
||||
if ((s_irq[31:24] !== '0) || (s_irq[19:18] !== '0)) begin
|
||||
if ((s_irq[31:25] !== '0) || (s_irq[19:18] !== '0)) begin
|
||||
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
|
||||
end
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ module sysctrl_tb;
|
||||
sysctrl.perf_dma_wait_i = 64'd13;
|
||||
sysctrl.perf_sdio0_wait_i = 64'd131;
|
||||
sysctrl.perf_sdio1_wait_i = 64'd141;
|
||||
sysctrl.perf_usb2_wait_i = 64'd151;
|
||||
sysctrl.perf_apb4_periph_wait_i = 64'd14;
|
||||
sysctrl.perf_apb4_system_wait_i = 64'd15;
|
||||
sysctrl.perf_sdram_wait_i = 64'd16;
|
||||
@@ -150,6 +151,8 @@ module sysctrl_tb;
|
||||
if (read_data !== 32'd131) $fatal(1, "SDIO0 performance snapshot was not recorded");
|
||||
read_register(32'h1000_B094, read_data);
|
||||
if (read_data !== 32'd141) $fatal(1, "SDIO1 performance snapshot was not recorded");
|
||||
read_register(32'h1000_B09C, read_data);
|
||||
if (read_data !== 32'd151) $fatal(1, "USB2 performance snapshot was not recorded");
|
||||
|
||||
read_register(32'h1000_B020, read_data);
|
||||
if (read_data !== 32'h0000_003F) $fatal(1, "user cores were not held in reset");
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module usb2_descriptor_tb;
|
||||
logic [31:0] s_buffer_addr;
|
||||
logic [31:0] s_byte_length;
|
||||
logic [31:0] s_next_addr;
|
||||
logic [31:0] s_control;
|
||||
logic [31:0] s_actual_length;
|
||||
logic [31:0] s_status;
|
||||
logic [31:0] s_frame;
|
||||
logic [31:0] s_reserved;
|
||||
logic [ 3:0] s_desc_index;
|
||||
logic s_valid;
|
||||
usb2_pkg::usb2_desc_error_e s_error;
|
||||
|
||||
usb2_dma_descriptor #(
|
||||
.MaxDescriptors(16)
|
||||
) u_descriptor (
|
||||
.buffer_addr_i (s_buffer_addr),
|
||||
.byte_length_i (s_byte_length),
|
||||
.next_addr_i (s_next_addr),
|
||||
.control_i (s_control),
|
||||
.actual_length_i(s_actual_length),
|
||||
.status_i (s_status),
|
||||
.frame_i (s_frame),
|
||||
.reserved_i (s_reserved),
|
||||
.desc_index_i (s_desc_index),
|
||||
.own_o (),
|
||||
.chain_o (),
|
||||
.end_o (),
|
||||
.irq_o (),
|
||||
.short_ok_o (),
|
||||
.zero_packet_o (),
|
||||
.valid_o (s_valid),
|
||||
.error_o (s_error)
|
||||
);
|
||||
|
||||
initial begin
|
||||
s_buffer_addr = 32'h2000_0000;
|
||||
s_byte_length = 32'd512;
|
||||
s_next_addr = 32'd0;
|
||||
s_control = 32'h0000_000D;
|
||||
s_actual_length = 32'd0;
|
||||
s_status = 32'd0;
|
||||
s_frame = 32'd0;
|
||||
s_reserved = 32'd0;
|
||||
s_desc_index = 4'd0;
|
||||
#1;
|
||||
if (!s_valid || (s_error != usb2_pkg::Usb2DescOk)) begin
|
||||
$fatal(1, "USB2 terminal descriptor rejected");
|
||||
end
|
||||
|
||||
s_buffer_addr = 32'h2000_0001;
|
||||
#1;
|
||||
if (s_valid || (s_error != usb2_pkg::Usb2DescBufferAlign)) begin
|
||||
$fatal(1, "USB2 unaligned buffer accepted");
|
||||
end
|
||||
|
||||
s_buffer_addr = 32'hFFFF_FFFC;
|
||||
s_byte_length = 32'd8;
|
||||
#1;
|
||||
if (s_valid || (s_error != usb2_pkg::Usb2DescAddressWrap)) begin
|
||||
$fatal(1, "USB2 wrapping buffer accepted");
|
||||
end
|
||||
|
||||
s_buffer_addr = 32'h2000_0000;
|
||||
s_byte_length = 32'd512;
|
||||
s_control = 32'h0000_0003;
|
||||
s_next_addr = 32'h2000_0FE0;
|
||||
#1;
|
||||
if (!s_valid) begin
|
||||
$fatal(1, "USB2 aligned chained descriptor rejected");
|
||||
end
|
||||
|
||||
s_next_addr = 32'h2000_0FF0;
|
||||
#1;
|
||||
if (s_valid || (s_error != usb2_pkg::Usb2DescNextAlign)) begin
|
||||
$fatal(1, "USB2 descriptor crossing 4 KiB boundary accepted");
|
||||
end
|
||||
$display("USB2 descriptor validation passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,216 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module usb2_dma_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic start_i = 1'b0;
|
||||
logic abort_i = 1'b0;
|
||||
logic memory_to_packet_i = 1'b1;
|
||||
logic allow_short_i = 1'b0;
|
||||
logic [31:0] desc_base_i = 32'd0;
|
||||
logic [15:0] desc_limit_i = 16'd1;
|
||||
logic [31:0] transfer_bytes_i = 32'd5;
|
||||
logic packet_in_valid_i = 1'b0;
|
||||
logic packet_in_ready_o;
|
||||
logic [31:0] packet_in_data_i = '0;
|
||||
logic [ 3:0] packet_in_strb_i = '0;
|
||||
logic packet_in_last_i = 1'b0;
|
||||
logic packet_out_valid_o;
|
||||
logic packet_out_ready_i = 1'b1;
|
||||
logic [31:0] packet_out_data_o;
|
||||
logic [ 3:0] packet_out_strb_o;
|
||||
logic packet_out_last_o;
|
||||
logic busy_o;
|
||||
logic done_o;
|
||||
logic error_o;
|
||||
logic [ 7:0] error_code_o;
|
||||
logic [31:0] current_desc_o;
|
||||
logic [31:0] next_desc_o;
|
||||
logic [31:0] bytes_done_o;
|
||||
logic [31:0] frame_o;
|
||||
logic descriptor_irq_o;
|
||||
logic abort_done_o;
|
||||
integer output_count;
|
||||
logic [31:0] output_data [0:1];
|
||||
logic [ 3:0] output_strb [0:1];
|
||||
logic output_last [0:1];
|
||||
logic descriptor_irq_seen;
|
||||
|
||||
axi4_if #(
|
||||
.ADDR_WIDTH(32),
|
||||
.DATA_WIDTH(32),
|
||||
.ID_WIDTH (1),
|
||||
.USER_WIDTH(1)
|
||||
) dma_axi4 (
|
||||
.aclk (clk_i),
|
||||
.aresetn(rst_n_i)
|
||||
);
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (!rst_n_i) begin
|
||||
output_count <= 0;
|
||||
descriptor_irq_seen <= 1'b0;
|
||||
end else begin
|
||||
if (packet_out_valid_o && packet_out_ready_i) begin
|
||||
output_data[output_count] <= packet_out_data_o;
|
||||
output_strb[output_count] <= packet_out_strb_o;
|
||||
output_last[output_count] <= packet_out_last_o;
|
||||
output_count <= output_count + 1;
|
||||
end
|
||||
if (descriptor_irq_o) begin
|
||||
descriptor_irq_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
usb2_dma #(
|
||||
.AddrWidth (32),
|
||||
.DataWidth (32),
|
||||
.MaxDescriptors(16)
|
||||
) u_usb2_dma (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (start_i),
|
||||
.abort_i (abort_i),
|
||||
.memory_to_packet_i(memory_to_packet_i),
|
||||
.allow_short_i (allow_short_i),
|
||||
.desc_base_i (desc_base_i),
|
||||
.desc_limit_i (desc_limit_i),
|
||||
.transfer_bytes_i (transfer_bytes_i),
|
||||
.packet_in_valid_i (packet_in_valid_i),
|
||||
.packet_in_ready_o (packet_in_ready_o),
|
||||
.packet_in_data_i (packet_in_data_i),
|
||||
.packet_in_strb_i (packet_in_strb_i),
|
||||
.packet_in_last_i (packet_in_last_i),
|
||||
.packet_out_valid_o(packet_out_valid_o),
|
||||
.packet_out_ready_i(packet_out_ready_i),
|
||||
.packet_out_data_o (packet_out_data_o),
|
||||
.packet_out_strb_o (packet_out_strb_o),
|
||||
.packet_out_last_o (packet_out_last_o),
|
||||
.busy_o (busy_o),
|
||||
.done_o (done_o),
|
||||
.error_o (error_o),
|
||||
.error_code_o (error_code_o),
|
||||
.current_desc_o (current_desc_o),
|
||||
.next_desc_o (next_desc_o),
|
||||
.bytes_done_o (bytes_done_o),
|
||||
.frame_o (frame_o),
|
||||
.descriptor_irq_o (descriptor_irq_o),
|
||||
.abort_done_o (abort_done_o),
|
||||
.dma_axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
sdio_axi_memory_responder #(
|
||||
.DepthWords(16384)
|
||||
) u_memory (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.axi4 (dma_axi4)
|
||||
);
|
||||
|
||||
task automatic reset_dut;
|
||||
begin
|
||||
rst_n_i = 1'b0;
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(posedge clk_i);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic start_dma;
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic send_packet_word(input logic [31:0] data_i, input logic [3:0] strb_i,
|
||||
input logic last_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
packet_in_data_i = data_i;
|
||||
packet_in_strb_i = strb_i;
|
||||
packet_in_last_i = last_i;
|
||||
packet_in_valid_i = 1'b1;
|
||||
while (!packet_in_ready_o) @(negedge clk_i);
|
||||
@(negedge clk_i);
|
||||
packet_in_valid_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
u_memory.clear_memory();
|
||||
reset_dut();
|
||||
|
||||
// Memory-to-packet transfer splits at 4 KiB and finishes with one valid byte.
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0FFC);
|
||||
u_memory.write_word(32'h0000_0004, 32'd5);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_000D);
|
||||
u_memory.write_word(32'h0000_0010, 32'd0);
|
||||
u_memory.write_word(32'h0000_0014, 32'd0);
|
||||
u_memory.write_word(32'h0000_0018, 32'd0);
|
||||
u_memory.write_word(32'h0000_001C, 32'd0);
|
||||
u_memory.write_word(32'h0000_0FFC, 32'h4433_2211);
|
||||
u_memory.write_word(32'h0000_1000, 32'h0000_0055);
|
||||
memory_to_packet_i = 1'b1;
|
||||
start_dma();
|
||||
wait (done_o || error_o);
|
||||
@(posedge clk_i);
|
||||
if (error_o || (bytes_done_o != 32'd5) || (output_count != 2) ||
|
||||
(output_data[0] != 32'h4433_2211) || (output_strb[0] != 4'hF) || output_last[0] ||
|
||||
(output_data[1] != 32'h0000_0055) || (output_strb[1] != 4'h1) || !output_last[1] ||
|
||||
(u_memory.read_burst_count != 3) || (u_memory.max_read_beats > 16) ||
|
||||
(u_memory.read_word(
|
||||
32'h0000_0010
|
||||
) != 32'd5) || (u_memory.read_word(
|
||||
32'h0000_0014
|
||||
) != 32'h0001_0000) || (u_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0000_000C) || !descriptor_irq_seen) begin
|
||||
$fatal(1, "memory-to-packet DMA failed err=%b code=%h bytes=%0d outputs=%0d", error_o,
|
||||
error_code_o, bytes_done_o, output_count);
|
||||
end
|
||||
|
||||
// Packet-to-memory transfer preserves tail strobes and commits OWN last.
|
||||
reset_dut();
|
||||
u_memory.write_word(32'h0000_0000, 32'h0000_0200);
|
||||
u_memory.write_word(32'h0000_0004, 32'd5);
|
||||
u_memory.write_word(32'h0000_0008, 32'd0);
|
||||
u_memory.write_word(32'h0000_000C, 32'h0000_000D);
|
||||
u_memory.write_word(32'h0000_0010, 32'd0);
|
||||
u_memory.write_word(32'h0000_0014, 32'd0);
|
||||
u_memory.write_word(32'h0000_0018, 32'd0);
|
||||
u_memory.write_word(32'h0000_001C, 32'd0);
|
||||
u_memory.write_word(32'h0000_0200, 32'd0);
|
||||
u_memory.write_word(32'h0000_0204, 32'd0);
|
||||
memory_to_packet_i = 1'b0;
|
||||
start_dma();
|
||||
send_packet_word(32'h8877_6655, 4'hF, 1'b0);
|
||||
send_packet_word(32'h0000_0099, 4'h1, 1'b1);
|
||||
wait (done_o || error_o);
|
||||
@(posedge clk_i);
|
||||
if (error_o || (bytes_done_o != 32'd5) || (u_memory.read_word(
|
||||
32'h0000_0200
|
||||
) != 32'h8877_6655) || (u_memory.read_word(
|
||||
32'h0000_0204
|
||||
) != 32'h0000_0099) || (u_memory.read_word(
|
||||
32'h0000_0010
|
||||
) != 32'd5) || (u_memory.read_word(
|
||||
32'h0000_0014
|
||||
) != 32'h0001_0000) || (u_memory.read_word(
|
||||
32'h0000_000C
|
||||
) != 32'h0000_000C) || !descriptor_irq_seen) begin
|
||||
$fatal(1, "packet-to-memory DMA failed err=%b code=%h bytes=%0d data=%h tail=%h", error_o,
|
||||
error_code_o, bytes_done_o, u_memory.read_word(32'h0000_0200), u_memory.read_word(
|
||||
32'h0000_0204));
|
||||
end
|
||||
|
||||
$display("USB2 descriptor DMA test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,115 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module usb2_link_domain_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic enable_i = 1'b0;
|
||||
logic fill_cmd_valid_i = 1'b1;
|
||||
logic drain_cmd_valid_i = 1'b1;
|
||||
logic [ 7:0][31:0] endpoint_cfg_i = '0;
|
||||
logic work_ready_o;
|
||||
logic result_valid_o;
|
||||
logic [63:0] result_data_o;
|
||||
logic setup_valid_o;
|
||||
logic [63:0] setup_data_o;
|
||||
logic fill_cmd_ready_o;
|
||||
logic fill_stream_ready_o;
|
||||
logic drain_cmd_ready_o;
|
||||
logic drain_stream_valid_o;
|
||||
logic [36:0] drain_stream_data_o;
|
||||
logic buffer_event_valid_o;
|
||||
logic [ 2:0] buffer_event_data_o;
|
||||
logic viewport_ready_o;
|
||||
logic viewport_resp_valid_o;
|
||||
logic [ 8:0] viewport_resp_data_o;
|
||||
logic [ 1:0] line_state_o;
|
||||
logic [ 1:0] vbus_state_o;
|
||||
logic id_ground_o;
|
||||
logic link_ready_o;
|
||||
logic [13:0] frame_o;
|
||||
logic transaction_busy_o;
|
||||
logic retry_o;
|
||||
logic protocol_error_o;
|
||||
logic [31:0] ecc_corrected_count_o;
|
||||
logic [31:0] ecc_uncorrectable_count_o;
|
||||
|
||||
usb2_ulpi_if ulpi ();
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
assign ulpi.data_di_i = 8'd0;
|
||||
assign ulpi.dir_i = 1'b0;
|
||||
assign ulpi.nxt_i = 1'b1;
|
||||
|
||||
usb2_link_domain u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (enable_i),
|
||||
.phy_reset_n_i (1'b1),
|
||||
.high_speed_i (1'b1),
|
||||
.active_role_i (usb2_pkg::Usb2RoleIdle),
|
||||
.device_addr_i (7'd0),
|
||||
.endpoint_cfg_i (endpoint_cfg_i),
|
||||
.timeout_i (32'd1000),
|
||||
.work_valid_i (1'b0),
|
||||
.work_ready_o (work_ready_o),
|
||||
.work_data_i ('0),
|
||||
.result_valid_o (result_valid_o),
|
||||
.result_ready_i (1'b1),
|
||||
.result_data_o (result_data_o),
|
||||
.setup_valid_o (setup_valid_o),
|
||||
.setup_ready_i (1'b1),
|
||||
.setup_data_o (setup_data_o),
|
||||
.fill_cmd_valid_i (fill_cmd_valid_i),
|
||||
.fill_cmd_ready_o (fill_cmd_ready_o),
|
||||
.fill_cmd_data_i ('0),
|
||||
.fill_stream_valid_i (1'b0),
|
||||
.fill_stream_ready_o (fill_stream_ready_o),
|
||||
.fill_stream_data_i ('0),
|
||||
.drain_cmd_valid_i (drain_cmd_valid_i),
|
||||
.drain_cmd_ready_o (drain_cmd_ready_o),
|
||||
.drain_cmd_data_i ('0),
|
||||
.drain_stream_valid_o (drain_stream_valid_o),
|
||||
.drain_stream_ready_i (1'b1),
|
||||
.drain_stream_data_o (drain_stream_data_o),
|
||||
.buffer_event_valid_o (buffer_event_valid_o),
|
||||
.buffer_event_ready_i (1'b1),
|
||||
.buffer_event_data_o (buffer_event_data_o),
|
||||
.viewport_valid_i (1'b0),
|
||||
.viewport_ready_o (viewport_ready_o),
|
||||
.viewport_data_i ('0),
|
||||
.viewport_resp_valid_o (viewport_resp_valid_o),
|
||||
.viewport_resp_ready_i (1'b1),
|
||||
.viewport_resp_data_o (viewport_resp_data_o),
|
||||
.line_state_o (line_state_o),
|
||||
.vbus_state_o (vbus_state_o),
|
||||
.id_ground_o (id_ground_o),
|
||||
.link_ready_o (link_ready_o),
|
||||
.frame_o (frame_o),
|
||||
.transaction_busy_o (transaction_busy_o),
|
||||
.retry_o (retry_o),
|
||||
.protocol_error_o (protocol_error_o),
|
||||
.ecc_corrected_count_o (ecc_corrected_count_o),
|
||||
.ecc_uncorrectable_count_o(ecc_uncorrectable_count_o),
|
||||
.ulpi (ulpi)
|
||||
);
|
||||
|
||||
initial begin
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (4) @(posedge clk_i);
|
||||
if (fill_cmd_ready_o || drain_cmd_ready_o || transaction_busy_o) begin
|
||||
$fatal(1, "USB2 disabled packet-store isolation failed");
|
||||
end
|
||||
fill_cmd_valid_i = 1'b0;
|
||||
drain_cmd_valid_i = 1'b0;
|
||||
enable_i = 1'b1;
|
||||
repeat (8) @(posedge clk_i);
|
||||
if (!link_ready_o || !ulpi.reset_n_o || ulpi.data_oe_o || ulpi.stp_o ||
|
||||
transaction_busy_o || result_valid_o || setup_valid_o || protocol_error_o ||
|
||||
(ecc_corrected_count_o != 32'd0) || (ecc_uncorrectable_count_o != 32'd0)) begin
|
||||
$fatal(1, "USB2 link domain idle smoke failed");
|
||||
end
|
||||
$display("USB2 link domain smoke passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module usb2_packet_ram_tb;
|
||||
logic s_clk;
|
||||
logic s_rst_n;
|
||||
logic s_write;
|
||||
logic s_read;
|
||||
logic [11:0] s_addr;
|
||||
logic [31:0] s_write_data;
|
||||
logic s_inject_single;
|
||||
logic s_inject_double;
|
||||
logic [31:0] s_read_data;
|
||||
logic s_read_valid;
|
||||
logic s_corrected;
|
||||
logic s_uncorrectable;
|
||||
|
||||
usb2_packet_ram #(
|
||||
.EnableFaultInjection(1'b1)
|
||||
) u_packet_ram (
|
||||
.clk_i (s_clk),
|
||||
.rst_n_i (s_rst_n),
|
||||
.write_i (s_write),
|
||||
.read_i (s_read),
|
||||
.addr_i (s_addr),
|
||||
.write_data_i (s_write_data),
|
||||
.inject_single_i(s_inject_single),
|
||||
.inject_double_i(s_inject_double),
|
||||
.read_data_o (s_read_data),
|
||||
.read_valid_o (s_read_valid),
|
||||
.corrected_o (s_corrected),
|
||||
.uncorrectable_o(s_uncorrectable)
|
||||
);
|
||||
|
||||
always #5 s_clk = ~s_clk;
|
||||
|
||||
task automatic write_word(input logic [11:0] addr_i, input logic [31:0] data_i,
|
||||
input logic single_i, input logic double_i);
|
||||
begin
|
||||
@(negedge s_clk);
|
||||
s_addr = addr_i;
|
||||
s_write_data = data_i;
|
||||
s_inject_single = single_i;
|
||||
s_inject_double = double_i;
|
||||
s_write = 1'b1;
|
||||
@(negedge s_clk);
|
||||
s_write = 1'b0;
|
||||
s_inject_single = 1'b0;
|
||||
s_inject_double = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic read_word(input logic [11:0] addr_i);
|
||||
begin
|
||||
@(negedge s_clk);
|
||||
s_addr = addr_i;
|
||||
s_read = 1'b1;
|
||||
@(posedge s_clk);
|
||||
#1;
|
||||
s_read = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
s_clk = 1'b0;
|
||||
s_rst_n = 1'b0;
|
||||
s_write = 1'b0;
|
||||
s_read = 1'b0;
|
||||
s_addr = '0;
|
||||
s_write_data = '0;
|
||||
s_inject_single = 1'b0;
|
||||
s_inject_double = 1'b0;
|
||||
repeat (2) @(negedge s_clk);
|
||||
s_rst_n = 1'b1;
|
||||
|
||||
write_word(12'h010, 32'h1357_9BDF, 1'b0, 1'b0);
|
||||
read_word(12'h010);
|
||||
if (!s_read_valid || s_corrected || s_uncorrectable || (s_read_data != 32'h1357_9BDF)) begin
|
||||
$fatal(1, "USB2 packet RAM clean read failed");
|
||||
end
|
||||
|
||||
write_word(12'h011, 32'h2468_ACE0, 1'b1, 1'b0);
|
||||
read_word(12'h011);
|
||||
if (!s_read_valid || !s_corrected || s_uncorrectable || (s_read_data != 32'h2468_ACE0)) begin
|
||||
$fatal(1, "USB2 packet RAM single-bit correction failed");
|
||||
end
|
||||
|
||||
write_word(12'h012, 32'h55AA_F00D, 1'b0, 1'b1);
|
||||
read_word(12'h012);
|
||||
if (!s_read_valid || !s_uncorrectable) begin
|
||||
$fatal(1, "USB2 packet RAM double-bit detection failed");
|
||||
end
|
||||
$display("USB2 packet RAM ECC test passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,213 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module usb2_packet_store_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic fill_start_valid_i = 1'b0;
|
||||
logic fill_start_ready_o;
|
||||
logic [11:0] fill_base_i = '0;
|
||||
logic [14:0] fill_bytes_i = '0;
|
||||
logic fill_valid_i = 1'b0;
|
||||
logic fill_ready_o;
|
||||
logic [31:0] fill_data_i = '0;
|
||||
logic [ 3:0] fill_strb_i = '0;
|
||||
logic fill_last_i = 1'b0;
|
||||
logic fill_done_o;
|
||||
logic fill_error_o;
|
||||
logic drain_start_valid_i = 1'b0;
|
||||
logic drain_start_ready_o;
|
||||
logic [11:0] drain_base_i = '0;
|
||||
logic [14:0] drain_bytes_i = '0;
|
||||
logic drain_valid_o;
|
||||
logic drain_ready_i = 1'b1;
|
||||
logic [31:0] drain_data_o;
|
||||
logic [ 3:0] drain_strb_o;
|
||||
logic drain_last_o;
|
||||
logic drain_done_o;
|
||||
logic rx_start_valid_i = 1'b0;
|
||||
logic rx_start_ready_o;
|
||||
logic [11:0] rx_base_i = '0;
|
||||
logic [14:0] rx_limit_i = '0;
|
||||
logic rx_valid_i = 1'b0;
|
||||
logic rx_ready_o;
|
||||
logic [ 7:0] rx_data_i = '0;
|
||||
logic rx_commit_i = 1'b0;
|
||||
logic rx_cancel_i = 1'b0;
|
||||
logic rx_done_o;
|
||||
logic [14:0] rx_bytes_o;
|
||||
logic rx_overflow_o;
|
||||
logic tx_start_valid_i = 1'b0;
|
||||
logic tx_start_ready_o;
|
||||
logic [11:0] tx_base_i = '0;
|
||||
logic [14:0] tx_bytes_i = '0;
|
||||
logic tx_valid_o;
|
||||
logic tx_ready_i = 1'b1;
|
||||
logic [ 7:0] tx_data_o;
|
||||
logic tx_done_o;
|
||||
logic busy_o;
|
||||
logic ecc_corrected_o;
|
||||
logic ecc_uncorrectable_o;
|
||||
integer tx_count;
|
||||
integer drain_count;
|
||||
logic [ 7:0] tx_bytes [0:4];
|
||||
logic [31:0] drain_words [0:1];
|
||||
logic [ 3:0] drain_strb [0:1];
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (!rst_n_i) begin
|
||||
tx_count <= 0;
|
||||
drain_count <= 0;
|
||||
end else begin
|
||||
if (tx_valid_o && tx_ready_i) begin
|
||||
tx_bytes[tx_count] <= tx_data_o;
|
||||
tx_count <= tx_count + 1;
|
||||
end
|
||||
if (drain_valid_o && drain_ready_i) begin
|
||||
drain_words[drain_count] <= drain_data_o;
|
||||
drain_strb[drain_count] <= drain_strb_o;
|
||||
drain_count <= drain_count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
usb2_packet_store u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.fill_start_valid_i (fill_start_valid_i),
|
||||
.fill_start_ready_o (fill_start_ready_o),
|
||||
.fill_base_i (fill_base_i),
|
||||
.fill_bytes_i (fill_bytes_i),
|
||||
.fill_valid_i (fill_valid_i),
|
||||
.fill_ready_o (fill_ready_o),
|
||||
.fill_data_i (fill_data_i),
|
||||
.fill_strb_i (fill_strb_i),
|
||||
.fill_last_i (fill_last_i),
|
||||
.fill_done_o (fill_done_o),
|
||||
.fill_error_o (fill_error_o),
|
||||
.drain_start_valid_i(drain_start_valid_i),
|
||||
.drain_start_ready_o(drain_start_ready_o),
|
||||
.drain_base_i (drain_base_i),
|
||||
.drain_bytes_i (drain_bytes_i),
|
||||
.drain_valid_o (drain_valid_o),
|
||||
.drain_ready_i (drain_ready_i),
|
||||
.drain_data_o (drain_data_o),
|
||||
.drain_strb_o (drain_strb_o),
|
||||
.drain_last_o (drain_last_o),
|
||||
.drain_done_o (drain_done_o),
|
||||
.rx_start_valid_i (rx_start_valid_i),
|
||||
.rx_start_ready_o (rx_start_ready_o),
|
||||
.rx_base_i (rx_base_i),
|
||||
.rx_limit_i (rx_limit_i),
|
||||
.rx_valid_i (rx_valid_i),
|
||||
.rx_ready_o (rx_ready_o),
|
||||
.rx_data_i (rx_data_i),
|
||||
.rx_commit_i (rx_commit_i),
|
||||
.rx_cancel_i (rx_cancel_i),
|
||||
.rx_done_o (rx_done_o),
|
||||
.rx_bytes_o (rx_bytes_o),
|
||||
.rx_overflow_o (rx_overflow_o),
|
||||
.tx_start_valid_i (tx_start_valid_i),
|
||||
.tx_start_ready_o (tx_start_ready_o),
|
||||
.tx_base_i (tx_base_i),
|
||||
.tx_bytes_i (tx_bytes_i),
|
||||
.tx_valid_o (tx_valid_o),
|
||||
.tx_ready_i (tx_ready_i),
|
||||
.tx_data_o (tx_data_o),
|
||||
.tx_done_o (tx_done_o),
|
||||
.busy_o (busy_o),
|
||||
.ecc_corrected_o (ecc_corrected_o),
|
||||
.ecc_uncorrectable_o(ecc_uncorrectable_o)
|
||||
);
|
||||
|
||||
task automatic pulse_fill(input logic [31:0] data_i, input logic [3:0] strb_i,
|
||||
input logic last_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
fill_data_i = data_i;
|
||||
fill_strb_i = strb_i;
|
||||
fill_last_i = last_i;
|
||||
fill_valid_i = 1'b1;
|
||||
while (!fill_ready_o) @(negedge clk_i);
|
||||
@(negedge clk_i);
|
||||
fill_valid_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic pulse_rx(input logic [7:0] data_i);
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
rx_data_i = data_i;
|
||||
rx_valid_i = 1'b1;
|
||||
while (!rx_ready_o) @(negedge clk_i);
|
||||
@(negedge clk_i);
|
||||
rx_valid_i = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
|
||||
@(negedge clk_i);
|
||||
fill_base_i = 12'd8;
|
||||
fill_bytes_i = 15'd5;
|
||||
fill_start_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fill_start_valid_i = 1'b0;
|
||||
pulse_fill(32'h4433_2211, 4'hF, 1'b0);
|
||||
pulse_fill(32'h0000_0055, 4'h1, 1'b1);
|
||||
wait (fill_done_o);
|
||||
if (fill_error_o) $fatal(1, "packet store fill failed");
|
||||
|
||||
@(negedge clk_i);
|
||||
tx_base_i = 12'd8;
|
||||
tx_bytes_i = 15'd5;
|
||||
tx_start_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
tx_start_valid_i = 1'b0;
|
||||
wait (tx_done_o);
|
||||
@(posedge clk_i);
|
||||
if ((tx_count != 5) || (tx_bytes[0] != 8'h11) || (tx_bytes[1] != 8'h22) ||
|
||||
(tx_bytes[2] != 8'h33) || (tx_bytes[3] != 8'h44) || (tx_bytes[4] != 8'h55)) begin
|
||||
$fatal(1, "packet store transmit mismatch count=%0d", tx_count);
|
||||
end
|
||||
|
||||
@(negedge clk_i);
|
||||
rx_base_i = 12'd16;
|
||||
rx_limit_i = 15'd5;
|
||||
rx_start_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
rx_start_valid_i = 1'b0;
|
||||
pulse_rx(8'hA1);
|
||||
pulse_rx(8'hB2);
|
||||
pulse_rx(8'hC3);
|
||||
pulse_rx(8'hD4);
|
||||
pulse_rx(8'hE5);
|
||||
@(negedge clk_i);
|
||||
rx_commit_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
rx_commit_i = 1'b0;
|
||||
wait (rx_done_o);
|
||||
if ((rx_bytes_o != 15'd5) || rx_overflow_o) $fatal(1, "packet store receive failed");
|
||||
|
||||
@(negedge clk_i);
|
||||
drain_base_i = 12'd16;
|
||||
drain_bytes_i = 15'd5;
|
||||
drain_start_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
drain_start_valid_i = 1'b0;
|
||||
wait (drain_done_o);
|
||||
@(posedge clk_i);
|
||||
if ((drain_count != 2) || (drain_words[0] != 32'hD4C3_B2A1) ||
|
||||
(drain_words[1][7:0] != 8'hE5) || (drain_strb[0] != 4'hF) ||
|
||||
(drain_strb[1] != 4'h1)) begin
|
||||
$fatal(1, "packet store drain mismatch count=%0d", drain_count);
|
||||
end
|
||||
if (ecc_corrected_o || ecc_uncorrectable_o) $fatal(1, "unexpected ECC event");
|
||||
|
||||
$display("USB2 packet store paths passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module usb2_pkg_tb;
|
||||
logic [15:0] s_crc16;
|
||||
|
||||
initial begin
|
||||
if (usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidOut) != 8'hE1) begin
|
||||
$fatal(1, "USB2 OUT PID encoding mismatch");
|
||||
end
|
||||
if (!usb2_pkg::usb2_pid_valid(8'hD2) || usb2_pkg::usb2_pid_valid(8'h52)) begin
|
||||
$fatal(1, "USB2 PID complement validation mismatch");
|
||||
end
|
||||
if (usb2_pkg::usb2_token_crc5(
|
||||
11'd0
|
||||
) != 5'h02 || usb2_pkg::usb2_token_crc5(
|
||||
11'd1
|
||||
) != 5'h1D) begin
|
||||
$fatal(1, "USB2 token CRC5 vector mismatch");
|
||||
end
|
||||
|
||||
s_crc16 = 16'hFFFF;
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "1");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "2");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "3");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "4");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "5");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "6");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "7");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "8");
|
||||
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "9");
|
||||
if (usb2_pkg::usb2_crc16_finish(s_crc16) != 16'hB4C8) begin
|
||||
$fatal(1, "USB2 CRC16 vector mismatch");
|
||||
end
|
||||
$display("USB2 PID and CRC vectors passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,235 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
`include "usb2_define.svh"
|
||||
|
||||
module usb2_reg_tb;
|
||||
localparam logic [31:0] UsbBase = 32'h1001_6000;
|
||||
logic s_clk;
|
||||
logic s_rst_n;
|
||||
logic [15:0] s_irq_event;
|
||||
logic s_irq;
|
||||
logic s_enable;
|
||||
logic [ 1:0] s_force_role;
|
||||
logic s_auto_role;
|
||||
logic [ 7:0][31:0] s_endpoint_status;
|
||||
logic [ 7:0][31:0] s_endpoint_bytes_in;
|
||||
logic [ 7:0][31:0] s_endpoint_bytes_out;
|
||||
logic [15:0][31:0] s_channel_status;
|
||||
logic [15:0][31:0] s_channel_bytes;
|
||||
logic [ 7:0][31:0] s_endpoint_cfg;
|
||||
logic [ 7:0][31:0] s_endpoint_ram_in;
|
||||
logic [ 7:0][31:0] s_endpoint_ram_out;
|
||||
logic [ 7:0][31:0] s_endpoint_desc_in;
|
||||
logic [ 7:0][31:0] s_endpoint_desc_out;
|
||||
logic [ 7:0][31:0] s_endpoint_command;
|
||||
logic [31:0] s_endpoint_complete_clear_in;
|
||||
logic [31:0] s_endpoint_complete_clear_out;
|
||||
logic s_endpoint_clear_seen;
|
||||
logic [15:0][31:0] s_channel_cfg;
|
||||
logic [15:0][31:0] s_channel_target;
|
||||
logic [15:0][31:0] s_channel_interval;
|
||||
logic [15:0][31:0] s_channel_ram;
|
||||
logic [15:0][31:0] s_channel_desc;
|
||||
logic [15:0][31:0] s_channel_command;
|
||||
apb4_if u_apb4 (
|
||||
.pclk (s_clk),
|
||||
.presetn(s_rst_n)
|
||||
);
|
||||
|
||||
usb2_reg u_reg (
|
||||
.clk_i (s_clk),
|
||||
.rst_n_i (s_rst_n),
|
||||
.global_status_i (32'd0),
|
||||
.role_status_i (32'd0),
|
||||
.phy_status_i (32'd0),
|
||||
.frame_i (32'd0),
|
||||
.device_status_i (32'd0),
|
||||
.setup_packet_i (64'd0),
|
||||
.endpoint_pending_in_i (32'd0),
|
||||
.endpoint_pending_out_i (32'd0),
|
||||
.endpoint_complete_in_i (32'd0),
|
||||
.endpoint_complete_out_i (32'd0),
|
||||
.endpoint_status_i (s_endpoint_status),
|
||||
.endpoint_bytes_in_i (s_endpoint_bytes_in),
|
||||
.endpoint_bytes_out_i (s_endpoint_bytes_out),
|
||||
.host_status_i (32'd0),
|
||||
.port_status_i (32'd0),
|
||||
.schedule_status_i (32'd0),
|
||||
.channel_status_i (s_channel_status),
|
||||
.channel_bytes_i (s_channel_bytes),
|
||||
.ram_status_i (32'd0),
|
||||
.ecc_status_i (32'd0),
|
||||
.ecc_corrected_count_i (32'd0),
|
||||
.ecc_uncorrectable_count_i (32'd0),
|
||||
.debug_status_i (32'd0),
|
||||
.perf_tx_bytes_i (32'd0),
|
||||
.perf_rx_bytes_i (32'd0),
|
||||
.perf_packets_i (32'd0),
|
||||
.perf_retries_i (32'd0),
|
||||
.perf_axi_stall_i (32'd0),
|
||||
.perf_ram_stall_i (32'd0),
|
||||
.perf_irq_count_i (32'd0),
|
||||
.irq_event_i (s_irq_event),
|
||||
.error_capture_i (1'b0),
|
||||
.error_status_i (32'd0),
|
||||
.error_code_i (32'd0),
|
||||
.error_info_i (32'd0),
|
||||
.error_desc_addr_i (32'd0),
|
||||
.error_buffer_addr_i (32'd0),
|
||||
.viewport_resp_valid_i (1'b0),
|
||||
.viewport_read_data_i (8'd0),
|
||||
.viewport_error_i (1'b0),
|
||||
.enable_o (s_enable),
|
||||
.soft_reset_o (),
|
||||
.abort_o (),
|
||||
.force_role_o (s_force_role),
|
||||
.auto_role_o (s_auto_role),
|
||||
.phy_reset_n_o (),
|
||||
.phy_suspend_o (),
|
||||
.remote_wake_o (),
|
||||
.timeout_o (),
|
||||
.test_ctrl_o (),
|
||||
.perf_clear_o (),
|
||||
.device_addr_o (),
|
||||
.device_ctrl_o (),
|
||||
.endpoint_cfg_o (s_endpoint_cfg),
|
||||
.endpoint_ram_in_o (s_endpoint_ram_in),
|
||||
.endpoint_ram_out_o (s_endpoint_ram_out),
|
||||
.endpoint_desc_in_o (s_endpoint_desc_in),
|
||||
.endpoint_desc_out_o (s_endpoint_desc_out),
|
||||
.endpoint_command_o (s_endpoint_command),
|
||||
.endpoint_complete_clear_in_o (s_endpoint_complete_clear_in),
|
||||
.endpoint_complete_clear_out_o(s_endpoint_complete_clear_out),
|
||||
.host_ctrl_o (),
|
||||
.port_ctrl_o (),
|
||||
.schedule_ctrl_o (),
|
||||
.channel_cfg_o (s_channel_cfg),
|
||||
.channel_target_o (s_channel_target),
|
||||
.channel_interval_o (s_channel_interval),
|
||||
.channel_ram_o (s_channel_ram),
|
||||
.channel_desc_o (s_channel_desc),
|
||||
.channel_command_o (s_channel_command),
|
||||
.ram_ctrl_o (),
|
||||
.ram_bist_start_o (),
|
||||
.viewport_valid_o (),
|
||||
.viewport_write_o (),
|
||||
.viewport_addr_o (),
|
||||
.viewport_write_data_o (),
|
||||
.irq_o (s_irq),
|
||||
.apb4 (u_apb4)
|
||||
);
|
||||
|
||||
always #5 s_clk = ~s_clk;
|
||||
|
||||
always_ff @(posedge s_clk or negedge s_rst_n) begin
|
||||
if (!s_rst_n) begin
|
||||
s_endpoint_clear_seen <= 1'b0;
|
||||
end else if ((s_endpoint_complete_clear_in == 32'h0000_00A5) &&
|
||||
(s_endpoint_complete_clear_out == 32'd0)) begin
|
||||
s_endpoint_clear_seen <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
task automatic apb_write(input logic [31:0] addr_i, input logic [31:0] data_i,
|
||||
input logic expected_error_i);
|
||||
begin
|
||||
@(negedge s_clk);
|
||||
u_apb4.paddr = addr_i;
|
||||
u_apb4.pwdata = data_i;
|
||||
u_apb4.pstrb = 4'hF;
|
||||
u_apb4.pwrite = 1'b1;
|
||||
u_apb4.psel = 1'b1;
|
||||
u_apb4.penable = 1'b0;
|
||||
@(negedge s_clk);
|
||||
u_apb4.penable = 1'b1;
|
||||
@(posedge s_clk);
|
||||
#1;
|
||||
if (!u_apb4.pready || (u_apb4.pslverr != expected_error_i)) begin
|
||||
$fatal(1, "USB2 APB write failure addr=%h ready=%b error=%b", addr_i, u_apb4.pready,
|
||||
u_apb4.pslverr);
|
||||
end
|
||||
@(negedge s_clk);
|
||||
u_apb4.psel = 1'b0;
|
||||
u_apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_read(input logic [31:0] addr_i, output logic [31:0] data_o,
|
||||
input logic expected_error_i);
|
||||
begin
|
||||
@(negedge s_clk);
|
||||
u_apb4.paddr = addr_i;
|
||||
u_apb4.pwdata = 32'd0;
|
||||
u_apb4.pstrb = 4'd0;
|
||||
u_apb4.pwrite = 1'b0;
|
||||
u_apb4.psel = 1'b1;
|
||||
u_apb4.penable = 1'b0;
|
||||
@(negedge s_clk);
|
||||
u_apb4.penable = 1'b1;
|
||||
@(posedge s_clk);
|
||||
#1;
|
||||
if (!u_apb4.pready || (u_apb4.pslverr != expected_error_i)) begin
|
||||
$fatal(1, "USB2 APB read failure addr=%h ready=%b error=%b", addr_i, u_apb4.pready,
|
||||
u_apb4.pslverr);
|
||||
end
|
||||
data_o = u_apb4.prdata;
|
||||
@(negedge s_clk);
|
||||
u_apb4.psel = 1'b0;
|
||||
u_apb4.penable = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
logic [31:0] value;
|
||||
s_clk = 1'b0;
|
||||
s_rst_n = 1'b0;
|
||||
s_irq_event = 16'd0;
|
||||
u_apb4.paddr = 32'd0;
|
||||
u_apb4.pprot = 3'd0;
|
||||
u_apb4.psel = 1'b0;
|
||||
u_apb4.penable = 1'b0;
|
||||
u_apb4.pwrite = 1'b0;
|
||||
u_apb4.pwdata = 32'd0;
|
||||
u_apb4.pstrb = 4'd0;
|
||||
for (int endpoint = 0; endpoint < 8; endpoint++) begin
|
||||
s_endpoint_status[endpoint] = 32'd0;
|
||||
s_endpoint_bytes_in[endpoint] = 32'd0;
|
||||
s_endpoint_bytes_out[endpoint] = 32'd0;
|
||||
end
|
||||
for (int channel = 0; channel < 16; channel++) begin
|
||||
s_channel_status[channel] = 32'd0;
|
||||
s_channel_bytes[channel] = 32'd0;
|
||||
end
|
||||
repeat (3) @(posedge s_clk);
|
||||
s_rst_n = 1'b1;
|
||||
|
||||
apb_read(UsbBase + `APB4_USB2__IP_ID, value, 1'b0);
|
||||
if (value != 32'h5553_4232) $fatal(1, "USB2 IP ID mismatch");
|
||||
apb_read(UsbBase + `APB4_USB2__CAPABILITY0, value, 1'b0);
|
||||
if ((value[19:16] != 4'd7) || (value[27:24] != 4'd15)) begin
|
||||
$fatal(1, "USB2 capability scale mismatch: %h", value);
|
||||
end
|
||||
apb_write(UsbBase + `APB4_USB2__ROLE_CTRL, 32'h0000_0006, 1'b0);
|
||||
if ((s_force_role != 2'd2) || !s_auto_role) $fatal(1, "USB2 role control mismatch");
|
||||
apb_write(
|
||||
UsbBase + `APB4_USB2__ENDPOINT_BASE + `APB4_USB2__ENDPOINT_STRIDE +
|
||||
`APB4_USB2__ENDPOINT_CFG,
|
||||
32'h0000_020D, 1'b0);
|
||||
apb_read(
|
||||
UsbBase + `APB4_USB2__ENDPOINT_BASE + `APB4_USB2__ENDPOINT_STRIDE +
|
||||
`APB4_USB2__ENDPOINT_CFG,
|
||||
value, 1'b0);
|
||||
if (value != 32'h0000_020D) $fatal(1, "USB2 endpoint configuration mismatch");
|
||||
apb_write(UsbBase + `APB4_USB2__ENDPOINT_COMPLETE_IN, 32'h0000_00A5, 1'b0);
|
||||
if (!s_endpoint_clear_seen) $fatal(1, "USB2 endpoint completion W1C mismatch");
|
||||
apb_write(UsbBase + `APB4_USB2__IRQ_ENABLE, 32'h0000_0020, 1'b0);
|
||||
apb_write(UsbBase + `APB4_USB2__IRQ_TEST, 32'h0000_0020, 1'b0);
|
||||
apb_write(UsbBase + `APB4_USB2__GLOBAL_CTRL, 32'h0000_0009, 1'b0);
|
||||
if (!s_enable || !s_irq) $fatal(1, "USB2 interrupt test failed");
|
||||
apb_write(UsbBase + `APB4_USB2__ROLE_CTRL, 32'h0000_0001, 1'b1);
|
||||
apb_read(UsbBase + 32'h0000_0001, value, 1'b1);
|
||||
$display("USB2 APB register contract passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,180 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module usb2_scheduler_tb;
|
||||
logic s_clk = 1'b0;
|
||||
logic s_rst_n = 1'b0;
|
||||
logic s_reinitialize = 1'b0;
|
||||
logic [13:0] s_frame = 14'd0;
|
||||
logic [ 7:0][31:0] s_endpoint_cfg = '0;
|
||||
logic [ 7:0][31:0] s_endpoint_ram_in = '0;
|
||||
logic [ 7:0][31:0] s_endpoint_ram_out = '0;
|
||||
logic [ 7:0][31:0] s_endpoint_desc_in = '0;
|
||||
logic [ 7:0][31:0] s_endpoint_desc_out = '0;
|
||||
logic [ 7:0][31:0] s_endpoint_command = '0;
|
||||
logic [15:0][31:0] s_channel_cfg = '0;
|
||||
logic [15:0][31:0] s_channel_target = '0;
|
||||
logic [15:0][31:0] s_channel_interval = '0;
|
||||
logic [15:0][31:0] s_channel_ram = '0;
|
||||
logic [15:0][31:0] s_channel_desc = '0;
|
||||
logic [15:0][31:0] s_channel_command = '0;
|
||||
logic s_work_valid;
|
||||
logic [63:0] s_work_data;
|
||||
logic s_result_valid = 1'b0;
|
||||
logic [63:0] s_result_data = '0;
|
||||
logic s_fill_ready = 1'b0;
|
||||
logic s_dma_start;
|
||||
logic s_dma_done = 1'b0;
|
||||
logic s_buffer_event_valid = 1'b0;
|
||||
logic [ 2:0] s_buffer_event_data = 3'd0;
|
||||
logic s_busy;
|
||||
logic [ 3:0] s_seen_index [0:3];
|
||||
integer s_work_count = 0;
|
||||
|
||||
always #5 s_clk = ~s_clk;
|
||||
|
||||
always_ff @(posedge s_clk) begin
|
||||
s_dma_done <= 1'b0;
|
||||
s_buffer_event_valid <= 1'b0;
|
||||
s_result_valid <= 1'b0;
|
||||
if (s_dma_start) begin
|
||||
s_dma_done <= 1'b1;
|
||||
s_buffer_event_valid <= 1'b1;
|
||||
end
|
||||
if (s_work_valid) begin
|
||||
s_seen_index[s_work_count] <= s_work_data[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
|
||||
s_work_count <= s_work_count + 1;
|
||||
s_result_data <= 64'd0;
|
||||
s_result_data[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] <= usb2_pkg::Usb2RoleHost;
|
||||
s_result_data[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] <=
|
||||
s_work_data[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
|
||||
s_result_data[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] <= 15'd4;
|
||||
s_result_valid <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
usb2_scheduler u_scheduler (
|
||||
.clk_i (s_clk),
|
||||
.rst_n_i (s_rst_n),
|
||||
.enable_i (1'b1),
|
||||
.abort_i (1'b0),
|
||||
.reinitialize_i (s_reinitialize),
|
||||
.perf_clear_i (1'b0),
|
||||
.high_speed_i (1'b1),
|
||||
.frame_i (s_frame),
|
||||
.active_role_i (usb2_pkg::Usb2RoleHost),
|
||||
.endpoint_cfg_i (s_endpoint_cfg),
|
||||
.endpoint_ram_in_i (s_endpoint_ram_in),
|
||||
.endpoint_ram_out_i (s_endpoint_ram_out),
|
||||
.endpoint_desc_in_i (s_endpoint_desc_in),
|
||||
.endpoint_desc_out_i (s_endpoint_desc_out),
|
||||
.endpoint_command_i (s_endpoint_command),
|
||||
.endpoint_complete_clear_in_i (32'd0),
|
||||
.endpoint_complete_clear_out_i(32'd0),
|
||||
.channel_cfg_i (s_channel_cfg),
|
||||
.channel_target_i (s_channel_target),
|
||||
.channel_interval_i (s_channel_interval),
|
||||
.channel_ram_i (s_channel_ram),
|
||||
.channel_desc_i (s_channel_desc),
|
||||
.channel_command_i (s_channel_command),
|
||||
.work_valid_o (s_work_valid),
|
||||
.work_ready_i (1'b1),
|
||||
.work_data_o (s_work_data),
|
||||
.result_valid_i (s_result_valid),
|
||||
.result_ready_o (),
|
||||
.result_data_i (s_result_data),
|
||||
.fill_cmd_valid_o (),
|
||||
.fill_cmd_ready_i (s_fill_ready),
|
||||
.fill_cmd_data_o (),
|
||||
.drain_cmd_valid_o (),
|
||||
.drain_cmd_ready_i (1'b1),
|
||||
.drain_cmd_data_o (),
|
||||
.buffer_event_valid_i (s_buffer_event_valid),
|
||||
.buffer_event_ready_o (),
|
||||
.buffer_event_data_i (s_buffer_event_data),
|
||||
.dma_start_o (s_dma_start),
|
||||
.dma_abort_o (),
|
||||
.dma_memory_to_packet_o (),
|
||||
.dma_desc_base_o (),
|
||||
.dma_transfer_bytes_o (),
|
||||
.dma_busy_i (1'b0),
|
||||
.dma_done_i (s_dma_done),
|
||||
.dma_error_i (1'b0),
|
||||
.dma_error_code_i (8'd0),
|
||||
.dma_current_desc_i (32'd0),
|
||||
.dma_bytes_done_i (32'd4),
|
||||
.retry_i (1'b0),
|
||||
.endpoint_pending_in_o (),
|
||||
.endpoint_pending_out_o (),
|
||||
.endpoint_complete_in_o (),
|
||||
.endpoint_complete_out_o (),
|
||||
.endpoint_status_o (),
|
||||
.endpoint_bytes_in_o (),
|
||||
.endpoint_bytes_out_o (),
|
||||
.channel_status_o (),
|
||||
.channel_bytes_o (),
|
||||
.irq_event_o (),
|
||||
.error_capture_o (),
|
||||
.error_status_o (),
|
||||
.error_code_o (),
|
||||
.error_info_o (),
|
||||
.error_desc_addr_o (),
|
||||
.error_buffer_addr_o (),
|
||||
.perf_tx_bytes_o (),
|
||||
.perf_rx_bytes_o (),
|
||||
.perf_packets_o (),
|
||||
.perf_retries_o (),
|
||||
.perf_irq_count_o (),
|
||||
.busy_o (s_busy)
|
||||
);
|
||||
|
||||
task automatic issue_channel(input int channel_i);
|
||||
begin
|
||||
@(negedge s_clk);
|
||||
s_channel_command[channel_i] = 32'd1;
|
||||
@(negedge s_clk);
|
||||
s_channel_command[channel_i] = 32'd0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
for (int channel = 0; channel < 16; channel++) begin
|
||||
s_channel_cfg[channel] = 32'h0040_0009;
|
||||
s_channel_ram[channel] = 32'h0004_0000;
|
||||
s_channel_desc[channel] = 32'h4000_0000 + (32'(channel) << 5);
|
||||
end
|
||||
s_channel_cfg[3][3:2] = usb2_pkg::Usb2TransferInterrupt;
|
||||
s_channel_interval[3] = 32'd8;
|
||||
|
||||
repeat (4) @(posedge s_clk);
|
||||
s_rst_n = 1'b1;
|
||||
|
||||
issue_channel(1);
|
||||
wait (s_busy);
|
||||
issue_channel(2);
|
||||
repeat (2) @(posedge s_clk);
|
||||
s_fill_ready = 1'b1;
|
||||
wait (s_work_count == 2);
|
||||
wait (!s_busy);
|
||||
if ((s_seen_index[0] != 4'd1) || (s_seen_index[1] != 4'd2)) begin
|
||||
$fatal(1, "USB2 busy command queue dropped or reordered work");
|
||||
end
|
||||
|
||||
issue_channel(3);
|
||||
wait (s_work_count == 3);
|
||||
wait (!s_busy);
|
||||
s_frame = 14'd1;
|
||||
issue_channel(3);
|
||||
repeat (8) @(posedge s_clk);
|
||||
if (s_work_count != 3) $fatal(1, "USB2 periodic command ran before its interval");
|
||||
s_frame = 14'd8;
|
||||
wait (s_work_count == 4);
|
||||
if (s_seen_index[3] != 4'd3) $fatal(1, "USB2 periodic command was not retained");
|
||||
|
||||
@(negedge s_clk);
|
||||
s_reinitialize = 1'b1;
|
||||
@(negedge s_clk);
|
||||
s_reinitialize = 1'b0;
|
||||
$display("USB2 scheduler queue and interval passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||
// retroSoC is licensed under Mulan PSL v2.
|
||||
|
||||
module usb2_sie_tb;
|
||||
logic s_clk;
|
||||
logic s_rst_n;
|
||||
logic s_rx_start;
|
||||
logic s_rx_valid;
|
||||
logic [ 7:0] s_rx_data;
|
||||
logic s_rx_end;
|
||||
logic s_rx_error;
|
||||
logic s_payload_valid;
|
||||
logic [ 7:0] s_payload_data;
|
||||
logic s_packet_done;
|
||||
logic s_packet_good;
|
||||
logic [ 3:0] s_packet_pid;
|
||||
logic [ 6:0] s_token_addr;
|
||||
logic [ 3:0] s_token_endpoint;
|
||||
logic [10:0] s_payload_length;
|
||||
logic s_crc_error;
|
||||
|
||||
logic s_request_valid;
|
||||
logic s_request_ready;
|
||||
logic [ 3:0] s_request_pid;
|
||||
logic [10:0] s_request_token;
|
||||
logic [10:0] s_request_length;
|
||||
logic s_link_start_valid;
|
||||
logic s_link_start_ready;
|
||||
logic [ 3:0] s_link_pid;
|
||||
logic s_link_has_data;
|
||||
logic s_link_data_valid;
|
||||
logic s_link_data_ready;
|
||||
logic [ 7:0] s_link_data;
|
||||
logic s_link_data_last;
|
||||
logic s_link_done;
|
||||
logic s_tx_done;
|
||||
logic [ 7:0] s_payload_seen [0:1];
|
||||
logic [ 1:0] s_payload_seen_count;
|
||||
logic [ 7:0] s_tx_seen [0:1];
|
||||
logic [ 1:0] s_tx_seen_count;
|
||||
|
||||
usb2_sie_rx u_rx (
|
||||
.clk_i (s_clk),
|
||||
.rst_n_i (s_rst_n),
|
||||
.rx_start_i (s_rx_start),
|
||||
.rx_valid_i (s_rx_valid),
|
||||
.rx_data_i (s_rx_data),
|
||||
.rx_end_i (s_rx_end),
|
||||
.rx_error_i (s_rx_error),
|
||||
.payload_valid_o (s_payload_valid),
|
||||
.payload_data_o (s_payload_data),
|
||||
.packet_done_o (s_packet_done),
|
||||
.packet_good_o (s_packet_good),
|
||||
.packet_pid_o (s_packet_pid),
|
||||
.token_addr_o (s_token_addr),
|
||||
.token_endpoint_o(s_token_endpoint),
|
||||
.frame_number_o (),
|
||||
.payload_length_o(s_payload_length),
|
||||
.pid_error_o (),
|
||||
.crc_error_o (s_crc_error),
|
||||
.framing_error_o ()
|
||||
);
|
||||
|
||||
usb2_sie_tx u_tx (
|
||||
.clk_i (s_clk),
|
||||
.rst_n_i (s_rst_n),
|
||||
.request_valid_i (s_request_valid),
|
||||
.request_ready_o (s_request_ready),
|
||||
.request_pid_i (s_request_pid),
|
||||
.request_token_i (s_request_token),
|
||||
.request_length_i (s_request_length),
|
||||
.payload_valid_i (1'b0),
|
||||
.payload_ready_o (),
|
||||
.payload_data_i (8'd0),
|
||||
.link_start_valid_o(s_link_start_valid),
|
||||
.link_start_ready_i(s_link_start_ready),
|
||||
.link_pid_o (s_link_pid),
|
||||
.link_has_data_o (s_link_has_data),
|
||||
.link_data_valid_o (s_link_data_valid),
|
||||
.link_data_ready_i (s_link_data_ready),
|
||||
.link_data_o (s_link_data),
|
||||
.link_data_last_o (s_link_data_last),
|
||||
.link_done_i (s_link_done),
|
||||
.link_error_i (1'b0),
|
||||
.busy_o (),
|
||||
.done_o (s_tx_done),
|
||||
.error_o ()
|
||||
);
|
||||
|
||||
always #5 s_clk = ~s_clk;
|
||||
|
||||
always_ff @(posedge s_clk) begin
|
||||
if (s_payload_valid) begin
|
||||
s_payload_seen[s_payload_seen_count] <= s_payload_data;
|
||||
s_payload_seen_count <= s_payload_seen_count + 1'b1;
|
||||
end
|
||||
if (s_link_data_valid && s_link_data_ready) begin
|
||||
s_tx_seen[s_tx_seen_count] <= s_link_data;
|
||||
s_tx_seen_count <= s_tx_seen_count + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
task automatic rx_begin;
|
||||
begin
|
||||
@(negedge s_clk);
|
||||
s_rx_start = 1'b1;
|
||||
@(negedge s_clk);
|
||||
s_rx_start = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic rx_byte(input logic [7:0] data_i);
|
||||
begin
|
||||
s_rx_data = data_i;
|
||||
s_rx_valid = 1'b1;
|
||||
@(negedge s_clk);
|
||||
s_rx_valid = 1'b0;
|
||||
@(negedge s_clk);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic rx_finish;
|
||||
begin
|
||||
s_rx_end = 1'b1;
|
||||
@(posedge s_clk);
|
||||
#1;
|
||||
s_rx_end = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
logic [15:0] data_crc;
|
||||
s_clk = 1'b0;
|
||||
s_rst_n = 1'b0;
|
||||
s_rx_start = 1'b0;
|
||||
s_rx_valid = 1'b0;
|
||||
s_rx_data = 8'd0;
|
||||
s_rx_end = 1'b0;
|
||||
s_rx_error = 1'b0;
|
||||
s_request_valid = 1'b0;
|
||||
s_request_pid = usb2_pkg::Usb2PidOut;
|
||||
s_request_token = 11'd0;
|
||||
s_request_length = 11'd0;
|
||||
s_link_start_ready = 1'b1;
|
||||
s_link_data_ready = 1'b1;
|
||||
s_link_done = 1'b0;
|
||||
s_payload_seen_count = '0;
|
||||
s_tx_seen_count = '0;
|
||||
repeat (2) @(negedge s_clk);
|
||||
s_rst_n = 1'b1;
|
||||
|
||||
rx_begin();
|
||||
rx_byte(usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidOut));
|
||||
rx_byte(8'h01);
|
||||
rx_byte(8'hE8);
|
||||
rx_finish();
|
||||
if (!s_packet_done || !s_packet_good || (s_packet_pid != usb2_pkg::Usb2PidOut) ||
|
||||
(s_token_addr != 7'd1) || (s_token_endpoint != 4'd0)) begin
|
||||
$fatal(1, "USB2 token packet decode failed");
|
||||
end
|
||||
|
||||
data_crc = usb2_pkg::usb2_crc16_byte(16'hFFFF, 8'h41);
|
||||
data_crc = usb2_pkg::usb2_crc16_byte(data_crc, 8'h42);
|
||||
data_crc = usb2_pkg::usb2_crc16_finish(data_crc);
|
||||
rx_begin();
|
||||
rx_byte(usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidData0));
|
||||
rx_byte(8'h41);
|
||||
rx_byte(8'h42);
|
||||
rx_byte(data_crc[7:0]);
|
||||
rx_byte(data_crc[15:8]);
|
||||
rx_finish();
|
||||
if (!s_packet_done || !s_packet_good || (s_payload_length != 11'd2) ||
|
||||
(s_payload_seen_count != 2'd2) || (s_payload_seen[0] != 8'h41) ||
|
||||
(s_payload_seen[1] != 8'h42)) begin
|
||||
$fatal(1, "USB2 data packet decode failed");
|
||||
end
|
||||
|
||||
rx_begin();
|
||||
rx_byte(usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidData0));
|
||||
rx_byte(8'h01);
|
||||
rx_byte(8'h00);
|
||||
rx_finish();
|
||||
if (!s_packet_done || s_packet_good || !s_crc_error) begin
|
||||
$fatal(1, "USB2 bad data CRC accepted");
|
||||
end
|
||||
|
||||
@(negedge s_clk);
|
||||
s_request_pid = usb2_pkg::Usb2PidOut;
|
||||
s_request_token = 11'd1;
|
||||
s_request_valid = 1'b1;
|
||||
@(negedge s_clk);
|
||||
s_request_valid = 1'b0;
|
||||
wait (s_link_data_last);
|
||||
@(negedge s_clk);
|
||||
s_link_done = 1'b1;
|
||||
@(posedge s_clk);
|
||||
@(posedge s_clk);
|
||||
#1;
|
||||
s_link_done = 1'b0;
|
||||
if (!s_tx_done || (s_link_pid != usb2_pkg::Usb2PidOut) || !s_link_has_data ||
|
||||
(s_tx_seen_count != 2'd2) || (s_tx_seen[0] != 8'h01) || (s_tx_seen[1] != 8'hE8)) begin
|
||||
$fatal(1, "USB2 token packet encode failed");
|
||||
end
|
||||
|
||||
$display("USB2 SIE packet encode/decode passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,277 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module usb2_transaction_engine_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
usb2_pkg::usb2_role_e active_role_i = usb2_pkg::Usb2RoleHost;
|
||||
logic [ 7:0][31:0] endpoint_cfg_i = '0;
|
||||
logic work_valid_i = 1'b0;
|
||||
logic work_ready_o;
|
||||
logic [63:0] work_data_i = '0;
|
||||
logic result_valid_o;
|
||||
logic [63:0] result_data_o;
|
||||
logic setup_valid_o;
|
||||
logic [63:0] setup_data_o;
|
||||
logic tx_request_valid_o;
|
||||
logic tx_request_ready_i = 1'b1;
|
||||
logic [ 3:0] tx_request_pid_o;
|
||||
logic [10:0] tx_request_token_o;
|
||||
logic [10:0] tx_request_length_o;
|
||||
logic tx_payload_valid_o;
|
||||
logic tx_payload_ready_i = 1'b1;
|
||||
logic [ 7:0] tx_payload_data_o;
|
||||
logic tx_done_i = 1'b0;
|
||||
logic tx_error_i = 1'b0;
|
||||
logic rx_payload_valid_i = 1'b0;
|
||||
logic [ 7:0] rx_payload_data_i = '0;
|
||||
logic rx_packet_done_i = 1'b0;
|
||||
logic rx_packet_good_i = 1'b0;
|
||||
logic [ 3:0] rx_packet_pid_i = '0;
|
||||
logic store_rx_start_valid;
|
||||
logic store_rx_start_ready;
|
||||
logic [11:0] store_rx_base;
|
||||
logic [14:0] store_rx_limit;
|
||||
logic store_rx_valid;
|
||||
logic store_rx_ready;
|
||||
logic [ 7:0] store_rx_data;
|
||||
logic store_rx_commit;
|
||||
logic store_rx_cancel;
|
||||
logic store_rx_done;
|
||||
logic [14:0] store_rx_bytes;
|
||||
logic store_rx_overflow;
|
||||
logic store_tx_start_valid;
|
||||
logic store_tx_start_ready;
|
||||
logic [11:0] store_tx_base;
|
||||
logic [14:0] store_tx_bytes;
|
||||
logic store_tx_valid;
|
||||
logic store_tx_ready;
|
||||
logic [ 7:0] store_tx_data;
|
||||
logic store_tx_done;
|
||||
logic fill_start_valid_i = 1'b0;
|
||||
logic fill_start_ready_o;
|
||||
logic fill_valid_i = 1'b0;
|
||||
logic fill_ready_o;
|
||||
logic [31:0] fill_data_i = '0;
|
||||
logic [ 3:0] fill_strb_i = '0;
|
||||
logic fill_last_i = 1'b0;
|
||||
logic fill_done_o;
|
||||
logic fill_error_o;
|
||||
logic busy_o;
|
||||
logic retry_o;
|
||||
logic protocol_error_o;
|
||||
logic pending_tx_q = 1'b0;
|
||||
logic pending_data_q = 1'b0;
|
||||
logic data_consumed_q = 1'b0;
|
||||
integer pid_count;
|
||||
logic [ 3:0] observed_pid [0:3];
|
||||
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
always_ff @(posedge clk_i) begin
|
||||
tx_done_i <= 1'b0;
|
||||
rx_packet_done_i <= 1'b0;
|
||||
rx_packet_good_i <= 1'b0;
|
||||
if (!rst_n_i) begin
|
||||
pending_tx_q <= 1'b0;
|
||||
pending_data_q <= 1'b0;
|
||||
data_consumed_q <= 1'b0;
|
||||
pid_count <= 0;
|
||||
end else begin
|
||||
if (tx_request_valid_o && tx_request_ready_i) begin
|
||||
observed_pid[pid_count] <= tx_request_pid_o;
|
||||
pid_count <= pid_count + 1;
|
||||
if (usb2_pkg::usb2_pid_is_data(tx_request_pid_o)) begin
|
||||
pending_data_q <= 1'b1;
|
||||
end else begin
|
||||
pending_tx_q <= 1'b1;
|
||||
end
|
||||
end
|
||||
if (pending_tx_q) begin
|
||||
tx_done_i <= 1'b1;
|
||||
pending_tx_q <= 1'b0;
|
||||
end
|
||||
if (pending_data_q && tx_payload_valid_o && tx_payload_ready_i &&
|
||||
(tx_payload_data_o == 8'h55)) begin
|
||||
data_consumed_q <= 1'b1;
|
||||
end
|
||||
if (pending_data_q && data_consumed_q) begin
|
||||
tx_done_i <= 1'b1;
|
||||
pending_data_q <= 1'b0;
|
||||
data_consumed_q <= 1'b0;
|
||||
end
|
||||
if ((pid_count == 2) && !pending_data_q && !pending_tx_q && tx_done_i) begin
|
||||
rx_packet_pid_i <= usb2_pkg::Usb2PidAck;
|
||||
rx_packet_done_i <= 1'b1;
|
||||
rx_packet_good_i <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
usb2_transaction_engine u_engine (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.active_role_i (active_role_i),
|
||||
.device_addr_i (7'd0),
|
||||
.endpoint_cfg_i (endpoint_cfg_i),
|
||||
.timeout_i (32'd1000),
|
||||
.work_valid_i (work_valid_i),
|
||||
.work_ready_o (work_ready_o),
|
||||
.work_data_i (work_data_i),
|
||||
.result_valid_o (result_valid_o),
|
||||
.result_ready_i (1'b1),
|
||||
.result_data_o (result_data_o),
|
||||
.setup_valid_o (setup_valid_o),
|
||||
.setup_ready_i (1'b1),
|
||||
.setup_data_o (setup_data_o),
|
||||
.tx_request_valid_o (tx_request_valid_o),
|
||||
.tx_request_ready_i (tx_request_ready_i),
|
||||
.tx_request_pid_o (tx_request_pid_o),
|
||||
.tx_request_token_o (tx_request_token_o),
|
||||
.tx_request_length_o (tx_request_length_o),
|
||||
.tx_payload_valid_o (tx_payload_valid_o),
|
||||
.tx_payload_ready_i (tx_payload_ready_i),
|
||||
.tx_payload_data_o (tx_payload_data_o),
|
||||
.tx_done_i (tx_done_i),
|
||||
.tx_error_i (tx_error_i),
|
||||
.rx_payload_valid_i (rx_payload_valid_i),
|
||||
.rx_payload_data_i (rx_payload_data_i),
|
||||
.rx_packet_done_i (rx_packet_done_i),
|
||||
.rx_packet_good_i (rx_packet_good_i),
|
||||
.rx_packet_pid_i (rx_packet_pid_i),
|
||||
.rx_token_addr_i (7'd0),
|
||||
.rx_token_endpoint_i (4'd0),
|
||||
.rx_payload_length_i (11'd0),
|
||||
.store_rx_start_valid_o(store_rx_start_valid),
|
||||
.store_rx_start_ready_i(store_rx_start_ready),
|
||||
.store_rx_base_o (store_rx_base),
|
||||
.store_rx_limit_o (store_rx_limit),
|
||||
.store_rx_valid_o (store_rx_valid),
|
||||
.store_rx_ready_i (store_rx_ready),
|
||||
.store_rx_data_o (store_rx_data),
|
||||
.store_rx_commit_o (store_rx_commit),
|
||||
.store_rx_cancel_o (store_rx_cancel),
|
||||
.store_rx_done_i (store_rx_done),
|
||||
.store_rx_bytes_i (store_rx_bytes),
|
||||
.store_rx_overflow_i (store_rx_overflow),
|
||||
.store_tx_start_valid_o(store_tx_start_valid),
|
||||
.store_tx_start_ready_i(store_tx_start_ready),
|
||||
.store_tx_base_o (store_tx_base),
|
||||
.store_tx_bytes_o (store_tx_bytes),
|
||||
.store_tx_valid_i (store_tx_valid),
|
||||
.store_tx_ready_o (store_tx_ready),
|
||||
.store_tx_data_i (store_tx_data),
|
||||
.store_tx_done_i (store_tx_done),
|
||||
.busy_o (busy_o),
|
||||
.retry_o (retry_o),
|
||||
.protocol_error_o (protocol_error_o)
|
||||
);
|
||||
|
||||
usb2_packet_store u_store (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.fill_start_valid_i (fill_start_valid_i),
|
||||
.fill_start_ready_o (fill_start_ready_o),
|
||||
.fill_base_i (12'd4),
|
||||
.fill_bytes_i (15'd5),
|
||||
.fill_valid_i (fill_valid_i),
|
||||
.fill_ready_o (fill_ready_o),
|
||||
.fill_data_i (fill_data_i),
|
||||
.fill_strb_i (fill_strb_i),
|
||||
.fill_last_i (fill_last_i),
|
||||
.fill_done_o (fill_done_o),
|
||||
.fill_error_o (fill_error_o),
|
||||
.drain_start_valid_i(1'b0),
|
||||
.drain_start_ready_o(),
|
||||
.drain_base_i ('0),
|
||||
.drain_bytes_i ('0),
|
||||
.drain_valid_o (),
|
||||
.drain_ready_i (1'b0),
|
||||
.drain_data_o (),
|
||||
.drain_strb_o (),
|
||||
.drain_last_o (),
|
||||
.drain_done_o (),
|
||||
.rx_start_valid_i (store_rx_start_valid),
|
||||
.rx_start_ready_o (store_rx_start_ready),
|
||||
.rx_base_i (store_rx_base),
|
||||
.rx_limit_i (store_rx_limit),
|
||||
.rx_valid_i (store_rx_valid),
|
||||
.rx_ready_o (store_rx_ready),
|
||||
.rx_data_i (store_rx_data),
|
||||
.rx_commit_i (store_rx_commit),
|
||||
.rx_cancel_i (store_rx_cancel),
|
||||
.rx_done_o (store_rx_done),
|
||||
.rx_bytes_o (store_rx_bytes),
|
||||
.rx_overflow_o (store_rx_overflow),
|
||||
.tx_start_valid_i (store_tx_start_valid),
|
||||
.tx_start_ready_o (store_tx_start_ready),
|
||||
.tx_base_i (store_tx_base),
|
||||
.tx_bytes_i (store_tx_bytes),
|
||||
.tx_valid_o (store_tx_valid),
|
||||
.tx_ready_i (store_tx_ready),
|
||||
.tx_data_o (store_tx_data),
|
||||
.tx_done_o (store_tx_done),
|
||||
.busy_o (),
|
||||
.ecc_corrected_o (),
|
||||
.ecc_uncorrectable_o()
|
||||
);
|
||||
|
||||
initial begin
|
||||
repeat (4) @(posedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fill_start_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fill_start_valid_i = 1'b0;
|
||||
@(negedge clk_i);
|
||||
fill_data_i = 32'h4433_2211;
|
||||
fill_strb_i = 4'hF;
|
||||
fill_last_i = 1'b0;
|
||||
fill_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fill_data_i = 32'h0000_0055;
|
||||
fill_strb_i = 4'h1;
|
||||
fill_last_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
fill_valid_i = 1'b0;
|
||||
wait (fill_done_o);
|
||||
if (fill_error_o) $fatal(1, "protocol test prefill failed");
|
||||
|
||||
work_data_i = '0;
|
||||
work_data_i[usb2_pkg::USB2_WORK_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
|
||||
work_data_i[usb2_pkg::USB2_WORK_INDEX_LSB+:4] = 4'd3;
|
||||
work_data_i[usb2_pkg::USB2_WORK_DIRECTION_IN] = 1'b0;
|
||||
work_data_i[usb2_pkg::USB2_WORK_TYPE_LSB+:2] = usb2_pkg::Usb2TransferBulk;
|
||||
work_data_i[usb2_pkg::USB2_WORK_ADDRESS_LSB+:7] = 7'd5;
|
||||
work_data_i[usb2_pkg::USB2_WORK_ENDPOINT_LSB+:4] = 4'd2;
|
||||
work_data_i[usb2_pkg::USB2_WORK_RAM_BASE_LSB+:12] = 12'd4;
|
||||
work_data_i[usb2_pkg::USB2_WORK_LENGTH_LSB+:15] = 15'd5;
|
||||
@(negedge clk_i);
|
||||
work_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
work_valid_i = 1'b0;
|
||||
wait (result_valid_o);
|
||||
if ((result_data_o[usb2_pkg::USB2_RESULT_CODE_LSB+:4] != usb2_pkg::Usb2ResultSuccess) ||
|
||||
(result_data_o[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] != 4'd3) || (pid_count != 2) ||
|
||||
(observed_pid[0] != usb2_pkg::Usb2PidOut) ||
|
||||
(observed_pid[1] != usb2_pkg::Usb2PidData0) || retry_o || protocol_error_o) begin
|
||||
$fatal(1, "host OUT transaction failed result=%h pids=%h,%h", result_data_o, observed_pid[0],
|
||||
observed_pid[1]);
|
||||
end
|
||||
wait (work_ready_o);
|
||||
work_data_i[usb2_pkg::USB2_WORK_INDEX_LSB+:4] = 4'd4;
|
||||
work_data_i[usb2_pkg::USB2_WORK_TYPE_LSB+:2] = usb2_pkg::Usb2TransferIsochronous;
|
||||
@(negedge clk_i);
|
||||
work_valid_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
work_valid_i = 1'b0;
|
||||
wait (result_valid_o);
|
||||
if ((result_data_o[usb2_pkg::USB2_RESULT_CODE_LSB+:4] != usb2_pkg::Usb2ResultSuccess) ||
|
||||
(result_data_o[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] != 4'd4) || (pid_count != 4) ||
|
||||
(observed_pid[2] != usb2_pkg::Usb2PidOut) ||
|
||||
(observed_pid[3] != usb2_pkg::Usb2PidData0) || retry_o || protocol_error_o) begin
|
||||
$fatal(1, "host ISO OUT transaction waited for a handshake result=%h", result_data_o);
|
||||
end
|
||||
$display("USB2 transaction engine host OUT passed");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in new issue
Block a user