From ed897849332dd0d23ae67e162d464adfeed269d7 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Wed, 5 Apr 2023 23:43:12 +0100 Subject: [PATCH] [chip_tb] Integrate usbdpi into chip tb Initial integration of usbdpi model into chip dvsim/tb; usbdev_test is up and running Simple chip_sw_usbdev_dpi_vseq created but incomplete. Ensure usbdev buffer memory is zero-initialized in test bench (workaround for mmio32 routines reading X from memory). Add pull up resistors in diff receiver; driven by usbdev_aon_wake module in pinmux. Minimal usb20_if to connect usb20_usbdpi module for now; UVM agent may follow later. Introduce usb20_if to prevent conflicts with other use of MIO SENSE pin and conflict with weak pull up on USB_P DIO. Introduce enable signal for DPI model. Signed-off-by: Adrian Lees --- hw/dv/dpi/usbdpi/usbdpi.c | 27 +-- hw/dv/dpi/usbdpi/usbdpi.h | 1 + hw/dv/dpi/usbdpi/usbdpi.sv | 51 +++--- hw/dv/sv/usb20_agent/usb20_if.sv | 49 +++++- hw/dv/sv/usb20_agent/usb20_usbdpi.core | 21 +++ hw/dv/sv/usb20_agent/usb20_usbdpi.sv | 160 ++++++++++++++++++ .../lint/prim_generic_usb_diff_rx.waiver | 6 +- .../rtl/prim_generic_usb_diff_rx.sv | 10 +- hw/top_earlgrey/data/chip_testplan.hjson | 7 + hw/top_earlgrey/dv/chip_sim.core | 2 + hw/top_earlgrey/dv/chip_sim_cfg.hjson | 9 + hw/top_earlgrey/dv/env/chip_env.core | 1 + hw/top_earlgrey/dv/env/chip_env.sv | 7 + hw/top_earlgrey/dv/env/chip_env_cfg.sv | 1 + hw/top_earlgrey/dv/env/chip_env_pkg.sv | 2 + .../dv/env/seq_lib/chip_sw_usbdev_dpi_vseq.sv | 30 ++++ .../dv/env/seq_lib/chip_vseq_list.sv | 1 + hw/top_earlgrey/dv/tb/chip_hier_macros.svh | 1 + hw/top_earlgrey/dv/tb/tb.sv | 40 +++++ hw/top_earlgrey/dv/verilator/chip_sim_tb.sv | 1 + .../rtl/chip_englishbreakfast_verilator.sv | 1 + sw/device/lib/testing/pinmux_testutils.c | 24 +++ sw/device/tests/BUILD | 1 + sw/device/tests/usbdev_test.c | 5 - 24 files changed, 413 insertions(+), 45 deletions(-) create mode 100644 hw/dv/sv/usb20_agent/usb20_usbdpi.core create mode 100644 hw/dv/sv/usb20_agent/usb20_usbdpi.sv create mode 100644 hw/top_earlgrey/dv/env/seq_lib/chip_sw_usbdev_dpi_vseq.sv diff --git a/hw/dv/dpi/usbdpi/usbdpi.c b/hw/dv/dpi/usbdpi/usbdpi.c index 8c3c9c5b55123..b82c0445885ca 100644 --- a/hw/dv/dpi/usbdpi/usbdpi.c +++ b/hw/dv/dpi/usbdpi/usbdpi.c @@ -62,7 +62,8 @@ static void setDeviceConfiguration(usbdpi_ctx_t *ctx, uint8_t config); static void setTestStatus(usbdpi_ctx_t *ctx, uint32_t status, const char *msg); // Change DP and DN outputs from host -static uint32_t set_driving(usbdpi_ctx_t *ctx, uint32_t d2p, uint32_t newval); +static uint32_t set_driving(usbdpi_ctx_t *ctx, uint32_t d2p, uint32_t newval, + bool p2d_oe); // Try to send OUT transfer. Optionally expect Status packet (eg. ACK|NAK) in // response @@ -181,6 +182,7 @@ void usbdpi_device_to_host(void *ctx_void, const svBitVecVal *usb_d2p) { "drives\n", ctx->frame, ctx->tick_bits, st_states[ctx->state], hs_states[ctx->hostSt]); + // TODO: stop the test break; // Device to host transmission; collect the bits @@ -193,14 +195,13 @@ void usbdpi_device_to_host(void *ctx_void, const svBitVecVal *usb_d2p) { case ST_IDLE: // Nothing to do + ctx->state = ST_GET; break; default: assert(!"Invalid/unknown state"); break; } - - ctx->state = ST_GET; } else { if (ctx->state == ST_GET) { ctx->state = ST_IDLE; @@ -1046,8 +1047,9 @@ void testUnimplEp(usbdpi_ctx_t *ctx, uint8_t pid, uint8_t device, } } -// Change DP and DN outputs from host -uint32_t set_driving(usbdpi_ctx_t *ctx, uint32_t d2p, uint32_t newval) { +// Change host outputs +uint32_t set_driving(usbdpi_ctx_t *ctx, uint32_t d2p, uint32_t newval, + bool p2d_oe) { // Always maintain the current state of VBUS uint32_t driving = ctx->driving & P2D_SENSE; if (d2p & D2P_DNPU) { @@ -1064,6 +1066,10 @@ uint32_t set_driving(usbdpi_ctx_t *ctx, uint32_t d2p, uint32_t newval) { driving |= P2D_DN; } } + // Enable host output drivers? + if (p2d_oe) { + driving |= P2D_OE; + } return driving; } @@ -1286,7 +1292,7 @@ uint8_t usbdpi_host_to_device(void *ctx_void, const svBitVecVal *usb_d2p) { case ST_SYNC: dat = ((USB_SYNC & ctx->bit)) ? P2D_DP : P2D_DN; - ctx->driving = set_driving(ctx, d2p, dat); + ctx->driving = set_driving(ctx, d2p, dat, true); force_stat = 1; ctx->bit <<= 1; if (ctx->bit == 0x100) { @@ -1307,7 +1313,7 @@ uint8_t usbdpi_host_to_device(void *ctx_void, const svBitVecVal *usb_d2p) { ctx->linebits = (ctx->linebits << 1); } else if (ctx->byte >= sending->num_bytes) { ctx->state = ST_EOP; - ctx->driving = set_driving(ctx, d2p, 0); // SE0 + ctx->driving = set_driving(ctx, d2p, 0, true); // SE0 ctx->bit = 1; force_stat = 1; } else { @@ -1329,19 +1335,20 @@ uint8_t usbdpi_host_to_device(void *ctx_void, const svBitVecVal *usb_d2p) { } break; case ST_EOP0: - ctx->driving = set_driving(ctx, d2p, 0); // SE0 + ctx->driving = set_driving(ctx, d2p, 0, true); // SE0 ctx->state = ST_EOP; break; case ST_EOP: // SE0 SE0 J if (ctx->bit == 4) { - ctx->driving = set_driving(ctx, d2p, P2D_DP); // J + ctx->driving = set_driving(ctx, d2p, P2D_DP, true); // J } if (ctx->bit == 8) { usbdpi_transfer_t *sending = ctx->sending; assert(sending); // Stop driving: host pulldown to SE0 unless there is a pullup on DP - ctx->driving = set_driving(ctx, d2p, (d2p & D2P_PU) ? P2D_DP : 0); + ctx->driving = + set_driving(ctx, d2p, (d2p & D2P_PU) ? P2D_DP : 0, false); if (ctx->byte == sending->data_start) { ctx->bit = 1; ctx->state = ST_SYNC; diff --git a/hw/dv/dpi/usbdpi/usbdpi.h b/hw/dv/dpi/usbdpi/usbdpi.h index eb2be17868603..18fb351403bf9 100644 --- a/hw/dv/dpi/usbdpi/usbdpi.h +++ b/hw/dv/dpi/usbdpi/usbdpi.h @@ -81,6 +81,7 @@ typedef uint32_t svBitVecVal; #define P2D_DN 2 #define P2D_DP 4 #define P2D_D 8 +#define P2D_OE 0x10 /* Remember these go LSB first */ diff --git a/hw/dv/dpi/usbdpi/usbdpi.sv b/hw/dv/dpi/usbdpi/usbdpi.sv index 67309933d2705..8c02f4bbf6696 100644 --- a/hw/dv/dpi/usbdpi/usbdpi.sv +++ b/hw/dv/dpi/usbdpi/usbdpi.sv @@ -11,14 +11,17 @@ module usbdpi #( parameter string NAME = "usb0", - parameter LOG_LEVEL = 1 + parameter int LOG_LEVEL = 1 )( input logic clk_i, input logic rst_ni, input logic clk_48MHz_i, + input logic enable, + output logic dp_en_p2d, output logic dp_p2d, input logic dp_d2p, input logic dp_en_d2p, + output logic dn_en_p2d, output logic dn_p2d, input logic dn_d2p, input logic dn_en_d2p, @@ -52,7 +55,6 @@ module usbdpi #( initial begin ctx = usbdpi_create(NAME, LOG_LEVEL); - sense_p2d = 1'b0; end final begin @@ -146,7 +148,7 @@ module usbdpi #( // Test steps typedef enum bit [6:0] { - STEP_BUS_RESET = 0, + STEP_BUS_RESET = 7'h0, STEP_SET_DEVICE_ADDRESS, STEP_GET_DEVICE_DESCRIPTOR, STEP_GET_CONFIG_DESCRIPTOR, @@ -169,13 +171,13 @@ module usbdpi #( STEP_ENDPT_UNIMPL_IN, STEP_DEVICE_UK_SETUP, STEP_IDLE_START, - STEP_IDLE_END = STEP_IDLE_START + 4, + STEP_IDLE_END = STEP_IDLE_START + 7'h4, // usbdev_stream_test - STEP_STREAM_SERVICE = 'h20, + STEP_STREAM_SERVICE = 7'h20, // Disconnect the device and stop - STEP_BUS_DISCONNECT = 'h7f + STEP_BUS_DISCONNECT = 7'h7f } usbdpi_test_step_t; // Make usb_monitor diagnostic information viewable in waveforms @@ -212,22 +214,33 @@ module usbdpi #( assign d2p = {dp_d2p, dp_en_d2p, dn_d2p, dn_en_d2p, d_d2p, d_en_d2p, se0_d2p, tx_use_d_se0_d2p, pullupdp_d2p, pullupdn_d2p, rx_enable}; - always_ff @(posedge clk_48MHz_i) begin - if (!sense_p2d || pullup_detect) begin - automatic byte p2d = usbdpi_host_to_device(ctx, d2p); - d_last <= d_p2d; - dp_int <= p2d[2]; - dn_int <= p2d[1]; - sense_p2d <= p2d[0]; - unused_dummy <= |p2d[7:4]; - d2p_r <= d2p; - if (d2p_r != d2p) begin - usbdpi_device_to_host(ctx, d2p); - end - end else begin // if (pullup_detect) + always_ff @(posedge clk_48MHz_i or negedge rst_ni) begin + if (!rst_ni) begin + sense_p2d <= 1'b0; + dp_en_p2d <= 1'b0; + dn_en_p2d <= 1'b0; d_last <= 0; dp_int <= 0; dn_int <= 0; + end else if (enable) begin + if (!sense_p2d || pullup_detect) begin + automatic byte p2d = usbdpi_host_to_device(ctx, d2p); + d_last <= d_p2d; + dp_en_p2d <= p2d[4]; + dn_en_p2d <= p2d[4]; + dp_int <= p2d[2]; + dn_int <= p2d[1]; + sense_p2d <= p2d[0]; + unused_dummy <= |p2d[7:5]; + d2p_r <= d2p; + if (d2p_r != d2p) begin + usbdpi_device_to_host(ctx, d2p); + end + end else begin + d_last <= 0; + dp_int <= 0; + dn_int <= 0; + end end end diff --git a/hw/dv/sv/usb20_agent/usb20_if.sv b/hw/dv/sv/usb20_agent/usb20_if.sv index c201dc3d677d5..f0d7be153ea58 100644 --- a/hw/dv/sv/usb20_agent/usb20_if.sv +++ b/hw/dv/sv/usb20_agent/usb20_if.sv @@ -2,10 +2,53 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 -interface usb20_if (); +interface usb20_if ( + input clk_i, + input rst_ni, - // interface pins + output wire usb_vbus, + inout wire usb_p, + inout wire usb_n +); - // debug signals + // This interface presently serves just to connect/disconnect the USB DPI + // model to/from the ASIC pins. In time it may be extended to support a DV + // block-level USB 2.0 agent. + + // Clock and reset not presently required + wire unused_clk = clk_i; + wire unused_rst_n = rst_ni; + + // Nomenclature notes: + // dp (or p) and dn (or n) are the two signals of the differential USB + // d2p means device to DPI + // p2d means DPI to device + + // VBUS/SENSE output from DPI + wire usb_sense_p2d; + // DPI driver enables + wire usb_dp_en_p2d; + wire usb_dn_en_p2d; + // DPI driver outputs + wire usb_dp_p2d; + wire usb_dn_p2d; + + // Are our drivers connected? + bit connected = 0; + + // Enable/disable the output drivers + function automatic void enable_driver(bit enabled); + connected = enabled; + endfunction + + assign usb_vbus = connected ? usb_sense_p2d : 1'bZ; + + // Weak pull downs so that we can detect the presence of the device, and we + // also prevent Z triggering 'X assertions' in usbdev + assign (weak0, weak1) usb_p = connected ? 1'b0 : 1'bZ; + assign (weak0, weak1) usb_n = connected ? 1'b0 : 1'bZ; + // Tri-stated output drivers + assign (strong0, strong1) usb_p = (connected & usb_dp_en_p2d) ? usb_dp_p2d : 1'bZ; + assign (strong0, strong1) usb_n = (connected & usb_dn_en_p2d) ? usb_dn_p2d : 1'bZ; endinterface diff --git a/hw/dv/sv/usb20_agent/usb20_usbdpi.core b/hw/dv/sv/usb20_agent/usb20_usbdpi.core new file mode 100644 index 0000000000000..275b6efbad523 --- /dev/null +++ b/hw/dv/sv/usb20_agent/usb20_usbdpi.core @@ -0,0 +1,21 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:usb20_usbdpi:0.1" +description: "USB20-USBDPI" + +filesets: + files_rtl: + files: + - usb20_if.sv: { file_type: systemVerilogSource } + - usb20_usbdpi.sv: { file_type: systemVerilogSource } + + files_dv: + depend: + - lowrisc:dv_dpi:usbdpi + +targets: + default: + filesets: + - files_rtl diff --git a/hw/dv/sv/usb20_agent/usb20_usbdpi.sv b/hw/dv/sv/usb20_agent/usb20_usbdpi.sv new file mode 100644 index 0000000000000..330b5e4cf1d15 --- /dev/null +++ b/hw/dv/sv/usb20_agent/usb20_usbdpi.sv @@ -0,0 +1,160 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module usb20_usbdpi ( + input clk_i, + input rst_ni, + + // Enable DPI module functionality + input enable, + + // Outputs from the DPI module + output usb_sense_p2d_o, + output usb_dp_en_p2d_o, + output usb_dn_en_p2d_o, + output usb_dp_p2d_o, + output usb_dn_p2d_o, + + // Bidirectional, differential bus. + inout usb_p, + inout usb_n +); + + // Functioning sketch of integration of USBDPI model into dv top-level tb + // as a connectivity and function test for ASIC + + // This module integrates the existing `usbdpi` module into the DV chip test + // bench, reconstructing the two unidirectional buses and driver enables that + // the DPI model requires for its operation, using just the bare bidirectional + // USB signals. + // + // Nomenclature notes: + // dp (or p) and dn (or n) are the two signals of the differential USB + // d2p means device to DPI + // p2d means DPI to device + + /////////////////////////////////////////////////////////////////////////// + // Simple detection of pull up assertion indicating device presence; + // we simply respond to the first line to be pulled high by the device after + // VSENSE assertion, without regard for proper USB 2.0 timing + /////////////////////////////////////////////////////////////////////////// + logic usb_pullupdp_d2p; + logic usb_pullupdn_d2p; + + // Has either pull up been asserted by the device? + wire usb_pullup_detect = usb_pullupdp_d2p | usb_pullupdn_d2p; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + usb_pullupdp_d2p <= 1'b0; + usb_pullupdn_d2p <= 1'b0; + end else if (enable & usb_sense_p2d_o && !usb_pullup_detect) begin + // Is the FS device pulling DP high? + if (usb_p === 1'b1) begin + usb_pullupdp_d2p <= 1'b1; + end + // Is the FS device pulling DN high, implying that it has been configured + // to perform pin-flipping? + if (usb_n === 1'b1) begin + usb_pullupdn_d2p <= 1'b1; + end + end + end + + /////////////////////////////////////////////////////////////////////////// + // Basic activity detection; DPI model indicates whether it is driving, + // but for the device we must detect a departure from the bus Idle state + /////////////////////////////////////////////////////////////////////////// + logic usb_dp_en_d2p_last; + logic usb_dn_en_d2p_last; + logic usb_dp_en_d2p; + logic usb_dn_en_d2p; + + // Idle state of _P and _N bus signals depends upon which pull up has been + // asserted; the wire that is carrying the true D+ signal will be high + wire idle_p = usb_pullupdp_d2p; + wire idle_n = usb_pullupdn_d2p; + + always_comb begin + usb_dp_en_d2p = usb_dp_en_d2p_last; + usb_dn_en_d2p = usb_dn_en_d2p_last; + // Detect transmission start of DPI (it tells us) or device (not already + // transmitting and there is a departure from the idle state) + if (usb_dp_en_p2d_o || (!usb_dp_en_d2p && usb_p != idle_p)) begin + usb_dp_en_d2p = !usb_dp_en_p2d_o; + end + if (usb_dn_en_p2d_o || (!usb_dn_en_d2p && usb_n != idle_n)) begin + usb_dn_en_d2p = !usb_dn_en_p2d_o; + end + end + + // Count of SE0 cycles (1/4 bit intervals). This is just an approximate + // detection of EOP for the purpose of ascertaining when the device is + // relinquishing the bus; it will also count during bus resets. + logic [2:0] se0_cnt; + always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + se0_cnt <= 'b0; + end else if (enable) begin + if (usb_p === 1'b0 && usb_n === 1'b0) begin + se0_cnt <= se0_cnt + 1'b1; + end else begin + se0_cnt <= 'b0; + end + end + end + + // Assume that the device is driving if the DPI model is not + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + usb_dp_en_d2p_last <= 1'b0; + usb_dn_en_d2p_last <= 1'b0; + end else if (enable & usb_sense_p2d_o & usb_pullup_detect) begin + // Detect the end of EOP when the device has been transmitting + if (&{usb_dp_en_d2p_last, se0_cnt}) begin + usb_dp_en_d2p_last <= 1'b0; + end else begin + usb_dp_en_d2p_last <= usb_dp_en_d2p; + end + // Detect the end of EOP when the device has been transmitting + if ({usb_dn_en_d2p_last, se0_cnt}) begin + usb_dn_en_d2p_last <= 1'b0; + end else begin + usb_dn_en_d2p_last <= usb_dn_en_d2p; + end + end + end + + // USB DPI + usbdpi u_usbdpi ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .clk_48MHz_i (clk_i), + + .enable (enable), + + .sense_p2d (usb_sense_p2d_o), + .pullupdp_d2p (usb_pullupdp_d2p), + .pullupdn_d2p (usb_pullupdn_d2p), + + .dp_en_p2d (usb_dp_en_p2d_o), + .dp_p2d (usb_dp_p2d_o), + .dp_d2p (usb_p), + .dp_en_d2p (usb_dp_en_d2p), + + .dn_en_p2d (usb_dn_en_p2d_o), + .dn_p2d (usb_dn_p2d_o), + .dn_d2p (usb_n), + .dn_en_d2p (usb_dn_en_d2p), + + // ASIC communicates via true differential signaling + .d_p2d (), + .d_d2p (1'b0), // not used + .d_en_d2p (1'b0), + .se0_d2p (1'b0), // not used + .rx_enable_d2p (1'b0), + .tx_use_d_se0_d2p(1'b0) + ); + +endmodule diff --git a/hw/ip/prim_generic/lint/prim_generic_usb_diff_rx.waiver b/hw/ip/prim_generic/lint/prim_generic_usb_diff_rx.waiver index 9e8a33d8670e7..dfc8626939492 100644 --- a/hw/ip/prim_generic/lint/prim_generic_usb_diff_rx.waiver +++ b/hw/ip/prim_generic/lint/prim_generic_usb_diff_rx.waiver @@ -5,9 +5,9 @@ # waiver file for prim_generic_usb_diff_rx # note that this code is NOT synthesizable and meant for sim only -waive -rules TRI_DRIVER -regexp {'(input_p|input_n)' is driven by a tristate driver} -location {prim_generic_usb_diff_rx.sv} \ +waive -rules TRI_DRIVER -regexp {'(input_pi|input_ni)' is driven by a tristate driver} -location {prim_generic_usb_diff_rx.sv} \ -comment "This models the pullup behavior, hence the TRI driver." -waive -rules MULTI_DRIVEN -regexp {'(input_p|input_n)' has 2 drivers, also driven at} -location {prim_generic_usb_diff_rx.sv} \ +waive -rules MULTI_DRIVEN -regexp {'(input_pi|input_ni)' has 2 drivers, also driven at} -location {prim_generic_usb_diff_rx.sv} \ -comment "The simulation model has multiple drivers to emulate different IO terminations." -waive -rules DRIVE_STRENGTH -regexp {Drive strength '\(weak0,weak1\)' encountered on assignment to '(input_p|input_n)'} -location {prim_generic_usb_diff_rx.sv} \ +waive -rules DRIVE_STRENGTH -regexp {Drive strength '\(weak0,pull1\)' encountered on assignment to '(input_pi|input_ni)'} -location {prim_generic_usb_diff_rx.sv} \ -comment "The simulation model uses driving strength attributes to emulate different IO terminations." diff --git a/hw/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv b/hw/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv index def212ac0aa0a..b94d39cdc2ba2 100644 --- a/hw/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv +++ b/hw/ip/prim_generic/rtl/prim_generic_usb_diff_rx.sv @@ -10,8 +10,8 @@ module prim_generic_usb_diff_rx #( parameter int CalibW = 32 ) ( - input wire input_pi, // differential input - input wire input_ni, // differential input + inout input_pi, // differential input + inout input_ni, // differential input input input_en_i, // input buffer enable input core_pok_h_i, // core power indication at VCC level input pullup_p_en_i, // pullup enable for P @@ -35,9 +35,9 @@ module prim_generic_usb_diff_rx #( assign unused_pullup_p_en = pullup_p_en_i; assign unused_pullup_n_en = pullup_n_en_i; `else - // pullup / pulldown termination - assign (weak0, weak1) input_p = pullup_p_en_i ? 1'b1 : 1'bz; - assign (weak0, weak1) input_n = pullup_n_en_i ? 1'b1 : 1'bz; + // pullup termination + assign (weak0, pull1) input_pi = pullup_p_en_i ? 1'b1 : 1'bz; + assign (weak0, pull1) input_ni = pullup_n_en_i ? 1'b1 : 1'bz; `endif assign input_o = (input_en_i) ? input_p & ~input_n : 1'b0; diff --git a/hw/top_earlgrey/data/chip_testplan.hjson b/hw/top_earlgrey/data/chip_testplan.hjson index c45f128a428f3..9a600cd0fd738 100644 --- a/hw/top_earlgrey/data/chip_testplan.hjson +++ b/hw/top_earlgrey/data/chip_testplan.hjson @@ -298,6 +298,13 @@ } // USB (pre-verified IP) integration tests: + { + name: chip_sw_usbdev_dpi + desc: '''Sketch of USBDPI integration. + ''' + stage: V2 + tests: ["chip_sw_usbdev_dpi"] + } { name: chip_sw_usb_fs_tx_rx desc: '''Verify the transmission of single-ended data over the USB at full speed. As a part of diff --git a/hw/top_earlgrey/dv/chip_sim.core b/hw/top_earlgrey/dv/chip_sim.core index 9341a4f3a28fd..e2f61b3b415be 100644 --- a/hw/top_earlgrey/dv/chip_sim.core +++ b/hw/top_earlgrey/dv/chip_sim.core @@ -28,6 +28,8 @@ filesets: - lowrisc:dv:sw_test_status - lowrisc:dv:sw_logger_if - lowrisc:dv:mem_bkdr_util + - lowrisc:dv_dpi:usbdpi + - lowrisc:dv:usb20_usbdpi files: - tb/chip_hier_macros.svh: {is_include_file: true} - autogen/tb__xbar_connect.sv: {is_include_file: true} diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson index f631a34664da7..5327b72aa83ae 100644 --- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson +++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson @@ -531,6 +531,15 @@ "+test_timeout_ns=80_000_000"] run_timeout_mins: 180 } + { + name: chip_sw_usbdev_dpi + uvm_test_seq: chip_sw_usbdev_dpi_vseq + sw_images: ["//sw/device/tests:usbdev_test:1"] + en_run_modes: ["sw_test_mode_test_rom"] + run_opts: ["+uart_idx=0", "+calibrate_usb_clk=1"] + run_timeout_mins: 120 + reseed: 1 + } { name: chip_sw_inject_scramble_seed uvm_test_seq: chip_sw_inject_scramble_seed_vseq diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core index 1d4dc89858b6a..13feab5ade1b4 100644 --- a/hw/top_earlgrey/dv/env/chip_env.core +++ b/hw/top_earlgrey/dv/env/chip_env.core @@ -121,6 +121,7 @@ filesets: - seq_lib/chip_sw_entropy_src_fuse_vseq.sv: {is_include_file: true} - seq_lib/chip_sw_csrng_lc_hw_debug_en_vseq.sv: {is_include_file: true} - seq_lib/chip_sw_usb_ast_clk_calib_vseq.sv: {is_include_file: true} + - seq_lib/chip_sw_usbdev_dpi_vseq.sv: {is_include_file: true} - seq_lib/chip_sw_i2c_tx_rx_vseq.sv: {is_include_file: true} - seq_lib/chip_sw_i2c_host_tx_rx_vseq.sv: {is_include_file: true} - seq_lib/chip_sw_spi_device_tpm_vseq.sv: {is_include_file: true} diff --git a/hw/top_earlgrey/dv/env/chip_env.sv b/hw/top_earlgrey/dv/env/chip_env.sv index df2b3f9de3eae..280eb75f8faf6 100644 --- a/hw/top_earlgrey/dv/env/chip_env.sv +++ b/hw/top_earlgrey/dv/env/chip_env.sv @@ -77,6 +77,13 @@ class chip_env extends cip_base_env #( `uvm_fatal(`gfn, "failed to get ast_ext_clk_vif from uvm_config_db") end + // get the handle to the usb20 interface. + if (!uvm_config_db#(usb20_vif)::get( + this, "", "usb20_vif", cfg.usb20_vif + )) begin + `uvm_fatal(`gfn, "failed to get usb20_vif from uvm_config_db") + end + // create components foreach (m_uart_agents[i]) begin m_uart_agents[i] = uart_agent::type_id::create($sformatf("m_uart_agent%0d", i), this); diff --git a/hw/top_earlgrey/dv/env/chip_env_cfg.sv b/hw/top_earlgrey/dv/env/chip_env_cfg.sv index 83b3f9b25f9fd..bbc817f5fc871 100644 --- a/hw/top_earlgrey/dv/env/chip_env_cfg.sv +++ b/hw/top_earlgrey/dv/env/chip_env_cfg.sv @@ -78,6 +78,7 @@ class chip_env_cfg #(type RAL_T = chip_ral_pkg::chip_reg_block) extends cip_base sw_test_status_vif sw_test_status_vif; ast_supply_vif ast_supply_vif; ast_ext_clk_vif ast_ext_clk_vif; + usb20_vif usb20_vif; // Number of RAM tiles for each RAM instance. uint num_ram_main_tiles; diff --git a/hw/top_earlgrey/dv/env/chip_env_pkg.sv b/hw/top_earlgrey/dv/env/chip_env_pkg.sv index 384baa0a7c6d5..6c8393c2d4772 100644 --- a/hw/top_earlgrey/dv/env/chip_env_pkg.sv +++ b/hw/top_earlgrey/dv/env/chip_env_pkg.sv @@ -69,6 +69,7 @@ package chip_env_pkg; typedef virtual sw_test_status_if sw_test_status_vif; typedef virtual ast_supply_if ast_supply_vif; typedef virtual ast_ext_clk_if ast_ext_clk_vif; + typedef virtual usb20_if usb20_vif; // Types of memories in the chip. // @@ -83,6 +84,7 @@ package chip_env_pkg; ICacheWay1Tag, ICacheWay0Data, ICacheWay1Data, + UsbdevBuf, OtbnDmem[16], OtbnImem, Otp, diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_usbdev_dpi_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_usbdev_dpi_vseq.sv new file mode 100644 index 0000000000000..f2e167b85ee56 --- /dev/null +++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_usbdev_dpi_vseq.sv @@ -0,0 +1,30 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class chip_sw_usbdev_dpi_vseq extends chip_sw_base_vseq; + `uvm_object_utils(chip_sw_usbdev_dpi_vseq) + + `uvm_object_new + + // Zero initialize the usbdev packet memory + function init_packet_mem(); + cfg.mem_bkdr_util_h[UsbdevBuf].clear_mem(); + endfunction + + virtual task body(); + super.body(); + + // We need to release the tb weak pull up on DP, otherwise usbdev appears + // to be connected much too soon + cfg.chip_vif.cfg_default_weak_pulls_on_dios(0); + + // Connect the drivers of the DPI model + cfg.usb20_vif.enable_driver(1); + + // Zero initialize the packet memory because otherwise partial word reads + // from a buffer will trigger 'X' assertions on the TileLink bus + init_packet_mem(); + endtask + +endclass : chip_sw_usbdev_dpi_vseq diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv index d55145702b902..2fa03faf53966 100644 --- a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv +++ b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv @@ -76,6 +76,7 @@ `include "chip_sw_entropy_src_fuse_vseq.sv" `include "chip_sw_csrng_lc_hw_debug_en_vseq.sv" `include "chip_sw_usb_ast_clk_calib_vseq.sv" +`include "chip_sw_usbdev_dpi_vseq.sv" `include "chip_sw_i2c_tx_rx_vseq.sv" `include "chip_sw_i2c_host_tx_rx_vseq.sv" `include "chip_sw_i2c_device_tx_rx_vseq.sv" diff --git a/hw/top_earlgrey/dv/tb/chip_hier_macros.svh b/hw/top_earlgrey/dv/tb/chip_hier_macros.svh index e52be966a97a5..7bd1a69f023fe 100644 --- a/hw/top_earlgrey/dv/tb/chip_hier_macros.svh +++ b/hw/top_earlgrey/dv/tb/chip_hier_macros.svh @@ -60,3 +60,4 @@ `define OTP_MEM_HIER `OTP_GENERIC_HIER.u_prim_ram_1p_adv.u_mem.`MEM_ARRAY_SUB `define OTBN_IMEM_HIER `OTBN_HIER.u_imem.u_prim_ram_1p_adv.u_mem.`MEM_ARRAY_SUB `define OTBN_DMEM_HIER `OTBN_HIER.u_dmem.u_prim_ram_1p_adv.u_mem.`MEM_ARRAY_SUB +`define USBDEV_BUF_HIER `USBDEV_HIER.gen_no_stubbed_memory.u_memory_2p.i_prim_ram_2p_async_adv.u_mem.`MEM_ARRAY_SUB diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv index 8aaf51c182235..4f90ed6ee059e 100644 --- a/hw/top_earlgrey/dv/tb/tb.sv +++ b/hw/top_earlgrey/dv/tb/tb.sv @@ -162,6 +162,35 @@ module tb; bit en_sim_sram = 1'b1; wire sel_sim_sram = !dut.chip_if.stub_cpu & en_sim_sram; + // Interface presently just permits the DPI model to be easily connected and + // disconnected as required, since SENSE pin is a MIO with other uses. + usb20_if u_usb20_if ( + .clk_i (dut.chip_if.usb_clk), + .rst_ni (dut.chip_if.usb_rst_n), + + .usb_vbus (dut.chip_if.mios[top_earlgrey_pkg::MioPadIoc7]), + .usb_p (dut.chip_if.dios[top_earlgrey_pkg::DioPadUsbP]), + .usb_n (dut.chip_if.dios[top_earlgrey_pkg::DioPadUsbN]) + ); + + // Instantiate & connect the USB DPI model for top-level testing. + usb20_usbdpi u_usb20_usbdpi ( + .clk_i (dut.chip_if.usb_clk), + .rst_ni (dut.chip_if.usb_rst_n), + + .enable (u_usb20_if.connected), + + // Outputs from the DPI module + .usb_sense_p2d_o (u_usb20_if.usb_sense_p2d), + .usb_dp_en_p2d_o (u_usb20_if.usb_dp_en_p2d), + .usb_dn_en_p2d_o (u_usb20_if.usb_dn_en_p2d), + .usb_dp_p2d_o (u_usb20_if.usb_dp_p2d), + .usb_dn_p2d_o (u_usb20_if.usb_dn_p2d), + + .usb_p (dut.chip_if.dios[top_earlgrey_pkg::DioPadUsbP]), + .usb_n (dut.chip_if.dios[top_earlgrey_pkg::DioPadUsbN]) + ); + sim_sram u_sim_sram ( .clk_i (sel_sim_sram ? `CPU_HIER.clk_i : 1'b0), .rst_ni (`CPU_HIER.rst_ni), @@ -212,6 +241,10 @@ module tb; uvm_config_db#(virtual ast_ext_clk_if)::set( null, "*.env", "ast_ext_clk_vif", dut.ast_ext_clk_if); + // USB DPI interface. + uvm_config_db#(virtual usb20_if)::set( + null, "*.env", "usb20_vif", u_usb20_if); + // Format time in microseconds losing no precision. The added "." makes it easier to determine // the order of magnitude without counting digits, as is needed if it was formatted as ps or ns. $timeformat(-6, 6, " us", 13); @@ -372,6 +405,13 @@ module tb; .err_detection_scheme(mem_bkdr_util_pkg::EccInv_39_32)); `MEM_BKDR_UTIL_FILE_OP(m_mem_bkdr_util[OtbnDmem0], `OTBN_DMEM_HIER) + `uvm_info("tb.sv", "Creating mem_bkdr_util instance for USBDEV BUFFER", UVM_MEDIUM) + m_mem_bkdr_util[UsbdevBuf] = new(.name ("mem_bkdr_util[UsbdevBuf]"), + .path (`DV_STRINGIFY(`USBDEV_BUF_HIER)), + .depth ($size(`USBDEV_BUF_HIER)), + .n_bits($bits(`USBDEV_BUF_HIER)), + .err_detection_scheme(mem_bkdr_util_pkg::ErrDetectionNone)); + mem = mem.first(); do begin if (mem inside {[RamMain1:RamMain15]} || diff --git a/hw/top_earlgrey/dv/verilator/chip_sim_tb.sv b/hw/top_earlgrey/dv/verilator/chip_sim_tb.sv index b37acb2428488..cabe4afc510d8 100644 --- a/hw/top_earlgrey/dv/verilator/chip_sim_tb.sv +++ b/hw/top_earlgrey/dv/verilator/chip_sim_tb.sv @@ -142,6 +142,7 @@ module chip_sim_tb ( .clk_i (clk_i), .rst_ni (rst_ni), .clk_48MHz_i (clk_i), + .enable (1'b1), .sense_p2d (cio_usbdev_sense_p2d), .pullupdp_d2p (cio_usbdev_dp_pullup_d2p), .pullupdn_d2p (cio_usbdev_dn_pullup_d2p), diff --git a/hw/top_englishbreakfast/rtl/chip_englishbreakfast_verilator.sv b/hw/top_englishbreakfast/rtl/chip_englishbreakfast_verilator.sv index abed7b2ffebd3..7fd6099f06d89 100644 --- a/hw/top_englishbreakfast/rtl/chip_englishbreakfast_verilator.sv +++ b/hw/top_englishbreakfast/rtl/chip_englishbreakfast_verilator.sv @@ -296,6 +296,7 @@ module chip_englishbreakfast_verilator ( .clk_i (clk_i), .rst_ni (rst_ni), .clk_48MHz_i (clk_i), + .enable (1'b1), .sense_p2d (cio_usbdev_sense_p2d), .pullupdp_d2p (cio_usbdev_dp_pullup_d2p), .pullupdn_d2p (cio_usbdev_dn_pullup_d2p), diff --git a/sw/device/lib/testing/pinmux_testutils.c b/sw/device/lib/testing/pinmux_testutils.c index 8cc6689d20e12..9beea99984b72 100644 --- a/sw/device/lib/testing/pinmux_testutils.c +++ b/sw/device/lib/testing/pinmux_testutils.c @@ -62,6 +62,30 @@ void pinmux_testutils_init(dif_pinmux_t *pinmux) { // Configure UART1 TX output to connect to MIO pad IOB5 CHECK_DIF_OK(dif_pinmux_output_select(pinmux, kTopEarlgreyPinmuxMioOutIob5, kTopEarlgreyPinmuxOutselUart1Tx)); + + // Configure a higher drive strength for the USB_P and USB_N pads because we + // must the pad drivers must be capable of overpowering the 'pull' signal + // strength of the internal pull ups in the differential receiver. + // + // 'pull' strength is required because at the host end of the USB, there + // are 'weak' pull downs, allowing it to detect device presence when it + // applies its pull up. + // strong PAD driver > internal pull up > weak pull down at host + // + // Normally the pull up on USB_P will be asserted, but we may be employing + // 'pin flipping' and instead choose to apply the _N pull up. + if (kDeviceType == kDeviceSimDV) { + dif_pinmux_pad_attr_t out_attr; + dif_pinmux_pad_attr_t in_attr = { + .slew_rate = 0, .drive_strength = 1, .flags = 0}; + + CHECK_DIF_OK( + dif_pinmux_pad_write_attrs(pinmux, kTopEarlgreyDirectPadsUsbdevUsbDp, + kDifPinmuxPadKindDio, in_attr, &out_attr)); + CHECK_DIF_OK( + dif_pinmux_pad_write_attrs(pinmux, kTopEarlgreyDirectPadsUsbdevUsbDn, + kDifPinmuxPadKindDio, in_attr, &out_attr)); + } #endif // Configure USBDEV SENSE outputs to be high-Z (IOC7) diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index f3f04a2856a3a..5f51938c30499 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD @@ -1943,6 +1943,7 @@ opentitan_functest( targets = [ "verilator", "cw310_test_rom", + "dv", ], verilator = verilator_params( timeout = "long", diff --git a/sw/device/tests/usbdev_test.c b/sw/device/tests/usbdev_test.c index 2ffffc599e510..a73ef9fc7b051 100644 --- a/sw/device/tests/usbdev_test.c +++ b/sw/device/tests/usbdev_test.c @@ -98,11 +98,6 @@ static void usb_receipt_callback(uint8_t c) { OTTF_DEFINE_TEST_CONFIG(); bool test_main(void) { - CHECK(kDeviceType == kDeviceSimVerilator || kDeviceType == kDeviceFpgaCw310, - "This test is not expected to run on platforms other than the " - "Verilator simulation or CW310 FPGA. It needs the USB DPI model " - "or host application."); - LOG_INFO("Running USBDEV test"); CHECK_DIF_OK(dif_pinmux_init(