// Copyright (c) 2017-2019 Bluespec, Inc.  All Rights Reserved

package ByteLane;

// ================================================================
// Library for 'adjusting/unadjusting' bytes on a wider bus

// Example: a 64b bit bus can be regarded as 8 "byte lanes"
//     with each lane selected by the lower 3 bits of an address.
// Such a bus typically carries data of 1, 2, 4 or 8 bytes.
// For 1,2, and 4, different apps use one of two representations:
// - carry the data on contiguous lower-order bytes, with a separate
//     indication of how many bytes are being carried (1,2,4)
// - carry the data bytes on the lanes corresponding to the byte addresses,
//     along with a 'mask' indicating which bytes are valid:
//        Lane:    7 6 5 4 3 2 1 0
//                 x x x x D D D D    4-byte data, address lsbs = 3'b000
//                 D D D D x x x x    4-byte data, address lsbs = 3'b100
//
//                 x x x x x x D D    2-byte data, address lsbs = 3'b000
//                 x x x x D D x x    2-byte data, address lsbs = 3'b010
//                   ...
//                 D D x x x x x x    2-byte data, address lsbs = 3'b110
//
//                 x x x x x x x D    1-byte data, address lsbs = 3'b000
//                 x x x x x x D x    1-byte data, address lsbs = 3'b001
//                   ...
//                 D x x x x x x x    1-byte data, address lsbs = 3'b111
// Only 1 bit/lane is needed for the 'mask' (a.k.a. 'byte-enable' or 'strobe')

// ================================================================
// BSV library imports

import Vector :: *;

// ================================================================
// Expand each bit in 'strobe' to a byte, creating a full-width mask.

function Bit #(TMul #(n,8)) fn_strobe_to_mask (Bit #(n) strobe);
   function Bit #(8) fn_bit_j_to_byte_j (Integer j);
      return signExtend (strobe [j]);
   endfunction

   Vector #(n, Bit #(8)) v = genWith (fn_bit_j_to_byte_j);
   return pack (v);
endfunction

// ================================================================
// Update an n-byte word taking into account an n-bit strobe

function Bit# (TMul #(n,8)) fn_update_strobed_bytes (Bit# (TMul #(n,8)) old_data,
                                                     Bit# (TMul #(n,8)) new_data,
                                                     Bit #(n)           strobe);
   Bit# (TMul #(n,8)) mask = fn_strobe_to_mask (strobe);
   return ((old_data & (~ mask))  |  (new_data & mask));
endfunction

// ================================================================
// Lane-adjust a data word according to the byte-address and data width
// Args: addr:     only bottom few bits are relevant
//       width:    # of relevant bytes: 1, 2, 4, 8
//       data_in:  lsb-justified data
// Results: Bool err:    misaligned, or bad data_width
//          strobe:      bit vector showing which lanes are active
//          data_out:    data shifted to be in-lane

// ----------------
// 32b version

