// TEACHING WRAPPER ONLY. fi_* controls must be removed from production RTL.
// KIND: 0=parity(5 bits), 1=complement(2 bits), 2=Hamming SECDED(8 bits).
// ECC_REJECT_CE=1: reject all error flags; =0: correct and reject only UE.
// One event: stable source pulse@2 OR next-state XOR@3 OR grant pulse@4.
// The harness must restrict mask width/weight; this module does not enforce it.
// No scrub. In reject mode, corrected data does not contribute to grant.
module lesson03_register_gate #(
  parameter int KIND = 1,
  parameter bit ECC_REJECT_CE = 1'b1
) (
  input logic clk_i, rst_ni,
  input logic verify_done_i, auth_ok_i,
  input logic fetch_valid_i, fetch_ready_i,
  input logic [7:0] fi_code_xor_i,
  input logic fi_source_xor_i, fi_grant_xor_i,
  output logic accepted_commit, checked_q, err_sticky_q,
  output logic [7:0] code_q,
  output logic [3:0] decoded_data,
  output logic ce, ue, local_bad, exec_grant_o
);
  logic [7:0] code_d, corrected;
  logic [2:0] syndrome;
  logic total_parity, write_en, grant_clean;

  function automatic logic [7:0] encode_data(input logic [3:0] data);
    logic [7:0] word;
    word = '0;
    if (KIND == 0) word[4:0] = {^data, data};
    else if (KIND == 1) word[1:0] = data[0] ? 2'b10 : 2'b01;
    else begin
      word[2] = data[0]; word[4] = data[1];
      word[5] = data[2]; word[6] = data[3];
      word[0] = data[0] ^ data[1] ^ data[3];
      word[1] = data[0] ^ data[2] ^ data[3];
      word[3] = data[1] ^ data[2] ^ data[3];
      word[7] = ^word[6:0];
    end
    return word;
  endfunction

  assign write_en = verify_done_i && !checked_q;
  assign code_d = (write_en ? encode_data({3'b000, auth_ok_i ^ fi_source_xor_i})
                           : code_q) ^ fi_code_xor_i;
  always_ff @(posedge clk_i or negedge rst_ni) begin
    if (!rst_ni) begin
      code_q <= encode_data(4'b0000);
      checked_q <= 1'b0;
      err_sticky_q <= 1'b0;
    end else begin
      code_q <= code_d;
      if (write_en) checked_q <= 1'b1;
      err_sticky_q <= err_sticky_q | local_bad;
    end
  end

  always_comb begin
    syndrome = '0; total_parity = 1'b0;
    corrected = code_q; decoded_data = '0;
    ce = 1'b0; ue = 1'b0;
    if (KIND == 0) begin
      decoded_data = code_q[3:0];
      ue = ^code_q[4:0];
    end else if (KIND == 1) begin
      decoded_data = {3'b000, code_q[1]};
      ue = !(code_q[1] ^ code_q[0]);
    end else begin
      syndrome[0] = code_q[0] ^ code_q[2] ^ code_q[4] ^ code_q[6];
      syndrome[1] = code_q[1] ^ code_q[2] ^ code_q[5] ^ code_q[6];
      syndrome[2] = code_q[3] ^ code_q[4] ^ code_q[5] ^ code_q[6];
      total_parity = ^code_q;
      ce = total_parity;
      ue = (syndrome != 3'b000) && !total_parity;
      if (ce) begin
        if (syndrome == 3'b000) corrected[7] = ~code_q[7];
        else corrected[syndrome - 1] = ~code_q[syndrome - 1];
      end
      if (ECC_REJECT_CE)
        decoded_data = {code_q[6], code_q[5], code_q[4], code_q[2]};
      else decoded_data = {corrected[6], corrected[5], corrected[4], corrected[2]};
    end
    local_bad = ue | ((KIND == 2) && ECC_REJECT_CE && ce);
  end
  // Sampled acceptance is blocked by the live error, before sticky updates.
  assign grant_clean = checked_q && (decoded_data == 4'b0001)
                       && !local_bad && !err_sticky_q;
  assign exec_grant_o = grant_clean ^ fi_grant_xor_i;
  assign accepted_commit = fetch_valid_i && fetch_ready_i && exec_grant_o;
endmodule
