[SystemC][HLS] ラプラシアンフィルタを SystemCで記述してみる。

この記事は HDL Advent Calendar 2013の 19日目の記事でした。

まだまだ参加者募集しております!!!


概要

皆さん @marsee1さんが書かれているブログ


FPGAの部屋


をご存知でしょうか?
その中に、Vivado HLSに関してのトライアルをした記事などがあります。

もう身近に高位合成が出来る時代になりましたね!


さて、今回のその記事の中からこちらを引用したいと思います。


SystemC版を作ってみよう

記事では C言語ですが、せっかくなので SystemC版を書いてみました。
※ただし、高位合成出来るかは試していません

  • laplacian_filter.h

  • #pragma once
    
    #include <stdlib.h> 
    #include <systemc.h>
    
    SC_MODULE( laplacian_filter ){
      sc_in_clk   clk;
      sc_in<bool> xreset;
      sc_in<int>  x0y0;
      sc_in<int>  x1y0;
      sc_in<int>  x2y0;
      sc_in<int>  x0y1;
      sc_in<int>  x1y1;
      sc_in<int>  x2y1;
      sc_in<int>  x0y2;
      sc_in<int>  x1y2;
      sc_in<int>  x2y2;
      sc_out<int> out;
      
      void process();
      
      int  _x0y0;
      int  _x1y0;
      int  _x2y0;
      int  _x0y1;
      int  _x1y1;
      int  _x2y1;
      int  _x0y2;
      int  _x1y2;
      int  _x2y2;
      int  _exe;
      
      SC_CTOR( laplacian_filter ):
         clk("clk")
        ,xreset("xreset")
        ,x0y0("x0y0")
        ,x1y0("x1y0") 
        ,x2y0("x2y0") 
        ,x0y1("x0y1") 
        ,x1y1("x1y1") 
        ,x2y1("x2y1") 
        ,x0y2("x0y2") 
        ,x1y2("x1y2") 
        ,x2y2("x2y2") 
        ,out ("out")
      {
        SC_CTHREAD( process, clk.pos() );
        async_reset_signal_is(xreset, false);
      }
    };
  • laplacian_filter.cpp

  • #include "laplacian_filter.h"
    
    void laplacian_filter::process() {
      out.write(0);
      wait();
    
      while (1) {
        { //input
          _x0y0 = x0y0.read();
          _x1y0 = x1y0.read();
          _x2y0 = x2y0.read();
          _x0y1 = x0y1.read();
          _x1y1 = x1y1.read();
          _x2y1 = x2y1.read();
          _x0y2 = x0y2.read();
          _x1y2 = x1y2.read();
          _x2y2 = x2y2.read();
        }
    
        { // execute
          _exe = abs(-_x0y0 -_x1y0 -_x2y0 -_x0y1 +8*(_x1y1) -_x2y1 -_x0y2 -_x1y2 -_x2y2);
        }
    
        { // output
          out.write(_exe);
        }
        wait();
      }
    }

