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

  • #16
    Nguyên văn bởi nemesis21 Xem bài viết
    Một vài góp ý vế FIR filter implementation:
    Trước tiên bạn nên tạo ra một fixed-point mathematical model với Matlab hoặc Numerical Python. Dù FIR filter hơi đơn giản một chút, nhưng bạn sẽ verify design của bạn như thế nào? Bạn nên đạt được bit-exact match giữa model và simulation/hardware output. Với algorithm khó hơn một chút như Cholesky decomposition, QR decomposition hoặc SVD decomposition, bạn không thể bỏ qua phần modeling được.
    Thanks, đây là con đường tiêu biểu và tất yếu của thiết kế các thuật toán.

    Comment


    • #17
      Tôi nghĩ nếu bạn cá chép đang gặp vấn đề với mạch delay, những giai đoạn tất yếu trước đó đã được hoàn thành? Dù với project nhỏ, chúng ta vẫn nên làm theo trình tự để giảm thiểu vấn đề.

      - Requirements / specs cho filter như thế nào? Có vài chỉ số được đưa ra, nhưng hình như thiếu sự liên kết giữa các chỉ số quan trọng. Dù với scalable design, chúng ta vẫn phải đặt ra performance bounds. Rất không nên nhảy vào coding trước khi establishing requirements / specs.

      - 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?

      - 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.

      Comment


      • #18
        Phần filter analysis / design không quá khó. Chỉ với vài dòng lệnh, bạn có thể generate filter coefficients và analyze frequency response như trong screen captures 01/02, so sánh 21/51/101 taps Hamming filters và 101 taps Boxcar filter.

        Trong screen captures 03 / 04, zero-padded sine wave được filter qua 21-tap Hamming filter. Zero-padded data được dùng để chúng ta có thể dễ dàng thấy được effect của filter.

        Phần FIR filter đơn giản chỉ có 3-4 dòng:

        Code:
        for in range (len(x)) :
          delay_buf[1:len(filter_coeffs)] = delay_buf[0:len(filter_coeffs)-1] 
          delay_buf[0] = x[i]
          y[i] = sum(delay_buf * filter_coeffs)
        Trong đó, phần delay buffer shifting là:
        Code:
          delay_buf[1:len(filter_coeffs)] = delay_buf[0:len(filter_coeffs)-1] 
          delay_buf[0] = x[i]
        Bạn có thể viết lại theo một form gần giống như HDL implementation hơn:
        Code:
          for j in range(len(filter_coeffs)-1,0,-1) :
            delay_buf[j] = delay_buf[j-1]
          delay_buf[0] = x[i]
        Khi chuyển phần for loop qua VHDL, bạn có thể dùng generate hoặc loop, chẳng hạn như:
        Code:
          DATA_SHIFT : for j in 20 downto 1 generate
            delay_buf(j) = delay_buf(j-1) when rising_edge(clk);
          end generate;
        Dòng lệnh delay_buf(j) = delay_buf(j-1) chỉ mang tính tượng trưng, delay_buf có thể là array of std_logic_vector hoặc std_logic_vector. Phần delay buffer, chúng ta chỉ cần vài dòng VHDL như bạn thấy. Bạn cũng có thể cần clock-enable nếu data không được transfer in mỗi clock cycle. Chúng ta đốt giai đoạn ở đây, nhưng bạn luôn phải establish requirements / specs / interface trước tiên.

        Tương tự như vậy, bạn cũng chuyển dần dòng lệnh dưới sang một dạng suitable cho HDL implementation hơn.
        Code:
          y[i] = sum(delay_buf * filter_coeffs)
        Bạn có thể chuyển sang dạng parallel/serial, hoặc semi-parallel tùy theo cấu trúc bạn muốn.


        Ở đây tôi đã dùng floating-point, nhưng đại khái là, bạn nên tạo ra một functional model trước, và sau đó chuyển thể từ từ sang dạng HDL suitable hơn. Như đã từng đề cập, phần viết VHDL nên là phần dễ dàng nhất. Chúc bạn thành công.
        Attached Files

        Comment


        • #19
          Nemesis21 có thể giúp dùng Matlab để tạo ra bộ coefficient, số tap, input và output vectors được kô? Thx
          Chúc một ngày vui vẻ
          Tony
          email : dientu_vip@yahoo.com

          Comment


          • #20
            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.

            Comment


            • #21
              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?

              .......

              Comment


              • #22
                ......

                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;
                .......

                Comment


                • #23
                  .......

                  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.

                  Comment


                  • #24
                    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.

                    Comment


                    • #25
                      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
                      Chúc một ngày vui vẻ
                      Tony
                      email : dientu_vip@yahoo.com

                      Comment


                      • #26
                        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à.

                        Comment


                        • #27
                          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

                          Comment


                          • #28
                            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

                            Comment


                            • #29
                              Nice !!! Đỉnh !!! ...

                              Comment


                              • #30
                                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/
                                Cupid1102 = Cupid độc nhất vô nhị

                                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