// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Authors:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>
// - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>

/// Filter atomic operations (ATOPs) in a protocol-compliant manner.
///
/// This module filters atomic operations (ATOPs), i.e., write transactions that have a non-zero
/// `aw_atop` value, from its `slv` to its `mst` port. This module guarantees that:
///
/// 1) `aw_atop` is always zero on the `mst` port;
///
/// 2) write transactions with non-zero `aw_atop` on the `slv` port are handled in conformance with
///    the AXI standard by replying to such write transactions with the proper B and R responses.
///    The response code on atomic operations that reach this module is always SLVERR
///    (implementation-specific, not defined in the AXI standard).
///
/// ## Intended usage
/// This module is intended to be placed between masters that may issue ATOPs and slaves that do not
/// support ATOPs. That way, this module ensures that the AXI protocol remains in a defined state on
/// systems with mixed ATOP capabilities.
///
/// ## Specification reminder
/// The AXI standard specifies that there may be no ordering requirements between different atomic
/// bursts (i.e., a burst started by an AW with ATOP other than 0) and none between atomic bursts
/// and non-atomic bursts [E2.1.4]. That is, **an atomic burst may never have the same ID as any
/// other write or read burst that is in-flight at the same time**.

/* verilator lint_off WIDTH */
module axi_atop_filter #(
  /// AXI ID width
  parameter int unsigned AxiIdWidth = 0,
  /// Maximum number of in-flight AXI write transactions
  parameter int unsigned AxiMaxWriteTxns = 0,
  /// AXI request type
  parameter type req_t  = logic,
  /// AXI response type
  parameter type resp_t = logic
) (
  /// Rising-edge clock of both ports
  input  logic  clk_i,
  /// Asynchronous reset, active low
  input  logic  rst_ni,
  /// Slave port request
  input  req_t  slv_req_i,
  /// Slave port response
  output resp_t slv_resp_o,
  /// Master port request
  output req_t  mst_req_o,
  /// Master port response
  input  resp_t mst_resp_i
);

  // Minimum counter width is 2 to detect underflows.
  localparam int unsigned COUNTER_WIDTH = (AxiMaxWriteTxns == 1) ? 2 : $clog2(AxiMaxWriteTxns+1);
  typedef struct packed {
    logic                     underflow;
    logic [COUNTER_WIDTH-1:0] cnt;
  } cnt_t;
  cnt_t   w_cnt_d, w_cnt_q;

  typedef enum logic [2:0] {
    W_FEEDTHROUGH, BLOCK_AW, ABSORB_W, HOLD_B, INJECT_B, WAIT_R
  } w_state_e;
  w_state_e   w_state_d, w_state_q;

  typedef enum logic [1:0] { R_FEEDTHROUGH, INJECT_R, R_HOLD } r_state_e;
  r_state_e   r_state_d, r_state_q;

  typedef logic [AxiIdWidth-1:0] id_t;
  id_t  id_d, id_q;

  typedef logic [7:0] len_t;
  len_t   r_beats_d,  r_beats_q;

  typedef struct packed {
    len_t len;
  } r_resp_cmd_t;
  r_resp_cmd_t  r_resp_cmd_push, r_resp_cmd_pop;

  logic aw_without_complete_w_downstream,
        complete_w_without_aw_downstream,
        r_resp_cmd_push_valid,  r_resp_cmd_push_ready,
        r_resp_cmd_pop_valid,   r_resp_cmd_pop_ready;

  // An AW without a complete W burst is in-flight downstream if the W counter is > 0 and not
  // underflowed.
  assign aw_without_complete_w_downstream = !w_cnt_q.underflow && (w_cnt_q.cnt > 0);
  // A complete W burst without AW is in-flight downstream if the W counter is -1.
  assign complete_w_without_aw_downstream = w_cnt_q.underflow && &(w_cnt_q.cnt);

  // Manage AW, W, and B channels.
  always_comb begin
    // Defaults:
    // Disable AW and W handshakes.
    mst_req_o.aw_valid  = 1'b0;
    slv_resp_o.aw_ready = 1'b0;
    mst_req_o.w_valid   = 1'b0;
    slv_resp_o.w_ready  = 1'b0;
    // Feed write responses through.
    mst_req_o.b_ready   = slv_req_i.b_ready;
    slv_resp_o.b_valid  = mst_resp_i.b_valid;
    slv_resp_o.b        = mst_resp_i.b;
    // Keep ID stored for B and R response.
    id_d = id_q;
    // Do not push R response commands.
    r_resp_cmd_push_valid = 1'b0;
    // Keep the current state.
    w_state_d = w_state_q;

    unique case (w_state_q)
      W_FEEDTHROUGH: begin
        // Feed AW channel through if the maximum number of outstanding bursts is not reached.
        if (complete_w_without_aw_downstream || (w_cnt_q.cnt < AxiMaxWriteTxns)) begin
          mst_req_o.aw_valid  = slv_req_i.aw_valid;
          slv_resp_o.aw_ready = mst_resp_i.aw_ready;
        end
        // Feed W channel through if ..
        if (aw_without_complete_w_downstream // .. downstream is missing W bursts ..
            // .. or a new non-ATOP AW is being applied and there is not already a complete W burst
            // downstream (to prevent underflows of w_cnt).
            || ((slv_req_i.aw_valid && slv_req_i.aw.atop[5:4] == axi_pkg::ATOP_NONE)
                && !complete_w_without_aw_downstream)
        ) begin
          mst_req_o.w_valid  = slv_req_i.w_valid;
          slv_resp_o.w_ready = mst_resp_i.w_ready;
        end
        // Filter out AWs that are atomic operations.
        if (slv_req_i.aw_valid && slv_req_i.aw.atop[5:4] != axi_pkg::ATOP_NONE) begin
          mst_req_o.aw_valid  = 1'b0; // Do not let AW pass to master port.
          slv_resp_o.aw_ready = 1'b1; // Absorb AW on slave port.
          id_d = slv_req_i.aw.id; // Store ID for B response.
          // All atomic operations except atomic stores require a response on the R channel.
          if (slv_req_i.aw.atop[5:4] != axi_pkg::ATOP_ATOMICSTORE) begin
            // Push R response command.  We do not have to wait for the ready of the register
            // because we know it is ready: we are its only master and will wait for the register to
            // be emptied before going back to the `W_FEEDTHROUGH` state.
            r_resp_cmd_push_valid = 1'b1;
          end
          // If downstream is missing W beats, block the AW channel and let the W bursts complete.
          if (aw_without_complete_w_downstream) begin
            w_state_d = BLOCK_AW;
          // If downstream is not missing W beats, absorb the W beats for this atomic AW.
          end else begin
            mst_req_o.w_valid  = 1'b0; // Do not let W beats pass to master port.
            slv_resp_o.w_ready = 1'b1; // Absorb W beats on slave port.
            if (slv_req_i.w_valid && slv_req_i.w.last) begin
              // If the W beat is valid and the last, proceed by injecting the B response.
              // However, if there is a non-handshaked B on our response port, we must let that
              // complete first.
              if (slv_resp_o.b_valid && !slv_req_i.b_ready) begin
                w_state_d = HOLD_B;
              end else begin
                w_state_d = INJECT_B;
              end
            end else begin
              // Otherwise continue with absorbing W beats.
              w_state_d = ABSORB_W;
            end
          end
        end
      end

      BLOCK_AW: begin
        // Feed W channel through to let outstanding bursts complete.
        if (aw_without_complete_w_downstream) begin
          mst_req_o.w_valid  = slv_req_i.w_valid;
          slv_resp_o.w_ready = mst_resp_i.w_ready;
        end else begin
          // If there are no more outstanding W bursts, start absorbing the next W burst.
          slv_resp_o.w_ready = 1'b1;
          if (slv_req_i.w_valid && slv_req_i.w.last) begin
            // If the W beat is valid and the last, proceed by injecting the B response.
            if (slv_resp_o.b_valid && !slv_req_i.b_ready) begin
              w_state_d = HOLD_B;
            end else begin
              w_state_d = INJECT_B;
            end
          end else begin
            // Otherwise continue with absorbing W beats.
            w_state_d = ABSORB_W;
          end
        end
      end

      ABSORB_W: begin
        // Absorb all W beats of the current burst.
        slv_resp_o.w_ready = 1'b1;
        if (slv_req_i.w_valid && slv_req_i.w.last) begin
          if (slv_resp_o.b_valid && !slv_req_i.b_ready) begin
            w_state_d = HOLD_B;
          end else begin
            w_state_d = INJECT_B;
          end
        end
      end

      HOLD_B: begin
        // Proceed with injection of B response upon handshake.
        if (slv_resp_o.b_valid && slv_req_i.b_ready) begin
          w_state_d = INJECT_B;
        end
      end

      INJECT_B: begin
        // Pause forwarding of B response.
        mst_req_o.b_ready = 1'b0;
        // Inject error response instead.  Since the B channel has an ID and the atomic burst we are
        // replying to is guaranteed to be the only burst with this ID in flight, we do not have to
        // observe any ordering and can immediately inject on the B channel.
        slv_resp_o.b = '0;
        slv_resp_o.b.id = id_q;
        slv_resp_o.b.resp = axi_pkg::RESP_SLVERR;
        slv_resp_o.b_valid = 1'b1;
        if (slv_req_i.b_ready) begin
          // If not all beats of the R response have been injected, wait for them. Otherwise, return
          // to `W_FEEDTHROUGH`.
          if (r_resp_cmd_pop_valid && !r_resp_cmd_pop_ready) begin
            w_state_d = WAIT_R;
          end else begin
            w_state_d = W_FEEDTHROUGH;
          end
        end
      end

      WAIT_R: begin
        // Wait with returning to `W_FEEDTHROUGH` until all beats of the R response have been
        // injected.
        if (!r_resp_cmd_pop_valid) begin
          w_state_d = W_FEEDTHROUGH;
        end
      end

      default: w_state_d = W_FEEDTHROUGH;
    endcase
  end
  // Connect signals on AW and W channel that are not managed by the control FSM from slave port to
  // master port.
  // Feed-through of the AW and W vectors, make sure that downstream aw.atop is always zero
  always_comb begin
    // overwrite the atop signal
    mst_req_o.aw      = slv_req_i.aw;
    mst_req_o.aw.atop = '0;
  end
  assign mst_req_o.w = slv_req_i.w;

  // Manage R channel.
  always_comb begin
    // Defaults:
    // Feed read responses through.
    slv_resp_o.r       = mst_resp_i.r;
    slv_resp_o.r_valid = mst_resp_i.r_valid;
    mst_req_o.r_ready  = slv_req_i.r_ready;
    // Do not pop R response command.
    r_resp_cmd_pop_ready = 1'b0;
    // Keep the current value of the beats counter.
    r_beats_d = r_beats_q;
    // Keep the current state.
    r_state_d = r_state_q;

    unique case (r_state_q)
      R_FEEDTHROUGH: begin
        if (mst_resp_i.r_valid && !slv_req_i.r_ready) begin
          r_state_d = R_HOLD;
        end else if (r_resp_cmd_pop_valid) begin
          // Upon a command to inject an R response, immediately proceed with doing so because there
          // are no ordering requirements with other bursts that may be ongoing on the R channel at
          // this moment.
          r_beats_d = r_resp_cmd_pop.len;
          r_state_d = INJECT_R;
        end
      end

      INJECT_R: begin
        mst_req_o.r_ready  = 1'b0;
        slv_resp_o.r       = '0;
        slv_resp_o.r.id    = id_q;
        slv_resp_o.r.resp  = axi_pkg::RESP_SLVERR;
        slv_resp_o.r.last  = (r_beats_q == '0);
        slv_resp_o.r_valid = 1'b1;
        if (slv_req_i.r_ready) begin
          if (slv_resp_o.r.last) begin
            r_resp_cmd_pop_ready = 1'b1;
            r_state_d = R_FEEDTHROUGH;
          end else begin
            r_beats_d -= 1;
          end
        end
      end

      R_HOLD: begin
        if (mst_resp_i.r_valid && slv_req_i.r_ready) begin
          r_state_d = R_FEEDTHROUGH;
        end
      end

      default: r_state_d = R_FEEDTHROUGH;
    endcase
  end
  // Feed all signals on AR through.
  assign mst_req_o.ar        = slv_req_i.ar;
  assign mst_req_o.ar_valid  = slv_req_i.ar_valid;
  assign slv_resp_o.ar_ready = mst_resp_i.ar_ready;

  // Keep track of outstanding downstream write bursts and responses.
  always_comb begin
    w_cnt_d = w_cnt_q;
    if (mst_req_o.aw_valid && mst_resp_i.aw_ready) begin
      w_cnt_d.cnt += 1;
    end
    if (mst_req_o.w_valid && mst_resp_i.w_ready && mst_req_o.w.last) begin
      w_cnt_d.cnt -= 1;
    end
    if (w_cnt_q.underflow && (w_cnt_d.cnt == '0)) begin
      w_cnt_d.underflow = 1'b0;
    end else if (w_cnt_q.cnt == '0 && &(w_cnt_d.cnt)) begin
      w_cnt_d.underflow = 1'b1;
    end
  end

  always_ff @(posedge clk_i, negedge rst_ni) begin
    if (!rst_ni) begin
      id_q <= '0;
      r_beats_q <= '0;
      r_state_q <= R_FEEDTHROUGH;
      w_cnt_q <= '{default: '0};
      w_state_q <= W_FEEDTHROUGH;
    end else begin
      id_q <= id_d;
      r_beats_q <= r_beats_d;
      r_state_q <= r_state_d;
      w_cnt_q <= w_cnt_d;
      w_state_q <= w_state_d;
    end
  end

  stream_register #(
    .T(r_resp_cmd_t)
  ) r_resp_cmd (
    .clk_i      (clk_i),
    .rst_ni     (rst_ni),
    .clr_i      (1'b0),
    .testmode_i (1'b0),
    .valid_i    (r_resp_cmd_push_valid),
    .ready_o    (r_resp_cmd_push_ready),
    .data_i     (r_resp_cmd_push),
    .valid_o    (r_resp_cmd_pop_valid),
    .ready_i    (r_resp_cmd_pop_ready),
    .data_o     (r_resp_cmd_pop)
  );
  assign r_resp_cmd_push.len = slv_req_i.aw.len;

// pragma translate_off
`ifndef VERILATOR
  initial begin: p_assertions
    assert (AxiIdWidth >= 1) else $fatal(1, "AXI ID width must be at least 1!");
    assert (AxiMaxWriteTxns >= 1)
      else $fatal(1, "Maximum number of outstanding write transactions must be at least 1!");
  end
`endif
// pragma translate_on
endmodule

`include "axi/assign.svh"
`include "axi/typedef.svh"

/// Interface variant of [`axi_atop_filter`](module.axi_atop_filter).
module axi_atop_filter_intf #(
  /// AXI ID width
  parameter int unsigned AXI_ID_WIDTH   = 0,
  /// AXI address width
  parameter int unsigned AXI_ADDR_WIDTH = 0,
  /// AXI data width
  parameter int unsigned AXI_DATA_WIDTH = 0,
  /// AXI user signal width
  parameter int unsigned AXI_USER_WIDTH = 0,
  /// Maximum number of in-flight AXI write transactions
  parameter int unsigned AXI_MAX_WRITE_TXNS = 0
) (
  /// Rising-edge clock of both ports
  input  logic    clk_i,
  /// Asynchronous reset, active low
  input  logic    rst_ni,
  /// Slave interface port
  AXI_BUS.Slave   slv,
  /// Master interface port
  AXI_BUS.Master  mst
);

  `AXI_TYPEDEF_W_CHAN_T(w_chan_t, data_t, strb_t, user_t)
  `AXI_TYPEDEF_B_CHAN_T(b_chan_t, id_t, user_t)
  `AXI_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t)
  `AXI_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t)
  `AXI_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t)
  `AXI_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t)

  req_t  slv_req,  mst_req;
  resp_t slv_resp, mst_resp;

  `AXI_ASSIGN_TO_REQ(slv_req, slv)
  `AXI_ASSIGN_FROM_RESP(slv, slv_resp)

  `AXI_ASSIGN_FROM_REQ(mst, mst_req)
  `AXI_ASSIGN_TO_RESP(mst_resp, mst)

  axi_atop_filter #(
    .AxiIdWidth      ( AXI_ID_WIDTH       ),
  // Maximum number of AXI write bursts outstanding at the same time
    .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ),
  // AXI request & response type
    .req_t           ( req_t              ),
    .resp_t          ( resp_t             )
  ) i_axi_atop_filter (
    .clk_i,
    .rst_ni,
    .slv_req_i  ( slv_req  ),
    .slv_resp_o ( slv_resp ),
    .mst_req_o  ( mst_req  ),
    .mst_resp_i ( mst_resp )
  );
