From 3a47f4b7a930c1a406bae667cec18fae481792f5 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Wed, 12 Oct 2022 12:18:31 +0000 Subject: [PATCH] Update pulp_riscv_dbg to pulp-platform/riscv-dbg@9155bfc Update code from upstream repository https://github.com/pulp- platform/riscv-dbg to revision 9155bfc3006589d255cbfc4aa4df83e1567acbf3 * Update CHANGELOG.md (bluew) * tb: Ignore comb warnings (bluew) * dmi_jtag: Update `dmi` `op` field based on DMI response (Andreas Kurth) * dmi_jtag: Add mechanism for capturing failed DM op (Andreas Kurth) * dmi_jtag: Set busy error only if no sticky error is set (Andreas Kurth) * tb: Fix make vcsify (bluew) * tb: Rewrite buggy openocd test script in Python (bluew) * tb/Makefile: Use tabs (bluew) * tb: Fix testbench build (bluew) * dm_mem: Clear state of hart upon ndmreset (Andreas Kurth) * dmi_jtag_tap: Bring all state to initial value in Test-Logic-Reset (Andreas Kurth) * dmi_jtag: Take DMI response into account for reads (Andreas Kurth) * dm_csrs: Return busy DMI response if SBA is busy (Andreas Kurth) * dm_csrs: Return busy DMI response if command is busy (Andreas Kurth) * dm_csrs: Put entire `dmi_resp_o` through FIFO (Andreas Kurth) * dmi_jtag_tap: Use generic tech cells (Luca Colagrande) * jtag_test: Add `read_dmi_exp_backoff()` and `sba_read_double()` functions (Luca Colagrande) * Fix r/s/t/u-reset commands (epsilon) * Fix for 64-bit accesses (Arjan Bink) * Alternative fix for pull request 27 (Arjan Bink) Signed-off-by: Andreas Kurth --- ...e-lowrisc-instead-of-PULP-primitives.patch | 28 +- hw/vendor/pulp_riscv_dbg.lock.hjson | 2 +- hw/vendor/pulp_riscv_dbg/.gitignore | 2 + hw/vendor/pulp_riscv_dbg/CHANGELOG.md | 12 + .../ci/veri-run-openocd-compliance.sh | 33 -- hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv | 104 ++++-- hw/vendor/pulp_riscv_dbg/src/dm_mem.sv | 39 +- hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv | 8 +- hw/vendor/pulp_riscv_dbg/src/dm_top.sv | 5 +- .../pulp_riscv_dbg/src/dmi_bscane_tap.sv | 80 +++++ hw/vendor/pulp_riscv_dbg/src/dmi_intf.sv | 59 +++ hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv | 31 +- hw/vendor/pulp_riscv_dbg/src/dmi_jtag_tap.sv | 9 +- hw/vendor/pulp_riscv_dbg/src/dmi_test.sv | 337 ++++++++++++++++++ hw/vendor/pulp_riscv_dbg/tb/Makefile | 80 +++-- .../pulp_riscv_dbg/tb/jtag_dmi/.gitignore | 3 + .../pulp_riscv_dbg/tb/jtag_dmi/jtag_intf.sv | 15 + .../pulp_riscv_dbg/tb/jtag_dmi/jtag_test.sv | 307 ++++++++++++++++ .../pulp_riscv_dbg/tb/jtag_dmi/run_vsim.sh | 28 ++ .../tb/jtag_dmi/run_vsim_xilinx.sh | 35 ++ .../pulp_riscv_dbg/tb/jtag_dmi/tb_jtag_dmi.sv | 304 ++++++++++++++++ .../tb/remote_bitbang/remote_bitbang.c | 40 ++- .../tb/remote_bitbang/remote_bitbang.h | 28 +- .../pulp_riscv_dbg/tb/veri-run-openocd.py | 56 +++ 24 files changed, 1487 insertions(+), 158 deletions(-) create mode 100644 hw/vendor/pulp_riscv_dbg/.gitignore delete mode 100755 hw/vendor/pulp_riscv_dbg/ci/veri-run-openocd-compliance.sh create mode 100644 hw/vendor/pulp_riscv_dbg/src/dmi_bscane_tap.sv create mode 100644 hw/vendor/pulp_riscv_dbg/src/dmi_intf.sv create mode 100644 hw/vendor/pulp_riscv_dbg/src/dmi_test.sv create mode 100644 hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/.gitignore create mode 100644 hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_intf.sv create mode 100644 hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_test.sv create mode 100755 hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim.sh create mode 100755 hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim_xilinx.sh create mode 100644 hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/tb_jtag_dmi.sv create mode 100755 hw/vendor/pulp_riscv_dbg/tb/veri-run-openocd.py diff --git a/hw/vendor/patches/pulp_riscv_dbg/0001-Use-lowrisc-instead-of-PULP-primitives.patch b/hw/vendor/patches/pulp_riscv_dbg/0001-Use-lowrisc-instead-of-PULP-primitives.patch index adbac35dac625..b0080fc047b8e 100644 --- a/hw/vendor/patches/pulp_riscv_dbg/0001-Use-lowrisc-instead-of-PULP-primitives.patch +++ b/hw/vendor/patches/pulp_riscv_dbg/0001-Use-lowrisc-instead-of-PULP-primitives.patch @@ -37,20 +37,20 @@ index 350c6c5..38d7b51 100644 - logic resp_queue_empty; - logic resp_queue_push; - logic resp_queue_pop; - logic [31:0] resp_queue_data; localparam dm::dm_csr_e DataEnd = dm::dm_csr_e'(dm::Data0 + {4'h0, dm::DataCount} - 8'h1); + localparam dm::dm_csr_e ProgBufEnd = dm::dm_csr_e'(dm::ProgBuf0 + {4'h0, dm::ProgBufSize} - 8'h1); @@ -181,9 +178,6 @@ module dm_csrs #( - - // a successful response returns zero - assign dmi_resp_o.resp = dm::DTM_SUCCESS; + + dm::dmi_resp_t resp_queue_inp; + - assign dmi_resp_valid_o = ~resp_queue_empty; - assign dmi_req_ready_o = ~resp_queue_full; - assign resp_queue_push = dmi_req_valid_i & dmi_req_ready_o; // SBA assign sbautoincrement_o = sbcs_q.sbautoincrement; assign sbreadonaddr_o = sbcs_q.sbreadonaddr; -@@ -551,28 +545,29 @@ module dm_csrs #( +@@ -579,28 +573,29 @@ module dm_csrs #( assign progbuf_o = progbuf_q; assign data_o = data_q; @@ -63,10 +63,10 @@ index 350c6c5..38d7b51 100644 + // response FIFO - fifo_v2 #( -- .dtype ( logic [31:0] ), -- .DEPTH ( 2 ) +- .dtype ( logic [$bits(dmi_resp_o)-1:0] ), +- .DEPTH ( 2 ) + prim_fifo_sync #( -+ .Width (32), ++ .Width ($bits(dmi_resp_o)), + .Pass (1'b0), + .Depth (2) ) i_fifo ( @@ -79,17 +79,17 @@ index 350c6c5..38d7b51 100644 - .empty_o ( resp_queue_empty ), - .alm_full_o ( ), - .alm_empty_o ( ), -- .data_i ( resp_queue_data ), +- .data_i ( resp_queue_inp ), - .push_i ( resp_queue_push ), -- .data_o ( dmi_resp_o.data ), +- .data_o ( dmi_resp_o ), - .pop_i ( resp_queue_pop ) + .clk_i ( clk_i ), + .rst_ni ( dmi_rst_ni ), // reset only when system is re-set + .clr_i ( 1'b0 ), -+ .wdata_i ( resp_queue_data ), ++ .wdata_i ( resp_queue_inp ), + .wvalid_i( dmi_req_valid_i ), + .wready_o( dmi_req_ready_o ), -+ .rdata_o ( dmi_resp_o.data ), ++ .rdata_o ( dmi_resp_o ), + .rvalid_o( dmi_resp_valid_o ), + .rready_i( dmi_resp_ready_i ), + .full_o ( ), // Unused @@ -287,12 +287,12 @@ index 0c188c0..a804da0 100644 // ---------------- - logic tck_n, tck_ni; - -- cluster_clock_inverter i_tck_inv ( +- tc_clk_inverter i_tck_inv ( - .clk_i ( tck_i ), - .clk_o ( tck_ni ) - ); - -- pulp_clock_mux2 i_dft_tck_mux ( +- tc_clk_mux2 i_dft_tck_mux ( - .clk0_i ( tck_ni ), - .clk1_i ( tck_i ), // bypass the inverted clock for testing - .clk_sel_i ( testmode_i ), diff --git a/hw/vendor/pulp_riscv_dbg.lock.hjson b/hw/vendor/pulp_riscv_dbg.lock.hjson index 3bd0cb026b982..f1b951291230d 100644 --- a/hw/vendor/pulp_riscv_dbg.lock.hjson +++ b/hw/vendor/pulp_riscv_dbg.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/pulp-platform/riscv-dbg - rev: 69be5ddc03ea1688c0eab47d6ed9d0e8725beda1 + rev: 9155bfc3006589d255cbfc4aa4df83e1567acbf3 } } diff --git a/hw/vendor/pulp_riscv_dbg/.gitignore b/hw/vendor/pulp_riscv_dbg/.gitignore new file mode 100644 index 0000000000000..c95e8c8fb3b50 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/.gitignore @@ -0,0 +1,2 @@ +.bender/ +Bender.lock diff --git a/hw/vendor/pulp_riscv_dbg/CHANGELOG.md b/hw/vendor/pulp_riscv_dbg/CHANGELOG.md index 35db898378116..77cf67f22faf8 100644 --- a/hw/vendor/pulp_riscv_dbg/CHANGELOG.md +++ b/hw/vendor/pulp_riscv_dbg/CHANGELOG.md @@ -5,6 +5,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.6.0] - 2022-10-11 +### Fixed +- Testbench build (#141, #142) +- remote_bitbang tb build for newer GCC versions (#133) [@epsilon537](https://github.com/noytzach) +- 32-bit access to abstract data (#27) [@Silabs-ArjanB](https://github.com/Silabs-ArjanB) +- `dm_mem`: Clear state of hart upon ndmreset (#140) [@andreaskurth](https://github.com/andreaskurth) +- `dmi_jtag_tap`: Bring all state to initial value in test-logic-reset (#139) [@andreaskurth](https://github.com/andreaskurth) +- Fix DMI response when command or SBA are busy (#138) [@andreaskurth](https://github.com/andreaskurth) + +### Changed +- Add expontential backoff to read_dmi in tb (#134) [@colluca](https://github.com/colluca) + ## [0.5.1] - 2022-04-12 ### Fixed - Fixed dmi_bscane_tap top-level signals diff --git a/hw/vendor/pulp_riscv_dbg/ci/veri-run-openocd-compliance.sh b/hw/vendor/pulp_riscv_dbg/ci/veri-run-openocd-compliance.sh deleted file mode 100755 index 09b589259dd6f..0000000000000 --- a/hw/vendor/pulp_riscv_dbg/ci/veri-run-openocd-compliance.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -set -e - -ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) - -if [ -z "${RISCV}" ] -then - echo "RISCV is empty" - exit 1 -fi - - -veri_out=$(mktemp) -openocd_out=openocd.log - -make -C "${ROOT}"/tb veri-run |& tee "${veri_out}"& -# record veri pid/pgid to kill it if it survives this script -veri_pid=$! -veri_pgid=$(ps -o pgid= ${veri_pid} | grep -o [0-9]*) - -# block until we get "Listening on port" so that we are safe to connect openocd -coproc grep -m 1 "Listening on port" -tail -f -n0 "${veri_out}" --pid "$COPROC_PID" >&"${COPROC[1]}" - -echo "Starting openocd" -"${RISCV}"/bin/openocd -f "${ROOT}"/tb/dm_compliance_test.cfg |& tee "${openocd_out}" - -if grep -q "ALL TESTS PASSED" "${openocd_out}"; then - exit 0 -fi -exit 1 - diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv b/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv index 38d7b51bf7d76..ee13a5029d1e7 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv @@ -87,7 +87,6 @@ module dm_csrs #( dm::dtm_op_e dtm_op; assign dtm_op = dm::dtm_op_e'(dmi_req_i.op); - logic [31:0] resp_queue_data; localparam dm::dm_csr_e DataEnd = dm::dm_csr_e'(dm::Data0 + {4'h0, dm::DataCount} - 8'h1); localparam dm::dm_csr_e ProgBufEnd = dm::dm_csr_e'(dm::ProgBuf0 + {4'h0, dm::ProgBufSize} - 8'h1); @@ -176,8 +175,8 @@ module dm_csrs #( logic [HartSelLen-1:0] selected_hart; - // a successful response returns zero - assign dmi_resp_o.resp = dm::DTM_SUCCESS; + dm::dmi_resp_t resp_queue_inp; + // SBA assign sbautoincrement_o = sbcs_q.sbautoincrement; assign sbreadonaddr_o = sbcs_q.sbreadonaddr; @@ -273,7 +272,8 @@ module dm_csrs #( sbaddr_d = 64'(sbaddress_i); sbdata_d = sbdata_q; - resp_queue_data = 32'h0; + resp_queue_inp.data = 32'h0; + resp_queue_inp.resp = dm::DTM_SUCCESS; cmd_valid_d = 1'b0; sbaddress_write_valid_o = 1'b0; sbdata_read_valid_o = 1'b0; @@ -288,62 +288,70 @@ module dm_csrs #( if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) begin unique case (dm_csr_addr) inside [(dm::Data0):DataEnd]: begin - resp_queue_data = data_q[$clog2(dm::DataCount)'(autoexecdata_idx)]; + resp_queue_inp.data = data_q[$clog2(dm::DataCount)'(autoexecdata_idx)]; if (!cmdbusy_i) begin // check whether we need to re-execute the command (just give a cmd_valid) cmd_valid_d = abstractauto_q.autoexecdata[autoexecdata_idx]; // An abstract command was executing while one of the data registers was read - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end - dm::DMControl: resp_queue_data = dmcontrol_q; - dm::DMStatus: resp_queue_data = dmstatus; - dm::Hartinfo: resp_queue_data = hartinfo_aligned[selected_hart]; - dm::AbstractCS: resp_queue_data = abstractcs; - dm::AbstractAuto: resp_queue_data = abstractauto_q; + dm::DMControl: resp_queue_inp.data = dmcontrol_q; + dm::DMStatus: resp_queue_inp.data = dmstatus; + dm::Hartinfo: resp_queue_inp.data = hartinfo_aligned[selected_hart]; + dm::AbstractCS: resp_queue_inp.data = abstractcs; + dm::AbstractAuto: resp_queue_inp.data = abstractauto_q; // command is read-only - dm::Command: resp_queue_data = '0; + dm::Command: resp_queue_inp.data = '0; [(dm::ProgBuf0):ProgBufEnd]: begin - resp_queue_data = progbuf_q[dmi_req_i.addr[$clog2(dm::ProgBufSize)-1:0]]; + resp_queue_inp.data = progbuf_q[dmi_req_i.addr[$clog2(dm::ProgBufSize)-1:0]]; if (!cmdbusy_i) begin // check whether we need to re-execute the command (just give a cmd_valid) // range of autoexecprogbuf is 31:16 cmd_valid_d = abstractauto_q.autoexecprogbuf[{1'b1, dmi_req_i.addr[3:0]}]; // An abstract command was executing while one of the progbuf registers was read - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end - dm::HaltSum0: resp_queue_data = haltsum0; - dm::HaltSum1: resp_queue_data = haltsum1; - dm::HaltSum2: resp_queue_data = haltsum2; - dm::HaltSum3: resp_queue_data = haltsum3; + dm::HaltSum0: resp_queue_inp.data = haltsum0; + dm::HaltSum1: resp_queue_inp.data = haltsum1; + dm::HaltSum2: resp_queue_inp.data = haltsum2; + dm::HaltSum3: resp_queue_inp.data = haltsum3; dm::SBCS: begin - resp_queue_data = sbcs_q; + resp_queue_inp.data = sbcs_q; end dm::SBAddress0: begin - resp_queue_data = sbaddr_q[31:0]; + resp_queue_inp.data = sbaddr_q[31:0]; end dm::SBAddress1: begin - resp_queue_data = sbaddr_q[63:32]; + resp_queue_inp.data = sbaddr_q[63:32]; end dm::SBData0: begin // access while the SBA was busy if (sbbusy_i || sbcs_q.sbbusyerror) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin sbdata_read_valid_o = (sbcs_q.sberror == '0); - resp_queue_data = sbdata_q[31:0]; + resp_queue_inp.data = sbdata_q[31:0]; end end dm::SBData1: begin // access while the SBA was busy if (sbbusy_i || sbcs_q.sbbusyerror) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin - resp_queue_data = sbdata_q[63:32]; + resp_queue_inp.data = sbdata_q[63:32]; end end default:; @@ -361,8 +369,11 @@ module dm_csrs #( // check whether we need to re-execute the command (just give a cmd_valid) cmd_valid_d = abstractauto_q.autoexecdata[autoexecdata_idx]; //An abstract command was executing while one of the data registers was written - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end end @@ -385,8 +396,11 @@ module dm_csrs #( // reads during abstract command execution are not allowed if (!cmdbusy_i) begin cmderr_d = dm::cmderr_e'(~a_abstractcs.cmderr & cmderr_q); - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end dm::Command: begin @@ -396,8 +410,11 @@ module dm_csrs #( command_d = dm::command_t'(dmi_req_i.data); // if there was an attempted to write during a busy execution // and the cmderror field is zero set the busy error - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end dm::AbstractAuto: begin @@ -406,8 +423,11 @@ module dm_csrs #( abstractauto_d = 32'h0; abstractauto_d.autoexecdata = 12'(dmi_req_i.data[dm::DataCount-1:0]); abstractauto_d.autoexecprogbuf = 16'(dmi_req_i.data[dm::ProgBufSize-1+16:16]); - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end [(dm::ProgBuf0):ProgBufEnd]: begin @@ -420,14 +440,18 @@ module dm_csrs #( // range of autoexecprogbuf is 31:16 cmd_valid_d = abstractauto_q.autoexecprogbuf[{1'b1, dmi_req_i.addr[3:0]}]; //An abstract command was executing while one of the progbuf registers was written - end else if (cmderr_q == dm::CmdErrNone) begin - cmderr_d = dm::CmdErrBusy; + end else begin + resp_queue_inp.resp = dm::DTM_BUSY; + if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end end end dm::SBCS: begin // access while the SBA was busy if (sbbusy_i) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin sbcs = dm::sbcs_t'(dmi_req_i.data); sbcs_d = sbcs; @@ -440,6 +464,7 @@ module dm_csrs #( // access while the SBA was busy if (sbbusy_i || sbcs_q.sbbusyerror) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin sbaddr_d[31:0] = dmi_req_i.data; sbaddress_write_valid_o = (sbcs_q.sberror == '0); @@ -449,6 +474,7 @@ module dm_csrs #( // access while the SBA was busy if (sbbusy_i || sbcs_q.sbbusyerror) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin sbaddr_d[63:32] = dmi_req_i.data; end @@ -457,6 +483,7 @@ module dm_csrs #( // access while the SBA was busy if (sbbusy_i || sbcs_q.sbbusyerror) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin sbdata_d[31:0] = dmi_req_i.data; sbdata_write_valid_o = (sbcs_q.sberror == '0); @@ -466,6 +493,7 @@ module dm_csrs #( // access while the SBA was busy if (sbbusy_i || sbcs_q.sbbusyerror) begin sbcs_d.sbbusyerror = 1'b1; + resp_queue_inp.resp = dm::DTM_BUSY; end else begin sbdata_d[63:32] = dmi_req_i.data; end @@ -552,17 +580,17 @@ module dm_csrs #( // response FIFO prim_fifo_sync #( - .Width (32), + .Width ($bits(dmi_resp_o)), .Pass (1'b0), .Depth (2) ) i_fifo ( .clk_i ( clk_i ), .rst_ni ( dmi_rst_ni ), // reset only when system is re-set .clr_i ( 1'b0 ), - .wdata_i ( resp_queue_data ), + .wdata_i ( resp_queue_inp ), .wvalid_i( dmi_req_valid_i ), .wready_o( dmi_req_ready_o ), - .rdata_o ( dmi_resp_o.data ), + .rdata_o ( dmi_resp_o ), .rvalid_o( dmi_resp_valid_o ), .rready_i( dmi_resp_ready_i ), .full_o ( ), // Unused diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv b/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv index 178259f6fdd38..c1cc0e87be8a3 100755 --- a/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv @@ -26,6 +26,7 @@ module dm_mem #( input logic rst_ni, // debug module reset output logic [NrHarts-1:0] debug_req_o, + input logic ndmreset_i, input logic [19:0] hartsel_i, // from Ctrl and Status register input logic [NrHarts-1:0] haltreq_i, @@ -201,6 +202,13 @@ module dm_mem #( cmderror_valid_o = 1'b1; cmderror_o = dm::CmdErrorException; end + + if (ndmreset_i) begin + // Clear state of hart and its control signals when it is being reset. + state_d = Idle; + go = 1'b0; + resume = 1'b0; + end end // word mux for 32bit and 64bit buses @@ -214,14 +222,13 @@ module dm_mem #( end // read/write logic - logic [63:0] data_bits; + logic [dm::DataCount-1:0][31:0] data_bits; logic [7:0][7:0] rdata; always_comb begin : p_rw_logic halted_d_aligned = NrHartsAligned'(halted_q); resuming_d_aligned = NrHartsAligned'(resuming_q); rdata_d = rdata_q; - // convert the data in bits representation data_bits = data_i; rdata = '0; @@ -258,9 +265,19 @@ module dm_mem #( // core can write data registers [DataBaseAddr:DataEndAddr]: begin data_valid_o = 1'b1; - for (int i = 0; i < $bits(be_i); i++) begin - if (be_i[i]) begin - data_bits[i*8+:8] = wdata_i[i*8+:8]; + for (int dc = 0; dc < dm::DataCount; dc++) begin + if ((addr_i[DbgAddressBits-1:2] - DataBaseAddr[DbgAddressBits-1:2]) == dc) begin + for (int i = 0; i < $bits(be_i); i++) begin + if (be_i[i]) begin + if (i>3) begin // for upper 32bit data write (only used for BusWidth == 64) + if ((dc+1) < dm::DataCount) begin // ensure we write to an implemented data register + data_bits[dc+1][(i-4)*8+:8] = wdata_i[i*8+:8]; + end + end else begin // for lower 32bit data write + data_bits[dc][i*8+:8] = wdata_i[i*8+:8]; + end + end + end end end end @@ -293,10 +310,8 @@ module dm_mem #( [DataBaseAddr:DataEndAddr]: begin rdata_d = { - data_i[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - - DataBaseAddr[DbgAddressBits-1:3] + 1'b1)], - data_i[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - - DataBaseAddr[DbgAddressBits-1:3])] + data_i[$clog2(dm::DataCount)'(((addr_i[DbgAddressBits-1:3] - DataBaseAddr[DbgAddressBits-1:3]) << 1) + 1'b1)], + data_i[$clog2(dm::DataCount)'(((addr_i[DbgAddressBits-1:3] - DataBaseAddr[DbgAddressBits-1:3]) << 1))] }; end @@ -325,6 +340,12 @@ module dm_mem #( end end + if (ndmreset_i) begin + // When harts are reset, they are neither halted nor resuming. + halted_d_aligned = '0; + resuming_d_aligned = '0; + end + data_o = data_bits; end diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv b/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv index 1c2b61954dfc4..9f3c3121c04ea 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv @@ -197,6 +197,12 @@ package dm; DTM_WRITE = 2'h2 } dtm_op_e; + typedef enum logic [1:0] { + DTM_SUCCESS = 2'h0, + DTM_ERR = 2'h2, + DTM_BUSY = 2'h3 + } dtm_op_status_e; + typedef struct packed { logic [31:29] sbversion; logic [28:23] zero0; @@ -215,8 +221,6 @@ package dm; logic sbaccess8; } sbcs_t; - localparam logic [1:0] DTM_SUCCESS = 2'h0; - typedef struct packed { logic [6:0] addr; dtm_op_e op; diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_top.sv b/hw/vendor/pulp_riscv_dbg/src/dm_top.sv index 3c9c445118c2c..c21e58d9cd8a1 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dm_top.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_top.sv @@ -86,6 +86,7 @@ module dm_top #( logic [dm::DataCount-1:0][31:0] data_csrs_mem; logic [dm::DataCount-1:0][31:0] data_mem_csrs; logic data_valid; + logic ndmreset; logic [19:0] hartsel; // System Bus Access Module logic [BusWidth-1:0] sbaddress_csrs_sba; @@ -104,6 +105,7 @@ module dm_top #( logic sberror_valid; logic [2:0] sberror; + assign ndmreset_o = ndmreset; dm_csrs #( .NrHarts(NrHarts), @@ -120,7 +122,7 @@ module dm_top #( .dmi_resp_valid_o, .dmi_resp_ready_i, .dmi_resp_o, - .ndmreset_o, + .ndmreset_o ( ndmreset ), .dmactive_o, .hartsel_o ( hartsel ), .hartinfo_i, @@ -201,6 +203,7 @@ module dm_top #( .clk_i, .rst_ni, .debug_req_o, + .ndmreset_i ( ndmreset ), .hartsel_i ( hartsel ), .haltreq_i ( haltreq ), .resumereq_i ( resumereq ), diff --git a/hw/vendor/pulp_riscv_dbg/src/dmi_bscane_tap.sv b/hw/vendor/pulp_riscv_dbg/src/dmi_bscane_tap.sv new file mode 100644 index 0000000000000..5fe5d86e51977 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/src/dmi_bscane_tap.sv @@ -0,0 +1,80 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Florian Zaruba + +/// Replacement for the full JTAG tap with `BSCANE2` Xilinx elements which hook +/// into the FPGA native scan chain. Meant for FPGA boards which do not expose a +/// usable pin-header or a separate, programmable FTDI chip. + +/// They replace the functionality of `dmi_jtag_tap.sv`. The file is +/// pin-compatible so that by selecting the appropriate file for the target it +/// can be transparently managed without relying on tick defines. +module dmi_jtag_tap #( + // Ignored, defined by the FPGA model. + parameter int unsigned IrLength = 5, + // JTAG IDCODE Value + parameter logic [31:0] IdcodeValue = 32'h00000001 + // xxxx version + // xxxxxxxxxxxxxxxx part number + // xxxxxxxxxxx manufacturer id + // 1 required by standard +) ( + /// Unused. Here to maintain pin compatibility with `dmi_jtag_tap` so that it + /// can be used as a drop-in replacement. + input logic tck_i, + input logic tms_i, + input logic trst_ni, + input logic td_i, + output logic td_o, + output logic tdo_oe_o, + input logic testmode_i, + + output logic tck_o, + output logic dmi_clear_o, + output logic update_o, + output logic capture_o, + output logic shift_o, + output logic tdi_o, + output logic dtmcs_select_o, + input logic dtmcs_tdo_i, + // we want to access DMI register + output logic dmi_select_o, + input logic dmi_tdo_i +); + + BSCANE2 #( + .JTAG_CHAIN (3) + ) i_tap_dtmcs ( + .CAPTURE (capture_o), + .DRCK (), + .RESET (dmi_clear_o), + .RUNTEST (), + .SEL (dtmcs_select_o), + .SHIFT (shift_o), + .TCK (tck_o), + .TDI (tdi_o), + .TMS (), + .TDO (dtmcs_tdo_i), + .UPDATE (update_o) + ); + + /// DMI Register + BSCANE2 #( + .JTAG_CHAIN (4) + ) i_tap_dmi ( + .CAPTURE (), + .DRCK (), + .RESET (), + .RUNTEST (), + .SEL (dmi_select_o), + .SHIFT (), + .TCK (), + .TDI (), + .TMS (), + .TDO (dmi_tdo_i), + .UPDATE () + ); + +endmodule diff --git a/hw/vendor/pulp_riscv_dbg/src/dmi_intf.sv b/hw/vendor/pulp_riscv_dbg/src/dmi_intf.sv new file mode 100644 index 0000000000000..8ebd749e2085d --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/src/dmi_intf.sv @@ -0,0 +1,59 @@ +// Copyright 2021 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Florian Zaruba + + +// The DV interface additionally carries a clock signal. +interface DMI_BUS_DV #( + /// The width of the address. + parameter int ADDR_WIDTH = -1 +) ( + input logic clk_i +); + + import dm::*; + + typedef logic [ADDR_WIDTH-1:0] addr_t; + typedef logic [31:0] data_t; + /// The request channel (Q). + addr_t q_addr; + dtm_op_e q_op; + data_t q_data; + logic q_valid; + logic q_ready; + + /// The response channel (P). + data_t p_data; + logic p_resp; + logic p_valid; + logic p_ready; + + modport in ( + input q_addr, q_op, q_data, q_valid, p_ready, + output q_ready, p_data, p_resp, p_valid + ); + modport out ( + output q_addr, q_op, q_data, q_valid, p_ready, + input q_ready, p_data, p_resp, p_valid + ); + modport monitor ( + input q_addr, q_op, q_data, q_valid, p_ready, + q_ready, p_data, p_resp, p_valid + ); + + // pragma translate_off + `ifndef VERILATOR + assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_addr))); + assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_op))); + assert property (@(posedge clk_i) (q_valid && !q_ready |=> $stable(q_data))); + assert property (@(posedge clk_i) (q_valid && !q_ready |=> q_valid)); + + assert property (@(posedge clk_i) (p_valid && !p_ready |=> $stable(p_data))); + assert property (@(posedge clk_i) (p_valid && !p_ready |=> $stable(p_resp))); + assert property (@(posedge clk_i) (p_valid && !p_ready |=> p_valid)); + `endif + // pragma translate_on + +endinterface diff --git a/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv b/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv index 048dbff787b1e..a7e5bffa75dc1 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv @@ -134,9 +134,11 @@ module dmi_jtag #( assign dmi_resp_ready = 1'b1; logic error_dmi_busy; + logic error_dmi_op_failed; always_comb begin : p_fsm error_dmi_busy = 1'b0; + error_dmi_op_failed = 1'b0; // default assignments state_d = state_q; address_d = address_q; @@ -177,7 +179,22 @@ module dmi_jtag #( WaitReadValid: begin // load data into register and shift out if (dmi_resp_valid) begin - data_d = dmi_resp.data; + unique case (dmi_resp.resp) + dm::DTM_SUCCESS: begin + data_d = dmi_resp.data; + end + dm::DTM_ERR: begin + data_d = 32'hDEAD_BEEF; + error_dmi_op_failed = 1'b1; + end + dm::DTM_BUSY: begin + data_d = 32'hB051_B051; + error_dmi_busy = 1'b1; + end + default: begin + data_d = 32'hBAAD_C0DE; + end + endcase state_d = Idle; end end @@ -193,6 +210,11 @@ module dmi_jtag #( WaitWriteValid: begin // got a valid answer go back to idle if (dmi_resp_valid) begin + unique case (dmi_resp.resp) + dm::DTM_ERR: error_dmi_op_failed = 1'b1; + dm::DTM_BUSY: error_dmi_busy = 1'b1; + default: ; + endcase state_d = Idle; end end @@ -218,9 +240,14 @@ module dmi_jtag #( error_dmi_busy = 1'b1; end - if (error_dmi_busy) begin + if (error_dmi_busy && error_q == DMINoError) begin error_d = DMIBusy; end + + if (error_dmi_op_failed && error_q == DMINoError) begin + error_d = DMIOPFailed; + end + // clear sticky error flag if (update && dtmcs_q.dmireset && dtmcs_select) begin error_d = DMINoError; diff --git a/hw/vendor/pulp_riscv_dbg/src/dmi_jtag_tap.sv b/hw/vendor/pulp_riscv_dbg/src/dmi_jtag_tap.sv index a804da045535a..3f913568a99cd 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dmi_jtag_tap.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dmi_jtag_tap.sv @@ -94,8 +94,9 @@ module dmi_jtag_tap #( jtag_ir_d = ir_reg_e'(jtag_ir_shift_q); end - // According to JTAG spec we have to reset the IR to IDCODE in test_logic_reset if (test_logic_reset) begin + // Bring all TAP state to the initial value. + jtag_ir_shift_d = '0; jtag_ir_d = IDCODE; end end @@ -135,6 +136,12 @@ module dmi_jtag_tap #( if (idcode_select) idcode_d = {td_i, 31'(idcode_q >> 1)}; if (bypass_select) bypass_d = td_i; end + + if (test_logic_reset) begin + // Bring all TAP state to the initial value. + idcode_d = IdcodeValue; + bypass_d = 1'b0; + end end // ---------------- diff --git a/hw/vendor/pulp_riscv_dbg/src/dmi_test.sv b/hw/vendor/pulp_riscv_dbg/src/dmi_test.sv new file mode 100644 index 0000000000000..06fb8cc45313e --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/src/dmi_test.sv @@ -0,0 +1,337 @@ +// Copyright 2021 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Florian Zaruba + +/// A set of testbench utilities for the DMI interfaces. +package dmi_test; + + import dm::*; + + class req_t #( + parameter int AW = 7 + ); + rand logic [AW-1:0] addr; + rand dtm_op_e op; + rand logic [31:0] data; + + /// Compare objects of same type. + function do_compare(req_t rhs); + return addr == rhs.addr & + op == rhs.op & + data == rhs.data; + endfunction + + endclass + + class rsp_t; + rand logic [31:0] data; + rand logic [1:0] resp; + + /// Compare objects of same type. + function do_compare(rsp_t rhs); + return data == rhs.data & + resp == rhs.resp; + endfunction + + endclass + + /// A driver for the DMI interface. + class dmi_driver #( + parameter int AW = -1, + parameter time TA = 0 , // stimuli application time + parameter time TT = 0 // stimuli test time + ); + virtual DMI_BUS_DV #( + .ADDR_WIDTH(AW) + ) bus; + + function new( + virtual DMI_BUS_DV #( + .ADDR_WIDTH(AW) + ) bus + ); + this.bus = bus; + endfunction + + task reset_master; + bus.q_addr <= '0; + bus.q_op <= DTM_NOP; + bus.q_data <= '0; + bus.q_valid <= '0; + bus.p_ready <= '0; + endtask + + task reset_slave; + bus.q_ready <= '0; + bus.p_data <= '0; + bus.p_resp <= '0; + bus.p_valid <= '0; + endtask + + task cycle_start; + #TT; + endtask + + task cycle_end; + @(posedge bus.clk_i); + endtask + + /// Send a request. + task send_req (input req_t req); + bus.q_addr <= #TA req.addr; + bus.q_op <= #TA req.op; + bus.q_data <= #TA req.data; + bus.q_valid <= #TA 1; + cycle_start(); + while (bus.q_ready != 1) begin cycle_end(); cycle_start(); end + cycle_end(); + bus.q_addr <= #TA '0; + bus.q_op <= #TA DTM_NOP; + bus.q_data <= #TA '0; + bus.q_valid <= #TA 0; + endtask + + /// Send a response. + task send_rsp (input rsp_t rsp); + bus.p_data <= #TA rsp.data; + bus.p_resp <= #TA rsp.resp; + bus.p_valid <= #TA 1; + cycle_start(); + while (bus.p_ready != 1) begin cycle_end(); cycle_start(); end + cycle_end(); + bus.p_data <= #TA '0; + bus.p_resp <= #TA '0; + bus.p_valid <= #TA 0; + endtask + + /// Receive a request. + task recv_req (output req_t req); + bus.q_ready <= #TA 1; + cycle_start(); + while (bus.q_valid != 1) begin cycle_end(); cycle_start(); end + req = new; + req.addr = bus.q_addr; + req.op = bus.q_op; + req.data = bus.q_data; + cycle_end(); + bus.q_ready <= #TA 0; + endtask + + /// Receive a response. + task recv_rsp (output rsp_t rsp); + bus.p_ready <= #TA 1; + cycle_start(); + while (bus.p_valid != 1) begin cycle_end(); cycle_start(); end + rsp = new; + rsp.data = bus.p_data; + rsp.resp = bus.p_resp; + cycle_end(); + bus.p_ready <= #TA 0; + endtask + + /// Monitor request. + task mon_req (output req_t req); + cycle_start(); + while (!(bus.q_valid && bus.q_ready)) begin cycle_end(); cycle_start(); end + req = new; + req.addr = bus.q_addr; + req.op = bus.q_op; + req.data = bus.q_data; + cycle_end(); + endtask + + /// Monitor response. + task mon_rsp (output rsp_t rsp); + cycle_start(); + while (!(bus.p_valid && bus.p_ready)) begin cycle_end(); cycle_start(); end + rsp = new; + rsp.data = bus.p_data; + rsp.resp = bus.p_resp; + cycle_end(); + endtask + + endclass + + // Super class for random dmi drivers. + virtual class rand_dmi #( + // dmi interface parameters + parameter int AW = 32, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps + ); + + typedef dmi_test::dmi_driver #( + // dmi bus interface parameters; + .AW ( AW ), + // Stimuli application and test time + .TA ( TA ), + .TT ( TT ) + ) dmi_driver_t; + + dmi_driver_t drv; + + function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); + this.drv = new (bus); + endfunction + + task automatic rand_wait(input int unsigned min, input int unsigned max); + int unsigned rand_success, cycles; + rand_success = std::randomize(cycles) with { + cycles >= min; + cycles <= max; + // Weigh the distribution so that the minimum cycle time is the common + // case. + cycles dist {min := 10, [min+1:max] := 1}; + }; + assert (rand_success) else $error("Failed to randomize wait cycles!"); + repeat (cycles) @(posedge this.drv.bus.clk_i); + endtask + + endclass + + /// Generate random requests as a master device. + class rand_dmi_master #( + // dmi interface parameters + parameter int AW = 32, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps, + parameter int unsigned REQ_MIN_WAIT_CYCLES = 1, + parameter int unsigned REQ_MAX_WAIT_CYCLES = 20, + parameter int unsigned RSP_MIN_WAIT_CYCLES = 1, + parameter int unsigned RSP_MAX_WAIT_CYCLES = 20 + ) extends rand_dmi #(.AW(AW), .TA(TA), .TT(TT)); + + int unsigned cnt = 0; + bit req_done = 0; + + /// Reset the driver. + task reset(); + drv.reset_master(); + endtask + + /// Constructor. + function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); + super.new(bus); + endfunction + + task run(input int n); + fork + send_requests(n); + recv_response(); + join + endtask + + /// Send random requests. + task send_requests (input int n); + automatic req_t r = new; + + repeat (n) begin + this.cnt++; + assert(r.randomize()); + rand_wait(REQ_MIN_WAIT_CYCLES, REQ_MAX_WAIT_CYCLES); + this.drv.send_req(r); + end + this.req_done = 1; + endtask + + /// Receive random responses. + task recv_response; + while (!this.req_done || this.cnt > 0) begin + automatic rsp_t rsp; + this.cnt--; + rand_wait(RSP_MIN_WAIT_CYCLES, RSP_MAX_WAIT_CYCLES); + this.drv.recv_rsp(rsp); + end + endtask + endclass + + class rand_dmi_slave #( + // dmi interface parameters + parameter int AW = 32, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps, + parameter int unsigned REQ_MIN_WAIT_CYCLES = 0, + parameter int unsigned REQ_MAX_WAIT_CYCLES = 10, + parameter int unsigned RSP_MIN_WAIT_CYCLES = 0, + parameter int unsigned RSP_MAX_WAIT_CYCLES = 10 + ) extends rand_dmi #(.AW(AW), .TA(TA), .TT(TT)); + + mailbox req_mbx = new(); + + /// Reset the driver. + task reset(); + drv.reset_slave(); + endtask + + task run(); + fork + recv_requests(); + send_responses(); + join + endtask + + /// Constructor. + function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); + super.new(bus); + endfunction + + task recv_requests(); + forever begin + automatic req_t req; + rand_wait(REQ_MIN_WAIT_CYCLES, REQ_MAX_WAIT_CYCLES); + this.drv.recv_req(req); + req_mbx.put(req); + end + endtask + + task send_responses(); + automatic rsp_t rsp = new; + automatic req_t req; + forever begin + req_mbx.get(req); + assert(rsp.randomize()); + @(posedge this.drv.bus.clk_i); + rand_wait(RSP_MIN_WAIT_CYCLES, RSP_MAX_WAIT_CYCLES); + this.drv.send_rsp(rsp); + end + endtask + endclass + + class dmi_monitor #( + // dmi interface parameters + parameter int AW = 32, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps + ) extends rand_dmi #(.AW(AW), .TA(TA), .TT(TT)); + + mailbox req_mbx = new, rsp_mbx = new; + + /// Constructor. + function new(virtual DMI_BUS_DV #( .ADDR_WIDTH (AW)) bus); + super.new(bus); + endfunction + + // dmi Monitor. + task monitor; + fork + forever begin + automatic dmi_test::req_t req; + this.drv.mon_req(req); + req_mbx.put(req); + end + forever begin + automatic dmi_test::rsp_t rsp; + this.drv.mon_rsp(rsp); + rsp_mbx.put(rsp); + end + join + endtask + endclass + +endpackage \ No newline at end of file diff --git a/hw/vendor/pulp_riscv_dbg/tb/Makefile b/hw/vendor/pulp_riscv_dbg/tb/Makefile index b6a3b29303845..c59368bf69861 100644 --- a/hw/vendor/pulp_riscv_dbg/tb/Makefile +++ b/hw/vendor/pulp_riscv_dbg/tb/Makefile @@ -20,7 +20,7 @@ MAKE = make CTAGS = ctags # vsim configuration -VVERSION = "10.7b" +VVERSION = "10.7b" VLIB = vlib-$(VVERSION) VWORK = work @@ -33,26 +33,26 @@ VOPT = vopt-$(VVERSION) VOPT_FLAGS = -debugdb -fsmdebug -pedanticerrors #=mnprft VSIM = vsim-$(VVERSION) -VSIM_HOME = /usr/pack/modelsim-$(VVERSION)-kgf/questasim -VSIM_FLAGS = # user defined +VSIM_HOME = /usr/pack/modelsim-$(VVERSION)-kgf/questasim +VSIM_FLAGS = # user defined ALL_VSIM_FLAGS = $(VSIM_FLAGS) -sv_lib remote_bitbang/librbs_vsim VSIM_DEBUG_FLAGS = -debugdb -VSIM_GUI_FLAGS = -gui -debugdb -VSIM_SCRIPT_BATCH = vsim_batch.tcl -VSIM_SCRIPT_GUI = vsim_gui.tcl +VSIM_GUI_FLAGS = -gui -debugdb +VSIM_SCRIPT_BATCH = vsim_batch.tcl +VSIM_SCRIPT_GUI = vsim_gui.tcl -VCS = vcs-2017.03-kgf vcs -VCS_HOME = /usr/pack/vcs-2017.03-kgf -VCS_FLAGS = -SIMV_FLAGS = +VCS = vcs-2017.03-kgf vcs +VCS_HOME = /usr/pack/vcs-2017.03-kgf +VCS_FLAGS = +SIMV_FLAGS = # verilator configuration VERILATOR = verilator -VERI_FLAGS = -VERI_COMPILE_FLAGS = -VERI_TRACE = -VERI_DIR = cobj_dir -VERI_CFLAGS = -O2 +VERI_FLAGS = +VERI_COMPILE_FLAGS = +VERI_TRACE = +VERI_DIR = cobj_dir +VERI_CFLAGS = -O2 # RTL source files RTLSRC_TB_PKG := @@ -67,13 +67,13 @@ RTLSRC_TB := boot_rom.sv \ tb_test_env.sv \ tb_top.sv -RTLSRC_VERI_TB := boot_rom.sv \ +RTLSRC_VERI_TB := boot_rom.sv \ dp_ram.sv \ mm_ram.sv \ SimJTAG.sv \ tb_top_verilator.sv -RTLSRC_INCDIR := riscv/rtl/include +RTLSRC_INCDIR := riscv/rtl/include common_cells/include RTLSRC_FPNEW_PKG := fpnew/src/fpnew_pkg.sv RTLSRC_RISCV_PKG += $(addprefix riscv/rtl/include/,\ @@ -81,7 +81,10 @@ RTLSRC_RISCV_PKG += $(addprefix riscv/rtl/include/,\ ../../bhv/include/cv32e40p_tracer_pkg.sv) RTLSRC_DM_PKG += ../src/dm_pkg.sv -RTLSRC_PKG = $(RTLSRC_FPNEW_PKG) dm_tb_pkg.sv $(RTLSRC_RISCV_PKG) $(RTLSRC_DM_PKG) +RTLSRC_COMMON_PKG := common_cells/src/cdc_reset_ctrlr_pkg.sv + +RTLSRC_PKG = $(RTLSRC_FPNEW_PKG) dm_tb_pkg.sv $(RTLSRC_RISCV_PKG) \ + $(RTLSRC_DM_PKG) $(RTLSRC_COMMON_PKG) RTLSRC_RISCV := $(addprefix riscv/rtl/,\ ../bhv/cv32e40p_sim_clock_gate.sv \ ../bhv/cv32e40p_tracer.sv \ @@ -109,13 +112,15 @@ RTLSRC_RISCV := $(addprefix riscv/rtl/,\ cv32e40p_prefetch_controller.sv \ cv32e40p_sleep_unit.sv \ cv32e40p_core.sv) -RTLSRC_COMMON := $(addprefix common_cells/src/,\ - cdc_2phase.sv fifo_v2.sv fifo_v3.sv\ - rstgen.sv rstgen_bypass.sv) +RTLSRC_COMMON := $(addprefix common_cells/src/,\ + spill_register_flushable.sv spill_register.sv \ + cdc_2phase.sv cdc_2phase_clearable.sv \ + cdc_reset_ctrlr.sv cdc_4phase.sv \ + deprecated/fifo_v2.sv fifo_v3.sv \ + rstgen.sv rstgen_bypass.sv sync.sv) RTLSRC_TECH := $(addprefix tech_cells_generic/src/,\ - cluster_clock_inverter.sv pulp_clock_mux2.sv\ - cluster_clock_gating.sv) -RTLSRC_DEBUG := ../debug_rom/debug_rom.sv + rtl/tc_clk.sv) +RTLSRC_DEBUG := ../debug_rom/debug_rom.sv RTLSRC_DEBUG += $(addprefix ../src/,\ dm_csrs.sv dmi_cdc.sv dmi_jtag.sv \ dmi_jtag_tap.sv dm_mem.sv \ @@ -124,12 +129,12 @@ RTLSRC_DEBUG += $(addprefix ../src/,\ RTLSRC += $(RTLSRC_RISCV) $(RTLSRC_COMMON) $(RTLSRC_TECH) $(RTLSRC_DEBUG) # versions for this tb -CV32E40P_SHA = f9d63290eea738cb0a6fbf1e77bbd18555015a03 -FPU_SHA = v0.6.1 -COMMON_SHA = 337f54a7cdfdad78b124cbdd2a627db3e0939141 -TECH_SHA = b35652608124b7ea813818b14a00ca76edd7599d +CV32E40P_SHA = f9d63290eea738cb0a6fbf1e77bbd18555015a03 +FPU_SHA = v0.6.1 +COMMON_SHA = v1.24.0 +TECH_SHA = v0.2.3 -RAM_START_ADDR = 0x1c000000 +RAM_START_ADDR = 0x1c000000 # TODO: clean this up RTLSRC_VLOG_TB_TOP := $(basename $(notdir $(RTLSRC_TB_TOP))) @@ -157,7 +162,7 @@ vlib: .lib-rtl # rebuild if we change some sourcefile .build-rtl: .lib-rtl $(RTLSRC_PKG) $(RTLSRC) $(RTLSRC_TB_PKG) $(RTLSRC_TB) - $(VLOG) -work $(VWORK) +incdir+$(RTLSRC_INCDIR) $(VLOG_FLAGS) \ + $(VLOG) -work $(VWORK) $(addprefix +incdir+,$(RTLSRC_INCDIR)) $(VLOG_FLAGS) \ $(RTLSRC_PKG) $(RTLSRC) $(RTLSRC_TB_PKG) $(RTLSRC_TB) touch .build-rtl @@ -172,10 +177,10 @@ vsim-all: .opt-rtl vcsify: $(RTLSRC_PKG) $(RTLSRC) $(RTLSRC_TB_PKG) $(RTLSRC_TB) remote_bitbang/librbs_vcs.so $(VCS) +vc -sverilog -race=all -ignore unique_checks -full64 \ - -timescale=1ns/1ps \ + -timescale=1ns/1ps -assert svaext \ -CC "-I$(VCS_HOME)/include -O3 -march=native" $(VCS_FLAGS) \ $(RTLSRC_PKG) $(RTLSRC) $(RTLSRC_TB_PKG) $(RTLSRC_TB) \ - +incdir+$(RTLSRC_INCDIR) + $(addprefix +incdir+,$(RTLSRC_INCDIR)) vcs-clean: rm -rf simv* *.daidir *.vpd *.db csrc ucli.key vc_hdrs.h @@ -199,8 +204,8 @@ verilate: testbench_verilator testbench_verilator: $(RTLSRC_VERI_TB) $(RTLSRC_PKG) $(RTLSRC) \ remote_bitbang/librbs_veri.so $(VERILATOR) --cc --sv --exe $(VERI_TRACE) \ - --Wno-lint --Wno-UNOPTFLAT --Wno-BLKANDNBLK \ - --Wno-MODDUP +incdir+$(RTLSRC_INCDIR) --top-module \ + --Wno-lint --Wno-UNOPTFLAT --Wno-BLKANDNBLK --Wno-COMBDLY \ + --Wno-MODDUP $(addprefix +incdir+,$(RTLSRC_INCDIR)) --top-module \ tb_top_verilator --Mdir $(VERI_DIR) \ -CFLAGS "-std=gnu++11 $(VERI_CFLAGS)" $(VERI_COMPILE_FLAGS) \ $(RTLSRC_PKG) $(RTLSRC_VERI_TB) $(RTLSRC) \ @@ -297,6 +302,13 @@ veri-run: verilate prog/test.hex ./testbench_verilator $(VERI_FLAGS) \ "+firmware=prog/test.hex" +# run openocd testbench +# Use OPENOCD to point to openocd binary +# Use OPENOCD_SCRIPT to point to test script +.PHONY: test-openocd +test-openocd: + ./veri-run-openocd.py + .PHONY: vsim-run vsim-run: vsim-all prog/test.hex vsim-run: ALL_VSIM_FLAGS += "+firmware=prog/test.hex" diff --git a/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/.gitignore b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/.gitignore new file mode 100644 index 0000000000000..c75929c85c736 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/.gitignore @@ -0,0 +1,3 @@ +compile.tcl +wlf* +work/ diff --git a/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_intf.sv b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_intf.sv new file mode 100644 index 0000000000000..8786554f1aed0 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_intf.sv @@ -0,0 +1,15 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +interface JTAG_DV ( + input logic clk_i +); + logic tdi; + logic tdo; + logic tms; + logic trst_n; + + modport in (input tdi, tms, trst_n, output tdo); + modport out (output tdi, tms, trst_n, input tdo); +endinterface diff --git a/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_test.sv b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_test.sv new file mode 100644 index 0000000000000..d043358a42a22 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/jtag_test.sv @@ -0,0 +1,307 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Andreas Traber +// Author: Florian Zaruba +// Author: Fabian Schuiki + +package jtag_test; + + class jtag_driver #( + parameter int IrLength = 0, + parameter IDCODE = 'h1, + parameter time TA = 0ns , // stimuli application time + parameter time TT = 0ns // stimuli test time + ); + + virtual JTAG_DV jtag; + + // last IR register select + protected logic [IrLength-1:0] ir_select; + + function new ( virtual JTAG_DV jtag); + this.jtag = jtag; + // Per JTAG specification the IDCODE should be at 'h1 and is selected by + // default after reset. + this.ir_select = 'h1; + endfunction + + task reset_master; + jtag.tms <= #TA 1; + jtag.tdi <= #TA 0; + jtag.trst_n <= #TA 0; + repeat (2) clock(); + jtag.trst_n <= #TA 1; + this.ir_select = 'h1; + clock(); + endtask + + task soft_reset(); + jtag.tms <= #TA 1; + jtag.tdi <= #TA 0; + repeat (6) clock(); + jtag.tms <= #TA 0; + clock(); + // After softreset the IR should be reset to IDCODE so we have to mirror + // this in our internal state. + this.ir_select = 'h1; + endtask + + // Set IR, but only if it needs to be set. + task set_ir(input logic [IrLength-1:0] opcode); + logic opcode_unpacked [IrLength]; + // check whether IR is already set to the right value + if (this.ir_select == opcode) return; + {<<{opcode_unpacked}} = opcode; + write_tms(1); // select DR scan + write_tms(1); // select IR scan + write_tms(0); // capture IR + write_tms(0); // shift IR + write_bits(opcode_unpacked, 1); + write_tms(1); // update IR + write_tms(0); // run test idle + this.ir_select = opcode; + endtask + // Go from `run_test_idle` to `shift_dr` + task shift_dr(); + write_tms(1); // select DR scan + write_tms(0); // capture DR + write_tms(0); // shift DR + endtask + + // Go to `run_test_idle` + task update_dr(bit exit_1_dr); + // depending on the state `exit_1_dr` is already reached when shifting data (`tms_on_last`). + if (exit_1_dr) write_tms(1); // exi 1 DR + write_tms(1); // update DR + write_tms(0); // run test idle + endtask + + task write_bits(input logic wdata [$], input logic tms_last); + for (int i = 0; i < $size(wdata); i++) begin + jtag.tdi <= #TA wdata[i]; + if (i == ($size(wdata) - 1)) jtag.tms <= #TA tms_last; + clock(); + end + jtag.tms <= #TA 0; + endtask + + // Assumes JTAG FSM is already in shift DR state + task readwrite_bits(output logic rdata [$], input logic wdata [$], input logic tms_last); + for (int i = 0; i < wdata.size(); i++) begin + jtag.tdi <= #TA wdata[i]; + if (i == (wdata.size() - 1)) jtag.tms <= #TA tms_last; // tms_last ? exit1 DR : shift DR + cycle_start(); + rdata[i] = jtag.tdo; + cycle_end(); + end + jtag.tms <= #TA 0; // tms_last ? pause DR : shift DR + endtask + + task wait_idle(int cycles); + repeat(cycles) clock(); + endtask + + // Protected methods + task write_tms(input logic tms_val); + jtag.tms <= #TA tms_val; + clock(); + endtask + + protected task clock(); + cycle_start(); cycle_end(); + endtask + + protected task cycle_start; + #TT; + endtask + + // TODO(zarubaf): I am not sure on which clock edge to trigger + protected task cycle_end; + @(posedge jtag.clk_i); + endtask + endclass + + // abstracts the debug module + class riscv_dbg #( + parameter int IrLength = 5, + parameter IDCODE = 'h1, + parameter DTMCSR = 'h10, + parameter DMIACCESS = 'h11, + parameter time TA = 0ns, // stimuli application time + parameter time TT = 0ns // stimuli test time + ); + + typedef jtag_test::jtag_driver#(.IrLength(IrLength), .IDCODE(IDCODE), .TA(TA), .TT(TT)) jtag_driver_t; + jtag_driver_t jtag; + + localparam DMIWidth = $bits(dm::dmi_req_t); + + function new (jtag_driver_t jtag); + this.jtag = jtag; + endfunction + + task reset_master(); + jtag.reset_master(); + jtag.soft_reset(); + endtask + + task wait_idle(int cycles); + jtag.wait_idle(cycles); + endtask + + task get_idcode(output logic [31:0] idcode); + logic read_data [32], write_data [32]; + write_data = '{default: 1'b0}; + jtag.set_ir(IDCODE); + jtag.shift_dr(); + jtag.readwrite_bits(read_data, write_data, 1'b0); + jtag.update_dr(1'b1); + idcode = {<<{read_data}}; + endtask + + task write_dtmcs(input logic [31:0] data); + logic write_data [32]; + logic [31:0] write_data_packed = {data}; + {<<{write_data}} = write_data_packed; + jtag.set_ir(DTMCSR); + jtag.shift_dr(); + jtag.write_bits(write_data, 1'b1); + jtag.update_dr(1'b0); + endtask + + task read_dtmcs(output dm::dtmcs_t data, input int wait_cycles = 10); + logic read_data [32], write_data [32]; + jtag.set_ir(DTMCSR); + jtag.shift_dr(); + // shift out read data + {<<{write_data}} = 32'b0; + jtag.readwrite_bits(read_data, write_data, 1'b1); + jtag.update_dr(1'b0); + data = dm::dtmcs_t'({<<{read_data}}); + endtask + + task reset_dmi(); + logic [31:0] dmireset = 1 << 16; + write_dtmcs(dmireset); + endtask + + task write_dmi(input dm::dm_csr_e address, input logic [31:0] data); + logic write_data [DMIWidth]; + logic [DMIWidth-1:0] write_data_packed = {address, data, dm::DTM_WRITE}; + {<<{write_data}} = write_data_packed; + jtag.set_ir(DMIACCESS); + jtag.shift_dr(); + jtag.write_bits(write_data, 1'b1); + jtag.update_dr(1'b0); + endtask + + task read_dmi(input dm::dm_csr_e address, output logic [31:0] data, input int wait_cycles = 10, + output dm::dtm_op_status_e op); + logic read_data [DMIWidth], write_data [DMIWidth]; + logic [DMIWidth-1:0] data_out = 0; + automatic logic [DMIWidth-1:0] write_data_packed = {address, 32'b0, dm::DTM_READ}; + {<<{write_data}} = write_data_packed; + jtag.set_ir(DMIACCESS); + // send read command + jtag.shift_dr(); + jtag.write_bits(write_data, 1'b1); + jtag.update_dr(1'b0); + jtag.wait_idle(wait_cycles); + // shift out read data + jtag.shift_dr(); + write_data_packed = {address, 32'b0, dm::DTM_NOP}; + {<<{write_data}} = write_data_packed; + jtag.readwrite_bits(read_data, write_data, 1'b1); + jtag.update_dr(1'b0); + data_out = {<<{read_data}}; + op = dm::dtm_op_status_e'(data_out[1:0]); + data = data_out[33:2]; + endtask + + // Repeatedly read DMI until we get a valid response. + // The delay between Update-DR and Capture-DR of + // successive operations is automatically adjusted through + // an exponential backoff scheme. + // Note: read operations which have side-effects (e.g. + // reading SBData0) should not use this function + task read_dmi_exp_backoff(input dm::dm_csr_e address, output logic [31:0] data); + logic read_data [DMIWidth], write_data [DMIWidth]; + logic [DMIWidth-1:0] write_data_packed; + logic [DMIWidth-1:0] data_out = 0; + dm::dtm_op_status_e op = dm::DTM_SUCCESS; + int trial_idx = 0; + int wait_cycles = 8; + + do begin + if (trial_idx != 0) begin + // Not entered upon first iteration, resets the + // sticky error state if previous read was unsuccessful + reset_dmi(); + end + read_dmi(address, data, wait_cycles, op); + wait_cycles *= 2; + trial_idx++; + end while (op == dm::DTM_BUSY); + endtask + + task sba_read_double(input logic [31:0] address, output logic [63:0] data); + // Attempt the access sequence. Two timing violations may + // occur: + // 1) an operation is attempted while a DMI request is still + // in progress; + // 2) a SB read is attempted while a read is still in progress + // or a SB access is attempted while one is in progress + // In either case the whole sequence must be re-attempted with + // increased delays. + // Case 1) is intercepted when the op returned by a read is == DTM_BUSY, + // the sequence can be interrupted early and the delay to be adjusted is + // that between the update phase and the capture phase of a successive op. + // Case 2) is intercepted at the end of the sequence by reading the + // SBCS register, and checking sbbusyerror. In this case the delay to be + // adjusted is that before the SBData read operations. + dm::dtm_op_status_e op; + automatic int dmi_wait_cycles = 2; + automatic int sba_wait_cycles = 2; + automatic dm::sbcs_t sbcs = '{sbreadonaddr: 1, sbaccess: 3, default: '0}; + dm::sbcs_t read_sbcs; + // Check address is 64b aligned + assert (address[2:0] == '0) else $error("[JTAG] 64b-unaligned accesses not supported"); + // Start SBA sequence attempts + while (1) begin + automatic bit failed = 0; + write_dmi(dm::SBCS, sbcs); + write_dmi(dm::SBAddress0, address); + wait_idle(sba_wait_cycles); + read_dmi(dm::SBData1, data[63:32], dmi_wait_cycles, op); + // Skip second read if we already have a DTM busy error + // else we can override op + if (op != dm::DTM_BUSY) begin + read_dmi(dm::SBData0, data[31:0], dmi_wait_cycles, op); + end + // If we had a DTM_BUSY error, increase dmi_wait_cycles and clear error + if (op == dm::DTM_BUSY) begin + dmi_wait_cycles *= 2; + failed = 1'b1; + reset_dmi(); + end + // Test sbbusyerror and wait for sbbusy == 0 + // Error is cleared in next iteration when writing SBCS + do begin + sbcs.sbbusyerror = 1'b0; + read_dmi_exp_backoff(dm::SBCS, read_sbcs); + if (read_sbcs.sbbusyerror) begin + sbcs.sbbusyerror = 1'b1; // set 1 to clear + sba_wait_cycles *= 2; + failed = 1'b1; + end + if (read_sbcs.sbbusy) wait_idle(sba_wait_cycles); + end while (read_sbcs.sbbusy); + // Exit loop if sequence was successful + if (!failed) break; + end + endtask + + endclass +endpackage diff --git a/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim.sh b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim.sh new file mode 100755 index 0000000000000..384a749de60b0 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright 2020-2021 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Fabian Schuiki +# Andreas Kurth + +set -e +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +[ ! -z "$VSIM" ] || VSIM=vsim + +bender script vsim -t test \ + --vlog-arg="-svinputport=compat" \ + --vlog-arg="-override_timescale 1ns/1ps" \ + --vlog-arg="-suppress 2583" \ + --vlog-arg="+cover=sbecft" \ + > compile.tcl +echo 'return 0' >> compile.tcl +$VSIM -c -do 'exit -code [source compile.tcl]' + +call_vsim() { + echo "log -r /*; run -all" | $VSIM -c -coverage -voptargs='+acc +cover=sbecft' "$@" | tee vsim.log 2>&1 + grep "Errors: 0," vsim.log +} + +call_vsim tb_jtag_dmi diff --git a/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim_xilinx.sh b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim_xilinx.sh new file mode 100755 index 0000000000000..535113f93ec22 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/run_vsim_xilinx.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2020-2021 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Fabian Schuiki +# Andreas Kurth + +set -e +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + +if [ -z "$VIVADO_HOME" ]; then + echo "Please set \$VIVADO_HOME to point to your Vivado installation."; exit 1; +fi + +[ ! -z "$VSIM" ] || VSIM=vsim + +bender script vsim -t test -t xilinx -t bscane \ + --vlog-arg="-svinputport=compat" \ + --vlog-arg="-override_timescale 1ns/1ps" \ + --vlog-arg="-suppress 2583" \ + --vlog-arg="+cover=sbecft" \ + > compile.tcl +echo 'return 0' >> compile.tcl +$VSIM -c -do 'exit -code [source compile.tcl]' + +call_vsim() { + vlog $VIVADO_HOME/data/verilog/src/glbl.v + vlog $VIVADO_HOME/data/verilog/src/unisims/BSCANE2.v + vlog $VIVADO_HOME/data/verilog/src/unisims/JTAG_SIME2.v + echo "log -r /*; run -all" | $VSIM -c -coverage -voptargs='+acc +cover=sbecft' "$@" | tee vsim.log 2>&1 + grep "Errors: 0," vsim.log +} + +call_vsim work.tb_jtag_dmi work.glbl diff --git a/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/tb_jtag_dmi.sv b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/tb_jtag_dmi.sv new file mode 100644 index 0000000000000..1d64a01e9d7c4 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/jtag_dmi/tb_jtag_dmi.sv @@ -0,0 +1,304 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +/// Selectively test the JTAG DMI. +module tb_jtag_dmi; + + logic clk, rst_n; + + localparam time ClkPeriod = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + + localparam time JTAGPeriod = 50ns; + + localparam int unsigned AW = 7; + localparam IDCode = 32'hdeadbeef | 32'b1; + + // ---------------- + // Clock generation + // ---------------- + initial begin + rst_n = 0; + repeat (3) begin + #(ClkPeriod/2) clk = 0; + #(ClkPeriod/2) clk = 1; + end + rst_n = 1; + forever begin + #(ClkPeriod/2) clk = 0; + #(ClkPeriod/2) clk = 1; + end + end + + logic tck; + + JTAG_DV jtag_mst (tck); + + initial begin + #100ns; + forever begin + tck = 1; + #(JTAGPeriod/2); + tck = 0; + #(JTAGPeriod/2); + end + end + + // --- + // DUT + // --- + DMI_BUS_DV #( + .ADDR_WIDTH ( AW ) + ) slave_dv (clk); + + logic dut_tck, dut_tms, dut_trstn, dut_tdi, dut_tdo; + logic start_rand; + + dm::dmi_req_t dmi_req; + dm::dmi_resp_t dmi_resp; + + assign slave_dv.q_addr = dmi_req.addr; + assign slave_dv.q_op = dmi_req.op; + assign slave_dv.q_data = dmi_req.data; + assign dmi_resp = '{ + data: slave_dv.p_data, + resp: slave_dv.p_resp + }; + + dmi_jtag #( + .IdcodeValue (IDCode) + ) dut ( + .clk_i (clk), + .rst_ni (rst_n), + .testmode_i (1'b0), + + .dmi_rst_no (), + .dmi_req_o (dmi_req), + .dmi_req_valid_o (slave_dv.q_valid), + .dmi_req_ready_i (slave_dv.q_ready), + + .dmi_resp_i (dmi_resp), + .dmi_resp_ready_o (slave_dv.p_ready), + .dmi_resp_valid_i (slave_dv.p_valid), + + .tck_i (dut_tck), + .tms_i (dut_tms), + .trst_ni (dut_trstn), + .td_i (dut_tdi), + .td_o (dut_tdo), + .tdo_oe_o () + ); + + // ------- + // Monitor + // ------- + typedef dmi_test::dmi_monitor #( + .AW ( AW ), + .TA ( ApplTime ), + .TT ( TestTime ) + ) dmi_monitor_t; + + dmi_monitor_t dmi_monitor = new (slave_dv); + + initial begin + @(posedge start_rand); + dmi_monitor.monitor(); + end + + // ------ + // Driver + // ------ + typedef dmi_test::rand_dmi_slave #( + // dmi bus interface paramaters; + .AW ( AW ), + // Stimuli application and test time + .TA ( ApplTime ), + .TT ( TestTime ) + ) dmi_rand_slave_t; + + dmi_rand_slave_t rand_dmi_slave = new (slave_dv); + + // dmi Slave. + initial begin + rand_dmi_slave.reset(); + @(posedge start_rand); + rand_dmi_slave.run(); + end + + `ifdef TARGET_BSCANE + + JTAG_SIME2 #( + .PART_NAME ("7K325T") + ) i_jtag_sime2 ( + .TDO (jtag_mst.tdo), + .TCK (tck), + .TDI (jtag_mst.tdi), + .TMS (jtag_mst.tms) + ); + + // Default part `7K325T` has an IrLength of 6. + localparam IRLength = i_jtag_sime2.IRLength; + localparam logic [23:0] IR_CAPTURE_VAL = 24'b010001010001010001010001, + BYPASS_INSTR = 24'b111111111111111111111111, + IDCODE_INSTR = 24'b001001001001001001001001, + USER1_INSTR = 24'b000010100100100100100100, + USER2_INSTR = 24'b000011100100100100100100, + USER3_INSTR = 24'b100010100100100100100100, + USER4_INSTR = 24'b100011100100100100100100; + + typedef jtag_test::riscv_dbg #( + .IDCODE (IDCODE_INSTR[23:(24-IRLength)]), + .DTMCSR (USER3_INSTR[23:(24-IRLength)]), + .DMIACCESS (USER4_INSTR[23:(24-IRLength)]), + .IrLength (IRLength), + .TA (JTAGPeriod*0.1), + .TT (JTAGPeriod*0.9) + ) riscv_dbg_t; + + /// Helper function to bring instruction register values. + // initial begin + // for (int i = 6; i < 25; i++) begin + // $display("IRLength %d: %h %h %h\n", i, + // (IDCODE_INSTR >> (24-i)) & (2**i-1), + // (USER3_INSTR >> (24-i)) & (2**i-1), + // (USER4_INSTR >> (24-i)) & (2**i-1)); + // end + // end + `else + + assign dut_tck = tck; + assign dut_tms = jtag_mst.tms; + assign dut_trstn = jtag_mst.trst_n; + assign dut_tdi = jtag_mst.tdi; + assign jtag_mst.tdo = dut_tdo; + + typedef jtag_test::riscv_dbg #( + .IrLength (5), + .TA (JTAGPeriod*0.1), + .TT (JTAGPeriod*0.9) + ) riscv_dbg_t; + `endif + + riscv_dbg_t::jtag_driver_t jtag_in = new (jtag_mst); + riscv_dbg_t riscv_dbg = new (jtag_in); + + mailbox req_mbx = new, rsp_mbx = new; + localparam NrRandomTransactions = 200; + int unsigned nr_transactions = 0; + + initial begin + logic [31:0] idcode; + dm::dtmcs_t dtmcs; + logic [4:0] opocode; + start_rand = 0; + + $display("Resetting"); + riscv_dbg.reset_master(); + + /// Test ID Code. + $display("Reading ID Code"); + riscv_dbg.get_idcode(idcode); + + $display("Got ID Code %h", idcode); + + // Check ID Code. + `ifdef TARGET_BSCANE + assert(idcode == i_jtag_sime2.IDCODEval_sig); + `else + assert(idcode == IDCode); + `endif + + /// Test DTMCs + riscv_dbg.read_dtmcs(dtmcs); + $display("DTMCS: %p", dtmcs); + assert(dtmcs.version == 1); + assert(dtmcs.abits == 7); + + riscv_dbg.write_dtmcs(32'hdeadbeef); + + riscv_dbg.read_dtmcs(dtmcs); + $display("DTMCS: %p", dtmcs); + assert(dtmcs.version == 1); + assert(dtmcs.abits == 7); + + /// Random DMI transactions. + // Generate a number of random transactions and drive them on the JTAG + // interface. + start_rand = 1; + for (int i = 0; i < NrRandomTransactions; i++) begin + automatic dmi_test::req_t transaction = new; + assert(transaction.randomize() with { + op inside {dm::DTM_WRITE, dm::DTM_READ}; + }); + if (transaction.op == dm::DTM_WRITE) begin + req_mbx.put(transaction); + riscv_dbg.write_dmi(dm::dm_csr_e'(transaction.addr), transaction.data); + rsp_mbx.put('h0); + end else if (transaction.op == dm::DTM_READ) begin + automatic logic [31:0] rdata; + req_mbx.put(transaction); + riscv_dbg.read_dmi(dm::dm_csr_e'(transaction.addr), rdata); + rsp_mbx.put(rdata); + end + // Randomly reset the dmi using either hard jtag trst_ni, JTAG + // TestLogicReset or the dtmcs.dmihardreset bit. + if ($urandom_range(0,100) < 5) begin + riscv_dbg.wait_idle(30); + case ($urandom_range(0,3)) + 0: begin + $info("Resetting JTAG DMI using asynchronous JTAG reset signal..."); + riscv_dbg.reset_master(); + end + + 1: begin + $info("Resetting JTAG DMI using JTAG softreset (TestLogicReset TAP state)."); + riscv_dbg.jtag.soft_reset(); + end + + 2: begin + dm::dtmcs_t dtmcs_value; + dtmcs_value = '0; + dtmcs_value.dmihardreset = 1; + $info("Resetting JTAG DMI using DMI dtmcs registers' dmihardreset control bit."); + riscv_dbg.write_dtmcs(dtmcs_value); + end + endcase + end + end + #1000; + $finish(); + end + + // ---------- + // Scoreboard + // ---------- + initial begin + forever begin + automatic dmi_test::req_t req, req_mon; + automatic dmi_test::rsp_t rsp_mon; + automatic logic [31:0] rsp; + + dmi_monitor.req_mbx.get(req_mon); + dmi_monitor.rsp_mbx.get(rsp_mon); + req_mbx.get(req); + rsp_mbx.get(rsp); + nr_transactions++; + assert(req.addr == req_mon.addr) else + $error("Invalid dmi request. Got address %0x instead of %0x.", req_mon.addr, req.addr); + assert(req.op == req_mon.op) else + $error("Invalid dmi request. Got op %0x instead of %0x.", req_mon.op, req.op);; + if (req.op == dm::DTM_READ) begin + assert(rsp_mon.data == rsp); + end else begin + assert(req.data == req_mon.data); + end + end + end + + final begin + assert(NrRandomTransactions == nr_transactions) else $error("Remaining transactions."); + end + +endmodule diff --git a/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.c b/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.c index cf24fbbc521db..196d0f8a71531 100644 --- a/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.c +++ b/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.c @@ -13,6 +13,24 @@ #include "remote_bitbang.h" +//Public globals, declared in remote_bitbang.h + +int rbs_err; + +unsigned char tck; +unsigned char tms; +unsigned char tdi; +unsigned char trstn; +unsigned char tdo; +unsigned char quit; + +int socket_fd; +int client_fd; + +//static const ssize_t buf_size = 64 * 1024; +char recv_buf[64 * 1024]; +ssize_t recv_start, recv_end; + int rbs_init(uint16_t port) { socket_fd = 0; @@ -118,6 +136,11 @@ void rbs_reset() trstn = 0; } +void rbs_set() +{ + trstn = 1; +} + void rbs_set_pins(char _tck, char _tms, char _tdi) { tck = _tck; @@ -170,24 +193,23 @@ void rbs_execute_command() case 'r': if (VERBOSE) fprintf(stderr, "r-reset\n"); - rbs_reset(); - break; // This is wrong. 'r' has other bits that indicated TRST and - // SRST. + rbs_set(); //r-reset command deasserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt + break; case 's': if (VERBOSE) fprintf(stderr, "s-reset\n"); - rbs_reset(); - break; // This is wrong. + rbs_set(); //s-reset command deasserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt + break; case 't': if (VERBOSE) fprintf(stderr, "t-reset\n"); - rbs_reset(); - break; // This is wrong. + rbs_reset(); //t-reset command asserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt + break; case 'u': if (VERBOSE) fprintf(stderr, "u-reset\n"); - rbs_reset(); - break; // This is wrong. + rbs_reset(); //u-reset command asserts TRST. See: openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt + break; case '0': if (VERBOSE) fprintf(stderr, "Write 0 0 0\n"); diff --git a/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.h b/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.h index 460819e20c560..f1e261bd2fa21 100644 --- a/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.h +++ b/hw/vendor/pulp_riscv_dbg/tb/remote_bitbang/remote_bitbang.h @@ -8,21 +8,21 @@ #define VERBOSE 0 -int rbs_err; +extern int rbs_err; -unsigned char tck; -unsigned char tms; -unsigned char tdi; -unsigned char trstn; -unsigned char tdo; -unsigned char quit; +extern unsigned char tck; +extern unsigned char tms; +extern unsigned char tdi; +extern unsigned char trstn; +extern unsigned char tdo; +extern unsigned char quit; -int socket_fd; -int client_fd; +extern int socket_fd; +extern int client_fd; -static const ssize_t buf_size = 64 * 1024; -char recv_buf[64 * 1024]; -ssize_t recv_start, recv_end; +//static const ssize_t buf_size = 64 * 1024; +extern char recv_buf[64 * 1024]; +extern ssize_t recv_start, recv_end; // Create a new server, listening for connections from localhost on the given // port. @@ -44,8 +44,8 @@ void rbs_accept(); // simulation to run. void rbs_execute_command(); -// Reset. Currently does nothing. -void rbs_reset(); +void rbs_reset(); //Assert TRST +void rbs_set(); //Deassert TRST void rbs_set_pins(char _tck, char _tms, char _tdi); diff --git a/hw/vendor/pulp_riscv_dbg/tb/veri-run-openocd.py b/hw/vendor/pulp_riscv_dbg/tb/veri-run-openocd.py new file mode 100755 index 0000000000000..a5d7a455f4513 --- /dev/null +++ b/hw/vendor/pulp_riscv_dbg/tb/veri-run-openocd.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +"""Launch riscv-dbg testbench and connect to openocd""" + +import sys +from subprocess import Popen +from subprocess import PIPE, STDOUT +from os import getenv +import shlex + +if __name__ == '__main__': + veri_proc = Popen(shlex.split('make veri-run'), + stdin=PIPE, stdout=PIPE, stderr=STDOUT, + universal_newlines=True) + for line in veri_proc.stdout: + print(line, end='') + if 'Listening on port' in line: + print('Starting OpenOCD') + break + elif 'failed to bind socket' in line: + print("Try 'killall testbench_verilator'", file=sys.stderr) + exit(1) + + # try a few paths where openocd could be + openocd = getenv('OPENOCD') + if not openocd: + openocd = getenv('RISCV') + if openocd: + openocd += '/bin/openocd' + if not openocd: + openocd = 'openocd' + + openocd_script = getenv('OPENOCD_SCRIPT') + if not openocd_script: + openocd_script = 'dm_compliance_test.cfg' + + print("Using '" + openocd) + openocd_proc = Popen(shlex.split(openocd + ' -f ' + openocd_script), + stdin=PIPE, stdout=PIPE, stderr=STDOUT, + universal_newlines=True) + print('Launched OpenOCD') + + ret = 1 + for line in openocd_proc.stdout: + print(line, end='') + if 'ALL TESTS PASSED' in line: + ret = 0 + + # Our spawned processes should have terminated by now. If not, we have to go after + # them with the hammer (openocd likes to ignore sigterms when it gets stuck) + if not openocd_proc.poll(): + openocd_proc.kill() + + if not veri_proc.poll(): + veri_proc.kill() + + exit(ret)