[prim] Add a duplicated prim_arbiter instance
- A prim module wraps two arbiters whose results are
constantly compared against one another.
Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/prim/rtl/prim_arbiter_tree_dup.sv b/hw/ip/prim/rtl/prim_arbiter_tree_dup.sv
new file mode 100644
index 0000000..58d7abb
--- /dev/null
+++ b/hw/ip/prim/rtl/prim_arbiter_tree_dup.sv
@@ -0,0 +1,124 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// This is a wrapper module that instantiates two prim_aribter_tree modules.
+// The reason for two is similar to modules such as prim_count/prim_lfsr where
+// we use spatial redundancy to ensure the arbitration results are not altered.
+//
+// Note there are limits to this check, as the inputs to the duplicated arbiter
+// is still a single lane. So an upstream attack can defeat this module. The
+// duplication merely protects against attacks directly on the arbiter.
+
+`include "prim_assert.sv"
+
+module prim_arbiter_tree_dup #(
+ parameter int N = 8,
+ parameter int DW = 32,
+
+ // Configurations
+ // EnDataPort: {0, 1}, if 0, input data will be ignored
+ parameter bit EnDataPort = 1,
+
+ // Derived parameters
+ localparam int IdxW = $clog2(N)
+) (
+ input clk_i,
+ input rst_ni,
+
+ input req_chk_i, // Used for gating assertions. Drive to 1 during normal
+ // operation.
+ input [ N-1:0] req_i,
+ input [DW-1:0] data_i [N],
+ output logic [ N-1:0] gnt_o,
+ output logic [IdxW-1:0] idx_o,
+
+ output logic valid_o,
+ output logic [DW-1:0] data_o,
+ input ready_i,
+ output logic err_o
+);
+
+ localparam int ArbInstances = 2;
+
+ //typedef struct packed {
+ // logic [N-1:0] req;
+ // logic [N-1:0][DW-1:0] data;
+ //} arb_inputs_t;
+
+ typedef struct packed {
+ logic valid;
+ logic [N-1:0] gnt;
+ logic [IdxW-1:0] idx;
+ logic [DW-1:0] data;
+ } arb_outputs_t;
+
+ // buffer up the inputs separately for each instance
+ //arb_inputs_t arb_in;
+ //arb_inputs_t [ArbInstances-1:0] arb_input_buf;
+ arb_outputs_t [ArbInstances-1:0] arb_output_buf;
+
+ for (genvar i = 0; i < ArbInstances; i++) begin : gen_input_bufs
+ logic [N-1:0] req_buf;
+ prim_buf #(
+ .Width(N)
+ ) u_req_buf (
+ .in_i(req_i),
+ .out_o(req_buf)
+ );
+
+ logic [DW-1:0] data_buf [N];
+ for (genvar j = 0; j < N; j++) begin : gen_data_bufs
+ prim_buf #(
+ .Width(DW)
+ ) u_dat_buf (
+ .in_i(data_i[j]),
+ .out_o(data_buf[j])
+ );
+ end
+
+ prim_arbiter_tree #(
+ .N(N),
+ .DW(DW),
+ .EnDataPort(EnDataPort)
+ ) u_arb (
+ .clk_i,
+ .rst_ni,
+ .req_chk_i,
+ .req_i(req_buf),
+ .data_i(data_buf),
+ .gnt_o(arb_output_buf[i].gnt),
+ .idx_o(arb_output_buf[i].idx),
+ .valid_o(arb_output_buf[i].valid),
+ .data_o(arb_output_buf[i].data),
+ .ready_i
+ );
+ end
+
+ // the last buffered position is sent out
+ assign gnt_o = arb_output_buf[ArbInstances-1].gnt;
+ assign idx_o = arb_output_buf[ArbInstances-1].idx;
+ assign valid_o = arb_output_buf[ArbInstances-1].valid;
+ assign data_o = arb_output_buf[ArbInstances-1].data;
+
+ // Check the last buffer index against all other instances
+ logic [ArbInstances-2:0] output_delta;
+
+ for (genvar i = 0; i < ArbInstances-1; i++) begin : gen_checks
+ assign output_delta[i] = arb_output_buf[ArbInstances-1] != arb_output_buf[i];
+ end
+
+ logic err_d, err_q;
+ // There is an error if anything ever disagrees
+ assign err_d = |output_delta;
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ err_q <= '0;
+ end else begin
+ err_q <= err_d | err_q;
+ end
+ end
+
+ assign err_o = err_q;
+
+endmodule // prim_arbiter_tree