blob: 3ea598de16b268ed95566f64547bca6694c06b8a [file] [log] [blame]
module usb_fs_rx (
// A 48MHz clock is required to recover the clock from the incoming data.
input clk_48mhz,
input reset,
// USB data+ and data- lines.
input dp,
input dn,
// pulse on every bit transition.
output bit_strobe,
// Pulse on beginning of new packet.
output pkt_start,
// Pulse on end of current packet.
output pkt_end,
// Most recent packet decoded.
output [3:0] pid,
output reg [6:0] addr = 0,
output reg [3:0] endp = 0,
output reg [10:0] frame_num = 0,
// Pulse on valid data on rx_data.
output rx_data_put,
output [7:0] rx_data,
// Most recent packet passes PID and CRC checks
output valid_packet
);
wire clk = clk_48mhz;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////
//////// usb receive path
////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// double flop for metastability
/*
all asynchronous inputs into the RTL need to be double-flopped to protect
against metastable scenarios. if the RTL clock samples an asynchronous signal
at the same time the signal is transitioning the result is undefined. flopping
the signal twice ensures it will be either 1 or 0 and nothing in between.
*/
reg [3:0] dpair_q = 0;
always @(posedge clk) begin
dpair_q[3:0] <= {dpair_q[1:0], dp, dn};
end
////////////////////////////////////////////////////////////////////////////////
// line state recovery state machine
/*
the recieve path doesn't currently use a differential reciever. because of
this there is a chance that one of the differential pairs will appear to have
changed to the new state while the other is still in the old state. the
following state machine detects transitions and waits an extra sampling clock
before decoding the state on the differential pair. this transition period
will only ever last for one clock as long as there is no noise on the line.
if there is enough noise on the line then the data may be corrupted and the
packet will fail the data integrity checks.
*/
reg [2:0] line_state = 0;
localparam DT = 3'b100;
localparam DJ = 3'b010;
localparam DK = 3'b001;
localparam SE0 = 3'b000;
localparam SE1 = 3'b011;
wire [1:0] dpair = dpair_q[3:2];
always @(posedge clk) begin
case (line_state)
// if we are in a transition state, then we can sample the pair and
// move to the next corresponding line state
DT : begin
case (dpair)
2'b10 : line_state <= DJ;
2'b01 : line_state <= DK;
2'b00 : line_state <= SE0;
2'b11 : line_state <= SE1;
endcase
end
// if we are in a valid line state and the value of the pair changes,
// then we need to move to the transition state
DJ : if (dpair != 2'b10) line_state <= DT;
DK : if (dpair != 2'b01) line_state <= DT;
SE0 : if (dpair != 2'b00) line_state <= DT;
SE1 : if (dpair != 2'b11) line_state <= DT;
// if we are in an invalid state we should move to the transition state
default : line_state <= DT;
endcase
end
////////////////////////////////////////////////////////////////////////////////
// clock recovery
/*
the DT state from the line state recovery state machine is used to align to
transmit clock. the line state is sampled in the middle of the bit time.
example of signal relationships
-------------------------------
line_state DT DJ DJ DJ DT DK DK DK DK DK DK DT DJ DJ DJ
line_state_valid ________----____________----____________----________----____
bit_phase 0 0 1 2 3 0 1 2 3 0 1 2 0 1 2
*/
reg [1:0] bit_phase = 0;
wire line_state_valid = (bit_phase == 1);
assign bit_strobe = (bit_phase == 2);
always @(posedge clk) begin
// keep track of phase within each bit
if (line_state == DT) begin
bit_phase <= 0;
end else begin
bit_phase <= bit_phase + 1;
end
end
////////////////////////////////////////////////////////////////////////////////
// packet detection
/*
usb uses a sync to denote the beginning of a packet and two single-ended-0 to
denote the end of a packet. this state machine recognizes the beginning and
end of packets for subsequent layers to process.
*/
reg [5:0] line_history = 0;
reg packet_valid = 0;
reg next_packet_valid;
wire packet_start = next_packet_valid && !packet_valid;
wire packet_end = !next_packet_valid && packet_valid;
always @* begin
if (line_state_valid) begin
// check for packet start: KJKJKK
if (!packet_valid && line_history[5:0] == 6'b100101) begin
next_packet_valid <= 1;
end
// check for packet end: SE0 SE0
else if (packet_valid && line_history[3:0] == 4'b0000) begin
next_packet_valid <= 0;
end else begin
next_packet_valid <= packet_valid;
end
end else begin
next_packet_valid <= packet_valid;
end
end
always @(posedge clk) begin
if (reset) begin
line_history <= 6'b101010;
packet_valid <= 0;
end else begin
// keep a history of the last two states on the line
if (line_state_valid) begin
line_history[5:0] <= {line_history[3:0], line_state[1:0]};
end
end
packet_valid <= next_packet_valid;
end
////////////////////////////////////////////////////////////////////////////////
// NRZI decode
/*
in order to ensure there are enough bit transitions for a receiver to recover
the clock usb uses NRZI encoding.
https://en.wikipedia.org/wiki/Non-return-to-zero
*/
reg dvalid_raw;
reg din;
always @* begin
case (line_history[3:0])
4'b0101 : din <= 1;
4'b0110 : din <= 0;
4'b1001 : din <= 0;
4'b1010 : din <= 1;
default : din <= 0;
endcase
if (packet_valid && line_state_valid) begin
case (line_history[3:0])
4'b0101 : dvalid_raw <= 1;
4'b0110 : dvalid_raw <= 1;
4'b1001 : dvalid_raw <= 1;
4'b1010 : dvalid_raw <= 1;
default : dvalid_raw <= 0;
endcase
end else begin
dvalid_raw <= 0;
end
end
reg [5:0] bitstuff_history = 0;
always @(posedge clk) begin
if (reset || packet_end) begin
bitstuff_history <= 6'b000000;
end else begin
if (dvalid_raw) begin
bitstuff_history <= {bitstuff_history[4:0], din};
end
end
end
wire dvalid = dvalid_raw && !(bitstuff_history == 6'b111111);
////////////////////////////////////////////////////////////////////////////////
// save and check pid
/*
shift in the entire 8-bit pid with an additional 9th bit used as a sentinal.
*/
reg [8:0] full_pid = 0;
wire pid_valid = full_pid[4:1] == ~full_pid[8:5];
wire pid_complete = full_pid[0];
always @(posedge clk) begin
if (packet_start) begin
full_pid <= 9'b100000000;
end
if (dvalid && !pid_complete) begin
full_pid <= {din, full_pid[8:1]};
end
end
////////////////////////////////////////////////////////////////////////////////
// check crc5
reg [4:0] crc5 = 0;
wire crc5_valid = crc5 == 5'b01100;
wire crc5_invert = din ^ crc5[4];
always @(posedge clk) begin
if (packet_start) begin
crc5 <= 5'b11111;
end
if (dvalid && pid_complete) begin
crc5[4] <= crc5[3];
crc5[3] <= crc5[2];
crc5[2] <= crc5[1] ^ crc5_invert;
crc5[1] <= crc5[0];
crc5[0] <= crc5_invert;
end
end
////////////////////////////////////////////////////////////////////////////////
// check crc16
reg [15:0] crc16 = 0;
wire crc16_valid = crc16 == 16'b1000000000001101;
wire crc16_invert = din ^ crc16[15];
always @(posedge clk) begin
if (packet_start) begin
crc16 <= 16'b1111111111111111;
end
if (dvalid && pid_complete) begin
crc16[15] <= crc16[14] ^ crc16_invert;
crc16[14] <= crc16[13];
crc16[13] <= crc16[12];
crc16[12] <= crc16[11];
crc16[11] <= crc16[10];
crc16[10] <= crc16[9];
crc16[9] <= crc16[8];
crc16[8] <= crc16[7];
crc16[7] <= crc16[6];
crc16[6] <= crc16[5];
crc16[5] <= crc16[4];
crc16[4] <= crc16[3];
crc16[3] <= crc16[2];
crc16[2] <= crc16[1] ^ crc16_invert;
crc16[1] <= crc16[0];
crc16[0] <= crc16_invert;
end
end
////////////////////////////////////////////////////////////////////////////////
// output control signals
wire pkt_is_token = full_pid[2:1] == 2'b01;
wire pkt_is_data = full_pid[2:1] == 2'b11;
wire pkt_is_handshake = full_pid[2:1] == 2'b10;
// TODO: need to check for data packet babble
// TODO: do i need to check for bitstuff error?
assign valid_packet = pid_valid && (
(pkt_is_handshake) ||
(pkt_is_data && crc16_valid) ||
(pkt_is_token && crc5_valid)
);
reg [11:0] token_payload = 0;
wire token_payload_done = token_payload[0];
always @(posedge clk) begin
if (packet_start) begin
token_payload <= 12'b100000000000;
end
if (dvalid && pid_complete && pkt_is_token && !token_payload_done) begin
token_payload <= {din, token_payload[11:1]};
end
end
always @(posedge clk) begin
if (token_payload_done && pkt_is_token) begin
addr <= token_payload[7:1];
endp <= token_payload[11:8];
frame_num <= token_payload[11:1];
end
end
assign pkt_start = packet_start;
assign pkt_end = packet_end;
assign pid = full_pid[4:1];
//assign addr = token_payload[7:1];
//assign endp = token_payload[11:8];
//assign frame_num = token_payload[11:1];
////////////////////////////////////////////////////////////////////////////////
// deserialize and output data
//assign rx_data_put = dvalid && pid_complete && pkt_is_data;
reg [8:0] rx_data_buffer = 0;
wire rx_data_buffer_full = rx_data_buffer[0];
assign rx_data_put = rx_data_buffer_full;
assign rx_data = rx_data_buffer[8:1];
always @(posedge clk) begin
if (packet_start || rx_data_buffer_full) begin
rx_data_buffer <= 9'b100000000;
end
if (dvalid && pid_complete && pkt_is_data) begin
rx_data_buffer <= {din, rx_data_buffer[8:1]};
end
end
endmodule // usb_fs_rx