Skip to content
Snippets Groups Projects
Commit 0372a11b authored by Tristan Gingold's avatar Tristan Gingold
Browse files

hydra: add ecc on dram, add a small memory test

parent b7b8eb3a
Branches
No related merge requests found
......@@ -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;
......
--------------------------------------------------------------------------------
-- 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
......@@ -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;
......
......@@ -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");
}
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment