A PWM controller for motor actuation has two jobs. It must produce a clean, configurable
switching waveform, and it must not produce an unsafe one. The second job, in most
implementations, is delegated to firmware. A microcontroller compares the commanded duty
against a limit, and refuses to load the compare register if the limit is exceeded.
This is convenient and almost always adequate. It is also a path that can be bypassed,
corrupted by a stack overflow, starved by a priority inversion, or silently reverted by
a firmware update. The failure mode is invisible until it is not.
FaultLatchedPWM takes a different position. The safety envelope belongs in the fabric.
The PWM generator is subordinate to a hardware guard module that observes the commanded
period and duty values and a fault signal, and holds a single latched bit: is the system
permitted to drive output. The guard is combinational and registered; the latch is
explicit; the reset path is explicit. The firmware cannot override it because there is
no firmware path to the latch. The only way out of a fault state is an externally
asserted recovery sequence, which can itself be gated by whatever higher-level
supervision the integrator chooses.
The state machine has five states. INIT establishes known values on power-up
and releases to IDLE when the configuration registers are stable. IDLE
drives the output low and waits for an enable. RUN generates the waveform
under the guard module's continuous supervision. FAULT is the latched
state: output is driven low, the fault signal is asserted on an external pin, and no
further transitions are accepted except a specific recovery pattern. SAFE
is reached after recovery, and returns to IDLE only when the configuration
has been re-validated. The critical property is that the RUN to FAULT
transition is single-cycle and irreversible without external intervention, and that
FAULT never yields to RUN directly. This eliminates an entire
class of chattering failures where a marginally-valid configuration oscillates across
the guard boundary.
verilog
// FaultLatchedPWM: guard-gated PWM generator, excerpt
// The output is driven low unless guard_ok is asserted AND
// the FSM is in the RUN state. There is no firmware path.
module fault_latched_pwm #(
parameter integer WIDTH = 32
)(
input wire clk,
input wire rst_n,
input wire [WIDTH-1:0] period_i,
input wire [WIDTH-1:0] duty_i,
input wire enable_i,
input wire recover_i,
output reg pwm_o,
output wire fault_o
);
localparam [2:0]
S_INIT = 3'd0,
S_IDLE = 3'd1,
S_RUN = 3'd2,
S_FAULT = 3'd3,
S_SAFE = 3'd4;
reg [2:0] state, state_n;
wire guard_ok = (duty_i <= period_i) && (period_i != 0);
assign fault_o = (state == S_FAULT);
always @(*) begin
state_n = state;
case (state)
S_INIT : if (guard_ok) state_n = S_IDLE;
S_IDLE : if (enable_i && guard_ok) state_n = S_RUN;
S_RUN : if (!guard_ok) state_n = S_FAULT; // single-cycle, irreversible
S_FAULT : if (recover_i) state_n = S_SAFE;
S_SAFE : if (guard_ok) state_n = S_IDLE;
default : state_n = S_INIT;
endcase
end
endmodule