用FPGA模拟fuse时出现的一个小问题

我们知道现在很多芯片里面都会有一个fuse的模块,通过fuse我们可以来定义芯片的生命周期,保存相关的秘钥等等,可以说fuse是芯片安全的一个基础,fuse虽然简单,但是至关重要,我们在芯片前期进行FPGA验证的时候,也需要在FPGA上实现FUSE的功能,但是FPGA本身并没有FUSE,所以我们还是要通过SRAM来模拟FUSE。

这次发现的问题就是FUSE模拟的时候出来的一个问题,在实际芯片中,FUSE都是一些Hard library,我们要写等效的Verilog模型来模拟Fuse的行为,因为FUSE都是单比特可烧写的,所以最开始我们是用一个寄存器模拟fuse的读写操作的,中间会涉及一些地址转换,总之写下来的等效模型如下:

`timescale 1ns/10ps
module fuse(
    AEN,
    RDEN,
    PGMEN,
    A,
    D,
    CLK
);
    parameter     FUSE_SIZE = 512;
    parameter     ADR_R_WIDTH=6;

    input         AEN;
    input         RDEN;
    input         PGMEN;
    input  [8:0]  A;
    output [7:0]  D;
    input         CLK;

    reg [FUSE_SIZE - 1:0]fuse = {FUSE_SIZE-1{1'b0}};
    // for read
    assign D=(AEN & RDEN) ? {
        fuse[FUSE_SIZE/8*7 + A[ADR_R_WIDTH-1:0]],
        fuse[FUSE_SIZE/8*6 + A[ADR_R_WIDTH-1:0]],
        fuse[FUSE_SIZE/8*5 + A[ADR_R_WIDTH-1:0]],
        fuse[FUSE_SIZE/8*4 + A[ADR_R_WIDTH-1:0]],
        fuse[FUSE_SIZE/8*3 + A[ADR_R_WIDTH-1:0]],
        fuse[FUSE_SIZE/8*2 + A[ADR_R_WIDTH-1:0]],
        fuse[FUSE_SIZE/8*1 + A[ADR_R_WIDTH-1:0]],
        fuse[0             + A[ADR_R_WIDTH-1:0]]
    }:8'hzz;
    // for program
    always @(posedge CLK)
        if (AEN && PGMEN)
            fuse[A] <= 1'b1;

endmodule

改完跑下仿真,完全没有问题,跑的是嗖嗖的,为了防止综合出现问题,我还特意找了资料,Xilinx官方有说明支持寄存器的初始化语句,所以也就能够保证不需要复位就能让寄存器为0值,这个真的是棒棒哒,毕竟我的fuse可不能一复位就清零了。

跑完仿真,直接综合PR上板。。。。直接悲剧,fuse似乎完全不起作用,后面查了一下综合后的网表,发现fuse被综合成下面这个死样子了:

fuse.png

天呐,我要的是基于RAM的fuse,你这给我一个组合逻辑是怎么回事?我的信号是不会动吗?给我搞成这样,怎么可能工作呀。

为了解决这个问题,我是找遍了资料,一遍一遍的尝试,最终也没能搞定,无奈,只好曲线救国。其实fuse位地址和字节地址转换的时候,我们可以不在输出上做转换,直接在写的时候转换也可以呀,其实这样做应该更适合我们思考,于是研究了半天我把模型改成了下面这个样子:

`timescale 1ns/10ps
module fuse(
    AEN,
    RDEN,
    PGMEN,
    A,
    D,
    CLK
);
    parameter INIT_FILE = ""
    input         AEN;
    input         RDEN;
    input         PGMEN;
    input  [8:0]  A;
    output [7:0]  D;
    input         CLK;

    reg [7:0]fuse[0:63];
    generate
        if (INIT_FILE != "") begin: use_init_file
            initial
                $readmemh(INIT_FILE, fuse, 0, 63);
        end else begin: use_fuse_zero
            integer fuse_index;
            initial
                for (fuse_index = 0; fuse_index < 64; fuse_index = fuse_index + 1)
                    fuse[fuse_index] = {8{1'b0}}
    // for read
    assign D=(AEN & RDEN) ? fuse[A[5:0]] : 8'hzz;
    // for program
    always @(posedge CLK)
        if (AEN && PGMEN)
            fuse[A[5:0]] <= fuse[A[5:0]] | (1 << A[8:6]);

endmodule

为了安全,我还是把这个模块单独综合了一下,我们来看看结果:

fusepass.png

嗯,复杂多了,感觉是没问题了,上板看看结果吧,因为这篇文章是等PR过程中写的,所以结果我也不清楚,要是翻车了,我后面会更新,否则就是成功了。


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

相关文章

发表新评论