Thông báo

Collapse
No announcement yet.

Project nhỏ: Bộ lọc số FIR thực hiện bằng FPGA

Collapse
This is a sticky topic.
X
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • tonyvandinh
    replied
    Phần C code cũng tương tự như bạn đã đưa lên, nhưng phần implementation lại nhắm vào ASIC, nên không thể nói rằng những biến thể đó là parallel vs semi-parallel vs serial architecture trên FPGA.

    Nếu có thể thì bạn làm một high level synthesis demonstration cho FPGA. Demo của Mentor không nhắm vào FPGA cho nên chưa được convincing lắm.
    Khi target cho FPGA và nếu muốn tận dụng những cell đặc biệt (DSP, accelerate) thì C/C++ cũng cần phải sửa đổi lại để ám chỉ (infer) cách thiết kế đó.

    Ví dụ cho DSP của Xilinx
    Code:
    #include "fir_filter.h"
    
    #pragma map_to_operator dsp_cell
    void dsp_cell(d_t xin, d_t h, acc_t yin, acc_t &yout)
    {
    	yout = (xin * h) + yin;
    }
    
    #pragma design top
    void fir_filter (d_t *input, c_t coeffs[NUM_TAPS], d_t *output ) {
      static d_t regs[NUM_TAPS];
      acc_t temp = 0;
      int i;
      
      SHIFT:for ( i = NUM_TAPS-1; i>=0; i--) {
        if ( i == 0 ) 
          regs[i] = *input;
        else 
          regs[i] = regs[i-1];
      }
    
      MAC:for ( i = NUM_TAPS-1; i>=0; i--) {
    		dsp_cell(regs[i], coeffs[i], temp, temp);
      }
    
      *output = temp>>7;
    }
    Ví dụ cho Altera
    Code:
    #include "fir_filter.h"
    
    #pragma map_to_operator dsp_cell
    acc_t dsp_cell(d_t xin[4], d_t h[4])
    {
      acc_t temp = 0;
      
      #pragma unroll yes
      MAC:for ( int i=3; i>=0; i--) 
          temp += xin[i]*h[i];
          
      return temp;;
    }
    
    #pragma design top
    void fir_filter (d_t *input, d_t coeffs[NUM_TAPS], d_t *output ) {
      static d_t regs[NUM_TAPS];
      acc_t temp = 0;
      d_t r[4], h[4];
      
      #pragma unroll yes
      for (int i=NUM_TAPS-1; i>=0; i--)
    	if (i==0)
    		regs[i] = *input;
    	else
    		regs[i] = regs[i-1];
    
      for (int j=(NUM_TAPS/4)-1; j>=0; j--)
      {	
    	  #pragma unroll yes
    	  for (int i=3; i>=0; i--)
    	  {
    		r[i] = regs[(j*4)+i];
    		h[i] = coeffs[(j*4)+i];
    	  }
    	  
    	  temp += dsp_cell(r,h);
      }
      *output = temp>>(NUM_TAPS-1);	
    }
    Chú ý : dsp_cell đã viết lại để mô tả cách kiến trúc đặc thù của DSP cell cho mỗi FPGA technology. Khi HLS tổng hợp sẽ tạo ra một khối RTL riêng với cấu trúc để tổng hợp RTL có thể ám chỉ (infer) những DSP cell của technology đó.
    Last edited by tonyvandinh; 29-10-2010, 00:48.

    Leave a comment:


  • hongmta
    replied
    Fir

    Nguyên văn bởi nemesis21 Xem bài viết
    Với FIR, nếu chúng ta khởi đầu với parallel - adder tree VHDL implementation, conversion sang parallel - simple systolic chỉ mất vài chục giây. Chuyển sang serial, 5-15 phút. Chuyển sang semi-parallel, 10-20 phút. Nếu designed well, semi-parallel FIR có thể trade-off throughput vs size chỉ trong vài giây bằng cách thay đổi generics.

    Nếu chúng ta cần gấp một configurable FIR core, chúng ta có thể bỏ ra vài phút với Coregen. FIR filter là một beginner project vừa tầm để introduce basic techniques, nhưng là một unconvincing case cho high level synthesis tools.


    Mentor có một demonstration video dùng Catapult C để implement FIR:
    http://www.mentor.com/products/esl/m...1-b8518e15cbbe

    Phần C code cũng tương tự như bạn đã đưa lên, nhưng phần implementation lại nhắm vào ASIC, nên không thể nói rằng những biến thể đó là parallel vs semi-parallel vs serial architecture trên FPGA.

    Nếu có thể thì bạn làm một high level synthesis demonstration cho FPGA. Demo của Mentor không nhắm vào FPGA cho nên chưa được convincing lắm.


    System C là một chuyện, more exposure về FPGA cho "software crowd" beginners. Có khá nhiều tool để chuyển từ System C sang HDL. Nhưng "algorithm synthesis tool" có lẽ thuộc vào một phạm trù khác. Về cost và availability, nó nằm ngoài tầm với, ngay cả cho nhiều companies. Tôi cảm thấy nó là một sự phụ thuộc vào tool từ một vài specific vendor hơn là một phương pháp đại trà.
    thiết kế bộ FIR theo sơ đồ này dễ hiểu chứ các bạn?
    Attached Files

    Leave a comment:


  • tonyvandinh
    replied
    Nguyên văn bởi DVinh Xem bài viết
    - bit-exact match: Làm luôn khâu này đi bác, làm ơn cho trót mà! Cách nào dễ để chuyển từ floating point của model qua fixed point, chọn fixedpoint coefficients sao cho gần 0dB nhất, rồi truncate output mà cân bằng được giữa SNR và dynamic range? Mấy cái này lý thuyết thường bị bỏ qua, đụng thực tế mới vỡ nợ, bản thân tui loay hoay mãi cái chuyện này.
    Thường thì SNR và dynamic range lệ thuộc vào test bench để bảo đảm sự chính xác độ dài của data. Bạn có thể profile những variables bằng cách dùng "printf" ra bài, rồi dùng excel để sort thì sẽ biết được min và max. Từ đó chọn độ dài (bit width) cho hợp với điều kiện của thiết kế. Nên dùng C/C++ để làm vấn đề này thì lẹ hơn là RTL vì nó chỉ dính líu tới sự hoạt động (functionality) và dùng typedef để fine tune bit width. SystemC data type thường được dùng để mô tả bit accurate models.

    Leave a comment:


  • DVinh
    replied
    Nguyên văn bởi nemesis21 Xem bài viết
    - Giai đoạn viết VHDL nên là giai đoạn dễ dàng nhất.

    Khuyến khích làm các project là một việc hay, nhưng tôi nghĩ rằng học về process và các trình tự tất yếu còn quan trọng hơn simulation output đối với những người mới.
    Rất hay!

    Chỉ có một vài điều nhỏ :
    - nếu là real-valued filter, thì coefficients sẽ phải đối xứng. Có thể optimize một chút hơn ở đây.
    - Output của adder phải thêm 1 bit cho mổi tầng; hình như trong code thiếu cái này ?
    - bit-exact match: Làm luôn khâu này đi bác, làm ơn cho trót mà! Cách nào dễ để chuyển từ floating point của model qua fixed point, chọn fixedpoint coefficients sao cho gần 0dB nhất, rồi truncate output mà cân bằng được giữa SNR và dynamic range? Mấy cái này lý thuyết thường bị bỏ qua, đụng thực tế mới vỡ nợ, bản thân tui loay hoay mãi cái chuyện này.

    Leave a comment:


  • stupid1102
    replied
    Em kiếm được cái master thesis của bọn này triển khai FIR filter trên FPGA, show các bác xem thế nào ^^

    Link download : http://hungnm.edu.vn/cupid1102/FIR_Filter_FPGA/

    Leave a comment:


  • jefflieu
    replied
    Nice !!! Đỉnh !!! ...

    Leave a comment:


  • nemesis21
    replied
    Processing element model, nếu bạn nhìn kỹ, là một partial model của DSP48 slice (hình đính kèm). Chúng ta vòng vo một chút để illustrate modeling technique, nhưng DSP48 là primitive mà bạn có thể instantiate directly. Đơn giản là một phần của DSP48 được design especially cho filter implementations.

    Code:
    class PE () :
      
      def __init__(self) :    
        self.leftCell = 0;    
        self.a1 = 0;
        self.a2 = 0;
        self.b2 = 0;
        self.m  = 0;
        self.p  = 0;
        
      def simClkCycle (self, a1 = 0) :    
        if (self.leftCell == 0) :    
          self.p = self.m + 0;           
          self.m = self.a2 * self.b2;
          self.a2 = self.a1;
          self.a1 = a1;      
        else :
          self.p = self.m + self.leftCell.p;      
          self.m = self.a2 * self.b2;
          self.a2 = self.a1;
          self.a1 = self.leftCell.a2;
    Đương nhiên chúng ta có thể model với bất kỳ language nào, chẳng hạn như Java, C, hay SystemC. Lợi thế của Python hay Matlab hoặc các tool tương tự là các mathematical libraries. Chúng ta có thể dễ dàng so sánh outputs của model vs output của Python/Matlab functions, chẳng hạn như fft, qr, etc.

    FIR filter có thể là quá đơn giản để model, nhưng các algorithm với multi-dimension systolic array có lẽ sẽ interesting hơn. FIR thì vẫn còn serial và semi-parallel implementation. Chúc thành công.
    Attached Files

    Leave a comment:


  • nemesis21
    replied
    Tiếp theo phần simple systolic FIR hôm trước, nếu bạn thử implement adder chain và synthesize FIR cho Virtex-5 speed grade -2 device, maximum frequency sẽ vào khoảng 250 MHz.

    Code:
      ADDER_GEN : for i in 0 to NUM_TAPS-1 generate
        ADDER_0_GEN : if i = 0 generate
          sum(i) <= prod(i) when rising_edge(clk);
        end generate;
        ADDER_X_GEN : if i > 0 generate
          sum(i) <= prod(i) + sum(i-1) when rising_edge(clk);
        end generate;
      end generate ADDER_GEN;
    Code:
    Timing Summary:
    ---------------
    Speed Grade: -2
    
       Minimum period: 3.883ns (Maximum Frequency: 257.506MHz)
       Minimum input arrival time before clock: 0.980ns
       Maximum output required time after clock: 2.910ns
       Maximum combinational path delay: No path found
    Làm thế nào để synthesize lên 500-600 MHz?

    Chúng ta bắt đầu lại với phần hoán chuyển từ adder tree sang adder chain. Đó là một well-documented technique. Hình đính kèm từ Lyrtech illustrate technique đó khá rõ. Chúng ta có thể thấy các systolic "processing elements" illustrated trong phần 3. Simplified systolic (Python) model đã được đưa ra ở trên. Nhưng chúng ta model full systolic array như thế nào?

    Đại khái là, processing element có thể được model bằng class:

    Code:
    class PE () :
      
      def __init__(self) :    
        self.leftCell = 0;    
        self.a1 = 0;
        self.a2 = 0;
        self.b2 = 0;
        self.m  = 0;
        self.p  = 0;
        
      def simClkCycle (self, a1 = 0) :    
        if (self.leftCell == 0) :    
          self.p = self.m + 0;           
          self.m = self.a2 * self.b2;
          self.a2 = self.a1;
          self.a1 = a1;      
        else :
          self.p = self.m + self.leftCell.p;      
          self.m = self.a2 * self.b2;
          self.a2 = self.a1;
          self.a1 = self.leftCell.a2;
    Sau đó được kết nối lại như một linked-list:

    Code:
    peList = list();
      previousPE = 0;
      for j in range(len(filter_coeffs)) :
        pe = PE();
        pe.b2 = filter_coeffs[j];
        pe.leftCell = previousPE;
        peList.append(pe);
        previousPE = pe;
    Và apply stimulus:

    Code:
    for i in range (len(x)) :
        for k in range (len(filter_coeffs)-1,0,-1) :
          peList[k].simClkCycle();
        peList[0].simClkCycle(x[i]);
        y[i] = peList[len(peList)-1].p;
    Phần hoán chuyển sang VHDL khá đơn giản:

    Code:
      type breg_type is array(natural range <>) of std_logic_vector(data_in'range);
      signal breg : breg_type(NUM_TAPS-1 downto 0);
      
      type preg_type is array(natural range <>) of std_logic_vector(data_in'length*2-1 downto 0);
      signal preg : preg_type(NUM_TAPS-1 downto 0);
    
      constant zero : std_logic_vector(47 downto 0) := (others => '0');
    Code:
      pe_inst : entity work.pe
      port map (
        clk => clk,
        a => filter_coeffs(0),
        bcin => data_in,
        bcout => breg(0),
        pcin => zero(preg(0)'range),
        pcout => preg(0)
      );
    
      PE_X: for i in 1 to NUM_TAPS-1 generate
      
        pe_inst : entity work.pe
        port map (
          clk => clk,
          a => filter_coeffs(i),
          bcin => breg(i-1),
          bcout => breg(i),
          pcin => preg(i-1),
          pcout => preg(i)
        );
      
      end generate;
    
      data_out <= preg(NUM_TAPS-1);
    Phần processing element các bạn hãy thử implement. Nếu implement đúng, thì khi synthesize, FIR filter implement sẽ chỉ tốn tài nguyên DSP48, và không tốn thêm bất kỳ resource nào khác.

    Code:
    Slice Logic Utilization: 
    
    Slice Logic Distribution: 
     Number of LUT Flip Flop pairs used:      0
       Number with an unused Flip Flop:       0  out of      0         
       Number with an unused LUT:             0  out of      0         
       Number of fully used LUT-FF pairs:     0  out of      0         
       Number of unique control sets:         0
    
    IO Utilization: 
     Number of IOs:                          49
     Number of bonded IOBs:                  49  out of    480    10%  
    
    Specific Feature Utilization:
     Number of BUFG/BUFGCTRLs:                1  out of     32     3%  
     Number of DSP48Es:                      21  out of    288     7%
    Và maximum frequency sẽ vào khoảng 590 MHz trên một Virtex-5 speed grade -2 (Virtex-5 có 3 speed grades, -1, -2, và -3).

    Code:
    Timing Summary:
    ---------------
    Speed Grade: -2
    
       Minimum period: 1.692ns (Maximum Frequency: 591.017MHz)
       Minimum input arrival time before clock: 1.190ns
       Maximum output required time after clock: 2.910ns
       Maximum combinational path delay: No path found
    Attached Files

    Leave a comment:


  • nemesis21
    replied
    Nguyên văn bởi tonyvandinh Xem bài viết
    Đây là những câu hỏi thường có trong đầu của người làm thiết kế. Khá phức tạp để chọn lựa cái architecture nào để làm. Khi chọn rồi, làm giữa chừng mà muốn đổi thì sao? Làm sao để trade-off giữa tốc độ và tốn kém? Vì vậy cần phải giữ thiết kế nguồn kô bị lệ thuộc vào thời gian and hardware architecture rồi dùng phương pháp tự động hóa để chuyển biến qua thiết kế hiện thực mà mình muốn. Link dưới đây sẽ cho các bạn có thêm khái niệm về phương pháp này (High Level Synthesis)

    http://en.wikipedia.org/wiki/High-level_synthesis
    Với FIR, nếu chúng ta khởi đầu với parallel - adder tree VHDL implementation, conversion sang parallel - simple systolic chỉ mất vài chục giây. Chuyển sang serial, 5-15 phút. Chuyển sang semi-parallel, 10-20 phút. Nếu designed well, semi-parallel FIR có thể trade-off throughput vs size chỉ trong vài giây bằng cách thay đổi generics.

    Nếu chúng ta cần gấp một configurable FIR core, chúng ta có thể bỏ ra vài phút với Coregen. FIR filter là một beginner project vừa tầm để introduce basic techniques, nhưng là một unconvincing case cho high level synthesis tools.


    Mentor có một demonstration video dùng Catapult C để implement FIR:
    http://www.mentor.com/products/esl/m...1-b8518e15cbbe

    Phần C code cũng tương tự như bạn đã đưa lên, nhưng phần implementation lại nhắm vào ASIC, nên không thể nói rằng những biến thể đó là parallel vs semi-parallel vs serial architecture trên FPGA.

    Nếu có thể thì bạn làm một high level synthesis demonstration cho FPGA. Demo của Mentor không nhắm vào FPGA cho nên chưa được convincing lắm.


    System C là một chuyện, more exposure về FPGA cho "software crowd" beginners. Có khá nhiều tool để chuyển từ System C sang HDL. Nhưng "algorithm synthesis tool" có lẽ thuộc vào một phạm trù khác. Về cost và availability, nó nằm ngoài tầm với, ngay cả cho nhiều companies. Tôi cảm thấy nó là một sự phụ thuộc vào tool từ một vài specific vendor hơn là một phương pháp đại trà.

    Leave a comment:


  • tonyvandinh
    replied
    Nguyên văn bởi nemesis21 Xem bài viết
    - Design cho filter như thế nào? Dựa vào requirements / specs, bạn đã chọn filter structure nào? Parallel, serial (single multiplier), hoặc semi-parallel? Nếu serial, mạch delay của bạn nên sử dụng RAM (hoặc registers và multiplexer để emulate RAM). Nếu parallel, mạch delay của bạn nên sử dụng registers. Bạn đã chọn structure / mạch delay nào? Bạn đã vẽ block diagram cho design của bạn? Generic design hay là target spefiic? Chẳng hạn như với DSP48 của Virtex-4, parallel design có thể hoàn toàn sử dụng intenal registers và routing của DSP48. Bạn đã diagram / model mạch delay của bạn?
    Đây là những câu hỏi thường có trong đầu của người làm thiết kế. Khá phức tạp để chọn lựa cái architecture nào để làm. Khi chọn rồi, làm giữa chừng mà muốn đổi thì sao? Làm sao để trade-off giữa tốc độ và tốn kém? Vì vậy cần phải giữ thiết kế nguồn kô bị lệ thuộc vào thời gian and hardware architecture rồi dùng phương pháp tự động hóa để chuyển biến qua thiết kế hiện thực mà mình muốn. Link dưới đây sẽ cho các bạn có thêm khái niệm về phương pháp này (High Level Synthesis)

    http://en.wikipedia.org/wiki/High-level_synthesis

    Leave a comment:


  • nemesis21
    replied
    Sang phần simulation, bạn có thể tiếp tục dùng math_real library.

    Code:
      signal data_in : std_logic_vector(15 downto 0) := (others => '0');
      signal data_out : std_logic_vector(31 downto 0) := (others => '0');
      signal counter : natural := 0;
    Đoạn code dưới generate sine wave tương tự như phần Python code:

    Code:
      data_gen_proc : process(clk)
      begin
        if (rising_edge(clk)) then      
          if (counter mod 4 = 0) then
            data_in <= std_logic_vector(to_signed(integer(sin(real(counter) / 10.0) * 32767.0), data_in'length));
          else
            data_in <= (others => '0');
          end if;
          counter <= counter + 1;
        end if;
      end process data_gen_proc;
    Phần simulation output trong hình đính kèm. Nói tóm lại là, cho một project, bạn nên dành thời gian cho specs / design / modeling. Với một project nhỏ, nếu bạn model tốt, thì bạn chỉ mất khoảng 5-15 phút để chuyển sang VHDL.

    =======

    Adder tree còn rườm rà; systolic form gọn hơn nhiều:

    Code:
      delay_buf = zeros(len(filter_coeffs)*2-1); y = zeros(len(x)); prod = zeros(len(filter_coeffs)); sum_out = zeros(len(filter_coeffs));
      
      for i in range (len(x)) :
        delay_buf[1:len(delay_buf)] = delay_buf[0:len(delay_buf)-1];delay_buf[0] = x[i]
        for k in range (len(filter_coeffs)) :
          prod[k] = delay_buf[k*2] * filter_coeffs[k];
        for k in range (len(filter_coeffs)-1,-1,-1) :
          if (k == 0) :
            sum_out[k] = prod[k];
          else :
            sum_out[k] = prod[k] + sum_out[k-1];
        y[i] = sum_out[len(sum_out)-1]
    Các bạn tiếp tục thử nhé.
    Attached Files
    Last edited by nemesis21; 08-05-2010, 07:14.

    Leave a comment:


  • nemesis21
    replied
    .......

    Adder tree rất cơ bản, chẳng hạn như chúng ta có 4 inputs a, b, c, d, nhưng adder chỉ có thể cộng lại tối đa mỗi lần 2 inputs, chúng ta trước tiên phải cộng (a+b) và (c+d), và sau đó cộng (a+b)+(c+d). Không khó để các bạn model adder tree, nhưng tôi đưa lên một version ở đây để tham khảo đối chiếu với VHDL implementation.

    Code:
    def adder_tree (data_in) :
      
      num_inputs = len(data_in);
      num_levels = int(ceil(log2(num_inputs)));
      sum_out = zeros((num_levels, num_inputs));
      
      for l in range(num_levels) :
        num_adders = int(ceil(num_inputs/(2.0**(l+1))));
        num_adder_prev_stage=int(ceil(num_inputs/(2.0**(l))));
        if (l == 0) :
          for m in range (num_adders) :
            if (m == (num_adders - 1)) and (num_adder_prev_stage % 2 == 1) :
              sum_out[l][m] = data_in[m*2];
            else :
              sum_out[l][m] = data_in[m*2] + data_in[m*2+1];    
        else :
          for m in range (num_adders) :
            if (m == (num_adders - 1)) and (num_adder_prev_stage % 2 == 1) :
              sum_out[l][m] = sum_out[l-1][m*2];
            else :
              sum_out[l][m] = sum_out[l-1][m*2] + sum_out[l-1][m*2+1];                       
      
      return sum_out[num_levels-1][0]

    So sánh với phần VHDL dưới đây:

    Code:
      type sum_mem_type is array (natural range <>, natural range <>) of signed(data_in'length*2-1 downto 0);
      constant num_levels : natural := integer(ceil(log(real(NUM_TAPS)) / log(2.0)));
      signal sum : sum_mem_type(num_levels-1 downto 0, NUM_TAPS-1 downto 0) := (others => (others => (others => '0')));
    Code:
      ADDER_LEVELS_GEN : for l in 0 to num_levels-1 generate
        constant num_adders : natural := integer(ceil(real(NUM_TAPS)/(2.0**(l+1))));
        constant num_adder_prev_stage : natural := integer(ceil(real(NUM_TAPS)/(2.0**(l))));
      begin
    
        LEVEL_0_GEN : if (l = 0) generate
    
          ADDERS_GEN : for m in 0 to num_adders-1 generate
    
            NO_ADDER_GEN : if ((m = (num_adders-1)) and ((num_adder_prev_stage mod 2) = 1)) generate
              sum(l,m)  <= prod(m*2) when rising_edge(clk);
            end generate NO_ADDER_GEN;
    
            ADDER_GEN : if ((m < (num_adders-1)) or ((num_adder_prev_stage mod 2) = 0)) generate
              sum(l,m) <= prod(m*2) + prod(m*2+1) when rising_edge(clk);
            end generate ADDER_GEN;
    
          end generate ADDERS_GEN;
    
        end generate LEVEL_0_GEN;
    
        LEVEL_X_GEN : if (l > 0) generate
    
          ADDERS_GEN : for m in 0 to num_adders-1 generate
    
            NO_ADDER_GEN : if ((m = (num_adders-1)) and ((num_adder_prev_stage mod 2) = 1)) generate
              sum(l,m)  <= sum(l-1,m*2) when rising_edge(clk);
            end generate NO_ADDER_GEN;
    
            ADDER_GEN : if ((m < (num_adders-1)) or ((num_adder_prev_stage mod 2) = 0)) generate
              sum(l,m) <= sum(l-1,m*2) + sum(l-1,m*2+1) when rising_edge(clk);
            end generate ADDER_GEN;
    
          end generate ADDERS_GEN;
    
        end generate LEVEL_X_GEN;
    
      end generate ADDER_LEVELS_GEN;
    
      data_out <= std_logic_vector(sum(num_levels-1,0)) when rising_edge(clk);
    Syntax giữa hai language có khác, nhưng về phần giải thuật hoàn toàn tương đồng. Như có thể thấy, nếu bạn model to the right form, bạn có thể nhanh chóng hoán chuyển giải thuật từ language ưa thích của bạn sang (V)HDL. Đừng quá chú tâm vào FPGA và HDL khi bạn implement một thuật toán cho FPGA. Những đoạn code trên hoàn toàn synthesizable.

    Leave a comment:


  • nemesis21
    replied
    ......

    Hãy chọn adder tree nhé? Tổng thể model như thế này:

    Code:
      delay_buf = zeros(len(filter_coeffs)); y = zeros(len(x)); prod = zeros(len(filter_coeffs))  
      
      for i in range (len(x)) :
        delay_buf[1:len(filter_coeffs)] = delay_buf[0:len(filter_coeffs)-1];delay_buf[0] = x[i]
        for k in range (len(filter_coeffs)) :
          prod[k] = delay_buf[k] * filter_coeffs[k];
        y[i] = adder_tree(prod)

    Sau đây hãy bắt đầu so chiếu với VHDL code đơn giản dùng pre-loaded coefficients.

    Code:
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.math_real.all;
    
    entity pfir is
    generic (
      NUM_TAPS : positive range 5 to 200 := 21
    );
    port ( 
      clk : in  STD_LOGIC;
      data_in : in  STD_LOGIC_VECTOR;
      data_out : out  STD_LOGIC_VECTOR
    );
    end pfir;
    Phần delay buffer declaration như sau:

    Code:
      type data_mem_type is array (natural range <>) of std_logic_vector(data_in'range);
      signal delay_buf : data_mem_type(NUM_TAPS-1 downto 0) := (others => (others => '0'));
    Phần delay buffer implementation, bạn có thể dùng generate loop như đã đề cập đến, hoặc đơn giản hơn:
    Code:
    delay_buf <= delay_buf(NUM_TAPS-2 downto 0) & data_in when rising_edge(clk);
    Phần filter coefficients thủ công một chút. Chúng ta assume filter data width tưong tự như input data width.

    Code:
      constant filter_coeffs : data_mem_type(NUM_TAPS-1 downto 0) := (
        std_logic_vector(to_signed(integer(-6.21115459e-19 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-2.12227115e-03 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-6.32535399e-03 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-1.16118104e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-1.23546567e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(4.19252935e-18 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(3.17744976e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(8.14359076e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(1.37493782e-01 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(1.82125490e-01 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(1.99168830e-01 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(1.82125490e-01 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(1.37493782e-01 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(8.14359076e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(3.17744976e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(4.19252935e-18 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-1.23546567e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-1.16118104e-02 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-6.32535399e-03 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-2.12227115e-03 * 2.0**(data_in'length-1)-1.0),data_in'length)),
        std_logic_vector(to_signed(integer(-6.21115459e-19 * 2.0**(data_in'length-1)-1.0),data_in'length))
      );

    Phần multiplication cũng không quá khác so với model, cũng dùng for loop.

    Code:
      type prod_mem_type is array (natural range <>) of signed(data_in'length*2-1 downto 0);
      signal prod : prod_mem_type(NUM_TAPS-1 downto 0) := (others => (others => '0'));
    Code:
      MULT_GEN : for i in 0 to NUM_TAPS-1 generate
        prod(i) <= signed(delay_buf(i)) * signed(filter_coeffs(i)) when rising_edge(clk);
      end generate MULT_GEN;
    .......

    Leave a comment:


  • nemesis21
    replied
    Sẵn rãnh một chút, tôi sẽ bàn tiếp về FIR filter implementation cho các bạn mới bắt đầu.

    Về cấu trúc, khi nào áp dụng serial, parallel, hoặc semi-parallel?

    Dựa vào các thông số number of taps N, processing clock rate C, và output sample rate S.

    Serial, bạn sử dụng 1 multiplier (M = 1). Bạn cần N clock cycles để calculate 1 output. Nếu processing clock rate = 200 MHz, và number of taps = 20, bạn có thể dùng 1 multiplier để calculate 10 Msps (Megasample per second) trở xuống, N * S <= C * M

    Nhưng nếu required output sample rate = 200 Msps, M = 1 không đủ. Trong trường hợp này, bạn cần fully parallel structure, M = N.

    Nếu output sample rate = 50 Msps chẳng hạn, chúng ta vẫn có thể dùng M = N. Nhưng thông thường, design goal là phải sử dụng ít resources nhất, cho nên chúng ta sử dụng semi-parallel structure, với M = N / 4.

    Nếu output sample rate > 200 Msps, chúng ta vẫn có thể xử lý, nhưng hơi advance cho mạch này.

    =======

    Sau khi chúng ta đã chọn filter structure hợp lý, cần phải model như thế nào?

    Code:
    y[i] = sum(delay_buf * filter_coeffs)
    Trong khâu này, bạn cần phải có khả năng visualize hardware một chút, và diagraming có lẽ sẽ giúp bạn rất nhiều.

    Break down một chút:

    Code:
      temp = 0;
      for k in range(len(filter_coeffs)) :
        temp = temp + delay_buf[k] * filter_coeffs[k];
      y[i] = temp;
    Nếu bạn chọn parallel implementation, bạn cần hình dung / diagram K delay data values và K coefficient values feed vào K multipliers và cho ra K products.

    Code:
      prod = zeros(len(filter_coeffs));
      for k in range(len(filter_coeffs)) :
        prod[k] =  delay_buf[k] * filter_coeffs[k];
    Bạn cộng lại các products như thế nào? Sử dụng adder tree hay là adder chain / cascade?

    .......

    Leave a comment:


  • nemesis21
    replied
    Nguyên văn bởi tonyvandinh Xem bài viết
    Nemesis21 có thể giúp dùng Matlab để tạo ra bộ coefficient, số tap, input và output vectors được kô? Thx
    Với Matlab bạn sử dụng fir1() thay vì firwin() cho Python. Lưu ý là Nth order filter cho bạn N+1 taps.

    Code:
    N = 20; cf = fir1(N, .2, hamming(N+1))
    Phần analysis đơn giản hơn Python:

    Code:
    freqz(cf, 1)
    Sine input, nếu bạn không cần zero-fill thì cũng đơn giản:

    Code:
    input = sin((1:100)/10))
    Thường tôi không dùng Matlab vì vấn đề license. Python và các numerical libraries có thể không hoàn toàn tiện lợi bằng nhưng là open source.

    Leave a comment:

Về tác giả

Collapse

cachep Tìm hiểu thêm về cachep

Bài viết mới nhất

Collapse

Đang tải...
X