blob: dedbc2adab6d5267b837c6670cbbf2cf0ac7f62c [file] [log] [blame]
// 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