// pragma translate_off
`ifndef VERILATOR
  initial begin: p_assertions
    assert (AXI_ADDR_WIDTH >= 1) else $fatal(1, "AXI ADDR width must be at least 1!");
    assert (AXI_DATA_WIDTH >= 1) else $fatal(1, "AXI DATA width must be at least 1!");
    assert (AXI_USER_WIDTH >= 1) else $fatal(1, "AXI USER width must be at least 1!");
  end
`endif
// pragma translate_on
endmodule

/* verilator lint_on WIDTH */

  typedef logic [AXI_ID_WIDTH-1:0]     id_t;
  typedef logic [AXI_ADDR_WIDTH-1:0]   addr_t;
  typedef logic [AXI_DATA_WIDTH-1:0]   data_t;
  typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t;


module pi_map(

	input  PiBus pi,
	output PiMap map
);

	wire pi_on 				= pi.oe | pi.we;
	wire pi_act 			= pi.act;
	wire [31:0]pi_addr 	= pi.addr[31:0];
	wire [1:0]pi_dst 		= pi_addr[24:23];//8M blocks
	wire pi_we_sync		= pi.we_sync;

	assign map.dst_mem	= pi_dst != 3;
	assign map.dst_map	= pi_dst == 3 & pi_addr[22:16] == 3;//64K.

