Catapultは、MentorからSiemensの製品となった高位合成ツールで、CやC++を入力として、ASIC設計、FPGA設計のネットリストまでを生成する。VDECに参加していれば、他のツール同様に利用することができる。FPGAのVitis_HLSは相当使いこんだし、日本の誇るCyberWorkBenchも少しは使った経験があるが、Catapultは、相当手ごわいツールで、忘れるといけないので、使い方をメモしておく。
ライブラリのディレクトリ構造
どのサーバにインストールするかによるが、現在2024.1_1をインストールする場合、/tool/Mentor/Catapult/2024.1_1/Mgc_homeというディレクトリの下に、バイナリやライブラリがすべて置かれる。pkgs/siflibの下にインタフェースのライブラリや、合成に必要なtechlibと呼ばれるライブラリが入っている。現状で利用可能なのはnangate45nmのもので、/tool/Mentor/Catapult/2024.1_1/Mgc_home/pkgs/siflibs/nangateの下にある。
Catapultはこのライブラリを指定しないとCやC++からRTLへの合成も行ってくれない(ChatGPTによるとやる方法もあるそうだが、何をやってもダメだった)。ちなみにnangate以外のライブラリは、saedとccs_fpgaで前者はSynopsysのStandard Cell Library、後者はFPGA関連らしいが詳しくは知らない
起動方法
binにpathを通しておくか、dlabならば起動コマンドasb0を使い
asb0 /tool/settings/csh-Catapult-2024.1_1 -shell -file goto.tcl
と打ち込む。 ここで-shellはGUIを使わないコマンド、-fileはtclのスクリプト(この場合goto.tcl)を指定する。-shellにするとGUIは一度上がってtclを実行して終了してしまう。GUIで何かやりたいときはこのオプションをつけないようにする。ちなみにGUIは相当使いにくいので、-shellを使わなくてもtclを打ち込んで使うのが普通らしい
tclコマンド
solution new -state initial
solution options defaults
solution options set /OnTheFly/VthAttributeType cell_lib
solution options set /Input/TargetPlatform x86_64
solution options set /Output/OutputVHDL false
solution options set /Output/GenerateCycleNetlist false
solution file add /data/design/usr/hunga/catapult/design.c -type C++
最初にslutionコマンドで、環境を設定する。ここではdesign.c (トップ関数はadd_top)を指定している。
directive set -DESIGN_GOAL area
directive set -SPECULATE true
directive set -MERGEABLE true
directive set -REGISTER_THRESHOLD 512
directive set -MEM_MAP_THRESHOLD 128
directive set -LOGIC_OPT false
directive set -FSM_ENCODING none
directive set -FSM_BINARY_ENCODING_THRESHOLD 64
directive set -REG_MAX_FANOUT 0
directive set -NO_X_ASSIGNMENTS true
directive set -SAFE_FSM false
directive set -REGISTER_SHARING_MAX_WIDTH_DIFFERENCE 8
directive set -REGISTER_SHARING_LIMIT 0
directive set -ASSIGN_OVERHEAD 0
directive set -TIMING_CHECKS true
directive set -MUXPATH true
directive set -REALLOC true
directive set -UNROLL no
directive set -IO_MODE super
directive set -CHAN_IO_PROTOCOL use_library
directive set -ARRAY_SIZE 4096
directive set -IDLE_SIGNAL {}
directive set -STALL_FLAG_SV off
directive set -STALL_FLAG false
directive set -TRANSACTION_DONE_SIGNAL true
directive set -DONE_FLAG {}
directive set -READY_FLAG {}
directive set -START_FLAG {}
directive set -TRANSACTION_SYNC ready
directive set -RESET_CLEARS_ALL_REGS use_library
directive set -CLOCK_OVERHEAD 20.000000
directive set -ON_THE_FLY_PROTOTYPING false
directive set -OPT_CONST_MULTS use_library
directive set -CHARACTERIZE_ROM false
directive set -PROTOTYPE_ROM true
directive set -ROM_THRESHOLD 512
directive set -CLUSTER_ADDTREE_IN_WIDTH_THRESHOLD 0
directive set -CLUSTER_ADDTREE_IN_COUNT_THRESHOLD 0
directive set -CLUSTER_OPT_CONSTANT_INPUTS true
directive set -CLUSTER_RTL_SYN false
directive set -CLUSTER_FAST_MODE false
directive set -CLUSTER_TYPE combinational
directive set -PROTOTYPING_ENGINE oasys
directive set -PIPELINE_RAMP_UP true
次に合成時のコマンドdirectiveをセットする。上記すべてが必要とは思えない。。。
go new
go analyze
solution design set add_top -top
go compile
solution library add nangate-45nm_beh -- -rtlsyntool DesignCompiler -vendor Nangate -technology 045nm
go libraries
directive set -CLOCKS {clk {-CLOCK_PERIOD 20.0 -CLOCK_EDGE rising -CLOCK_UNCERTAINTY 0.0 -CLOCK_HIGH_TIME 10.0 -RESET_SYNC_NAME rst_n -RESET_ASYNC_NAME arst_n -RESET_KIND sync -RESET_SYNC_ACTIVE low -RESET_ASYNC_ACTIVE low -ENABLE_ACTIVE high}}
go assembly
go extract
高位合成動作は、go analyse, go compile, go libraries, go assembly, go extractである。(ほかにgo scheduleもあるが使ってない)go extractがうまくいくと、Catapult_X/add_top.v1/というディレクトリの下にrtl.vが出力される。
高位合成
例に用いたC記述は恐ろしく単純な
#include <stdint.h>
int32_t add_top(int32_t a, int32_t b) {
return a + b;
}
である。これがrtl.vとなると以下の長大なものとなる。
module add_top_core_core_fsm (
clk, rst_n, fsm_output
);
input clk;
input rst_n;
output [1:0] fsm_output;
reg [1:0] fsm_output;
// FSM State Type Declaration for add_top_core_core_fsm_1
parameter
main_C_0 = 1'd0,
main_C_1 = 1'd1;
reg state_var;
reg state_var_NS;
// Interconnect Declarations for Component Instantiations
always @(*)
begin : add_top_core_core_fsm_1
case (state_var)
main_C_1 : begin
fsm_output = 2'b10;
state_var_NS = main_C_0;
end
// main_C_0
default : begin
fsm_output = 2'b01;
state_var_NS = main_C_1;
end
endcase
end
always @(posedge clk) begin
if ( ~ rst_n ) begin
state_var <= main_C_0;
end
else begin
state_var <= state_var_NS;
end
end
endmodule
このモジュールは値を設定するFSMになっている。次のadd_top_coreが全体のモジュールである。
module add_top_core (
clk, rst_n, a_rsc_dat, a_triosy_lz, b_rsc_dat, b_triosy_lz, return_rsc_dat, return_triosy_lz
);
input clk;
input rst_n;
input [31:0] a_rsc_dat;
output a_triosy_lz;
input [31:0] b_rsc_dat;
output b_triosy_lz;
output [31:0] return_rsc_dat;
output return_triosy_lz;
// Interconnect Declarations
wire [31:0] a_rsci_idat;
wire [31:0] b_rsci_idat;
reg [31:0] return_rsci_idat;
wire [32:0] nl_return_rsci_idat;
wire [1:0] fsm_output;
reg reg_return_triosy_obj_ld_cse;
// Interconnect Declarations for Component Instantiations
ccs_in_v1 #(.rscid(32'sd1),
.width(32'sd32)) a_rsci (
.dat(a_rsc_dat),
.idat(a_rsci_idat)
);
ccs_in_v1 #(.rscid(32'sd2),
.width(32'sd32)) b_rsci (
.dat(b_rsc_dat),
.idat(b_rsci_idat)
);
ccs_out_v1 #(.rscid(32'sd3),
.width(32'sd32)) return_rsci (
.idat(return_rsci_idat),
.dat(return_rsc_dat)
);
mgc_io_sync_v2 #(.valid(32'sd0)) a_triosy_obj (
.ld(reg_return_triosy_obj_ld_cse),
.lz(a_triosy_lz)
);
mgc_io_sync_v2 #(.valid(32'sd0)) b_triosy_obj (
.ld(reg_return_triosy_obj_ld_cse),
.lz(b_triosy_lz)
);
mgc_io_sync_v2 #(.valid(32'sd0)) return_triosy_obj (
.ld(reg_return_triosy_obj_ld_cse),
.lz(return_triosy_lz)
);
add_top_core_core_fsm add_top_core_core_fsm_inst (
.clk(clk),
.rst_n(rst_n),
.fsm_output(fsm_output)
);
always @(posedge clk) begin
if ( ~ rst_n ) begin
return_rsci_idat <= 32'b00000000000000000000000000000000;
end
else if ( ~ (fsm_output[1]) ) begin
return_rsci_idat <= nl_return_rsci_idat[31:0];
end
end
always @(posedge clk) begin
if ( ~ rst_n ) begin
reg_return_triosy_obj_ld_cse <= 1'b0;
end
else begin
reg_return_triosy_obj_ld_cse <= ~ (fsm_output[1]);
end
end
assign nl_return_rsci_idat = a_rsci_idat + b_rsci_idat;
endmodule
このモジュールは、上位から2つの値を順にセットする記述になっている。インタフェースを用いて、同期もしている。さらにFSMを使って2つの値を設定している。実際に計算が行われるのは、最後のalways文で、与えられた二つの値を加算して出力している。
module add_top (
clk, rst_n, a_rsc_dat, a_triosy_lz, b_rsc_dat, b_triosy_lz, return_rsc_dat, return_triosy_lz
);
input clk;
input rst_n;
input [31:0] a_rsc_dat;
output a_triosy_lz;
input [31:0] b_rsc_dat;
output b_triosy_lz;
output [31:0] return_rsc_dat;
output return_triosy_lz;
// Interconnect Declarations for Component Instantiations
add_top_core add_top_core_inst (
.clk(clk),
.rst_n(rst_n),
.a_rsc_dat(a_rsc_dat),
.a_triosy_lz(a_triosy_lz),
.b_rsc_dat(b_rsc_dat),
.b_triosy_lz(b_triosy_lz),
.return_rsc_dat(return_rsc_dat),
.return_triosy_lz(return_triosy_lz)
);
endmodule
最後のadd_topは、add_top_coreを呼ぶだけである。これでは計算が簡単な割に前後の道具立てが大変すぎるようだが、FSMの部分は外部からCPUで設定するAgile-Xのような枠組みでは、プログラムに置き換えることができる。なのでインタフェースをコントロールすれば使い物にならないことはないように思う。もうちょっと使い込まないと、、