From 1ad6ef3f1a2f3c10b34d1e94c5c24d452f37e728 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Mon, 17 Jul 2023 16:14:56 +0200 Subject: [PATCH 1/3] gateware.usb2: add disconnect signal to USBResetSequencer --- luna/gateware/usb/usb2/device.py | 2 ++ luna/gateware/usb/usb2/reset.py | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/luna/gateware/usb/usb2/device.py b/luna/gateware/usb/usb2/device.py index 36ed8a84..086917bb 100644 --- a/luna/gateware/usb/usb2/device.py +++ b/luna/gateware/usb/usb2/device.py @@ -243,6 +243,8 @@ def elaborate(self, platform): reset_sequencer.vbus_connected .eq(~self.utmi.session_end), reset_sequencer.line_state .eq(self.utmi.line_state), + + reset_sequencer.disconnect .eq(~self.connect), ] diff --git a/luna/gateware/usb/usb2/reset.py b/luna/gateware/usb/usb2/reset.py index f39707ee..cae87848 100644 --- a/luna/gateware/usb/usb2/reset.py +++ b/luna/gateware/usb/usb2/reset.py @@ -65,6 +65,8 @@ class USBResetSequencer(Elaboratable): be held in perpetual bus reset, and reset handshaking will be disabled. line_state: Signal(2), input The UTMI linestate signals; used to read the current state of the USB D+ and D- lines. + disconnect: Signal(), input + If set, the device will be switched into non-driving operating mode to force a host disconnect. bus_reset: Signal(), output Strobe; pulses high for one cycle when a bus reset is detected. This signal indicates that the @@ -123,6 +125,8 @@ def __init__(self): self.vbus_connected = Signal() self.line_state = Signal(2) + self.disconnect = Signal() + self.bus_reset = Signal() self.suspended = Signal() @@ -173,6 +177,13 @@ def elaborate(self, platform): with m.Else(): m.d.comb += bus_idle.eq(self.line_state == self._LINE_STATE_LS_J) + # Switch to non-driving operating mode when we're not + # connected and we're not seeing an SE0. + with m.If(self.disconnect & (self.line_state != self._LINE_STATE_SE0)): + m.d.usb += [ + self.operating_mode.eq(UTMIOperatingMode.NON_DRIVING), + timer.eq(0), + ] # # Core reset sequences. From f2b479a7bca8b1bbdb9f592cd66a31ba1479ce1d Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 31 May 2024 11:18:28 +0200 Subject: [PATCH 2/3] gateware.usb2: hold UTMI in non-driving operating mode for at least Tddis=2.5uS during forced disconnect --- luna/gateware/usb/usb2/reset.py | 35 ++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/luna/gateware/usb/usb2/reset.py b/luna/gateware/usb/usb2/reset.py index cae87848..805cecac 100644 --- a/luna/gateware/usb/usb2/reset.py +++ b/luna/gateware/usb/usb2/reset.py @@ -170,20 +170,13 @@ def elaborate(self, platform): with m.If(self.current_speed == USBSpeed.HIGH): m.d.comb += bus_idle.eq(self.line_state == self._LINE_STATE_SQUELCH) - # Full and low-speed busses see a 'J' state when idle, due to the device pull-up restistors. + # Full and low-speed busses see a 'J' state when idle, due to the device pull-up resistors. # (The line_state values for these are flipped between speeds.) [USB2.0: 7.1.7.4.1; USB2.0: Table 7-2]. with m.Elif(self.current_speed == USBSpeed.FULL): m.d.comb += bus_idle.eq(self.line_state == self._LINE_STATE_FS_HS_J) with m.Else(): m.d.comb += bus_idle.eq(self.line_state == self._LINE_STATE_LS_J) - # Switch to non-driving operating mode when we're not - # connected and we're not seeing an SE0. - with m.If(self.disconnect & (self.line_state != self._LINE_STATE_SE0)): - m.d.usb += [ - self.operating_mode.eq(UTMIOperatingMode.NON_DRIVING), - timer.eq(0), - ] # # Core reset sequences. @@ -211,7 +204,9 @@ def elaborate(self, platform): # potential reset. Keep our timer at zero. with m.If(self.line_state != self._LINE_STATE_SE0): m.d.usb += timer.eq(0) - + # Enter forced disconnect when self.disconnect is high. + with m.If(self.disconnect): + m.next = "DISCONNECT" # If VBUS isn't connected, don't go through the whole reset process; # but also consider ourselves permanently in reset. This ensures we @@ -253,6 +248,9 @@ def elaborate(self, platform): # potential reset. Keep our timer at zero. with m.If(self.line_state != self._LINE_STATE_SE0): m.d.usb += timer.eq(0) + # Enter forced disconnect when self.disconnect is high. + with m.If(self.disconnect): + m.next = "DISCONNECT" # If VBUS isn't connected, our device/host relationship is effectively # a blank state. We'll want to present our detection pull-up to the host, @@ -519,4 +517,23 @@ def elaborate(self, platform): with m.Else(): m.next = 'START_HS_DETECTION' + + # DISCONNECT -- our device has entered a forced USB disconnect; hold the device in + # NON_DRIVING operating mode for Tddis=0.25us and wait for self.disconnect to go low. + with m.State('DISCONNECT'): + m.d.usb += self.operating_mode.eq(UTMIOperatingMode.NON_DRIVING) + + # A disconnect condition is indicated if the host or hub is not driving the data lines and an + # SE0 persists on a downstream facing port for more than Tddis. + # [USB2.0: 7.1.7.3]. + tddis = Signal() + with m.If(timer == self._CYCLES_2P5_MICROSECONDS): + m.d.usb += tddis.eq(1) + + # Exit DISCONNECT once the Tddis timer has expired and self.disconnect is low. + with m.If((~self.disconnect) & tddis): + m.d.usb += tddis.eq(0) + m.d.usb += timer.eq(0) + m.next = 'START_HS_DETECTION' + return m From c546c84f17cc31e3da887aeac6b23cb1f048b024 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Fri, 7 Jun 2024 10:12:59 +0200 Subject: [PATCH 3/3] gateware.usb2: reset current speed, operating mode and termination select on exiting forced disconnect --- luna/gateware/usb/usb2/reset.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/luna/gateware/usb/usb2/reset.py b/luna/gateware/usb/usb2/reset.py index 805cecac..a41fb246 100644 --- a/luna/gateware/usb/usb2/reset.py +++ b/luna/gateware/usb/usb2/reset.py @@ -532,8 +532,12 @@ def elaborate(self, platform): # Exit DISCONNECT once the Tddis timer has expired and self.disconnect is low. with m.If((~self.disconnect) & tddis): - m.d.usb += tddis.eq(0) - m.d.usb += timer.eq(0) - m.next = 'START_HS_DETECTION' + m.d.usb += [ + tddis.eq(0), + self.current_speed.eq(USBSpeed.FULL), + self.operating_mode.eq(UTMIOperatingMode.NORMAL), + self.termination_select.eq(1), + ] + m.next = 'INITIALIZE' return m