// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 Western Digital Corporation or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the 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.

//********************************************************************************
// $Id$
//
//
// Owner:
// Function: Store Buffer
// Comments: Dual writes and single drain
//
//
// DC1 -> DC2 -> DC3 -> DC4 (Commit)
//
// //********************************************************************************

module el2_lsu_stbuf
import el2_pkg::*;
#(
`include "el2_param.vh"
 )
(
   input logic                           clk,                         // core clock
   input logic                           rst_l,                       // reset

   input logic                           lsu_stbuf_c1_clk,            // stbuf clock
   input logic                           lsu_free_c2_clk,             // free clk

   // Store Buffer input
   input logic                           store_stbuf_reqvld_r,        // core instruction goes to stbuf
   input logic                           lsu_commit_r,                // lsu commits
   input logic                           dec_lsu_valid_raw_d,         // Speculative decode valid
   input logic [pt.DCCM_DATA_WIDTH-1:0]  store_data_hi_r,             // merged data from the dccm for stores. This is used for fwding
   input logic [pt.DCCM_DATA_WIDTH-1:0]  store_data_lo_r,             // merged data from the dccm for stores. This is used for fwding
   input logic [pt.DCCM_DATA_WIDTH-1:0]  store_datafn_hi_r,           // merged data from the dccm for stores
   input logic [pt.DCCM_DATA_WIDTH-1:0]  store_datafn_lo_r,           // merged data from the dccm for stores

   // Store Buffer output
   output logic                          stbuf_reqvld_any,            // stbuf is draining
   output logic                          stbuf_reqvld_flushed_any,    // Top entry is flushed
   output logic [pt.LSU_SB_BITS-1:0]     stbuf_addr_any,              // address
   output logic [pt.DCCM_DATA_WIDTH-1:0] stbuf_data_any,              // stbuf data

   input  logic                          lsu_stbuf_commit_any,        // pop the stbuf as it commite
   output logic                          lsu_stbuf_full_any,          // stbuf is full
   output logic                          lsu_stbuf_empty_any,         // stbuf is empty
   output logic                          ldst_stbuf_reqvld_r,         // needed for clocking

   input logic [pt.LSU_SB_BITS-1:0]      lsu_addr_d,                  // lsu address D-stage
   input logic [31:0]                    lsu_addr_m,                  // lsu address M-stage
   input logic [31:0]                    lsu_addr_r,                  // lsu address R-stage

   input logic [pt.LSU_SB_BITS-1:0]      end_addr_d,                  // lsu end address D-stage - needed to check unaligned
   input logic [31:0]                    end_addr_m,                  // lsu end address M-stage - needed to check unaligned
   input logic [31:0]                    end_addr_r,                  // lsu end address R-stage - needed to check unaligned

   input logic                           ldst_dual_d, ldst_dual_m, ldst_dual_r,
   input logic                           addr_in_dccm_m,              // address is in dccm
   input logic                           addr_in_dccm_r,              // address is in dccm

   // Forwarding signals
   input logic                           lsu_cmpen_m,                 // needed for forwarding stbuf - load
   input el2_lsu_pkt_t                  lsu_pkt_m,                   // LSU packet M-stage
   input el2_lsu_pkt_t                  lsu_pkt_r,                   // LSU packet R-stage

   output logic [pt.DCCM_DATA_WIDTH-1:0] stbuf_fwddata_hi_m,          // stbuf data
   output logic [pt.DCCM_DATA_WIDTH-1:0] stbuf_fwddata_lo_m,          // stbuf data
   output logic [pt.DCCM_BYTE_WIDTH-1:0] stbuf_fwdbyteen_hi_m,        // stbuf data
   output logic [pt.DCCM_BYTE_WIDTH-1:0] stbuf_fwdbyteen_lo_m,        // stbuf data

   // Excluding scan_mode from coverage as its usage is determined by the integrator of the VeeR core.
   /*pragma coverage off*/
   input  logic       scan_mode                                       // Scan mode
   /*pragma coverage on*/

);

   localparam DEPTH      = pt.LSU_STBUF_DEPTH;
   localparam DATA_WIDTH = pt.DCCM_DATA_WIDTH;
   localparam BYTE_WIDTH = pt.DCCM_BYTE_WIDTH;
   localparam DEPTH_LOG2 = $clog2(DEPTH);

   // These are the fields in the store queue
   logic [DEPTH-1:0]                     stbuf_vld;
   logic [DEPTH-1:0]                     stbuf_dma_kill;
   logic [DEPTH-1:0][pt.LSU_SB_BITS-1:0] stbuf_addr;
   logic [DEPTH-1:0][BYTE_WIDTH-1:0]     stbuf_byteen;
   logic [DEPTH-1:0][DATA_WIDTH-1:0]     stbuf_data;

   logic [DEPTH-1:0]                     sel_lo;
   logic [DEPTH-1:0]                     stbuf_wr_en;
   logic [DEPTH-1:0]                     stbuf_dma_kill_en;
   logic [DEPTH-1:0]                     stbuf_reset;
   logic [DEPTH-1:0][pt.LSU_SB_BITS-1:0] stbuf_addrin;
   logic [DEPTH-1:0][DATA_WIDTH-1:0]     stbuf_datain;
   logic [DEPTH-1:0][BYTE_WIDTH-1:0]     stbuf_byteenin;

   logic [7:0]             store_byteen_ext_r;
   logic [BYTE_WIDTH-1:0]  store_byteen_hi_r;
   logic [BYTE_WIDTH-1:0]  store_byteen_lo_r;

   logic                   WrPtrEn, RdPtrEn;
   logic [DEPTH_LOG2-1:0]  WrPtr, RdPtr;
   logic [DEPTH_LOG2-1:0]  NxtWrPtr, NxtRdPtr;
   logic [DEPTH_LOG2-1:0]  WrPtrPlus1, WrPtrPlus2, RdPtrPlus1;

   logic                   dual_stbuf_write_r;

   logic                   isdccmst_m, isdccmst_r;
   logic [3:0]             stbuf_numvld_any, stbuf_specvld_any;
   logic [1:0]             stbuf_specvld_m, stbuf_specvld_r;

   logic [pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] cmpaddr_hi_m, cmpaddr_lo_m;

   // variables to detect matching from the store queue
   logic [DEPTH-1:0]                 stbuf_match_hi, stbuf_match_lo;
   logic [DEPTH-1:0][BYTE_WIDTH-1:0] stbuf_fwdbyteenvec_hi, stbuf_fwdbyteenvec_lo;
   logic [DATA_WIDTH-1:0]            stbuf_fwddata_hi_pre_m, stbuf_fwddata_lo_pre_m;
   logic [BYTE_WIDTH-1:0]            stbuf_fwdbyteen_hi_pre_m, stbuf_fwdbyteen_lo_pre_m;

   // logic to detect matching from the pipe - needed for store - load forwarding
   logic [BYTE_WIDTH-1:0]  ld_byte_rhit_lo_lo, ld_byte_rhit_hi_lo, ld_byte_rhit_lo_hi, ld_byte_rhit_hi_hi;
   logic                   ld_addr_rhit_lo_lo, ld_addr_rhit_hi_lo, ld_addr_rhit_lo_hi, ld_addr_rhit_hi_hi;

   logic [BYTE_WIDTH-1:0]  ld_byte_hit_lo, ld_byte_rhit_lo;
   logic [BYTE_WIDTH-1:0]  ld_byte_hit_hi, ld_byte_rhit_hi;

   logic [BYTE_WIDTH-1:0]  ldst_byteen_hi_r;
   logic [BYTE_WIDTH-1:0]  ldst_byteen_lo_r;
   // byte_en flowing down
   logic [7:0]             ldst_byteen_r;
   logic [7:0]             ldst_byteen_ext_r;
   // fwd data through the pipe
   logic [31:0]       ld_fwddata_rpipe_lo;
   logic [31:0]       ld_fwddata_rpipe_hi;

   // coalescing signals
   logic [DEPTH-1:0]      store_matchvec_lo_r, store_matchvec_hi_r;
   logic                  store_coalesce_lo_r, store_coalesce_hi_r;

   //----------------------------------------
   // Logic starts here
   //----------------------------------------
   // Create high/low byte enables
   assign store_byteen_ext_r[7:0]           = ldst_byteen_r[7:0] << lsu_addr_r[1:0];
   assign store_byteen_hi_r[BYTE_WIDTH-1:0] = store_byteen_ext_r[7:4] & {4{lsu_pkt_r.store}};
   assign store_byteen_lo_r[BYTE_WIDTH-1:0] = store_byteen_ext_r[3:0] & {4{lsu_pkt_r.store}};

   assign RdPtrPlus1[DEPTH_LOG2-1:0]     = RdPtr[DEPTH_LOG2-1:0] + 1'b1;
   assign WrPtrPlus1[DEPTH_LOG2-1:0]     = WrPtr[DEPTH_LOG2-1:0] + 1'b1;
   assign WrPtrPlus2[DEPTH_LOG2-1:0]     = WrPtr[DEPTH_LOG2-1:0] + 2'b10;

   // ecc error on both hi/lo
   assign dual_stbuf_write_r   = ldst_dual_r & store_stbuf_reqvld_r;
   assign ldst_stbuf_reqvld_r  = ((lsu_commit_r | lsu_pkt_r.dma) & store_stbuf_reqvld_r);

  // Store Buffer coalescing
   for (genvar i=0; i<DEPTH; i++) begin: FindMatchEntry
       assign store_matchvec_lo_r[i] = (stbuf_addr[i][pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] == lsu_addr_r[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)]) & stbuf_vld[i] & ~stbuf_dma_kill[i] & ~stbuf_reset[i];
       assign store_matchvec_hi_r[i] = (stbuf_addr[i][pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] == end_addr_r[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)]) & stbuf_vld[i] & ~stbuf_dma_kill[i] & dual_stbuf_write_r & ~stbuf_reset[i];
   end: FindMatchEntry

   assign store_coalesce_lo_r = |store_matchvec_lo_r[DEPTH-1:0];
   assign store_coalesce_hi_r = |store_matchvec_hi_r[DEPTH-1:0];

   if (pt.DCCM_ENABLE == 1) begin: Gen_dccm_enable
      // Allocate new in this entry if :
      // 1. wrptr, single allocate, lo did not coalesce
      // 2. wrptr, double allocate, lo ^ hi coalesced
      // 3. wrptr + 1, double alloacte, niether lo or hi coalesced
      // Also update if there is a hi or a lo coalesce to this entry
      // Store Buffer instantiation
      for (genvar i=0; i<DEPTH; i++) begin: GenStBuf
         assign stbuf_wr_en[i] = ldst_stbuf_reqvld_r & (
                                   ( (i == WrPtr[DEPTH_LOG2-1:0])      &  ~store_coalesce_lo_r)   |                                                    // Allocate : new Lo
                                   ( (i == WrPtr[DEPTH_LOG2-1:0])      &  dual_stbuf_write_r & ~store_coalesce_hi_r) |                               // Allocate : only 1 new Write Either
                                   ( (i == WrPtrPlus1[DEPTH_LOG2-1:0]) &  dual_stbuf_write_r & ~(store_coalesce_lo_r | store_coalesce_hi_r)) |     // Allocate2 : 2 new so Write Hi
                                   store_matchvec_lo_r[i] | store_matchvec_hi_r[i]);                                                                 // Coalesced Write Lo or Hi
         assign stbuf_reset[i] = (lsu_stbuf_commit_any | stbuf_reqvld_flushed_any) & (i == RdPtr[DEPTH_LOG2-1:0]);

         // Mux select for start/end address
         assign sel_lo[i]                         = ((~ldst_dual_r | store_stbuf_reqvld_r) & (i == WrPtr[DEPTH_LOG2-1:0]) & ~store_coalesce_lo_r) |   // lo allocated new entry
                                                    store_matchvec_lo_r[i];                                                                                // lo coalesced in to this entry
         assign stbuf_addrin[i][pt.LSU_SB_BITS-1:0]  = sel_lo[i] ? lsu_addr_r[pt.LSU_SB_BITS-1:0]       : end_addr_r[pt.LSU_SB_BITS-1:0];
         assign stbuf_byteenin[i][BYTE_WIDTH-1:0] = sel_lo[i] ? (stbuf_byteen[i][BYTE_WIDTH-1:0] | store_byteen_lo_r[BYTE_WIDTH-1:0])          : (stbuf_byteen[i][BYTE_WIDTH-1:0] | store_byteen_hi_r[BYTE_WIDTH-1:0]);
         assign stbuf_datain[i][7:0]              = sel_lo[i] ? ((~stbuf_byteen[i][0] | store_byteen_lo_r[0]) ? store_datafn_lo_r[7:0]   : stbuf_data[i][7:0])    :
                                                                ((~stbuf_byteen[i][0] | store_byteen_hi_r[0]) ? store_datafn_hi_r[7:0]   : stbuf_data[i][7:0]);
         assign stbuf_datain[i][15:8]             = sel_lo[i] ? ((~stbuf_byteen[i][1] | store_byteen_lo_r[1]) ? store_datafn_lo_r[15:8]  : stbuf_data[i][15:8])    :
                                                                ((~stbuf_byteen[i][1] | store_byteen_hi_r[1]) ? store_datafn_hi_r[15:8]  : stbuf_data[i][15:8]);
         assign stbuf_datain[i][23:16]            = sel_lo[i] ? ((~stbuf_byteen[i][2] | store_byteen_lo_r[2]) ? store_datafn_lo_r[23:16] : stbuf_data[i][23:16])    :
                                                                ((~stbuf_byteen[i][2] | store_byteen_hi_r[2]) ? store_datafn_hi_r[23:16] : stbuf_data[i][23:16]);
         assign stbuf_datain[i][31:24]            = sel_lo[i] ? ((~stbuf_byteen[i][3] | store_byteen_lo_r[3]) ? store_datafn_lo_r[31:24] : stbuf_data[i][31:24])    :
                                                                ((~stbuf_byteen[i][3] | store_byteen_hi_r[3]) ? store_datafn_hi_r[31:24] : stbuf_data[i][31:24]);

         rvdffsc #(.WIDTH(1))              stbuf_vldff         (.din(1'b1),                                .dout(stbuf_vld[i]),                      .en(stbuf_wr_en[i]), .clear(stbuf_reset[i]), .clk(lsu_free_c2_clk), .*);
         rvdffsc #(.WIDTH(1))              stbuf_killff        (.din(1'b1),                                .dout(stbuf_dma_kill[i]),                 .en(stbuf_dma_kill_en[i]), .clear(stbuf_reset[i]), .clk(lsu_free_c2_clk), .*);
         rvdffe  #(.WIDTH(pt.LSU_SB_BITS)) stbuf_addrff        (.din(stbuf_addrin[i][pt.LSU_SB_BITS-1:0]), .dout(stbuf_addr[i][pt.LSU_SB_BITS-1:0]), .en(stbuf_wr_en[i]), .*);
         rvdffsc #(.WIDTH(BYTE_WIDTH))     stbuf_byteenff      (.din(stbuf_byteenin[i][BYTE_WIDTH-1:0]),   .dout(stbuf_byteen[i][BYTE_WIDTH-1:0]),   .en(stbuf_wr_en[i]), .clear(stbuf_reset[i]), .clk(lsu_stbuf_c1_clk), .*);
         rvdffe  #(.WIDTH(DATA_WIDTH))     stbuf_dataff        (.din(stbuf_datain[i][DATA_WIDTH-1:0]),     .dout(stbuf_data[i][DATA_WIDTH-1:0]),     .en(stbuf_wr_en[i]), .*);
      end
   end else begin: Gen_dccm_disable
      assign stbuf_wr_en[DEPTH-1:0] = '0;
      assign stbuf_reset[DEPTH-1:0] = '0;
      assign stbuf_vld[DEPTH-1:0]   = '0;
      assign stbuf_dma_kill[DEPTH-1:0] = '0;
      assign stbuf_addr[DEPTH-1:0]  = '0;
      assign stbuf_byteen[DEPTH-1:0] = '0;
      assign stbuf_data[DEPTH-1:0]   = '0;
   end

   // Store Buffer drain logic
   assign stbuf_reqvld_flushed_any            = stbuf_vld[RdPtr] & stbuf_dma_kill[RdPtr];
   assign stbuf_reqvld_any                    = stbuf_vld[RdPtr] & ~stbuf_dma_kill[RdPtr] & ~(|stbuf_dma_kill_en[DEPTH-1:0]);  // Don't drain if some kill bit is being set this cycle
   assign stbuf_addr_any[pt.LSU_SB_BITS-1:0]  = stbuf_addr[RdPtr][pt.LSU_SB_BITS-1:0];
   assign stbuf_data_any[DATA_WIDTH-1:0]      = stbuf_data[RdPtr][DATA_WIDTH-1:0];

   // Update the RdPtr/WrPtr logic
   // Need to revert the WrPtr for flush cases. Also revert the pipe WrPtrs
   assign WrPtrEn                  = (ldst_stbuf_reqvld_r  & ~dual_stbuf_write_r & ~(store_coalesce_hi_r | store_coalesce_lo_r))  |  // writing 1 and did not coalesce
                                     (ldst_stbuf_reqvld_r  &  dual_stbuf_write_r & ~(store_coalesce_hi_r & store_coalesce_lo_r));    // writing 2 and atleast 1 did not coalesce
   assign NxtWrPtr[DEPTH_LOG2-1:0] = (ldst_stbuf_reqvld_r & dual_stbuf_write_r & ~(store_coalesce_hi_r | store_coalesce_lo_r)) ? WrPtrPlus2[DEPTH_LOG2-1:0] : WrPtrPlus1[DEPTH_LOG2-1:0];
   assign RdPtrEn                  = lsu_stbuf_commit_any | stbuf_reqvld_flushed_any;
   assign NxtRdPtr[DEPTH_LOG2-1:0] = RdPtrPlus1[DEPTH_LOG2-1:0];

   always_comb begin
      stbuf_numvld_any[3:0] = '0;
      for (int i=0; i<DEPTH; i++) begin
         stbuf_numvld_any[3:0] += {3'b0, stbuf_vld[i]};
      end
   end

    // These go to store buffer to detect full
   assign isdccmst_m = lsu_pkt_m.valid & lsu_pkt_m.store & addr_in_dccm_m & ~lsu_pkt_m.dma;
   assign isdccmst_r = lsu_pkt_r.valid & lsu_pkt_r.store & addr_in_dccm_r & ~lsu_pkt_r.dma;

   assign stbuf_specvld_m[1:0] = {1'b0,isdccmst_m} << (isdccmst_m & ldst_dual_m);
   assign stbuf_specvld_r[1:0] = {1'b0,isdccmst_r} << (isdccmst_r & ldst_dual_r);
   assign stbuf_specvld_any[3:0] = stbuf_numvld_any[3:0] +  {2'b0, stbuf_specvld_m[1:0]} + {2'b0, stbuf_specvld_r[1:0]};

   assign lsu_stbuf_full_any  = (~ldst_dual_d & dec_lsu_valid_raw_d) ? (stbuf_specvld_any[3:0] >= DEPTH) : (stbuf_specvld_any[3:0] >= (DEPTH-1));
   assign lsu_stbuf_empty_any = (stbuf_numvld_any[3:0] == 4'b0);

   // Load forwarding logic from the store queue
   assign cmpaddr_hi_m[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] = end_addr_m[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)];

   assign cmpaddr_lo_m[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] = lsu_addr_m[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)];

   always_comb begin: GenLdFwd
      stbuf_fwdbyteen_hi_pre_m[BYTE_WIDTH-1:0]   = '0;
      stbuf_fwdbyteen_lo_pre_m[BYTE_WIDTH-1:0]   = '0;

      for (int i=0; i<DEPTH; i++) begin
         stbuf_match_hi[i] = (stbuf_addr[i][pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] == cmpaddr_hi_m[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)]) & stbuf_vld[i] & ~stbuf_dma_kill[i] & addr_in_dccm_m;
         stbuf_match_lo[i] = (stbuf_addr[i][pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)] == cmpaddr_lo_m[pt.LSU_SB_BITS-1:$clog2(BYTE_WIDTH)]) & stbuf_vld[i] & ~stbuf_dma_kill[i] & addr_in_dccm_m;

         // Kill the store buffer entry if there is a dma store since it already updated the dccm
         stbuf_dma_kill_en[i] = (stbuf_match_hi[i] | stbuf_match_lo[i]) & lsu_pkt_m.valid & lsu_pkt_m.dma & lsu_pkt_m.store;

         for (int j=0; j<BYTE_WIDTH; j++) begin
            stbuf_fwdbyteenvec_hi[i][j] = stbuf_match_hi[i] & stbuf_byteen[i][j] & stbuf_vld[i];
            stbuf_fwdbyteen_hi_pre_m[j]  |= stbuf_fwdbyteenvec_hi[i][j];

            stbuf_fwdbyteenvec_lo[i][j] = stbuf_match_lo[i] & stbuf_byteen[i][j] & stbuf_vld[i];
            stbuf_fwdbyteen_lo_pre_m[j]  |= stbuf_fwdbyteenvec_lo[i][j];
         end
      end
   end // block: GenLdFwd

   always_comb begin: GenLdData
      stbuf_fwddata_hi_pre_m[31:0]   = '0;
      stbuf_fwddata_lo_pre_m[31:0]   = '0;

      for (int i=0; i<DEPTH; i++) begin
         stbuf_fwddata_hi_pre_m[31:0] |= {32{stbuf_match_hi[i]}} & stbuf_data[i][31:0];
         stbuf_fwddata_lo_pre_m[31:0] |= {32{stbuf_match_lo[i]}} & stbuf_data[i][31:0];

      end

   end // block: GenLdData

   // Create Hi/Lo signals - needed for the pipe forwarding
   assign ldst_byteen_r[7:0] =  ({8{lsu_pkt_r.by}}    & 8'b0000_0001) |
                                 ({8{lsu_pkt_r.half}}  & 8'b0000_0011) |
                                 ({8{lsu_pkt_r.word}}  & 8'b0000_1111) |
                                 ({8{lsu_pkt_r.dword}} & 8'b1111_1111);

   assign ldst_byteen_hi_r[3:0]   = ldst_byteen_ext_r[7:4];
   assign ldst_byteen_lo_r[3:0]   = ldst_byteen_ext_r[3:0];

   assign ld_addr_rhit_lo_lo = (lsu_addr_m[31:2] == lsu_addr_r[31:2]) & lsu_pkt_r.valid & lsu_pkt_r.store & ~lsu_pkt_r.dma;
   assign ld_addr_rhit_lo_hi = (end_addr_m[31:2] == lsu_addr_r[31:2]) & lsu_pkt_r.valid & lsu_pkt_r.store & ~lsu_pkt_r.dma;
   assign ld_addr_rhit_hi_lo = (lsu_addr_m[31:2] == end_addr_r[31:2]) & lsu_pkt_r.valid & lsu_pkt_r.store & ~lsu_pkt_r.dma & dual_stbuf_write_r;
   assign ld_addr_rhit_hi_hi = (end_addr_m[31:2] == end_addr_r[31:2]) & lsu_pkt_r.valid & lsu_pkt_r.store & ~lsu_pkt_r.dma & dual_stbuf_write_r;

   for (genvar i=0; i<BYTE_WIDTH; i++) begin
      assign ld_byte_rhit_lo_lo[i] = ld_addr_rhit_lo_lo & ldst_byteen_lo_r[i];
      assign ld_byte_rhit_lo_hi[i] = ld_addr_rhit_lo_hi & ldst_byteen_lo_r[i];
      assign ld_byte_rhit_hi_lo[i] = ld_addr_rhit_hi_lo & ldst_byteen_hi_r[i];
      assign ld_byte_rhit_hi_hi[i] = ld_addr_rhit_hi_hi & ldst_byteen_hi_r[i];

      assign ld_byte_rhit_lo[i] = ld_byte_rhit_lo_lo[i] | ld_byte_rhit_hi_lo[i];
      assign ld_byte_rhit_hi[i] = ld_byte_rhit_lo_hi[i] | ld_byte_rhit_hi_hi[i];

       assign ld_fwddata_rpipe_lo[(8*i)+7:(8*i)] = ({8{ld_byte_rhit_lo_lo[i]}} & store_data_lo_r[(8*i)+7:(8*i)]) |
                                                     ({8{ld_byte_rhit_hi_lo[i]}} & store_data_hi_r[(8*i)+7:(8*i)]);

       assign ld_fwddata_rpipe_hi[(8*i)+7:(8*i)] = ({8{ld_byte_rhit_lo_hi[i]}} & store_data_lo_r[(8*i)+7:(8*i)]) |
                                                     ({8{ld_byte_rhit_hi_hi[i]}} & store_data_hi_r[(8*i)+7:(8*i)]);

      assign ld_byte_hit_lo[i] = ld_byte_rhit_lo_lo[i] | ld_byte_rhit_hi_lo[i];
      assign ld_byte_hit_hi[i] = ld_byte_rhit_lo_hi[i] | ld_byte_rhit_hi_hi[i];

      assign stbuf_fwdbyteen_hi_m[i] = ld_byte_hit_hi[i] | stbuf_fwdbyteen_hi_pre_m[i];
      assign stbuf_fwdbyteen_lo_m[i] = ld_byte_hit_lo[i] | stbuf_fwdbyteen_lo_pre_m[i];
      // // Pipe vs Store Queue priority
      assign stbuf_fwddata_lo_m[(8*i)+7:(8*i)] = ld_byte_rhit_lo[i]    ? ld_fwddata_rpipe_lo[(8*i)+7:(8*i)] : stbuf_fwddata_lo_pre_m[(8*i)+7:(8*i)];
      // // Pipe vs Store Queue priority
      assign stbuf_fwddata_hi_m[(8*i)+7:(8*i)] = ld_byte_rhit_hi[i]    ? ld_fwddata_rpipe_hi[(8*i)+7:(8*i)] : stbuf_fwddata_hi_pre_m[(8*i)+7:(8*i)];
   end

   // Flops
   rvdffs #(.WIDTH(DEPTH_LOG2)) WrPtrff (.din(NxtWrPtr[DEPTH_LOG2-1:0]), .dout(WrPtr[DEPTH_LOG2-1:0]), .en(WrPtrEn), .clk(lsu_stbuf_c1_clk), .*);
   rvdffs #(.WIDTH(DEPTH_LOG2)) RdPtrff (.din(NxtRdPtr[DEPTH_LOG2-1:0]), .dout(RdPtr[DEPTH_LOG2-1:0]), .en(RdPtrEn), .clk(lsu_stbuf_c1_clk), .*);

`ifdef RV_ASSERT_ON

   assert_stbuf_overflow: assert #0 (stbuf_specvld_any[2:0] <= DEPTH);
   property stbuf_wren_store_dccm;
      @(posedge clk)  disable iff(~rst_l) (|stbuf_wr_en[DEPTH-1:0]) |-> (lsu_pkt_r.valid & lsu_pkt_r.store & addr_in_dccm_r);
   endproperty
   assert_stbuf_wren_store_dccm: assert property (stbuf_wren_store_dccm) else
      $display("Illegal store buffer write");

