- VexRiscvの生成環境の構築
- VexRiscvコアの作成
- SoCの作成
- Verilatorによるsimulation
- SIM TOP階層(C++階層)の作成
- class BrieyWorkspace
- RAMデータ(命令コード)の読み込み
- makefileの代わりにsim実行スクリプトを作成します
- JTAGデバッグ
- 参考にしたサイト
VexRiscvにはOpenOCDを用いて、RTL simulationでJTAGデバッグが比較的簡単に行う仕組みが用意されています。 その利用方法をここにとどめておきます。
VexRiscvの生成環境の構築
本筋ではありませんが、簡単に記載しておきます。 wslのubuntuにて構築しました。 まずは、以下のVexRiscvのgithubからリポジトリを持ってきます。
git clone https://github.com/SpinalHDL/VexRiscv.git
READMEのDependenciesに従い必要なツールのインストールを行います。
JAVA, SBT, Verilatorをインストールします。
Verilatorは現在ver5系が取得できますが、READMEに記載のv4.216でなくても良いですが、ver4系を取得するのが無難です。
自分が試した時はver5系だとvexriscvコアがうまく動作しませんでした。(そのうち改善されるかもですね)
ubuntu 20だとmakeすら不要でsudo apt install verilator
でよいと思われます。
VexRiscvコアの作成
先ほどgit cloneしたdirから一段下りて
cd VexRiscv
します。
今回JTAGデバッグしたいので、
src/main/scala/vexriscv/demo/
配下にあるものでJTAG付きのものを利用します。今回は
VexRiscvAxi4WithIntegratedJtag.scala
を使います。
いよいよRTLを作成します。
sbt "runMain vexriscv.demo.VexRiscvAxi4WithIntegratedJtag"
scala配下のdir区切りの/
を.
へ置き換えたものを指定すれば良いようです。
1分もかからずRTLが生成されます。以下のファイルが生成されます。
VexRiscvAxi4.v
cpu0.yaml
2つめのcpu0.yaml
はOpenOCDを実行する際に使います。
SoCの作成
さきほど作成したVexRiscvコアをインスタンスしてSoCを作成します。
boot時の命令を格納するaxi_ram
モジュールとuart
モジュールのみの最低限のSoCを作成しました。(必要に応じてバス変換とcrossbarスイッチも)
バス構成は、VexRiscvの命令バス、データバス2系統を1系統にまとめるのとアクセスアドレスに応じて、2系統のマスターポートへ振り分けるaxi_crossbar
モジュールを置いています。
SoCについての詳細はここでは省略します。参考にSoCトップのRTLだけ載せておきます。
各子モジュールは記載しません。
またwire宣言も省略しています。
作成したSoCの雰囲気だけを感じていただければと。
module soc_slideshow ( input wire io_asyncResetx, input wire io_mainClk, input wire io_jtag_tms, input wire io_jtag_tdi, output wire io_jtag_tdo, input wire io_jtag_tck, output wire io_uart_txd, input wire io_uart_rxd ); /* verilator lint_off PINMISSING */ vexriscv_wrap vexriscv_wrap ( .M0_AXI_ARADDR(M0_AXI_ARADDR), .M0_AXI_ARBURST(M0_AXI_ARBURST), .M0_AXI_ARCACHE(M0_AXI_ARCACHE), .M0_AXI_ARID(M0_AXI_ARID), .M0_AXI_ARLEN(M0_AXI_ARLEN), .M0_AXI_ARLOCK(M0_AXI_ARLOCK), .M0_AXI_ARPROT(M0_AXI_ARPROT), .M0_AXI_ARQOS(M0_AXI_ARQOS), .M0_AXI_ARREADY(M0_AXI_ARREADY), .M0_AXI_ARSIZE(M0_AXI_ARSIZE), .M0_AXI_ARVALID(M0_AXI_ARVALID), .M0_AXI_RDATA(M0_AXI_RDATA), .M0_AXI_RID(M0_AXI_RID), .M0_AXI_RLAST(M0_AXI_RLAST), .M0_AXI_RREADY(M0_AXI_RREADY), .M0_AXI_RRESP(M0_AXI_RRESP), .M0_AXI_RUSER(1'b0), .M0_AXI_RVALID(M0_AXI_RVALID), .M1_AXI_ARADDR(M1_AXI_ARADDR), .M1_AXI_ARBURST(M1_AXI_ARBURST), .M1_AXI_ARCACHE(M1_AXI_ARCACHE), .M1_AXI_ARID(M1_AXI_ARID), .M1_AXI_ARLEN(M1_AXI_ARLEN), .M1_AXI_ARLOCK(M1_AXI_ARLOCK), .M1_AXI_ARPROT(M1_AXI_ARPROT), .M1_AXI_ARQOS(M1_AXI_ARQOS), .M1_AXI_ARREADY(M1_AXI_ARREADY), .M1_AXI_ARSIZE(M1_AXI_ARSIZE), .M1_AXI_ARVALID(M1_AXI_ARVALID), .M1_AXI_AWADDR(M1_AXI_AWADDR), .M1_AXI_AWBURST(M1_AXI_AWBURST), .M1_AXI_AWCACHE(M1_AXI_AWCACHE), .M1_AXI_AWID(M1_AXI_AWID), .M1_AXI_AWLEN(M1_AXI_AWLEN), .M1_AXI_AWLOCK(M1_AXI_AWLOCK), .M1_AXI_AWPROT(M1_AXI_AWPROT), .M1_AXI_AWQOS(M1_AXI_AWQOS), .M1_AXI_AWREADY(M1_AXI_AWREADY), .M1_AXI_AWSIZE(M1_AXI_AWSIZE), .M1_AXI_AWVALID(M1_AXI_AWVALID), .M1_AXI_BID(M1_AXI_BID), .M1_AXI_BREADY(M1_AXI_BREADY), .M1_AXI_BRESP(M1_AXI_BRESP), .M1_AXI_BUSER(1'b0), .M1_AXI_BVALID(M1_AXI_BVALID), .M1_AXI_RDATA(M1_AXI_RDATA), .M1_AXI_RID(M1_AXI_RID), .M1_AXI_RLAST(M1_AXI_RLAST), .M1_AXI_RREADY(M1_AXI_RREADY), .M1_AXI_RRESP(M1_AXI_RRESP), .M1_AXI_RUSER(1'b0), .M1_AXI_RVALID(M1_AXI_RVALID), .M1_AXI_WDATA(M1_AXI_WDATA), .M1_AXI_WLAST(M1_AXI_WLAST), .M1_AXI_WREADY(M1_AXI_WREADY), .M1_AXI_WSTRB(M1_AXI_WSTRB), .M1_AXI_WVALID(M1_AXI_WVALID), .debug_resetOut(debug_resetOut), .clk(io_mainClk), .externalInterrupt(1'b0), .jtag_tck(io_jtag_tck), .jtag_tdi(io_jtag_tdi), .jtag_tdo(io_jtag_tdo), .jtag_tms(io_jtag_tms), .resetx(io_asyncResetx), .softwareInterrupt(1'b0), .timerInterrupt(1'b0) ); /* verilator lint_on PINMISSING */ axi_crossbar_wrap_2x2 # ( .S_ID_WIDTH(1) ) uaxi_crossbar_wrap_2x2 ( .clk(io_mainClk), .rst((~io_asyncResetx) | debug_resetOut), /* * AXI slave interface */ // S0 read only (IRAM) .s00_axi_awid(1'b0), .s00_axi_awaddr(32'h0), .s00_axi_awlen(8'h00), .s00_axi_awsize(3'b000), .s00_axi_awburst(2'b00), .s00_axi_awlock(1'b0), .s00_axi_awcache(4'h0), .s00_axi_awprot(3'b000), .s00_axi_awqos(4'h0), .s00_axi_awuser(1'b0), .s00_axi_awvalid(1'b0), .s00_axi_awready(/* open */), .s00_axi_wdata(32'h0), .s00_axi_wstrb(4'b0), .s00_axi_wlast(1'b0), .s00_axi_wuser(1'b0), .s00_axi_wvalid(1'b0), .s00_axi_wready(/* open*/), .s00_axi_bid(/* open */), .s00_axi_bresp(/* open */), .s00_axi_buser(/* open */), .s00_axi_bvalid(/* open */), .s00_axi_bready(1'b1), .s00_axi_arid(M0_AXI_ARID), .s00_axi_araddr(M0_AXI_ARADDR), .s00_axi_arlen(M0_AXI_ARLEN), .s00_axi_arsize(M0_AXI_ARSIZE), .s00_axi_arburst(M0_AXI_ARBURST), .s00_axi_arlock(M0_AXI_ARLOCK), .s00_axi_arcache(M0_AXI_ARCACHE), .s00_axi_arprot(M0_AXI_ARPROT), .s00_axi_arqos(M0_AXI_ARQOS), .s00_axi_aruser(M0_AXI_ARUSER), .s00_axi_arvalid(M0_AXI_ARVALID), .s00_axi_arready(M0_AXI_ARREADY), .s00_axi_rid(M0_AXI_RID), .s00_axi_rdata(M0_AXI_RDATA), .s00_axi_rresp(M0_AXI_RRESP), .s00_axi_rlast(M0_AXI_RLAST), .s00_axi_ruser(M0_AXI_RUSER), .s00_axi_rvalid(M0_AXI_RVALID), .s00_axi_rready(M0_AXI_RREADY), .s01_axi_awid(M1_AXI_AWID), .s01_axi_awaddr(M1_AXI_AWADDR), .s01_axi_awlen(M1_AXI_AWLEN), .s01_axi_awsize(M1_AXI_AWSIZE), .s01_axi_awburst(M1_AXI_AWBURST), .s01_axi_awlock(M1_AXI_AWLOCK), .s01_axi_awcache(M1_AXI_AWCACHE), .s01_axi_awprot(M1_AXI_AWPROT), .s01_axi_awqos(M1_AXI_AWQOS), .s01_axi_awuser(M1_AXI_AWUSER), .s01_axi_awvalid(M1_AXI_AWVALID), .s01_axi_awready(M1_AXI_AWREADY), .s01_axi_wdata(M1_AXI_WDATA), .s01_axi_wstrb(M1_AXI_WSTRB), .s01_axi_wlast(M1_AXI_WLAST), .s01_axi_wuser(M1_AXI_WUSER), .s01_axi_wvalid(M1_AXI_WVALID), .s01_axi_wready(M1_AXI_WREADY), .s01_axi_bid(M1_AXI_BID), .s01_axi_bresp(M1_AXI_BRESP), .s01_axi_buser(M1_AXI_BUSER), .s01_axi_bvalid(M1_AXI_BVALID), .s01_axi_bready(M1_AXI_BREADY), .s01_axi_arid(M1_AXI_ARID), .s01_axi_araddr(M1_AXI_ARADDR), .s01_axi_arlen(M1_AXI_ARLEN), .s01_axi_arsize(M1_AXI_ARSIZE), .s01_axi_arburst(M1_AXI_ARBURST), .s01_axi_arlock(M1_AXI_ARLOCK), .s01_axi_arcache(M1_AXI_ARCACHE), .s01_axi_arprot(M1_AXI_ARPROT), .s01_axi_arqos(M1_AXI_ARQOS), .s01_axi_aruser(M1_AXI_ARUSER), .s01_axi_arvalid(M1_AXI_ARVALID), .s01_axi_arready(M1_AXI_ARREADY), .s01_axi_rid(M1_AXI_RID), .s01_axi_rdata(M1_AXI_RDATA), .s01_axi_rresp(M1_AXI_RRESP), .s01_axi_rlast(M1_AXI_RLAST), .s01_axi_ruser(M1_AXI_RUSER), .s01_axi_rvalid(M1_AXI_RVALID), .s01_axi_rready(M1_AXI_RREADY), /* * AXI master interface */ .m00_axi_awid(crs_m00_axi_awid), .m00_axi_awaddr(crs_m00_axi_awaddr), .m00_axi_awlen(crs_m00_axi_awlen), .m00_axi_awsize(crs_m00_axi_awsize), .m00_axi_awburst(crs_m00_axi_awburst), .m00_axi_awlock(crs_m00_axi_awlock), .m00_axi_awcache(crs_m00_axi_awcache), .m00_axi_awprot(crs_m00_axi_awprot), .m00_axi_awqos(crs_m00_axi_awqos), .m00_axi_awregion(crs_m00_axi_awregion), .m00_axi_awuser(crs_m00_axi_awuser), .m00_axi_awvalid(crs_m00_axi_awvalid), .m00_axi_awready(crs_m00_axi_awready), .m00_axi_wdata(crs_m00_axi_wdata), .m00_axi_wstrb(crs_m00_axi_wstrb), .m00_axi_wlast(crs_m00_axi_wlast), .m00_axi_wuser(crs_m00_axi_wuser), .m00_axi_wvalid(crs_m00_axi_wvalid), .m00_axi_wready(crs_m00_axi_wready), .m00_axi_bid(crs_m00_axi_bid), .m00_axi_bresp(crs_m00_axi_bresp), .m00_axi_buser(crs_m00_axi_buser), .m00_axi_bvalid(crs_m00_axi_bvalid), .m00_axi_bready(crs_m00_axi_bready), .m00_axi_arid(crs_m00_axi_arid), .m00_axi_araddr(crs_m00_axi_araddr), .m00_axi_arlen(crs_m00_axi_arlen), .m00_axi_arsize(crs_m00_axi_arsize), .m00_axi_arburst(crs_m00_axi_arburst), .m00_axi_arlock(crs_m00_axi_arlock), .m00_axi_arcache(crs_m00_axi_arcache), .m00_axi_arprot(crs_m00_axi_arprot), .m00_axi_arqos(crs_m00_axi_arqos), .m00_axi_arregion(crs_m00_axi_arregion), .m00_axi_aruser(crs_m00_axi_aruser), .m00_axi_arvalid(crs_m00_axi_arvalid), .m00_axi_arready(crs_m00_axi_arready), .m00_axi_rid(crs_m00_axi_rid), .m00_axi_rdata(crs_m00_axi_rdata), .m00_axi_rresp(crs_m00_axi_rresp), .m00_axi_rlast(crs_m00_axi_rlast), .m00_axi_ruser(crs_m00_axi_ruser), .m00_axi_rvalid(crs_m00_axi_rvalid), .m00_axi_rready(crs_m00_axi_rready), .m01_axi_awid(crs_m01_axi_awid), .m01_axi_awaddr(crs_m01_axi_awaddr), .m01_axi_awlen(crs_m01_axi_awlen), .m01_axi_awsize(crs_m01_axi_awsize), .m01_axi_awburst(crs_m01_axi_awburst), .m01_axi_awlock(crs_m01_axi_awlock), .m01_axi_awcache(crs_m01_axi_awcache), .m01_axi_awprot(crs_m01_axi_awprot), .m01_axi_awqos(crs_m01_axi_awqos), .m01_axi_awregion(crs_m01_axi_awregion), .m01_axi_awuser(crs_m01_axi_awuser), .m01_axi_awvalid(crs_m01_axi_awvalid), .m01_axi_awready(crs_m01_axi_awready), .m01_axi_wdata(crs_m01_axi_wdata), .m01_axi_wstrb(crs_m01_axi_wstrb), .m01_axi_wlast(crs_m01_axi_wlast), .m01_axi_wuser(crs_m01_axi_wuser), .m01_axi_wvalid(crs_m01_axi_wvalid), .m01_axi_wready(crs_m01_axi_wready), .m01_axi_bid(crs_m01_axi_bid), .m01_axi_bresp(crs_m01_axi_bresp), .m01_axi_buser(crs_m01_axi_buser), .m01_axi_bvalid(crs_m01_axi_bvalid), .m01_axi_bready(crs_m01_axi_bready), .m01_axi_arid(crs_m01_axi_arid), .m01_axi_araddr(crs_m01_axi_araddr), .m01_axi_arlen(crs_m01_axi_arlen), .m01_axi_arsize(crs_m01_axi_arsize), .m01_axi_arburst(crs_m01_axi_arburst), .m01_axi_arlock(crs_m01_axi_arlock), .m01_axi_arcache(crs_m01_axi_arcache), .m01_axi_arprot(crs_m01_axi_arprot), .m01_axi_arqos(crs_m01_axi_arqos), .m01_axi_arregion(crs_m01_axi_arregion), .m01_axi_aruser(crs_m01_axi_aruser), .m01_axi_arvalid(crs_m01_axi_arvalid), .m01_axi_arready(crs_m01_axi_arready), .m01_axi_rid(crs_m01_axi_rid), .m01_axi_rdata(crs_m01_axi_rdata), .m01_axi_rresp(crs_m01_axi_rresp), .m01_axi_rlast(crs_m01_axi_rlast), .m01_axi_ruser(crs_m01_axi_ruser), .m01_axi_rvalid(crs_m01_axi_rvalid), .m01_axi_rready(crs_m01_axi_rready) ); axi_ram #( .ADDR_WIDTH(14), .ID_WIDTH(2) ) uaxi_ram ( .clk(io_mainClk), .rst(~io_asyncResetx), .s_axi_awid(crs_m00_axi_awid), .s_axi_awaddr(crs_m00_axi_awaddr[13:0]), .s_axi_awlen(crs_m00_axi_awlen), .s_axi_awsize(crs_m00_axi_awsize), .s_axi_awburst(crs_m00_axi_awburst), .s_axi_awlock(crs_m00_axi_awlock), .s_axi_awcache(crs_m00_axi_awcache), .s_axi_awprot(crs_m00_axi_awprot), .s_axi_awvalid(crs_m00_axi_awvalid), .s_axi_awready(crs_m00_axi_awready), .s_axi_wdata(crs_m00_axi_wdata), .s_axi_wstrb(crs_m00_axi_wstrb), .s_axi_wlast(crs_m00_axi_wlast), .s_axi_wvalid(crs_m00_axi_wvalid), .s_axi_wready(crs_m00_axi_wready), .s_axi_bid(crs_m00_axi_bid), .s_axi_bresp(crs_m00_axi_bresp), .s_axi_bvalid(crs_m00_axi_bvalid), .s_axi_bready(crs_m00_axi_bready), .s_axi_arid(crs_m00_axi_arid), .s_axi_araddr(crs_m00_axi_araddr[13:0]), .s_axi_arlen(crs_m00_axi_arlen), .s_axi_arsize(crs_m00_axi_arsize), .s_axi_arburst(crs_m00_axi_arburst), .s_axi_arlock(crs_m00_axi_arlock), .s_axi_arcache(crs_m00_axi_arcache), .s_axi_arprot(crs_m00_axi_arprot), .s_axi_arvalid(crs_m00_axi_arvalid), .s_axi_arready(crs_m00_axi_arready), .s_axi_rid(crs_m00_axi_rid), .s_axi_rdata(crs_m00_axi_rdata), .s_axi_rresp(crs_m00_axi_rresp), .s_axi_rlast(crs_m00_axi_rlast), .s_axi_rvalid(crs_m00_axi_rvalid), .s_axi_rready(crs_m00_axi_rready) ); axi2apb #( .AXI4_ID_WIDTH(2), .AXI4_USER_WIDTH(1) ) uaxi2apb ( .ACLK(io_mainClk), .ARESETn(io_asyncResetx), .AWID_i(crs_m01_axi_awid), .AWADDR_i(crs_m01_axi_awaddr), .AWLEN_i(crs_m01_axi_awlen), .AWSIZE_i(crs_m01_axi_awsize), .AWBURST_i(crs_m01_axi_awburst), .AWLOCK_i(crs_m01_axi_awlock), .AWCACHE_i(crs_m01_axi_awcache), .AWPROT_i(crs_m01_axi_awprot), .AWREGION_i(crs_m01_axi_awregion), .AWUSER_i(crs_m01_axi_awuser), .AWQOS_i(crs_m01_axi_awqos), .AWVALID_i(crs_m01_axi_awvalid), .AWREADY_o(crs_m01_axi_awready), .WDATA_i(crs_m01_axi_wdata), .WSTRB_i(crs_m01_axi_wstrb), .WLAST_i(crs_m01_axi_wlast), .WUSER_i(crs_m01_axi_wuser), .WVALID_i(crs_m01_axi_wvalid), .WREADY_o(crs_m01_axi_wready), .BID_o(crs_m01_axi_bid), .BRESP_o(crs_m01_axi_bresp), .BVALID_o(crs_m01_axi_bvalid), .BUSER_o(crs_m01_axi_buser), .BREADY_i(crs_m01_axi_bready), .ARID_i(crs_m01_axi_arid), .ARADDR_i(crs_m01_axi_araddr), .ARLEN_i(crs_m01_axi_arlen), .ARSIZE_i(crs_m01_axi_arsize), .ARBURST_i(crs_m01_axi_arburst), .ARLOCK_i(crs_m01_axi_arlock), .ARCACHE_i(crs_m01_axi_arcache), .ARPROT_i(crs_m01_axi_arprot), .ARREGION_i(crs_m01_axi_arregion), .ARUSER_i(crs_m01_axi_aruser), .ARQOS_i(crs_m01_axi_arqos), .ARVALID_i(crs_m01_axi_arvalid), .ARREADY_o(crs_m01_axi_arready), .RID_o(crs_m01_axi_rid), .RDATA_o(crs_m01_axi_rdata), .RRESP_o(crs_m01_axi_rresp), .RLAST_o(crs_m01_axi_rlast), .RUSER_o(crs_m01_axi_ruser), .RVALID_o(crs_m01_axi_rvalid), .RREADY_i(crs_m01_axi_rready), .PENABLE(apbrg_penable), .PWRITE(apbrg_pwrite), .PADDR(apbrg_paddr), .PSEL(apbrg_psel), .PWDATA(apbrg_pwdata), .PRDATA(apbrg_prdata), .PREADY(apbrg_pready), .PSLVERR(apbrg_pslverr) ); simpleuart_apb_wrap u_simpleuart_apb_wrap ( .PADDR(apbrg_paddr[4:0]), .PSEL(apbrg_psel), .PENABLE(apbrg_penable), .PREADY(apbrg_pready), .PWRITE(apbrg_pwrite), .PWDATA(apbrg_pwdata), .PRDATA(apbrg_prdata), .PSLVERR(apbrg_pslverr), .io_uart_txd(io_uart_txd), .io_uart_rxd(io_uart_rxd), .pclk(io_mainClk), .resetn(io_asyncResetx) ); endmodule
Verilatorによるsimulation
simulation環境作成に当たり、Briey soc環境を参考にしました。
なので、準備としてBriery socのSIMが流れるように環境を整えます。
READMEのBriey SoC
に記載があるように、以下の様に色々なモジュールをインストールします。
一見論理SIMに関係ある?というようなものもインストールしますが、本環境を使うと画像データをリアルタイムで表示出来たりしますので便利です。
sudo apt-get install build-essential xorg-dev libudev-dev libgl1-mesa-dev libglu1-mesa-dev libasound2-dev libpulse-dev libopenal-dev libogg-dev libvorbis-dev libaudiofile-dev libpng12-dev libfreetype6-dev libusb-dev libdbus-1-dev zlib1g-dev libdirectfb-dev libsdl2-dev
sbt "runMain vexriscv.demo.Briey"
でBriey socのRTLを生成すれば、
cd src/test/cpp/briey make clean run TRACE=yes
で波形ダンプ付きでSIMが流れます。
SIM TOP階層(C++階層)の作成
本dirにあるmakefile
を見るとSoCのRTL以外にmain.cpp
がverilatorに呼ばれているのが分かります。
該当行は以下です。
verilator -cc ../../../../Briey.v -CFLAGS -std=c++11 ${ADDCFLAGS} --gdbbt ${VERILATOR_ARGS} -Wno-WIDTH -Wno-UNOPTFLAT --x-assign unique --exe main.cpp
main.cpp
はテスト階層になります。
このmain.cpp
を参考にしてオリジナルSoC用のmain.cppを作成します。
先頭でincludeされているVBriey.h
は、verilog階層のtopモジュール名がBrieryであることに由来しています。
オリジナルSOCのtopモジュール名の先頭にV
を付けたファイル名の*.h
をインクルードするようにします。
最低限SIMするだけなら、デザイン由来のヘッダファイルはこれだけをインクルードすればSIM可能なようですが、
main.cppは他にもヘッダファイルをインクルードしていて、JTAGを動かすためのものやverilatorが持っているヘッダファイルをインクルードしています。
verilogデザイン由来のものは、階層名をファイル名の一部にした*.h
のファイル名になるので複雑です。インクルードするファイル名を間違えないようにするため、あらかじめ作成しておくと良いです。
verilator -cc verilogファイル名 ...
でコンパイルが出来るので、生成されたディレクトリobj_dir/
の中を見ると良いです。
-f でverilogのファイルリスト指定も出来ます。verilog simになれた方はこちらの形式の方がしっくりくるかと思います。
verilator -cc -f filelist
一部憶測も含んでしまいますが、main.cpp
を解説します。
#include "VBriey_VexRiscv.h"
VexRiscvコア階層のヘッダファイルです。コアは差し替えてモジュール名が変わっているのと、また階層も異なっているので、ファイル名は変わっているはずです。
obj_dir/
配下を見ると、Vxxx_VexRiscvAxi4.h
があるはずです。このファイルをインクルードするように変更すれば良いです。
本ヘッダファイルを見るとDESIGN SPECIFIC STATE
に続くstruct {}のところに端子名によく似た名前があります。verilatorがこのような名前に変換しているのだろうと思われます。
また、このVexRiscvコアのヘッダファイルをインクルードしている理由は、main.cpp
にclass VexRiscvTracer
というclass定義がありますが、
このclassの中でVexRiscvコアを呼び出し、命令トレースログなどをファイルへ出力する仕組みを実現しているようです。
ここで呼び出されているVexRiscvコアのモジュール名も変更します。
VBriey_VexRiscv *cpu; VexRiscvTracer(VBriey_VexRiscv *cpu){
の2行をご自分のSoC環境に合わせて書き換えます。
main.cpp
のヘッダファイルのincludeしている行へ戻ると、以降class定義がされています。
不要なものは、削除して構いません。
必要なclassを、以下に列挙します。
- 先に記載した
class VexRiscvTracer
class BrieyWorkspace
の2個を残せばよいです。
簡単にそれぞれのclassを説明すると
class SdramConfig
から続くのはsdram関連のclassです。特に利用していなければ削除してよいです。class Display
はsimulation中,リアルタイムで画像データを表示する仕組みです。使わないのであれば、削除します(残しておいても悪さはしません)class Vga
はSoCとの結線記述があるので削除します。
class BrieyWorkspace
class BrieyWorkspace
は重要です。clockの供給、リセット解除を行います。
public Workspace<VBriey>{
赤字のVBriey
はTOPモジュール名に"V"を頭に付けたものです。適宜変更します。
ClockDomain
に続く記述は、クロック名の定義になります。
*axiClk : クロック名
new ClockDomain()
でクロックの作成
()内で、左から順に、モジュールの端子との結線、リセット要因(NULL定義)、周期(ns)、発信開始時刻(ns)
class ClockDomain
は../common/framework.h
の中で定義されています。
ここで指定するクロックがUARTのクロックに利用しているのであれば正しい周期を設定してください。
シリアルコンソール出力させるために必要です。
(試していませんが)verilatorはサイクルシミュレータなので、2系統以上のクロックがある場合は、整数倍でないと動かないかもしれません。
AsyncReset
に続く記述はリセットです。先のクロックの要領で結線及びリセット解除タイミングを定義します。
このリセットは正論理です。私のデザインは負論理リセットだったので、負論理用のリセットclassを作成しました。
framework.h
の中で定義されているclass AsyncReset
を参考に作成します。
class AsyncResetx : public TimeProcess{ public: CData* resetx; uint32_t state; uint64_t duration; AsyncResetx(CData *resetx, uint64_t duration){ this->resetx = resetx; *resetx = 1; state = 0; this->duration = duration; schedule(0); } virtual void tick(){ switch(state){ case 0: *resetx = 0; state = 1; schedule(duration); break; case 1: *resetx = 1; state = 2; break; } } };
もっとうまい作り方がある気がしますが、力技で作ってしまいました。
Jtag, UartRx
は端子名を変更していなければそのまま使えます。
timeProcesses.push_back(xxx)は、おまじないでそのまま使います。
SdramConfig 以下は不要なので削除します。
こうしてできたmain.cpp
をsimlution実行dirへ置きます。
また、../common/*
のファイルをsim実行dirの./common
へコピーします。中身は以下になります。
framework.h jtag.h uart.h
framework.h
はclass AsyncResetx
の定義が追加されたものになります。
出来上がったmain.cppを以下に貼り付けました。
あくまで参考ですので、作成したSoCに合わせて変更する必要があります。
verilator sim sample main · GitHub
RAMデータ(命令コード)の読み込み
RAMモデルには$readmemhによる初期化が仕込んであるのですが、verilatorのsim開始時にうまく読み込めませんでした。
なので、$readmemhを実行するモジュールを作成しました。
module raminit; initial begin // IRAM初期化 $readmemh("iram_init_file.mem", soc_slideshow.uaxi_ram.mem); end endmodule
本モジュールはインスタンスしないで、読み込むのみです。その場合にTOPモジュールが複数あるとメッセージが出てverilatorが動かないので、それを避けるため
verilatorのオプションで-Wno-MULTITOP
を付加しています。
makefileの代わりにsim実行スクリプトを作成します
makefileを改造してもよいのですが、あまりmakefileに詳しくない為、simを実行するシェルスクリプトを作りました。
set ADDCFLAGS = "" verilator -cc -f ../soc_slideshow.f ../tb/raminit_for_verilator.v -CFLAGS -std=c++11 $ADDCFLAGS -CFLAGS -pthread -CFLAGS -lSDL2 -LDFLAGS -lSDL2 -CFLAGS "-O3" -CFLAGS -DVGA -CFLAGS -DTRACE_START=0 --gdbbt $WNO --x-assign unique --exe main.cpp make -j -C obj_dir/ -f Vsoc_slideshow.mk Vsoc_slideshow obj_dir/Vsoc_slideshow
- soc_slideshow.f はSoCのファイルリスト
- raminit_for_verilator.vは先に記載したRAMの初期化モジュール
fstファイル(波形ファイル)を出力する場合は
set ADDCFLAGS = "-CFLAGS -DTRACE --trace-fst "
とすればよいです。
出来上がったcshellスクリプトを以下に貼り付けます。
verilator 実行cshellスクリプト · GitHub
ダウンロード後chmod +x run_verilator.csh
で実行権を付加後、
./run_verilator.csh もしくは ./run_verilator.csh wave #波形付き
でSIMが流れます。
JTAGデバッグ
OpenOCDの準備
riscv用のopenocdを準備する。
適当なdirを準備し
上記サイトのREADMEに従いインストールする。
sudo apt-get install libtool automake libusb-1.0.0-dev texinfo libusb-dev libyaml-dev pkg-config git clone https://github.com/SpinalHDL/openocd_riscv.git cd openocd_riscv ./bootstrap ./configure --enable-ftdi --enable-dummy make
OpenOCDの実行
SIMを動かしている状態で以下を起動します。
src/openocd -f tcl/interface/jtag_tcp.cfg -c 'set BRIEY_CPU0_YAML ../VexRiscv/cpu0.yaml' -f tcl/target/briey.cfg
../VexRiscv/cpu0.yaml
はvexriscvコアのRTLを作成した際に生成されたファイルになります。
OpenOCDのログメッセージは以下
Open On-Chip Debugger 0.11.0+dev-04033-g058dfa50d (2024-04-24-00:03) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html DEPRECATED! use 'adapter driver' not 'interface' Info : only one transport option; autoselect 'jtag' ../VexRiscv/cpu0.yaml DEPRECATED! use 'adapter speed' not 'adapter_khz' DEPRECATED! use 'adapter srst delay' not 'adapter_nsrst_delay' Info : set servers polling period to 50ms Info : clock speed 4000 kHz Info : JTAG tap: fpga_spinal.bridge tap/device found: 0x10001fff (mfg: 0x7ff (), part: 0x0001, ver: 0x1) [fpga_spinal.cpu0] Target successfully examined. Info : starting gdb server for fpga_spinal.cpu0 on 3333 Info : Listening on port 3333 for gdb connections requesting target halt and executing a soft reset Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections
openocdを実行するとSIMを動かしているターミナルには以下の様なメッセージが出ます。
CONNECTION RESET CONNECTED
GDBの実行
riscvのツールチェーンに含まれているgdbを利用します。
elfファイルを準備して
riscv64-unknown-elf-gdb ./slideshow.elf
GNU gdb (GDB) 14.0.50.20230701-git Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv64-unknown-elf". Type "show configuration" for configuration details. For bug reporting instructions, please see: https://www.gnu.org/software/gdb/bugs/. Find the GDB manual and other documentation resources online at: http://www.gnu.org/software/gdb/documentation/. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./slideshow.elf... (gdb) target remote localhost:3333 Remote debugging using localhost:3333 crtStart () at ../src/crt.S:8 8 j crtInit (gdb) monitor reset halt JTAG tap: fpga_spinal.bridge tap/device found: 0x10001fff (mfg: 0x7ff (), part: 0x0001, ver: 0x1) (gdb) load Loading section .vector, size 0x144 lma 0x40000000 Loading section .memory, size 0x170 lma 0x40000144 Loading section .text.printchar, size 0x2c lma 0x400002b4 Loading section .text.prints, size 0xd8 lma 0x400002e0 Loading section .text.printi, size 0xfc lma 0x400003b8 Loading section .text.print, size 0x208 lma 0x400004b4 Loading section .text.printf, size 0x44 lma 0x400006bc Loading section .text.uart_init, size 0x4c lma 0x40000700 Loading section .text.simpleuart_outbyte, size 0x4c lma 0x4000074c Loading section .text.main, size 0x9c lma 0x40000798 Loading section .text.irqCallback, size 0x1c lma 0x40000834 Loading section .rodata, size 0x3d lma 0x40000850 Start address 0x40000000, load size 2189 Transfer rate: 2 KB/sec, 182 bytes/write. (gdb) c Continuing.
elfをloadして、continueすると、プログラムが最初から始まります。
SIMを実行している端末では
CONNECTION RESET
CONNECTED
SYSCLKFREQ=83333000
uart setting done
LED chika chika
UARTのログ(赤字)が出て、実行されているのが分かります。
VS codeからGDBの実行
debug対象の*.elf
があるdirでVScodeを立ち上げます。
code .
を実行
拡張機能Cortex-Debug
をインストールします。(riscvだけどmemory windowが使えたりと便利なので)
左ペインの虫さんマークをクリックし、"launch.jsonファイルを作成します"ボタンを押します。
デバッガ―の選択窓が出るのでCortex Debug
を選択します。
殆んど何も書かれていないひな形ファイルが開くので,以下の様にlaunch.jsonを作成もしくは追記します。
{ // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Riscv (Cortex Debug)", "cwd": "${workspaceFolder}", "executable": "${workspaceFolder}/slideshow.elf", "request": "launch", "type": "cortex-debug", "runToEntryPoint": "main", "servertype": "external", "gdbPath" : "/mnt/ext_storage/riscv/tools/rv64gc/bin/riscv64-unknown-elf-gdb", "gdbTarget": "localhost:3333", }, ] }
gdbPath
行はgdbのパスを書いてください。
executable
行は読み込むelfファイル
を指定します。
ctrl+sで保存し、
▶マークがある"Riscv (Cortex Debug)"をクリックします。
gdbが起動されてOpenOCDと繋がります。
デバッガの制御ボタンが出てきます。
main()
関数のすぐのところで停止します。
ソースコードの行番号あたりをクリックするとブレークポインタの設定が出来、プログラムの実行が停止します。
左ペインにはレジスタの状態が表示されています。
ソースコードの変数のところをホバーすると現在の値がバルーンで出たり、右クリック→"ウオッチに追加"で左ペインに変数を置いとけます。
などなどデバッグが可能です。
再生ボタン(|▶)を押すと、プログラムが再開されます。
以上になります。
論理SIMでjtagが使えるのは新鮮です。
論理SIMでは波形が出せるので、波形debugが最強ですが、波形を出せないような長時間SIMの場合とか役に立つ場面があるかもしれません。
参考にしたサイト
GitHub - SpinalHDL/VexRiscv: A FPGA friendly 32 bit RISC-V CPU implementation
GitHub - SpinalHDL/openocd_riscv: Spen's Official OpenOCD Mirror