diff --git a/hw/ip/lc_ctrl/rtl/lc_ctrl.sv b/hw/ip/lc_ctrl/rtl/lc_ctrl.sv index d35944fdd4a056..18b6a5d1298bcd 100644 --- a/hw/ip/lc_ctrl/rtl/lc_ctrl.sv +++ b/hw/ip/lc_ctrl/rtl/lc_ctrl.sv @@ -212,7 +212,8 @@ module lc_ctrl logic req_ready; assign req_ready = dmi_req_ready & dmi_resp_ready; dmi_jtag #( - .IdcodeValue(IdcodeValue) + .IdcodeValue(IdcodeValue), + .NumDmiWordAbits(7) ) u_dmi_jtag ( .clk_i, .rst_ni, @@ -264,6 +265,7 @@ module lc_ctrl // These signals are unused logic unused_tap_tl_d2h; assign unused_tap_tl_d2h = ^{ + dmi_req.addr[31:30], tap_tl_d2h.d_opcode, tap_tl_d2h.d_param, tap_tl_d2h.d_size, diff --git a/hw/ip/rv_dm/data/rv_dm.hjson b/hw/ip/rv_dm/data/rv_dm.hjson index 87bdc3a15d1e87..6fee2595ae21c1 100644 --- a/hw/ip/rv_dm/data/rv_dm.hjson +++ b/hw/ip/rv_dm/data/rv_dm.hjson @@ -55,6 +55,18 @@ } ], inter_signal_list: [ + { + package: "rv_dm_pkg" + struct: "next_dm_addr" + type: "uni" + name: "next_dm_addr" + act: "rcv" + default: "'0" + desc: ''' + 32bit word address of the next debug module. + Set to 0x0 if this is the last debug module in the chain. + ''' + }, { struct: "jtag", type: "req_rsp", name: "jtag", diff --git a/hw/ip/rv_dm/doc/interfaces.md b/hw/ip/rv_dm/doc/interfaces.md index a82530b9853306..5d5f44263ae478 100644 --- a/hw/ip/rv_dm/doc/interfaces.md +++ b/hw/ip/rv_dm/doc/interfaces.md @@ -17,6 +17,7 @@ Referring to the [Comportable guideline for peripheral device functionality](htt | Port Name | Package::Struct | Type | Act | Width | Description | |:-------------------------|:-----------------------------------|:--------|:------|--------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| next_dm_addr | rv_dm_pkg::next_dm_addr | uni | rcv | 1 | 32bit word address of the next debug module. Set to 0x0 if this is the last debug module in the chain. | | jtag | jtag_pkg::jtag | req_rsp | rsp | 1 | JTAG signals for the RISC-V TAP. | | lc_hw_debug_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Multibit life cycle hardware debug enable signal coming from life cycle controller, asserted when the hardware debug mechanisms are enabled in the system. | | lc_dft_en | lc_ctrl_pkg::lc_tx | uni | rcv | 1 | Multibit life cycle hardware debug enable signal coming from life cycle controller, asserted when the DFT mechanisms are enabled in the system. | diff --git a/hw/ip/rv_dm/rtl/rv_dm.sv b/hw/ip/rv_dm/rtl/rv_dm.sv index a9530bf887a15e..afa0f5b4efb50f 100644 --- a/hw/ip/rv_dm/rtl/rv_dm.sv +++ b/hw/ip/rv_dm/rtl/rv_dm.sv @@ -21,6 +21,7 @@ module rv_dm input logic clk_i, // clock input logic rst_ni, // asynchronous reset active low, connect PoR // here, not the system reset + input logic [31:0] next_dm_addr_i, // static word address of the next debug module. // SEC_CM: LC_HW_DEBUG_EN.INTERSIG.MUBI // HW Debug lifecycle enable signal (live version from the life cycle controller) input lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i, @@ -402,7 +403,8 @@ module rv_dm // JTAG TAP dmi_jtag #( - .IdcodeValue (IdcodeValue) + .IdcodeValue (IdcodeValue), + .NumDmiWordAbits(7) ) dap ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -497,8 +499,10 @@ module rv_dm ) u_dm_top ( .clk_i, .rst_ni, + .next_dm_addr_i, .testmode_i (testmode ), .ndmreset_o (ndmreset_req ), + .ndmreset_ack_i (ndmreset_req ), .dmactive_o, .debug_req_o (debug_req ), .unavailable_i, diff --git a/hw/ip/rv_dm/rtl/rv_dm_pkg.sv b/hw/ip/rv_dm/rtl/rv_dm_pkg.sv new file mode 100644 index 00000000000000..d9b118e6c8dcb1 --- /dev/null +++ b/hw/ip/rv_dm/rtl/rv_dm_pkg.sv @@ -0,0 +1,12 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +package rv_dm_pkg; + + typedef logic [31:0] next_dm_addr_t; + + parameter next_dm_addr_t NEXT_DM_ADDR_DEFAULT = '0; + +endpackage : rv_dm_pkg diff --git a/hw/ip/rv_dm/rv_dm.core b/hw/ip/rv_dm/rv_dm.core index 8de56a02b8c465..69f92f99a4e649 100644 --- a/hw/ip/rv_dm/rv_dm.core +++ b/hw/ip/rv_dm/rv_dm.core @@ -20,6 +20,7 @@ filesets: files: - rtl/rv_dm_reg_pkg.sv - rtl/rv_dm_regs_reg_top.sv + - rtl/rv_dm_pkg.sv - rtl/rv_dm.sv file_type: systemVerilogSource diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson index 3460c440fc74f6..d085ddb7c9b130 100644 --- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson +++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson @@ -5207,6 +5207,22 @@ ] inter_signal_list: [ + { + name: next_dm_addr + desc: + ''' + 32bit word address of the next debug module. + Set to 0x0 if this is the last debug module in the chain. + ''' + struct: next_dm_addr + package: rv_dm_pkg + type: uni + act: rcv + width: 1 + default: "'0" + inst_name: rv_dm + index: -1 + } { name: jtag desc: JTAG signals for the RISC-V TAP. @@ -18670,6 +18686,22 @@ top_signame: flash_ctrl_mem_tl index: -1 } + { + name: next_dm_addr + desc: + ''' + 32bit word address of the next debug module. + Set to 0x0 if this is the last debug module in the chain. + ''' + struct: next_dm_addr + package: rv_dm_pkg + type: uni + act: rcv + width: 1 + default: "'0" + inst_name: rv_dm + index: -1 + } { name: jtag desc: JTAG signals for the RISC-V TAP. diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv index 60973250c44af4..473c2355aa58ba 100644 --- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv +++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv @@ -2153,6 +2153,7 @@ module top_earlgrey #( .alert_rx_i ( alert_rx[40:40] ), // Inter-module signals + .next_dm_addr_i('0), .jtag_i(pinmux_aon_rv_jtag_req), .jtag_o(pinmux_aon_rv_jtag_rsp), .lc_hw_debug_en_i(lc_ctrl_lc_hw_debug_en), diff --git a/hw/vendor/patches/pulp_riscv_dbg/0001-Fix-lint-errors.patch b/hw/vendor/patches/pulp_riscv_dbg/0001-Fix-lint-errors.patch index 6dae46ef8eeec4..b9a6794861298a 100644 --- a/hw/vendor/patches/pulp_riscv_dbg/0001-Fix-lint-errors.patch +++ b/hw/vendor/patches/pulp_riscv_dbg/0001-Fix-lint-errors.patch @@ -1,15 +1,32 @@ -From e0f9a11ece9c212d5d59183cb4263af415e0ea23 Mon Sep 17 00:00:00 2001 +From 144819373e52cbc15ecb19cf11b374be41a26016 Mon Sep 17 00:00:00 2001 From: Michael Schaffner Date: Tue, 25 Oct 2022 18:28:07 -0700 -Subject: [PATCH 1/3] Fix lint errors +Subject: [PATCH 1/7] Fix lint errors Signed-off-by: Michael Schaffner diff --git a/src/dm_mem.sv b/src/dm_mem.sv -index b14b390..5b2c556 100755 +index 9ff3c86..62cdf02 100755 --- a/src/dm_mem.sv +++ b/src/dm_mem.sv -@@ -265,12 +265,13 @@ module dm_mem #( +@@ -82,7 +82,15 @@ module dm_mem #( + localparam logic [DbgAddressBits-1:0] ResumingAddr = 'h110; + localparam logic [DbgAddressBits-1:0] ExceptionAddr = 'h118; + +- logic [dm::ProgBufSize/2-1:0][63:0] progbuf; ++ localparam logic [DbgAddressBits-1:0] RomBaseAddr = dm::HaltAddress; ++ // The size is arbitrarily set to 0x800, so as to make the dm_space exactly 0x900 long. This is ++ // more than eough to cover the 19 x 64bit = 0x98 bytes currenty allocated in the debug ROM. ++ localparam logic [DbgAddressBits-1:0] RomEndAddr = dm::HaltAddress + 'h7FF; ++ // Prog buff size after repacking the 32bit array into a 64bit array. ++ localparam int unsigned ProgBuf64Size = dm::ProgBufSize / 2; ++ localparam int unsigned ProgBuf64AddrSize = $clog2(ProgBuf64Size); ++ ++ logic [ProgBuf64Size-1:0][63:0] progbuf; + logic [7:0][63:0] abstract_cmd; + logic [NrHarts-1:0] halted_d, halted_q; + logic [NrHarts-1:0] resuming_d, resuming_q; +@@ -265,12 +273,13 @@ module dm_mem #( // core can write data registers [DataBaseAddr:DataEndAddr]: begin data_valid_o = 1'b1; @@ -26,7 +43,7 @@ index b14b390..5b2c556 100755 data_bits[dc+1][(i-4)*8+:8] = wdata_i[i*8+:8]; end end else begin // for lower 32bit data write -@@ -310,8 +311,11 @@ module dm_mem #( +@@ -310,14 +319,17 @@ module dm_mem #( [DataBaseAddr:DataEndAddr]: begin rdata_d = { @@ -40,8 +57,16 @@ index b14b390..5b2c556 100755 }; end + [ProgBufBaseAddr:ProgBufEndAddr]: begin +- rdata_d = progbuf[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - +- ProgBufBaseAddr[DbgAddressBits-1:3])]; ++ rdata_d = progbuf[ProgBuf64AddrSize'(addr_i[DbgAddressBits-1:3] - ++ ProgBufBaseAddr[DbgAddressBits-1:3])]; + end + + // two slots for abstract command diff --git a/src/dmi_jtag.sv b/src/dmi_jtag.sv -index e897bf5..6be89a6 100644 +index 577c3fd..24edad0 100644 --- a/src/dmi_jtag.sv +++ b/src/dmi_jtag.sv @@ -58,6 +58,7 @@ module dmi_jtag #( diff --git a/hw/vendor/patches/pulp_riscv_dbg/0002-Add-access-error-signal-to-dm_mem.patch b/hw/vendor/patches/pulp_riscv_dbg/0002-Add-access-error-signal-to-dm_mem.patch index 76a3aa040bdc4c..74346e20d2bde5 100644 --- a/hw/vendor/patches/pulp_riscv_dbg/0002-Add-access-error-signal-to-dm_mem.patch +++ b/hw/vendor/patches/pulp_riscv_dbg/0002-Add-access-error-signal-to-dm_mem.patch @@ -1,12 +1,12 @@ -From 107fe81668549a470caccc46aad1d74c1647042c Mon Sep 17 00:00:00 2001 +From d2fe1d8aa26b8d6c03ada39962bdf779d0c43efd Mon Sep 17 00:00:00 2001 From: Michael Schaffner Date: Tue, 25 Oct 2022 19:38:49 -0700 -Subject: [PATCH 2/3] Add access error signal to dm_mem +Subject: [PATCH 2/7] Add access error signal to dm_mem Signed-off-by: Michael Schaffner diff --git a/src/dm_mem.sv b/src/dm_mem.sv -index 5b2c556..cc0cc69 100755 +index 62cdf02..f7a5f7d 100755 --- a/src/dm_mem.sv +++ b/src/dm_mem.sv @@ -20,7 +20,8 @@ module dm_mem #( @@ -31,19 +31,7 @@ index 5b2c556..cc0cc69 100755 ); localparam int unsigned DbgAddressBits = 12; localparam int unsigned HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); -@@ -82,6 +84,11 @@ module dm_mem #( - localparam logic [DbgAddressBits-1:0] ResumingAddr = 'h110; - localparam logic [DbgAddressBits-1:0] ExceptionAddr = 'h118; - -+ localparam logic [DbgAddressBits-1:0] RomBaseAddr = dm::HaltAddress; -+ // The size is arbitrarily set to 0x800, so as to make the dm_space exactly 0x900 long. This is -+ // more than eough to cover the 19 x 64bit = 0x98 bytes currenty allocated in the debug ROM. -+ localparam logic [DbgAddressBits-1:0] RomEndAddr = dm::HaltAddress + 'h7FF; -+ - logic [dm::ProgBufSize/2-1:0][63:0] progbuf; - logic [7:0][63:0] abstract_cmd; - logic [NrHarts-1:0] halted_d, halted_q; -@@ -231,6 +238,7 @@ module dm_mem #( +@@ -239,6 +241,7 @@ module dm_mem #( rdata_d = rdata_q; data_bits = data_i; rdata = '0; @@ -51,7 +39,7 @@ index 5b2c556..cc0cc69 100755 // write data in csr register data_valid_o = 1'b0; -@@ -282,7 +290,7 @@ module dm_mem #( +@@ -290,7 +293,7 @@ module dm_mem #( end end end @@ -60,7 +48,7 @@ index 5b2c556..cc0cc69 100755 endcase // this is a read -@@ -339,6 +347,11 @@ module dm_mem #( +@@ -347,6 +350,11 @@ module dm_mem #( end rdata_d = rdata; end @@ -72,7 +60,7 @@ index 5b2c556..cc0cc69 100755 default: ; endcase end -@@ -353,6 +366,54 @@ module dm_mem #( +@@ -361,6 +369,54 @@ module dm_mem #( data_o = data_bits; end @@ -127,7 +115,7 @@ index 5b2c556..cc0cc69 100755 always_comb begin : p_abstract_cmd_rom // this abstract command is currently unsupported unsupported_command = 1'b0; -@@ -517,10 +578,6 @@ module dm_mem #( +@@ -527,10 +583,6 @@ module dm_mem #( ); end diff --git a/hw/vendor/patches/pulp_riscv_dbg/0003-Use-lowrisc-instead-of-PULP-primitives.patch b/hw/vendor/patches/pulp_riscv_dbg/0003-Use-lowrisc-instead-of-PULP-primitives.patch index 6bcb36edfc7b40..f949eb1ebd02a6 100644 --- a/hw/vendor/patches/pulp_riscv_dbg/0003-Use-lowrisc-instead-of-PULP-primitives.patch +++ b/hw/vendor/patches/pulp_riscv_dbg/0003-Use-lowrisc-instead-of-PULP-primitives.patch @@ -1,7 +1,7 @@ -From 51db2dfa1d52ea995f244fba457a101215381382 Mon Sep 17 00:00:00 2001 +From 9323ea206e15381283e67a780aac8ffbb5a5d19b Mon Sep 17 00:00:00 2001 From: Michael Schaffner Date: Tue, 25 Oct 2022 18:27:27 -0700 -Subject: [PATCH 3/3] Use lowrisc instead of PULP primitives +Subject: [PATCH 3/7] Use lowrisc instead of PULP primitives Signed-off-by: Michael Schaffner @@ -235,7 +235,7 @@ index f9d66fd..ef9e57c 100644 endmodule : dmi_cdc diff --git a/src/dmi_jtag.sv b/src/dmi_jtag.sv -index 6be89a6..a7e5bff 100644 +index 24edad0..f8b8483 100644 --- a/src/dmi_jtag.sv +++ b/src/dmi_jtag.sv @@ -22,9 +22,10 @@ module dmi_jtag #( diff --git a/hw/vendor/patches/pulp_riscv_dbg/0004-Extend-DMI-address-width.patch b/hw/vendor/patches/pulp_riscv_dbg/0004-Extend-DMI-address-width.patch new file mode 100644 index 00000000000000..5c1658e0680339 --- /dev/null +++ b/hw/vendor/patches/pulp_riscv_dbg/0004-Extend-DMI-address-width.patch @@ -0,0 +1,97 @@ +From c1d7ebe40689eced35ae6b8cc2f1431f6ce42e51 Mon Sep 17 00:00:00 2001 +From: Michael Schaffner +Date: Thu, 17 Aug 2023 10:30:17 -0700 +Subject: [PATCH 4/7] Extend DMI address width + +Make the DMI interface address 32bits so that larger +address ranges can be supported (e.g. to take advantage +of a TL-UL to DMI bridge). + +The effective number of address bits is made configurable +in the TAP so that this can be determined based on +the specific integration. + +Signed-off-by: Michael Schaffner + +diff --git a/src/dm_csrs.sv b/src/dm_csrs.sv +index 259f3fc..b899b17 100644 +--- a/src/dm_csrs.sv ++++ b/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); + +- + 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); + +@@ -212,6 +211,9 @@ module dm_csrs #( + + // Get the data index, i.e. 0 for dm::Data0 up to 11 for dm::Data11 + assign dm_csr_addr = dm::dm_csr_e'({1'b0, dmi_req_i.addr}); ++ logic unused_addr_bits; ++ assign unused_addr_bits = ^dmi_req_i.addr[31:$bits(dm_csr_addr)]; ++ + // Xilinx Vivado 2020.1 does not allow subtraction of two enums; do the subtraction with logic + // types instead. + assign autoexecdata_idx = 4'({dm_csr_addr} - {dm::Data0}); +diff --git a/src/dm_pkg.sv b/src/dm_pkg.sv +index cc2e1eb..a8a27d2 100644 +--- a/src/dm_pkg.sv ++++ b/src/dm_pkg.sv +@@ -222,7 +222,7 @@ package dm; + } sbcs_t; + + typedef struct packed { +- logic [6:0] addr; ++ logic [31:0] addr; + dtm_op_e op; + logic [31:0] data; + } dmi_req_t; +diff --git a/src/dmi_jtag.sv b/src/dmi_jtag.sv +index f8b8483..b82bde1 100644 +--- a/src/dmi_jtag.sv ++++ b/src/dmi_jtag.sv +@@ -17,7 +17,8 @@ + */ + + module dmi_jtag #( +- parameter logic [31:0] IdcodeValue = 32'h00000DB3 ++ parameter logic [31:0] IdcodeValue = 32'h00000DB3, ++ parameter int unsigned NumDmiWordAbits = 16 // Number of DMI address bits (7 - 32) + ) ( + input logic clk_i, // DMI Clock + input logic rst_ni, // Asynchronous reset active low +@@ -78,7 +79,7 @@ module dmi_jtag #( + zero0 : '0, + idle : 3'd1, // 1: Enter Run-Test/Idle and leave it immediately + dmistat : error_q, // 0: No error, 2: Op failed, 3: too fast +- abits : 6'd7, // The size of address in dmi ++ abits : 6'(NumDmiWordAbits), // The size of address in dmi + version : 4'd1 // Version described in spec version 0.13 (and later?) + }; + end +@@ -113,7 +114,7 @@ module dmi_jtag #( + logic dmi_resp_ready; + + typedef struct packed { +- logic [6:0] address; ++ logic [NumDmiWordAbits-1:0] address; + logic [31:0] data; + logic [1:0] op; + } dmi_t; +@@ -122,12 +123,12 @@ module dmi_jtag #( + state_e state_d, state_q; + + logic [$bits(dmi_t)-1:0] dr_d, dr_q; +- logic [6:0] address_d, address_q; ++ logic [NumDmiWordAbits-1:0] address_d, address_q; + logic [31:0] data_d, data_q; + + dmi_t dmi; + assign dmi = dmi_t'(dr_q); +- assign dmi_req.addr = address_q; ++ assign dmi_req.addr = $bits(dmi_req.addr)'(address_q); + assign dmi_req.data = data_q; + assign dmi_req.op = (state_q == Write) ? dm::DTM_WRITE : dm::DTM_READ; + // We will always be ready to accept the data we requested. diff --git a/hw/vendor/patches/pulp_riscv_dbg/0005-dm_csrs-Implement-nextdm-register.patch b/hw/vendor/patches/pulp_riscv_dbg/0005-dm_csrs-Implement-nextdm-register.patch new file mode 100644 index 00000000000000..59d1457bc8c56b --- /dev/null +++ b/hw/vendor/patches/pulp_riscv_dbg/0005-dm_csrs-Implement-nextdm-register.patch @@ -0,0 +1,83 @@ +From 149fb72ccfe2af0a18e37884146c73e214817050 Mon Sep 17 00:00:00 2001 +From: Michael Schaffner +Date: Tue, 21 Nov 2023 19:40:16 -0800 +Subject: [PATCH 5/7] [dm_csrs] Implement nextdm register + +Signed-off-by: Michael Schaffner + +diff --git a/src/dm_csrs.sv b/src/dm_csrs.sv +index b899b17..9686883 100644 +--- a/src/dm_csrs.sv ++++ b/src/dm_csrs.sv +@@ -22,6 +22,7 @@ module dm_csrs #( + ) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low ++ input logic [31:0] next_dm_addr_i, // Static next_dm word address. + input logic testmode_i, + input logic dmi_rst_ni, // sync. DTM reset, + // active-low +@@ -307,8 +308,8 @@ module dm_csrs #( + 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_inp.data = '0; ++ dm::Command: resp_queue_inp.data = '0; ++ dm::NextDM: resp_queue_inp.data = next_dm_addr_i; + [(dm::ProgBuf0):ProgBufEnd]: begin + resp_queue_inp.data = progbuf_q[dmi_req_i.addr[$clog2(dm::ProgBufSize)-1:0]]; + if (!cmdbusy_i) begin +@@ -419,6 +420,7 @@ module dm_csrs #( + end + end + end ++ dm::NextDM:; // nextdm is R/O + dm::AbstractAuto: begin + // this field can only be written legally when there is no command executing + if (!cmdbusy_i) begin +diff --git a/src/dm_top.sv b/src/dm_top.sv +index 6188b28..89dc590 100644 +--- a/src/dm_top.sv ++++ b/src/dm_top.sv +@@ -30,6 +30,12 @@ module dm_top #( + input logic clk_i, // clock + // asynchronous reset active low, connect PoR here, not the system reset + input logic rst_ni, ++ // Subsequent debug modules can be chained by setting the nextdm register value to the offset of ++ // the next debug module. The RISC-V debug spec mandates that the first debug module located at ++ // 0x0, and that the last debug module in the chain sets the nextdm register to 0x0. The nextdm ++ // register is a word address and not a byte address. This value is passed in as a static signal ++ // so that it becomes possible to assign this value with chiplet tie-offs or straps, if needed. ++ input logic [31:0] next_dm_addr_i, + input logic testmode_i, + output logic ndmreset_o, // non-debug module reset + output logic dmactive_o, // debug module is active +@@ -115,6 +121,7 @@ module dm_top #( + ) i_dm_csrs ( + .clk_i, + .rst_ni, ++ .next_dm_addr_i, + .testmode_i, + .dmi_rst_ni, + .dmi_req_valid_i, +diff --git a/tb/tb_test_env.sv b/tb/tb_test_env.sv +index 4b300ca..9ac8f28 100644 +--- a/tb/tb_test_env.sv ++++ b/tb/tb_test_env.sv +@@ -245,6 +245,7 @@ module tb_test_env #( + + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), ++ .next_dm_addr_i ( '0 ), + .testmode_i ( 1'b0 ), + .ndmreset_o ( ndmreset ), + .dmactive_o ( ), // active debug session TODO +@@ -258,6 +259,7 @@ module tb_test_env #( + .slave_be_i ( dm_be ), + .slave_wdata_i ( dm_wdata ), + .slave_rdata_o ( dm_rdata ), ++ .slave_err_o ( ), + + .master_req_o ( sb_req ), + .master_add_o ( sb_addr ), diff --git a/hw/vendor/patches/pulp_riscv_dbg/0006-dm_csrs-Correct-behavior-of-hartsel.patch b/hw/vendor/patches/pulp_riscv_dbg/0006-dm_csrs-Correct-behavior-of-hartsel.patch new file mode 100644 index 00000000000000..2d99be38af8ce9 --- /dev/null +++ b/hw/vendor/patches/pulp_riscv_dbg/0006-dm_csrs-Correct-behavior-of-hartsel.patch @@ -0,0 +1,24 @@ +From d7544597def65a4fcd7a853ed64604bdc24ae9df Mon Sep 17 00:00:00 2001 +From: Michael Schaffner +Date: Wed, 13 Mar 2024 12:15:24 -0700 +Subject: [PATCH 6/7] [dm_csrs] Correct behavior of hartsel + +The hartsel register is supposed to have WARL behavior depending on how +many harts are supported. + +Signed-off-by: Michael Schaffner + +diff --git a/src/dm_csrs.sv b/src/dm_csrs.sv +index 9686883..e90a96a 100644 +--- a/src/dm_csrs.sv ++++ b/src/dm_csrs.sv +@@ -548,6 +548,9 @@ module dm_csrs #( + if (dmcontrol_q.resumereq && resumeack_i) begin + dmcontrol_d.resumereq = 1'b0; + end ++ // WARL behavior of hartsel, depending on NrHarts. ++ // If NrHarts = 1 this is just masked to all-zeros. ++ {dmcontrol_d.hartselhi, dmcontrol_d.hartsello} &= (2**$clog2(NrHarts))-1; + // static values for dcsr + sbcs_d.sbversion = 3'd1; + sbcs_d.sbbusy = sbbusy_i; diff --git a/hw/vendor/patches/pulp_riscv_dbg/0007-dm_csrs-Pull-out-acknowledgement-signal-for-ndmreset.patch b/hw/vendor/patches/pulp_riscv_dbg/0007-dm_csrs-Pull-out-acknowledgement-signal-for-ndmreset.patch new file mode 100644 index 00000000000000..1cab824c43ce24 --- /dev/null +++ b/hw/vendor/patches/pulp_riscv_dbg/0007-dm_csrs-Pull-out-acknowledgement-signal-for-ndmreset.patch @@ -0,0 +1,53 @@ +From 91dd09788d0151fb48b85fa32eb07e9e1b0b0dd3 Mon Sep 17 00:00:00 2001 +From: Michael Schaffner +Date: Thu, 14 Mar 2024 09:36:40 -0700 +Subject: [PATCH 7/7] [dm_csrs] Pull out acknowledgement signal for ndmreset + +This signal can be used to implement more accurate ndmreset completion +tracking outside of RV_DM. + +Signed-off-by: Michael Schaffner + +diff --git a/src/dm_csrs.sv b/src/dm_csrs.sv +index e90a96a..a800141 100644 +--- a/src/dm_csrs.sv ++++ b/src/dm_csrs.sv +@@ -35,6 +35,7 @@ module dm_csrs #( + output dm::dmi_resp_t dmi_resp_o, + // global ctrl + output logic ndmreset_o, // non-debug module reset active-high ++ input logic ndmreset_ack_i, // non-debug module reset ack pulse + output logic dmactive_o, // 1 -> debug-module is active, + // 0 -> synchronous re-set + // hart status +@@ -515,8 +516,8 @@ module dm_csrs #( + data_d = data_i; + end + +- // set the havereset flag when we did a ndmreset +- if (ndmreset_o) begin ++ // set the havereset flag when the ndmreset completed ++ if (ndmreset_ack_i) begin + havereset_d_aligned[NrHarts-1:0] = '1; + end + // ------------- +diff --git a/src/dm_top.sv b/src/dm_top.sv +index 89dc590..87b480a 100644 +--- a/src/dm_top.sv ++++ b/src/dm_top.sv +@@ -38,6 +38,7 @@ module dm_top #( + input logic [31:0] next_dm_addr_i, + input logic testmode_i, + output logic ndmreset_o, // non-debug module reset ++ input logic ndmreset_ack_i, // non-debug module reset acknowledgement pulse + output logic dmactive_o, // debug module is active + output logic [NrHarts-1:0] debug_req_o, // async debug request + // communicate whether the hart is unavailable (e.g.: power down) +@@ -131,6 +132,7 @@ module dm_top #( + .dmi_resp_ready_i, + .dmi_resp_o, + .ndmreset_o ( ndmreset ), ++ .ndmreset_ack_i ( ndmreset_ack_i ), + .dmactive_o, + .hartsel_o ( hartsel ), + .hartinfo_i, diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv b/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv index 259f3fcca8901e..a800141b3ba92b 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_csrs.sv @@ -22,6 +22,7 @@ module dm_csrs #( ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low + input logic [31:0] next_dm_addr_i, // Static next_dm word address. input logic testmode_i, input logic dmi_rst_ni, // sync. DTM reset, // active-low @@ -34,6 +35,7 @@ module dm_csrs #( output dm::dmi_resp_t dmi_resp_o, // global ctrl output logic ndmreset_o, // non-debug module reset active-high + input logic ndmreset_ack_i, // non-debug module reset ack pulse output logic dmactive_o, // 1 -> debug-module is active, // 0 -> synchronous re-set // hart status @@ -87,7 +89,6 @@ module dm_csrs #( dm::dtm_op_e dtm_op; assign dtm_op = dm::dtm_op_e'(dmi_req_i.op); - 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); @@ -212,6 +213,9 @@ module dm_csrs #( // Get the data index, i.e. 0 for dm::Data0 up to 11 for dm::Data11 assign dm_csr_addr = dm::dm_csr_e'({1'b0, dmi_req_i.addr}); + logic unused_addr_bits; + assign unused_addr_bits = ^dmi_req_i.addr[31:$bits(dm_csr_addr)]; + // Xilinx Vivado 2020.1 does not allow subtraction of two enums; do the subtraction with logic // types instead. assign autoexecdata_idx = 4'({dm_csr_addr} - {dm::Data0}); @@ -305,8 +309,8 @@ module dm_csrs #( 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_inp.data = '0; + dm::Command: resp_queue_inp.data = '0; + dm::NextDM: resp_queue_inp.data = next_dm_addr_i; [(dm::ProgBuf0):ProgBufEnd]: begin resp_queue_inp.data = progbuf_q[dmi_req_i.addr[$clog2(dm::ProgBufSize)-1:0]]; if (!cmdbusy_i) begin @@ -417,6 +421,7 @@ module dm_csrs #( end end end + dm::NextDM:; // nextdm is R/O dm::AbstractAuto: begin // this field can only be written legally when there is no command executing if (!cmdbusy_i) begin @@ -511,8 +516,8 @@ module dm_csrs #( data_d = data_i; end - // set the havereset flag when we did a ndmreset - if (ndmreset_o) begin + // set the havereset flag when the ndmreset completed + if (ndmreset_ack_i) begin havereset_d_aligned[NrHarts-1:0] = '1; end // ------------- @@ -544,6 +549,9 @@ module dm_csrs #( if (dmcontrol_q.resumereq && resumeack_i) begin dmcontrol_d.resumereq = 1'b0; end + // WARL behavior of hartsel, depending on NrHarts. + // If NrHarts = 1 this is just masked to all-zeros. + {dmcontrol_d.hartselhi, dmcontrol_d.hartsello} &= (2**$clog2(NrHarts))-1; // static values for dcsr sbcs_d.sbversion = 3'd1; sbcs_d.sbbusy = sbbusy_i; diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv b/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv index d0d12feaeffcee..f7a5f7df514057 100755 --- a/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_mem.sv @@ -88,8 +88,11 @@ module dm_mem #( // The size is arbitrarily set to 0x800, so as to make the dm_space exactly 0x900 long. This is // more than eough to cover the 19 x 64bit = 0x98 bytes currenty allocated in the debug ROM. localparam logic [DbgAddressBits-1:0] RomEndAddr = dm::HaltAddress + 'h7FF; + // Prog buff size after repacking the 32bit array into a 64bit array. + localparam int unsigned ProgBuf64Size = dm::ProgBufSize / 2; + localparam int unsigned ProgBuf64AddrSize = $clog2(ProgBuf64Size); - logic [dm::ProgBufSize/2-1:0][63:0] progbuf; + logic [ProgBuf64Size-1:0][63:0] progbuf; logic [7:0][63:0] abstract_cmd; logic [NrHarts-1:0] halted_d, halted_q; logic [NrHarts-1:0] resuming_d, resuming_q; @@ -328,8 +331,8 @@ module dm_mem #( end [ProgBufBaseAddr:ProgBufEndAddr]: begin - rdata_d = progbuf[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - - ProgBufBaseAddr[DbgAddressBits-1:3])]; + rdata_d = progbuf[ProgBuf64AddrSize'(addr_i[DbgAddressBits-1:3] - + ProgBufBaseAddr[DbgAddressBits-1:3])]; end // two slots for abstract command diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv b/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv index cc2e1ebcfadf09..a8a27d2f1fa314 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_pkg.sv @@ -222,7 +222,7 @@ package dm; } sbcs_t; typedef struct packed { - logic [6:0] addr; + logic [31:0] addr; dtm_op_e op; logic [31:0] data; } dmi_req_t; diff --git a/hw/vendor/pulp_riscv_dbg/src/dm_top.sv b/hw/vendor/pulp_riscv_dbg/src/dm_top.sv index 6188b285dbabff..87b480aa1ce9aa 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dm_top.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dm_top.sv @@ -30,8 +30,15 @@ module dm_top #( input logic clk_i, // clock // asynchronous reset active low, connect PoR here, not the system reset input logic rst_ni, + // Subsequent debug modules can be chained by setting the nextdm register value to the offset of + // the next debug module. The RISC-V debug spec mandates that the first debug module located at + // 0x0, and that the last debug module in the chain sets the nextdm register to 0x0. The nextdm + // register is a word address and not a byte address. This value is passed in as a static signal + // so that it becomes possible to assign this value with chiplet tie-offs or straps, if needed. + input logic [31:0] next_dm_addr_i, input logic testmode_i, output logic ndmreset_o, // non-debug module reset + input logic ndmreset_ack_i, // non-debug module reset acknowledgement pulse output logic dmactive_o, // debug module is active output logic [NrHarts-1:0] debug_req_o, // async debug request // communicate whether the hart is unavailable (e.g.: power down) @@ -115,6 +122,7 @@ module dm_top #( ) i_dm_csrs ( .clk_i, .rst_ni, + .next_dm_addr_i, .testmode_i, .dmi_rst_ni, .dmi_req_valid_i, @@ -124,6 +132,7 @@ module dm_top #( .dmi_resp_ready_i, .dmi_resp_o, .ndmreset_o ( ndmreset ), + .ndmreset_ack_i ( ndmreset_ack_i ), .dmactive_o, .hartsel_o ( hartsel ), .hartinfo_i, diff --git a/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv b/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv index f8b8483bca039b..b82bde1248335c 100644 --- a/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv +++ b/hw/vendor/pulp_riscv_dbg/src/dmi_jtag.sv @@ -17,7 +17,8 @@ */ module dmi_jtag #( - parameter logic [31:0] IdcodeValue = 32'h00000DB3 + parameter logic [31:0] IdcodeValue = 32'h00000DB3, + parameter int unsigned NumDmiWordAbits = 16 // Number of DMI address bits (7 - 32) ) ( input logic clk_i, // DMI Clock input logic rst_ni, // Asynchronous reset active low @@ -78,7 +79,7 @@ module dmi_jtag #( zero0 : '0, idle : 3'd1, // 1: Enter Run-Test/Idle and leave it immediately dmistat : error_q, // 0: No error, 2: Op failed, 3: too fast - abits : 6'd7, // The size of address in dmi + abits : 6'(NumDmiWordAbits), // The size of address in dmi version : 4'd1 // Version described in spec version 0.13 (and later?) }; end @@ -113,7 +114,7 @@ module dmi_jtag #( logic dmi_resp_ready; typedef struct packed { - logic [6:0] address; + logic [NumDmiWordAbits-1:0] address; logic [31:0] data; logic [1:0] op; } dmi_t; @@ -122,12 +123,12 @@ module dmi_jtag #( state_e state_d, state_q; logic [$bits(dmi_t)-1:0] dr_d, dr_q; - logic [6:0] address_d, address_q; + logic [NumDmiWordAbits-1:0] address_d, address_q; logic [31:0] data_d, data_q; dmi_t dmi; assign dmi = dmi_t'(dr_q); - assign dmi_req.addr = address_q; + assign dmi_req.addr = $bits(dmi_req.addr)'(address_q); assign dmi_req.data = data_q; assign dmi_req.op = (state_q == Write) ? dm::DTM_WRITE : dm::DTM_READ; // We will always be ready to accept the data we requested. diff --git a/hw/vendor/pulp_riscv_dbg/tb/tb_test_env.sv b/hw/vendor/pulp_riscv_dbg/tb/tb_test_env.sv index 4b300ca62bcba0..9ac8f28a57c8b5 100644 --- a/hw/vendor/pulp_riscv_dbg/tb/tb_test_env.sv +++ b/hw/vendor/pulp_riscv_dbg/tb/tb_test_env.sv @@ -245,6 +245,7 @@ module tb_test_env #( .clk_i ( clk_i ), .rst_ni ( rst_ni ), + .next_dm_addr_i ( '0 ), .testmode_i ( 1'b0 ), .ndmreset_o ( ndmreset ), .dmactive_o ( ), // active debug session TODO @@ -258,6 +259,7 @@ module tb_test_env #( .slave_be_i ( dm_be ), .slave_wdata_i ( dm_wdata ), .slave_rdata_o ( dm_rdata ), + .slave_err_o ( ), .master_req_o ( sb_req ), .master_add_o ( sb_addr ), diff --git a/sw/host/tests/chip/rv_dm/src/control_status.rs b/sw/host/tests/chip/rv_dm/src/control_status.rs index 677ca999aa1f7e..bea56cc024a751 100644 --- a/sw/host/tests/chip/rv_dm/src/control_status.rs +++ b/sw/host/tests/chip/rv_dm/src/control_status.rs @@ -47,14 +47,9 @@ fn test_control_status(opts: &Opts, transport: &TransportWrapper) -> Result<()> | 0x3ff << consts::DMCONTROL_HARTSELHI_SHIFT | consts::DMCONTROL_DMACTIVE_MASK; dmi.dmi_write(consts::DMCONTROL, dmcontrol)?; - assert_eq!(dmi.dmi_read(consts::DMCONTROL)?, dmcontrol); - - // Write 0 to hartsel and confirm readback. - let dmcontrol = 0 << consts::DMCONTROL_HARTSELLO_SHIFT - | 0 << consts::DMCONTROL_HARTSELHI_SHIFT - | consts::DMCONTROL_DMACTIVE_MASK; - dmi.dmi_write(consts::DMCONTROL, dmcontrol)?; - assert_eq!(dmi.dmi_read(consts::DMCONTROL)?, dmcontrol); + // Since this target only supports 1 hart, the WARL behavior of this register is such that no + // bits should be set at this point. + assert_eq!(dmi.dmi_read(consts::DMCONTROL)?, 0); let mut hart = dmi.select_hart(0)?; assert!(hart.state()?.running);