blob: c9ba07a8588708f12a22b86ab3ba2ac74a0af912 [file] [log] [blame]
module usb_fs_tx (
// A 48MHz clock is required to receive USB data at 12MHz
// it's simpler to juse use 48MHz everywhere
input clk_48mhz,
input reset,
// bit strobe from rx to align with senders clock
input bit_strobe,
// output enable to take ownership of bus and data out
output reg oe = 0,
output reg dp = 0,
output reg dn = 0,
// pulse to initiate new packet transmission
input pkt_start,
output pkt_end,
// pid to send
input [3:0] pid,
// tx logic pulls data until there is nothing available
input tx_data_avail,
output reg tx_data_get = 0,
input [7:0] tx_data
);
wire clk = clk_48mhz;
// save packet parameters at pkt_start
reg [3:0] pidq = 0;
always @(posedge clk) begin
if (pkt_start) begin
pidq <= pid;
end
end
reg [7:0] data_shift_reg = 0;
reg [7:0] oe_shift_reg = 0;
reg [7:0] se0_shift_reg = 0;
wire serial_tx_data = data_shift_reg[0];
wire serial_tx_oe = oe_shift_reg[0];
wire serial_tx_se0 = se0_shift_reg[0];
// serialize sync, pid, data payload, and crc16
reg byte_strobe = 0;
reg [2:0] bit_count = 0;
reg [4:0] bit_history_q = 0;
wire [5:0] bit_history = {serial_tx_data, bit_history_q};
wire bitstuff = bit_history == 6'b111111;
reg bitstuff_q = 0;
reg bitstuff_qq = 0;
reg bitstuff_qqq = 0;
reg bitstuff_qqqq = 0;
always @(posedge clk) begin
bitstuff_q <= bitstuff;
bitstuff_qq <= bitstuff_q;
bitstuff_qqq <= bitstuff_qq;
bitstuff_qqqq <= bitstuff_qqq;
end
assign pkt_end = bit_strobe && se0_shift_reg[1:0] == 2'b01;
reg data_payload = 0;
reg [31:0] pkt_state = 0;
localparam IDLE = 0;
localparam SYNC = 1;
localparam PID = 2;
localparam DATA_OR_CRC16_0 = 3;
localparam CRC16_1 = 4;
localparam EOP = 5;
reg [15:0] crc16 = 0;
always @(posedge clk) begin
case (pkt_state)
IDLE : begin
if (pkt_start) begin
pkt_state <= SYNC;
end
end
SYNC : begin
if (byte_strobe) begin
pkt_state <= PID;
data_shift_reg <= 8'b10000000;
oe_shift_reg <= 8'b11111111;
se0_shift_reg <= 8'b00000000;
end
end
PID : begin
if (byte_strobe) begin
if (pidq[1:0] == 2'b11) begin
pkt_state <= DATA_OR_CRC16_0;
end else begin
pkt_state <= EOP;
end
data_shift_reg <= {~pidq, pidq};
oe_shift_reg <= 8'b11111111;
se0_shift_reg <= 8'b00000000;
end
end
DATA_OR_CRC16_0 : begin
if (byte_strobe) begin
if (tx_data_avail) begin
pkt_state <= DATA_OR_CRC16_0;
data_payload <= 1;
tx_data_get <= 1;
data_shift_reg <= tx_data;
oe_shift_reg <= 8'b11111111;
se0_shift_reg <= 8'b00000000;
end else begin
pkt_state <= CRC16_1;
data_payload <= 0;
tx_data_get <= 0;
data_shift_reg <= ~{crc16[8], crc16[9], crc16[10], crc16[11], crc16[12], crc16[13], crc16[14], crc16[15]};
oe_shift_reg <= 8'b11111111;
se0_shift_reg <= 8'b00000000;
end
end else begin
tx_data_get <= 0;
end
end
CRC16_1 : begin
if (byte_strobe) begin
pkt_state <= EOP;
data_shift_reg <= ~{crc16[0], crc16[1], crc16[2], crc16[3], crc16[4], crc16[5], crc16[6], crc16[7]};
oe_shift_reg <= 8'b11111111;
se0_shift_reg <= 8'b00000000;
end
end
EOP : begin
if (byte_strobe) begin
pkt_state <= IDLE;
oe_shift_reg <= 8'b00000111;
se0_shift_reg <= 8'b00000111;
end
end
endcase
if (bit_strobe && !bitstuff) begin
byte_strobe <= (bit_count == 3'b000);
end else begin
byte_strobe <= 0;
end
if (pkt_start) begin
bit_count <= 1;
bit_history_q <= 0;
end else if (bit_strobe) begin
// bitstuff
if (bitstuff /* && !serial_tx_se0*/) begin
bit_history_q <= bit_history[5:1];
data_shift_reg[0] <= 0;
// normal deserialize
end else begin
bit_count <= bit_count + 1;
data_shift_reg <= (data_shift_reg >> 1);
oe_shift_reg <= (oe_shift_reg >> 1);
se0_shift_reg <= (se0_shift_reg >> 1);
bit_history_q <= bit_history[5:1];
end
end
end
// calculate crc16
wire crc16_invert = serial_tx_data ^ crc16[15];
always @(posedge clk) begin
if (pkt_start) begin
crc16 <= 16'b1111111111111111;
end
if (bit_strobe && data_payload && !bitstuff_qqqq && !pkt_start) 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
reg [2:0] dp_eop = 0;
// nrzi and differential driving
always @(posedge clk) begin
if (pkt_start) begin
// J
dp <= 1;
dn <= 0;
dp_eop <= 3'b100;
end else if (bit_strobe) begin
oe <= serial_tx_oe;
if (serial_tx_se0) begin
dp <= dp_eop[0];
dn <= 0;
dp_eop <= dp_eop >> 1;
end else if (serial_tx_data) begin
// value should stay the same, do nothing
end else begin
dp <= !dp;
dn <= !dn;
end
end
end
endmodule