Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Full-Speed PRE handling #15

Open
desowin opened this issue Jan 10, 2023 · 3 comments · May be fixed by #19
Open

Feature request: Full-Speed PRE handling #15

desowin opened this issue Jan 10, 2023 · 3 comments · May be fixed by #19

Comments

@desowin
Copy link

desowin commented Jan 10, 2023

Copied from openvizsla#54

Currently the capture speed is fixed for a capture session duration. This is always fine for High-Speed and Low-Speed captures and mostly fine for Full-Speed captures. There is one corner case when it is not fine for Full-Speed captures: when capture the link between host and hub while there is Low-Speed device connected.

Due to very strict timings, the speed change must be handled by FPGA. The Low-Speed packet captured on Full-Speed link should probably be indicated with new flag. Universal Serial Bus Specification Revision 2.0 section 8.6.5 Low-speed Transactions describes what is sent at Full-Speed and what at Low-Speed.

To obtain best Wireshark experience, the data would have to be written to pcapng format. I can handle the pcapng related work if the bitstream handles the PRE processing and sets low-speed flag on packets.

@desowin
Copy link
Author

desowin commented Apr 6, 2023

FS-hub-LS-keyboard-IN-NAK.sal.zip is zipped (zipped because GitHub does not accept .sal attachments) sample Saleae Logic 2 capture file showing ULPI signals together with D+/D-. I would appreciate if someone could convert this capture into sigrok PulseView project.

@desowin
Copy link
Author

desowin commented Apr 11, 2023

