RGB2YUV回路(BCA)
前回TLMで作成したRGB2YUV回路をBCAに変更する。
変更概要
- いくつかの記述はSystemC2.3.3では使用不可になっている。これはCQ出版の正誤表に記載有
- tbとdutで画像データをやり取りするチャネルについて、TLMではsc_fifoを使用していたがBCAではsc_signalでデータ部とハンドシェイク信号(ready,valid)を明示的に使用する
- リセット信号の追加
- コンストラクタのプロセス宣言部分にreset_signal_is(信号名, 極性);で同期リセット信号を明示
- これの有無による違いがよくわからない。高位合成向け記述?
- async_reset_signal_is(信号名, 極性); で非同期リセット
- dutの端子(Bus I/F)部分はサイクル精度で記述
- VCDダンプ記述の追加
- TLMに対して、BCAと同様のVCDダンプ記述を入れてみたところ、エラーでsimが実行できなかった(コンパイルはできた)。別途検討
$ ./run.x SystemC 2.3.3-Accellera --- Mar 8 2020 14:00:56 Copyright (c) 1996-2018 by all Contributors, ALL RIGHTS RESERVED Error: (E519) wait() is only allowed in SC_THREADs and SC_CTHREADs: in SC_METHODs use next_trigger() instead In file: ../../../src/sysc/kernel/sc_wait.cpp:94
コード変更箇所
rgb2yuv.h
- TLMではsc_fifo_in, sc_fifo_outを使用して、データ入出力ポートを定義していたが、sc_in, sc_outに変更。ハンドシェイク用のready, validを明示的に追加
- 初期化関数、データ入出力関数を追加
- SC_THREADから合成可能なSC_CTHREADに変更。リセット信号を関連付け
- reset信号記述については、後ほど確認する
SC_MODULE(rgb2yuv){ //ports sc_in < bool > clock; sc_in < bool > reset; // reset信号を追加 sc_in < rgb8_t > offset_in; // チャネルをsc_fifo_in -> sc_inに変更 sc_in < rgb8_t > rgb_in; // チャネルをsc_fifo_in -> sc_inに変更 sc_out < bool > in_ready; // ハンドシェイク信号を追加 sc_in < bool > in_valid; // ハンドシェイク信号を追加 sc_out < yuv8_t > yuv_out; // チャネルをsc_fifo_out -> sc_outに変更 sc_in < bool > out_ready; // ハンドシェイク信号を追加 sc_out < bool > out_valid; // ハンドシェイク信号を追加 // member functions void initialize(); // 初期化関数を追加 rgb8_t get(); // データ入力関数を追加 void put(yuv8_t &yuv); // データ出力関数を追加 void thread0(); yuv8_t calc_rgb2yuv(rgb8_t& rgb); // member variables rgb8_t rgb; yuv8_t yuv; // constructor SC_CTOR(rgb2yuv) { SC_CTHREAD(thread0, clock.pos()); // SC_THREAD -> SC_CTHREADに変更 // watching(reset.delayed() == 0); // SystemC2.3.3では使用不可 reset_signal_is(reset, false); // reset定義 } };
rgb2yuv.cpp
- thread0()にwait()を挿入して、1cycle目でinitialize()をコール、2cycle目でoffsetの読み込み、3cycle目で変換処理をするようにしている
- while文の中にはwait()がないので、1cycleで処理しようとする。ただし、get(), put()関数内のハンドシェイク待ちでクロックを待つことになる
- TLMではclockの概念なく、1cycleでoffsetの読み込みと変換処理が実行されていると思われる
- dutから出力されるハンドシェイク信号(in_ready, out_valid)を0初期化するintialize()関数を新規追加
- ハンドシェイク信号を用いたデータリード関数get()を新規追加
- readyアサート→valid待ち→readyネゲート+データリード
- ハンドシェイク信号を用いたデータライト関数put()を新規追加
- ready待ち→validアサート+データライト→validネゲート
- 変換関数本体calc_rgb2yuv()はTLMと同じ
void rgb2yuv::thread0() { // internal variables rgb8_t offset; // call reset function initialize(); // 初期化関数の呼び出しを追加 wait(); // clock edgeを待つ offset = offset_in.read(); wait(); // clock edgeを待つ while(true) { // read data rgb = get(); // get関数を使用してデータをリードするように変更 // add offset rgb = offset + rgb; // translation yuv = calc_rgb2yuv(rgb); // output result put(yuv); // put関数を使用してデータをライトするように変更 } } // reset function void rgb2yuv::initialize() // in_ready, out_validを0初期化する関数を新規追加 { in_ready = 0; out_valid = 0; } // get data function rgb8_t rgb2yuv::get() // ハンドシェイク信号を用いたデータリード関数を新規追加 { in_ready = 1; // readyをアサート // wait_until(in_valid.delayed() == 1); // SystemC2.3.3では使用不可 do { wait(); } while ( !in_valid.read() ); // tbからのvalid信号がHになるまで待つ in_ready = 0; // readyをネゲート return rgb_in.read(); // データリードした結果を戻り値として返却 } // put data function void rgb2yuv::put(yuv8_t& yuv) // ハンドシェイク信号を用いたデータライト関数を新規追加 { // wait_until(out_ready.delayed() == 1); // SystemC2.3.3では使用不可 do { wait(); } while ( !out_ready.read() ); // readyがHになるまで待つ yuv_out.write(yuv); // データを出力 out_valid = 1; // validをアサート wait(); // 1cycle待ち out_valid = 0; // validネゲート } yuv8_t rgb2yuv::calc_rgb2yuv(rgb8_t& rgb) { yuv8_t yuv; // local variable yuv.y = ( rgb.r*77 + rgb.b*151 + rgb.b*29 ) / 256; yuv.u = ( rgb.r*(-43) - rgb.b*85 + rgb.b*128) / 256 + 128; yuv.v = ( rgb.r*128 - rgb.b*107 - rgb.b*21 ) / 256 + 128; // output internal variables cout << "rgb = (" << rgb << "), yuv = (" << yuv << ")" << endl; return yuv; }
tb.h
- ポート宣言をrgb2yuvと対になるように変更
SC_MODULE(tb) { // ports sc_in < bool > clock; sc_out < bool > reset; // reset信号を追加 sc_out < rgb8_t > offset_out; // チャネルをsc_fifo_out -> sc_outに変更 sc_out < rgb8_t > rgb_out; // チャネルをsc_fifo_out -> sc_outに変更 sc_in < bool > out_ready; // ハンドシェイク信号を追加 sc_out < bool > out_valid; // ハンドシェイク信号を追加 sc_in < yuv8_t > yuv_in; // チャネルをsc_fifo_in -> sc_inに変更 sc_out < bool > in_ready; // ハンドシェイク信号を追加 sc_in < bool > in_valid; // ハンドシェイク信号を追加 // file pointers FILE *fi; FILE *fo; // member functions void source(); void sink(); // constructor SC_CTOR(tb) { // open and check file if ((fi = fopen(input_file, "r")) == NULL) { fprintf(stderr, "Can't open file : %s\n", input_file); sc_stop(); } if ((fo = fopen(output_file, "w")) == NULL) { fprintf(stderr, "Can't open file : %s\n", output_file); sc_stop(); } // processes SC_THREAD(source); sensitive << clock.pos(); SC_THREAD(sink); sensitive << clock.pos(); } // deconstructor ~tb() { fclose(fi); fclose(fo); } };
tb.cpp
- source()関数
- 初期化処理追加
- 2cycle期間resetアサート・validを0初期化、3cycle目でリセット解除
- ハンドシェイク信号を使用したデータ出力
- ready待ち→validアサート+データ出力→validネゲート
- 初期化処理追加
- sink()関数
- ハンドシェイク信号を使用したデータ読み込み
- readyアサート→valid待ち→データ読み込み+readyネゲート
- 実際にはreadyネゲート→次のreadyアサートの間でwait()がないため、readyは常時アサートされそう
- readyアサート→valid待ち→データ読み込み+readyネゲート
- ハンドシェイク信号を使用したデータ読み込み
void tb::source() { int data_r, data_g, data_b; rgb8_t rgb; // initialize reset = 0; // resetアサート out_valid = 0; // validを0初期化 wait(2); // 2cycle待ち reset = 1; // reset解除 // read from file fscanf(fi, "%d", &data_r); fscanf(fi, "%d", &data_g); fscanf(fi, "%d", &data_b); // assign to struct rgb.write(data_r, data_g, data_b); // output offset offset_out.write(rgb); wait(); // output RGB data for(int i=0; i<NUM; i++) { fscanf(fi, "%d", &data_r); fscanf(fi, "%d", &data_g); fscanf(fi, "%d", &data_b); rgb.write(data_r, data_g, data_b); // output data with handshaking // wait_until(out_ready.delayed() == 1); // SystemC2.3.3では使用不可 do { wait(); } while ( !out_ready.read() ); // ready待ち rgb_out.write(rgb); // rgbデータ出力 out_valid.write(1); // validアサート wait(); // 1cycle待ち out_valid.write(0); // validネゲート } } void tb::sink() { yuv8_t yuv; fprintf(fo, " y u v\n"); for(int i=0; i<NUM; i++) { in_ready = 1; // readyアサート // wait_until(in_valid.delayed() == 1); // SystemC2.3.3では使用不可 do { wait(); } while ( !in_valid.read() ); // valid待ち // read output data yuv = yuv_in.read(); // データ読み込み // output read data to file fprintf(fo,"%3d %3d %3d\n", (int)yuv.y, (int)yuv.u, (int)yuv.v); in_ready = 0; // readyネゲート } sc_stop(); }
main.cpp
- TLMではsc_fifo, を使用して、接続信号を定義していたが、sc_signalに変更。ハンドシェイク用のready, validを明示的に追加
- VCDダンプ記述を追加
int sc_main( int argc, char *argv[]) { // clock sc_clock clock("clock", 10.0, SC_NS); // conecting wires sc_signal < bool > reset; // reset信号新規追加 sc_signal < bool > rgb_ready; // ハンドシェイク信号を新規追加 sc_signal < bool > rgb_valid; // ハンドシェイク信号を新規追加 sc_signal < bool > yuv_ready; // ハンドシェイク信号を新規追加 sc_signal < bool > yuv_valid; // ハンドシェイク信号を新規追加 sc_signal < rgb8_t > offset; // sc_fifo -> sc_signalに変更 sc_signal < rgb8_t > rgb; // sc_fifo -> sc_signalに変更 sc_signal < yuv8_t > yuv; // sc_fifo -> sc_signalに変更 // instanciate rgb2yuv i0("rgb2yuv"); tb t0("tb"); // conectivity i0.clock(clock); i0.reset(reset); i0.offset_in(offset); i0.rgb_in(rgb); i0.in_ready(rgb_ready); i0.in_valid(rgb_valid); i0.yuv_out(yuv); i0.out_ready(yuv_ready); i0.out_valid(yuv_valid); t0.clock(clock); t0.reset(reset); t0.offset_out(offset); t0.rgb_out(rgb); t0.out_ready(rgb_ready); t0.out_valid(rgb_valid); t0.yuv_in(yuv); t0.in_ready(yuv_ready); t0.in_valid(yuv_valid); // dump VCD sc_trace_file *fp; fp = sc_create_vcd_trace_file("rgb2yuv"); sc_trace(fp, clock, "clock"); sc_trace(fp, reset, "reset"); sc_trace(fp, offset, "offset"); sc_trace(fp, rgb_ready, "rgb_ready"); sc_trace(fp, rgb_valid, "rgb_valid"); sc_trace(fp, rgb, "rgb"); sc_trace(fp, yuv_ready, "yuv_ready"); sc_trace(fp, yuv_valid, "yuv_valid"); sc_trace(fp, yuv, "yuv"); // start sim sc_start(); return 0; }