※これはVisionグループOBのfukudaさんのページから持ってきました。
fukudaさんのwiki→http://www.am.ics.keio.ac.jp/members/fukuda/wiki/index.php?Kyoshow
Celoxica社*1のHandel-Cは、C言語をハードウェア向けに拡張した*2言語である。
開発環境およびコンパイラは、同社のDK3を利用する。
回路を意識せずに、処理したいことを記述することによってLSIを設計することができる。
また、合成の対象をFPGAに絞っているため、プロセス技術や細かいタイミングに起因する
トラブルがユーザの設計に影響を与える可能性が小さい。
Handel-Cで書かれたソースコードはVerilogHDLやVHDL,EDIFに変換される。
ただし、性能や回路面積についてはVerilogHDLやVHDLで直接記述したほうが
良い結果が得られる。
●DK3による開発フロー
- プロジェクトの作成
- ソースコードの記述
- デバッグ
「Build」→「Set Active Configuration」で「Debug」を指定する。
デバッグモードでビルド(F7)すると、次のようなデバッグ機能が利用可能になる。
(Verilog HDLやVHDLへ変換するときは、デバッグコードはコメントアウトする必要あり)
- ソースコードデバッグ
変数の値を表示しながら、ステップ実行を行ったり(F11)、
ブレークポイントの指定等を行うことができる。
- プラグインの利用
Handel-Cと外部の接続を定義するinterface文を使うと、
デバッグ時に入出力信号をVisual C++などで作成したDLLと結合することができるらしい。
- chanin/chanout
chanin/chanoutを利用して、外部ファイルから入力を行ったり
結果をファイルへ出力したりする。
(例)
> chanin input with {infile = "ina.txt"}; //ファイルから入力
> chanout output with {outfile = "out.txt"}; //ファイルへ出力
>
> input ? x;
> output ! 5;
または、
> chanout 8 stdout; //ディスプレイに表示させたい
> stdout ! 5;
- C/C++関数のコール
C/C++で書いた関数をそのまま使用できる。
その際、Handel-Cコードには下のような宣言が必要になる。
> extern "C" // 外部関数がC++なら、 extern "C++" になる
> {
> int printf(const char *fmt, ...); //標準ライブラリも利用できる
> unsigned short user_func(void); //自分で定義したのも利用できる
> }
- Logic estimation
「Build」→「Set Active Configuration」で「EDIF」を指定。
「Project」→「Settings」の「Linker」タブで、「Generate estimation info」にチェックを入れる。
これでビルドすると、EDIFフォルダ内にクリティカルパスやLUT数の情報がHTML形式で保存される。
- VerilogHDL(VHDL)へのコンパイル
「Build」→「Set Active Configuration」で「Verilog(VHDL)」を指定。
これでビルドすれば、Verilog(VHDL)フォルダ内に*.v(vhd)ファイルが生成される。
●Handel-Cのサンプル
とりあえずVerilogやEDIFに変換できるサンプルコードを下に置いておきます。
set family = XilinxVirtexII;
set part = "xc2v6000bf957-4";
set clock = external;
set reset = external;
void main (void)
{
unsigned int 4 DOut;
interface port_in(unsigned int 4 DIn1) DataIn1();
interface port_in(unsigned int 4 DIn2) DataIn2();
interface port_out() DataOut(DOut);
DOut = DataIn1.DIn1 + DataIn2.DIn2;
}
●特徴
- bit幅の指定
好きなbit幅を指定することができる。
> unsigned int 8 x;
キャストによってbit幅を変えるのは無理みたい。
また、次のようにbitを選択することができる。
(例)
> unsigned int 8 x;
> unsigned int 1 y;
> unsigned int 5 z;
>
> x = 0b01001001;
> y = x[4]; //0b0
> z = x[7:3]; //0b01001
- Handel-C特有の演算子
- <- (Take LSBs)
- \\ (Drop LSBs)
最下位の数bitだけ残したり、切り落としたりする。
(例)
> unsigned int 8 x;
> unsigned int 4 y;
> unsigned int 4 z;
>
> x = 0b11000111;
> y = x <- 4; // 0b0111
> z = x \\ 4; // 0b1100
- @ (Concatenation)
複数の変数を連結する。
(例)
> unsigned int 8 x;
> unsigned int 4 y;
> unsigned int 4 z;
>
> y = 0xC;
> z = 0x7;
> x = y @ z; // 0xC7
また、0をパディングするために用いられる。
(例)
> unsigned int 8 x;
> unsigned int 8 y;
> unsigned int 16 z;
>
> z = (0 @ x) * (0 @ y); // 上位8bitを0で埋める
- Timing
1代入文には、必ず1クロックと定められている。
delayは何もせずに1クロック消費し、リソースの競合を避けるために使われる。
また、チャネルを用いた通信にも1クロックかかる。
==や<、>=などの比較にはクロックはかからない。
- 並列処理
なにも指定しなければ、暗黙的にシーケンシャルな回路が生成される
(seq文が省略されていると見なされる)。
並列処理を行いたいときはpar文を用いる。
(例)
> par
> {
> x = 1;
> {
> delay;
> x = 2;
> }
> }
早く処理が終わった分岐部分は、最も遅い分岐の処理が終わるのを待つ。
- Channel
並列に処理している分岐間でのデータの受け渡しにはチャネルが用いられる。
> Channel ! Expression; // 送信
> Channel ? Variable; // 受信
送信側は受信側が準備できるまで待機する。逆に、受信側も送信側が準備できるまで待機する。
(例)
> par
> {
> {
> a = b;
> c = d;
> link ! x;
> }
> link ? y;
> }
この場合、タイミングは下表のようになる。
Cycle | Branch 1 | Branch 2 |
1 | a = b; | delay |
2 | c = d; | delay |
3 | Channel output | Channel input |
- 変数の共有について
変数のスコープは、下図のようになっている。
上のようなスコープだと、並列処理をしている複数のプロセスが
一つの変数を同時に変更するようなコードが書けてしまう(コンパイルが通ってしまう)。
こんなときはprialtを使うとよい。
(例)
> prialt {
> case chan1 ? y:
> //statement
> break;
> case chan2 ? y:
> //statement
> break;
> default: // なくてもよい
> //statement
> break;
> }
prialtは、最初に準備のできたチャネルを選択して通信を行う。
複数のチャネルが同時に準備できた場合は、上に書かれたものが優先的に選択される。
defaultなしのprialtの場合、どれかのチャネルが準備できるまで待機しつづける。
defaultありのprialtの場合、どのチャネルも準備できていないなら、defaultの
処理を行い、prialtから抜ける。
●ファンクションとマクロ
| Return value? | Typed return values? | Called by reference? | Shared hardware? |
Functions | Can have | YES | NO | YES |
Arrays of functions | Can have | YES | NO | YES |
Inline functions | Can have | YES | NO | NO |
Preprocessor macros | Can have | NO | YES | NO |
Macro expressions | Must have | NO | YES | NO |
Shared expression | Must have | NO | YES | YES |
Macro procedures | None | NO | YES | NO |
- ファンクションとマクロの例(変数を1.5倍する)
- Function
void f_sesqui (int *d, int s) // "shared" function without return
{
*d = s;
*d += ((*d) >> 1);
}
int rf_sesqui (int s) // "shared" function with return
{
int ret;
ret = s;
ret += (ret >> 1);
return ret;
}
- Array of function
void af_sesqui [5] (int *d, int s)
{
*d = s;
*d += ((*d) >> 1);
}
- Inline function
void inline if_sesqui (int *d, int s)
{
*d = s;
*d += ((*d) >> 1);
}
- Preprocessor macro
#define de_sesqui (s) ((s) + ((s) >> 1))
#define dp_sesqui (d,s) ((d) = (s) + ((s) >> 1))
- Macro expression
macro expr me_sesqui (s) = s + (s >> 1);
- Shared expression
shared expr se_sesqui (s) = s + (s >> 1);
- Macro procedure
macro proc mp_sesqui (d, s)
{
d = s;
d += (d >> 1);
}
- 呼び方
{
int 5 x, y;
x = 10;
f_sesqui (&y, x); // Function without return
y = rf_sesqui (x); // Function with return
af_sesqui[2] (&y, x); // Array of Function
if_sesqui (&y, x); // Inline Function
y = de_sesqui (x); // Preprocessor macro with return
dp_sesqui (y, x); // Preprocessor macro without return
y = me_sesqui (x); // Macro expression
y = se_sesqui (x); // Shared expression
mp_sesqui (y, x); // Macro procedure
}
- 注意点
次の記述は許されていない。
> y = f(g(x));
> y = f(x) + g(z);
また、再帰も使えない。
- ハードウェアの共有について
ファンクションを用いてハードウェアの共有を行えば、回路の面積を小さく抑えられる。
並列処理中に、異なるブロックで同じファンクションを使ってはならない。
その場合は関数の配列やインライン関数を用いる。→回路は増大する。
- クロックについて
Macro expression と Shared expression は、変数に代入された時に1クロックかかる。
それ以外は、処理内容に応じたクロック数が必要になる。
●雑記
- main関数について
main関数の引数と戻り値はvoidと決まっている。
main関数はクロックと関連付けられ、異なるクロックで動作するパーツが
他に存在する場合は、複数のmain関数が必要になる。
- familyとpart
ターゲットとするデバイスは何か
> set family = XilinxVirtexII;
> set part = "xc2v6000-4bf957";
partは、「Project」→「Setting」の「Chip」タブで見れる。
- clockとreset
クロックとリセットの信号を外から引っ張ってくる
> set clock = external;
> set reset = external;
ISEを通すには必要。
- interface
> signal unsigned char OutData;
> interface bus_in(unsigned InputData) InputPort();
> interface bus_out() OutputPort( OutData );
Verilogに落としたときに、この記述がモジュールの入出力になります。
これがないと中身のある回路ができないぽいです
●知っていると便利
- Macroの便利なオペレータ
- Select
- ifselect
- let ... in
●良いコードの書き方
●CとHandel-Cの比較メモ
- Statements
両方 | Cのみ | Handel-Cのみ |
switch | | par |
do...while | | delay |
while | | ? |
if...else | | ! |
for | | prialt |
break | | seq |
continue | | ifselect |
return | | |
goto | | |
assert | | |
- Type
両方 | Cのみ | Handel-Cのみ |
int | double | chan |
unsigned | float | ram |
char | union | rom |
long | | wom |
short | | mpram |
enum | | signal |
register | | chanin |
static | | chanout |
extern | | undefined |
struct | | interface |
volatile | | <> |
void | | inline |
const | | typeof |
auto | | |
signed | | |
typedef | | |
- Expression
両方 | Cのみ | Handel-Cのみ |
* (pointer indirection) | sizeof | select(...) |
& (address of) | | width(...) |
他、全ての演算子が使えます | | @ |
| | \\ |
| | <- |
| | [:] |
| | let...in |
(souichi)
(・ω・)627ノシ