FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

FPGA開発日記 カテゴリ別インデックス

RISC-VにおけるRVWMOの仕様について読み直す

続きを読む

LiteXによるSoC環境構築を試行する (19. ILAを使ったデザインのデバッグ)

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によるSoC環境構築を試行する (18. ILAを使ったデザインのデバッグ)

LiteXのLiteScopeではどうにも細かい信号を取得することができないので、Xilinxの純正の波形のデバッグツールであるILA(Integrated Logic Analyzer)を使って内部の信号を観察する方法を見てみる。

ある程度信号波形を取れてきたので、必要な個所にデバッグ信号を入れて動かない場所を特定してみた。

問題になったのは、Vivadoで論理合成されるかどうかを確認せずにTemporaryで入れてしまった$countones()で論理が消えてしまっていたこと(これはVivadoにちゃんとエラーとして出力してほしい...)。 これを解決してとりあえずIssue Queueの問題は消えたが、まだLSUでメモリアクセスを行うとハングしてしまう。引き続き解析をしていこうと思う。

LiteXによるSoC環境構築を試行する (17. LiteXデザインにILAを挿入する方法の調査)

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によるSoC環境構築を試行する (16. 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によるSoC環境構築を試行する (15. Litescopeによるデバッグを試行する)

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環境構築を試行する (14. 自作CPUのLiteXのインテグレーション試行)

LiteXはオープンソースのSoC構成環境なのだが、これに対して自作CPUのインテグレーションを試行してみる。

既存のシミュレーション環境に対して、以下の定義を追加して、LiteX用に改造してみる。

  • CLINT / PLICのメモリマップを定義する。
    {
        "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()の部分で引っかかっているようだ。 書き込みをした後、割り込みが発生するようにしてほしい、という訳だ。 どうもこれが動作していない。