data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v
1: b'%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:27: Cannot find include file: or1k_defines.v\n`include "or1k_defines.v" \n         ^~~~~~~~~~~~~~~~\n        ... Looked in:\n             data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog,data/full_repos/permissive/447681894/or1k_defines.v\n             data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog,data/full_repos/permissive/447681894/or1k_defines.v.v\n             data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog,data/full_repos/permissive/447681894/or1k_defines.v.sv\n             or1k_defines.v\n             or1k_defines.v.v\n             or1k_defines.v.sv\n             obj_dir/or1k_defines.v\n             obj_dir/or1k_defines.v.v\n             obj_dir/or1k_defines.v.sv\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:59: Define or directive not defined: \'`OR1K_INSN_WIDTH\'\n  output reg     [`OR1K_INSN_WIDTH-1:0] ic_dat_o,\n                  ^~~~~~~~~~~~~~~~\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:69: Define or directive not defined: \'`OR1K_INSN_WIDTH\'\n  input          [`OR1K_INSN_WIDTH-1:0] ibus_dat_i,\n                  ^~~~~~~~~~~~~~~~\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:257: Define or directive not defined: \'`OR1K_SPR_IC_BASE\'\n  wire spr_ic_cs  = spr_bus_stb_r & (spr_bus_addr_r[14:11] == `OR1K_SPR_IC_BASE);\n                                                              ^~~~~~~~~~~~~~~~~\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:257: syntax error, unexpected \')\', expecting TYPE-IDENTIFIER\n  wire spr_ic_cs  = spr_bus_stb_r & (spr_bus_addr_r[14:11] == `OR1K_SPR_IC_BASE);\n                                                                               ^\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:258: Define or directive not defined: \'`SPR_OFFSET\'\n  wire spr_ic_inv = spr_bus_we_r & (`SPR_OFFSET(({1\'b0, spr_bus_addr_r})) == `SPR_OFFSET(`OR1K_SPR_ICBIR_ADDR));\n                                    ^~~~~~~~~~~\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:258: Define or directive not defined: \'`SPR_OFFSET\'\n  wire spr_ic_inv = spr_bus_we_r & (`SPR_OFFSET(({1\'b0, spr_bus_addr_r})) == `SPR_OFFSET(`OR1K_SPR_ICBIR_ADDR));\n                                                                             ^~~~~~~~~~~\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:258: Define or directive not defined: \'`OR1K_SPR_ICBIR_ADDR\'\n  wire spr_ic_inv = spr_bus_we_r & (`SPR_OFFSET(({1\'b0, spr_bus_addr_r})) == `SPR_OFFSET(`OR1K_SPR_ICBIR_ADDR));\n                                                                                         ^~~~~~~~~~~~~~~~~~~~\n%Error: data/full_repos/permissive/447681894/pythondata_cpu_marocchino/verilog/rtl/verilog/or1k_marocchino_icache.v:258: syntax error, unexpected \')\', expecting TYPE-IDENTIFIER\n  wire spr_ic_inv = spr_bus_we_r & (`SPR_OFFSET(({1\'b0, spr_bus_addr_r})) == `SPR_OFFSET(`OR1K_SPR_ICBIR_ADDR));\n                                                                                                             ^\n%Error: Exiting due to 9 error(s)\n'
module or1k_marocchino_icache
#(
  parameter OPTION_OPERAND_WIDTH        = 32,
  parameter OPTION_ICACHE_BLOCK_WIDTH   =  5,
  parameter OPTION_ICACHE_SET_WIDTH     =  8,
  parameter OPTION_ICACHE_WAYS          =  2,
  parameter OPTION_ICACHE_LIMIT_WIDTH   = 32,
  parameter OPTION_ICACHE_CLEAR_ON_INIT =  0
)
(

  input                                 cpu_clk,
  input                                 cpu_rst,


  input                                 padv_s1_i,
  input                                 padv_s2_i,

  input                                 ibus_err_i,


  input                                 ic_enable_i,


  input      [OPTION_OPERAND_WIDTH-1:0] virt_addr_mux_i,
  input      [OPTION_OPERAND_WIDTH-1:0] virt_addr_s1o_i,
  input      [OPTION_OPERAND_WIDTH-1:0] virt_addr_s2o_i,
  input      [OPTION_OPERAND_WIDTH-1:0] phys_addr_s2t_i,
  input                                 immu_cache_inhibit_i,
  output                                ic_ack_o,
  output reg     [`OR1K_INSN_WIDTH-1:0] ic_dat_o,


  output                                ibus_read_req_o,


  output                                refill_req_o,
  input                                 to_refill_i,
  output reg                            ic_refill_first_o,
  input      [OPTION_OPERAND_WIDTH-1:0] phys_addr_s2o_i,
  input          [`OR1K_INSN_WIDTH-1:0] ibus_dat_i,
  input                                 ibus_burst_last_i,
  input                                 ibus_ack_i,


  input                          [15:0] spr_bus_addr_i,
  input                                 spr_bus_we_i,
  input                                 spr_bus_stb_i,
  input      [OPTION_OPERAND_WIDTH-1:0] spr_bus_dat_i,
  output     [OPTION_OPERAND_WIDTH-1:0] spr_bus_dat_o,
  output                                spr_bus_ack_o
);


  localparam WAY_WIDTH = OPTION_ICACHE_BLOCK_WIDTH + OPTION_ICACHE_SET_WIDTH;



  localparam TAG_WIDTH = (OPTION_ICACHE_LIMIT_WIDTH - WAY_WIDTH);



  localparam TAGMEM_WAY_WIDTH = TAG_WIDTH + 1;
  localparam TAGMEM_WAY_VALID = TAGMEM_WAY_WIDTH - 1;




  localparam TAG_LRU_WIDTH = (OPTION_ICACHE_WAYS > 1) ? (OPTION_ICACHE_WAYS*(OPTION_ICACHE_WAYS-1) >> 1) : 1;


  localparam TAGMEM_WIDTH = TAGMEM_WAY_WIDTH * OPTION_ICACHE_WAYS + TAG_LRU_WIDTH;



  localparam TAG_LRU_MSB = TAGMEM_WIDTH - 1;
  localparam TAG_LRU_LSB = TAG_LRU_MSB - TAG_LRU_WIDTH + 1;



  localparam [3:0] IC_READ       = 4'b0001,
                   IC_REFILL     = 4'b0010,
                   IC_INVALIDATE = 4'b0100,
                   IC_SPR_ACK    = 4'b1000;

  reg [3:0] ic_state;


  wire   ic_refill     = ic_state[1];
  wire   ic_invalidate = ic_state[2];
  assign spr_bus_ack_o = ic_state[3];



  wire [OPTION_ICACHE_SET_WIDTH-1:0] tag_rindex;
  wire [OPTION_ICACHE_SET_WIDTH-1:0] tag_windex;


  wire       [TAGMEM_WIDTH-1:0] tag_dout;
  wire   [TAGMEM_WAY_WIDTH-1:0] tag_dout_way [OPTION_ICACHE_WAYS-1:0];
  reg    [TAGMEM_WAY_WIDTH-1:0] tag_dout_way_s2o [OPTION_ICACHE_WAYS-1:0];


  wire       [TAGMEM_WIDTH-1:0] tag_din;
  reg    [TAGMEM_WAY_WIDTH-1:0] tag_din_way [OPTION_ICACHE_WAYS-1:0];
  reg       [TAG_LRU_WIDTH-1:0] tag_din_lru;



  reg                           tag_we;


  wire [OPTION_OPERAND_WIDTH-1:0] way_dout [OPTION_ICACHE_WAYS-1:0];


  wire                          hit;
  wire [OPTION_ICACHE_WAYS-1:0] hit_way;



  wire [OPTION_ICACHE_WAYS-1:0] lru_way;
  reg  [OPTION_ICACHE_WAYS-1:0] lru_way_refill_r;



  reg  [OPTION_ICACHE_WAYS-1:0] access_way_for_lru;
  reg  [OPTION_ICACHE_WAYS-1:0] access_way_for_lru_s2o;

  reg       [TAG_LRU_WIDTH-1:0] current_lru_history_s2o;

  wire      [TAG_LRU_WIDTH-1:0] next_lru_history;


  genvar i;






  generate
  if (OPTION_ICACHE_LIMIT_WIDTH > OPTION_OPERAND_WIDTH) begin
    initial begin
      $display("ICACHE: OPTION_ICACHE_LIMIT_WIDTH > OPTION_OPERAND_WIDTH");
      $finish();
    end
  end
  endgenerate



  generate
  for (i = 0; i < OPTION_ICACHE_WAYS; i=i+1) begin : ways_out

    assign tag_dout_way[i] = tag_dout[(i+1)*TAGMEM_WAY_WIDTH-1:i*TAGMEM_WAY_WIDTH];

    assign hit_way[i] = tag_dout_way[i][TAGMEM_WAY_VALID] &
                        (tag_dout_way[i][TAG_WIDTH-1:0] ==
                         phys_addr_s2t_i[OPTION_ICACHE_LIMIT_WIDTH-1:WAY_WIDTH]);
  end
  endgenerate



  assign hit = |hit_way;



  wire   is_cacheble  = ic_enable_i & (~immu_cache_inhibit_i);

  assign ic_ack_o     = is_cacheble &   hit;

  assign refill_req_o = is_cacheble & (~hit);


  assign ibus_read_req_o = (~is_cacheble);



  integer w0;
  always @ (*) begin
    ic_dat_o = {OPTION_OPERAND_WIDTH{1'b0}};

    for (w0 = 0; w0 < OPTION_ICACHE_WAYS; w0=w0+1) begin : mux_dat_o
      if (hit_way[w0])
        ic_dat_o = way_dout[w0];
    end
  end













  reg                              spr_bus_stb_r;
  reg                              spr_bus_we_r;
  reg                       [14:0] spr_bus_addr_r;
  reg [(OPTION_OPERAND_WIDTH-1):0] spr_bus_dat_r;

  always @(posedge cpu_clk) begin
    if (cpu_rst)
      spr_bus_stb_r <= 1'b0;
    else if (spr_bus_ack_o)
      spr_bus_stb_r <= 1'b0;
    else
      spr_bus_stb_r <= spr_bus_stb_i;
  end

  always @(posedge cpu_clk) begin
    spr_bus_we_r   <= spr_bus_we_i;
    spr_bus_addr_r <= spr_bus_addr_i[14:0];
    spr_bus_dat_r  <= spr_bus_dat_i;
  end



  wire spr_ic_cs  = spr_bus_stb_r & (spr_bus_addr_r[14:11] == `OR1K_SPR_IC_BASE);
  wire spr_ic_inv = spr_bus_we_r & (`SPR_OFFSET(({1'b0, spr_bus_addr_r})) == `SPR_OFFSET(`OR1K_SPR_ICBIR_ADDR));
  assign spr_bus_dat_o = {OPTION_OPERAND_WIDTH{1'b0}};





  always @(posedge cpu_clk) begin
    if (cpu_rst) begin
      ic_refill_first_o <= 1'b0;
      ic_state          <= IC_READ;
    end
    else begin

      (* parallel_case *)
      case (ic_state)
        IC_READ: begin


          if (to_refill_i) begin
            ic_refill_first_o <= 1'b1;
            ic_state          <= IC_REFILL;
          end
          else if (spr_ic_cs) begin
            ic_state <= spr_ic_inv ? IC_INVALIDATE : IC_SPR_ACK;
          end
        end

        IC_REFILL: begin


          if (ibus_err_i) begin
            ic_refill_first_o <= 1'b0;
            ic_state          <= IC_READ;
          end
          else if (ibus_ack_i) begin
            ic_refill_first_o <= 1'b0;
            if (ibus_burst_last_i)
              ic_state <= IC_READ;
          end
        end

        IC_INVALIDATE: begin
          ic_state <= IC_SPR_ACK;
        end

        IC_SPR_ACK: begin
          ic_state <= IC_READ;
        end

        default:;
      endcase
    end
  end











  reg  [OPTION_OPERAND_WIDTH-1:0] virt_addr_rfl_r;
  wire [OPTION_OPERAND_WIDTH-1:0] virt_addr_rfl_next;


  assign virt_addr_rfl_next = (OPTION_ICACHE_BLOCK_WIDTH == 5) ?
    {virt_addr_rfl_r[31:5], virt_addr_rfl_r[4:0] + 5'd4} :
    {virt_addr_rfl_r[31:4], virt_addr_rfl_r[3:0] + 4'd4};

  always @(posedge cpu_clk) begin
    (* parallel_case *)
    case (ic_state)
      IC_READ: begin
        if (spr_ic_cs)
          virt_addr_rfl_r <= spr_bus_dat_r;
        else if (padv_s2_i)
          virt_addr_rfl_r <= virt_addr_s1o_i;
      end
      IC_REFILL: begin
        if (ibus_ack_i)
          virt_addr_rfl_r <= virt_addr_rfl_next;
      end
      default:;
    endcase
  end


  wire [WAY_WIDTH-3:0] way_windex = virt_addr_rfl_r[WAY_WIDTH-1:2];


  wire [WAY_WIDTH-3:0] way_rindex = ic_refill ? virt_addr_s1o_i[WAY_WIDTH-1:2]:
                                                virt_addr_mux_i[WAY_WIDTH-1:2];



  wire [OPTION_ICACHE_WAYS-1:0] way_rwp_en;
  wire [OPTION_ICACHE_WAYS-1:0] way_rwp_we;

  wire way_rwp_same_addr = (way_windex == virt_addr_s1o_i[WAY_WIDTH-1:2]);


  wire [OPTION_ICACHE_WAYS-1:0] way_wp_we;

  wire way_wp_diff_addr = (way_windex != virt_addr_s1o_i[WAY_WIDTH-1:2]);


  generate
  for (i = 0; i < OPTION_ICACHE_WAYS; i=i+1) begin : ways_ram


    assign way_rwp_we[i] = ibus_ack_i & lru_way_refill_r[i] & way_rwp_same_addr;
    assign way_rwp_en[i] = padv_s1_i | way_rwp_we[i];


    assign way_wp_we[i] = ibus_ack_i & lru_way_refill_r[i] & way_wp_diff_addr;


    or1k_dpram_en_w1st
    #(
      .ADDR_WIDTH     (WAY_WIDTH-2),
      .DATA_WIDTH     (OPTION_OPERAND_WIDTH),
      .CLEAR_ON_INIT  (OPTION_ICACHE_CLEAR_ON_INIT)
    )
    ic_way_ram
    (

      .clk_a          (cpu_clk),
      .en_a           (way_rwp_en[i]),
      .we_a           (way_rwp_we[i]),
      .addr_a         (way_rindex),
      .din_a          (ibus_dat_i),
      .dout_a         (way_dout[i]),

      .clk_b          (cpu_clk),
      .en_b           (way_wp_we[i]),
      .we_b           (1'b1),
      .addr_b         (way_windex),
      .din_b          (ibus_dat_i),
      .dout_b ()
    );
  end
  endgenerate







  reg s2o_ic_ack;

  always @(posedge cpu_clk) begin
    if (cpu_rst)
      s2o_ic_ack <= 1'b0;
    else if (padv_s2_i)
      s2o_ic_ack <= ic_ack_o;
    else
      s2o_ic_ack <= 1'b0;
  end


  generate
  if (OPTION_ICACHE_WAYS >= 2) begin : gen_u_lru
    or1k_marocchino_cache_lru
    #(
      .NUMWAYS(OPTION_ICACHE_WAYS)
    )
    ic_lru
    (

      .current     (current_lru_history_s2o),
      .access      (access_way_for_lru),

      .update      (next_lru_history),
      .lru_post    (lru_way)
    );
  end
  else begin
    assign next_lru_history = 1'b1;
    assign lru_way          = 1'b1;
  end
  endgenerate


  always @(posedge cpu_clk) begin
    if (padv_s2_i) begin
      access_way_for_lru_s2o  <= hit_way;
      current_lru_history_s2o <= tag_dout[TAG_LRU_MSB:TAG_LRU_LSB];
    end
  end


  always @(posedge cpu_clk) begin
    if (to_refill_i) begin
      lru_way_refill_r <= lru_way;
    end
    else if (ic_refill) begin
      if ((ibus_ack_i & ibus_burst_last_i) | ibus_err_i) begin
        lru_way_refill_r <= {OPTION_ICACHE_WAYS{1'b0}};
      end
    end
    else
      lru_way_refill_r <= {OPTION_ICACHE_WAYS{1'b0}};
  end


  integer w1;
  always @(posedge cpu_clk) begin
    if (padv_s2_i) begin
      for (w1 = 0; w1 < OPTION_ICACHE_WAYS; w1 = w1 + 1) begin
        tag_dout_way_s2o[w1] <= tag_dout_way[w1];
      end
    end
  end


  integer w2;
  always @(*) begin

    tag_we = 1'b0;



    access_way_for_lru  = access_way_for_lru_s2o;

    tag_din_lru = next_lru_history;

    for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
      tag_din_way[w2] = tag_dout_way_s2o[w2];
    end

    (* parallel_case *)
    case (ic_state)
      IC_READ: begin

        if (s2o_ic_ack) begin
          tag_we = 1'b1;
        end
      end

      IC_REFILL: begin
        if (ibus_err_i) begin





          for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
            tag_din_way[w2] = {TAGMEM_WAY_WIDTH{1'b0}};
          end
          tag_din_lru = {TAG_LRU_WIDTH{1'b1}};
          tag_we      = 1'b1;
        end
        else if (ibus_ack_i & ibus_burst_last_i) begin



          for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
            if (lru_way_refill_r[w2]) begin
              tag_din_way[w2] = {1'b1,phys_addr_s2o_i[OPTION_ICACHE_LIMIT_WIDTH-1:WAY_WIDTH]};
            end
          end
          access_way_for_lru = lru_way_refill_r;
          tag_we             = 1'b1;
        end
      end

      IC_INVALIDATE: begin

        for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
          tag_din_way[w2] = {TAGMEM_WAY_WIDTH{1'b0}};
        end
        tag_din_lru = {TAG_LRU_WIDTH{1'b1}};
        tag_we      = 1'b1;
      end

      default:;
    endcase
  end




  assign tag_din[TAG_LRU_MSB:TAG_LRU_LSB] = tag_din_lru;

  generate
  for (i = 0; i < OPTION_ICACHE_WAYS; i=i+1) begin : tw_sections
    assign tag_din[(i+1)*TAGMEM_WAY_WIDTH-1:i*TAGMEM_WAY_WIDTH] = tag_din_way[i];
  end
  endgenerate


  assign tag_windex = virt_addr_rfl_r[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH];


  assign tag_rindex = padv_s1_i ? virt_addr_mux_i[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH] :
                                  virt_addr_s1o_i[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH];


  wire tag_rwp_we = tag_we & (tag_rindex == tag_windex);
  wire tag_rwp_en = padv_s1_i | tag_rwp_we;


  wire tag_wp_en = tag_we & (tag_rindex != tag_windex);





  or1k_dpram_en_w1st
  #(
    .ADDR_WIDTH     (OPTION_ICACHE_SET_WIDTH),
    .DATA_WIDTH     (TAGMEM_WIDTH),
    .CLEAR_ON_INIT  (OPTION_ICACHE_CLEAR_ON_INIT)
  )
  ic_tag_ram
  (

    .clk_a  (cpu_clk),
    .en_a   (tag_rwp_en),
    .we_a   (tag_rwp_we),
    .addr_a (tag_rindex),
    .din_a  (tag_din),
    .dout_a (tag_dout),

    .clk_b  (cpu_clk),
    .en_b   (tag_wp_en),
    .we_b   (1'b1),
    .addr_b (tag_windex),
    .din_b  (tag_din),
    .dout_b ()
  );

endmodule
module or1k_marocchino_icache
#(
  parameter OPTION_OPERAND_WIDTH        = 32,
  parameter OPTION_ICACHE_BLOCK_WIDTH   =  5,
  parameter OPTION_ICACHE_SET_WIDTH     =  8,
  parameter OPTION_ICACHE_WAYS          =  2,
  parameter OPTION_ICACHE_LIMIT_WIDTH   = 32,
  parameter OPTION_ICACHE_CLEAR_ON_INIT =  0
)
(

  input                                 cpu_clk,
  input                                 cpu_rst,


  input                                 padv_s1_i,
  input                                 padv_s2_i,

  input                                 ibus_err_i,


  input                                 ic_enable_i,


  input      [OPTION_OPERAND_WIDTH-1:0] virt_addr_mux_i,
  input      [OPTION_OPERAND_WIDTH-1:0] virt_addr_s1o_i,
  input      [OPTION_OPERAND_WIDTH-1:0] virt_addr_s2o_i,
  input      [OPTION_OPERAND_WIDTH-1:0] phys_addr_s2t_i,
  input                                 immu_cache_inhibit_i,
  output                                ic_ack_o,
  output reg     [`OR1K_INSN_WIDTH-1:0] ic_dat_o,


  output                                ibus_read_req_o,


  output                                refill_req_o,
  input                                 to_refill_i,
  output reg                            ic_refill_first_o,
  input      [OPTION_OPERAND_WIDTH-1:0] phys_addr_s2o_i,
  input          [`OR1K_INSN_WIDTH-1:0] ibus_dat_i,
  input                                 ibus_burst_last_i,
  input                                 ibus_ack_i,


  input                          [15:0] spr_bus_addr_i,
  input                                 spr_bus_we_i,
  input                                 spr_bus_stb_i,
  input      [OPTION_OPERAND_WIDTH-1:0] spr_bus_dat_i,
  output     [OPTION_OPERAND_WIDTH-1:0] spr_bus_dat_o,
  output                                spr_bus_ack_o
);



  localparam WAY_WIDTH = OPTION_ICACHE_BLOCK_WIDTH + OPTION_ICACHE_SET_WIDTH;



  localparam TAG_WIDTH = (OPTION_ICACHE_LIMIT_WIDTH - WAY_WIDTH);



  localparam TAGMEM_WAY_WIDTH = TAG_WIDTH + 1;
  localparam TAGMEM_WAY_VALID = TAGMEM_WAY_WIDTH - 1;




  localparam TAG_LRU_WIDTH = (OPTION_ICACHE_WAYS > 1) ? (OPTION_ICACHE_WAYS*(OPTION_ICACHE_WAYS-1) >> 1) : 1;


  localparam TAGMEM_WIDTH = TAGMEM_WAY_WIDTH * OPTION_ICACHE_WAYS + TAG_LRU_WIDTH;



  localparam TAG_LRU_MSB = TAGMEM_WIDTH - 1;
  localparam TAG_LRU_LSB = TAG_LRU_MSB - TAG_LRU_WIDTH + 1;



  localparam [3:0] IC_READ       = 4'b0001,
                   IC_REFILL     = 4'b0010,
                   IC_INVALIDATE = 4'b0100,
                   IC_SPR_ACK    = 4'b1000;

  reg [3:0] ic_state;


  wire   ic_refill     = ic_state[1];
  wire   ic_invalidate = ic_state[2];
  assign spr_bus_ack_o = ic_state[3];



  wire [OPTION_ICACHE_SET_WIDTH-1:0] tag_rindex;
  wire [OPTION_ICACHE_SET_WIDTH-1:0] tag_windex;


  wire       [TAGMEM_WIDTH-1:0] tag_dout;
  wire   [TAGMEM_WAY_WIDTH-1:0] tag_dout_way [OPTION_ICACHE_WAYS-1:0];
  reg    [TAGMEM_WAY_WIDTH-1:0] tag_dout_way_s2o [OPTION_ICACHE_WAYS-1:0];


  wire       [TAGMEM_WIDTH-1:0] tag_din;
  reg    [TAGMEM_WAY_WIDTH-1:0] tag_din_way [OPTION_ICACHE_WAYS-1:0];
  reg       [TAG_LRU_WIDTH-1:0] tag_din_lru;



  reg                           tag_we;


  wire [OPTION_OPERAND_WIDTH-1:0] way_dout [OPTION_ICACHE_WAYS-1:0];


  wire                          hit;
  wire [OPTION_ICACHE_WAYS-1:0] hit_way;



  wire [OPTION_ICACHE_WAYS-1:0] lru_way;
  reg  [OPTION_ICACHE_WAYS-1:0] lru_way_refill_r;



  reg  [OPTION_ICACHE_WAYS-1:0] access_way_for_lru;
  reg  [OPTION_ICACHE_WAYS-1:0] access_way_for_lru_s2o;

  reg       [TAG_LRU_WIDTH-1:0] current_lru_history_s2o;

  wire      [TAG_LRU_WIDTH-1:0] next_lru_history;


  genvar i;






  generate
  if (OPTION_ICACHE_LIMIT_WIDTH > OPTION_OPERAND_WIDTH) begin
    initial begin
      $display("ICACHE: OPTION_ICACHE_LIMIT_WIDTH > OPTION_OPERAND_WIDTH");
      $finish();
    end
  end
  endgenerate



  generate
  for (i = 0; i < OPTION_ICACHE_WAYS; i=i+1) begin : ways_out

    assign tag_dout_way[i] = tag_dout[(i+1)*TAGMEM_WAY_WIDTH-1:i*TAGMEM_WAY_WIDTH];

    assign hit_way[i] = tag_dout_way[i][TAGMEM_WAY_VALID] &
                        (tag_dout_way[i][TAG_WIDTH-1:0] ==
                         phys_addr_s2t_i[OPTION_ICACHE_LIMIT_WIDTH-1:WAY_WIDTH]);
  end
  endgenerate



  assign hit = |hit_way;



  wire   is_cacheble  = ic_enable_i & (~immu_cache_inhibit_i);

  assign ic_ack_o     = is_cacheble &   hit;

  assign refill_req_o = is_cacheble & (~hit);


  assign ibus_read_req_o = (~is_cacheble);



  integer w0;
  always @ (*) begin
    ic_dat_o = {OPTION_OPERAND_WIDTH{1'b0}};

    for (w0 = 0; w0 < OPTION_ICACHE_WAYS; w0=w0+1) begin : mux_dat_o
      if (hit_way[w0])
        ic_dat_o = way_dout[w0];
    end
  end













  reg                              spr_bus_stb_r;
  reg                              spr_bus_we_r;
  reg                       [14:0] spr_bus_addr_r;
  reg [(OPTION_OPERAND_WIDTH-1):0] spr_bus_dat_r;

  always @(posedge cpu_clk) begin
    if (cpu_rst)
      spr_bus_stb_r <= 1'b0;
    else if (spr_bus_ack_o)
      spr_bus_stb_r <= 1'b0;
    else
      spr_bus_stb_r <= spr_bus_stb_i;
  end

  always @(posedge cpu_clk) begin
    spr_bus_we_r   <= spr_bus_we_i;
    spr_bus_addr_r <= spr_bus_addr_i[14:0];
    spr_bus_dat_r  <= spr_bus_dat_i;
  end



  wire spr_ic_cs  = spr_bus_stb_r & (spr_bus_addr_r[14:11] == `OR1K_SPR_IC_BASE);
  wire spr_ic_inv = spr_bus_we_r & (`SPR_OFFSET(({1'b0, spr_bus_addr_r})) == `SPR_OFFSET(`OR1K_SPR_ICBIR_ADDR));
  assign spr_bus_dat_o = {OPTION_OPERAND_WIDTH{1'b0}};





  always @(posedge cpu_clk) begin
    if (cpu_rst) begin
      ic_refill_first_o <= 1'b0;
      ic_state          <= IC_READ;
    end
    else begin

      (* parallel_case *)
      case (ic_state)
        IC_READ: begin


          if (to_refill_i) begin
            ic_refill_first_o <= 1'b1;
            ic_state          <= IC_REFILL;
          end
          else if (spr_ic_cs) begin
            ic_state <= spr_ic_inv ? IC_INVALIDATE : IC_SPR_ACK;
          end
        end

        IC_REFILL: begin


          if (ibus_err_i) begin
            ic_refill_first_o <= 1'b0;
            ic_state          <= IC_READ;
          end
          else if (ibus_ack_i) begin
            ic_refill_first_o <= 1'b0;
            if (ibus_burst_last_i)
              ic_state <= IC_READ;
          end
        end

        IC_INVALIDATE: begin
          ic_state <= IC_SPR_ACK;
        end

        IC_SPR_ACK: begin
          ic_state <= IC_READ;
        end

        default:;
      endcase
    end
  end











  reg  [OPTION_OPERAND_WIDTH-1:0] virt_addr_rfl_r;
  wire [OPTION_OPERAND_WIDTH-1:0] virt_addr_rfl_next;


  assign virt_addr_rfl_next = (OPTION_ICACHE_BLOCK_WIDTH == 5) ?
    {virt_addr_rfl_r[31:5], virt_addr_rfl_r[4:0] + 5'd4} :
    {virt_addr_rfl_r[31:4], virt_addr_rfl_r[3:0] + 4'd4};

  always @(posedge cpu_clk) begin
    (* parallel_case *)
    case (ic_state)
      IC_READ: begin
        if (spr_ic_cs)
          virt_addr_rfl_r <= spr_bus_dat_r;
        else if (padv_s2_i)
          virt_addr_rfl_r <= virt_addr_s1o_i;
      end
      IC_REFILL: begin
        if (ibus_ack_i)
          virt_addr_rfl_r <= virt_addr_rfl_next;
      end
      default:;
    endcase
  end


  wire [WAY_WIDTH-3:0] way_windex = virt_addr_rfl_r[WAY_WIDTH-1:2];


  wire [WAY_WIDTH-3:0] way_rindex = ic_refill ? virt_addr_s1o_i[WAY_WIDTH-1:2]:
                                                virt_addr_mux_i[WAY_WIDTH-1:2];



  wire [OPTION_ICACHE_WAYS-1:0] way_rwp_en;
  wire [OPTION_ICACHE_WAYS-1:0] way_rwp_we;

  wire way_rwp_same_addr = (way_windex == virt_addr_s1o_i[WAY_WIDTH-1:2]);


  wire [OPTION_ICACHE_WAYS-1:0] way_wp_we;

  wire way_wp_diff_addr = (way_windex != virt_addr_s1o_i[WAY_WIDTH-1:2]);


  generate
  for (i = 0; i < OPTION_ICACHE_WAYS; i=i+1) begin : ways_ram


    assign way_rwp_we[i] = ibus_ack_i & lru_way_refill_r[i] & way_rwp_same_addr;
    assign way_rwp_en[i] = padv_s1_i | way_rwp_we[i];


    assign way_wp_we[i] = ibus_ack_i & lru_way_refill_r[i] & way_wp_diff_addr;


    or1k_dpram_en_w1st
    #(
      .ADDR_WIDTH     (WAY_WIDTH-2),
      .DATA_WIDTH     (OPTION_OPERAND_WIDTH),
      .CLEAR_ON_INIT  (OPTION_ICACHE_CLEAR_ON_INIT)
    )
    ic_way_ram
    (

      .clk_a          (cpu_clk),
      .en_a           (way_rwp_en[i]),
      .we_a           (way_rwp_we[i]),
      .addr_a         (way_rindex),
      .din_a          (ibus_dat_i),
      .dout_a         (way_dout[i]),

      .clk_b          (cpu_clk),
      .en_b           (way_wp_we[i]),
      .we_b           (1'b1),
      .addr_b         (way_windex),
      .din_b          (ibus_dat_i),
      .dout_b ()
    );
  end
  endgenerate







  reg s2o_ic_ack;

  always @(posedge cpu_clk) begin
    if (cpu_rst)
      s2o_ic_ack <= 1'b0;
    else if (padv_s2_i)
      s2o_ic_ack <= ic_ack_o;
    else
      s2o_ic_ack <= 1'b0;
  end


  generate
  if (OPTION_ICACHE_WAYS >= 2) begin : gen_u_lru
    or1k_marocchino_cache_lru
    #(
      .NUMWAYS(OPTION_ICACHE_WAYS)
    )
    ic_lru
    (

      .current     (current_lru_history_s2o),
      .access      (access_way_for_lru),

      .update      (next_lru_history),
      .lru_post    (lru_way)
    );
  end
  else begin
    assign next_lru_history = 1'b1;
    assign lru_way          = 1'b1;
  end
  endgenerate


  always @(posedge cpu_clk) begin
    if (padv_s2_i) begin
      access_way_for_lru_s2o  <= hit_way;
      current_lru_history_s2o <= tag_dout[TAG_LRU_MSB:TAG_LRU_LSB];
    end
  end


  always @(posedge cpu_clk) begin
    if (to_refill_i) begin
      lru_way_refill_r <= lru_way;
    end
    else if (ic_refill) begin
      if ((ibus_ack_i & ibus_burst_last_i) | ibus_err_i) begin
        lru_way_refill_r <= {OPTION_ICACHE_WAYS{1'b0}};
      end
    end
    else
      lru_way_refill_r <= {OPTION_ICACHE_WAYS{1'b0}};
  end


  integer w1;
  always @(posedge cpu_clk) begin
    if (padv_s2_i) begin
      for (w1 = 0; w1 < OPTION_ICACHE_WAYS; w1 = w1 + 1) begin
        tag_dout_way_s2o[w1] <= tag_dout_way[w1];
      end
    end
  end


  integer w2;
  always @(*) begin

    tag_we = 1'b0;



    access_way_for_lru  = access_way_for_lru_s2o;

    tag_din_lru = next_lru_history;

    for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
      tag_din_way[w2] = tag_dout_way_s2o[w2];
    end

    (* parallel_case *)
    case (ic_state)
      IC_READ: begin

        if (s2o_ic_ack) begin
          tag_we = 1'b1;
        end
      end

      IC_REFILL: begin
        if (ibus_err_i) begin





          for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
            tag_din_way[w2] = {TAGMEM_WAY_WIDTH{1'b0}};
          end
          tag_din_lru = {TAG_LRU_WIDTH{1'b1}};
          tag_we      = 1'b1;
        end
        else if (ibus_ack_i & ibus_burst_last_i) begin



          for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
            if (lru_way_refill_r[w2]) begin
              tag_din_way[w2] = {1'b1,phys_addr_s2o_i[OPTION_ICACHE_LIMIT_WIDTH-1:WAY_WIDTH]};
            end
          end
          access_way_for_lru = lru_way_refill_r;
          tag_we             = 1'b1;
        end
      end

      IC_INVALIDATE: begin

        for (w2 = 0; w2 < OPTION_ICACHE_WAYS; w2 = w2 + 1) begin
          tag_din_way[w2] = {TAGMEM_WAY_WIDTH{1'b0}};
        end
        tag_din_lru = {TAG_LRU_WIDTH{1'b1}};
        tag_we      = 1'b1;
      end

      default:;
    endcase
  end




  assign tag_din[TAG_LRU_MSB:TAG_LRU_LSB] = tag_din_lru;

  generate
  for (i = 0; i < OPTION_ICACHE_WAYS; i=i+1) begin : tw_sections
    assign tag_din[(i+1)*TAGMEM_WAY_WIDTH-1:i*TAGMEM_WAY_WIDTH] = tag_din_way[i];
  end
  endgenerate


  assign tag_windex = virt_addr_rfl_r[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH];


  assign tag_rindex = padv_s1_i ? virt_addr_mux_i[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH] :
                                  virt_addr_s1o_i[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH];


  wire tag_rwp_we = tag_we & (tag_rindex == tag_windex);
  wire tag_rwp_en = padv_s1_i | tag_rwp_we;


  wire tag_wp_en = tag_we & (tag_rindex != tag_windex);





  or1k_dpram_en_w1st
  #(
    .ADDR_WIDTH     (OPTION_ICACHE_SET_WIDTH),
    .DATA_WIDTH     (TAGMEM_WIDTH),
    .CLEAR_ON_INIT  (OPTION_ICACHE_CLEAR_ON_INIT)
  )
  ic_tag_ram
  (

    .clk_a  (cpu_clk),
    .en_a   (tag_rwp_en),
    .we_a   (tag_rwp_we),
    .addr_a (tag_rindex),
    .din_a  (tag_din),
    .dout_a (tag_dout),

    .clk_b  (cpu_clk),
    .en_b   (tag_wp_en),
    .we_b   (1'b1),
    .addr_b (tag_windex),
    .din_b  (tag_din),
    .dout_b ()
  );

endmodule