LiteXのLiteScopeではどうにも細かい信号を取得することができないので、Xilinxの純正の波形のデバッグツールであるILA(Integrated Logic Analyzer)を使って内部の信号を観察する方法を見てみる。
一応、一歩ずつ問題を特定しているが、まだまだ動いていない。 分岐命令のオペランドがReadyにならずに、命令がデッドロックしている。 一応リネーム・マップを確認すると、Readyになっているのでこれがうまく伝搬していない。
これも要解析だな。
LiteXのLiteScopeではどうにも細かい信号を取得することができないので、Xilinxの純正の波形のデバッグツールであるILA(Integrated Logic Analyzer)を使って内部の信号を観察する方法を見てみる。
一応、一歩ずつ問題を特定しているが、まだまだ動いていない。 分岐命令のオペランドがReadyにならずに、命令がデッドロックしている。 一応リネーム・マップを確認すると、Readyになっているのでこれがうまく伝搬していない。
これも要解析だな。
LiteXのLiteScopeではどうにも細かい信号を取得することができないので、Xilinxの純正の波形のデバッグツールであるILA(Integrated Logic Analyzer)を使って内部の信号を観察する方法を見てみる。
もうちょっと細かい信号を取得するために、tclでILAを挿入する場所を細かく変えながらいろいろ動作を取得している。
以下のようなtclファイルを用意して、取得したい信号をILAに挿入する。
# BRU dispatch set bru_net_lists { "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_issue_unit/w_entry_valid.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_issue_unit/w_entry_ready.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_issue_unit/entry_loop.0..u_issue_entry/r_state.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_issue_unit/entry_loop.1..u_issue_entry/r_state.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_issue_unit/entry_loop.0..u_issue_entry/r_entry.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_issue_unit/entry_loop.1..u_issue_entry/r_entry.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_bru_pipe/w_ex0_issue.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_bru_pipe/r_ex1_issue.*" "mycpu_subsystem_axi_wrapper/u_mycpu_subsystem/u_tile/u_bru/u_bru_pipe/r_ex2_issue.*" } foreach net $bru_net_lists { set debug_nets [lsort -dictionary [get_nets -hierarchical -regexp $net]] puts $debug_nets set new_port [create_debug_port u_ila_0 probe] set_property port_width [llength $debug_nets] [get_debug_ports $new_port] set_property PROBE_TYPE DATA_AND_TRIGGER $new_port connect_debug_port $new_port $debug_nets }
必要な信号部分は、Vivadoでの最適化を抑制するためのアトリビュートを挿入する必要がある。
(* mark_debug="true" *)(* dont_touch="yes" *) scariv_bru_pkg::issue_entry_t w_ex0_issue;
論理合成を最初からやり直しているのでそれぞれのイタレーションは30分~1時間くらいかかってしまうが、仕方がない。 これで、少しずつ命令がデッドロックしてしまう原因を特定している。時間はかかるが、もう少しだ。
LiteXのLiteScopeではどうにも細かい信号を取得することができないので、Xilinxの純正の波形のデバッグツールであるILA(Integrated Logic Analyzer)を使って内部の信号を観察する方法を見てみる。
ある程度信号波形を取れてきたので、必要な個所にデバッグ信号を入れて動かない場所を特定してみた。
問題になったのは、Vivadoで論理合成されるかどうかを確認せずにTemporaryで入れてしまった$countones()
で論理が消えてしまっていたこと(これはVivadoにちゃんとエラーとして出力してほしい...)。
これを解決してとりあえずIssue Queueの問題は消えたが、まだLSUでメモリアクセスを行うとハングしてしまう。引き続き解析をしていこうと思う。
LiteXのLiteScopeではどうにも細かい信号を取得することができないので、Xilinxの純正の波形のデバッグツールであるILA(Integrated Logic Analyzer)を使って内部の信号を観察する方法を見てみる。
まず、ILA無しでLiteXでFPGAをビット・ストリームファイルを作成するのは、以下のコマンドを実行すればよいだろう。
python3 -m litex_boards.targets.digilent_nexys_video --cpu-type=vexriscv --build --output-dir vexriscv_build
この時、vexriscv_build/gateware
にVivadoを実行したtcl
ファイルなどのログが残る。これを改造して、ILAを挿入する方法を試行していきたいと思う。
digilent_nexys_video.tcl
# Create Project create_project -force -name digilent_nexys_video -part xc7a200t-sbg484-1 set_msg_config -id {Common 17-55} -new_severity {Warning} # Add project commands # Add Sources ...
基本的にこのコマンドを使えば、LiteXのFPGAビット・ファイルを再現できる。
vivado -source ./digilent_nexys_video.tcl -mode tcl
したがって、このdigilent_nexys_video.tcl
を改造して、ILAを挿入する。
簡単に言えば、以下のようなコマンドをファイル内に挿入する。
# ILA create_debug_core u_ila_0 ila set_property C_DATA_DEPTH 1024 [get_debug_cores u_ila_0] set_property C_TRIGIN_EN false [get_debug_cores u_ila_0] set_property C_TRIGOUT_EN false [get_debug_cores u_ila_0] set_property C_ADV_TRIGGER false [get_debug_cores u_ila_0] set_property C_INPUT_PIPE_STAGES 0 [get_debug_cores u_ila_0] set_property C_EN_STRG_QUAL false [get_debug_cores u_ila_0] set_property ALL_PROBE_SAME_MU true [get_debug_cores u_ila_0] set_property ALL_PROBE_SAME_MU_CNT 1 [get_debug_cores u_ila_0]
connect_debug_port u_ila_0/clk [get_nets [list sys_clk]] set_property port_width 1 [get_debug_ports u_ila_0/probe0] set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe0] connect_debug_port u_ila_0/probe0 [get_nets main_basesoc_dbus_ack]
set debug_nets [lsort -dictionary [get_nets -regexp main_basesoc_dbus_cyc.*]] set new_port [create_debug_port u_ila_0 probe] set_property port_width [llength $debug_nets] $new_port set_property PROBE_TYPE DATA_AND_TRIGGER $new_port connect_debug_port $new_port $debug_nets
ポイントは、get_nets
によってプローブしたい信号を取得し、それを新たに作成したILAのデバッグポートにひたすらつないでいくことだ。
ある程度テンプレート化して、debug_nets
で信号を取得したうえで数字順にソーティングするようにしている。
最後に、ltxファイルを出力してデバッグ用のファイルを書き出す。
# Bitstream generation write_bitstream -force digilent_nexys_video.bit write_cfgmem -force -format bin -interface spix4 -size 16 -loadbit "up 0x0 digilent_nexys_video.bit" -file digilent_nexys_video.bin write_debug_probes -force digilent_nexys_video.ltx
Vivado LabでFPGAに書き込みを行うと、ILAが立ち上がって、トリガによる実行が可能になる。 うーん、やっぱりLiteScopeより便利だ。
LiteXでどのように波形をダンプしてデバッグすればよいかを調査していたのだが、LiteXはブリッジを通じてホストに対して情報をダンプする機能が多く備わっているらしい。 これは便利だ。 ChipScopeのように内部の信号まで取り出したいのだが、このためにはデバッグ用の信号をCPUサブシステムのトップまでもっていかなければならないのだろうか? ちょっといろいろ試行している。
CPUサブシステムのトップに、デバッグ用の信号を下記のように引き上げている。
input logic [mycpu_lsu_pkg::L2_CMD_TAG_W+2-1:0] axi_if_r_id, `ifdef ILA_DEBUG output mycpu_pkg::cmt_id_t o_ila_debug_tile_rob_head_ptr, output mycpu_pkg::cmt_id_t o_ila_debug_tile_rob_tail_ptr, output logic o_ila_debug_tile_rob_entry_valid, output logic o_ila_debug_tile_rob_entry_cmt_id_msb, output mycpu_pkg::grp_id_t o_ila_debug_tile_rob_entry_grp_id, output mycpu_pkg::grp_id_t o_ila_debug_tile_rob_entry_done_grp_id, output mycpu_pkg::grp_id_t o_ila_debug_tile_rob_entry_dead, output mycpu_pkg::grp_id_t o_ila_debug_tile_rob_entry_fflags_update_valid, output mycpu_pkg::fflags_t o_ila_debug_tile_rob_entry_fflags_0,
これを、core.py
でバスに変換してデバッグ用に接続する。
core.py
ila_debug_layout = [ ("rob_head_ptr", 5), ("rob_tail_ptr", 5), ("rob_entry", [ ("valid", 1), ("cmt_id_msb", 1), ("grp_id", 2), ("done_grp_id", 2), ("dead", 1), ("fflags_update_valid", 1), ("fflags_0", 5), ("fflags_1", 5), ("int_inserted", 1), ]), ("rob_payload", [ ("disp0", [ ("pc_addr", 32), ("typ", 1), ("regidx", 5), ("rnid", 7), ("old_rnid", 7), ("inst", 32), ]), ("disp1", [ ("pc_addr", 32), ("typ", 1), ("regidx", 5), ("rnid", 7), ("old_rnid", 7), ("inst", 32), ]), ]), ] self.ila_debug_bus = ila_debug_bus = Record(ila_debug_layout) self.cpu_params.update ( o_o_ila_debug_tile_rob_head_ptr = ila_debug_bus.rob_head_ptr, o_o_ila_debug_tile_rob_tail_ptr = ila_debug_bus.rob_tail_ptr, o_o_ila_debug_tile_rob_entry_valid = ila_debug_bus.rob_entry.valid , o_o_ila_debug_tile_rob_entry_cmt_id_msb = ila_debug_bus.rob_entry.cmt_id_msb , o_o_ila_debug_tile_rob_entry_grp_id = ila_debug_bus.rob_entry.grp_id , o_o_ila_debug_tile_rob_entry_done_grp_id = ila_debug_bus.rob_entry.done_grp_id , o_o_ila_debug_tile_rob_entry_dead = ila_debug_bus.rob_entry.dead , o_o_ila_debug_tile_rob_entry_fflags_update_valid = ila_debug_bus.rob_entry.fflags_update_valid, o_o_ila_debug_tile_rob_entry_fflags_0 = ila_debug_bus.rob_entry.fflags_0 , o_o_ila_debug_tile_rob_entry_fflags_1 = ila_debug_bus.rob_entry.fflags_1 , o_o_ila_debug_tile_rob_entry_int_inserted = ila_debug_bus.rob_entry.int_inserted , o_o_ila_debug_tile_rob_payload_disp0_pc_addr = ila_debug_bus.rob_payload.disp0.pc_addr , o_o_ila_debug_tile_rob_payload_disp0_typ = ila_debug_bus.rob_payload.disp0.typ , o_o_ila_debug_tile_rob_payload_disp0_regidx = ila_debug_bus.rob_payload.disp0.regidx , o_o_ila_debug_tile_rob_payload_disp0_rnid = ila_debug_bus.rob_payload.disp0.rnid , o_o_ila_debug_tile_rob_payload_disp0_old_rnid = ila_debug_bus.rob_payload.disp0.old_rnid, o_o_ila_debug_tile_rob_payload_disp0_inst = ila_debug_bus.rob_payload.disp0.inst , o_o_ila_debug_tile_rob_payload_disp1_pc_addr = ila_debug_bus.rob_payload.disp1.pc_addr , o_o_ila_debug_tile_rob_payload_disp1_typ = ila_debug_bus.rob_payload.disp1.typ , o_o_ila_debug_tile_rob_payload_disp1_regidx = ila_debug_bus.rob_payload.disp1.regidx , o_o_ila_debug_tile_rob_payload_disp1_rnid = ila_debug_bus.rob_payload.disp1.rnid , o_o_ila_debug_tile_rob_payload_disp1_old_rnid = ila_debug_bus.rob_payload.disp1.old_rnid, o_o_ila_debug_tile_rob_payload_disp1_inst = ila_debug_bus.rob_payload.disp1.inst , )
この結果、シミュレーション用の信号が以下のように追加される。
$ litex_sim --cpu-type=mycpu--output-dir mycpu_build --with-etherbone --with-analyzer --soc-csv csr.csv
assign analyzer_mux_payload_data = {simsoc_ila_debug_bus_rob_payload_disp1_inst, simsoc_ila_debug_bus_rob_payload_disp1_old_rnid, simsoc_ila_debug_bus_rob_payload_disp1_rnid, simsoc_ila_debug_bus_rob_payload_disp1_regidx, simsoc_ila_debug_bus_rob_payload_disp1_typ, simsoc_ila_debug_bus_rob_payload_disp1_pc_addr, simsoc_ila_debug_bus_rob_payload_disp0_inst, simsoc_ila_debug_bus_rob_payload_disp0_old_rnid, simsoc_ila_debug_bus_rob_payload_disp0_rnid, simsoc_ila_debug_bus_rob_payload_disp0_regidx, simsoc_ila_debug_bus_rob_payload_disp0_typ, simsoc_ila_debug_bus_rob_payload_disp0_pc_addr, simsoc_ila_debug_bus_rob_entry_int_inserted, simsoc_ila_debug_bus_rob_entry_fflags_1, simsoc_ila_debug_bus_rob_entry_fflags_0, simsoc_ila_debug_bus_rob_entry_fflags_update_valid, simsoc_ila_debug_bus_rob_entry_dead, simsoc_ila_debug_bus_rob_entry_done_grp_id, simsoc_ila_debug_bus_rob_entry_grp_id, simsoc_ila_debug_bus_rob_entry_cmt_id_msb, simsoc_ila_debug_bus_rob_entry_valid, simsoc_ila_debug_bus_rob_tail_ptr, simsoc_ila_debug_bus_rob_head_ptr, simsoc_mmio_axi_r_payload_data, simsoc_mmio_axi_r_param_id, simsoc_mmio_axi_r_ready, simsoc_mmio_axi_r_valid, simsoc_mmio_axi_ar_param_id, simsoc_mmio_axi_ar_payload_addr, simsoc_mmio_axi_ar_ready, simsoc_mmio_axi_ar_valid, simsoc_mmio_axi_w_payload_data, simsoc_mmio_axi_w_param_id, simsoc_mmio_axi_w_ready, simsoc_mmio_axi_w_valid, simsoc_mmio_axi_aw_param_id, simsoc_mmio_axi_aw_payload_addr, simsoc_mmio_axi_aw_ready, simsoc_mmio_axi_aw_valid}; /* ... 途中省略 ... */ .axi_if_w_valid (simsoc_mmio_axi_w_valid), .o_ila_debug_tile_rob_entry_cmt_id_msb (simsoc_ila_debug_bus_rob_entry_cmt_id_msb), .o_ila_debug_tile_rob_entry_dead (simsoc_ila_debug_bus_rob_entry_dead), .o_ila_debug_tile_rob_entry_done_grp_id (simsoc_ila_debug_bus_rob_entry_done_grp_id), .o_ila_debug_tile_rob_entry_fflags_0 (simsoc_ila_debug_bus_rob_entry_fflags_0), .o_ila_debug_tile_rob_entry_fflags_1 (simsoc_ila_debug_bus_rob_entry_fflags_1), .o_ila_debug_tile_rob_entry_fflags_update_valid (simsoc_ila_debug_bus_rob_entry_fflags_update_valid), .o_ila_debug_tile_rob_entry_grp_id (simsoc_ila_debug_bus_rob_entry_grp_id),
LiteXでどのように波形をダンプしてデバッグすればよいかを調査していたのだが、LiteXはブリッジを通じてホストに対して情報をダンプする機能が多く備わっているらしい。
これは便利だ。
まずは、RTLシミュレーション環境:litex_sim
で試してみる。
各種情報を出力するために、litex_sim
に以下のオプションをつけてシミュレーションを開始する。
litex_sim --with-etherbone --with-analyzer --sim-debug --csr-csv csr.csv
--with-ethrbone
はEthernet経由でのデバッグ用ブリッジを設定する。
--with-analyzer
によって、ILA(Integrated Logic Analyzer)のような指定の波形を取得できるようになる。
--sim-debug
はILA向けのCSV情報を出力するのに必要。
--csr-csv
は、取得可能なCSRレジスタの一覧を出力する。
これにより、シミュレーションを行っているディレクトリに、`analyzer
次に、DUTとホストを接続するためのlitex_server
を立ち上げる。
litex_server --udp --udp-ip=192.168.1.51 --debug
[CommUDP] ip: 192.168.1.51 / port: 1234 / tcp port: 1234
--udp-ip
は正直よくわからない。とりあえずマニュアルに書いてあったのでこれをつかう。
では、まずはレジスタダンプをしてみよう。litex_cli
コマンドで、レジスタをダンプしてみよう。
$ litex_cli --regs
0xf0000000 : 0x00000000 analyzer_mux_value 0xf0000004 : 0x00000000 analyzer_trigger_enable ... 0xf000400c : 0x00000001 uart_ev_status 0xf0004010 : 0x00000000 uart_ev_pending 0xf0004014 : 0x00000003 uart_ev_enable 0xf0004018 : 0x00000001 uart_txempty 0xf000401c : 0x00000000 uart_rxfull
波形を取得することもできる。litescope_cli
を使用してみよう。
$ litescope_cli
No trigger, immediate capture. [running]... [uploading]... [====================>] 100% [writing to dump.vcd]...
一応これでVCDファイルが取得できる。GTKWaveにインポートすれば、こんな感じで、波形として表示できる。
トリガもできるようなので、cycにトリガをかけて波形を取得してみたいと思う。
$ litescope_cli -r simsoc_ibus_cyc # Rising Edge のCYC信号をトリガにして波形を取得する。
LiteXコンソール側で、何か文字を打ち込むと波形取得が始まる。
Exact: simsoc_ibus_cyc Rising edge: simsoc_ibus_cyc [running]... [uploading]... [====================>] 100% [writing to dump.vcd]...
これはなかなか便利だ。自作CPU環境でも使えるようにしていきたい。
LiteXはオープンソースのSoC構成環境なのだが、これに対して自作CPUのインテグレーションを試行してみる。
既存のシミュレーション環境に対して、以下の定義を追加して、LiteX用に改造してみる。
{ "base": "0x0200_0000", "size": "0x0001_0000", "attr": {"R": 1, "W": 1, "A": 1, "X": 0, "C": 0}, "comment":"CLINT" }, { "base": "0x0c00_0000", "size": "0x0400_0000", "attr": {"R": 1, "W": 1, "A": 1, "X": 0, "C": 0}, "comment":"PLIC" },
diff --git a/src/litex_defines.svh b/src/litex_defines.svh index d863281b..11461bea 100644 --- a/src/litex_defines.svh +++ b/src/litex_defines.svh @@ -1 +1,5 @@ `define RV64 +`define RV_AMO (1) +`define SIMULATION +`define SUBSYSTEM_TOP sim.scariv_subsystem_axi_wrapper.u_scariv_subsystem +`define NATIVE_DUMP
問題は、uart_write()
の部分で引っかかっているようだ。
書き込みをした後、割り込みが発生するようにしてほしい、という訳だ。
どうもこれが動作していない。