blob: 738d3588f0d69910b8f613f26af4d6efbce3bfc9 [file] [log] [blame]
/*
Taken from: https://github.com/avakar/usbcorev
No-notice MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
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
module usb_ep(
input clk,
input direction_in,
input setup,
input success,
input[6:0] cnt,
output reg toggle,
output bank_usb,
output reg[1:0] handshake,
output bank_in,
output bank_out,
output in_data_valid,
input ctrl_dir_in,
output reg[15:0] ctrl_rd_data,
input[15:0] ctrl_wr_data,
input[1:0] ctrl_wr_en
);
localparam
hs_ack = 2'b00,
hs_none = 2'b01,
hs_nak = 2'b10,
hs_stall = 2'b11;
reg ep_setup;
reg ep_out_full;
reg ep_out_empty;
reg ep_in_full;
reg ep_out_stall;
reg ep_in_stall;
reg ep_out_toggle;
reg ep_in_toggle;
reg[6:0] ep_in_cnt;
reg[6:0] ep_out_cnt;
assign in_data_valid = (cnt != ep_in_cnt);
assign bank_usb = 1'b0;
assign bank_in = 1'b0;
assign bank_out = 1'b0;
always @(*) begin
if (!direction_in && setup)
toggle = 1'b0;
else if (ep_setup)
toggle = 1'b1;
else if (direction_in)
toggle = ep_in_toggle;
else
toggle = ep_out_toggle;
end
always @(*) begin
if (direction_in) begin
if (!ep_in_stall && !ep_setup && ep_in_full) begin
handshake = hs_ack;
end else if (!ep_setup && ep_in_stall) begin
handshake = hs_stall;
end else begin
handshake = hs_nak;
end
end else begin
if (setup || (!ep_out_stall && !ep_setup && ep_out_full)) begin
handshake = hs_ack;
end else if (!ep_setup && ep_out_stall) begin
handshake = hs_stall;
end else begin
handshake = hs_nak;
end
end
end
always @(*) begin
if (ctrl_dir_in) begin
ctrl_rd_data[15:8] = ep_in_cnt;
ctrl_rd_data[7:0] = { ep_in_toggle, ep_in_stall, 1'b0, !ep_in_full, ep_in_full };
end else begin
ctrl_rd_data[15:8] = ep_out_cnt;
ctrl_rd_data[7:0] = { ep_out_toggle, ep_out_stall, ep_setup, ep_out_empty, ep_out_full };
end
end
wire flush = ctrl_wr_data[5] || ctrl_wr_data[4] || ctrl_wr_data[3];
always @(posedge clk) begin
if (success) begin
if (direction_in) begin
ep_in_full <= 1'b0;
ep_in_toggle <= !ep_in_toggle;
end else begin
if (setup)
ep_setup <= 1'b1;
ep_out_toggle <= !ep_out_toggle;
ep_out_empty <= 1'b0;
ep_out_full <= 1'b0;
ep_out_cnt <= cnt;
end
end
if (ctrl_wr_en[1] && ctrl_dir_in) begin
ep_in_cnt <= ctrl_wr_data[14:8];
end
if (ctrl_wr_en[0] && ctrl_dir_in) begin
if (ctrl_wr_data[5]) begin
ep_in_toggle <= 1'b0;
ep_in_stall <= 1'b0;
end
if (ctrl_wr_data[4]) begin
ep_in_toggle <= 1'b1;
ep_in_stall <= 1'b0;
end
if (ctrl_wr_data[3])
ep_in_stall <= 1'b1;
if (flush)
ep_in_full <= 1'b0;
if (ctrl_wr_data[0])
ep_in_full <= 1'b1;
end
if (ctrl_wr_en[0] && !ctrl_dir_in) begin
if (ctrl_wr_data[5]) begin
ep_out_toggle <= 1'b0;
ep_out_stall <= 1'b0;
end
if (ctrl_wr_data[4]) begin
ep_out_toggle <= 1'b1;
ep_out_stall <= 1'b0;
end
if (ctrl_wr_data[3])
ep_out_stall <= 1'b1;
if (flush) begin
ep_out_full <= 1'b0;
ep_out_empty <= 1'b1;
end
if (ctrl_wr_data[2])
ep_setup <= 1'b0;
if (ctrl_wr_data[1])
ep_out_empty <= 1'b1;
if (ctrl_wr_data[0])
ep_out_full <= 1'b1;
end
end
endmodule
module usb_ep_banked(
input clk,
input direction_in,
input setup,
input success,
input[6:0] cnt,
output reg toggle,
output bank_usb,
output reg[1:0] handshake,
output bank_in,
output bank_out,
output in_data_valid,
input ctrl_dir_in,
output reg[15:0] ctrl_rd_data,
input[15:0] ctrl_wr_data,
input[1:0] ctrl_wr_en
);
localparam
hs_ack = 2'b00,
hs_none = 2'b01,
hs_nak = 2'b10,
hs_stall = 2'b11;
reg ep_setup;
reg ep_out_full;
reg ep_out_empty;
reg ep_in_empty;
reg ep_out_stall;
reg ep_in_stall;
reg ep_out_toggle;
reg ep_in_toggle;
reg ep_in_bank;
reg[6:0] ep_in_cnt_0;
reg[6:0] ep_in_cnt_1;
reg[6:0] ep_out_cnt;
assign in_data_valid = (cnt != (ep_in_toggle? ep_in_cnt_1: ep_in_cnt_0));
assign bank_usb = direction_in? ep_in_toggle: 1'b0;
assign bank_in = ep_in_bank;
assign bank_out = 1'b0;
always @(*) begin
if (!direction_in && setup)
toggle = 1'b0;
else if (ep_setup)
toggle = 1'b1;
else if (direction_in)
toggle = ep_in_toggle;
else
toggle = ep_out_toggle;
end
always @(*) begin
if (direction_in) begin
if (!ep_in_stall && !ep_setup && !ep_in_empty) begin
handshake = hs_ack;
end else if (!ep_setup && ep_in_stall) begin
handshake = hs_stall;
end else begin
handshake = hs_nak;
end
end else begin
if (setup || (!ep_out_stall && !ep_setup && ep_out_full)) begin
handshake = hs_ack;
end else if (!ep_setup && ep_out_stall) begin
handshake = hs_stall;
end else begin
handshake = hs_nak;
end
end
end
always @(*) begin
if (ctrl_dir_in) begin
ctrl_rd_data[15:8] = ep_in_bank? ep_in_cnt_1: ep_in_cnt_0;
ctrl_rd_data[7:0] = { ep_in_bank, ep_in_toggle, ep_in_stall, 1'b0, ep_in_empty, !ep_in_empty && ep_in_toggle == ep_in_bank };
end else begin
ctrl_rd_data[15:8] = ep_out_cnt;
ctrl_rd_data[7:0] = { ep_out_toggle, ep_out_stall, ep_setup, ep_out_empty, ep_out_full };
end
end
wire flush = ctrl_wr_data[5] || ctrl_wr_data[4] || ctrl_wr_data[3];
always @(posedge clk) begin
if (success) begin
if (direction_in) begin
if (ep_in_toggle != ep_in_bank)
ep_in_empty <= 1'b1;
ep_in_toggle = !ep_in_toggle;
end else begin
if (setup)
ep_setup <= 1'b1;
ep_out_toggle = !ep_out_toggle;
ep_out_empty <= 1'b0;
ep_out_cnt <= cnt;
end
end
if (ctrl_wr_en[1] && ctrl_dir_in) begin
if (ep_in_bank)
ep_in_cnt_1 <= ctrl_wr_data[14:8];
else
ep_in_cnt_0 <= ctrl_wr_data[14:8];
end
if (ctrl_wr_en[0] && ctrl_dir_in) begin
if (ctrl_wr_data[5]) begin
ep_in_toggle = 1'b0;
ep_in_stall <= 1'b0;
ep_in_bank <= 1'b0;
end
if (ctrl_wr_data[4]) begin
ep_in_toggle = 1'b1;
ep_in_stall <= 1'b0;
ep_in_bank <= 1'b1;
end
if (ctrl_wr_data[3]) begin
ep_in_stall <= 1'b1;
ep_in_bank <= ep_in_toggle;
end
if (flush) begin
ep_in_empty <= 1'b1;
end
if (ctrl_wr_data[0]) begin
ep_in_empty <= 1'b0;
ep_in_bank <= !ep_in_bank;
end
end
if (ctrl_wr_en[0] && !ctrl_dir_in) begin
if (ctrl_wr_data[5]) begin
ep_out_toggle = 1'b0;
ep_out_stall <= 1'b0;
end
if (ctrl_wr_data[4]) begin
ep_out_toggle = 1'b1;
ep_out_stall <= 1'b0;
end
if (ctrl_wr_data[3])
ep_out_stall <= 1'b1;
if (flush) begin
ep_out_full <= 1'b0;
ep_out_empty <= 1'b1;
end
if (ctrl_wr_data[2])
ep_setup <= 1'b0;
if (ctrl_wr_data[1]) begin
ep_out_empty <= 1'b1;
ep_out_full <= 1'b0;
end
if (ctrl_wr_data[0])
ep_out_full <= 1'b1;
end
end
endmodule
module usb_recv_sm(
input rst_n,
input clk,
input strobe,
input din,
input sync,
input se0,
output reg[3:0] xpid,
output reg[7:0] xdata,
output xpacket,
output reg xdatastrobe,
output reg xcrc5_ok,
output reg xcrc16_ok
);
reg clear_shift;
reg[7:0] shift_reg;
reg[8:0] next_shift;
always @(*) begin
if (clear_shift)
next_shift = { 7'b1, din };
else
next_shift = { shift_reg[7:0], din };
end
always @(posedge clk) begin
if (strobe) begin
shift_reg <= next_shift[7:0];
end
end
localparam
st_idle = 2'b00,
st_done = 2'b10,
st_pid = 2'b01,
st_data = 2'b11;
reg[1:0] state;
wire crc5_valid;
usb_crc5 crc5(
.rst_n(rst_n && xpacket),
.clk(clk),
.clken(strobe),
.d(din),
.valid(crc5_valid)
);
wire crc16_valid;
usb_crc16 crc16(
.rst_n(rst_n && xpacket),
.clk(clk),
.clken(strobe),
.d(din),
.dump(1'b0),
.out(),
.valid(crc16_valid)
);
assign xpacket = (state == st_data);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= st_idle;
clear_shift <= 1'bx;
xpid <= 1'sbx;
xdata <= 1'sbx;
xdatastrobe <= 1'b0;
xcrc5_ok <= 1'b0;
xcrc16_ok <= 1'b0;
end else if (strobe) begin
clear_shift <= 1'bx;
xdatastrobe <= 1'b0;
case (state)
st_idle: begin
if (sync && !se0) begin
state <= st_pid;
clear_shift <= 1'b1;
end
end
st_pid: begin
if (se0) begin
state <= st_idle;
end else begin
if (next_shift[8]) begin
if (next_shift[7:4] == ~next_shift[3:0]) begin
clear_shift <= 1'b1;
xpid <= { next_shift[4], next_shift[5], next_shift[6], next_shift[7] };
state <= st_data;
xcrc5_ok <= 1'b0;
xcrc16_ok <= 1'b0;
end else begin
state <= st_done;
end
end else begin
clear_shift <= 1'b0;
end
end
end
st_data: begin
if (se0) begin
state <= st_idle;
end else begin
clear_shift <= 1'b0;
if (next_shift[8]) begin
clear_shift <= 1'b1;
xdata <= {
next_shift[0], next_shift[1], next_shift[2], next_shift[3],
next_shift[4], next_shift[5], next_shift[6], next_shift[7] };
xdatastrobe <= 1'b1;
xcrc5_ok <= crc5_valid;
xcrc16_ok <= crc16_valid;
end
end
end
default: begin
if (se0)
state <= st_idle;
end
endcase
end
end
endmodule
module usb_recv(
input rst_n,
input clk_48,
input rx_j,
input rx_se0,
output short_idle,
output usb_rst,
output[3:0] xpid,
output[7:0] xdata,
output xpacket,
output xdatastrobe,
output xcrc5_ok,
output xcrc16_ok
);
wire j;
multisample3 d_filter(
.clk(clk_48),
.in(rx_j),
.out(j));
wire se0;
multisample5 se0_filter(
.clk(clk_48),
.in(rx_se0),
.out(se0));
reg[2:0] short_idle_counter;
assign short_idle = short_idle_counter == 1'b0;
always @(posedge clk_48) begin
if (se0 || !j || xpacket)
short_idle_counter <= 3'b111;
else if (short_idle_counter != 1'b0)
short_idle_counter <= short_idle_counter - 1'b1;
end
wire nrzi_strobe;
usb_clk_recovery clk_rcvr(
.rst_n(rst_n),
.clk(clk_48),
.i(j),
.strobe(nrzi_strobe)
);
wire d;
nrzi_decode nrzi_decoder(
.clk(clk_48),
.clken(nrzi_strobe),
.i(j),
.o(d));
wire strobe;
usb_bit_destuff destuffer(
.rst_n(rst_n),
.clk(clk_48),
.clken(nrzi_strobe),
.d(d),
.strobe(strobe)
);
usb_reset_detect reset_detect(
.rst_n(rst_n),
.clk(clk_48),
.se0(se0),
.usb_rst(usb_rst));
wire sync_seq;
usb_sync_detect sync_detect(
.rst_n(rst_n),
.clk(clk_48),
.clken(nrzi_strobe),
.j(j),
.se0(se0),
.sync(sync_seq));
wire strobed_xdatastrobe;
assign xdatastrobe = strobed_xdatastrobe && strobe;
usb_recv_sm sm(
.rst_n(rst_n),
.clk(clk_48),
.strobe(strobe),
.din(d),
.sync(sync_seq),
.se0(se0),
.xpid(xpid),
.xdata(xdata),
.xpacket(xpacket),
.xdatastrobe(strobed_xdatastrobe),
.xcrc5_ok(xcrc5_ok),
.xcrc16_ok(xcrc16_ok)
);
endmodule
module usb_tx(
input rst_n,
input clk_48,
output tx_en,
output tx_j,
output tx_se0,
input transmit,
input[7:0] data,
input update_crc16,
input send_crc16,
output data_strobe
);
reg[1:0] tx_clock;
wire bit_strobe = tx_clock == 2'b00;
always @(posedge clk_48 or negedge rst_n) begin
if (!rst_n) begin
tx_clock <= 2'b00;
end else begin
tx_clock <= tx_clock + 1'b1;
end
end
wire bit_stuff;
wire tx_strobe = bit_strobe && !bit_stuff;
reg[2:0] state;
localparam
st_idle = 3'b000,
st_sync = 3'b101,
st_run = 3'b001,
st_eop1 = 3'b010,
st_eop2 = 3'b011,
st_eop3 = 3'b100,
st_crc1 = 3'b110,
st_crc2 = 3'b111;
assign tx_en = (state != st_idle);
reg[8:0] tx_data;
reg crc_enabled;
wire dump_crc = state == st_crc1 || state == st_crc2;
wire crc_out;
wire d = dump_crc? !crc_out: tx_data[0];
wire se0 = !bit_stuff && (state == st_eop1 || state == st_eop2);
wire tx_data_empty = (tx_data[8:2] == 1'b0);
assign data_strobe = transmit && tx_data_empty && tx_strobe;
always @(posedge clk_48 or negedge rst_n) begin
if (!rst_n) begin
state <= st_idle;
end else if (tx_strobe) begin
case (state)
st_idle: begin
if (transmit)
state <= st_run;
end
st_sync: begin
if (tx_data_empty)
state <= st_run;
end
st_run: begin
if (tx_data_empty && !transmit) begin
if (send_crc16)
state <= st_crc1;
else
state <= st_eop1;
end
end
st_crc1: begin
if (tx_data_empty)
state <= st_crc2;
end
st_crc2: begin
if (tx_data_empty)
state <= st_eop1;
end
st_eop1: begin
state <= st_eop2;
end
st_eop2: begin
state <= st_eop3;
end
st_eop3: begin
state <= st_idle;
end
endcase
end
end
always @(posedge clk_48) begin
if (tx_strobe) begin
if (!tx_en) begin
tx_data <= 9'b110000000; // starting with J, go through KJKJKJKK
crc_enabled <= 1'b0;
end else if (tx_data_empty) begin
tx_data <= { 1'b1, data };
crc_enabled <= update_crc16;
end else begin
tx_data <= { 1'b0, tx_data[8:1] };
end
end
end
reg[2:0] bit_stuff_counter;
assign bit_stuff = bit_stuff_counter == 3'd6;
always @(posedge clk_48 or negedge rst_n) begin
if (!rst_n) begin
bit_stuff_counter <= 1'b0;
end else if (bit_strobe) begin
if (state == st_idle || !d || bit_stuff || se0)
bit_stuff_counter <= 1'b0;
else
bit_stuff_counter <= bit_stuff_counter + 1'b1;
end
end
reg last_j;
wire j = state == st_idle || state == st_eop3? 1'b1: ((bit_stuff || !d)? !last_j: last_j);
always @(posedge clk_48) begin
if (bit_strobe)
last_j <= tx_en? j: 1'b1;
end
assign tx_j = j;
assign tx_se0 = se0;
usb_crc16 tx_crc(
.rst_n(rst_n && state != st_idle),
.clk(clk_48),
.clken(tx_strobe && (dump_crc || crc_enabled)),
.d(d),
.dump(dump_crc),
.out(crc_out),
.valid()
);
endmodule
module usb_crc5(
input rst_n,
input clk,
input clken,
input d,
output valid
);
reg[4:0] r;
reg[4:0] next;
wire top = r[4];
assign valid = (next == 5'b01100);
always @(*) begin
if (top == d)
next = { r[3], r[2], r[1], r[0], 1'b0 };
else
next = { r[3], r[2], !r[1], r[0], 1'b1 };
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
r <= 5'b11111;
end else if (clken) begin
r <= next;
end
end
endmodule
//---------------------------------------------------------------------
module usb_crc16(
input rst_n,
input clk,
input clken,
input d,
input dump,
output out,
output valid
);
reg[15:0] r;
reg[15:0] next;
assign out = r[15];
assign valid = (next == 16'b1000000000001101);
always @(*) begin
if (dump || out == d)
next = { r[14:0], 1'b0 };
else
next = { !r[14], r[13:2], !r[1], r[0], 1'b1 };
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
r <= 16'hffff;
end else if (clken) begin
r <= next;
end
end
endmodule
//---------------------------------------------------------------------
module usb_clk_recovery(
input rst_n,
input clk,
input i,
output strobe
);
reg[1:0] cntr;
reg prev_i;
assign strobe = cntr == 1'b0;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cntr <= 1'b0;
prev_i <= 1'b0;
end else begin
if (i == prev_i) begin
cntr <= cntr - 1'b1;
end else begin
cntr <= 1'b1;
end
prev_i <= i;
end
end
endmodule
//---------------------------------------------------------------------
module usb_bit_destuff(
input rst_n,
input clk,
input clken,
input d,
output strobe);
reg[6:0] data;
assign strobe = clken && (data != 7'b0111111);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data <= 7'b0000000;
end else if (clken) begin
data <= { data[5:0], d };
end
end
endmodule
//---------------------------------------------------------------------
module usb_sync_detect(
input rst_n,
input clk,
input clken,
input j,
input se0,
output sync);
// 3KJ's followed by 2K's
reg[6:0] data;
assign sync = (data == 7'b0101010 && !j && !se0);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data <= 1'd0;
end else if (clken) begin
data <= { data[5:0], j || se0 };
end
end
endmodule
//---------------------------------------------------------------------
module usb_reset_detect(
input rst_n,
input clk,
input se0,
output usb_rst);
reg[18:0] cntr;
assign usb_rst = cntr == 1'b0;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cntr <= 1'b0;
end else begin
if (se0) begin
if (!usb_rst)
cntr <= cntr - 1'b1;
end else begin
cntr <= 19'd480000;
end
end
end
endmodule
module multisample3(
input clk,
input in,
output reg out
);
reg[2:0] r;
always @(r) begin
case (r)
3'b000: out = 1'b0;
3'b001: out = 1'b0;
3'b010: out = 1'b0;
3'b011: out = 1'b1;
3'b100: out = 1'b0;
3'b101: out = 1'b1;
3'b110: out = 1'b1;
3'b111: out = 1'b1;
endcase
end
always @(posedge clk) begin
r <= { r[1:0], in };
end
endmodule
//---------------------------------------------------------------------
module multisample5(
input clk,
input in,
output reg out
);
reg[4:0] r;
always @(r) begin
case (r)
5'b00000: out = 1'b0;
5'b00001: out = 1'b0;
5'b00010: out = 1'b0;
5'b00011: out = 1'b0;
5'b00100: out = 1'b0;
5'b00101: out = 1'b0;
5'b00110: out = 1'b0;
5'b00111: out = 1'b1;
5'b01000: out = 1'b0;
5'b01001: out = 1'b0;
5'b01010: out = 1'b0;
5'b01011: out = 1'b1;
5'b01100: out = 1'b0;
5'b01101: out = 1'b1;
5'b01110: out = 1'b1;
5'b01111: out = 1'b1;
5'b10000: out = 1'b0;
5'b10001: out = 1'b0;
5'b10010: out = 1'b0;
5'b10011: out = 1'b1;
5'b10100: out = 1'b0;
5'b10101: out = 1'b1;
5'b10110: out = 1'b1;
5'b10111: out = 1'b1;
5'b11000: out = 1'b0;
5'b11001: out = 1'b1;
5'b11010: out = 1'b1;
5'b11011: out = 1'b1;
5'b11100: out = 1'b1;
5'b11101: out = 1'b1;
5'b11110: out = 1'b1;
5'b11111: out = 1'b1;
endcase
end
always @(posedge clk) begin
r <= { r[3:0], in };
end
endmodule
//---------------------------------------------------------------------
module nrzi_decode(
input clk,
input clken,
input i,
output o
);
reg prev_i;
assign o = (prev_i == i);
always @(posedge clk) begin
if (clken) begin
prev_i <= i;
end
end
endmodule