[usbdev] Generate internal SOF when needed
If the host's SOF is missing, generate an internal SOF. Wait an extra
5us for the maximal frequency difference allowed. If no SOF is received,
increment the frame number and signal a new frame.
Signed-off-by: Alexander Williams <awill@google.com>
diff --git a/hw/ip/usbdev/rtl/usbdev.sv b/hw/ip/usbdev/rtl/usbdev.sv
index dbb7404..de36a30 100644
--- a/hw/ip/usbdev/rtl/usbdev.sv
+++ b/hw/ip/usbdev/rtl/usbdev.sv
@@ -136,7 +136,7 @@
logic usb_event_rx_bitstuff_err;
logic usb_event_in_err;
logic usb_event_out_err;
- logic usb_event_frame;
+ logic usb_event_frame, usb_event_sof;
logic usb_link_active;
logic event_link_reset, event_link_suspend, event_link_resume;
@@ -585,6 +585,7 @@
// status
.frame_o (usb_frame),
.frame_start_o (usb_event_frame),
+ .sof_valid_o (usb_event_sof),
.link_state_o (usb_link_state),
.link_disconnect_o (usb_event_disconnect),
.link_powered_o (usb_event_powered),
@@ -1080,7 +1081,7 @@
);
// Directly forward the pulse unless disabled.
- assign usb_ref_pulse_o = usb_ref_disable ? 1'b0 : usb_event_frame;
+ assign usb_ref_pulse_o = usb_ref_disable ? 1'b0 : usb_event_sof;
// The first pulse is always ignored, but causes the valid to be asserted.
// The valid signal is deasserted when:
diff --git a/hw/ip/usbdev/rtl/usbdev_linkstate.sv b/hw/ip/usbdev/rtl/usbdev_linkstate.sv
index 4108925..2b28f8b 100644
--- a/hw/ip/usbdev/rtl/usbdev_linkstate.sv
+++ b/hw/ip/usbdev/rtl/usbdev_linkstate.sv
@@ -28,6 +28,7 @@
output logic link_suspend_o, // level
output logic link_resume_o, // pulse
output logic host_lost_o, // level
+ output logic sof_missed_o, // pulse
output logic [2:0] link_state_o
);
@@ -38,6 +39,10 @@
// confuse the 2 *low-speed* bit times (1.33us) of SE0 that terminate resume
// signaling. Use 3us here.
localparam logic [2:0] RESET_TIMEOUT = 3'd3;
+ // Consider an SOF lost after 1.005 ms. The extra 5 us helps accommodate
+ // the worst case frequency difference between the host and device, due to a
+ // +/- 2500 ppm range around 12 MHz.
+ localparam logic [9:0] SOF_TIMEOUT = 10'd1005;
typedef enum logic [2:0] {
// No power and/or no pull-up connected state
@@ -349,24 +354,37 @@
end
end
- /////////////////////////
- // Host loss detection //
- /////////////////////////
- // host_lost if no sof in 4.096ms (supposed to be every 1ms)
- // and the link is active
- logic [12:0] host_presence_timer;
+ /////////////////////////////////////////
+ // Host loss and missing sof detection //
+ /////////////////////////////////////////
+ // sof_missed if no SOF was observed in 1.005ms and the link is active
+ // host_lost if 4 frames have gone by without observing a SOF
+ logic [2:0] missed_sof_count;
+ logic [9:0] missing_sof_timer;
- assign host_lost_o = host_presence_timer[12];
+ assign host_lost_o = missed_sof_count[2];
always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
if (!rst_ni) begin
- host_presence_timer <= '0;
+ missed_sof_count <= '0;
end else begin
if (sof_valid_i || !link_active_o || link_reset) begin
- host_presence_timer <= '0;
- end else if (us_tick_i && !host_lost_o) begin
- host_presence_timer <= host_presence_timer + 1;
+ missed_sof_count <= '0;
+ end else if (sof_missed_o && !host_lost_o) begin
+ missed_sof_count <= missed_sof_count + '1;
end
end
end
+ assign sof_missed_o = (missing_sof_timer == SOF_TIMEOUT);
+ always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ missing_sof_timer <= '0;
+ end else begin
+ if (sof_missed_o || sof_valid_i || !link_active_o || link_reset) begin
+ missing_sof_timer <= '0;
+ end else if (us_tick_i) begin
+ missing_sof_timer <= missing_sof_timer + 1;
+ end
+ end
+ end
endmodule
diff --git a/hw/ip/usbdev/rtl/usbdev_usbif.sv b/hw/ip/usbdev/rtl/usbdev_usbif.sv
index e1eca63..790cc4d 100644
--- a/hw/ip/usbdev/rtl/usbdev_usbif.sv
+++ b/hw/ip/usbdev/rtl/usbdev_usbif.sv
@@ -82,8 +82,10 @@
input logic resume_link_active_i, // Jump from LinkPowered to LinkResuming
// status
- output logic frame_start_o,
+ output logic frame_start_o, // Pulses with host-generated and internal SOF
output logic [10:0] frame_o,
+ output logic sof_valid_o, // Pulses with only host-generated SOF.
+ // Used for clock sync.
output logic [2:0] link_state_o,
output logic link_disconnect_o,
output logic link_powered_o,
@@ -116,7 +118,6 @@
logic mem_read;
logic [SramAw-1:0] mem_waddr, mem_raddr;
logic link_reset;
- logic sof_valid;
// Make sure out_endpoint_o can safely be used to index signals of NEndpoints width.
assign out_endpoint_val_o = int'(out_ep_current) < NEndpoints;
@@ -124,7 +125,6 @@
assign link_reset_o = link_reset;
assign clr_devaddr_o = ~enable_i | link_reset;
- assign frame_start_o = sof_valid;
assign link_out_err_o = out_ep_rollback;
always_comb begin
@@ -329,13 +329,14 @@
.rx_bitstuff_err_o (rx_bitstuff_err_o),
// sof interface
- .sof_valid_o (sof_valid),
+ .sof_valid_o (sof_valid_o),
.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
@@ -350,15 +351,27 @@
end
end
- // Capture frame number (host sends evert 1ms)
- // TODO(#10678): Handle missing SOF packets
+ // Capture frame number (host sends every 1ms)
+ // Generate an internal SOF if the host's is missing.
+ logic [10:0] frame_d, frame_q;
+
+ assign frame_o = frame_q;
+ assign frame_start_o = (frame_q != frame_d);
+
+ always_comb begin
+ frame_d = frame_q;
+ if (sof_valid_o) begin
+ frame_d = frame_index_raw;
+ end else if (do_internal_sof) begin
+ frame_d = frame_q + 1;
+ end
+ end
+
always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
if (!rst_ni) begin
- frame_o <= '0;
+ frame_q <= '0;
end else begin
- if (sof_valid) begin
- frame_o <= frame_index_raw;
- end
+ frame_q <= frame_d;
end
end
@@ -373,7 +386,7 @@
.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),
+ .sof_valid_i (sof_valid_o),
.resume_link_active_i (resume_link_active_i),
.link_disconnect_o (link_disconnect_o),
.link_powered_o (link_powered_o),
@@ -382,7 +395,8 @@
.link_suspend_o (link_suspend_o),
.link_resume_o (link_resume_o),
.link_state_o (link_state_o),
- .host_lost_o (host_lost_o)
+ .host_lost_o (host_lost_o),
+ .sof_missed_o (do_internal_sof)
);
////////////////