`ifdef HWC_ROM1_OFF
	assign map.ce_rom0 	= pi_on  & pi_dst <= 1;							 		//16M		0x0000000
`elsif HWC_ROM1_ON
	assign map.ce_rom0 	= pi_on  & pi_dst == 0;							 		//8M		0x0000000
	assign map.ce_rom1 	= pi_on  & pi_dst == 1;							 		//8M		0x0800000
`else
	"undefined hardware config"
`endif
	assign map.ce_sram 	= pi_on  & pi_dst == 2 & pi_addr[22:19] == 0;	//512K	0x1000000
	assign map.ce_bram 	= pi_on  & pi_dst == 2 & pi_addr[22:19] == 1;	//512K	0x1080000
	assign map.ce_sys  	= pi_act & pi_dst == 3 & pi_addr[22:16] == 0;	//64K		0x1800000
	assign map.ce_fifo 	= pi_act & pi_dst == 3 & pi_addr[22:16] == 1;	//64K 	0x1810000 do not use next 64k
	assign map.ce_map  	= pi_act & map.dst_map;									//64K 	0x1830000 mapper io
	assign map.ce_mcd		= pi_act & pi_dst == 3 & pi_addr[22:16] == 4;	//64K		0x1840000 MCD io
	assign map.ce_mdp		= pi_act & pi_dst == 3 & pi_addr[22:16] == 5;	//64K		0x1850000 MD+ io

	//map config
	assign map.ce_ggc	 	= map.ce_sys & {pi_addr[15:7], 7'd0} == 16'h0000;//128B 	cheats

	assign map.ce_cfg	 	= map.ce_sys & {pi_addr[15:3], 3'd0} == 16'h00f8;//8B 	sys config
	assign map.ce_sst	 	= map.ce_sys & {pi_addr[15:8], 8'd0} == 16'h0100;//256B 	save state data. 128B sniffer, 128B map regs
	assign map.ce_mst	 	= map.ce_sys & {pi_addr[15:1], 1'd0} == 16'h0200;//2B 	mapper status

	assign map.ce_mcfg	= map.ce_map & {pi_addr[15:8], 8'd0}  == 16'hff00;//256B	mapper config.


//
// limiter.sv
//
// BSD 3-Clause License
//
// Copyright (c) 2024, Shinobu Hashimoto
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

`default_nettype none

module LIMITER #(
    parameter   IN_WIDTH = 11,
    parameter   OUT_WIDTH = 10
)(
    input wire [IN_WIDTH-1:0] IN,
    output wire [OUT_WIDTH-1:0] OUT
);

    localparam [IN_WIDTH-OUT_WIDTH:0] sign_bits = -1;
    localparam [IN_WIDTH-OUT_WIDTH:0] zero_bits = 0;
    localparam [OUT_WIDTH - 1 : 0] max = ( (2**(OUT_WIDTH-1)) - 1);
    localparam [OUT_WIDTH - 1 : 0] min = (-(2**(OUT_WIDTH-1))    );

    assign OUT = (IN[IN_WIDTH-1:OUT_WIDTH-1] == sign_bits) ? IN[OUT_WIDTH-1:0] :
                 (IN[IN_WIDTH-1:OUT_WIDTH-1] == zero_bits) ? IN[OUT_WIDTH-1:0] :
                 (IN[IN_WIDTH-1])                          ? min : max;
endmodule

module LIMITER_FF #(
    parameter   IN_WIDTH = 11,
    parameter   OUT_WIDTH = 10
)(
    input wire                  CLK,
    input wire                  RESET_n,
    input wire [IN_WIDTH-1:0]   IN,
    output reg [OUT_WIDTH-1:0]  OUT
);

    localparam [IN_WIDTH-OUT_WIDTH:0] sign_bits = -1;
    localparam [IN_WIDTH-OUT_WIDTH:0] zero_bits = 0;
    localparam [OUT_WIDTH - 1 : 0] max = ( (2**(OUT_WIDTH-1)) - 1);
    localparam [OUT_WIDTH - 1 : 0] min = (-(2**(OUT_WIDTH-1))    );

    always_ff @(posedge CLK or negedge RESET_n) begin
        if(!RESET_n)    OUT <= 0;
        else            OUT <= (IN[IN_WIDTH-1:OUT_WIDTH-1] == sign_bits) ? IN[OUT_WIDTH-1:0] :
                               (IN[IN_WIDTH-1:OUT_WIDTH-1] == zero_bits) ? IN[OUT_WIDTH-1:0] :
                               (IN[IN_WIDTH-1])                          ? min : max;


// Copyright 2023 Silicon Labs, Inc.
//
// This file, and derivatives thereof are licensed under the
// Solderpad License, Version 2.0 (the "License");
// Use of this file means you agree to the terms and conditions
// of the license and are in full compliance with the License.
// You may obtain a copy of the License at
//
//     https://solderpad.org/licenses/SHL-2.0/
//
// Unless required by applicable law or agreed to in writing, software
// and hardware implementations thereof
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESSED OR IMPLIED.
// See the License for the specific language governing permissions and
// limitations under the License.

////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Authors:        Oystein Knauserud - oystein.knauserud@silabs.com           //
//                                                                            //
// Description:    debug_triggers assertions                                  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

module cv32e40x_debug_triggers_sva
  import uvm_pkg::*;
  import cv32e40x_pkg::*;
#(
    parameter int     DBG_NUM_TRIGGERS = 1,
    parameter a_ext_e A_EXT = A_NONE
  )

  (
   input logic        clk,
   input logic        rst_n,
   input csr_opcode_e csr_op,       // from cs_registers
   input logic [31:0] csr_wdata,    // from cs_registers
   input csr_num_e    csr_waddr,    // from cs_registers
   input ex_wb_pipe_t ex_wb_pipe_i,
   input ctrl_fsm_t   ctrl_fsm_i,
   input logic [31:0] tselect_q,
   input logic [31:0] tdata1_q[DBG_NUM_TRIGGERS],
   input logic [31:0] tdata2_q[DBG_NUM_TRIGGERS],
   input logic [DBG_NUM_TRIGGERS-1 : 0] lsu_addr_match_en,
   input privlvl_t    priv_lvl_ex_i,
   input lsu_atomic_e lsu_atomic_ex_i,
   input logic        lsu_valid_ex_i,

  /////////////////////////////////////////////////////////////////////////////////////////
  // Asserts to check that the CSR flops remain unchanged if a set/clear has all_zero rs1
  /////////////////////////////////////////////////////////////////////////////////////////
  a_set_clear_tselect_q:
  assert property (@(posedge clk) disable iff (!rst_n)
                  (csr_waddr == CSR_TSELECT) &&
                  ((csr_op == CSR_OP_SET) || (csr_op == CSR_OP_CLEAR)) &&
                  !(|csr_wdata) &&
                  ex_wb_pipe_i.csr_en &&
                  !ctrl_fsm_i.kill_wb
                  |=>
                  $stable(tselect_q))
    else `uvm_error("debug_triggers", "tselect_q changed after set/clear with rs1==0")

  a_set_clear_tdata1_q:
  assert property (@(posedge clk) disable iff (!rst_n)
                  (csr_waddr == CSR_TDATA1) &&
                  ((csr_op == CSR_OP_SET) || (csr_op == CSR_OP_CLEAR)) &&
                  !(|csr_wdata) &&
                  ex_wb_pipe_i.csr_en &&
                  !ctrl_fsm_i.kill_wb
                  |=>
                  $stable(tdata1_q)) // Checking stability of ALL tdata1, not just the one selected
    else `uvm_error("debug_triggers", "tdata1_q changed after set/clear with rs1==0")

  a_set_clear_tdata2_q:
  assert property (@(posedge clk) disable iff (!rst_n)
                  (csr_waddr == CSR_TDATA2) &&
                  ((csr_op == CSR_OP_SET) || (csr_op == CSR_OP_CLEAR)) &&
                  !(|csr_wdata) &&
                  ex_wb_pipe_i.csr_en &&
                  !ctrl_fsm_i.kill_wb
                  |=>
                  $stable(tdata2_q)) // Checking stability of ALL tdata2, not just the one selected
    else `uvm_error("debug_triggers", "tdata2_q changed after set/clear with rs1==0")

generate
  for (genvar i=0; i<DBG_NUM_TRIGGERS; i++) begin
    // Check that tdata1.hit0 is 1 and tdata1.hit1 is 0 when a mcontrol6 trigger matched when debug is entered
    a_mcontrol6_hit_set:
    assert property (@(posedge clk) disable iff (!rst_n)
                    (tdata1_q[i][TDATA1_TTYPE_HIGH:TDATA1_TTYPE_LOW] == TTYPE_MCONTROL6) &&
                    (trigger_match_if_wb[i] || trigger_match_ex_wb[i]) &&
                    wb_valid_i
                    |=>
                    (tdata1_q[i][MCONTROL_6_HIT0] == 1'b1) &&
                    (tdata1_q[i][MCONTROL_6_HIT1] == 1'b0))
      else `uvm_error("debug_triggers", "mcontrol6.hit0 not set on trigger match")

    // Check that trigger that did not match has stable {hit1, hit0}
    a_mcontrol6_not_hit_stable:
    assert property (@(posedge clk) disable iff (!rst_n)
                    (tdata1_q[i][TDATA1_TTYPE_HIGH:TDATA1_TTYPE_LOW] == TTYPE_MCONTROL6) &&
                    |(trigger_match_if_wb | trigger_match_ex_wb) &&         // Any trigger matched
                    !(trigger_match_if_wb[i] || trigger_match_ex_wb[i]) &&  // But not this one
                    wb_valid_i
                    |=>
                    ($stable(tdata1_q[i][MCONTROL_6_HIT0])) &&
                    ($stable(tdata1_q[i][MCONTROL_6_HIT1])))
      else `uvm_error("debug_triggers", "mcontrol6.hit0/1 changed without a trigger match")
  end
endgenerate

  // Check that SW writes to tdata1 cannot happen at the same time as the controller wants to upate hit bits in mcontrol6
  a_tdata1_writes_unique:
  assert property (@(posedge clk) disable iff (!rst_n)
                  (tdata1_we_i || ctrl_fsm_i.debug_trigger_hit_update)
                  |->
                  (tdata1_we_i != ctrl_fsm_i.debug_trigger_hit_update))
    else `uvm_error("debug_triggers", "SW write to tdata1 at the same time as controller write.")

  // Check that we cannot have trigger match from both IF and EX for the same trigger
  a_if_ex_trig_exclusive:
  assert property (@(posedge clk) disable iff (!rst_n)
                  |(trigger_match_if_wb & trigger_match_ex_wb) == 1'b0)
    else `uvm_error("debug_triggers", "Trigger match from both IF and EX for the same trigger should not happen")

  // Check that DBG_NUM_TRIGGERS is <= 32
  a_max_num_triggers:
  assert property (@(posedge clk) disable iff (!rst_n)
                  (DBG_NUM_TRIGGERS <= 32))
    else `uvm_error("debug_triggers", "DBG_NUM_TRIGGERS can max have the value of 32")

generate
  if (A_EXT == A) begin : gen_amo_trigger_assert
    for (genvar idx=0; idx<DBG_NUM_TRIGGERS; idx++) begin
      // Since AMOs perform both a read and a write, it must be possible to get a trigger match on its address
      // when any of the tdata1.LOAD or tdata1.STORE bits are set.
      a_amo_enable_trig_on_load:
        assert property (@(posedge clk) disable iff (!rst_n)
                        (tdata1_q[idx][MCONTROL2_6_LOAD] || tdata1_q[idx][MCONTROL2_6_STORE]) &&  // Trig on loads or stores enabled
                        (tdata1_q[idx][MCONTROL2_6_M] && (priv_lvl_ex_i == PRIV_LVL_M)) && // Matches privilege level
                        (lsu_atomic_ex_i == AT_AMO)  && // An Atomic AMO instruction is in EX
                        lsu_valid_ex_i
                        |->
                        lsu_addr_match_en[idx])    // Address matching must be enabled on the trigger
          else `uvm_error("debug_triggers", "Address matching not enabled on an AMO instruction when trig on loads are configured")
    end
  end
endgenerate

endmodule

   input logic [31:0] trigger_match_if_wb,
   input logic [31:0] trigger_match_ex_wb,
   input logic        wb_valid_i,


module map_097(

	input  MapIn  mai,
	output MapOut mao
);
//************************************************************* base header
	CpuBus cpu;
	PpuBus ppu;
	SysCfg cfg;
	SSTBus sst;
	assign cpu = mai.cpu;
	assign ppu = mai.ppu;
	assign cfg = mai.cfg;
	assign sst = mai.sst;

	MemCtrl chr;
	MemCtrl srm;
	assign mao.prg = prg;
	assign mao.chr = chr;
	assign mao.srm = srm;

	assign prg.dati			= cpu.data;
	assign chr.dati			= ppu.data;
	assign srm.dati			= cpu.data;

	wire int_cpu_oe;
	wire int_ppu_oe;
	wire [7:0]int_cpu_data;
	wire [7:0]int_ppu_data;

	assign mao.map_cpu_oe	= int_cpu_oe | (srm.ce & srm.oe) | (prg.ce & prg.oe);
	assign mao.map_cpu_do	= int_cpu_oe ? int_cpu_data : srm.ce ? mai.srm_do : mai.prg_do;

	assign mao.map_ppu_oe	= int_ppu_oe | (chr.ce & chr.oe);
	assign mao.map_ppu_do	= int_ppu_oe ? int_ppu_data : mai.chr_do;
//************************************************************* configuration
	assign mao.prg_mask_off = 0;
	assign mao.chr_mask_off = 0;
	assign mao.srm_mask_off = 0;
	assign mao.mir_4sc		= 0;//enable support for 4-screen mirroring. for activation should be enabled in cfg.mir_4 also
	assign mao.bus_cf 		= 0;//bus conflicts
//************************************************************* save state regs read
	assign mao.sst_di[7:0] =
	sst.addr[7:0] == 0 	? {mirror_mode[1:0], 2'b00, prg_reg[3:0]} :
	sst.addr[7:0] == 127 ? cfg.map_idx : 8'hff;
//************************************************************* mapper-controlled pins
	assign srm.ce				= 0;
	assign srm.oe				= 0;
	assign srm.we				= 0;
	assign srm.addr[12:0]	= cpu.addr[12:0];

	assign prg.ce				= cpu.addr[15];
	assign prg.oe 				= cpu.rw;
	assign prg.we				= 0;
	assign prg.addr[13:0]	= cpu.addr[13:0];
	assign prg.addr[17:14] 	= cpu.addr[14] ? prg_reg[3:0] : 4'b1111;

	assign chr.ce 				= mao.ciram_ce;
	assign chr.oe 				= !ppu.oe;
	assign chr.we 				= cfg.chr_ram ? !ppu.we & mao.ciram_ce : 0;
	assign chr.addr[12:0]	= ppu.addr[12:0];

	//A10-Vmir, A11-Hmir
	assign mao.ciram_a10 	= mirror_mode == 0 ? 0 : mirror_mode == 3 ? 1 : !mirror_mode[0] ? ppu.addr[10] : ppu.addr[11];
	assign mao.ciram_ce 		= !ppu.addr[13];

	assign mao.irq				= 0;
//************************************************************* mapper implementation

	reg [3:0]prg_reg;
	reg [1:0]mirror_mode;

	always @(negedge cpu.m2)
	if(sst.act)
	begin
		if(sst.we_reg & sst.addr[7:0] == 0){mirror_mode[1:0], prg_reg[3:0]} <= {sst.dato[7:6], sst.dato[3:0]};
	end
		else
	if(cpu.addr[15] & !cpu.rw)
	begin
		{mirror_mode[1:0], prg_reg[3:0]} <= {cpu.data[7:6], cpu.data[3:0]};
	end

endmodule


module shadowmask
(
	input             clk,
	input             clk_sys,

	input             cmd_wr,
	input      [15:0] cmd_in,

	input      [23:0] din,
	input             hs_in,vs_in,
	input             de_in,
	input             brd_in,
	input             enable,

	output reg [23:0] dout,
	output reg        hs_out,vs_out,
	output reg        de_out
);

reg  [4:0] hmax;
reg  [4:0] vmax;
reg  [7:0] mask_idx;
reg        mask_2x;
reg        mask_rotate;
reg        mask_enable;
reg [10:0] mask_lut[256];

always @(posedge clk) begin
	reg [4:0] hcount;
	reg [4:0] vcount;
	reg [3:0] hindex;
	reg [3:0] vindex;
	reg [4:0] hmax2;
	reg [4:0] vmax2;
	reg [11:0] pcnt,pde;
	reg old_hs, old_vs, old_brd;
	reg next_v;

	old_hs <= hs_in;
	old_vs <= vs_in;
	old_brd<= brd_in;

	// hcount and vcount counts pixel rows and columns
	// hindex and vindex half the value of the counters for double size patterns
	// hindex2, vindex2 swap the h and v counters for drawing rotated masks
	hindex <= mask_2x ? hcount[4:1] : hcount[3:0];
	vindex <= mask_2x ? vcount[4:1] : vcount[3:0];
	mask_idx <= mask_rotate ? {hindex,vindex} : {vindex,hindex};

	// hmax and vmax store these sizes
	// hmax2 and vmax2 swap the values to handle rotation
	hmax2 <= ((mask_rotate ? vmax : hmax) << mask_2x) | mask_2x;
	vmax2 <= ((mask_rotate ? hmax : vmax) << mask_2x) | mask_2x;

	pcnt <= pcnt+1'd1;
	if(old_brd && ~brd_in) pde <= pcnt-4'd3;

	hcount <= hcount+1'b1;
	if(hcount == hmax2 || pde == pcnt) hcount <= 0;

	if(~old_brd && brd_in) next_v <= 1;
	if(old_vs && ~vs_in) vcount <= 0;
	if(old_hs && ~hs_in) begin
		vcount <= vcount + next_v;
		next_v <= 0;
		pcnt   <= 0;
		if (vcount == vmax2) vcount <= 0;
	end
end

reg [4:0] r_mul, g_mul, b_mul; // 1.4 fixed point multipliers
always @(posedge clk) begin
	reg [10:0] lut;


//------------------------------------------------------------------------------
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileType: SOURCE
// SPDX-FileCopyrightText: (c) 2023, OpenGateware authors and contributors
//------------------------------------------------------------------------------
//
// Copyright (c) 2020 Alexey Melnikov <pour.garbage@gmail.com>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------
// result = (mul1*mul2)/div
//------------------------------------------------------------------------------

`default_nettype none

module sys_umuldiv
    #(
         parameter NB_MUL1 = 0,
         parameter NB_MUL2 = 0,
         parameter NB_DIV  = 0
     ) (
         input                        clk,
         input                        start,

    wire mul_run;
    wire [NB_MUL1+NB_MUL2-1:0] mul_res;
    sys_umul #(NB_MUL1,NB_MUL2) umul(clk,start,mul_run,mul1,mul2,mul_res);

    sys_udiv #(NB_MUL1+NB_MUL2,NB_DIV) udiv(clk,start|mul_run,busy,mul_res,div,result,remainder);

endmodule

         output                       busy,

         input          [NB_MUL1-1:0] mul1,
         input          [NB_MUL2-1:0] mul2,
         input           [NB_DIV-1:0] div,


      jb_if.taste <= #2ns YUCKY;
    else
      jb_if.taste <= #2ns YUMMY;


module StoreQueue
#(
    parameter NUM_ENTRIES=`SQ_SIZE,
    parameter WIDTH_RN = `DEC_WIDTH,
    parameter NUM_OUT=2
)
(
    input wire clk,
    input wire rst,

    output reg OUT_empty,
    output wire OUT_done,

    input LD_UOp IN_uopLd[NUM_AGUS-1:0],
    output StFwdResult OUT_fwd[NUM_AGUS-1:0],

    input AGU_UOp IN_uopSt[NUM_AGUS-1:0],

    input R_UOp IN_rnUOp[WIDTH_RN-1:0],
    input StDataUOp IN_stDataUOp[NUM_AGUS-1:0],

    input SqN IN_curSqN,
    input SqN IN_comStSqN,

    input BranchProv IN_branch,

    output SQ_UOp OUT_uop[NUM_OUT-1:0],
    input wire IN_stall[NUM_OUT-1:0],

    output wire OUT_flush,
    output SqN OUT_maxStoreSqN
);

localparam AXI_BWIDTH_E = $clog2(`AXI_WIDTH/8);
localparam IDX_LEN = $clog2(NUM_ENTRIES);

typedef struct packed
{
    RegT data;
    logic[29:0] addr;

    // wmask == 0 is escape sequence for special operations
    logic[3:0] wmask;
    logic loaded;
    logic addrAvail;
} SQEntry;

reg[NUM_ENTRIES-1:0] entryReady_r /* verilator public */;
always_ff@(posedge clk /*or posedge rst*/)
    if (rst) entryReady_r <= 0;
    else entryReady_r <= entryReady_c;

wire[NUM_ENTRIES-1:0] entryReady_c;
RangeMaskGen#(NUM_ENTRIES, 0) readyRangeGen
(
    .IN_allOnes(SqN'(baseIndex + SqN'(NUM_ENTRIES)) == IN_comStSqN),
    .IN_enable(1'b1),
    .IN_startIdx(baseIndex[IDX_LEN-1:0]),
    .IN_endIdx(IN_comStSqN[IDX_LEN-1:0]),
    .OUT_range(entryReady_c)
);

wire[NUM_ENTRIES-1:0] invalRange_c;
RangeMaskGen#(NUM_ENTRIES, 1, 1, 0) invalRangeGen
(
    .IN_allOnes(1'b0),
    .IN_enable(($signed(IN_branch.storeSqN - baseIndex) < NUM_ENTRIES-1)),
    .IN_startIdx(IN_branch.storeSqN[IDX_LEN-1:0]),
    .IN_endIdx(baseIndex[IDX_LEN-1:0]),
    .OUT_range(invalRange_c)
);
`ifndef SQ_LINEAR
wire[NUM_ENTRIES-1:0] forwardRange_c[NUM_AGUS-1:0];
`else
wire[2*NUM_ENTRIES-1:0] forwardRange_c[NUM_AGUS-1:0];
`endif

generate
for (genvar i = 0; i < NUM_AGUS; i=i+1) begin
    wire SqN endSqN = IN_uopLd[i].storeSqN + (IN_uopLd[i].atomic ? 0 : 1);
    `ifndef SQ_LINEAR
    RangeMaskGen#(NUM_ENTRIES, 0) forwardRangeGen
    (
        .IN_allOnes($signed(endSqN - baseIndex) >= NUM_ENTRIES),
        .IN_enable(1'b1),
        .IN_startIdx(baseIndex[IDX_LEN-1:0]),
        .IN_endIdx(endSqN[IDX_LEN-1:0]),
        .OUT_range(forwardRange_c[i])
    );
    `else
    wire[NUM_ENTRIES-1:0] mask = ($signed(endSqN - baseIndex) >= NUM_ENTRIES) ? {NUM_ENTRIES{1'b1}} :
        (1 << IDX_LEN'(endSqN - baseIndex)) - 1;
    assign forwardRange_c[i] = {NUM_ENTRIES'(0), mask} << baseIndex[IDX_LEN-1:0];
    `endif
end
endgenerate

SQEntry entries[NUM_ENTRIES-1:0] /* verilator public */;
SqN baseIndex /* verilator public */;

reg empty;
always_comb begin
    empty = 1;
    for (integer i = 0; i < NUM_ENTRIES; i=i+1) begin
        if (entries[i].addrAvail) empty = 0;
    end
end

typedef enum logic[0:0] {LOAD, STORE_FUSE} LookupType;

reg[31:0] lookupAddr[NUM_AGUS-1:0];
always_comb begin
    for (integer h = 0; h < NUM_AGUS; h=h+1)
        lookupAddr[h] = IN_uopLd[h].addr;
end

reg[3:0] readMask[NUM_AGUS-1:0];
always_comb begin
    for (integer i = 0; i < NUM_AGUS; i=i+1) begin
        readMask[i] = 4'b1111;
        if (IN_uopLd[i].valid)
            case (IN_uopLd[i].size)
                0: readMask[i] = (4'b1 << IN_uopLd[i].addr[1:0]);
                1: readMask[i] = ((IN_uopLd[i].addr[1:0] == 2) ? 4'b1100 : 4'b0011);
                default: readMask[i] = 4'b1111;
            endcase
    end
end

reg[3:0] lookupMask[NUM_AGUS-1:0];
reg[31:0] lookupData[NUM_AGUS-1:0];
reg lookupConflict[NUM_AGUS-1:0];
// Store queue lookup
for (genvar h = 0; h < NUM_AGUS; h=h+1)
always_comb begin

    reg[31:0] data = 'x;
    reg[3:0] mask = 'x;

    // Bytes that are not read by this op are set to available in the lookup mask
    // (could also do this in LSU)
    lookupMask[h] = ~readMask[h];
    lookupData[h] = 32'bx;
    lookupConflict[h] = 0;

        if (OUT_uop[i].valid &&
            OUT_uop[i].addr[31:2] == lookupAddr[h][31:2] &&
            !`IS_MMIO_PMA(OUT_uop[i].addr)
        ) begin
            for (integer j = 0; j < 4; j=j+1)
                if (OUT_uop[i].wmask[j])
                    lookupData[h][j*8 +: 8] = OUT_uop[i].data[j*8 +: 8];
            lookupMask[h] = lookupMask[h] | OUT_uop[i].wmask;
        end
    end

    `ifdef SQ_LINEAR
    for (integer i = 0; i < 2 * NUM_ENTRIES; i=i+1) begin

        integer ii = i % NUM_ENTRIES;

        if (entries[ii].addrAvail &&
            entries[ii].addr == lookupAddr[h][31:2] &&
            (forwardRange_c[h][i] || (i < NUM_ENTRIES && entryReady_r[ii])) &&
            !`IS_MMIO_PMA_W(entries[ii].addr)
        ) begin

            if (entries[ii].loaded) begin

                for (integer j = 0; j < 4; j=j+1)
                    if (entries[ii].wmask[j]) begin
                        lookupData[h][j*8 +: 8] = entries[ii].data[j*8 +: 8];
                        lookupMask[h][j] = 1;
                    end
            end
            else if ((entries[ii].wmask & readMask[h]) != 0) lookupConflict[h] = 1;
        end
    end
    `else
    for (integer i = 0; i < 4; i=i+1)
        if (lookupMaskIter[h][outputIdx][i])
            lookupData[h][i*8 +: 8] = lookupDataIter[h][outputIdx][i*8 +: 8];

    lookupMask[h] = lookupMask[h] | lookupMaskIter[h][outputIdx];
    lookupConflict[h] = |lookupConflictList[h];
    `endif
end

`ifndef SQ_LINEAR
// This generates circular logic to iterate through the StoreQueue for forwarding data to loads.
// Circular logic is necessary to efficiently iterate through a circular buffer (which the SQ is).
// If tooling does not support this, it might be necessary to make the SQ a shift register again
// or chose one of the less efficient methods of iteration.
logic[31:0] lookupDataIter[NUM_AGUS-1:0][NUM_ENTRIES-1:0];
logic[3:0]  lookupMaskIter[NUM_AGUS-1:0][NUM_ENTRIES-1:0];
logic[NUM_ENTRIES-1:0] lookupConflictList[NUM_AGUS-1:0];
wire[IDX_LEN-1:0] outputIdx = baseIndex[IDX_LEN-1:0] - 1;
generate
for (genvar h = 0; h < NUM_AGUS; h=h+1)
for (genvar i = 0; i < NUM_ENTRIES; i=i+1)
always_comb begin

    integer prev = ((i-1) >= 0) ? (i-1) : (NUM_ENTRIES-1);
    // break in circular feedback
    if (i == baseIndex[IDX_LEN-1:0]) begin
        lookupMaskIter[h][i] = 0;
        lookupDataIter[h][i] = 0;
    end
    // continue circular feedback
    else begin
        lookupMaskIter[h][i] = lookupMaskIter[h][prev];
        lookupDataIter[h][i] = lookupDataIter[h][prev];
    end

    // actual forwarding
    lookupConflictList[h][i] = 0;
    if (entries[i].addrAvail &&
        entries[i].addr == lookupAddr[h][31:2] && (forwardRange_c[h][i] || entryReady_r[i]) &&
        !`IS_MMIO_PMA_W(entries[i].addr)
    ) begin

        if (entries[i].loaded) begin
            for (integer j = 0; j < 4; j=j+1)
                if (entries[i].wmask[j]) begin
                    lookupDataIter[h][i][j*8 +: 8] = entries[i].data[j*8 +: 8];
                    lookupMaskIter[h][i][j] = 1;
                end
        end
        else if ((entries[i].wmask & readMask[h]) != 0) lookupConflictList[h][i] = 1;
    end
end
endgenerate
`endif

wire[IDX_LEN-1:0] baseIndexI = baseIndex[IDX_LEN-1:0];
wire[IDX_LEN-1:0] comStSqNI = IN_comStSqN[IDX_LEN-1:0];

assign OUT_done = baseIndex == IN_comStSqN;

// Sort uops to enqueue by storeSqN
R_UOp rnUOpSorted[`DEC_WIDTH-1:0];
always_comb begin
    for (integer i = 0; i < `DEC_WIDTH; i=i+1) begin
        rnUOpSorted[i] = 'x;
        rnUOpSorted[i].valid = 0;

        for (integer j = 0; j < `DEC_WIDTH; j=j+1) begin
            // This could be one-hot...
            if (IN_rnUOp[j].valid && IN_rnUOp[j].storeSqN[$clog2(`DEC_WIDTH)-1:0] == i[$clog2(`DEC_WIDTH)-1:0] &&
                ((IN_rnUOp[j].fu == FU_AGU && IN_rnUOp[j].opcode >= LSU_SC_W) || IN_rnUOp[j].fu == FU_ATOMIC)
            ) begin
                rnUOpSorted[i] = IN_rnUOp[j];
            end
        end
    end
end

// Dequeue logic to infer sequential reads from SQ
logic[IDX_LEN-1:0] deqAddrsSorted[NUM_OUT-1:0];
logic[IDX_LEN-1:0] deqAddrs[NUM_OUT-1:0];
always_comb begin
    for (integer i = 0; i < NUM_OUT; i=i+1)
        deqAddrs[i] = baseIndexI + i[IDX_LEN-1:0];

    for (integer i = 0; i < NUM_OUT; i=i+1)
        deqAddrsSorted[i] = 'x;

    for (integer i = 0; i < NUM_OUT; i=i+1)
        deqAddrsSorted[deqAddrs[i][$clog2(NUM_OUT)-1:0]] = deqAddrs[i];
end
SQ_UOp deqPorts[NUM_OUT-1:0];
always_comb begin

    for (integer i = 0; i < NUM_OUT; i=i+1) begin
        logic[IDX_LEN-1:0] addr =
            {deqAddrsSorted[i][IDX_LEN-1:$clog2(NUM_OUT)], i[$clog2(NUM_OUT)-1:0]};
        SQEntry entry = entries[addr];
        logic ready = entryReady_r[addr] && entry.loaded;

        deqPorts[i] = SQ_UOp'{valid: 0, default: 'x};
        if (ready) begin
            deqPorts[i].data = entry.data;
            deqPorts[i].addr = {entry.addr, 2'b0};
            deqPorts[i].wmask = entry.wmask;
            deqPorts[i].isMgmt = entry.wmask == 0;
            deqPorts[i].valid = 1;
        end
    end
end
SQ_UOp deqEntries[NUM_OUT-1:0];
always_comb begin
    logic prevValid = 1;
    for (integer i = 0; i < NUM_OUT; i=i+1) begin
        deqEntries[i] = prevValid ?
            deqPorts[deqAddrs[i][$clog2(NUM_OUT)-1:0]] :
            SQ_UOp'{valid: 0, default: 'x};
        prevValid = deqEntries[i].valid;
    end
end

SQ_UOp outDeqView[NUM_OUT*2-1:0];
always_comb begin
    for (integer i = 0; i < NUM_OUT; i=i+1)
        outDeqView[i] = OUT_uop[i];

    for (integer i = 0; i < NUM_OUT; i=i+1)
        outDeqView[i+NUM_OUT] = deqEntries[i];
end

logic[NUM_OUT*2-1:0] unhandled;
always_comb begin
    for (integer i = 0; i < NUM_OUT; i=i+1)
        unhandled[i] = OUT_uop[i].valid && IN_stall[i];
    for (integer i = NUM_OUT; i < NUM_OUT*2; i=i+1)
        unhandled[i] = 1;
end
logic[$clog2(NUM_OUT):0] srcIdx[NUM_OUT-1:0];
PriorityEncoder#(2*NUM_OUT, NUM_OUT) penc
(
    .IN_data(unhandled),
    .OUT_idx(srcIdx),
    .OUT_idxValid()
);

logic[NUM_OUT-1:0] entryWasDeqd;
logic[NUM_OUT-1:0] deqCountUnary;
always_comb begin
    entryWasDeqd = '0;

    for (integer i = 0; i < NUM_OUT; i=i+1) begin
        reg[$clog2(NUM_OUT):0] idx = srcIdx[i];
        reg[$clog2(NUM_ENTRIES)-1:0] idxSQ =
                baseIndexI + $clog2(NUM_ENTRIES)'(idx) - $clog2(NUM_ENTRIES)'(NUM_OUT);

        deqCountUnary[i] = 0;
        if (outDeqView[idx].valid && idx >= NUM_OUT) begin
            entryWasDeqd[idxSQ[$clog2(NUM_OUT)-1:0]] = 1;
            deqCountUnary[i] = 1;
        end
    end
end

logic[$clog2(NUM_OUT):0] deqCount;
PopCnt#(NUM_OUT) popc(.in(deqCountUnary), .res(deqCount));
wire SqN nextBaseIndex = baseIndex + SqN'(deqCount);

// Dequeue/Enqueue
reg flushing;
assign OUT_flush = flushing;
always_ff@(posedge clk /*or posedge rst*/) begin

    for (integer i = 0; i < NUM_AGUS; i=i+1) begin
        OUT_fwd[i] <= 'x;
        OUT_fwd[i].valid <= 0;
    end

    if (rst) begin
        baseIndex <= 0;

        OUT_maxStoreSqN <= NUM_ENTRIES[$bits(SqN)-1:0] - 1;
        OUT_empty <= 1;
        flushing <= 0;

        for (integer i = 0; i < NUM_OUT; i=i+1)
            OUT_uop[i] <= SQ_UOp'{valid: 0, default: 'x};

        for (integer i = 0; i < NUM_ENTRIES; i=i+1)
            entries[i] <= SQEntry'{addrAvail: 0, loaded: 0, default: 'x};
    end
    else begin
        reg modified = 0;

        // Dequeue
        for (integer i = 0; i < NUM_OUT; i=i+1) begin
            reg[$clog2(NUM_OUT):0] idx = srcIdx[i];
            OUT_uop[i] <= outDeqView[idx];
        end
        for (integer i = 0; i < NUM_OUT; i=i+1) begin
            reg[$clog2(NUM_OUT)-1:0] offs = i[$clog2(NUM_OUT)-1:0];
            reg[$clog2(NUM_ENTRIES)-1:0] addr = {deqAddrsSorted[offs][$clog2(NUM_ENTRIES)-1:$clog2(NUM_OUT)], offs};

            if (entryWasDeqd[i]) begin
                entries[addr].loaded <= 0;
                entries[addr].addrAvail <= 0;
            end
        end

        // Write Loaded Data
        for (integer i = 0; i < NUM_AGUS; i=i+1) begin
            if (IN_stDataUOp[i].valid && (!IN_branch.taken ||
                (!IN_branch.flush && $signed(IN_stDataUOp[i].storeSqN - IN_branch.storeSqN) <= 0))
            ) begin
                logic[IDX_LEN-1:0] idx = IN_stDataUOp[i].storeSqN[IDX_LEN-1:0];

                assert(idx[0] == i[0]); idx[0] = i[0];

                entries[idx].loaded <= 1;
                entries[idx].data <= IN_stDataUOp[i].data;
            end
        end

        // Invalidate
        if (IN_branch.taken) begin
            flushing <= IN_branch.flush;

            for (integer i = 0; i < NUM_ENTRIES; i=i+1) begin
                if (invalRange_c[i] || (IN_branch.flush && !entryReady_r[i]))
                    entries[i] <= SQEntry'{addrAvail: 0, loaded: 0, default: 'x};
            end
        end

        // Set Address
        for (integer i = 0; i < NUM_AGUS; i=i+1) begin
            if (IN_uopSt[i].valid && IN_uopSt[i].isStore &&
                (!IN_branch.taken || ($signed(IN_uopSt[i].sqN - IN_branch.sqN) <= 0 && !IN_branch.flush))
            ) begin
                reg[IDX_LEN-1:0] index = IN_uopSt[i].storeSqN[IDX_LEN-1:0];
                assert(index[0] == i[0]); index[0] = i[0];

                assert(IN_uopSt[i].storeSqN <= nextBaseIndex + NUM_ENTRIES[$bits(SqN)-1:0] - 1);
                assert(!entries[index].addrAvail);

                entries[index].addr <= IN_uopSt[i].addr[31:2];
                entries[index].wmask <= IN_uopSt[i].wmask;
                entries[index].addrAvail <= 1;

                modified = 1;
            end
        end

        OUT_empty <= empty && !modified;
        if (OUT_empty && flushing) begin
            flushing <= 0;
        end
        OUT_maxStoreSqN <= nextBaseIndex + NUM_ENTRIES[$bits(SqN)-1:0] - 1;

        for (integer i = 0; i < NUM_AGUS; i=i+1)
            if (IN_uopLd[i].valid) begin
                OUT_fwd[i].valid <= 1;
                OUT_fwd[i].data <= lookupData[i];
                OUT_fwd[i].mask <= lookupMask[i];
                OUT_fwd[i].conflict <= lookupConflict[i];
            end

        baseIndex <= nextBaseIndex;
    end
end
endmodule


// tb.vhd
// ------------------------------------
// Testbench for logic_ex
// ------------------------------------
// Author : Frank Bruno
// Exhaustively test all combinations for the logic_ex module
`timescale 1ns/ 100ps;

module tb;

  logic [1:0] SW;
  logic [3:0] LED;

  logic_ex u_logic_ex (.*);

  // Stimulus
  initial begin
    $printtimescale(tb);
    SW = '0;
    for (int i = 0; i < 4; i++) begin
      $display("Setting switches to %2b", i[1:0]);
      SW  = i[1:0];
      #100;
    end
    $display("PASS: logic_ex test PASSED!");
    $stop;
  end

      $display("FAIL: NOT Gate mismatch");
      $stop;
    end
    if (&SW[1:0] !== LED[1]) begin
      $display("FAIL: AND Gate mismatch");
      $stop;
    end
    if (|SW[1:0] !== LED[2]) begin
      $display("FAIL: OR Gate mismatch");
      $stop;
    end
    if (^SW[1:0] !== LED[3]) begin
      $display("FAIL: XOR Gate mismatch");
      $stop;
    end
  end
endmodule // tb