/* | |
usb_uart | |
Luke Valenti's USB module, as adapted by Lawrie Griffiths. | |
This module was originally tinyfpga_bootloader.v in Luke's code. | |
It creates the endpoint modules and the protocol engine to run things. Whereas | |
the original creates a usb_spi_bridge, this one creates a usb_uart_bridge. | |
Instanciation template | |
---------------------------------------------------- | |
usb_uart uart ( | |
.clk_48mhz (clk_48mhz), | |
.reset (reset), | |
// pins - these must be connected properly to the outside world. See below. | |
.usb_p_tx(usb_p_tx), | |
.usb_n_tx(usb_n_tx), | |
.usb_p_rx(usb_p_rx), | |
.usb_n_rx(usb_n_rx), | |
.usb_tx_en(usb_tx_en), | |
// uart pipeline in | |
.uart_in_data( uart_in_data ), | |
.uart_in_valid( uart_in_valid ), | |
.uart_in_ready( uart_in_ready ), | |
// uart pipeline out | |
.uart_out_data( uart_out_data ), | |
.uart_out_valid( uart_out_valid ), | |
.uart_out_ready( uart_out_ready ), | |
.debug( debug ) | |
); | |
---------------------------------------------------- | |
Then, the actual physical pins need some special handling. Get some | |
help from the device. | |
---------------------------------------------------- | |
assign usb_p_rx = usb_tx_en ? 1'b1 : usb_p_in; | |
assign usb_n_rx = usb_tx_en ? 1'b0 : usb_n_in; | |
SB_IO #( | |
.PIN_TYPE(6'b 1010_01), // PIN_OUTPUT_TRISTATE - PIN_INPUT | |
.PULLUP(1'b 0) | |
) | |
iobuf_usbp | |
( | |
.PACKAGE_PIN(pin_usbp), | |
.OUTPUT_ENABLE(usb_tx_en), | |
.D_OUT_0(usb_p_tx), | |
.D_IN_0(usb_p_in) | |
); | |
SB_IO #( | |
.PIN_TYPE(6'b 1010_01), // PIN_OUTPUT_TRISTATE - PIN_INPUT | |
.PULLUP(1'b 0) | |
) | |
iobuf_usbn | |
( | |
.PACKAGE_PIN(pin_usbn), | |
.OUTPUT_ENABLE(usb_tx_en), | |
.D_OUT_0(usb_n_tx), | |
.D_IN_0(usb_n_in) | |
); | |
---------------------------------------------------- | |
It should be noted that there are no other invocations of usb stuff other than | |
usb_uart.v. Since it's the top, I'm going to put some doc in here. | |
General note: USB communications happen over endpoints. The OUT endpoints are out | |
with respect to the HOST, and IN endpoints are in with respect to the HOST. | |
Files: | |
usb_uart.v - top level module creates the end points clusters, (usb_serial_ctrl_ep, | |
usb_uart_bridge_ep) and the main protocol engine (usb_fs_pe - passing | |
in the in (3) and out (2) count, along with the actual usb signal | |
lines). Also, all the end point signals are connected to the protocol | |
engine in its invocation. The serial end point cluster and the control | |
end point cluster have two endpoints each - one in and one out. | |
usb_serial_ctrl_ep - serial control logic. Two end point interfaces are (one in one | |
out) passed in with their various signal lines. Contains all the | |
USB setup logic. Returns the configuration etc. Vendor 50 1D | |
Product 30 61. Two interfaces. Descriptors. Obviously the main | |
configuration file. | |
usb_uart_bridge.v - where the data action is for us. Is passed in the out endpoint | |
and the in endpoint, and also the (strange) UART interface signals | |
(uart_we, uart_re, uart_di, uart_do, uart_wait). So this translates | |
between endpoint talk and UART talk. | |
usb_fs_pe.v - full speed protocol engine - instanciates all the endpoints, making | |
arrays of in and out end point signals. Also is passed in are all | |
the actual interfaces to the end points. | |
Creates the in and out arbitors (usb_fs_in_arb, usb_fs_out_arb) | |
Creates the in protocol engine (usb_fs_in_pe), and the out protocol | |
engine (usb_fs_out_pe) | |
Creates the receiver and transmitter (usb_fs_rx, usb_fs_tx) | |
Creates the tx mux and the rx mux which permit the different engines | |
to talk. | |
usb_fs_in_pe.v - in protocol engine | |
usb_fs_out_pe.v - out protocol engine | |
usb_fs_rx.v - Actual rx logic | |
usb_fs_tx.v - Actual tx logic | |
edge_detect.v - rising and falling edge detectors | |
serial.v - width adapter (x widths to y widths) | |
*/ | |
module usb_uart ( | |
input clk_48mhz, | |
input reset, | |
// USB lines. Split into input vs. output and oe control signal to maintain | |
// highest level of compatibility with synthesis tools. | |
output usb_p_tx, | |
output usb_n_tx, | |
input usb_p_rx, | |
input usb_n_rx, | |
output usb_tx_en, | |
// uart pipeline in (into the module, out of the device, into the host) | |
input [7:0] uart_in_data, | |
input uart_in_valid, | |
output uart_in_ready, | |
// uart pipeline out (out of the host, into the device, out of the module) | |
output [7:0] uart_out_data, | |
output uart_out_valid, | |
input uart_out_ready, | |
output [11:0] debug | |
); | |
//////////////////////////////////////////////////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
//////// | |
//////// usb engine | |
//////// | |
//////////////////////////////////////////////////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
wire [6:0] dev_addr; | |
wire [7:0] out_ep_data; | |
wire ctrl_out_ep_req; | |
wire ctrl_out_ep_grant; | |
wire ctrl_out_ep_data_avail; | |
wire ctrl_out_ep_setup; | |
wire ctrl_out_ep_data_get; | |
wire ctrl_out_ep_stall; | |
wire ctrl_out_ep_acked; | |
wire ctrl_in_ep_req; | |
wire ctrl_in_ep_grant; | |
wire ctrl_in_ep_data_free; | |
wire ctrl_in_ep_data_put; | |
wire [7:0] ctrl_in_ep_data; | |
wire ctrl_in_ep_data_done; | |
wire ctrl_in_ep_stall; | |
wire ctrl_in_ep_acked; | |
wire serial_out_ep_req; | |
wire serial_out_ep_grant; | |
wire serial_out_ep_data_avail; | |
wire serial_out_ep_setup; | |
wire serial_out_ep_data_get; | |
wire serial_out_ep_stall; | |
wire serial_out_ep_acked; | |
wire serial_in_ep_req; | |
wire serial_in_ep_grant; | |
wire serial_in_ep_data_free; | |
wire serial_in_ep_data_put; | |
wire [7:0] serial_in_ep_data; | |
wire serial_in_ep_data_done; | |
wire serial_in_ep_stall; | |
wire serial_in_ep_acked; | |
wire sof_valid; | |
wire [10:0] frame_index; | |
reg [31:0] host_presence_timer = 0; | |
reg host_presence_timeout = 0; | |
usb_serial_ctrl_ep ctrl_ep_inst ( | |
.clk(clk_48mhz), | |
.reset(reset), | |
.dev_addr(dev_addr), | |
// out endpoint interface | |
.out_ep_req(ctrl_out_ep_req), | |
.out_ep_grant(ctrl_out_ep_grant), | |
.out_ep_data_avail(ctrl_out_ep_data_avail), | |
.out_ep_setup(ctrl_out_ep_setup), | |
.out_ep_data_get(ctrl_out_ep_data_get), | |
.out_ep_data(out_ep_data), | |
.out_ep_stall(ctrl_out_ep_stall), | |
.out_ep_acked(ctrl_out_ep_acked), | |
// in endpoint interface | |
.in_ep_req(ctrl_in_ep_req), | |
.in_ep_grant(ctrl_in_ep_grant), | |
.in_ep_data_free(ctrl_in_ep_data_free), | |
.in_ep_data_put(ctrl_in_ep_data_put), | |
.in_ep_data(ctrl_in_ep_data), | |
.in_ep_data_done(ctrl_in_ep_data_done), | |
.in_ep_stall(ctrl_in_ep_stall), | |
.in_ep_acked(ctrl_in_ep_acked) | |
); | |
usb_uart_bridge_ep usb_uart_bridge_ep_inst ( | |
.clk(clk_48mhz), | |
.reset(reset), | |
// out endpoint interface | |
.out_ep_req(serial_out_ep_req), | |
.out_ep_grant(serial_out_ep_grant), | |
.out_ep_data_avail(serial_out_ep_data_avail), | |
.out_ep_setup(serial_out_ep_setup), | |
.out_ep_data_get(serial_out_ep_data_get), | |
.out_ep_data(out_ep_data), | |
.out_ep_stall(serial_out_ep_stall), | |
.out_ep_acked(serial_out_ep_acked), | |
// in endpoint interface | |
.in_ep_req(serial_in_ep_req), | |
.in_ep_grant(serial_in_ep_grant), | |
.in_ep_data_free(serial_in_ep_data_free), | |
.in_ep_data_put(serial_in_ep_data_put), | |
.in_ep_data(serial_in_ep_data), | |
.in_ep_data_done(serial_in_ep_data_done), | |
.in_ep_stall(serial_in_ep_stall), | |
.in_ep_acked(serial_in_ep_acked), | |
// uart pipeline in | |
.uart_in_data( uart_in_data ), | |
.uart_in_valid( uart_in_valid ), | |
.uart_in_ready( uart_in_ready ), | |
// uart pipeline out | |
.uart_out_data( uart_out_data ), | |
.uart_out_valid( uart_out_valid ), | |
.uart_out_ready( uart_out_ready ), | |
.debug(debug[3:0]) | |
); | |
wire nak_in_ep_grant; | |
wire nak_in_ep_data_free; | |
wire nak_in_ep_acked; | |
usb_fs_pe #( | |
.NUM_OUT_EPS(5'd2), | |
.NUM_IN_EPS(5'd3) | |
) usb_fs_pe_inst ( | |
.clk(clk_48mhz), | |
.reset(reset), | |
.usb_p_tx(usb_p_tx), | |
.usb_n_tx(usb_n_tx), | |
.usb_p_rx(usb_p_rx), | |
.usb_n_rx(usb_n_rx), | |
.usb_tx_en(usb_tx_en), | |
.dev_addr(dev_addr), | |
// out endpoint interfaces | |
.out_ep_req({serial_out_ep_req, ctrl_out_ep_req}), | |
.out_ep_grant({serial_out_ep_grant, ctrl_out_ep_grant}), | |
.out_ep_data_avail({serial_out_ep_data_avail, ctrl_out_ep_data_avail}), | |
.out_ep_setup({serial_out_ep_setup, ctrl_out_ep_setup}), | |
.out_ep_data_get({serial_out_ep_data_get, ctrl_out_ep_data_get}), | |
.out_ep_data(out_ep_data), | |
.out_ep_stall({serial_out_ep_stall, ctrl_out_ep_stall}), | |
.out_ep_acked({serial_out_ep_acked, ctrl_out_ep_acked}), | |
// in endpoint interfaces | |
.in_ep_req({1'b0, serial_in_ep_req, ctrl_in_ep_req}), | |
.in_ep_grant({nak_in_ep_grant, serial_in_ep_grant, ctrl_in_ep_grant}), | |
.in_ep_data_free({nak_in_ep_data_free, serial_in_ep_data_free, ctrl_in_ep_data_free}), | |
.in_ep_data_put({1'b0, serial_in_ep_data_put, ctrl_in_ep_data_put}), | |
.in_ep_data({8'b0, serial_in_ep_data[7:0], ctrl_in_ep_data[7:0]}), | |
.in_ep_data_done({1'b0, serial_in_ep_data_done, ctrl_in_ep_data_done}), | |
.in_ep_stall({1'b0, serial_in_ep_stall, ctrl_in_ep_stall}), | |
.in_ep_acked({nak_in_ep_acked, serial_in_ep_acked, ctrl_in_ep_acked}), | |
// sof interface | |
.sof_valid(sof_valid), | |
.frame_index(frame_index), | |
// Debug | |
.debug(debug[11:4]) | |
); | |
//////////////////////////////////////////////////////////////////////////////// | |
// host presence detection | |
//////////////////////////////////////////////////////////////////////////////// | |
always @(posedge clk_48mhz) begin | |
if (sof_valid) begin | |
host_presence_timer <= 0; | |
host_presence_timeout <= 0; | |
end else begin | |
host_presence_timer <= host_presence_timer + 1; | |
end | |
if (host_presence_timer > 48000000) begin | |
host_presence_timeout <= 1; | |
end | |
end | |
endmodule |