diff --git a/hdl/rtl/hydra_core.vhd b/hdl/rtl/hydra_core.vhd index b67d2c5c32253fdc7ece0cbb2337b72f012b0484..9404b9effd1702a615a8bae450b176d6d99bab0d 100644 --- a/hdl/rtl/hydra_core.vhd +++ b/hdl/rtl/hydra_core.vhd @@ -83,15 +83,16 @@ architecture arch of hydra_core is return x; end function f_x_to_zero; - signal cpu_rst : std_logic; + signal cpu_rst : std_logic_vector(1 to 3); signal cpu_rst_err : std_logic; + signal cpu_sync : std_logic_vector(1 to 3); signal im_addr : std_logic_vector(31 downto 0); 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 im2_err, im2_valid : std_logic; - signal err_cpu : std_logic; + signal err_cpu_dm : 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); @@ -121,6 +122,8 @@ architecture arch of hydra_core is signal nbr_dram_ecc_uncorr : std_logic_vector (31 downto 0); signal iram_ecc_err, iram_ecc_fatal : std_logic; signal dram_ecc_err, dram_ecc_fatal : std_logic; + signal nbr_cpu_data_err : std_logic_vector (31 downto 0); + signal nbr_cpu_iaddr_err : std_logic_vector (31 downto 0); signal reset_cause_cpu, reset_cause_ecc, reset_cause_wd : std_logic; signal dram_scrub_period, iram_scrub_period : std_logic_vector(15 downto 0); @@ -129,15 +132,19 @@ architecture arch of hydra_core is signal wd_period_wr, wd_key_wr, wd_timeout : std_logic; signal force_divergence, force_divergence_d, dm_force_divergence : std_logic; -begin - cpu_rst <= cpu_rst_err or (not cpu_rst_n_i); + signal cpu_wr : std_logic; + + type t_state is (S_VOTER, S_LOCK); + signal state : t_state; + signal cpu_recovery, cpu_recovery_in : std_logic; +begin dwb_o <= dwb_out; - U_cpu_core : entity work.hydra_triple_cpu + inst_cpus : entity work.hydra_triple_cpu port map ( clk_i => clk_sys_i, - rst_i => cpu_rst, + cpu_rst_i => cpu_rst, im_addr_o => im_addr, im_rd_o => im_rd, im_data_i => im_data, @@ -151,8 +158,9 @@ begin dm_load_done_i => dm_load_done, dm_store_done_i => dm_store_done, + cpu_sync_o => cpu_sync, dm_force_divergence_i => dm_force_divergence, - err_cpu_o => err_cpu); + err_cpu_dm_o => err_cpu_dm); -- Add registers on uRV data bus process (clk_sys_i) @@ -170,7 +178,6 @@ begin end if; end process; - inst_iram: entity work.hydra_iram generic map ( g_RAM_LOG_SIZE => g_IRAM_LOG_SIZE, @@ -416,7 +423,7 @@ begin -- Timeout wd_timeout <= '1'; wd_counter <= wd_period; - elsif cpu_rst = '1' or (wd_key_wr = '1' and wd_key_val = x"c0423bc9") then + elsif cpu_rst = "111" or (wd_key_wr = '1' and wd_key_val = x"c0423bc9") then -- Key -> reload -- Also restart the watchdog if the cpu is reset. wd_counter <= wd_period; @@ -429,27 +436,8 @@ begin end if; end process; - -- Supervisor - p_sv: process (clk_sys_i) - begin - if rising_edge(clk_sys_i) then - if rst_n_i = '0' then - reset_cause_cpu <= '0'; - reset_cause_ecc <= '0'; - reset_cause_wd <= '0'; - cpu_rst_err <= '0'; - else - cpu_rst_err <= '0'; - if dram_ecc_fatal = '1' or iram_ecc_fatal = '1' or wd_timeout = '1' or err_cpu = '1' then - -- Reset CPU on error. - cpu_rst_err <= '1'; - reset_cause_cpu <= err_cpu; - reset_cause_ecc <= dram_ecc_fatal or iram_ecc_fatal; - reset_cause_wd <= wd_timeout; - end if; - end if; - end if; - end process; + -- Any fatal error that should reset cpus + cpu_rst_err <= dram_ecc_fatal or iram_ecc_fatal or wd_timeout; inst_sv_regs: entity work.hydra_supervisor_regs port map ( @@ -460,9 +448,11 @@ begin reset_cause_cpu_i => reset_cause_cpu, reset_cause_ecc_i => reset_cause_ecc, reset_cause_watchdog_i => reset_cause_wd, - cpu_status_i => "000", + cpu_status_i => cpu_rst, cpu_boot_done_i => '0', - cpu_recovery_i => '0', + cpu_recovery_i => cpu_recovery, + cpu_recovery_o => cpu_recovery_in, + cpu_wr_o => cpu_wr, force_divergence_i => x"00000000", force_divergence_rd_o => force_divergence, wd_period_i => wd_period, @@ -478,4 +468,51 @@ begin iram_scrub_period_o => iram_scrub_period, dram_scrub_period_o => dram_scrub_period ); + + process (clk_sys_i) is + begin + if rising_edge(clk_sys_i) then + if rst_n_i = '0' or cpu_rst_n_i = '0' then + state <= S_VOTER; + cpu_rst <= (others => '1'); + cpu_recovery <= '0'; + nbr_cpu_data_err <= (others => '0'); + nbr_cpu_iaddr_err <= (others => '0'); + reset_cause_cpu <= '0'; + reset_cause_ecc <= '0'; + reset_cause_wd <= '0'; + else + case state is + when S_VOTER => + cpu_rst <= "000"; + -- Software can clear recovery flag. + if cpu_wr = '1' and cpu_recovery_in = '0' then + cpu_recovery <= '0'; + end if; + if err_cpu_dm = '1' then + nbr_cpu_data_err <= std_logic_vector(unsigned(nbr_cpu_data_err) + 1); + end if; + if cpu_sync = "110" or cpu_sync = "101" or cpu_sync = "011" then + -- Disable the cpu out of sync. + cpu_rst <= not cpu_sync; + nbr_cpu_iaddr_err <= std_logic_vector(unsigned(nbr_cpu_iaddr_err) + 1); + state <= S_LOCK; + end if; + when S_LOCK => + if cpu_wr = '1' and cpu_recovery_in = '1' then + cpu_recovery <= '1'; + end if; + end case; + + if cpu_sync = "000" or cpu_rst_err = '1' or (err_cpu_dm = '1' and state = S_LOCK) then + -- Fatal error + cpu_rst <= (others => '1'); + reset_cause_cpu <= not cpu_rst_err; + reset_cause_ecc <= dram_ecc_fatal or iram_ecc_fatal; + reset_cause_wd <= wd_timeout; + state <= S_VOTER; + end if; + end if; + end if; + end process; end arch; diff --git a/hdl/rtl/hydra_triple_cpu.vhd b/hdl/rtl/hydra_triple_cpu.vhd index 7d19b2fecafcb051b5adc406eb936e206cf0a32d..c0b819634b4ddab6348fa1ae6870a6a989e3c52c 100644 --- a/hdl/rtl/hydra_triple_cpu.vhd +++ b/hdl/rtl/hydra_triple_cpu.vhd @@ -6,7 +6,7 @@ use work.urv_pkg.all; entity hydra_triple_cpu is port ( clk_i : in std_logic; - rst_i : in std_logic; + cpu_rst_i : in std_logic_vector(3 downto 1); im_data_i : in std_logic_vector (31 downto 0); im_valid_i : in std_logic; im_addr_o : out std_logic_vector (31 downto 0); @@ -19,25 +19,27 @@ entity hydra_triple_cpu is dm_data_select_o : out std_logic_vector (3 downto 0); dm_store_o : out std_logic; dm_load_o : out std_logic; - + + -- True when a cpu is in sync with at least another one. + -- (ie same outputs on the instruction bus) + cpu_sync_o : out std_logic_vector(3 downto 1); + -- If set, will give different values to each cpu. dm_force_divergence_i : in std_logic; - -- Set to 1 if an output differs - err_cpu_o : out std_logic); + -- Set to 1 if an output differs on the data bus + err_cpu_dm_o : out std_logic); end entity hydra_triple_cpu; architecture arch of hydra_triple_cpu is type t_slv32_x3 is array(1 to 3) of std_logic_vector(31 downto 0); type t_slv4_x3 is array(1 to 3) of std_logic_vector(3 downto 0); - subtype t_sl_x3 is std_logic_vector(1 to 3); + subtype t_sl_x3 is std_logic_vector(3 downto 1); signal im_addr, dm_addr, dm_data_s, dm_data_l : t_slv32_x3; - signal err_im_addr, err_dm_addr, err_dm_data_s : std_logic; signal dm_data_select : t_slv4_x3; - signal err_dm_data_select : std_logic; signal im_rd, dm_store, dm_load : t_sl_x3; - signal err_dm_store, err_dm_load, err_im_rd : std_logic; - signal en_im, en_dm_load, en_dm_store : std_logic; + + signal ok_dm_addr, ok_dm_data, ok_dm_select, ok_dm_load, ok_dm_store, ok_dm : std_logic_vector(3 downto 1); begin gen_triple: for i in 1 to 3 generate inst_cpu : urv_cpu @@ -49,7 +51,7 @@ begin ) port map ( clk_i => clk_i, - rst_i => rst_i, + rst_i => cpu_rst_i(i), irq_i => '0', im_addr_o => im_addr(i), im_rd_o => im_rd(i), @@ -82,7 +84,7 @@ begin b => im_addr(2), c => im_addr(3), res => im_addr_o, - err => err_im_addr + err => open ); inst_voter_dm_addr: entity work.voter_vec_status @@ -94,7 +96,7 @@ begin b => dm_addr(2), c => dm_addr(3), res => dm_addr_o, - err => err_dm_addr + err => open ); inst_voter_dm_data_s: entity work.voter_vec_status @@ -106,7 +108,7 @@ begin b => dm_data_s(2), c => dm_data_s(3), res => dm_data_s_o, - err => err_dm_data_s + err => open ); inst_voter_dm_data_select: entity work.voter_vec_status @@ -118,38 +120,71 @@ begin b => dm_data_select(2), c => dm_data_select(3), res => dm_data_select_o, - err => err_dm_data_select + err => open ); inst_voter_im_rd: entity work.voter_status port map ( inp => im_rd, res => im_rd_o, - err => err_im_rd + err => open ); inst_voter_dm_load: entity work.voter_status port map ( inp => dm_load, res => dm_load_o, - err => err_dm_load + err => open ); inst_voter_dm_store: entity work.voter_status port map ( inp => dm_store, res => dm_store_o, - err => err_dm_store + err => open ); dm_data_l(1) <= dm_data_l_i when dm_force_divergence_i = '0' else x"0000_0001"; dm_data_l(2) <= dm_data_l_i when dm_force_divergence_i = '0' else x"0000_0002"; dm_data_l(3) <= dm_data_l_i when dm_force_divergence_i = '0' else x"0000_0004"; - en_im <= im_rd(1) or im_rd(2) or im_rd(3); - en_dm_load <= dm_load(1) or dm_load(2) or dm_load(3); - en_dm_store <= dm_store(1) or dm_store(2) or dm_store(3); - err_cpu_o <= (en_im and err_im_addr) or err_im_rd - or ((en_dm_load or en_dm_store) and err_dm_addr) - or (en_dm_store and (err_dm_data_s or err_dm_data_select)) or err_dm_load or err_dm_store; + gen_s: for i in 0 to 2 generate + constant n0 : natural := 1 + i; + constant n1 : natural := 1 + (i + 1) mod 3; + constant n2 : natural := 1 + (i + 2) mod 3; + begin + cpu_sync_o(n0) <= '1' when cpu_rst_i(n0) = '1' + or (im_rd(n0) = '0' and (im_rd(n1) = '0' or im_rd(n2) = '0')) + or (im_rd(n0) = '1' and ((im_rd(n1) = '1' and im_addr(n1) = im_addr(n0)) + or (im_rd(n2) = '1' and im_addr(n2) = im_addr(n0)))) else '0'; + ok_dm_addr (n0) <= '1' when cpu_rst_i(n0) = '1' + or (dm_load(n0) = '0' and (dm_load(n1) = '0' or dm_load(n2) = '0')) + or (dm_store(n0) = '0' and (dm_store(n1) = '0' or dm_store(n2) = '0')) + or (((dm_load(n0) = '1' and dm_load(n1) = '1') or (dm_store(n0) = '1' and dm_store(n1) = '1')) + and dm_addr(n0) = dm_addr(n1)) + or (((dm_load(n0) = '1' and dm_load(n2) = '1') or (dm_store(n0) = '1' and dm_store(n2) = '1')) + and dm_addr(n0) = dm_addr(n2)) + else '0'; + ok_dm_data (n0) <= '1' when cpu_rst_i(n0) = '1' + or (dm_store(n0) = '0' and (dm_store(n1) = '0' or dm_store(n2) = '0')) + or ((dm_store(n0) = '1' and dm_store(n1) = '1') and dm_data_s(n0) = dm_data_s(n1)) + or ((dm_store(n0) = '1' and dm_store(n2) = '1') and dm_data_s(n0) = dm_data_s(n2)) + else '0'; + ok_dm_select (n0) <= '1' when cpu_rst_i(n0) = '1' + or (dm_store(n0) = '0' and (dm_store(n1) = '0' or dm_store(n2) = '0')) + or ((dm_store(n0) = '1' and dm_store(n1) = '1') and dm_data_select(n0) = dm_data_select(n1)) + or ((dm_store(n0) = '1' and dm_store(n2) = '1') and dm_data_select(n0) = dm_data_select(n2)) + else '0'; + ok_dm_store (n0) <= '1' when cpu_rst_i(n0) = '1' + or (dm_store(n0) = dm_store(n1)) + or (dm_store(n0) = dm_store(n2)) else '0'; + ok_dm_load (n0) <= '1' when cpu_rst_i(n0) = '1' + or (dm_load(n0) = dm_load(n1)) + or (dm_load(n0) = dm_load(n2)) else '0'; + ok_dm (n0) <= ok_dm_addr(n0) and ok_dm_data(n0) and ok_dm_select(n0) and ok_dm_store(n0) and ok_dm_load(n0); + + end generate; + + err_cpu_dm_o <= '1' when ok_dm /= "111" else '0'; + end arch; diff --git a/sw/sf2-test/main.c b/sw/sf2-test/main.c index 6e09eb32093b227d424b3edf30aeb22f11c9bbaf..693e6872b75aaa714e4a184a7f9fa4add33729cf 100644 --- a/sw/sf2-test/main.c +++ b/sw/sf2-test/main.c @@ -14,6 +14,29 @@ #define SUPERVISOR ((volatile struct hydra_supervisor_regs *)SUPERVISOR_BASE) #define WD_KEY 0xc0423bc9 +static enum t_test + { + TEST_WD, + TEST_CPU0_ERR +} cur_test; + +static void +uart_init (void) +{ + /* Unreset UART 0 */ + *(volatile unsigned *)SYS_RESET &= 0xffffff7f; + + /* Select baud. */ + *(volatile unsigned *)UART_LCR = 0x80; + + /* Set baudrate */ + *(volatile unsigned *)UART_DLR = 0x36; + *(volatile unsigned *)UART_DMR = 0x00; + + /* start operation. */ + *(volatile unsigned *)UART_LCR = 0x03; +} + static void uart_raw_putc (unsigned char c) { @@ -88,24 +111,16 @@ ram_test(void) int main (void) { - /* Unreset UART 0 */ - *(volatile unsigned *)SYS_RESET &= 0xffffff7f; - - /* Select baud. */ - *(volatile unsigned *)UART_LCR = 0x80; - - /* Set baudrate */ - *(volatile unsigned *)UART_DLR = 0x36; - *(volatile unsigned *)UART_DMR = 0x00; - - /* start operation. */ - *(volatile unsigned *)UART_LCR = 0x03; + uart_init (); uart_puts ("Rst: "); unsigned v = SUPERVISOR->reset_cause; uart_put_hex_digit (v & 0x0f); uart_putc('\n'); + /* On the first reset, v=0 (no cause), so watchdog won't be restarted + and will expire during ram test. + On later reset, there is always a reset cause. */ if (v) SUPERVISOR->wd_key = WD_KEY; @@ -115,7 +130,22 @@ main (void) uart_puts("Error\n"); } - /* Read a different value. */ + /* Read a different value. + This will trigger a vote and a data error increment. */ + v = SUPERVISOR->force_divergence; + uart_put_hex_digit(v & 0x0f); + + /* Force divergence by executing different code. */ + v = SUPERVISOR->force_divergence; + if (v != 1) + uart_putc('D'); + + /* Must be in lock-step. */ + v = SUPERVISOR->cpu; + uart_put_hex_digit(v & 0x0f); + + /* Force a data error in lock-step. + This will generate a reset. */ v = SUPERVISOR->force_divergence; uart_put_hex_digit(v & 0x0f);