From bb1720757d0cd32d2fd7d4febe9ececb9caa22a7 Mon Sep 17 00:00:00 2001
From: Tristan Gingold <tristan.gingold@cern.ch>
Date: Mon, 9 May 2022 13:32:08 +0200
Subject: [PATCH] Add cycle counter for iram ecc

---
 hdl/rtl/hydra_core.vhd              | 22 +++++--
 hdl/rtl/hydra_iram.vhd              | 34 +++++++----
 hdl/rtl/hydra_supervisor_regs.cheby | 24 ++++++--
 hdl/rtl/hydra_supervisor_regs.vhd   | 90 +++++++++++++++++++----------
 sw/include/hydra_supervisor_regs.h  | 87 ++++++++++++++++++----------
 sw/sf2-test/main.c                  | 18 +++---
 6 files changed, 185 insertions(+), 90 deletions(-)

diff --git a/hdl/rtl/hydra_core.vhd b/hdl/rtl/hydra_core.vhd
index d2d489f..20e9332 100644
--- a/hdl/rtl/hydra_core.vhd
+++ b/hdl/rtl/hydra_core.vhd
@@ -124,13 +124,15 @@ architecture arch of hydra_core is
   signal nbr_dram_ecc_corr   : std_logic_vector (31 downto 0);
   signal nbr_iram_scrub_corr   : std_logic_vector (31 downto 0);
   signal nbr_dram_scrub_corr   : std_logic_vector (31 downto 0);
-  signal nbr_iram_scrub_uncorr : std_logic_vector (31 downto 0);
+  signal nbr_iram_scrub_uncorr_curr, nbr_iram_scrub_uncorr_last : std_logic_vector (15 downto 0);
   signal nbr_dram_scrub_uncorr : std_logic_vector (31 downto 0);
+  signal nbr_iram_scrub_cycle  : std_logic_vector (7 downto 0);
   signal iram_ecc_err, iram_ecc_fatal : std_logic;
   signal iram_scrub_se, iram_scrub_de : std_logic;
   signal dram_ecc_err, dram_ecc_fatal : std_logic;
   signal dram_scrub_se, dram_scrub_de : std_logic;
   signal iram_scrub_en, dram_scrub_en : std_logic;
+  signal iram_scrub_cycle : std_logic;
   signal scrub_cfg_wr, scrub_cfg_iram_val, scrub_cfg_dram_val : std_logic;
   signal nbr_cpu_data_err : std_logic_vector (31 downto 0);
   signal nbr_cpu_iaddr_err : std_logic_vector (31 downto 0);
@@ -238,6 +240,7 @@ begin
       ecc_fatal_addr_o => iram_de_addr(g_IRAM_LOG_SIZE - 1 downto 2),
       scrub_se_o => iram_scrub_se,
       scrub_de_o => iram_scrub_de,
+      scrub_cycle_o => iram_scrub_cycle,
       scrubber_period_i => iram_scrub_period
     );
 
@@ -423,7 +426,9 @@ begin
         nbr_dram_ecc_corr <= (others => '0');
         nbr_iram_scrub_corr <= (others => '0');
         nbr_dram_scrub_corr <= (others => '0');
-        nbr_iram_scrub_uncorr <= (others => '0');
+        nbr_iram_scrub_uncorr_curr <= (others => '0');
+        nbr_iram_scrub_uncorr_last <= (others => '0');
+        nbr_iram_scrub_cycle <= (others => '0');
         nbr_dram_scrub_uncorr <= (others => '0');
       else
         if iram_ecc_err = '1' then
@@ -432,8 +437,13 @@ begin
         if iram_scrub_se = '1' then
           nbr_iram_scrub_corr <= std_logic_vector(unsigned(nbr_iram_scrub_corr) + 1);
         end if;
-        if iram_scrub_de = '1' then
-          nbr_iram_scrub_uncorr <= std_logic_vector(unsigned(nbr_iram_scrub_uncorr) + 1);
+        if iram_scrub_de = '1' and nbr_iram_scrub_uncorr_curr(15) /= '1' then
+          nbr_iram_scrub_uncorr_curr <= std_logic_vector(unsigned(nbr_iram_scrub_uncorr_curr) + 1);
+        end if;
+        if iram_scrub_cycle = '1' then
+          nbr_iram_scrub_uncorr_last <= nbr_iram_scrub_uncorr_curr;
+          nbr_iram_scrub_uncorr_curr <= (others => '0');
+          nbr_iram_scrub_cycle <= std_logic_vector(unsigned(nbr_iram_scrub_cycle) + 1);
         end if;
         if dram_ecc_err = '1' then
           nbr_dram_ecc_corr <= std_logic_vector(unsigned(nbr_dram_ecc_corr) + 1);
@@ -523,7 +533,9 @@ begin
       wd_key_wr_o => wd_key_wr,
       iram_ecc_se_i => nbr_iram_ecc_corr,
       iram_scrub_se_i => nbr_iram_scrub_corr,
-      iram_scrub_de_i => nbr_iram_scrub_uncorr,
+      iram_scrub_de_curr_i => nbr_iram_scrub_uncorr_curr,
+      iram_scrub_de_last_i => nbr_iram_scrub_uncorr_last,
+      iram_scrub_cycle_i => nbr_iram_scrub_cycle,
       dram_ecc_se_i => nbr_dram_ecc_corr,
       dram_scrub_se_i => nbr_dram_scrub_corr,
       dram_scrub_de_i => nbr_dram_scrub_uncorr,
diff --git a/hdl/rtl/hydra_iram.vhd b/hdl/rtl/hydra_iram.vhd
index edf6c25..9fb6e8c 100644
--- a/hdl/rtl/hydra_iram.vhd
+++ b/hdl/rtl/hydra_iram.vhd
@@ -66,11 +66,12 @@ entity hydra_iram is
     wdone_o      : out std_logic;
 
     --  For statistics
-    ecc_one_o    : out std_logic;
-    ecc_fatal_o  : out std_logic;
+    ecc_one_o        : out std_logic;
+    ecc_fatal_o      : out std_logic;
     ecc_fatal_addr_o : out std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
-    scrub_se_o  : out std_logic;
-    scrub_de_o  : out std_logic;
+    scrub_se_o       : out std_logic;
+    scrub_de_o       : out std_logic;
+    scrub_cycle_o    : out std_logic;
 
     --  Scrubber
     scrubber_period_i : in  std_logic_vector(15 downto 0)
@@ -92,7 +93,13 @@ architecture arch of hydra_iram is
   signal wdone, n_wdone : std_logic;
 
   signal n_ecc_one, n_ecc_fatal : std_logic;
-  signal last_raddr, n_last_raddr, scrub_addr : std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+  signal last_raddr, n_last_raddr : std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+
+  --  The scrubber counter has an extra bit to signal a new cycle.
+  signal scrub_addr_ext : std_logic_vector(g_RAM_LOG_SIZE - 1 + 1 downto 2);
+  alias scrub_cycle : std_logic is scrub_addr_ext (g_RAM_LOG_SIZE);
+  alias scrub_addr : std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2) is scrub_addr_ext (g_RAM_LOG_SIZE - 1 downto 2);
+  signal scrub_cycle_d1, scrub_cycle_d2 : std_logic;
   
   signal scrub_counter : unsigned(15 downto 0);
   signal scrub_rd, scrub_done, n_scrub_done : std_logic;
@@ -150,17 +157,24 @@ begin
   r2_done_o <= r2_done;
   wdone_o <= wdone;
 
+  --  We need to delay scrub_cycle_o so that it happens after a possible
+  --  error on the last address.
+  scrub_cycle_o <= scrub_cycle_d2;
+
   p_scrub: process (clk_i)
   begin
     if rising_edge(clk_i) then
-      if rst_n_i = '0' then
+      if rst_n_i = '0' or scrub_cycle_d2 = '1' then
         scrub_counter <= unsigned(scrubber_period_i);
-        scrub_addr <= (others => '0');
+        scrub_addr_ext <= (others => '0');
         scrub_rd <= '0';
+        scrub_cycle_d2 <= '0';
+        scrub_cycle_d1 <= '0';
       else
+        scrub_cycle_d1 <= scrub_cycle;
+        scrub_cycle_d2 <= scrub_cycle_d1;
         if n_scrub_done = '1' then
-          scrub_counter <= unsigned(scrubber_period_i);
-          scrub_addr <= std_logic_vector(unsigned(scrub_addr) + 1);
+          scrub_addr_ext <= std_logic_vector(unsigned(scrub_addr_ext) + 1);
           scrub_rd <= '0';
         elsif scrub_en_i = '1' then
           if scrub_counter = (scrub_counter'range => '0') then
@@ -239,7 +253,7 @@ begin
           ren <= '1';
           n_r2_done <= '1';
           n_last_raddr <= r2_addr_i;
-        elsif scrub_en_i = '1' then
+        elsif scrub_en_i = '1' and scrub_cycle = '0' then
           -- scrub if idle (but not during reset, as write is high priority
           -- and is a single pulse).
           raddr <= scrub_addr;
diff --git a/hdl/rtl/hydra_supervisor_regs.cheby b/hdl/rtl/hydra_supervisor_regs.cheby
index c87b88c..2f7351e 100644
--- a/hdl/rtl/hydra_supervisor_regs.cheby
+++ b/hdl/rtl/hydra_supervisor_regs.cheby
@@ -76,15 +76,29 @@ memory-map:
       description: Number of ECC correctable errors for iram
       width: 32
       access: ro
+  - reg:
+      name: iram_scrub_cycle
+      description: IRAM scrubber cycle number.
+      width: 8
+      access: ro
   - reg:
       name: iram_scrub_se
       description: Number of ECC corrected errors by scrubber in iram
       width: 32
       access: ro
   - reg:
-      name: iram_scrub_de
-      description: Number of ECC uncorrected errors by scrubber in iram
-      width: 32
+      name: iram_scrub_de_curr
+      description: >
+        Number of ECC uncorrected errors by scrubber in iram in the current
+        scrubber cycle.  Saturate.
+      width: 16
+      access: ro
+  - reg:
+      name: iram_scrub_de_last
+      description: >
+        Number of ECC uncorrected errors by scrubber in iram during the last
+        scrubber cycle.  Saturate.
+      width: 16
       access: ro
   - reg:
       name: dram_ecc_se
@@ -132,11 +146,11 @@ memory-map:
       children:
       - field:
           name: iram_en
-          description: Write 1 to enable iram scrubber
+          description: Write 1 to enable iram scrubber (cannot be disabled)
           range: 0
       - field:
           name: dram_en
-          description: Write 1 to enable dram scrubber
+          description: Write 1 to enable dram scrubber (cannot be disabled)
           range: 1
   - reg:
       name: iram_scrub_period
diff --git a/hdl/rtl/hydra_supervisor_regs.vhd b/hdl/rtl/hydra_supervisor_regs.vhd
index 859e95c..25e172b 100644
--- a/hdl/rtl/hydra_supervisor_regs.vhd
+++ b/hdl/rtl/hydra_supervisor_regs.vhd
@@ -52,11 +52,19 @@ entity hydra_supervisor_regs is
     -- Number of ECC correctable errors for iram
     iram_ecc_se_i        : in    std_logic_vector(31 downto 0);
 
+    -- IRAM scrubber cycle number.
+    iram_scrub_cycle_i   : in    std_logic_vector(7 downto 0);
+
     -- Number of ECC corrected errors by scrubber in iram
     iram_scrub_se_i      : in    std_logic_vector(31 downto 0);
 
-    -- Number of ECC uncorrected errors by scrubber in iram
-    iram_scrub_de_i      : in    std_logic_vector(31 downto 0);
+    -- Number of ECC uncorrected errors by scrubber in iram in the current scrubber cycle.  Saturate.
+
+    iram_scrub_de_curr_i : in    std_logic_vector(15 downto 0);
+
+    -- Number of ECC uncorrected errors by scrubber in iram during the last scrubber cycle.  Saturate.
+
+    iram_scrub_de_last_i : in    std_logic_vector(15 downto 0);
 
     -- Number of ECC correctable errors for dram
     dram_ecc_se_i        : in    std_logic_vector(31 downto 0);
@@ -80,10 +88,10 @@ entity hydra_supervisor_regs is
     iram_de_addr_i       : in    std_logic_vector(31 downto 0);
 
     -- configuration of scrubbers
-    -- Write 1 to enable iram scrubber
+    -- Write 1 to enable iram scrubber (cannot be disabled)
     scrub_cfg_iram_en_i  : in    std_logic;
     scrub_cfg_iram_en_o  : out   std_logic;
-    -- Write 1 to enable dram scrubber
+    -- Write 1 to enable dram scrubber (cannot be disabled)
     scrub_cfg_dram_en_i  : in    std_logic;
     scrub_cfg_dram_en_o  : out   std_logic;
     scrub_cfg_wr_o       : out   std_logic;
@@ -210,9 +218,13 @@ begin
 
   -- Register iram_ecc_se
 
+  -- Register iram_scrub_cycle
+
   -- Register iram_scrub_se
 
-  -- Register iram_scrub_de
+  -- Register iram_scrub_de_curr
+
+  -- Register iram_scrub_de_last
 
   -- Register dram_ecc_se
 
@@ -315,42 +327,48 @@ begin
       -- Reg iram_ecc_se
       wr_ack_int <= wr_req_d0;
     when "00111" =>
-      -- Reg iram_scrub_se
+      -- Reg iram_scrub_cycle
       wr_ack_int <= wr_req_d0;
     when "01000" =>
-      -- Reg iram_scrub_de
+      -- Reg iram_scrub_se
       wr_ack_int <= wr_req_d0;
     when "01001" =>
-      -- Reg dram_ecc_se
+      -- Reg iram_scrub_de_curr
       wr_ack_int <= wr_req_d0;
     when "01010" =>
-      -- Reg dram_scrub_se
+      -- Reg iram_scrub_de_last
       wr_ack_int <= wr_req_d0;
     when "01011" =>
-      -- Reg dram_scrub_de
+      -- Reg dram_ecc_se
       wr_ack_int <= wr_req_d0;
     when "01100" =>
-      -- Reg cpu_data_err
+      -- Reg dram_scrub_se
       wr_ack_int <= wr_req_d0;
     when "01101" =>
-      -- Reg cpu_iaddr_err
+      -- Reg dram_scrub_de
       wr_ack_int <= wr_req_d0;
     when "01110" =>
+      -- Reg cpu_data_err
+      wr_ack_int <= wr_req_d0;
+    when "01111" =>
+      -- Reg cpu_iaddr_err
+      wr_ack_int <= wr_req_d0;
+    when "10000" =>
       -- Reg dram_ecc_mask
       dram_ecc_mask_wreq <= wr_req_d0;
       wr_ack_int <= dram_ecc_mask_wack;
-    when "01111" =>
+    when "10001" =>
       -- Reg iram_de_addr
       wr_ack_int <= wr_req_d0;
-    when "10000" =>
+    when "10010" =>
       -- Reg scrub_cfg
       scrub_cfg_wreq <= wr_req_d0;
       wr_ack_int <= wr_req_d0;
-    when "10001" =>
+    when "10011" =>
       -- Reg iram_scrub_period
       iram_scrub_period_wreq <= wr_req_d0;
       wr_ack_int <= iram_scrub_period_wack;
-    when "10010" =>
+    when "10100" =>
       -- Reg dram_scrub_period
       dram_scrub_period_wreq <= wr_req_d0;
       wr_ack_int <= dram_scrub_period_wack;
@@ -363,7 +381,8 @@ begin
   process (adr_int, rd_req_int, reset_cause_cpu_i, reset_cause_iram_ecc_i,
            reset_cause_dram_ecc_i, reset_cause_watchdog_i, cpu_reset_i,
            cpu_recovery_i, force_divergence_i, wd_period_i, wd_count_i,
-           iram_ecc_se_i, iram_scrub_se_i, iram_scrub_de_i, dram_ecc_se_i,
+           iram_ecc_se_i, iram_scrub_cycle_i, iram_scrub_se_i,
+           iram_scrub_de_curr_i, iram_scrub_de_last_i, dram_ecc_se_i,
            dram_scrub_se_i, dram_scrub_de_i, cpu_data_err_i, cpu_iaddr_err_i,
            dram_ecc_mask_reg, iram_de_addr_i, scrub_cfg_iram_en_i,
            scrub_cfg_dram_en_i, iram_scrub_period_reg, dram_scrub_period_reg) begin
@@ -407,54 +426,65 @@ begin
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= iram_ecc_se_i;
     when "00111" =>
-      -- Reg iram_scrub_se
+      -- Reg iram_scrub_cycle
       rd_ack_d0 <= rd_req_int;
-      rd_dat_d0 <= iram_scrub_se_i;
+      rd_dat_d0(7 downto 0) <= iram_scrub_cycle_i;
+      rd_dat_d0(31 downto 8) <= (others => '0');
     when "01000" =>
-      -- Reg iram_scrub_de
+      -- Reg iram_scrub_se
       rd_ack_d0 <= rd_req_int;
-      rd_dat_d0 <= iram_scrub_de_i;
+      rd_dat_d0 <= iram_scrub_se_i;
     when "01001" =>
+      -- Reg iram_scrub_de_curr
+      rd_ack_d0 <= rd_req_int;
+      rd_dat_d0(15 downto 0) <= iram_scrub_de_curr_i;
+      rd_dat_d0(31 downto 16) <= (others => '0');
+    when "01010" =>
+      -- Reg iram_scrub_de_last
+      rd_ack_d0 <= rd_req_int;
+      rd_dat_d0(15 downto 0) <= iram_scrub_de_last_i;
+      rd_dat_d0(31 downto 16) <= (others => '0');
+    when "01011" =>
       -- Reg dram_ecc_se
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= dram_ecc_se_i;
-    when "01010" =>
+    when "01100" =>
       -- Reg dram_scrub_se
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= dram_scrub_se_i;
-    when "01011" =>
+    when "01101" =>
       -- Reg dram_scrub_de
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= dram_scrub_de_i;
-    when "01100" =>
+    when "01110" =>
       -- Reg cpu_data_err
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= cpu_data_err_i;
-    when "01101" =>
+    when "01111" =>
       -- Reg cpu_iaddr_err
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= cpu_iaddr_err_i;
-    when "01110" =>
+    when "10000" =>
       -- Reg dram_ecc_mask
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0(7 downto 0) <= dram_ecc_mask_reg;
       rd_dat_d0(31 downto 8) <= (others => '0');
-    when "01111" =>
+    when "10001" =>
       -- Reg iram_de_addr
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0 <= iram_de_addr_i;
-    when "10000" =>
+    when "10010" =>
       -- Reg scrub_cfg
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0(0) <= scrub_cfg_iram_en_i;
       rd_dat_d0(1) <= scrub_cfg_dram_en_i;
       rd_dat_d0(31 downto 2) <= (others => '0');
-    when "10001" =>
+    when "10011" =>
       -- Reg iram_scrub_period
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0(15 downto 0) <= iram_scrub_period_reg;
       rd_dat_d0(31 downto 16) <= (others => '0');
-    when "10010" =>
+    when "10100" =>
       -- Reg dram_scrub_period
       rd_ack_d0 <= rd_req_int;
       rd_dat_d0(15 downto 0) <= dram_scrub_period_reg;
diff --git a/sw/include/hydra_supervisor_regs.h b/sw/include/hydra_supervisor_regs.h
index a92d887..b74baf7 100644
--- a/sw/include/hydra_supervisor_regs.h
+++ b/sw/include/hydra_supervisor_regs.h
@@ -1,6 +1,6 @@
 #ifndef __CHEBY__HYDRA_SUPERVISOR_REGS__H__
 #define __CHEBY__HYDRA_SUPERVISOR_REGS__H__
-#define HYDRA_SUPERVISOR_REGS_SIZE 74 /* 0x4a */
+#define HYDRA_SUPERVISOR_REGS_SIZE 82 /* 0x52 */
 
 /* Cause of a reset */
 #define HYDRA_SUPERVISOR_REGS_RESET_CAUSE 0x0UL
@@ -30,44 +30,52 @@
 /* Number of ECC correctable errors for iram */
 #define HYDRA_SUPERVISOR_REGS_IRAM_ECC_SE 0x18UL
 
+/* IRAM scrubber cycle number. */
+#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_CYCLE 0x1cUL
+
 /* Number of ECC corrected errors by scrubber in iram */
-#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_SE 0x1cUL
+#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_SE 0x20UL
+
+/* Number of ECC uncorrected errors by scrubber in iram in the current scrubber cycle.  Saturate.
+ */
+#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_DE_CURR 0x24UL
 
-/* Number of ECC uncorrected errors by scrubber in iram */
-#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_DE 0x20UL
+/* Number of ECC uncorrected errors by scrubber in iram during the last scrubber cycle.  Saturate.
+ */
+#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_DE_LAST 0x28UL
 
 /* Number of ECC correctable errors for dram */
-#define HYDRA_SUPERVISOR_REGS_DRAM_ECC_SE 0x24UL
+#define HYDRA_SUPERVISOR_REGS_DRAM_ECC_SE 0x2cUL
 
 /* Number of ECC corrected errors by scrubber in dram */
-#define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_SE 0x28UL
+#define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_SE 0x30UL
 
 /* Number of ECC uncorrected errors by scrubber in dram */
-#define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_DE 0x2cUL
+#define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_DE 0x34UL
 
 /* Number of CPU errors on data bus */
-#define HYDRA_SUPERVISOR_REGS_CPU_DATA_ERR 0x30UL
+#define HYDRA_SUPERVISOR_REGS_CPU_DATA_ERR 0x38UL
 
 /* Number of CPU errors on instruction bus */
-#define HYDRA_SUPERVISOR_REGS_CPU_IADDR_ERR 0x34UL
+#define HYDRA_SUPERVISOR_REGS_CPU_IADDR_ERR 0x3cUL
 
 /* Change ECC bits on dram writes for testing */
-#define HYDRA_SUPERVISOR_REGS_DRAM_ECC_MASK 0x38UL
+#define HYDRA_SUPERVISOR_REGS_DRAM_ECC_MASK 0x40UL
 
 /* Address of the last fatal error */
-#define HYDRA_SUPERVISOR_REGS_IRAM_DE_ADDR 0x3cUL
+#define HYDRA_SUPERVISOR_REGS_IRAM_DE_ADDR 0x44UL
 
 /* configuration of scrubbers */
-#define HYDRA_SUPERVISOR_REGS_SCRUB_CFG 0x40UL
+#define HYDRA_SUPERVISOR_REGS_SCRUB_CFG 0x48UL
 #define HYDRA_SUPERVISOR_REGS_SCRUB_CFG_IRAM_EN 0x1UL
 #define HYDRA_SUPERVISOR_REGS_SCRUB_CFG_DRAM_EN 0x2UL
 
 /* Maximum number of cycles between a scrub */
-#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_PERIOD 0x44UL
+#define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_PERIOD 0x4cUL
 #define HYDRA_SUPERVISOR_REGS_IRAM_SCRUB_PERIOD_PRESET 0x7ffUL
 
 /* Maximum number of cycles between a scrub */
-#define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_PERIOD 0x48UL
+#define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_PERIOD 0x50UL
 #define HYDRA_SUPERVISOR_REGS_DRAM_SCRUB_PERIOD_PRESET 0x7ffUL
 
 struct hydra_supervisor_regs {
@@ -92,46 +100,63 @@ struct hydra_supervisor_regs {
   /* [0x18]: REG (ro) Number of ECC correctable errors for iram */
   uint32_t iram_ecc_se;
 
-  /* [0x1c]: REG (ro) Number of ECC corrected errors by scrubber in iram */
+  /* [0x1c]: REG (ro) IRAM scrubber cycle number. */
+  uint8_t iram_scrub_cycle;
+
+  /* padding to: 32 words */
+  uint8_t __padding_0[3];
+
+  /* [0x20]: REG (ro) Number of ECC corrected errors by scrubber in iram */
   uint32_t iram_scrub_se;
 
-  /* [0x20]: REG (ro) Number of ECC uncorrected errors by scrubber in iram */
-  uint32_t iram_scrub_de;
+  /* [0x24]: REG (ro) Number of ECC uncorrected errors by scrubber in iram in the current scrubber cycle.  Saturate.
+ */
+  uint16_t iram_scrub_de_curr;
 
-  /* [0x24]: REG (ro) Number of ECC correctable errors for dram */
+  /* padding to: 40 words */
+  uint8_t __padding_1[2];
+
+  /* [0x28]: REG (ro) Number of ECC uncorrected errors by scrubber in iram during the last scrubber cycle.  Saturate.
+ */
+  uint16_t iram_scrub_de_last;
+
+  /* padding to: 44 words */
+  uint8_t __padding_2[2];
+
+  /* [0x2c]: REG (ro) Number of ECC correctable errors for dram */
   uint32_t dram_ecc_se;
 
-  /* [0x28]: REG (ro) Number of ECC corrected errors by scrubber in dram */
+  /* [0x30]: REG (ro) Number of ECC corrected errors by scrubber in dram */
   uint32_t dram_scrub_se;
 
-  /* [0x2c]: REG (ro) Number of ECC uncorrected errors by scrubber in dram */
+  /* [0x34]: REG (ro) Number of ECC uncorrected errors by scrubber in dram */
   uint32_t dram_scrub_de;
 
-  /* [0x30]: REG (ro) Number of CPU errors on data bus */
+  /* [0x38]: REG (ro) Number of CPU errors on data bus */
   uint32_t cpu_data_err;
 
-  /* [0x34]: REG (ro) Number of CPU errors on instruction bus */
+  /* [0x3c]: REG (ro) Number of CPU errors on instruction bus */
   uint32_t cpu_iaddr_err;
 
-  /* [0x38]: REG (rw) Change ECC bits on dram writes for testing */
+  /* [0x40]: REG (rw) Change ECC bits on dram writes for testing */
   uint8_t dram_ecc_mask;
 
-  /* padding to: 60 words */
-  uint8_t __padding_0[3];
+  /* padding to: 68 words */
+  uint8_t __padding_3[3];
 
-  /* [0x3c]: REG (ro) Address of the last fatal error */
+  /* [0x44]: REG (ro) Address of the last fatal error */
   uint32_t iram_de_addr;
 
-  /* [0x40]: REG (rw) configuration of scrubbers */
+  /* [0x48]: REG (rw) configuration of scrubbers */
   uint32_t scrub_cfg;
 
-  /* [0x44]: REG (rw) Maximum number of cycles between a scrub */
+  /* [0x4c]: REG (rw) Maximum number of cycles between a scrub */
   uint16_t iram_scrub_period;
 
-  /* padding to: 72 words */
-  uint8_t __padding_1[2];
+  /* padding to: 80 words */
+  uint8_t __padding_4[2];
 
-  /* [0x48]: REG (rw) Maximum number of cycles between a scrub */
+  /* [0x50]: REG (rw) Maximum number of cycles between a scrub */
   uint16_t dram_scrub_period;
 };
 
diff --git a/sw/sf2-test/main.c b/sw/sf2-test/main.c
index 9b50cd4..592f89b 100644
--- a/sw/sf2-test/main.c
+++ b/sw/sf2-test/main.c
@@ -222,7 +222,7 @@ main (void)
     if (SUPERVISOR->dram_scrub_se != 0
         || SUPERVISOR->dram_scrub_de != 0)
       unreachable();
-    uart_puts("Start DRAM scrubber");
+    uart_puts("Scrub DRAM");
     SUPERVISOR->scrub_cfg = HYDRA_SUPERVISOR_REGS_SCRUB_CFG_DRAM_EN;
     next_test = TEST_IRAM_SE;
     do {
@@ -254,23 +254,23 @@ main (void)
 
   case TEST_IRAM_SCRUB:
     if (SUPERVISOR->iram_scrub_se != 0
-        || SUPERVISOR->iram_scrub_de != 0)
+        || SUPERVISOR->iram_scrub_de_curr != 0
+        || SUPERVISOR->iram_scrub_de_last != 0
+        || SUPERVISOR->iram_scrub_cycle != 0)
       unreachable();
-    uart_puts("Start IRAM scrubber");
+    uart_puts("Scrub IRAM");
     SUPERVISOR->scrub_cfg = HYDRA_SUPERVISOR_REGS_SCRUB_CFG_IRAM_EN;
     next_test = TEST_WD;
     do {
-      v = SUPERVISOR->iram_scrub_de;
       SUPERVISOR->wd_key = WD_KEY;
-    } while (v == 0);
-    /* Because the scrubber is always running, the counter is always
-       incrementing.  So the original value is kept. */
+    } while (SUPERVISOR->iram_scrub_cycle == 0);
     uart_putc('\n');
-    if (SUPERVISOR->iram_scrub_se != 1 || v > 2) {
+    if (SUPERVISOR->iram_scrub_se != 1
+        || SUPERVISOR->iram_scrub_de_last !=  2) {
       uart_puts ("se:");
       uart_put_hex8 (SUPERVISOR->iram_scrub_se);
       uart_puts (", de:");
-      uart_put_hex8 (v);
+      uart_put_hex8 (SUPERVISOR->iram_scrub_de_last);
       unreachable();
     }
     /* Fallthrough */
-- 
GitLab