さて、ここまでが機能部分のところですが、
検証環境はというとこちらになります。

  • sc_main.cpp

  • #include <systemc.h>
    #include <stdlib.h>
    
    #include "laplacian_filter.h"
    #include "testbench.h"
    
    int sc_main(int argc, char* argv[])
    {
    
      sc_clock clk("clk"10SC_NS);
      sc_signal<bool> xreset;
      sc_signal<int> x0y0;
      sc_signal<int> x1y0;
      sc_signal<int> x2y0;
      sc_signal<int> x0y1;
      sc_signal<int> x1y1;
      sc_signal<int> x2y1;
      sc_signal<int> x0y2;
      sc_signal<int> x1y2;
      sc_signal<int> x2y2;
      sc_signal<int> y;
    
      laplacian_filter dut("dut");
      testbench tb("tb");
    
      dut.clk(clk);
      dut.xreset(xreset);
      dut.x0y0(x0y0);
      dut.x1y0(x1y0);
      dut.x2y0(x2y0);
      dut.x0y1(x0y1);
      dut.x1y1(x1y1);
      dut.x2y1(x2y1);
      dut.x0y2(x0y2);
      dut.x1y2(x1y2);
      dut.x2y2(x2y2);
      dut.out(y);
      
      tb.clk(clk);
      tb.xreset(xreset);
      tb.x0y0(x0y0);
      tb.x1y0(x1y0);
      tb.x2y0(x2y0);
      tb.x0y1(x0y1);
      tb.x1y1(x1y1);
      tb.x2y1(x2y1);
      tb.x0y2(x0y2);
      tb.x1y2(x1y2);
      tb.x2y2(x2y2);
      tb.y(y);
    
      xreset = 1;
      sc_start(1SC_NS);
      xreset = 0;
      sc_start(10SC_NS);
      xreset = 1;
      sc_start();
    
    }
  • testbench.h

  • #pragma once
    
    #include <stdlib.h> 
    #include <systemc.h>
    
    SC_MODULE( testbench ){
      sc_in_clk   clk;
      sc_in<bool> xreset;
      sc_in<int>  y;
      sc_out<int> x0y0;
      sc_out<int> x1y0;
      sc_out<int> x2y0;
      sc_out<int> x0y1;
      sc_out<int> x1y1;
      sc_out<int> x2y1;
      sc_out<int> x0y2;
      sc_out<int> x1y2;
      sc_out<int> x2y2;
    
      sc_event _e_comp;
      int      _data[3][3];
    
      void set_data();
      void process();
      void compere();
    
      SC_CTOR( testbench ):
         clk("clk")
        ,xreset("xreset")
        ,y ("y")
        ,x0y0("x0y0")
        ,x1y0("x1y0") 
        ,x2y0("x2y0") 
        ,x0y1("x0y1") 
        ,x1y1("x1y1") 
        ,x2y1("x2y1") 
        ,x0y2("x0y2") 
        ,x1y2("x1y2") 
        ,x2y2("x2y2") 
      {
        SC_THREAD( process );
          sensitive << clk.pos();
        SC_METHOD( compere );
          sensitive << _e_comp ;
          dont_initialize();
      }
    };
  • testbench.cpp

  • #include "testbench.h"
    
    void testbench::set_data() {
      _data[0][0] = 1;
      _data[1][0] = 1;
      _data[2][0] = 1;
      _data[0][1] = 1;
      _data[1][1] = 2;
      _data[2][1] = 1;
      _data[0][2] = 1;
      _data[1][2] = 1;
      _data[2][2] = 1;
    }
    
    void testbench::process() {
      wait();
    
      set_data();
    
      x0y0.write(_data[0][0]);
      x1y0.write(_data[1][0]);
      x2y0.write(_data[2][0]);
      x0y1.write(_data[0][1]);
      x1y1.write(_data[1][1]);
      x2y1.write(_data[2][1]);
      x0y2.write(_data[0][2]);
      x1y2.write(_data[1][2]);
      x2y2.write(_data[2][2]);
    
      wait(3);
      _e_comp.notify();
      wait(3);
      sc_stop();
    }
    
    void testbench::compere() {
    
      int hw_result, sw_result;
    
      hw_result = y.read();
      sw_result = abs(-_data[0][0] -_data[1][0] -_data[2][0]
                      -_data[0][1] + 8*(_data[1][1]) -_data[2][1]
                      -_data[0][2] -_data[1][2] -_data[2][2]);
    
      std::cout << "simulation time = " << sc_time_stamp() << std::endl;
      printf("HW result = %d\n", hw_result);
      printf("SW result = %d\n", sw_result);
    
      if (hw_result == sw_result){
        printf("Success SW and HW results match\n");
        // return(0);
      } else {
        printf("ERROR SW and HW results mismatch\n");
        // return(1);
      }
    }


まとめ

今回は単純に書き換え(AXIインターフェースは抜きにして信号で表現) を行っただけですが、設計対象および検証環境はまだまだ改善されるべきところがあります。
今後は、こちらをベースにして色々な設計スタイルや検証環境のTopicsについて
書いていきたいと思います。


※次回以降に乞うご期待。


書きたいと思っている内容については以下です。

  • Modular InterfaceやDesign Interface(p2p, AXI)について
  • 高位合成での合成
  • 汎用なデータ入力(検証環境)
  • ランダム検証(検証環境)

上記以外でリクエストあれば、ご連絡ください。

関連記事

コメントの投稿

非公開コメント

修正依頼

ごめんなさい。ラプラシアンフィルタのソースが間違ってまして、AXI Master の一部は直したんですが、AXI Lite Slave版はまあいいか?ということで直していませんでした。すみません。。。
具体的には、abs()だけだと-側が+に折り返されちゃうので、0にする処理を追加しました。あと、飽和演算もです。
修正お願いします。
<pre>// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1 8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
// return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2)); // 元の実装
y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
if (y<0)
y = 0;
else if (y>255)
y = 255;
return(y);
}</pre>
自分のブログはあとで直しておきます。
よろしくお願いします。

私のブログでは、8ビットに制限した値を引数として、与えているので、255で飽和演算していますが、そうでなければ飽和演算はいらないかもしれませんね。
charでなく、intで宣言しているのは、32ビット幅のAXI Lite バスになるのを意識しています。
ついでですが、intなので、内部的にcharに分けてunion で値を割り振ればもっと早くなると思います。IOが遅いです。
プロフィール

Kocha

Author:Kocha
なんでもチャレンジ!(^o^)/
E-mail
github:Kocha
イベントカレンダー

カレンダー
06 | 2017/07 | 08
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 - - - - -
カテゴリ
OVP (4)
最新記事
最新コメント
アーカイブ
リンク
Twitter
アクセス人数