高级FPGA设计笔记——Chapter 2 架构面积

本章讨论关于数字设计中的“面积”问题,并提出若干在FPGA设计中优化面积的方法。
我们将通过选择不同拓扑结构来减少面积消耗,这与我们熟知的通过综合、布线工具的约束来优化电路面积不同,约束方式是针对不同器件特性来降低逻辑门与模块的数量,而拓扑结构是在更高层面上来解决这个问题,适用于所有器件。
优化面积通常是最大限度地重用逻辑资源,这会以牺牲数据吞吐量为代价。
本章将具体针对下列问题进行讨论:
1)收起流水线以在不同计算阶段重用逻辑资源。
2)在无现有的递归、循环等算法时,通过控制逻辑来实现逻辑复用。
3)在不同功能操作之间共享逻辑资源。
4)复位对面积优化的影响:

  • 缺少reset功能的影响;
  • 缺少set功能的影响;
  • 缺少异步reset功能的影响;
  • RAM reset的影响;
  • 使用set/reset对逻辑资源进行优化。

2.1 收起流水线

顾名思义,本小节与上一章的流水线操作正相反,通过复用流水线中的重复逻辑,实现面积的降低。
定点数乘法是常见的可以写为流水线的设计模块,我们可以通过添加移位和加法器来“收起”它,乘法器被优化为受B位数控制的A的移位累加算法。
优化前代码:

module mult8(
output [7:0] product, 
input [7:0] A,
input [7:0] B,
input clk); 
reg [15:0] prod16;
    assign product = prod16[15:8];
    always @(posedge clk)
      prod16 <= A * B;
endmodule

优化后代码:

module mult8(
output done, 
output reg [7:0] product,
input [7:0] A,
input [7:0] B,
input clk,
input start);
    reg [4:0] multcounter; // counter for number of shift/adds
    reg [7:0] shiftB; // shift register for B
    reg [7:0] shiftA; // shift register for A
    wire adden; // enable addition
    assign adden = shiftB[7] & !done;
    assign done = multcounter[3];
    always @(posedge clk) begin
    // increment multiply counter for shift/add ops 
    if(start) multcounter <= 0;
    else if(!done) multcounter <= multcounter + 1;
      // shift register for B
      if(start) shiftB <= B;
      else shiftB[7:0] <= {shiftB[6:0], 1’b0};
      // shift register for A
      if(start) shiftA <= A;
      else shiftA[7:0] <= {shiftA[7], shiftA[7:1]};
      // calculate multiplication
      if(start)      product <= 0;
      else if(adden) product <= product + shiftA;
    end
endmodule

2.2 基于控制信号的逻辑复用

当共享逻辑明显大于所需的控制逻辑时,可以通过添加控制逻辑的方式实现共享逻辑复用。

多数情况下,逻辑的复用并不想2.1节那么简单,需要创建一些控制逻辑,比如状态机,来更好的实现。以低通FIR滤波器的实现为例。


低通FIR滤波器公式

实现代码:

module lowpassfir(
    output reg [7:0] filtout,
    output reg done,
    input clk,
    input [7:0] datain, // X[0]
    input datavalid, // X[0] is valid
    input [7:0] coeffA, coeffB; coeffC); // coeffs for low pass filter

// define input/output samples
reg [7:0] X0, X1, X2;
reg multdonedelay;
reg multstart; // signal to multiplier to begin computation
reg [7:0] multdat;
reg [7:0] multcoeff; // the registers that are multiplied together
reg [2:0] state; // holds state for sequencing through mults
reg [7:0] accum; // accumulates multiplier products
reg clearaccum; // sets accum to zero 
reg [7:0] accumsum;
wire multdone; // multiplier has completed 
wire [7:0] multout; // multiplier product

// shift-add multiplier for sample-coeff mults 
mult8�8 mult8�8(.clk(clk), 
      .dat1(multdat),
      .dat2(multcoeff), 
      .start(multstart),
      .done(multdone), 
      .multout(multout));

always @(posedge clk) begin
      multdonedelay <= multdone;
      // accumulates sample-coeff products
      accumsum <= accum + multout[7:0];
      // clearing and loading accumulator 
      if(clearaccum) accum <= 0;
      else if(multdonedelay) accum <= accumsum;
      // do not process state machine if multiply is not done
      case(state)
         0: begin
         // idle state
         if(datavalid) begin
              // if a new sample has arrived
              // shift samples
              X0 <= datain;
              X1 <= X0;
              X2     <= X1;
              multdat  <= datain;
              multcoeff <= coeffA;
              multstart <= 1;
              clearaccum <= 1; // clear accum
              state   <= 1;
          end
          else begin
              multstart <= 0;
              clearaccum <= 0;
              done    <= 0;
         end
     end
    1: begin
          if(multdonedelay) begin
               // A*X[0] is done, load B*X[1]
               multdat  <= X1;
               multcoeff <= coeffB;
               multstart <= 1;
               state   <= 2;
          end
          else begin
               multstart <= 0;
               clearaccum <= 0;
               done    <= 0;
          end
    end
    2: begin
          if(multdonedelay) begin
               // B*X[1] is done, load C*X[2]
               multdat  <= X2;
               multcoeff <= coeffC;
               multstart <= 1;
               state   <= 3;
         end
         else begin
               multstart <= 0;
               clearaccum <= 0;
               done    <= 0;
        end
    end
    3: begin
         if(multdonedelay) begin
              // C*X[2] is done, load output
              filtout  <= accumsum;
              done    <= 1;
              state   <= 0;
         end
         else begin
              multstart <= 0;
              clearaccum <= 0;
              done    <= 0;
         end
     end
  default
      state   <= 0;
endcase
end
endmodule

2.3 逻辑共享

对于以面积为主要要求的紧凑型设计,应在不同模块中搜索相似的逻辑,这些逻辑可以被带到全局层面被多个模块共享。
下图中模块A和B是两个完全独立的逻辑。A是一个计数分频器,8-bit计数器自动计数和归零;B是一个PWM生成器,11-bit计数器在固定值归零。通过逻辑共享,可以有效节省面积。
优化前:


完全独立的两个模块

优化后:


计数器共享

2.4 复位对面积的影响

不恰当的复位方式可能造成大量额外的逻辑资源浪费,不利于面积优化。

2.4.1 reset对资源的影响

FPGA内部有许多特定的built-in资源,例如移位寄存器,可以有效节省LUT与FF的使用,但是在非必要的reset使用情况下,综合工具就会放弃使用built-in资源,造成资源浪费。如下两种设计会导致完全不同的结果:


移位寄存器的不同设计

生成的电路区别如下:


两种设计方式生成的不同电路

2.4.2 set对资源的影响

与移位寄存器不同,大多数FPGA的内置DSP资源包含reset功能,但如何非要使用set,则会在输出端的产生多余逻辑。


不恰当的set

2.4.3 异步reset对资源的影响

许多新出的高性能FPGA内置资源还有同步reset功能,如DSP,但如果非要使用异步reset,也会浪费大量逻辑资源。

2.4.4 reset RAM

去复位RAM通常是一种糟糕的设计,尤其是使用异步复位。

通常情况下,我们希望使用BRAM资源来完成FPGA内部存储功能,节省FF资源,但不恰当的复位(异步复位)会适得其反。


复位方式对RAM的影响

2.4.5 利用FF的set/reset因脚来优化设计

可以发现,FF的set可以完成外部或门功能;reset则与外部的与门功能等效。如下图所示:


set等价于或门

reset等价于与门

利用这个原理可以使一些看似复杂的逻辑变得意外简单:


你以为需要你个逻辑门?

就是这么简单
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容