From 92d94d5bd10136d5a53a8eea838131622ca285ab Mon Sep 17 00:00:00 2001 From: "sem23h18 Nicola Maronese (nmaronese)" Date: Mon, 9 Oct 2023 16:37:49 +0200 Subject: [PATCH 01/46] created testbench configuration --- hardware/deps/snitch/tb/scripts/compile.tcl | 247 +++++++ hardware/deps/snitch/tb/scripts/gen_script.sh | 5 + hardware/deps/snitch/tb/scripts/sim_setup.tcl | 10 + .../tb/src/snitch_read_only_cache_tb.sv | 699 ++++++++++++++++++ 4 files changed, 961 insertions(+) create mode 100644 hardware/deps/snitch/tb/scripts/compile.tcl create mode 100755 hardware/deps/snitch/tb/scripts/gen_script.sh create mode 100644 hardware/deps/snitch/tb/scripts/sim_setup.tcl create mode 100644 hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv diff --git a/hardware/deps/snitch/tb/scripts/compile.tcl b/hardware/deps/snitch/tb/scripts/compile.tcl new file mode 100644 index 000000000..cd4570c4e --- /dev/null +++ b/hardware/deps/snitch/tb/scripts/compile.tcl @@ -0,0 +1,247 @@ +# This script was generated automatically by bender. +set ROOT "/scratch/sem23h18/project/mempool" + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "$ROOT/hardware/deps/common_verification/src/clk_rst_gen.sv" \ + "$ROOT/hardware/deps/common_verification/src/rand_id_queue.sv" \ + "$ROOT/hardware/deps/common_verification/src/rand_stream_mst.sv" \ + "$ROOT/hardware/deps/common_verification/src/rand_synch_holdable_driver.sv" \ + "$ROOT/hardware/deps/common_verification/src/rand_verif_pkg.sv" \ + "$ROOT/hardware/deps/common_verification/src/sim_timeout.sv" \ + "$ROOT/hardware/deps/common_verification/src/rand_synch_driver.sv" \ + "$ROOT/hardware/deps/common_verification/src/rand_stream_slv.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/cluster_clk_cells.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/pulp_clk_cells.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "$ROOT/hardware/deps/tech_cells_generic/src/rtl/tc_clk.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/cluster_pwr_cells.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/generic_memory.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/generic_rom.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/pad_functional.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/pulp_buffer.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/pulp_pwr_cells.sv" \ + "$ROOT/hardware/deps/tech_cells_generic/src/tc_pwr.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "$ROOT/hardware/deps/tech_cells_generic/src/deprecated/pulp_clock_gating_async.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/common_cells/src/binary_to_gray.sv" \ + "$ROOT/hardware/deps/common_cells/src/cb_filter_pkg.sv" \ + "$ROOT/hardware/deps/common_cells/src/cc_onehot.sv" \ + "$ROOT/hardware/deps/common_cells/src/cdc_2phase.sv" \ + "$ROOT/hardware/deps/common_cells/src/cf_math_pkg.sv" \ + "$ROOT/hardware/deps/common_cells/src/clk_div.sv" \ + "$ROOT/hardware/deps/common_cells/src/delta_counter.sv" \ + "$ROOT/hardware/deps/common_cells/src/ecc_pkg.sv" \ + "$ROOT/hardware/deps/common_cells/src/edge_propagator_tx.sv" \ + "$ROOT/hardware/deps/common_cells/src/exp_backoff.sv" \ + "$ROOT/hardware/deps/common_cells/src/fifo_v3.sv" \ + "$ROOT/hardware/deps/common_cells/src/gray_to_binary.sv" \ + "$ROOT/hardware/deps/common_cells/src/isochronous_4phase_handshake.sv" \ + "$ROOT/hardware/deps/common_cells/src/isochronous_spill_register.sv" \ + "$ROOT/hardware/deps/common_cells/src/lfsr.sv" \ + "$ROOT/hardware/deps/common_cells/src/lfsr_16bit.sv" \ + "$ROOT/hardware/deps/common_cells/src/lfsr_8bit.sv" \ + "$ROOT/hardware/deps/common_cells/src/mv_filter.sv" \ + "$ROOT/hardware/deps/common_cells/src/onehot_to_bin.sv" \ + "$ROOT/hardware/deps/common_cells/src/plru_tree.sv" \ + "$ROOT/hardware/deps/common_cells/src/popcount.sv" \ + "$ROOT/hardware/deps/common_cells/src/rr_arb_tree.sv" \ + "$ROOT/hardware/deps/common_cells/src/rstgen_bypass.sv" \ + "$ROOT/hardware/deps/common_cells/src/serial_deglitch.sv" \ + "$ROOT/hardware/deps/common_cells/src/shift_reg.sv" \ + "$ROOT/hardware/deps/common_cells/src/spill_register_flushable.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_demux.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_filter.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_fork.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_intf.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_join.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_mux.sv" \ + "$ROOT/hardware/deps/common_cells/src/sub_per_hash.sv" \ + "$ROOT/hardware/deps/common_cells/src/sync.sv" \ + "$ROOT/hardware/deps/common_cells/src/sync_wedge.sv" \ + "$ROOT/hardware/deps/common_cells/src/unread.sv" \ + "$ROOT/hardware/deps/common_cells/src/addr_decode.sv" \ + "$ROOT/hardware/deps/common_cells/src/cb_filter.sv" \ + "$ROOT/hardware/deps/common_cells/src/cdc_fifo_2phase.sv" \ + "$ROOT/hardware/deps/common_cells/src/counter.sv" \ + "$ROOT/hardware/deps/common_cells/src/ecc_decode.sv" \ + "$ROOT/hardware/deps/common_cells/src/ecc_encode.sv" \ + "$ROOT/hardware/deps/common_cells/src/edge_detect.sv" \ + "$ROOT/hardware/deps/common_cells/src/lzc.sv" \ + "$ROOT/hardware/deps/common_cells/src/max_counter.sv" \ + "$ROOT/hardware/deps/common_cells/src/rstgen.sv" \ + "$ROOT/hardware/deps/common_cells/src/spill_register.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_delay.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_fifo.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_fork_dynamic.sv" \ + "$ROOT/hardware/deps/common_cells/src/cdc_fifo_gray.sv" \ + "$ROOT/hardware/deps/common_cells/src/fall_through_register.sv" \ + "$ROOT/hardware/deps/common_cells/src/id_queue.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_to_mem.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_arbiter_flushable.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_register.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_xbar.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_arbiter.sv" \ + "$ROOT/hardware/deps/common_cells/src/stream_omega_net.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/sram.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/clock_divider_counter.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/find_first_one.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/generic_LFSR_8bit.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/generic_fifo.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/prioarbiter.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/pulp_sync.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/pulp_sync_wedge.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/rrarbiter.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/clock_divider.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/fifo_v2.sv" \ + "$ROOT/hardware/deps/common_cells/src/deprecated/fifo_v1.sv" \ + "$ROOT/hardware/deps/common_cells/src/edge_propagator.sv" \ + "$ROOT/hardware/deps/common_cells/src/edge_propagator_rx.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/axi/include" \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/axi/src/axi_pkg.sv" \ + "$ROOT/hardware/deps/axi/src/axi_intf.sv" \ + "$ROOT/hardware/deps/axi/src/axi_atop_filter.sv" \ + "$ROOT/hardware/deps/axi/src/axi_burst_splitter.sv" \ + "$ROOT/hardware/deps/axi/src/axi_cdc_dst.sv" \ + "$ROOT/hardware/deps/axi/src/axi_cdc_src.sv" \ + "$ROOT/hardware/deps/axi/src/axi_cut.sv" \ + "$ROOT/hardware/deps/axi/src/axi_delayer.sv" \ + "$ROOT/hardware/deps/axi/src/axi_demux.sv" \ + "$ROOT/hardware/deps/axi/src/axi_dw_downsizer.sv" \ + "$ROOT/hardware/deps/axi/src/axi_dw_upsizer.sv" \ + "$ROOT/hardware/deps/axi/src/axi_id_remap.sv" \ + "$ROOT/hardware/deps/axi/src/axi_id_prepend.sv" \ + "$ROOT/hardware/deps/axi/src/axi_isolate.sv" \ + "$ROOT/hardware/deps/axi/src/axi_join.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_demux.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_join.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_mailbox.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_mux.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_regs.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_to_apb.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_to_axi.sv" \ + "$ROOT/hardware/deps/axi/src/axi_modify_address.sv" \ + "$ROOT/hardware/deps/axi/src/axi_mux.sv" \ + "$ROOT/hardware/deps/axi/src/axi_serializer.sv" \ + "$ROOT/hardware/deps/axi/src/axi_cdc.sv" \ + "$ROOT/hardware/deps/axi/src/axi_err_slv.sv" \ + "$ROOT/hardware/deps/axi/src/axi_dw_converter.sv" \ + "$ROOT/hardware/deps/axi/src/axi_id_serialize.sv" \ + "$ROOT/hardware/deps/axi/src/axi_multicut.sv" \ + "$ROOT/hardware/deps/axi/src/axi_to_axi_lite.sv" \ + "$ROOT/hardware/deps/axi/src/axi_iw_converter.sv" \ + "$ROOT/hardware/deps/axi/src/axi_lite_xbar.sv" \ + "$ROOT/hardware/deps/axi/src/axi_xbar.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/axi/include" \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/axi/src/axi_sim_mem.sv" \ + "$ROOT/hardware/deps/axi/src/axi_test.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+SNITCH_ENABLE_PERF=1 \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/axi/include" \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/snitch/src/riscv_instr.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_pkg.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_axi_pkg.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_regfile_ff.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_lsu.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_ipu.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_shared_muldiv.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_demux.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_axi_adapter.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_lfsr.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_parallel.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_icache/snitch_icache_refill.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_read_only_cache/snitch_axi_to_cache.sv" \ + "$ROOT/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv" +}]} {return 1} + +if {[catch {vlog -incr -sv \ + -svinputport=compat \ + +define+TARGET_ROCACHE_TEST \ + +define+TARGET_SIMULATION \ + +define+TARGET_VSIM \ + "+incdir+$ROOT/hardware/deps/axi/include" \ + "+incdir+$ROOT/hardware/deps/common_cells/include" \ + "$ROOT/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv" +}]} {return 1} diff --git a/hardware/deps/snitch/tb/scripts/gen_script.sh b/hardware/deps/snitch/tb/scripts/gen_script.sh new file mode 100755 index 000000000..4a9b75dd1 --- /dev/null +++ b/hardware/deps/snitch/tb/scripts/gen_script.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +#bender -d /scratch/sem23h18/project/mempool/hardware/deps/snitch script vsim -t rocache_test -p snitch --vlog-arg="-svinputport=compat" > compile.tcl + +bender -d /scratch/sem23h18/project/mempool script vsim -t rocache_test -p snitch --vlog-arg="-svinputport=compat" > compile.tcl diff --git a/hardware/deps/snitch/tb/scripts/sim_setup.tcl b/hardware/deps/snitch/tb/scripts/sim_setup.tcl new file mode 100644 index 000000000..ba8209539 --- /dev/null +++ b/hardware/deps/snitch/tb/scripts/sim_setup.tcl @@ -0,0 +1,10 @@ +do compile.tcl + +vopt snitch_read_only_cache_tb -o snitch_read_only_cache_tb_opt +acc + +vsim snitch_read_only_cache_tb_opt -voptargs=+acc + +# waves +add wave -noupdate -expand -group lookup /snitch_read_only_cache_tb/dut/i_lookup/* +add wave -noupdate -group handler /snitch_read_only_cache_tb/dut/i_handler/* +add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* \ No newline at end of file diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv new file mode 100644 index 000000000..de8af09e9 --- /dev/null +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -0,0 +1,699 @@ +// Copyright 2021 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Author: Florian Zaruba +// Samuel Riedel + +class icache_request #( + parameter int unsigned AddrWidth = 48 +); + rand logic [AddrWidth-1:0] addr; + rand bit flush; + + constraint flush_c { + flush dist { 1 := 2, 0 := 200}; + } + + constraint addr_c { + addr[1:0] == 0; + } +endclass + +class riscv_inst; + rand logic [31:0] inst; + rand bit ctrl_flow; + constraint inst_c { + ctrl_flow dist { 1 := 3, 0 := 10}; + inst[1:0] == 2'b11; + if (ctrl_flow) { + inst[6:0] inside { + riscv_instr::BEQ[6:0], + riscv_instr::JAL[6:0] + }; + // we don't support compressed instructions, make sure + // that we only emit aligned jump targets. + if (inst[6:0] == riscv_instr::BEQ[6:0]) { + inst[8] == 0; + } + if (inst[6:0] == riscv_instr::JAL[6:0]) { + inst[21] == 0; + } + // make sure that we don't emit control flow instructions + } else { + !(inst[6:0] inside { + riscv_instr::BEQ[6:0], + riscv_instr::JAL[6:0] + }); + } + } +endclass + +// Inherit from the random AXI master, but modify the request to emulate a core requesting intstructions. +class semirand_axi_master #( + // AXI interface parameters + parameter int AW = 32, + parameter int DW = 32, + parameter int IW = 8, + parameter int UW = 1, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps, + // Maximum number of read and write transactions in flight + parameter int MAX_READ_TXNS = 1, + parameter int MAX_WRITE_TXNS = 1, + // Upper and lower bounds on wait cycles on Ax, W, and resp (R and B) channels + parameter int AX_MIN_WAIT_CYCLES = 0, + parameter int AX_MAX_WAIT_CYCLES = 100, + parameter int W_MIN_WAIT_CYCLES = 0, + parameter int W_MAX_WAIT_CYCLES = 5, + parameter int RESP_MIN_WAIT_CYCLES = 0, + parameter int RESP_MAX_WAIT_CYCLES = 20, + // AXI feature usage + parameter int AXI_MAX_BURST_LEN = 0, // maximum number of beats in burst; 0 = AXI max (256) + parameter int TRAFFIC_SHAPING = 0, + parameter int AXI_ADDR_INCR = 1, + parameter bit AXI_EXCLS = 1'b0, + parameter bit AXI_ATOPS = 1'b0, + parameter bit AXI_BURST_FIXED = 1'b1, + parameter bit AXI_BURST_INCR = 1'b1, + parameter bit AXI_BURST_WRAP = 1'b0, + // Dependent parameters, do not override. + parameter int AXI_STRB_WIDTH = DW/8, + parameter int N_AXI_IDS = 2**IW +) extends axi_test::axi_rand_master #( + .AW ( AW ), + .DW ( DW ), + .IW ( IW ), + .UW ( UW ), + .TA ( TA ), + .TT ( TT ), + .MAX_READ_TXNS ( MAX_READ_TXNS ), + .MAX_WRITE_TXNS ( MAX_WRITE_TXNS ), + .AX_MIN_WAIT_CYCLES ( AX_MIN_WAIT_CYCLES ), + .AX_MAX_WAIT_CYCLES ( AX_MAX_WAIT_CYCLES ), + .W_MIN_WAIT_CYCLES ( W_MIN_WAIT_CYCLES ), + .W_MAX_WAIT_CYCLES ( W_MAX_WAIT_CYCLES ), + .RESP_MIN_WAIT_CYCLES ( RESP_MIN_WAIT_CYCLES ), + .RESP_MAX_WAIT_CYCLES ( RESP_MAX_WAIT_CYCLES ), + .AXI_MAX_BURST_LEN ( AXI_MAX_BURST_LEN ), + .TRAFFIC_SHAPING ( TRAFFIC_SHAPING ), + .AXI_EXCLS ( AXI_EXCLS ), + .AXI_ATOPS ( AXI_ATOPS ), + .AXI_BURST_FIXED ( AXI_BURST_FIXED ), + .AXI_BURST_INCR ( AXI_BURST_INCR ), + .AXI_BURST_WRAP ( AXI_BURST_WRAP ) +); + + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AW), + .AXI_DATA_WIDTH(DW), + .AXI_ID_WIDTH(IW), + .AXI_USER_WIDTH(UW) + ) axi + ); + super.new(axi); + endfunction + + task send_ars(input int n_reads); + automatic logic rand_success; + automatic ax_beat_t ar_beat = new_rand_burst(1'b1); + repeat (n_reads) begin + automatic id_t id; + automatic logic jump; + // Increment address per default, randomize sporadically + rand_success = std::randomize(jump) with {jump dist {1'b0 := 90, 1'b1 := 10};}; + assert(rand_success); + if (jump) begin + ar_beat = new_rand_burst(1'b1); + end else begin + ar_beat.ax_addr = ar_beat.ax_addr + AXI_ADDR_INCR; + end + while (tot_r_flight_cnt >= MAX_READ_TXNS) begin + rand_wait(1, 1); + end + if (AXI_EXCLS) begin + rand_excl_ar(ar_beat); + end + if (AXI_ATOPS) begin + // The ID must not be the same as that of any in-flight ATOP. + forever begin + cnt_sem.get(); + rand_success = std::randomize(id); assert(rand_success); + if (!atop_resp_b[id] && !atop_resp_r[id]) begin + break; + end else begin + // The random ID does not meet the requirements, so try another ID in the next cycle. + cnt_sem.put(); + rand_wait(1, 1); + end + end + ar_beat.ax_id = id; + end else begin + cnt_sem.get(); + end + r_flight_cnt[ar_beat.ax_id]++; + tot_r_flight_cnt++; + cnt_sem.put(); + rand_wait(AX_MIN_WAIT_CYCLES, AX_MAX_WAIT_CYCLES); + drv.send_ar(ar_beat); + if (ar_beat.ax_lock) excl_queue.push_back(ar_beat); + end + endtask + + // Issue n_reads random read and n_writes random write transactions to an address range. + task run(input int n_reads, input int n_writes); + automatic logic ar_done = 1'b0, + aw_done = 1'b0; + fork + begin + send_ars(n_reads); + ar_done = 1'b1; + end + recv_rs(ar_done, aw_done); + begin + create_aws(n_writes); + aw_done = 1'b1; + end + send_aws(aw_done); + send_ws(aw_done); + recv_bs(aw_done); + join + endtask + +endclass + +// Inherit from the random AXI slave, but return the same data for reads from the same address. +// --> Emulate a random ROM +class const_axi_slave #( + // AXI interface parameters + parameter int AW = 32, + parameter int DW = 32, + parameter int IW = 8, + parameter int UW = 1, + // Stimuli application and test time + parameter time TA = 0ps, + parameter time TT = 0ps, + parameter bit RAND_RESP = 0, + // Upper and lower bounds on wait cycles on Ax, W, and resp (R and B) channels + parameter int AX_MIN_WAIT_CYCLES = 0, + parameter int AX_MAX_WAIT_CYCLES = 100, + parameter int R_MIN_WAIT_CYCLES = 0, + parameter int R_MAX_WAIT_CYCLES = 5, + parameter int RESP_MIN_WAIT_CYCLES = 0, + parameter int RESP_MAX_WAIT_CYCLES = 20 +) extends axi_test::axi_rand_slave #( + .AW ( AW ), + .DW ( DW ), + .IW ( IW ), + .UW ( UW ), + .TA ( TA ), + .TT ( TT ), + .RAND_RESP ( RAND_RESP ), + .AX_MIN_WAIT_CYCLES ( AX_MIN_WAIT_CYCLES ), + .AX_MAX_WAIT_CYCLES ( AX_MAX_WAIT_CYCLES ), + .R_MIN_WAIT_CYCLES ( R_MIN_WAIT_CYCLES ), + .R_MAX_WAIT_CYCLES ( R_MAX_WAIT_CYCLES ), + .RESP_MIN_WAIT_CYCLES ( RESP_MIN_WAIT_CYCLES ), + .RESP_MAX_WAIT_CYCLES ( RESP_MAX_WAIT_CYCLES ) +); + function new( + virtual AXI_BUS_DV #( + .AXI_ADDR_WIDTH(AW), + .AXI_DATA_WIDTH(DW), + .AXI_ID_WIDTH(IW), + .AXI_USER_WIDTH(UW) + ) axi + ); + super.new(axi); + endfunction + + task send_rs(); + forever begin + automatic logic rand_success; + automatic ax_beat_t ar_beat; + automatic r_beat_t r_beat = new; + wait (!ar_queue.empty()); + ar_beat = ar_queue.peek(); + rand_success = std::randomize(r_beat); assert(rand_success); + for (int i = 0; i < (DW/32); i++) begin + r_beat.r_data[i*32 +: 32] = (ar_beat.ax_addr >> $clog2(DW/8) << $clog2(DW/8)) + (4*i); + end + r_beat.r_id = ar_beat.ax_id; + if (RAND_RESP && !ar_beat.ax_atop[5]) + r_beat.r_resp[1] = $urandom(); + if (ar_beat.ax_lock) + r_beat.r_resp[0]= $urandom(); + rand_wait(R_MIN_WAIT_CYCLES, R_MAX_WAIT_CYCLES); + if (ar_beat.ax_len == '0) begin + r_beat.r_last = 1'b1; + void'(ar_queue.pop_id(ar_beat.ax_id)); + end else begin + ar_beat.ax_len--; + ar_beat.ax_addr += 1 << ar_beat.ax_size; + ar_queue.set(ar_beat.ax_id, ar_beat); + end + drv.send_r(r_beat); + end + endtask + + task run(); + fork + recv_ars(); + send_rs(); + recv_aws(); + recv_ws(); + send_bs(); + join + endtask + +endclass + +`include "common_cells/assertions.svh" + +module snitch_read_only_cache_tb import snitch_pkg::*; #( + parameter int unsigned AxiAddrWidth = 32, + parameter int unsigned AxiDataWidth = 128, + parameter int unsigned AxiIdWidth = 5, + parameter int unsigned LineWidth = 256, + parameter int unsigned LineCount = 128, + parameter int unsigned SetCount = 2 +); + + localparam time ClkPeriod = 10ns; + localparam time TA = 2ns; + localparam time TT = 8ns; + localparam bit DEBUG = 1'b0; + + // AXI parameters + `include "axi/typedef.svh" + `include "axi/assign.svh" + + localparam int unsigned AxiStrbWidth = AxiDataWidth/8; + localparam int unsigned AxiInIdWidth = AxiIdWidth; + localparam int unsigned AxiOutIdWidth = AxiInIdWidth+1; + localparam int unsigned AxiUserWidth = 1; + + typedef logic [AxiAddrWidth-1:0] axi_addr_t; + typedef logic [AxiDataWidth-1:0] axi_data_t; + typedef logic [AxiStrbWidth-1:0] axi_strb_t; + typedef logic [AxiInIdWidth-1:0] axi_in_id_t; + typedef logic [AxiOutIdWidth-1:0] axi_out_id_t; + typedef logic [AxiUserWidth-1:0] axi_user_t; + + `AXI_TYPEDEF_ALL(axi_mst, axi_addr_t, axi_in_id_t, axi_data_t, axi_strb_t, axi_user_t) + `AXI_TYPEDEF_ALL(axi_slv, axi_addr_t, axi_out_id_t, axi_data_t, axi_strb_t, axi_user_t) + + // Address regions + localparam axi_addr_t CachedRegionStart = axi_addr_t'(32'h8000_0000); + localparam axi_addr_t CachedRegionEnd = axi_addr_t'(32'h8000_1000); + + // backing memory + logic [LineWidth-1:0] memory [logic [AxiAddrWidth-1:0]]; + + logic clk, rst; + + typedef semirand_axi_master #( + .AW ( AxiAddrWidth ), + .DW ( AxiDataWidth ), + .IW ( AxiInIdWidth ), + .UW ( AxiUserWidth ), + .TA ( TA ), + .TT ( TT ), + .MAX_READ_TXNS ( 16 ), + .MAX_WRITE_TXNS ( 4 ), + .AX_MIN_WAIT_CYCLES ( 0 ), + .AX_MAX_WAIT_CYCLES ( 8 ), + .W_MIN_WAIT_CYCLES ( 0 ), + .W_MAX_WAIT_CYCLES ( 8 ), + .RESP_MIN_WAIT_CYCLES ( 0 ), + .RESP_MAX_WAIT_CYCLES ( 8 ), + .AXI_MAX_BURST_LEN ( 16 ), + .TRAFFIC_SHAPING ( 0 ), + .AXI_ADDR_INCR ( LineWidth/8 ), + .AXI_EXCLS ( 1'b1 ), + .AXI_ATOPS ( 1'b1 ), + .AXI_BURST_FIXED ( 1'b0 ), + .AXI_BURST_INCR ( 1'b1 ), + .AXI_BURST_WRAP ( 1'b0 ) + ) axi_rand_master_t; + + typedef const_axi_slave #( + .AW ( AxiAddrWidth ), + .DW ( AxiDataWidth ), + .IW ( AxiOutIdWidth ), + .UW ( AxiUserWidth ), + .TA ( TA ), + .TT ( TT ), + .RAND_RESP ( 0 ), + .AX_MIN_WAIT_CYCLES ( 0 ), + .AX_MAX_WAIT_CYCLES ( 8 ), + .R_MIN_WAIT_CYCLES ( 0 ), + .R_MAX_WAIT_CYCLES ( 8 ), + .RESP_MIN_WAIT_CYCLES ( 0 ), + .RESP_MAX_WAIT_CYCLES ( 8 ) + ) axi_rand_slave_t; + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiInIdWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) axi_mst_dv ( + .clk_i ( clk ) + ); + + AXI_BUS_DV #( + .AXI_ADDR_WIDTH ( AxiAddrWidth ), + .AXI_DATA_WIDTH ( AxiDataWidth ), + .AXI_ID_WIDTH ( AxiOutIdWidth ), + .AXI_USER_WIDTH ( AxiUserWidth ) + ) axi_slv_dv ( + .clk_i ( clk ) + ); + + axi_rand_master_t mst_intf = new(axi_mst_dv); + axi_rand_slave_t slv_intf = new(axi_slv_dv); + + axi_mst_req_t axi_mst_req; + axi_mst_resp_t axi_mst_resp; + + axi_slv_req_t axi_slv_req; + axi_slv_resp_t axi_slv_resp; + + `AXI_ASSIGN_TO_REQ(axi_mst_req, axi_mst_dv) + `AXI_ASSIGN_FROM_RESP(axi_mst_dv, axi_mst_resp) + + `AXI_ASSIGN_FROM_REQ(axi_slv_dv, axi_slv_req) + `AXI_ASSIGN_TO_RESP(axi_slv_resp, axi_slv_dv) + + snitch_read_only_cache #( + .LineWidth ( LineWidth ), + .LineCount ( LineCount ), + .SetCount ( SetCount ), + .AxiAddrWidth ( AxiAddrWidth ), + .AxiDataWidth ( AxiDataWidth ), + .AxiIdWidth ( AxiInIdWidth ), + .AxiUserWidth ( 1 ), + .MaxTrans ( 32'd8 ), + .NrAddrRules ( 1 ), + .slv_req_t ( axi_mst_req_t ), + .slv_rsp_t ( axi_mst_resp_t ), + .mst_req_t ( axi_slv_req_t ), + .mst_rsp_t ( axi_slv_resp_t ) + ) dut ( + .clk_i ( clk ), + .rst_ni ( ~rst ), + .enable_i ( 1'b1 ), + .flush_valid_i ( 1'b0 ), + .flush_ready_o ( /*unused*/ ), + .start_addr_i ( {CachedRegionStart} ), + .end_addr_i ( {CachedRegionEnd} ), + .axi_slv_req_i ( axi_mst_req ), + .axi_slv_rsp_o ( axi_mst_resp ), + .axi_mst_req_o ( axi_slv_req ), + .axi_mst_rsp_i ( axi_slv_resp ) + ); + + task static cycle_start; + #TT; + endtask + + task static cycle_end; + @(posedge clk); + endtask + + typedef axi_test::axi_driver #( + .AW(AxiAddrWidth), .DW(AxiDataWidth), .IW(AxiInIdWidth), .UW(AxiUserWidth), .TA(TA), .TT(TT) + ) axi_driver_t; + + typedef axi_driver_t::ax_beat_t ar_beat_t; + typedef axi_driver_t::r_beat_t r_beat_t; + + initial begin + automatic logic rand_success; + automatic ar_beat_t ar_beat = new; + automatic r_beat_t r_beat = new; + + // Initialize memory region of random axi master to only fetch from two lines + mst_intf.add_memory_region(CachedRegionStart, CachedRegionEnd, axi_pkg::WTHRU_RALLOCATE); + + // Reset + mst_intf.reset(); + @(negedge rst); + #1000ns; + + // Issue single miss and hit burst for debugging + ar_beat.ax_id = '1; + ar_beat.ax_addr = CachedRegionStart; + ar_beat.ax_len = 7; + ar_beat.ax_size = $clog2(AxiStrbWidth); + ar_beat.ax_burst = axi_pkg::BURST_INCR; + ar_beat.ax_lock = 1'b0; + ar_beat.ax_cache = axi_pkg::WTHRU_RALLOCATE; + ar_beat.ax_prot = '0; + ar_beat.ax_qos = '0; + ar_beat.ax_region = '0; + ar_beat.ax_atop = '0; + ar_beat.ax_user = '1; + + mst_intf.drv.send_ar(ar_beat); + for (int i = 0; i <= ar_beat.ax_len; i++) begin + mst_intf.drv.recv_r(r_beat); + end + #10000ns; + mst_intf.drv.send_ar(ar_beat); + for (int i = 0; i <= ar_beat.ax_len; i++) begin + mst_intf.drv.recv_r(r_beat); + end + #10000ns; + + // Issue two requests to the same location simultaneously + fork + begin + ar_beat.ax_addr = CachedRegionStart+'h100; + ar_beat.ax_len = 0; + mst_intf.drv.send_ar(ar_beat); + ar_beat.ax_id = '0; + mst_intf.drv.send_ar(ar_beat); + end + begin + for (int i = 0; i <= ar_beat.ax_len; i++) begin + mst_intf.drv.recv_r(r_beat); + end + for (int i = 0; i <= ar_beat.ax_len; i++) begin + mst_intf.drv.recv_r(r_beat); + end + end + join + #10000ns; + + // Issue a cache and bypass request simultaneously + // to test reordering or transactions + fork + begin + ar_beat.ax_addr = CachedRegionStart+'h200; + ar_beat.ax_len = 1; + mst_intf.drv.send_ar(ar_beat); + ar_beat.ax_len = 0; + ar_beat.ax_addr = CachedRegionEnd+'h200; + mst_intf.drv.send_ar(ar_beat); + end + begin + for (int i = 0; i <= ar_beat.ax_len; i++) begin + mst_intf.drv.recv_r(r_beat); + end + for (int i = 0; i <= ar_beat.ax_len; i++) begin + mst_intf.drv.recv_r(r_beat); + end + end + join + #10000ns; + + // Issue random request to the cached region + mst_intf.run(10000, 0); + mst_intf.add_memory_region(CachedRegionStart, + CachedRegionStart+2*(CachedRegionEnd-CachedRegionStart), + axi_pkg::WTHRU_RALLOCATE); + + #1000ns; + // Issue random requests, 50% to the cached region + mst_intf.run(10000, 0); + + $finish(); + end + + initial begin : proc_sim_mem + slv_intf.reset(); + @(negedge rst); + slv_intf.run(); + end + + // Time-out + initial begin + automatic int unsigned timeout; + @(negedge rst); + timeout = 0; + while (1) begin + @(posedge clk); + timeout += 1; + if (axi_mst_req.ar_valid && axi_mst_resp.ar_ready) begin + timeout = 0; + end + if (timeout > 5000) begin + $error("Simulation timed out at %0t", $time); + $finish(); + end + end + end + + //////////////////////// + // Checker tasks // + //////////////////////// + + // Queues + localparam int unsigned NoIds = 2**AxiInIdWidth; + axi_mst_ar_chan_t ar_queues[NoIds][$]; + axi_mst_r_chan_t r_queues[NoIds][$]; + + // channel sampling into queues + always @(posedge clk) #TT begin : proc_channel_sample + automatic axi_mst_ar_chan_t ar_beat; + // only execute when reset is high + if (!rst) begin + // AR channel + if (axi_mst_req.ar_valid && axi_mst_resp.ar_ready) begin + ar_queues[axi_mst_req.ar.id].push_back(axi_mst_req.ar); + end + // R channel + if (axi_mst_resp.r_valid && axi_mst_req.r_ready) begin + r_queues[axi_mst_resp.r.id].push_back(axi_mst_resp.r); + end + end + end + + initial begin + automatic axi_mst_ar_chan_t ar_beat; + automatic axi_mst_r_chan_t r_beat; + automatic axi_addr_t addr; + automatic axi_addr_t aligned_addr; + automatic axi_data_t exp_data; + automatic int unsigned no_r_beat[NoIds]; + $timeformat(-9, 2, " ns", 20); + @(negedge rst); + forever begin + @(posedge clk); + #TT; + // Check all read queues + for (int unsigned i = 0; i < NoIds; i++) begin + while (ar_queues[i].size() != 0 && r_queues[i].size() != 0) begin + ar_beat = ar_queues[i][0]; + addr = ar_beat.addr; + for (int unsigned j = 0; j <= ar_beat.len; j++) begin + wait (r_queues[i].size() > 0); + r_beat = r_queues[i].pop_front(); + // Check data + aligned_addr = addr >> $clog2(AxiDataWidth/8) << $clog2(AxiDataWidth/8); + for (int i = 0; i < (AxiDataWidth/32); i++) begin + exp_data[i*32 +: 32] = aligned_addr + (4*i); + end + if (r_beat.data != exp_data) begin + $display("Error (%0t): Wrong data. Addr=0x%x, Beat=%d, Size=%d Aqc=0x%x Exp=0x%x", + $time, ar_beat.addr, no_r_beat[i], ar_beat.size, r_beat.data, exp_data); + end + + if (r_beat.last && !(ar_beat.len == no_r_beat[i])) begin + $display("ERROR> Last flag was not expected!!!!!!!!!!!!!"); + end + no_r_beat[i]++; + // pop the queue if it is the last flag + if (r_beat.last) begin + ar_beat = ar_queues[i].pop_front(); + no_r_beat[i] = 0; + end else begin + addr = addr + (1 << ar_beat.size); + end + end + end + end + end + end + + //////////////////////// + // Debug // + //////////////////////// + if (DEBUG) begin: gen_chan_logger + axi_chan_logger #( + .TestTime ( 8ns ), + .LoggerName ( "mst_logger" ), + .aw_chan_t ( axi_mst_aw_chan_t ), + .w_chan_t ( axi_mst_w_chan_t ), + .b_chan_t ( axi_mst_b_chan_t ), + .ar_chan_t ( axi_mst_ar_chan_t ), + .r_chan_t ( axi_mst_r_chan_t ) + ) i_axi_chan_logger_mst ( + .clk_i ( clk ), + .rst_ni ( ~rst ), + .end_sim_i ( 1'b0 ), + .aw_chan_i ( axi_mst_req.aw ), + .aw_valid_i ( axi_mst_req.aw_valid ), + .aw_ready_i ( axi_mst_resp.aw_ready ), + .w_chan_i ( axi_mst_req.w ), + .w_valid_i ( axi_mst_req.w_valid ), + .w_ready_i ( axi_mst_resp.w_ready ), + .b_chan_i ( axi_mst_resp.b ), + .b_valid_i ( axi_mst_resp.b_valid ), + .b_ready_i ( axi_mst_req.b_ready ), + .ar_chan_i ( axi_mst_req.ar ), + .ar_valid_i ( axi_mst_req.ar_valid ), + .ar_ready_i ( axi_mst_resp.ar_ready ), + .r_chan_i ( axi_mst_resp.r ), + .r_valid_i ( axi_mst_resp.r_valid ), + .r_ready_i ( axi_mst_req.r_ready ) + ); + + axi_chan_logger #( + .TestTime ( 8ns ), + .LoggerName ( "slv_logger" ), + .aw_chan_t ( axi_slv_aw_chan_t ), + .w_chan_t ( axi_slv_w_chan_t ), + .b_chan_t ( axi_slv_b_chan_t ), + .ar_chan_t ( axi_slv_ar_chan_t ), + .r_chan_t ( axi_slv_r_chan_t ) + ) i_axi_chan_logger_slv ( + .clk_i ( clk ), + .rst_ni ( ~rst ), + .end_sim_i ( 1'b0 ), + .aw_chan_i ( axi_slv_req.aw ), + .aw_valid_i ( axi_slv_req.aw_valid ), + .aw_ready_i ( axi_slv_resp.aw_ready ), + .w_chan_i ( axi_slv_req.w ), + .w_valid_i ( axi_slv_req.w_valid ), + .w_ready_i ( axi_slv_resp.w_ready ), + .b_chan_i ( axi_slv_resp.b ), + .b_valid_i ( axi_slv_resp.b_valid ), + .b_ready_i ( axi_slv_req.b_ready ), + .ar_chan_i ( axi_slv_req.ar ), + .ar_valid_i ( axi_slv_req.ar_valid ), + .ar_ready_i ( axi_slv_resp.ar_ready ), + .r_chan_i ( axi_slv_resp.r ), + .r_valid_i ( axi_slv_resp.r_valid ), + .r_ready_i ( axi_slv_req.r_ready ) + ); + end + + // Clock generation. + initial begin + rst = 1; + repeat (3) begin + #(ClkPeriod/2) clk = 0; + #(ClkPeriod/2) clk = 1; + end + rst = 0; + forever begin + #(ClkPeriod/2) clk = 0; + #(ClkPeriod/2) clk = 1; + end + end +endmodule From e4398717a23a9d721989a7155b7df02f976c25c4 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Thu, 12 Oct 2023 18:34:36 +0200 Subject: [PATCH 02/46] added parity bits and checks to tag and data banks --- hardware/deps/snitch/Bender.yml | 6 ++ .../snitch_icache_lookup_serial.sv | 82 +++++++++++++++---- hardware/deps/snitch/tb/scripts/sim_setup.tcl | 4 +- .../tb/src/snitch_read_only_cache_tb.sv | 8 +- hardware/src/bootrom.sv | 6 +- 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/hardware/deps/snitch/Bender.yml b/hardware/deps/snitch/Bender.yml index 24427e910..9200fa1ee 100644 --- a/hardware/deps/snitch/Bender.yml +++ b/hardware/deps/snitch/Bender.yml @@ -38,3 +38,9 @@ sources: - src/snitch_icache/snitch_icache_refill.sv - src/snitch_read_only_cache/snitch_axi_to_cache.sv - src/snitch_read_only_cache/snitch_read_only_cache.sv + + - target: rocache_test + files: + #- ../../tb/RO_cache_tb/sourcecode/tb/snitch_read_only_cache_tb.sv + - tb/src/snitch_read_only_cache_tb.sv + diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index f9c1268a7..bdbf3678d 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -8,7 +8,8 @@ /// An actual cache lookup. module snitch_icache_lookup_serial #( - parameter snitch_icache_pkg::config_t CFG = '0 + parameter snitch_icache_pkg::config_t CFG = '0, + parameter logic RELIABILITY_MODE = '1 // Fault tolerance enabled setting it to 1 )( input logic clk_i, input logic rst_ni, @@ -40,6 +41,7 @@ module snitch_icache_lookup_serial #( ); localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; + localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? CFG.SET_COUNT : '0; `ifndef SYNTHESIS initial assert(CFG != '0); @@ -80,7 +82,7 @@ module snitch_icache_lookup_serial #( logic [CFG.COUNT_ALIGN-1:0] tag_addr; logic [CFG.SET_COUNT-1:0] tag_enable; - logic [CFG.TAG_WIDTH+1:0] tag_wdata, tag_rdata [CFG.SET_COUNT]; + logic [CFG.TAG_WIDTH+1+RELIABILITY_MODE:0] tag_wdata, tag_rdata [CFG.SET_COUNT]; logic tag_write; tag_req_t tag_req_d, tag_req_q; @@ -90,6 +92,7 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; + logic [CFG.SET_COUNT-1:0] tag_parity_error; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; @@ -100,9 +103,13 @@ module snitch_icache_lookup_serial #( // Multiplex read and write access to the tag banks onto one port, prioritizing write accesses always_comb begin - tag_addr = in_addr_i >> CFG.LINE_ALIGN; + tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the tag (first 8 bits determine the line of the ) tag_enable = '0; - tag_wdata = {1'b1, write_error_i, write_tag_i}; + if(RELIABILITY_MODE) begin + tag_wdata = {^write_tag_i, 1'b1, write_error_i, write_tag_i}; + end else begin + tag_wdata = {1'b1, write_error_i, write_tag_i}; + end tag_write = 1'b0; write_ready_o = 1'b0; @@ -146,13 +153,13 @@ module snitch_icache_lookup_serial #( ); end end else begin : gen_sram - logic [CFG.SET_COUNT*(CFG.TAG_WIDTH+2)-1:0] tag_rdata_flat; + logic [CFG.SET_COUNT*(CFG.TAG_WIDTH+2+RELIABILITY_MODE)-1:0] tag_rdata_flat; for (genvar i = 0; i < CFG.SET_COUNT; i++) begin : g_sets_rdata - assign tag_rdata[i] = tag_rdata_flat[i*(CFG.TAG_WIDTH+2)+:CFG.TAG_WIDTH+2]; + assign tag_rdata[i] = tag_rdata_flat[i*(CFG.TAG_WIDTH+2+RELIABILITY_MODE)+:CFG.TAG_WIDTH+2+RELIABILITY_MODE]; end tc_sram #( - .DataWidth ( (CFG.TAG_WIDTH+2) * CFG.SET_COUNT ), - .ByteWidth ( CFG.TAG_WIDTH+2 ), + .DataWidth ( (CFG.TAG_WIDTH+2+RELIABILITY_MODE) * CFG.SET_COUNT ), //we store set_count number of tags (and valid/error bits) in every line + .ByteWidth ( CFG.TAG_WIDTH+2+RELIABILITY_MODE ), .NumWords ( CFG.LINE_COUNT ), .NumPorts ( 1 ) ) i_tag ( @@ -167,15 +174,29 @@ module snitch_icache_lookup_serial #( ); end + // compute tag parity bit the cycle before reading it + logic exp_tag_parity_bit; + if (RELIABILITY_MODE) begin + assign exp_tag_parity_bit = ^required_tag; + end + // Determine which set hit always_comb begin automatic logic [CFG.SET_COUNT-1:0] errors; required_tag = tag_req_q.addr >> (CFG.LINE_ALIGN + CFG.COUNT_ALIGN); + tag_parity_error = '0; for (int i = 0; i < CFG.SET_COUNT; i++) begin - line_hit[i] = tag_rdata[i][CFG.TAG_WIDTH+1] && tag_rdata[i][CFG.TAG_WIDTH-1:0] == required_tag; - errors[i] = tag_rdata[i][CFG.TAG_WIDTH] && line_hit[i]; + line_hit[i] = tag_rdata[i][CFG.TAG_WIDTH+1] && tag_rdata[i][CFG.TAG_WIDTH-1:0] == required_tag; //check valid bit and tag + errors[i] = tag_rdata[i][CFG.TAG_WIDTH] && line_hit[i]; //check error bit + if (RELIABILITY_MODE) begin + tag_parity_error[i] = (tag_rdata[i][CFG.TAG_WIDTH+2] == exp_tag_parity_bit) ? '0:'1; //check tag parity ^tag_rdata[i][CFG.TAG_WIDTH-1:0]) + end; + end + if (RELIABILITY_MODE) begin + tag_rsp_s.hit = |(line_hit & ~tag_parity_error); + end else begin + tag_rsp_s.hit = |line_hit; end - tag_rsp_s.hit = |line_hit; tag_rsp_s.error = |errors; end @@ -222,7 +243,13 @@ module snitch_icache_lookup_serial #( logic error; } data_req_t; - typedef logic [CFG.LINE_WIDTH-1:0] data_rsp_t; + /*typedef struct packed{ + logic [CFG.LINE_WIDTH-1:0] data_line; + logic [DATA_PARITY_WIDTH-1:0] parity_bit; + }data_rsp_t;*/ + + typedef logic [CFG.LINE_WIDTH + DATA_PARITY_WIDTH - 1:0] data_rsp_t; + logic [DATA_ADDR_WIDTH-1:0] data_addr; logic data_enable; @@ -240,15 +267,24 @@ module snitch_icache_lookup_serial #( assign data_req_d.hit = tag_rsp.hit; assign data_req_d.error = tag_rsp.error; - assign lookup_addr = {tag_rsp.cset, tag_req_q.addr[CFG.LINE_ALIGN +: CFG.COUNT_ALIGN]}; + assign lookup_addr = {tag_rsp.cset, tag_req_q.addr[CFG.LINE_ALIGN +: CFG.COUNT_ALIGN]}; //eg if 256 line width Line_align is log2(nr of bytes), 2 set counts, i.e. 3 bits for line, 1 for set, take bits from 1 to 5 assign write_addr = {write_set_i, write_addr_i}; + localparam WORD_WIDTH = CFG.LINE_WIDTH/CFG.SET_COUNT; // Data bank port mux always_comb begin + + // Compute parity bit + if (RELIABILITY_MODE) begin + for (int i = 0; i < CFG.SET_COUNT; i++) begin + data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ^write_data_i[CFG.LINE_WIDTH - WORD_WIDTH*i -1 -: WORD_WIDTH]; + end + end + // Default read request data_addr = lookup_addr; data_enable = tag_valid && tag_rsp.hit; // Only read data on hit - data_wdata = write_data_i; + data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i; data_write = 1'b0; // Write takes priority if (!init_phase && write_valid_i) begin @@ -259,9 +295,9 @@ module snitch_icache_lookup_serial #( end tc_sram #( - .DataWidth ( CFG.LINE_WIDTH ), - .NumWords ( CFG.LINE_COUNT * CFG.SET_COUNT ), - .NumPorts ( 1 ) + .DataWidth ( CFG.LINE_WIDTH + DATA_PARITY_WIDTH ), + .NumWords ( CFG.LINE_COUNT * CFG.SET_COUNT ), + .NumPorts ( 1 ) ) i_data ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -273,6 +309,18 @@ module snitch_icache_lookup_serial #( .rdata_o ( data_rdata ) ); + logic [CFG.SET_COUNT-1:0] parity_error; + always_comb begin : p_parity_check + parity_error = '0; + if (RELIABILITY_MODE) begin + for (int i = 0; i < CFG.SET_COUNT; i++) begin + parity_error[CFG.SET_COUNT-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - WORD_WIDTH*i -1 -: WORD_WIDTH])? '0 : '1; + end + end + + + end + // Buffer the metadata on a valid handshake. Stall on write (implicit in tag_ready) `FFL(data_req_q, data_req_d, tag_valid && tag_ready, '0, clk_i, rst_ni) `FF(data_valid, (tag_valid && !data_write) ? 1'b1 : data_ready ? 1'b0 : data_valid, '0, clk_i, rst_ni) diff --git a/hardware/deps/snitch/tb/scripts/sim_setup.tcl b/hardware/deps/snitch/tb/scripts/sim_setup.tcl index ba8209539..14f499fb2 100644 --- a/hardware/deps/snitch/tb/scripts/sim_setup.tcl +++ b/hardware/deps/snitch/tb/scripts/sim_setup.tcl @@ -7,4 +7,6 @@ vsim snitch_read_only_cache_tb_opt -voptargs=+acc # waves add wave -noupdate -expand -group lookup /snitch_read_only_cache_tb/dut/i_lookup/* add wave -noupdate -group handler /snitch_read_only_cache_tb/dut/i_handler/* -add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* \ No newline at end of file +add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* + +run -all \ No newline at end of file diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv index de8af09e9..dcb052a54 100644 --- a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -271,13 +271,13 @@ class const_axi_slave #( endclass `include "common_cells/assertions.svh" - +localparam debug = 0; module snitch_read_only_cache_tb import snitch_pkg::*; #( parameter int unsigned AxiAddrWidth = 32, - parameter int unsigned AxiDataWidth = 128, + parameter int unsigned AxiDataWidth = debug? 32:128, parameter int unsigned AxiIdWidth = 5, - parameter int unsigned LineWidth = 256, - parameter int unsigned LineCount = 128, + parameter int unsigned LineWidth = debug? 64:256, + parameter int unsigned LineCount = debug? 32:128, parameter int unsigned SetCount = 2 ); diff --git a/hardware/src/bootrom.sv b/hardware/src/bootrom.sv index 6494806e7..87f1c259e 100644 --- a/hardware/src/bootrom.sv +++ b/hardware/src/bootrom.sv @@ -8,7 +8,7 @@ module bootrom #( /* Automatically generated. DO NOT CHANGE! */ - parameter int unsigned DataWidth = 512, + parameter int unsigned DataWidth = 256, parameter int unsigned AddrWidth = 32 ) ( input logic clk_i, @@ -20,14 +20,14 @@ module bootrom #( localparam int AddrBits = RomSize > 1 ? $clog2(RomSize) : 1; const logic [RomSize-1:0][DataWidth-1:0] mem = { - 512'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00050067_10500073_00050513_e0000517 + 256'h00000000_00000000_00000000_00000000_00050067_10500073_00050513_e0000517 }; logic [AddrBits-1:0] addr_q; always_ff @(posedge clk_i) begin if (req_i) begin - addr_q <= addr_i[AddrBits-1+6:6]; + addr_q <= addr_i[AddrBits-1+5:5]; end end From fc731df2a51c4ad80242aeebae393158a76afe0b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 15 Oct 2023 20:46:20 +0200 Subject: [PATCH 03/46] [hardware]: implemented first version of faulty tag invalidation --- .../snitch_icache_lookup_serial.sv | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index bdbf3678d..c5f9e17e4 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -31,7 +31,7 @@ module snitch_icache_lookup_serial #( output logic out_valid_o, input logic out_ready_i, - input logic [CFG.COUNT_ALIGN-1:0] write_addr_i, + input logic [CFG.COUNT_ALIGN-1:0] write_addr_i, //when write valid, it goes contemporaritly to both stages, otherwise data stage uses lookup addr from tag stage input logic [CFG.SET_ALIGN-1:0] write_set_i, input logic [CFG.LINE_WIDTH-1:0] write_data_i, input logic [CFG.TAG_WIDTH-1:0] write_tag_i, @@ -92,7 +92,8 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; - logic [CFG.SET_COUNT-1:0] tag_parity_error; + logic [CFG.SET_COUNT-1:0] tag_parity_error_d, tag_parity_error_q; + logic faulty_hit; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; @@ -127,6 +128,16 @@ module snitch_icache_lookup_serial #( tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; + end else if (faulty_hit) begin //we need to set second bit (valid) of write data of the previous adress to 0 + //in_ready_o = '0; //we do not accept new read request + // Request to store data in pipeline, zero as no new data + //req_valid = 1'b0; + + tag_addr = tag_req_q.addr; //buffered version of in_addr_i + tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) + tag_wdata = '0; + tag_write = '1; + end else if (in_valid_i) begin // Check cache tag_enable = '1; @@ -174,26 +185,32 @@ module snitch_icache_lookup_serial #( ); end - // compute tag parity bit the cycle before reading it - logic exp_tag_parity_bit; + // compute tag parity bit the cycle before reading it and buffer it + logic exp_tag_parity_bit_d, exp_tag_parity_bit_q; + if (RELIABILITY_MODE) begin - assign exp_tag_parity_bit = ^required_tag; + assign exp_tag_parity_bit_d = ^(tag_req_d.addr >> (CFG.LINE_ALIGN + CFG.COUNT_ALIGN)); + end + if (RELIABILITY_MODE) begin + `FFL(exp_tag_parity_bit_q, exp_tag_parity_bit_d, req_valid && req_ready, '0, clk_i, rst_ni); end // Determine which set hit always_comb begin automatic logic [CFG.SET_COUNT-1:0] errors; required_tag = tag_req_q.addr >> (CFG.LINE_ALIGN + CFG.COUNT_ALIGN); - tag_parity_error = '0; + tag_parity_error_d = '0; + faulty_hit = '0; for (int i = 0; i < CFG.SET_COUNT; i++) begin line_hit[i] = tag_rdata[i][CFG.TAG_WIDTH+1] && tag_rdata[i][CFG.TAG_WIDTH-1:0] == required_tag; //check valid bit and tag errors[i] = tag_rdata[i][CFG.TAG_WIDTH] && line_hit[i]; //check error bit - if (RELIABILITY_MODE) begin - tag_parity_error[i] = (tag_rdata[i][CFG.TAG_WIDTH+2] == exp_tag_parity_bit) ? '0:'1; //check tag parity ^tag_rdata[i][CFG.TAG_WIDTH-1:0]) + if (RELIABILITY_MODE && line_hit[i]) begin + tag_parity_error_d[i] = (tag_rdata[i][CFG.TAG_WIDTH+2] == exp_tag_parity_bit_q) ? '0:'1; //check tag parity ^tag_rdata[i][CFG.TAG_WIDTH-1:0]) end; end if (RELIABILITY_MODE) begin - tag_rsp_s.hit = |(line_hit & ~tag_parity_error); + tag_rsp_s.hit = |(line_hit & ~tag_parity_error_d); + faulty_hit = |(line_hit & tag_parity_error_d); //if correspondent bits are both one, there was a false hit end else begin tag_rsp_s.hit = |line_hit; end @@ -208,9 +225,17 @@ module snitch_icache_lookup_serial #( // Buffer the metadata on a valid handshake. Stall on write (implicit in req_valid/ready) `FFL(tag_req_q, tag_req_d, req_valid && req_ready, '0, clk_i, rst_ni) + if(RELIABILITY_MODE) begin + `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) + end `FF(tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0, clk_i, rst_ni) + if(!RELIABILITY_MODE) begin // Ready if buffer is empy or downstream is reading. Stall on write - assign req_ready = (!tag_valid || tag_ready) && !tag_write; + assign req_ready = (!tag_valid || tag_ready) && !tag_write; + end else begin + // Ready if buffer is empy or downstream is reading. Stall on write + assign req_ready = (!tag_valid || tag_ready) && !tag_write; //procedure must be the same, we must have handshake with the data stage, in_ready_o set to 0 when invalidating + end // Register the handshake of the reg stage to buffer the tag output data in the next cycle `FF(req_handshake, req_valid && req_ready, 1'b0, clk_i, rst_ni) From f1ac75710e11944d063828a86155379f9e42c96b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 16 Oct 2023 14:26:44 +0200 Subject: [PATCH 04/46] [hardware] Fixed data parity bit width, added configurable split of the data lines --- .../snitch_icache_lookup_serial.sv | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index c5f9e17e4..fbd8d3622 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -41,7 +41,7 @@ module snitch_icache_lookup_serial #( ); localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; - localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? CFG.SET_COUNT : '0; + localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd4 : '0; `ifndef SYNTHESIS initial assert(CFG != '0); @@ -295,14 +295,14 @@ module snitch_icache_lookup_serial #( assign lookup_addr = {tag_rsp.cset, tag_req_q.addr[CFG.LINE_ALIGN +: CFG.COUNT_ALIGN]}; //eg if 256 line width Line_align is log2(nr of bytes), 2 set counts, i.e. 3 bits for line, 1 for set, take bits from 1 to 5 assign write_addr = {write_set_i, write_addr_i}; - localparam WORD_WIDTH = CFG.LINE_WIDTH/CFG.SET_COUNT; + localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; // Data bank port mux always_comb begin // Compute parity bit if (RELIABILITY_MODE) begin - for (int i = 0; i < CFG.SET_COUNT; i++) begin - data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ^write_data_i[CFG.LINE_WIDTH - WORD_WIDTH*i -1 -: WORD_WIDTH]; + for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin + data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ^write_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; end end @@ -334,16 +334,14 @@ module snitch_icache_lookup_serial #( .rdata_o ( data_rdata ) ); - logic [CFG.SET_COUNT-1:0] parity_error; - always_comb begin : p_parity_check - parity_error = '0; - if (RELIABILITY_MODE) begin - for (int i = 0; i < CFG.SET_COUNT; i++) begin - parity_error[CFG.SET_COUNT-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - WORD_WIDTH*i -1 -: WORD_WIDTH])? '0 : '1; + logic [DATA_PARITY_WIDTH-1:0] data_parity_error; + if (RELIABILITY_MODE) begin + always_comb begin : p_parity_check + data_parity_error = '0; + for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin + data_parity_error[DATA_PARITY_WIDTH-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT])? '0 : '1; end end - - end // Buffer the metadata on a valid handshake. Stall on write (implicit in tag_ready) From 414d3b6dc18a6c6b08ebbc96b78a46e35b0e5a7c Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 16 Oct 2023 17:43:02 +0200 Subject: [PATCH 05/46] [hardware] Moved logic related to reliability mode outside the always_comb block --- .../snitch_icache_lookup_serial.sv | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index fbd8d3622..e2709ba54 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -80,10 +80,10 @@ module snitch_icache_lookup_serial #( logic req_valid, req_ready; logic req_handshake; - logic [CFG.COUNT_ALIGN-1:0] tag_addr; - logic [CFG.SET_COUNT-1:0] tag_enable; - logic [CFG.TAG_WIDTH+1+RELIABILITY_MODE:0] tag_wdata, tag_rdata [CFG.SET_COUNT]; - logic tag_write; + logic [CFG.COUNT_ALIGN-1:0] tag_addr; + logic [CFG.SET_COUNT-1:0] tag_enable; + logic [CFG.TAG_WIDTH+1+RELIABILITY_MODE:0] tag_wdata, tag_rdata [CFG.SET_COUNT]; + logic tag_write; tag_req_t tag_req_d, tag_req_q; tag_rsp_t tag_rsp_s, tag_rsp_d, tag_rsp_q, tag_rsp; @@ -93,7 +93,7 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; logic [CFG.SET_COUNT-1:0] tag_parity_error_d, tag_parity_error_q; - logic faulty_hit; + logic faulty_hit, faulty_data; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; @@ -103,14 +103,14 @@ module snitch_icache_lookup_serial #( assign tag_req_d.id = in_id_i; // Multiplex read and write access to the tag banks onto one port, prioritizing write accesses + if (RELIABILITY_MODE) begin + assign tag_wdata [CFG.TAG_WIDTH+2] = ^write_tag_i; + end + always_comb begin tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the tag (first 8 bits determine the line of the ) tag_enable = '0; - if(RELIABILITY_MODE) begin - tag_wdata = {^write_tag_i, 1'b1, write_error_i, write_tag_i}; - end else begin - tag_wdata = {1'b1, write_error_i, write_tag_i}; - end + tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i}; tag_write = 1'b0; write_ready_o = 1'b0; @@ -120,7 +120,7 @@ module snitch_icache_lookup_serial #( if (init_phase) begin tag_addr = init_count_q; tag_enable = '1; - tag_wdata = '0; + tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; end else if (write_valid_i) begin // Write a refill request @@ -128,14 +128,14 @@ module snitch_icache_lookup_serial #( tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; - end else if (faulty_hit) begin //we need to set second bit (valid) of write data of the previous adress to 0 + end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 //in_ready_o = '0; //we do not accept new read request // Request to store data in pipeline, zero as no new data //req_valid = 1'b0; tag_addr = tag_req_q.addr; //buffered version of in_addr_i - tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) - tag_wdata = '0; + tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) + tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = '1; end else if (in_valid_i) begin @@ -190,8 +190,6 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin assign exp_tag_parity_bit_d = ^(tag_req_d.addr >> (CFG.LINE_ALIGN + CFG.COUNT_ALIGN)); - end - if (RELIABILITY_MODE) begin `FFL(exp_tag_parity_bit_q, exp_tag_parity_bit_d, req_valid && req_ready, '0, clk_i, rst_ni); end @@ -199,24 +197,23 @@ module snitch_icache_lookup_serial #( always_comb begin automatic logic [CFG.SET_COUNT-1:0] errors; required_tag = tag_req_q.addr >> (CFG.LINE_ALIGN + CFG.COUNT_ALIGN); - tag_parity_error_d = '0; - faulty_hit = '0; for (int i = 0; i < CFG.SET_COUNT; i++) begin line_hit[i] = tag_rdata[i][CFG.TAG_WIDTH+1] && tag_rdata[i][CFG.TAG_WIDTH-1:0] == required_tag; //check valid bit and tag errors[i] = tag_rdata[i][CFG.TAG_WIDTH] && line_hit[i]; //check error bit - if (RELIABILITY_MODE && line_hit[i]) begin - tag_parity_error_d[i] = (tag_rdata[i][CFG.TAG_WIDTH+2] == exp_tag_parity_bit_q) ? '0:'1; //check tag parity ^tag_rdata[i][CFG.TAG_WIDTH-1:0]) - end; - end - if (RELIABILITY_MODE) begin - tag_rsp_s.hit = |(line_hit & ~tag_parity_error_d); - faulty_hit = |(line_hit & tag_parity_error_d); //if correspondent bits are both one, there was a false hit - end else begin - tag_rsp_s.hit = |line_hit; end tag_rsp_s.error = |errors; end + if (RELIABILITY_MODE) begin + for (genvar i = 0; i < CFG.SET_COUNT; i++) begin : gen_check + assign tag_parity_error_d[i] = ((tag_rdata[i][CFG.TAG_WIDTH+2] == exp_tag_parity_bit_q)) ? '0:'1; //check both ways' parity bit with expected one + end + assign tag_rsp_s.hit = |(line_hit & ~tag_parity_error_d); + assign faulty_hit = |(line_hit & tag_parity_error_d); //if correspondent bits are both one, there was a false hit + end else begin + assign tag_rsp_s.hit = |line_hit; + end + lzc #(.WIDTH(CFG.SET_COUNT)) i_lzc ( .in_i ( line_hit ), .cnt_o ( tag_rsp_s.cset ), @@ -226,7 +223,7 @@ module snitch_icache_lookup_serial #( // Buffer the metadata on a valid handshake. Stall on write (implicit in req_valid/ready) `FFL(tag_req_q, tag_req_d, req_valid && req_ready, '0, clk_i, rst_ni) if(RELIABILITY_MODE) begin - `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) + `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) end `FF(tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0, clk_i, rst_ni) if(!RELIABILITY_MODE) begin @@ -297,15 +294,14 @@ module snitch_icache_lookup_serial #( localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; // Data bank port mux + + // Compute parity bit + if (RELIABILITY_MODE) begin + for (genvar i = 0; i < DATA_PARITY_WIDTH; i++) begin + assign data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ^write_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; + end + end always_comb begin - - // Compute parity bit - if (RELIABILITY_MODE) begin - for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin - data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ^write_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; - end - end - // Default read request data_addr = lookup_addr; data_enable = tag_valid && tag_rsp.hit; // Only read data on hit @@ -334,6 +330,7 @@ module snitch_icache_lookup_serial #( .rdata_o ( data_rdata ) ); + // Parity check logic [DATA_PARITY_WIDTH-1:0] data_parity_error; if (RELIABILITY_MODE) begin always_comb begin : p_parity_check @@ -341,9 +338,9 @@ module snitch_icache_lookup_serial #( for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin data_parity_error[DATA_PARITY_WIDTH-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT])? '0 : '1; end + faulty_data = |data_parity_error; end end - // Buffer the metadata on a valid handshake. Stall on write (implicit in tag_ready) `FFL(data_req_q, data_req_d, tag_valid && tag_ready, '0, clk_i, rst_ni) `FF(data_valid, (tag_valid && !data_write) ? 1'b1 : data_ready ? 1'b0 : data_valid, '0, clk_i, rst_ni) From d979936e192fd70dc69dcfed5551cd7693e90c8a Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 17 Oct 2023 18:12:57 +0200 Subject: [PATCH 06/46] [hardware] Started verification of detection mechanism, there are some problems with faulty_data signal --- .../snitch_icache_lookup_serial.sv | 79 +++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index e2709ba54..5770ca8b4 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -9,7 +9,7 @@ /// An actual cache lookup. module snitch_icache_lookup_serial #( parameter snitch_icache_pkg::config_t CFG = '0, - parameter logic RELIABILITY_MODE = '1 // Fault tolerance enabled setting it to 1 + parameter logic RELIABILITY_MODE = '0 // Fault tolerance enabled setting it to 1 )( input logic clk_i, input logic rst_ni, @@ -63,6 +63,23 @@ module snitch_icache_lookup_serial #( init_count_q <= '0; end + //clock to generate some periodic faults (To be deleted) + localparam int FAULT_CYCLE = 10; + logic [$clog2(FAULT_CYCLE):0] fault_count_q; + logic data_fault_inject, tag_fault_inject; + + // We are always ready to flush + assign data_fault_inject = fault_count_q == FAULT_CYCLE;//fault_count_q%11 == FAULT_CYCLE/10; //for some timing it works, but there are handshaking problems + assign tag_fault_inject = '0; //fault_count_q == FAULT_CYCLE; //hard to reproduce a faulty hit + // Initialization and flush FSM + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) + fault_count_q <= '0; + else + fault_count_q <= fault_count_q + 1; + end + + // -------------------------------------------------- // Tag stage // -------------------------------------------------- @@ -77,6 +94,11 @@ module snitch_icache_lookup_serial #( logic error; } tag_rsp_t; + typedef struct packed { + logic [CFG.FETCH_AW-1:0] addr; + logic [CFG.SET_ALIGN-1:0] cset; + } tag_inv_req_t; + logic req_valid, req_ready; logic req_handshake; @@ -93,24 +115,33 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; logic [CFG.SET_COUNT-1:0] tag_parity_error_d, tag_parity_error_q; - logic faulty_hit, faulty_data; + logic faulty_hit, faulty_data_d, faulty_data_q; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; + tag_inv_req_t tag_inv_req_d, tag_inv_req_q; + // Connect input requests to tag stage assign tag_req_d.addr = in_addr_i; assign tag_req_d.id = in_id_i; // Multiplex read and write access to the tag banks onto one port, prioritizing write accesses + + logic tag_parity_bit; if (RELIABILITY_MODE) begin - assign tag_wdata [CFG.TAG_WIDTH+2] = ^write_tag_i; + always_comb begin + tag_parity_bit = ^write_tag_i; + if (faulty_data_q || faulty_hit) begin + tag_parity_bit = 1'b1; //make sure also parity check fails, as the content of the tag is all zeros when invalid + end + tag_wdata [CFG.TAG_WIDTH+2] = tag_parity_bit; + end end - always_comb begin tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the tag (first 8 bits determine the line of the ) tag_enable = '0; - tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i}; + tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i} + tag_fault_inject; tag_write = 1'b0; write_ready_o = 1'b0; @@ -128,15 +159,20 @@ module snitch_icache_lookup_serial #( tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; + end else if (faulty_data_q && RELIABILITY_MODE) begin + //use data_req_q data to invalidate the address NO, it won't work + tag_addr = tag_inv_req_q.addr; + tag_enable = $unsigned(1 << tag_inv_req_q.cset); + tag_wdata[CFG.TAG_WIDTH+1:0] = '0; + tag_write = 1'b1; + write_ready_o = 1'b1; end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 - //in_ready_o = '0; //we do not accept new read request - // Request to store data in pipeline, zero as no new data - //req_valid = 1'b0; - - tag_addr = tag_req_q.addr; //buffered version of in_addr_i - tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) - tag_wdata[CFG.TAG_WIDTH+1:0] = '0; - tag_write = '1; + //we do not accept read requests and we do not store data in the pipeline. + tag_addr = tag_req_q.addr; //buffered version of in_addr_i + tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) + tag_wdata[CFG.TAG_WIDTH+1:0] = '0; + tag_write = 1'b1; + write_ready_o = 1'b1; end else if (in_valid_i) begin // Check cache @@ -305,7 +341,7 @@ module snitch_icache_lookup_serial #( // Default read request data_addr = lookup_addr; data_enable = tag_valid && tag_rsp.hit; // Only read data on hit - data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i; + data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i + data_fault_inject; data_write = 1'b0; // Write takes priority if (!init_phase && write_valid_i) begin @@ -338,7 +374,7 @@ module snitch_icache_lookup_serial #( for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin data_parity_error[DATA_PARITY_WIDTH-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT])? '0 : '1; end - faulty_data = |data_parity_error; + faulty_data_d = |data_parity_error; end end // Buffer the metadata on a valid handshake. Stall on write (implicit in tag_ready) @@ -356,11 +392,22 @@ module snitch_icache_lookup_serial #( `FFL(data_rsp_q, data_rdata, tag_handshake && !data_ready, '0, clk_i, rst_ni) assign out_data_o = tag_handshake ? data_rdata : data_rsp_q; + // Buffer the metadata when there is faulty data for the invalidation procedure + if (RELIABILITY_MODE) begin + assign tag_inv_req_d.addr = data_req_q.addr; + assign tag_inv_req_d.cset = data_req_q.id; + //`FF(faulty_data_q, faulty_data_d && tag_handshake, '0, clk_i, rst_ni); + assign faulty_data_q = faulty_data_d; + `FFL(tag_inv_req_q, tag_inv_req_d, faulty_data_q && tag_handshake, '0, clk_i, rst_ni) + end + + + // Generate the remaining output signals. assign out_addr_o = data_req_q.addr; assign out_id_o = data_req_q.id; assign out_set_o = data_req_q.cset; - assign out_hit_o = data_req_q.hit; + assign out_hit_o = data_req_q.hit && !(faulty_data_q && RELIABILITY_MODE); assign out_error_o = data_req_q.error; assign out_valid_o = data_valid; assign data_ready = out_ready_i; From a192ef7d5d278478e27d819025db6fbf44074c8b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Fri, 20 Oct 2023 18:47:32 +0200 Subject: [PATCH 07/46] [hardware] First version of invalidation handshaking implemented, still broken --- .../snitch_icache_lookup_serial.sv | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 5770ca8b4..89b77d998 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -9,7 +9,7 @@ /// An actual cache lookup. module snitch_icache_lookup_serial #( parameter snitch_icache_pkg::config_t CFG = '0, - parameter logic RELIABILITY_MODE = '0 // Fault tolerance enabled setting it to 1 + parameter logic RELIABILITY_MODE = '1 // Fault tolerance enabled setting it to 1 )( input logic clk_i, input logic rst_ni, @@ -97,6 +97,7 @@ module snitch_icache_lookup_serial #( typedef struct packed { logic [CFG.FETCH_AW-1:0] addr; logic [CFG.SET_ALIGN-1:0] cset; + logic parity_error; } tag_inv_req_t; logic req_valid, req_ready; @@ -115,12 +116,13 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; logic [CFG.SET_COUNT-1:0] tag_parity_error_d, tag_parity_error_q; - logic faulty_hit, faulty_data_d, faulty_data_q; + logic faulty_hit; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; - tag_inv_req_t tag_inv_req_d, tag_inv_req_q; + tag_inv_req_t data_parity_inv_d, data_parity_inv_q; + logic data_fault_valid, data_fault_ready; // Connect input requests to tag stage assign tag_req_d.addr = in_addr_i; @@ -132,21 +134,24 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin always_comb begin tag_parity_bit = ^write_tag_i; - if (faulty_data_q || faulty_hit) begin + if (data_fault_valid || faulty_hit) begin tag_parity_bit = 1'b1; //make sure also parity check fails, as the content of the tag is all zeros when invalid end tag_wdata [CFG.TAG_WIDTH+2] = tag_parity_bit; end end + assign data_fault_ready = !(write_valid_i || init_phase); + assign data_fault_valid = data_parity_inv_q.parity_error; always_comb begin tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the tag (first 8 bits determine the line of the ) tag_enable = '0; tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i} + tag_fault_inject; tag_write = 1'b0; - write_ready_o = 1'b0; - in_ready_o = 1'b0; - req_valid = 1'b0; + write_ready_o = 1'b0; + in_ready_o = 1'b0; + req_valid = 1'b0; + //data_fault_ready = 1'b0; if (init_phase) begin tag_addr = init_count_q; @@ -159,13 +164,14 @@ module snitch_icache_lookup_serial #( tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; - end else if (faulty_data_q && RELIABILITY_MODE) begin + end else if (data_parity_inv_q.parity_error && RELIABILITY_MODE) begin //use data_req_q data to invalidate the address NO, it won't work - tag_addr = tag_inv_req_q.addr; - tag_enable = $unsigned(1 << tag_inv_req_q.cset); + tag_addr = data_parity_inv_q.addr; + tag_enable = $unsigned(1 << data_parity_inv_q.cset); tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; write_ready_o = 1'b1; + //data_fault_ready = 1'b1; end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 //we do not accept read requests and we do not store data in the pipeline. tag_addr = tag_req_q.addr; //buffered version of in_addr_i @@ -173,7 +179,6 @@ module snitch_icache_lookup_serial #( tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; write_ready_o = 1'b1; - end else if (in_valid_i) begin // Check cache tag_enable = '1; @@ -276,7 +281,7 @@ module snitch_icache_lookup_serial #( // Fall-through buffer the tag data: Store the tag data if the SRAM bank accepted a request in // the previous cycle and if we actually have to buffer them because the receiver is not ready `FF(tag_rsp_q, tag_rsp_d, '0, clk_i, rst_ni) - assign tag_rsp = req_handshake ? tag_rsp_s : tag_rsp_q; + assign tag_rsp = req_handshake ? tag_rsp_s : tag_rsp_q; // At handshake take data directly from tag stage, select buffer always_comb begin tag_rsp_d = tag_rsp_q; // Load the FF if new data is incoming and downstream is not ready @@ -317,6 +322,7 @@ module snitch_icache_lookup_serial #( data_req_t data_req_d, data_req_q; data_rsp_t data_rsp_q; logic data_valid, data_ready; + logic data_fault; // Connect tag stage response to data stage request assign data_req_d.addr = tag_req_q.addr; @@ -374,7 +380,7 @@ module snitch_icache_lookup_serial #( for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin data_parity_error[DATA_PARITY_WIDTH-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT])? '0 : '1; end - faulty_data_d = |data_parity_error; + data_parity_inv_d.parity_error = |data_parity_error; end end // Buffer the metadata on a valid handshake. Stall on write (implicit in tag_ready) @@ -391,14 +397,15 @@ module snitch_icache_lookup_serial #( // the previous cycle and if we actually have to buffer them because the receiver is not ready `FFL(data_rsp_q, data_rdata, tag_handshake && !data_ready, '0, clk_i, rst_ni) assign out_data_o = tag_handshake ? data_rdata : data_rsp_q; + assign data_fault = RELIABILITY_MODE ? (tag_handshake ? data_parity_inv_d.parity_error : data_parity_inv_q.parity_error) : 1'b0; // Buffer the metadata when there is faulty data for the invalidation procedure if (RELIABILITY_MODE) begin - assign tag_inv_req_d.addr = data_req_q.addr; - assign tag_inv_req_d.cset = data_req_q.id; - //`FF(faulty_data_q, faulty_data_d && tag_handshake, '0, clk_i, rst_ni); - assign faulty_data_q = faulty_data_d; - `FFL(tag_inv_req_q, tag_inv_req_d, faulty_data_q && tag_handshake, '0, clk_i, rst_ni) + assign data_parity_inv_d.addr = data_req_q.addr; + assign data_parity_inv_d.cset = data_req_q.id; + + `FFL(data_parity_inv_q, data_fault_ready ? data_parity_inv_d : '0, data_valid, '0, clk_i, rst_ni); + end @@ -407,7 +414,7 @@ module snitch_icache_lookup_serial #( assign out_addr_o = data_req_q.addr; assign out_id_o = data_req_q.id; assign out_set_o = data_req_q.cset; - assign out_hit_o = data_req_q.hit && !(faulty_data_q && RELIABILITY_MODE); + assign out_hit_o = data_req_q.hit && !data_fault; assign out_error_o = data_req_q.error; assign out_valid_o = data_valid; assign data_ready = out_ready_i; From 12fd49a11579e94b01cf3eb2bf916df70852b2e3 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sat, 21 Oct 2023 20:32:33 +0200 Subject: [PATCH 08/46] [hardware] Fixed invalidation handshaking --- .../src/snitch_icache/snitch_icache_lookup_serial.sv | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 89b77d998..2ba2a4f7e 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -164,7 +164,7 @@ module snitch_icache_lookup_serial #( tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; - end else if (data_parity_inv_q.parity_error && RELIABILITY_MODE) begin + end else if (data_fault_valid && RELIABILITY_MODE) begin //use data_req_q data to invalidate the address NO, it won't work tag_addr = data_parity_inv_q.addr; tag_enable = $unsigned(1 << data_parity_inv_q.cset); @@ -172,6 +172,7 @@ module snitch_icache_lookup_serial #( tag_write = 1'b1; write_ready_o = 1'b1; //data_fault_ready = 1'b1; + $display("Invalidating address %h", tag_addr); end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 //we do not accept read requests and we do not store data in the pipeline. tag_addr = tag_req_q.addr; //buffered version of in_addr_i @@ -403,9 +404,7 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin assign data_parity_inv_d.addr = data_req_q.addr; assign data_parity_inv_d.cset = data_req_q.id; - - `FFL(data_parity_inv_q, data_fault_ready ? data_parity_inv_d : '0, data_valid, '0, clk_i, rst_ni); - + `FFL(data_parity_inv_q, data_fault_ready && tag_handshake ? data_parity_inv_d : '0, data_valid, '0, clk_i, rst_ni); end From 5723f731dbb55f3b641b9d06aa682d656c43e321 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 23 Oct 2023 08:28:48 +0200 Subject: [PATCH 09/46] [hardware] Modified handshaking for invalidation stage from data stage, fixed tag invalidation stage from tag stage --- .../snitch_icache_lookup_serial.sv | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 2ba2a4f7e..68e0b6dbe 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -64,11 +64,10 @@ module snitch_icache_lookup_serial #( end //clock to generate some periodic faults (To be deleted) - localparam int FAULT_CYCLE = 10; + localparam int FAULT_CYCLE = 8; logic [$clog2(FAULT_CYCLE):0] fault_count_q; logic data_fault_inject, tag_fault_inject; - // We are always ready to flush assign data_fault_inject = fault_count_q == FAULT_CYCLE;//fault_count_q%11 == FAULT_CYCLE/10; //for some timing it works, but there are handshaking problems assign tag_fault_inject = '0; //fault_count_q == FAULT_CYCLE; //hard to reproduce a faulty hit // Initialization and flush FSM @@ -116,7 +115,7 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; logic [CFG.SET_COUNT-1:0] tag_parity_error_d, tag_parity_error_q; - logic faulty_hit; + logic faulty_hit, faulty_hit_d, faulty_hit_q; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; @@ -134,13 +133,13 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin always_comb begin tag_parity_bit = ^write_tag_i; - if (data_fault_valid || faulty_hit) begin + if (data_fault_valid || faulty_hit_q) begin tag_parity_bit = 1'b1; //make sure also parity check fails, as the content of the tag is all zeros when invalid end tag_wdata [CFG.TAG_WIDTH+2] = tag_parity_bit; end end - assign data_fault_ready = !(write_valid_i || init_phase); + assign data_fault_valid = data_parity_inv_q.parity_error; always_comb begin tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the tag (first 8 bits determine the line of the ) @@ -151,28 +150,27 @@ module snitch_icache_lookup_serial #( write_ready_o = 1'b0; in_ready_o = 1'b0; req_valid = 1'b0; - //data_fault_ready = 1'b0; + data_fault_ready = 1'b0; if (init_phase) begin tag_addr = init_count_q; tag_enable = '1; tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; - end else if (write_valid_i) begin - // Write a refill request - tag_addr = write_addr_i; - tag_enable = $unsigned(1 << write_set_i); - tag_write = 1'b1; - write_ready_o = 1'b1; end else if (data_fault_valid && RELIABILITY_MODE) begin - //use data_req_q data to invalidate the address NO, it won't work tag_addr = data_parity_inv_q.addr; tag_enable = $unsigned(1 << data_parity_inv_q.cset); tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; write_ready_o = 1'b1; - //data_fault_ready = 1'b1; + data_fault_ready = 1'b1; $display("Invalidating address %h", tag_addr); + end else if (write_valid_i) begin + // Write a refill request + tag_addr = write_addr_i; + tag_enable = $unsigned(1 << write_set_i); + tag_write = 1'b1; + write_ready_o = 1'b1; end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 //we do not accept read requests and we do not store data in the pipeline. tag_addr = tag_req_q.addr; //buffered version of in_addr_i @@ -194,7 +192,7 @@ module snitch_icache_lookup_serial #( for (genvar i = 0; i < CFG.SET_COUNT; i++) begin : g_sets latch_scm #( .ADDR_WIDTH ($clog2(CFG.LINE_COUNT)), - .DATA_WIDTH (CFG.TAG_WIDTH+2 ) + .DATA_WIDTH (CFG.TAG_WIDTH+2+RELIABILITY_MODE) ) i_tag ( .clk ( clk_i ), .ReadEnable ( tag_enable[i] && !tag_write ), @@ -251,7 +249,7 @@ module snitch_icache_lookup_serial #( assign tag_parity_error_d[i] = ((tag_rdata[i][CFG.TAG_WIDTH+2] == exp_tag_parity_bit_q)) ? '0:'1; //check both ways' parity bit with expected one end assign tag_rsp_s.hit = |(line_hit & ~tag_parity_error_d); - assign faulty_hit = |(line_hit & tag_parity_error_d); //if correspondent bits are both one, there was a false hit + assign faulty_hit_d = |(line_hit & tag_parity_error_d); //if correspondent bits are both one, there was a false hit end else begin assign tag_rsp_s.hit = |line_hit; end @@ -265,8 +263,11 @@ module snitch_icache_lookup_serial #( // Buffer the metadata on a valid handshake. Stall on write (implicit in req_valid/ready) `FFL(tag_req_q, tag_req_d, req_valid && req_ready, '0, clk_i, rst_ni) if(RELIABILITY_MODE) begin - `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) + `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) + `FFL(faulty_hit_q, faulty_hit_d, req_valid && req_ready, '0, clk_i, rst_ni) end + assign faulty_hit = RELIABILITY_MODE ? ((req_valid && req_ready) ? faulty_hit_d : faulty_hit_q) : '0; //if there is a write request, select the buffered version to be invalidated + `FF(tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0, clk_i, rst_ni) if(!RELIABILITY_MODE) begin // Ready if buffer is empy or downstream is reading. Stall on write @@ -323,7 +324,7 @@ module snitch_icache_lookup_serial #( data_req_t data_req_d, data_req_q; data_rsp_t data_rsp_q; logic data_valid, data_ready; - logic data_fault; + logic hit_invalid, hit_invalid_q; // Connect tag stage response to data stage request assign data_req_d.addr = tag_req_q.addr; @@ -398,13 +399,14 @@ module snitch_icache_lookup_serial #( // the previous cycle and if we actually have to buffer them because the receiver is not ready `FFL(data_rsp_q, data_rdata, tag_handshake && !data_ready, '0, clk_i, rst_ni) assign out_data_o = tag_handshake ? data_rdata : data_rsp_q; - assign data_fault = RELIABILITY_MODE ? (tag_handshake ? data_parity_inv_d.parity_error : data_parity_inv_q.parity_error) : 1'b0; + assign hit_invalid = RELIABILITY_MODE ? (tag_handshake ? data_parity_inv_d.parity_error : hit_invalid_q) : 1'b0; // Buffer the metadata when there is faulty data for the invalidation procedure if (RELIABILITY_MODE) begin assign data_parity_inv_d.addr = data_req_q.addr; assign data_parity_inv_d.cset = data_req_q.id; - `FFL(data_parity_inv_q, data_fault_ready && tag_handshake ? data_parity_inv_d : '0, data_valid, '0, clk_i, rst_ni); + `FFL(data_parity_inv_q, data_fault_ready ? '0 : data_parity_inv_d, tag_handshake || data_fault_ready, '0, clk_i, rst_ni) + `FFL(hit_invalid_q, data_parity_inv_d.parity_error, tag_handshake, '0, clk_i, rst_ni) end @@ -413,7 +415,7 @@ module snitch_icache_lookup_serial #( assign out_addr_o = data_req_q.addr; assign out_id_o = data_req_q.id; assign out_set_o = data_req_q.cset; - assign out_hit_o = data_req_q.hit && !data_fault; + assign out_hit_o = data_req_q.hit && !hit_invalid; assign out_error_o = data_req_q.error; assign out_valid_o = data_valid; assign data_ready = out_ready_i; From 1acb39b240e478755ae524bc7a80751b3b2c35dd Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 23 Oct 2023 14:09:42 +0200 Subject: [PATCH 10/46] [hardware] Modified simple injection mechanism --- .../snitch/src/snitch_icache/snitch_icache_lookup_serial.sv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 68e0b6dbe..40c0fb16d 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -349,7 +349,8 @@ module snitch_icache_lookup_serial #( // Default read request data_addr = lookup_addr; data_enable = tag_valid && tag_rsp.hit; // Only read data on hit - data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i + data_fault_inject; + data_wdata[CFG.LINE_WIDTH -1:1] = write_data_i[CFG.LINE_WIDTH -1:1]; + data_wdata[0] = write_data_i[0] ^ data_fault_inject; data_write = 1'b0; // Write takes priority if (!init_phase && write_valid_i) begin From 00a2762005ae6bd360dc3a62aeec4c0e03468073 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 24 Oct 2023 11:37:37 +0200 Subject: [PATCH 11/46] [hardware] Added fault injection script and clock --- .../snitch_icache_lookup_serial.sv | 4 +- .../tb/fault_injection/ROC_inject_fault.tcl | 42 + .../tb/fault_injection/extract_nets.tcl | 216 +++++ .../tb/fault_injection/inject_fault.tcl | 802 ++++++++++++++++++ .../deps/snitch/tb/scripts/FI_sim_setup.tcl | 14 + .../tb/src/snitch_read_only_cache_tb.sv | 13 +- 6 files changed, 1087 insertions(+), 4 deletions(-) create mode 100644 hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl create mode 100644 hardware/deps/snitch/tb/fault_injection/extract_nets.tcl create mode 100644 hardware/deps/snitch/tb/fault_injection/inject_fault.tcl create mode 100644 hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 40c0fb16d..8cb1d120a 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -64,11 +64,11 @@ module snitch_icache_lookup_serial #( end //clock to generate some periodic faults (To be deleted) - localparam int FAULT_CYCLE = 8; + localparam int FAULT_CYCLE = 100; logic [$clog2(FAULT_CYCLE):0] fault_count_q; logic data_fault_inject, tag_fault_inject; - assign data_fault_inject = fault_count_q == FAULT_CYCLE;//fault_count_q%11 == FAULT_CYCLE/10; //for some timing it works, but there are handshaking problems + assign data_fault_inject = '0;//fault_count_q == FAULT_CYCLE;//fault_count_q%11 == FAULT_CYCLE/10; //for some timing it works, but there are handshaking problems assign tag_fault_inject = '0; //fault_count_q == FAULT_CYCLE; //hard to reproduce a faulty hit // Initialization and flush FSM always_ff @(posedge clk_i, negedge rst_ni) begin diff --git a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl new file mode 100644 index 000000000..45746332b --- /dev/null +++ b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl @@ -0,0 +1,42 @@ +transcript quietly + +set verbosity 1 +set log_injections 1 +set seed 12345 +set script_base_path "/scratch/sem23h18/project/mempool/hardware/deps/snitch/tb/fault_injection/" +set inject_start_time 500000ns +set inject_stop_time 0 +#use a clock in the tb +set injection_clock "/snitch_read_only_cache_tb/fault_clk" +set injection_clock_trigger 0 +set fault_period 10 +set rand_initial_injection_phase 0 +set max_num_fault_inject 1000 +set signal_fault_duration 20ns +set register_fault_duration 0ns + +set allow_multi_bit_upset 1 +set use_bitwidth_as_weight 1 +set check_core_output_modification 0 +set check_core_next_state_modification 0 +set reg_to_sig_ratio 1 + +proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} + + + +#set inject_register_netlist [find nets [base_path]/*] +#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram +#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram + +set inject_register_netlist { + /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram[*] +} +#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram + +set inject_signals_netlist [] +set output_netlist [] +set next_state_netlist [] +set assertion_disable_list [] + +source ${::script_base_path}inject_fault.tcl \ No newline at end of file diff --git a/hardware/deps/snitch/tb/fault_injection/extract_nets.tcl b/hardware/deps/snitch/tb/fault_injection/extract_nets.tcl new file mode 100644 index 000000000..24503aac4 --- /dev/null +++ b/hardware/deps/snitch/tb/fault_injection/extract_nets.tcl @@ -0,0 +1,216 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Author: Luca Rufer (lrufer@student.ethz.ch) + +# Description: This file provides some generic procs to extract next from a +# circuit. + +# ================================= Overview ================================== +# ----------------------- General Netlist utility procs ----------------------- +# 'get_net_type' : Get the type of a Net using the describe command. +# Example return type are "Register", "Net", "Enum", +# "Array", "Record" (struct), and others +# 'get_net_array_length' : Get the length of an Array using the describe +# command. +# 'get_net_reg_width' : Get the width (number of bits) of a Register or +# Net using the describe command +# 'get_record_field_names' : Get the names of all fiels of a Record (struct). +# ------------------------- Netlist Extraction procs -------------------------- +# 'get_state_netlist' : Example function for how to extract state nets. +# Non-recursive implementation. +# 'get_state_netlist_revursive' : Example function for how to extract state nets +# Recursive implementation. +# 'get_next_state_netlist' : Example function for how to extract next state +# nets. Non-recursive implementation. +# 'get_next_state_netlist_recursive' : Example function for how to extract +# next state nets. Recursive implementation. +# 'get_output_netlist' : Example function for how to extract output nets +# of a circuit. +# 'extract_netlists' : Given a list of nets obtained e.g. from the 'find' +# command, recursively extract signals until only +# signals of type "Register", "Net" and "Enum" +# remain. +# 'extract_all_nets_recursive_filtered' : Extract all nets from a circuit, +# filter them using the given patterns, and +# recursively extract them using 'extract_netlists'. + +################################## +# Net extraction utility procs # +################################## + +proc get_net_type {signal_name} { + set sig_description [examine -describe $signal_name] + set type_string [string trim [string range $sig_description 1 [string wordend $sig_description 1]] " \n\r()\[\]{}"] + if { $type_string == "Verilog" } { set type_string "Enum"} + return $type_string +} + +proc get_net_array_length {signal_name} { + set sig_description [examine -describe $signal_name] + regexp "\\\[length (\\d+)\\\]" $sig_description -> match + return $match +} + +proc get_net_reg_width {signal_name} { + set sig_description [examine -describe $signal_name] + set length 1 + if {[regexp "\\\[(\\d+):(\\d+)\\\]" $sig_description -> up_lim low_lim]} { + set length [expr $up_lim - $low_lim + 1] + } + return $length +} + +proc get_record_number_of_fields {signal_name} { + set sig_description [examine -describe $signal_name] + if {[regexp "^\{Record \\\[(\\d+) elements?\\\]" $sig_description -> num]} { + return $num + } else { + echo "\[Netlist Extraction Error\] Failed to extract the number of fields in record $signal_name." + return 0 + } +} + +proc get_record_field_names {signal_name} { + set sig_description [examine -describe $signal_name] + set matches [regexp -all -inline "\\n Element #\\d* \"\[a-zA-Z_\]\[a-zA-Z0-9_\]*\"" $sig_description] + set field_names [list] + foreach match $matches { lappend field_names [lindex [split $match \"] 1] } + set num_fields_expected [get_record_number_of_fields $signal_name] + if {[llength $field_names] != $num_fields_expected} { + echo "\[Netlist Extraction Error\] Could not determine the field names of signal $signal_name." + echo "Expected $num_fields_expected Fields, extracted [llength $field_names]: $field_names. Skipping this net..." + return [list] + } + return $field_names +} + +########################################### +# Recursevely extract all nets and enums # +########################################### + +# description: recursively extract all nets from the given 'item_list' by +# breaking them down into the most fundamental bit vectors. +# +# Set 'injection_save' to 1 to make sure only nets that can be +# injected are extracted. Questa SIM has a bug in the 'force' +# command: When trying to force a signal in an array inside a +# struct (Record), the force command will force the field at array +# index 0, and not the selected index. E.g. forcing a.b[7] will +# actually force a.b[0]. When 'injection_save' is set to 1, +# arrays inside structs are skipped from extraction, and a +# info message is generated if the verbosity is high enough +proc extract_netlists {item_list {injection_save 0}} { + set extract_list [list] + foreach item $item_list { + set item_type [get_net_type $item] + if {$item_type == "Register" || $item_type == "Net" || $item_type == "Enum"} { + lappend extract_list $item + } elseif { $item_type == "Array"} { + set array_length [get_net_array_length $item] + if {$injection_save && [string first "." $item] != -1} { + if { $::verbosity >= 3 } { + echo "\[Netlist Extraction\] Net $item is an array inside a struct and will be skipped." + } + } else { + for {set i 0} {$i < $array_length} {incr i} { + set new_net "$item\[$i\]" + set extract_list [concat $extract_list [extract_netlists $new_net $injection_save]] + } + } + } elseif { $item_type == "Record"} { + set fields [get_record_field_names $item] + foreach field $fields { + set new_net $item.$field + set extract_list [concat $extract_list [extract_netlists $new_net $injection_save]] + } + } elseif { $item_type == "int"} { + # Ignore + } else { + if { $::verbosity >= 2 } { + echo "\[Netlist Extraction\] Unknown Type $item_type of net $item. Skipping..." + } + } + } + return $extract_list +} + +#################### +# State Netlists # +#################### + +# Example proc on how to extract state nets (not recursive) +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_state_netlist {base_path} { + return [extract_netlists [find signal $base_path/*_q] 1] +} + +# Example proc on how to extract state nets. +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_state_netlist_revursive {base_path} { + return [extract_netlists [find signal -r $base_path/*_q] 1] +} + +##################### +# Next State Nets # +##################### + +# Example proc on how to extract next state nets (not recursive). +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_next_state_netlist {base_path} { + return [find signal $base_path/*_d] +} + +# Example proc on how to extract next state nets. +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_next_state_netlist_recursive {base_path} { + return [find signal -r $base_path/*_d] +} + +######################### +# Circuit Output Nets # +######################### + +proc get_output_netlist {base_path} { + return [find signal -out $base_path/*] +} + +################## +# Get all nets # +################## + +proc extract_all_nets_recursive_filtered {base_path filter_list} { + + # recursively extract all signals from the circuit + set netlist [find signal -r $base_path/*]; + + # filter and sort the signals + set netlist_filtered [list]; + foreach net $netlist { + set ignore_net 0 + # ignore any net that matches any ignore pattern + foreach ignore_pattern $filter_list { + if {[string match $ignore_pattern $net]} { + set ignore_net 1 + break + } + } + # add all nets that are not ignored + if {$ignore_net == 0} { + lappend netlist_filtered $net + } + } + + # sort the filtered nets alphabetically + set netlist_filtered [lsort -dictionary $netlist_filtered] + + # recursively extract all nets and enums from arrays and structs + set netlist_extracted [extract_netlists $netlist_filtered 1] + + return $netlist_extracted +} diff --git a/hardware/deps/snitch/tb/fault_injection/inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/inject_fault.tcl new file mode 100644 index 000000000..9b206b438 --- /dev/null +++ b/hardware/deps/snitch/tb/fault_injection/inject_fault.tcl @@ -0,0 +1,802 @@ +# Copyright 2021 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Author: Luca Rufer (lrufer@student.ethz.ch) + +# ============ List of variables that may be passed to this script ============ +# Any of these variables may not be changed while the fault injection script +# is running, unless noted otherwise. Changing any of the settings during +# runtime may result in undefined behaviour. +# ----------------------------------- General --------------------------------- +# 'verbosity' : Controls the amount of information printed during script +# execution. Possible values are: +# 0 : No statements at all +# 1 : Only important initializaion information +# 2 : Important information and occurences of bitflips +# (Recommended). Default +# 3 : All information that is possible +# 'log_injections' : Create a logfile of all injected faults, including +# timestamps, the absolute path of the flipped net, the +# value before the flip, the value after the flip and +# more. +# The logfile is named "fault_injection_.log". +# 0 : Disable logging (Default) +# 1 : Enable logging +# 'seed' : Set the seed for the number generator. Default: 12345 +# To reset the seed and start samping numbers from the +# start of the seed, it is in the responsibility of the +# user to call 'srand'. This is only done one in this +# script when it's sourced. +# 'print_statistics' : Print statistics about the fault injections at the end +# of the fault injection. Which statistics are printed +# also depends on other settings below. +# 0 : Don't print statistics +# 1 : Print statistics (Default) +# 'script_base_path' : Base path of all the scripts that are sourced here: +# Default is set to './' +# ------------------------------- Timing settings ----------------------------- +# 'inject_start_time' : Earliest time of the first fault injection. +# 'inject_stop_time' : Latest possible time for a fault injection. +# Set to 0 for no stop. +# 'injection_clock' : Absolute path to the net that is used as an injected +# trigger and clock. Can be a special trigger clock in +# the testbench, or the normal system clock. +# If 'injection_clock' is set to an empty string (""), +# this script will not perform periodical fault injection. +# 'injection_clock_trigger' : Signal value of 'injection_clock' that triggers +# the fault injection. If a normal clock of a rising edge +# triggered circuit is used as injection clock, it is +# recommended to set the trigger to '0', so injected +# flips can clearly be distinguished in the waveforms. +# 'fault_period' : Period of the fault injection in clock cycles of the +# injection clock. Set to 0 for only a single flip. +# 'rand_initial_injection_phase' : Set the phase relative to the 'fault_period' +# to a random initial value between 0 (inclusive) and +# 'fault_period' (exclusive). If multiple simulation +# with different seeds are performed, this option allows +# the injected faults to be evenly distributed accross +# the 'injection_clock' cycles. +# 0 : Disable random phase. The first fault injection +# is performed at the first injeciton clock trigger +# after the 'inject_start_time'. Default. +# 1 : Enable random phase. +# 'max_num_fault_inject' : Maximum number of faults to be injected. The number +# of faults injected may be lower than this if the +# simualtion finishes before, or if the 'inject_stop_time' +# is reached. If 'max_num_fault_inject' is set to 0, this +# setting is ignored (default). +# 'forced_injection_times' : Provide an explicit list of times when faults are +# to be injected into the simulation. If an empty +# 'forced_injection_signals' list is provided, the signals +# are selected at random according to the Flip settings. +# By default, this list is empty. +# Note that flips forced by this list are not bound +# by the 'inject_start_time' and 'inject_stop_time'. +# Flips forced by this list only count towards the +# 'max_num_fault_inject' limit if +# 'include_forced_inj_in_stats' is set to 1. +# 'forced_injection_signals' : Provide an explicit list of signals where faults +# are to be injected into the simulation at the +# 'forced_injection_times'. This list must have the same +# length as 'forced_injection_times'. Entries in the list +# must have the format {'signal_name' 'is_register}. +# For example: {{"tb/data_q" 1} {"tb/enable" 0}} +# If this list is empty, the signals to be injected at the +# 'forced_injection_times' are selected randomly according +# to the settings below (default). +# Note that listing enums, or signals with a width wider +# than one bit will case a random bit to be selected, +# which will alter the outcome of the periodic random +# fault injection. +# 'include_forced_inj_in_stats' : Select wether the forced injections should be +# included in the statistics or not. Including them in the +# statistics will also cause them to be logged (if logging +# is enabled) and variables like 'last_flipped_net' and +# 'last_injection_time' to be changed. +# 0: Don't include forced injections in statistics and +# logs (default). +# 1: Include forced injections in statistics and logs. +# 'signal_fault_duration' : Duration of faults injected into combinatorial +# signals, before the original value is restored. +# 'register_fault_duration' : Minumum duration of faults injected into +# registers. Faults injected into registers are not +# restored after the 'register_fault_duration' and will +# persist until overwritten by the circuit under test. +# -------------------------------- Flip settings ------------------------------ +# 'allow_multi_bit_upset' : Allow injecting another error in a Register that was +# already flipped and not driven to another value yet. +# 0 : Disable multi bit upsets (default) +# 1 : Enable multi bit upsets +# 'use_bitwidth_as_weight' : Use the bit width of a net as a weight for the +# random fault injection net selection. If this option +# is enabled, a N-bit net has an N times higher chance +# than a 1-bit net of being selected for fault injection. +# 0 : Disable using the bitwidth as weight and give every +# net the same chance of being picked (Default). +# 1 : Enable using the bit width of nets as weights. +# 'check_core_output_modification' : Check if an injected fault changes the +# output of the circuit under test. All nets in +# 'output_netlist' are checked. The result of the check +# is printed after every flip (if verbosity high enough), +# and logged to the logfile. +# 0 : Disable output modification checks. The check will +# be logged as 'x'. +# 1 : Enable output modification checks. +# 'check_core_next_state_modification' : Check if an injected fault changes the +# next state of the circuit under test. All nets in +# 'next_state_netlist' are checked. The result of the +# check is printed after every flip (if verbosity high +# enough), and logged to the logfile. +# 0 : Disable next state modification checks. The check +# will be logged as 'x'. +# 1 : Enable next state modification checks. +# 'reg_to_sig_ratio' : Ratio of Registers to combinatorial signals to be +# selected for a fault injection. Example: A value of 4 +# selects a ratio of 4:1, giving an 80% for a Register to +# be selected, and a 20% change of a combinatorial signal +# to be selected. If the provided +# 'inject_register_netlist' is empty, or the +# 'inject_signals_netlist' is empty, this parameter is +# ignored and nets are only selected from the non-empty +# netlist. +# Default value is 1, so the default ratio is 1:1. +# ---------------------------------- Netlists --------------------------------- +# 'inject_register_netlist' : List of absolute paths to Registers to be flipped +# in the simulation. This is used to simulate Single +# Event Upsets (SEUs). Flips injected in registers are not +# removed by the injection script. If the inject netlist +# is changed after this script was first called, the proc +# 'updated_inject_netlist' must be called. +# 'inject_signals_netlist' : List of absolute paths to combinatorial signals to +# be flipped in the simulation. This is used to simulate +# Single Event Transients (SETs). A fault injection +# drives the target signal for a 'fault_duration', and +# afterwards returns the signal to its original state. +# If the inject netlist is changed after this script was +# first called, the proc 'updated_inject_netlist' must be +# called. +# 'output_netlist' : List of absolute net or register paths to be used for +# the output modification check. +# 'next_state_netlist' : List of absolute net or register paths to be used for +# the next state modification check. +# 'assertion_disable_list' : List of absolute paths to named assertions that +# need to be disabled for during fault injecton. +# Assertions are enabled again after the simulation stop +# time. + +################################## +# Set default parameter values # +################################## + +# General +if {![info exists verbosity]} { set verbosity 2 } +if {![info exists log_injections]} { set log_injections 0 } +if {![info exists seed]} { set seed 12345 } +if {![info exists print_statistics]} { set print_statistics 1 } +if {![info exists script_base_path]} { set script_base_path "./" } +# Timing settings +if {![info exists inject_start_time]} { set inject_start_time 100ns } +if {![info exists inject_stop_time]} { set inject_stop_time 0 } +if {![info exists injection_clock]} { set injection_clock "clk" } +if {![info exists injection_clock_trigger]} { set injection_clock_trigger 0 } +if {![info exists fault_period]} { set fault_period 0 } +if {![info exists rand_initial_injection_phase]} { set rand_initial_injection_phase 0 } +if {![info exists max_num_fault_inject]} { set max_num_fault_inject 0 } +if {![info exists forced_injection_times]} { set forced_injection_times [list] } +if {![info exists forced_injection_signals]} { set forced_injection_signals [list] } +if {![info exists include_forced_inj_in_stats]} { set include_forced_inj_in_stats 0 } +if {![info exists signal_fault_duration]} { set signal_fault_duration 1ns } +if {![info exists register_fault_duration]} { set register_fault_duration 0ns } +# Flip settings +if {![info exists allow_multi_bit_upset]} { set allow_multi_bit_upset 0 } +if {![info exists check_core_output_modification]} { set check_core_output_modification 0 } +if {![info exists check_core_next_state_modification]} { set check_core_next_state_modification 0 } +if {![info exists reg_to_sig_ratio]} { set reg_to_sig_ratio 1 } +if {![info exists use_bitwidth_as_weight]} { set use_bitwidth_as_weight 0 } +# Netlists +if {![info exists inject_register_netlist]} { set inject_register_netlist [list] } +if {![info exists inject_signals_netlist]} { set inject_signals_netlist [list] } +if {![info exists output_netlist]} { set output_netlist [list] } +if {![info exists next_state_netlist]} { set next_state_netlist [list] } +if {![info exists assertion_disable_list]} { set assertion_disable_list [list] } + +# Additional checks +if {[llength $forced_injection_times] != [llength $forced_injection_signals] && \ + [llength $forced_injection_times] != 0 && \ + [llength $forced_injection_signals] != 0} { + if {$::verbosity >= 1} { + echo "\[Fault Injection\] Error: 'forced_injection_times' and \ + 'forced_injection_signals' don't have the same non-zero length!" + } + exit +} + +# Source generic netlist extraction procs +source [subst ${::script_base_path}extract_nets.tcl] + +######################################## +# Finish setup depending on settings # +######################################## + +# Common path sections of all nets where errors can be injected +set ::netlist_common_path_sections [list] + +proc restart_fault_injection {} { + # Start the Error injection script + if {$::verbosity >= 1} { + echo "\[Fault Injection\] Info: Injection script running." + } + + # cleanup from previous runs + if {[info exists ::forced_injection_when_labels]} { + foreach l $::forced_injection_when_labels { + catch {nowhen $l} + } + } + + # Last net that was flipped + set ::last_flipped_net "" + set ::last_injection_time -1 + + # Open the log file + if {$::log_injections} { + set time_stamp [exec date +%Y%m%d_%H%M%S] + set ::injection_log [open "fault_injection_$time_stamp.log" w+] + puts $::injection_log "timestamp,netname,pre_flip_value,post_flip_value,output_changed,new_state_changed" + } else { + set ::injection_log "" + } + + # Dictionary to keep track of injections + set ::inject_dict [dict create] + + # determine the phase for the initial fault injection + if {$::rand_initial_injection_phase} { + set ::prescaler [expr int(rand() * $::fault_period)] + } else { + set ::prescaler [expr $::fault_period - 1] + } + + # List of when-statement labels of forced injection times + set ::forced_injection_when_labels [list] + + # determine the first injection time + set start_time [earliest_time [concat $::forced_injection_times $::inject_start_time]] + + # determine the injection stop time + if {$::inject_stop_time == 0 && ![string equal $::injection_clock ""]} { + set stop_time 0 + } else { + set stop_time [latest_time [concat $::forced_injection_times $::inject_stop_time]] + } + + # Create all When statements + + # start fault injection + when -label inject_start "\$now == @$start_time" { + ::start_fault_injection + nowhen inject_start + } + + # periodically inject faults + if {![string equal $::injection_clock ""]} { + when -label inject_fault "\$now >= @$::inject_start_time and $::injection_clock == $::injection_clock_trigger" { + ::inject_trigger + } + } + + # forced injection times + for {set i 0} { $i < [llength $::forced_injection_times] } { incr i } { + set label "forced_injection_$i" + set t [lindex $::forced_injection_times $i] + if {[llength $::forced_injection_signals] == 0} { + set cmd "::inject_fault $::include_forced_inj_in_stats" + } else { + # Extract the signal infos + set signal_info [lindex $::forced_injection_signals $i] + foreach {signal_name is_register} $signal_info {} + if {$::include_forced_inj_in_stats} { + set cmd "fault_injection_pre_flip_statistics; \ + set flip_return \[::flipbit $signal_name $is_register\]; \ + fault_injection_post_flip_statistics $signal_name \$flip_return" + } else { + set cmd "::flipbit $signal_name $is_register" + } + } + # Create the when statement to flip the bit + when -label $label "\$now == @$t" "$cmd" + # Store the label + lappend ::forced_injection_when_labels $label + } + + # stop the simulation and output statistics + if {$stop_time != 0} { + when -label inject_stop "\$now > @$stop_time" { + ::stop_fault_injection + nowhen inject_stop + } + } +} + +proc start_fault_injection {} { + if {$::verbosity >= 1} { + echo "[time_ns $::now]: \[Fault Injection\] Starting fault injection." + } + # Disable Assertions + foreach assertion $::assertion_disable_list { + assertion enable -off $assertion + } + # Reset statistics + set ::stat_num_bitflips 0 + set ::stat_num_outputs_changed 0 + set ::stat_num_state_changed 0 + set ::stat_num_flip_propagated 0 +} + +################ +# User Procs # +################ + +proc stop_fault_injection {} { + # Stop fault injection + catch {nowhen inject_fault} + # Enable Assertions again + foreach assertion $::assertion_disable_list { + assertion enable -on $assertion + } + # Output simulation Statistics + if {$::verbosity >= 1 && $::print_statistics} { + echo " ========== Fault Injection Statistics ========== " + echo " Number of Bitflips : $::stat_num_bitflips" + if {$::check_core_output_modification} { + echo " Number of Bitflips propagated to outputs : $::stat_num_outputs_changed" + } + if {$::check_core_next_state_modification} { + echo " Number of Bitflips propagated to new state : $::stat_num_state_changed" + } + if {$::check_core_output_modification && $::check_core_next_state_modification} { + echo " Number of Bitflips propagated : $::stat_num_flip_propagated" + } + echo "" + } + # Close the logfile + if {$::log_injections} { + close $::injection_log + } + return $::stat_num_bitflips +} + +####################### +# Helper Procedures # +####################### + +proc earliest_time {time_list} { + if {[llength $time_list] == 0} { + return 0 + } + set earliest [lindex $time_list 0] + foreach t $time_list { + if {[ltTime $t $earliest]} { + set earliest $t + } + } + return $earliest +} + +proc latest_time {time_list} { + if {[llength $time_list] == 0} { + return -1 + } + set latest [lindex $time_list 0] + foreach t $time_list { + if {[gtTime $t $latest]} { + set latest $t + } + } + return $latest +} + +proc time_ns {time_ps} { + set time_str "" + append time_str "[expr $time_ps / 1000]" + set remainder [expr $time_ps % 1000] + if {$remainder != 0} { + append time_str "." + if {$remainder < 100} {append time_str "0"} + if {$remainder < 10} {append time_str "0"} + append time_str "$remainder" + } + append time_str " ns" + return $time_str +} + +proc find_common_path_sections {netlist} { + # Safety check if the list has any elements + if {[llength $netlist] == 0} { + return [list] + } + # Extract the first net as reference + set first_net [lindex $netlist 0] + set first_net_sections [split $first_net "/"] + # Determine the minimal number of sections in the netlist + set min_num_sections 9999 + foreach net $netlist { + set cur_path_sections [split $net "/"] + set num_sections [llength $cur_path_sections] + if {$num_sections < $min_num_sections} {set min_num_sections $num_sections} + } + # Create a match list + set match_list [list] + for {set i 0} {$i < $min_num_sections} {incr i} {lappend match_list 1} + # Test for every net which sections in its path matches the first net path + foreach net $netlist { + set cur_path_sections [split $net "/"] + # Test every section + for {set i 0} {$i < $min_num_sections} {incr i} { + # prevent redundant checking for speedup + if {[lindex $match_list $i] != 0} { + # check if the sections matches the first net section + if {[lindex $first_net_sections $i] != [lindex $cur_path_sections $i]} { + lset match_list $i 0 + } + } + } + } + return $match_list +} + +proc net_print_str {net_name} { + # Check if the list exists + if {[llength $::netlist_common_path_sections] == 0} { + return $net_name + } + # Split the netname path + set cur_path_sections [split $net_name "/"] + set print_str "" + set printed_dots 0 + # check sections individually + for {set i 0} {$i < [llength $cur_path_sections]} {incr i} { + # check if the section at the current index is a common to all paths + if {$i < [llength $::netlist_common_path_sections] && [lindex $::netlist_common_path_sections $i] == 1} { + # Do not print the dots if multiple sections match in sequence + if {!$printed_dots} { + # Print dots to indicate the path was shortened + append print_str "\[...\]" + if {$i != [llength $cur_path_sections] - 1} {append print_str "/"} + set printed_dots 1 + } + } else { + # Sections don't match, print the path section + append print_str "[lindex $cur_path_sections $i]" + if {$i != [llength $cur_path_sections] - 1} {append print_str "/"} + set printed_dots 0 + } + } + return $print_str +} + +proc calculate_weight_by_width {netlist} { + set total_weight 0 + set group_weight_dict [dict create] + set group_net_dict [dict create] + foreach net $netlist { + # determine the width of a net (used as weight) + set width [get_net_reg_width $net] + if {![dict exists $group_weight_dict $width]} { + # New width discovered, add new entry + dict set group_weight_dict $width $width + dict set group_net_dict $width [list $net] + } else { + dict incr group_weight_dict $width $width + dict lappend group_net_dict $width $net + } + } + # Sum weights of all groups + foreach group_weight [dict values $group_weight_dict] { + set total_weight [expr $total_weight + $group_weight] + } + return [list $total_weight $group_weight_dict $group_net_dict] +} + +proc updated_inject_netlist {} { + # print how many nets were found + set num_reg_nets [llength $::inject_register_netlist] + set num_comb_nets [llength $::inject_signals_netlist] + if {$::verbosity >= 1} { + echo "\[Fault Injection\] Selected $num_reg_nets Registers for fault injection." + echo "\[Fault Injection\] Selected $num_comb_nets combinatorial Signals for fault injection." + } + # print all nets that were found + if {$::verbosity >= 3} { + echo "Registers: " + foreach net $::inject_register_netlist { + echo " - [get_net_reg_width $net]-bit [get_net_type $net] : $net" + } + echo "Combinatorial Signals: " + foreach net $::inject_signals_netlist { + echo " - [get_net_reg_width $net]-bit [get_net_type $net] : $net" + } + echo "" + } + # determine the common sections + set combined_inject_netlist [concat $::inject_register_netlist $::inject_signals_netlist] + set ::netlist_common_path_sections [find_common_path_sections $combined_inject_netlist] + # determine the distribution of the nets + if {$::use_bitwidth_as_weight} { + set ::inject_register_distibrution_info [calculate_weight_by_width $::inject_register_netlist] + set ::inject_signals_distibrution_info [calculate_weight_by_width $::inject_signals_netlist] + } +} + +########################## +# Random Net Selection # +########################## + +proc select_random_net {} { + # Choose between Register and Signal + if {[llength $::inject_register_netlist] != 0 && \ + ([llength $::inject_signals_netlist] == 0 || \ + rand() * ($::reg_to_sig_ratio + 1) >= 1)} { + set is_register 1 + set selected_list $::inject_register_netlist + } else { + set is_register 0 + set selected_list $::inject_signals_netlist + } + # Select the distribution + if {$::use_bitwidth_as_weight} { + # select the distribution + if {$is_register} { + set distibrution_info $::inject_register_distibrution_info + } else { + set distibrution_info $::inject_signals_distibrution_info + } + # unpack the distribution + set distribution_total_weight [lindex $distibrution_info 0] + set distribution_weight_dict [lindex $distibrution_info 1] + set distribution_net_dict [lindex $distibrution_info 2] + # determine the group + set selec [expr rand() * $distribution_total_weight] + dict for {group group_weight} $distribution_weight_dict { + if {$selec <= $group_weight} { + break + } else { + set selec [expr $selec - $group_weight] + } + } + set selected_list [dict get $distribution_net_dict $group] + } + set idx [expr int(rand()*[llength $selected_list])] + set selected_net [lindex $selected_list $idx] + return [list $selected_net $is_register] +} + +################ +# Flip a Bit # +################ + +# flip a spefific bit of the given net name. returns a 1 if the bit could be flipped +proc flipbit {signal_name is_register} { + set success 0 + set old_value [examine -radixenumsymbolic $signal_name] + # check if net is an enum + if {[examine -radixenumnumeric $signal_name] != [examine -radixenumsymbolic $signal_name]} { + set old_value_numeric [examine -radix binary,enumnumeric $signal_name] + set new_value_numeric [expr int(rand()*([expr 2 ** [string length $old_value_numeric]]))] + while {$old_value_numeric == $new_value_numeric && [string length $old_value_numeric] != 1} { + set new_value_numeric [expr int(rand()*([expr 2 ** [string length $old_value_numeric]]))] + } + if {$is_register} { + force -freeze $signal_name $new_value_numeric -cancel $::register_fault_duration + } else { + force -freeze $signal_name $new_value_numeric, $old_value_numeric $::signal_fault_duration -cancel $::signal_fault_duration + } + set success 1 + } else { + set flip_signal_name $signal_name + set bin_val [examine -radix binary $signal_name] + set len [string length $bin_val] + set flip_index 0 + if {$len != 1} { + set flip_index [expr int(rand()*$len)] + set flip_signal_name $signal_name\($flip_index\) + } + set old_bit_value "0" + set new_bit_value "1" + if {[string index $bin_val [expr $len - 1 - $flip_index]] == "1"} { + set new_bit_value "0" + set old_bit_value "1" + } + if {$is_register} { + force -freeze $flip_signal_name $new_bit_value -cancel $::register_fault_duration + } else { + force -freeze $flip_signal_name $new_bit_value, $old_bit_value $::signal_fault_duration -cancel $::signal_fault_duration + } + if {[examine -radix binary $signal_name] != $bin_val} {set success 1} + } + set new_value [examine -radixenumsymbolic $signal_name] + set result [list $success $old_value $new_value] + return $result +} + +################################ +# Fault Injection Statistics # +################################ + +proc fault_injection_pre_flip_statistics {} { + # record the output before the flip + set ::pre_flip_out_val [list] + if {$::check_core_output_modification} { + foreach net $::output_netlist { + lappend ::pre_flip_out_val [examine $net] + } + } + # record the new state before the flip + set ::pre_flip_next_state_val [list] + if {$::check_core_next_state_modification} { + foreach net $::next_state_netlist { + lappend ::pre_flip_next_state_val [examine $net] + } + } +} + +proc fault_injection_post_flip_statistics {flipped_net flip_return} { + incr ::stat_num_bitflips + set ::last_flipped_net $flipped_net + set ::last_injection_time $::now + + set flip_propagated 0 + # record the output after the flip + set post_flip_out_val [list] + if {$::check_core_output_modification} { + foreach net $::output_netlist { + lappend post_flip_out_val [examine $net] + } + # check if the output changed + set output_state "not modified" + set output_changed [expr ![string equal $::pre_flip_out_val $post_flip_out_val]] + if {$output_changed} { + set output_state "changed" + incr ::stat_num_outputs_changed + set flip_propagated 1 + } + } else { + set output_changed "x" + } + # record the new state before the flip + set post_flip_next_state_val [list] + if {$::check_core_next_state_modification} { + foreach net $::next_state_netlist { + lappend post_flip_next_state_val [examine $net] + } + # check if the new state changed + set new_state_state "not modified" + set new_state_changed [expr ![string equal $::pre_flip_next_state_val $post_flip_next_state_val]] + if {$new_state_changed} { + set new_state_state "changed" + incr ::stat_num_state_changed + set flip_propagated 1 + } + } else { + set new_state_changed "x" + } + + if {$flip_propagated} { + incr ::stat_num_flip_propagated + } + # display the result + if {$::verbosity >= 2} { + set print_str "[time_ns $::now]: \[Fault Injection\] " + append print_str "Flipped net [net_print_str $flipped_net] from [lindex $flip_return 1] to [lindex $flip_return 2]. " + if {$::check_core_output_modification} { + append print_str "Output signals $output_state. " + } + if {$::check_core_next_state_modification} { + append print_str "New state $new_state_state. " + } + echo $print_str + } + # Log the result + if {$::log_injections} { + puts $::injection_log "$::now,$flipped_net,[lindex $flip_return 1],[lindex $flip_return 2],$output_changed,$new_state_changed" + flush $::injection_log + } +} + +############################## +# Fault injection routine # +############################## + +proc inject_fault {include_in_statistics} { + # If enabled, prepare the statistics for the flip + if {$include_in_statistics} { fault_injection_pre_flip_statistics } + + # Questa currently has a bug that it won't force certain nets. So we retry + # until we successfully flip a net. + # The bug primarily affects arrays of structs: + # If you try to force a member/field of a struct in an array, QuestaSim will + # flip force that member/field in the struct/record with index 0 in the + # array, not at the array index that was specified. + set success 0 + set attempts 0 + while {!$success && [incr attempts] < 50} { + # get a random net + set net_selc_info [::select_random_net] + set net_to_flip [lindex $net_selc_info 0] + set is_register [lindex $net_selc_info 1] + # Check if the selected net is allowed to be flipped + set allow_flip 1 + if {$is_register && !$::allow_multi_bit_upset} { + set net_value [examine -radixenumsymbolic $net_to_flip] + if {[dict exists $::inject_dict $net_to_flip] && [dict get $::inject_dict $net_to_flip] == $net_value} { + set allow_flip 0 + if {$::verbosity >= 3} { + echo "[time_ns $::now]: \[Fault Injection\] Tried to flip [net_print_str $net_to_flip], but was already flipped." + } + } + } + # flip the random net + if {$allow_flip} { + if {[catch {set flip_return [::flipbit $net_to_flip $is_register]}]} { + set flip_return {0 "x" "x"} + } + if {[lindex $flip_return 0]} { + set success 1 + if {$is_register && !$::allow_multi_bit_upset} { + # save the new value to the dict + dict set ::inject_dict $net_to_flip [examine -radixenumsymbolic $net_to_flip] + } + } else { + if {$::verbosity >= 3} { + echo "[time_ns $::now]: \[Fault Injection\] Failed to flip [net_print_str $net_to_flip]. Choosing another one." + } + } + } + } + if {$success && !$include_in_statistics} { + if {$::verbosity >= 2} { + echo "[time_ns $::now]: \[Fault Injection\] \ + Flipped net [net_print_str $net_to_flip] \ + from [lindex $flip_return 1] \ + to [lindex $flip_return 2]. " + } + } + if {$success && $include_in_statistics} { + fault_injection_post_flip_statistics $net_to_flip $flip_return + } +} + +proc ::inject_trigger {} { + # check if any nets are selected for injection + if {[llength $::inject_register_netlist] == 0 && \ + [llength $::inject_signals_netlist] == 0} { + return + } + # check if we reached the injection limit + if {($::max_num_fault_inject != 0) && ($::stat_num_bitflips >= $::max_num_fault_inject)} { + # Stop the simulation + if {$::verbosity >= 2} { + echo "\[Fault Injection\] Injection limit ($::max_num_fault_inject) reached. Stopping error injection..." + } + # Disable the trigger (if not already done so) + catch {nowhen inject_fault} + return + } + # increase prescaler + incr ::prescaler + if {$::prescaler == $::fault_period} { + set ::prescaler 0 + # inject a fault + ::inject_fault 1 + } +} + +# Set the seed for the first time +expr srand($::seed) + +# Update the inject netlist +updated_inject_netlist + +# Reset the fault injection +restart_fault_injection diff --git a/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl b/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl new file mode 100644 index 000000000..efb189279 --- /dev/null +++ b/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl @@ -0,0 +1,14 @@ +do compile.tcl + +vopt snitch_read_only_cache_tb -o snitch_read_only_cache_tb_opt +acc + +vsim snitch_read_only_cache_tb_opt -voptargs=+acc + +# waves +add wave -noupdate -expand -group lookup /snitch_read_only_cache_tb/dut/i_lookup/* +add wave -noupdate -group handler /snitch_read_only_cache_tb/dut/i_handler/* +add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* + +do ../fault_injection/ROC_inject_fault.tcl + +#run -all \ No newline at end of file diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv index dcb052a54..54293dc6b 100644 --- a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -278,7 +278,8 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( parameter int unsigned AxiIdWidth = 5, parameter int unsigned LineWidth = debug? 64:256, parameter int unsigned LineCount = debug? 32:128, - parameter int unsigned SetCount = 2 + parameter int unsigned SetCount = 2, + parameter int unsigned FaultFreq = 10 ); localparam time ClkPeriod = 10ns; @@ -312,7 +313,7 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( // backing memory logic [LineWidth-1:0] memory [logic [AxiAddrWidth-1:0]]; - logic clk, rst; + logic clk, rst, fault_clk; typedef semirand_axi_master #( .AW ( AxiAddrWidth ), @@ -696,4 +697,12 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( #(ClkPeriod/2) clk = 1; end end + + // Fault injection clock generation + initial begin + forever begin + #(ClkPeriod/2*FaultFreq) fault_clk = 0; + #(ClkPeriod/2*FaultFreq) fault_clk = 1; + end + end endmodule From 10ba933fe2e479e3e547746b3d9f2c2c869c4502 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 29 Oct 2023 16:11:19 +0100 Subject: [PATCH 12/46] [hardware] Fixed error in invalidation stage tag address, added debug features --- .../snitch_icache_lookup_serial.sv | 65 +++++++++---------- .../tb/fault_injection/ROC_inject_fault.tcl | 4 +- .../deps/snitch/tb/scripts/FI_sim_setup.tcl | 1 + .../tb/src/snitch_read_only_cache_tb.sv | 15 +++-- .../questa/Mempool_fault_injection.tcl | 44 +++++++++++++ 5 files changed, 87 insertions(+), 42 deletions(-) create mode 100644 hardware/scripts/questa/Mempool_fault_injection.tcl diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 8cb1d120a..cb645bbb0 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -63,22 +63,6 @@ module snitch_icache_lookup_serial #( init_count_q <= '0; end - //clock to generate some periodic faults (To be deleted) - localparam int FAULT_CYCLE = 100; - logic [$clog2(FAULT_CYCLE):0] fault_count_q; - logic data_fault_inject, tag_fault_inject; - - assign data_fault_inject = '0;//fault_count_q == FAULT_CYCLE;//fault_count_q%11 == FAULT_CYCLE/10; //for some timing it works, but there are handshaking problems - assign tag_fault_inject = '0; //fault_count_q == FAULT_CYCLE; //hard to reproduce a faulty hit - // Initialization and flush FSM - always_ff @(posedge clk_i, negedge rst_ni) begin - if (!rst_ni) - fault_count_q <= '0; - else - fault_count_q <= fault_count_q + 1; - end - - // -------------------------------------------------- // Tag stage // -------------------------------------------------- @@ -133,8 +117,16 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin always_comb begin tag_parity_bit = ^write_tag_i; - if (data_fault_valid || faulty_hit_q) begin - tag_parity_bit = 1'b1; //make sure also parity check fails, as the content of the tag is all zeros when invalid + if (init_phase) begin + tag_parity_bit = 1'b0; + end else if (data_fault_valid) begin + tag_parity_bit = 1'b1; + end else if (write_valid_i) begin + tag_parity_bit = ^write_tag_i; + end else if (faulty_hit) begin + tag_parity_bit = 1'b1; + end else if (in_valid_i) begin + // read phase: write tag not used end tag_wdata [CFG.TAG_WIDTH+2] = tag_parity_bit; end @@ -142,9 +134,9 @@ module snitch_icache_lookup_serial #( assign data_fault_valid = data_parity_inv_q.parity_error; always_comb begin - tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the tag (first 8 bits determine the line of the ) + tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the index tag_enable = '0; - tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i} + tag_fault_inject; + tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i}; tag_write = 1'b0; write_ready_o = 1'b0; @@ -158,13 +150,12 @@ module snitch_icache_lookup_serial #( tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; end else if (data_fault_valid && RELIABILITY_MODE) begin - tag_addr = data_parity_inv_q.addr; + tag_addr = data_parity_inv_q.addr >> CFG.LINE_ALIGN; tag_enable = $unsigned(1 << data_parity_inv_q.cset); tag_wdata[CFG.TAG_WIDTH+1:0] = '0; - tag_write = 1'b1; - write_ready_o = 1'b1; + tag_write = 1'b1; data_fault_ready = 1'b1; - $display("Invalidating address %h", tag_addr); + //$display("%t [ROcache_lookup]: DataFault -> Invalidating address %h", $time, data_parity_inv_q.addr); end else if (write_valid_i) begin // Write a refill request tag_addr = write_addr_i; @@ -173,11 +164,11 @@ module snitch_icache_lookup_serial #( write_ready_o = 1'b1; end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 //we do not accept read requests and we do not store data in the pipeline. - tag_addr = tag_req_q.addr; //buffered version of in_addr_i + tag_addr = tag_req_q.addr >> CFG.LINE_ALIGN; //buffered version of in_addr_i tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; - write_ready_o = 1'b1; + if (clk_i == '0) $display("[ROcache_lookup]: TagFault -> Invalidating address %h", tag_req_q.addr); end else if (in_valid_i) begin // Check cache tag_enable = '1; @@ -186,6 +177,11 @@ module snitch_icache_lookup_serial #( req_valid = 1'b1; end end + always @ (posedge clk_i) begin + if(data_fault_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); + else if(faulty_hit && RELIABILITY_MODE) $display("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); + //if(out_valid_o && out_ready_i && out_data_o == '0) $display("(%t) [ROcache_lookup]: ReadData at Address %h (idx %h) is %h", $time, out_addr_o, out_addr_o[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], out_data_o); + end // Instantiate the tag sets. if (CFG.L1_TAG_SCM) begin : gen_scm @@ -270,11 +266,11 @@ module snitch_icache_lookup_serial #( `FF(tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0, clk_i, rst_ni) if(!RELIABILITY_MODE) begin - // Ready if buffer is empy or downstream is reading. Stall on write + // Ready if buffer is empy or downstream is reading. Stall on write (and data invalidation not needed as it is included in req_valid) assign req_ready = (!tag_valid || tag_ready) && !tag_write; end else begin - // Ready if buffer is empy or downstream is reading. Stall on write - assign req_ready = (!tag_valid || tag_ready) && !tag_write; //procedure must be the same, we must have handshake with the data stage, in_ready_o set to 0 when invalidating + // Ready if buffer is empy or downstream is reading. Stall on write + assign req_ready = (!tag_valid || tag_ready) && !tag_write; end // Register the handshake of the reg stage to buffer the tag output data in the next cycle @@ -291,7 +287,7 @@ module snitch_icache_lookup_serial #( tag_rsp_d = tag_rsp_s; end // Override the hit if the write that stalled us invalidated the data - if (lookup_addr == write_addr && write_valid_i) begin + if (lookup_addr == write_addr && write_valid_i && write_ready_o) begin tag_rsp_d.hit = 1'b0; end end @@ -349,11 +345,10 @@ module snitch_icache_lookup_serial #( // Default read request data_addr = lookup_addr; data_enable = tag_valid && tag_rsp.hit; // Only read data on hit - data_wdata[CFG.LINE_WIDTH -1:1] = write_data_i[CFG.LINE_WIDTH -1:1]; - data_wdata[0] = write_data_i[0] ^ data_fault_inject; + data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i; data_write = 1'b0; - // Write takes priority - if (!init_phase && write_valid_i) begin + // Before: Write takes priority, with FT-> write does not have priority over invalidation + if (!init_phase && write_valid_i && !data_fault_valid) begin data_addr = write_addr; data_enable = 1'b1; data_write = 1'b1; @@ -406,7 +401,7 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin assign data_parity_inv_d.addr = data_req_q.addr; assign data_parity_inv_d.cset = data_req_q.id; - `FFL(data_parity_inv_q, data_fault_ready ? '0 : data_parity_inv_d, tag_handshake || data_fault_ready, '0, clk_i, rst_ni) + `FFL(data_parity_inv_q, (data_fault_ready && !tag_handshake) ? '0 : data_parity_inv_d, tag_handshake || data_fault_ready, '0, clk_i, rst_ni) //reset value when it has been invalidated and there is no new value `FFL(hit_invalid_q, data_parity_inv_d.parity_error, tag_handshake, '0, clk_i, rst_ni) end diff --git a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl index 45746332b..74faafcaf 100644 --- a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl +++ b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl @@ -30,9 +30,11 @@ proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_ta #/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram set inject_register_netlist { - /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram[*] + + /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram } #/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram +# set inject_signals_netlist [] set output_netlist [] diff --git a/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl b/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl index efb189279..ab0291d0e 100644 --- a/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl +++ b/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl @@ -8,6 +8,7 @@ vsim snitch_read_only_cache_tb_opt -voptargs=+acc add wave -noupdate -expand -group lookup /snitch_read_only_cache_tb/dut/i_lookup/* add wave -noupdate -group handler /snitch_read_only_cache_tb/dut/i_handler/* add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* +add wave -noupdate -group top /snitch_read_only_cache_tb/dut/* do ../fault_injection/ROC_inject_fault.tcl diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv index 54293dc6b..5af0d75fc 100644 --- a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -271,15 +271,15 @@ class const_axi_slave #( endclass `include "common_cells/assertions.svh" -localparam debug = 0; +localparam debug = 1; module snitch_read_only_cache_tb import snitch_pkg::*; #( parameter int unsigned AxiAddrWidth = 32, parameter int unsigned AxiDataWidth = debug? 32:128, parameter int unsigned AxiIdWidth = 5, - parameter int unsigned LineWidth = debug? 64:256, + parameter int unsigned LineWidth = debug? 32:256, parameter int unsigned LineCount = debug? 32:128, - parameter int unsigned SetCount = 2, - parameter int unsigned FaultFreq = 10 + parameter int unsigned SetCount = debug? 2:2, + parameter int unsigned FaultFreq = 200 ); localparam time ClkPeriod = 10ns; @@ -600,8 +600,11 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( exp_data[i*32 +: 32] = aligned_addr + (4*i); end if (r_beat.data != exp_data) begin - $display("Error (%0t): Wrong data. Addr=0x%x, Beat=%d, Size=%d Aqc=0x%x Exp=0x%x", - $time, ar_beat.addr, no_r_beat[i], ar_beat.size, r_beat.data, exp_data); + $display("Error (%0t): Wrong data. Addr=0x%x (idx=%h), Beat=%d, Size=%d Aqc=0x%x Exp=0x%x", + $time, ar_beat.addr, ar_beat.addr[2+:5], no_r_beat[i], ar_beat.size, r_beat.data, exp_data); + //end else begin + // $display("(%0t): Correct data. Addr=0x%x, Beat=%d, Size=%d Aqc=0x%x Exp=0x%x", + // $time, ar_beat.addr, no_r_beat[i], ar_beat.size, r_beat.data, exp_data); end if (r_beat.last && !(ar_beat.len == no_r_beat[i])) begin diff --git a/hardware/scripts/questa/Mempool_fault_injection.tcl b/hardware/scripts/questa/Mempool_fault_injection.tcl new file mode 100644 index 000000000..74faafcaf --- /dev/null +++ b/hardware/scripts/questa/Mempool_fault_injection.tcl @@ -0,0 +1,44 @@ +transcript quietly + +set verbosity 1 +set log_injections 1 +set seed 12345 +set script_base_path "/scratch/sem23h18/project/mempool/hardware/deps/snitch/tb/fault_injection/" +set inject_start_time 500000ns +set inject_stop_time 0 +#use a clock in the tb +set injection_clock "/snitch_read_only_cache_tb/fault_clk" +set injection_clock_trigger 0 +set fault_period 10 +set rand_initial_injection_phase 0 +set max_num_fault_inject 1000 +set signal_fault_duration 20ns +set register_fault_duration 0ns + +set allow_multi_bit_upset 1 +set use_bitwidth_as_weight 1 +set check_core_output_modification 0 +set check_core_next_state_modification 0 +set reg_to_sig_ratio 1 + +proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} + + + +#set inject_register_netlist [find nets [base_path]/*] +#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram +#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram + +set inject_register_netlist { + + /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram +} +#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram +# + +set inject_signals_netlist [] +set output_netlist [] +set next_state_netlist [] +set assertion_disable_list [] + +source ${::script_base_path}inject_fault.tcl \ No newline at end of file From 29a96d26af8eaebf6b998d16eec22432930a2ef2 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 30 Oct 2023 12:06:46 +0100 Subject: [PATCH 13/46] [hardware] Changed data caches parity from even to odd --- .../snitch_icache_lookup_serial.sv | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index cb645bbb0..3a821a918 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -132,7 +132,7 @@ module snitch_icache_lookup_serial #( end end - assign data_fault_valid = data_parity_inv_q.parity_error; + assign data_fault_valid = RELIABILITY_MODE ? data_parity_inv_q.parity_error : '0; always_comb begin tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the index tag_enable = '0; @@ -151,6 +151,7 @@ module snitch_icache_lookup_serial #( tag_write = 1'b1; end else if (data_fault_valid && RELIABILITY_MODE) begin tag_addr = data_parity_inv_q.addr >> CFG.LINE_ALIGN; + //$display("invalidation addr: %h,\n %b\n tag_addr: %h, LA=%d, CA=%d", data_parity_inv_q.addr, data_parity_inv_q.addr, tag_addr, CFG.LINE_ALIGN, CFG.COUNT_ALIGN ); tag_enable = $unsigned(1 << data_parity_inv_q.cset); tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; @@ -168,7 +169,6 @@ module snitch_icache_lookup_serial #( tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; - if (clk_i == '0) $display("[ROcache_lookup]: TagFault -> Invalidating address %h", tag_req_q.addr); end else if (in_valid_i) begin // Check cache tag_enable = '1; @@ -178,9 +178,9 @@ module snitch_icache_lookup_serial #( end end always @ (posedge clk_i) begin - if(data_fault_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); + if(data_fault_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h, set %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], data_parity_inv_q.cset); else if(faulty_hit && RELIABILITY_MODE) $display("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); - //if(out_valid_o && out_ready_i && out_data_o == '0) $display("(%t) [ROcache_lookup]: ReadData at Address %h (idx %h) is %h", $time, out_addr_o, out_addr_o[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], out_data_o); + //if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h (idx %h) set %h", $time, write_addr_i, write_set_i); end // Instantiate the tag sets. @@ -287,7 +287,7 @@ module snitch_icache_lookup_serial #( tag_rsp_d = tag_rsp_s; end // Override the hit if the write that stalled us invalidated the data - if (lookup_addr == write_addr && write_valid_i && write_ready_o) begin + if ((lookup_addr == write_addr) && write_valid_i && write_ready_o) begin tag_rsp_d.hit = 1'b0; end end @@ -338,7 +338,7 @@ module snitch_icache_lookup_serial #( // Compute parity bit if (RELIABILITY_MODE) begin for (genvar i = 0; i < DATA_PARITY_WIDTH; i++) begin - assign data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ^write_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; + assign data_wdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] = ~^write_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; end end always_comb begin @@ -353,6 +353,8 @@ module snitch_icache_lookup_serial #( data_enable = 1'b1; data_write = 1'b1; end + assert property ( @(posedge clk_i) tag_write && write_valid_i |-> data_write ) else $error("Writing tag but not data"); + assert property ( @(posedge clk_i) data_write && write_valid_i |-> tag_write ) else $error("Writing data but not tag"); end tc_sram #( @@ -369,6 +371,10 @@ module snitch_icache_lookup_serial #( .be_i ( '1 ), .rdata_o ( data_rdata ) ); + always @ (posedge clk_i) begin + if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h set %h", $time, write_addr_i, write_set_i); + //if(out_valid_o && hit_invalid && out_ready_i) $display("(%t) [ROcache_lookup]: Wrong data 0x%x (addr=%h) invalidated", $time, out_data_o, out_addr_o); + end // Parity check logic [DATA_PARITY_WIDTH-1:0] data_parity_error; @@ -376,7 +382,7 @@ module snitch_icache_lookup_serial #( always_comb begin : p_parity_check data_parity_error = '0; for (int i = 0; i < DATA_PARITY_WIDTH; i++) begin - data_parity_error[DATA_PARITY_WIDTH-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ^data_rdata[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT])? '0 : '1; + data_parity_error[DATA_PARITY_WIDTH-1-i] = (data_rdata[CFG.LINE_WIDTH + DATA_PARITY_WIDTH -1 - i] == ~^data_rdata[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT])? '0 : '1; end data_parity_inv_d.parity_error = |data_parity_error; end From bd01e07da0feca06ff5458444d46705c0002a0de Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 31 Oct 2023 12:10:59 +0100 Subject: [PATCH 14/46] [hardware] Fixed tag invalidation procedure combinational loop --- .../snitch_icache_lookup_serial.sv | 18 +++++++------- .../tb/fault_injection/ROC_inject_fault.tcl | 24 +++++++++++++++---- .../questa/Mempool_fault_injection.tcl | 7 ++---- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 3a821a918..abc70e91d 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -99,7 +99,7 @@ module snitch_icache_lookup_serial #( logic [CFG.TAG_WIDTH-1:0] required_tag; logic [CFG.SET_COUNT-1:0] line_hit; logic [CFG.SET_COUNT-1:0] tag_parity_error_d, tag_parity_error_q; - logic faulty_hit, faulty_hit_d, faulty_hit_q; + logic faulty_hit_valid, faulty_hit_ready, faulty_hit_d, faulty_hit_q; logic [DATA_ADDR_WIDTH-1:0] lookup_addr; logic [DATA_ADDR_WIDTH-1:0] write_addr; @@ -123,7 +123,7 @@ module snitch_icache_lookup_serial #( tag_parity_bit = 1'b1; end else if (write_valid_i) begin tag_parity_bit = ^write_tag_i; - end else if (faulty_hit) begin + end else if (faulty_hit_valid) begin tag_parity_bit = 1'b1; end else if (in_valid_i) begin // read phase: write tag not used @@ -143,6 +143,7 @@ module snitch_icache_lookup_serial #( in_ready_o = 1'b0; req_valid = 1'b0; data_fault_ready = 1'b0; + faulty_hit_ready = 1'b0; if (init_phase) begin tag_addr = init_count_q; @@ -163,12 +164,13 @@ module snitch_icache_lookup_serial #( tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; - end else if (faulty_hit && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 + end else if (faulty_hit_valid && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 //we do not accept read requests and we do not store data in the pipeline. tag_addr = tag_req_q.addr >> CFG.LINE_ALIGN; //buffered version of in_addr_i tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; + faulty_hit_ready = 1'b1; end else if (in_valid_i) begin // Check cache tag_enable = '1; @@ -179,7 +181,7 @@ module snitch_icache_lookup_serial #( end always @ (posedge clk_i) begin if(data_fault_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h, set %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], data_parity_inv_q.cset); - else if(faulty_hit && RELIABILITY_MODE) $display("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); + else if(faulty_hit_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); //if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h (idx %h) set %h", $time, write_addr_i, write_set_i); end @@ -260,9 +262,9 @@ module snitch_icache_lookup_serial #( `FFL(tag_req_q, tag_req_d, req_valid && req_ready, '0, clk_i, rst_ni) if(RELIABILITY_MODE) begin `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) - `FFL(faulty_hit_q, faulty_hit_d, req_valid && req_ready, '0, clk_i, rst_ni) + `FFL(faulty_hit_q, (faulty_hit_ready && !(req_valid && req_ready))? 1'b0 : faulty_hit_d, req_valid && req_ready || faulty_hit_ready, '0, clk_i, rst_ni) end - assign faulty_hit = RELIABILITY_MODE ? ((req_valid && req_ready) ? faulty_hit_d : faulty_hit_q) : '0; //if there is a write request, select the buffered version to be invalidated + assign faulty_hit_valid = RELIABILITY_MODE ? faulty_hit_q : '0; //if there is a write request, select the buffered version to be invalidated `FF(tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0, clk_i, rst_ni) if(!RELIABILITY_MODE) begin @@ -353,8 +355,8 @@ module snitch_icache_lookup_serial #( data_enable = 1'b1; data_write = 1'b1; end - assert property ( @(posedge clk_i) tag_write && write_valid_i |-> data_write ) else $error("Writing tag but not data"); - assert property ( @(posedge clk_i) data_write && write_valid_i |-> tag_write ) else $error("Writing data but not tag"); + //assert property ( @(posedge clk_i) tag_write && write_valid_i |-> data_write ) else $error("Writing tag but not data"); + //assert property ( @(posedge clk_i) data_write && write_valid_i |-> tag_write ) else $error("Writing data but not tag"); end tc_sram #( diff --git a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl index 74faafcaf..5916e4824 100644 --- a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl +++ b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl @@ -26,15 +26,29 @@ proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_ta #set inject_register_netlist [find nets [base_path]/*] -#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram -#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram - set inject_register_netlist { - /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram } + +# Set the path to the SRAM +set itag_sram_path "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram" + +# Determine the maximum possible index for the first index +set max_first_index 31 ; # Replace with the actual maximum value + +# Determine the largest possible second index +set max_second_index 57 ; # Replace with the actual maximum value + +# Add the signal paths with maximum indices to the inject_register_netlist +for {set index $max_first_index} {$index >= 0} {incr index -1} { + set signal_path "${itag_sram_path}[${index}][${max_second_index}]" + lappend inject_register_netlist $signal_path +} +lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram + +#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram #/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram -# + set inject_signals_netlist [] set output_netlist [] diff --git a/hardware/scripts/questa/Mempool_fault_injection.tcl b/hardware/scripts/questa/Mempool_fault_injection.tcl index 74faafcaf..bbb9a0cff 100644 --- a/hardware/scripts/questa/Mempool_fault_injection.tcl +++ b/hardware/scripts/questa/Mempool_fault_injection.tcl @@ -30,12 +30,9 @@ proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_ta #/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram set inject_register_netlist { - - /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram + /snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram } -#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram -# - +#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram set inject_signals_netlist [] set output_netlist [] set next_state_netlist [] From 1a16e900634b20d892cc1a482e60bda4fda483d3 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sat, 4 Nov 2023 18:22:37 +0100 Subject: [PATCH 15/46] [hardware] Changed definition of registers where to inject faults, fixed problem with reading all zeros --- .../tb/fault_injection/ROC_inject_fault.tcl | 25 ++++++++++++++----- .../deps/snitch/tb/scripts/FI_sim_setup.tcl | 3 +++ hardware/deps/snitch/tb/scripts/sim_setup.tcl | 2 ++ .../tb/src/snitch_read_only_cache_tb.sv | 3 ++- hardware/src/mempool_pkg.sv | 2 ++ 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl index 5916e4824..ccedeb465 100644 --- a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl +++ b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl @@ -16,7 +16,7 @@ set signal_fault_duration 20ns set register_fault_duration 0ns set allow_multi_bit_upset 1 -set use_bitwidth_as_weight 1 +set use_bitwidth_as_weight 0 set check_core_output_modification 0 set check_core_next_state_modification 0 set reg_to_sig_ratio 1 @@ -34,17 +34,30 @@ set inject_register_netlist { set itag_sram_path "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram" # Determine the maximum possible index for the first index -set max_first_index 31 ; # Replace with the actual maximum value +set max_index_value 127 ; # Replace with the actual maximum value # Determine the largest possible second index -set max_second_index 57 ; # Replace with the actual maximum value +set max_tag_line_index 47 ; # Replace with the actual maximum value # Add the signal paths with maximum indices to the inject_register_netlist -for {set index $max_first_index} {$index >= 0} {incr index -1} { - set signal_path "${itag_sram_path}[${index}][${max_second_index}]" +for {set index $max_index_value} {$index >= 0} {incr index -1} { + set signal_path "${itag_sram_path}[${index}][${max_tag_line_index}]" lappend inject_register_netlist $signal_path } -lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram +#lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram + +#set max_data_index 63; +set max_data_index [expr { + $max_index_value * 2 + 1 +}] +set idata_sram_path "/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram"; +for {set index $max_data_index} {$index >= 0} {incr index -1} { + set signal_path "${idata_sram_path}[${index}]" + lappend inject_register_netlist $signal_path +} + +#lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram[63] + #/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram #/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram diff --git a/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl b/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl index ab0291d0e..c9af0bdac 100644 --- a/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl +++ b/hardware/deps/snitch/tb/scripts/FI_sim_setup.tcl @@ -9,6 +9,9 @@ add wave -noupdate -expand -group lookup /snitch_read_only_cache_tb/dut/i_lookup add wave -noupdate -group handler /snitch_read_only_cache_tb/dut/i_handler/* add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* add wave -noupdate -group top /snitch_read_only_cache_tb/dut/* +add wave -noupdate /snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram +add wave -noupdate /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram + do ../fault_injection/ROC_inject_fault.tcl diff --git a/hardware/deps/snitch/tb/scripts/sim_setup.tcl b/hardware/deps/snitch/tb/scripts/sim_setup.tcl index 14f499fb2..dcc595cbd 100644 --- a/hardware/deps/snitch/tb/scripts/sim_setup.tcl +++ b/hardware/deps/snitch/tb/scripts/sim_setup.tcl @@ -8,5 +8,7 @@ vsim snitch_read_only_cache_tb_opt -voptargs=+acc add wave -noupdate -expand -group lookup /snitch_read_only_cache_tb/dut/i_lookup/* add wave -noupdate -group handler /snitch_read_only_cache_tb/dut/i_handler/* add wave -noupdate -group refill /snitch_read_only_cache_tb/dut/i_refill/* +add wave -noupdate /snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram +add wave -noupdate /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram run -all \ No newline at end of file diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv index 5af0d75fc..80fed9172 100644 --- a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -271,7 +271,7 @@ class const_axi_slave #( endclass `include "common_cells/assertions.svh" -localparam debug = 1; +localparam debug = 0; module snitch_read_only_cache_tb import snitch_pkg::*; #( parameter int unsigned AxiAddrWidth = 32, parameter int unsigned AxiDataWidth = debug? 32:128, @@ -602,6 +602,7 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( if (r_beat.data != exp_data) begin $display("Error (%0t): Wrong data. Addr=0x%x (idx=%h), Beat=%d, Size=%d Aqc=0x%x Exp=0x%x", $time, ar_beat.addr, ar_beat.addr[2+:5], no_r_beat[i], ar_beat.size, r_beat.data, exp_data); + //$finish(); //end else begin // $display("(%0t): Correct data. Addr=0x%x, Beat=%d, Size=%d Aqc=0x%x Exp=0x%x", // $time, ar_beat.addr, no_r_beat[i], ar_beat.size, r_beat.data, exp_data); diff --git a/hardware/src/mempool_pkg.sv b/hardware/src/mempool_pkg.sv index de8449b99..006aaa525 100644 --- a/hardware/src/mempool_pkg.sv +++ b/hardware/src/mempool_pkg.sv @@ -27,6 +27,7 @@ package mempool_pkg; localparam integer unsigned AxiDataWidth = `ifdef AXI_DATA_WIDTH `AXI_DATA_WIDTH `else 0 `endif; localparam integer unsigned AxiLiteDataWidth = 32; + /*********************** * MEMORY PARAMETERS * ***********************/ @@ -134,6 +135,7 @@ package mempool_pkg; localparam int unsigned ICacheSizeByte = 512 * NumCoresPerCache; // Total Size of instruction cache in bytes localparam int unsigned ICacheSets = NumCoresPerCache / 2; // Number of sets localparam int unsigned ICacheLineWidth = 32 * 2 * NumCoresPerCache; // Size of each cache line in bits + // TODO: add RELIABILITY ICACHE/ RO parameters /********************* * READ-ONLY CACHE * From e55a72817ac20a63844ff1b7efb0a5385832baa0 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 5 Nov 2023 15:27:52 +0100 Subject: [PATCH 16/46] [hardware] Added control parameters for fault tolerance in ROC and L1IC --- config/config.mk | 5 + hardware/Makefile | 1 + .../snitch/src/snitch_icache/snitch_icache.sv | 4 + .../snitch_icache_lookup_serial.sv | 9 +- .../src/snitch_icache/snitch_icache_pkg.sv | 1 + .../snitch_read_only_cache.sv | 3 + .../tb/src/snitch_read_only_cache_tb.sv | 2 + hardware/src/axi_hier_interco.sv | 158 +++++++++--------- hardware/src/bootrom.sv | 6 +- hardware/src/mempool_group.sv | 33 ++-- hardware/src/mempool_pkg.sv | 3 +- hardware/src/mempool_tile.sv | 1 + 12 files changed, 124 insertions(+), 102 deletions(-) diff --git a/config/config.mk b/config/config.mk index 0691071c8..20db190b0 100644 --- a/config/config.mk +++ b/config/config.mk @@ -68,3 +68,8 @@ xqueue_size ?= 0 # Enable the XpulpIMG extension xpulpimg ?= 1 + +# Enable the Reliability mode for RO caches and L1 icache +rel_rocache ?= 0 + +rel_l1icache ?= 0 diff --git a/hardware/Makefile b/hardware/Makefile index 2faf4d255..f75d0f710 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -97,6 +97,7 @@ vlog_defs += -DRO_LINE_WIDTH=$(ro_line_width) vlog_defs += -DDMAS_PER_GROUP=$(dmas_per_group) vlog_defs += -DAXI_HIER_RADIX=$(axi_hier_radix) -DAXI_MASTERS_PER_GROUP=$(axi_masters_per_group) vlog_defs += -DSEQ_MEM_SIZE=$(seq_mem_size) -DXQUEUE_SIZE=$(xqueue_size) +vlog_defs += -DREL_ROCACHE=$(rel_rocache) -DREL_L1ICACHE=$(rel_l1icache) # Traffic generation enabled ifdef tg diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv index 56615429b..fe1fc86d3 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv @@ -26,6 +26,8 @@ module snitch_icache #( parameter int FILL_AW = -1, /// Fill interface data width. Power of two; >= 8. parameter int FILL_DW = -1, + /// Add parity checks in the lookup module + parameter bit RELIABILITY = 0, /// Replace the L1 tag banks with latch-based SCM. parameter bit L1_TAG_SCM = 0, /// This reduces area impact at the cost of @@ -42,6 +44,7 @@ module snitch_icache #( parameter int L0_EARLY_TAG_WIDTH = -1, /// Operate L0 cache in slower clock-domain parameter bit ISO_CROSSING = 1, + parameter type axi_req_t = logic, parameter type axi_rsp_t = logic ) ( @@ -79,6 +82,7 @@ module snitch_icache #( FETCH_DW: FETCH_DW, FILL_AW: FILL_AW, FILL_DW: FILL_DW, + ENABLE_RELIABILITY: RELIABILITY, L1_TAG_SCM: L1_TAG_SCM, EARLY_LATCH: EARLY_LATCH, BUFFER_LOOKUP: 0, diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index abc70e91d..111c640bc 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -8,8 +8,7 @@ /// An actual cache lookup. module snitch_icache_lookup_serial #( - parameter snitch_icache_pkg::config_t CFG = '0, - parameter logic RELIABILITY_MODE = '1 // Fault tolerance enabled setting it to 1 + parameter snitch_icache_pkg::config_t CFG = '0 )( input logic clk_i, input logic rst_ni, @@ -39,9 +38,9 @@ module snitch_icache_lookup_serial #( input logic write_valid_i, output logic write_ready_o ); - + localparam bit RELIABILITY_MODE = CFG.ENABLE_RELIABILITY; localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; - localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd4 : '0; + localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd4 : '0; // TODO: propagate it up as a parameter `ifndef SYNTHESIS initial assert(CFG != '0); @@ -374,7 +373,7 @@ module snitch_icache_lookup_serial #( .rdata_o ( data_rdata ) ); always @ (posedge clk_i) begin - if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h set %h", $time, write_addr_i, write_set_i); + //if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h set %h", $time, write_addr_i, write_set_i); //if(out_valid_o && hit_invalid && out_ready_i) $display("(%t) [ROcache_lookup]: Wrong data 0x%x (addr=%h) invalidated", $time, out_data_o, out_addr_o); end diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv index 2dfd2dedb..deacf6202 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv @@ -25,6 +25,7 @@ package snitch_icache_pkg; int FETCH_DW; int FILL_AW; int FILL_DW; + bit ENABLE_RELIABILITY; bit L1_TAG_SCM; bit EARLY_LATCH; bit BUFFER_LOOKUP; diff --git a/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv b/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv index cdf76cbc3..a32b8b6b8 100644 --- a/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv +++ b/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv @@ -15,6 +15,8 @@ module snitch_read_only_cache #( parameter int unsigned LineCount = -1, /// The set associativity of the cache. Power of two; >= 1. parameter int unsigned SetCount = 1, + /// Reliability mode adds parity checks in the lookup module + parameter bit Reliability = 0, /// AXI address width parameter int unsigned AxiAddrWidth = 0, /// AXI data width @@ -195,6 +197,7 @@ module snitch_read_only_cache #( FETCH_DW: AxiDataWidth, FILL_AW: AxiAddrWidth, FILL_DW: AxiDataWidth, + ENABLE_RELIABILITY: Reliability, L1_TAG_SCM: 0, // Unused here EARLY_LATCH: 0, // Unused here BUFFER_LOOKUP: 1, // Mandatory here diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv index 80fed9172..8ef823ef2 100644 --- a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -279,6 +279,7 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( parameter int unsigned LineWidth = debug? 32:256, parameter int unsigned LineCount = debug? 32:128, parameter int unsigned SetCount = debug? 2:2, + parameter bit Reliability = 1'b1, parameter int unsigned FaultFreq = 200 ); @@ -393,6 +394,7 @@ module snitch_read_only_cache_tb import snitch_pkg::*; #( .LineWidth ( LineWidth ), .LineCount ( LineCount ), .SetCount ( SetCount ), + .Reliability ( Reliability ), .AxiAddrWidth ( AxiAddrWidth ), .AxiDataWidth ( AxiDataWidth ), .AxiIdWidth ( AxiInIdWidth ), diff --git a/hardware/src/axi_hier_interco.sv b/hardware/src/axi_hier_interco.sv index 49249c940..e52877593 100644 --- a/hardware/src/axi_hier_interco.sv +++ b/hardware/src/axi_hier_interco.sv @@ -25,22 +25,23 @@ module axi_hier_interco import mempool_pkg::ro_cache_ctrl_t; #( - parameter int unsigned NumSlvPorts = 0, - parameter int unsigned NumMstPorts = 0, - parameter int unsigned Radix = 2, - parameter int unsigned EnableCache = 0, - parameter int unsigned CacheLineWidth = 0, - parameter int unsigned CacheSizeByte = 0, - parameter int unsigned CacheSets = 0, - parameter int unsigned AddrWidth = 0, - parameter int unsigned DataWidth = 0, - parameter int unsigned SlvIdWidth = 0, - parameter int unsigned MstIdWidth = 0, - parameter int unsigned UserWidth = 0, - parameter type slv_req_t = logic, - parameter type slv_resp_t = logic, - parameter type mst_req_t = logic, - parameter type mst_resp_t = logic + parameter int unsigned NumSlvPorts = 0, + parameter int unsigned NumMstPorts = 0, + parameter int unsigned Radix = 2, + parameter int unsigned EnableCache = 0, + parameter int unsigned CacheLineWidth = 0, + parameter int unsigned CacheSizeByte = 0, + parameter int unsigned CacheSets = 0, + parameter bit CacheReliability = 0, + parameter int unsigned AddrWidth = 0, + parameter int unsigned DataWidth = 0, + parameter int unsigned SlvIdWidth = 0, + parameter int unsigned MstIdWidth = 0, + parameter int unsigned UserWidth = 0, + parameter type slv_req_t = logic, + parameter type slv_resp_t = logic, + parameter type mst_req_t = logic, + parameter type mst_resp_t = logic ) ( input logic clk_i, input logic rst_ni, @@ -120,60 +121,62 @@ module axi_hier_interco for (genvar i = 0; i < NumMuxes; i++) begin : gen_lower_level axi_hier_interco #( - .NumSlvPorts (Radix ), - .NumMstPorts (1 ), - .Radix (Radix ), - .EnableCache (EnableCache ), - .CacheLineWidth (CacheLineWidth), - .CacheSizeByte (CacheSizeByte ), - .CacheSets (CacheSets ), - .AddrWidth (AddrWidth ), - .DataWidth (DataWidth ), - .SlvIdWidth (SlvIdWidth ), - .MstIdWidth (SlvIdWidth ), - .UserWidth (UserWidth ), - .slv_req_t (slv_req_t ), - .slv_resp_t (slv_resp_t ), - .mst_req_t (slv_req_t ), - .mst_resp_t (slv_resp_t ) + .NumSlvPorts (Radix ), + .NumMstPorts (1 ), + .Radix (Radix ), + .EnableCache (EnableCache ), + .CacheLineWidth (CacheLineWidth ), + .CacheSizeByte (CacheSizeByte ), + .CacheSets (CacheSets ), + .CacheReliability (CacheReliability), + .AddrWidth (AddrWidth ), + .DataWidth (DataWidth ), + .SlvIdWidth (SlvIdWidth ), + .MstIdWidth (SlvIdWidth ), + .UserWidth (UserWidth ), + .slv_req_t (slv_req_t ), + .slv_resp_t (slv_resp_t ), + .mst_req_t (slv_req_t ), + .mst_resp_t (slv_resp_t ) ) i_axi_interco ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .test_i (test_i ), - .ro_cache_ctrl_i (ro_cache_ctrl_i ), - .slv_req_i (slv_req_i[i*Radix +: Radix] ), - .slv_resp_o (slv_resp_o[i*Radix +: Radix]), - .mst_req_o (int_req[i] ), - .mst_resp_i (int_resp[i] ) + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .test_i (test_i ), + .ro_cache_ctrl_i (ro_cache_ctrl_i ), + .slv_req_i (slv_req_i[i*Radix +: Radix] ), + .slv_resp_o (slv_resp_o[i*Radix +: Radix]), + .mst_req_o (int_req[i] ), + .mst_resp_i (int_resp[i] ) ); end axi_hier_interco #( - .NumSlvPorts (NumMuxes ), - .NumMstPorts (NumMstPorts ), - .Radix (Radix ), - .EnableCache (EnableCache>>1), - .CacheLineWidth (CacheLineWidth), - .CacheSizeByte (CacheSizeByte ), - .CacheSets (CacheSets ), - .AddrWidth (AddrWidth ), - .DataWidth (DataWidth ), - .SlvIdWidth (SlvIdWidth ), - .MstIdWidth (MstIdWidth ), - .UserWidth (UserWidth ), - .slv_req_t (slv_req_t ), - .slv_resp_t (slv_resp_t ), - .mst_req_t (mst_req_t ), - .mst_resp_t (mst_resp_t ) + .NumSlvPorts (NumMuxes ), + .NumMstPorts (NumMstPorts ), + .Radix (Radix ), + .EnableCache (EnableCache>>1 ), + .CacheLineWidth (CacheLineWidth ), + .CacheSizeByte (CacheSizeByte ), + .CacheSets (CacheSets ), + .CacheReliability (CacheReliability), + .AddrWidth (AddrWidth ), + .DataWidth (DataWidth ), + .SlvIdWidth (SlvIdWidth ), + .MstIdWidth (MstIdWidth ), + .UserWidth (UserWidth ), + .slv_req_t (slv_req_t ), + .slv_resp_t (slv_resp_t ), + .mst_req_t (mst_req_t ), + .mst_resp_t (mst_resp_t ) ) i_axi_interco ( - .clk_i (clk_i ), - .rst_ni (rst_ni ), - .test_i (test_i ), - .ro_cache_ctrl_i (ro_cache_ctrl_i), - .slv_req_i (int_req ), - .slv_resp_o (int_resp ), - .mst_req_o (mst_req_o ), - .mst_resp_i (mst_resp_i ) + .clk_i (clk_i ), + .rst_ni (rst_ni ), + .test_i (test_i ), + .ro_cache_ctrl_i (ro_cache_ctrl_i), + .slv_req_i (int_req ), + .slv_resp_o (int_resp ), + .mst_req_o (mst_req_o ), + .mst_resp_i (mst_resp_i ) ); end end else if (NumSlvPorts <= Radix && NumMstPorts == 1) begin : gen_bottom_level @@ -225,19 +228,20 @@ module axi_hier_interco if (EnableCache[0]) begin: gen_ro_cache localparam int unsigned LineCount = CacheSizeByte/(CacheSets*CacheLineWidth/8); snitch_read_only_cache #( - .LineWidth (CacheLineWidth), - .LineCount (LineCount ), - .SetCount (CacheSets ), - .AxiAddrWidth (AddrWidth ), - .AxiDataWidth (DataWidth ), - .AxiIdWidth (IntIdWidth ), - .AxiUserWidth (UserWidth ), - .MaxTrans (32'd16 ), - .NrAddrRules (NrAddrRules ), - .slv_req_t (int_req_t ), - .slv_rsp_t (int_resp_t ), - .mst_req_t (cache_req_t ), - .mst_rsp_t (cache_resp_t ) + .LineWidth (CacheLineWidth ), + .LineCount (LineCount ), + .SetCount (CacheSets ), + .Reliability (CacheReliability), + .AxiAddrWidth (AddrWidth ), + .AxiDataWidth (DataWidth ), + .AxiIdWidth (IntIdWidth ), + .AxiUserWidth (UserWidth ), + .MaxTrans (32'd16 ), + .NrAddrRules (NrAddrRules ), + .slv_req_t (int_req_t ), + .slv_rsp_t (int_resp_t ), + .mst_req_t (cache_req_t ), + .mst_rsp_t (cache_resp_t ) ) i_snitch_read_only_cache ( .clk_i (clk_i ), .rst_ni (rst_ni ), diff --git a/hardware/src/bootrom.sv b/hardware/src/bootrom.sv index 87f1c259e..6494806e7 100644 --- a/hardware/src/bootrom.sv +++ b/hardware/src/bootrom.sv @@ -8,7 +8,7 @@ module bootrom #( /* Automatically generated. DO NOT CHANGE! */ - parameter int unsigned DataWidth = 256, + parameter int unsigned DataWidth = 512, parameter int unsigned AddrWidth = 32 ) ( input logic clk_i, @@ -20,14 +20,14 @@ module bootrom #( localparam int AddrBits = RomSize > 1 ? $clog2(RomSize) : 1; const logic [RomSize-1:0][DataWidth-1:0] mem = { - 256'h00000000_00000000_00000000_00000000_00050067_10500073_00050513_e0000517 + 512'h00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000_00050067_10500073_00050513_e0000517 }; logic [AddrBits-1:0] addr_q; always_ff @(posedge clk_i) begin if (req_i) begin - addr_q <= addr_i[AddrBits-1+5:5]; + addr_q <= addr_i[AddrBits-1+6:6]; end end diff --git a/hardware/src/mempool_group.sv b/hardware/src/mempool_group.sv index 458c061d6..a39c9b050 100644 --- a/hardware/src/mempool_group.sv +++ b/hardware/src/mempool_group.sv @@ -386,22 +386,23 @@ module mempool_group end : gen_axi_slv_vec axi_hier_interco #( - .NumSlvPorts (NumTilesPerGroup+NumDmasPerGroup), - .NumMstPorts (NumAXIMastersPerGroup ), - .Radix (AxiHierRadix ), - .EnableCache (32'hFFFFFFFF ), - .CacheLineWidth (ROCacheLineWidth ), - .CacheSizeByte (ROCacheSizeByte ), - .CacheSets (ROCacheSets ), - .AddrWidth (AddrWidth ), - .DataWidth (AxiDataWidth ), - .SlvIdWidth (AxiTileIdWidth ), - .MstIdWidth (AxiTileIdWidth ), - .UserWidth (1 ), - .slv_req_t (axi_tile_req_t ), - .slv_resp_t (axi_tile_resp_t ), - .mst_req_t (axi_tile_req_t ), - .mst_resp_t (axi_tile_resp_t ) + .NumSlvPorts (NumTilesPerGroup+NumDmasPerGroup), + .NumMstPorts (NumAXIMastersPerGroup ), + .Radix (AxiHierRadix ), + .EnableCache (32'hFFFFFFFF ), + .CacheLineWidth (ROCacheLineWidth ), + .CacheSizeByte (ROCacheSizeByte ), + .CacheSets (ROCacheSets ), + .CacheReliability (ROCacheReliability ), + .AddrWidth (AddrWidth ), + .DataWidth (AxiDataWidth ), + .SlvIdWidth (AxiTileIdWidth ), + .MstIdWidth (AxiTileIdWidth ), + .UserWidth (1 ), + .slv_req_t (axi_tile_req_t ), + .slv_resp_t (axi_tile_resp_t ), + .mst_req_t (axi_tile_req_t ), + .mst_resp_t (axi_tile_resp_t ) ) i_axi_interco ( .clk_i (clk_i ), .rst_ni (rst_ni ), diff --git a/hardware/src/mempool_pkg.sv b/hardware/src/mempool_pkg.sv index 006aaa525..6868f3712 100644 --- a/hardware/src/mempool_pkg.sv +++ b/hardware/src/mempool_pkg.sv @@ -135,7 +135,7 @@ package mempool_pkg; localparam int unsigned ICacheSizeByte = 512 * NumCoresPerCache; // Total Size of instruction cache in bytes localparam int unsigned ICacheSets = NumCoresPerCache / 2; // Number of sets localparam int unsigned ICacheLineWidth = 32 * 2 * NumCoresPerCache; // Size of each cache line in bits - // TODO: add RELIABILITY ICACHE/ RO parameters + localparam bit ICacheReliability = `ifdef REL_L1ICACHE `REL_L1ICACHE `else 1'bX `endif; // Reliability for icaches enabled? /********************* * READ-ONLY CACHE * @@ -145,6 +145,7 @@ package mempool_pkg; localparam int unsigned ROCacheLineWidth = `ifdef RO_LINE_WIDTH `RO_LINE_WIDTH `else 0 `endif; localparam int unsigned ROCacheSizeByte = 8192; localparam int unsigned ROCacheSets = 2; + localparam bit ROCacheReliability = `ifdef REL_ROCACHE `REL_ROCACHE `else 1'bX `endif; // Reliability for ROcaches enabled? localparam int unsigned ROCacheNumAddrRules = 4; typedef struct packed { diff --git a/hardware/src/mempool_tile.sv b/hardware/src/mempool_tile.sv index a3a6aa50b..40dc142c1 100644 --- a/hardware/src/mempool_tile.sv +++ b/hardware/src/mempool_tile.sv @@ -177,6 +177,7 @@ module mempool_tile .FETCH_DW (DataWidth ), .FILL_AW (AddrWidth ), .FILL_DW (AxiDataWidth ), + .RELIABILITY (ICacheReliability ), .L1_TAG_SCM (1 ), /// Make the early cache latch-based. This reduces latency at the cost of /// increased combinatorial path lengths and the hassle of having latches in From 6b1e4adbcaa45e91e6585424ce1d993f561606cd Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 6 Nov 2023 12:45:12 +0100 Subject: [PATCH 17/46] [hardware] Changed default flags --- config/config.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.mk b/config/config.mk index 20db190b0..c2173952d 100644 --- a/config/config.mk +++ b/config/config.mk @@ -70,6 +70,6 @@ xqueue_size ?= 0 xpulpimg ?= 1 # Enable the Reliability mode for RO caches and L1 icache -rel_rocache ?= 0 +rel_rocache ?= 1 -rel_l1icache ?= 0 +rel_l1icache ?= 1 From 1505f206969a741e5792f55a53955eccaedb30b4 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Wed, 8 Nov 2023 17:05:25 +0100 Subject: [PATCH 18/46] [hardware] Added injection script for mempool, including extracting nets --- config/config.mk | 6 + hardware/Makefile | 2 + .../tb/fault_injection/ROC_inject_fault.tcl | 4 +- .../questa/Mempool_fault_injection.tcl | 41 - hardware/scripts/questa/extract_nets.tcl | 216 +++++ hardware/scripts/questa/get_banks.tcl | 83 ++ hardware/scripts/questa/inject_fault.tcl | 802 ++++++++++++++++++ .../scripts/questa/mempool_inject_fault.tcl | 65 ++ hardware/scripts/questa/run.tcl | 6 + hardware/tb/mempool_tb.sv | 24 + 10 files changed, 1207 insertions(+), 42 deletions(-) delete mode 100644 hardware/scripts/questa/Mempool_fault_injection.tcl create mode 100644 hardware/scripts/questa/extract_nets.tcl create mode 100644 hardware/scripts/questa/get_banks.tcl create mode 100644 hardware/scripts/questa/inject_fault.tcl create mode 100644 hardware/scripts/questa/mempool_inject_fault.tcl diff --git a/config/config.mk b/config/config.mk index c2173952d..a139e4403 100644 --- a/config/config.mk +++ b/config/config.mk @@ -73,3 +73,9 @@ xpulpimg ?= 1 rel_rocache ?= 1 rel_l1icache ?= 1 + +# Enable fault injection in the instruction caches and define fault rate (clok_periods/fault) + +fault_injection ?= 0 + +fault_rate ?= 100 \ No newline at end of file diff --git a/hardware/Makefile b/hardware/Makefile index f75d0f710..615046b72 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -98,6 +98,8 @@ vlog_defs += -DDMAS_PER_GROUP=$(dmas_per_group) vlog_defs += -DAXI_HIER_RADIX=$(axi_hier_radix) -DAXI_MASTERS_PER_GROUP=$(axi_masters_per_group) vlog_defs += -DSEQ_MEM_SIZE=$(seq_mem_size) -DXQUEUE_SIZE=$(xqueue_size) vlog_defs += -DREL_ROCACHE=$(rel_rocache) -DREL_L1ICACHE=$(rel_l1icache) +vlog_defs += -DFAULT_INJECTION_ICACHE=$(fault_injection) +vlog_defs += -DFAULT_RATE=$(fault_rate) # Traffic generation enabled ifdef tg diff --git a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl index ccedeb465..de426d82b 100644 --- a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl +++ b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl @@ -35,16 +35,18 @@ set itag_sram_path "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram" # Determine the maximum possible index for the first index set max_index_value 127 ; # Replace with the actual maximum value +#debug : 32, normal 127 # Determine the largest possible second index set max_tag_line_index 47 ; # Replace with the actual maximum value +#debug 57, normal 47 # Add the signal paths with maximum indices to the inject_register_netlist for {set index $max_index_value} {$index >= 0} {incr index -1} { set signal_path "${itag_sram_path}[${index}][${max_tag_line_index}]" lappend inject_register_netlist $signal_path } -#lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram +#lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram #do not do this: it flips entire registers to zero #set max_data_index 63; set max_data_index [expr { diff --git a/hardware/scripts/questa/Mempool_fault_injection.tcl b/hardware/scripts/questa/Mempool_fault_injection.tcl deleted file mode 100644 index bbb9a0cff..000000000 --- a/hardware/scripts/questa/Mempool_fault_injection.tcl +++ /dev/null @@ -1,41 +0,0 @@ -transcript quietly - -set verbosity 1 -set log_injections 1 -set seed 12345 -set script_base_path "/scratch/sem23h18/project/mempool/hardware/deps/snitch/tb/fault_injection/" -set inject_start_time 500000ns -set inject_stop_time 0 -#use a clock in the tb -set injection_clock "/snitch_read_only_cache_tb/fault_clk" -set injection_clock_trigger 0 -set fault_period 10 -set rand_initial_injection_phase 0 -set max_num_fault_inject 1000 -set signal_fault_duration 20ns -set register_fault_duration 0ns - -set allow_multi_bit_upset 1 -set use_bitwidth_as_weight 1 -set check_core_output_modification 0 -set check_core_next_state_modification 0 -set reg_to_sig_ratio 1 - -proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} - - - -#set inject_register_netlist [find nets [base_path]/*] -#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram -#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram - -set inject_register_netlist { - /snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram -} -#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram -set inject_signals_netlist [] -set output_netlist [] -set next_state_netlist [] -set assertion_disable_list [] - -source ${::script_base_path}inject_fault.tcl \ No newline at end of file diff --git a/hardware/scripts/questa/extract_nets.tcl b/hardware/scripts/questa/extract_nets.tcl new file mode 100644 index 000000000..24503aac4 --- /dev/null +++ b/hardware/scripts/questa/extract_nets.tcl @@ -0,0 +1,216 @@ +# Copyright 2023 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Author: Luca Rufer (lrufer@student.ethz.ch) + +# Description: This file provides some generic procs to extract next from a +# circuit. + +# ================================= Overview ================================== +# ----------------------- General Netlist utility procs ----------------------- +# 'get_net_type' : Get the type of a Net using the describe command. +# Example return type are "Register", "Net", "Enum", +# "Array", "Record" (struct), and others +# 'get_net_array_length' : Get the length of an Array using the describe +# command. +# 'get_net_reg_width' : Get the width (number of bits) of a Register or +# Net using the describe command +# 'get_record_field_names' : Get the names of all fiels of a Record (struct). +# ------------------------- Netlist Extraction procs -------------------------- +# 'get_state_netlist' : Example function for how to extract state nets. +# Non-recursive implementation. +# 'get_state_netlist_revursive' : Example function for how to extract state nets +# Recursive implementation. +# 'get_next_state_netlist' : Example function for how to extract next state +# nets. Non-recursive implementation. +# 'get_next_state_netlist_recursive' : Example function for how to extract +# next state nets. Recursive implementation. +# 'get_output_netlist' : Example function for how to extract output nets +# of a circuit. +# 'extract_netlists' : Given a list of nets obtained e.g. from the 'find' +# command, recursively extract signals until only +# signals of type "Register", "Net" and "Enum" +# remain. +# 'extract_all_nets_recursive_filtered' : Extract all nets from a circuit, +# filter them using the given patterns, and +# recursively extract them using 'extract_netlists'. + +################################## +# Net extraction utility procs # +################################## + +proc get_net_type {signal_name} { + set sig_description [examine -describe $signal_name] + set type_string [string trim [string range $sig_description 1 [string wordend $sig_description 1]] " \n\r()\[\]{}"] + if { $type_string == "Verilog" } { set type_string "Enum"} + return $type_string +} + +proc get_net_array_length {signal_name} { + set sig_description [examine -describe $signal_name] + regexp "\\\[length (\\d+)\\\]" $sig_description -> match + return $match +} + +proc get_net_reg_width {signal_name} { + set sig_description [examine -describe $signal_name] + set length 1 + if {[regexp "\\\[(\\d+):(\\d+)\\\]" $sig_description -> up_lim low_lim]} { + set length [expr $up_lim - $low_lim + 1] + } + return $length +} + +proc get_record_number_of_fields {signal_name} { + set sig_description [examine -describe $signal_name] + if {[regexp "^\{Record \\\[(\\d+) elements?\\\]" $sig_description -> num]} { + return $num + } else { + echo "\[Netlist Extraction Error\] Failed to extract the number of fields in record $signal_name." + return 0 + } +} + +proc get_record_field_names {signal_name} { + set sig_description [examine -describe $signal_name] + set matches [regexp -all -inline "\\n Element #\\d* \"\[a-zA-Z_\]\[a-zA-Z0-9_\]*\"" $sig_description] + set field_names [list] + foreach match $matches { lappend field_names [lindex [split $match \"] 1] } + set num_fields_expected [get_record_number_of_fields $signal_name] + if {[llength $field_names] != $num_fields_expected} { + echo "\[Netlist Extraction Error\] Could not determine the field names of signal $signal_name." + echo "Expected $num_fields_expected Fields, extracted [llength $field_names]: $field_names. Skipping this net..." + return [list] + } + return $field_names +} + +########################################### +# Recursevely extract all nets and enums # +########################################### + +# description: recursively extract all nets from the given 'item_list' by +# breaking them down into the most fundamental bit vectors. +# +# Set 'injection_save' to 1 to make sure only nets that can be +# injected are extracted. Questa SIM has a bug in the 'force' +# command: When trying to force a signal in an array inside a +# struct (Record), the force command will force the field at array +# index 0, and not the selected index. E.g. forcing a.b[7] will +# actually force a.b[0]. When 'injection_save' is set to 1, +# arrays inside structs are skipped from extraction, and a +# info message is generated if the verbosity is high enough +proc extract_netlists {item_list {injection_save 0}} { + set extract_list [list] + foreach item $item_list { + set item_type [get_net_type $item] + if {$item_type == "Register" || $item_type == "Net" || $item_type == "Enum"} { + lappend extract_list $item + } elseif { $item_type == "Array"} { + set array_length [get_net_array_length $item] + if {$injection_save && [string first "." $item] != -1} { + if { $::verbosity >= 3 } { + echo "\[Netlist Extraction\] Net $item is an array inside a struct and will be skipped." + } + } else { + for {set i 0} {$i < $array_length} {incr i} { + set new_net "$item\[$i\]" + set extract_list [concat $extract_list [extract_netlists $new_net $injection_save]] + } + } + } elseif { $item_type == "Record"} { + set fields [get_record_field_names $item] + foreach field $fields { + set new_net $item.$field + set extract_list [concat $extract_list [extract_netlists $new_net $injection_save]] + } + } elseif { $item_type == "int"} { + # Ignore + } else { + if { $::verbosity >= 2 } { + echo "\[Netlist Extraction\] Unknown Type $item_type of net $item. Skipping..." + } + } + } + return $extract_list +} + +#################### +# State Netlists # +#################### + +# Example proc on how to extract state nets (not recursive) +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_state_netlist {base_path} { + return [extract_netlists [find signal $base_path/*_q] 1] +} + +# Example proc on how to extract state nets. +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_state_netlist_revursive {base_path} { + return [extract_netlists [find signal -r $base_path/*_q] 1] +} + +##################### +# Next State Nets # +##################### + +# Example proc on how to extract next state nets (not recursive). +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_next_state_netlist {base_path} { + return [find signal $base_path/*_d] +} + +# Example proc on how to extract next state nets. +# This is not guaranteed to work for every circuit, as net may not be named +# according to conventions! +proc get_next_state_netlist_recursive {base_path} { + return [find signal -r $base_path/*_d] +} + +######################### +# Circuit Output Nets # +######################### + +proc get_output_netlist {base_path} { + return [find signal -out $base_path/*] +} + +################## +# Get all nets # +################## + +proc extract_all_nets_recursive_filtered {base_path filter_list} { + + # recursively extract all signals from the circuit + set netlist [find signal -r $base_path/*]; + + # filter and sort the signals + set netlist_filtered [list]; + foreach net $netlist { + set ignore_net 0 + # ignore any net that matches any ignore pattern + foreach ignore_pattern $filter_list { + if {[string match $ignore_pattern $net]} { + set ignore_net 1 + break + } + } + # add all nets that are not ignored + if {$ignore_net == 0} { + lappend netlist_filtered $net + } + } + + # sort the filtered nets alphabetically + set netlist_filtered [lsort -dictionary $netlist_filtered] + + # recursively extract all nets and enums from arrays and structs + set netlist_extracted [extract_netlists $netlist_filtered 1] + + return $netlist_extracted +} diff --git a/hardware/scripts/questa/get_banks.tcl b/hardware/scripts/questa/get_banks.tcl new file mode 100644 index 000000000..6cbd6a9e6 --- /dev/null +++ b/hardware/scripts/questa/get_banks.tcl @@ -0,0 +1,83 @@ +# Search for all instances of tag banks for ROC +set pattern_match "*/i_snitch_read_only_cache/i_lookup/gen_sram/i_tag*" ; +# Get the list of instance paths +set inst_list [find instances -r $pattern_match] ; +# Initialize an empty list to strip off the architecture names +set ROC_tag_list [list] ; +foreach inst $inst_list { + set ipath [lindex $inst 0] + if {[string match $pattern_match $ipath]} { + lappend ROC_tag_list $ipath + } +} +# At this point, ROC_tag_list contains the list of instances only-- +# no architecture names +# +# Begin sorting list +set ROC_tag_list [lsort -dictionary $ROC_tag_list] +# Open a file to write out the list + +set fhandle [open "ROC_tag.txt" w] +foreach inst $ROC_tag_list { + # Print instance path, one per line + puts $fhandle $inst +} +# Close the file, done. +close $fhandle ; + + + +# Search for all instances of tag banks for L1I +set pattern_match "*]/i_tag*" +set inst_list [find instances -r $pattern_match] ; +set L1_tag_list [list] ; +foreach inst $inst_list { + set ipath [lindex $inst 0] + if {[string match $pattern_match $ipath]} { + lappend L1_tag_list $ipath + } +} +set L1_tag_list [lsort -dictionary $L1_tag_list] +set fhandle [open "L1I_tag.txt" w] +foreach inst $L1_tag_list { + puts $fhandle $inst +} +close $fhandle ; + + + +# Search for all instances of data banks for ROC +set pattern_match "*i_snitch_read_only_cache/i_lookup/i_data*" +set inst_list [find instances -r $pattern_match] ; +set ROC_data_list [list] ; +foreach inst $inst_list { + set ipath [lindex $inst 0] + if {[string match $pattern_match $ipath]} { + lappend ROC_data_list $ipath + } +} +set ROC_data_list [lsort -dictionary $ROC_data_list] +set fhandle [open "ROC_data.txt" w] +foreach inst $ROC_data_list { + puts $fhandle $inst +} +close $fhandle ; + + + +# Search for all instances of data banks for L1I +set pattern_match "*/i_snitch_icache/i_lookup/i_data*" +set inst_list [find instances -r $pattern_match] ; +set L1_data_list [list] ; +foreach inst $inst_list { + set ipath [lindex $inst 0] + if {[string match $pattern_match $ipath]} { + lappend L1_data_list $ipath + } +} +set L1_data_list [lsort -dictionary $L1_data_list] +set fhandle [open "L1I_data.txt" w] +foreach inst $L1_data_list { + puts $fhandle $inst +} +close $fhandle ; \ No newline at end of file diff --git a/hardware/scripts/questa/inject_fault.tcl b/hardware/scripts/questa/inject_fault.tcl new file mode 100644 index 000000000..9b206b438 --- /dev/null +++ b/hardware/scripts/questa/inject_fault.tcl @@ -0,0 +1,802 @@ +# Copyright 2021 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Author: Luca Rufer (lrufer@student.ethz.ch) + +# ============ List of variables that may be passed to this script ============ +# Any of these variables may not be changed while the fault injection script +# is running, unless noted otherwise. Changing any of the settings during +# runtime may result in undefined behaviour. +# ----------------------------------- General --------------------------------- +# 'verbosity' : Controls the amount of information printed during script +# execution. Possible values are: +# 0 : No statements at all +# 1 : Only important initializaion information +# 2 : Important information and occurences of bitflips +# (Recommended). Default +# 3 : All information that is possible +# 'log_injections' : Create a logfile of all injected faults, including +# timestamps, the absolute path of the flipped net, the +# value before the flip, the value after the flip and +# more. +# The logfile is named "fault_injection_.log". +# 0 : Disable logging (Default) +# 1 : Enable logging +# 'seed' : Set the seed for the number generator. Default: 12345 +# To reset the seed and start samping numbers from the +# start of the seed, it is in the responsibility of the +# user to call 'srand'. This is only done one in this +# script when it's sourced. +# 'print_statistics' : Print statistics about the fault injections at the end +# of the fault injection. Which statistics are printed +# also depends on other settings below. +# 0 : Don't print statistics +# 1 : Print statistics (Default) +# 'script_base_path' : Base path of all the scripts that are sourced here: +# Default is set to './' +# ------------------------------- Timing settings ----------------------------- +# 'inject_start_time' : Earliest time of the first fault injection. +# 'inject_stop_time' : Latest possible time for a fault injection. +# Set to 0 for no stop. +# 'injection_clock' : Absolute path to the net that is used as an injected +# trigger and clock. Can be a special trigger clock in +# the testbench, or the normal system clock. +# If 'injection_clock' is set to an empty string (""), +# this script will not perform periodical fault injection. +# 'injection_clock_trigger' : Signal value of 'injection_clock' that triggers +# the fault injection. If a normal clock of a rising edge +# triggered circuit is used as injection clock, it is +# recommended to set the trigger to '0', so injected +# flips can clearly be distinguished in the waveforms. +# 'fault_period' : Period of the fault injection in clock cycles of the +# injection clock. Set to 0 for only a single flip. +# 'rand_initial_injection_phase' : Set the phase relative to the 'fault_period' +# to a random initial value between 0 (inclusive) and +# 'fault_period' (exclusive). If multiple simulation +# with different seeds are performed, this option allows +# the injected faults to be evenly distributed accross +# the 'injection_clock' cycles. +# 0 : Disable random phase. The first fault injection +# is performed at the first injeciton clock trigger +# after the 'inject_start_time'. Default. +# 1 : Enable random phase. +# 'max_num_fault_inject' : Maximum number of faults to be injected. The number +# of faults injected may be lower than this if the +# simualtion finishes before, or if the 'inject_stop_time' +# is reached. If 'max_num_fault_inject' is set to 0, this +# setting is ignored (default). +# 'forced_injection_times' : Provide an explicit list of times when faults are +# to be injected into the simulation. If an empty +# 'forced_injection_signals' list is provided, the signals +# are selected at random according to the Flip settings. +# By default, this list is empty. +# Note that flips forced by this list are not bound +# by the 'inject_start_time' and 'inject_stop_time'. +# Flips forced by this list only count towards the +# 'max_num_fault_inject' limit if +# 'include_forced_inj_in_stats' is set to 1. +# 'forced_injection_signals' : Provide an explicit list of signals where faults +# are to be injected into the simulation at the +# 'forced_injection_times'. This list must have the same +# length as 'forced_injection_times'. Entries in the list +# must have the format {'signal_name' 'is_register}. +# For example: {{"tb/data_q" 1} {"tb/enable" 0}} +# If this list is empty, the signals to be injected at the +# 'forced_injection_times' are selected randomly according +# to the settings below (default). +# Note that listing enums, or signals with a width wider +# than one bit will case a random bit to be selected, +# which will alter the outcome of the periodic random +# fault injection. +# 'include_forced_inj_in_stats' : Select wether the forced injections should be +# included in the statistics or not. Including them in the +# statistics will also cause them to be logged (if logging +# is enabled) and variables like 'last_flipped_net' and +# 'last_injection_time' to be changed. +# 0: Don't include forced injections in statistics and +# logs (default). +# 1: Include forced injections in statistics and logs. +# 'signal_fault_duration' : Duration of faults injected into combinatorial +# signals, before the original value is restored. +# 'register_fault_duration' : Minumum duration of faults injected into +# registers. Faults injected into registers are not +# restored after the 'register_fault_duration' and will +# persist until overwritten by the circuit under test. +# -------------------------------- Flip settings ------------------------------ +# 'allow_multi_bit_upset' : Allow injecting another error in a Register that was +# already flipped and not driven to another value yet. +# 0 : Disable multi bit upsets (default) +# 1 : Enable multi bit upsets +# 'use_bitwidth_as_weight' : Use the bit width of a net as a weight for the +# random fault injection net selection. If this option +# is enabled, a N-bit net has an N times higher chance +# than a 1-bit net of being selected for fault injection. +# 0 : Disable using the bitwidth as weight and give every +# net the same chance of being picked (Default). +# 1 : Enable using the bit width of nets as weights. +# 'check_core_output_modification' : Check if an injected fault changes the +# output of the circuit under test. All nets in +# 'output_netlist' are checked. The result of the check +# is printed after every flip (if verbosity high enough), +# and logged to the logfile. +# 0 : Disable output modification checks. The check will +# be logged as 'x'. +# 1 : Enable output modification checks. +# 'check_core_next_state_modification' : Check if an injected fault changes the +# next state of the circuit under test. All nets in +# 'next_state_netlist' are checked. The result of the +# check is printed after every flip (if verbosity high +# enough), and logged to the logfile. +# 0 : Disable next state modification checks. The check +# will be logged as 'x'. +# 1 : Enable next state modification checks. +# 'reg_to_sig_ratio' : Ratio of Registers to combinatorial signals to be +# selected for a fault injection. Example: A value of 4 +# selects a ratio of 4:1, giving an 80% for a Register to +# be selected, and a 20% change of a combinatorial signal +# to be selected. If the provided +# 'inject_register_netlist' is empty, or the +# 'inject_signals_netlist' is empty, this parameter is +# ignored and nets are only selected from the non-empty +# netlist. +# Default value is 1, so the default ratio is 1:1. +# ---------------------------------- Netlists --------------------------------- +# 'inject_register_netlist' : List of absolute paths to Registers to be flipped +# in the simulation. This is used to simulate Single +# Event Upsets (SEUs). Flips injected in registers are not +# removed by the injection script. If the inject netlist +# is changed after this script was first called, the proc +# 'updated_inject_netlist' must be called. +# 'inject_signals_netlist' : List of absolute paths to combinatorial signals to +# be flipped in the simulation. This is used to simulate +# Single Event Transients (SETs). A fault injection +# drives the target signal for a 'fault_duration', and +# afterwards returns the signal to its original state. +# If the inject netlist is changed after this script was +# first called, the proc 'updated_inject_netlist' must be +# called. +# 'output_netlist' : List of absolute net or register paths to be used for +# the output modification check. +# 'next_state_netlist' : List of absolute net or register paths to be used for +# the next state modification check. +# 'assertion_disable_list' : List of absolute paths to named assertions that +# need to be disabled for during fault injecton. +# Assertions are enabled again after the simulation stop +# time. + +################################## +# Set default parameter values # +################################## + +# General +if {![info exists verbosity]} { set verbosity 2 } +if {![info exists log_injections]} { set log_injections 0 } +if {![info exists seed]} { set seed 12345 } +if {![info exists print_statistics]} { set print_statistics 1 } +if {![info exists script_base_path]} { set script_base_path "./" } +# Timing settings +if {![info exists inject_start_time]} { set inject_start_time 100ns } +if {![info exists inject_stop_time]} { set inject_stop_time 0 } +if {![info exists injection_clock]} { set injection_clock "clk" } +if {![info exists injection_clock_trigger]} { set injection_clock_trigger 0 } +if {![info exists fault_period]} { set fault_period 0 } +if {![info exists rand_initial_injection_phase]} { set rand_initial_injection_phase 0 } +if {![info exists max_num_fault_inject]} { set max_num_fault_inject 0 } +if {![info exists forced_injection_times]} { set forced_injection_times [list] } +if {![info exists forced_injection_signals]} { set forced_injection_signals [list] } +if {![info exists include_forced_inj_in_stats]} { set include_forced_inj_in_stats 0 } +if {![info exists signal_fault_duration]} { set signal_fault_duration 1ns } +if {![info exists register_fault_duration]} { set register_fault_duration 0ns } +# Flip settings +if {![info exists allow_multi_bit_upset]} { set allow_multi_bit_upset 0 } +if {![info exists check_core_output_modification]} { set check_core_output_modification 0 } +if {![info exists check_core_next_state_modification]} { set check_core_next_state_modification 0 } +if {![info exists reg_to_sig_ratio]} { set reg_to_sig_ratio 1 } +if {![info exists use_bitwidth_as_weight]} { set use_bitwidth_as_weight 0 } +# Netlists +if {![info exists inject_register_netlist]} { set inject_register_netlist [list] } +if {![info exists inject_signals_netlist]} { set inject_signals_netlist [list] } +if {![info exists output_netlist]} { set output_netlist [list] } +if {![info exists next_state_netlist]} { set next_state_netlist [list] } +if {![info exists assertion_disable_list]} { set assertion_disable_list [list] } + +# Additional checks +if {[llength $forced_injection_times] != [llength $forced_injection_signals] && \ + [llength $forced_injection_times] != 0 && \ + [llength $forced_injection_signals] != 0} { + if {$::verbosity >= 1} { + echo "\[Fault Injection\] Error: 'forced_injection_times' and \ + 'forced_injection_signals' don't have the same non-zero length!" + } + exit +} + +# Source generic netlist extraction procs +source [subst ${::script_base_path}extract_nets.tcl] + +######################################## +# Finish setup depending on settings # +######################################## + +# Common path sections of all nets where errors can be injected +set ::netlist_common_path_sections [list] + +proc restart_fault_injection {} { + # Start the Error injection script + if {$::verbosity >= 1} { + echo "\[Fault Injection\] Info: Injection script running." + } + + # cleanup from previous runs + if {[info exists ::forced_injection_when_labels]} { + foreach l $::forced_injection_when_labels { + catch {nowhen $l} + } + } + + # Last net that was flipped + set ::last_flipped_net "" + set ::last_injection_time -1 + + # Open the log file + if {$::log_injections} { + set time_stamp [exec date +%Y%m%d_%H%M%S] + set ::injection_log [open "fault_injection_$time_stamp.log" w+] + puts $::injection_log "timestamp,netname,pre_flip_value,post_flip_value,output_changed,new_state_changed" + } else { + set ::injection_log "" + } + + # Dictionary to keep track of injections + set ::inject_dict [dict create] + + # determine the phase for the initial fault injection + if {$::rand_initial_injection_phase} { + set ::prescaler [expr int(rand() * $::fault_period)] + } else { + set ::prescaler [expr $::fault_period - 1] + } + + # List of when-statement labels of forced injection times + set ::forced_injection_when_labels [list] + + # determine the first injection time + set start_time [earliest_time [concat $::forced_injection_times $::inject_start_time]] + + # determine the injection stop time + if {$::inject_stop_time == 0 && ![string equal $::injection_clock ""]} { + set stop_time 0 + } else { + set stop_time [latest_time [concat $::forced_injection_times $::inject_stop_time]] + } + + # Create all When statements + + # start fault injection + when -label inject_start "\$now == @$start_time" { + ::start_fault_injection + nowhen inject_start + } + + # periodically inject faults + if {![string equal $::injection_clock ""]} { + when -label inject_fault "\$now >= @$::inject_start_time and $::injection_clock == $::injection_clock_trigger" { + ::inject_trigger + } + } + + # forced injection times + for {set i 0} { $i < [llength $::forced_injection_times] } { incr i } { + set label "forced_injection_$i" + set t [lindex $::forced_injection_times $i] + if {[llength $::forced_injection_signals] == 0} { + set cmd "::inject_fault $::include_forced_inj_in_stats" + } else { + # Extract the signal infos + set signal_info [lindex $::forced_injection_signals $i] + foreach {signal_name is_register} $signal_info {} + if {$::include_forced_inj_in_stats} { + set cmd "fault_injection_pre_flip_statistics; \ + set flip_return \[::flipbit $signal_name $is_register\]; \ + fault_injection_post_flip_statistics $signal_name \$flip_return" + } else { + set cmd "::flipbit $signal_name $is_register" + } + } + # Create the when statement to flip the bit + when -label $label "\$now == @$t" "$cmd" + # Store the label + lappend ::forced_injection_when_labels $label + } + + # stop the simulation and output statistics + if {$stop_time != 0} { + when -label inject_stop "\$now > @$stop_time" { + ::stop_fault_injection + nowhen inject_stop + } + } +} + +proc start_fault_injection {} { + if {$::verbosity >= 1} { + echo "[time_ns $::now]: \[Fault Injection\] Starting fault injection." + } + # Disable Assertions + foreach assertion $::assertion_disable_list { + assertion enable -off $assertion + } + # Reset statistics + set ::stat_num_bitflips 0 + set ::stat_num_outputs_changed 0 + set ::stat_num_state_changed 0 + set ::stat_num_flip_propagated 0 +} + +################ +# User Procs # +################ + +proc stop_fault_injection {} { + # Stop fault injection + catch {nowhen inject_fault} + # Enable Assertions again + foreach assertion $::assertion_disable_list { + assertion enable -on $assertion + } + # Output simulation Statistics + if {$::verbosity >= 1 && $::print_statistics} { + echo " ========== Fault Injection Statistics ========== " + echo " Number of Bitflips : $::stat_num_bitflips" + if {$::check_core_output_modification} { + echo " Number of Bitflips propagated to outputs : $::stat_num_outputs_changed" + } + if {$::check_core_next_state_modification} { + echo " Number of Bitflips propagated to new state : $::stat_num_state_changed" + } + if {$::check_core_output_modification && $::check_core_next_state_modification} { + echo " Number of Bitflips propagated : $::stat_num_flip_propagated" + } + echo "" + } + # Close the logfile + if {$::log_injections} { + close $::injection_log + } + return $::stat_num_bitflips +} + +####################### +# Helper Procedures # +####################### + +proc earliest_time {time_list} { + if {[llength $time_list] == 0} { + return 0 + } + set earliest [lindex $time_list 0] + foreach t $time_list { + if {[ltTime $t $earliest]} { + set earliest $t + } + } + return $earliest +} + +proc latest_time {time_list} { + if {[llength $time_list] == 0} { + return -1 + } + set latest [lindex $time_list 0] + foreach t $time_list { + if {[gtTime $t $latest]} { + set latest $t + } + } + return $latest +} + +proc time_ns {time_ps} { + set time_str "" + append time_str "[expr $time_ps / 1000]" + set remainder [expr $time_ps % 1000] + if {$remainder != 0} { + append time_str "." + if {$remainder < 100} {append time_str "0"} + if {$remainder < 10} {append time_str "0"} + append time_str "$remainder" + } + append time_str " ns" + return $time_str +} + +proc find_common_path_sections {netlist} { + # Safety check if the list has any elements + if {[llength $netlist] == 0} { + return [list] + } + # Extract the first net as reference + set first_net [lindex $netlist 0] + set first_net_sections [split $first_net "/"] + # Determine the minimal number of sections in the netlist + set min_num_sections 9999 + foreach net $netlist { + set cur_path_sections [split $net "/"] + set num_sections [llength $cur_path_sections] + if {$num_sections < $min_num_sections} {set min_num_sections $num_sections} + } + # Create a match list + set match_list [list] + for {set i 0} {$i < $min_num_sections} {incr i} {lappend match_list 1} + # Test for every net which sections in its path matches the first net path + foreach net $netlist { + set cur_path_sections [split $net "/"] + # Test every section + for {set i 0} {$i < $min_num_sections} {incr i} { + # prevent redundant checking for speedup + if {[lindex $match_list $i] != 0} { + # check if the sections matches the first net section + if {[lindex $first_net_sections $i] != [lindex $cur_path_sections $i]} { + lset match_list $i 0 + } + } + } + } + return $match_list +} + +proc net_print_str {net_name} { + # Check if the list exists + if {[llength $::netlist_common_path_sections] == 0} { + return $net_name + } + # Split the netname path + set cur_path_sections [split $net_name "/"] + set print_str "" + set printed_dots 0 + # check sections individually + for {set i 0} {$i < [llength $cur_path_sections]} {incr i} { + # check if the section at the current index is a common to all paths + if {$i < [llength $::netlist_common_path_sections] && [lindex $::netlist_common_path_sections $i] == 1} { + # Do not print the dots if multiple sections match in sequence + if {!$printed_dots} { + # Print dots to indicate the path was shortened + append print_str "\[...\]" + if {$i != [llength $cur_path_sections] - 1} {append print_str "/"} + set printed_dots 1 + } + } else { + # Sections don't match, print the path section + append print_str "[lindex $cur_path_sections $i]" + if {$i != [llength $cur_path_sections] - 1} {append print_str "/"} + set printed_dots 0 + } + } + return $print_str +} + +proc calculate_weight_by_width {netlist} { + set total_weight 0 + set group_weight_dict [dict create] + set group_net_dict [dict create] + foreach net $netlist { + # determine the width of a net (used as weight) + set width [get_net_reg_width $net] + if {![dict exists $group_weight_dict $width]} { + # New width discovered, add new entry + dict set group_weight_dict $width $width + dict set group_net_dict $width [list $net] + } else { + dict incr group_weight_dict $width $width + dict lappend group_net_dict $width $net + } + } + # Sum weights of all groups + foreach group_weight [dict values $group_weight_dict] { + set total_weight [expr $total_weight + $group_weight] + } + return [list $total_weight $group_weight_dict $group_net_dict] +} + +proc updated_inject_netlist {} { + # print how many nets were found + set num_reg_nets [llength $::inject_register_netlist] + set num_comb_nets [llength $::inject_signals_netlist] + if {$::verbosity >= 1} { + echo "\[Fault Injection\] Selected $num_reg_nets Registers for fault injection." + echo "\[Fault Injection\] Selected $num_comb_nets combinatorial Signals for fault injection." + } + # print all nets that were found + if {$::verbosity >= 3} { + echo "Registers: " + foreach net $::inject_register_netlist { + echo " - [get_net_reg_width $net]-bit [get_net_type $net] : $net" + } + echo "Combinatorial Signals: " + foreach net $::inject_signals_netlist { + echo " - [get_net_reg_width $net]-bit [get_net_type $net] : $net" + } + echo "" + } + # determine the common sections + set combined_inject_netlist [concat $::inject_register_netlist $::inject_signals_netlist] + set ::netlist_common_path_sections [find_common_path_sections $combined_inject_netlist] + # determine the distribution of the nets + if {$::use_bitwidth_as_weight} { + set ::inject_register_distibrution_info [calculate_weight_by_width $::inject_register_netlist] + set ::inject_signals_distibrution_info [calculate_weight_by_width $::inject_signals_netlist] + } +} + +########################## +# Random Net Selection # +########################## + +proc select_random_net {} { + # Choose between Register and Signal + if {[llength $::inject_register_netlist] != 0 && \ + ([llength $::inject_signals_netlist] == 0 || \ + rand() * ($::reg_to_sig_ratio + 1) >= 1)} { + set is_register 1 + set selected_list $::inject_register_netlist + } else { + set is_register 0 + set selected_list $::inject_signals_netlist + } + # Select the distribution + if {$::use_bitwidth_as_weight} { + # select the distribution + if {$is_register} { + set distibrution_info $::inject_register_distibrution_info + } else { + set distibrution_info $::inject_signals_distibrution_info + } + # unpack the distribution + set distribution_total_weight [lindex $distibrution_info 0] + set distribution_weight_dict [lindex $distibrution_info 1] + set distribution_net_dict [lindex $distibrution_info 2] + # determine the group + set selec [expr rand() * $distribution_total_weight] + dict for {group group_weight} $distribution_weight_dict { + if {$selec <= $group_weight} { + break + } else { + set selec [expr $selec - $group_weight] + } + } + set selected_list [dict get $distribution_net_dict $group] + } + set idx [expr int(rand()*[llength $selected_list])] + set selected_net [lindex $selected_list $idx] + return [list $selected_net $is_register] +} + +################ +# Flip a Bit # +################ + +# flip a spefific bit of the given net name. returns a 1 if the bit could be flipped +proc flipbit {signal_name is_register} { + set success 0 + set old_value [examine -radixenumsymbolic $signal_name] + # check if net is an enum + if {[examine -radixenumnumeric $signal_name] != [examine -radixenumsymbolic $signal_name]} { + set old_value_numeric [examine -radix binary,enumnumeric $signal_name] + set new_value_numeric [expr int(rand()*([expr 2 ** [string length $old_value_numeric]]))] + while {$old_value_numeric == $new_value_numeric && [string length $old_value_numeric] != 1} { + set new_value_numeric [expr int(rand()*([expr 2 ** [string length $old_value_numeric]]))] + } + if {$is_register} { + force -freeze $signal_name $new_value_numeric -cancel $::register_fault_duration + } else { + force -freeze $signal_name $new_value_numeric, $old_value_numeric $::signal_fault_duration -cancel $::signal_fault_duration + } + set success 1 + } else { + set flip_signal_name $signal_name + set bin_val [examine -radix binary $signal_name] + set len [string length $bin_val] + set flip_index 0 + if {$len != 1} { + set flip_index [expr int(rand()*$len)] + set flip_signal_name $signal_name\($flip_index\) + } + set old_bit_value "0" + set new_bit_value "1" + if {[string index $bin_val [expr $len - 1 - $flip_index]] == "1"} { + set new_bit_value "0" + set old_bit_value "1" + } + if {$is_register} { + force -freeze $flip_signal_name $new_bit_value -cancel $::register_fault_duration + } else { + force -freeze $flip_signal_name $new_bit_value, $old_bit_value $::signal_fault_duration -cancel $::signal_fault_duration + } + if {[examine -radix binary $signal_name] != $bin_val} {set success 1} + } + set new_value [examine -radixenumsymbolic $signal_name] + set result [list $success $old_value $new_value] + return $result +} + +################################ +# Fault Injection Statistics # +################################ + +proc fault_injection_pre_flip_statistics {} { + # record the output before the flip + set ::pre_flip_out_val [list] + if {$::check_core_output_modification} { + foreach net $::output_netlist { + lappend ::pre_flip_out_val [examine $net] + } + } + # record the new state before the flip + set ::pre_flip_next_state_val [list] + if {$::check_core_next_state_modification} { + foreach net $::next_state_netlist { + lappend ::pre_flip_next_state_val [examine $net] + } + } +} + +proc fault_injection_post_flip_statistics {flipped_net flip_return} { + incr ::stat_num_bitflips + set ::last_flipped_net $flipped_net + set ::last_injection_time $::now + + set flip_propagated 0 + # record the output after the flip + set post_flip_out_val [list] + if {$::check_core_output_modification} { + foreach net $::output_netlist { + lappend post_flip_out_val [examine $net] + } + # check if the output changed + set output_state "not modified" + set output_changed [expr ![string equal $::pre_flip_out_val $post_flip_out_val]] + if {$output_changed} { + set output_state "changed" + incr ::stat_num_outputs_changed + set flip_propagated 1 + } + } else { + set output_changed "x" + } + # record the new state before the flip + set post_flip_next_state_val [list] + if {$::check_core_next_state_modification} { + foreach net $::next_state_netlist { + lappend post_flip_next_state_val [examine $net] + } + # check if the new state changed + set new_state_state "not modified" + set new_state_changed [expr ![string equal $::pre_flip_next_state_val $post_flip_next_state_val]] + if {$new_state_changed} { + set new_state_state "changed" + incr ::stat_num_state_changed + set flip_propagated 1 + } + } else { + set new_state_changed "x" + } + + if {$flip_propagated} { + incr ::stat_num_flip_propagated + } + # display the result + if {$::verbosity >= 2} { + set print_str "[time_ns $::now]: \[Fault Injection\] " + append print_str "Flipped net [net_print_str $flipped_net] from [lindex $flip_return 1] to [lindex $flip_return 2]. " + if {$::check_core_output_modification} { + append print_str "Output signals $output_state. " + } + if {$::check_core_next_state_modification} { + append print_str "New state $new_state_state. " + } + echo $print_str + } + # Log the result + if {$::log_injections} { + puts $::injection_log "$::now,$flipped_net,[lindex $flip_return 1],[lindex $flip_return 2],$output_changed,$new_state_changed" + flush $::injection_log + } +} + +############################## +# Fault injection routine # +############################## + +proc inject_fault {include_in_statistics} { + # If enabled, prepare the statistics for the flip + if {$include_in_statistics} { fault_injection_pre_flip_statistics } + + # Questa currently has a bug that it won't force certain nets. So we retry + # until we successfully flip a net. + # The bug primarily affects arrays of structs: + # If you try to force a member/field of a struct in an array, QuestaSim will + # flip force that member/field in the struct/record with index 0 in the + # array, not at the array index that was specified. + set success 0 + set attempts 0 + while {!$success && [incr attempts] < 50} { + # get a random net + set net_selc_info [::select_random_net] + set net_to_flip [lindex $net_selc_info 0] + set is_register [lindex $net_selc_info 1] + # Check if the selected net is allowed to be flipped + set allow_flip 1 + if {$is_register && !$::allow_multi_bit_upset} { + set net_value [examine -radixenumsymbolic $net_to_flip] + if {[dict exists $::inject_dict $net_to_flip] && [dict get $::inject_dict $net_to_flip] == $net_value} { + set allow_flip 0 + if {$::verbosity >= 3} { + echo "[time_ns $::now]: \[Fault Injection\] Tried to flip [net_print_str $net_to_flip], but was already flipped." + } + } + } + # flip the random net + if {$allow_flip} { + if {[catch {set flip_return [::flipbit $net_to_flip $is_register]}]} { + set flip_return {0 "x" "x"} + } + if {[lindex $flip_return 0]} { + set success 1 + if {$is_register && !$::allow_multi_bit_upset} { + # save the new value to the dict + dict set ::inject_dict $net_to_flip [examine -radixenumsymbolic $net_to_flip] + } + } else { + if {$::verbosity >= 3} { + echo "[time_ns $::now]: \[Fault Injection\] Failed to flip [net_print_str $net_to_flip]. Choosing another one." + } + } + } + } + if {$success && !$include_in_statistics} { + if {$::verbosity >= 2} { + echo "[time_ns $::now]: \[Fault Injection\] \ + Flipped net [net_print_str $net_to_flip] \ + from [lindex $flip_return 1] \ + to [lindex $flip_return 2]. " + } + } + if {$success && $include_in_statistics} { + fault_injection_post_flip_statistics $net_to_flip $flip_return + } +} + +proc ::inject_trigger {} { + # check if any nets are selected for injection + if {[llength $::inject_register_netlist] == 0 && \ + [llength $::inject_signals_netlist] == 0} { + return + } + # check if we reached the injection limit + if {($::max_num_fault_inject != 0) && ($::stat_num_bitflips >= $::max_num_fault_inject)} { + # Stop the simulation + if {$::verbosity >= 2} { + echo "\[Fault Injection\] Injection limit ($::max_num_fault_inject) reached. Stopping error injection..." + } + # Disable the trigger (if not already done so) + catch {nowhen inject_fault} + return + } + # increase prescaler + incr ::prescaler + if {$::prescaler == $::fault_period} { + set ::prescaler 0 + # inject a fault + ::inject_fault 1 + } +} + +# Set the seed for the first time +expr srand($::seed) + +# Update the inject netlist +updated_inject_netlist + +# Reset the fault injection +restart_fault_injection diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl new file mode 100644 index 000000000..23563ccc1 --- /dev/null +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -0,0 +1,65 @@ +transcript quietly + +set verbosity 3 +set log_injections 1 +set seed 12345 +set script_base_path "/scratch/sem23h18/project/mempool/hardware/scripts/questa/" +set inject_start_time 2000ns +set inject_stop_time 0 +#use a clock in the tb +set injection_clock "/mempool_tb/fault_clk" +set injection_clock_trigger 0 +set fault_period 100 +set rand_initial_injection_phase 0 +set max_num_fault_inject 20 +set signal_fault_duration 20ns +set register_fault_duration 0ns + +set allow_multi_bit_upset 1 +set use_bitwidth_as_weight 1 +set check_core_output_modification 0 +set check_core_next_state_modification 0 +set reg_to_sig_ratio 1 + +#proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} + + + +#set inject_register_netlist [find nets [base_path]/*] + +#/mempool_tb/dut/i_mempool_cluster/gen_groups[3]/i_group/gen_tiles[13]/i_tile/gen_caches[0]/i_snitch_icache/i_lookup/i_data/sram +#/mempool_tb/dut/i_mempool_cluster/gen_groups[3]/i_group/gen_tiles[15]/i_tile/gen_caches[0]/i_snitch_icache/i_lookup/gen_scm/g_sets[1]/i_tag/MemContentxDP + +set itag_sram_path "/mempool_tb/dut/i_mempool_cluster/gen_groups[3]/i_group/gen_tiles[15]/i_tile/gen_caches[0]/i_snitch_icache/i_lookup/gen_scm/g_sets[1]/i_tag/MemContentxDP" + +# Determine the maximum possible index for the first index +set max_index_value 31 ; # Replace with the actual maximum value +#debug : 32, normal 127 + +# Determine the largest possible second index +set max_tag_line_index 25 ; # Replace with the actual maximum value +#debug 57, normal 47 + +# Add the signal paths with maximum indices to the inject_register_netlist +for {set index $max_index_value} {$index >= 0} {incr index -1} { + set signal_path "${itag_sram_path}[${index}][${max_tag_line_index}]" + lappend inject_register_netlist $signal_path +} + +set max_data_index 63; +#set max_data_index [expr { +# $max_index_value * 2 + 1 +#}] +set idata_sram_path "/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram"; +for {set index $max_data_index} {$index >= 0} {incr index -1} { + set signal_path "${idata_sram_path}[${index}]" + #lappend inject_register_netlist $signal_path +} + + +set inject_signals_netlist [] +set output_netlist [] +set next_state_netlist [] +set assertion_disable_list [] + +source ${::script_base_path}inject_fault.tcl diff --git a/hardware/scripts/questa/run.tcl b/hardware/scripts/questa/run.tcl index 9310e79e5..51d069355 100644 --- a/hardware/scripts/questa/run.tcl +++ b/hardware/scripts/questa/run.tcl @@ -1,7 +1,13 @@ # Copyright 2021 ETH Zurich and University of Bologna. # Solderpad Hardware License, Version 0.51, see LICENSE for details. # SPDX-License-Identifier: SHL-0.51 +set allow_faults 1 +#[lindex $argv 0] +if {$allow_faults ==1} { + source ../scripts/questa/mempool_inject_fault.tcl + +} do ../scripts/questa/wave.tcl log -r /* run -a diff --git a/hardware/tb/mempool_tb.sv b/hardware/tb/mempool_tb.sv index dcefc5365..848827dbe 100644 --- a/hardware/tb/mempool_tb.sv +++ b/hardware/tb/mempool_tb.sv @@ -393,4 +393,28 @@ module mempool_tb; `endif `endif + /******************************** + * Fault injection on icaches * + ********************************/ + + `ifdef FAULT_INJECTION_ICACHE + localparam bit fault_injection_icache = `FAULT_INJECTION_ICACHE; + `ifdef FAULT_RATE + localparam int fault_rate = `FAULT_RATE; + `else + localparam int fault_rate = 1000; + `endif + `else + localparam bit fault_injection_icache = 1; + localparam int fault_rate = 100; + `endif + logic fault_clk; + if (fault_injection_icache) begin : gen_fault_clock + // Toggling the clock + always #(ClockPeriod*fault_rate/2) fault_clk = !fault_clk; + initial begin + fault_clk = 1'b0; + end + end + endmodule : mempool_tb From 119c9681ddad22abe3cbb08d25deb37a804ca00e Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Thu, 9 Nov 2023 17:45:21 +0100 Subject: [PATCH 19/46] [hardware] Injection scripts working --- hardware/scripts/questa/get_banks.tcl | 60 +++++++------- .../scripts/questa/mempool_inject_fault.tcl | 83 ++++++++++++------- 2 files changed, 84 insertions(+), 59 deletions(-) diff --git a/hardware/scripts/questa/get_banks.tcl b/hardware/scripts/questa/get_banks.tcl index 6cbd6a9e6..1d7debee0 100644 --- a/hardware/scripts/questa/get_banks.tcl +++ b/hardware/scripts/questa/get_banks.tcl @@ -7,6 +7,7 @@ set ROC_tag_list [list] ; foreach inst $inst_list { set ipath [lindex $inst 0] if {[string match $pattern_match $ipath]} { + append ipath "/sram" lappend ROC_tag_list $ipath } } @@ -14,16 +15,16 @@ foreach inst $inst_list { # no architecture names # # Begin sorting list -set ROC_tag_list [lsort -dictionary $ROC_tag_list] -# Open a file to write out the list - -set fhandle [open "ROC_tag.txt" w] -foreach inst $ROC_tag_list { - # Print instance path, one per line - puts $fhandle $inst -} -# Close the file, done. -close $fhandle ; +#set ROC_tag_list [lsort -dictionary $ROC_tag_list] +## Open a file to write out the list +# +#set fhandle [open "ROC_tag.txt" w] +#foreach inst $ROC_tag_list { +# # Print instance path, one per line +# puts $fhandle $inst +#} +## Close the file, done. +#close $fhandle ; @@ -34,15 +35,16 @@ set L1_tag_list [list] ; foreach inst $inst_list { set ipath [lindex $inst 0] if {[string match $pattern_match $ipath]} { + append ipath "/MemContentxDP" lappend L1_tag_list $ipath } } -set L1_tag_list [lsort -dictionary $L1_tag_list] -set fhandle [open "L1I_tag.txt" w] -foreach inst $L1_tag_list { - puts $fhandle $inst -} -close $fhandle ; +#set L1_tag_list [lsort -dictionary $L1_tag_list] +#set fhandle [open "L1I_tag.txt" w] +#foreach inst $L1_tag_list { +# puts $fhandle $inst +#} +#close $fhandle ; @@ -53,15 +55,16 @@ set ROC_data_list [list] ; foreach inst $inst_list { set ipath [lindex $inst 0] if {[string match $pattern_match $ipath]} { + append ipath "/sram" lappend ROC_data_list $ipath } } -set ROC_data_list [lsort -dictionary $ROC_data_list] -set fhandle [open "ROC_data.txt" w] -foreach inst $ROC_data_list { - puts $fhandle $inst -} -close $fhandle ; +#set ROC_data_list [lsort -dictionary $ROC_data_list] +#set fhandle [open "ROC_data.txt" w] +#foreach inst $ROC_data_list { +# puts $fhandle $inst +#} +#close $fhandle ; @@ -72,12 +75,13 @@ set L1_data_list [list] ; foreach inst $inst_list { set ipath [lindex $inst 0] if {[string match $pattern_match $ipath]} { + append ipath "/sram" lappend L1_data_list $ipath } } -set L1_data_list [lsort -dictionary $L1_data_list] -set fhandle [open "L1I_data.txt" w] -foreach inst $L1_data_list { - puts $fhandle $inst -} -close $fhandle ; \ No newline at end of file +#set L1_data_list [lsort -dictionary $L1_data_list] +#set fhandle [open "L1I_data.txt" w] +#foreach inst $L1_data_list { +# puts $fhandle $inst +#} +#close $fhandle ; \ No newline at end of file diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index 23563ccc1..807daf734 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -7,54 +7,75 @@ set script_base_path "/scratch/sem23h18/project/mempool/hardware/scripts/questa/ set inject_start_time 2000ns set inject_stop_time 0 #use a clock in the tb -set injection_clock "/mempool_tb/fault_clk" +set injection_clock "/mempool_tb/clk" set injection_clock_trigger 0 -set fault_period 100 +set fault_period 500 set rand_initial_injection_phase 0 -set max_num_fault_inject 20 +set max_num_fault_inject 2000 set signal_fault_duration 20ns set register_fault_duration 0ns -set allow_multi_bit_upset 1 +set allow_multi_bit_upset 0 set use_bitwidth_as_weight 1 set check_core_output_modification 0 set check_core_next_state_modification 0 set reg_to_sig_ratio 1 #proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} +source ${::script_base_path}get_banks.tcl +proc add_signals_injection {tag_list tag_idx tag_max_width data_list no_databanks} { + foreach inst $tag_list { + for {set index $tag_idx} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}][${tag_max_width}]" + lappend inject_register_netlist $signal_path + } + } + foreach inst $data_list { + for {set index $no_databanks} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}]" + lappend inject_register_netlist $signal_path + } + } +} +set inject_register_netlist {} +set max_idx_icache 31 +set max_tag_width_icache 25 +set no_banks_icache 63 +#add_signals_injection $L1_tag_list $max_idx_icache $max_tag_width_icache $L1_data_list $no_banks_icache -#set inject_register_netlist [find nets [base_path]/*] - -#/mempool_tb/dut/i_mempool_cluster/gen_groups[3]/i_group/gen_tiles[13]/i_tile/gen_caches[0]/i_snitch_icache/i_lookup/i_data/sram -#/mempool_tb/dut/i_mempool_cluster/gen_groups[3]/i_group/gen_tiles[15]/i_tile/gen_caches[0]/i_snitch_icache/i_lookup/gen_scm/g_sets[1]/i_tag/MemContentxDP - -set itag_sram_path "/mempool_tb/dut/i_mempool_cluster/gen_groups[3]/i_group/gen_tiles[15]/i_tile/gen_caches[0]/i_snitch_icache/i_lookup/gen_scm/g_sets[1]/i_tag/MemContentxDP" - -# Determine the maximum possible index for the first index -set max_index_value 31 ; # Replace with the actual maximum value -#debug : 32, normal 127 + foreach inst $L1_tag_list { + for {set index $max_idx_icache} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}][${max_tag_width_icache}]" + #lappend inject_register_netlist $signal_path + } + } + foreach inst $L1_data_list { + for {set index $no_banks_icache} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}]" + lappend inject_register_netlist $signal_path + } + } -# Determine the largest possible second index -set max_tag_line_index 25 ; # Replace with the actual maximum value -#debug 57, normal 47 +set max_idx_rocache 63 +set max_tag_width_rocache 47 -# Add the signal paths with maximum indices to the inject_register_netlist -for {set index $max_index_value} {$index >= 0} {incr index -1} { - set signal_path "${itag_sram_path}[${index}][${max_tag_line_index}]" - lappend inject_register_netlist $signal_path -} +set no_banks_rocache 127 +#add_signals_injection $ROC_tag_list $max_idx_rocache $max_tag_width_rocache $L1_data_list $no_banks_rocache -set max_data_index 63; -#set max_data_index [expr { -# $max_index_value * 2 + 1 -#}] -set idata_sram_path "/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram"; -for {set index $max_data_index} {$index >= 0} {incr index -1} { - set signal_path "${idata_sram_path}[${index}]" - #lappend inject_register_netlist $signal_path -} + foreach inst $ROC_tag_list { + for {set index $max_idx_rocache} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}][${max_tag_width_rocache}]" + lappend inject_register_netlist $signal_path + } + } + foreach inst $ROC_data_list { + for {set index $no_banks_rocache} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}]" + lappend inject_register_netlist $signal_path + } + } set inject_signals_netlist [] From 05c8e2e0d1d32eb9c69436053cd53e95b38dbff2 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 13 Nov 2023 11:16:54 +0100 Subject: [PATCH 20/46] [hardware] Changed number of parity bits in the instruction data banks --- .../snitch/src/snitch_icache/snitch_icache_lookup_serial.sv | 5 +++-- hardware/scripts/questa/mempool_inject_fault.tcl | 6 +++--- hardware/scripts/questa/run.tcl | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 111c640bc..26a005682 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -38,9 +38,9 @@ module snitch_icache_lookup_serial #( input logic write_valid_i, output logic write_ready_o ); - localparam bit RELIABILITY_MODE = CFG.ENABLE_RELIABILITY; + localparam int RELIABILITY_MODE = CFG.ENABLE_RELIABILITY; localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; - localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd4 : '0; // TODO: propagate it up as a parameter + localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd8 : '0; // TODO: propagate it up as a parameter `ifndef SYNTHESIS initial assert(CFG != '0); @@ -408,6 +408,7 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin assign data_parity_inv_d.addr = data_req_q.addr; assign data_parity_inv_d.cset = data_req_q.id; + // add check that next address is different from previous one `FFL(data_parity_inv_q, (data_fault_ready && !tag_handshake) ? '0 : data_parity_inv_d, tag_handshake || data_fault_ready, '0, clk_i, rst_ni) //reset value when it has been invalidated and there is no new value `FFL(hit_invalid_q, data_parity_inv_d.parity_error, tag_handshake, '0, clk_i, rst_ni) end diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index 807daf734..6c31df62c 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -4,12 +4,12 @@ set verbosity 3 set log_injections 1 set seed 12345 set script_base_path "/scratch/sem23h18/project/mempool/hardware/scripts/questa/" -set inject_start_time 2000ns +set inject_start_time 20000ns set inject_stop_time 0 #use a clock in the tb set injection_clock "/mempool_tb/clk" set injection_clock_trigger 0 -set fault_period 500 +set fault_period 10 set rand_initial_injection_phase 0 set max_num_fault_inject 2000 set signal_fault_duration 20ns @@ -48,7 +48,7 @@ set no_banks_icache 63 foreach inst $L1_tag_list { for {set index $max_idx_icache} {$index >= 0} {incr index -1} { set signal_path "${inst}[${index}][${max_tag_width_icache}]" - #lappend inject_register_netlist $signal_path + lappend inject_register_netlist $signal_path } } foreach inst $L1_data_list { diff --git a/hardware/scripts/questa/run.tcl b/hardware/scripts/questa/run.tcl index 51d069355..46b3041b4 100644 --- a/hardware/scripts/questa/run.tcl +++ b/hardware/scripts/questa/run.tcl @@ -1,7 +1,7 @@ # Copyright 2021 ETH Zurich and University of Bologna. # Solderpad Hardware License, Version 0.51, see LICENSE for details. # SPDX-License-Identifier: SHL-0.51 -set allow_faults 1 +set allow_faults 1 #[lindex $argv 0] if {$allow_faults ==1} { From d5e983ac03bef1cd245606d72d78db2ec4c749ac Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Thu, 16 Nov 2023 16:40:52 +0100 Subject: [PATCH 21/46] [hardware] Added assertions in lookup --- .../src/snitch_icache/snitch_icache_lookup_serial.sv | 7 +++++-- hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 26a005682..63f85bd8e 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -40,7 +40,7 @@ module snitch_icache_lookup_serial #( ); localparam int RELIABILITY_MODE = CFG.ENABLE_RELIABILITY; localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; - localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd8 : '0; // TODO: propagate it up as a parameter + localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd4 : '0; // TODO: propagate it up as a parameter `ifndef SYNTHESIS initial assert(CFG != '0); @@ -256,7 +256,8 @@ module snitch_icache_lookup_serial #( .cnt_o ( tag_rsp_s.cset ), .empty_o ( ) ); - + //assert property (@(negedge clk_i) |tag_enable && in_ready_o |-> ##1 $countones(line_hit) <=1 ) else $error("two or more sets hit!, index=%h, address=%h", tag_req_q.addr >> CFG.LINE_ALIGN, tag_req_q.addr); + //assert property (@(negedge clk_i) |tag_enable && in_ready_o |-> ##1 tag_rdata[0] != tag_rdata[1] || ((tag_rdata[0] == tag_rdata[1]) && !tag_rdata[0][CFG.TAG_WIDTH+1])) else $error("two sets with same tag!, tag1=%h, tag2=%h", tag_rdata[0], tag_rdata[1]); // Buffer the metadata on a valid handshake. Stall on write (implicit in req_valid/ready) `FFL(tag_req_q, tag_req_d, req_valid && req_ready, '0, clk_i, rst_ni) if(RELIABILITY_MODE) begin @@ -409,6 +410,8 @@ module snitch_icache_lookup_serial #( assign data_parity_inv_d.addr = data_req_q.addr; assign data_parity_inv_d.cset = data_req_q.id; // add check that next address is different from previous one + //assign same_invalidation_address = (data_req_q.addr == data_parity_inv_q.addr) && hit_invalid; //if address is the same as before, no need to invalidate it again + ///*|| (tag_handshake && same_invalidation_address)*/) ? /*{data_parity_inv_q.addr, data_parity_inv_q.cset, 1'b0}*/ `FFL(data_parity_inv_q, (data_fault_ready && !tag_handshake) ? '0 : data_parity_inv_d, tag_handshake || data_fault_ready, '0, clk_i, rst_ni) //reset value when it has been invalidated and there is no new value `FFL(hit_invalid_q, data_parity_inv_d.parity_error, tag_handshake, '0, clk_i, rst_ni) end diff --git a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv index 8ef823ef2..b5d2f3438 100644 --- a/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv +++ b/hardware/deps/snitch/tb/src/snitch_read_only_cache_tb.sv @@ -271,7 +271,7 @@ class const_axi_slave #( endclass `include "common_cells/assertions.svh" -localparam debug = 0; +localparam debug = 1; module snitch_read_only_cache_tb import snitch_pkg::*; #( parameter int unsigned AxiAddrWidth = 32, parameter int unsigned AxiDataWidth = debug? 32:128, From 3456ab014037cf80957438a30852396dbdb6b89e Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 19 Nov 2023 14:16:40 +0100 Subject: [PATCH 22/46] [hardware] Added fault option in make file --- hardware/Makefile | 2 ++ hardware/scripts/questa/run.tcl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hardware/Makefile b/hardware/Makefile index 615046b72..a1062f7db 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -47,6 +47,8 @@ verilator_top ?= mempool_tb_verilator python ?= python3 # Enable tracing snitch_trace ?= 0 +# Enable faults +icache_faults ?= 0 # Check if the specified QuestaSim version exists ifeq (, $(shell which $(questa_cmd))) diff --git a/hardware/scripts/questa/run.tcl b/hardware/scripts/questa/run.tcl index 46b3041b4..bceb31d3c 100644 --- a/hardware/scripts/questa/run.tcl +++ b/hardware/scripts/questa/run.tcl @@ -4,7 +4,7 @@ set allow_faults 1 #[lindex $argv 0] -if {$allow_faults ==1} { +if {$::env(icache_faults) ==1} { source ../scripts/questa/mempool_inject_fault.tcl } From 81c33f2ced90a4f7124121452ba0585cc7dfb3cc Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 26 Nov 2023 19:22:20 +0100 Subject: [PATCH 23/46] [snitch] Added waves for debugging --- hardware/scripts/questa/run.tcl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hardware/scripts/questa/run.tcl b/hardware/scripts/questa/run.tcl index bceb31d3c..ba6649ff4 100644 --- a/hardware/scripts/questa/run.tcl +++ b/hardware/scripts/questa/run.tcl @@ -9,5 +9,13 @@ if {$::env(icache_faults) ==1} { } do ../scripts/questa/wave.tcl +add wave -Group gr1_axitocache {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/*} +add wave -Group gr1_lookup {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_lookup/*} +add wave -Group gr1_roc {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/*} +add wave -Group gr1_refill {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_refill/*} +add wave -Group gr1_handler {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_handler/*} +add wave -Group gr1_splittertable {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/i_axi_burst_splitter_table/*} +add wave -Group idq {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/i_axi_burst_splitter_table/i_idq/*} + log -r /* run -a From 2ac39b8f352b4413729577a140f2ea9c1b4a91d9 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 26 Nov 2023 19:24:54 +0100 Subject: [PATCH 24/46] [snitch] icache handler support for fault having priority over write request --- .../deps/snitch/src/snitch_icache/snitch_icache_handler.sv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv index 04d9dabce..70e5c49a2 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv @@ -152,7 +152,7 @@ module snitch_icache_handler #( .empty_o ( ) ); - // Gurarntee ordering + // Guarantee ordering // Check if there is a miss in flight from this ID. In that case, stall all // further requests to guarantee correct ordering of requests. logic [CFG.ID_WIDTH_RESP-1:0] miss_in_flight_d, miss_in_flight_q; @@ -215,7 +215,7 @@ module snitch_icache_handler #( // The cache lookup was a miss, but there is already a pending // refill that covers the line. - end else if (pending) begin + end else if (pending && !(write_valid_o && !write_ready_i)) begin push_index = pending_id; push_enable = 1; @@ -339,7 +339,7 @@ module snitch_icache_handler #( in_rsp_served_q <= 0; end else begin write_served_q <= rsp_valid & ~rsp_ready & (write_served_q | write_ready_i); - in_rsp_served_q <= rsp_valid & ~rsp_ready & (in_rsp_served_q | in_rsp_ready_i); + in_rsp_served_q <= rsp_valid & ~rsp_ready & (in_rsp_served_q | in_rsp_ready_i) & !push_enable; end end From 0554fa4eada02576bb90fa689a373073529d45dd Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 26 Nov 2023 19:32:01 +0100 Subject: [PATCH 25/46] [hardware] Modified fault injection clock --- hardware/scripts/questa/mempool_inject_fault.tcl | 16 +++++++++------- hardware/tb/mempool_tb.sv | 4 +++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index 6c31df62c..da059be11 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -1,17 +1,17 @@ transcript quietly -set verbosity 3 +set verbosity 1 set log_injections 1 set seed 12345 set script_base_path "/scratch/sem23h18/project/mempool/hardware/scripts/questa/" -set inject_start_time 20000ns +set inject_start_time 10ns set inject_stop_time 0 -#use a clock in the tb +#use a clock in the tb, fault_clk is 10 times faster than clk set injection_clock "/mempool_tb/clk" set injection_clock_trigger 0 -set fault_period 10 +set fault_period 1 set rand_initial_injection_phase 0 -set max_num_fault_inject 2000 +set max_num_fault_inject 12000 set signal_fault_duration 20ns set register_fault_duration 0ns @@ -47,7 +47,8 @@ set no_banks_icache 63 foreach inst $L1_tag_list { for {set index $max_idx_icache} {$index >= 0} {incr index -1} { - set signal_path "${inst}[${index}][${max_tag_width_icache}]" + set signal_path "${inst}[${index}]" +#set signal_path "${inst}[${index}][${max_tag_width_icache}]" lappend inject_register_netlist $signal_path } } @@ -66,7 +67,8 @@ set no_banks_rocache 127 foreach inst $ROC_tag_list { for {set index $max_idx_rocache} {$index >= 0} {incr index -1} { - set signal_path "${inst}[${index}][${max_tag_width_rocache}]" +#set signal_path "${inst}[${index}][${max_tag_width_rocache}]" + set signal_path "${inst}[${index}]" lappend inject_register_netlist $signal_path } } diff --git a/hardware/tb/mempool_tb.sv b/hardware/tb/mempool_tb.sv index 848827dbe..c2e1c2ee0 100644 --- a/hardware/tb/mempool_tb.sv +++ b/hardware/tb/mempool_tb.sv @@ -40,15 +40,17 @@ module mempool_tb; * Clock and Reset Generation * ********************************/ - logic clk; + logic clk, fault_clk; logic rst_n; // Toggling the clock always #(ClockPeriod/2) clk = !clk; + always #(ClockPeriod/20) fault_clk = !fault_clk; // Controlling the reset initial begin clk = 1'b1; + fault_clk = 1'b1; rst_n = 1'b0; repeat (5) From ee71a4cd466fba06ba785820edf65e8fd6bd16d2 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 27 Nov 2023 08:15:16 +0100 Subject: [PATCH 26/46] [hardware] Fixed handler flag --- hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv index 70e5c49a2..b87fda9b5 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv @@ -339,7 +339,7 @@ module snitch_icache_handler #( in_rsp_served_q <= 0; end else begin write_served_q <= rsp_valid & ~rsp_ready & (write_served_q | write_ready_i); - in_rsp_served_q <= rsp_valid & ~rsp_ready & (in_rsp_served_q | in_rsp_ready_i) & !push_enable; + in_rsp_served_q <= rsp_valid & ~rsp_ready & (in_rsp_served_q | in_rsp_ready_i); end end From 70eeb2d7111d7d3d070ac7e86fbd4ea01568736b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 27 Nov 2023 08:20:42 +0100 Subject: [PATCH 27/46] [hardware] Changed parity width --- .../snitch/src/snitch_icache/snitch_icache_lookup_serial.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 63f85bd8e..1da4d35c0 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -40,7 +40,7 @@ module snitch_icache_lookup_serial #( ); localparam int RELIABILITY_MODE = CFG.ENABLE_RELIABILITY; localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; - localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd4 : '0; // TODO: propagate it up as a parameter + localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd8 : '0; // TODO: propagate it up as a parameter `ifndef SYNTHESIS initial assert(CFG != '0); From 867f6fe72a9e05ce22a521330bb4036d08d9574c Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 27 Nov 2023 11:29:35 +0100 Subject: [PATCH 28/46] [hardware] Set injection clock --- hardware/Makefile | 2 -- .../scripts/questa/mempool_inject_fault.tcl | 2 +- hardware/tb/mempool_tb.sv | 24 ------------------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/hardware/Makefile b/hardware/Makefile index a1062f7db..05d71149a 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -100,8 +100,6 @@ vlog_defs += -DDMAS_PER_GROUP=$(dmas_per_group) vlog_defs += -DAXI_HIER_RADIX=$(axi_hier_radix) -DAXI_MASTERS_PER_GROUP=$(axi_masters_per_group) vlog_defs += -DSEQ_MEM_SIZE=$(seq_mem_size) -DXQUEUE_SIZE=$(xqueue_size) vlog_defs += -DREL_ROCACHE=$(rel_rocache) -DREL_L1ICACHE=$(rel_l1icache) -vlog_defs += -DFAULT_INJECTION_ICACHE=$(fault_injection) -vlog_defs += -DFAULT_RATE=$(fault_rate) # Traffic generation enabled ifdef tg diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index da059be11..f78820a75 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -7,7 +7,7 @@ set script_base_path "/scratch/sem23h18/project/mempool/hardware/scripts/questa/ set inject_start_time 10ns set inject_stop_time 0 #use a clock in the tb, fault_clk is 10 times faster than clk -set injection_clock "/mempool_tb/clk" +set injection_clock "/mempool_tb/fault_clk" set injection_clock_trigger 0 set fault_period 1 set rand_initial_injection_phase 0 diff --git a/hardware/tb/mempool_tb.sv b/hardware/tb/mempool_tb.sv index c2e1c2ee0..7250ef43b 100644 --- a/hardware/tb/mempool_tb.sv +++ b/hardware/tb/mempool_tb.sv @@ -395,28 +395,4 @@ module mempool_tb; `endif `endif - /******************************** - * Fault injection on icaches * - ********************************/ - - `ifdef FAULT_INJECTION_ICACHE - localparam bit fault_injection_icache = `FAULT_INJECTION_ICACHE; - `ifdef FAULT_RATE - localparam int fault_rate = `FAULT_RATE; - `else - localparam int fault_rate = 1000; - `endif - `else - localparam bit fault_injection_icache = 1; - localparam int fault_rate = 100; - `endif - logic fault_clk; - if (fault_injection_icache) begin : gen_fault_clock - // Toggling the clock - always #(ClockPeriod*fault_rate/2) fault_clk = !fault_clk; - initial begin - fault_clk = 1'b0; - end - end - endmodule : mempool_tb From 92aa868ca05ff031c87022eaf39c2d0dfae782e8 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 21 Nov 2023 10:48:11 +0100 Subject: [PATCH 29/46] [hardware] Added parity bits to l0 cache --- .../src/snitch_icache/snitch_icache_l0.sv | 110 +++++++++++++----- hardware/scripts/questa/wave.tcl | 2 + 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index 8906490fc..af916e59c 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -37,17 +37,19 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( input logic out_rsp_valid_i, output logic out_rsp_ready_o ); + localparam bit RELIABILITY_MODE = 1'b1; + localparam int DATA_PARITY_WIDTH = RELIABILITY_MODE ? 8 : 0; typedef logic [CFG.FETCH_AW-1:0] addr_t; typedef struct packed { - logic [CFG.L0_TAG_WIDTH-1:0] tag; + logic [CFG.L0_TAG_WIDTH+RELIABILITY_MODE-1:0] tag; logic vld; } tag_t; logic [CFG.L0_TAG_WIDTH-1:0] addr_tag, addr_tag_prefetch; tag_t [CFG.L0_LINE_COUNT-1:0] tag; - logic [CFG.L0_LINE_COUNT-1:0][CFG.LINE_WIDTH-1:0] data; + logic [CFG.L0_LINE_COUNT-1:0][CFG.LINE_WIDTH+DATA_PARITY_WIDTH-1:0] data; logic [CFG.L0_LINE_COUNT-1:0] hit, hit_early, hit_prefetch; logic hit_early_is_onehot; @@ -131,40 +133,86 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_array // Tag Array - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - tag[i].vld <= 0; - tag[i].tag <= 0; - end else begin - if (evict_strb[i]) begin - tag[i].vld <= 1'b0; - tag[i].tag <= evict_because_prefetch ? addr_tag_prefetch : addr_tag; - end else if (validate_strb[i]) begin - tag[i].vld <= 1'b1; + if(RELIABILITY_MODE) begin + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + tag[i].vld <= 0; + tag[i].tag <= 0; + end else begin + if (evict_strb[i]) begin + tag[i].vld <= 1'b0; + tag[i].tag <= evict_because_prefetch ? addr_tag_prefetch : addr_tag; + end else if (validate_strb[i]) begin + tag[i].vld <= 1'b1; + end + if (flush_strb[i]) begin + tag[i].vld <= 1'b0; + end end - if (flush_strb[i]) begin - tag[i].vld <= 1'b0; + end + if (CFG.EARLY_LATCH) begin : gen_latch + logic clk_vld; + tc_clk_gating i_clk_gate ( + .clk_i (clk_inv ), + .en_i (validate_strb[i]), + .test_en_i (1'b0 ), + .clk_o (clk_vld ) + ); + // Data Array + /* verilator lint_off NOLATCH */ + always_latch begin + if (clk_vld) begin + data[i] <= out_rsp_data_i; + end end + /* verilator lint_on NOLATCH */ + end else begin : gen_ff + `FFLNR(data[i], out_rsp_data_i, validate_strb[i], clk_i) end - end - if (CFG.EARLY_LATCH) begin : gen_latch - logic clk_vld; - tc_clk_gating i_clk_gate ( - .clk_i (clk_inv ), - .en_i (validate_strb[i]), - .test_en_i (1'b0 ), - .clk_o (clk_vld ) - ); - // Data Array - /* verilator lint_off NOLATCH */ - always_latch begin - if (clk_vld) begin - data[i] <= out_rsp_data_i; + end else begin + // Compute parity bit + logic [DATA_PARITY_WIDTH-1:0] data_parity; + localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; + if (RELIABILITY_MODE) begin + for (genvar i = 0; i < DATA_PARITY_WIDTH; i++) begin + assign data_parity = ~^out_rsp_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; + end + end + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + tag[i].vld <= 0; + tag[i].tag <= 0; + end else begin + if (evict_strb[i]) begin + tag[i].vld <= 1'b0; + tag[i].tag <= evict_because_prefetch ? {~^addr_tag_prefetch, addr_tag_prefetch} : {~^addr_tag, addr_tag}; + end else if (validate_strb[i]) begin + tag[i].vld <= 1'b1; + end + if (flush_strb[i]) begin + tag[i].vld <= 1'b0; + end + end + end + if (CFG.EARLY_LATCH) begin : gen_latch + logic clk_vld; + tc_clk_gating i_clk_gate ( + .clk_i (clk_inv ), + .en_i (validate_strb[i]), + .test_en_i (1'b0 ), + .clk_o (clk_vld ) + ); + // Data Array + /* verilator lint_off NOLATCH */ + always_latch begin + if (clk_vld) begin + data[i] <= {data_parity, out_rsp_data_i}; + end end + /* verilator lint_on NOLATCH */ + end else begin : gen_ff + `FFLNR(data[i], out_rsp_data_i, validate_strb[i], clk_i) end - /* verilator lint_on NOLATCH */ - end else begin : gen_ff - `FFLNR(data[i], out_rsp_data_i, validate_strb[i], clk_i) end end diff --git a/hardware/scripts/questa/wave.tcl b/hardware/scripts/questa/wave.tcl index 970e59cfb..dc388218f 100644 --- a/hardware/scripts/questa/wave.tcl +++ b/hardware/scripts/questa/wave.tcl @@ -84,4 +84,6 @@ add wave -Group DMA_midend_cluster /mempool_tb/dut/i_mempool_cluster/i_idma_dist add wave -Group DMA_split /mempool_tb/dut/i_mempool_cluster/i_idma_split_midend/* +add wave -Group L0CACHE /mempool_tb/dut/i_mempool_cluster/gen_groups[0]/i_group/gen_tiles[0]/i_tile/gen_caches[0]/i_snitch_icache/gen_prefetcher[0]/i_snitch_icache_l0/* + do ../scripts/questa/wave_cache.tcl 0 0 0 From 370211d982dbcc1b559a704202c335e1807984bb Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 28 Nov 2023 13:45:12 +0100 Subject: [PATCH 30/46] [hardware] Added shuffle of registers --- hardware/scripts/questa/mempool_inject_fault.tcl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index f78820a75..a48b20b82 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -78,7 +78,12 @@ set no_banks_rocache 127 lappend inject_register_netlist $signal_path } } - +for {set i 0} {$i < (llength $inject_register_netlist)} {incr i} { + set j [expr {int(rand() * (lllength $inject_register_netlist))}] + set temp [lindex $inject_register_netlist $j] + set inject_register_netlist [lreplace $inject_register_netlist $j $j [lindex $inject_register_netlist $i]] + set inject_register_netlist [lreplace $inject_register_netlist $i $i $temp] +} set inject_signals_netlist [] set output_netlist [] From 3221d818f10cbc0cbbf40222be74f459ef3cb549 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 28 Nov 2023 13:45:58 +0100 Subject: [PATCH 31/46] [snitch] Added parity checks in the L0 caches --- .../src/snitch_icache/snitch_icache_l0.sv | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index af916e59c..9ac8bbe35 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -39,6 +39,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( ); localparam bit RELIABILITY_MODE = 1'b1; localparam int DATA_PARITY_WIDTH = RELIABILITY_MODE ? 8 : 0; + localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; typedef logic [CFG.FETCH_AW-1:0] addr_t; typedef struct packed { @@ -133,7 +134,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_array // Tag Array - if(RELIABILITY_MODE) begin + if(!RELIABILITY_MODE) begin always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin tag[i].vld <= 0; @@ -172,10 +173,9 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end else begin // Compute parity bit logic [DATA_PARITY_WIDTH-1:0] data_parity; - localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; if (RELIABILITY_MODE) begin for (genvar i = 0; i < DATA_PARITY_WIDTH; i++) begin - assign data_parity = ~^out_rsp_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; + assign data_parity[i] = ~^out_rsp_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; end end always_ff @(posedge clk_i or negedge rst_ni) begin @@ -211,7 +211,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end /* verilator lint_on NOLATCH */ end else begin : gen_ff - `FFLNR(data[i], out_rsp_data_i, validate_strb[i], clk_i) + `FFLNR(data[i], {data_parity, out_rsp_data_i}, validate_strb[i], clk_i) end end end @@ -220,7 +220,21 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // HIT // ---- // we hit in the cache and there was a unique hit. - assign in_ready_o = hit_any & hit_early_is_onehot; + logic [CFG.L0_LINE_COUNT-1:0][DATA_PARITY_WIDTH-1:0] exp_data_parity; + logic [CFG.L0_LINE_COUNT-1:0] data_parity_error_vect; + logic data_parity_error; + if (RELIABILITY_MODE) begin + for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin + for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin + assign exp_data_parity[i][j] = ~^data[i][CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; + end + assign data_parity_error_vect[i] = (exp_data_parity != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]); + end + assign data_parity_error = data_parity_error_vect >> in_addr_i[CFG.LINE_ALIGN-1:CFG.FETCH_ALIGN]; + assign in_ready_o = hit_any & hit_early_is_onehot & !data_parity_error; + end else begin + assign in_ready_o = hit_any & hit_early_is_onehot; + end logic [CFG.LINE_WIDTH-1:0] ins_data; always_comb begin : data_muxer From 0c2bbeb98f47be75dc73cf1c55c4fb3bd136c887 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Wed, 29 Nov 2023 22:36:52 +0100 Subject: [PATCH 32/46] [snitch] Completed parity check for data part in l0 --- .../src/snitch_icache/snitch_icache_l0.sv | 28 ++++++++++--------- .../scripts/questa/mempool_inject_fault.tcl | 4 +-- hardware/scripts/questa/run.tcl | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index 9ac8bbe35..941dc7576 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -86,7 +86,9 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( `FF(last_cycle_was_prefetch_q, latch_prefetch, '0) logic evict_because_miss, evict_because_prefetch; - + + + logic data_parity_error; typedef struct packed { logic is_prefetch; logic [CFG.FETCH_AW-1:0] addr; @@ -119,10 +121,10 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( (tag[i].tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH] == addr_tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH]); end - assign hit_prefetch[i] = tag[i].vld & (tag[i].tag == addr_tag_prefetch); + assign hit_prefetch[i] = tag[i].vld & (tag[i].tag[CFG.L0_TAG_WIDTH-1:0] == addr_tag_prefetch); end - assign hit_any = |hit; + assign hit_any = |hit && !data_parity_error; assign hit_prefetch_any = |hit_prefetch; assign miss = ~hit_any & in_valid_i & ~pending_refill_q; @@ -132,6 +134,12 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( .clk_o (clk_inv) ); + logic [DATA_PARITY_WIDTH-1:0] data_parity; + if (RELIABILITY_MODE) begin + for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin + assign data_parity[j] = ~^out_rsp_data_i[CFG.LINE_WIDTH - LINE_SPLIT*j -1 -: LINE_SPLIT]; + end + end for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_array // Tag Array if(!RELIABILITY_MODE) begin @@ -172,12 +180,6 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end end else begin // Compute parity bit - logic [DATA_PARITY_WIDTH-1:0] data_parity; - if (RELIABILITY_MODE) begin - for (genvar i = 0; i < DATA_PARITY_WIDTH; i++) begin - assign data_parity[i] = ~^out_rsp_data_i[CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; - end - end always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin tag[i].vld <= 0; @@ -222,16 +224,16 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // we hit in the cache and there was a unique hit. logic [CFG.L0_LINE_COUNT-1:0][DATA_PARITY_WIDTH-1:0] exp_data_parity; logic [CFG.L0_LINE_COUNT-1:0] data_parity_error_vect; - logic data_parity_error; + if (RELIABILITY_MODE) begin for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin - assign exp_data_parity[i][j] = ~^data[i][CFG.LINE_WIDTH - LINE_SPLIT*i -1 -: LINE_SPLIT]; + assign exp_data_parity[i][j] = ~^data[i][CFG.LINE_WIDTH - LINE_SPLIT*j -1 -: LINE_SPLIT]; end - assign data_parity_error_vect[i] = (exp_data_parity != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]); + assign data_parity_error_vect[i] = (exp_data_parity[i] != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]) && hit[i]; end assign data_parity_error = data_parity_error_vect >> in_addr_i[CFG.LINE_ALIGN-1:CFG.FETCH_ALIGN]; - assign in_ready_o = hit_any & hit_early_is_onehot & !data_parity_error; + assign in_ready_o = hit_any & hit_early_is_onehot; end else begin assign in_ready_o = hit_any & hit_early_is_onehot; end diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index a48b20b82..ef4dc9454 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -4,8 +4,8 @@ set verbosity 1 set log_injections 1 set seed 12345 set script_base_path "/scratch/sem23h18/project/mempool/hardware/scripts/questa/" -set inject_start_time 10ns -set inject_stop_time 0 +set inject_start_time 5500ns +set inject_stop_time 10000ns #use a clock in the tb, fault_clk is 10 times faster than clk set injection_clock "/mempool_tb/fault_clk" set injection_clock_trigger 0 diff --git a/hardware/scripts/questa/run.tcl b/hardware/scripts/questa/run.tcl index ba6649ff4..fa7b133a1 100644 --- a/hardware/scripts/questa/run.tcl +++ b/hardware/scripts/questa/run.tcl @@ -16,6 +16,6 @@ add wave -Group gr1_refill {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/ add wave -Group gr1_handler {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_handler/*} add wave -Group gr1_splittertable {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/i_axi_burst_splitter_table/*} add wave -Group idq {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/i_axi_burst_splitter_table/i_idq/*} - +add wave -Group gr0_l0 {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[0]/i_group/gen_tiles[0]/i_tile/gen_caches[0]/i_snitch_icache/gen_prefetcher[0]/i_snitch_icache_l0/*} log -r /* run -a From 5aa1caaa8bb000be3ddcdd4fb3af8bf9e6b33b1b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Thu, 30 Nov 2023 10:42:59 +0100 Subject: [PATCH 33/46] [snitch] Added parity checks for the tag in L0 caches --- .../src/snitch_icache/snitch_icache_l0.sv | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index 941dc7576..a255ffd92 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -87,8 +87,10 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( logic evict_because_miss, evict_because_prefetch; - logic data_parity_error; + logic [CFG.L0_LINE_COUNT-1:0] tag_parity_error_vect; + logic [CFG.L0_LINE_COUNT-1:0] exp_tag_parity; + typedef struct packed { logic is_prefetch; logic [CFG.FETCH_AW-1:0] addr; @@ -109,19 +111,43 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // ------------ // Tag Compare // ------------ - for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_cmp_fetch - assign hit_early[i] = tag[i].vld & - (tag[i].tag[CFG.L0_EARLY_TAG_WIDTH-1:0] == addr_tag[CFG.L0_EARLY_TAG_WIDTH-1:0]); - // The two signals calculate the same. - if (CFG.L0_TAG_WIDTH == CFG.L0_EARLY_TAG_WIDTH) begin : gen_hit_assign - assign hit[i] = hit_early[i]; - // Compare the rest of the tag. - end else begin : gen_hit - assign hit[i] = hit_early[i] & - (tag[i].tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH] - == addr_tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH]); + if (RELIABILITY_MODE) begin + for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_exp_parity + assign exp_tag_parity[i] = ~^tag[i].tag[CFG.L0_TAG_WIDTH-1:0]; + end + end + + if (!RELIABILITY_MODE) begin + for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_cmp_fetch + assign hit_early[i] = tag[i].vld & + (tag[i].tag[CFG.L0_EARLY_TAG_WIDTH-1:0] == addr_tag[CFG.L0_EARLY_TAG_WIDTH-1:0]); + // The two signals calculate the same. + if (CFG.L0_TAG_WIDTH == CFG.L0_EARLY_TAG_WIDTH) begin : gen_hit_assign + assign hit[i] = hit_early[i]; + // Compare the rest of the tag. + end else begin : gen_hit + assign hit[i] = hit_early[i] & + (tag[i].tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH] + == addr_tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH]); + end + assign hit_prefetch[i] = tag[i].vld & (tag[i].tag[CFG.L0_TAG_WIDTH-1:0] == addr_tag_prefetch); + end + end else begin + for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_cmp_fetch + assign tag_parity_error_vect[i] = exp_tag_parity[i] != tag[i].tag[CFG.L0_TAG_WIDTH]; + assign hit_early[i] = tag[i].vld & + (tag[i].tag[CFG.L0_EARLY_TAG_WIDTH-1:0] == addr_tag[CFG.L0_EARLY_TAG_WIDTH-1:0]); + // The two signals calculate the same. + if (CFG.L0_TAG_WIDTH == CFG.L0_EARLY_TAG_WIDTH) begin : gen_hit_assign + assign hit[i] = hit_early[i] & !tag_parity_error_vect[i]; + // Compare the rest of the tag. + end else begin : gen_hit + assign hit[i] = hit_early[i] & + (tag[i].tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH] + == addr_tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH]) & !tag_parity_error_vect[i]; + end + assign hit_prefetch[i] = tag[i].vld & (tag[i].tag[CFG.L0_TAG_WIDTH-1:0] == addr_tag_prefetch); end - assign hit_prefetch[i] = tag[i].vld & (tag[i].tag[CFG.L0_TAG_WIDTH-1:0] == addr_tag_prefetch); end assign hit_any = |hit && !data_parity_error; @@ -289,6 +315,10 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // but didn't hit in the final comparison. flush_strb = ~hit & hit_early; end + if (RELIABILITY_MODE && data_parity_error) begin + // Evict entry that hit but have faults on the data + //flush_strb = data_parity_error_vect; + end if (flush_valid_i) flush_strb = '1; end From 4bb3cb52b584aa699c059c552d51c37ef1ab8ca1 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Fri, 1 Dec 2023 11:11:08 +0100 Subject: [PATCH 34/46] [snitch] Tag invalidation after fault in data banks in l0 caches --- .../src/snitch_icache/snitch_icache_l0.sv | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index a255ffd92..e2ddcaf52 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -88,6 +88,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( logic evict_because_miss, evict_because_prefetch; logic data_parity_error; + logic tag_parity_error; logic [CFG.L0_LINE_COUNT-1:0] tag_parity_error_vect; logic [CFG.L0_LINE_COUNT-1:0] exp_tag_parity; @@ -134,7 +135,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end end else begin for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_cmp_fetch - assign tag_parity_error_vect[i] = exp_tag_parity[i] != tag[i].tag[CFG.L0_TAG_WIDTH]; + assign tag_parity_error_vect[i] = (exp_tag_parity[i] != tag[i].tag[CFG.L0_TAG_WIDTH]) && tag[i].vld; assign hit_early[i] = tag[i].vld & (tag[i].tag[CFG.L0_EARLY_TAG_WIDTH-1:0] == addr_tag[CFG.L0_EARLY_TAG_WIDTH-1:0]); // The two signals calculate the same. @@ -256,7 +257,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin assign exp_data_parity[i][j] = ~^data[i][CFG.LINE_WIDTH - LINE_SPLIT*j -1 -: LINE_SPLIT]; end - assign data_parity_error_vect[i] = (exp_data_parity[i] != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]) && hit[i]; + assign data_parity_error_vect[i] = (exp_data_parity[i] != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]) && tag[i].vld; end assign data_parity_error = data_parity_error_vect >> in_addr_i[CFG.LINE_ALIGN-1:CFG.FETCH_ALIGN]; assign in_ready_o = hit_any & hit_early_is_onehot; @@ -315,12 +316,21 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // but didn't hit in the final comparison. flush_strb = ~hit & hit_early; end - if (RELIABILITY_MODE && data_parity_error) begin - // Evict entry that hit but have faults on the data - //flush_strb = data_parity_error_vect; + if (RELIABILITY_MODE && tag_parity_error_vect!='0) begin + // Evict all tags that have a fault + flush_strb = tag_parity_error_vect; + end + if (RELIABILITY_MODE && data_parity_error_vect!='0) begin + // Evict entry that hit but has faults on the data + flush_strb = flush_strb | data_parity_error_vect; end if (flush_valid_i) flush_strb = '1; end + always @ (posedge clk_i) begin + if (RELIABILITY_MODE && tag_parity_error_vect != '0 && data_parity_error_vect != '0) $display("%t [l0cache]:tag and data fault: flushing tags: %b",$time, flush_strb); + else if (RELIABILITY_MODE && tag_parity_error_vect != '0) $display("%t [l0cache]:tag fault: flushing tags: %b",$time, flush_strb); + else if (RELIABILITY_MODE && data_parity_error_vect != '0) $display("%t [l0cache]:data fault: flushing tags: %b",$time, flush_strb); + end `FF(cnt_q, cnt_d, '0) From 097f42a36099e07472612958cc297c4889f3c9b9 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Fri, 1 Dec 2023 11:17:10 +0100 Subject: [PATCH 35/46] [hardware] Included l0 banks in injection scripts --- hardware/scripts/questa/get_banks.tcl | 27 ++++++++- .../scripts/questa/mempool_inject_fault.tcl | 56 +++++++++++++------ 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/hardware/scripts/questa/get_banks.tcl b/hardware/scripts/questa/get_banks.tcl index 1d7debee0..11f312b3f 100644 --- a/hardware/scripts/questa/get_banks.tcl +++ b/hardware/scripts/questa/get_banks.tcl @@ -84,4 +84,29 @@ foreach inst $inst_list { #foreach inst $L1_data_list { # puts $fhandle $inst #} -#close $fhandle ; \ No newline at end of file +#close $fhandle ; + + +# Search for all instances of tag banks for L0 +set pattern_match "*i_snitch_icache_l0" +set inst_list [find instances -r $pattern_match] ; +set L0_tag_list [list] ; +foreach inst $inst_list { + set ipath [lindex $inst 0] + if {[string match $pattern_match $ipath]} { + append ipath "/tag" + lappend L0_tag_list $ipath + } +} + +# Search for all instances of data banks for L0 +set pattern_match "*i_snitch_icache_l0" +set inst_list [find instances -r $pattern_match] ; +set L0_data_list [list] ; +foreach inst $inst_list { + set ipath [lindex $inst 0] + if {[string match $pattern_match $ipath]} { + append ipath "/data" + lappend L0_data_list $ipath + } +} \ No newline at end of file diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index ef4dc9454..d8293373b 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -24,20 +24,20 @@ set reg_to_sig_ratio 1 #proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} source ${::script_base_path}get_banks.tcl -proc add_signals_injection {tag_list tag_idx tag_max_width data_list no_databanks} { - foreach inst $tag_list { - for {set index $tag_idx} {$index >= 0} {incr index -1} { - set signal_path "${inst}[${index}][${tag_max_width}]" - lappend inject_register_netlist $signal_path - } - } - foreach inst $data_list { - for {set index $no_databanks} {$index >= 0} {incr index -1} { - set signal_path "${inst}[${index}]" - lappend inject_register_netlist $signal_path - } - } -} +#proc add_signals_injection {tag_list tag_idx tag_max_width data_list no_databanks} { +# foreach inst $tag_list { +# for {set index $tag_idx} {$index >= 0} {incr index -1} { +# set signal_path "${inst}[${index}][${tag_max_width}]" +# lappend inject_register_netlist $signal_path +# } +# } +# foreach inst $data_list { +# for {set index $no_databanks} {$index >= 0} {incr index -1} { +# set signal_path "${inst}[${index}]" +# lappend inject_register_netlist $signal_path +# } +# } +#} set inject_register_netlist {} set max_idx_icache 31 set max_tag_width_icache 25 @@ -69,17 +69,39 @@ set no_banks_rocache 127 for {set index $max_idx_rocache} {$index >= 0} {incr index -1} { #set signal_path "${inst}[${index}][${max_tag_width_rocache}]" set signal_path "${inst}[${index}]" - lappend inject_register_netlist $signal_path + #lappend inject_register_netlist $signal_path } } foreach inst $ROC_data_list { for {set index $no_banks_rocache} {$index >= 0} {incr index -1} { + set signal_path "${inst}[${index}]" + #lappend inject_register_netlist $signal_path + } + } + +set max_idx_l0cache 3 +set max_tag_width_l0cache 27 +set no_banks_l0cache 3 + + foreach inst $L0_tag_list { + for {set index $max_idx_l0cache} {$index >= 0} {incr index -1} { + #set signal_path "${inst}[${index}].tag[${max_tag_width_l0cache}]" + set signal_path "${inst}[${index}].tag" + #set signal_path "${inst}[${index}]" + #lappend inject_register_netlist $signal_path + } + } + foreach inst $L0_data_list { + for {set index $no_banks_l0cache} {$index >= 0} {incr index -1} { set signal_path "${inst}[${index}]" lappend inject_register_netlist $signal_path } } -for {set i 0} {$i < (llength $inject_register_netlist)} {incr i} { - set j [expr {int(rand() * (lllength $inject_register_netlist))}] + + +#random shuffle of the list +for {set i 0} {$i < [llength $inject_register_netlist]} {incr i} { + set j [expr {int(rand() * [llength $inject_register_netlist])}] set temp [lindex $inject_register_netlist $j] set inject_register_netlist [lreplace $inject_register_netlist $j $j [lindex $inject_register_netlist $i]] set inject_register_netlist [lreplace $inject_register_netlist $i $i $temp] From e4cd02349b5cfeebb6ca3050f67febdcf910cef2 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sat, 2 Dec 2023 16:48:07 +0100 Subject: [PATCH 36/46] [hardware] Modified parameters to enable FT in L0 caches --- config/config.mk | 4 +++- hardware/Makefile | 2 +- .../snitch/src/snitch_icache/snitch_icache.sv | 9 ++++++--- .../src/snitch_icache/snitch_icache_l0.sv | 18 +++++++++--------- .../snitch_icache_lookup_serial.sv | 2 +- .../src/snitch_icache/snitch_icache_pkg.sv | 3 ++- .../snitch_read_only_cache.sv | 3 ++- hardware/src/mempool_pkg.sv | 3 ++- hardware/src/mempool_tile.sv | 3 ++- 9 files changed, 28 insertions(+), 19 deletions(-) diff --git a/config/config.mk b/config/config.mk index a139e4403..46c298637 100644 --- a/config/config.mk +++ b/config/config.mk @@ -69,11 +69,13 @@ xqueue_size ?= 0 # Enable the XpulpIMG extension xpulpimg ?= 1 -# Enable the Reliability mode for RO caches and L1 icache +# Enable the Reliability mode for RO caches and L1 and L0 icache rel_rocache ?= 1 rel_l1icache ?= 1 +rel_l0icache ?= 1 + # Enable fault injection in the instruction caches and define fault rate (clok_periods/fault) fault_injection ?= 0 diff --git a/hardware/Makefile b/hardware/Makefile index 05d71149a..9da9c0fba 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -99,7 +99,7 @@ vlog_defs += -DRO_LINE_WIDTH=$(ro_line_width) vlog_defs += -DDMAS_PER_GROUP=$(dmas_per_group) vlog_defs += -DAXI_HIER_RADIX=$(axi_hier_radix) -DAXI_MASTERS_PER_GROUP=$(axi_masters_per_group) vlog_defs += -DSEQ_MEM_SIZE=$(seq_mem_size) -DXQUEUE_SIZE=$(xqueue_size) -vlog_defs += -DREL_ROCACHE=$(rel_rocache) -DREL_L1ICACHE=$(rel_l1icache) +vlog_defs += -DREL_ROCACHE=$(rel_rocache) -DREL_L1ICACHE=$(rel_l1icache) -DREL_L0ICACHE=$(rel_l0icache) # Traffic generation enabled ifdef tg diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv index fe1fc86d3..6a026b53e 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache.sv @@ -26,8 +26,10 @@ module snitch_icache #( parameter int FILL_AW = -1, /// Fill interface data width. Power of two; >= 8. parameter int FILL_DW = -1, - /// Add parity checks in the lookup module - parameter bit RELIABILITY = 0, + /// Add parity checks for the L1 caches + parameter bit RELIABILITY_L1 = 0, + /// Add parity checks for the L0 caches + parameter bit RELIABILITY_L0 = 0, /// Replace the L1 tag banks with latch-based SCM. parameter bit L1_TAG_SCM = 0, /// This reduces area impact at the cost of @@ -82,7 +84,8 @@ module snitch_icache #( FETCH_DW: FETCH_DW, FILL_AW: FILL_AW, FILL_DW: FILL_DW, - ENABLE_RELIABILITY: RELIABILITY, + RELIABILITY_L1: RELIABILITY_L1, + RELIABILITY_L0: RELIABILITY_L0, L1_TAG_SCM: L1_TAG_SCM, EARLY_LATCH: EARLY_LATCH, BUFFER_LOOKUP: 0, diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index e2ddcaf52..f529e0206 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -37,8 +37,8 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( input logic out_rsp_valid_i, output logic out_rsp_ready_o ); - localparam bit RELIABILITY_MODE = 1'b1; - localparam int DATA_PARITY_WIDTH = RELIABILITY_MODE ? 8 : 0; + localparam bit RELIABILITY_MODE = CFG.RELIABILITY_L0; + localparam int DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd8 : '0; localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; typedef logic [CFG.FETCH_AW-1:0] addr_t; @@ -88,7 +88,6 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( logic evict_because_miss, evict_because_prefetch; logic data_parity_error; - logic tag_parity_error; logic [CFG.L0_LINE_COUNT-1:0] tag_parity_error_vect; logic [CFG.L0_LINE_COUNT-1:0] exp_tag_parity; @@ -150,8 +149,11 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( assign hit_prefetch[i] = tag[i].vld & (tag[i].tag[CFG.L0_TAG_WIDTH-1:0] == addr_tag_prefetch); end end - - assign hit_any = |hit && !data_parity_error; + if(RELIABILITY_MODE) begin + assign hit_any = |hit && !data_parity_error; + end else begin + assign hit_any = |hit; + end assign hit_prefetch_any = |hit_prefetch; assign miss = ~hit_any & in_valid_i & ~pending_refill_q; @@ -249,10 +251,10 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // HIT // ---- // we hit in the cache and there was a unique hit. - logic [CFG.L0_LINE_COUNT-1:0][DATA_PARITY_WIDTH-1:0] exp_data_parity; logic [CFG.L0_LINE_COUNT-1:0] data_parity_error_vect; if (RELIABILITY_MODE) begin + logic [CFG.L0_LINE_COUNT-1:0][DATA_PARITY_WIDTH-1:0] exp_data_parity; for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin assign exp_data_parity[i][j] = ~^data[i][CFG.LINE_WIDTH - LINE_SPLIT*j -1 -: LINE_SPLIT]; @@ -260,10 +262,8 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( assign data_parity_error_vect[i] = (exp_data_parity[i] != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]) && tag[i].vld; end assign data_parity_error = data_parity_error_vect >> in_addr_i[CFG.LINE_ALIGN-1:CFG.FETCH_ALIGN]; - assign in_ready_o = hit_any & hit_early_is_onehot; - end else begin - assign in_ready_o = hit_any & hit_early_is_onehot; end + assign in_ready_o = hit_any & hit_early_is_onehot; logic [CFG.LINE_WIDTH-1:0] ins_data; always_comb begin : data_muxer diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 1da4d35c0..f6a4734c1 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -38,7 +38,7 @@ module snitch_icache_lookup_serial #( input logic write_valid_i, output logic write_ready_o ); - localparam int RELIABILITY_MODE = CFG.ENABLE_RELIABILITY; + localparam int RELIABILITY_MODE = '0;//CFG.RELIABILITY_L1; localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd8 : '0; // TODO: propagate it up as a parameter diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv index deacf6202..0be4f525c 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_pkg.sv @@ -25,7 +25,8 @@ package snitch_icache_pkg; int FETCH_DW; int FILL_AW; int FILL_DW; - bit ENABLE_RELIABILITY; + bit RELIABILITY_L1; + bit RELIABILITY_L0; bit L1_TAG_SCM; bit EARLY_LATCH; bit BUFFER_LOOKUP; diff --git a/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv b/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv index a32b8b6b8..3ae450445 100644 --- a/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv +++ b/hardware/deps/snitch/src/snitch_read_only_cache/snitch_read_only_cache.sv @@ -197,7 +197,8 @@ module snitch_read_only_cache #( FETCH_DW: AxiDataWidth, FILL_AW: AxiAddrWidth, FILL_DW: AxiDataWidth, - ENABLE_RELIABILITY: Reliability, + RELIABILITY_L1: Reliability, + RELIABILITY_L0: 0, // Unused here L1_TAG_SCM: 0, // Unused here EARLY_LATCH: 0, // Unused here BUFFER_LOOKUP: 1, // Mandatory here diff --git a/hardware/src/mempool_pkg.sv b/hardware/src/mempool_pkg.sv index 6868f3712..d81d5e6a2 100644 --- a/hardware/src/mempool_pkg.sv +++ b/hardware/src/mempool_pkg.sv @@ -135,7 +135,8 @@ package mempool_pkg; localparam int unsigned ICacheSizeByte = 512 * NumCoresPerCache; // Total Size of instruction cache in bytes localparam int unsigned ICacheSets = NumCoresPerCache / 2; // Number of sets localparam int unsigned ICacheLineWidth = 32 * 2 * NumCoresPerCache; // Size of each cache line in bits - localparam bit ICacheReliability = `ifdef REL_L1ICACHE `REL_L1ICACHE `else 1'bX `endif; // Reliability for icaches enabled? + localparam bit ICacheReliabilityL1 = `ifdef REL_L1ICACHE `REL_L1ICACHE `else 1'bX `endif; // Reliability for L1 icaches enabled? + localparam bit ICacheReliabilityL0 = `ifdef REL_L0ICACHE `REL_L0ICACHE `else 1'bX `endif; // Reliability for L0 icaches enabled? /********************* * READ-ONLY CACHE * diff --git a/hardware/src/mempool_tile.sv b/hardware/src/mempool_tile.sv index 40dc142c1..314105ee2 100644 --- a/hardware/src/mempool_tile.sv +++ b/hardware/src/mempool_tile.sv @@ -177,7 +177,8 @@ module mempool_tile .FETCH_DW (DataWidth ), .FILL_AW (AddrWidth ), .FILL_DW (AxiDataWidth ), - .RELIABILITY (ICacheReliability ), + .RELIABILITY_L1 (ICacheReliabilityL1 ), + .RELIABILITY_L0 (ICacheReliabilityL0 ), .L1_TAG_SCM (1 ), /// Make the early cache latch-based. This reduces latency at the cost of /// increased combinatorial path lengths and the hassle of having latches in From c9c3b97e009e08622d39cc69f7eca39939d981b7 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 3 Dec 2023 16:10:34 +0100 Subject: [PATCH 37/46] [snitch] Fixed missing control parameter --- .../snitch/src/snitch_icache/snitch_icache_lookup_serial.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index f6a4734c1..b696ae082 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -38,7 +38,7 @@ module snitch_icache_lookup_serial #( input logic write_valid_i, output logic write_ready_o ); - localparam int RELIABILITY_MODE = '0;//CFG.RELIABILITY_L1; + localparam int RELIABILITY_MODE = CFG.RELIABILITY_L1; localparam int unsigned DATA_ADDR_WIDTH = $clog2(CFG.SET_COUNT) + CFG.COUNT_ALIGN; localparam int unsigned DATA_PARITY_WIDTH = RELIABILITY_MODE ? 'd8 : '0; // TODO: propagate it up as a parameter From dc9452707e71e991ce3c7f5e5ed0836dbbf33371 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 4 Dec 2023 16:24:19 +0100 Subject: [PATCH 38/46] [snitch] Removed comments --- .../snitch_icache/snitch_icache_lookup_serial.sv | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index b696ae082..61ea3fd19 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -179,8 +179,8 @@ module snitch_icache_lookup_serial #( end end always @ (posedge clk_i) begin - if(data_fault_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h, set %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], data_parity_inv_q.cset); - else if(faulty_hit_valid && RELIABILITY_MODE) $display("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); + if(data_fault_valid && RELIABILITY_MODE) $warning("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h, set %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], data_parity_inv_q.cset); + else if(faulty_hit_valid && RELIABILITY_MODE) $warning("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); //if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h (idx %h) set %h", $time, write_addr_i, write_set_i); end @@ -306,11 +306,6 @@ module snitch_icache_lookup_serial #( logic error; } data_req_t; - /*typedef struct packed{ - logic [CFG.LINE_WIDTH-1:0] data_line; - logic [DATA_PARITY_WIDTH-1:0] parity_bit; - }data_rsp_t;*/ - typedef logic [CFG.LINE_WIDTH + DATA_PARITY_WIDTH - 1:0] data_rsp_t; @@ -373,10 +368,6 @@ module snitch_icache_lookup_serial #( .be_i ( '1 ), .rdata_o ( data_rdata ) ); - always @ (posedge clk_i) begin - //if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h set %h", $time, write_addr_i, write_set_i); - //if(out_valid_o && hit_invalid && out_ready_i) $display("(%t) [ROcache_lookup]: Wrong data 0x%x (addr=%h) invalidated", $time, out_data_o, out_addr_o); - end // Parity check logic [DATA_PARITY_WIDTH-1:0] data_parity_error; @@ -409,9 +400,6 @@ module snitch_icache_lookup_serial #( if (RELIABILITY_MODE) begin assign data_parity_inv_d.addr = data_req_q.addr; assign data_parity_inv_d.cset = data_req_q.id; - // add check that next address is different from previous one - //assign same_invalidation_address = (data_req_q.addr == data_parity_inv_q.addr) && hit_invalid; //if address is the same as before, no need to invalidate it again - ///*|| (tag_handshake && same_invalidation_address)*/) ? /*{data_parity_inv_q.addr, data_parity_inv_q.cset, 1'b0}*/ `FFL(data_parity_inv_q, (data_fault_ready && !tag_handshake) ? '0 : data_parity_inv_d, tag_handshake || data_fault_ready, '0, clk_i, rst_ni) //reset value when it has been invalidated and there is no new value `FFL(hit_invalid_q, data_parity_inv_d.parity_error, tag_handshake, '0, clk_i, rst_ni) end From 5118bf09010eecee0878cd25b19f6eeff907e985 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 5 Dec 2023 11:27:09 +0100 Subject: [PATCH 39/46] [snitch] Inserted counters for faults, removed comments --- .../snitch_icache_lookup_serial.sv | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 61ea3fd19..5320c92df 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -151,22 +151,20 @@ module snitch_icache_lookup_serial #( tag_write = 1'b1; end else if (data_fault_valid && RELIABILITY_MODE) begin tag_addr = data_parity_inv_q.addr >> CFG.LINE_ALIGN; - //$display("invalidation addr: %h,\n %b\n tag_addr: %h, LA=%d, CA=%d", data_parity_inv_q.addr, data_parity_inv_q.addr, tag_addr, CFG.LINE_ALIGN, CFG.COUNT_ALIGN ); tag_enable = $unsigned(1 << data_parity_inv_q.cset); tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; data_fault_ready = 1'b1; - //$display("%t [ROcache_lookup]: DataFault -> Invalidating address %h", $time, data_parity_inv_q.addr); end else if (write_valid_i) begin // Write a refill request tag_addr = write_addr_i; tag_enable = $unsigned(1 << write_set_i); tag_write = 1'b1; write_ready_o = 1'b1; - end else if (faulty_hit_valid && RELIABILITY_MODE) begin //we need to set second bit (valid) of write data of the previous adress to 0 - //we do not accept read requests and we do not store data in the pipeline. - tag_addr = tag_req_q.addr >> CFG.LINE_ALIGN; //buffered version of in_addr_i - tag_enable = tag_parity_error_q; // which set must be written to (the faulty one(s)) + end else if (faulty_hit_valid && RELIABILITY_MODE) begin // we need to set second bit (valid) of write data of the previous adress to 0 + // we do not accept read requests and we do not store data in the pipeline. + tag_addr = tag_req_q.addr >> CFG.LINE_ALIGN; // buffered version of in_addr_i + tag_enable = tag_parity_error_q; // which set must be written to (the one(s) with faults tag_wdata[CFG.TAG_WIDTH+1:0] = '0; tag_write = 1'b1; faulty_hit_ready = 1'b1; @@ -178,10 +176,16 @@ module snitch_icache_lookup_serial #( req_valid = 1'b1; end end + int unsigned tag_faults=0, data_faults=0; always @ (posedge clk_i) begin - if(data_fault_valid && RELIABILITY_MODE) $warning("%t [ROcache_lookup]: DataFault -> Invalidating address %h, index: %h, set %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], data_parity_inv_q.cset); - else if(faulty_hit_valid && RELIABILITY_MODE) $warning("%t [ROcache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); - //if(write_valid_i && write_ready_o && data_wdata == '0) $display("(%t) [ROcache_lookup]: Writing 0 at index %h (idx %h) set %h", $time, write_addr_i, write_set_i); + if(data_fault_valid && RELIABILITY_MODE) begin + $warning("%t [snitch_icache_lookup]: DataFault -> Invalidating address %h, index: %h, set %h", $time, data_parity_inv_q.addr, data_parity_inv_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN], data_parity_inv_q.cset); + data_faults = data_faults+1; + end + else if(faulty_hit_valid && RELIABILITY_MODE) begin + $warning("%t [snitch_icache_lookup]: TagFault -> Invalidating address %h, index: %h", $time, tag_req_q.addr, tag_req_q.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]); + tag_faults = tag_faults+1; + end end // Instantiate the tag sets. @@ -222,7 +226,7 @@ module snitch_icache_lookup_serial #( ); end - // compute tag parity bit the cycle before reading it and buffer it + // compute tag parity bit the cycle before reading the tag and buffer it logic exp_tag_parity_bit_d, exp_tag_parity_bit_q; if (RELIABILITY_MODE) begin @@ -405,7 +409,6 @@ module snitch_icache_lookup_serial #( end - // Generate the remaining output signals. assign out_addr_o = data_req_q.addr; assign out_id_o = data_req_q.id; From d42a18d1b5d45a84ede7ebe4593969130364945b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Tue, 5 Dec 2023 11:27:39 +0100 Subject: [PATCH 40/46] [snitch] Print fault statistics in a file --- hardware/scripts/questa/get_number_faults.tcl | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 hardware/scripts/questa/get_number_faults.tcl diff --git a/hardware/scripts/questa/get_number_faults.tcl b/hardware/scripts/questa/get_number_faults.tcl new file mode 100644 index 000000000..2c62372ec --- /dev/null +++ b/hardware/scripts/questa/get_number_faults.tcl @@ -0,0 +1,47 @@ +# Find all signals in the design +set signal_list [find signals -r */data_faults] + +# Open a file for writing +set file_id [open "data_tag_faults_stats.txt" w] + +# Initialize variables for sum and count +set sum 0.0 +set count 0 + +# Loop through each signal in the list +foreach signal $signal_list { + # Get the value of the signal and store it in a variable + set signal_value [examine -radix decimal $signal] + + # Print the signal name and value to the file + puts $file_id "Signal: $signal, Value: $signal_value" + + # Add the value to the sum + set sum [expr {$sum + $signal_value}] + + # Increment the count + incr count +} + +# Calculate the average +set average [expr {$sum / $count}] + +# Print the average to the file +puts $file_id "\nAverage of all values: $average\n\n" + +set signal_list [find signals -r */tag_faults] +set sum 0.0 +set count 0 + +foreach signal $signal_list { + set signal_value [examine -radix decimal $signal] + puts $file_id "Signal: $signal, Value: $signal_value" + set sum [expr {$sum + $signal_value}] + incr count +} +set average [expr {$sum / $count}] + +puts $file_id "\nAverage of all values: $average" + +# Close the file +close $file_id \ No newline at end of file From 217b9f7d35995ca8c51bb8010328c6acf5af9419 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Fri, 8 Dec 2023 16:43:32 +0100 Subject: [PATCH 41/46] [snitch] Fixed eviction vector --- hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index f529e0206..386083568 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -318,7 +318,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end if (RELIABILITY_MODE && tag_parity_error_vect!='0) begin // Evict all tags that have a fault - flush_strb = tag_parity_error_vect; + flush_strb = flush_strb | tag_parity_error_vect; end if (RELIABILITY_MODE && data_parity_error_vect!='0) begin // Evict entry that hit but has faults on the data From 38d1ffd829a422ea10cd99165b0c59265888a433 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Fri, 8 Dec 2023 16:44:24 +0100 Subject: [PATCH 42/46] [snitch] Changed comments --- config/config.mk | 2 +- .../snitch/src/snitch_icache/snitch_icache_lookup_serial.sv | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.mk b/config/config.mk index 46c298637..f30040cb5 100644 --- a/config/config.mk +++ b/config/config.mk @@ -74,7 +74,7 @@ rel_rocache ?= 1 rel_l1icache ?= 1 -rel_l0icache ?= 1 +rel_l0icache ?= 0 # Enable fault injection in the instruction caches and define fault rate (clok_periods/fault) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 5320c92df..80d45464e 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -330,7 +330,7 @@ module snitch_icache_lookup_serial #( assign data_req_d.hit = tag_rsp.hit; assign data_req_d.error = tag_rsp.error; - assign lookup_addr = {tag_rsp.cset, tag_req_q.addr[CFG.LINE_ALIGN +: CFG.COUNT_ALIGN]}; //eg if 256 line width Line_align is log2(nr of bytes), 2 set counts, i.e. 3 bits for line, 1 for set, take bits from 1 to 5 + assign lookup_addr = {tag_rsp.cset, tag_req_q.addr[CFG.LINE_ALIGN +: CFG.COUNT_ALIGN]}; assign write_addr = {write_set_i, write_addr_i}; localparam LINE_SPLIT = CFG.LINE_WIDTH/DATA_PARITY_WIDTH; @@ -348,7 +348,7 @@ module snitch_icache_lookup_serial #( data_enable = tag_valid && tag_rsp.hit; // Only read data on hit data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i; data_write = 1'b0; - // Before: Write takes priority, with FT-> write does not have priority over invalidation + // Write takes priority, with FT-> write does not have priority over invalidation if (!init_phase && write_valid_i && !data_fault_valid) begin data_addr = write_addr; data_enable = 1'b1; From 7d0369c5f711b9a1075f59707aaa7579117e8499 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Wed, 3 Jan 2024 16:11:08 +0100 Subject: [PATCH 43/46] [Snitch] Added comments in the lookup serial --- .../snitch_icache_lookup_serial.sv | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv index 80d45464e..fd1179438 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_lookup_serial.sv @@ -30,7 +30,7 @@ module snitch_icache_lookup_serial #( output logic out_valid_o, input logic out_ready_i, - input logic [CFG.COUNT_ALIGN-1:0] write_addr_i, //when write valid, it goes contemporaritly to both stages, otherwise data stage uses lookup addr from tag stage + input logic [CFG.COUNT_ALIGN-1:0] write_addr_i, input logic [CFG.SET_ALIGN-1:0] write_set_i, input logic [CFG.LINE_WIDTH-1:0] write_data_i, input logic [CFG.TAG_WIDTH-1:0] write_tag_i, @@ -133,7 +133,7 @@ module snitch_icache_lookup_serial #( assign data_fault_valid = RELIABILITY_MODE ? data_parity_inv_q.parity_error : '0; always_comb begin - tag_addr = in_addr_i >> CFG.LINE_ALIGN; //if 128 line count, shift right 8 bits to get the index + tag_addr = in_addr_i >> CFG.LINE_ALIGN; //right shift to get {tag, index} bits tag_enable = '0; tag_wdata[CFG.TAG_WIDTH+1:0] = {1'b1, write_error_i, write_tag_i}; tag_write = 1'b0; @@ -265,19 +265,16 @@ module snitch_icache_lookup_serial #( // Buffer the metadata on a valid handshake. Stall on write (implicit in req_valid/ready) `FFL(tag_req_q, tag_req_d, req_valid && req_ready, '0, clk_i, rst_ni) if(RELIABILITY_MODE) begin + // save faulty sets and clear when upstream invalidated them `FFL(tag_parity_error_q, tag_parity_error_d, req_valid && req_ready, '0, clk_i, rst_ni) `FFL(faulty_hit_q, (faulty_hit_ready && !(req_valid && req_ready))? 1'b0 : faulty_hit_d, req_valid && req_ready || faulty_hit_ready, '0, clk_i, rst_ni) end assign faulty_hit_valid = RELIABILITY_MODE ? faulty_hit_q : '0; //if there is a write request, select the buffered version to be invalidated `FF(tag_valid, req_valid ? 1'b1 : tag_ready ? 1'b0 : tag_valid, '0, clk_i, rst_ni) - if(!RELIABILITY_MODE) begin - // Ready if buffer is empy or downstream is reading. Stall on write (and data invalidation not needed as it is included in req_valid) - assign req_ready = (!tag_valid || tag_ready) && !tag_write; - end else begin - // Ready if buffer is empy or downstream is reading. Stall on write - assign req_ready = (!tag_valid || tag_ready) && !tag_write; - end + + // Ready if buffer is empy or downstream is reading. Stall on write + assign req_ready = (!tag_valid || tag_ready) && !tag_write; // Register the handshake of the reg stage to buffer the tag output data in the next cycle `FF(req_handshake, req_valid && req_ready, 1'b0, clk_i, rst_ni) @@ -348,12 +345,14 @@ module snitch_icache_lookup_serial #( data_enable = tag_valid && tag_rsp.hit; // Only read data on hit data_wdata[CFG.LINE_WIDTH -1:0] = write_data_i; data_write = 1'b0; - // Write takes priority, with FT-> write does not have priority over invalidation + // Write takes priority, with FT-> write does not have priority over invalidation to not invalidate new data if (!init_phase && write_valid_i && !data_fault_valid) begin data_addr = write_addr; data_enable = 1'b1; data_write = 1'b1; end + + // Assertions to check whether new data is written in both data and tag //assert property ( @(posedge clk_i) tag_write && write_valid_i |-> data_write ) else $error("Writing tag but not data"); //assert property ( @(posedge clk_i) data_write && write_valid_i |-> tag_write ) else $error("Writing data but not tag"); end From b323e58c430d98d47b606d90f224b8c036eebc9b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Sun, 7 Jan 2024 22:44:55 +0100 Subject: [PATCH 44/46] [snitch] Added readme file for the ROC test bench and updated comments --- .../src/snitch_icache/snitch_icache_handler.sv | 2 +- .../src/snitch_icache/snitch_icache_l0.sv | 18 ++++++++++++++---- hardware/deps/snitch/tb/README.md | 16 ++++++++++++++++ .../tb/fault_injection/ROC_inject_fault.tcl | 10 ++-------- 4 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 hardware/deps/snitch/tb/README.md diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv index b87fda9b5..f149a70ea 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_handler.sv @@ -214,7 +214,7 @@ module snitch_icache_handler #( in_req_ready_o = hit_ready; // The cache lookup was a miss, but there is already a pending - // refill that covers the line. + // refill that covers the line and the lookup accepted the request. end else if (pending && !(write_valid_o && !write_ready_i)) begin push_index = pending_id; push_enable = 1; diff --git a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv index 386083568..35f7f4546 100644 --- a/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv +++ b/hardware/deps/snitch/src/snitch_icache/snitch_icache_l0.sv @@ -111,6 +111,8 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // ------------ // Tag Compare // ------------ + + // For every line, compute the current parity bit if (RELIABILITY_MODE) begin for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_exp_parity assign exp_tag_parity[i] = ~^tag[i].tag[CFG.L0_TAG_WIDTH-1:0]; @@ -134,13 +136,14 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end end else begin for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin : gen_cmp_fetch + // Compute a parity error vector comparing the expected parity bit and the current one. assign tag_parity_error_vect[i] = (exp_tag_parity[i] != tag[i].tag[CFG.L0_TAG_WIDTH]) && tag[i].vld; assign hit_early[i] = tag[i].vld & (tag[i].tag[CFG.L0_EARLY_TAG_WIDTH-1:0] == addr_tag[CFG.L0_EARLY_TAG_WIDTH-1:0]); // The two signals calculate the same. if (CFG.L0_TAG_WIDTH == CFG.L0_EARLY_TAG_WIDTH) begin : gen_hit_assign assign hit[i] = hit_early[i] & !tag_parity_error_vect[i]; - // Compare the rest of the tag. + // Compare the rest of the tag and corresponding parity error vector. end else begin : gen_hit assign hit[i] = hit_early[i] & (tag[i].tag[CFG.L0_TAG_WIDTH-1:CFG.L0_EARLY_TAG_WIDTH] @@ -150,6 +153,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end end if(RELIABILITY_MODE) begin + // A parity error in the data overwrites the hit into a miss assign hit_any = |hit && !data_parity_error; end else begin assign hit_any = |hit; @@ -165,6 +169,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( logic [DATA_PARITY_WIDTH-1:0] data_parity; if (RELIABILITY_MODE) begin + // For every block of the configured block size, compute the parity bit for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin assign data_parity[j] = ~^out_rsp_data_i[CFG.LINE_WIDTH - LINE_SPLIT*j -1 -: LINE_SPLIT]; end @@ -234,6 +239,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( .clk_o (clk_vld ) ); // Data Array + // Store both the parity and the data /* verilator lint_off NOLATCH */ always_latch begin if (clk_vld) begin @@ -255,12 +261,14 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( if (RELIABILITY_MODE) begin logic [CFG.L0_LINE_COUNT-1:0][DATA_PARITY_WIDTH-1:0] exp_data_parity; + // For every line, we compute the expected parity for every block, then we determine which lines are faulty for (genvar i = 0; i < CFG.L0_LINE_COUNT; i++) begin for (genvar j = 0; j < DATA_PARITY_WIDTH; j++) begin assign exp_data_parity[i][j] = ~^data[i][CFG.LINE_WIDTH - LINE_SPLIT*j -1 -: LINE_SPLIT]; end assign data_parity_error_vect[i] = (exp_data_parity[i] != data[i][CFG.LINE_WIDTH+:DATA_PARITY_WIDTH]) && tag[i].vld; end + // Check whether the currently selected data has an error assign data_parity_error = data_parity_error_vect >> in_addr_i[CFG.LINE_ALIGN-1:CFG.FETCH_ALIGN]; end assign in_ready_o = hit_any & hit_early_is_onehot; @@ -326,10 +334,12 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end if (flush_valid_i) flush_strb = '1; end + + // Send a warning that an error was detected always @ (posedge clk_i) begin - if (RELIABILITY_MODE && tag_parity_error_vect != '0 && data_parity_error_vect != '0) $display("%t [l0cache]:tag and data fault: flushing tags: %b",$time, flush_strb); - else if (RELIABILITY_MODE && tag_parity_error_vect != '0) $display("%t [l0cache]:tag fault: flushing tags: %b",$time, flush_strb); - else if (RELIABILITY_MODE && data_parity_error_vect != '0) $display("%t [l0cache]:data fault: flushing tags: %b",$time, flush_strb); + if (RELIABILITY_MODE && tag_parity_error_vect != '0 && data_parity_error_vect != '0) $display("%t [l0cache]: tag and data fault: flushing tags: %b",$time, flush_strb); + else if (RELIABILITY_MODE && tag_parity_error_vect != '0) $display("%t [l0cache]: tag fault: flushing tags: %b",$time, flush_strb); + else if (RELIABILITY_MODE && data_parity_error_vect != '0) $display("%t [l0cache]: data fault: flushing tags: %b",$time, flush_strb); end `FF(cnt_q, cnt_d, '0) diff --git a/hardware/deps/snitch/tb/README.md b/hardware/deps/snitch/tb/README.md new file mode 100644 index 000000000..abfbe0720 --- /dev/null +++ b/hardware/deps/snitch/tb/README.md @@ -0,0 +1,16 @@ +## Read-only cache test bench + +This folder contains the test bench for the read-only cache and the fault injection scripts to test the fault tolerance mechanism. +Faults are injected on the tag and data lines inside the lookup module. + +In order to test fault detection on the tag lines, a fault can be injected only on the parity bits, otherwise it will be treated as a miss. +To test fault detection on the data lines, each line is added separately to the list of signals where to inject faults. + +In order to test for the faults, the dimensions inside the `mempool/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl` file have to be updated. + +Usage: + +```bash +cd questa +questa-2022.3 vsim -do ../FI_sim_setup.tcl +``` \ No newline at end of file diff --git a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl index de426d82b..540a39062 100644 --- a/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl +++ b/hardware/deps/snitch/tb/fault_injection/ROC_inject_fault.tcl @@ -42,11 +42,12 @@ set max_tag_line_index 47 ; # Replace with the actual maximum value #debug 57, normal 47 # Add the signal paths with maximum indices to the inject_register_netlist +# Do not add the tag signal without indexes, as it flips entire lines to 0 +# To check tag functionality, we invert only the parity bit, which is the last one for {set index $max_index_value} {$index >= 0} {incr index -1} { set signal_path "${itag_sram_path}[${index}][${max_tag_line_index}]" lappend inject_register_netlist $signal_path } -#lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram #do not do this: it flips entire registers to zero #set max_data_index 63; set max_data_index [expr { @@ -58,13 +59,6 @@ for {set index $max_data_index} {$index >= 0} {incr index -1} { lappend inject_register_netlist $signal_path } -#lappend inject_register_netlist /snitch_read_only_cache_tb/dut/i_lookup/i_data/sram[63] - - -#/snitch_read_only_cache_tb/dut/i_lookup/i_data/sram -#/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag/sram - - set inject_signals_netlist [] set output_netlist [] set next_state_netlist [] From 79aeb583bab91f6b0d58f4e7ea1cb9aa7583b95b Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 8 Jan 2024 11:22:13 +0100 Subject: [PATCH 45/46] [hardware] Added README file about fault injection --- hardware/scripts/questa/README.md | 27 +++++++++++++++++ hardware/scripts/questa/get_banks.tcl | 12 +++++--- .../scripts/questa/mempool_inject_fault.tcl | 30 ++++++------------- hardware/scripts/questa/run.tcl | 13 ++------ 4 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 hardware/scripts/questa/README.md diff --git a/hardware/scripts/questa/README.md b/hardware/scripts/questa/README.md new file mode 100644 index 000000000..d4627bebd --- /dev/null +++ b/hardware/scripts/questa/README.md @@ -0,0 +1,27 @@ +## Testing MemPool with Fault Injection + +The fault injection scripts (`inject_fault.tcl`, `extract_nets.tcl`) are called by the script `mempool_inject_fault.tcl`. +In this script, the signals under fault injection must be listed and the parameters modified. + +This can be done with the `find signal` command in Questa, but for packed arrays this approach does not work. + +Therefore, the full signal path is appended to the instance path of the modules containing the cache. This is done in the script `get_banks.tcl`. + +To run the simulation, in the script `mempool_inject_fault.tcl` adapt the list of nets where to inject faults. Then, in the hardware folder run the following command: + +```bash +app=hello_world icache_faults=1 make sim +``` + + +## Get the number of faults that the Snitch Lookup had to deal with + +After the simulation finishes, it is possible to get the information on how many faults were detected. + +In the Questa command line, run the following script: + +```bash +do ../script/questa/get_number_faults.tcl +``` + +This script generates a file `mempool/hardware/build/data_tag_faults_stats.txt` listing, for each lookup modules, how many tag and data faults occurred. It also computes the average value across all modules. \ No newline at end of file diff --git a/hardware/scripts/questa/get_banks.tcl b/hardware/scripts/questa/get_banks.tcl index 11f312b3f..98a16ede2 100644 --- a/hardware/scripts/questa/get_banks.tcl +++ b/hardware/scripts/questa/get_banks.tcl @@ -1,3 +1,6 @@ +# This script produces a list of the tag and data nets in the L0, L1 and RO cache. +# Optionally, these can be saved in a file. + # Search for all instances of tag banks for ROC set pattern_match "*/i_snitch_read_only_cache/i_lookup/gen_sram/i_tag*" ; # Get the list of instance paths @@ -11,6 +14,7 @@ foreach inst $inst_list { lappend ROC_tag_list $ipath } } + # At this point, ROC_tag_list contains the list of instances only-- # no architecture names # @@ -28,7 +32,7 @@ foreach inst $inst_list { -# Search for all instances of tag banks for L1I +# Search for all instances of tag banks for L1 IC set pattern_match "*]/i_tag*" set inst_list [find instances -r $pattern_match] ; set L1_tag_list [list] ; @@ -68,7 +72,7 @@ foreach inst $inst_list { -# Search for all instances of data banks for L1I +# Search for all instances of data banks for L1 IC set pattern_match "*/i_snitch_icache/i_lookup/i_data*" set inst_list [find instances -r $pattern_match] ; set L1_data_list [list] ; @@ -87,7 +91,7 @@ foreach inst $inst_list { #close $fhandle ; -# Search for all instances of tag banks for L0 +# Search for all instances of tag banks for L0 IC set pattern_match "*i_snitch_icache_l0" set inst_list [find instances -r $pattern_match] ; set L0_tag_list [list] ; @@ -99,7 +103,7 @@ foreach inst $inst_list { } } -# Search for all instances of data banks for L0 +# Search for all instances of data banks for L0 IC set pattern_match "*i_snitch_icache_l0" set inst_list [find instances -r $pattern_match] ; set L0_data_list [list] ; diff --git a/hardware/scripts/questa/mempool_inject_fault.tcl b/hardware/scripts/questa/mempool_inject_fault.tcl index d8293373b..b7930fa8f 100644 --- a/hardware/scripts/questa/mempool_inject_fault.tcl +++ b/hardware/scripts/questa/mempool_inject_fault.tcl @@ -21,30 +21,18 @@ set check_core_output_modification 0 set check_core_next_state_modification 0 set reg_to_sig_ratio 1 -#proc base_path {} {return "/snitch_read_only_cache_tb/dut/i_lookup/gen_sram/i_tag"} source ${::script_base_path}get_banks.tcl - -#proc add_signals_injection {tag_list tag_idx tag_max_width data_list no_databanks} { -# foreach inst $tag_list { -# for {set index $tag_idx} {$index >= 0} {incr index -1} { -# set signal_path "${inst}[${index}][${tag_max_width}]" -# lappend inject_register_netlist $signal_path -# } -# } -# foreach inst $data_list { -# for {set index $no_databanks} {$index >= 0} {incr index -1} { -# set signal_path "${inst}[${index}]" -# lappend inject_register_netlist $signal_path -# } -# } -#} set inject_register_netlist {} + +# Tag fault injection on Snitch Instruction Cache +# how many lines in the tag banks set max_idx_icache 31 +# length of tag banks line set max_tag_width_icache 25 - +# lines in the data banks set no_banks_icache 63 -#add_signals_injection $L1_tag_list $max_idx_icache $max_tag_width_icache $L1_data_list $no_banks_icache +# To check error detection on the tag, inject faults only on the parity bits. To check performance, inject on the entire line. foreach inst $L1_tag_list { for {set index $max_idx_icache} {$index >= 0} {incr index -1} { set signal_path "${inst}[${index}]" @@ -59,11 +47,10 @@ set no_banks_icache 63 } } +# Tag fault injection on Snitch RO Cache set max_idx_rocache 63 set max_tag_width_rocache 47 - set no_banks_rocache 127 -#add_signals_injection $ROC_tag_list $max_idx_rocache $max_tag_width_rocache $L1_data_list $no_banks_rocache foreach inst $ROC_tag_list { for {set index $max_idx_rocache} {$index >= 0} {incr index -1} { @@ -79,6 +66,7 @@ set no_banks_rocache 127 } } +# Tag fault injection in the L0 Cache, unfortunately not working correctly due to a bug. See fault injection script for more details. set max_idx_l0cache 3 set max_tag_width_l0cache 27 set no_banks_l0cache 3 @@ -99,7 +87,7 @@ set no_banks_l0cache 3 } -#random shuffle of the list +# Random shuffle of the list, the log showed that fault injection script is not so random in picking the signals for {set i 0} {$i < [llength $inject_register_netlist]} {incr i} { set j [expr {int(rand() * [llength $inject_register_netlist])}] set temp [lindex $inject_register_netlist $j] diff --git a/hardware/scripts/questa/run.tcl b/hardware/scripts/questa/run.tcl index fa7b133a1..cb74cbe5f 100644 --- a/hardware/scripts/questa/run.tcl +++ b/hardware/scripts/questa/run.tcl @@ -4,18 +4,11 @@ set allow_faults 1 #[lindex $argv 0] -if {$::env(icache_faults) ==1} { +# Run the fault injection script +if {$::env(icache_faults) == 1} { source ../scripts/questa/mempool_inject_fault.tcl - } do ../scripts/questa/wave.tcl -add wave -Group gr1_axitocache {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/*} -add wave -Group gr1_lookup {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_lookup/*} -add wave -Group gr1_roc {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/*} -add wave -Group gr1_refill {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_refill/*} -add wave -Group gr1_handler {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_handler/*} -add wave -Group gr1_splittertable {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/i_axi_burst_splitter_table/*} -add wave -Group idq {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[1]/i_group/i_axi_interco/gen_bottom_level/gen_ro_cache/i_snitch_read_only_cache/i_axi_to_cache/i_axi_burst_splitter_table/i_idq/*} -add wave -Group gr0_l0 {sim:/mempool_tb/dut/i_mempool_cluster/gen_groups[0]/i_group/gen_tiles[0]/i_tile/gen_caches[0]/i_snitch_icache/gen_prefetcher[0]/i_snitch_icache_l0/*} + log -r /* run -a From 9dcfc028dc59b91961fdf4a6c39d0304f7d08540 Mon Sep 17 00:00:00 2001 From: nicomar0 Date: Mon, 8 Jan 2024 11:24:31 +0100 Subject: [PATCH 46/46] [hardware] Updated readme --- hardware/scripts/questa/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/scripts/questa/README.md b/hardware/scripts/questa/README.md index d4627bebd..90897fe53 100644 --- a/hardware/scripts/questa/README.md +++ b/hardware/scripts/questa/README.md @@ -14,7 +14,7 @@ app=hello_world icache_faults=1 make sim ``` -## Get the number of faults that the Snitch Lookup had to deal with +### Get the number of faults that the Snitch Lookup had to deal with After the simulation finishes, it is possible to get the information on how many faults were detected.