From 17aef61561e9075f9c8d3eddb0d8e4c4da861835 Mon Sep 17 00:00:00 2001
From: Tristan Gingold <tristan.gingold@cern.ch>
Date: Mon, 13 Dec 2021 15:17:55 +0100
Subject: [PATCH] Add hydra_ram.vhd

---
 hdl/rtl/hydra_core.vhd        |  93 +++++-------
 hdl/rtl/hydra_ram.vhd         | 257 ++++++++++++++++++++++++++++++++++
 hdl/tb/sf2-test/uart.vhd      |   2 +-
 hdl/top/sf2-test/sf2_test.vhd |   4 +-
 4 files changed, 293 insertions(+), 63 deletions(-)
 create mode 100644 hdl/rtl/hydra_ram.vhd

diff --git a/hdl/rtl/hydra_core.vhd b/hdl/rtl/hydra_core.vhd
index 427caab..141a4f8 100644
--- a/hdl/rtl/hydra_core.vhd
+++ b/hdl/rtl/hydra_core.vhd
@@ -43,7 +43,8 @@ entity hydra_core is
     dwb_o     : out t_wishbone_master_out;
     dwb_i     : in  t_wishbone_master_in;
 
-    iram_addr : in  std_logic_vector(g_IRAM_LOG_SIZE + 1 downto 2);
+    --  IRAM write access (during reset)
+    iram_addr : in  std_logic_vector(g_IRAM_LOG_SIZE - 1 downto 2);
     iram_we   : in  std_logic;
     iram_data : in  std_logic_vector(31 downto 0)
   );
@@ -79,11 +80,11 @@ architecture arch of hydra_core is
   end function f_x_to_zero;
 
   signal cpu_rst        : std_logic;
-  signal cpu_rst_d      : std_logic;
 
   signal im_addr  : std_logic_vector(31 downto 0);
-  signal im_data  : std_logic_vector(31 downto 0);
-  signal im_valid : std_logic;
+  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 dm_addr, dm_data_s, dm_data_l                            : std_logic_vector(31 downto 0);
   signal dm_data_select                                           : std_logic_vector(3 downto 0);
@@ -116,6 +117,7 @@ begin
       rst_i            => cpu_rst,
       irq_i            => '0',
       im_addr_o        => im_addr,
+      im_rd_o          => im_rd,
       im_data_i        => im_data,
       im_valid_i       => im_valid,
       dm_addr_o        => dm_addr,
@@ -153,63 +155,34 @@ begin
     end if;
   end process;
 
-  p_rom: process (clk_sys_i)
-  is
-    constant IRAM_WSIZE : natural := 2 ** (g_IRAM_LOG_SIZE - 2);
-    type t_ram39_type is array(natural range <>) of std_logic_vector(38 downto 0);
-    variable iram : t_ram39_type(0 to IRAM_WSIZE - 1);
---     := f_load_mem32_from_file ("../../../sw/fip_urv/fip_dbg.ram", IRAM_WSIZE, True);
-    variable addr : std_logic_vector (g_IRAM_LOG_SIZE + 1 downto 2);
-    variable data, wdata : std_logic_vector(38 downto 0);
-    variable syndrome : std_logic_vector(6 downto 0);
-    type state_t is (S_READ, S_REWRITE, S_WRITE);
-    variable state : state_t;
-  begin
-    if rising_edge(clk_sys_i) then
-      if cpu_rst = '1' then
-        if iram_we = '1' then
-          iram (to_integer(unsigned(iram_addr))) := f_calc_ecc (iram_data) & iram_data;
-        end if;
-        im_valid <= '0';
-        cpu_rst_d <= '1';
-        state := S_WRITE;
-      else
-        cpu_rst_d <= cpu_rst;
-      end if;
+  im1_dm_en <= '1' when reg_dm_load = '1' and reg_dm_is_wishbone = '0' and reg_dm_addr (16) = '0' else '0';
 
-      case state is
-        when S_READ =>
-          if reg_dm_load = '1' and reg_dm_is_wishbone = '0' and reg_dm_addr (16) = '0' then
-            --  Data read from iram
-            addr := reg_dm_addr(g_IRAM_LOG_SIZE + 1 downto 2);
-            im_valid <= '0';
-          else
-            --  Data read from dram.
-            addr := im_addr(g_IRAM_LOG_SIZE + 1 downto 2);
-            im_valid  <= (not cpu_rst_d);
-          end if;
-          data := iram (to_integer(unsigned(addr)));
-          syndrome := data(38 downto 32) xor f_calc_ecc(data(31 downto 0));
-          im_data <= data(31 downto 0);
-          if f_ecc_errors(syndrome) = '1' then
-            im_valid <= '0';
-            --  id_valid <= '0';
-            state := S_REWRITE;
-          end if;
-        when S_WRITE | S_REWRITE =>
-          if cpu_rst = '1' and iram_we = '1' then
-            addr := iram_addr;
-            wdata := f_calc_ecc (iram_data) & iram_data;
-          else
-            wdata := f_fix_error (syndrome, data(38 downto 32), data(31 downto 0));
-          end if;
-          iram (to_integer(unsigned(iram_addr))) := wdata;
-          if cpu_rst = '0' then
-            state := S_READ;
-          end if;
-      end case;
-    end if;
-  end process;
+  inst_rom: entity work.hydra_ram
+    generic map (
+      g_RAM_LOG_SIZE => g_IRAM_LOG_SIZE
+    )
+    port map (
+      clk_i => clk_sys_i,
+      rst_n_i => rst_n_i,
+
+      r1_addr_i => reg_dm_addr(g_IRAM_LOG_SIZE - 1 downto 2),
+      r1_en_i   => im1_dm_en,
+      r1_data_o => im1_data,
+      r1_done_o => im1_done,
+      r1_err_o  => im1_err,
+
+      r2_addr_i => im_addr(g_IRAM_LOG_SIZE - 1 downto 2),
+      r2_en_i   => im_rd, --rst_n_i,
+      r2_data_o => im_data,
+      r2_done_o => im_valid,
+
+      waddr_i => iram_addr,
+      we_i => iram_we,
+      wdata_i => iram_data,
+      wforce_i => (others => '0'),
+
+      scrubber_period_i => x"0010"
+    );
 
   -- 1st MByte of the mem is the RAM
   -- 1st 64KB is the IRAM.
