// 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 |