I have experimented with reconfiguring transceiver to either LS transceiver or FS transceiver for LS packets. The gateware change I did was definitely incomplete as it reconfigured back to FS after first EOP while it should reconfigure only after the response from device (if there is any, e.g. there won't be response after SETUP token - FS PRE LS SETUP FS PRE LS DATA0 LS ACK). However the reconfiguration cannot be done if the LS packet from host is not correctly received.

The patch applied on top of 9e3cd34 was

diff --git a/software/fpga/ov3/ovhw/ulpi.py b/software/fpga/ov3/ovhw/ulpi.py
index 677bf48..59461e7 100644
--- a/software/fpga/ov3/ovhw/ulpi.py
+++ b/software/fpga/ov3/ovhw/ulpi.py
@@ -69,6 +69,14 @@ class ULPI_ctrl(Module):
 		ulpi_data_tristate_next = Signal()
 		ulpi_stp_next = Signal()
 
+		first_byte_received = Signal()
+		switch_to_low_speed = Signal()
+		switch_to_full_speed = Signal()
+		full_speed_after_eop = Signal()
+		reg_write_addr = Signal(8)
+		reg_write_data = Signal(8)
+		internal_reg_write = Signal(1)
+
 		ulpi_state_rx = Signal()
 		ulpi_state_rrd = Signal()
 		
@@ -144,19 +152,37 @@ class ULPI_ctrl(Module):
 			ulpi_data_next.eq(0x00), # NOOP
 			ulpi_data_tristate_next.eq(0),
 			ulpi_stp_next.eq(0),
-			If(~ulpi_bus.dir & ~ulpi_bus.nxt & ~(RegWriteReq | RegReadReq), 
+			If(~ulpi_bus.dir & ~ulpi_bus.nxt & ~(switch_to_low_speed | switch_to_full_speed | RegWriteReq | RegReadReq),
 				NextState("IDLE")
 			).Elif(ulpi_bus.dir, # TA, and then either RXCMD or Data
 				NextState("RX"),
+				NextValue(first_byte_received, 0),
 				ulpi_data_tristate_next.eq(1),
 				# If dir & nxt, we're starting a packet, so stuff a custom SOP
 				If(ulpi_bus.nxt,
 					ulpi_rx_stuff.eq(1),
 					ulpi_rx_stuff_d.eq(RXCMD_MAGIC_SOP)
 				)
+			).Elif(switch_to_low_speed,
+				NextState("RW0"),
+				ulpi_data_next.eq(0x84), # REGW FUNC_CTL
+				NextValue(reg_write_addr, 0x84),
+				NextValue(reg_write_data, 0x4b),
+				NextValue(internal_reg_write, 1),
+				ulpi_data_tristate_next.eq(0),
+			).Elif(switch_to_full_speed,
+				NextState("RW0"),
+				ulpi_data_next.eq(0x84), # REGW FUNC_CTL
+				NextValue(reg_write_addr, 0x84),
+				NextValue(reg_write_data, 0x49),
+				NextValue(internal_reg_write, 1),
+				ulpi_data_tristate_next.eq(0),
 			).Elif(RegWriteReq,
 				NextState("RW0"),
 				ulpi_data_next.eq(0x80 | ulpi_reg.waddr), # REGW
+				NextValue(reg_write_addr, 0x80 | ulpi_reg.waddr),
+				NextValue(reg_write_data, ulpi_reg.wdata),
+				NextValue(internal_reg_write, 0),
 				ulpi_data_tristate_next.eq(0),
 				ulpi_stp_next.eq(0)
 			).Elif(RegReadReq,
@@ -172,12 +198,24 @@ class ULPI_ctrl(Module):
 			If(ulpi_bus.dir, # stay in RX
 				NextState("RX"),
 				ulpi_state_rx.eq(1),
-				ulpi_data_tristate_next.eq(1)
+				ulpi_data_tristate_next.eq(1),
+				If(ulpi_bus.nxt & ~first_byte_received,
+					NextValue(first_byte_received, 1),
+					If(ulpi_bus.di == 0x3C,
+						# Request stop on PRE packet ID
+						ulpi_stp_next.eq(1),
+						NextValue(switch_to_low_speed, 1)
+					)
+				)
 			).Else( # TA back to idle
 				# Stuff an EOP on return to idle
 				ulpi_rx_stuff.eq(1),
 				ulpi_rx_stuff_d.eq(RXCMD_MAGIC_EOP),
-				ulpi_data_tristate_next.eq(0), 
+				ulpi_data_tristate_next.eq(0),
+				If(full_speed_after_eop,
+					NextValue(switch_to_full_speed, 1),
+					NextValue(full_speed_after_eop, 0),
+				),
 				NextState("IDLE")
 			))
 	
@@ -186,7 +224,7 @@ class ULPI_ctrl(Module):
 				NextState("RX"),
 				ulpi_data_tristate_next.eq(1),
 			).Elif(~ulpi_bus.dir,
-				ulpi_data_next.eq(0x80 | ulpi_reg.waddr), # REGW
+				ulpi_data_next.eq(reg_write_addr), # REGW
 				ulpi_data_tristate_next.eq(0),
 				ulpi_stp_next.eq(0),
 				If(ulpi_bus.nxt, NextState("RWD")).Else(NextState("RW0")),
@@ -200,7 +238,7 @@ class ULPI_ctrl(Module):
 				ulpi_data_tristate_next.eq(1)
 			).Elif(~ulpi_bus.dir & ulpi_bus.nxt,
 				NextState("RWS"),
-				ulpi_data_next.eq(ulpi_reg.wdata),
+				ulpi_data_next.eq(reg_write_data),
 				ulpi_data_tristate_next.eq(0),
 				ulpi_stp_next.eq(0)
 			).Else(
@@ -214,7 +252,14 @@ class ULPI_ctrl(Module):
 				ulpi_data_next.eq(0x00), # NOOP
 				ulpi_data_tristate_next.eq(0),
 				ulpi_stp_next.eq(1),
-				RegWriteAckSet.eq(1)
+				If(internal_reg_write,
+					NextValue(internal_reg_write, 0),
+					NextValue(full_speed_after_eop, switch_to_low_speed),
+					NextValue(switch_to_low_speed, 0),
+					NextValue(switch_to_full_speed, 0),
+				).Else(
+					RegWriteAckSet.eq(1),
+				)
 			).Elif(ulpi_bus.dir,
 				NextState("RX"),
 				ulpi_data_tristate_next.eq(1),

However it seems that this approach cannot really work. The FS transceiver for LS packets is really intended for host use (i.e. when the transceiver is actively driving the bus). Just switching to the LS mode does not reset transceiver and therefore it starts picking up bits immediately - without waiting for the SYNC.

I think manual steps to extract the bits out of the unsynchronized stream are doomed to fail. 2023-04-11 FS hub LS keyboard.zip contains two capture files where the same LS packet is offset by one LS bit (from each other capture).

Probably the only way to handle PRE packets is to do FS/LS capture in gateware (D+ and D- lines are connected to FPGA however whether the pins the signals are connected to are suitable for the task is currently unknown).

@desowin
Copy link
Author

desowin commented Apr 11, 2023

Actually it seems that setting Reset bit in Function Control alongside speed bit makes it possible to receive Low-speed packets correctly. However it needs careful state transitions because it is easy to prematurely switch back to Full-speed.

@desowin desowin linked a pull request May 6, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant