diff --git a/hdl/rtl/hydra_core.vhd b/hdl/rtl/hydra_core.vhd
index b67d2c5c32253fdc7ece0cbb2337b72f012b0484..9404b9effd1702a615a8bae450b176d6d99bab0d 100644
--- a/hdl/rtl/hydra_core.vhd
+++ b/hdl/rtl/hydra_core.vhd
@@ -83,15 +83,16 @@ architecture arch of hydra_core is
     return x;
   end function f_x_to_zero;
 
-  signal cpu_rst        : std_logic;
+  signal cpu_rst        : std_logic_vector(1 to 3);
   signal cpu_rst_err    : std_logic;
+  signal cpu_sync       : std_logic_vector(1 to 3);
 
   signal im_addr  : std_logic_vector(31 downto 0);
   signal im_data, im1_data  : std_logic_vector(31 downto 0);
   signal im_rd, im_valid : std_logic;
   signal im1_done, im1_dm_en, im1_err : std_logic;
   signal im2_err, im2_valid : std_logic;
-  signal err_cpu : std_logic;
+  signal err_cpu_dm : std_logic;
 
   signal dm_addr, dm_data_s, dm_data_l       : std_logic_vector(31 downto 0);
   signal dm_data_select                      : std_logic_vector(3 downto 0);
@@ -121,6 +122,8 @@ architecture arch of hydra_core is
   signal nbr_dram_ecc_uncorr : std_logic_vector (31 downto 0);
   signal iram_ecc_err, iram_ecc_fatal : std_logic;
   signal dram_ecc_err, dram_ecc_fatal : std_logic;
+  signal nbr_cpu_data_err : std_logic_vector (31 downto 0);
+  signal nbr_cpu_iaddr_err : std_logic_vector (31 downto 0);
 
   signal reset_cause_cpu, reset_cause_ecc, reset_cause_wd : std_logic;
   signal dram_scrub_period, iram_scrub_period : std_logic_vector(15 downto 0);
@@ -129,15 +132,19 @@ architecture arch of hydra_core is
   signal wd_period_wr, wd_key_wr, wd_timeout : std_logic;
 
   signal force_divergence, force_divergence_d, dm_force_divergence : std_logic;
-begin
-  cpu_rst <= cpu_rst_err or (not cpu_rst_n_i);
+  signal cpu_wr : std_logic;
+
+  type t_state is (S_VOTER, S_LOCK);
+  signal state : t_state;
 
+  signal cpu_recovery, cpu_recovery_in : std_logic;
+begin
   dwb_o <= dwb_out;
 
-  U_cpu_core : entity work.hydra_triple_cpu
+  inst_cpus : entity work.hydra_triple_cpu
     port map (
       clk_i            => clk_sys_i,
-      rst_i            => cpu_rst,
+      cpu_rst_i        => cpu_rst,
       im_addr_o        => im_addr,
       im_rd_o          => im_rd,
       im_data_i        => im_data,
@@ -151,8 +158,9 @@ begin
       dm_load_done_i   => dm_load_done,
       dm_store_done_i  => dm_store_done,
 
+      cpu_sync_o       => cpu_sync,
       dm_force_divergence_i => dm_force_divergence,
-      err_cpu_o        => err_cpu);
+      err_cpu_dm_o     => err_cpu_dm);
 
   --  Add registers on uRV data bus
   process (clk_sys_i)
@@ -170,7 +178,6 @@ begin
     end if;
   end process;
 
