../src/lowrisc_ip_usbdev_0.1/rtl/usbdev_linkstate.sv Cov: 100%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // Link state detection
   6: //
   7: 
   8: module usbdev_linkstate (
   9:   input  logic clk_48mhz_i,
  10:   input  logic rst_ni,
  11:   input  logic us_tick_i,
  12:   input  logic usb_sense_i,
  13:   input  logic usb_rx_d_i,
  14:   input  logic usb_rx_se0_i,
  15:   input  logic sof_valid_i,
  16:   output logic link_disconnect_o,  // level
  17:   output logic link_connect_o,     // level
  18:   output logic link_reset_o,       // level
  19:   output logic link_active_o,      // level
  20:   output logic link_suspend_o,     // level
  21:   output logic link_resume_o,      // pulse
  22:   output logic host_lost_o,        // level
  23: 
  24:   output logic [2:0] link_state_o
  25: );
  26: 
  27:   localparam logic [11:0] SUSPEND_TIMEOUT = 12'd3000; // 3ms by spec
  28:   localparam logic [2:0]  RESET_TIMEOUT   = 3'd3;     // 3us. Can be 2.5us - 10ms by spec
  29: 
  30:   typedef enum logic [2:0] {
  31:     // Unpowered state
  32:     LinkDisconnect = 0,
  33:     // Powered states
  34:     LinkPowered = 1,
  35:     LinkPoweredSuspend = 2,
  36:     // Active states
  37:     LinkActive = 3,
  38:     LinkSuspend = 4
  39:   } link_state_e;
  40: 
  41:   typedef enum logic [1:0] {
  42:     NoRst,
  43:     RstCnt,
  44:     RstPend
  45:   } link_rst_state_e;
  46: 
  47:   typedef enum logic [1:0] {
  48:     Active,
  49:     InactCnt,
  50:     InactPend
  51:   } link_inac_state_e;
  52: 
  53:   link_state_e  link_state_d, link_state_q;
  54:   logic         line_se0_raw, line_idle_raw;
  55:   logic         see_se0, see_idle, see_pwr_sense;
  56: 
  57:   // Reset FSM
  58:   logic [2:0]      link_rst_timer_d, link_rst_timer_q;
  59:   link_rst_state_e link_rst_state_d, link_rst_state_q;
  60:   logic            link_reset; // reset detected (level)
  61: 
  62:   // Link inactivity detection
  63:   logic              monitor_inac; // monitor link inactivity
  64:   logic [11:0]       link_inac_timer_d, link_inac_timer_q;
  65:   link_inac_state_e  link_inac_state_d, link_inac_state_q;
  66: 
  67: 
  68:   // Events that are not triggered by a timeout
  69:   logic ev_bus_active;
  70: 
  71:   // Events that are triggered by timeout
  72:   logic ev_bus_inactive, ev_reset;
  73: 
  74:   assign link_disconnect_o = (link_state_q == LinkDisconnect);
  75:   assign link_connect_o    = (link_state_q != LinkDisconnect);
  76:   assign link_suspend_o    = (link_state_q == LinkSuspend ||
  77:     link_state_q == LinkPoweredSuspend);
  78:   assign link_active_o     = (link_state_q == LinkActive);
  79:   // Link state is stable, so we can output it to the register
  80:   assign link_state_o      =  link_state_q;
  81: 
  82:   assign line_se0_raw = usb_rx_se0_i;
  83:   assign line_idle_raw = usb_rx_d_i && !usb_rx_se0_i; // same as J
  84: 
  85:   // four ticks is a bit time
  86:   // Could completely filter out 2-cycle EOP SE0 here but
  87:   // does not seem needed
  88:   prim_filter #(.Cycles(6)) filter_se0 (
  89:     .clk_i    (clk_48mhz_i),
  90:     .rst_ni   (rst_ni),
  91:     .enable_i (1'b1),
  92:     .filter_i (line_se0_raw),
  93:     .filter_o (see_se0)
  94:   );
  95: 
  96:   prim_filter #(.Cycles(6)) filter_idle (
  97:     .clk_i    (clk_48mhz_i),
  98:     .rst_ni   (rst_ni),
  99:     .enable_i (1'b1),
 100:     .filter_i (line_idle_raw),
 101:     .filter_o (see_idle)
 102:   );
 103: 
 104:   prim_filter #(.Cycles(6)) filter_pwr_sense (
 105:     .clk_i    (clk_48mhz_i),
 106:     .rst_ni   (rst_ni),
 107:     .enable_i (1'b1),
 108:     .filter_i (usb_sense_i),
 109:     .filter_o (see_pwr_sense)
 110:   );
 111: 
 112:   // Simple events
 113:   assign ev_bus_active = !see_idle;
 114: 
 115:   always_comb begin
 116:     link_state_d = link_state_q;
 117:     link_resume_o = 0;
 118:     monitor_inac = see_pwr_sense ? ((link_state_q == LinkPowered) | (link_state_q == LinkActive)) :
 119:                                    1'b0;
 120: 
 121:     // If VBUS ever goes away the link has disconnected
 122:     if (!see_pwr_sense) begin
 123:       link_state_d = LinkDisconnect;
 124:     end else begin
 125:       unique case (link_state_q)
 126:         // No USB supply detected (USB spec: Attached)
 127:         LinkDisconnect: begin
 128:           if (see_pwr_sense) begin
 129:             link_state_d = LinkPowered;
 130:           end
 131:         end
 132: 
 133:         LinkPowered: begin
 134:           if (ev_reset) begin
 135:             link_state_d = LinkActive;
 136:           end else if (ev_bus_inactive) begin
 137:             link_state_d = LinkPoweredSuspend;
 138:           end
 139:         end
 140: 
 141:         LinkPoweredSuspend: begin
 142:           if (ev_reset) begin
 143:             link_state_d = LinkActive;
 144:           end else if (ev_bus_active) begin
 145:             link_resume_o = 1;
 146:             link_state_d  = LinkPowered;
 147:           end
 148:         end
 149: 
 150:         // Active (USB spec: Default / Address / Configured)
 151:         LinkActive: begin
 152:           if (ev_bus_inactive) begin
 153:             link_state_d = LinkSuspend;
 154:           end
 155:         end
 156: 
 157:         LinkSuspend: begin
 158:           if (ev_reset || ev_bus_active) begin
 159:             link_resume_o = 1;
 160:             link_state_d  = LinkActive;
 161:           end
 162:         end
 163: 
 164:         default: begin
 165:           link_state_d = LinkDisconnect;
 166:         end
 167:       endcase // case (link_state_q)
 168:     end
 169:   end
 170: 
 171:   always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
 172:     if (!rst_ni) begin
 173:       link_state_q <= LinkDisconnect;
 174:     end else begin
 175:       link_state_q <= link_state_d;
 176:     end
 177:   end
 178: 
 179:   /////////////////////
 180:   // Reset detection //
 181:   /////////////////////
 182:   //  Here we clean up the SE0 signal and generate a signle ev_reset at
 183:   //  the end of a valid reset
 184: 
 185:   always_comb begin : proc_rst_fsm
 186:     link_rst_state_d  = link_rst_state_q;
 187:     link_rst_timer_d  = link_rst_timer_q;
 188:     ev_reset          = 1'b0;
 189:     link_reset        = 1'b0;
 190: 
 191:     unique case (link_rst_state_q)
 192:       // No reset signal detected
 193:       NoRst: begin
 194:         if (see_se0) begin
 195:           link_rst_state_d = RstCnt;
 196:           link_rst_timer_d = 0;
 197:         end
 198:       end
 199: 
 200:       // Reset signal detected -> counting
 201:       RstCnt: begin
 202:         if (!see_se0) begin
 203:           link_rst_state_d = NoRst;
 204:         end else begin
 205:           if (us_tick_i) begin
 206:             if (link_rst_timer_q == RESET_TIMEOUT) begin
 207:               link_rst_state_d = RstPend;
 208:             end else begin
 209:               link_rst_timer_d = link_rst_timer_q + 1;
 210:             end
 211:           end
 212:         end
 213:       end
 214: 
 215:       // Detected reset -> wait for falling edge
 216:       RstPend: begin
 217:         if (!see_se0) begin
 218:           link_rst_state_d = NoRst;
 219:           ev_reset = 1'b1;
 220:         end
 221:         link_reset = 1'b1;
 222:       end
 223: 
 224:       default : link_rst_state_d = NoRst;
 225:     endcase
 226:   end
 227: 
 228:   assign link_reset_o = link_reset;
 229: 
 230:   always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin : proc_reg_rst
 231:     if (!rst_ni) begin
 232:       link_rst_state_q <= NoRst;
 233:       link_rst_timer_q <= 0;
 234:     end else begin
 235:       link_rst_state_q <= link_rst_state_d;
 236:       link_rst_timer_q <= link_rst_timer_d;
 237:     end
 238:   end
 239: 
 240:   ////////////////////
 241:   // Idle detection //
 242:   ////////////////////
 243:   //  Here we clean up the idle signal and generate a signle ev_bus_inactive
 244:   //  after the timer expires
 245:   always_comb begin : proc_idle_det
 246:     link_inac_state_d = link_inac_state_q;
 247:     link_inac_timer_d = link_inac_timer_q;
 248:     ev_bus_inactive   = 0;
 249: 
 250:     unique case (link_inac_state_q)
 251:       // Active or disabled
 252:       Active: begin
 253:         link_inac_timer_d = 0;
 254:         if (see_idle && monitor_inac) begin
 255:           link_inac_state_d = InactCnt;
 256:         end
 257:       end
 258: 
 259:       // Got an inactivity signal -> count duration
 260:       InactCnt: begin
 261:         if (!see_idle || !monitor_inac) begin
 262:           link_inac_state_d  = Active;
 263:         end else if (us_tick_i) begin
 264:           if (link_inac_timer_q == SUSPEND_TIMEOUT) begin
 265:             link_inac_state_d = InactPend;
 266:             ev_bus_inactive = 1;
 267:           end else begin
 268:             link_inac_timer_d = link_inac_timer_q + 1;
 269:           end
 270:         end
 271:       end
 272: 
 273:       // Counter expired & event sent, wait here
 274:       InactPend: begin
 275:         if (!see_idle || !monitor_inac) begin
 276:           link_inac_state_d  = Active;
 277:         end
 278:       end
 279: 
 280:       default : link_inac_state_d = Active;
 281:     endcase
 282:   end
 283: 
 284:   always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin : proc_reg_idle_det
 285:     if (!rst_ni) begin
 286:       link_inac_state_q <= Active;
 287:       link_inac_timer_q <= 0;
 288:     end else begin
 289:       link_inac_state_q <= link_inac_state_d;
 290:       link_inac_timer_q <= link_inac_timer_d;
 291:     end
 292:   end
 293: 
 294:   /////////////////////////
 295:   // Host loss detection //
 296:   /////////////////////////
 297:   // host_lost if no sof in 4.096ms (supposed to be every 1ms)
 298:   // and the link is active
 299:   logic [12:0] host_presence_timer;
 300: 
 301:   assign host_lost_o = host_presence_timer[12];
 302:   always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
 303:     if (!rst_ni) begin
 304:       host_presence_timer <= '0;
 305:     end else begin
 306:       if (sof_valid_i || !link_active_o || link_reset) begin
 307:         host_presence_timer <= '0;
 308:       end else if (us_tick_i && !host_lost_o) begin
 309:         host_presence_timer <= host_presence_timer + 1;
 310:       end
 311:     end
 312:   end
 313: 
 314: endmodule
 315: