blob: 4a76ac2a49447e3e148456f401aab58ac314cf3b [file] [log] [blame]
// The OUT Protocol Engine receives data from the host.
module usb_fs_out_pe #(
parameter NUM_OUT_EPS = 1,
parameter MAX_OUT_PACKET_SIZE = 32
) (
input clk,
input reset,
input [NUM_OUT_EPS-1:0] reset_ep,
input [6:0] dev_addr,
////////////////////
// endpoint interface
////////////////////
output [NUM_OUT_EPS-1:0] out_ep_data_avail,
output reg [NUM_OUT_EPS-1:0] out_ep_setup = 0,
input [NUM_OUT_EPS-1:0] out_ep_data_get,
output reg [7:0] out_ep_data,
input [NUM_OUT_EPS-1:0] out_ep_stall,
output reg [NUM_OUT_EPS-1:0] out_ep_acked = 0,
// Added to provide a more constant way of detecting the current OUT EP than data get
input [NUM_OUT_EPS-1:0] out_ep_grant,
////////////////////
// rx path
////////////////////
// Strobed on reception of packet.
input rx_pkt_start,
input rx_pkt_end,
input rx_pkt_valid,
// Most recent packet received.
input [3:0] rx_pid,
input [6:0] rx_addr,
input [3:0] rx_endp,
input [10:0] rx_frame_num,
// rx_data is pushed into endpoint controller.
input rx_data_put,
input [7:0] rx_data,
////////////////////
// tx path
////////////////////
// Strobe to send new packet.
output reg tx_pkt_start = 0,
input tx_pkt_end,
output reg [3:0] tx_pid = 0
);
////////////////////////////////////////////////////////////////////////////////
// endpoint state machine
////////////////////////////////////////////////////////////////////////////////
localparam READY_FOR_PKT = 0;
localparam PUTTING_PKT = 1;
localparam GETTING_PKT = 2;
localparam STALL = 3;
reg [1:0] ep_state [NUM_OUT_EPS - 1:0];
reg [1:0] ep_state_next [NUM_OUT_EPS - 1:0];
////////////////////////////////////////////////////////////////////////////////
// out transfer state machine
////////////////////////////////////////////////////////////////////////////////
localparam IDLE = 0;
localparam RCVD_OUT = 1;
localparam RCVD_DATA_START = 2;
localparam RCVD_DATA_END = 3;
reg [1:0] out_xfr_state = IDLE;
reg [1:0] out_xfr_state_next;
reg out_xfr_start = 0;
reg new_pkt_end = 0;
reg rollback_data = 0;
reg [3:0] out_ep_num = 0;
reg [NUM_OUT_EPS - 1:0] out_ep_data_avail_i = 0;
reg [NUM_OUT_EPS - 1:0] out_ep_data_avail_j = 0;
// set when the endpoint buffer is unable to receive the out transfer
reg nak_out_transfer = 0;
// data toggle state
reg [NUM_OUT_EPS - 1:0] data_toggle = 0;
// latched on valid OUT/SETUP token
reg [3:0] current_endp = 0;
wire [1:0] current_ep_state = ep_state[current_endp];
// endpoint data buffer
reg [7:0] out_data_buffer [(MAX_OUT_PACKET_SIZE * NUM_OUT_EPS) - 1:0];
// current get_addr when outputting a packet from the buffer
reg [5:0] ep_get_addr [NUM_OUT_EPS - 1:0];
reg [5:0] ep_get_addr_next [NUM_OUT_EPS - 1:0];
// endpoint put_addrs when inputting a packet into the buffer
reg [5:0] ep_put_addr [NUM_OUT_EPS - 1:0];
// total buffer put addr, uses endpoint number and that endpoints current
// put address
wire [8:0] buffer_put_addr = {current_endp[3:0], ep_put_addr[current_endp][4:0]};
wire [8:0] buffer_get_addr = {out_ep_num[3:0], ep_get_addr[out_ep_num][4:0]};
wire token_received =
rx_pkt_end &&
rx_pkt_valid &&
rx_pid[1:0] == 2'b01 &&
rx_addr == dev_addr &&
rx_endp < NUM_OUT_EPS;
wire out_token_received =
token_received &&
rx_pid[3:2] == 2'b00;
wire setup_token_received =
token_received &&
rx_pid[3:2] == 2'b11;
wire invalid_packet_received =
rx_pkt_end &&
!rx_pkt_valid;
wire data_packet_received =
rx_pkt_end &&
rx_pkt_valid &&
rx_pid[2:0] == 3'b011;
wire non_data_packet_received =
rx_pkt_end &&
rx_pkt_valid &&
rx_pid[2:0] != 3'b011;
//reg last_data_toggle = 0;
wire bad_data_toggle =
data_packet_received &&
rx_pid[3] != data_toggle[rx_endp];
//last_data_toggle == data_toggle[current_endp];
////////////////////////////////////////////////////////////////////////////////
// endpoint state machine
////////////////////////////////////////////////////////////////////////////////
genvar ep_num;
generate
for (ep_num = 0; ep_num < NUM_OUT_EPS; ep_num = ep_num + 1) begin
always @* begin
ep_state_next[ep_num] <= ep_state[ep_num];
if (out_ep_stall[ep_num]) begin
ep_state_next[ep_num] <= STALL;
end else begin
case (ep_state[ep_num])
READY_FOR_PKT : begin
if (out_xfr_start && rx_endp == ep_num) begin
ep_state_next[ep_num] <= PUTTING_PKT;
end else begin
ep_state_next[ep_num] <= READY_FOR_PKT;
end
end
PUTTING_PKT : begin
if (new_pkt_end && current_endp == ep_num) begin
ep_state_next[ep_num] <= GETTING_PKT;
end else if (rollback_data && current_endp == ep_num) begin
ep_state_next[ep_num] <= READY_FOR_PKT;
end else begin
ep_state_next[ep_num] <= PUTTING_PKT;
end
end
GETTING_PKT : begin
if (ep_get_addr[ep_num][5:0] >= (ep_put_addr[ep_num][5:0] - 6'H2)) begin
ep_state_next[ep_num] <= READY_FOR_PKT;
end else begin
ep_state_next[ep_num] <= GETTING_PKT;
end
end
STALL : begin
if (setup_token_received && rx_endp == ep_num) begin
ep_state_next[ep_num] <= READY_FOR_PKT;
end else begin
ep_state_next[ep_num] <= STALL;
end
end
default begin
ep_state_next[ep_num] <= READY_FOR_PKT;
end
endcase
end
// Determine the next get_address (init, inc, maintain)
if (ep_state_next[ep_num][1:0] == READY_FOR_PKT) begin
ep_get_addr_next[ep_num][5:0] <= 0;
end else if (ep_state_next[ep_num][1:0] == GETTING_PKT && out_ep_data_get[ep_num]) begin
ep_get_addr_next[ep_num][5:0] <= ep_get_addr[ep_num][5:0] + 6'H1;
end else begin
ep_get_addr_next[ep_num][5:0] <= ep_get_addr[ep_num][5:0];
end
end // end of the always @*
// Advance the state to the next one.
always @(posedge clk) begin
if (reset || reset_ep[ep_num]) begin
ep_state[ep_num] <= READY_FOR_PKT;
end else begin
ep_state[ep_num] <= ep_state_next[ep_num];
end
ep_get_addr[ep_num][5:0] <= ep_get_addr_next[ep_num][5:0];
end
assign out_ep_data_avail[ep_num] =
(ep_get_addr[ep_num][5:0] < (ep_put_addr[ep_num][5:0] - 6'H2)) &&
(ep_state[ep_num][1:0] == GETTING_PKT);
end
endgenerate
integer i;
always @(posedge clk) begin
if (reset) begin
out_ep_setup <= 0;
end else begin
if (setup_token_received) begin
out_ep_setup[rx_endp] <= 1;
end else if (out_token_received) begin
out_ep_setup[rx_endp] <= 0;
end
end
for (i = 0; i < NUM_OUT_EPS; i = i + 1) begin
if (reset_ep[i]) begin
out_ep_setup[i] <= 0;
end
end
end
always @(posedge clk) out_ep_data <= out_data_buffer[buffer_get_addr][7:0];
// use the bus grant line to determine the out_ep_num (where the data is)
integer ep_num_decoder;
always @* begin
out_ep_num <= 0;
for (ep_num_decoder = 0; ep_num_decoder < NUM_OUT_EPS; ep_num_decoder = ep_num_decoder + 1) begin
if (out_ep_grant[ep_num_decoder]) begin
out_ep_num <= ep_num_decoder;
end
end
end
////////////////////////////////////////////////////////////////////////////////
// out transfer state machine
////////////////////////////////////////////////////////////////////////////////
always @* begin
out_ep_acked <= 0;
out_xfr_start <= 0;
out_xfr_state_next <= out_xfr_state;
tx_pkt_start <= 0;
tx_pid <= 0;
new_pkt_end <= 0;
rollback_data <= 0;
case (out_xfr_state)
IDLE : begin
if (out_token_received || setup_token_received) begin
out_xfr_state_next <= RCVD_OUT;
out_xfr_start <= 1;
end else begin
out_xfr_state_next <= IDLE;
end
end
RCVD_OUT : begin
if (rx_pkt_start) begin
out_xfr_state_next <= RCVD_DATA_START;
end else begin
out_xfr_state_next <= RCVD_OUT;
end
end
RCVD_DATA_START : begin
if (bad_data_toggle) begin
out_xfr_state_next <= IDLE;
rollback_data <= 1;
tx_pkt_start <= 1;
tx_pid <= 4'b0010; // ACK
end else if (invalid_packet_received || non_data_packet_received) begin
out_xfr_state_next <= IDLE;
rollback_data <= 1;
end else if (data_packet_received) begin
out_xfr_state_next <= RCVD_DATA_END;
end else begin
out_xfr_state_next <= RCVD_DATA_START;
end
end
RCVD_DATA_END : begin
out_xfr_state_next <= IDLE;
tx_pkt_start <= 1;
if (ep_state[current_endp] == STALL) begin
tx_pid <= 4'b1110; // STALL
end else if (nak_out_transfer) begin
tx_pid <= 4'b1010; // NAK
rollback_data <= 1;
end else begin
tx_pid <= 4'b0010; // ACK
new_pkt_end <= 1;
out_ep_acked[current_endp] <= 1;
//end else begin
// tx_pid <= 4'b0010; // ACK
// rollback_data <= 1;
end
end
default begin
out_xfr_state_next <= IDLE;
end
endcase
end
wire current_ep_busy =
(ep_state[current_endp] == GETTING_PKT) ||
(ep_state[current_endp] == READY_FOR_PKT);
integer j;
always @(posedge clk) begin
if (reset) begin
out_xfr_state <= IDLE;
end else begin
out_xfr_state <= out_xfr_state_next;
if (out_xfr_start) begin
current_endp <= rx_endp;
//last_data_toggle <= setup_token_received ? 0 : data_toggle[rx_endp];
end
if (new_pkt_end) begin
data_toggle[current_endp] <= !data_toggle[current_endp];
end
if (setup_token_received) begin
data_toggle[rx_endp] <= 0;
end
case (out_xfr_state)
IDLE : begin
end
RCVD_OUT : begin
if (current_ep_busy) begin
nak_out_transfer <= 1;
end else begin
nak_out_transfer <= 0;
ep_put_addr[current_endp][5:0] <= 0;
end
end
RCVD_DATA_START : begin
if (!nak_out_transfer && rx_data_put && !ep_put_addr[current_endp][5]) begin
out_data_buffer[buffer_put_addr][7:0] <= rx_data;
end
if (!nak_out_transfer && rx_data_put) begin
ep_put_addr[current_endp][5:0] <= ep_put_addr[current_endp][5:0] + 6'H1;
end
end
RCVD_DATA_END : begin
end
endcase
end
for (j = 0; j < NUM_OUT_EPS; j = j + 1) begin
if (reset || reset_ep[j]) begin
data_toggle[j] <= 0;
ep_put_addr[j][5:0] <= 0;
end
end
end
endmodule