mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-30 18:56:16 +00:00
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)
466 lines
17 KiB
Verilog
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
|