diff --git a/hdl/rtl/hydra_core.vhd b/hdl/rtl/hydra_core.vhd
index f5d6972980c268bb7c58b13644532cc2b5680c49..854da39ec9814b9777e384f6609b8cff85e4bd63 100644
--- a/hdl/rtl/hydra_core.vhd
+++ b/hdl/rtl/hydra_core.vhd
@@ -94,11 +94,13 @@ architecture arch of hydra_core is
   signal reg_dm_addr, reg_dm_data_s : std_logic_vector(31 downto 0);
   signal reg_dm_data_select         : std_logic_vector(3 downto 0);
   signal reg_dm_load, reg_dm_store  : std_logic;
+  signal reg_dm_en : std_logic;
 
   signal dm_cycle_in_progress, reg_dm_is_wishbone, reg_dm_is_iram : std_logic;
 
   signal dm_mem_rdata, dm_wb_rdata : std_logic_vector(31 downto 0);
   signal dm_wb_write, dm_select_wb : std_logic;
+  signal dm_done, dm_err : std_logic;
 
   signal dwb_out         : t_wishbone_master_out;
 begin
@@ -143,11 +145,8 @@ begin
   begin
     if rising_edge(clk_sys_i) then
       if rst_n_i = '0' then
-        reg_dm_load <= '0';
-        reg_dm_store <= '0';
+        null;
       else
-        reg_dm_load <= dm_load;
-        reg_dm_store <= dm_store;
         if dm_load = '1' or dm_store = '1' then
           reg_dm_addr <= dm_addr;
           reg_dm_data_s <= dm_data_s;
@@ -197,30 +196,29 @@ begin
   -- 2nd 64KB is the DRAM
   reg_dm_is_wishbone <= '1' when reg_dm_addr(31 downto 20) /= x"000" else '0';
   reg_dm_is_iram <= '1' when reg_dm_addr(16) = '0' and reg_dm_is_wishbone = '0' else '0';
-  dm_data_l <= dm_wb_rdata when (dm_select_wb = '1' or reg_dm_is_iram = '1') else
-               dm_mem_rdata;
+  dm_data_l <= dm_wb_rdata;
 
-  p_ram: process (clk_sys_i)
-  is
-    variable dram : t_ram32_type (2**(g_DRAM_LOG_SIZE - 2) - 1 downto 0);
-    variable addr : natural range dram'range;
-  begin
-    if rising_edge(clk_sys_i) then
-      if reg_dm_load = '1' then
-        addr := to_integer(unsigned(reg_dm_addr(g_DRAM_LOG_SIZE - 1 downto 2)));
-        dm_mem_rdata <= dram(addr);
-      else
-        dm_mem_rdata <= (others => 'X');
-      end if;
-      if reg_dm_store = '1' and reg_dm_addr(16) = '1' and reg_dm_is_wishbone = '0' then
-        for i in 0 to 3 loop
-          if reg_dm_data_select (i) = '1' then
-            dram(addr)(8*i + 7 downto 8*i) := reg_dm_data_s(8*i + 7 downto 8*i);
-          end if;
-        end loop;
-      end if;
-    end if;
-  end process;
+  reg_dm_en <= '1' when reg_dm_addr(16) = '1' and reg_dm_is_wishbone = '0' and (reg_dm_store or reg_dm_load) = '1' else '0';
+
+  inst_dram: entity work.hydra_dram
+    generic map (
+      g_ram_log_size => g_dram_log_size
+    )
+    port map (
+      clk_i => clk_sys_i,
+      rst_n_i => rst_n_i,
+      addr_i => reg_dm_addr(g_DRAM_LOG_SIZE - 1 downto 2),
+      en_i => reg_dm_en,
+      we_i => reg_dm_store,
+      sel_i => reg_dm_data_select,
+      data_i => reg_dm_data_s,
+      data_o => dm_mem_rdata,
+      done_o => dm_done,
+      err_o => dm_err,
+--      ecc_one_o => ecc_one_o,
+--      ecc_fatal_o => ecc_fatal_o,
+      scrubber_period_i => x"0010"
+    );
 
   -- Data bus
   -- Wishbone bus arbitration / internal RAM access
@@ -239,28 +237,29 @@ begin
         dwb_out.dat          <= (others => '0');
         dm_cycle_in_progress <= '0';
         dm_select_wb         <= '0';
+        reg_dm_load          <= '0';
+        reg_dm_store         <= '0';
       else
         if dm_cycle_in_progress = '0' then
           --  Data bus was idle.
-          dm_wb_write  <= reg_dm_store;
-          if reg_dm_is_wishbone = '0' then
-            --  Internal access
-            dm_select_wb  <= '0';
-            if reg_dm_store = '1' then
-              dm_store_done <= '1';
-            elsif reg_dm_load = '1' then
-              if reg_dm_is_iram = '1' then
-                --  Need to wait for the done (may need extra cycles due to ECC)
-                dm_cycle_in_progress <= '1';
-              else
-                --  Immediate answer
-                dm_load_done  <= '1';
-              end if;
-            end if;
-          else
-            --  Wishbone access
-            dm_select_wb <= '1';
-            if reg_dm_load = '1' or reg_dm_store = '1' then
+          if dm_load = '1' then
+            reg_dm_load <= '1';
+          end if;
+          if dm_store = '1' then
+            reg_dm_store <= '1';
+          end if;
+
+          if reg_dm_load = '1' or reg_dm_store = '1' then
+            dm_wb_write  <= reg_dm_store;
+            if reg_dm_is_wishbone = '0' then
+              --  Internal access
+              dm_select_wb  <= '0';
+              --  Need to wait for the done (may need extra cycles due to ECC)
+              dm_cycle_in_progress <= '1';
+            --  TODO: detect write access to IRAM ?
+            else
+              --  Wishbone access
+              dm_select_wb <= '1';
               dwb_out.cyc          <= '1';
               dwb_out.stb          <= '1';
               dwb_out.we           <= reg_dm_store;
@@ -272,6 +271,9 @@ begin
           end if;
         else
           --  Transfer in progress
+          --  Cannot start a new transfer.
+          assert dm_load = '0';
+          assert dm_store = '0';
           if dm_select_wb = '1' then
             --  Wishbone transfer in progress.
             if dwb_i.stall = '0' then
@@ -282,8 +284,10 @@ begin
               if dm_wb_write = '0' then
                 dm_wb_rdata  <= f_x_to_zero(dwb_i.dat);
                 dm_load_done <= '1';
+                reg_dm_load <= '0';
               else
                 dm_store_done <= '1';
+                reg_dm_store <= '0';
               end if;
 
               dm_cycle_in_progress <= '0';
@@ -291,10 +295,25 @@ begin
             end if;
           else
             --  IRAM/DRAM transfer in progress
-            if im1_done = '1' and im1_err = '0' then
-              dm_wb_rdata <= im1_data;
-              dm_load_done <= '1';
-              dm_cycle_in_progress <= '0';
+            if reg_dm_is_iram = '1' then
+              if im1_done = '1' and im1_err = '0' then
+                dm_wb_rdata <= im1_data;
+                dm_load_done <= '1';
+                reg_dm_load <= '0';
+                dm_cycle_in_progress <= '0';
+              end if;
+            else
+              if dm_done = '1' and dm_err = '0' then
+                dm_wb_rdata <= dm_mem_rdata;
+                dm_cycle_in_progress <= '0';
+                if dm_wb_write = '0' then
+                  dm_load_done <= '1';
+                  reg_dm_load <= '0';
+                  else
+                  dm_store_done <= '1';
+                  reg_dm_store <= '0';
+                end if;
+              end if;
             end if;
           end if;
         end if;
