[usbdev] Add link state to handle resume to active The new LinkResuming state gives a place for the FSM to park until it sees a symbol that isn't part of the resume signaling. It's only after seeing the end of resume that the module should begin tracking SOF. Add the ability for software to direct the link state FSM to jump to LinkResuming from LinkPowered. This enables software to recover an active link state after going to deep sleep. Convert the usbctrl wake_config regs to use the async attribute. Prevent protocol engine from responding to transactions unless the link is in one of the active states. Signed-off-by: Alexander Williams <awill@google.com>
diff --git a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_in_pe.sv b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_in_pe.sv index a2b8562..a165869 100644 --- a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_in_pe.sv +++ b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_in_pe.sv
@@ -27,6 +27,7 @@ input logic clk_48mhz_i, input logic rst_ni, input logic link_reset_i, + input logic link_active_i, input logic [6:0] dev_addr_i, @@ -284,7 +285,7 @@ if (!rst_ni) begin in_xact_state <= StIdle; in_ep_rollback_o <= 1'b0; - end else if (link_reset_i) begin + end else if (link_reset_i || !link_active_i) begin in_xact_state <= StIdle; in_ep_rollback_o <= 1'b0; end else begin
diff --git a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_out_pe.sv b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_out_pe.sv index d868bab..bb03fa3 100644 --- a/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_out_pe.sv +++ b/hw/ip/usb_fs_nb_pe/rtl/usb_fs_nb_out_pe.sv
@@ -28,6 +28,7 @@ input logic clk_48mhz_i, input logic rst_ni, input logic link_reset_i, + input logic link_active_i, input logic [6:0] dev_addr_i, //////////////////////// @@ -350,7 +351,7 @@ always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin if (!rst_ni) begin data_toggle_q <= '0; // All endpoints - end else if (link_reset_i) begin + end else if (link_reset_i || !link_active_i) begin data_toggle_q <= '0; // All endpoints end else begin data_toggle_q <= data_toggle_d;
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 78bfb13..9f128a5 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
@@ -23,6 +23,8 @@ input logic clk_48mhz_i, input logic rst_ni, // Async. reset, active low input logic link_reset_i, // USB reset, sync to 48 MHz, active high + input logic link_active_i, // Device is in Default/Addressed/Configured state + // and may respond to transactions input logic [6:0] dev_addr_i, input logic cfg_eop_single_bit_i, // 1: detect a single SE0 bit as EOP @@ -72,6 +74,7 @@ // RX line status output logic rx_jjj_det_o, + output logic rx_j_det_o, // RX errors output logic rx_crc_err_o, @@ -135,6 +138,7 @@ .clk_48mhz_i (clk_48mhz_i), .rst_ni (rst_ni), .link_reset_i (link_reset_i), + .link_active_i (link_active_i), .dev_addr_i (dev_addr_i), // endpoint interface @@ -177,6 +181,7 @@ .clk_48mhz_i (clk_48mhz_i), .rst_ni (rst_ni), .link_reset_i (link_reset_i), + .link_active_i (link_active_i), .dev_addr_i (dev_addr_i), // endpoint interface @@ -233,6 +238,7 @@ .rx_data_o (rx_data), .valid_packet_o (rx_pkt_valid), .rx_jjj_det_o (rx_jjj_det_o), + .rx_j_det_o (rx_j_det_o), .crc_error_o (rx_crc_err_o), .pid_error_o (rx_pid_err_o), .bitstuff_error_o (rx_bitstuff_err_o)
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 6a1a9c8..374ff7a 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
@@ -47,6 +47,7 @@ // line status for the status detection (actual rx bits after clock recovery) output logic rx_jjj_det_o, + output logic rx_j_det_o, // Error detection output logic crc_error_o, @@ -319,6 +320,9 @@ // mask out jjj detection when transmitting (because rx is forced to J) assign rx_jjj_det_o = ~tx_en_i & (line_history_q[5:0] == 6'b101010); // three Js + // 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); + ///////////////// // NRZI decode // /////////////////
diff --git a/hw/ip/usbdev/data/usbdev.hjson b/hw/ip/usbdev/data/usbdev.hjson index cd6319f..d0c0e21 100644 --- a/hw/ip/usbdev/data/usbdev.hjson +++ b/hw/ip/usbdev/data/usbdev.hjson
@@ -212,17 +212,29 @@ desc: "USB Control", swaccess: "rw", hwaccess: "hro", + hwqe: "true", + async: "clk_usb_48mhz_i", fields: [ { bits: "0", name: "enable", desc: ''' - Set to enable the USB interface and assert the pullup. + Set to connect the USB interface (i.e. assert the pullup). ''' tags: [// Prevent usb from being enabled to avoid other unforeseen side effects. "excl:CsrNonInitTests:CsrExclWrite"] } { + bits: "1", + name: "resume_link_active", + swaccess: "wo" + desc: ''' + Write a 1 to this bit to instruct usbdev to jump to the LinkResuming state. + The write will only have an effect when the device is in the LinkPowered state. + Its intention is to handle a resume-from-suspend event after the IP has been powered down. + ''' + } + { bits: "22:16", name: "device_address", hwaccess: "hrw", @@ -309,15 +321,15 @@ ''' enum: [ { value: "0", - name: "disconnect", - desc: "Link disconnected (no VBUS)" + name: "disconnected", + desc: "Link disconnected (no VBUS or no pull-up connected)" }, { value: "1", name: "powered", - desc: "Link powered, but not reset yet" + desc: "Link powered and connected, but not reset yet" }, { value: "2", - name: "powered_suspend", + name: "powered_suspended", desc: "Link suspended (constant idle/J for > 3 ms), but not reset yet" }, { value: "3", @@ -325,13 +337,17 @@ desc: "Link active" }, { value: "4", - name: "suspend", + name: "suspended", desc: "Link suspended (constant idle for > 3 ms), was active before becoming suspended" }, { value: "5", name: "active_nosof", desc: "Link active but no SOF has been received since the last reset." }, + { value: "6", + name: "resuming", + desc: "Link resuming to an active state, pending the end of resume signaling" + }, ] } { @@ -843,6 +859,8 @@ desc: "USB wake configuration for suspend / resume", swaccess: "rw", hwaccess: "hro", + async: "clk_aon_i", + hwqe: "true", fields: [ { bits: "0", @@ -862,6 +880,8 @@ bits: "1", resval: "0", name: "wake_ack", + swaccess: "wo", + hwaccess: "hro", desc: ''' Wake acknowledgement. Once the usb device resumes from suspend, this acknowledgement is used to transition the module back to normal operation.
diff --git a/hw/ip/usbdev/doc/_index.md b/hw/ip/usbdev/doc/_index.md index e5a1bb6..dcf568c 100644 --- a/hw/ip/usbdev/doc/_index.md +++ b/hw/ip/usbdev/doc/_index.md
@@ -193,18 +193,20 @@ |State| Description | |-----|-------------| -|Disconnect | The link is disconnected. This is signaled when the VBUS is not driven by the host, which results in the sense input pin being low. An interrupt is raised on entering this state.| -|Powered| The device has been powered as VBUS is being driven by the host, but has not been reset yet. The link is reset whenever the D+ and D- are both low (an SE0 condition) for an extended period. The host will assert reset for a minimum of 10 ms, but the USB specification allows the device to detect and respond to a reset after 2.5 us. The implementation here will report the reset state and raise an interrupt when the link is in SE0 for 3 us.| -|Powered Suspend| The link is suspended when at idle (a J condition) for more than 3 ms. An interrupt is generated when the suspend is detected and a resume interrupt is generated when the link exits the suspend state. This state is entered, if the device has not been reset yet.| +|Disconnected | The link is disconnected. This is signaled when the VBUS is not driven by the host, which results in the sense input pin being low, or when the user has not connected the pull-up by enabling the interface. An interrupt is raised on entering this state.| +|Powered| The device has been powered as VBUS is being driven by the host and the user has connected the pull-up, but the device has not been reset yet. The link is reset whenever the D+ and D- are both low (an SE0 condition) for an extended period. The host will assert reset for a minimum of 10 ms, but the USB specification allows the device to detect and respond to a reset after 2.5 us. The implementation here will report the reset state and raise an interrupt when the link is in SE0 for 3 us.| +|Powered Suspended| The link is suspended when at idle (a J condition) for more than 3 ms. An interrupt is generated when the suspend is detected and a resume interrupt is generated when the link exits the suspend state. This state is entered, if the device has not been reset yet.| +|Active No SOF| The link has been reset and can begin receiving packets, but no Start-of-Frame packets have yet been seen.| |Active| The link is active when it is running normally. | -|Suspend| Similar to 'Powered Suspend', but the device was in the active state before being suspended.| +|Suspended| Similar to 'Powered Suspended', but the device was in the active state before being suspended.| +|Resuming| The link is awaiting the end of resume signaling before transitioning to the Active No SOF state.| |Link Events| Description | |-----------|-------------| |Disconnect| VBUS has been lost. | |Link Reset| The link has been in the SE0 state for 3 us.| |Link Suspend| The link has been in the J state for more than 3 ms, upon which we have to enter the Suspend state.| -|Link Resume| The link has been driven to a non-J state after being in Suspend.| +|Link Resume| The link has been driven to a non-J state after being in Suspend. For the case of resuming to active link states, the end of resume signaling has occurred.| |Host Lost| Signaled using an interrupt if the link is active but a start of frame (SOF) packet has not been received from the host in 4.096 ms. The host is required to send a SOF packet every 1 ms. This is not an expected condition.|
diff --git a/hw/ip/usbdev/rtl/usbdev.sv b/hw/ip/usbdev/rtl/usbdev.sv index 48ae5a4..9239ea1 100644 --- a/hw/ip/usbdev/rtl/usbdev.sv +++ b/hw/ip/usbdev/rtl/usbdev.sv
@@ -159,6 +159,7 @@ logic [2:0] usb_link_state; logic usb_enable; logic [6:0] usb_device_addr; + logic usb_resume_link_active; logic data_toggle_clear_qe; logic usb_data_toggle_clear_en; @@ -584,6 +585,7 @@ .tx_osc_test_mode_i (reg2hw.phy_config.tx_osc_test_mode.q), // cdc ok: quasi-static .cfg_rx_differential_i (reg2hw.phy_config.rx_differential_mode.q), // cdc ok: quasi-static .data_toggle_clear_i (usb_data_toggle_clear), + .resume_link_active_i (usb_resume_link_active), // status .frame_o (usb_frame), @@ -617,15 +619,10 @@ .q_o ({hw2reg.usbstat.link_state.d, hw2reg.usbstat.frame.d}) ); - // sys clk -> USB clk - prim_flop_2sync #( - .Width (1+7) - ) cdc_sys_to_usb ( - .clk_i (clk_usb_48mhz_i), - .rst_ni (rst_usb_48mhz_ni), - .d_i ({reg2hw.usbctrl.enable.q, reg2hw.usbctrl.device_address.q}), - .q_o ({usb_enable, usb_device_addr}) - ); + assign usb_enable = reg2hw.usbctrl.enable.q; + assign usb_device_addr = reg2hw.usbctrl.device_address.q; + assign usb_resume_link_active = reg2hw.usbctrl.resume_link_active.qe & + reg2hw.usbctrl.resume_link_active.q; // CDC for event signals (arguably they are there for a long time so would be ok) // Just want a pulse to ensure only one interrupt for an event @@ -651,14 +648,7 @@ assign hw2reg.usbstat.host_lost.d = event_host_lost; // resets etc cause the device address to clear - prim_pulse_sync usbdev_devclr ( - .clk_src_i (clk_usb_48mhz_i), - .clk_dst_i (clk_i), - .rst_src_ni (rst_usb_48mhz_ni), - .rst_dst_ni (rst_ni), - .src_pulse_i (usb_clr_devaddr), - .dst_pulse_o (hw2reg.usbctrl.device_address.de) - ); + assign hw2reg.usbctrl.device_address.de = usb_clr_devaddr; assign hw2reg.usbctrl.device_address.d = '0; // AV empty is a single pulse so needs pulsesync @@ -766,6 +756,8 @@ .rst_ni, .clk_aon_i, .rst_aon_ni, + .clk_usb_48mhz_i, + .rst_usb_48mhz_ni, .tl_i (tl_i), .tl_o (tl_o), @@ -1142,7 +1134,8 @@ end assign usb_aon_wake_en_o = reg2hw.wake_config.wake_en.q; - assign usb_aon_wake_ack_o = reg2hw.wake_config.wake_ack.q; + assign usb_aon_wake_ack_o = reg2hw.wake_config.wake_ack.qe & + reg2hw.wake_config.wake_ack.q; // re-use I/O version to allow software override if needed assign usb_suspend_o = cio_suspend_o;
diff --git a/hw/ip/usbdev/rtl/usbdev_linkstate.sv b/hw/ip/usbdev/rtl/usbdev_linkstate.sv index 43196db..ebc8098 100644 --- a/hw/ip/usbdev/rtl/usbdev_linkstate.sv +++ b/hw/ip/usbdev/rtl/usbdev_linkstate.sv
@@ -13,8 +13,11 @@ input logic usb_dp_i, input logic usb_dn_i, input logic usb_oe_i, + input logic usb_pullup_en_i, input logic rx_jjj_det_i, + input logic rx_j_det_i, input logic sof_valid_i, + input logic resume_link_active_i, // pulse output logic link_disconnect_o, // level output logic link_connect_o, // level @@ -35,15 +38,16 @@ localparam logic [2:0] RESET_TIMEOUT = 3'd3; typedef enum logic [2:0] { - // Unpowered state - LinkDisconnect = 0, - // Powered states + // No power and/or no pull-up connected state + LinkDisconnected = 0, + // Powered / connected states LinkPowered = 1, - LinkPoweredSuspend = 2, + LinkPoweredSuspended = 2, // Active states - LinkActive = 3, LinkActiveNoSOF = 5, - LinkSuspend = 4 + LinkActive = 3, + LinkSuspended = 4, + LinkResuming = 6 } link_state_e; typedef enum logic [1:0] { @@ -78,10 +82,10 @@ // Events that are triggered by timeout logic ev_bus_inactive, ev_reset; - assign link_disconnect_o = (link_state_q == LinkDisconnect); - assign link_connect_o = (link_state_q != LinkDisconnect); - assign link_suspend_o = (link_state_q == LinkSuspend || - link_state_q == LinkPoweredSuspend); + assign link_disconnect_o = (link_state_q == LinkDisconnected); + assign link_connect_o = (link_state_q != LinkDisconnected); + assign link_suspend_o = (link_state_q == LinkSuspended || + link_state_q == LinkPoweredSuspended); assign link_active_o = (link_state_q == LinkActive) || (link_state_q == LinkActiveNoSOF); // Link state is stable, so we can output it to the register @@ -128,14 +132,15 @@ link_state_d = link_state_q; link_resume_o = 0; - // If VBUS ever goes away the link has disconnected - if (!see_pwr_sense) begin - link_state_d = LinkDisconnect; + // If VBUS ever goes away the link has disconnected (likewise if the + // pull-up goes away / user requested disconnection) + if (!see_pwr_sense || !usb_pullup_en_i) begin + link_state_d = LinkDisconnected; end else begin unique case (link_state_q) // No USB supply detected (USB spec: Attached) - LinkDisconnect: begin - if (see_pwr_sense) begin + LinkDisconnected: begin + if (see_pwr_sense & usb_pullup_en_i) begin link_state_d = LinkPowered; end end @@ -143,12 +148,16 @@ LinkPowered: begin if (ev_reset) begin link_state_d = LinkActiveNoSOF; + end else if (resume_link_active_i) begin + // Software-directed jump to resume from LinkSuspended, in case + // this module was previously powered down. + link_state_d = LinkResuming; end else if (ev_bus_inactive) begin - link_state_d = LinkPoweredSuspend; + link_state_d = LinkPoweredSuspended; end end - LinkPoweredSuspend: begin + LinkPoweredSuspended: begin if (ev_reset) begin link_state_d = LinkActiveNoSOF; end else if (ev_bus_active) begin @@ -157,13 +166,27 @@ end end + // An event occurred that brought the link out of LinkSuspended, but + // the end-of-resume signaling may not have occurred yet. + // Park here before starting to count towards not seeing SOF. Wait for + // the end of resume signaling before expecting SOF. The host will + // return the link to idle after a low-speed EOP. Instead of trying to + // capture the termination of resume signaling direclty, wait for + // any J / idle symbol (or a bus reset). + LinkResuming: begin + if (rx_j_det_i | ev_reset) begin + link_resume_o = 1; + link_state_d = LinkActiveNoSOF; + end + end + // Active but not yet seen a frame // One reason for getting stuck here is the host thinks it is a LS link // which could happen if the flipped bit does not match the actual pins // Annother is the SI is bad so good data is not recovered from the link LinkActiveNoSOF: begin if (ev_bus_inactive) begin - link_state_d = LinkSuspend; + link_state_d = LinkSuspended; end else if (sof_valid_i) begin link_state_d = LinkActive; end @@ -172,35 +195,34 @@ // Active (USB spec: Default / Address / Configured) LinkActive: begin if (ev_bus_inactive) begin - link_state_d = LinkSuspend; + link_state_d = LinkSuspended; end else if (ev_reset) begin link_state_d = LinkActiveNoSOF; end end - LinkSuspend: begin + LinkSuspended: begin if (ev_reset) begin link_resume_o = 1; link_state_d = LinkActiveNoSOF; end else if (ev_bus_active) begin - link_resume_o = 1; - link_state_d = LinkActive; + link_state_d = LinkResuming; end end default: begin - link_state_d = LinkDisconnect; + link_state_d = LinkDisconnected; end endcase // case (link_state_q) end end - `ASSERT(LinkStateValid_A, link_state_q inside {LinkDisconnect, LinkPowered, LinkPoweredSuspend, - LinkActiveNoSOF, LinkActive, LinkSuspend}, clk_48mhz_i) + `ASSERT(LinkStateValid_A, link_state_q inside {LinkDisconnected, LinkPowered, + LinkPoweredSuspended, LinkResuming, LinkActiveNoSOF, LinkActive, LinkSuspended}, clk_48mhz_i) always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin if (!rst_ni) begin - link_state_q <= LinkDisconnect; + link_state_q <= LinkDisconnected; end else begin link_state_q <= link_state_d; end
diff --git a/hw/ip/usbdev/rtl/usbdev_reg_pkg.sv b/hw/ip/usbdev/rtl/usbdev_reg_pkg.sv index 5fe06ca..1cce7fd 100644 --- a/hw/ip/usbdev/rtl/usbdev_reg_pkg.sv +++ b/hw/ip/usbdev/rtl/usbdev_reg_pkg.sv
@@ -204,9 +204,15 @@ typedef struct packed { struct packed { logic q; + logic qe; } enable; struct packed { + logic q; + logic qe; + } resume_link_active; + struct packed { logic [6:0] q; + logic qe; } device_address; } usbdev_reg2hw_usbctrl_reg_t; @@ -339,9 +345,11 @@ typedef struct packed { struct packed { logic q; + logic qe; } wake_en; struct packed { logic q; + logic qe; } wake_ack; } usbdev_reg2hw_wake_config_reg_t; @@ -541,25 +549,25 @@ // Register -> HW type typedef struct packed { - usbdev_reg2hw_intr_state_reg_t intr_state; // [398:382] - usbdev_reg2hw_intr_enable_reg_t intr_enable; // [381:365] - usbdev_reg2hw_intr_test_reg_t intr_test; // [364:331] - usbdev_reg2hw_alert_test_reg_t alert_test; // [330:329] - usbdev_reg2hw_usbctrl_reg_t usbctrl; // [328:321] - usbdev_reg2hw_ep_out_enable_mreg_t [11:0] ep_out_enable; // [320:309] - usbdev_reg2hw_ep_in_enable_mreg_t [11:0] ep_in_enable; // [308:297] - usbdev_reg2hw_avbuffer_reg_t avbuffer; // [296:291] - usbdev_reg2hw_rxfifo_reg_t rxfifo; // [290:270] - usbdev_reg2hw_rxenable_setup_mreg_t [11:0] rxenable_setup; // [269:258] - usbdev_reg2hw_rxenable_out_mreg_t [11:0] rxenable_out; // [257:246] - usbdev_reg2hw_out_stall_mreg_t [11:0] out_stall; // [245:234] - usbdev_reg2hw_in_stall_mreg_t [11:0] in_stall; // [233:222] - usbdev_reg2hw_configin_mreg_t [11:0] configin; // [221:54] - usbdev_reg2hw_iso_mreg_t [11:0] iso; // [53:42] - usbdev_reg2hw_data_toggle_clear_mreg_t [11:0] data_toggle_clear; // [41:18] - usbdev_reg2hw_phy_pins_drive_reg_t phy_pins_drive; // [17:8] - usbdev_reg2hw_phy_config_reg_t phy_config; // [7:2] - usbdev_reg2hw_wake_config_reg_t wake_config; // [1:0] + usbdev_reg2hw_intr_state_reg_t intr_state; // [404:388] + usbdev_reg2hw_intr_enable_reg_t intr_enable; // [387:371] + usbdev_reg2hw_intr_test_reg_t intr_test; // [370:337] + usbdev_reg2hw_alert_test_reg_t alert_test; // [336:335] + usbdev_reg2hw_usbctrl_reg_t usbctrl; // [334:323] + usbdev_reg2hw_ep_out_enable_mreg_t [11:0] ep_out_enable; // [322:311] + usbdev_reg2hw_ep_in_enable_mreg_t [11:0] ep_in_enable; // [310:299] + usbdev_reg2hw_avbuffer_reg_t avbuffer; // [298:293] + usbdev_reg2hw_rxfifo_reg_t rxfifo; // [292:272] + usbdev_reg2hw_rxenable_setup_mreg_t [11:0] rxenable_setup; // [271:260] + usbdev_reg2hw_rxenable_out_mreg_t [11:0] rxenable_out; // [259:248] + usbdev_reg2hw_out_stall_mreg_t [11:0] out_stall; // [247:236] + usbdev_reg2hw_in_stall_mreg_t [11:0] in_stall; // [235:224] + usbdev_reg2hw_configin_mreg_t [11:0] configin; // [223:56] + usbdev_reg2hw_iso_mreg_t [11:0] iso; // [55:44] + usbdev_reg2hw_data_toggle_clear_mreg_t [11:0] data_toggle_clear; // [43:20] + usbdev_reg2hw_phy_pins_drive_reg_t phy_pins_drive; // [19:10] + usbdev_reg2hw_phy_config_reg_t phy_config; // [9:4] + usbdev_reg2hw_wake_config_reg_t wake_config; // [3:0] } usbdev_reg2hw_t; // HW -> register type
diff --git a/hw/ip/usbdev/rtl/usbdev_reg_top.sv b/hw/ip/usbdev/rtl/usbdev_reg_top.sv index c40ac3b..77ad6e9 100644 --- a/hw/ip/usbdev/rtl/usbdev_reg_top.sv +++ b/hw/ip/usbdev/rtl/usbdev_reg_top.sv
@@ -9,6 +9,8 @@ module usbdev_reg_top ( input clk_i, input rst_ni, + input clk_usb_48mhz_i, + input rst_usb_48mhz_ni, input clk_aon_i, input rst_aon_ni, input tlul_pkg::tl_h2d_t tl_i, @@ -157,6 +159,18 @@ ); // cdc oversampling signals + logic sync_usb_48mhz_update; + prim_sync_reqack u_usb_48mhz_tgl ( + .clk_src_i(clk_usb_48mhz_i), + .rst_src_ni(rst_usb_48mhz_ni), + .clk_dst_i(clk_i), + .rst_dst_ni(rst_ni), + .req_chk_i(1'b1), + .src_req_i(1'b1), + .src_ack_o(), + .dst_req_o(sync_usb_48mhz_update), + .dst_ack_i(sync_usb_48mhz_update) + ); logic sync_aon_update; prim_sync_reqack u_aon_tgl ( .clk_src_i(clk_aon_i), @@ -267,10 +281,8 @@ logic alert_test_we; logic alert_test_wd; logic usbctrl_we; - logic usbctrl_enable_qs; - logic usbctrl_enable_wd; - logic [6:0] usbctrl_device_address_qs; - logic [6:0] usbctrl_device_address_wd; + logic [22:0] usbctrl_qs; + logic usbctrl_busy; logic ep_out_enable_we; logic ep_out_enable_enable_0_qs; logic ep_out_enable_enable_0_wd; @@ -654,15 +666,85 @@ logic phy_config_tx_osc_test_mode_qs; logic phy_config_tx_osc_test_mode_wd; logic wake_config_we; - logic wake_config_wake_en_qs; - logic wake_config_wake_en_wd; - logic wake_config_wake_ack_qs; - logic wake_config_wake_ack_wd; + logic [1:0] wake_config_qs; + logic wake_config_busy; logic [9:0] wake_events_qs; logic wake_events_busy; // Define register CDC handling. // CDC handling is done on a per-reg instead of per-field boundary. + logic usb_48mhz_usbctrl_enable_qs_int; + logic [6:0] usb_48mhz_usbctrl_device_address_qs_int; + logic [22:0] usb_48mhz_usbctrl_d; + logic [22:0] usb_48mhz_usbctrl_wdata; + logic usb_48mhz_usbctrl_we; + logic unused_usb_48mhz_usbctrl_wdata; + + always_comb begin + usb_48mhz_usbctrl_d = '0; + usb_48mhz_usbctrl_d[0] = usb_48mhz_usbctrl_enable_qs_int; + usb_48mhz_usbctrl_d[22:16] = usb_48mhz_usbctrl_device_address_qs_int; + end + + prim_reg_cdc #( + .DataWidth(23), + .ResetVal(23'h0), + .BitMask(23'h7f0003) + ) u_usbctrl_cdc ( + .clk_src_i (clk_i), + .rst_src_ni (rst_ni), + .clk_dst_i (clk_usb_48mhz_i), + .rst_dst_ni (rst_usb_48mhz_ni), + .src_update_i (sync_usb_48mhz_update), + .src_regwen_i ('0), + .src_we_i (usbctrl_we), + .src_re_i ('0), + .src_wd_i (reg_wdata[22:0]), + .src_busy_o (usbctrl_busy), + .src_qs_o (usbctrl_qs), // for software read back + .dst_d_i (usb_48mhz_usbctrl_d), + .dst_we_o (usb_48mhz_usbctrl_we), + .dst_re_o (), + .dst_regwen_o (), + .dst_wd_o (usb_48mhz_usbctrl_wdata) + ); + assign unused_usb_48mhz_usbctrl_wdata = ^usb_48mhz_usbctrl_wdata; + + logic aon_wake_config_wake_en_qs_int; + logic [1:0] aon_wake_config_d; + logic [1:0] aon_wake_config_wdata; + logic aon_wake_config_we; + logic unused_aon_wake_config_wdata; + + always_comb begin + aon_wake_config_d = '0; + aon_wake_config_d[0] = aon_wake_config_wake_en_qs_int; + end + + prim_reg_cdc #( + .DataWidth(2), + .ResetVal(2'h0), + .BitMask(2'h3) + ) u_wake_config_cdc ( + .clk_src_i (clk_i), + .rst_src_ni (rst_ni), + .clk_dst_i (clk_aon_i), + .rst_dst_ni (rst_aon_ni), + .src_update_i (sync_aon_update), + .src_regwen_i ('0), + .src_we_i (wake_config_we), + .src_re_i ('0), + .src_wd_i (reg_wdata[1:0]), + .src_busy_o (wake_config_busy), + .src_qs_o (wake_config_qs), // for software read back + .dst_d_i (aon_wake_config_d), + .dst_we_o (aon_wake_config_we), + .dst_re_o (), + .dst_regwen_o (), + .dst_wd_o (aon_wake_config_wdata) + ); + assign unused_aon_wake_config_wdata = ^aon_wake_config_wdata; + logic [2:0] aon_wake_events_state_qs_int; logic aon_wake_events_disconnected_qs_int; logic aon_wake_events_bus_reset_qs_int; @@ -1815,23 +1897,48 @@ .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (1'h0) ) u_usbctrl_enable ( - .clk_i (clk_i), - .rst_ni (rst_ni), + .clk_i (clk_usb_48mhz_i), + .rst_ni (rst_usb_48mhz_ni), // from register interface - .we (usbctrl_we), - .wd (usbctrl_enable_wd), + .we (usb_48mhz_usbctrl_we), + .wd (usb_48mhz_usbctrl_wdata[0]), // from internal hardware .de (1'b0), .d ('0), // to internal hardware - .qe (), + .qe (reg2hw.usbctrl.enable.qe), .q (reg2hw.usbctrl.enable.q), // to register interface (read) - .qs (usbctrl_enable_qs) + .qs (usb_48mhz_usbctrl_enable_qs_int) + ); + + // F[resume_link_active]: 1:1 + prim_subreg #( + .DW (1), + .SwAccess(prim_subreg_pkg::SwAccessWO), + .RESVAL (1'h0) + ) u_usbctrl_resume_link_active ( + .clk_i (clk_usb_48mhz_i), + .rst_ni (rst_usb_48mhz_ni), + + // from register interface + .we (usb_48mhz_usbctrl_we), + .wd (usb_48mhz_usbctrl_wdata[1]), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.usbctrl.resume_link_active.qe), + .q (reg2hw.usbctrl.resume_link_active.q), + + // to register interface (read) + .qs () ); // F[device_address]: 22:16 @@ -1840,23 +1947,23 @@ .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (7'h0) ) u_usbctrl_device_address ( - .clk_i (clk_i), - .rst_ni (rst_ni), + .clk_i (clk_usb_48mhz_i), + .rst_ni (rst_usb_48mhz_ni), // from register interface - .we (usbctrl_we), - .wd (usbctrl_device_address_wd), + .we (usb_48mhz_usbctrl_we), + .wd (usb_48mhz_usbctrl_wdata[22:16]), // from internal hardware .de (hw2reg.usbctrl.device_address.de), .d (hw2reg.usbctrl.device_address.d), // to internal hardware - .qe (), + .qe (reg2hw.usbctrl.device_address.qe), .q (reg2hw.usbctrl.device_address.q), // to register interface (read) - .qs (usbctrl_device_address_qs) + .qs (usb_48mhz_usbctrl_device_address_qs_int) ); @@ -6574,48 +6681,48 @@ .SwAccess(prim_subreg_pkg::SwAccessRW), .RESVAL (1'h0) ) u_wake_config_wake_en ( - .clk_i (clk_i), - .rst_ni (rst_ni), + .clk_i (clk_aon_i), + .rst_ni (rst_aon_ni), // from register interface - .we (wake_config_we), - .wd (wake_config_wake_en_wd), + .we (aon_wake_config_we), + .wd (aon_wake_config_wdata[0]), // from internal hardware .de (1'b0), .d ('0), // to internal hardware - .qe (), + .qe (reg2hw.wake_config.wake_en.qe), .q (reg2hw.wake_config.wake_en.q), // to register interface (read) - .qs (wake_config_wake_en_qs) + .qs (aon_wake_config_wake_en_qs_int) ); // F[wake_ack]: 1:1 prim_subreg #( .DW (1), - .SwAccess(prim_subreg_pkg::SwAccessRW), + .SwAccess(prim_subreg_pkg::SwAccessWO), .RESVAL (1'h0) ) u_wake_config_wake_ack ( - .clk_i (clk_i), - .rst_ni (rst_ni), + .clk_i (clk_aon_i), + .rst_ni (rst_aon_ni), // from register interface - .we (wake_config_we), - .wd (wake_config_wake_ack_wd), + .we (aon_wake_config_we), + .wd (aon_wake_config_wdata[1]), // from internal hardware .de (1'b0), .d ('0), // to internal hardware - .qe (), + .qe (reg2hw.wake_config.wake_ack.qe), .q (reg2hw.wake_config.wake_ack.q), // to register interface (read) - .qs (wake_config_wake_ack_qs) + .qs () ); @@ -6886,9 +6993,8 @@ assign alert_test_wd = reg_wdata[0]; assign usbctrl_we = addr_hit[4] & reg_we & !reg_error; - assign usbctrl_enable_wd = reg_wdata[0]; - assign usbctrl_device_address_wd = reg_wdata[22:16]; + assign ep_out_enable_we = addr_hit[5] & reg_we & !reg_error; assign ep_out_enable_enable_0_wd = reg_wdata[0]; @@ -7264,9 +7370,7 @@ assign phy_config_tx_osc_test_mode_wd = reg_wdata[7]; assign wake_config_we = addr_hit[32] & reg_we & !reg_error; - assign wake_config_wake_en_wd = reg_wdata[0]; - assign wake_config_wake_ack_wd = reg_wdata[1]; // Read data return always_comb begin @@ -7337,10 +7441,8 @@ end addr_hit[4]: begin - reg_rdata_next[0] = usbctrl_enable_qs; - reg_rdata_next[22:16] = usbctrl_device_address_qs; + reg_rdata_next = DW'(usbctrl_qs); end - addr_hit[5]: begin reg_rdata_next[0] = ep_out_enable_enable_0_qs; reg_rdata_next[1] = ep_out_enable_enable_1_qs; @@ -7618,10 +7720,8 @@ end addr_hit[32]: begin - reg_rdata_next[0] = wake_config_wake_en_qs; - reg_rdata_next[1] = wake_config_wake_ack_qs; + reg_rdata_next = DW'(wake_config_qs); end - addr_hit[33]: begin reg_rdata_next = DW'(wake_events_qs); end @@ -7641,6 +7741,12 @@ always_comb begin reg_busy_sel = '0; unique case (1'b1) + addr_hit[4]: begin + reg_busy_sel = usbctrl_busy; + end + addr_hit[32]: begin + reg_busy_sel = wake_config_busy; + end addr_hit[33]: begin reg_busy_sel = wake_events_busy; end
diff --git a/hw/ip/usbdev/rtl/usbdev_usbif.sv b/hw/ip/usbdev/rtl/usbdev_usbif.sv index 88235e8..3ff52ae 100644 --- a/hw/ip/usbdev/rtl/usbdev_usbif.sv +++ b/hw/ip/usbdev/rtl/usbdev_usbif.sv
@@ -78,6 +78,7 @@ input logic cfg_rx_differential_i, // 1: use differential rx data on usb_d_i input logic tx_osc_test_mode_i, // Oscillator test mode: constant JK output input logic [NEndpoints-1:0] data_toggle_clear_i, // Clear the data toggles for an EP + input logic resume_link_active_i, // Jump from LinkPowered to LinkResuming // status output logic frame_start_o, @@ -262,6 +263,7 @@ logic [10:0] frame_index_raw; logic rx_jjj_det; + logic rx_j_det; usb_fs_nb_pe #( .NumOutEps (NEndpoints), @@ -271,6 +273,7 @@ .clk_48mhz_i (clk_48mhz_i), .rst_ni (rst_ni), .link_reset_i (link_reset), + .link_active_i (link_active_o), .cfg_eop_single_bit_i (cfg_eop_single_bit_i), .cfg_rx_differential_i (cfg_rx_differential_i), @@ -317,6 +320,7 @@ // rx status .rx_jjj_det_o (rx_jjj_det), + .rx_j_det_o (rx_j_det), // error signals .rx_crc_err_o (rx_crc_err_o), @@ -357,23 +361,26 @@ end usbdev_linkstate u_usbdev_linkstate ( - .clk_48mhz_i (clk_48mhz_i), - .rst_ni (rst_ni), - .us_tick_i (us_tick), - .usb_sense_i (usb_sense_i), - .usb_dp_i (usb_dp_i), - .usb_dn_i (usb_dn_i), - .usb_oe_i (usb_oe_o), - .rx_jjj_det_i (rx_jjj_det), - .sof_valid_i (sof_valid), - .link_disconnect_o (link_disconnect_o), - .link_connect_o (link_connect_o), - .link_reset_o (link_reset), - .link_active_o (link_active_o), - .link_suspend_o (link_suspend_o), - .link_resume_o (link_resume_o), - .link_state_o (link_state_o), - .host_lost_o (host_lost_o) + .clk_48mhz_i (clk_48mhz_i), + .rst_ni (rst_ni), + .us_tick_i (us_tick), + .usb_sense_i (usb_sense_i), + .usb_dp_i (usb_dp_i), + .usb_dn_i (usb_dn_i), + .usb_oe_i (usb_oe_o), + .usb_pullup_en_i (enable_i), + .rx_jjj_det_i (rx_jjj_det), + .rx_j_det_i (rx_j_det), + .sof_valid_i (sof_valid), + .resume_link_active_i (resume_link_active_i), + .link_disconnect_o (link_disconnect_o), + .link_connect_o (link_connect_o), + .link_reset_o (link_reset), + .link_active_o (link_active_o), + .link_suspend_o (link_suspend_o), + .link_resume_o (link_resume_o), + .link_state_o (link_state_o), + .host_lost_o (host_lost_o) ); ////////////////
diff --git a/sw/device/lib/dif/dif_usbdev.c b/sw/device/lib/dif/dif_usbdev.c index d4e2253..2aa08aa 100644 --- a/sw/device/lib/dif/dif_usbdev.c +++ b/sw/device/lib/dif/dif_usbdev.c
@@ -725,20 +725,26 @@ USBDEV_USBSTAT_LINK_STATE_MASK, USBDEV_USBSTAT_LINK_STATE_OFFSET); switch (val) { - case USBDEV_USBSTAT_LINK_STATE_VALUE_DISCONNECT: + case USBDEV_USBSTAT_LINK_STATE_VALUE_DISCONNECTED: *link_state = kDifUsbdevLinkStateDisconnected; break; case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED: *link_state = kDifUsbdevLinkStatePowered; break; - case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED_SUSPEND: - *link_state = kDifUsbdevLinkStatePoweredSuspend; + case USBDEV_USBSTAT_LINK_STATE_VALUE_POWERED_SUSPENDED: + *link_state = kDifUsbdevLinkStatePoweredSuspended; break; case USBDEV_USBSTAT_LINK_STATE_VALUE_ACTIVE: *link_state = kDifUsbdevLinkStateActive; break; - case USBDEV_USBSTAT_LINK_STATE_VALUE_SUSPEND: - *link_state = kDifUsbdevLinkStateSuspend; + case USBDEV_USBSTAT_LINK_STATE_VALUE_SUSPENDED: + *link_state = kDifUsbdevLinkStateSuspended; + break; + case USBDEV_USBSTAT_LINK_STATE_VALUE_ACTIVE_NOSOF: + *link_state = kDifUsbdevLinkStateActiveNoSof; + break; + case USBDEV_USBSTAT_LINK_STATE_VALUE_RESUMING: + *link_state = kDifUsbdevLinkStateResuming; break; default: return kDifError;
diff --git a/sw/device/lib/dif/dif_usbdev.h b/sw/device/lib/dif/dif_usbdev.h index 8322ea2..377effe 100644 --- a/sw/device/lib/dif/dif_usbdev.h +++ b/sw/device/lib/dif/dif_usbdev.h
@@ -579,9 +579,11 @@ typedef enum dif_usbdev_link_state { kDifUsbdevLinkStateDisconnected, kDifUsbdevLinkStatePowered, - kDifUsbdevLinkStatePoweredSuspend, + kDifUsbdevLinkStatePoweredSuspended, kDifUsbdevLinkStateActive, - kDifUsbdevLinkStateSuspend, + kDifUsbdevLinkStateSuspended, + kDifUsbdevLinkStateActiveNoSof, + kDifUsbdevLinkStateResuming, } dif_usbdev_link_state_t; /**