diff --git a/hdl/rtl/hydra_ram.vhd b/hdl/rtl/hydra_ram.vhd
new file mode 100644
index 0000000..7e41dca
--- /dev/null
+++ b/hdl/rtl/hydra_ram.vhd
@@ -0,0 +1,257 @@
+--------------------------------------------------------------------------------
+-- CERN BE-CO-HT
+-- Mock Turtle
+-- https://gitlab.cern.ch/coht/mockturtle
+--------------------------------------------------------------------------------
+--
+-- unit name:   hydra_ram
+--
+-- 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_ram 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;
+
+    --  Port 1: read access (high priority)
+    --  RDONE_O is a pulse.
+    r1_addr_i      : in  std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+    r1_en_i        : in  std_logic;
+    r1_data_o      : out std_logic_vector(31 downto 0);
+    r1_done_o      : out std_logic;
+    r1_err_o       : out std_logic;
+
+    --  Port 2: read access (low priority)
+    --  RDONE_O is a pulse.
+    r2_addr_i      : in  std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+    r2_en_i        : in  std_logic;
+    r2_data_o      : out std_logic_vector(31 downto 0);
+    r2_done_o      : out std_logic;
+    r2_err_o       : out std_logic;
+
+    --  Write access
+    --  WFORCE_I are raw bits modified after ECC.  Can be used to force an ECC error.
+    waddr_i      : in  std_logic_vector(g_RAM_LOG_SIZE - 1 downto 2);
+    we_i         : in  std_logic;
+    wforce_i     : in  std_logic_vector(38 downto 0);
+    wdata_i      : in  std_logic_vector(31 downto 0);
+    wdone_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_ram;
+
+architecture arch of hydra_ram 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 raddr, waddr : 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 r1_done, n_r1_done : std_logic;
+  signal r2_done, n_r2_done : std_logic;
+  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 scrub_counter : unsigned(15 downto 0);
+  signal scrub_rd, scrub_done, 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);
+  begin
+    if rising_edge(clk_i) then
+      if wen = '1' then
+        iram (to_integer(unsigned(waddr))) := wdata_ecc;
+      end if;
+      if ren = '1' then
+        rdata_ecc <= iram (to_integer(unsigned(raddr)));
+      else
+        rdata_ecc <= (others => 'X');
+      end if;
+      ren_d <= ren;
+    end if;
+  end process;
+
+  r1_data_o <= rdata_ecc (31 downto 0);
+  r2_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;
+
+  r1_err_o <= rerr;
+  r1_done_o <= r1_done;
+  r2_err_o <= rerr;
+  r2_done_o <= r2_done;
+  wdone_o <= wdone;
+
+  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' 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, r1_done, r2_done, rerr, rdata_ecc, rsyndrome, last_raddr,
+                   r1_en_i, r1_addr_i, we_i, waddr_i, wdata_i, wforce_i,
+                   r2_en_i, r2_addr_i, scrub_rd, scrub_addr, scrub_done)
+  begin
+    wen <= '0';
+    waddr <= (others => 'X');
+    wdata_ecc <= (others => 'X');
+    n_wdone <= '0';
+    ren <= '0';
+    raddr <= (others => 'X');
+    n_r1_done <= '0';
+    n_r2_done <= '0';
+    n_scrub_done <= '0';
+
+    n_ecc_one <= '0';
+    n_ecc_fatal <= '0';
+    n_state <= state;
+    n_last_raddr <= (others => 'X');
+
+    case state is
+      when S_READ =>
+        if (r2_done = '1' or r1_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;
+          waddr <= last_raddr;
+          wen <= '1';
+          n_last_raddr <= last_raddr;
+          n_state <= S_REWRITE;
+        elsif we_i = '1' then
+          --  Write
+          waddr <= waddr_i;
+          wen <= '1';
+          wdata_ecc <= (f_calc_ecc (wdata_i) & wdata_i) xor wforce_i;
+          n_wdone <= '1';
+          n_state <= S_READ;
+        elsif scrub_rd = '1' and scrub_done = '0' then
+          -- scrubber
+          raddr <= scrub_addr;
+          ren <= '1';
+          n_scrub_done <= '1';
+          n_last_raddr <= scrub_addr;
+        elsif r1_en_i = '1' then
+          --  Read.
+          raddr <= r1_addr_i;
+          ren <= '1';
+          n_r1_done <= '1';
+          n_last_raddr <= r1_addr_i;
+        elsif r2_en_i = '1' then
+          --  Read.
+          raddr <= r2_addr_i;
+          ren <= '1';
+          n_r2_done <= '1';
+          n_last_raddr <= r2_addr_i;
+        else
+          -- scrub if idle.
+          raddr <= scrub_addr;
+          ren <= '1';
+          n_scrub_done <= '1';
+          n_last_raddr <= scrub_addr;
+        end if;
+      when S_REWRITE =>
+        --  Reread
+        raddr <= last_raddr;
+        ren <= '1';
+        n_r1_done <= '1';
+        n_last_raddr <= last_raddr;
+    end case;
+  end process;
+
+  p_ctrl_reg: process (clk_i)
+  begin
+    if rising_edge(clk_i) then
+      if rst_n_i = '0' then
+        r1_done <= '0';
+        r2_done <= '0';
+        scrub_done <= '0';
+        state <= S_READ;
+        last_raddr <= (others => 'X');
+
+        ecc_one_o <= '0';
+        ecc_fatal_o <= '0';
+      else
+        r1_done <= n_r1_done;
+        r2_done <= n_r2_done;
+        scrub_done <= n_scrub_done;
+        assert ((r2_done or scrub_done) and rerr) = '0' severity error; --  TODO
+
+        state <= n_state;
+        last_raddr <= n_last_raddr;
+
+        ecc_one_o <= n_ecc_one;
+        ecc_fatal_o <= n_ecc_fatal;
+      end if;
+
+      wdone <= n_wdone;
+    end if;
+  end process;
+end arch;
\ No newline at end of file
diff --git a/hdl/tb/sf2-test/uart.vhd b/hdl/tb/sf2-test/uart.vhd
index 0d2908a..17d5228 100644
--- a/hdl/tb/sf2-test/uart.vhd
+++ b/hdl/tb/sf2-test/uart.vhd
@@ -91,7 +91,7 @@ begin
     lock <= '1';
     
     process
-      variable mem: mem_t (4095 downto 0);
+      variable mem: mem_t (4095 downto 0) := (others => (others => '0'));
     begin
       read_mem (mem, "../../../sw/sf2-test/main.mem");
 
diff --git a/hdl/top/sf2-test/sf2_test.vhd b/hdl/top/sf2-test/sf2_test.vhd
index 0791521..6107d77 100644
--- a/hdl/top/sf2-test/sf2_test.vhd
+++ b/hdl/top/sf2-test/sf2_test.vhd
@@ -65,7 +65,7 @@ architecture behav of sf2_test is
   --  IRAM log size in bytes.
   constant IRAM_LOG_SIZE : natural := 8;
 
-  signal iram_addr : std_logic_vector(IRAM_LOG_SIZE - 1 downto 0);
+  signal iram_addr : std_logic_vector(IRAM_LOG_SIZE - 1 downto 2);
   signal iram_we : std_logic;
   signal iram_data : std_logic_vector(31 downto 0);
 
@@ -133,7 +133,7 @@ begin
     end if;
   end process;
 
-  iram_addr <= iahb_addr (IRAM_LOG_SIZE + 2 - 1 downto 2);
+  iram_addr <= iahb_addr (IRAM_LOG_SIZE - 1 downto 2);
   iram_data <= ahb_rdata;
 
   proc_init: process (clk_100)
-- 
GitLab