diff --git a/hdl/rtl/hydra_dram.vhd b/hdl/rtl/hydra_dram.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..f1a80008cb1bb3b9ee0abdb77e5414d3f5a89926
--- /dev/null
+++ b/hdl/rtl/hydra_dram.vhd
@@ -0,0 +1,291 @@
+--------------------------------------------------------------------------------
+-- CERN BE-CO-HT
+-- Mock Turtle
+-- https://gitlab.cern.ch/coht/mockturtle
+--------------------------------------------------------------------------------
+--
+-- unit name:   hydra_dram
+--
+-- description: a 32b RAM for rad-tol systems (with ECC and scrubing)
+--   2 ports: 1 ro, 1 wo.
+--
+--------------------------------------------------------------------------------
+-- Copyright CERN 2014-2018
+--------------------------------------------------------------------------------
+-- Copyright and related rights are licensed under the Solderpad Hardware
+-- License, Version 2.0 (the "License"); you may not use this file except
+-- in compliance with the License. You may obtain a copy of the License at
+-- http://solderpad.org/licenses/SHL-2.0.
+-- Unless required by applicable law or agreed to in writing, software,
+-- hardware and materials distributed under this License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+-- or implied. See the License for the specific language governing permissions
+-- and limitations under the License.
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+use work.secded_32b_pkg.all;
+
+entity hydra_dram is
+  generic(
+    g_RAM_LOG_SIZE : natural := 12);  --  In bytes
+  port(
+    --  Note: only writes are allowed during reset.
+    clk_i            : in  std_logic;
+    rst_n_i          : in  std_logic;
+
+    --  Read port
+    --  RDONE_O is a pulse.
+    addr_i      : in  std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+    en_i        : in  std_logic;
+    we_i        : in  std_logic;
+    sel_i       : in  std_logic_vector(3 downto 0);
+    data_i      : in  std_logic_vector(31 downto 0);
+    data_o      : out std_logic_vector(31 downto 0);
+    done_o      : out std_logic;
+    err_o       : out std_logic;
+
+    --  For statistics
+    ecc_one_o    : out std_logic;
+    ecc_fatal_o  : out std_logic;
+
+    --  Scrubber
+    scrubber_period_i : in  std_logic_vector(15 downto 0)
+  );
+end hydra_dram;
+
+architecture arch of hydra_dram is
+  constant RAM_WSIZE : natural := 2 ** (g_RAM_LOG_SIZE - 2);
+  type t_ram39_type is array(natural range <>) of std_logic_vector(38 downto 0);
+
+  signal addr : std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+  signal rdata_ecc, wdata_ecc : std_logic_vector(38 downto 0);
+  signal wen, ren, ren_d : std_logic;
+
+  signal recc, rsyndrome : std_logic_vector(6 downto 0);
+  signal rerr, rerr_one : std_logic;
+  signal r_done, n_r_done, r_done_d : std_logic;
+  signal p_done, n_p_done : std_logic;
+  signal w_done, n_w_done : std_logic;
+
+  signal n_ecc_one, n_ecc_fatal : std_logic;
+  signal last_addr, n_last_addr, scrub_addr : std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+  signal last_we, n_last_we : std_logic;
+  signal last_sel, n_last_sel : std_logic_vector(3 downto 0);
+  
+  signal scrub_counter : unsigned(15 downto 0);
+  signal scrub_rd, scrub_done, scrub_done_d, n_scrub_done : std_logic;
+
+  type state_t is (S_READ, S_REWRITE);
+  signal state, n_state : state_t;
+begin
+  --  The raw ram.
+  p_ram: process (clk_i)
+  is
+    variable iram : t_ram39_type(0 to RAM_WSIZE - 1);
+    variable d : std_logic_vector(38 downto 0);
+    variable err : std_logic_vector(38 downto 0) := (0 => '1', others => '0');
+    variable sim_cnt : natural;
+  begin
+    if rising_edge(clk_i) then
+      if wen = '1' then
+        iram (to_integer(unsigned(addr))) := wdata_ecc;
+      end if;
+      if ren = '1' then
+        d := iram (to_integer(unsigned(addr)));
+
+        --  Simulate errors.
+        if sim_cnt < 7 then
+          sim_cnt := sim_cnt + 1;
+        else
+          sim_cnt := 0;
+          d := d xor err;
+          err := err(err'left - 1 downto 0) & err(err'left);
+        end if;
+
+        rdata_ecc <= d;
+      else
+        rdata_ecc <= (others => 'X');
+      end if;
+      ren_d <= ren;
+    end if;
+  end process;
+
+  data_o <= rdata_ecc (31 downto 0);
+
+  recc <= f_calc_ecc(rdata_ecc (31 downto 0));
+  rsyndrome <= recc xor rdata_ecc(38 downto 32);
+  rerr <= f_ecc_errors(rsyndrome) and ren_d;
+  rerr_one <= f_ecc_one_error(rsyndrome) and ren_d;
+
+  err_o <= rerr;
+  done_o <= r_done or w_done;
+
+  p_scrub: process (clk_i)
+  begin
+    if rising_edge(clk_i) then
+      if rst_n_i = '0' then
+        scrub_counter <= unsigned(scrubber_period_i);
+        scrub_addr <= (others => '0');
+        scrub_rd <= '0';
+      else
+        if scrub_done = '1' and rerr = '0' then
+          scrub_counter <= unsigned(scrubber_period_i);
+          scrub_addr <= std_logic_vector(unsigned(scrub_addr) + 1);
+          scrub_rd <= '0';
+        else
+          if scrub_counter = (scrub_counter'range => '0') then
+            scrub_rd <= '1';
+          else
+            scrub_counter <= scrub_counter - 1;
+          end if;
+        end if;
+      end if;
+    end if;
+  end process;
+
+  p_ctrl: process (state,
+                   p_done, r_done,  scrub_done, rerr,
+                   rerr_one, rdata_ecc, rsyndrome, last_addr, last_we, last_sel, recc,
+                   en_i, addr_i, we_i, data_i, r_done_d,
+                   scrub_rd, scrub_addr, scrub_done_d, rst_n_i)
+  is
+    variable d : std_logic_vector(31 downto 0);
+  begin
+    wen <= '0';
+    addr <= (others => 'X');
+    wdata_ecc <= (others => 'X');
+    n_w_done <= '0';
+    ren <= '0';
+    n_r_done <= '0';
+    n_p_done <= '0';
+    n_scrub_done <= '0';
+    n_last_we <= '0';
+    n_last_sel <= "0000";
+
+    n_ecc_one <= '0';
+    n_ecc_fatal <= '0';
+    n_state <= state;
+    n_last_addr <= (others => 'X');
+
+    case state is
+      when S_READ =>
+        if (p_done = '1' or r_done = '1' or scrub_done = '1') and rerr = '1' then
+          --  There was an error on the last access.
+          --  Write to fix it.
+          if rerr_one = '1' then
+            --  Correctable: correct it.
+            n_ecc_one <= '1';
+            wdata_ecc <= f_fix_error(rsyndrome, rdata_ecc(38 downto 32), rdata_ecc(31 downto 0));
+          else
+            --  Uncorrectable.  Just recompute the ECC to be able to continue.
+            n_ecc_fatal <= '1';
+            wdata_ecc <= rdata_ecc;
+          end if;
+          addr <= last_addr;
+          wen <= '1';
+          n_last_addr <= last_addr;
+          n_last_we <= last_we;
+          n_last_sel <= last_sel;
+          n_state <= S_REWRITE;
+        elsif last_we = '1' then
+          assert last_sel /= "1111";
+          --  The partial write;
+          d := rdata_ecc(31 downto 0);
+          for i in 3 downto 0 loop
+            if last_sel (i) = '1' then
+              d(8 * i + 7 downto 8 * i) := data_i(8 *i + 7 downto 8 * i);
+            end if;
+          end loop;
+          wen <= '1';
+          wdata_ecc <= (f_calc_ecc (d) & d);
+          addr <= last_addr;
+          n_w_done <= '1';
+        elsif scrub_rd = '1' and scrub_done = '0' then
+          -- scrubber
+          addr <= scrub_addr;
+          ren <= '1';
+          n_scrub_done <= '1';
+          n_last_addr <= scrub_addr;
+        elsif en_i = '1' then
+          addr <= addr_i;
+          n_last_addr <= addr_i;
+          n_state <= S_READ;
+          if we_i = '1' then
+            --  Write
+            if sel_i = "1111" then
+              --  Full write
+              wen <= '1';
+              wdata_ecc <= (f_calc_ecc (data_i) & data_i);
+              n_w_done <= '1';
+            else
+              --  Partial write: first read the word
+              ren <= '1';
+              n_last_we <= we_i;
+              n_last_sel <= sel_i;
+              n_p_done <= '1';
+            end if;
+          else
+            --  Read.
+            ren <= '1';
+            n_r_done <= '1';
+          end if;
+        elsif rst_n_i = '1' then
+          -- scrub if idle (but not during reset)
+          addr <= scrub_addr;
+          ren <= '1';
+          n_scrub_done <= '1';
+          n_last_addr <= scrub_addr;
+        end if;
+      when S_REWRITE =>
+        --  Reread
+        addr <= last_addr;
+        ren <= '1';
+        n_r_done <= r_done_d;
+        n_scrub_done <= scrub_done_d;
+        n_last_addr <= last_addr;
+        n_last_we <= last_we;
+        n_last_sel <= last_sel;
+        n_state <= S_READ;
+    end case;
+  end process;
+
+  p_ctrl_reg: process (clk_i)
+  begin
+    if rising_edge(clk_i) then
+      if rst_n_i = '0' then
+        r_done <= '0';
+        r_done_d <= '0';
+        scrub_done <= '0';
+        scrub_done_d <= '0';
+        p_done <= '0';
+        state <= S_READ;
+        last_addr <= (others => 'X');
+        last_we <= '0';
+        last_sel <= (others => 'X');
+
+        ecc_one_o <= '0';
+        ecc_fatal_o <= '0';
+      else
+        r_done_d <= r_done;
+        r_done <= n_r_done;
+        p_done <= n_p_done;
+        scrub_done_d <= scrub_done;
+        scrub_done <= n_scrub_done;
+
+        state <= n_state;
+        last_addr <= n_last_addr;
+        last_we <= n_last_we;
+        last_sel <= n_last_sel;
+
+        ecc_one_o <= n_ecc_one;
+        ecc_fatal_o <= n_ecc_fatal;
+      end if;
+
+      w_done <= n_w_done;
+    end if;
+  end process;
+end arch;
\ No newline at end of file
diff --git a/hdl/top/sf2-test/sf2_test.vhd b/hdl/top/sf2-test/sf2_test.vhd
index f580898d53e52d35af9ccce4b8dc243ea1b06f90..a866d361be9eaf56553801e561d1fb7f511e4e4d 100644
--- a/hdl/top/sf2-test/sf2_test.vhd
+++ b/hdl/top/sf2-test/sf2_test.vhd
@@ -63,7 +63,7 @@ architecture behav of sf2_test is
   signal ahb_state : ahb_state_t;
 
   --  IRAM log size in bytes.
-  constant IRAM_LOG_SIZE : natural := 8;
+  constant IRAM_LOG_SIZE : natural := 9;
 
   signal iram_addr : std_logic_vector(IRAM_LOG_SIZE - 1 downto 2);
   signal iram_we : std_logic;
diff --git a/sw/sf2-test/main.c b/sw/sf2-test/main.c
index f6f8af7069b9b5db792cdcb1dde329010b1cc17b..131dbf34163a8ada40d55b95e549d886bbf7f503 100644
--- a/sw/sf2-test/main.c
+++ b/sw/sf2-test/main.c
@@ -32,6 +32,35 @@ uart_puts (const char *s)
     uart_putc (*s++);
 }
 