function Tuple3 #(Bool,                // 
                  Bit #(4),            // strobe
                  Bit #(32))           // lane-adjusted data
         fn_lane_adjust_32b (Bit #(32) addr, Bit #(3) dw, Bit #(32) data);

   Bit #(4) strobe = 0;
   Bool     err    = False;

   case (dw)
      1: case (addr [1:0])
            2'b00: begin strobe = 'b_0001;                      end
            2'b01: begin strobe = 'b_0010; data = (data << 8);  end
            2'b10: begin strobe = 'b_0100; data = (data << 16); end
            2'b11: begin strobe = 'b_1000; data = (data << 24); end
         endcase
      2: case (addr [1:0])
            2'b00: begin strobe = 'b_0011;                      end
            2'b10: begin strobe = 'b_1100; data = (data << 16); end
            default: err = True;
         endcase
      4: case (addr [1:0])
            2'b00: strobe = 'b_1111;
            default: err = True;
         endcase
      default: err = True;
   endcase
   return tuple3 (err, strobe, data);
endfunction

// ----------------
// 64b version

function Tuple3 #(Bool,                // err: misaligned, or bad data_width
                  Bit #(8),            // strobe
                  Bit #(64))           // lane-adjusted data
          fn_lane_adjust_64b (Bit #(64) addr, Bit #(4) dw, Bit #(64) data);

   Bit #(8) strobe = 0;
   Bool     err    = False;

   case (dw)
      1: case (addr [2:0])
            3'b000: begin strobe = 'b_0000_0001;                      end
            3'b001: begin strobe = 'b_0000_0010; data = (data <<  8); end
            3'b010: begin strobe = 'b_0000_0100; data = (data << 16); end
            3'b011: begin strobe = 'b_0000_1000; data = (data << 24); end
            3'b100: begin strobe = 'b_0001_0000; data = (data << 32); end
            3'b101: begin strobe = 'b_0010_0000; data = (data << 40); end
            3'b110: begin strobe = 'b_0100_0000; data = (data << 48); end
            3'b111: begin strobe = 'b_1000_0000; data = (data << 56); end
         endcase
      2: case (addr [2:0])
            3'b000: begin strobe = 'b_0000_0011;                      end
            3'b010: begin strobe = 'b_0000_1100; data = (data << 16); end
            3'b100: begin strobe = 'b_0011_0000; data = (data << 32); end
            3'b110: begin strobe = 'b_1100_0000; data = (data << 48); end
            default: err = True;
         endcase
      3: case (addr [2:0])
            3'b000: begin strobe = 'b_0000_1111;                      end
            3'b100: begin strobe = 'b_1111_0000; data = (data << 32); end
            default: err = True;
         endcase
      4: case (addr [2:0])
            3'b000: begin strobe = 'b_1111_1111; end
            default: err = True;
         endcase
      default: err = True;
   endcase
   return tuple3 (err, strobe, data);
endfunction

// ================================================================
// Lane-unadjust a data word according to the byte-address and data width

// ----------------
// 32b version

function Tuple2 #(Bool,                // err: misaligned, or bad data_width
                  Bit #(32))           // lane-unadjusted data
          fn_lane_unadjust_32b (Bit #(32) addr, Bit #(3) dw, Bit #(32) data);

   Bool     err    = False;

   case (dw)
      1: case (addr [1:0])
            2'b00: data = (data >>  0);
            2'b01: data = (data >>  8);
            2'b10: data = (data >> 16);
            2'b11: data = (data >> 24);
         endcase
      2: case (addr [1:0])
            2'b00: data = (data >>  0);
            2'b10: data = (data >> 16);
            default: err = True;
         endcase
      4: case (addr [1:0])
            2'b00: data = (data >>  0);
            default: err = True;
         endcase
      default: err = True;
   endcase
   return tuple2 (err, data);
endfunction

// ----------------
// 64b version

function Tuple2 #(Bool,                // err: misaligned, or bad data_width
                  Bit #(64))           // lane-unadjusted data
          fn_lane_unadjust_64b (Bit #(64) addr, Bit #(4) dw, Bit #(64) data);

   Bool     err    = False;

   case (dw)
      1: case (addr [2:0])
            3'b000: data = (data >>  0);
            3'b001: data = (data >>  8);
            3'b010: data = (data >> 16);
            3'b011: data = (data >> 24);
            3'b100: data = (data >> 32);
            3'b101: data = (data >> 40);
            3'b110: data = (data >> 48);
            3'b111: data = (data >> 56);
         endcase
      2: case (addr [2:0])
            3'b000: data = (data >>  0);
            3'b010: data = (data >> 16);
            3'b100: data = (data >> 32);
            3'b110: data = (data >> 48);
            default: err = True;
         endcase
      4: case (addr [2:0])
            3'b000: data = (data >>  0);
            3'b100: data = (data >> 32);
            default: err = True;
         endcase
      8: case (addr [2:0])
            3'b000: data = (data >>  0);
            default: err = True;
         endcase
      default: err = True;
   endcase
   return tuple2 (err, data);
endfunction

// ================================================================

endpackage
