Help us understand the problem. What is going on with this article?

高位合成おぼえがき

More than 3 years have passed since last update.

この記事は Aizu Advent Calendarの6日目の記事です。
前記事は @stringamp さんの 畳み込み演算を用いた残響効果の理論と実装

はじめに

この記事では高位合成に関する説明と、Vivado HLSとSystemCを使った高位合成での簡単な実装例を示します。
前者に関しては寄せ集めの情報を自分なりにまとめたものになりますので、誤情報等を含んでいる可能性があります。
見つけた方はビシバシご指摘いただけると泣いて喜びます。

高位合成とはなんぞや

元来のデジタル回路は、Verilog,やVHDLに代表されるハードウェア記述言語(HDL)によってレジスタトランスファーレベル(RTL)で書かれるのが主でした。
550px-Full-adder.svg.png
RTLレベルでの加算器表現 Wikipedia - Full-adderより

しかし、これらのHDLは記述の抽象度が低く、相対的に記述量が増えてしまうため、近年の半導体微細化技術による回路規模の増大に伴い、より抽象度が高く、少ない記述量で回路を表現可能な方法が模索されてきました。

これを解決せんとする方法が高位合成であり、
C言語等の高位言語からHDLを生成、もしくはゲートレベルのネットリストまでを生成して回路を設計までを行う技術です。

高位合成ツール

現状幾つもの企業、団体が高位合成用のツールを提供しています。
有名なものとしては、FPGAを対象としたEDAツールを作成しているXilinxのVivado HLSと現IntelのIntel HLS Compiler、そしてASICを対象としたEDAツールを作成しているCadenceのC-to-Sillicon Compiler、Mentor GraphicsのCatapult HLS Platformがあります。
以下に各ツールの比較を示します。

Vivado HLS Intel HLS Compiler C-to-Silicon Compiler Catapult C
開発 Xilinx Intel (Altera) Cadence Mentor Graphics
対応言語 C/C++/SystemC ANSI C/C++/OpenCL SystemC ANSI C/C++/SystemC
価格 無償版あり 無償版あり 有償 有償

高位合成での実装例

次に高位合成での実装例として、画像処理に用いるアルゴリズムのCモデルとSystem Cモデル、Vivado HLSでの合成結果を示します。

対象アプリケーション

Prewitt-filter

Prewitt-filterは画像処理に用いられるエッジ検出のアルゴリズムです。
prewitt.png
詳しいアルゴリズムに関しては 物理のかぎしっぽ - Prewittフィルタでエッジ抽出する を参照

5x5ピクセルのイメージを対象としたPrewitt-filterのCコードを以下に示します。

prewitt-filter.c
void prewitt_filter(int *mem) {

    int i,j,k,l;
    int sumrw, sumrh;
    int mem_temp[HEIGHT*WIDTH];

    //
    for(i = 0; i < HEIGHT*WIDTH; i++) {
        mem_temp[i] = mem[i];
    }

    //prewitt filter
    for(i = 1; i < HEIGHT-1; i++) {
        for(j = 1; j < WIDTH-1; j++) {
            sumrw = sumrh = 0;
            for(k = -1; k <= 1; k++) {
                for(l = -1; l <= 1; l++) {
                    sumrw += l * mem_temp[(i+k)*WIDTH+j+l];
                    sumrh += k * mem_temp[(i+k)*WIDTH+j+l];
                }
            }
            mem[j+i*WIDTH] = mysqrt((sumrw * sumrw + sumrh * sumrh));
        }
    }
}

int mysqrt(unsigned int x){

  int a = 0, c = 0, y = 0, i = 0, t = x;
  while(t >>= 1){
    ++i;
  }
  for(i += i & 1; i >= 0; i -= 2){
    c = (y << 1 | 1) <= x >> i;
    a = a << 1 | c;
    y = y << 1 | c;
    x -= c * y << i;
    /* if(c){
      x -= y << i;
    } */
    y += c;
  }
  return a;
}

コード中、平方根の算出にsqrt関数を使っていないのは、一部高位合成ツールではmath.hが対応していないためです。(Vivado HLSは対応)

開発環境

Vivado Design Suite - HLx Edition 17.3

SystemCコード

上記のCコードから作成したSystemCコードとして、prewitt-filterを示したのモジュール(prewitt-filter.cpp)、使用するポートを示したヘッダ(prewitt-filter.h)、テストベンチ(sc_main.cpp)をそれぞれ用意しました。

prewitt-filter.cpp
#include "prewitt.h"

#define HEIGHT 5
#define WIDTH 5

void prewitt_filter(int *mem);

int mysqrt(unsigned int x){
  int a = 0, c = 0, y = 0, i = 0, t = x;
  while(t >>= 1){
    ++i;
    wait();
  }
  for(i += i & 1; i >= 0; i -= 2){
    c = (y << 1 | 1) <= x >> i;
    a = a << 1 | c;
    y = y << 1 | c;
    x -= c * y << i;
    /* if(c){
      x -= y << i;
    } */
    y += c;
    wait();
  }
  return a;
}


