[rtl] Add CSR module and instantiate

- This change should have no functional impact on the design
- Adding the separate module will allow easy parameterization
  of security hardening for individual CSRs in the future
- As a side benefit, clock gating is added for CSRs that didn't
  previously have it
- Note that this change makes the cpuctrl register always present,
  rather than individual bits being added depending on parameterized
  features. This is not ideal, but the parameterization becomes rather
  messy otherwise.

Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
This commit is contained in:
Tom Roberts 2020-09-16 09:27:49 +01:00 committed by Tom Roberts
parent 8953d82ca4
commit 1a9545baaf
5 changed files with 437 additions and 195 deletions

View file

@ -37,6 +37,7 @@ ${PRJ_DIR}/rtl/ibex_alu.sv
${PRJ_DIR}/rtl/ibex_branch_predict.sv ${PRJ_DIR}/rtl/ibex_branch_predict.sv
${PRJ_DIR}/rtl/ibex_compressed_decoder.sv ${PRJ_DIR}/rtl/ibex_compressed_decoder.sv
${PRJ_DIR}/rtl/ibex_controller.sv ${PRJ_DIR}/rtl/ibex_controller.sv
${PRJ_DIR}/rtl/ibex_csr.sv
${PRJ_DIR}/rtl/ibex_cs_registers.sv ${PRJ_DIR}/rtl/ibex_cs_registers.sv
${PRJ_DIR}/rtl/ibex_counter.sv ${PRJ_DIR}/rtl/ibex_counter.sv
${PRJ_DIR}/rtl/ibex_decoder.sv ${PRJ_DIR}/rtl/ibex_decoder.sv

View file

@ -19,6 +19,7 @@ filesets:
- rtl/ibex_compressed_decoder.sv - rtl/ibex_compressed_decoder.sv
- rtl/ibex_controller.sv - rtl/ibex_controller.sv
- rtl/ibex_cs_registers.sv - rtl/ibex_cs_registers.sv
- rtl/ibex_csr.sv
- rtl/ibex_counter.sv - rtl/ibex_counter.sv
- rtl/ibex_decoder.sv - rtl/ibex_decoder.sv
- rtl/ibex_ex_block.sv - rtl/ibex_ex_block.sv

View file

@ -161,7 +161,7 @@ lint_off -rule UNUSED -file "*/rtl/ibex_decoder.sv" -match "*instr_alu*"
// Signal unoptimizable: Feedback to clock or circular logic: // Signal unoptimizable: Feedback to clock or circular logic:
// ibex_core.cs_registers_i.mie_q // ibex_core.cs_registers_i.mie_q
// Issue lowrisc/ibex#212 // Issue lowrisc/ibex#212
lint_off -rule UNOPTFLAT -file "*/rtl/ibex_cs_registers.sv" -match "*ibex_core.cs_registers_i.mie_q*" lint_off -rule UNOPTFLAT -file "*/rtl/ibex_csr.sv" -match "*ibex_core.cs_registers_i.u_mie_csr.rdata_q*"
// Temporary waivers until OpenTitan primitives are lint-clean // Temporary waivers until OpenTitan primitives are lint-clean
// https://github.com/lowRISC/opentitan/issues/2313 // https://github.com/lowRISC/opentitan/issues/2313

View file

