| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // Description: USB uart interface in USB clock domain | 
 | // | 
 | module usbuart_usbif ( | 
 |   input               clk_48mhz_i, | 
 |   input               rst_ni, | 
 |  | 
 |   // USB lines.  Split into input vs. output and oe control signal to maintain | 
 |   // highest level of compatibility with synthesis tools. | 
 |   input  logic        usb_d_i, | 
 |   input  logic        usb_se0_i, | 
 |  | 
 |   output logic        usb_d_o, | 
 |   output logic        usb_se0_o, | 
 |   output logic        usb_oe_o, | 
 |  | 
 |   // Fifo used to communicate with system | 
 |   input               tx_empty, | 
 |   input               rx_full, | 
 |   output              tx_read, | 
 |   output              rx_write, | 
 |   output              rx_err, // Also becomes bit 8 to the fifo | 
 |   output [7:0]        rx_fifo_wdata, | 
 |   input [7:0]         tx_fifo_rdata, | 
 |  | 
 |   // Status | 
 |   output logic [10:0] status_frame_o, | 
 |   output logic        status_host_lost_o, | 
 |   output logic        status_host_timeout_o, | 
 |   output logic [6:0]  status_device_address_o, | 
 |   output logic [1:0]  parity_o, | 
 |   output logic [15:0] baud_o | 
 | ); | 
 |  | 
 |   localparam int unsigned MaxPktSizeByte = 32; | 
 |   localparam int unsigned PktW = $clog2(MaxPktSizeByte); | 
 |   localparam int unsigned CtrlEp = 0; | 
 |   localparam int unsigned FifoEp = 1; | 
 |  | 
 |   // us_tick ticks for one cycle every us | 
 |   logic [5:0]   ns_cnt; | 
 |   logic         us_tick; | 
 |   assign us_tick = (ns_cnt == 6'd48); | 
 |   always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       ns_cnt <= '0; | 
 |     end  else if (us_tick) begin | 
 |       ns_cnt <= '0; | 
 |     end else begin | 
 |       ns_cnt <= ns_cnt + 1'b1; | 
 |     end | 
 |   end | 
 |  | 
 |   logic [6:0] dev_addr; | 
 |   logic [7:0] out_ep_data; | 
 |  | 
 |   logic [3:0] in_ep_current; | 
 |   logic       in_ep_rollback; | 
 |   logic       in_ep_acked; | 
 |   logic [PktW - 1:0] in_ep_get_addr; | 
 |   logic               in_ep_data_get; | 
 |  | 
 |   logic [3:0]         out_ep_current; | 
 |   logic               out_ep_rollback; | 
 |   logic               out_ep_acked; | 
 |   logic [PktW - 1:0]  out_ep_put_addr; | 
 |   logic               out_ep_data_put; | 
 |  | 
 |   logic ctrl_out_ep_setup; | 
 |   logic ctrl_out_ep_stall; | 
 |   logic ctrl_out_ep_full; | 
 |  | 
 |   logic [7:0] ctrl_in_ep_data; | 
 |   logic ctrl_in_ep_data_done; | 
 |   logic ctrl_in_ep_stall; | 
 |   logic ctrl_in_ep_has_data; | 
 |  | 
 |   logic serial_out_ep_setup; | 
 |   logic serial_out_ep_stall; | 
 |   logic serial_out_ep_full; | 
 |  | 
 |   logic [7:0] serial_in_ep_data; | 
 |   logic serial_in_ep_data_done; | 
 |   logic serial_in_ep_stall; | 
 |   logic serial_in_ep_has_data; | 
 |  | 
 |   logic sof_valid; | 
 |   logic [10:0] frame_index_raw; | 
 |  | 
 |   logic [19:0]  host_presence_timer; | 
 |  | 
 |   assign status_device_address_o = dev_addr; | 
 |  | 
 |   logic  out_ctrl_put, out_ctrl_acked, out_ctrl_rollback; | 
 |   logic  in_ctrl_get, in_ctrl_acked, in_ctrl_rollback; | 
 |   assign out_ctrl_put      = out_ep_data_put && (out_ep_current == CtrlEp); | 
 |   assign out_ctrl_acked    = out_ep_acked    && (out_ep_current == CtrlEp); | 
 |   assign out_ctrl_rollback = out_ep_rollback && (out_ep_current == CtrlEp); | 
 |   assign in_ctrl_get       = in_ep_data_get  && (in_ep_current  == CtrlEp); | 
 |   assign in_ctrl_acked     = in_ep_acked     && (in_ep_current  == CtrlEp); | 
 |   assign in_ctrl_rollback  = in_ep_rollback  && (in_ep_current  == CtrlEp); | 
 |  | 
 |   usb_serial_ctrl_ep #( | 
 |     .MaxPktSizeByte(MaxPktSizeByte) | 
 |   ) u_usb_serial_ctrl_ep ( | 
 |     .clk_i(clk_48mhz_i), | 
 |     .rst_ni(rst_ni), | 
 |     .dev_addr(dev_addr), | 
 |  | 
 |     // out endpoint interface | 
 |     .out_ep_data_put_i(out_ctrl_put), | 
 |     .out_ep_put_addr_i(out_ep_put_addr), | 
 |     .out_ep_data_i(out_ep_data), | 
 |     .out_ep_acked_i(out_ctrl_acked), | 
 |     .out_ep_rollback_i(out_ctrl_rollback), | 
 |     .out_ep_setup_i(ctrl_out_ep_setup), | 
 |     .out_ep_full_o(ctrl_out_ep_full), | 
 |     .out_ep_stall_o(ctrl_out_ep_stall), | 
 |  | 
 |     // in endpoint interface | 
 |     .in_ep_rollback_i(in_ctrl_rollback), | 
 |     .in_ep_acked_i(in_ctrl_acked), | 
 |     .in_ep_get_addr_i(in_ep_get_addr), | 
 |     .in_ep_data_get_i(in_ctrl_get), | 
 |     .in_ep_stall_o(ctrl_in_ep_stall), | 
 |     .in_ep_has_data_o(ctrl_in_ep_has_data), | 
 |     .in_ep_data_o(ctrl_in_ep_data[7:0]), | 
 |     .in_ep_data_done_o(ctrl_in_ep_data_done) | 
 |   ); | 
 |  | 
 |   logic  out_fifo_put, out_fifo_acked, out_fifo_rollback; | 
 |   logic  in_fifo_get, in_fifo_acked, in_fifo_rollback; | 
 |   assign out_fifo_put      = out_ep_data_put && (out_ep_current == FifoEp); | 
 |   assign out_fifo_acked    = out_ep_acked    && (out_ep_current == FifoEp); | 
 |   assign out_fifo_rollback = out_ep_rollback && (out_ep_current == FifoEp); | 
 |   assign in_fifo_get       = in_ep_data_get  && (in_ep_current  == FifoEp); | 
 |   assign in_fifo_acked     = in_ep_acked     && (in_ep_current  == FifoEp); | 
 |   assign in_fifo_rollback  = in_ep_rollback  && (in_ep_current  == FifoEp); | 
 |  | 
 |   usb_serial_fifo_ep #( | 
 |     .MaxPktSizeByte(MaxPktSizeByte) | 
 |   ) u_usb_serial_fifo_ep ( | 
 |     .clk_i(clk_48mhz_i), | 
 |     .rst_ni(rst_ni), | 
 |     // out endpoint interface | 
 |     .out_ep_data_put_i(out_fifo_put), | 
 |     .out_ep_put_addr_i(out_ep_put_addr), | 
 |     .out_ep_data_i(out_ep_data), | 
 |     .out_ep_acked_i(out_fifo_acked), | 
 |     .out_ep_rollback_i(out_fifo_rollback), | 
 |     .out_ep_setup_i(serial_out_ep_setup), | 
 |     .out_ep_full_o(serial_out_ep_full), | 
 |     .out_ep_stall_o(serial_out_ep_stall), | 
 |  | 
 |     // in endpoint interface | 
 |     .in_ep_rollback_i(in_fifo_rollback), | 
 |     .in_ep_acked_i(in_fifo_acked), | 
 |     .in_ep_get_addr_i(in_ep_get_addr), | 
 |     .in_ep_data_get_i(in_fifo_get), | 
 |     .in_ep_stall_o(serial_in_ep_stall), | 
 |     .in_ep_has_data_o(serial_in_ep_has_data), | 
 |     .in_ep_data_o(serial_in_ep_data[7:0]), | 
 |     .in_ep_data_done_o(serial_in_ep_data_done), | 
 |  | 
 |     // fifo interface | 
 |     .tx_empty(tx_empty), | 
 |     .rx_full(rx_full), | 
 |     .tx_read(tx_read), | 
 |     .rx_write(rx_write), | 
 |     .rx_err(rx_err), // Also becomes bit 8 to the fifo | 
 |     .rx_fifo_wdata(rx_fifo_wdata), | 
 |     .tx_fifo_rdata(tx_fifo_rdata), | 
 |     // information | 
 |     .parity_o(parity_o), | 
 |     .baud_o(baud_o) | 
 |   ); | 
 |  | 
 |   usb_fs_nb_pe #( | 
 |     .NumOutEps(2), | 
 |     .NumInEps(2), | 
 |     .MaxPktSizeByte(MaxPktSizeByte) | 
 |   ) u_usb_fs_nb_pe ( | 
 |     .clk_48mhz_i                (clk_48mhz_i), | 
 |     .rst_ni                     (rst_ni), | 
 |     .link_reset_i               (1'b0), // TODO need to reset if link resets | 
 |  | 
 |     // USB TRX interface (sync) | 
 |     .usb_d_i                    (usb_d_i), | 
 |     .usb_se0_i                  (usb_se0_i), | 
 |     .usb_d_o                    (usb_d_o), | 
 |     .usb_se0_o                  (usb_se0_o), | 
 |     .usb_oe_o                   (usb_oe_o), | 
 |  | 
 |     // Global configuration (static) | 
 |     .cfg_eop_single_bit_i       (1'b1), | 
 |     .tx_osc_test_mode_i         (1'b0), | 
 |     .data_toggle_clear_i        (2'b0), | 
 |  | 
 |     .dev_addr_i                 (dev_addr), | 
 |  | 
 |     // out endpoint interfaces | 
 |     .out_ep_current_o           (out_ep_current), | 
 |     .out_ep_data_put_o          (out_ep_data_put), | 
 |     .out_ep_put_addr_o          (out_ep_put_addr), | 
 |     .out_ep_data_o              (out_ep_data), | 
 |     .out_ep_acked_o             (out_ep_acked), | 
 |     .out_ep_rollback_o          (out_ep_rollback), | 
 |     .out_ep_newpkt_o            (), | 
 |     .out_ep_setup_o             ({serial_out_ep_setup, ctrl_out_ep_setup}), | 
 |     .out_ep_full_i              ({serial_out_ep_full, ctrl_out_ep_full}), | 
 |     .out_ep_stall_i             ({serial_out_ep_stall, ctrl_out_ep_stall}), | 
 |     .out_ep_iso_i               (2'b0), | 
 |  | 
 |     // in endpoint interfaces | 
 |     .in_ep_current_o            (in_ep_current), | 
 |     .in_ep_rollback_o           (in_ep_rollback), | 
 |     .in_ep_acked_o              (in_ep_acked), | 
 |     .in_ep_get_addr_o           (in_ep_get_addr), | 
 |     .in_ep_data_get_o           (in_ep_data_get), | 
 |     .in_ep_newpkt_o             (), | 
 |     .in_ep_stall_i              ({serial_in_ep_stall, ctrl_in_ep_stall}), | 
 |     .in_ep_has_data_i           ({serial_in_ep_has_data, ctrl_in_ep_has_data}), | 
 |     .in_ep_data_i               ((in_ep_current == 4'b0001) ? serial_in_ep_data : ctrl_in_ep_data), | 
 |     .in_ep_data_done_i          ({serial_in_ep_data_done, ctrl_in_ep_data_done}), | 
 |     .in_ep_iso_i                (2'b0), | 
 |  | 
 |     // Errors | 
 |     .rx_crc_err_o               (), | 
 |     .rx_pid_err_o               (), | 
 |     .rx_bitstuff_err_o          (), | 
 |  | 
 |     // sof interface | 
 |     .sof_valid_o                (sof_valid), | 
 |     .frame_index_o              (frame_index_raw) | 
 |   ); | 
 |  | 
 |   // host presence detection | 
 |   // host_lost if no sof in 2.048ms (supposed to be every 1ms) | 
 |   // host_presence_timeout if no sof in 1s (spec) | 
 |   assign status_host_lost_o = host_presence_timer[19:12] != 0; | 
 |   always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin | 
 |     if (!rst_ni) begin | 
 |       host_presence_timer <= '0; | 
 |       status_host_timeout_o <= 1'b0; | 
 |       status_frame_o <= '0; | 
 |     end else begin | 
 |       if (sof_valid) begin | 
 |         host_presence_timer <= 0; | 
 |         status_host_timeout_o <= 0; | 
 |         status_frame_o <= frame_index_raw; | 
 |       end else if (host_presence_timer > 1000000) begin | 
 |         status_host_timeout_o <= 1; | 
 |       end else if (us_tick) begin | 
 |         host_presence_timer <= host_presence_timer + 1; | 
 |       end | 
 |     end | 
 |   end | 
 | endmodule |