Research-Stack/4-Infrastructure/hardware/tmr_oepi_safety_fsm.v
allaun 475f6319ea chore(repo): push local 768-commit branch state onto clean remote baseline
This squashes all local history (768 commits) onto the scrubbed PR #90
baseline. Individual commits were lost during filter-repo corruption;
the working tree content is preserved intact.

Build: N/A (working tree state only)
2026-06-15 22:46:50 -05:00

466 lines
17 KiB
Verilog

// TMR OEPI Safety FSM
// Derived from expansion paths document: DTMR with triplicated voters per ESA FPGA-003
// Target: Gowin GW1NR-9 or iCE40 HX8K
// Q16.16 fixed-point arithmetic for OEPI score
// Safety valve: bounded-veto protocol for swarm consensus
`timescale 1ns / 1ps
`include "state_definitions.vh"
// ═══════════════════════════════════════════════════════════════════════════
// TMR Voter Module (Triplicated Redundancy)
// Votes on 3 inputs, outputs majority
// ═══════════════════════════════════════════════════════════════════════════
module tmr_voter (
input wire [3:0] in0,
input wire [3:0] in1,
input wire [3:0] in2,
output reg [3:0] voted,
output reg error_detected
);
always @(*) begin
// Majority voting: if at least 2 agree, use that value
if (in0 == in1 || in0 == in2) begin
voted = in0;
error_detected = (in0 != in1) || (in0 != in2);
end else if (in1 == in2) begin
voted = in1;
error_detected = 1'b1;
end else begin
// All different - use in0 as fallback
voted = in0;
error_detected = 1'b1;
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// OEPI Safety Score Calculation
// OEPI (Operator Emergency Protection Index) in Q1.7 format
// Threshold-based safety valve
// ═══════════════════════════════════════════════════════════════════════════
module oepi_calculator (
input wire clk,
input wire rst_n,
input wire [3:0] scalar_state,
input wire [31:0] s3c_j_score,
input wire [31:0] s3c_mass,
input wire s3c_emit,
output reg [7:0] oepi_score, // Q1.7 OEPI score
output reg safety_violation
);
// OEPI threshold (Q1.7 format)
localparam OEPI_THRESHOLD = 8'd120; // ~0.94 in Q1.7
// Calculate OEPI based on state and S3C metrics
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
oepi_score <= 8'd0;
safety_violation <= 1'b0;
end else begin
// Base OEPI from scalar state
case (scalar_state)
STATE_SUPERPOSED: oepi_score <= 8'd20; // Low risk
STATE_SCOUTING: oepi_score <= 8'd30; // Low risk
STATE_MEASURE_LOCAL_NEED: oepi_score <= 8'd40;
STATE_COLLAPSED_PROFILE: oepi_score <= 8'd50;
STATE_EXECUTE: oepi_score <= 8'd80; // Medium risk
STATE_RECEIPT: oepi_score <= 8'd60;
STATE_AMPLITUDE_UPDATE: oepi_score <= 8'd70;
STATE_QUERY_COLLECTIVE: oepi_score <= 8'd50;
STATE_COLLECTIVE_RESPONSE: oepi_score <= 8'd60;
STATE_QUERY_LLM: oepi_score <= 8'd55;
STATE_DIRECTED: oepi_score <= 8'd75;
STATE_HOLD: oepi_score <= 8'd30;
STATE_OPERATOR_ALERT: oepi_score <= 8'd120; // High risk
STATE_LOW_POWER_PASSIVE: oepi_score <= 8'd25;
STATE_QUARANTINE: oepi_score <= 8'd127; // Maximum risk
STATE_MIGRATE: oepi_score <= 8'd45;
default: oepi_score <= 8'd64;
endcase
// FIX: Combine s3c_emit and s3c_j_score into single conditional
// to prevent double-increment race when both fire in same cycle
begin
reg [7:0] oepi_delta;
oepi_delta = 8'd0;
if (s3c_emit)
oepi_delta = oepi_delta + 8'd10;
if (s3c_j_score > 32'd5000)
oepi_delta = oepi_delta + 8'd5;
if (oepi_delta > 8'd0 && oepi_score <= 8'd127 - oepi_delta)
oepi_score <= oepi_score + oepi_delta;
else if (oepi_delta > 8'd0)
oepi_score <= 8'd127;
end
// Safety violation detection
safety_violation <= (oepi_score >= OEPI_THRESHOLD);
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Triplicated Scalar State Machine (DTMR)
// Three parallel FSM instances with TMR voting
// ═══════════════════════════════════════════════════════════════════════════
module scalar_fsm_triplet (
input wire clk,
input wire rst_n,
input wire state_transition,
input wire [3:0] target_state,
input wire operator_available,
output reg [3:0] fsm_state [0:2], // Three FSM instances
output reg [3:0] voted_state,
output reg voter_error
);
// FSM instance 0
reg [3:0] fsm0_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
fsm0_state <= STATE_SUPERPOSED;
end else if (state_transition && operator_available) begin
fsm0_state <= target_state;
end
end
assign fsm_state[0] = fsm0_state;
// FSM instance 1
reg [3:0] fsm1_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
fsm1_state <= STATE_SUPERPOSED;
end else if (state_transition && operator_available) begin
fsm1_state <= target_state;
end
end
assign fsm_state[1] = fsm1_state;
// FSM instance 2
reg [3:0] fsm2_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
fsm2_state <= STATE_SUPERPOSED;
end else if (state_transition && operator_available) begin
fsm2_state <= target_state;
end
end
assign fsm_state[2] = fsm2_state;
// TMR voter
tmr_voter state_voter (
.in0(fsm0_state),
.in1(fsm1_state),
.in2(fsm2_state),
.voted(voted_state),
.error_detected(voter_error)
);
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Triplicated OEPI Calculator (DTMR)
// Three parallel OEPI calculators with TMR voting
// ═══════════════════════════════════════════════════════════════════════════
module oepi_triplet (
input wire clk,
input wire rst_n,
input wire [3:0] scalar_state,
input wire [31:0] s3c_j_score,
input wire [31:0] s3c_mass,
input wire s3c_emit,
output reg [7:0] oepi_scores [0:2], // Three OEPI instances
output reg [7:0] voted_oepi,
output reg voter_error,
output reg safety_violation
);
// OEPI instance 0
reg [7:0] oepi0;
wire violation0;
oepi_calculator oepi_inst0 (
.clk(clk),
.rst_n(rst_n),
.scalar_state(scalar_state),
.s3c_j_score(s3c_j_score),
.s3c_mass(s3c_mass),
.s3c_emit(s3c_emit),
.oepi_score(oepi0),
.safety_violation(violation0)
);
assign oepi_scores[0] = oepi0;
// OEPI instance 1
reg [7:0] oepi1;
wire violation1;
oepi_calculator oepi_inst1 (
.clk(clk),
.rst_n(rst_n),
.scalar_state(scalar_state),
.s3c_j_score(s3c_j_score),
.s3c_mass(s3c_mass),
.s3c_emit(s3c_emit),
.oepi_score(oepi1),
.safety_violation(violation1)
);
assign oepi_scores[1] = oepi1;
// OEPI instance 2
reg [7:0] oepi2;
wire violation2;
oepi_calculator oepi_inst2 (
.clk(clk),
.rst_n(rst_n),
.scalar_state(scalar_state),
.s3c_j_score(s3c_j_score),
.s3c_mass(s3c_mass),
.s3c_emit(s3c_emit),
.oepi_score(oepi2),
.safety_violation(violation2)
);
assign oepi_scores[2] = oepi2;
// TMR voter for OEPI (8-bit, vote per bit)
integer k;
always @(*) begin
for (k = 0; k < 8; k = k + 1) begin
voted_oepi[k] = (oepi0[k] & oepi1[k]) | (oepi0[k] & oepi2[k]) | (oepi1[k] & oepi2[k]);
end
end
// Voter error detection
always @(*) begin
voter_error = (oepi0 != oepi1) || (oepi0 != oepi2);
end
// Safety violation (any instance triggers)
always @(*) begin
safety_violation = violation0 || violation1 || violation2;
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Bounded-Veto Safety Protocol
// Any node raising OEPI above threshold forces swarm to safe state
// ═══════════════════════════════════════════════════════════════════════════
module bounded_veto_protocol (
input wire clk,
input wire rst_n,
input wire [7:0] local_oepi,
input wire [7:0] remote_oepi, // From other nodes via gossip
input wire veto_request, // Remote veto request
output reg safe_state_enforced,
output reg [3:0] safe_state
);
localparam OEPI_VETO_THRESHOLD = 8'd100; // Veto threshold
localparam SAFE_STATE = STATE_QUARANTINE;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
safe_state_enforced <= 1'b0;
safe_state <= STATE_SUPERPOSED;
end else begin
// Check local OEPI
if (local_oepi >= OEPI_VETO_THRESHOLD) begin
safe_state_enforced <= 1'b1;
safe_state <= SAFE_STATE;
end
// Check remote veto
else if (veto_request || remote_oepi >= OEPI_VETO_THRESHOLD) begin
safe_state_enforced <= 1'b1;
safe_state <= SAFE_STATE;
end
// Clear safe state when OEPI drops
else if (safe_state_enforced && local_oepi < OEPI_VETO_THRESHOLD &&
remote_oepi < OEPI_VETO_THRESHOLD && !veto_request) begin
safe_state_enforced <= 1'b0;
safe_state <= STATE_SUPERPOSED;
end
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// TMR OEPI Safety FSM Top-Level
// Combines DTMR state machine, DTMR OEPI calculator, and bounded-veto protocol
// ═══════════════════════════════════════════════════════════════════════════
module tmr_oepi_safety_fsm (
input wire clk,
input wire rst_n,
input wire state_transition,
input wire [3:0] target_state,
input wire operator_available,
input wire [31:0] s3c_j_score,
input wire [31:0] s3c_mass,
input wire s3c_emit,
input wire [7:0] remote_oepi,
input wire veto_request,
output reg [3:0] scalar_state,
output reg [7:0] oepi_score,
output reg safety_violation,
output reg fsm_voter_error,
output reg oepi_voter_error,
output reg safe_state_enforced
);
// Triplicated scalar state machine
wire [3:0] fsm_states [0:2];
wire [3:0] voted_fsm_state;
wire fsm_voter_err;
scalar_fsm_triplet fsm_triplet (
.clk(clk),
.rst_n(rst_n),
.state_transition(state_transition && !safe_state_enforced),
.target_state(target_state),
.operator_available(operator_available),
.fsm_state(fsm_states),
.voted_state(voted_fsm_state),
.voter_error(fsm_voter_err)
);
// Triplicated OEPI calculator
wire [7:0] oepi_scores [0:2];
wire [7:0] voted_oepi;
wire oepi_voter_err;
wire safety_violation_wire;
oepi_triplet oepi_triplet_inst (
.clk(clk),
.rst_n(rst_n),
.scalar_state(voted_fsm_state),
.s3c_j_score(s3c_j_score),
.s3c_mass(s3c_mass),
.s3c_emit(s3c_emit),
.oepi_scores(oepi_scores),
.voted_oepi(voted_oepi),
.voter_error(oepi_voter_err),
.safety_violation(safety_violation_wire)
);
// Bounded-veto protocol
wire safe_enforced;
wire [3:0] safe_state_wire;
bounded_veto_protocol veto_protocol (
.clk(clk),
.rst_n(rst_n),
.local_oepi(voted_oepi),
.remote_oepi(remote_oepi),
.veto_request(veto_request),
.safe_state_enforced(safe_enforced),
.safe_state(safe_state_wire)
);
// Output registers
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
scalar_state <= STATE_SUPERPOSED;
oepi_score <= 8'd0;
safety_violation <= 1'b0;
fsm_voter_error <= 1'b0;
oepi_voter_error <= 1'b0;
safe_state_enforced <= 1'b0;
end else begin
// Apply safe state override
if (safe_enforced) begin
scalar_state <= safe_state_wire;
safe_state_enforced <= 1'b1;
end else begin
scalar_state <= voted_fsm_state;
safe_state_enforced <= 1'b0;
end
oepi_score <= voted_oepi;
safety_violation <= safety_violation_wire;
fsm_voter_error <= fsm_voter_err;
oepi_voter_error <= oepi_voter_err;
end
end
endmodule
// ═══════════════════════════════════════════════════════════════════════════
// Testbench
// ═══════════════════════════════════════════════════════════════════════════
module tmr_oepi_testbench;
reg clk;
reg rst_n;
reg state_transition;
reg [3:0] target_state;
reg operator_available;
reg [31:0] s3c_j_score;
reg [31:0] s3c_mass;
reg s3c_emit;
reg [7:0] remote_oepi;
reg veto_request;
wire [3:0] scalar_state;
wire [7:0] oepi_score;
wire safety_violation;
wire fsm_voter_error;
wire oepi_voter_error;
wire safe_state_enforced;
tmr_oepi_safety_fsm dut (
.clk(clk),
.rst_n(rst_n),
.state_transition(state_transition),
.target_state(target_state),
.operator_available(operator_available),
.s3c_j_score(s3c_j_score),
.s3c_mass(s3c_mass),
.s3c_emit(s3c_emit),
.remote_oepi(remote_oepi),
.veto_request(veto_request),
.scalar_state(scalar_state),
.oepi_score(oepi_score),
.safety_violation(safety_violation),
.fsm_voter_error(fsm_voter_error),
.oepi_voter_error(oepi_voter_error),
.safe_state_enforced(safe_state_enforced)
);
initial clk = 0;
always #18.5185 clk = ~clk;
initial begin
rst_n = 0;
state_transition = 0;
target_state = 4'd0;
operator_available = 1;
s3c_j_score = 32'd0;
s3c_mass = 32'd0;
s3c_emit = 0;
remote_oepi = 8'd0;
veto_request = 0;
#100;
rst_n = 1;
#100;
// Test normal state transition
$display("Test 1: Normal state transition");
target_state = STATE_EXECUTE;
state_transition = 1;
#100;
state_transition = 0;
#500;
$display("State: %d, OEPI: %d, Safety: %b", scalar_state, oepi_score, safety_violation);
// Test high OEPI (safety violation)
$display("Test 2: High OEPI safety violation");
s3c_j_score = 32'd10000;
s3c_emit = 1;
#500;
$display("State: %d, OEPI: %d, Safety: %b", scalar_state, oepi_score, safety_violation);
// Test remote veto
$display("Test 3: Remote veto");
veto_request = 1;
#500;
$display("State: %d, Safe Enforced: %b", scalar_state, safe_state_enforced);
veto_request = 0;
#1000;
$finish;
end
endmodule