USB2.0 full-speed的CRC5理解
大家好,这里是菜菜汤姆哥!
在学习usb时候,无法理解bit-level的crc5,经过查阅资料(好多良莠不齐确实难以理解)并实现了crc5计算
说明
如图所示在usb控制传输中,Host(一般是电脑PC)发送SYNC、PID、Frame、CRC5、EOP
Host发送SYNC、PID、ADDR、EP、CRC5、EOP
可以看到两个CRC5,但是要怎么计算呢?
计算方法
-
图片放大可以看到11位的Frame号是897,二进制表示就是01110000001
-
由于usb协议低位在前,经过倒序,01110000001变成10000001110,与上上图一致
-
11位的原始值src=10000001110要变成16位,先填充5个0,变成src_padding=1000000111000000
-
16位异或序列是默认的:sequence_xor=1111100000000000
-
对二者进行异或src_xor = src_padding ^ sequence_xor; 得到如下图的0111100111000000
-
对0111100111000000进行模二除法,除以100101,这个100101是usb固定的二项式,求它的余数:这里我用在在线工具计算:https://www.23bei.com/tool/744.html
可以看到求出的结果是11011,因为只取5位 -
对11011取反,得到00100
-
对00100倒序,得到00100,最终发现00100与CRC5=0x4一致
代码
推荐一个在线仿真工具:https://hdlbits.01xz.net/wiki/Iverilog
verilog代码,源码如下
// 在线仿真工具:iverilog
// https://hdlbits.01xz.net/wiki/Iverilog
module top_module();
reg clk = 0;
reg [10:0]src = 11'b10010000101; //usb的原始数据,即crc5的前11位
wire [4:0]crc;
reg rst_n = 0;
reg start = 0;
wire [15:0] remainder_n;
wire cal_done;
crc5_d11 IP (
.clk(clk),
.rst_n(rst_n),
.src(src),
.start(start),
.crc(crc),
.cal_done(cal_done)
);
always #1 clk = ~clk; // Create clock with period=10
initial `probe_start; // Start the timing diagram
initial begin
#4 rst_n <= 1;
#4 src <= 11'b11010000101;
#4 start <= 1;
#30 $finish;// Quit the simulation
end
endmodule
module crc5_d11 (
input wire clk,
input wire rst_n,
input wire [10:0]src,
input wire start,
output wire [4:0]crc,
output wire cal_done//除法完成标志
);
wire [4:0] zero_padding = 5'b0_0000;
wire [15:0] src_padding = {src, zero_padding};
wire [15:0] sequence_xor = 16'b1111_1000_0000_0000;
wire [15:0] src_xor = src_padding ^ sequence_xor;
wire [5:0] div_100101 = 6'b10_0101;
reg [5:0] remainder;//余数
reg [15:0] dividend_tmp;//被除数的中间结果
reg [3:0] div_cnt;//计数器
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
div_cnt <= 4'd0;//计数器
remainder <= 6'd0;//余数
dividend_tmp <= 16'd0;
end
else begin
if (start) begin//开始
if (div_cnt < 4'd12) begin
div_cnt <= div_cnt + 1;
end
else begin
div_cnt <= div_cnt;
end
if (div_cnt == 4'd0) begin
if (remainder[5]) begin
remainder <= div_100101 ^ remainder;
end
else begin
remainder <= remainder;
end
dividend_tmp <= dividend_tmp;
end
else if (div_cnt < 4'd11) begin
if (remainder[4]) begin
remainder <= div_100101 ^ {remainder[4:0], dividend_tmp[9]};
end
else begin
remainder <= {remainder[4:0], dividend_tmp[9]};
end
dividend_tmp <= dividend_tmp << 1;
end
else begin
remainder <= remainder;//余数
dividend_tmp <= dividend_tmp;
end
end
else begin//未开始
div_cnt <= 4'd0;//计数器
dividend_tmp <= src_xor;
remainder <= src_xor[15:10];//余数
end
end
end
assign cal_done = (div_cnt == 4'd12) ? 1'b1 : 1'b0;
assign crc = {~remainder[0], ~remainder[1], ~remainder[2], ~remainder[3], ~remainder[4]};
`probe(clk);
`probe(rst_n);
`probe(start);
`probe(div_100101);
`probe(div_cnt);
`probe(src_xor);
`probe(dividend_tmp);
`probe(dividend_tmp[9]);
`probe(remainder);
`probe(cal_done);
`probe(crc);
endmodule
另一种生成的代码,也可以用
// 亲测在线生成工具也可以用:http://www.easics.com
// 在线仿真工具:iverilog
// https://hdlbits.01xz.net/wiki/Iverilog
module top_module ();
reg clk = 0;
reg [10:0]data_11 = 11'd0;
reg [4:0]crc_0x1f = 5'h1f;
wire [4:0]crc;
`probe(clk);
always #1 clk = ~clk;
initial `probe_start;
initial begin
#10 data_11 <= 11'b10011001001;
crc_0x1f <= 5'h1f;
$display ("The current time is (%0d ps)", $time);
#10 $finish;
end
usb_crc5_d11 my_crc5 (
.data_11(data_11),
.crc_0x1f(crc_0x1f),
.crc(crc)
);
endmodule
module usb_crc5_d11(
input wire [10:0] data_11,
input wire [4:0] crc_0x1f,
output wire [4:0] crc
);
wire [4:0] crc_temp;
assign crc_temp[0] = data_11[10] ^ data_11[9] ^ data_11[6] ^ data_11[5] ^ data_11[3] ^
data_11[0] ^ crc_0x1f[0] ^ crc_0x1f[3] ^ crc_0x1f[4];
assign crc_temp[1] = data_11[10] ^ data_11[7] ^ data_11[6] ^ data_11[4] ^ data_11[1] ^
crc_0x1f[0] ^ crc_0x1f[1] ^ crc_0x1f[4];
assign crc_temp[2] = data_11[10] ^ data_11[9] ^ data_11[8] ^ data_11[7] ^ data_11[6] ^ data_11[3] ^ data_11[2] ^ data_11[0] ^ crc_0x1f[0] ^ crc_0x1f[1] ^ crc_0x1f[2] ^ crc_0x1f[3] ^ crc_0x1f[4];
assign crc_temp[3] = data_11[10] ^ data_11[9] ^ data_11[8] ^ data_11[7] ^ data_11[4] ^ data_11[3] ^ data_11[1] ^ crc_0x1f[1] ^ crc_0x1f[2] ^ crc_0x1f[3] ^ crc_0x1f[4];
assign crc_temp[4] = data_11[10] ^ data_11[9] ^ data_11[8] ^ data_11[5] ^ data_11[4] ^ data_11[2] ^ crc_0x1f[2] ^ crc_0x1f[3] ^ crc_0x1f[4];
assign crc = ~crc_temp;
wire [4:0] crc_people_view = {crc_temp[0], crc_temp[1], crc_temp[2], crc_temp[3], crc_temp[4]};
`probe(data_11);
`probe(crc_0x1f);
`probe(crc);
`probe(crc_people_view);
endmodule
验证+结论
在线仿真软件上,修改src <= 11’b10000001110;
它就是我们从第一张图片里读到的11位数
点击“submit”完成仿真得到0x04
附上完整仿真图片,与逻辑分析仪抓到的0x04一致,crc5没问题