[otbn] Add instruction counter.
A 32-bit register is added to count instructions during the OTBN
procedure.
Signed-off-by: Vladimir Rozic <vrozic@lowrisc.org>
diff --git a/hw/ip/otbn/data/otbn.hjson b/hw/ip/otbn/data/otbn.hjson
index 9637400..bafcc5c 100644
--- a/hw/ip/otbn/data/otbn.hjson
+++ b/hw/ip/otbn/data/otbn.hjson
@@ -253,7 +253,22 @@
desc: "Set on any ECC error in a register file"
}
]
- }
+ } // register : fatal_alert_cause
+ { name: "INSN_CNT",
+ desc: "Instruction Counter",
+ swaccess: "ro",
+ hwaccess: "hwo",
+ fields: [
+ { bits: "31:0",
+ name: "insn_cnt",
+ desc: '''
+ The number of instructions executed in the current or last OTBN run.
+ Saturates at 2^32-1. The counter is reset to zero when a new operation
+ is started. Instructions triggering an error do not count as being executed.
+ '''
+ }
+ ]
+ } // register : insn_cnt
// Give IMEM and DMEM 16 KiB address space, each, to allow for easy expansion
// of the actual IMEM and DMEM sizes without changing the address map.
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index b3b6810..ec4e6c1 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -89,7 +89,8 @@
.edn_urnd_req_o ( edn_urnd_req ),
.edn_urnd_ack_i ( edn_urnd_ack ),
- .edn_urnd_data_i ( edn_urnd_data )
+ .edn_urnd_data_i ( edn_urnd_data ),
+ .insn_cnt_o ()
);
// The top bits of IMEM rdata aren't currently used (they will eventually be used for integrity
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index e41955c..8e37e7d 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -520,6 +520,11 @@
assign hw2reg.fatal_alert_cause.reg_error.de = 0;
assign hw2reg.fatal_alert_cause.reg_error.d = 0;
+ // INSN_CNT register
+ logic [31:0] insn_cnt;
+ assign hw2reg.insn_cnt.d = insn_cnt;
+ assign hw2reg.insn_cnt.de = 1'b1;
+
// Alerts ====================================================================
logic [NumAlerts-1:0] alert_test;
@@ -698,7 +703,9 @@
.edn_urnd_req_o (edn_urnd_req),
.edn_urnd_ack_i (edn_urnd_ack),
- .edn_urnd_data_i (edn_urnd_data)
+ .edn_urnd_data_i (edn_urnd_data),
+
+ .insn_cnt_o (insn_cnt)
);
`else
otbn_core #(
@@ -741,7 +748,9 @@
.edn_urnd_req_o (edn_urnd_req),
.edn_urnd_ack_i (edn_urnd_ack),
- .edn_urnd_data_i (edn_urnd_data)
+ .edn_urnd_data_i (edn_urnd_data),
+
+ .insn_cnt_o (insn_cnt)
);
`endif
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv
index 29fc216..de7788c 100644
--- a/hw/ip/otbn/rtl/otbn_controller.sv
+++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -121,7 +121,9 @@
output logic rnd_req_o,
output logic rnd_prefetch_req_o,
- input logic rnd_valid_i
+ input logic rnd_valid_i,
+
+ output logic [31:0] insn_cnt_o
);
otbn_state_e state_q, state_d, state_raw;
@@ -201,6 +203,9 @@
logic rf_a_indirect_err, rf_b_indirect_err, rf_d_indirect_err, rf_indirect_err;
+ logic insn_cnt_en;
+ logic [31:0] insn_cnt_d, insn_cnt_q;
+
// Stall a cycle on loads to allow load data writeback to happen the following cycle. Stall not
// required on stores as there is no response to deal with.
// TODO: Possibility of error response on store? Probably still don't need to stall in that case
@@ -345,6 +350,18 @@
end
end
+ assign insn_cnt_d = start_i ? 32'd0 : (insn_cnt_q + 32'd1);
+ assign insn_cnt_en = (insn_executing & ~stall & (insn_cnt_q != 32'hffffffff)) | start_i;
+ assign insn_cnt_o = insn_cnt_q;
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ insn_cnt_q <= 32'd0;
+ end else if (insn_cnt_en) begin
+ insn_cnt_q <= insn_cnt_d;
+ end
+ end
+
otbn_loop_controller #(
.ImemAddrWidth(ImemAddrWidth)
) u_otbn_loop_controller (
diff --git a/hw/ip/otbn/rtl/otbn_core.sv b/hw/ip/otbn/rtl/otbn_core.sv
index 13ec3a0..cfc7ac7 100644
--- a/hw/ip/otbn/rtl/otbn_core.sv
+++ b/hw/ip/otbn/rtl/otbn_core.sv
@@ -64,7 +64,9 @@
output logic edn_urnd_req_o,
input logic edn_urnd_ack_i,
- input logic [EdnDataWidth-1:0] edn_urnd_data_i
+ input logic [EdnDataWidth-1:0] edn_urnd_data_i,
+
+ output logic [31:0] insn_cnt_o
);
// Fetch request (the next instruction)
logic [ImemAddrWidth-1:0] insn_fetch_req_addr;
@@ -164,6 +166,8 @@
logic controller_start;
logic [ImemAddrWidth-1:0] controller_start_addr;
+ logic [31:0] insn_cnt;
+
// Start stop control start OTBN execution when requested and deals with any pre start or post
// stop actions.
otbn_start_stop_control #(
@@ -339,9 +343,13 @@
.rnd_req_o (rnd_req),
.rnd_prefetch_req_o (rnd_prefetch_req),
- .rnd_valid_i (rnd_valid)
+ .rnd_valid_i (rnd_valid),
+
+ .insn_cnt_o (insn_cnt)
);
+ assign insn_cnt_o = insn_cnt;
+
// Load store unit: read and write data from data memory
otbn_lsu u_otbn_lsu (
.clk_i,
diff --git a/hw/ip/otbn/rtl/otbn_reg_pkg.sv b/hw/ip/otbn/rtl/otbn_reg_pkg.sv
index 7bbf78e..f44e2e7 100644
--- a/hw/ip/otbn/rtl/otbn_reg_pkg.sv
+++ b/hw/ip/otbn/rtl/otbn_reg_pkg.sv
@@ -112,6 +112,11 @@
} reg_error;
} otbn_hw2reg_fatal_alert_cause_reg_t;
+ typedef struct packed {
+ logic [31:0] d;
+ logic de;
+ } otbn_hw2reg_insn_cnt_reg_t;
+
// Register -> HW type
typedef struct packed {
otbn_reg2hw_intr_state_reg_t intr_state; // [41:41]
@@ -124,10 +129,11 @@
// HW -> register type
typedef struct packed {
- otbn_hw2reg_intr_state_reg_t intr_state; // [26:25]
- otbn_hw2reg_status_reg_t status; // [24:24]
- otbn_hw2reg_err_bits_reg_t err_bits; // [23:8]
- otbn_hw2reg_fatal_alert_cause_reg_t fatal_alert_cause; // [7:0]
+ otbn_hw2reg_intr_state_reg_t intr_state; // [59:58]
+ otbn_hw2reg_status_reg_t status; // [57:57]
+ otbn_hw2reg_err_bits_reg_t err_bits; // [56:41]
+ otbn_hw2reg_fatal_alert_cause_reg_t fatal_alert_cause; // [40:33]
+ otbn_hw2reg_insn_cnt_reg_t insn_cnt; // [32:0]
} otbn_hw2reg_t;
// Register offsets
@@ -140,6 +146,7 @@
parameter logic [BlockAw-1:0] OTBN_ERR_BITS_OFFSET = 16'h 18;
parameter logic [BlockAw-1:0] OTBN_START_ADDR_OFFSET = 16'h 1c;
parameter logic [BlockAw-1:0] OTBN_FATAL_ALERT_CAUSE_OFFSET = 16'h 20;
+ parameter logic [BlockAw-1:0] OTBN_INSN_CNT_OFFSET = 16'h 24;
// Reset values for hwext registers and their fields
parameter logic [0:0] OTBN_INTR_TEST_RESVAL = 1'h 0;
@@ -166,11 +173,12 @@
OTBN_STATUS,
OTBN_ERR_BITS,
OTBN_START_ADDR,
- OTBN_FATAL_ALERT_CAUSE
+ OTBN_FATAL_ALERT_CAUSE,
+ OTBN_INSN_CNT
} otbn_id_e;
// Register width information to check illegal writes
- parameter logic [3:0] OTBN_PERMIT [9] = '{
+ parameter logic [3:0] OTBN_PERMIT [10] = '{
4'b 0001, // index[0] OTBN_INTR_STATE
4'b 0001, // index[1] OTBN_INTR_ENABLE
4'b 0001, // index[2] OTBN_INTR_TEST
@@ -179,7 +187,8 @@
4'b 0001, // index[5] OTBN_STATUS
4'b 0001, // index[6] OTBN_ERR_BITS
4'b 1111, // index[7] OTBN_START_ADDR
- 4'b 0001 // index[8] OTBN_FATAL_ALERT_CAUSE
+ 4'b 0001, // index[8] OTBN_FATAL_ALERT_CAUSE
+ 4'b 1111 // index[9] OTBN_INSN_CNT
};
endpackage
diff --git a/hw/ip/otbn/rtl/otbn_reg_top.sv b/hw/ip/otbn/rtl/otbn_reg_top.sv
index bb250f7..aefb4a4 100644
--- a/hw/ip/otbn/rtl/otbn_reg_top.sv
+++ b/hw/ip/otbn/rtl/otbn_reg_top.sv
@@ -187,6 +187,7 @@
logic fatal_alert_cause_imem_error_qs;
logic fatal_alert_cause_dmem_error_qs;
logic fatal_alert_cause_reg_error_qs;
+ logic [31:0] insn_cnt_qs;
// Register instances
// R[intr_state]: V(False)
@@ -653,9 +654,35 @@
);
+ // R[insn_cnt]: V(False)
+
+ prim_subreg #(
+ .DW (32),
+ .SWACCESS("RO"),
+ .RESVAL (32'h0)
+ ) u_insn_cnt (
+ .clk_i (clk_i ),
+ .rst_ni (rst_ni ),
+
+ .we (1'b0),
+ .wd ('0 ),
+
+ // from internal hardware
+ .de (hw2reg.insn_cnt.de),
+ .d (hw2reg.insn_cnt.d ),
+
+ // to internal hardware
+ .qe (),
+ .q (),
+
+ // to register interface (read)
+ .qs (insn_cnt_qs)
+ );
- logic [8:0] addr_hit;
+
+
+ logic [9:0] addr_hit;
always_comb begin
addr_hit = '0;
addr_hit[0] = (reg_addr == OTBN_INTR_STATE_OFFSET);
@@ -667,6 +694,7 @@
addr_hit[6] = (reg_addr == OTBN_ERR_BITS_OFFSET);
addr_hit[7] = (reg_addr == OTBN_START_ADDR_OFFSET);
addr_hit[8] = (reg_addr == OTBN_FATAL_ALERT_CAUSE_OFFSET);
+ addr_hit[9] = (reg_addr == OTBN_INSN_CNT_OFFSET);
end
assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
@@ -682,7 +710,8 @@
(addr_hit[5] & (|(OTBN_PERMIT[5] & ~reg_be))) |
(addr_hit[6] & (|(OTBN_PERMIT[6] & ~reg_be))) |
(addr_hit[7] & (|(OTBN_PERMIT[7] & ~reg_be))) |
- (addr_hit[8] & (|(OTBN_PERMIT[8] & ~reg_be)))));
+ (addr_hit[8] & (|(OTBN_PERMIT[8] & ~reg_be))) |
+ (addr_hit[9] & (|(OTBN_PERMIT[9] & ~reg_be)))));
end
assign intr_state_we = addr_hit[0] & reg_we & !reg_error;
@@ -759,6 +788,10 @@
reg_rdata_next[3] = fatal_alert_cause_reg_error_qs;
end
+ addr_hit[9]: begin
+ reg_rdata_next[31:0] = insn_cnt_qs;
+ end
+
default: begin
reg_rdata_next = '1;
end