hw/ip/prim/rtl/prim_packer.sv Cov: 97.3%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // Combine InW data and write to OutW data if packed to full word or stop signal
   6: 
   7: module prim_packer #(
   8:   parameter int InW  = 32,
   9:   parameter int OutW = 32
  10: ) (
  11:   input clk_i ,
  12:   input rst_ni,
  13: 
  14:   input                   valid_i,
  15:   input        [InW-1:0]  data_i,
  16:   input        [InW-1:0]  mask_i,
  17:   output                  ready_o,
  18: 
  19:   output logic            valid_o,
  20:   output logic [OutW-1:0] data_o,
  21:   output logic [OutW-1:0] mask_o,
  22:   input                   ready_i,
  23: 
  24:   input                   flush_i,  // If 1, send out remnant and clear state
  25:   output logic            flush_done_o
  26: );
  27: 
  28:   localparam int Width = InW + OutW;
  29:   localparam int PtrW = $clog2(Width+1);
  30:   localparam int MaxW = (InW > OutW) ? InW : OutW;
  31: 
  32:   logic valid_next, ready_next;
  33:   logic [MaxW-1:0]  stored_data, stored_mask;
  34:   logic [Width-1:0] concat_data, concat_mask;
  35:   logic [Width-1:0] shiftl_data, shiftl_mask;
  36: 
  37:   logic [PtrW-1:0]        pos, pos_next; // Current write position
  38:   logic [$clog2(InW)-1:0] lod_idx;       // result of Leading One Detector
  39:   logic [$clog2(InW+1)-1:0] inmask_ones;   // Counting Ones for mask_i
  40: 
  41:   logic ack_in, ack_out;
  42: 
  43:   logic flush_ready; // flush_i is pulse, so only when the output is ready flush_ready assets
  44: 
  45:   // Computing next position
  46:   always_comb begin
  47:     // counting mask_i ones
  48:     inmask_ones = '0;
  49:     for (int i = 0 ; i < InW ; i++) begin
  50:       inmask_ones = inmask_ones + mask_i[i];
  51:     end
  52:   end
  53: 
  54:   assign pos_next = (valid_i) ? pos + PtrW'(inmask_ones) : pos;  // pos always stays (% OutW)
  55: 
  56:   always_ff @(posedge clk_i or negedge rst_ni) begin
  57:     if (!rst_ni) begin
  58:       pos <= '0;
  59:     end else if (flush_ready) begin
  60:       pos <= '0;
  61:     end else if (ack_out) begin
  62:       `ASSERT_I(pos_next_gte_outw_p, pos_next >= OutW)
  63:       pos <= pos_next - OutW;
  64:     end else if (ack_in) begin
  65:       pos <= pos_next;
  66:     end
  67:   end
  68: 
  69:   // Leading one detector for mask_i
  70:   always_comb begin
  71:     lod_idx = 0;
  72:     for (int i = InW-1; i >= 0 ; i--) begin
  73:       if (mask_i[i] == 1'b1) begin
  74:         lod_idx = i;
  75:       end
  76:     end
  77:   end
  78: 
  79:   assign ack_in  = valid_i & ready_o;
  80:   assign ack_out = valid_o & ready_i;
  81: 
  82:   // Data process
  83:   assign shiftl_data = (valid_i) ? Width'(data_i >> lod_idx) << pos : '0;
  84:   assign shiftl_mask = (valid_i) ? Width'(mask_i >> lod_idx) << pos : '0;
  85:   assign concat_data = {{(Width-MaxW){1'b0}}, stored_data & stored_mask} |
  86:                        (shiftl_data & shiftl_mask);
  87:   assign concat_mask = {{(Width-MaxW){1'b0}}, stored_mask} | shiftl_mask;
  88: 
  89:   logic [MaxW-1:0] stored_data_next, stored_mask_next;
  90: 
  91:   if (InW >= OutW) begin : gen_stored_in
  92:     assign stored_data_next = concat_data[OutW+:InW];
  93:     assign stored_mask_next = concat_mask[OutW+:InW];
  94:   end else begin : gen_stored_out
  95:     assign stored_data_next = {{(OutW-InW){1'b0}}, concat_data[OutW+:InW]};
  96:     assign stored_mask_next = {{(OutW-InW){1'b0}}, concat_mask[OutW+:InW]};
  97:   end
  98: 
  99:   // Store the data temporary if it doesn't exceed OutW
 100:   always_ff @(posedge clk_i or negedge rst_ni) begin
 101:     if (!rst_ni) begin
 102:       stored_data <= '0;
 103:       stored_mask <= '0;
 104:     end else if (flush_ready) begin
 105:       stored_data <= '0;
 106:       stored_mask <= '0;
 107:     end else if (ack_out) begin
 108:       stored_data <= stored_data_next;
 109:       stored_mask <= stored_mask_next;
 110:     end else if (ack_in) begin
 111:       // When the requested size is smaller than OutW or output isn't ready
 112:       // Assume when output isn't ready, the module  holds the input request
 113:       stored_data <= concat_data[MaxW-1:0];
 114:       stored_mask <= concat_mask[MaxW-1:0];
 115:     end
 116:   end
 117: 
 118:   // flush_ready handling
 119:   typedef enum logic {
 120:     FlushIdle,
 121:     FlushWait
 122:   } flush_st_e;
 123:   flush_st_e flush_st, flush_st_next;
 124: 
 125:   always_ff @(posedge clk_i or negedge rst_ni) begin
 126:     if (!rst_ni) begin
 127:       flush_st <= FlushIdle;
 128:     end else begin
 129:       flush_st <= flush_st_next;
 130:     end
 131:   end
 132: 
 133:   always_comb begin
 134:     flush_st_next = FlushIdle;
 135: 
 136:     flush_ready = 1'b0;
 137: 
 138:     unique case (flush_st)
 139:       FlushIdle: begin
 140:         if (flush_i && !ready_i) begin
 141:           // Wait until hold released
 142:           flush_st_next = FlushWait;
 143: 
 144:           flush_ready = 1'b0;
 145:         end else if (flush_i && ready_i) begin
 146:           // Can write right away!
 147:           flush_st_next = FlushIdle;
 148: 
 149:           flush_ready = 1'b1;
 150:         end else begin
 151:           flush_st_next = FlushIdle;
 152:         end
 153:       end
 154: 
 155:       FlushWait: begin
 156:         // TODO: Add timeout and force flush
 157:         if (ready_i) begin
 158:           // Ready to write
 159:           flush_st_next = FlushIdle;
 160: 
 161:           flush_ready = 1'b1;
 162:         end else begin
 163:           // Wait ...
 164:           flush_st_next = FlushWait;
 165: 
 166:           flush_ready = 1'b0;
 167:         end
 168:       end
 169: 
 170:       default: begin
 171:         flush_st_next = FlushIdle;
 172: 
 173:         flush_ready = 1'b0;
 174:       end
 175:     endcase
 176:   end
 177: 
 178:   assign flush_done_o = flush_ready;
 179: 
 180:   assign valid_next = (pos_next >= OutW) ? 1'b 1 : flush_ready & (pos != '0);
 181:   assign ready_next = ack_out ? 1'b1 : pos_next <= MaxW; // New `we` needs to be hold.
 182: 
 183:   // Output request
 184:   assign valid_o = valid_next;
 185:   assign data_o  = concat_data[OutW-1:0];
 186:   assign mask_o  = concat_mask[OutW-1:0];
 187: 
 188:   // ready_o
 189:   assign ready_o = ready_next;
 190: 
 191:   // TODO: Implement Pipelined logic
 192:   //       Need to change pos logic, mask&data calculation logic too
 193: 
 194:   //////////////////////////////////////////////
 195:   // Assertions, Assumptions, and Coverpoints //
 196:   //////////////////////////////////////////////
 197:   // Assumption: mask_i should be contiguous ones
 198:   // e.g: 0011100 --> OK
 199:   //      0100011 --> Not OK
 200:   `ASSUME(ContiguousOnesMask_M,
 201:           valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2,
 202:           clk_i, !rst_ni)
 203: 
 204:   // Assume data pattern to reduce FPV test time
 205:   //`ASSUME_FPV(FpvDataWithin_M,
 206:   //            data_i inside {'0, '1, 32'hDEAD_BEEF},
 207:   //            clk_i, !rst_ni)
 208: 
 209:   // Flush and Write Enable cannot be asserted same time
 210:   `ASSUME(ExFlushValid_M, flush_i |-> !valid_i, clk_i, !rst_ni)
 211: 
 212:   // While in flush state, new request shouldn't come
 213:   `ASSUME(ValidIDeassertedOnFlush_M,
 214:           flush_st == FlushWait |-> $stable(valid_i),
 215:           clk_i, !rst_ni)
 216: 
 217:   // If not acked, input port keeps asserting valid and data
 218:   `ASSUME(DataIStable_M,
 219:           ##1 valid_i && $past(valid_i) && !$past(ready_o)
 220:           |-> $stable(data_i) && $stable(mask_i),
 221:           clk_i, !rst_ni)
 222:   `ASSUME(ValidIPairedWithReadyO_M,
 223:           valid_i && !ready_o |=> valid_i,
 224:           clk_i, !rst_ni)
 225: 
 226:   `ASSERT(FlushFollowedByDone_A,
 227:           ##1 $rose(flush_i) && !flush_done_o |-> !flush_done_o [*0:$] ##1 flush_done_o,
 228:           clk_i, !rst_ni)
 229: 
 230:   // If not acked, valid_o should keep asserting
 231:   `ASSERT(ValidOPairedWidthReadyI_A,
 232:           valid_o && !ready_i |=> valid_o,
 233:           clk_i, !rst_ni)
 234: 
 235:   // If input mask + stored data is greater than output width, valid should be asserted
 236:   `ASSERT(ValidOAssertedForInputGTEOutW_A,
 237:           valid_i && (($countones(mask_i) + $countones(stored_mask)) >= OutW) |-> valid_o,
 238:           clk_i, !rst_ni)
 239: 
 240:   // If output port doesn't accept the data, the data should be stable
 241:   `ASSERT(DataOStableWhenPending_A,
 242:           ##1 valid_o && $past(valid_o)
 243:           && !$past(ready_i) |-> $stable(data_o),
 244:           clk_i, !rst_ni)
 245: 
 246:   // If input data & stored data are greater than OutW, remained should be stored
 247:   // TODO: Find out how the FPV time can be reduced.
 248:   //`ASSERT(ExcessiveDataStored_A,
 249:   //        ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=>
 250:   //          (($past(data_i) &  $past(mask_i)) >>
 251:   //          ($past(lod_idx)+OutW-$countones($past(stored_mask))))
 252:   //          == stored_data,
 253:   //        clk_i, !rst_ni)
 254:   `ASSERT(ExcessiveMaskStored_A,
 255:           ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=>
 256:           ($past(mask_i) >>
 257:           ($past(lod_idx)+OutW-$countones($past(stored_mask))))
 258:             == stored_mask,
 259:           clk_i, !rst_ni)
 260: 
 261: endmodule
 262: