data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv
1: b"%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:28: syntax error, unexpected IDENTIFIER, expecting PACKAGE-IDENTIFIER\nmodule cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(\n                          ^~~~~~~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:30: syntax error, unexpected parameter, expecting IDENTIFIER\n  parameter ariane_pkg::ariane_cfg_t    ArianeCfg          = ariane_pkg::ArianeDefaultConfig      \n  ^~~~~~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:33: syntax error, unexpected input, expecting IDENTIFIER or '=' or do or final\n  input  logic                      rst_ni,\n  ^~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:35: syntax error, unexpected input, expecting IDENTIFIER or '=' or do or final\n  input  logic                      flush_i,               \n  ^~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:36: syntax error, unexpected input, expecting IDENTIFIER or '=' or do or final\n  input  logic                      en_i,                  \n  ^~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:37: syntax error, unexpected output, expecting IDENTIFIER or '=' or do or final\n  output logic                      miss_o,                \n  ^~~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:39: syntax error, unexpected input, expecting IDENTIFIER or '=' or do or final\n  input  icache_areq_i_t            areq_i,\n  ^~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:46: syntax error, unexpected input, expecting IDENTIFIER or '=' or do or final\n  input  icache_rtrn_t              mem_rtrn_i,\n  ^~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:48: syntax error, unexpected input, expecting IDENTIFIER or '=' or do or final\n  input  logic                      mem_data_ack_i,\n  ^~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:49: syntax error, unexpected output, expecting IDENTIFIER or '=' or do or final\n  output icache_req_t               mem_data_o\n  ^~~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:54: syntax error, unexpected ::, expecting ':'\n  logic [riscv::VLEN-1:0]               vaddr_d, vaddr_q;\n              ^~\n                                                                                                                               : ... Perhaps 'riscv' is a package which needs to be predeclared? (IEEE 1800-2017 26.3)\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:100: syntax error, unexpected assign\n  assign cl_tag_d  = (areq_i.fetch_valid) ? areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH] : cl_tag_q;\n  ^~~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:151: syntax error, unexpected assign\n  assign addr_ni = is_inside_nonidempotent_regions(ArianeCfg, areq_i.fetch_paddr);\n  ^~~~~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:389: syntax error, unexpected for\n  for (genvar i=0;i<ICACHE_SET_ASSOC;i++) begin : gen_tag_cmpsel\n  ^~~\n%Error: data/full_repos/permissive/509468557/generators/cva6/src/main/resources/vsrc/cva6/src/cache_subsystem/cva6_icache.sv:413: syntax error, unexpected for\n  for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_sram\n  ^~~\n%Error: Exiting due to 15 error(s)\n"
module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
  parameter logic [CACHE_ID_WIDTH-1:0]  RdTxId             = 0,
  parameter ariane_pkg::ariane_cfg_t    ArianeCfg          = ariane_pkg::ArianeDefaultConfig
) (
  input  logic                      clk_i,
  input  logic                      rst_ni,

  input  logic                      flush_i,
  input  logic                      en_i,
  output logic                      miss_o,

  input  icache_areq_i_t            areq_i,
  output icache_areq_o_t            areq_o,

  input  icache_dreq_i_t            dreq_i,
  output icache_dreq_o_t            dreq_o,

  input  logic                      mem_rtrn_vld_i,
  input  icache_rtrn_t              mem_rtrn_i,
  output logic                      mem_data_req_o,
  input  logic                      mem_data_ack_i,
  output icache_req_t               mem_data_o
);


  logic                                 cache_en_d, cache_en_q;
  logic [riscv::VLEN-1:0]               vaddr_d, vaddr_q;
  logic                                 paddr_is_nc;
  logic [ICACHE_SET_ASSOC-1:0]          cl_hit;
  logic                                 cache_rden;
  logic                                 cache_wren;
  logic                                 cmp_en_d, cmp_en_q;
  logic                                 flush_d, flush_q;


  logic                                 update_lfsr;
  logic [$clog2(ICACHE_SET_ASSOC)-1:0]  inv_way;
  logic [$clog2(ICACHE_SET_ASSOC)-1:0]  rnd_way;
  logic [$clog2(ICACHE_SET_ASSOC)-1:0]  repl_way;
  logic [ICACHE_SET_ASSOC-1:0]          repl_way_oh_d, repl_way_oh_q;
  logic                                 all_ways_valid;


  logic                                 inv_en;
  logic                                 inv_d, inv_q;
  logic                                 flush_en, flush_done;
  logic [ICACHE_CL_IDX_WIDTH-1:0]       flush_cnt_d, flush_cnt_q;


  logic                                 cl_we;
  logic [ICACHE_SET_ASSOC-1:0]          cl_req;
  logic [ICACHE_CL_IDX_WIDTH-1:0]       cl_index;
  logic [ICACHE_OFFSET_WIDTH-1:0]       cl_offset_d, cl_offset_q;
  logic [ICACHE_TAG_WIDTH-1:0]          cl_tag_d, cl_tag_q;
  logic [ICACHE_TAG_WIDTH-1:0]          cl_tag_rdata [ICACHE_SET_ASSOC-1:0];
  logic [ICACHE_LINE_WIDTH-1:0]         cl_rdata     [ICACHE_SET_ASSOC-1:0];
  logic [ICACHE_SET_ASSOC-1:0][FETCH_WIDTH-1:0]cl_sel;
  logic [ICACHE_SET_ASSOC-1:0]          vld_req;
  logic                                 vld_we;
  logic [ICACHE_SET_ASSOC-1:0]          vld_wdata;
  logic [ICACHE_SET_ASSOC-1:0]          vld_rdata;
  logic [ICACHE_CL_IDX_WIDTH-1:0]       vld_addr;


  typedef enum logic[2:0] {FLUSH, IDLE, READ, MISS, KILL_ATRANS, KILL_MISS} state_e;
  state_e state_d, state_q;






  assign cl_tag_d  = (areq_i.fetch_valid) ? areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH] : cl_tag_q;


  assign paddr_is_nc = (~cache_en_q) | (~ariane_pkg::is_inside_cacheable_regions(ArianeCfg, {{{64-riscv::PLEN}{1'b0}}, cl_tag_d, {ICACHE_INDEX_WIDTH{1'b0}}}));


  assign dreq_o.ex = areq_i.fetch_exception;



  assign vaddr_d = (dreq_o.ready & dreq_i.req) ? dreq_i.vaddr : vaddr_q;
  assign areq_o.fetch_vaddr = {vaddr_q>>2, 2'b0};


  assign cl_index    = vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH];


  if (ArianeCfg.Axi64BitCompliant) begin : gen_axi_offset

    assign cl_offset_d = ( dreq_o.ready & dreq_i.req)      ? {dreq_i.vaddr>>2, 2'b0} :
                         ( paddr_is_nc  & mem_data_req_o ) ? cl_offset_q[2]<<2 :
                                                             cl_offset_q;

    assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:3], 3'b0} :
                                              {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH], {ICACHE_OFFSET_WIDTH{1'b0}}};
end else begin : gen_piton_offset


    assign cl_offset_d = ( dreq_o.ready & dreq_i.req)      ? {dreq_i.vaddr>>2, 2'b0} :
                                                             cl_offset_q;


    assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:2], 2'b0} :
                                              {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH], {ICACHE_OFFSET_WIDTH{1'b0}}};
  end


  assign mem_data_o.tid   = RdTxId;

  assign mem_data_o.nc    = paddr_is_nc;

  assign mem_data_o.way   = repl_way;
  assign dreq_o.vaddr     = vaddr_q;


  assign inv_d = inv_en;




  logic addr_ni;
  assign addr_ni = is_inside_nonidempotent_regions(ArianeCfg, areq_i.fetch_paddr);
  always_comb begin : p_fsm

    state_d      = state_q;
    cache_en_d   = cache_en_q & en_i;
    flush_en     = 1'b0;
    cmp_en_d     = 1'b0;
    cache_rden   = 1'b0;
    cache_wren   = 1'b0;
    inv_en       = 1'b0;
    flush_d      = flush_q | flush_i;


    dreq_o.ready     = 1'b0;
    areq_o.fetch_req = 1'b0;
    dreq_o.valid     = 1'b0;
    mem_data_req_o   = 1'b0;

    miss_o           = 1'b0;






    if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_INV_REQ) begin
      inv_en = 1'b1;
    end

    unique case (state_q)


      FLUSH: begin
          flush_en = 1'b1;
        if (flush_done) begin
          state_d = IDLE;
          flush_d = 1'b0;

          cache_en_d = en_i;
        end
      end


      IDLE: begin

          cmp_en_d = cache_en_q;


          if (flush_d || (en_i && !cache_en_q)) begin
            state_d    = FLUSH;

          end else begin

            if (!mem_rtrn_vld_i) begin
              dreq_o.ready = 1'b1;

              if (dreq_i.req) begin
                cache_rden       = 1'b1;
                state_d          = READ;
              end
            end
            if (dreq_i.kill_s1) begin
              state_d = IDLE;
            end
          end
      end






      READ: begin
          areq_o.fetch_req = '1;

          cmp_en_d    = cache_en_q;

          cache_rden  = cache_en_q;

          if (areq_i.fetch_valid && (!dreq_i.spec || !addr_ni) ) begin

            if (flush_d) begin
              state_d  = IDLE;

            end else if (((|cl_hit && cache_en_q) || areq_i.fetch_exception.valid) && !inv_q) begin
              dreq_o.valid     = ~dreq_i.kill_s2;
              state_d          = IDLE;




              if (!mem_rtrn_vld_i) begin
                dreq_o.ready     = 1'b1;
                if (dreq_i.req) begin
                  state_d          = READ;
                end
              end


              if (dreq_i.kill_s1) begin
                state_d = IDLE;
              end

            end else if (dreq_i.kill_s2) begin
              state_d = IDLE;
            end else if (!inv_q) begin
              cmp_en_d = 1'b0;



              mem_data_req_o = 1'b1;
              if (mem_data_ack_i) begin
                miss_o         = ~paddr_is_nc;
                state_d        = MISS;
              end
            end

          end else if (dreq_i.kill_s2 || flush_d) begin
            state_d  = KILL_ATRANS;
          end
      end




      MISS: begin


        if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_IFILL_ACK) begin
          state_d      = IDLE;

          if (!(dreq_i.kill_s2 || flush_d)) begin
            dreq_o.valid = 1'b1;

            cache_wren   = ~paddr_is_nc;
          end

        end else if (dreq_i.kill_s2 || flush_d) begin
          state_d  = KILL_MISS;
        end
      end




      KILL_ATRANS: begin
        areq_o.fetch_req = '1;
        if (areq_i.fetch_valid) begin
          state_d      = IDLE;
        end
      end




      KILL_MISS: begin
        if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_IFILL_ACK) begin
          state_d      = IDLE;
        end
      end
      default: begin

        state_d = FLUSH;
      end
    endcase
  end










  assign flush_cnt_d = (flush_done) ? '0               :
                       (flush_en)   ?  flush_cnt_q + 1 :
                                       flush_cnt_q;

  assign flush_done  = (flush_cnt_q==(ICACHE_NUM_WORDS-1));



  assign vld_addr = (flush_en)       ? flush_cnt_q        :
                    (inv_en)         ? mem_rtrn_i.inv.idx[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH] :
                                       cl_index;

  assign vld_req  = (flush_en || cache_rden)        ? '1                                    :
                    (mem_rtrn_i.inv.all && inv_en)  ? '1                                    :
                    (mem_rtrn_i.inv.vld && inv_en)  ? icache_way_bin2oh(mem_rtrn_i.inv.way) :
                                                      repl_way_oh_q;

  assign vld_wdata = (cache_wren) ? '1 : '0;

  assign vld_we    = (cache_wren | inv_en | flush_en);




  assign update_lfsr   = cache_wren & all_ways_valid;
  assign repl_way      = (all_ways_valid) ? rnd_way : inv_way;
  assign repl_way_oh_d = (cmp_en_q) ? icache_way_bin2oh(repl_way) : repl_way_oh_q;


  assign cl_req   = (cache_rden) ? '1            :
                    (cache_wren) ? repl_way_oh_q :
                                   '0;
  assign cl_we    = cache_wren;



  lzc #(
    .WIDTH ( ICACHE_SET_ASSOC )
  ) i_lzc (
    .in_i    ( ~vld_rdata     ),
    .cnt_o   ( inv_way        ),
    .empty_o ( all_ways_valid )
  );


  lfsr_8bit #(
    .WIDTH (ICACHE_SET_ASSOC)
  ) i_lfsr (
    .clk_i          ( clk_i       ),
    .rst_ni         ( rst_ni      ),
    .en_i           ( update_lfsr ),
    .refill_way_oh  (             ),
    .refill_way_bin ( rnd_way     )
  );






  logic [$clog2(ICACHE_SET_ASSOC)-1:0] hit_idx;

  for (genvar i=0;i<ICACHE_SET_ASSOC;i++) begin : gen_tag_cmpsel
    assign cl_hit[i] = (cl_tag_rdata[i] == cl_tag_d) & vld_rdata[i];
    assign cl_sel[i] = cl_rdata[i][{cl_offset_q,3'b0} +: FETCH_WIDTH];
  end


  lzc #(
    .WIDTH ( ICACHE_SET_ASSOC )
  ) i_lzc_hit (
    .in_i    ( cl_hit  ),
    .cnt_o   ( hit_idx ),
    .empty_o (         )
  );

  assign dreq_o.data = (cmp_en_q) ? cl_sel[hit_idx] :
                                    mem_rtrn_i.data[{cl_offset_q,3'b0} +: FETCH_WIDTH];






  logic [ICACHE_TAG_WIDTH:0] cl_tag_valid_rdata [ICACHE_SET_ASSOC-1:0];

  for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_sram

    sram #(

      .DATA_WIDTH ( ICACHE_TAG_WIDTH+1 ),
      .NUM_WORDS  ( ICACHE_NUM_WORDS   )
    ) tag_sram (
      .clk_i     ( clk_i                    ),
      .rst_ni    ( rst_ni                   ),
      .req_i     ( vld_req[i]               ),
      .we_i      ( vld_we                   ),
      .addr_i    ( vld_addr                 ),


      .wdata_i   ( {vld_wdata[i], cl_tag_q} ),
      .be_i      ( '1                       ),
      .rdata_o   ( cl_tag_valid_rdata[i]    )
    );

    assign cl_tag_rdata[i] = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH-1:0];
    assign vld_rdata[i]    = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH];


    sram #(
      .DATA_WIDTH ( ICACHE_LINE_WIDTH ),
      .NUM_WORDS  ( ICACHE_NUM_WORDS  )
    ) data_sram (
      .clk_i     ( clk_i               ),
      .rst_ni    ( rst_ni              ),
      .req_i     ( cl_req[i]           ),
      .we_i      ( cl_we               ),
      .addr_i    ( cl_index            ),
      .wdata_i   ( mem_rtrn_i.data     ),
      .be_i      ( '1                  ),
      .rdata_o   ( cl_rdata[i]         )
    );
  end


  always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
    if(!rst_ni) begin
      cl_tag_q      <= '0;
      flush_cnt_q   <= '0;
      vaddr_q       <= '0;
      cmp_en_q      <= '0;
      cache_en_q    <= '0;
      flush_q       <= '0;
      state_q       <= IDLE;
      cl_offset_q   <= '0;
      repl_way_oh_q <= '0;
      inv_q         <= '0;
    end else begin
      cl_tag_q      <= cl_tag_d;
      flush_cnt_q   <= flush_cnt_d;
      vaddr_q       <= vaddr_d;
      cmp_en_q      <= cmp_en_d;
      cache_en_q    <= cache_en_d;
      flush_q       <= flush_d;
      state_q       <= state_d;
      cl_offset_q   <= cl_offset_d;
      repl_way_oh_q <= repl_way_oh_d;
      inv_q         <= inv_d;
    end
  end






`ifndef VERILATOR
  repl_inval0: assert property (
    @(posedge clk_i) disable iff (!rst_ni) cache_wren |-> !(mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld))
      else $fatal(1,"[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");

  repl_inval1: assert property (
    @(posedge clk_i) disable iff (!rst_ni) (mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld) |-> !cache_wren)
      else $fatal(1,"[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");

  invalid_state: assert property (
    @(posedge clk_i) disable iff (!rst_ni) (state_q inside {FLUSH, IDLE, READ, MISS, KILL_ATRANS, KILL_MISS}))
      else $fatal(1,"[l1 icache] fsm reached an invalid state");

  hot1: assert property (
    @(posedge clk_i) disable iff (!rst_ni) (!inv_en) |-> cache_rden |=> cmp_en_q |-> $onehot0(cl_hit))
      else $fatal(1,"[l1 icache] cl_hit signal must be hot1");


  logic                                    vld_mirror[wt_cache_pkg::ICACHE_NUM_WORDS-1:0][ariane_pkg::ICACHE_SET_ASSOC-1:0];
  logic [ariane_pkg::ICACHE_TAG_WIDTH-1:0] tag_mirror[wt_cache_pkg::ICACHE_NUM_WORDS-1:0][ariane_pkg::ICACHE_SET_ASSOC-1:0];
  logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] tag_write_duplicate_test;

  always_ff @(posedge clk_i or negedge rst_ni) begin : p_mirror
    if(!rst_ni) begin
      vld_mirror <= '{default:'0};
      tag_mirror <= '{default:'0};
    end else begin
      for (int i = 0; i < ICACHE_SET_ASSOC; i++) begin
        if(vld_req[i] & vld_we) begin
          vld_mirror[vld_addr][i] <= vld_wdata[i];
          tag_mirror[vld_addr][i] <= cl_tag_q;
        end
      end
    end
  end

  for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_tag_dupl
    assign tag_write_duplicate_test[i] = (tag_mirror[vld_addr][i] == cl_tag_q) & vld_mirror[vld_addr][i] & (|vld_wdata);
  end

  tag_write_duplicate: assert property (
    @(posedge clk_i) disable iff (!rst_ni) |vld_req |-> vld_we |-> !(|tag_write_duplicate_test))
      else $fatal(1,"[l1 icache] cannot allocate a CL that is already present in the cache");


 initial begin

  assert (ICACHE_INDEX_WIDTH<=12)
    else $fatal(1,"[l1 icache] cache index width can be maximum 12bit since VM uses 4kB pages");
 end
`endif


endmodule
module cva6_icache import ariane_pkg::*;
 import wt_cache_pkg::*; #(
  parameter logic [CACHE_ID_WIDTH-1:0]  RdTxId             = 0,
  parameter ariane_pkg::ariane_cfg_t    ArianeCfg          = ariane_pkg::ArianeDefaultConfig
) (
  input  logic                      clk_i,
  input  logic                      rst_ni,

  input  logic                      flush_i,
  input  logic                      en_i,
  output logic                      miss_o,

  input  icache_areq_i_t            areq_i,
  output icache_areq_o_t            areq_o,

  input  icache_dreq_i_t            dreq_i,
  output icache_dreq_o_t            dreq_o,

  input  logic                      mem_rtrn_vld_i,
  input  icache_rtrn_t              mem_rtrn_i,
  output logic                      mem_data_req_o,
  input  logic                      mem_data_ack_i,
  output icache_req_t               mem_data_o
);


  logic                                 cache_en_d, cache_en_q;
  logic [riscv::VLEN-1:0]               vaddr_d, vaddr_q;
  logic                                 paddr_is_nc;
  logic [ICACHE_SET_ASSOC-1:0]          cl_hit;
  logic                                 cache_rden;
  logic                                 cache_wren;
  logic                                 cmp_en_d, cmp_en_q;
  logic                                 flush_d, flush_q;


  logic                                 update_lfsr;
  logic [$clog2(ICACHE_SET_ASSOC)-1:0]  inv_way;
  logic [$clog2(ICACHE_SET_ASSOC)-1:0]  rnd_way;
  logic [$clog2(ICACHE_SET_ASSOC)-1:0]  repl_way;
  logic [ICACHE_SET_ASSOC-1:0]          repl_way_oh_d, repl_way_oh_q;
  logic                                 all_ways_valid;


  logic                                 inv_en;
  logic                                 inv_d, inv_q;
  logic                                 flush_en, flush_done;
  logic [ICACHE_CL_IDX_WIDTH-1:0]       flush_cnt_d, flush_cnt_q;


  logic                                 cl_we;
  logic [ICACHE_SET_ASSOC-1:0]          cl_req;
  logic [ICACHE_CL_IDX_WIDTH-1:0]       cl_index;
  logic [ICACHE_OFFSET_WIDTH-1:0]       cl_offset_d, cl_offset_q;
  logic [ICACHE_TAG_WIDTH-1:0]          cl_tag_d, cl_tag_q;
  logic [ICACHE_TAG_WIDTH-1:0]          cl_tag_rdata [ICACHE_SET_ASSOC-1:0];
  logic [ICACHE_LINE_WIDTH-1:0]         cl_rdata     [ICACHE_SET_ASSOC-1:0];
  logic [ICACHE_SET_ASSOC-1:0][FETCH_WIDTH-1:0]cl_sel;
  logic [ICACHE_SET_ASSOC-1:0]          vld_req;
  logic                                 vld_we;
  logic [ICACHE_SET_ASSOC-1:0]          vld_wdata;
  logic [ICACHE_SET_ASSOC-1:0]          vld_rdata;
  logic [ICACHE_CL_IDX_WIDTH-1:0]       vld_addr;


  typedef enum logic[2:0] {FLUSH, IDLE, READ, MISS, KILL_ATRANS, KILL_MISS} state_e;
  state_e state_d, state_q;






  assign cl_tag_d  = (areq_i.fetch_valid) ? areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH] : cl_tag_q;


  assign paddr_is_nc = (~cache_en_q) | (~ariane_pkg::is_inside_cacheable_regions(ArianeCfg, {{{64-riscv::PLEN}{1'b0}}, cl_tag_d, {ICACHE_INDEX_WIDTH{1'b0}}}));


  assign dreq_o.ex = areq_i.fetch_exception;



  assign vaddr_d = (dreq_o.ready & dreq_i.req) ? dreq_i.vaddr : vaddr_q;
  assign areq_o.fetch_vaddr = {vaddr_q>>2, 2'b0};


  assign cl_index    = vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH];


  if (ArianeCfg.Axi64BitCompliant) begin : gen_axi_offset

    assign cl_offset_d = ( dreq_o.ready & dreq_i.req)      ? {dreq_i.vaddr>>2, 2'b0} :
                         ( paddr_is_nc  & mem_data_req_o ) ? cl_offset_q[2]<<2 :
                                                             cl_offset_q;

    assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:3], 3'b0} :
                                              {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH], {ICACHE_OFFSET_WIDTH{1'b0}}};
end else begin : gen_piton_offset


    assign cl_offset_d = ( dreq_o.ready & dreq_i.req)      ? {dreq_i.vaddr>>2, 2'b0} :
                                                             cl_offset_q;


    assign mem_data_o.paddr = (paddr_is_nc) ? {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:2], 2'b0} :
                                              {cl_tag_d, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH], {ICACHE_OFFSET_WIDTH{1'b0}}};
  end


  assign mem_data_o.tid   = RdTxId;

  assign mem_data_o.nc    = paddr_is_nc;

  assign mem_data_o.way   = repl_way;
  assign dreq_o.vaddr     = vaddr_q;


  assign inv_d = inv_en;




  logic addr_ni;
  assign addr_ni = is_inside_nonidempotent_regions(ArianeCfg, areq_i.fetch_paddr);
  always_comb begin : p_fsm

    state_d      = state_q;
    cache_en_d   = cache_en_q & en_i;
    flush_en     = 1'b0;
    cmp_en_d     = 1'b0;
    cache_rden   = 1'b0;
    cache_wren   = 1'b0;
    inv_en       = 1'b0;
    flush_d      = flush_q | flush_i;


    dreq_o.ready     = 1'b0;
    areq_o.fetch_req = 1'b0;
    dreq_o.valid     = 1'b0;
    mem_data_req_o   = 1'b0;

    miss_o           = 1'b0;






    if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_INV_REQ) begin
      inv_en = 1'b1;
    end

    unique case (state_q)


      FLUSH: begin
          flush_en = 1'b1;
        if (flush_done) begin
          state_d = IDLE;
          flush_d = 1'b0;

          cache_en_d = en_i;
        end
      end


      IDLE: begin

          cmp_en_d = cache_en_q;


          if (flush_d || (en_i && !cache_en_q)) begin
            state_d    = FLUSH;

          end else begin

            if (!mem_rtrn_vld_i) begin
              dreq_o.ready = 1'b1;

              if (dreq_i.req) begin
                cache_rden       = 1'b1;
                state_d          = READ;
              end
            end
            if (dreq_i.kill_s1) begin
              state_d = IDLE;
            end
          end
      end






      READ: begin
          areq_o.fetch_req = '1;

          cmp_en_d    = cache_en_q;

          cache_rden  = cache_en_q;

          if (areq_i.fetch_valid && (!dreq_i.spec || !addr_ni) ) begin

            if (flush_d) begin
              state_d  = IDLE;

            end else if (((|cl_hit && cache_en_q) || areq_i.fetch_exception.valid) && !inv_q) begin
              dreq_o.valid     = ~dreq_i.kill_s2;
              state_d          = IDLE;




              if (!mem_rtrn_vld_i) begin
                dreq_o.ready     = 1'b1;
                if (dreq_i.req) begin
                  state_d          = READ;
                end
              end


              if (dreq_i.kill_s1) begin
                state_d = IDLE;
              end

            end else if (dreq_i.kill_s2) begin
              state_d = IDLE;
            end else if (!inv_q) begin
              cmp_en_d = 1'b0;



              mem_data_req_o = 1'b1;
              if (mem_data_ack_i) begin
                miss_o         = ~paddr_is_nc;
                state_d        = MISS;
              end
            end

          end else if (dreq_i.kill_s2 || flush_d) begin
            state_d  = KILL_ATRANS;
          end
      end




      MISS: begin


        if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_IFILL_ACK) begin
          state_d      = IDLE;

          if (!(dreq_i.kill_s2 || flush_d)) begin
            dreq_o.valid = 1'b1;

            cache_wren   = ~paddr_is_nc;
          end

        end else if (dreq_i.kill_s2 || flush_d) begin
          state_d  = KILL_MISS;
        end
      end




      KILL_ATRANS: begin
        areq_o.fetch_req = '1;
        if (areq_i.fetch_valid) begin
          state_d      = IDLE;
        end
      end




      KILL_MISS: begin
        if (mem_rtrn_vld_i && mem_rtrn_i.rtype == ICACHE_IFILL_ACK) begin
          state_d      = IDLE;
        end
      end
      default: begin

        state_d = FLUSH;
      end
    endcase
  end










  assign flush_cnt_d = (flush_done) ? '0               :
                       (flush_en)   ?  flush_cnt_q + 1 :
                                       flush_cnt_q;

  assign flush_done  = (flush_cnt_q==(ICACHE_NUM_WORDS-1));



  assign vld_addr = (flush_en)       ? flush_cnt_q        :
                    (inv_en)         ? mem_rtrn_i.inv.idx[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH] :
                                       cl_index;

  assign vld_req  = (flush_en || cache_rden)        ? '1                                    :
                    (mem_rtrn_i.inv.all && inv_en)  ? '1                                    :
                    (mem_rtrn_i.inv.vld && inv_en)  ? icache_way_bin2oh(mem_rtrn_i.inv.way) :
                                                      repl_way_oh_q;

  assign vld_wdata = (cache_wren) ? '1 : '0;

  assign vld_we    = (cache_wren | inv_en | flush_en);




  assign update_lfsr   = cache_wren & all_ways_valid;
  assign repl_way      = (all_ways_valid) ? rnd_way : inv_way;
  assign repl_way_oh_d = (cmp_en_q) ? icache_way_bin2oh(repl_way) : repl_way_oh_q;


  assign cl_req   = (cache_rden) ? '1            :
                    (cache_wren) ? repl_way_oh_q :
                                   '0;
  assign cl_we    = cache_wren;



  lzc #(
    .WIDTH ( ICACHE_SET_ASSOC )
  ) i_lzc (
    .in_i    ( ~vld_rdata     ),
    .cnt_o   ( inv_way        ),
    .empty_o ( all_ways_valid )
  );


  lfsr_8bit #(
    .WIDTH (ICACHE_SET_ASSOC)
  ) i_lfsr (
    .clk_i          ( clk_i       ),
    .rst_ni         ( rst_ni      ),
    .en_i           ( update_lfsr ),
    .refill_way_oh  (             ),
    .refill_way_bin ( rnd_way     )
  );






  logic [$clog2(ICACHE_SET_ASSOC)-1:0] hit_idx;

  for (genvar i=0;i<ICACHE_SET_ASSOC;i++) begin : gen_tag_cmpsel
    assign cl_hit[i] = (cl_tag_rdata[i] == cl_tag_d) & vld_rdata[i];
    assign cl_sel[i] = cl_rdata[i][{cl_offset_q,3'b0} +: FETCH_WIDTH];
  end


  lzc #(
    .WIDTH ( ICACHE_SET_ASSOC )
  ) i_lzc_hit (
    .in_i    ( cl_hit  ),
    .cnt_o   ( hit_idx ),
    .empty_o (         )
  );

  assign dreq_o.data = (cmp_en_q) ? cl_sel[hit_idx] :
                                    mem_rtrn_i.data[{cl_offset_q,3'b0} +: FETCH_WIDTH];






  logic [ICACHE_TAG_WIDTH:0] cl_tag_valid_rdata [ICACHE_SET_ASSOC-1:0];

  for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_sram

    sram #(

      .DATA_WIDTH ( ICACHE_TAG_WIDTH+1 ),
      .NUM_WORDS  ( ICACHE_NUM_WORDS   )
    ) tag_sram (
      .clk_i     ( clk_i                    ),
      .rst_ni    ( rst_ni                   ),
      .req_i     ( vld_req[i]               ),
      .we_i      ( vld_we                   ),
      .addr_i    ( vld_addr                 ),


      .wdata_i   ( {vld_wdata[i], cl_tag_q} ),
      .be_i      ( '1                       ),
      .rdata_o   ( cl_tag_valid_rdata[i]    )
    );

    assign cl_tag_rdata[i] = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH-1:0];
    assign vld_rdata[i]    = cl_tag_valid_rdata[i][ICACHE_TAG_WIDTH];


    sram #(
      .DATA_WIDTH ( ICACHE_LINE_WIDTH ),
      .NUM_WORDS  ( ICACHE_NUM_WORDS  )
    ) data_sram (
      .clk_i     ( clk_i               ),
      .rst_ni    ( rst_ni              ),
      .req_i     ( cl_req[i]           ),
      .we_i      ( cl_we               ),
      .addr_i    ( cl_index            ),
      .wdata_i   ( mem_rtrn_i.data     ),
      .be_i      ( '1                  ),
      .rdata_o   ( cl_rdata[i]         )
    );
  end


  always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
    if(!rst_ni) begin
      cl_tag_q      <= '0;
      flush_cnt_q   <= '0;
      vaddr_q       <= '0;
      cmp_en_q      <= '0;
      cache_en_q    <= '0;
      flush_q       <= '0;
      state_q       <= IDLE;
      cl_offset_q   <= '0;
      repl_way_oh_q <= '0;
      inv_q         <= '0;
    end else begin
      cl_tag_q      <= cl_tag_d;
      flush_cnt_q   <= flush_cnt_d;
      vaddr_q       <= vaddr_d;
      cmp_en_q      <= cmp_en_d;
      cache_en_q    <= cache_en_d;
      flush_q       <= flush_d;
      state_q       <= state_d;
      cl_offset_q   <= cl_offset_d;
      repl_way_oh_q <= repl_way_oh_d;
      inv_q         <= inv_d;
    end
  end






`ifndef VERILATOR
  repl_inval0: assert property (
    @(posedge clk_i) disable iff (!rst_ni) cache_wren |-> !(mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld))
      else $fatal(1,"[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");

  repl_inval1: assert property (
    @(posedge clk_i) disable iff (!rst_ni) (mem_rtrn_i.inv.all | mem_rtrn_i.inv.vld) |-> !cache_wren)
      else $fatal(1,"[l1 icache] cannot replace cacheline and invalidate cacheline simultaneously");

  invalid_state: assert property (
    @(posedge clk_i) disable iff (!rst_ni) (state_q inside {FLUSH, IDLE, READ, MISS, KILL_ATRANS, KILL_MISS}))
      else $fatal(1,"[l1 icache] fsm reached an invalid state");

  hot1: assert property (
    @(posedge clk_i) disable iff (!rst_ni) (!inv_en) |-> cache_rden |=> cmp_en_q |-> $onehot0(cl_hit))
      else $fatal(1,"[l1 icache] cl_hit signal must be hot1");


  logic                                    vld_mirror[wt_cache_pkg::ICACHE_NUM_WORDS-1:0][ariane_pkg::ICACHE_SET_ASSOC-1:0];
  logic [ariane_pkg::ICACHE_TAG_WIDTH-1:0] tag_mirror[wt_cache_pkg::ICACHE_NUM_WORDS-1:0][ariane_pkg::ICACHE_SET_ASSOC-1:0];
  logic [ariane_pkg::ICACHE_SET_ASSOC-1:0] tag_write_duplicate_test;

  always_ff @(posedge clk_i or negedge rst_ni) begin : p_mirror
    if(!rst_ni) begin
      vld_mirror <= '{default:'0};
      tag_mirror <= '{default:'0};
    end else begin
      for (int i = 0; i < ICACHE_SET_ASSOC; i++) begin
        if(vld_req[i] & vld_we) begin
          vld_mirror[vld_addr][i] <= vld_wdata[i];
          tag_mirror[vld_addr][i] <= cl_tag_q;
        end
      end
    end
  end

  for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : gen_tag_dupl
    assign tag_write_duplicate_test[i] = (tag_mirror[vld_addr][i] == cl_tag_q) & vld_mirror[vld_addr][i] & (|vld_wdata);
  end

  tag_write_duplicate: assert property (
    @(posedge clk_i) disable iff (!rst_ni) |vld_req |-> vld_we |-> !(|tag_write_duplicate_test))
      else $fatal(1,"[l1 icache] cannot allocate a CL that is already present in the cache");


 initial begin

  assert (ICACHE_INDEX_WIDTH<=12)
    else $fatal(1,"[l1 icache] cache index width can be maximum 12bit since VM uses 4kB pages");
 end
`endif


endmodule