From 0372a11b58d01c4de5a0954184fb3311826cc5f7 Mon Sep 17 00:00:00 2001 From: Tristan Gingold <tristan.gingold@cern.ch> Date: Thu, 16 Dec 2021 10:21:30 +0100 Subject: [PATCH] hydra: add ecc on dram, add a small memory test --- hdl/rtl/hydra_core.vhd | 119 ++++++++------ hdl/rtl/hydra_dram.vhd | 291 ++++++++++++++++++++++++++++++++++ hdl/top/sf2-test/sf2_test.vhd | 2 +- sw/sf2-test/main.c | 40 ++++- 4 files changed, 399 insertions(+), 53 deletions(-) create mode 100644 hdl/rtl/hydra_dram.vhd diff --git a/hdl/rtl/hydra_core.vhd b/hdl/rtl/hydra_core.vhd index f5d6972..854da39 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 0000000..f1a8000 --- /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 f580898..a866d36 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 f6f8af7..131dbf3 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"); + } } } -- GitLab