-
   inst_iram: entity work.hydra_iram
     generic map (
       g_RAM_LOG_SIZE => g_IRAM_LOG_SIZE,
@@ -416,7 +423,7 @@ begin
             --  Timeout
             wd_timeout <= '1';
             wd_counter <= wd_period;
-          elsif cpu_rst = '1' or (wd_key_wr = '1' and wd_key_val = x"c0423bc9") then
+          elsif cpu_rst = "111" or (wd_key_wr = '1' and wd_key_val = x"c0423bc9") then
             --  Key -> reload
             --  Also restart the watchdog if the cpu is reset.
             wd_counter <= wd_period;
@@ -429,27 +436,8 @@ begin
     end if;
   end process;
 
-  --  Supervisor
-  p_sv: process (clk_sys_i)
-  begin
-    if rising_edge(clk_sys_i) then
-      if rst_n_i = '0' then
-        reset_cause_cpu <= '0';
-        reset_cause_ecc <= '0';
-        reset_cause_wd <= '0';
-        cpu_rst_err <= '0';
-      else
-        cpu_rst_err <= '0';
-        if dram_ecc_fatal = '1' or iram_ecc_fatal = '1' or wd_timeout = '1' or err_cpu = '1' then
-          --  Reset CPU on error.
-          cpu_rst_err <= '1';
-          reset_cause_cpu <= err_cpu;
-          reset_cause_ecc <= dram_ecc_fatal or iram_ecc_fatal;
-          reset_cause_wd <= wd_timeout;
-        end if;
-      end if;
-    end if;
-  end process;
+  --  Any fatal error that should reset cpus
+  cpu_rst_err <= dram_ecc_fatal or iram_ecc_fatal or wd_timeout;
 
   inst_sv_regs: entity work.hydra_supervisor_regs
     port map (
@@ -460,9 +448,11 @@ begin
       reset_cause_cpu_i => reset_cause_cpu,
       reset_cause_ecc_i => reset_cause_ecc,
       reset_cause_watchdog_i => reset_cause_wd,
-      cpu_status_i => "000",
+      cpu_status_i => cpu_rst,
       cpu_boot_done_i => '0',
-      cpu_recovery_i => '0',
+      cpu_recovery_i => cpu_recovery,
+      cpu_recovery_o => cpu_recovery_in,
+      cpu_wr_o => cpu_wr,
       force_divergence_i => x"00000000",
       force_divergence_rd_o => force_divergence,
       wd_period_i => wd_period,
@@ -478,4 +468,51 @@ begin
       iram_scrub_period_o => iram_scrub_period,
       dram_scrub_period_o => dram_scrub_period
     );
+
+  process (clk_sys_i) is
+  begin
+    if rising_edge(clk_sys_i) then
+      if rst_n_i = '0' or cpu_rst_n_i = '0' then
+        state <= S_VOTER;
+        cpu_rst <= (others => '1');
+        cpu_recovery <= '0';
+        nbr_cpu_data_err <= (others => '0');
+        nbr_cpu_iaddr_err <= (others => '0');
+        reset_cause_cpu <= '0';
+        reset_cause_ecc <= '0';
+        reset_cause_wd <= '0';
+      else
+        case state is
+          when S_VOTER =>
+            cpu_rst <= "000";
+            --  Software can clear recovery flag.
+            if cpu_wr = '1' and cpu_recovery_in = '0' then
+              cpu_recovery <= '0';
+            end if;
+            if err_cpu_dm = '1' then
+              nbr_cpu_data_err <= std_logic_vector(unsigned(nbr_cpu_data_err) + 1);
+            end if;
+            if cpu_sync = "110" or cpu_sync = "101" or cpu_sync = "011" then
+              --  Disable the cpu out of sync.
+              cpu_rst <= not cpu_sync;
+              nbr_cpu_iaddr_err <= std_logic_vector(unsigned(nbr_cpu_iaddr_err) + 1);
+              state <= S_LOCK;
+            end if;
+          when S_LOCK =>
+            if cpu_wr = '1' and cpu_recovery_in = '1' then
+              cpu_recovery <= '1';
+            end if;
+        end case;
+
+        if cpu_sync = "000" or cpu_rst_err = '1' or (err_cpu_dm = '1' and state = S_LOCK) then
+          --  Fatal error
+          cpu_rst <= (others => '1');
+          reset_cause_cpu <= not cpu_rst_err;
+          reset_cause_ecc <= dram_ecc_fatal or iram_ecc_fatal;
+          reset_cause_wd <= wd_timeout;
+          state <= S_VOTER;
+        end if;
+      end if;
+    end if;
+  end process;  
 end arch;
diff --git a/hdl/rtl/hydra_triple_cpu.vhd b/hdl/rtl/hydra_triple_cpu.vhd
index 7d19b2fecafcb051b5adc406eb936e206cf0a32d..c0b819634b4ddab6348fa1ae6870a6a989e3c52c 100644
--- a/hdl/rtl/hydra_triple_cpu.vhd
+++ b/hdl/rtl/hydra_triple_cpu.vhd
@@ -6,7 +6,7 @@ use work.urv_pkg.all;
 entity hydra_triple_cpu is
   port (
     clk_i : in std_logic;
-    rst_i : in std_logic;
+    cpu_rst_i : in std_logic_vector(3 downto 1);
     im_data_i : in std_logic_vector (31 downto 0);
     im_valid_i : in std_logic;
     im_addr_o : out std_logic_vector (31 downto 0);
@@ -19,25 +19,27 @@ entity hydra_triple_cpu is
     dm_data_select_o : out std_logic_vector (3 downto 0);
     dm_store_o : out std_logic;
     dm_load_o : out std_logic;
-    
+
+    --  True when a cpu is in sync with at least another one.
+    --  (ie same outputs on the instruction bus)
+    cpu_sync_o : out std_logic_vector(3 downto 1);
+
     --  If set, will give different values to each cpu.
     dm_force_divergence_i : in std_logic;
 
-    --  Set to 1 if an output differs
-    err_cpu_o : out std_logic);
+    --  Set to 1 if an output differs on the data bus
+    err_cpu_dm_o : out std_logic);
 end entity hydra_triple_cpu;
 
 architecture arch of hydra_triple_cpu is
   type t_slv32_x3 is array(1 to 3) of std_logic_vector(31 downto 0);
   type t_slv4_x3 is array(1 to 3) of std_logic_vector(3 downto 0);
