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

この記事は [HDL Advent Calendar 2013](http://qiita.com/advent-calendar/2013/hdl)の 19日目の記事です。
まだまだ参加者募集しております！！！

### 概要

皆さん [@marsee1](https://twitter.com/marsee101)さんが書かれているブログ  

***[FPGAの部屋](http://marsee101.blog19.fc2.com/)***

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

- [Vivado HLSの現状について](http://marsee101.blog19.fc2.com/blog-category-105.html)

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

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

- [Vivado HLSでラプラシアン・フィルタ式のみをaxi lite slaveモジュールにする２](http://marsee101.blog19.fc2.com/blog-entry-2603.html)

### SystemC版を作ってみよう

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

- laplacian_filter.h

```cpp
#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

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

```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", 10, SC_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(1, SC_NS);
  xreset = 0;
  sc_start(10, SC_NS);
  xreset = 1;
  sc_start();

}
```

- testbench.h

```cpp
#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

```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);
  }
}
```

<br />

### まとめ

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


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


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

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

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