| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| // |
| // The comparator inside the ROM checker |
| // |
| // This module is in charge of comparing the digest that was computed over the ROM data with the |
| // expected digest stored in the top few words. |
| // |
| // |
| // TODO: Think properly about hardening here. A glitch that messes up the comparison isn't a |
| // show-stopper (because the attacker will still have the wrong CreatorRootKey in the key |
| // manager), but glitching the FSM our counter could probably confuse some of the other blocks |
| // that we communicate with. |
| |
| `include "prim_assert.sv" |
| |
| module rom_ctrl_compare #( |
| parameter int NumWords = 2 |
| ) ( |
| input logic clk_i, |
| input logic rst_ni, |
| |
| input logic start_i, |
| output logic done_o, |
| output logic good_o, |
| |
| // CSR inputs for DIGEST and EXP_DIGEST. Ordered with word 0 as LSB. |
| input logic [NumWords*32-1:0] digest_i, |
| input logic [NumWords*32-1:0] exp_digest_i, |
| |
| // To alert system |
| output logic alert_o |
| ); |
| |
| import prim_util_pkg::vbits; |
| |
| `ASSERT_INIT(NumWordsPositive_A, 0 < NumWords) |
| |
| localparam int AW = vbits(NumWords); |
| |
| localparam int unsigned EndAddrInt = NumWords; |
| localparam int unsigned LastAddrInt = NumWords - 1; |
| |
| // Note that if NumWords is a power of 2 then EndAddr will be zero. That's ok: we're just using a |
| // comparison with EndAddr to check that the address counter hasn't started wandering around when |
| // we're in the wrong state. |
| localparam bit [AW-1:0] EndAddr = EndAddrInt[AW-1:0]; |
| localparam bit [AW-1:0] LastAddr = LastAddrInt[AW-1:0]; |
| |
| logic [AW-1:0] addr_q; |
| |
| // This module must wait until triggered by a write to start_i. At that point, it cycles through |
| // the words of DIGEST and EXP_DIGEST, comparing them to one another and passing each digest word |
| // to the key manager. Finally, it gets to the Done state. |
| // |
| // States: |
| // |
| // Waiting |
| // Checking |
| // Done |
| // |
| // Encoding generated with: |
| // $ util/design/sparse-fsm-encode.py -d 3 -m 3 -n 5 -s 1 --language=sv |
| // |
| // Hamming distance histogram: |
| // |
| // 0: -- |
| // 1: -- |
| // 2: -- |
| // 3: |||||||||||||||||||| (66.67%) |
| // 4: |||||||||| (33.33%) |
| // 5: -- |
| // |
| // Minimum Hamming distance: 3 |
| // Maximum Hamming distance: 4 |
| // Minimum Hamming weight: 1 |
| // Maximum Hamming weight: 3 |
| // |
| typedef enum logic [4:0] { |
| Waiting = 5'b00100, |
| Checking = 5'b10010, |
| Done = 5'b11001 |
| } state_e; |
| |
| logic [4:0] state_q, state_d; |
| logic matches_q, matches_d; |
| logic fsm_alert; |
| |
| prim_flop #(.Width(5), .ResetValue({Waiting})) |
| u_state_regs ( |
| .clk_i (clk_i), |
| .rst_ni (rst_ni), |
| .d_i (state_d), |
| .q_o (state_q) |
| ); |
| |
| always_comb begin |
| state_d = state_q; |
| fsm_alert = 1'b0; |
| unique case (state_q) |
| Waiting: begin |
| if (start_i) state_d = Checking; |
| end |
| Checking: begin |
| if (addr_q == LastAddr) state_d = Done; |
| end |
| Done: begin |
| // Final state |
| end |
| default: fsm_alert = 1'b1; |
| endcase |
| end |
| |
| // start_i should only be signalled when we're in the Waiting state |
| logic start_alert; |
| assign start_alert = start_i && (state_q != Waiting); |
| |
| // addr_q should be zero when we're in the Waiting state |
| logic wait_addr_alert; |
| assign wait_addr_alert = (state_q == Waiting) && (addr_q != '0); |
| |
| // addr_q should be EndAddr when we're in the Done state |
| logic done_addr_alert; |
| assign done_addr_alert = (state_q == Done) && (addr_q != EndAddr); |
| |
| // Increment addr_q on each cycle when in Checking |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| addr_q <= '0; |
| matches_q <= 1'b1; |
| end else begin |
| if (state_q == Checking) begin |
| addr_q <= addr_q + AW'(1); |
| matches_q <= matches_d; |
| end |
| end |
| end |
| |
| logic [AW+5-1:0] digest_idx; |
| logic [31:0] digest_word, exp_digest_word; |
| assign digest_idx = {addr_q, 5'd31}; |
| assign digest_word = digest_i[digest_idx -: 32]; |
| assign exp_digest_word = exp_digest_i[digest_idx -: 32]; |
| assign matches_d = matches_q && (digest_word == exp_digest_word); |
| |
| assign done_o = (state_q == Done); |
| assign good_o = matches_q; |
| |
| assign alert_o = fsm_alert | start_alert | wait_addr_alert | done_addr_alert; |
| |
| endmodule |