-  subtype t_sl_x3 is std_logic_vector(1 to 3);
+  subtype t_sl_x3 is std_logic_vector(3 downto 1);
   signal im_addr, dm_addr, dm_data_s, dm_data_l : t_slv32_x3;
-  signal err_im_addr, err_dm_addr, err_dm_data_s : std_logic;
   signal dm_data_select : t_slv4_x3;
-  signal err_dm_data_select : std_logic;
   signal im_rd, dm_store, dm_load : t_sl_x3;
-  signal err_dm_store, err_dm_load, err_im_rd : std_logic;
-  signal en_im, en_dm_load, en_dm_store : std_logic;
+
+  signal ok_dm_addr, ok_dm_data, ok_dm_select, ok_dm_load, ok_dm_store, ok_dm : std_logic_vector(3 downto 1);
 begin
   gen_triple: for i in 1 to 3 generate
     inst_cpu : urv_cpu
@@ -49,7 +51,7 @@ begin
         )
       port map (
         clk_i            => clk_i,
-        rst_i            => rst_i,
+        rst_i            => cpu_rst_i(i),
         irq_i            => '0',
         im_addr_o        => im_addr(i),
         im_rd_o          => im_rd(i),
@@ -82,7 +84,7 @@ begin
       b => im_addr(2),
       c => im_addr(3),
       res => im_addr_o,
-      err => err_im_addr
+      err => open
     );
 
   inst_voter_dm_addr: entity work.voter_vec_status
@@ -94,7 +96,7 @@ begin
       b => dm_addr(2),
       c => dm_addr(3),
       res => dm_addr_o,
-      err => err_dm_addr
+      err => open
     );
  
   inst_voter_dm_data_s: entity work.voter_vec_status
@@ -106,7 +108,7 @@ begin
       b => dm_data_s(2),
       c => dm_data_s(3),
       res => dm_data_s_o,