+#define PAD_LEN 8
+static volatile unsigned pad[8];
+
+int
+ram_test(void)
+{
+  int i;
+  volatile unsigned char *p;
+
+  for (i = 0; i < PAD_LEN; i++) {
+    if (pad[i] != 0)
+      return -1;
+    pad[i] = 0xffffffff;
+  }
+  for (i = 0; i < PAD_LEN; i++) {
+    p = (volatile unsigned char *)&pad[i];
+    p[i & 3] = i;
+  }
+  for (i = 0; i < PAD_LEN; i++) {
+    p = (volatile unsigned char *)&pad[i];
+    unsigned v = 0xffffffff;
+    v &= ~(0xff << (8 * (i & 3)));
+    v |= i << (8 * (i & 3));
+    if (pad[i] != v)
+      return -2;
+  }
+  return 0;
+}
+
 int
 main (void)
 {
@@ -48,7 +77,14 @@ main (void)
   /* start operation.  */
   *(volatile unsigned *)UART_LCR = 0x03;
 
-  while (1) {
-    uart_puts ("Hello diot.\n");
+  uart_puts ("Ram\n");
+  if (ram_test() != 0) {
+    while (1)
+      uart_puts("Error\n");
+  }
+  else {
+    while (1) {
+      uart_puts ("Hello diot.\n");
+    }
   }
 }