Files
retroSoC/tests/rtl/usb2_transaction_engine_tb.sv
T
maksyuki e219973c45 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.
2026-08-22 07:15:41 +08:00

278 lines
12 KiB
Systemverilog

`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