-      err => err_dm_data_s
+      err => open
     );
  
   inst_voter_dm_data_select: entity work.voter_vec_status
@@ -118,38 +120,71 @@ begin
       b => dm_data_select(2),
       c => dm_data_select(3),
       res => dm_data_select_o,
-      err => err_dm_data_select
+      err => open
     );
 
   inst_voter_im_rd: entity work.voter_status
     port map (
       inp => im_rd,
       res => im_rd_o,
-      err => err_im_rd
+      err => open
     );
 
   inst_voter_dm_load: entity work.voter_status
     port map (
       inp => dm_load,
       res => dm_load_o,
-      err => err_dm_load
+      err => open
     );
 
   inst_voter_dm_store: entity work.voter_status
     port map (
       inp => dm_store,
       res => dm_store_o,
-      err => err_dm_store
+      err => open
     );
 
   dm_data_l(1) <= dm_data_l_i when dm_force_divergence_i = '0' else x"0000_0001";
   dm_data_l(2) <= dm_data_l_i when dm_force_divergence_i = '0' else x"0000_0002";
   dm_data_l(3) <= dm_data_l_i when dm_force_divergence_i = '0' else x"0000_0004";
 
-  en_im <= im_rd(1) or im_rd(2) or im_rd(3);
-  en_dm_load <= dm_load(1) or dm_load(2) or dm_load(3);
-  en_dm_store <= dm_store(1) or dm_store(2) or dm_store(3);
-  err_cpu_o <= (en_im and err_im_addr) or err_im_rd
-     or ((en_dm_load or en_dm_store) and err_dm_addr)
-     or (en_dm_store and (err_dm_data_s or err_dm_data_select)) or err_dm_load or err_dm_store;
+  gen_s: for i in 0 to 2 generate
+    constant n0 : natural := 1 + i;
+    constant n1 : natural := 1 + (i + 1) mod 3;
+    constant n2 : natural := 1 + (i + 2) mod 3;
+  begin
+    cpu_sync_o(n0) <= '1' when cpu_rst_i(n0) = '1'
+       or (im_rd(n0) = '0' and (im_rd(n1) = '0' or im_rd(n2) = '0'))
+       or (im_rd(n0) = '1' and ((im_rd(n1) = '1' and im_addr(n1) = im_addr(n0))
+                                or (im_rd(n2) = '1' and im_addr(n2) = im_addr(n0)))) else '0';
+    ok_dm_addr (n0) <= '1' when cpu_rst_i(n0) = '1'
+        or (dm_load(n0) = '0' and (dm_load(n1) = '0' or dm_load(n2) = '0'))
+        or (dm_store(n0) = '0' and (dm_store(n1) = '0' or dm_store(n2) = '0'))
+        or (((dm_load(n0) = '1' and dm_load(n1) = '1') or (dm_store(n0) = '1' and dm_store(n1) = '1'))
+            and dm_addr(n0) = dm_addr(n1))
+        or (((dm_load(n0) = '1' and dm_load(n2) = '1') or (dm_store(n0) = '1' and dm_store(n2) = '1'))
+           and dm_addr(n0) = dm_addr(n2))
+      else '0';
+    ok_dm_data (n0) <= '1' when cpu_rst_i(n0) = '1'
+        or (dm_store(n0) = '0' and (dm_store(n1) = '0' or dm_store(n2) = '0'))
+        or ((dm_store(n0) = '1' and dm_store(n1) = '1') and dm_data_s(n0) = dm_data_s(n1))
+        or ((dm_store(n0) = '1' and dm_store(n2) = '1') and dm_data_s(n0) = dm_data_s(n2))
+      else '0';
+    ok_dm_select (n0) <= '1' when cpu_rst_i(n0) = '1'
+        or (dm_store(n0) = '0' and (dm_store(n1) = '0' or dm_store(n2) = '0'))
+        or ((dm_store(n0) = '1' and dm_store(n1) = '1') and dm_data_select(n0) = dm_data_select(n1))
+        or ((dm_store(n0) = '1' and dm_store(n2) = '1') and dm_data_select(n0) = dm_data_select(n2))
+      else '0';
+    ok_dm_store (n0) <= '1' when cpu_rst_i(n0) = '1'
+       or (dm_store(n0) = dm_store(n1))
+       or (dm_store(n0) = dm_store(n2)) else '0';
+    ok_dm_load (n0) <= '1' when cpu_rst_i(n0) = '1'
+       or (dm_load(n0) = dm_load(n1))
+       or (dm_load(n0) = dm_load(n2)) else '0';
+    ok_dm (n0) <= ok_dm_addr(n0) and ok_dm_data(n0) and ok_dm_select(n0) and ok_dm_store(n0) and ok_dm_load(n0);
+
+  end generate;
+
+  err_cpu_dm_o <= '1' when ok_dm /= "111" else '0';
+
 end arch;
