天天看点

FPGA设计思想之“逻辑复制”

1、逻辑复制是一种通过增加面积来改善时序条件的优化手段,它最主要的应用时调整信号的扇出。如果某个信号需要驱动的后级逻辑信号较多,也就是其扇出非常大,那么为了增加这个信号的驱动能力,就必须插入很多级的Buffer,这样就在一定程度上增加了这个信号的路径延迟。

这种情况下就可以复制生成这个信号的逻辑,用多路同频同相的信号驱动后续电路,使平均到每路的扇出变低,这样不需要插入Buffer就能满足驱动能力增加的要求,从而节省路径延迟。

举一个例子:这个例子来自于特权同学《深入浅出玩转FPGA》

例一:未进行逻辑复制时:

module logic_copy(
input a,b,c,d,
input  sel,
output dout
);

assign dout = sel?(a + b):(c + d);


endmodule
           

对应的RTL原理图为:

FPGA设计思想之“逻辑复制”

可见,占用了两个加法器和一个二选一的多路选择器;

例二:逻辑复制后:

module logic_copy(
input a,b,c,d,
input  sel,
output dout
);
wire ab,cd;
assign ab = sel ? a : c;
assign cd = sel ? b : d;
assign dout = ab + cd;


endmodule
           
FPGA设计思想之“逻辑复制”

可见,占用了两个选择器和一个加法器;

2、FPGA中需要做很多重复工作

在某些FPGA设计中,需要很多重复设计的时候,这时候逻辑复制也就有用了。

例如:在某个特殊应用场合需要设计方向可以任意改变的240位宽的三态IO管脚。我们先看看常用的一个位宽的三态管脚怎么设计。

module inout_interface(
    dat_in,
    io_out,
    io_dir,
    dat_out
    );
    input       dat_in;
    input       io_dir;
    output      dat_out;
    inout       io_out;
    
    assign      io_out  = io_dir ? dat_in : 1'bz;
    assign      dat_out = io_out;
    
endmodule           

如上述程序所示为单个双向IO口的典型设计代码,中间由IO输入方向控制数据和高阻之间的切换,难题出现了,怎么设计240位宽的双向IO口呢?难道如下列程序所示:

module inout_interface(
    dat_in,
    io_out,
    io_dir,
    dat_out
    );
    input  [239 : 0]     dat_in;
    input  [239 : 0]     io_dir;
    output [239 : 0]     dat_out;
    inout  [239 : 0]     io_out;
    
    assign      io_out  = io_dir ? dat_in : 240'bz;
    assign      dat_out = io_out;
    
endmodule           

显然这样是不行的,因为当io_dir为240位的时候只有当全为0的时候此式才为假,其余时候都为真,显然达不到想要的每个IO都是双向口的设计。

修改代码如下:

module inout_interface(

    dat_in,

    io_out,

    io_dir,

    dat_out

    );

    input  [239 : 0]     dat_in;

    input  [239 : 0]     io_dir;

    output [239 : 0]     dat_out;

    inout  [239 : 0]     io_out;

   

    assign      io_out[0]  = io_dir[0] ? dat_in[0] : 1'bz;

    assign      dat_out[0] = io_out[0];

   

    assign      io_out[1]  = io_dir[1] ? dat_in[1] : 1'bz;

    assign      dat_out[1] = io_out[1];

   

    assign      io_out[2]  = io_dir[2] ? dat_in[2] : 1'bz;

    assign      dat_out[2] = io_out[2];

   

    .

    .       // 此处略去1万行

    .

   

    assign      io_out[239]  = io_dir[239] ? dat_in[239] : 1'bz;

    assign      dat_out[239] = io_out[239];

   

endmodule           

  显然这种办法能实现240位宽的独立方向控制IO,但是估计写代码要累死人,有没得更好的办法呢?

  当然有,在verilog2001中有个逻辑复制语法——generate,可以对verilog模块进行无限复制。有了这个模块我们即可轻松通过逻辑复制来达到我们的要求了。

// 单个双向IO实现模块

module  pin_inout(

    indat,

    indir,

    outdat,

    outdatin

    );

   

    input       indat;

    input       indir;

    inout       outdat;

    output      outdatin;

   

    assign      outdat   = indir ? indat : 1'bz;

    assign      outdatin = outdat;

 

endmodule

 

module inout_interface(

    dat_in,

    io_out,

    io_dir,

    dat_out

    );

    input  [239 : 0]     dat_in;

    input  [239 : 0]     io_dir;

    output [239 : 0]     dat_out;

    inout  [239 : 0]     io_out;

   

    // 逻辑复制240次

    genvar  i;

    generate

        for(i = 0; i < 240; i = i + 1)

        begin : pin_loop

            pin_inout   pin_inout_inst(

                .indat          (   dat_in[i]       ),

                .indir          (   io_dir[i]       ),

                .outdat         (   io_out[i]       ),

                .outdatin       (   dat_out[i]      )

            );

        end

    endgenerate

   

endmodule
           

由上面代码可看出,巧妙利用verilog语法能减少自身工作量。

在FPGA设计中有些情况的逻辑复制不需要我们做,但是有些情况的逻辑复制不得不手工完成,因此,熟练掌握verilog语法是设计出好的模型、减少工作量的前提。