// DESCRIPTION: Test that slice assignment overflows are handled correctly,
// i.e. that if you assign to a slice such that some of the bits you assign to
// do not actually exist, that those bits get correctly discarded.
// Issue #2803 existed in a number number of different codepaths in
// verilated.h and V3Expand.cpp.  This test should cover all of these cases
// when run both with and without the -Ox flag to verilator.
//  - Select offset constant, insert IData into CData
//  - Select offset constant, insert IData into SData
//  - Select offset constant, insert IData into IData
//  - Select offset constant, insert QData into QData
//  - Select offset constant, insert IData into WData within a word
//  - Select offset constant, insert IData into WData crossing a word boundary
//  - Select offset constant, insert IData into WData whole word insertion
//  - Select offset constant, insert QData into WData
//  - Select offset constant, insert WData into WData, several whole words
//  - Select offset constant, insert WData into WData, starting at word-offset
//  - Select offset constant, insert WData into WData, all other cases
//  - Select offset is non-constant, destination is wide, bit-select width == 1
//  - Select offset is non-constant, destination is wide, bit-select width != 1
//  - Select offset is non-constant, destination is narrow
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2021 David Turner
// SPDX-License-Identifier: CC0-1.0

module t (
    input clk
);

  integer cyc = 0;
  // Non-constant offsets
  reg varoffset1;
  reg [6:0] varoffset2;
  reg [6:0] varoffset3;

  // Destinations for variable-offset assignments
  reg [69:0] dstwide1;
  reg [69:0] dstwide2;
  reg [1:0] dstnarrow;

  // Constant offsets
  reg [6:0] constoffset;

  // Destinations for constant-offset assignments
  reg [2:0] dst_cdata;
  reg [11:0] dst_sdata;
  reg [29:0] dst_idata;
  reg [59:0] dst_qdata;
  reg [69:0] dst_wdata1;  // assign idata within word
  reg [69:0] dst_wdata2;  // assign idata crossing word boundary
  reg [69:0] dst_wdata3;  // assign idata corresponding to whole word
  reg [69:0] dst_wdata4;  // assign qdata
  reg [69:0] dst_wdata5;  // assign wdata corresponding to several whole words
  reg [69:0] dst_wdata6;  // assign wdata starting at word-offset
  reg [69:0] dst_wdata7;  // assign wdata unaligned

  always @(*) begin
    // Non-constant select offset, destination narrow
    dstnarrow = 2'd0;
    dstnarrow[varoffset1+:2'd2] = 2'd2;

    // Non-constant select offset, destination wide, width == 1
    dstwide1 = 70'd0;
    dstwide1[varoffset2+:1'd1] = 1'd1;

    // Non-constant select offset, destination wide, width != 1
    dstwide2 = 70'd0;
    dstwide2[varoffset3+:2'd2] = 2'd2;

    // Constant offset, IData into CData
    constoffset = 7'd2;
    dst_cdata = 3'd0;
    dst_cdata[constoffset[0+:2]+:3'd3] = 3'd6;

    // Constant offset, IData into SData
    constoffset = 7'd11;
    dst_sdata = 12'd0;
    dst_sdata[constoffset[0+:4]+:2'd2] = 2'd2;

    // Constant offset, IData into IData
    constoffset = 7'd29;
    dst_idata = 30'd0;
    dst_idata[constoffset[0+:5]+:2'd2] = 2'd2;

    // Constant offset, QData into QData
    constoffset = 7'd59;
    dst_qdata = 60'd0;
    dst_qdata[constoffset[0+:6]+:2'd2] = 2'd2;

    // Constant offset, IData into WData within word
    constoffset = 7'd69;
    dst_wdata1 = 70'd0;
    dst_wdata1[constoffset+:2'd2] = 2'd2;

    // Constant offset, IData into WData crossing word boundary
    constoffset = 7'd61;
    dst_wdata2 = 70'd0;
    dst_wdata2[constoffset+:4'd10] = 10'd1 << 4'd9;

    // Constant offset, IData into WData replacing a whole word
    constoffset = 7'd64;
    dst_wdata3 = 70'd0;
    dst_wdata3[constoffset+:6'd32] = 32'd1 << 3'd6;

    // Constant offset, QData into WData
    constoffset = 7'd31;
    dst_wdata4 = 70'd0;
    dst_wdata4[constoffset+:7'd40] = 40'd1 << 7'd39;

    // Constant offset, WData into WData replacing whole words
    constoffset = 7'd32;
    dst_wdata5 = 70'd0;
    dst_wdata5[constoffset+:7'd64] = 64'd1 << 7'd38;

    // Constant offset, WData into WData offset word aligned
    constoffset = 7'd32;
    dst_wdata6 = 70'd0;
    dst_wdata6[constoffset+:7'd40] = 40'd1 << 7'd38;

    // Constant offset, WData into WData unaligned
    constoffset = 7'd1;
    dst_wdata7 = 70'd0;
    dst_wdata7[constoffset+:7'd70] = 70'd1 << 7'd69;
  end

  // Test loop
  always @(posedge clk) begin
    // State machine to avoid verilator constant-folding offset
    if (cyc == 0) begin
      // Initialisation
      varoffset1 <= 1'd0;
      varoffset2 <= 7'd0;
      varoffset3 <= 7'd0;
    end
    else if (cyc == 1) begin
      // Variable offsets set here to avoid verilator constant folding
      varoffset1 <= 1'd1;
      varoffset2 <= 7'd70;
      varoffset3 <= 7'd69;
    end
    else if (cyc == 2) begin
      // Check all destinations are 0
      $write("dstwide1   = %23d, downshifted = %23d\n", dstwide1, dstwide1 >> 1);
      $write("dstwide2   = %23d, downshifted = %23d\n", dstwide2, dstwide2 >> 1);
      $write("dstnarrow  = %23d, downshifted = %23d\n", dstnarrow, dstnarrow >> 1);
      $write("dst_cdata  = %23d, downshifted = %23d\n", dst_cdata, dst_cdata >> 1);
      $write("dst_sdata  = %23d, downshifted = %23d\n", dst_sdata, dst_sdata >> 1);
      $write("dst_idata  = %23d, downshifted = %23d\n", dst_idata, dst_idata >> 1);
      $write("dst_qdata  = %23d, downshifted = %23d\n", dst_qdata, dst_qdata >> 1);
      $write("dst_wdata1 = %23d, downshifted = %23d\n", dst_wdata1, dst_wdata1 >> 1);
      $write("dst_wdata2 = %23d, downshifted = %23d\n", dst_wdata2, dst_wdata2 >> 1);
      $write("dst_wdata3 = %23d, downshifted = %23d\n", dst_wdata3, dst_wdata3 >> 1);
      $write("dst_wdata4 = %23d, downshifted = %23d\n", dst_wdata4, dst_wdata4 >> 1);
      $write("dst_wdata5 = %23d, downshifted = %23d\n", dst_wdata5, dst_wdata5 >> 1);
      $write("dst_wdata6 = %23d, downshifted = %23d\n", dst_wdata6, dst_wdata6 >> 1);
      $write("dst_wdata7 = %23d, downshifted = %23d\n", dst_wdata7, dst_wdata7 >> 1);

      if (dstwide1 !== 70'd0 || (dstwide1 >> 1) !== 70'd0) $stop;
      if (dstwide2 !== 70'd0 || (dstwide2 >> 1) !== 70'd0) $stop;
      if (dstnarrow !== 2'd0 || (dstnarrow >> 1) !== 2'd0) $stop;
      if (dst_cdata !== 3'd0 || (dst_cdata >> 1) !== 3'd0) $stop;
      if (dst_sdata !== 12'd0 || (dst_sdata >> 1) !== 12'd0) $stop;
      if (dst_idata !== 30'd0 || (dst_idata >> 1) !== 30'd0) $stop;
      if (dst_qdata !== 60'd0 || (dst_qdata >> 1) !== 60'd0) $stop;
      if (dst_wdata1 !== 70'd0 || (dst_wdata1 >> 1) !== 70'd0) $stop;
      if (dst_wdata2 !== 70'd0 || (dst_wdata2 >> 1) !== 70'd0) $stop;
      if (dst_wdata3 !== 70'd0 || (dst_wdata3 >> 1) !== 70'd0) $stop;
      if (dst_wdata4 !== 70'd0 || (dst_wdata4 >> 1) !== 70'd0) $stop;
      if (dst_wdata5 !== 70'd0 || (dst_wdata5 >> 1) !== 70'd0) $stop;
      if (dst_wdata6 !== 70'd0 || (dst_wdata6 >> 1) !== 70'd0) $stop;
      if (dst_wdata7 !== 70'd0 || (dst_wdata7 >> 1) !== 70'd0) $stop;
    end
    else begin
      $write("*-* All Finished *-*\n");
      $finish;
    end

    cyc <= cyc + 1;
  end
endmodule
