| ////////////////////////////////////////////////////////////////////////////// |
| // SPDX-FileCopyrightText: Syntacore LLC © 2016-2021 |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-FileContributor: Syntacore LLC |
| // ////////////////////////////////////////////////////////////////////////// |
| /// @file <scr1_pipe_hdu.sv> |
| /// @brief HART Debug Unit (HDU) |
| /// |
| |
| //------------------------------------------------------------------------------ |
| // |
| // |
| // Functionality: |
| // - Controls HART state (RUN, Debug RUN, Debug HALTED) |
| // - Setups Debug Mode execution |
| // - Provides status information about Debug Mode execution |
| // - Provides Program Buffer functionality (a few instructions execution while |
| // in Debug Mode) |
| // - Provides access to Debug CSRs |
| // |
| // Structure: |
| // - Debug state FSM |
| // - HART Control logic |
| // - HART Status logic |
| // - Program Buffer |
| // - Debug CSRs |
| // - HDU <-> DM interface |
| // - HDU <-> EXU interface |
| // - HDU <-> IFU interface |
| // - HDU <-> CSR interface |
| // - HDU <-> TDU interface |
| // |
| //------------------------------------------------------------------------------ |
| |
| `include "scr1_arch_description.svh" |
| |
| `ifdef SCR1_DBG_EN |
| `include "scr1_arch_types.svh" |
| `include "scr1_riscv_isa_decoding.svh" |
| `include "scr1_hdu.svh" |
| |
| module scr1_pipe_hdu #(parameter HART_PBUF_INSTR_REGOUT_EN = 1'b1) ( |
| // Common signals |
| input logic rst_n, // HDU reset |
| input logic clk, // HDU clock |
| input logic clk_en, // HDU clock enable |
| `ifdef SCR1_CLKCTRL_EN |
| input logic clk_pipe_en, // Pipeline clock enable |
| `endif // SCR1_CLKCTRL_EN |
| input logic pipe2hdu_rdc_qlfy_i, // Pipeline RDC qualifier |
| |
| // HDU <-> CSR i/f |
| input logic csr2hdu_req_i, // CSR i/f request |
| input type_scr1_csr_cmd_sel_e csr2hdu_cmd_i, // CSR i/f command |
| input logic [SCR1_HDU_DEBUGCSR_ADDR_WIDTH-1:0] csr2hdu_addr_i, // CSR i/f address |
| input logic [`SCR1_XLEN-1:0] csr2hdu_wdata_i, // CSR i/f write data |
| output type_scr1_csr_resp_e hdu2csr_resp_o, // CSR i/f response |
| output logic [`SCR1_XLEN-1:0] hdu2csr_rdata_o, // CSR i/f read data |
| |
| // HDU <-> DM i/f |
| // HART Run Control i/f |
| input logic dm2hdu_cmd_req_i, // DM-HART Command request |
| input type_scr1_hdu_dbgstates_e dm2hdu_cmd_i, // DM-HART Command |
| output logic hdu2dm_cmd_resp_o, // DM-HART Command response |
| output logic hdu2dm_cmd_rcode_o, // DM-HART Command return code: 0 - Ok; 1 - Error |
| output logic hdu2dm_hart_event_o, // DM-HART Event: 1 if HART debug state changed |
| output type_scr1_hdu_hartstatus_s hdu2dm_hart_status_o, // DM-HART Status |
| |
| // Program Buffer i/f |
| output logic [SCR1_HDU_PBUF_ADDR_WIDTH-1:0] hdu2dm_pbuf_addr_o, // Program Buffer address - so far request only for 1 instruction |
| input logic [SCR1_HDU_CORE_INSTR_WIDTH-1:0] dm2hdu_pbuf_instr_i, // Program Buffer instruction |
| |
| // HART Abstract Data regs i/f |
| output logic hdu2dm_dreg_req_o, // Abstract Data Register request |
| output logic hdu2dm_dreg_wr_o, // Abstract Data Register write |
| output logic [`SCR1_XLEN-1:0] hdu2dm_dreg_wdata_o, // Abstract Data Register write data |
| input logic dm2hdu_dreg_resp_i, // Abstract Data Register response |
| input logic dm2hdu_dreg_fail_i, // Abstract Data Register fail |
| input logic [`SCR1_XLEN-1:0] dm2hdu_dreg_rdata_i, // Abstract Data Register read data |
| |
| `ifdef SCR1_TDU_EN |
| // HDU <-> TDU interface |
| output logic hdu2tdu_hwbrk_dsbl_o, // Disables BRKM |
| input logic tdu2hdu_dmode_req_i, // Trigger Module requests transition to debug mode |
| input logic exu2hdu_ibrkpt_hw_i, // Hardware breakpoint on current instruction |
| `endif // SCR1_TDU_EN |
| |
| // HART Run Status |
| input logic pipe2hdu_exu_busy_i, // EXU busy |
| input logic pipe2hdu_instret_i, // Instruction retired (with or without exception) |
| input logic pipe2hdu_init_pc_i, // Reset exit |
| |
| // HART Halt Status |
| input logic pipe2hdu_exu_exc_req_i, // Exception request |
| input logic pipe2hdu_brkpt_i, // Software Breakpoint (EBREAK) |
| |
| // HDU <-> EXU i/f |
| // HART Run Control |
| output logic hdu2exu_pbuf_fetch_o, // Fetch instruction from Program Buffer |
| output logic hdu2exu_no_commit_o, // Forbid instruction commitment |
| output logic hdu2exu_irq_dsbl_o, // Disable IRQ |
| output logic hdu2exu_pc_advmt_dsbl_o, // Forbid PC advancement |
| output logic hdu2exu_dmode_sstep_en_o, // Enable single-step |
| |
| // HART state |
| output logic hdu2exu_dbg_halted_o, // Debug halted state |
| output logic hdu2exu_dbg_run2halt_o, // Transition to debug halted state |
| output logic hdu2exu_dbg_halt2run_o, // Transition to run state |
| output logic hdu2exu_dbg_run_start_o, // First cycle of run state |
| |
| // PC interface |
| input logic [`SCR1_XLEN-1:0] pipe2hdu_pc_curr_i, // Current PC |
| output logic [`SCR1_XLEN-1:0] hdu2exu_dbg_new_pc_o, // New PC for resume |
| |
| // HDU <-> IFU i/f |
| // Program Buffer Instruction interface |
| input logic ifu2hdu_pbuf_instr_rdy_i, // Program Buffer Instruction i/f ready |
| output logic hdu2ifu_pbuf_instr_vd_o, // Program Buffer Instruction valid |
| output logic hdu2ifu_pbuf_instr_err_o, // Program Buffer Instruction i/f error |
| output logic [SCR1_HDU_CORE_INSTR_WIDTH-1:0] hdu2ifu_pbuf_instr_o // Program Buffer Instruction itself |
| ); |
| |
| //------------------------------------------------------------------------------ |
| // Local Parameters |
| //------------------------------------------------------------------------------ |
| |
| localparam int unsigned SCR1_HDU_TIMEOUT = 64; // must be power of 2 |
| localparam int unsigned SCR1_HDU_TIMEOUT_WIDTH = $clog2(SCR1_HDU_TIMEOUT); |
| |
| //------------------------------------------------------------------------------ |
| // Local Signals |
| //------------------------------------------------------------------------------ |
| |
| // Debug FSM |
| //------------------------------------------------------------------------------ |
| |
| // FSM control signals |
| logic dm_dhalt_req; |
| logic dm_run_req; |
| |
| logic dm_cmd_run; |
| logic dm_cmd_dhalted; |
| logic dm_cmd_drun; |
| |
| // Debug state FSM signals |
| type_scr1_hdu_dbgstates_e dbg_state; |
| type_scr1_hdu_dbgstates_e dbg_state_next; |
| logic dbg_state_dhalted; |
| logic dbg_state_drun; |
| logic dbg_state_run; |
| logic dbg_state_reset; |
| |
| // FSM transition, update and event registers |
| logic dfsm_trans; |
| logic dfsm_trans_next; |
| logic dfsm_update; |
| logic dfsm_update_next; |
| logic dfsm_event; |
| logic dfsm_event_next; |
| |
| // HART Control signals |
| //------------------------------------------------------------------------------ |
| |
| logic hart_resume_req; |
| logic hart_halt_req; |
| logic hart_cmd_req; |
| |
| // HART Run Control register |
| logic hart_runctrl_upd; |
| logic hart_runctrl_clr; |
| type_scr1_hdu_runctrl_s hart_runctrl; |
| |
| // HART halt request timeout counter signals |
| logic [SCR1_HDU_TIMEOUT_WIDTH-1:0] halt_req_timeout_cnt; |
| logic [SCR1_HDU_TIMEOUT_WIDTH-1:0] halt_req_timeout_cnt_next; |
| logic halt_req_timeout_cnt_en; |
| logic halt_req_timeout_flag; |
| |
| // HART Status signals |
| //------------------------------------------------------------------------------ |
| |
| type_scr1_hdu_haltstatus_s hart_haltstatus; |
| type_scr1_hdu_haltcause_e hart_haltcause; |
| |
| logic hart_halt_pnd; |
| logic hart_halt_ack; |
| |
| // Debug mode cause decoder signals |
| logic dmode_cause_sstep; |
| logic dmode_cause_except; |
| logic dmode_cause_ebreak; |
| logic dmode_cause_any; |
| `ifdef SCR1_TDU_EN |
| logic dmode_cause_tmreq; |
| `endif // SCR1_TDU_EN |
| |
| // Program Buffer FSM |
| //------------------------------------------------------------------------------ |
| |
| // PBUF FSM control signals |
| logic ifu_handshake_done; |
| logic pbuf_exc_inj_req; |
| logic pbuf_exc_inj_end; |
| logic pbuf_start_fetch; |
| |
| // PBUF FSM signals |
| type_scr1_hdu_pbufstates_e pbuf_fsm_curr; |
| type_scr1_hdu_pbufstates_e pbuf_fsm_next; |
| logic pbuf_fsm_idle; |
| logic pbuf_fsm_fetch; |
| logic pbuf_fsm_excinj; |
| |
| // PBUF address signals |
| logic [SCR1_HDU_PBUF_ADDR_WIDTH-1:0] pbuf_addr_ff; |
| logic [SCR1_HDU_PBUF_ADDR_WIDTH-1:0] pbuf_addr_next; |
| logic pbuf_addr_end; |
| logic pbuf_addr_next_vd; |
| |
| logic pbuf_instr_wait_latching; |
| |
| // Debugs CSRs |
| //------------------------------------------------------------------------------ |
| |
| // CSRs write/read interface signals |
| logic csr_upd_on_halt; |
| logic csr_wr; |
| logic [`SCR1_XLEN-1:0] csr_wr_data; |
| logic [`SCR1_XLEN-1:0] csr_rd_data; |
| |
| // Debug Control and Status register (DCSR) |
| logic csr_dcsr_sel; |
| logic csr_dcsr_wr; |
| type_scr1_hdu_dcsr_s csr_dcsr_in; |
| type_scr1_hdu_dcsr_s csr_dcsr_out; |
| logic csr_dcsr_ebreakm; |
| logic csr_dcsr_stepie; |
| logic csr_dcsr_step; |
| logic [SCR1_HDU_DCSR_CAUSE_BIT_L- |
| SCR1_HDU_DCSR_CAUSE_BIT_R:0] csr_dcsr_cause; |
| |
| // Debug Program Counter register (DPC) |
| logic csr_dpc_sel; |
| logic csr_dpc_wr; |
| logic [`SCR1_XLEN-1:0] csr_dpc_ff; |
| logic [`SCR1_XLEN-1:0] csr_dpc_next; |
| logic [`SCR1_XLEN-1:0] csr_dpc_out; |
| |
| // Debug Scratch register 0 (DSCRATCH0) |
| logic csr_addr_dscratch0; |
| logic csr_dscratch0_sel; |
| logic csr_dscratch0_wr; |
| logic [`SCR1_XLEN-1:0] csr_dscratch0_out; |
| type_scr1_csr_resp_e csr_dscratch0_resp; |
| |
| //------------------------------------------------------------------------------ |
| // Debug state FSM logic |
| //------------------------------------------------------------------------------ |
| // |
| // Debug state FSM logic consists of the following functional units: |
| // - FSM control logic |
| // - Debug state FSM |
| // - FSM transition, update and event registers |
| // |
| |
| // FSM control logic |
| //------------------------------------------------------------------------------ |
| |
| assign dm_cmd_dhalted = (dm2hdu_cmd_i == SCR1_HDU_DBGSTATE_DHALTED); |
| assign dm_cmd_run = (dm2hdu_cmd_i == SCR1_HDU_DBGSTATE_RUN); |
| assign dm_cmd_drun = (dm2hdu_cmd_i == SCR1_HDU_DBGSTATE_DRUN); |
| |
| assign dm_dhalt_req = dm2hdu_cmd_req_i & dm_cmd_dhalted; |
| assign dm_run_req = dm2hdu_cmd_req_i & (dm_cmd_run | dm_cmd_drun); |
| |
| // Debug state FSM |
| //------------------------------------------------------------------------------ |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| dbg_state <= SCR1_HDU_DBGSTATE_RESET; |
| end else begin |
| dbg_state <= dbg_state_next; |
| end |
| end |
| |
| always_comb begin |
| if (~pipe2hdu_rdc_qlfy_i) begin |
| dbg_state_next = SCR1_HDU_DBGSTATE_RESET; |
| end else begin |
| case (dbg_state) |
| SCR1_HDU_DBGSTATE_RESET: begin |
| dbg_state_next = ~pipe2hdu_init_pc_i ? SCR1_HDU_DBGSTATE_RESET |
| : dm_dhalt_req ? SCR1_HDU_DBGSTATE_DHALTED |
| : SCR1_HDU_DBGSTATE_RUN; |
| end |
| SCR1_HDU_DBGSTATE_RUN: begin |
| dbg_state_next = dfsm_update ? SCR1_HDU_DBGSTATE_DHALTED |
| : SCR1_HDU_DBGSTATE_RUN; |
| end |
| SCR1_HDU_DBGSTATE_DHALTED: begin |
| dbg_state_next = ~dfsm_update ? SCR1_HDU_DBGSTATE_DHALTED |
| : dm_cmd_drun ? SCR1_HDU_DBGSTATE_DRUN |
| : SCR1_HDU_DBGSTATE_RUN; |
| end |
| SCR1_HDU_DBGSTATE_DRUN: begin |
| dbg_state_next = dfsm_update ? SCR1_HDU_DBGSTATE_DHALTED |
| : SCR1_HDU_DBGSTATE_DRUN; |
| end |
| default: begin |
| `ifdef SCR1_XPROP_EN |
| dbg_state_next = SCR1_HDU_DBGSTATE_XXX; |
| `else // SCR1_XPROP_EN |
| dbg_state_next = dbg_state; |
| `endif // SCR1_XPROP_EN |
| end |
| endcase |
| end |
| end |
| |
| assign dbg_state_dhalted = (dbg_state == SCR1_HDU_DBGSTATE_DHALTED); |
| assign dbg_state_drun = (dbg_state == SCR1_HDU_DBGSTATE_DRUN); |
| assign dbg_state_run = (dbg_state == SCR1_HDU_DBGSTATE_RUN); |
| assign dbg_state_reset = (dbg_state == SCR1_HDU_DBGSTATE_RESET); |
| |
| // FSM transition, update and event registers |
| //------------------------------------------------------------------------------ |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| dfsm_trans <= 1'b0; |
| dfsm_update <= 1'b0; |
| dfsm_event <= 1'b0; |
| end else begin |
| dfsm_trans <= dfsm_trans_next; |
| dfsm_update <= dfsm_update_next; |
| dfsm_event <= dfsm_event_next; |
| end |
| end |
| |
| always_comb begin |
| dfsm_trans_next = 1'b0; |
| dfsm_update_next = 1'b0; |
| dfsm_event_next = 1'b0; |
| |
| if (~pipe2hdu_rdc_qlfy_i) begin |
| dfsm_trans_next = 1'b0; |
| dfsm_update_next = 1'b0; |
| dfsm_event_next = 1'b1; |
| end else begin |
| case (dbg_state) |
| SCR1_HDU_DBGSTATE_RESET: begin |
| dfsm_trans_next = 1'b0; |
| dfsm_update_next = 1'b0; |
| dfsm_event_next = pipe2hdu_init_pc_i & ~dm2hdu_cmd_req_i; |
| end |
| |
| SCR1_HDU_DBGSTATE_RUN, |
| SCR1_HDU_DBGSTATE_DRUN: begin |
| dfsm_trans_next = ~dfsm_update ? hart_halt_pnd : dfsm_trans; |
| dfsm_update_next = ~dfsm_update & hart_halt_ack; |
| dfsm_event_next = dfsm_update; |
| end |
| |
| SCR1_HDU_DBGSTATE_DHALTED: begin |
| dfsm_trans_next = ~dfsm_update ? ~dfsm_trans & dm_run_req |
| : dfsm_trans; |
| dfsm_update_next = ~dfsm_update & dfsm_trans; |
| dfsm_event_next = dfsm_update; |
| end |
| |
| default : begin |
| dfsm_trans_next = 'X; |
| dfsm_update_next = 'X; |
| dfsm_event_next = 'X; |
| end |
| endcase |
| end |
| end |
| |
| //------------------------------------------------------------------------------ |
| // HART Control logic |
| //------------------------------------------------------------------------------ |
| // |
| // HART Control logic consists of the following functional units: |
| // - Control signals |
| // - HART Run Control register |
| // - HART Halt Request Time-Out counter |
| // |
| |
| // Control logic |
| always_comb begin |
| hart_cmd_req = 1'b0; |
| |
| if (~pipe2hdu_rdc_qlfy_i) begin |
| hart_cmd_req = 1'b0; |
| end else begin |
| case (dbg_state) |
| SCR1_HDU_DBGSTATE_RESET : hart_cmd_req = dm2hdu_cmd_req_i; |
| SCR1_HDU_DBGSTATE_DHALTED: hart_cmd_req = (dfsm_update | dfsm_trans); |
| SCR1_HDU_DBGSTATE_RUN, |
| SCR1_HDU_DBGSTATE_DRUN : hart_cmd_req = ~dfsm_update & dfsm_trans; |
| default : hart_cmd_req = 'X; |
| endcase |
| end |
| end |
| |
| assign hart_halt_req = dm_cmd_dhalted & hart_cmd_req; |
| assign hart_resume_req = (dm_cmd_run | dm_cmd_drun) & hart_cmd_req; |
| |
| // HART Run Control register |
| //------------------------------------------------------------------------------ |
| |
| assign hart_runctrl_clr = (dbg_state_run | dbg_state_drun) |
| & (dbg_state_next == SCR1_HDU_DBGSTATE_DHALTED); |
| assign hart_runctrl_upd = dbg_state_dhalted & dfsm_trans_next; |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| hart_runctrl.irq_dsbl <= 1'b0; |
| hart_runctrl.fetch_src <= SCR1_HDU_FETCH_SRC_NORMAL; |
| hart_runctrl.pc_advmt_dsbl <= 1'b0; |
| hart_runctrl.hwbrkpt_dsbl <= 1'b0; |
| hart_runctrl.redirect <= '0; |
| end else if(clk_en) begin |
| if (hart_runctrl_clr) begin |
| hart_runctrl <= '0; |
| end else begin |
| if (hart_runctrl_upd) begin |
| if (~dm_cmd_drun) begin |
| // Case : resume to RUN state |
| hart_runctrl.irq_dsbl <= csr_dcsr_step ? ~csr_dcsr_stepie : 1'b0; |
| hart_runctrl.fetch_src <= SCR1_HDU_FETCH_SRC_NORMAL; |
| hart_runctrl.pc_advmt_dsbl <= 1'b0; |
| hart_runctrl.hwbrkpt_dsbl <= 1'b0; |
| hart_runctrl.redirect.sstep <= csr_dcsr_step; |
| hart_runctrl.redirect.ebreak <= csr_dcsr_ebreakm; |
| end else begin |
| // Case : resume to DRUN state |
| hart_runctrl.irq_dsbl <= 1'b1; |
| hart_runctrl.fetch_src <= SCR1_HDU_FETCH_SRC_PBUF; |
| hart_runctrl.pc_advmt_dsbl <= 1'b1; |
| hart_runctrl.hwbrkpt_dsbl <= 1'b1; |
| hart_runctrl.redirect.sstep <= 1'b0; |
| hart_runctrl.redirect.ebreak <= 1'b1; |
| end |
| end |
| end |
| end |
| end |
| |
| // HART Halt Request Time-Out counter |
| //------------------------------------------------------------------------------ |
| // HART goes into halt state only if the halt request is present for timeout period |
| // of time |
| |
| assign halt_req_timeout_cnt_en = hdu2exu_dbg_halt2run_o |
| | (hart_halt_req & ~hdu2exu_dbg_run2halt_o); |
| |
| always_ff @(posedge clk, negedge rst_n) begin |
| if (~rst_n) begin |
| halt_req_timeout_cnt <= '1; |
| end else if (halt_req_timeout_cnt_en) begin |
| halt_req_timeout_cnt <= halt_req_timeout_cnt_next; |
| end |
| end |
| |
| assign halt_req_timeout_cnt_next = hdu2exu_dbg_halt2run_o ? '1 |
| : (hart_halt_req & ~hdu2exu_dbg_run2halt_o) ? halt_req_timeout_cnt - 1'b1 |
| : halt_req_timeout_cnt; |
| |
| assign halt_req_timeout_flag = ~|halt_req_timeout_cnt; |
| |
| //------------------------------------------------------------------------------ |
| // HART Status logic |
| //------------------------------------------------------------------------------ |
| // |
| // HART Status logic consists of the following functional units: |
| // - Debug mode cause decoder |
| // - Hart halt status cause encoder |
| // - Hart halt status register |
| // |
| |
| // Debug mode cause decoder |
| //------------------------------------------------------------------------------ |
| |
| assign dmode_cause_sstep = hart_runctrl.redirect.sstep & pipe2hdu_instret_i; |
| assign dmode_cause_except = dbg_state_drun & pipe2hdu_exu_exc_req_i |
| & ~pipe2hdu_brkpt_i |
| `ifdef SCR1_TDU_EN |
| & ~exu2hdu_ibrkpt_hw_i |
| `endif // SCR1_TDU_EN |
| ; |
| assign dmode_cause_ebreak = hart_runctrl.redirect.ebreak & pipe2hdu_brkpt_i; |
| `ifdef SCR1_TDU_EN |
| assign dmode_cause_tmreq = tdu2hdu_dmode_req_i & exu2hdu_ibrkpt_hw_i; |
| `endif // SCR1_TDU_EN |
| |
| assign dmode_cause_any = dmode_cause_sstep | dmode_cause_ebreak | dmode_cause_except |
| | hart_halt_req |
| `ifdef SCR1_TDU_EN |
| | dmode_cause_tmreq |
| `endif // SCR1_TDU_EN |
| ; |
| |
| // HART halt cause encoder |
| //------------------------------------------------------------------------------ |
| |
| always_comb begin |
| case (1'b1) |
| `ifdef SCR1_TDU_EN |
| dmode_cause_tmreq : hart_haltcause = SCR1_HDU_HALTCAUSE_TMREQ; |
| `endif // SCR1_TDU_EN |
| dmode_cause_ebreak : hart_haltcause = SCR1_HDU_HALTCAUSE_EBREAK; |
| hart_halt_req : hart_haltcause = SCR1_HDU_HALTCAUSE_DMREQ; |
| dmode_cause_sstep : hart_haltcause = SCR1_HDU_HALTCAUSE_SSTEP; |
| default : hart_haltcause = SCR1_HDU_HALTCAUSE_NONE; |
| endcase |
| end |
| |
| // HART halt status register |
| //------------------------------------------------------------------------------ |
| |
| always_ff @(posedge clk, negedge rst_n) begin |
| if (~rst_n) begin |
| hart_haltstatus <= '0; |
| end else if (hart_halt_ack) begin |
| hart_haltstatus.except <= dmode_cause_except; |
| hart_haltstatus.cause <= hart_haltcause; |
| end |
| end |
| |
| assign hart_halt_pnd = (dfsm_trans | dm_dhalt_req) & ~hart_halt_ack; |
| assign hart_halt_ack = ~hdu2exu_dbg_halted_o |
| & (halt_req_timeout_flag | (~pipe2hdu_exu_busy_i & dmode_cause_any)); |
| |
| //------------------------------------------------------------------------------ |
| // Program Buffer (PBUF) logic |
| //------------------------------------------------------------------------------ |
| // |
| // Program Buffer allows to execute small programs in debug mode |
| // |
| |
| // To terminate Program Buffer execution exception should be raised. There are 2 |
| // cases: |
| // - One of PBUF instructions raise an exception |
| // - No PBUF instruction raise an exception before the last PBUF instruction has |
| // been issued. In this case FSM goes into EXCINJECT state and an "Instruction |
| // fetch access fault" exception is injected |
| |
| // PBUF FSM |
| //------------------------------------------------------------------------------ |
| |
| assign ifu_handshake_done = hdu2ifu_pbuf_instr_vd_o & ifu2hdu_pbuf_instr_rdy_i; |
| assign pbuf_addr_end = (pbuf_addr_ff == (SCR1_HDU_PBUF_ADDR_SPAN-1)); |
| |
| assign pbuf_start_fetch = dbg_state_dhalted & (dbg_state_next == SCR1_HDU_DBGSTATE_DRUN); |
| assign pbuf_exc_inj_req = ifu_handshake_done & pbuf_addr_end; |
| assign pbuf_exc_inj_end = pipe2hdu_exu_exc_req_i | ifu_handshake_done; |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| pbuf_fsm_curr <= SCR1_HDU_PBUFSTATE_IDLE; |
| end else if(clk_en) begin |
| pbuf_fsm_curr <= pbuf_fsm_next; |
| end |
| end |
| |
| always_comb begin |
| case (pbuf_fsm_curr) |
| SCR1_HDU_PBUFSTATE_IDLE: begin |
| pbuf_fsm_next = pbuf_start_fetch ? SCR1_HDU_PBUFSTATE_FETCH |
| : SCR1_HDU_PBUFSTATE_IDLE; |
| end |
| SCR1_HDU_PBUFSTATE_FETCH: begin |
| pbuf_fsm_next = pipe2hdu_exu_exc_req_i ? SCR1_HDU_PBUFSTATE_WAIT4END |
| : pbuf_exc_inj_req ? SCR1_HDU_PBUFSTATE_EXCINJECT |
| : SCR1_HDU_PBUFSTATE_FETCH; |
| end |
| SCR1_HDU_PBUFSTATE_EXCINJECT: begin |
| pbuf_fsm_next = pbuf_exc_inj_end ? SCR1_HDU_PBUFSTATE_WAIT4END |
| : SCR1_HDU_PBUFSTATE_EXCINJECT; |
| end |
| SCR1_HDU_PBUFSTATE_WAIT4END: begin |
| pbuf_fsm_next = hdu2exu_dbg_halted_o ? SCR1_HDU_PBUFSTATE_IDLE |
| : SCR1_HDU_PBUFSTATE_WAIT4END; |
| end |
| endcase |
| end |
| |
| assign pbuf_fsm_idle = (pbuf_fsm_curr == SCR1_HDU_PBUFSTATE_IDLE); |
| assign pbuf_fsm_fetch = (pbuf_fsm_curr == SCR1_HDU_PBUFSTATE_FETCH); |
| assign pbuf_fsm_excinj = (pbuf_fsm_curr == SCR1_HDU_PBUFSTATE_EXCINJECT); |
| |
| // Program Buffer address register |
| //------------------------------------------------------------------------------ |
| |
| assign pbuf_addr_next_vd = pbuf_fsm_fetch & ifu_handshake_done |
| & ~pipe2hdu_exu_exc_req_i & ~pbuf_addr_end; |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| pbuf_addr_ff <= '0; |
| end else if(clk_en) begin |
| pbuf_addr_ff <= pbuf_addr_next; |
| end |
| end |
| |
| assign pbuf_addr_next = pbuf_fsm_idle ? '0 |
| : pbuf_addr_next_vd ? pbuf_addr_ff + 1'b1 |
| : pbuf_addr_ff; |
| |
| // Pass instruction from debug program buffer to cpu pipeline with two options: |
| // - through register, better for frequency |
| // - through wires, better for area |
| generate if (HART_PBUF_INSTR_REGOUT_EN) begin |
| always_ff @(posedge clk, negedge rst_n) begin |
| if (~rst_n) begin |
| pbuf_instr_wait_latching <= 1'b0; |
| end else begin |
| pbuf_instr_wait_latching <= ifu_handshake_done; |
| end |
| end |
| end else begin |
| assign pbuf_instr_wait_latching = 1'b0; |
| end endgenerate |
| |
| //------------------------------------------------------------------------------ |
| // Debug CSRs |
| //------------------------------------------------------------------------------ |
| |
| assign csr_upd_on_halt = (dbg_state_reset | dbg_state_run) |
| & (dbg_state_next == SCR1_HDU_DBGSTATE_DHALTED); |
| |
| // CSRs select logic |
| //------------------------------------------------------------------------------ |
| |
| always_comb begin : csr_if_regsel |
| csr_dcsr_sel = 1'b0; |
| csr_dpc_sel = 1'b0; |
| csr_dscratch0_sel = 1'b0; |
| //csr_dscratch1_sel = 1'b0; |
| |
| if (csr2hdu_req_i) begin |
| case (csr2hdu_addr_i) |
| SCR1_HDU_DBGCSR_OFFS_DCSR : csr_dcsr_sel = 1'b1; |
| SCR1_HDU_DBGCSR_OFFS_DPC : csr_dpc_sel = 1'b1; |
| SCR1_HDU_DBGCSR_OFFS_DSCRATCH0: csr_dscratch0_sel = 1'b1; |
| default : begin |
| csr_dcsr_sel = 1'bX; |
| csr_dpc_sel = 1'bX; |
| csr_dscratch0_sel = 1'bX; |
| end |
| endcase |
| end |
| end : csr_if_regsel |
| |
| // CSRs read interface |
| //------------------------------------------------------------------------------ |
| |
| assign csr_rd_data = csr_dcsr_out | csr_dpc_out | csr_dscratch0_out; |
| |
| // CSRs write interface |
| //------------------------------------------------------------------------------ |
| |
| assign csr_wr = csr2hdu_req_i; |
| |
| always_comb begin : csr_if_write |
| csr_wr_data = '0; |
| |
| if (csr2hdu_req_i) begin |
| case (csr2hdu_cmd_i) |
| SCR1_CSR_CMD_WRITE : csr_wr_data = csr2hdu_wdata_i; |
| SCR1_CSR_CMD_SET : csr_wr_data = csr_rd_data | csr2hdu_wdata_i; |
| SCR1_CSR_CMD_CLEAR : csr_wr_data = csr_rd_data & (~csr2hdu_wdata_i); |
| default : csr_wr_data = 'X; |
| endcase |
| end |
| end : csr_if_write |
| |
| // Debug Control and Status register |
| //------------------------------------------------------------------------------ |
| // Setups the HART behaviour in Debug Mode and holds Debug status information |
| |
| always_comb begin |
| csr_dcsr_in = csr_wr_data; |
| csr_dcsr_wr = csr_wr & csr_dcsr_sel; |
| |
| csr_dcsr_out = '0; |
| if (csr_dcsr_sel) begin |
| csr_dcsr_out.xdebugver = SCR1_HDU_DEBUGCSR_DCSR_XDEBUGVER; |
| csr_dcsr_out.ebreakm = csr_dcsr_ebreakm; |
| csr_dcsr_out.stepie = csr_dcsr_stepie; |
| csr_dcsr_out.step = csr_dcsr_step; |
| csr_dcsr_out.prv = 2'b11; |
| csr_dcsr_out.cause = csr_dcsr_cause; |
| end |
| end |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| csr_dcsr_ebreakm <= 1'b0; |
| csr_dcsr_stepie <= 1'b0; |
| csr_dcsr_step <= 1'b0; |
| end else if(clk_en) begin |
| if (csr_dcsr_wr) begin |
| csr_dcsr_ebreakm <= csr_dcsr_in.ebreakm; |
| csr_dcsr_stepie <= csr_dcsr_in.stepie; |
| csr_dcsr_step <= csr_dcsr_in.step; |
| end |
| end |
| end |
| |
| always_ff @(negedge rst_n, posedge clk) begin |
| if (~rst_n) begin |
| csr_dcsr_cause <= 1'b0; |
| end else if(clk_en) begin |
| if(csr_upd_on_halt) begin |
| csr_dcsr_cause <= hart_haltstatus.cause; |
| end |
| end |
| end |
| |
| // Debug PC register |
| //------------------------------------------------------------------------------ |
| // Saves the virtual address of the next instruction to be executed when Debug |
| // Mode is entered. Could be changed by debugger |
| |
| assign csr_dpc_wr = csr_wr & csr_dpc_sel; |
| |
| always_ff @(posedge clk, negedge rst_n) begin |
| if (~rst_n) begin |
| csr_dpc_ff <= '0; |
| end else if(clk_en) begin |
| csr_dpc_ff <= csr_dpc_next; |
| end |
| end |
| |
| assign csr_dpc_next = csr_upd_on_halt ? pipe2hdu_pc_curr_i |
| : csr_dpc_wr ? csr_wr_data |
| : csr_dpc_ff; |
| assign csr_dpc_out = csr_dpc_sel ? csr_dpc_ff : '0; |
| |
| // Debug Scratch 0 register |
| //------------------------------------------------------------------------------ |
| |
| assign csr_dscratch0_resp = (~dm2hdu_dreg_resp_i | dm2hdu_dreg_fail_i) |
| ? SCR1_CSR_RESP_ER |
| : SCR1_CSR_RESP_OK; |
| assign csr_dscratch0_out = csr_dscratch0_sel ? dm2hdu_dreg_rdata_i : '0; |
| |
| //------------------------------------------------------------------------------ |
| // HDU <-> DM interface |
| //------------------------------------------------------------------------------ |
| |
| assign hdu2dm_hart_event_o = dfsm_event; |
| |
| // HART status |
| always_comb begin |
| hdu2dm_hart_status_o = '0; |
| hdu2dm_hart_status_o.dbg_state = dbg_state; |
| hdu2dm_hart_status_o.except = dbg_state_dhalted & hart_haltstatus.except; |
| hdu2dm_hart_status_o.ebreak = dbg_state_dhalted & (hart_haltstatus.cause == SCR1_HDU_HALTCAUSE_EBREAK); |
| end |
| |
| assign hdu2dm_cmd_rcode_o = dbg_state_reset |
| ? ~pipe2hdu_rdc_qlfy_i | ~pipe2hdu_init_pc_i | ~dm2hdu_cmd_req_i |
| : ~pipe2hdu_rdc_qlfy_i | ~dfsm_update; |
| |
| always_comb begin |
| hdu2dm_cmd_resp_o = 1'b0; |
| |
| case (dbg_state) |
| SCR1_HDU_DBGSTATE_RESET: begin |
| hdu2dm_cmd_resp_o = pipe2hdu_rdc_qlfy_i & pipe2hdu_init_pc_i & dm2hdu_cmd_req_i; |
| end |
| |
| SCR1_HDU_DBGSTATE_RUN: begin |
| hdu2dm_cmd_resp_o = pipe2hdu_rdc_qlfy_i & dfsm_update & dm2hdu_cmd_req_i; |
| end |
| |
| SCR1_HDU_DBGSTATE_DHALTED: begin |
| hdu2dm_cmd_resp_o = pipe2hdu_rdc_qlfy_i ? dfsm_update : dm2hdu_cmd_req_i; |
| end |
| |
| SCR1_HDU_DBGSTATE_DRUN: begin |
| hdu2dm_cmd_resp_o = (~pipe2hdu_rdc_qlfy_i | dfsm_update) & dm2hdu_cmd_req_i; |
| end |
| |
| default: begin |
| hdu2dm_cmd_resp_o = 'X; |
| end |
| endcase |
| end |
| |
| assign hdu2dm_pbuf_addr_o = pbuf_addr_ff; |
| assign hdu2dm_dreg_req_o = csr_dscratch0_sel; |
| assign hdu2dm_dreg_wr_o = csr_wr & csr_dscratch0_sel; |
| assign hdu2dm_dreg_wdata_o = csr_wr_data; |
| |
| //------------------------------------------------------------------------------ |
| // HDU <-> EXU interface |
| //------------------------------------------------------------------------------ |
| |
| assign hdu2exu_dbg_halted_o = (dbg_state_next == SCR1_HDU_DBGSTATE_DHALTED) |
| | (~pipe2hdu_rdc_qlfy_i & ~dbg_state_run); |
| assign hdu2exu_dbg_run_start_o = dbg_state_dhalted & pipe2hdu_rdc_qlfy_i & dfsm_update; |
| assign hdu2exu_dbg_halt2run_o = hdu2exu_dbg_halted_o & hart_resume_req |
| `ifdef SCR1_CLKCTRL_EN |
| & clk_pipe_en |
| `endif // SCR1_CLKCTRL_EN |
| ; |
| assign hdu2exu_dbg_run2halt_o = hart_halt_ack; |
| |
| assign hdu2exu_pbuf_fetch_o = hart_runctrl.fetch_src; |
| assign hdu2exu_irq_dsbl_o = hart_runctrl.irq_dsbl; |
| assign hdu2exu_pc_advmt_dsbl_o = hart_runctrl.pc_advmt_dsbl; |
| // No change in arch. state if dmode caused by breakpoint |
| assign hdu2exu_no_commit_o = dmode_cause_ebreak |
| `ifdef SCR1_TDU_EN |
| | dmode_cause_tmreq |
| `endif // SCR1_TDU_EN |
| ; |
| assign hdu2exu_dmode_sstep_en_o = hart_runctrl.redirect.sstep; |
| assign hdu2exu_dbg_new_pc_o = csr_dpc_ff; |
| |
| //------------------------------------------------------------------------------ |
| // HDU <-> IFU interface |
| //------------------------------------------------------------------------------ |
| |
| assign hdu2ifu_pbuf_instr_vd_o = (pbuf_fsm_fetch | pbuf_fsm_excinj) |
| & ~pbuf_instr_wait_latching; |
| assign hdu2ifu_pbuf_instr_err_o = pbuf_fsm_excinj; |
| |
| generate if (HART_PBUF_INSTR_REGOUT_EN) begin |
| always_ff @(posedge clk) begin |
| hdu2ifu_pbuf_instr_o <= dm2hdu_pbuf_instr_i; |
| end |
| end else begin |
| assign hdu2ifu_pbuf_instr_o = dm2hdu_pbuf_instr_i; |
| end endgenerate |
| |
| //------------------------------------------------------------------------------ |
| // HDU <-> CSR interface |
| //------------------------------------------------------------------------------ |
| |
| assign csr_addr_dscratch0 = (csr2hdu_addr_i == SCR1_HDU_DBGCSR_OFFS_DSCRATCH0); |
| |
| assign hdu2csr_resp_o = ~dbg_state_drun ? SCR1_CSR_RESP_ER |
| : csr_addr_dscratch0 ? csr_dscratch0_resp |
| : csr2hdu_req_i ? SCR1_CSR_RESP_OK |
| : SCR1_CSR_RESP_ER; |
| assign hdu2csr_rdata_o = csr_rd_data; |
| |
| `ifdef SCR1_TDU_EN |
| //------------------------------------------------------------------------------ |
| // HDU <-> TDU interface |
| //------------------------------------------------------------------------------ |
| |
| assign hdu2tdu_hwbrk_dsbl_o = hart_runctrl.hwbrkpt_dsbl; |
| `endif // SCR1_TDU_EN |
| |
| `ifdef SCR1_TRGT_SIMULATION |
| //------------------------------------------------------------------------------- |
| // Assertion |
| //------------------------------------------------------------------------------- |
| |
| SVA_HDU_XCHECK_COMMON : |
| assert property ( |
| @(negedge clk) disable iff (~rst_n) |
| !$isunknown( {rst_n,clk,clk_en,csr2hdu_req_i,pipe2hdu_rdc_qlfy_i} ) |
| ) |
| else $error("HDU Error: common signals are in X state"); |
| |
| SVA_HDU_XCHECK_CSR_INTF : |
| assert property ( |
| @(negedge clk) disable iff (~rst_n) |
| csr2hdu_req_i |-> !$isunknown( {csr2hdu_cmd_i,csr2hdu_addr_i,csr2hdu_wdata_i} ) |
| ) |
| else $error("HDU Error: CSR i/f is in X state"); |
| |
| SVA_HDU_XCHECK_DM_INTF : |
| assert property ( |
| @(negedge clk) disable iff (~rst_n) |
| !$isunknown( {dm2hdu_cmd_req_i,dm2hdu_cmd_i,dm2hdu_dreg_resp_i, |
| dm2hdu_dreg_fail_i} ) |
| ) |
| else $error("HDU Error: DM i/f is in X state"); |
| |
| SVA_HDU_XCHECK_TDU_INTF : |
| assert property ( |
| @(negedge clk) disable iff (~rst_n) |
| !$isunknown( {tdu2hdu_dmode_req_i,exu2hdu_ibrkpt_hw_i} ) |
| ) |
| else $error("HDU Error: TDU i/f is in X state"); |
| |
| SVA_HDU_XCHECK_HART_INTF : |
| assert property ( |
| @(negedge clk) disable iff (~rst_n) |
| !$isunknown( {pipe2hdu_exu_busy_i,pipe2hdu_instret_i,pipe2hdu_init_pc_i,pipe2hdu_exu_exc_req_i,pipe2hdu_brkpt_i, |
| pipe2hdu_pc_curr_i,ifu2hdu_pbuf_instr_rdy_i} ) |
| ) |
| else $error("HDU Error: HART i/f is in X state"); |
| |
| `endif // SCR1_TRGT_SIMULATION |
| |
| endmodule : scr1_pipe_hdu |
| |
| `endif // SCR1_DBG_EN |