void prewitt_filter(int *mem) {

    int i,j,k,l;
    int sumrw, sumrh;
    int mem_temp[HEIGHT*WIDTH];

    for(i = 0; i < HEIGHT*WIDTH; i++) {
        mem_temp[i] = mem[i];
        wait();
    }

    //prewitt filter
    for(i = 1; i < HEIGHT-1; i++) {
        for(j = 1; j < WIDTH-1; j++) {
            sumrw = sumrh = 0;
            for(k = -1; k <= 1; k++) {
                for(l = -1; l <= 1; l++) {
                    sumrw += l * mem_temp[(i+k)*WIDTH+j+l];
                    sumrh += k * mem_temp[(i+k)*WIDTH+j+l];
                    wait();
                }
            }
            mem[j+i*WIDTH] = mysqrt((sumrw * sumrw + sumrh * sumrh));
            wait();
        }
    }
}

void prewitt::proc() {
    int i, j;
    int mem[25] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    //int test_data[25];

    out_data.write(0);
    wait();

    while(true) {
        while(start.read() == false) wait();
        for(i=0;i<25;i++) {
            mem[i] = in_data.read();
            wait();
        }

        prewitt_filter(mem);
        for(i = 0; i< 25; i++) {
            out_data.write(mem[i]);
            wait();
        }
    }

}

prewitt-filter.h
#include <systemc.h>

SC_MODULE(prewitt) {
    sc_in_clk clock;
    sc_in<bool> reset;
    sc_in<bool> start;

    sc_in<int> in_data;
    sc_out<int> out_data;

    void proc();

    SC_CTOR(prewitt) {
        SC_CTHREAD(proc, clock.pos());
        reset_signal_is(reset, true);
    }

#ifdef NC_SYSTEMC
  public:
    void ncsc_replace_names() {
        ncsc_replace_name(clock, "clock");
        ncsc_replace_name(reset, "reset");
        ncsc_replace_name(start, "start");
        ncsc_replace_name(in_data, "in_data");
        ncsc_replace_name(out_data, "out_data");
    }
#endif
};

#ifdef __CTOS__
SC_MODULE_EXPORT(prewitt);
#endif

sd_main.cpp
#include <systemc.h>
#include "prewitt.h"

#ifdef CTOS_RTL
#include "prewitt_ctos_wrapper.h"
#endif

int sc_main(int argc, char** argv){

  sc_clock clock("clock", 37.6, SC_NS, 0.5, 18.8, SC_NS, true);

  sc_signal<bool> reset;
  sc_signal<bool> start;
  sc_signal<int> in_data;
  sc_signal<int> out_data;

  const int test_data[25] = {0,1,0,1,0,0,1,2,3,4,0,1,0,1,0,0,1,2,3,4,0,1,0,1,0};

#ifdef CTOS_RTL
  prewitt_ctos_wrapper i0 ("prewitt", "_rtl", "", true);
#else
  prewitt i0 ("prewitt");
#endif

  i0.clock(clock);
  i0.reset(reset);
  i0.start(start);
  i0.in_data(in_data);
  i0.out_data(out_data);

  sc_trace_file *trace_f = sc_create_vcd_trace_file("prewitt");
  trace_f->set_time_unit(1.0,SC_PS);
  sc_trace(trace_f,clock, "clock");
  sc_trace(trace_f,reset, "reset");
  sc_trace(trace_f,start, "start");
  sc_trace(trace_f,in_data, "in_data");
  sc_trace(trace_f,out_data, "out_data");

  sc_start(5, SC_NS);
  reset = false;
  start = false;

  sc_start(37.6, SC_NS);
  reset = true;
  sc_start(37.6, SC_NS);
  reset = false;
  sc_start(18.8, SC_NS);
  start = true;

  int i;
  for(i = 0; i < 25; i++){
    in_data = test_data[i];
    sc_start(37.6, SC_NS);
  }
  start = false;
  sc_start(10000, SC_NS);
  sc_close_vcd_trace_file(trace_f);
  return 0;
}

結果

シミュレータの波形より、Cプログラムと結果が正しいことを確認しました
Payload Too Large

さいごに

高位合成自体はかなり前からあるのですが、実用レベルになってきたのは最近の話で、まだツールにしろ言語にしろまだ統一化がなされておらずどこから手を出せばいいか難しい状況です。現状、手っ取り早く使ってみたいのであれば、無償体験が出来てC/C++を扱えるVivado HLS、Intel HLS Compiler辺りが無難かと思います。
また、後半の実装例に関して、本当はデバイスへの実装までを行おうと思っていたのですが、研究室のサーバーメンテナンスに時間を取られてしまって断念しました。
論文提出数日前にデータを置いてるサーバーが起動しなくなるとか本当にあるんですね…
次書く機会があればそこまで対応したいです。

ここまで記事を読んでくださりありがとうございました。
アドベントカレンダー、次の方は@kyoto_cruiseさん、宜しくお願いします。

参照

FPGA開発日記 '高位合成言語Chiselイントロダクション(1)' -
http://msyksphinz.hatenablog.com/entry/advent20161219
qiita 'AltHDLを雑に紹介する。' -
https://qiita.com/tethys_seesaa/items/34c07f1a42e8bad14479
qiita 'SystemC高位合成のはじめ方' - https://qiita.com/kkumt93/items/2d8ab0170daee427f1c8

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away