@ -166,7 +166,6 @@ module ibex_cs_registers #(
// CPU control register fields // CPU control register fields
typedef struct packed { typedef struct packed {
logic [31:6] unused_ctrl;
logic [2:0] dummy_instr_mask; logic [2:0] dummy_instr_mask;
logic dummy_instr_en; logic dummy_instr_en;
logic data_ind_timing; logic data_ind_timing;
@ -179,21 +178,32 @@ module ibex_cs_registers #(
// CSRs // CSRs
priv_lvl_e priv_lvl_q, priv_lvl_d; priv_lvl_e priv_lvl_q, priv_lvl_d;
status_t mstatus_q, mstatus_d; status_t mstatus_q, mstatus_d;
logic mstatus_en;
irqs_t mie_q, mie_d; irqs_t mie_q, mie_d;
logic [31:0] mscratch_q, mscratch_d; logic mie_en;
logic [31:0] mscratch_q;
logic mscratch_en;
logic [31:0] mepc_q, mepc_d; logic [31:0] mepc_q, mepc_d;
logic mepc_en;
logic [5:0] mcause_q, mcause_d; logic [5:0] mcause_q, mcause_d;
logic mcause_en;
logic [31:0] mtval_q, mtval_d; logic [31:0] mtval_q, mtval_d;
logic mtval_en;
logic [31:0] mtvec_q, mtvec_d; logic [31:0] mtvec_q, mtvec_d;
logic mtvec_en;
irqs_t mip; irqs_t mip;
dcsr_t dcsr_q, dcsr_d; dcsr_t dcsr_q, dcsr_d;
logic dcsr_en;
logic [31:0] depc_q, depc_d; logic [31:0] depc_q, depc_d;
logic [31:0] dscratch0_q, dscratch0_d; logic depc_en;
logic [31:0] dscratch1_q, dscratch1_d; logic [31:0] dscratch0_q;
logic [31:0] dscratch1_q;
logic dscratch0_en, dscratch1_en;
// CSRs for recoverable NMIs // CSRs for recoverable NMIs
// NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261 // NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261
status_stk_t mstack_q, mstack_d; status_stk_t mstack_q, mstack_d;
logic mstack_en;
logic [31:0] mstack_epc_q, mstack_epc_d; logic [31:0] mstack_epc_q, mstack_epc_d;
logic [5:0] mstack_cause_q, mstack_cause_d; logic [5:0] mstack_cause_q, mstack_cause_d;
@ -223,7 +233,8 @@ module ibex_cs_registers #(
logic [31:0] tmatch_value_rdata; logic [31:0] tmatch_value_rdata;
// CPU control bits // CPU control bits
cpu_ctrl_t cpuctrl_rdata, cpuctrl_wdata; cpu_ctrl_t cpuctrl_q, cpuctrl_d, cpuctrl_wdata;
logic cpuctrl_we;
// CSR update logic // CSR update logic
logic [31:0] csr_wdata_int; logic [31:0] csr_wdata_int;
@ -425,7 +436,7 @@ module ibex_cs_registers #(
// Custom CSR for controlling CPU features // Custom CSR for controlling CPU features
CSR_CPUCTRL: begin CSR_CPUCTRL: begin
csr_rdata_int = {cpuctrl_rdata}; csr_rdata_int = {{32-$bits(cpu_ctrl_t){1'b0}},cpuctrl_q};
end end
// Custom CSR for LFSR re-seeding (cannot be read) // Custom CSR for LFSR re-seeding (cannot be read)
@ -444,30 +455,45 @@ module ibex_cs_registers #(
exception_pc = pc_id_i; exception_pc = pc_id_i;
priv_lvl_d = priv_lvl_q; priv_lvl_d = priv_lvl_q;
mstatus_en = 1'b0;
mstatus_d = mstatus_q; mstatus_d = mstatus_q;
mie_d = mie_q; mie_en = 1'b0;
mscratch_d = mscratch_q; mscratch_en = 1'b0;
mepc_d = mepc_q; mepc_en = 1'b0;
mcause_d = mcause_q; mepc_d = {csr_wdata_int[31:1], 1'b0};
mtval_d = mtval_q; mcause_en = 1'b0;
mtvec_d = csr_mtvec_init_i ? {boot_addr_i[31:8], 6'b0, 2'b01} : mtvec_q; mcause_d = {csr_wdata_int[31], csr_wdata_int[4:0]};
mtval_en = 1'b0;
mtval_d = csr_wdata_int;
mtvec_en = csr_mtvec_init_i;
// mtvec.MODE set to vectored
// mtvec.BASE must be 256-byte aligned
mtvec_d = csr_mtvec_init_i ? {boot_addr_i[31:8], 6'b0, 2'b01} :
{csr_wdata_int[31:8], 6'b0, 2'b01};
dcsr_en = 1'b0;
dcsr_d = dcsr_q; dcsr_d = dcsr_q;
depc_d = depc_q; depc_d = {csr_wdata_int[31:1], 1'b0};
dscratch0_d = dscratch0_q; depc_en = 1'b0;
dscratch1_d = dscratch1_q; dscratch0_en = 1'b0;
dscratch1_en = 1'b0;
mstack_d = mstack_q; mstack_en = 1'b0;
mstack_epc_d = mstack_epc_q; mstack_d.mpie = mstatus_q.mpie;
mstack_cause_d = mstack_cause_q; mstack_d.mpp = mstatus_q.mpp;
mstack_epc_d = mepc_q;
mstack_cause_d = mcause_q;
mcountinhibit_we = 1'b0; mcountinhibit_we = 1'b0;
mhpmcounter_we = '0; mhpmcounter_we = '0;
mhpmcounterh_we = '0; mhpmcounterh_we = '0;
cpuctrl_we = 1'b0;
if (csr_we_int) begin if (csr_we_int) begin
unique case (csr_addr_i) unique case (csr_addr_i)
// mstatus: IE bit // mstatus: IE bit
CSR_MSTATUS: begin CSR_MSTATUS: begin
mstatus_en = 1'b1;
mstatus_d = '{ mstatus_d = '{
mie: csr_wdata_int[CSR_MSTATUS_MIE_BIT], mie: csr_wdata_int[CSR_MSTATUS_MIE_BIT],
mpie: csr_wdata_int[CSR_MSTATUS_MPIE_BIT], mpie: csr_wdata_int[CSR_MSTATUS_MPIE_BIT],
@ -482,28 +508,21 @@ module ibex_cs_registers #(
end end
// interrupt enable // interrupt enable
CSR_MIE: begin CSR_MIE: mie_en = 1'b1;
mie_d.irq_software = csr_wdata_int[CSR_MSIX_BIT];
mie_d.irq_timer = csr_wdata_int[CSR_MTIX_BIT];
mie_d.irq_external = csr_wdata_int[CSR_MEIX_BIT];
mie_d.irq_fast = csr_wdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW];
end
CSR_MSCRATCH: mscratch_d = csr_wdata_int; CSR_MSCRATCH: mscratch_en = 1'b1;
// mepc: exception program counter // mepc: exception program counter
CSR_MEPC: mepc_d = {csr_wdata_int[31:1], 1'b0}; CSR_MEPC: mepc_en = 1'b1;
// mcause // mcause
CSR_MCAUSE: mcause_d = {csr_wdata_int[31], csr_wdata_int[4:0]}; CSR_MCAUSE: mcause_en = 1'b1;
// mtval: trap value // mtval: trap value
CSR_MTVAL: mtval_d = csr_wdata_int; CSR_MTVAL: mtval_en = 1'b1;
// mtvec // mtvec
// mtvec.MODE set to vectored CSR_MTVEC: mtvec_en = 1'b1;
// mtvec.BASE must be 256-byte aligned
CSR_MTVEC: mtvec_d = {csr_wdata_int[31:8], 6'b0, 2'b01};
CSR_DCSR: begin CSR_DCSR: begin
dcsr_d = csr_wdata_int; dcsr_d = csr_wdata_int;
@ -523,13 +542,14 @@ module ibex_cs_registers #(
dcsr_d.zero0 = 1'b0; dcsr_d.zero0 = 1'b0;
dcsr_d.zero1 = 1'b0; dcsr_d.zero1 = 1'b0;
dcsr_d.zero2 = 12'h0; dcsr_d.zero2 = 12'h0;
dcsr_en = 1'b1;
end end
// dpc: debug program counter // dpc: debug program counter
CSR_DPC: depc_d = {csr_wdata_int[31:1], 1'b0}; CSR_DPC: depc_en = 1'b1;
CSR_DSCRATCH0: dscratch0_d = csr_wdata_int; CSR_DSCRATCH0: dscratch0_en = 1'b1;
CSR_DSCRATCH1: dscratch1_d = csr_wdata_int; CSR_DSCRATCH1: dscratch1_en = 1'b1;
// machine counter/timers // machine counter/timers
CSR_MCOUNTINHIBIT: mcountinhibit_we = 1'b1; CSR_MCOUNTINHIBIT: mcountinhibit_we = 1'b1;
@ -560,6 +580,8 @@ module ibex_cs_registers #(
mhpmcounterh_we[mhpmcounter_idx] = 1'b1; mhpmcounterh_we[mhpmcounter_idx] = 1'b1;
end end
CSR_CPUCTRL: cpuctrl_we = 1'b1;
default:; default:;
endcase endcase
end end
@ -589,22 +611,25 @@ module ibex_cs_registers #(
// do not update cause, epc, tval, epc and status // do not update cause, epc, tval, epc and status
dcsr_d.prv = priv_lvl_q; dcsr_d.prv = priv_lvl_q;
dcsr_d.cause = debug_cause_i; dcsr_d.cause = debug_cause_i;
dcsr_en = 1'b1;
depc_d = exception_pc; depc_d = exception_pc;
depc_en = 1'b1;
end else if (!debug_mode_i) begin end else if (!debug_mode_i) begin
// In debug mode, "exceptions do not update any registers. That // In debug mode, "exceptions do not update any registers. That
// includes cause, epc, tval, dpc and mstatus." [Debug Spec v0.13.2, p.39] // includes cause, epc, tval, dpc and mstatus." [Debug Spec v0.13.2, p.39]
mtval_en = 1'b1;
mtval_d = csr_mtval_i; mtval_d = csr_mtval_i;
mstatus_en = 1'b1;
mstatus_d.mie = 1'b0; // disable interrupts mstatus_d.mie = 1'b0; // disable interrupts
// save current status // save current status
mstatus_d.mpie = mstatus_q.mie; mstatus_d.mpie = mstatus_q.mie;
mstatus_d.mpp = priv_lvl_q; mstatus_d.mpp = priv_lvl_q;
mepc_en = 1'b1;
mepc_d = exception_pc; mepc_d = exception_pc;
mcause_en = 1'b1;
mcause_d = {csr_mcause_i}; mcause_d = {csr_mcause_i};
// save previous status for recoverable NMI // save previous status for recoverable NMI
mstack_d.mpie = mstatus_q.mpie; mstack_en = 1'b1;
mstack_d.mpp = mstatus_q.mpp;
mstack_epc_d = mepc_q;
mstack_cause_d = mcause_q;
end end
end // csr_save_cause_i end // csr_save_cause_i
@ -614,13 +639,16 @@ module ibex_cs_registers #(
csr_restore_mret_i: begin // MRET csr_restore_mret_i: begin // MRET
priv_lvl_d = mstatus_q.mpp; priv_lvl_d = mstatus_q.mpp;
mstatus_en = 1'b1;
mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts
if (nmi_mode_i) begin if (nmi_mode_i) begin
// when returning from an NMI restore state from mstack CSR // when returning from an NMI restore state from mstack CSR
mstatus_d.mpie = mstack_q.mpie; mstatus_d.mpie = mstack_q.mpie;
mstatus_d.mpp = mstack_q.mpp; mstatus_d.mpp = mstack_q.mpp;
mepc_en = 1'b1;
mepc_d = mstack_epc_q; mepc_d = mstack_epc_q;
mcause_en = 1'b1;
mcause_d = mstack_cause_q; mcause_d = mstack_cause_q;
end else begin end else begin
// otherwise just set mstatus.MPIE/MPP // otherwise just set mstatus.MPIE/MPP
@ -634,6 +662,22 @@ module ibex_cs_registers #(
endcase endcase
end end
// Update current priv level
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
priv_lvl_q <= PRIV_LVL_M;
end else begin
priv_lvl_q <= priv_lvl_d;
end
end
// Send current priv level to the decoder
assign priv_mode_id_o = priv_lvl_q;
// New instruction fetches need to account for updates to priv_lvl_q this cycle
assign priv_mode_if_o = priv_lvl_d;
// Load/store instructions must factor in MPRV for PMP checking
assign priv_mode_lsu_o = mstatus_q.mprv ? mstatus_q.mpp : priv_lvl_q;
// CSR operation logic // CSR operation logic
always_comb begin always_comb begin
unique case (csr_op_i) unique case (csr_op_i)
@ -671,68 +715,224 @@ module ibex_cs_registers #(
assign irqs_o = mip & mie_q; assign irqs_o = mip & mie_q;
assign irq_pending_o = |irqs_o; assign irq_pending_o = |irqs_o;
// actual registers ////////////////////////
always_ff @(posedge clk_i or negedge rst_ni) begin // CSR instantiations //
if (!rst_ni) begin ////////////////////////
priv_lvl_q <= PRIV_LVL_M;
mstatus_q <= '{ // MSTATUS
mie: 1'b0, localparam status_t MSTATUS_RST_VAL = '{mie: 1'b0,
mpie: 1'b1, mpie: 1'b1,
mpp: PRIV_LVL_U, mpp: PRIV_LVL_U,
mprv: 1'b0, mprv: 1'b0,
tw: 1'b0 tw: 1'b0};
}; ibex_csr #(
mie_q <= '0; .Width ($bits(status_t)),
mscratch_q <= '0; .ShadowCopy (1'b0),
mepc_q <= '0; .ResetValue ({MSTATUS_RST_VAL})
mcause_q <= '0; ) u_mstatus_csr (
mtval_q <= '0; .clk_i (clk_i),
mtvec_q <= 32'h0000_0001; .rst_ni (rst_ni),
dcsr_q <= '{ .wr_data_i ({mstatus_d}),
.wr_en_i (mstatus_en),
.rd_data_o (mstatus_q),
.rd_error_o ()
);
// MEPC
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_mepc_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (mepc_d),
.wr_en_i (mepc_en),
.rd_data_o (mepc_q),
.rd_error_o ()
);
// MIE
assign mie_d.irq_software = csr_wdata_int[CSR_MSIX_BIT];
assign mie_d.irq_timer = csr_wdata_int[CSR_MTIX_BIT];
assign mie_d.irq_external = csr_wdata_int[CSR_MEIX_BIT];
assign mie_d.irq_fast = csr_wdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW];
ibex_csr #(
.Width ($bits(irqs_t)),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_mie_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i ({mie_d}),
.wr_en_i (mie_en),
.rd_data_o (mie_q),
.rd_error_o ()
);
// MSCRATCH
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_mscratch_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (csr_wdata_int),
.wr_en_i (mscratch_en),
.rd_data_o (mscratch_q),
.rd_error_o ()
);
// MCAUSE
ibex_csr #(
.Width (6),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_mcause_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (mcause_d),
.wr_en_i (mcause_en),
.rd_data_o (mcause_q),
.rd_error_o ()
);
// MTVAL
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_mtval_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (mtval_d),
.wr_en_i (mtval_en),
.rd_data_o (mtval_q),
.rd_error_o ()
);
// MTVEC
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue (32'd1)
) u_mtvec_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (mtvec_d),
.wr_en_i (mtvec_en),
.rd_data_o (mtvec_q),
.rd_error_o ()
);
// DCSR
localparam dcsr_t DCSR_RESET_VAL = '{
xdebugver: XDEBUGVER_STD, xdebugver: XDEBUGVER_STD,
cause: DBG_CAUSE_NONE, // 3'h0 cause: DBG_CAUSE_NONE, // 3'h0
prv: PRIV_LVL_M, prv: PRIV_LVL_M,
default: '0 default: '0
}; };
depc_q <= '0; ibex_csr #(
dscratch0_q <= '0; .Width ($bits(dcsr_t)),
dscratch1_q <= '0; .ShadowCopy (1'b0),
.ResetValue ({DCSR_RESET_VAL})
) u_dcsr_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i ({dcsr_d}),
.wr_en_i (dcsr_en),
.rd_data_o (dcsr_q),
.rd_error_o ()
);
mstack_q <= '{ // DEPC
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_depc_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (depc_d),
.wr_en_i (depc_en),
.rd_data_o (depc_q),
.rd_error_o ()
);
// DSCRATCH0
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_dscratch0_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (csr_wdata_int),
.wr_en_i (dscratch0_en),
.rd_data_o (dscratch0_q),
.rd_error_o ()
);
// DSCRATCH1
ibex_csr #(
.Width (32),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_dscratch1_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i (csr_wdata_int),
.wr_en_i (dscratch1_en),
.rd_data_o (dscratch1_q),
.rd_error_o ()
);
// MSTACK
localparam status_stk_t MSTACK_RESET_VAL = '{
mpie: 1'b1, mpie: 1'b1,
mpp: PRIV_LVL_U mpp: PRIV_LVL_U
}; };
mstack_epc_q <= '0; ibex_csr #(
mstack_cause_q <= '0; .Width ($bits(status_stk_t)),
.ShadowCopy (1'b0),
.ResetValue ({MSTACK_RESET_VAL})
) u_mstack_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i ({mstack_d}),
.wr_en_i (mstack_en),
.rd_data_o (mstack_q),
.rd_error_o ()
);
end else begin // MSTACK_EPC
// update CSRs ibex_csr #(
priv_lvl_q <= priv_lvl_d; .Width (32),
mstatus_q <= mstatus_d; .ShadowCopy (1'b0),
mie_q <= mie_d; .ResetValue ('0)
mscratch_q <= mscratch_d; ) u_mstack_epc_csr (
mepc_q <= mepc_d; .clk_i (clk_i),
mcause_q <= mcause_d; .rst_ni (rst_ni),
mtval_q <= mtval_d; .wr_data_i (mstack_epc_d),
mtvec_q <= mtvec_d; .wr_en_i (mstack_en),
dcsr_q <= dcsr_d; .rd_data_o (mstack_epc_q),
depc_q <= depc_d; .rd_error_o ()
dscratch0_q <= dscratch0_d; );
dscratch1_q <= dscratch1_d;
mstack_q <= mstack_d; // MSTACK_CAUSE
mstack_epc_q <= mstack_epc_d; ibex_csr #(
mstack_cause_q <= mstack_cause_d; .Width (6),
.ShadowCopy (1'b0),
end .ResetValue ('0)
end ) u_mstack_cause_csr (
.clk_i (clk_i),
// Send current priv level to the decoder .rst_ni (rst_ni),
assign priv_mode_id_o = priv_lvl_q; .wr_data_i (mstack_cause_d),
// New instruction fetches need to account for updates to priv_lvl_q this cycle .wr_en_i (mstack_en),
assign priv_mode_if_o = priv_lvl_d; .rd_data_o (mstack_cause_q),
// Load/store instructions must factor in MPRV for PMP checking .rd_error_o ()
assign priv_mode_lsu_o = mstatus_q.mprv ? mstatus_q.mpp : priv_lvl_q; );
// ----------------- // -----------------
// PMP registers // PMP registers
@ -814,13 +1014,18 @@ module ibex_cs_registers #(
assign pmp_cfg_wdata[i].write = &csr_wdata_int[(i%4)*PMP_CFG_W+:2]; assign pmp_cfg_wdata[i].write = &csr_wdata_int[(i%4)*PMP_CFG_W+:2];
assign pmp_cfg_wdata[i].read = csr_wdata_int[(i%4)*PMP_CFG_W]; assign pmp_cfg_wdata[i].read = csr_wdata_int[(i%4)*PMP_CFG_W];
always_ff @(posedge clk_i or negedge rst_ni) begin ibex_csr #(
if (!rst_ni) begin .Width ($bits(pmp_cfg_t)),
pmp_cfg[i] <= pmp_cfg_t'('b0); .ShadowCopy (1'b0),
end else if (pmp_cfg_we[i]) begin .ResetValue ('0)
pmp_cfg[i] <= pmp_cfg_wdata[i]; ) u_pmp_cfg_csr (
end .clk_i (clk_i),
end .rst_ni (rst_ni),
.wr_data_i ({pmp_cfg_wdata[i]}),
.wr_en_i (pmp_cfg_we[i]),
.rd_data_o (pmp_cfg[i]),
.rd_error_o ()
);
// -------------------------- // --------------------------
// Instantiate addr registers // Instantiate addr registers
@ -834,13 +1039,19 @@ module ibex_cs_registers #(
(csr_addr == (CSR_OFF_PMP_ADDR + i[11:0])); (csr_addr == (CSR_OFF_PMP_ADDR + i[11:0]));
end end
always_ff @(posedge clk_i or negedge rst_ni) begin ibex_csr #(
if (!rst_ni) begin .Width (32),
pmp_addr[i] <= 'b0; .ShadowCopy (1'b0),
end else if (pmp_addr_we[i]) begin .ResetValue ('0)
pmp_addr[i] <= csr_wdata_int; ) u_pmp_addr_csr (
end .clk_i (clk_i),
end .rst_ni (rst_ni),
.wr_data_i (csr_wdata_int),
.wr_en_i (pmp_addr_we[i]),
.rd_data_o (pmp_addr[i]),
.rd_error_o ()
);
assign csr_pmp_cfg_o[i] = pmp_cfg[i]; assign csr_pmp_cfg_o[i] = pmp_cfg[i];
assign csr_pmp_addr_o[i] = {pmp_addr[i],2'b00}; assign csr_pmp_addr_o[i] = {pmp_addr[i],2'b00};
end end
@ -997,27 +1208,35 @@ module ibex_cs_registers #(
assign tmatch_value_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TDATA2); assign tmatch_value_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TDATA2);
// tmatch_control is enabled when the execute bit is set // tmatch_control is enabled when the execute bit is set
assign tmatch_control_d = tmatch_control_we ? csr_wdata_int[2] : assign tmatch_control_d = csr_wdata_int[2];
tmatch_control_q;
// tmatch_value has its own clock gate
assign tmatch_value_d = csr_wdata_int[31:0]; assign tmatch_value_d = csr_wdata_int[31:0];
// Registers // Registers
always_ff @(posedge clk_i or negedge rst_ni) begin ibex_csr #(
if (!rst_ni) begin .Width (1),
tmatch_control_q <= 'b0; .ShadowCopy (1'b0),
end else begin .ResetValue ('0)
tmatch_control_q <= tmatch_control_d; ) u_tmatch_control_csr (
end .clk_i (clk_i),
end .rst_ni (rst_ni),
.wr_data_i (tmatch_control_d),
.wr_en_i (tmatch_control_we),
.rd_data_o (tmatch_control_q),
.rd_error_o ()
);
always_ff @(posedge clk_i or negedge rst_ni) begin ibex_csr #(
if (!rst_ni) begin .Width (32),
tmatch_value_q <= 'b0; .ShadowCopy (1'b0),
end else if (tmatch_value_we) begin .ResetValue ('0)
tmatch_value_q <= tmatch_value_d; ) u_tmatch_value_csr (
end .clk_i (clk_i),
end .rst_ni (rst_ni),
.wr_data_i (tmatch_value_d),
.wr_en_i (tmatch_value_we),
.rd_data_o (tmatch_value_q),
.rd_error_o ()
);
// Assign read data // Assign read data
// TSELECT - only one supported // TSELECT - only one supported
@ -1054,27 +1273,16 @@ module ibex_cs_registers #(
assign trigger_match_o = 'b0; assign trigger_match_o = 'b0;
end end
// CPU control fields //////////////////////////
assign cpuctrl_rdata.unused_ctrl = '0; // CPU control register //
//////////////////////////
// Cast register write data // Cast register write data
assign cpuctrl_wdata = cpu_ctrl_t'(csr_wdata_int); assign cpuctrl_wdata = cpu_ctrl_t'(csr_wdata_int[$bits(cpu_ctrl_t)-1:0]);
// Generate fixed time execution bit // Generate fixed time execution bit
if (DataIndTiming) begin : gen_dit if (DataIndTiming) begin : gen_dit
logic data_ind_timing_d, data_ind_timing_q; assign cpuctrl_d.data_ind_timing = cpuctrl_wdata.data_ind_timing;
assign data_ind_timing_d = (csr_we_int && (csr_addr == CSR_CPUCTRL)) ?
cpuctrl_wdata.data_ind_timing : data_ind_timing_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
data_ind_timing_q <= 1'b0; // disabled on reset
end else begin
data_ind_timing_q <= data_ind_timing_d;
end
end
assign cpuctrl_rdata.data_ind_timing = data_ind_timing_q;
end else begin : gen_no_dit end else begin : gen_no_dit
// tieoff for the unused bit // tieoff for the unused bit
@ -1082,34 +1290,16 @@ module ibex_cs_registers #(
assign unused_dit = cpuctrl_wdata.data_ind_timing; assign unused_dit = cpuctrl_wdata.data_ind_timing;
// field will always read as zero if not configured // field will always read as zero if not configured
assign cpuctrl_rdata.data_ind_timing = 1'b0; assign cpuctrl_d.data_ind_timing = 1'b0;
end end
assign data_ind_timing_o = cpuctrl_rdata.data_ind_timing; assign data_ind_timing_o = cpuctrl_q.data_ind_timing;
// Generate dummy instruction signals // Generate dummy instruction signals
if (DummyInstructions) begin : gen_dummy if (DummyInstructions) begin : gen_dummy
logic dummy_instr_en_d, dummy_instr_en_q; assign cpuctrl_d.dummy_instr_en = cpuctrl_wdata.dummy_instr_en;
logic [2:0] dummy_instr_mask_d, dummy_instr_mask_q; assign cpuctrl_d.dummy_instr_mask = cpuctrl_wdata.dummy_instr_mask;
assign dummy_instr_en_d = (csr_we_int && (csr_addr == CSR_CPUCTRL)) ?
cpuctrl_wdata.dummy_instr_en : dummy_instr_en_q;
assign dummy_instr_mask_d = (csr_we_int && (csr_addr == CSR_CPUCTRL)) ?
cpuctrl_wdata.dummy_instr_mask : dummy_instr_mask_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
dummy_instr_en_q <= 1'b0; // disabled on reset
dummy_instr_mask_q <= 3'b000;
end else begin
dummy_instr_en_q <= dummy_instr_en_d;
dummy_instr_mask_q <= dummy_instr_mask_d;
end
end
assign cpuctrl_rdata.dummy_instr_en = dummy_instr_en_q;
assign cpuctrl_rdata.dummy_instr_mask = dummy_instr_mask_q;
// Signal a write to the seed register // Signal a write to the seed register
assign dummy_instr_seed_en_o = csr_we_int && (csr_addr == CSR_SECURESEED); assign dummy_instr_seed_en_o = csr_we_int && (csr_addr == CSR_SECURESEED);
assign dummy_instr_seed_o = csr_wdata_int; assign dummy_instr_seed_o = csr_wdata_int;
@ -1122,48 +1312,41 @@ module ibex_cs_registers #(
assign unused_dummy_mask = cpuctrl_wdata.dummy_instr_mask; assign unused_dummy_mask = cpuctrl_wdata.dummy_instr_mask;
// field will always read as zero if not configured // field will always read as zero if not configured
assign cpuctrl_rdata.dummy_instr_en = 1'b0; assign cpuctrl_d.dummy_instr_en = 1'b0;
assign cpuctrl_rdata.dummy_instr_mask = 3'b000; assign cpuctrl_d.dummy_instr_mask = 3'b000;
assign dummy_instr_seed_en_o = 1'b0; assign dummy_instr_seed_en_o = 1'b0;
assign dummy_instr_seed_o = '0; assign dummy_instr_seed_o = '0;
end end
assign dummy_instr_en_o = cpuctrl_rdata.dummy_instr_en; assign dummy_instr_en_o = cpuctrl_q.dummy_instr_en;
assign dummy_instr_mask_o = cpuctrl_rdata.dummy_instr_mask; assign dummy_instr_mask_o = cpuctrl_q.dummy_instr_mask;
// Generate icache enable bit // Generate icache enable bit
if (ICache) begin : gen_icache_enable if (ICache) begin : gen_icache_enable
logic icache_enable_d, icache_enable_q; assign cpuctrl_d.icache_enable = cpuctrl_wdata.icache_enable;
// Update the value when cpuctrl register is written
assign icache_enable_d = (csr_we_int & (csr_addr == CSR_CPUCTRL)) ?
cpuctrl_wdata.icache_enable : icache_enable_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
icache_enable_q <= 1'b0; // disabled on reset
end else begin
icache_enable_q <= icache_enable_d;
end
end
assign cpuctrl_rdata.icache_enable = icache_enable_q;
end else begin : gen_no_icache end else begin : gen_no_icache
// tieoff for the unused icen bit // tieoff for the unused icen bit
logic unused_icen; logic unused_icen;
assign unused_icen = cpuctrl_wdata.icache_enable; assign unused_icen = cpuctrl_wdata.icache_enable;
// icen field will always read as zero if ICache not configured // icen field will always read as zero if ICache not configured
assign cpuctrl_rdata.icache_enable = 1'b0; assign cpuctrl_d.icache_enable = 1'b0;
end end
assign icache_enable_o = cpuctrl_rdata.icache_enable; assign icache_enable_o = cpuctrl_q.icache_enable;
// tieoff for the currently unused bits of cpuctrl
logic [31:6] unused_cpuctrl;
assign unused_cpuctrl = {cpuctrl_wdata[31:6]};
ibex_csr #(
.Width ($bits(cpu_ctrl_t)),
.ShadowCopy (1'b0),
.ResetValue ('0)
) u_cpuctrl_csr (
.clk_i (clk_i),
.rst_ni (rst_ni),
.wr_data_i ({cpuctrl_d}),
.wr_en_i (cpuctrl_we),
.rd_data_o (cpuctrl_q),
.rd_error_o ()
);
//////////////// ////////////////
// Assertions // // Assertions //

57
rtl/ibex_csr.sv Normal file
View file

@ -0,0 +1,57 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Control / status register primitive
*/
`include "prim_assert.sv"
module ibex_csr #(
parameter int unsigned Width = 32,
parameter bit ShadowCopy = 1'b0,
parameter bit [Width-1:0] ResetValue = '0
) (
input logic clk_i,
input logic rst_ni,
input logic [Width-1:0] wr_data_i,
input logic wr_en_i,
output logic [Width-1:0] rd_data_o,
output logic rd_error_o
);
logic [Width-1:0] rdata_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rdata_q <= ResetValue;
end else if (wr_en_i) begin
rdata_q <= wr_data_i;
end
end
assign rd_data_o = rdata_q;
if (ShadowCopy) begin : gen_shadow
logic [Width-1:0] shadow_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
shadow_q <= ~ResetValue;
end else if (wr_en_i) begin
shadow_q <= ~wr_data_i;
end
end
assign rd_error_o = rdata_q != ~shadow_q;
end else begin : gen_no_shadow
assign rd_error_o = 1'b0;
end
`ASSERT_KNOWN(IbexCSREnValid, wr_en_i)
endmodule