1
0
mirror of https://github.com/avakar/usbcorev.git synced 2024-10-22 02:17:39 +08:00
usbcorev/usb.v
2014-09-07 00:24:18 +02:00

342 lines
10 KiB
Verilog

module usb(
input rst_n,
input clk_48,
input rx_j,
input rx_se0,
output tx_en,
output tx_j,
output tx_se0,
input[6:0] usb_address,
output usb_rst,
output reg transaction_active,
output reg[3:0] endpoint,
output reg direction_in,
output reg setup,
input data_toggle,
input[1:0] handshake,
output reg[7:0] data_out,
input[7:0] data_in,
input data_in_valid,
output reg data_strobe,
output reg success
);
localparam
hs_ack = 2'b00,
hs_none = 2'b01,
hs_nak = 2'b10,
hs_stall = 2'b11;
wire[3:0] recv_pid;
wire[7:0] recv_data;
wire recv_packet;
wire recv_datastrobe;
wire recv_crc5_ok;
wire recv_crc16_ok;
wire recv_short_idle;
usb_recv recv(
.rst_n(rst_n),
.clk_48(clk_48),
.rx_j(rx_j),
.rx_se0(rx_se0),
.short_idle(recv_short_idle),
.usb_rst(usb_rst),
.xpid(recv_pid),
.xdata(recv_data),
.xpacket(recv_packet),
.xdatastrobe(recv_datastrobe),
.xcrc5_ok(recv_crc5_ok),
.xcrc16_ok(recv_crc16_ok)
);
reg tx_transmit;
reg[7:0] tx_data;
wire tx_data_strobe;
reg tx_enable_crc16;
wire tx_send_crc16;
usb_tx tx(
.rst_n(rst_n),
.clk_48(clk_48),
.tx_en(tx_en),
.tx_j(tx_j),
.tx_se0(tx_se0),
.transmit(tx_transmit),
.data(tx_data),
.data_strobe(tx_data_strobe),
.update_crc16(tx_enable_crc16),
.send_crc16(tx_send_crc16)
);
reg[7:0] recv_queue_0;
reg[7:0] recv_queue_1;
reg recv_queue_0_valid;
reg recv_queue_1_valid;
always @(posedge clk_48) begin
if (!recv_packet) begin
recv_queue_1_valid <= 1'b0;
recv_queue_0_valid <= 1'b0;
end else if (recv_datastrobe) begin
data_out <= recv_queue_1;
recv_queue_1 <= recv_queue_0;
recv_queue_0 <= recv_data;
recv_queue_1_valid <= recv_queue_0_valid;
recv_queue_0_valid <= 1'b1;
end
end
localparam
st_idle = 3'b000,
st_data = 3'b001,
st_err = 3'b010,
st_send_handshake = 3'b011,
st_in = 3'b100,
st_prep_recv_ack = 3'b101,
st_recv_ack = 3'b110,
st_send_ack = 3'b111;
reg[2:0] state;
assign tx_send_crc16 = state == st_prep_recv_ack;
localparam
pt_special = 2'b00,
pt_token = 2'b01,
pt_handshake = 2'b10,
pt_data = 2'b11;
localparam
tok_out = 2'b00,
tok_sof = 2'b01,
tok_in = 2'b10,
tok_setup = 2'b11;
// Note that the token is perishable. The standard prescribes at most
// 7.5 bits of inter-packet idle time. We allow at most 31 bits between
// token activation and receiving the corresponding DATA packet.
reg[6:0] token_timeout;
wire token_active = token_timeout != 1'b0;
reg[1:0] handshake_latch;
always @(posedge clk_48 or negedge rst_n) begin
if (!rst_n) begin
success <= 1'b0;
state <= st_idle;
data_strobe <= 1'b0;
endpoint <= 1'sbx;
direction_in <= 1'bx;
setup <= 1'bx;
transaction_active <= 1'b0;
token_timeout <= 1'b0;
tx_transmit <= 1'b0;
tx_enable_crc16 <= 1'b0;
handshake_latch <= 2'bxx;
end else begin
if (token_timeout != 1'b0)
token_timeout <= token_timeout - 1'b1;
if (!transaction_active) begin
endpoint <= 1'sbx;
direction_in <= 1'bx;
setup <= 1'bx;
handshake_latch <= 2'bxx;
end
success <= 1'b0;
data_strobe <= 1'b0;
tx_transmit <= 1'b0;
case (state)
st_idle: begin
if (!token_active)
transaction_active <= 1'b0;
if (recv_packet) begin
if (recv_pid[1:0] == pt_token) begin
state <= st_data;
end else begin
if (recv_pid[1:0] == pt_data && !recv_pid[2] && token_active) begin
handshake_latch <= handshake;
state <= recv_pid[3] == data_toggle? st_data: st_send_ack;
end else begin
state <= st_err;
end
end
end
end
st_data: begin
if (!recv_packet) begin
state <= st_idle;
case (recv_pid[1:0])
pt_token: begin
if (recv_queue_1_valid && recv_crc5_ok && recv_queue_1[6:0] == usb_address && recv_pid[3:2] != tok_sof) begin
token_timeout <= 7'h7f;
transaction_active <= 1'b1;
endpoint <= { recv_queue_0[2:0], recv_queue_1[7] };
case (recv_pid[3:2])
tok_in: begin
direction_in <= 1'b1;
setup <= 1'bx;
state <= st_in;
end
tok_out: begin
direction_in <= 1'b0;
setup <= 1'b0;
end
tok_setup: begin
direction_in <= 1'b0;
setup <= 1'b1;
end
endcase
end else begin
transaction_active <= 1'b0;
endpoint <= 1'sbx;
direction_in <= 1'bx;
setup <= 1'bx;
end
end
pt_data: begin
if (recv_queue_1_valid && recv_crc16_ok) begin
if (handshake_latch == hs_ack || handshake_latch == hs_none)
success <= 1'b1;
state <= st_send_handshake;
end
end
default: begin
endpoint <= 1'sbx;
direction_in <= 1'bx;
setup <= 1'bx;
end
endcase
end else if (recv_datastrobe) begin
case (recv_pid[1:0])
pt_token: begin
if (recv_queue_1_valid)
state <= st_err;
end
pt_data: begin
if (recv_queue_1_valid && (handshake_latch == hs_ack || handshake_latch == hs_none))
data_strobe <= 1'b1;
end
default: begin
state <= st_err;
end
endcase
end
end
st_in: begin
tx_transmit <= tx_transmit;
if (!tx_transmit && recv_short_idle) begin
if (handshake != hs_ack && handshake != hs_none) begin
handshake_latch <= handshake;
state <= st_send_handshake;
end else begin
tx_data <= { !data_toggle, 3'b100, data_toggle, 3'b011 };
tx_transmit <= 1'b1;
end
end
if (tx_transmit && tx_data_strobe) begin
if (!data_in_valid) begin
if (handshake == hs_ack) begin
state <= st_prep_recv_ack;
end else begin
state <= st_err;
success <= 1'b1;
transaction_active <= 1'b0;
end
tx_enable_crc16 <= 1'b0;
tx_transmit <= 1'b0;
end else begin
tx_data <= data_in;
data_strobe <= 1'b1;
tx_enable_crc16 <= 1'b1;
end
end
end
st_prep_recv_ack: begin
token_timeout <= 7'h7f;
if (!tx_en && !recv_packet)
state <= st_recv_ack;
end
st_recv_ack: begin
if (recv_packet) begin
state <= st_err;
if (recv_pid == 4'b0010) begin
success <= 1'b1;
transaction_active <= 1'b0;
end
end
if (!token_active && !recv_packet)
state <= st_idle;
end
st_send_ack: begin
tx_transmit <= tx_transmit;
if (!tx_transmit && recv_short_idle) begin
tx_data <= 8'b11010010; // ACK
tx_transmit <= 1'b1;
end
if (tx_transmit && tx_data_strobe) begin
tx_transmit <= 1'b0;
state <= st_err;
end
end
st_send_handshake: begin
tx_transmit <= tx_transmit;
if (!tx_transmit && recv_short_idle) begin
case (handshake_latch)
hs_none: begin
state <= st_idle;
end
hs_ack: begin
tx_data <= 8'b11010010;
tx_transmit <= 1'b1;
end
hs_nak: begin
tx_data <= 8'b01011010;
tx_transmit <= 1'b1;
end
hs_stall: begin
tx_data <= 8'b00011110;
tx_transmit <= 1'b1;
end
endcase
end
if (tx_transmit && tx_data_strobe) begin
tx_transmit <= 1'b0;
state <= st_err;
end
end
default: begin
transaction_active <= 1'b0;
if (!tx_en && !recv_packet)
state <= st_idle;
end
endcase
end
end
endmodule