diff --git a/sw/sf2-test/main.c b/sw/sf2-test/main.c
index 6e09eb32093b227d424b3edf30aeb22f11c9bbaf..693e6872b75aaa714e4a184a7f9fa4add33729cf 100644
--- a/sw/sf2-test/main.c
+++ b/sw/sf2-test/main.c
@@ -14,6 +14,29 @@
 #define SUPERVISOR ((volatile struct hydra_supervisor_regs *)SUPERVISOR_BASE)
 #define WD_KEY 0xc0423bc9
 
+static enum t_test
+  {
+   TEST_WD,
+   TEST_CPU0_ERR
+} cur_test;
+
+static void
+uart_init (void)
+{
+  /* Unreset UART 0 */
+  *(volatile unsigned *)SYS_RESET &= 0xffffff7f;
+
+  /* Select baud.  */
+  *(volatile unsigned *)UART_LCR = 0x80;
+
+  /* Set baudrate */
+  *(volatile unsigned *)UART_DLR = 0x36;
+  *(volatile unsigned *)UART_DMR = 0x00;
+
+  /* start operation.  */
+  *(volatile unsigned *)UART_LCR = 0x03;
+}
+
 static void
 uart_raw_putc (unsigned char c)
 {
@@ -88,24 +111,16 @@ ram_test(void)
 int
 main (void)
 {
-  /* Unreset UART 0 */
-  *(volatile unsigned *)SYS_RESET &= 0xffffff7f;
-
-  /* Select baud.  */
-  *(volatile unsigned *)UART_LCR = 0x80;
-
-  /* Set baudrate */
-  *(volatile unsigned *)UART_DLR = 0x36;
-  *(volatile unsigned *)UART_DMR = 0x00;
-
-  /* start operation.  */
-  *(volatile unsigned *)UART_LCR = 0x03;
+  uart_init ();
 
   uart_puts ("Rst: ");
   unsigned v = SUPERVISOR->reset_cause;
   uart_put_hex_digit (v & 0x0f);
   uart_putc('\n');
 
+  /* On the first reset, v=0 (no cause), so watchdog won't be restarted
+     and will expire during ram test.
+     On later reset, there is always a reset cause.  */
   if (v)
     SUPERVISOR->wd_key = WD_KEY;
 
@@ -115,7 +130,22 @@ main (void)
       uart_puts("Error\n");
   }
 
-  /* Read a different value. */
+  /* Read a different value.
+     This will trigger a vote and a data error increment. */
+  v = SUPERVISOR->force_divergence;
+  uart_put_hex_digit(v & 0x0f);
+
+  /* Force divergence by executing different code.  */
+  v = SUPERVISOR->force_divergence;
+  if (v != 1)
+    uart_putc('D');
+
+  /* Must be in lock-step.  */
+  v = SUPERVISOR->cpu;
+  uart_put_hex_digit(v & 0x0f);
+
+  /* Force a data error in lock-step.
+     This will generate a reset.  */
   v = SUPERVISOR->force_divergence;
   uart_put_hex_digit(v & 0x0f);