USB的CRC5理解

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,但是要怎么计算呢?

计算方法

在这里插入图片描述

  1. 图片放大可以看到11位的Frame号是897,二进制表示就是01110000001
    在这里插入图片描述

  2. 由于usb协议低位在前,经过倒序,01110000001变成10000001110,与上上图一致

  3. 11位的原始值src=10000001110要变成16位,先填充5个0,变成src_padding=1000000111000000

  4. 16位异或序列是默认的:sequence_xor=1111100000000000

  5. 对二者进行异或src_xor = src_padding ^ sequence_xor; 得到如下图的0111100111000000
    在这里插入图片描述

  6. 对0111100111000000进行模二除法,除以100101,这个100101是usb固定的二项式,求它的余数:这里我用在在线工具计算:https://www.23bei.com/tool/744.html
    在这里插入图片描述
    可以看到求出的结果是11011,因为只取5位

  7. 对11011取反,得到00100

  8. 对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没问题
在这里插入图片描述

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值