URL
https://opencores.org/ocsvn/usb_host_core/usb_host_core/trunk
Subversion Repositories usb_host_core
Compare Revisions
- This comparison shows the changes necessary to convert path
/usb_host_core/trunk
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/rtl/usbh_crc5.v
0,0 → 1,59
//----------------------------------------------------------------- |
// USB Full Speed Host |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: 5-bit CRC used by USB tokens |
//----------------------------------------------------------------- |
module usbh_crc5 |
( |
input [4:0] crc_i, |
input [10:0] data_i, |
output [4:0] crc_o |
); |
|
//----------------------------------------------------------------- |
// Implementation |
//----------------------------------------------------------------- |
assign crc_o[0] = data_i[10] ^ data_i[9] ^ data_i[6] ^ data_i[5] ^ data_i[3] ^ data_i[0] ^ |
crc_i[0] ^ crc_i[3] ^ crc_i[4]; |
|
assign crc_o[1] = data_i[10] ^ data_i[7] ^ data_i[6] ^ data_i[4] ^ data_i[1] ^ |
crc_i[0] ^ crc_i[1] ^ crc_i[4]; |
|
assign crc_o[2] = data_i[10] ^ data_i[9] ^ data_i[8] ^ data_i[7] ^ data_i[6] ^ data_i[3] ^ data_i[2] ^ data_i[0] ^ |
crc_i[0] ^ crc_i[1] ^ crc_i[2] ^ crc_i[3] ^ crc_i[4]; |
|
assign crc_o[3] = data_i[10] ^ data_i[9] ^ data_i[8] ^ data_i[7] ^ data_i[4] ^ data_i[3] ^ data_i[1] ^ |
crc_i[1] ^ crc_i[2] ^ crc_i[3] ^ crc_i[4]; |
|
assign crc_o[4] = data_i[10] ^ data_i[9] ^ data_i[8] ^ data_i[5] ^ data_i[4] ^ data_i[2] ^ |
crc_i[2] ^ crc_i[3] ^ crc_i[4]; |
|
endmodule |
/rtl/usbh_crc16.v
0,0 → 1,69
//----------------------------------------------------------------- |
// USB Full Speed Host |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: 16-bit CRC used by USB data packets |
//----------------------------------------------------------------- |
module usbh_crc16 |
( |
input [15:0] crc_i, |
input [7:0] data_i, |
output [15:0] crc_o |
); |
|
//----------------------------------------------------------------- |
// Implementation |
//----------------------------------------------------------------- |
assign crc_o[15] = data_i[0] ^ data_i[1] ^ data_i[2] ^ data_i[3] ^ data_i[4] ^ |
data_i[5] ^ data_i[6] ^ data_i[7] ^ crc_i[7] ^ crc_i[6] ^ |
crc_i[5] ^ crc_i[4] ^ crc_i[3] ^ crc_i[2] ^ |
crc_i[1] ^ crc_i[0]; |
assign crc_o[14] = data_i[0] ^ data_i[1] ^ data_i[2] ^ data_i[3] ^ data_i[4] ^ data_i[5] ^ |
data_i[6] ^ crc_i[6] ^ crc_i[5] ^ crc_i[4] ^ |
crc_i[3] ^ crc_i[2] ^ crc_i[1] ^ crc_i[0]; |
assign crc_o[13] = data_i[6] ^ data_i[7] ^ crc_i[7] ^ crc_i[6]; |
assign crc_o[12] = data_i[5] ^ data_i[6] ^ crc_i[6] ^ crc_i[5]; |
assign crc_o[11] = data_i[4] ^ data_i[5] ^ crc_i[5] ^ crc_i[4]; |
assign crc_o[10] = data_i[3] ^ data_i[4] ^ crc_i[4] ^ crc_i[3]; |
assign crc_o[9] = data_i[2] ^ data_i[3] ^ crc_i[3] ^ crc_i[2]; |
assign crc_o[8] = data_i[1] ^ data_i[2] ^ crc_i[2] ^ crc_i[1]; |
assign crc_o[7] = data_i[0] ^ data_i[1] ^ crc_i[15] ^ crc_i[1] ^ crc_i[0]; |
assign crc_o[6] = data_i[0] ^ crc_i[14] ^ crc_i[0]; |
assign crc_o[5] = crc_i[13]; |
assign crc_o[4] = crc_i[12]; |
assign crc_o[3] = crc_i[11]; |
assign crc_o[2] = crc_i[10]; |
assign crc_o[1] = crc_i[9]; |
assign crc_o[0] = data_i[0] ^ data_i[1] ^ data_i[2] ^ data_i[3] ^ data_i[4] ^ data_i[5] ^ |
data_i[6] ^ data_i[7] ^ crc_i[8] ^ crc_i[7] ^ crc_i[6] ^ |
crc_i[5] ^ crc_i[4] ^ crc_i[3] ^ crc_i[2] ^ |
crc_i[1] ^ crc_i[0]; |
|
endmodule |
/rtl/usbh.v
0,0 → 1,420
//----------------------------------------------------------------- |
// USB Full Speed Host |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: Basic USB host interface |
//----------------------------------------------------------------- |
module usbh |
|
//----------------------------------------------------------------- |
// Params |
//----------------------------------------------------------------- |
#( |
parameter TX_FIFO_DEPTH = 64, |
parameter TX_FIFO_ADDR_W = 6, |
parameter RX_FIFO_DEPTH = 64, |
parameter RX_FIFO_ADDR_W = 6 |
) |
|
//----------------------------------------------------------------- |
// Ports |
//----------------------------------------------------------------- |
( |
// Clocking (48MHz) & Reset |
input clk_i, |
input rst_i, |
|
// Interrupt output |
output intr_o, |
|
// Peripheral Interface (from CPU) |
input [7:0] addr_i, |
input [31:0] data_i, |
output [31:0] data_o, |
input we_i, |
input stb_i, |
|
// UTMI Interface |
output [7:0] utmi_data_o, |
output utmi_txvalid_o, |
input utmi_txready_i, |
input [7:0] utmi_data_i, |
input utmi_rxvalid_i, |
input utmi_rxactive_i, |
input utmi_rxerror_i, |
input [1:0] utmi_linestate_i, |
|
output usb_rst_o |
); |
|
//----------------------------------------------------------------- |
// Registers / Wires |
//----------------------------------------------------------------- |
// SOF |
reg [10:0] sof_value_q; |
reg [15:0] sof_time_q; |
reg sof_irq_q; |
|
reg transfer_req_ack_q; |
|
// Requests for transfers |
wire ctrl_sof_en_w; |
wire ctrl_in_transfer_w; |
wire ctrl_resp_expected_w; |
wire ctrl_data_idx_w; |
wire [15:0] ctrl_tx_count_w; |
wire [7:0] ctrl_token_pid_w; |
wire [10:0] ctrl_token_data_w; |
|
wire [7:0] fifo_tx_data_w; |
wire fifo_tx_pop_w; |
|
wire [7:0] fifo_rx_data_w; |
wire fifo_rx_push_w; |
|
wire [7:0] ctrl_fifo_tx_data_w; |
wire ctrl_fifo_tx_push_w; |
wire ctrl_fifo_tx_flush_w; |
|
wire [7:0] ctrl_fifo_rx_data_w; |
wire ctrl_fifo_rx_pop_w; |
|
wire ctrl_start_w; |
|
reg fifo_flush_q; |
|
wire [7:0] token_pid_w; |
wire [6:0] token_dev_w; |
wire [3:0] token_ep_w; |
|
reg transfer_start_q; |
reg in_transfer_q; |
reg sof_transfer_q; |
reg resp_expected_q; |
wire transfer_ack_w; |
|
wire status_crc_err_w; |
wire status_timeout_w; |
wire [7:0] status_response_w; |
wire [15:0] status_rx_count_w; |
wire status_sie_idle_w; |
wire status_tx_done_w; |
wire status_rx_done_w; |
|
wire send_sof_w; |
wire sof_gaurd_band_w; |
wire clear_to_send_w; |
|
//----------------------------------------------------------------- |
// Definitions |
//----------------------------------------------------------------- |
localparam [15:0] SOF_ZERO = 0; |
localparam [15:0] SOF_INC = 1; |
localparam [15:0] SOF_THRESHOLD = 48000; |
|
localparam [15:0] EOF1_THRESHOLD = (50 * 4); // EOF1 + some margin |
localparam [15:0] MAX_XFER_SIZE = (TX_FIFO_DEPTH > RX_FIFO_DEPTH) ? TX_FIFO_DEPTH : RX_FIFO_DEPTH; |
localparam [15:0] MAX_XFER_PERIOD = ((MAX_XFER_SIZE + 6) * 10 * 4); // Max packet transfer time (+ margin) |
localparam [15:0] SOF_GAURD_LOW = (20 * 4); |
localparam [15:0] SOF_GAURD_HIGH = SOF_THRESHOLD - EOF1_THRESHOLD - MAX_XFER_PERIOD; |
|
localparam PID_OUT = 8'hE1; |
localparam PID_IN = 8'h69; |
localparam PID_SOF = 8'hA5; |
localparam PID_SETUP = 8'h2D; |
|
localparam PID_DATA0 = 8'hC3; |
localparam PID_DATA1 = 8'h4B; |
|
localparam PID_ACK = 8'hD2; |
localparam PID_NAK = 8'h5A; |
localparam PID_STALL = 8'h1E; |
|
//----------------------------------------------------------------- |
// SIE |
//----------------------------------------------------------------- |
usbh_sie |
u_sie |
( |
// Clock (48MHz) & reset |
.clk_i(clk_i), |
.rst_i(rst_i), |
|
// Control |
.start_i(transfer_start_q), |
.in_transfer_i(in_transfer_q), |
.sof_transfer_i(sof_transfer_q), |
.resp_expected_i(resp_expected_q), |
.ack_o(transfer_ack_w), |
|
// Token packet |
.token_pid_i(token_pid_w), |
.token_dev_i(token_dev_w), |
.token_ep_i(token_ep_w), |
|
// Data packet |
.data_len_i(ctrl_tx_count_w), |
.data_idx_i(ctrl_data_idx_w), |
|
// Tx Data FIFO |
.tx_data_i(fifo_tx_data_w), |
.tx_pop_o(fifo_tx_pop_w), |
|
// Rx Data FIFO |
.rx_data_o(fifo_rx_data_w), |
.rx_push_o(fifo_rx_push_w), |
|
// Status |
.rx_done_o(status_rx_done_w), |
.tx_done_o(status_tx_done_w), |
.crc_err_o(status_crc_err_w), |
.timeout_o(status_timeout_w), |
.response_o(status_response_w), |
.rx_count_o(status_rx_count_w), |
.idle_o(status_sie_idle_w), |
|
// UTMI Interface |
.utmi_data_o(utmi_data_o), |
.utmi_txvalid_o(utmi_txvalid_o), |
.utmi_txready_i(utmi_txready_i), |
.utmi_data_i(utmi_data_i), |
.utmi_rxvalid_i(utmi_rxvalid_i), |
.utmi_rxactive_i(utmi_rxactive_i) |
); |
|
//----------------------------------------------------------------- |
// Peripheral Interface |
//----------------------------------------------------------------- |
usbh_periph |
u_pif |
( |
.clk_i(clk_i), |
.rst_i(rst_i), |
|
.intr_o(intr_o), |
|
// Peripheral Interface (from CPU) |
.addr_i(addr_i), |
.data_i(data_i), |
.data_o(data_o), |
.we_i(we_i), |
.stb_i(stb_i), |
|
// UTMI interface |
.utmi_linestate_i(utmi_linestate_i), |
.utmi_rxerror_i(utmi_rxerror_i), |
|
// Control |
.sie_start_o(ctrl_start_w), |
.sie_sof_en_o(ctrl_sof_en_w), |
.sie_rst_o(usb_rst_o), |
.sie_token_pid_o(ctrl_token_pid_w), |
.sie_token_data_o(ctrl_token_data_w), |
.sie_tx_count_o(ctrl_tx_count_w), |
.sie_data_idx_o(ctrl_data_idx_w), |
.sie_in_transfer_o(ctrl_in_transfer_w), |
.sie_resp_expected_o(ctrl_resp_expected_w), |
|
// FIFO |
.sie_tx_data_o(ctrl_fifo_tx_data_w), |
.sie_tx_push_o(ctrl_fifo_tx_push_w), |
.sie_tx_flush_o(ctrl_fifo_tx_flush_w), |
.sie_rx_pop_o(ctrl_fifo_rx_pop_w), |
.sie_rx_data_i(ctrl_fifo_rx_data_w), |
|
// Status |
.sie_rx_crc_err_i(status_crc_err_w), |
.sie_rx_resp_timeout_i(status_timeout_w), |
.sie_rx_resp_pid_i(status_response_w), |
.sie_rx_count_i(status_rx_count_w), |
.sie_rx_idle_i(status_sie_idle_w), |
.sie_req_ack_i(transfer_req_ack_q), |
.sie_sof_time_i(sof_time_q), |
.sie_rx_done_i(status_rx_done_w), |
.sie_tx_done_i(status_tx_done_w), |
.sie_sof_irq_i(sof_irq_q) |
); |
|
//----------------------------------------------------------------- |
// Tx FIFO (Host -> Device) |
//----------------------------------------------------------------- |
usbh_fifo |
#( |
.DEPTH(TX_FIFO_DEPTH), |
.ADDR_W(TX_FIFO_ADDR_W) |
) |
u_fifo_tx |
( |
.clk_i(clk_i), |
.rst_i(rst_i), |
|
.data_i(ctrl_fifo_tx_data_w), |
.push_i(ctrl_fifo_tx_push_w), |
|
.flush_i(ctrl_fifo_tx_flush_w), |
|
.full_o(), |
.empty_o(), |
|
.data_o(fifo_tx_data_w), |
.pop_i(fifo_tx_pop_w) |
); |
|
//----------------------------------------------------------------- |
// Rx FIFO (Device -> Host) |
//----------------------------------------------------------------- |
usbh_fifo |
#( |
.DEPTH(RX_FIFO_DEPTH), |
.ADDR_W(RX_FIFO_ADDR_W) |
) |
u_fifo_rx |
( |
.clk_i(clk_i), |
.rst_i(rst_i), |
|
// Receive from UTMI interface |
.data_i(fifo_rx_data_w), |
.push_i(fifo_rx_push_w), |
|
.flush_i(fifo_flush_q), |
|
.full_o(), |
.empty_o(), |
|
.data_o(ctrl_fifo_rx_data_w), |
.pop_i(ctrl_fifo_rx_pop_w) |
); |
|
//----------------------------------------------------------------- |
// Assignments |
//----------------------------------------------------------------- |
assign send_sof_w = (sof_time_q == SOF_THRESHOLD && ctrl_sof_en_w) & status_sie_idle_w; |
assign sof_gaurd_band_w = (sof_time_q <= SOF_GAURD_LOW || sof_time_q >= SOF_GAURD_HIGH); |
assign clear_to_send_w = (~sof_gaurd_band_w | ~ctrl_sof_en_w) & status_sie_idle_w; |
|
assign token_pid_w = sof_transfer_q ? PID_SOF : ctrl_token_pid_w; |
|
assign token_dev_w = sof_transfer_q ? |
{sof_value_q[0], sof_value_q[1], sof_value_q[2], |
sof_value_q[3], sof_value_q[4], sof_value_q[5], sof_value_q[6]} : |
ctrl_token_data_w[10:4]; |
|
assign token_ep_w = sof_transfer_q ? |
{sof_value_q[7], sof_value_q[8], sof_value_q[9], sof_value_q[10]} : |
ctrl_token_data_w[3:0]; |
|
//----------------------------------------------------------------- |
// Control logic |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
begin |
if (rst_i == 1'b1) |
begin |
fifo_flush_q <= 1'b0; |
transfer_start_q <= 1'b0; |
sof_transfer_q <= 1'b0; |
transfer_req_ack_q <= 1'b0; |
in_transfer_q <= 1'b0; |
resp_expected_q <= 1'b0; |
end |
else |
begin |
// Transfer in progress? |
if (transfer_start_q) |
begin |
// Transfer accepted |
if (transfer_ack_w) |
transfer_start_q <= 1'b0; |
|
fifo_flush_q <= 1'b0; |
transfer_req_ack_q <= 1'b0; |
end |
// Time to send another SOF token? |
else if (send_sof_w) |
begin |
// Start transfer |
in_transfer_q <= 1'b0; |
resp_expected_q <= 1'b0; |
transfer_start_q <= 1'b1; |
sof_transfer_q <= 1'b1; |
end |
// Not in SOF gaurd band region or SOF disabled? |
else if (clear_to_send_w) |
begin |
// Transfer request |
if (ctrl_start_w) |
begin |
// Flush un-used previous Rx data |
fifo_flush_q <= 1'b1; |
|
// Start transfer |
in_transfer_q <= ctrl_in_transfer_w; |
resp_expected_q <= ctrl_resp_expected_w; |
transfer_start_q <= 1'b1; |
sof_transfer_q <= 1'b0; |
transfer_req_ack_q <= 1'b1; |
end |
end |
end |
end |
|
//----------------------------------------------------------------- |
// SOF Frame Number |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
begin |
if (rst_i == 1'b1) |
begin |
sof_value_q <= 11'd0; |
sof_time_q <= SOF_ZERO; |
sof_irq_q <= 1'b0; |
end |
// Time to send another SOF token? |
else if (send_sof_w) |
begin |
sof_time_q <= SOF_ZERO; |
sof_value_q <= sof_value_q + 11'd1; |
|
// Start of frame interrupt |
sof_irq_q <= 1'b1; |
end |
else |
begin |
// Increment the SOF timer |
if (sof_time_q != SOF_THRESHOLD) |
sof_time_q <= sof_time_q + SOF_INC; |
|
sof_irq_q <= 1'b0; |
end |
end |
|
endmodule |
|
/rtl/usbh_sie.v
0,0 → 1,775
//----------------------------------------------------------------- |
// USB Full Speed Host |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: USB host serial interface engine |
//----------------------------------------------------------------- |
module usbh_sie |
( |
// Clocking (48MHz) & Reset |
input clk_i, |
input rst_i, |
|
// Control |
input start_i, |
input in_transfer_i, |
input sof_transfer_i, |
input resp_expected_i, |
output ack_o, |
|
// Token packet |
input [7:0] token_pid_i, |
input [6:0] token_dev_i, |
input [3:0] token_ep_i, |
|
// Data packet |
input [15:0] data_len_i, |
input data_idx_i, |
|
// Tx Data FIFO |
input [7:0] tx_data_i, |
output tx_pop_o, |
|
// Rx Data FIFO |
output [7:0] rx_data_o, |
output rx_push_o, |
|
// Status |
output tx_done_o, |
output rx_done_o, |
output crc_err_o, |
output timeout_o, |
output [7:0] response_o, |
output [15:0] rx_count_o, |
output idle_o, |
|
// UTMI Interface |
output [7:0] utmi_data_o, |
output utmi_txvalid_o, |
input utmi_txready_i, |
input [7:0] utmi_data_i, |
input utmi_rxvalid_i, |
input utmi_rxactive_i |
); |
|
//----------------------------------------------------------------- |
// Registers / Wires |
//----------------------------------------------------------------- |
reg start_ack_q; |
reg tx_pop_q; |
|
// Status |
reg status_tx_done_q; |
reg status_rx_done_q; |
reg status_crc_err_q; |
reg status_timeout_q; |
reg [7:0] status_response_q; |
|
reg utmi_txvalid_q; |
|
reg pid_byte_q; |
reg [15:0] byte_count_q; |
reg in_transfer_q; |
|
reg [2:0] rx_time_q; |
reg rx_time_en_q; |
reg [7:0] last_tx_time_q; |
|
reg send_data1_q; |
reg send_sof_q; |
reg send_ack_q; |
|
// CRC16 |
reg [15:0] crc_sum_q; |
wire [15:0] crc_out_w; |
wire [7:0] crc_data_in_w; |
|
// CRC5 |
wire [4:0] crc5_out_w; |
wire [4:0] crc5_next_w = crc5_out_w ^ 5'h1F; |
|
reg [15:0] token_q; |
|
reg wait_resp_q; |
|
//----------------------------------------------------------------- |
// Definitions |
//----------------------------------------------------------------- |
localparam RX_TIMEOUT = 8'd255; // ~5uS |
|
localparam PID_OUT = 8'hE1; |
localparam PID_IN = 8'h69; |
localparam PID_SOF = 8'hA5; |
localparam PID_SETUP = 8'h2D; |
|
localparam PID_DATA0 = 8'hC3; |
localparam PID_DATA1 = 8'h4B; |
|
localparam PID_ACK = 8'hD2; |
localparam PID_NAK = 8'h5A; |
localparam PID_STALL = 8'h1E; |
|
// States |
localparam STATE_IDLE = 4'd0; |
localparam STATE_RX_DATA = 4'd1; |
localparam STATE_TX_DATA = 4'd2; |
localparam STATE_TX_CRC = 4'd3; |
localparam STATE_TX_CRC1 = 4'd4; |
localparam STATE_TX_CRC2 = 4'd5; |
localparam STATE_TX_TOKEN1 = 4'd6; |
localparam STATE_TX_TOKEN2 = 4'd7; |
localparam STATE_TX_TOKEN3 = 4'd8; |
localparam STATE_TX_ACKNAK = 4'd9; |
localparam STATE_TX_WAIT = 4'd10; |
localparam STATE_RX_WAIT = 4'd11; |
|
localparam RX_TIME_ZERO = 3'd0; |
localparam RX_TIME_INC = 3'd1; |
localparam RX_TIME_READY = 3'd7; // 2-bit times |
|
//----------------------------------------------------------------- |
// Wires |
//----------------------------------------------------------------- |
|
// New byte received |
wire data_ready_w = (utmi_rxvalid_i & utmi_rxactive_i); |
|
// 2-bit times after last RX (inter-packet delay)? |
wire autoresp_thresh_w = send_ack_q & rx_time_en_q & (rx_time_q == RX_TIME_READY); |
|
// Response timeout (no response after 500uS from transmit) |
wire rx_resp_timeout_w = (last_tx_time_q >= RX_TIMEOUT) & wait_resp_q; |
|
//----------------------------------------------------------------- |
// State Machine |
//----------------------------------------------------------------- |
|
// Current state |
reg [3:0] state_q; |
reg [3:0] next_state_r; |
|
always @ * |
begin |
next_state_r = state_q; |
|
//----------------------------------------- |
// Tx State Machine |
//----------------------------------------- |
case (state_q) |
|
//----------------------------------------- |
// TX_TOKEN1 (byte 1 of token) |
//----------------------------------------- |
STATE_TX_TOKEN1 : |
begin |
// Data sent? |
if (utmi_txready_i) |
next_state_r = STATE_TX_TOKEN2; |
end |
//----------------------------------------- |
// TX_TOKEN2 (byte 2 of token) |
//----------------------------------------- |
STATE_TX_TOKEN2 : |
begin |
// Data sent? |
if (utmi_txready_i) |
next_state_r = STATE_TX_TOKEN3; |
end |
//----------------------------------------- |
// TX_TOKEN3 (byte 3 of token) |
//----------------------------------------- |
STATE_TX_TOKEN3 : |
begin |
// Data sent? |
if (utmi_txready_i) |
begin |
// SOF - no data packet |
if (send_sof_q) |
next_state_r = STATE_IDLE; |
// IN - wait for data |
else if (in_transfer_q) |
next_state_r = STATE_RX_WAIT; |
// OUT/SETUP - Send data or ZLP |
else |
next_state_r = STATE_TX_DATA; |
end |
end |
//----------------------------------------- |
// TX_DATA |
//----------------------------------------- |
STATE_TX_DATA : |
begin |
// Last data byte sent? |
if (utmi_txready_i && (byte_count_q == 16'b0)) |
next_state_r = STATE_TX_CRC; |
end |
//----------------------------------------- |
// TX_CRC (generate) |
//----------------------------------------- |
STATE_TX_CRC : |
begin |
next_state_r = STATE_TX_CRC1; |
end |
//----------------------------------------- |
// TX_CRC1 (first byte) |
//----------------------------------------- |
STATE_TX_CRC1 : |
begin |
// Data sent? |
if (utmi_txready_i) |
next_state_r = STATE_TX_CRC2; |
end |
//----------------------------------------- |
// TX_CRC (second byte) |
//----------------------------------------- |
STATE_TX_CRC2 : |
begin |
// Data sent? |
if (utmi_txready_i) |
begin |
// If a response is expected |
if (wait_resp_q) |
next_state_r = STATE_RX_WAIT; |
// No response expected (e.g ISO transfer) |
else |
next_state_r = STATE_IDLE; |
end |
end |
//----------------------------------------- |
// STATE_TX_WAIT |
//----------------------------------------- |
STATE_TX_WAIT : |
begin |
// Waited long enough? |
if (autoresp_thresh_w) |
next_state_r = STATE_TX_ACKNAK; |
end |
//----------------------------------------- |
// STATE_TX_ACKNAK |
//----------------------------------------- |
STATE_TX_ACKNAK : |
begin |
// Data sent? |
if (utmi_txready_i) |
next_state_r = STATE_IDLE; |
end |
//----------------------------------------- |
// STATE_RX_WAIT |
//----------------------------------------- |
STATE_RX_WAIT : |
begin |
// Data received? |
if (data_ready_w) |
next_state_r = STATE_RX_DATA; |
// Waited long enough? |
else if (rx_resp_timeout_w) |
next_state_r = STATE_IDLE; |
end |
//----------------------------------------- |
// RX_DATA |
//----------------------------------------- |
STATE_RX_DATA : |
begin |
// Receive complete |
if (~utmi_rxvalid_i & ~utmi_rxactive_i) |
begin |
// Send an ACK/NAK response without CPU interaction? |
if (send_ack_q && (status_response_q == PID_DATA0 || status_response_q == PID_DATA1)) |
next_state_r = STATE_TX_WAIT; |
else |
next_state_r = STATE_IDLE; |
end |
end |
//----------------------------------------- |
// IDLE / RECEIVE BEGIN |
//----------------------------------------- |
STATE_IDLE : |
begin |
// Token transfer request |
if (start_i) |
next_state_r = STATE_TX_TOKEN1; |
end |
default : |
; |
endcase |
end |
|
// Update state |
always @ (posedge rst_i or posedge clk_i) |
if (rst_i == 1'b1) |
state_q <= STATE_IDLE; |
else |
state_q <= next_state_r; |
|
//----------------------------------------------------------------- |
// Tx Enable |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
begin |
if (rst_i == 1'b1) |
begin |
utmi_txvalid_q <= 1'b0; |
end |
else |
begin |
case (state_q) |
|
//----------------------------------------- |
// TX_TOKEN1 (byte 1 of token) |
//----------------------------------------- |
STATE_TX_TOKEN1 : |
begin |
utmi_txvalid_q <= 1'b1; |
end |
//----------------------------------------- |
// TX_TOKEN3 (byte 3 of token) |
//----------------------------------------- |
STATE_TX_TOKEN3 : |
begin |
// Data sent? |
if (utmi_txready_i) |
utmi_txvalid_q <= 1'b0; |
end |
//----------------------------------------- |
// TX_DATA |
//----------------------------------------- |
STATE_TX_DATA : |
begin |
// Tx active |
utmi_txvalid_q <= 1'b1; |
end |
//----------------------------------------- |
// TX_CRC (second byte) |
//----------------------------------------- |
STATE_TX_CRC2 : |
begin |
// Data sent? |
if (utmi_txready_i) |
utmi_txvalid_q <= 1'b0; |
end |
//----------------------------------------- |
// STATE_TX_ACKNAK |
//----------------------------------------- |
STATE_TX_ACKNAK : |
begin |
// Data sent? |
if (utmi_txready_i) |
utmi_txvalid_q <= 1'b0; |
else |
utmi_txvalid_q <= 1'b1; |
end |
|
default : |
; |
endcase |
end |
end |
|
//----------------------------------------------------------------- |
// Tx Token |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
token_q <= 16'h0000; |
else if (state_q == STATE_IDLE) |
token_q <= {token_dev_i, token_ep_i, 5'b0}; |
// PID of token sent, capture calculated CRC for token packet |
else if (state_q == STATE_TX_TOKEN1 && utmi_txready_i) |
token_q[4:0] <= crc5_next_w; |
|
//----------------------------------------------------------------- |
// Tx Timer |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
last_tx_time_q <= 8'd0; |
// Start counting from last Tx |
else if (state_q == STATE_IDLE || utmi_txvalid_q) |
last_tx_time_q <= 8'd0; |
// Increment the Tx timeout |
else if (last_tx_time_q != RX_TIMEOUT) |
last_tx_time_q <= last_tx_time_q + 8'd1; |
|
//----------------------------------------------------------------- |
// Transmit / Receive counter |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
byte_count_q <= 16'h0000; |
// New transfer request (not automatic SOF request) |
else if (state_q == STATE_IDLE && start_i && !sof_transfer_i) |
byte_count_q <= data_len_i; |
// Transmit byte |
else if (state_q == STATE_TX_DATA && utmi_txready_i) |
begin |
// Count down data left to send |
if (byte_count_q != 16'd0) |
byte_count_q <= byte_count_q - 16'd1; |
end |
// Received byte |
else if ((state_q == STATE_RX_WAIT || state_q == STATE_RX_DATA) && data_ready_w) |
byte_count_q <= byte_count_q + 16'd1; |
|
// Recognise first byte in the Tx data packet |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
pid_byte_q <= 1'b0; |
// New transfer request, first byte sent in TX_DATA state is the PID |
else if (state_q == STATE_IDLE && start_i) |
pid_byte_q <= 1'b1; |
// Transmit byte |
else if (state_q == STATE_TX_DATA && utmi_txready_i) |
pid_byte_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// Transfer start ack |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
start_ack_q <= 1'b0; |
// First byte of PID sent, ack transfer request |
else if (state_q == STATE_TX_TOKEN1 && utmi_txready_i) |
start_ack_q <= 1'b1; |
else |
start_ack_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// Record request details |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
in_transfer_q <= 1'b0; |
send_ack_q <= 1'b0; |
send_data1_q <= 1'b0; |
send_sof_q <= 1'b0; |
end |
// Start of new request |
else if (state_q == STATE_IDLE && start_i) |
begin |
// Transfer request |
// e.g. (H)SOF [sof_transfer_i] |
// (H)OUT + (H)DATA + (F)ACK/NACK/STALL [data_len_i >= 0 && !in_transfer_i] |
// (H)IN + (F)DATA + (H)ACK [in_transfer_i] |
// (H)IN + (F)NAK/STALL [in_transfer_i] |
in_transfer_q <= in_transfer_i; |
|
// Send ACK in response to IN DATA |
send_ack_q <= in_transfer_i && resp_expected_i; |
|
// DATA0/1 |
send_data1_q <= data_idx_i; |
|
send_sof_q <= sof_transfer_i; |
end |
|
//----------------------------------------------------------------- |
// Response delay timer |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
rx_time_q <= RX_TIME_ZERO; |
rx_time_en_q <= 1'b0; |
end |
else if (state_q == STATE_IDLE) |
begin |
rx_time_q <= RX_TIME_ZERO; |
rx_time_en_q <= 1'b0; |
end |
// Receive complete |
else if (state_q == STATE_RX_DATA && !utmi_rxactive_i) |
begin |
// Reset time since end of last data byte |
rx_time_q <= RX_TIME_ZERO; |
rx_time_en_q <= 1'b1; |
end |
// Increment timer if enabled (and less than the threshold) |
else if (rx_time_en_q && rx_time_q != RX_TIME_READY) |
rx_time_q <= rx_time_q + RX_TIME_INC; |
|
// Response expected |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
wait_resp_q <= 1'b0; |
// Incoming data |
else if (state_q == STATE_RX_WAIT && data_ready_w) |
wait_resp_q <= 1'b0; |
else if (state_q == STATE_IDLE && start_i) |
wait_resp_q <= resp_expected_i; |
|
//----------------------------------------------------------------- |
// Status |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
begin |
if (rst_i == 1'b1) |
begin |
status_response_q <= 8'h00; |
status_timeout_q <= 1'b0; |
status_rx_done_q <= 1'b0; |
status_tx_done_q <= 1'b0; |
end |
else |
begin |
case (state_q) |
|
//----------------------------------------- |
// RX_WAIT |
//----------------------------------------- |
STATE_RX_WAIT : |
begin |
// Store response PID |
if (data_ready_w) |
status_response_q <= utmi_data_i; |
|
// Waited long enough? |
if (rx_resp_timeout_w) |
status_timeout_q <= 1'b1; |
|
status_tx_done_q <= 1'b0; |
end |
//----------------------------------------- |
// RX_DATA |
//----------------------------------------- |
STATE_RX_DATA : |
begin |
// Receive complete |
if (!utmi_rxactive_i) |
status_rx_done_q <= 1'b1; |
else |
status_rx_done_q <= 1'b0; |
end |
//----------------------------------------- |
// TX_CRC (second byte) |
//----------------------------------------- |
STATE_TX_CRC2 : |
begin |
// Data sent? |
if (utmi_txready_i && !wait_resp_q) |
begin |
// Transfer now complete |
status_tx_done_q <= 1'b1; |
end |
end |
//----------------------------------------- |
// IDLE / RECEIVE BEGIN |
//----------------------------------------- |
STATE_IDLE : |
begin |
// Transfer request |
// e.g. (H)SOF [sof_transfer_i] |
// (H)OUT + (H)DATA + (F)ACK/NACK/STALL [data_len_i >= 0 && !in_transfer_i] |
// (H)IN + (F)DATA + (H)ACK [in_transfer_i] |
// (H)IN + (F)NAK/STALL [in_transfer_i] |
if (start_i && !sof_transfer_i) // (not automatic SOF request) |
begin |
// Clear status |
status_response_q <= 8'h00; |
status_timeout_q <= 1'b0; |
end |
|
status_rx_done_q <= 1'b0; |
status_tx_done_q <= 1'b0; |
end |
//----------------------------------------- |
// DEFAULT |
//----------------------------------------- |
default : |
begin |
status_rx_done_q <= 1'b0; |
status_tx_done_q <= 1'b0; |
end |
endcase |
end |
end |
|
//----------------------------------------------------------------- |
// FIFO access |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
tx_pop_q <= 1'b0; |
// Data byte unload (not PID) |
else if (state_q == STATE_TX_DATA && utmi_txready_i && !pid_byte_q) |
tx_pop_q <= 1'b1; |
else |
tx_pop_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// CRC |
//----------------------------------------------------------------- |
|
// CRC16 (Data) |
usbh_crc16 |
u_crc16 |
( |
.crc_i(crc_sum_q), |
.data_i(crc_data_in_w), |
.crc_o(crc_out_w) |
); |
|
// CRC5 (Token) |
usbh_crc5 |
u_crc5 |
( |
.crc_i(5'h1F), |
.data_i(token_q[15:5]), |
.crc_o(crc5_out_w) |
); |
|
// CRC control / check |
always @ (posedge rst_i or posedge clk_i ) |
begin |
if (rst_i == 1'b1) |
begin |
crc_sum_q <= 16'hFFFF; |
status_crc_err_q <= 1'b0; |
end |
else |
begin |
case (state_q) |
//----------------------------------------- |
// TX_DATA |
//----------------------------------------- |
STATE_TX_DATA : |
begin |
// Data sent? |
if (utmi_txready_i) |
begin |
// First byte is PID (not CRC'd), reset CRC16 |
if (pid_byte_q) |
crc_sum_q <= 16'hFFFF; |
// Next CRC start value |
else |
crc_sum_q <= crc_out_w; |
end |
end |
//----------------------------------------- |
// TX_CRC (generate) |
//----------------------------------------- |
STATE_TX_CRC : |
begin |
// Next CRC start value |
crc_sum_q <= crc_sum_q ^ 16'hFFFF; |
end |
//----------------------------------------- |
// RX_WAIT |
//----------------------------------------- |
STATE_RX_WAIT : |
begin |
// Reset CRC16 |
crc_sum_q <= 16'hFFFF; |
end |
//----------------------------------------- |
// RX_DATA |
//----------------------------------------- |
STATE_RX_DATA : |
begin |
// Data received? |
if (data_ready_w) |
begin |
// Next CRC start value |
crc_sum_q <= crc_out_w; |
end |
// Receive complete |
else if (utmi_rxactive_i == 1'b0) |
begin |
// If some data received, check CRC |
if (crc_sum_q != 16'hB001 && in_transfer_q && byte_count_q != 16'd1) |
status_crc_err_q <= 1'b1; |
else |
status_crc_err_q <= 1'b0; |
end |
end |
|
//----------------------------------------- |
// IDLE / RECEIVE BEGIN |
//----------------------------------------- |
STATE_IDLE : |
begin |
// Start transfer request |
if (start_i && !sof_transfer_i) |
begin |
// Clear error flag! |
status_crc_err_q <= 1'b0; |
end |
end |
default : |
; |
endcase |
end |
end |
|
//----------------------------------------------------------------- |
// Assignments |
//----------------------------------------------------------------- |
wire [15:0] token_rev_w; |
|
genvar i; |
generate |
for (i=0; i < 16; i=i+1) |
begin : LOOP |
assign token_rev_w[i] = token_q[15-i]; |
end |
endgenerate |
|
// Transmit data |
assign utmi_data_o = (state_q == STATE_TX_CRC1) ? crc_sum_q[7:0] : |
(state_q == STATE_TX_CRC2) ? crc_sum_q[15:8] : |
(state_q == STATE_TX_TOKEN1) ? token_pid_i : |
(state_q == STATE_TX_TOKEN2) ? token_rev_w[7:0] : |
(state_q == STATE_TX_TOKEN3) ? token_rev_w[15:8] : |
(state_q == STATE_TX_DATA && pid_byte_q && send_data1_q) ? PID_DATA1 : |
(state_q == STATE_TX_DATA && pid_byte_q && ~send_data1_q) ? PID_DATA0 : |
(state_q == STATE_TX_ACKNAK) ? PID_ACK : |
tx_data_i; |
assign utmi_txvalid_o = utmi_txvalid_q; |
|
// Push incoming data into FIFO (not PID or CRC) |
assign rx_data_o = utmi_data_i; |
assign rx_push_o = (state_q != STATE_IDLE && state_q != STATE_RX_WAIT) & data_ready_w; |
|
assign crc_data_in_w = (state_q == STATE_RX_DATA || state_q == STATE_RX_WAIT) ? utmi_data_i : tx_data_i; |
|
assign rx_count_o = byte_count_q; |
assign idle_o = (state_q == STATE_IDLE); |
|
assign ack_o = start_ack_q; |
|
assign tx_pop_o = tx_pop_q; |
|
assign tx_done_o = status_tx_done_q; |
assign rx_done_o = status_rx_done_q; |
assign crc_err_o = status_crc_err_q; |
assign timeout_o = status_timeout_q; |
assign response_o = status_response_q; |
|
endmodule |
/rtl/usbh_fifo.v
0,0 → 1,124
//----------------------------------------------------------------- |
// USB Full Speed Host |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: USB FIFO - simple FIFO |
//----------------------------------------------------------------- |
module usbh_fifo |
|
//----------------------------------------------------------------- |
// Params |
//----------------------------------------------------------------- |
#( |
parameter WIDTH = 8, |
parameter DEPTH = 4, |
parameter ADDR_W = 2 |
) |
|
//----------------------------------------------------------------- |
// Ports |
//----------------------------------------------------------------- |
( |
input clk_i, |
input rst_i, |
|
input [WIDTH-1:0] data_i, |
input push_i, |
|
output full_o, |
output empty_o, |
|
output [WIDTH-1:0] data_o, |
input pop_i, |
|
input flush_i |
); |
|
//----------------------------------------------------------------- |
// Defs / Params |
//----------------------------------------------------------------- |
localparam COUNT_W = ADDR_W + 1; |
|
//----------------------------------------------------------------- |
// Registers |
//----------------------------------------------------------------- |
reg [WIDTH-1:0] ram [DEPTH-1:0]; |
reg [ADDR_W-1:0] rd_ptr_q; |
reg [ADDR_W-1:0] wr_ptr_q; |
reg [COUNT_W-1:0] count_q; |
|
//----------------------------------------------------------------- |
// Sequential |
//----------------------------------------------------------------- |
always @ (posedge clk_i or posedge rst_i) |
begin |
if (rst_i) |
begin |
count_q <= {(COUNT_W) {1'b0}}; |
rd_ptr_q <= {(ADDR_W) {1'b0}}; |
wr_ptr_q <= {(ADDR_W) {1'b0}}; |
end |
else if (flush_i) |
begin |
count_q <= {(COUNT_W) {1'b0}}; |
rd_ptr_q <= {(ADDR_W) {1'b0}}; |
wr_ptr_q <= {(ADDR_W) {1'b0}}; |
end |
else |
begin |
// Push |
if (push_i && !full_o) |
begin |
ram[wr_ptr_q] <= data_i; |
wr_ptr_q <= wr_ptr_q + 1; |
end |
|
// Pop |
if (pop_i && !empty_o) |
rd_ptr_q <= rd_ptr_q + 1; |
|
// Count up |
if ((push_i && !full_o) && !(pop_i && !empty_o)) |
count_q <= count_q + 1; |
// Count down |
else if (!(push_i && !full_o) && (pop_i && !empty_o)) |
count_q <= count_q - 1; |
end |
end |
|
//------------------------------------------------------------------- |
// Assignments |
//------------------------------------------------------------------- |
assign full_o = (count_q == DEPTH); |
assign empty_o = (count_q == 0); |
|
assign data_o = ram[rd_ptr_q]; |
|
endmodule |
/rtl/usbh_periph.v
0,0 → 1,376
//----------------------------------------------------------------- |
// USB Full Speed Host |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: Peripheral interface for USB host |
//----------------------------------------------------------------- |
module usbh_periph |
( |
// Clocking (48MHz) & Reset |
input clk_i, |
input rst_i, |
|
output intr_o, |
|
// Peripheral Interface (from CPU) |
input [7:0] addr_i, |
input [31:0] data_i, |
output [31:0] data_o, |
input we_i, |
input stb_i, |
|
// UTMI interface |
input [1:0] utmi_linestate_i, |
input utmi_rxerror_i, |
|
// Control |
output sie_start_o, |
output sie_sof_en_o, |
output sie_rst_o, |
output [7:0] sie_token_pid_o, |
output [10:0] sie_token_data_o, |
output [15:0] sie_tx_count_o, |
output sie_data_idx_o, |
output sie_in_transfer_o, |
output sie_resp_expected_o, |
|
// FIFO |
output [7:0] sie_tx_data_o, |
output sie_tx_push_o, |
output sie_tx_flush_o, |
output sie_rx_pop_o, |
input [7:0] sie_rx_data_i, |
|
// Status |
input sie_rx_crc_err_i, |
input [7:0] sie_rx_resp_pid_i, |
input sie_rx_resp_timeout_i, |
input [15:0] sie_rx_count_i, |
input sie_rx_idle_i, |
input sie_req_ack_i, |
input [15:0] sie_sof_time_i, |
input sie_rx_done_i, |
input sie_tx_done_i, |
input sie_sof_irq_i |
); |
|
//----------------------------------------------------------------- |
// Peripheral Memory Map |
//----------------------------------------------------------------- |
`define USB_CTRL 8'h00 |
`define USB_TX_FLUSH 2 |
`define USB_ENABLE_SOF 1 |
`define USB_RESET_ACTIVE 0 |
`define USB_STATUS 8'h00 |
`define USB_STAT_SOF_TIME 31:16 |
`define USB_STAT_RX_ERROR 2 |
`define USB_STAT_LINESTATE_BITS 1:0 |
`define USB_IRQ 8'h04 |
`define USB_IRQ_MASK 8'h08 |
`define USB_IRQ_SOF 0 |
`define USB_IRQ_DONE 1 |
`define USB_IRQ_ERR 2 |
`define USB_XFER_DATA 8'h0c |
`define USB_XFER_DATA_TX_LEN 15:0 |
`define USB_XFER_TOKEN 8'h10 |
`define USB_XFER_START 31 |
`define USB_XFER_IN 30 |
`define USB_XFER_ACK 29 |
`define USB_XFER_PID_DATAX 28 |
`define USB_XFER_PID_BITS 23:16 |
`define USB_RX_STAT 8'h14 |
`define USB_RX_STAT_START_PEND 31 |
`define USB_RX_STAT_CRC_ERR 30 |
`define USB_RX_STAT_RESP_TIMEOUT 29 |
`define USB_RX_STAT_IDLE 28 |
`define USB_RX_STAT_RESP_BITS 23:16 |
`define USB_RX_STAT_COUNT_BITS 15:0 |
`define USB_WR_DATA 8'h18 |
`define USB_RD_DATA 8'h18 |
|
//----------------------------------------------------------------- |
// Registers / Wires |
//----------------------------------------------------------------- |
reg usb_err_q; |
|
reg intr_done_q; |
reg intr_sof_q; |
reg intr_err_q; |
|
// Interrupt Mask |
reg intr_mask_done_q; |
reg intr_mask_sof_q; |
reg intr_mask_err_q; |
|
// Control |
reg sie_start_q; |
reg sie_sof_en_q; |
reg sie_rst_q; |
reg [7:0] sie_token_pid_q; |
reg [10:0] sie_token_data_q; |
reg [15:0] sie_tx_count_q; |
reg sie_data_idx_q; |
reg sie_in_transfer_q; |
reg sie_resp_expected_q; |
|
// FIFO |
reg [7:0] sie_tx_data_q; |
reg sie_tx_push_q; |
reg sie_tx_flush_q; |
reg sie_rx_pop_q; |
|
//----------------------------------------------------------------- |
// Control |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
sie_sof_en_q <= 1'b0; |
sie_rst_q <= 1'b0; |
sie_tx_flush_q <= 1'b0; |
end |
// IO Write Cycle |
else if (we_i && stb_i && (addr_i == `USB_CTRL)) |
begin |
sie_rst_q <= data_i[`USB_RESET_ACTIVE]; |
sie_sof_en_q <= data_i[`USB_ENABLE_SOF]; |
sie_tx_flush_q <= data_i[`USB_TX_FLUSH]; |
end |
else |
sie_tx_flush_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// Data FIFO Write |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
sie_tx_data_q <= 8'h00; |
sie_tx_push_q <= 1'b0; |
end |
// IO Write Cycle |
else if (we_i && stb_i && (addr_i == `USB_WR_DATA)) |
begin |
sie_tx_data_q <= data_i[7:0]; |
sie_tx_push_q <= 1'b1; |
end |
else |
sie_tx_push_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// Tx Length |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
sie_tx_count_q <= 16'h0000; |
// IO Write Cycle |
else if (we_i && stb_i && (addr_i == `USB_XFER_DATA)) |
sie_tx_count_q <= data_i[`USB_XFER_DATA_TX_LEN]; |
|
//----------------------------------------------------------------- |
// Tx Token |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
sie_start_q <= 1'b0; |
sie_in_transfer_q <= 1'b0; |
sie_resp_expected_q<= 1'b0; |
sie_token_pid_q <= 8'h00; |
sie_token_data_q <= 11'h000; |
sie_data_idx_q <= 1'b0; |
|
end |
// IO Write Cycle |
else if (we_i && stb_i && (addr_i == `USB_XFER_TOKEN)) |
begin |
sie_start_q <= data_i[`USB_XFER_START]; |
sie_in_transfer_q <= data_i[`USB_XFER_IN]; |
sie_resp_expected_q<= data_i[`USB_XFER_ACK]; |
sie_data_idx_q <= data_i[`USB_XFER_PID_DATAX]; |
sie_token_pid_q <= data_i[`USB_XFER_PID_BITS]; |
sie_token_data_q <= { data_i[9], data_i[10], data_i[11], data_i[12], data_i[13], data_i[14], data_i[15], |
data_i[5], data_i[6], data_i[7], data_i[8] }; |
end |
else if (sie_req_ack_i) |
sie_start_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// Interrupt Masks |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
intr_mask_done_q <= 1'b0; |
intr_mask_sof_q <= 1'b0; |
intr_mask_err_q <= 1'b0; |
end |
// IO Write Cycle |
else if (we_i && stb_i && (addr_i == `USB_IRQ_MASK)) |
begin |
intr_mask_done_q <= data_i[`USB_IRQ_DONE]; |
intr_mask_sof_q <= data_i[`USB_IRQ_SOF]; |
intr_mask_err_q <= data_i[`USB_IRQ_ERR]; |
end |
|
//----------------------------------------------------------------- |
// Record Errors |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
usb_err_q <= 1'b0; |
// Clear error |
else if (we_i && stb_i && (addr_i == `USB_XFER_TOKEN || addr_i == `USB_CTRL)) |
usb_err_q <= 1'b0; |
// Record bus errors |
else if (utmi_rxerror_i) |
usb_err_q <= 1'b1; |
|
//----------------------------------------------------------------- |
// Data FIFO Read |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
sie_rx_pop_q <= 1'b0; |
// IO Read Cycle |
else if (~we_i && stb_i && (addr_i == `USB_RD_DATA)) |
sie_rx_pop_q <= 1'b1; |
else |
sie_rx_pop_q <= 1'b0; |
|
assign sie_start_o = sie_start_q; |
assign sie_sof_en_o = sie_sof_en_q; |
assign sie_rst_o = sie_rst_q; |
assign sie_token_pid_o = sie_token_pid_q; |
assign sie_token_data_o = sie_token_data_q; |
assign sie_tx_count_o = sie_tx_count_q; |
assign sie_data_idx_o = sie_data_idx_q; |
assign sie_in_transfer_o = sie_in_transfer_q; |
assign sie_resp_expected_o = sie_resp_expected_q; |
assign sie_tx_data_o = sie_tx_data_q; |
assign sie_tx_push_o = sie_tx_push_q; |
assign sie_tx_flush_o = sie_tx_flush_q; |
assign sie_rx_pop_o = sie_rx_pop_q; |
|
//----------------------------------------------------------------- |
// Peripheral Registers (Read) |
//----------------------------------------------------------------- |
reg [31:0] data_r; |
|
always @ * |
begin |
data_r = 32'h00000000; |
|
case (addr_i) |
|
`USB_STATUS : |
begin |
data_r[`USB_STAT_SOF_TIME] = sie_sof_time_i; |
data_r[`USB_STAT_RX_ERROR] = usb_err_q; |
data_r[`USB_STAT_LINESTATE_BITS] = utmi_linestate_i; |
end |
|
`USB_RX_STAT : |
begin |
data_r[`USB_RX_STAT_START_PEND] = sie_start_o; |
data_r[`USB_RX_STAT_CRC_ERR] = sie_rx_crc_err_i; |
data_r[`USB_RX_STAT_RESP_TIMEOUT] = sie_rx_resp_timeout_i; |
data_r[`USB_RX_STAT_IDLE] = sie_rx_idle_i; |
data_r[`USB_RX_STAT_RESP_BITS] = sie_rx_resp_pid_i; |
data_r[`USB_RX_STAT_COUNT_BITS] = sie_rx_count_i; |
end |
|
`USB_RD_DATA : |
data_r = {24'h000000, sie_rx_data_i}; |
|
`USB_IRQ : |
begin |
data_r[`USB_IRQ_DONE] = intr_done_q; |
data_r[`USB_IRQ_SOF] = intr_sof_q; |
data_r[`USB_IRQ_ERR] = intr_err_q; |
end |
|
`USB_IRQ_MASK : |
begin |
data_r[`USB_IRQ_DONE] = intr_mask_done_q; |
data_r[`USB_IRQ_SOF] = intr_mask_sof_q; |
data_r[`USB_IRQ_ERR] = intr_mask_err_q; |
end |
|
default : |
data_r = 32'h00000000; |
endcase |
end |
|
assign data_o = data_r; |
|
//----------------------------------------------------------------- |
// Interrupts |
//----------------------------------------------------------------- |
reg err_cond_q; |
reg intr_q; |
|
always @ (posedge rst_i or posedge clk_i ) |
if (rst_i == 1'b1) |
begin |
intr_done_q <= 1'b0; |
intr_sof_q <= 1'b0; |
intr_err_q <= 1'b0; |
err_cond_q <= 1'b0; |
|
intr_q <= 1'b0; |
end |
else |
begin |
if (sie_rx_done_i || sie_tx_done_i) |
intr_done_q <= 1'b1; |
else if (we_i && stb_i && (addr_i == `USB_IRQ) && data_i[`USB_IRQ_DONE]) |
intr_done_q <= 1'b0; |
|
if (sie_sof_irq_i) |
intr_sof_q <= 1'b1; |
else if (we_i && stb_i && (addr_i == `USB_IRQ) && data_i[`USB_IRQ_SOF]) |
intr_sof_q <= 1'b0; |
|
if ((sie_rx_crc_err_i || sie_rx_resp_timeout_i) && (!err_cond_q)) |
intr_err_q <= 1'b1; |
else if (we_i && stb_i && (addr_i == `USB_IRQ) && data_i[`USB_IRQ_ERR]) |
intr_err_q <= 1'b0; |
|
err_cond_q <= (sie_rx_crc_err_i | sie_rx_resp_timeout_i); |
|
intr_q <= (intr_done_q & intr_mask_done_q) | |
(intr_err_q & intr_mask_err_q) | |
(intr_sof_q & intr_mask_sof_q); |
end |
|
assign intr_o = intr_q; |
|
endmodule |
/docs/registers.md
0,0 → 1,74
##### Register Map |
|
| Offset | Name | Description | |
| ------ | --------------- | --------------------------------------------------- | |
| 0x00 | USB_CTRL | [Write] Control of USB reset, SOF and Tx FIFO flush | |
| 0x00 | USB_STATUS | [Read] line state, Rx error status and frame time | |
| 0x04 | USB_IRQ_ACK | [Write] Acknowledge IRQ by setting relevant bit | |
| 0x04 | USB_IRQ_STS | [Read] Interrupt status | |
| 0x08 | USB_IRQ_MASK | [Read/Write] Interrupt mask | |
| 0x0c | USB_XFER_DATA | [Write] Tx payload transfer length | |
| 0x10 | USB_XFER_TOKEN | [Write] Transfer control info (direction, type) | |
| 0x14 | USB_RX_STAT | [Read] Transfer status (Rx length, error, idle) | |
| 0x18 | USB_WR_DATA | [Write] Tx FIFO address for write data | |
| 0x18 | USB_RD_DATA | [Read] Tx FIFO address for read data | |
|
|
##### Register: USB_CTRL |
|
| Bits | Name | Description | |
| ---- | ------------------- | ------------------------------------------------ | |
| 2 | USB_TX_FLUSH | Flush Tx FIFO | |
| 1 | USB_ENABLE_SOF | Enable SOF (start of frame) packet generation | |
| 0 | USB_RESET_ACTIVE | 1 = assert USB reset state, 0 = normal | |
|
##### Register: USB_STATUS |
|
| Bits | Name | Description | |
| ----- | ----------------------- | ------------------------------------------- | |
| 31:16 | USB_STAT_SOF_TIME | Current frame time (0 - 47999) | |
| 2 | USB_STAT_RX_ERROR | Rx error detected (UTMI). Clear on new xfer.| |
| 1:0 | USB_STAT_LINESTATE_BITS | Line state (1 = D-, 1 = D+) | |
|
##### Register: USB_IRQ_ACK / USB_IRQ_STS / USB_IRQ_MASK |
|
| Bits | Name | Description | |
| ---- | -------------- | ----------------------------------------------------- | |
| 2 | USB_IRQ_ERR | Interrupt on error conditions. | |
| 1 | USB_IRQ_DONE | Interrupt on transfer completion | |
| 0 | USB_IRQ_SOF | Interrupt on start of frame | |
|
##### Register: USB_XFER_DATA |
|
| Bits | Name | Description | |
| ----- | ----------------------- | ------------------------------------------- | |
| 15:0 | USB_XFER_DATA_TX_LEN | Tx transfer data length | |
|
##### Register: USB_XFER_TOKEN |
|
| Bits | Name | Description | |
| ----- | ------------------ | ------------------------------------------------ | |
| 31 | USB_XFER_START | Transfer start request | |
| 30 | USB_XFER_IN | IN transfer (1) or OUT transfer (0) | |
| 29 | USB_XFER_ACK | Send ACK in response to IN data | |
| 28 | USB_XFER_PID_DATAX | DATA1 (1) or DATA0 (0) | |
| 23:16 | USB_XFER_PID_BITS | Token PID (SETUP=0x2d, OUT=0xE1 or IN=0x69) | |
| 15:9 | USB_XFER_DEV_ADDR | Device address | |
| 8:5 | USB_XFER_EP_ADDR | Endpoint address | |
|
##### Register: USB_RX_STAT |
|
| Bits | Name | Description | |
| ----- | ------------------------ | ------------------------------------------ | |
| 31 | USB_RX_STAT_START_PEND | Transfer start pending | |
| 30 | USB_RX_STAT_CRC_ERR | CRC error detected | |
| 29 | USB_RX_STAT_RESP_TIMEOUT | Response timeout detected (no response) | |
| 28 | USB_RX_STAT_IDLE | SIE idle | |
| 23:16 | USB_RX_STAT_RESP_BITS | Received response PID | |
| 15:0 | USB_RX_STAT_COUNT_BITS | Received data count | |
|
##### Register: USB_WR_DATA / USB_RD_DATA |
|
| Bits | Name | Description | |
| ----- | ---- | -------------------------------------------------------------- | |
| 7:0 | DATA | Data byte | |