blob: e23e54b9f0288f5e9b0abfa85a3778ef887e22ef [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// 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