1
0
mirror of https://github.com/avakar/usbcorev.git synced 2024-10-22 02:17:39 +08:00

Initial commit.

The module works correctly during USB enumeration, no more tests were
performed at this point. No documentation or tests.
This commit is contained in:
Martin Vejnár 2013-05-10 19:51:51 +02:00
commit fb2e32dc58
7 changed files with 1019 additions and 0 deletions

13
crc16.py Normal file
View File

@ -0,0 +1,13 @@
import sys
while True:
crc = 0xffff
binary = raw_input('Enter sequence: ')
for ch in binary:
top = (crc & 0x8000) != 0
crc = (crc << 1) & 0xffff
if (ch == '1') != top:
crc = crc ^ 0x8005
print bin(crc)

13
crc5.py Normal file
View File

@ -0,0 +1,13 @@
import sys
while True:
crc = 0x1f
binary = raw_input('Enter sequence: ')
for ch in binary:
top = (crc & 0x10) != 0
crc = (crc << 1) & 0x1f
if (ch == '1') != top:
crc = crc ^ 5
print bin(crc)

339
usb.v Normal file
View File

@ -0,0 +1,339 @@
module usb(
input rst_n,
input clk_48,
input dp_in,
input dn_in,
input d0p_in,
input d0n_in,
output dp_out,
output dn_out,
output d_dir_out,
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),
.dp_in(dp_in),
.dn_in(dn_in),
.d0p_in(d0p_in),
.d0n_in(d0n_in),
.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),
.usb_dp(dp_out),
.usb_dn(dn_out),
.usb_tx_en(d_dir_out),
.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;
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;
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;
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)
state <= recv_pid[3] == data_toggle? st_data: st_send_ack;
else
state <= st_err;
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
transaction_active <= 1'b0;
if (handshake == hs_ack || handshake == 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)
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
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 (!d_dir_out && !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)
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 (!d_dir_out && !recv_packet)
state <= st_idle;
end
endcase
end
end
endmodule

237
usb_recv.v Normal file
View File

@ -0,0 +1,237 @@
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 dp_in,
input dn_in,
input d0p_in,
input d0n_in,
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 d_presync;
IBUFDS diff_buffer(.I(dp_in), .IB(dn_in), .O(d_presync));
wire d_prefilter, d0p, d0n;
sync d_sync(.clk(clk_48), .i(d_presync), .o(d_prefilter));
sync d0p_sync(.clk(clk_48), .i(d0p_in), .o(d0p));
sync d0n_sync(.clk(clk_48), .i(d0n_in), .o(d0n));
wire j;
multisample3 d_filter(
.clk(clk_48),
.in(d_prefilter),
.out(j));
wire se0;
multisample5 se0_filter(
.clk(clk_48),
.in(!d0p && !d0n),
.out(se0));
reg[2:0] short_idle_counter;
assign short_idle = short_idle_counter == 1'b0;
always @(posedge clk_48) begin
if (se0 || !j)
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

142
usb_tx.v Normal file
View File

@ -0,0 +1,142 @@
module usb_tx(
input rst_n,
input clk_48,
output usb_dp,
output usb_dn,
output usb_tx_en,
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 usb_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 = state == st_eop1 || state == st_eop2;
wire tx_data_empty = (tx_data[8:2] == 1'b0);
assign data_strobe = transmit && tx_data_empty && bit_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 (!usb_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 <= usb_tx_en? j: 1'b1;
end
assign usb_dp = se0? 1'b0: j;
assign usb_dn = se0? 1'b0: !j;
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

163
usb_utils.v Normal file
View File

@ -0,0 +1,163 @@
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

112
utils.v Normal file
View File

@ -0,0 +1,112 @@
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 sync(
input clk,
input i,
output o);
reg[2:0] s;
assign o = s[1];
always @(posedge clk) begin
s <= { s[1:0], i };
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