[usbdev] Power on the diff receiver before using it Add a parameter to usbdev to count time until a differential receiver should be operational. If the device is configured to use a differential receiver, reject detected K and J symbols for the power-on time after enabling the receiver. Signed-off-by: Alexander Williams <awill@google.com>
diff --git a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_pe.sv b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_pe.sv index f1a7c4d..82c6070 100644 --- a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_pe.sv +++ b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_pe.sv
@@ -34,6 +34,10 @@ input logic cfg_use_diff_rcvr_i, // 1: use usb_d_i from a differential receiver input logic tx_osc_test_mode_i, // Oscillator test mode (constantly output JK) input logic [NumOutEps-1:0] data_toggle_clear_i, // Clear the data toggles for an EP + input logic diff_rx_ok_i, // 1: received differential data symbols are valid. + // Set low if K and J symbols might be invalid, such + // as when an external differential receiver is + // powering on. //////////////////////////// // USB Endpoint Interface // @@ -234,6 +238,7 @@ .link_reset_i (link_reset_i), .cfg_eop_single_bit_i (cfg_eop_single_bit_i), .cfg_use_diff_rcvr_i (cfg_use_diff_rcvr_i), + .diff_rx_ok_i (diff_rx_ok_i), .usb_d_i (usb_d_i), .usb_dp_i (usb_dp_i), .usb_dn_i (usb_dn_i),
diff --git a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_rx.sv b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_rx.sv index e7916d8..b63d915 100644 --- a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_rx.sv +++ b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_rx.sv
@@ -13,6 +13,7 @@ // configuration input logic cfg_eop_single_bit_i, input logic cfg_use_diff_rcvr_i, + input logic diff_rx_ok_i, // USB data+ and data- lines (synchronous) input logic usb_d_i, @@ -290,8 +291,12 @@ always_comb begin : proc_packet_valid_d if (line_state_valid) begin + // If the differential K and J symbols are not valid, reject the + // containing packet as invalid. + if (~diff_rx_ok_i) begin + packet_valid_d = 0; // check for packet start: KJKJKK, we use the last 6 bits - if (!packet_valid_q && line_history_q[11:0] == 12'b011001100101) begin + end else if (!packet_valid_q && line_history_q[11:0] == 12'b011001100101) begin packet_valid_d = 1; end @@ -341,7 +346,7 @@ end // Used for seeing a J after the completion of resume signaling - assign rx_j_det_o = ~tx_en_i & (line_history_q[1:0] == 2'b10); + assign rx_j_det_o = diff_rx_ok_i & ~tx_en_i & (line_history_q[1:0] == 2'b10); ///////////////// // NRZI decode //
diff --git a/hw/ip/usbdev/data/usbdev.hjson b/hw/ip/usbdev/data/usbdev.hjson index f07f71d..93229e1 100644 --- a/hw/ip/usbdev/data/usbdev.hjson +++ b/hw/ip/usbdev/data/usbdev.hjson
@@ -150,6 +150,13 @@ default: "12", desc: "Number of endpoints", local: "true" + }, + { name: "RcvrWakeTimeUs", + type: "int", + default: "1", + desc: "Maximum number of microseconds for the differential receiver to become operational", + local: "false", + expose: "true" } ], interrupt_list: [
diff --git a/hw/ip/usbdev/rtl/usbdev.sv b/hw/ip/usbdev/rtl/usbdev.sv index de36a30..e2ccc2b 100644 --- a/hw/ip/usbdev/rtl/usbdev.sv +++ b/hw/ip/usbdev/rtl/usbdev.sv
@@ -11,8 +11,12 @@ module usbdev import usbdev_pkg::*; import usbdev_reg_pkg::*; + import prim_util_pkg::vbits; #( - parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} + parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, + parameter int RcvrWakeTimeUs = 1 // Max time (in microseconds) from rx_enable_o high to the + // external differential receiver outputting valid data (when + // configured to use one). ) ( input logic clk_i, input logic rst_ni, @@ -177,6 +181,27 @@ logic usb_pwr_sense; logic usb_pullup_en; + ////////////////////////////////// + // Microsecond timing reference // + ////////////////////////////////// + // us_tick ticks for one cycle every us, and it is based off a free-running + // counter. + logic [5:0] ns_cnt; + logic us_tick; + + assign us_tick = (ns_cnt == 6'd48); + always_ff @(posedge clk_usb_48mhz_i or negedge rst_usb_48mhz_ni) begin + if (!rst_usb_48mhz_ni) begin + ns_cnt <= '0; + end else begin + if (us_tick) begin + ns_cnt <= '0; + end else begin + ns_cnt <= ns_cnt + 1'b1; + end + end + end + ///////////////////////////// // Receive interface fifos // ///////////////////////////// @@ -269,6 +294,7 @@ logic [NEndpoints-1:0] in_rdy_async; logic [3:0] usb_out_endpoint; logic usb_out_endpoint_val; + logic usb_use_diff_rcvr, usb_diff_rx_ok; // Endpoint enables always_comb begin : proc_map_ep_enable @@ -568,6 +594,9 @@ .mem_wdata_o (usb_mem_b_wdata), .mem_rdata_i (usb_mem_b_rdata), + // time reference + .us_tick_i (us_tick), + // control .enable_i (usb_enable), .devaddr_i (usb_device_addr), @@ -576,6 +605,7 @@ .out_ep_enabled_i (usb_ep_out_enable), .out_ep_iso_i (ep_out_iso), // cdc ok, quasi-static .in_ep_iso_i (ep_in_iso), // cdc ok, quasi-static + .diff_rx_ok_i (usb_diff_rx_ok), .cfg_eop_single_bit_i (reg2hw.phy_config.eop_single_bit.q), // cdc ok: quasi-static .tx_osc_test_mode_i (reg2hw.phy_config.tx_osc_test_mode.q), // cdc ok: quasi-static .cfg_use_diff_rcvr_i (reg2hw.phy_config.use_diff_rcvr.q), // cdc ok: quasi-static @@ -1057,11 +1087,44 @@ .usb_suspend_i (usb_event_link_suspend) ); + // Differential receiver enable + prim_flop_2sync #( + .Width (1) + ) usbdev_sync_rcvr_enable ( + .clk_i (clk_usb_48mhz_i), + .rst_ni (rst_usb_48mhz_ni), + .d_i (reg2hw.phy_config.use_diff_rcvr.q), + .q_o (usb_use_diff_rcvr) + ); // enable rx only when the single-ended input is enabled and the device is // not suspended. - // TODO(#10901): This can cause undefined behavior if this module stays - // powered to detect resume (instead of the AON module). - assign usb_rx_enable_o = reg2hw.phy_config.use_diff_rcvr.q & ~usb_suspend_o; + assign usb_rx_enable_o = usb_use_diff_rcvr & ~usb_suspend_o; + + // Symbols from the differential receiver are invalid until it has finished + // waking up / powering on + // Add 1 to the specified time to account for uncertainty in the + // free-running counter for us_tick. + localparam int RcvrWakeTimeWidth = vbits(RcvrWakeTimeUs + 1); + logic [RcvrWakeTimeWidth-1:0] usb_rcvr_ok_counter_d, usb_rcvr_ok_counter_q; + + assign usb_diff_rx_ok = (usb_rcvr_ok_counter_q == '0); + always_comb begin + // When don't need to use a differential receiver, RX is always ready + usb_rcvr_ok_counter_d = '0; + if (usb_use_diff_rcvr & !usb_rx_enable_o) begin + usb_rcvr_ok_counter_d = RcvrWakeTimeUs[0 +: RcvrWakeTimeWidth] + '1; + end else if (us_tick && (usb_rcvr_ok_counter_q > '0)) begin + usb_rcvr_ok_counter_d = usb_rcvr_ok_counter_q - '1; + end + end + + always_ff @(posedge clk_usb_48mhz_i or negedge rst_usb_48mhz_ni) begin + if (!rst_usb_48mhz_ni) begin + usb_rcvr_ok_counter_q <= RcvrWakeTimeUs[0 +: RcvrWakeTimeWidth] + '1; + end else begin + usb_rcvr_ok_counter_q <= usb_rcvr_ok_counter_d; + end + end ///////////////////////////////////////// // SOF Reference for Clock Calibration //
diff --git a/hw/ip/usbdev/rtl/usbdev_usbif.sv b/hw/ip/usbdev/rtl/usbdev_usbif.sv index a23f450..d1ef16d 100644 --- a/hw/ip/usbdev/rtl/usbdev_usbif.sv +++ b/hw/ip/usbdev/rtl/usbdev_usbif.sv
@@ -67,6 +67,9 @@ output logic [31:0] mem_wdata_o, input logic [31:0] mem_rdata_i, + // time reference + input logic us_tick_i, + // control input logic enable_i, input logic [6:0] devaddr_i, @@ -75,6 +78,7 @@ input logic [NEndpoints-1:0] out_ep_enabled_i, input logic [NEndpoints-1:0] out_ep_iso_i, input logic [NEndpoints-1:0] in_ep_iso_i, + input logic diff_rx_ok_i, // 1: differential symbols (K/J) are valid input logic cfg_eop_single_bit_i, // 1: detect a single SE0 bit as EOP input logic cfg_use_diff_rcvr_i, // 1: use single-ended rx data on usb_d_i input logic tx_osc_test_mode_i, // Oscillator test mode: constant JK output @@ -280,6 +284,7 @@ .cfg_use_diff_rcvr_i (cfg_use_diff_rcvr_i), .tx_osc_test_mode_i (tx_osc_test_mode_i), .data_toggle_clear_i (data_toggle_clear_i), + .diff_rx_ok_i (diff_rx_ok_i), .usb_d_i (usb_d_i), .usb_dp_i (usb_dp_i), @@ -333,26 +338,9 @@ .frame_index_o (frame_index_raw) ); - // us_tick ticks for one cycle every us - logic [5:0] ns_cnt; - logic us_tick; - logic do_internal_sof; - - assign us_tick = (ns_cnt == 6'd48); - always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin - if (!rst_ni) begin - ns_cnt <= '0; - end else begin - if (us_tick) begin - ns_cnt <= '0; - end else begin - ns_cnt <= ns_cnt + 1'b1; - end - end - end - // Capture frame number (host sends every 1ms) // Generate an internal SOF if the host's is missing. + logic do_internal_sof; logic [10:0] frame_d, frame_q; assign frame_o = frame_q; @@ -378,7 +366,7 @@ usbdev_linkstate u_usbdev_linkstate ( .clk_48mhz_i (clk_48mhz_i), .rst_ni (rst_ni), - .us_tick_i (us_tick), + .us_tick_i (us_tick_i), .usb_sense_i (usb_sense_i), .usb_dp_i (usb_dp_i), .usb_dn_i (usb_dn_i),
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index cbab91a..e02b07f 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -1112,7 +1112,18 @@ "0" ] param_decl: {} - param_list: [] + memory: {} + param_list: + [ + { + name: RcvrWakeTimeUs + desc: Maximum number of microseconds for the differential receiver to become operational + type: int + default: "1" + expose: "true" + name_top: UsbdevRcvrWakeTimeUs + } + ] inter_signal_list: [ {
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 9fdbb12..c68f94b 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -26,6 +26,7 @@ // parameters for pattgen // parameters for rv_timer // parameters for usbdev + parameter int UsbdevRcvrWakeTimeUs = 1, // parameters for otp_ctrl parameter OtpCtrlMemInitFile = "", // parameters for lc_ctrl @@ -1368,7 +1369,8 @@ .rst_ni (rstmgr_aon_resets.rst_sys_io_div4_n[rstmgr_pkg::Domain0Sel]) ); usbdev #( - .AlertAsyncOn(alert_handler_reg_pkg::AsyncOn[11:11]) + .AlertAsyncOn(alert_handler_reg_pkg::AsyncOn[11:11]), + .RcvrWakeTimeUs(UsbdevRcvrWakeTimeUs) ) u_usbdev ( // Input