blob: b7ad43708d12d43d5b960d5a08bca22baf78e280 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Combine InW data and write to OutW data if packed to full word or stop signal
`include "prim_assert.sv"
module prim_packer #(
parameter int InW = 32,
parameter int OutW = 32
) (
input clk_i ,
input rst_ni,
input valid_i,
input [InW-1:0] data_i,
input [InW-1:0] mask_i,
output ready_o,
output logic valid_o,
output logic [OutW-1:0] data_o,
output logic [OutW-1:0] mask_o,
input ready_i,
input flush_i, // If 1, send out remnant and clear state
output logic flush_done_o
);
localparam int Width = InW + OutW;
localparam int PtrW = $clog2(Width+1);
localparam int MaxW = (InW > OutW) ? InW : OutW;
logic valid_next, ready_next;
logic [MaxW-1:0] stored_data, stored_mask;
logic [Width-1:0] concat_data, concat_mask;
logic [Width-1:0] shiftl_data, shiftl_mask;
logic [PtrW-1:0] pos, pos_next; // Current write position
logic [$clog2(InW)-1:0] lod_idx; // result of Leading One Detector
logic [$clog2(InW+1)-1:0] inmask_ones; // Counting Ones for mask_i
logic ack_in, ack_out;
logic flush_ready; // flush_i is pulse, so only when the output is ready flush_ready assets
// Computing next position
always_comb begin
// counting mask_i ones
inmask_ones = '0;
for (int i = 0 ; i < InW ; i++) begin
inmask_ones = inmask_ones + mask_i[i];
end
end
assign pos_next = (valid_i) ? pos + PtrW'(inmask_ones) : pos; // pos always stays (% OutW)
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
pos <= '0;
end else if (flush_ready) begin
pos <= '0;
end else if (ack_out) begin
`ASSERT_I(pos_next_gte_outw_p, pos_next >= OutW)
pos <= pos_next - OutW;
end else if (ack_in) begin
pos <= pos_next;
end
end
// Leading one detector for mask_i
always_comb begin
lod_idx = 0;
for (int i = InW-1; i >= 0 ; i--) begin
if (mask_i[i] == 1'b1) begin
lod_idx = i;
end
end
end
assign ack_in = valid_i & ready_o;
assign ack_out = valid_o & ready_i;
// Data process
assign shiftl_data = (valid_i) ? Width'(data_i >> lod_idx) << pos : '0;
assign shiftl_mask = (valid_i) ? Width'(mask_i >> lod_idx) << pos : '0;
assign concat_data = {{(Width-MaxW){1'b0}}, stored_data & stored_mask} |
(shiftl_data & shiftl_mask);
assign concat_mask = {{(Width-MaxW){1'b0}}, stored_mask} | shiftl_mask;
logic [MaxW-1:0] stored_data_next, stored_mask_next;
if (InW >= OutW) begin : gen_stored_in
assign stored_data_next = concat_data[OutW+:InW];
assign stored_mask_next = concat_mask[OutW+:InW];
end else begin : gen_stored_out
assign stored_data_next = {{(OutW-InW){1'b0}}, concat_data[OutW+:InW]};
assign stored_mask_next = {{(OutW-InW){1'b0}}, concat_mask[OutW+:InW]};
end
// Store the data temporary if it doesn't exceed OutW
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
stored_data <= '0;
stored_mask <= '0;
end else if (flush_ready) begin
stored_data <= '0;
stored_mask <= '0;
end else if (ack_out) begin
stored_data <= stored_data_next;
stored_mask <= stored_mask_next;
end else if (ack_in) begin
// When the requested size is smaller than OutW or output isn't ready
// Assume when output isn't ready, the module holds the input request
stored_data <= concat_data[MaxW-1:0];
stored_mask <= concat_mask[MaxW-1:0];
end
end
// flush_ready handling
typedef enum logic {
FlushIdle,
FlushWait
} flush_st_e;
flush_st_e flush_st, flush_st_next;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
flush_st <= FlushIdle;
end else begin
flush_st <= flush_st_next;
end
end
always_comb begin
flush_st_next = FlushIdle;
flush_ready = 1'b0;
unique case (flush_st)
FlushIdle: begin
if (flush_i && !ready_i) begin
// Wait until hold released
flush_st_next = FlushWait;
flush_ready = 1'b0;
end else if (flush_i && ready_i) begin
// Can write right away!
flush_st_next = FlushIdle;
flush_ready = 1'b1;
end else begin
flush_st_next = FlushIdle;
end
end
FlushWait: begin
// TODO: Add timeout and force flush
if (ready_i) begin
// Ready to write
flush_st_next = FlushIdle;
flush_ready = 1'b1;
end else begin
// Wait ...
flush_st_next = FlushWait;
flush_ready = 1'b0;
end
end
default: begin
flush_st_next = FlushIdle;
flush_ready = 1'b0;
end
endcase
end
assign flush_done_o = flush_ready;
assign valid_next = (pos_next >= OutW) ? 1'b 1 : flush_ready & (pos != '0);
assign ready_next = ack_out ? 1'b1 : pos_next <= MaxW; // New `we` needs to be hold.
// Output request
assign valid_o = valid_next;
assign data_o = concat_data[OutW-1:0];
assign mask_o = concat_mask[OutW-1:0];
// ready_o
assign ready_o = ready_next;
// TODO: Implement Pipelined logic
// Need to change pos logic, mask&data calculation logic too
//////////////////////////////////////////////
// Assertions, Assumptions, and Coverpoints //
//////////////////////////////////////////////
// Assumption: mask_i should be contiguous ones
// e.g: 0011100 --> OK
// 0100011 --> Not OK
`ASSUME(ContiguousOnesMask_M,
valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2)
// Assume data pattern to reduce FPV test time
//`ASSUME_FPV(FpvDataWithin_M,
// data_i inside {'0, '1, 32'hDEAD_BEEF},
// clk_i, !rst_ni)
// Flush and Write Enable cannot be asserted same time
`ASSUME(ExFlushValid_M, flush_i |-> !valid_i)
// While in flush state, new request shouldn't come
`ASSUME(ValidIDeassertedOnFlush_M,
flush_st == FlushWait |-> $stable(valid_i))
// If not acked, input port keeps asserting valid and data
`ASSUME(DataIStable_M,
##1 valid_i && $past(valid_i) && !$past(ready_o)
|-> $stable(data_i) && $stable(mask_i))
`ASSUME(ValidIPairedWithReadyO_M,
valid_i && !ready_o |=> valid_i)
`ASSERT(FlushFollowedByDone_A,
##1 $rose(flush_i) && !flush_done_o |-> !flush_done_o [*0:$] ##1 flush_done_o)
// If not acked, valid_o should keep asserting
`ASSERT(ValidOPairedWidthReadyI_A,
valid_o && !ready_i |=> valid_o)
// If input mask + stored data is greater than output width, valid should be asserted
`ASSERT(ValidOAssertedForInputGTEOutW_A,
valid_i && (($countones(mask_i) + $countones(stored_mask)) >= OutW) |-> valid_o)
// If output port doesn't accept the data, the data should be stable
`ASSERT(DataOStableWhenPending_A,
##1 valid_o && $past(valid_o)
&& !$past(ready_i) |-> $stable(data_o))
// If input data & stored data are greater than OutW, remained should be stored
// TODO: Find out how the FPV time can be reduced.
//`ASSERT(ExcessiveDataStored_A,
// ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=>
// (($past(data_i) & $past(mask_i)) >>
// ($past(lod_idx)+OutW-$countones($past(stored_mask))))
// == stored_data,
// clk_i, !rst_ni)
`ASSERT(ExcessiveMaskStored_A,
ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=>
($past(mask_i) >>
($past(lod_idx)+OutW-$countones($past(stored_mask))))
== stored_mask)
endmodule