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