`endif

endmodule

assign ldst_byteen_ext_r[7:0] = ldst_byteen_r[7:0] << lsu_addr_r[1:0];


// SPDX-License-Identifier: Apache-2.0
// Copyright 2019 Western Digital Corporation or its affiliates.
// Copyright 2024 Antmicro <www.antmicro.com>
// //
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the 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.
//
`ifdef RV_BUILD_AHB_LITE

module ahb_sif (
    input logic [63:0] HWDATA,
    input logic HCLK,
    input logic HSEL,
    input logic [3:0] HPROT,
    input logic HWRITE,
    input logic [1:0] HTRANS,
    input logic [2:0] HSIZE,
    input logic HREADY,
    input logic HRESETn,
    input logic [31:0] HADDR,
    input logic [2:0] HBURST,

    output logic HREADYOUT,
    output logic HRESP,
    output logic [63:0] HRDATA
);

  logic write;
  logic [31:0] laddr, addr;
  logic [7:0] strb_lat;
  logic [63:0] rdata;

  bit [7:0] mem[bit [31:0]];
  bit [7:0] wscnt;
  int dws = 0;
  int iws = 0;
  bit dws_rand;
  bit iws_rand;
  bit ok;

  // Wires
  wire [7:0] strb =  HSIZE == 3'b000 ? 8'h1 << HADDR[2:0] :
                     HSIZE == 3'b001 ? 8'h3 << {HADDR[2:1],1'b0} :
                     HSIZE == 3'b010 ? 8'hf << {HADDR[2],2'b0} : 8'hff;

  initial begin
    if ($value$plusargs("iws=%d", iws));
    if ($value$plusargs("dws=%d", dws));
    dws_rand = dws < 0;
    iws_rand = iws < 0;
  end

  always @(negedge HCLK) begin
    if (HREADY) addr = HADDR;
    if (write & HREADY) begin
      if (strb_lat[7]) mem[{laddr[31:3], 3'd7}] = HWDATA[63:56];
      if (strb_lat[6]) mem[{laddr[31:3], 3'd6}] = HWDATA[55:48];
      if (strb_lat[5]) mem[{laddr[31:3], 3'd5}] = HWDATA[47:40];
      if (strb_lat[4]) mem[{laddr[31:3], 3'd4}] = HWDATA[39:32];
      if (strb_lat[3]) mem[{laddr[31:3], 3'd3}] = HWDATA[31:24];
      if (strb_lat[2]) mem[{laddr[31:3], 3'd2}] = HWDATA[23:16];
      if (strb_lat[1]) mem[{laddr[31:3], 3'd1}] = HWDATA[15:08];
      if (strb_lat[0]) mem[{laddr[31:3], 3'd0}] = HWDATA[07:00];
    end
    if (HREADY & HSEL & |HTRANS) begin
`ifdef VERILATOR
      if (iws_rand & ~HPROT[0]) iws = $random & 15;
      if (dws_rand & HPROT[0]) dws = $random & 15;
`else
      if (iws_rand & ~HPROT[0])
        ok = std::randomize(
            iws
        ) with {
          iws dist {
            0 := 10,
            [1 : 3] :/ 2,
            [4 : 15] :/ 1
          };
        };
      if (dws_rand & HPROT[0])
        ok = std::randomize(
            dws
        ) with {
          dws dist {
            0 := 10,
            [1 : 3] :/ 2,
            [4 : 15] :/ 1
          };
        };
`endif
    end
  end

  assign HRDATA = HREADY ? rdata : ~rdata;
  assign HREADYOUT = wscnt == 0;
  assign HRESP = 0;

  always @(posedge HCLK or negedge HRESETn) begin
    if (~HRESETn) begin
      laddr <= 32'b0;
      write <= 1'b0;
      rdata <= '0;
      wscnt <= 0;
    end else begin
      if (HREADY & HSEL) begin
        laddr <= HADDR;
        write <= HWRITE & |HTRANS;
        if (|HTRANS & ~HWRITE)
          rdata <= {
            mem[{addr[31:3], 3'd7}],
            mem[{addr[31:3], 3'd6}],
            mem[{addr[31:3], 3'd5}],
            mem[{addr[31:3], 3'd4}],
            mem[{addr[31:3], 3'd3}],
            mem[{addr[31:3], 3'd2}],
            mem[{addr[31:3], 3'd1}],
            mem[{addr[31:3], 3'd0}]
          };
        strb_lat <= strb;
      end
    end
    if (HREADY & HSEL & |HTRANS) wscnt <= HPROT[0] ? dws[7:0] : iws[7:0];
    else if (wscnt != 0) wscnt <= wscnt - 1;
  end

endmodule

    TAGW = 1
) (
    input                 aclk,
    input                 rst_l,
    input                 arvalid,
    output reg            arready,
    input      [    31:0] araddr,
    input      [TAGW-1:0] arid,
    input      [     7:0] arlen,
    input      [     1:0] arburst,
    input      [     2:0] arsize,

    output reg            rvalid,
    input                 rready,
    output reg [    63:0] rdata,
    output reg [     1:0] rresp,
    output reg [TAGW-1:0] rid,
    output reg            rlast,

    input             awvalid,
    output reg        awready,
    input  [    31:0] awaddr,
    input  [TAGW-1:0] awid,
    input  [     7:0] awlen,
    input  [     1:0] awburst,
    input  [     2:0] awsize,

    input  [63:0] wdata,
    input  [ 7:0] wstrb,
    input         wvalid,
    output reg    wready,

    output reg            bvalid,
    input                 bready,
    output reg [     1:0] bresp,
    output reg [TAGW-1:0] bid
);

  bit [7:0] mem[bit [31:0]];
  bit [31:0] write_address;
  bit [31:0] read_address;

  initial begin
    wready  = 1;
    awready = 1;
    arready = 1'b1;
    rlast   = 1'b0;
    rvalid  = 0;
    bvalid  = 0;
  end

  always @(posedge aclk) begin
    if (arvalid && arready) begin
      read_address = {araddr[31:3], 3'b000};
       rdata <= {
        mem[read_address+7],
        mem[read_address+6],
        mem[read_address+5],
        mem[read_address+4],
        mem[read_address+3],
        mem[read_address+2],
        mem[read_address+1],
        mem[read_address]
      };
       arready <= 0;
       rvalid <= 1;
       rid <= arid;
       rlast <= 1;
       rresp <= 0;
    end else if (rready) begin
       rvalid <= 0;
       arready <= 1;
       rlast <= 0;
    end

    if (awvalid) begin
       write_address = {awaddr[31:3], 3'b000};
       awready <= 0;
    end
     if (wvalid) begin
        bid    <= awid;
        bvalid <= 1;
        wready <= 0;
        bresp <= 0;
      if (wstrb[7]) mem[write_address+7] = wdata[63:56];
      if (wstrb[6]) mem[write_address+6] = wdata[55:48];
      if (wstrb[5]) mem[write_address+5] = wdata[47:40];
      if (wstrb[4]) mem[write_address+4] = wdata[39:32];
      if (wstrb[3]) mem[write_address+3] = wdata[31:24];
      if (wstrb[2]) mem[write_address+2] = wdata[23:16];
      if (wstrb[1]) mem[write_address+1] = wdata[15:08];
      if (wstrb[0]) mem[write_address+0] = wdata[07:00];
     end if (bready && bvalid) begin
        bvalid <= 0;
        awready <= 1;
        wready <= 1;
     end
  end
endmodule


/*
 * Copyright © 2019-2023 Yuhui Gao, Chris Keilbart, Lesley Shannon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the 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.
 *
 * Initial code developed under the supervision of Dr. Lesley Shannon,
 * Reconfigurable Computing Lab, Simon Fraser University.
 *
 * Author(s):
 *             Yuhui Gao <yuhuig@sfu.ca>
 *             Chris Keilbart <ckeilbar@sfu.ca>
 */

module fp_div_sqrt_wrapper

    import cva5_config::*;
    import fpu_types::*;

    (
        input logic clk,
        input logic rst,
        input fp_div_inputs_t div_inputs,
        input fp_sqrt_inputs_t sqrt_inputs,
        unit_issue_interface.unit div_issue,
        unit_issue_interface.unit sqrt_issue,
        fp_intermediate_wb_interface.unit wb
    );

    fp_intermediate_wb_interface div_wb();
    fp_intermediate_wb_interface sqrt_wb();

    ////////////////////////////////////////////////////
    //Implementation
    //Div/Sqrt with distinct issue
    //Shared writeback
    fp_div div (
        .args(div_inputs),
        .issue(div_issue),
        .wb(div_wb),
    .*);

    fp_sqrt sqrt (
        .args(sqrt_inputs),
        .issue(sqrt_issue),
        .wb(sqrt_wb),
    .*);

    //SQRT has higher priority on ties because of longer latency
    always_comb begin
        sqrt_wb.ack = wb.ack & sqrt_wb.done;
        div_wb.ack = wb.ack & ~sqrt_wb.done;

        if (sqrt_wb.done) begin
            wb.id = sqrt_wb.id;
            wb.done = 1;
            wb.rd = sqrt_wb.rd;
            wb.expo_overflow = sqrt_wb.expo_overflow;
            wb.fflags = sqrt_wb.fflags;
            wb.rm = sqrt_wb.rm;
            wb.carry = sqrt_wb.carry;
            wb.safe = sqrt_wb.safe;
            wb.hidden = sqrt_wb.hidden;
            //Collapse sticky - this saves a wide 2:1 mux
            wb.grs[GRS_WIDTH-1-:2] = sqrt_wb.grs[GRS_WIDTH-1-:2];
            wb.grs[GRS_WIDTH-3] = |sqrt_wb.grs[GRS_WIDTH-3:0];
            wb.grs[GRS_WIDTH-4:0] = '0;
            wb.clz = sqrt_wb.clz;
            wb.right_shift = sqrt_wb.right_shift;
            wb.right_shift_amt = sqrt_wb.right_shift_amt;
            wb.subnormal = sqrt_wb.subnormal;
            wb.ignore_max_expo = sqrt_wb.ignore_max_expo;
            wb.d2s = sqrt_wb.d2s;
        end else begin
            wb.id = div_wb.id;
            wb.done = div_wb.done;
            wb.rd = div_wb.rd;
            wb.expo_overflow = div_wb.expo_overflow;
            wb.fflags = div_wb.fflags;
            wb.rm = div_wb.rm;
            wb.carry = div_wb.carry;
            wb.safe = div_wb.safe;
            wb.hidden = div_wb.hidden;
            //Collapse sticky - this saves a wide 2:1 mux
            wb.grs[GRS_WIDTH-1-:3] = div_wb.grs[GRS_WIDTH-1-:3]; //Preserve MSB sticky because there can be a left shift of 1
            wb.grs[GRS_WIDTH-4] = |div_wb.grs[GRS_WIDTH-4:0];
            wb.grs[GRS_WIDTH-5:0] = '0;

            wb.right_shift = div_wb.right_shift;
            wb.right_shift_amt = div_wb.right_shift_amt;
            wb.subnormal = div_wb.subnormal;
            wb.ignore_max_expo = div_wb.ignore_max_expo;
            wb.d2s = div_wb.d2s;
        end
    end

endmodule


/*
 * Copyright © 2017 Eric Matthews,  Lesley Shannon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the 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.
 *
 * Initial code developed under the supervision of Dr. Lesley Shannon,
 * Reconfigurable Computing Lab, Simon Fraser University.
 *
 * Author(s):
 *             Eric Matthews <ematthew@sfu.ca>
 */

module itag_banks

    import cva5_config::*;
    import cva5_types::*;

    # (
        parameter cpu_config_t CONFIG = EXAMPLE_CONFIG,
        parameter derived_cache_config_t SCONFIG = '{default: 0}
    )

    (
        input logic clk,
        input logic rst,

        input logic[SCONFIG.LINE_ADDR_W-1:0] ifence_addr,

        input logic[SCONFIG.LINE_ADDR_W-1:0] stage1_line_addr,
        input logic[SCONFIG.LINE_ADDR_W-1:0] stage2_line_addr,
        input logic[SCONFIG.TAG_W-1:0] stage2_tag,

        input logic[CONFIG.ICACHE.WAYS-1:0] update_way,
        input logic update,

        input logic stage1_adv,

        output tag_hit,
        output logic[CONFIG.ICACHE.WAYS-1:0] tag_hit_way
        );

    //Valid + tag
    typedef logic [SCONFIG.TAG_W : 0] itag_entry_t;
    itag_entry_t[CONFIG.ICACHE.WAYS-1:0]  tag_line;

    logic hit_allowed;

    always_ff @ (posedge clk) begin
        if (rst)
            hit_allowed <= 0;
        else
            hit_allowed <= stage1_adv;
    end

    sdp_ram_padded #(
        .ADDR_WIDTH(SCONFIG.LINE_ADDR_W),
        .NUM_COL(CONFIG.ICACHE.WAYS),
        .COL_WIDTH(SCONFIG.TAG_W+1),
        .PIPELINE_DEPTH(0)
    ) itag_bank (
        .a_en(update | ifence),
        .a_wbe(update_way | {CONFIG.ICACHE.WAYS{ifence}}),
        .a_wdata({CONFIG.ICACHE.WAYS{~ifence, stage2_tag}}),
        .a_addr(ifence ? ifence_addr : stage2_line_addr),
        .b_en(stage1_adv),
        .b_addr(stage1_line_addr),
        .b_rdata(tag_line),
    .*);

    always_comb begin
        for (int i = 0; i < CONFIG.ICACHE.WAYS; i++)
            tag_hit_way[i] = ({hit_allowed, 1'b1, stage2_tag} == {1'b1, tag_line[i]});
    end

    assign tag_hit = |tag_hit_way;

endmodule


`ifndef TNOC_BFM_PACKET_VC_DRIVER_SVH
`define TNOC_BFM_PACKET_VC_DRIVER_SVH

typedef tue_driver #(
  .CONFIGURATION  (tnoc_bfm_configuration ),
  .STATUS         (tnoc_bfm_status        ),
  .REQ            (tnoc_bfm_packet_item   )
) tnoc_bfm_packet_vc_driver_base;

class tnoc_bfm_packet_vc_driver extends tnoc_bfm_component_base #(
  tnoc_bfm_packet_vc_driver_base
);
  int                   vc;
  tnoc_bfm_flit_vif     vif;
  tnoc_bfm_packet_item  packet_item;
  tnoc_bfm_flit_item    flit_items[$];

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    vif = configuration.tx_vif[vc];
  endfunction

  task run_phase(uvm_phase phase);
    forever @(vif.master_cb, negedge vif.i_rst_n) begin
      if (!vif.i_rst_n) begin
        do_reset();
      end
      else begin
        if (vif.monitor_cb.ack) begin
          finish_flit();
        end

        if (packet_item == null) begin
          uvm_wait_for_nba_region();
          if (seq_item_port.has_do_available()) begin
            get_packet_item();
          end
        end

        if (packet_item != null) begin
          drive_flit();
        end
      end
    end
  endtask

  task do_reset();
    vif.valid = '0;
    vif.flit  = '0;
    if (packet_item != null) begin
      finish_packet_item();
    end
  endtask

  task get_packet_item();
    seq_item_port.get_next_item(packet_item);
    void'(begin_tr(packet_item));
    flit_items.delete();
    packet_item.pack_flit_items(flit_items);
  endtask

  task drive_flit();
    if (flit_items[0].begin_event.is_off) begin
      void'(begin_child_tr(flit_items[0], packet_item.tr_handle));
      vif.master_cb.valid <= '1;
      vif.master_cb.flit  <= flit_items[0].get_flit();
    end
  endtask

  task finish_flit();
    vif.master_cb.valid <= '0;
    vif.master_cb.flit  <= '0;

    end_tr(flit_items[0]);
    void'(flit_items.pop_front());

    if (flit_items.size == 0) begin
      finish_packet_item();
    end
  endtask

  task finish_packet_item();
    end_tr(packet_item);
    packet_item = null;

  endtask

  `tue_component_default_constructor(tnoc_bfm_packet_vc_driver)
  `uvm_component_utils(tnoc_bfm_packet_vc_driver)
endclass
`endif


interface tnoc_axi_id_locker
  import  tnoc_axi_pkg::*;
#(
  parameter tnoc_axi_config AXI_CONFIG  = TNOC_DEFAULT_AXI_CONFIG
)(
  tnoc_axi_types  axi_types,
  input var logic i_clk,
  input var logic i_rst_n
);
  localparam  int TOTAL_ID  = 2**AXI_CONFIG.id_width;

  typedef axi_types.tnoc_axi_id tnoc_axi_id;

  logic [TOTAL_ID-1:0]  id_locked;
  logic                 lock_valid;
  tnoc_axi_id           lock_id;
  logic                 free_valid;
  tnoc_axi_id           free_id;

  function automatic logic is_locked(tnoc_axi_id id);
    return id_locked[id];
  endfunction

  function automatic void lock(logic valid, tnoc_axi_id id);
    lock_valid  = valid;
    lock_id     = id;
  endfunction

  function automatic void free(logic valid, tnoc_axi_id id);
    free_valid  = valid;
    free_id     = id;
  endfunction

  always_ff @(posedge i_clk, negedge i_rst_n) begin

      id_locked <= '0;
    end
    else begin
      if (free_valid) begin
        id_locked[free_id]  <= '0;
      end
      if (lock_valid) begin
        id_locked[lock_id]  <= '1;
      end
    end
  end

`ifndef SYNTHESIS
  int in_flight_id_count;
  always_comb begin
    in_flight_id_count  = $countones(id_locked);
  end
`endif
endinterface


// test calling prot_task_module
module prot_task_module_test();
    reg [1:0] a;
    reg [1:0] b;
    reg [1:0] out;

    prot_task_module prot_task_(
        .a(a),
        .b(b),
        .out(out)
    );

    initial begin
        a = 0;
        b = 1;
        #1;
        `assert(out == 1);

        a = 2;
        b = 1;
        #1;
        `assert(out == 3);

        a = 3;
        b = 1;
        #1;
        `assert(out == 0);


/*
Add two floats

for now, we use a pipeline, but we don't do the whole reg-writing thing

a, b, req, should be flip-flop inputs, change on clock tick

ack and out will be flip-flop outputs, change on clock tick
ack is set at same time as out, once result available
both will be available for a single clock tick, then we go back to idle

timing:

Propagation delay is between any pair of combinatorially connected
inputs and outputs, drawn from:
    - module inputs
    - module outputs,
    - flip-flop outputs (treated as inputs), and
    - flip-flop inputs (treated as outputs)

max propagation delay: 51.2 nand units
*/

`default_nettype none
module float_add_pipeline(
    input                         clk,
    input                         rst,
    input                         req,
    input [float_width - 1:0]     a,
    input [float_width - 1:0]     b,
    output reg [float_width -1:0] out,
    output reg                      ack
);
    reg                             a_sign;
    reg [float_exp_width - 1:0]     a_exp;
    reg [float_mant_width + 2:0]    a_mant; // [sign][overflow][extra one][stored mantissa]

    reg                             b_sign;
    reg [float_exp_width - 1:0]     b_exp;
    reg [float_mant_width + 2:0]    b_mant;

    reg [float_exp_width - 1:0]     new_exp;
    reg [float_mant_width + 2:0]    new_mant;

    reg [float_exp_width - 1:0]     exp_diff;

    reg [float_mant_width + 2:0]    new_mant_lookup[float_mant_width];
    reg [$clog2(float_mant_width) - 1:0]
                                    norm_shift;

    reg                             new_sign;

    reg [float_width -1:0]          n_out;
    reg                             n_ack;

    reg                             n_a_sign;
    reg [float_exp_width - 1:0]     n_a_exp;
    reg [float_mant_width + 2:0]    n_a_mant; // [sign][overflow][extra one][stored mantissa]

    reg                             n_b_sign;
    reg [float_exp_width - 1:0]     n_b_exp;
    reg [float_mant_width + 2:0]    n_b_mant;

    reg [float_exp_width - 1:0]     n_new_exp;
    reg [float_mant_width + 2:0]    n_new_mant;

    reg                             n_new_sign;

    typedef enum bit[1:0] {
        IDLE,
        S1,
        S2
    } e_state;
    reg [1:0] state;
    reg [1:0] n_state;

    // try single cycle first... see how long that takes
    always @(state, req) begin
        n_state = state;

        n_a_sign = a_sign;
        n_a_exp = a_exp;
        n_a_mant = a_mant;
        n_b_sign = b_sign;
        n_b_exp = b_exp;
        n_b_mant = b_mant;
        n_new_exp = new_exp;

        // $display("t=%0d float_add always(*) state=%0d", $time, state);
        `assert_known(state);
        case(state)
            IDLE: begin
                `assert_known(req);
                if(req) begin
                    // $display("float got req");
                    `assert_known(a);
                    `assert_known(b);

                    n_a_mant[float_mant_width + 2:float_mant_width] = 3'b001;
                    {n_a_sign, n_a_exp, n_a_mant[float_mant_width - 1:0]} = a;

                    n_b_mant[float_mant_width + 2:float_mant_width] = 3'b001;
                    {n_b_sign, n_b_exp, n_b_mant[float_mant_width - 1:0]} = b;

                    // this if...else block add 40 nand unit delay
                    // probalby from the subtraction
                    if(n_a_exp > n_b_exp) begin
                        n_new_exp = n_a_exp;
                        exp_diff = n_a_exp - n_b_exp;
                        n_b_mant = n_b_mant >> exp_diff;
                    end else begin
                        n_new_exp = n_b_exp;
                        exp_diff = n_b_exp - n_a_exp;
                        n_a_mant = n_a_mant >> exp_diff;
                    end
                    // $display(
                    //     "new_exp %0d a_mant %0d %b b_mant %0d %b",
                    //     n_new_exp, n_a_mant, n_a_mant, n_b_mant, n_b_mant);

                    n_state = S1;
                end
            end
            S1: begin
                // $display("float.S1");
                // this if-else block adds 56 nand units, from the subtraction and addition presumably
                `assert_known(n_a_sign);
                `assert_known(n_b_sign);
                if(n_a_sign != n_b_sign) begin
                    `assert_known(n_a_mant);
                    `assert_known(n_b_mant);
                    if(n_a_mant > n_b_mant) begin
                        // a bigger, sign comes from a,
                        // subract b from a
                        n_new_mant = n_a_mant - n_b_mant;
                        n_new_sign = n_a_sign;
                    end else begin
                        // b bigger
                        // take sign from a, subtract a from b
                        n_new_mant = n_b_mant - n_a_mant;
                        n_new_sign = n_b_sign;
                    end
                end else begin
                    n_new_sign = n_a_sign;
                    n_new_mant = n_a_mant + n_b_mant;
                end

                // $display("a        %b", n_a_mant);
                // $display("b        %b", n_b_mant);
                // $display("new_mant %b", n_new_mant);

                n_state = S2;
            end
            S2: begin
                // $display("float.S2");
                // this if adds 8 nand units delay
                `assert_known(n_new_mant);
                if(n_new_mant[float_mant_width + 1] == 1) begin
                    n_new_mant[float_mant_width + 1:0] = n_new_mant[float_mant_width + 1:0] >> 1;
                    n_new_exp = n_new_exp + 1;
                end
                // $display("new_mant %b", n_new_mant);
                norm_shift = 0;
                // this if-else adds 35 nand units delay, which is surprisingly small really...
                `assert_known(n_new_mant);
                if(|n_new_mant == 0) begin
                    // if eveyrthing is zero ,then just return zero
                    // $display("all zero");
                    n_new_exp = '0;
                    n_new_sign = 0;
                end else begin
                    // shift_int needs to be signed, oterhwise the condition shift_int >= 0 doesnt work
                    // but shift needs to have only $clog2(float_mant_width) bits, because that
                    // is the size of the index of new_mant_lookup
                    for(int shift_int = float_mant_width - 1; shift_int >= 0; shift_int--) begin
                        reg [$clog2(float_mant_width) - 1:0] shift;
                        shift = shift_int[$clog2(float_mant_width) - 1:0];
                        // $display("shift %0d new_mant[shift]=%0d", shift, n_new_mant[float_mant_width - shift]);
                        `assert_known(n_new_mant);
                        if(n_new_mant[float_mant_width - shift] == 1) begin
                            norm_shift = shift;
                            new_mant_lookup[shift] = n_new_mant << shift;
                            // $display("    new_mant_lookup[shift]=%b norm_shift=%0d", new_mant_lookup[shift], norm_shift);
                        end
                    end
                    n_new_mant = new_mant_lookup[norm_shift];
                    // norm_shift has fewer bits than n_new_exp
                    // - norm_shift is unsigned, so we can fill up the extra bits with zeros
                    // - n_new_exp is of width float_exp_width
                    // - norm_shift is of width $clog2(float_mant_width)
                    n_new_exp = n_new_exp - { {float_exp_width - $clog2(float_mant_width) {1'b0}}, norm_shift };
                end
                // $display("new_mant %b new_exp %0d", n_new_mant, n_new_exp);
                n_out = {n_new_sign, n_new_exp, n_new_mant[float_mant_width - 1:0]};
                n_ack = 1;
                n_state = IDLE;
            end
            default: begin
            end
        endcase

    end

    always @(posedge clk, negedge rst) begin
        if(~rst) begin
            state <= IDLE;

            out <= 0;
            ack <= 0;

            a_sign <= 0;
            a_exp <= '0;
            a_mant <= '0;

            b_sign <= 0;
            b_exp <= '0;
            b_mant <= '0;

            new_exp <= '0;
            new_mant <= '0;
            new_sign <= '0;
        end else begin
            // $display("float_add_pipeline not rst state=%0d", n_state);
            out <= n_out;
            ack <= n_ack;

            state <= n_state;

            a_sign <= n_a_sign;
            a_exp <= n_a_exp;
            a_mant <= n_a_mant;

            b_sign <= n_b_sign;
            b_exp <= n_b_exp;
            b_mant <= n_b_mant;

            new_exp <= n_new_exp;
            new_mant <= n_new_mant;
            new_sign <= n_new_sign;
        end
    end
endmodule

        n_new_mant = new_mant;
        n_new_sign = new_sign;

        n_out = '0;
        n_ack = 0;


// Copyright (c) 2020 University of Florida
//
// This program 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 <https://www.gnu.org/licenses/>.

// Greg Stitt
// University of Florida

// Module Name:  delay.sv
// Description:  Delays an input signal by a specified number of cycles.

//===================================================================
// Parameter Description
// CYCLES   : The number of cycles to delay the input. Must be positive.
// WIDTH    : The number of bits in the input and output signal being delayed.
// INIT_VAL : An initialization value to use on reset for each internal
//            register of the delay.
//===================================================================

//===================================================================
// Interface Description
// clk  : Clock input
// rst  : Reset (active high)
// en : Enable (active high), 0 stalls the delay
// data_in : Input to be delayed
// data_out : The input after CYCLES cycles pass (assuming no stalls from en=0)
//===================================================================

module delay
  #(
    parameter int 		CYCLES,
    parameter int 		WIDTH,
    parameter logic [WIDTH-1:0] INIT_VAL = 0
    )
   (
    input 		     clk,
    input logic 	     rst,
    input logic 	     en,
    input [WIDTH-1:0] 	     data_in,
    output logic [WIDTH-1:0] data_out
    );

	 logic [WIDTH-1:0] regs[CYCLES];

	 always_ff @(posedge clk or posedge rst) begin
	    if (rst) begin
	       for (int i=0; i < CYCLES; i++)
		 regs[i] <= INIT_VAL;
	    end
	    else begin
	       if (en) begin
		  regs[0] <= data_in;
		  for (int i=0; i < CYCLES-1; i++)
		    regs[i+1] <= regs[i];
	       end
	    end
	 end

	 assign data_out = regs[CYCLES-1];
      end
      else begin
	 initial
	   $error("Delay CYCLES parameter (%0d) must have positive value.", CYCLES);
      end
   endgenerate

endmodule

   generate
      if (CYCLES == 0) begin
	 assign data_out = data_in;


// ***************************************************************************
// Copyright (c) 2013-2016, Intel Corporation
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Intel Corporation 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 OWNER 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.
//
// Module Name :    ccip_std_afu
// Project :        ccip afu top
// Description :    This module instantiates CCI-P compliant AFU

// ***************************************************************************

`include "platform_if.vh"

module ccip_std_afu
  (
   // CCI-P Clocks and Resets
   input logic 	     pClk, // Primary CCI-P interface clock.
   input logic 	     pClkDiv2, // Aligned, pClk divided by 2.
   input logic 	     pClkDiv4, // Aligned, pClk divided by 4.
   input logic 	     uClk_usr, // User clock domain. Refer to clock programming guide.
   input logic 	     uClk_usrDiv2, // Aligned, user clock divided by 2.
   input logic 	     pck_cp2af_softReset, // CCI-P ACTIVE HIGH Soft Reset

   input logic [1:0] pck_cp2af_pwrState, // CCI-P AFU Power State
   input logic 	     pck_cp2af_error, // CCI-P Protocol Error Detected

   // CCI-P structures
   input 	     t_if_ccip_Rx pck_cp2af_sRx, // CCI-P Rx Port
   output 	     t_if_ccip_Tx pck_af2cp_sTx         // CCI-P Tx Port
   );

   // =============================================================
   // Register SR <--> PR signals at interface before consuming it
   // =============================================================

   (* noprune *) logic [1:0]  pck_cp2af_pwrState_T1;
   (* noprune *) logic        pck_cp2af_error_T1;

   logic 	     pck_cp2af_softReset_T1;
   t_if_ccip_Rx pck_cp2af_sRx_T1;
   t_if_ccip_Tx pck_af2cp_sTx_T0;

   // =============================================================
   // Register PR <--> PR signals near interface before consuming it
   // =============================================================

   ccip_interface_reg inst_green_ccip_interface_reg
     (
      .pClk                     (pClk),
      .pck_cp2af_softReset_T0   (pck_cp2af_softReset),
      .pck_cp2af_pwrState_T0    (pck_cp2af_pwrState),
      .pck_cp2af_error_T0       (pck_cp2af_error),
      .pck_cp2af_sRx_T0         (pck_cp2af_sRx),
      .pck_af2cp_sTx_T0         (pck_af2cp_sTx_T0),

      .pck_cp2af_softReset_T1   (pck_cp2af_softReset_T1),
      .pck_cp2af_pwrState_T1    (pck_cp2af_pwrState_T1),
      .pck_cp2af_error_T1       (pck_cp2af_error_T1),
      .pck_cp2af_sRx_T1         (pck_cp2af_sRx_T1),

     (
      .clk (pClk),
      .rst (pck_cp2af_softReset_T1),
      .rx  (pck_cp2af_sRx_T1),
      .tx  (pck_af2cp_sTx_T0)
      );

endmodule


// Copyright 2022 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Thomas Benz <tbenz@ethz.ch>

/// Optimal implementation of a stream FIFO based on the common cells modules.
/// Selects the smaller and faster spill register if the depth is 2 and the FIFO if
/// the depth is >2. Throws an error for the meaningless configurations depth 0 and 1.
module stream_fifo_optimal_wrap #(
    /// Depth can be arbitrary from 2 to 2**32
    parameter int unsigned Depth = 32'd8,
    /// Type of the FIFO
    parameter type type_t = logic,
    /// Print information when the simulation launches
    parameter bit PrintInfo = 1'b0,
    // DO NOT OVERWRITE THIS PARAMETER
    parameter int unsigned AddrDepth  = (Depth > 32'd1) ? $clog2(Depth) : 32'd1
) (
    input  logic                 clk_i,      // Clock
    input  logic                 rst_ni,     // Asynchronous reset active low
    input  logic                 flush_i,    // flush the fifo
    input  logic                 testmode_i, // test_mode to bypass clock gating
    output logic [AddrDepth-1:0] usage_o,    // fill pointer
    // input interface
    input  type_t                data_i,     // data to push into the fifo
    input  logic                 valid_i,    // input data valid
    output logic                 ready_o,    // fifo is not full
    // output interface
    output type_t                data_o,     // output data
    output logic                 valid_o,    // fifo is not empty
    input  logic                 ready_i     // pop head from fifo
);

    //--------------------------------------
    // Prevent Depth 0 and 1
    //--------------------------------------
    // Throw an error if depth is 0 or 1
    `ifndef SYNTHESIS
    if (Depth < 32'd2) begin : gen_fatal
        initial begin
            $fatal(1, "FIFO of depth %d does not make any sense!", Depth);
        end
    end
    `endif

    //--------------------------------------
    // Spill register (depth 2)
    //--------------------------------------
    // Instantiate a spill register for depth 2
    if (Depth == 32'd2) begin : gen_spill

        // print info
        `ifndef SYNTHESIS
        if (PrintInfo) begin : gen_info
            initial begin
                $display("[%m] Instantiate spill register (of depth %d)", Depth);
            end
        end
        `endif

        // spill register
        spill_register_flushable #(
            .T       ( type_t ),
            .Bypass  ( 1'b0   )
        ) i_spill_register_flushable (
            .clk_i,
            .rst_ni,
            .flush_i,
            .valid_i,
            .ready_o,
            .data_i,
            .valid_o,
            .ready_i,
            .data_o
        );

        // usage is not supported
        assign usage_o = 'x;
    end

    //--------------------------------------
    // FIFO register (depth 3+)
    //--------------------------------------
    // default to stream fifo

        // print info
        `ifndef SYNTHESIS
        if (PrintInfo) begin : gen_info
            initial begin
                $info("[%m] Instantiate stream FIFO of depth %d", Depth);
            end
        end
        `endif

        // stream fifo
        stream_fifo #(
            .DEPTH        ( Depth  ),
            .T            ( type_t )
        ) i_stream_fifo (
            .clk_i,
            .rst_ni,
            .flush_i,
            .testmode_i,
            .usage_o,
            .data_i,
            .valid_i,
            .ready_o,
            .data_o,
            .valid_o,
            .ready_i
        );
    end

endmodule : stream_fifo_optimal_wrap


// Copyright 2020 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.

// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>

/// Stream test package containing drivers and common definitions for the
/// stream interface.
package stream_test;

  class stream_driver #(
    /// Payload data type.
    parameter type payload_t = logic,
    /// Stimuli application time.
    parameter time TA = 2ns,
    /// Stimuli test time.
    parameter time TT = 8ns
  );
    virtual STREAM_DV #(
      .payload_t (payload_t)
    ) stream;

    function new (
      virtual STREAM_DV #(
        .payload_t (payload_t)
      ) stream
    );
      this.stream = stream;
    endfunction

    function void reset_in();
      stream.valid = 1'b0;
    endfunction

    function void reset_out();
      stream.ready = 1'b0;
    endfunction

    task automatic cycle_start;
      #TT;
    endtask

    task automatic cycle_end;
      @(posedge stream.clk_i);
    endtask

    /// Send a packet on the stream interface.
    task automatic send (input payload_t data);
      stream.data  <= #TA data;
      stream.valid <= #TA 1'b1;
      cycle_start();
      while (stream.ready != 1) begin cycle_end(); cycle_start(); end

      stream.valid <= #TA 1'b0;
    endtask

    /// Receive a packet on the stream interface.
    task automatic recv(output payload_t data);
      stream.ready <= #TA 1'b1;
      cycle_start();
      while (stream.valid != 1) begin cycle_end(); cycle_start(); end
      data = stream.data;
      cycle_end();
      stream.ready <= #TA 1'b0;
    endtask
  endclass
endpackage


parameter [3:0]
OP_ADD = 4'd0,			// 0
OP_SUB = (OP_ADD+4'd1),	// 1
OP_AND = (OP_SUB+4'd1), // 2
OP_OR  = (OP_AND+4'd1), // 3
/**
 * OP_CLR (4)
 * Uses OP_A as a mask, where each set bit
 * clears the corresponding bit in OP_B
 */
OP_CLR = (OP_OR+4'd1),	// 4
OP_EQ  = (OP_CLR+4'd1),	// 5
OP_NE  = (OP_EQ+4'd1),	// 6
OP_LT  = (OP_NE+4'd1),	// 7


/****************************************************************************
 * fwrisc_fetch_formal_seq32_test.sv
 ****************************************************************************/
`include "fwrisc_fetch_formal_defines.svh"

/**
 * Module: fwrisc_fetch_formal_seq_test
 *
 * TODO: Add module documentation
 */
module fwrisc_fetch_formal_seqmix_test(
		input				clock,
		input				reset,
		output reg[31:0]	next_pc,
		output reg			next_pc_seq,
		input[31:0]			iaddr,
		output reg[31:0]	idata,
		input				ivalid,
		output 				iready,
		input				fetch_valid,
		output				decode_complete,
		input[31:0]			instr,
		input				instr_c
		);

	// 00 - 32-bit instruction at aligned address
	// 01 - 32-bit instruction at unaligned address
	// 10 - 16-bit instruction at aligned address
	// 11 - 16-bit instruction at unaligned address
`ifdef FORMAL
	wire [2:0]		data_type = `anyconst;
//	reg [2:0]		data_type;
`else
	reg [1:0]		data_type;
`endif
//	reg [1:0]		data_type;

	// Instruction-memory unit
	// 'h63010000
	// 'h00376301
	// 'h00000037
	// 'h6301
	always @* begin
		case (data_type)
			2'b00: idata = 'h63010003; // aligned 32-bit instruction
			2'b01: idata = 'h00030000; // unaligned 32-bit instruction
			2'b10: idata = 'h00000000; // aligned 16-bit instruction
			default /*2'b11*/: idata = 'h00000003; // unaligned 16-bit instruction
		endcase

		case (data_type)
			2'b00: next_pc = 'h8000_0000;
			2'b01: next_pc = 'h8000_0002;
			2'b10: next_pc = 'h8000_0000;
			default /*2'b11*/: next_pc = 'h8000_0002;
		endcase

			fetch_count <= 0;
			iready_r <= 0;
		end else begin
			if (ivalid) begin
				iready_r <= ~iready_r;
			end
			if (iready && ivalid) begin
				fetch_count <= fetch_count + 1;
			end
//			`cover(fetch_count == 3);
		end
	end

	// Decode unit
	reg[1:0] decode_state = 0;
	reg[3:0] instr_count = 0;
	reg in_reset;
	// decode_ready signals that the instruction is complete
	assign decode_complete = (decode_state == 2'b11);
	always @(posedge clock) begin
		if (reset) begin
			next_pc_seq <= 1;
			decode_state <= 0;
			instr_count <= 0;
			in_reset <= 1;
		end else begin
			if (in_reset) begin
				in_reset <= 0;
`ifdef FORMAL
//				data_type <= data_type_w;
`else
				data_type <= `anyconst;
`endif
			end
			case (decode_state)
				2'b00: begin // wait for fetch to complete
					if (fetch_valid) begin
						decode_state <= 2'b01;
					end
				end
				2'b01: begin // instruction executing
					decode_state <= 2'b10;
				end

				2'b10: begin // instruction executing
					decode_state <= 2'b11;
				end

				2'b11: begin // ready for next instruction
					next_pc_seq <= 1;
					instr_count <= instr_count + 1;
					`cover(data_type == 2'b00);
					`cover(data_type == 2'b01);
					`cover(data_type == 2'b10);
					// Formal tool really doesn't like to cover this case for some reason
//					`cover(data_type == 2'b11);
//					data_type <= data_type + 1;
					decode_state <= 2'b00;
				end
			endcase
//			`cover (instr_count == 3);
		end
	end

endmodule

	end

	reg[3:0] fetch_count;
	reg iready_r;


//Copyright (C)2014-2021 Gowin Semiconductor Corporation.
//All rights reserved.
//File Title: IP file
//GOWIN Version: V1.9.8
//Part Number: GW1NR-LV9QN88PC6/I5
//Device: GW1NR-9C
//Created Time: Fri Nov 12 13:46:33 2021

module Gowin_CLKDIV (clkout, hclkin, resetn);

output clkout;
input hclkin;
input resetn;

wire gw_gnd;

assign gw_gnd = 1'b0;

CLKDIV clkdiv_inst (
    .CLKOUT(clkout),
    .HCLKIN(hclkin),
    .RESETN(resetn),
    .CALIB(gw_gnd)

);

defparam clkdiv_inst.DIV_MODE = "5";


/*
    top_lcd.sv - atarist on tang nano 20k toplevel

    This top level implements the lcd variant for 20k with
    FPGA Companion
*/

module top(
  input			clk,

  input			reset, // S2
  input			user, // S1

  output [5:0]	leds_n,
  output		ws2812,

  // spi flash interface
  output		mspi_cs,
  output		mspi_clk,
  inout			mspi_di,
  inout			mspi_hold,
  inout			mspi_wp,
  inout			mspi_do,

  // "Magic" port names that the gowin compiler connects to the on-chip SDRAM
  output		O_sdram_clk,
  output		O_sdram_cke,
  output		O_sdram_cs_n, // chip select
  output		O_sdram_cas_n,

 // columns address select
  output		O_sdram_ras_n, // row address select
  output		O_sdram_wen_n, // write enable
  inout [31:0]	IO_sdram_dq, // 32 bit bidirectional data bus
  output [10:0]	O_sdram_addr, // 11 bit multiplexed address bus
  output [1:0]	O_sdram_ba, // two banks
  output [3:0]	O_sdram_dqm, // 32/4

  // internal lcd
  output		lcd_dclk,
  output		lcd_hs, // lcd horizontal synchronization
  output		lcd_vs, // lcd vertical synchronization
  output		lcd_de, // lcd data enable
  output		lcd_bl, // lcd backlight control
  output [4:0]	lcd_r,  // lcd red
  output [5:0]	lcd_g,  // lcd green
  output [4:0]	lcd_b,  // lcd blue

  // SD card slot
  output		sd_clk,
  inout			sd_cmd, // MOSI
  inout [3:0]	sd_dat, // 0: MISO

  // SPI connection to on-board BL616 for 3921 assemblies
  // By default an external connection is used with a M0S Dock
  input                 spi_sclk,// in
  input                 spi_csn, // in
  output                spi_dir, // out
  input                 spi_dat, // in
  output                spi_irqn,// out

  // audio
  output		hp_bck,
  output		hp_ws,
  output		hp_din,
  output		pa_en
);

// the lcd variante does not need the 160MHz hdmi clock. Thus a pll
// generates the 32MHz directly
wire clk32;
wire pll_lock_32m;

pll_32m pll_32m (
    .clkout( clk32 ),
    .lock(pll_lock_32m),
    .clkin(clk)
);

// power on reset is returned by misterynano whenever all plls
// are up and running.
wire por;

wire r0, b0;  // lowest color bits to be left unconnected

wire [15:0] audio_stereo [2];

// sdram address can be up to 13 bits wide. tang nano 20k uses only 11
wire [13:0] sdram_addr;
assign O_sdram_addr = sdram_addr[10:0];

misterynano misterynano (
  .clk   ( clk ),    // 27 Mhz in (e.g. for further PLLs)
  .reset ( reset ),
  .user  ( user ),

  // clock and power on reset from system
  .clk32 ( clk32 ),

  .mspi_cs   ( mspi_cs   ),
  .mspi_clk  ( mspi_clk  ),
  .mspi_di   ( mspi_di   ),
  .mspi_hold ( mspi_hold ),
  .mspi_wp   ( mspi_wp   ),
  .mspi_do   ( mspi_do   ),

  // SDRAM 2Mbit*32 = 8 MByte
  .sdram_clk   ( O_sdram_clk    ),
  .sdram_cke   ( O_sdram_cke    ),
  .sdram_cs_n  ( O_sdram_cs_n   ), // chip select
  .sdram_cas_n ( O_sdram_cas_n  ), // columns address select
  .sdram_ras_n ( O_sdram_ras_n  ), // row address select
  .sdram_wen_n ( O_sdram_wen_n  ), // write enable
  .sdram_dq    ( IO_sdram_dq    ), // 32 bit bidirectional data bus
  .sdram_addr  ( sdram_addr     ), // 11 bit multiplexed address bus
  .sdram_ba    ( O_sdram_ba     ), // two banks
  .sdram_dqm   ( O_sdram_dqm    ), // 32/4

  // generic IO, used for mouse/joystick/...
  .io          ( 1'b11111111    ), // unused

  // mcu interface
  .mcu_sclk ( spi_sclk    ),
  .mcu_csn  ( spi_csn     ),
  .mcu_miso ( spi_dir     ), // from FPGA to MCU
  .mcu_mosi ( spi_dat     ), // from MCU to FPGA
  .mcu_intn ( spi_irqn    ),

  // parallel port and MIDI are unconnected

  // SD card slot
  .sd_clk ( sd_clk ),
  .sd_cmd ( sd_cmd ), // MOSI
  .sd_dat ( sd_dat ), // 0: MISO

  // scandoubled digital video to be
  // used with lcds
  .lcd_clk  ( lcd_dclk ),
  .lcd_hs_n ( lcd_hs   ),
  .lcd_vs_n ( lcd_vs   ),
  .lcd_de   ( lcd_de   ),       // LCD is RGB 565
  .lcd_r    ( { lcd_r, r0 } ),  // leave lcb unconnected
  .lcd_g    ( lcd_g    ),
  .lcd_b    ( { lcd_b, b0 } ),  // -"-

  // digital 16 bit audio output
  .audio ( audio_stereo )
);

// enable lcd backlight if cpu is out of reset
assign lcd_bl = !por;

/* ------------------- audio processing --------------- */

assign pa_en = !por;   // enable amplifier

reg clk_audio;
reg [7:0] aclk_cnt;
always @(posedge clk32) begin
    if(aclk_cnt < 32000000 / (24000*32) / 2 - 1)
        aclk_cnt <= aclk_cnt + 8'd1;
    else begin
        aclk_cnt <= 8'd0;
        clk_audio <= ~clk_audio;
    end
end

// mix both stereo channels into one mono channel
wire [15:0] audio_mixed = audio_stereo[0] + audio_stereo[1];

// count 32 bits
reg [15:0] audio;
reg [4:0] audio_bit_cnt;
always @(posedge clk_audio) begin
    if(por) audio_bit_cnt <= 5'd0;
    else    audio_bit_cnt <= audio_bit_cnt + 5'd1;

   // latch data so it's stable during transmission
   if(audio_bit_cnt == 5'd31)
	 audio <= 16'h8000 + audio_mixed;
end

// generate i2s signals
assign hp_bck = !clk_audio;
assign hp_ws = por?1'b0:audio_bit_cnt[4];
assign hp_din = por?1'b0:audio[15-audio_bit_cnt[3:0]];

endmodule

// To match emacs with gw_ide default
// Local Variables:
// tab-width: 4
// End:

  .pll_lock_main( pll_lock_32m ),
  .por   ( por ),

  .leds_n ( leds_n ),


// megafunction wizard: %Altera PLL v17.0%
// GENERATION: XML
// pll_audio.v

// Generated using ACDS version 17.0 602

`timescale 1 ps / 1 ps
module pll_audio (
		input  wire  refclk,   //  refclk.clk
		input  wire  rst,      //   reset.reset
		output wire  outclk_0  // outclk0.clk

		.outclk_0 (outclk_0), // outclk0.clk
		.locked   ()          // (terminated)
	);

endmodule
// Retrieval info: <?xml version="1.0"?>
//<!--
//	Generated by Altera MegaWizard Launcher Utility version 1.0
//	************************************************************
//	THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//	************************************************************
//	Copyright (C) 1991-2020 Altera Corporation
//	Any megafunction design, and related net list (encrypted or decrypted),
//	support information, device programming or simulation file, and any other
//	associated documentation or information provided by Altera or a partner
//	under Altera's Megafunction Partnership Program may be used only to
//	program PLD devices (but not masked PLD devices) from Altera.  Any other
//	use of such megafunction design, net list, support information, device
//	programming or simulation file, or any other related documentation or
//	information is prohibited for any other purpose, including, but not
//	limited to modification, reverse engineering, de-compiling, or use with
//	any other silicon devices, unless such use is explicitly licensed under
//	a separate agreement with Altera or a megafunction partner.  Title to
//	the intellectual property, including patents, copyrights, trademarks,
//	trade secrets, or maskworks, embodied in any such megafunction design,
//	net list, support information, device programming or simulation file, or
//	any other related documentation or information provided by Altera or a
//	megafunction partner, remains with Altera, the megafunction partner, or
//	their respective licensors.  No other licenses, including any licenses
//	needed under any third party's intellectual property, are provided herein.
//-->
// Retrieval info: <instance entity-name="altera_pll" version="17.0" >
// Retrieval info: 	<generic name="debug_print_output" value="false" />
// Retrieval info: 	<generic name="debug_use_rbc_taf_method" value="false" />
// Retrieval info: 	<generic name="device_family" value="Cyclone V" />
// Retrieval info: 	<generic name="device" value="5CEBA2F17A7" />
// Retrieval info: 	<generic name="gui_device_speed_grade" value="1" />
// Retrieval info: 	<generic name="gui_pll_mode" value="Fractional-N PLL" />
// Retrieval info: 	<generic name="gui_reference_clock_frequency" value="50.0" />
// Retrieval info: 	<generic name="gui_channel_spacing" value="0.0" />
// Retrieval info: 	<generic name="gui_operation_mode" value="direct" />
// Retrieval info: 	<generic name="gui_feedback_clock" value="Global Clock" />
// Retrieval info: 	<generic name="gui_fractional_cout" value="32" />
// Retrieval info: 	<generic name="gui_dsm_out_sel" value="1st_order" />
// Retrieval info: 	<generic name="gui_use_locked" value="false" />
// Retrieval info: 	<generic name="gui_en_adv_params" value="false" />
// Retrieval info: 	<generic name="gui_number_of_clocks" value="1" />
// Retrieval info: 	<generic name="gui_multiply_factor" value="1" />
// Retrieval info: 	<generic name="gui_frac_multiply_factor" value="1" />
// Retrieval info: 	<generic name="gui_divide_factor_n" value="1" />
// Retrieval info: 	<generic name="gui_cascade_counter0" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency0" value="24.576" />
// Retrieval info: 	<generic name="gui_divide_factor_c0" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency0" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units0" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift0" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg0" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift0" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle0" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter1" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency1" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c1" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency1" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units1" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift1" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg1" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift1" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle1" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter2" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency2" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c2" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency2" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units2" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift2" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg2" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift2" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle2" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter3" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency3" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c3" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency3" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units3" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift3" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg3" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift3" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle3" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter4" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency4" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c4" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency4" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units4" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift4" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg4" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift4" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle4" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter5" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency5" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c5" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency5" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units5" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift5" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg5" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift5" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle5" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter6" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency6" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c6" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency6" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units6" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift6" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg6" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift6" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle6" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter7" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency7" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c7" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency7" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units7" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift7" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg7" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift7" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle7" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter8" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency8" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c8" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency8" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units8" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift8" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg8" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift8" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle8" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter9" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency9" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c9" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency9" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units9" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift9" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg9" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift9" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle9" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter10" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency10" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c10" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency10" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units10" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift10" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg10" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift10" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle10" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter11" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency11" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c11" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency11" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units11" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift11" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg11" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift11" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle11" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter12" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency12" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c12" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency12" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units12" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift12" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg12" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift12" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle12" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter13" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency13" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c13" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency13" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units13" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift13" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg13" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift13" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle13" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter14" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency14" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c14" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency14" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units14" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift14" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg14" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift14" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle14" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter15" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency15" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c15" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency15" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units15" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift15" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg15" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift15" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle15" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter16" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency16" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c16" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency16" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units16" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift16" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg16" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift16" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle16" value="50" />
// Retrieval info: 	<generic name="gui_cascade_counter17" value="false" />
// Retrieval info: 	<generic name="gui_output_clock_frequency17" value="100.0" />
// Retrieval info: 	<generic name="gui_divide_factor_c17" value="1" />
// Retrieval info: 	<generic name="gui_actual_output_clock_frequency17" value="0 MHz" />
// Retrieval info: 	<generic name="gui_ps_units17" value="ps" />
// Retrieval info: 	<generic name="gui_phase_shift17" value="0" />
// Retrieval info: 	<generic name="gui_phase_shift_deg17" value="0.0" />
// Retrieval info: 	<generic name="gui_actual_phase_shift17" value="0" />
// Retrieval info: 	<generic name="gui_duty_cycle17" value="50" />
// Retrieval info: 	<generic name="gui_pll_auto_reset" value="On" />
// Retrieval info: 	<generic name="gui_pll_bandwidth_preset" value="Auto" />
// Retrieval info: 	<generic name="gui_en_reconf" value="false" />
// Retrieval info: 	<generic name="gui_en_dps_ports" value="false" />
// Retrieval info: 	<generic name="gui_en_phout_ports" value="false" />
// Retrieval info: 	<generic name="gui_phout_division" value="1" />
// Retrieval info: 	<generic name="gui_mif_generate" value="false" />
// Retrieval info: 	<generic name="gui_enable_mif_dps" value="false" />
// Retrieval info: 	<generic name="gui_dps_cntr" value="C0" />
// Retrieval info: 	<generic name="gui_dps_num" value="1" />
// Retrieval info: 	<generic name="gui_dps_dir" value="Positive" />
// Retrieval info: 	<generic name="gui_refclk_switch" value="false" />
// Retrieval info: 	<generic name="gui_refclk1_frequency" value="100.0" />
// Retrieval info: 	<generic name="gui_switchover_mode" value="Automatic Switchover" />
// Retrieval info: 	<generic name="gui_switchover_delay" value="0" />
// Retrieval info: 	<generic name="gui_active_clk" value="false" />
// Retrieval info: 	<generic name="gui_clk_bad" value="false" />
// Retrieval info: 	<generic name="gui_enable_cascade_out" value="false" />
// Retrieval info: 	<generic name="gui_cascade_outclk_index" value="0" />
// Retrieval info: 	<generic name="gui_enable_cascade_in" value="false" />
// Retrieval info: 	<generic name="gui_pll_cascading_mode" value="Create an adjpllin signal to connect with an upstream PLL" />
// Retrieval info: </instance>
// IPFS_FILES : pll_audio.vo
// RELATED_FILES: pll_audio.v, pll_audio_0002.v


`include "vunit_defines.svh"

module cp_tb;
  parameter r = 0;
  parameter q = 0;

  bench bench();

      cp_flags = {1'b1, 1'b0};
    end else begin
      cp_flags = {1'b0, 1'b1};
    end
  endfunction

  `TEST_SUITE begin

    `TEST_CASE("CP XH i should set flags") begin
      bench.initialize(12'hA4F); // CP XH, i

      bench.run_until_complete();
      #1;

      bench.assert_expected(bench.prev_pc + 1, bench.prev_a, bench.prev_b, bench.prev_x, bench.prev_y, bench.prev_sp);
      bench.assert_cycle_length(7);

      // F > 2
      bench.assert_carry(1);
      bench.assert_zero(0);
    end

    `TEST_CASE("CP XL i should set flags") begin
      bench.initialize(12'hA52); // CP XL, i

      bench.run_until_complete();
      #1;

      bench.assert_expected(bench.prev_pc + 1, bench.prev_a, bench.prev_b, bench.prev_x, bench.prev_y, bench.prev_sp);
      bench.assert_cycle_length(7);

      // 2 == 2
      bench.assert_carry(0);
      bench.assert_zero(1);
    end

    `TEST_CASE("CP YH i should set flags") begin
      bench.initialize(12'hA62); // CP YH, i

      bench.run_until_complete();
      #1;

      bench.assert_expected(bench.prev_pc + 1, bench.prev_a, bench.prev_b, bench.prev_x, bench.prev_y, bench.prev_sp);
      bench.assert_cycle_length(7);

      // 2 < 3
      bench.assert_carry(0);
      bench.assert_zero(0);
    end

    `TEST_CASE("CP YL i should set flags") begin
      bench.initialize(12'hA74); // CP YH, i

      bench.run_until_complete();
      #1;

      bench.assert_expected(bench.prev_pc + 1, bench.prev_a, bench.prev_b, bench.prev_x, bench.prev_y, bench.prev_sp);
      bench.assert_cycle_length(7);

      // 4 > 3
      bench.assert_carry(1);
      bench.assert_zero(0);
    end

    `TEST_CASE("GENr CP r i should set flags") begin
      reg [3:0] temp_a;
      reg carry;
      reg zero;

      bench.initialize(12'hDC4 | (r << 4)); // CP r, i

      bench.cpu_uut.core.regs.a = 4'h1;
      bench.cpu_uut.core.regs.b = 4'h8;
      bench.cpu_uut.core.regs.y = 12'h279;
      bench.cpu_uut.ram.memory[bench.cpu_uut.core.regs.x] = 4'h5;
      bench.cpu_uut.ram.memory[bench.cpu_uut.core.regs.y] = 4'hE;
      bench.update_prevs();

      temp_a = bench.get_r_value(r);

      {carry, zero} = cp_flags(temp_a, 4'h4);

      bench.run_until_complete();
      #1;

      bench.assert_expected(bench.prev_pc + 1, bench.prev_a, bench.prev_b, bench.prev_x, bench.prev_y, bench.prev_sp);
      bench.assert_cycle_length(7);

      bench.assert_ram(bench.cpu_uut.core.regs.x, 4'h5);
      bench.assert_ram(bench.cpu_uut.core.regs.y, 4'hE);

      bench.assert_carry(carry);
      bench.assert_zero(zero);
    end

    `TEST_CASE("GENrq CP r q should set flags") begin
      reg [3:0] temp_a;
      reg [3:0] temp_b;
      reg carry;
      reg zero;

      bench.initialize(12'hF00 | (r << 2) | q); // CP r, q

      bench.cpu_uut.core.regs.a = 4'h1;
      bench.cpu_uut.core.regs.b = 4'h8;
      bench.cpu_uut.core.regs.y = 12'h279;
      bench.cpu_uut.ram.memory[bench.cpu_uut.core.regs.x] = 4'h5;
      bench.cpu_uut.ram.memory[bench.cpu_uut.core.regs.y] = 4'hE;
      bench.update_prevs();

      temp_a = bench.get_r_value(r);
      temp_b = bench.get_r_value(q);

      {carry, zero} = cp_flags(temp_a, temp_b);

      bench.run_until_complete();
      #1;

      bench.assert_expected(bench.prev_pc + 1, bench.prev_a, bench.prev_b, bench.prev_x, bench.prev_y, bench.prev_sp);
      bench.assert_cycle_length(7);

      bench.assert_ram(bench.cpu_uut.core.regs.x, 4'h5);
      bench.assert_ram(bench.cpu_uut.core.regs.y, 4'hE);

      bench.assert_carry(carry);
      bench.assert_zero(zero);
    end
  end;

  // The watchdog macro is optional, but recommended. If present, it
  // must not be placed inside any initial or always-block.
  `WATCHDOG(1ns);
endmodule

  function [1:0] cp_flags(reg [3:0] a, reg [3:0] b);
    if (a > b) begin


// Copyright 2019 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.
//
// SPDX-License-Identifier: SHL-0.51

// Author: Stefan Mach <smach@iis.ee.ethz.ch>

module fpnew_rounding #(
  parameter int unsigned AbsWidth=2 // Width of the abolute value, without sign bit
) (
  // Input value
  input logic [AbsWidth-1:0]   abs_value_i,             // absolute value without sign

  input fpnew_pkg::roundmode_e rnd_mode_i,
  input logic                  effective_subtraction_i, // sign of inputs affects rounding of zeroes
  // Output value
  output logic [AbsWidth-1:0]  abs_rounded_o,           // absolute value without sign
  output logic                 sign_o,
  // Output classification
  output logic                 exact_zero_o             // output is an exact zero
);

  logic round_up; // Rounding decision

  // Take the rounding decision according to RISC-V spec
  // RoundMode | Mnemonic | Meaning
  // :--------:|:--------:|:-------
  //    000    |   RNE    | Round to Nearest, ties to Even
  //    001    |   RTZ    | Round towards Zero
  //    010    |   RDN    | Round Down (towards -\infty)
  //    011    |   RUP    | Round Up (towards \infty)
  //    100    |   RMM    | Round to Nearest, ties to Max Magnitude
  //    101    |   ROD    | Round towards odd (this mode is not define in RISC-V FP-SPEC)
  //  others   |          | *invalid*
  always_comb begin : rounding_decision
    unique case (rnd_mode_i)
      fpnew_pkg::RNE: // Decide accoring to round/sticky bits
        unique case (round_sticky_bits_i)
          2'b00,
          2'b01: round_up = 1'b0;           // < ulp/2 away, round down
          2'b10: round_up = abs_value_i[0]; // = ulp/2 away, round towards even result
          2'b11: round_up = 1'b1;           // > ulp/2 away, round up
          default: round_up = fpnew_pkg::DONT_CARE;
        endcase
      fpnew_pkg::RTZ: round_up = 1'b0; // always round down
      fpnew_pkg::RDN: round_up = (| round_sticky_bits_i) ? sign_i  : 1'b0; // to 0 if +, away if -
      fpnew_pkg::RUP: round_up = (| round_sticky_bits_i) ? ~sign_i : 1'b0; // to 0 if -, away if +
      fpnew_pkg::RMM: round_up = round_sticky_bits_i[1]; // round down if < ulp/2 away, else up
      fpnew_pkg::ROD: round_up = ~abs_value_i[0] & (| round_sticky_bits_i);
      default: round_up = fpnew_pkg::DONT_CARE; // propagate x
    endcase
  end

  // Perform the rounding, exponent change and overflow to inf happens automagically
  assign abs_rounded_o = abs_value_i + round_up;

  // True zero result is a zero result without dirty round/sticky bits
  assign exact_zero_o = (abs_value_i == '0) && (round_sticky_bits_i == '0);

  // In case of effective subtraction (thus signs of addition operands must have differed) and a
  // true zero result, the result sign is '-' in case of RDN and '+' for other modes.
  assign sign_o = (exact_zero_o && effective_subtraction_i)
                  ? (rnd_mode_i == fpnew_pkg::RDN)
                  : sign_i;

endmodule

  input logic                  sign_i,
  // Rounding information