blob: 95c143da520f0d3f9f59b64ea021abff2d26692b [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Supports packed and unpacked modes
// Uses FIFO timing on the control signals
// No masking or flush functions supported
// Timings - case where InW < OutW
// clk_i __|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__
// wvalid_i _____|~~~~|_____|~~~~|_____|~~~~|_____|~~~~|___________________
// wdata_i Val N |Val N+1 |Val N+2 |Val N+3 |-------------------
// wready_o ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|__________|~~~~~~~~
// rvalid_o ___________________________________________|~~~~~~~~~~|________
// rdata_o -------------------------------------------|Val |--------
// rready_i _________________________________________________|~~~~|________
// depth_o 0000000000|1111111111|2222222222|3333333333|4444444444|00000000
// Timings - case where InW > OutW
// clk_i __|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__
// wvalid_i _____|~~~~|____________________________________________________
// wdata_i -----|Val |----------------------------------------------------
// wready_o ~~~~~~~~~~|___________________________________________|~~~~~~~~
// rvalid_o __________|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|________
// rdata_o ----------|Val N |Val N+1 |Val N+2 |Val N+3 |--------
// rready_i ________________|~~~~|_____|~~~~|_____|~~~~|_____|~~~~|________
// depth_o 0000000000|4444444444|3333333333|2222222222|1111111111|00000000
// Timings - case where InW = OutW
// clk_i __|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__
// wvalid_i _____|~~~~|____________________________________________________
// wdata_i -----|Val |----------------------------------------------------
// wready_o ~~~~~~~~~~|__________|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// rvalid_o __________|~~~~~~~~~~|_________________________________________
// rdata_o ----------|Val |-----------------------------------------
// rready_i ________________|~~~~|_________________________________________
// depth_o 0000000000|1111111111|00000000000000000000000000000000000000000
`include "prim_assert.sv"
module prim_packer_fifo #(
parameter int InW = 32,
parameter int OutW = 8,
parameter bit ClearOnRead = 1'b1, // if == 1 always output 0 after read
// derived parameters
localparam int MaxW = (InW > OutW) ? InW : OutW,
localparam int MinW = (InW < OutW) ? InW : OutW,
localparam int DepthW = $clog2(MaxW/MinW)
) (
input logic clk_i ,
input logic rst_ni,
input logic clr_i,
input logic wvalid_i,
input logic [InW-1:0] wdata_i,
output logic wready_o,
output logic rvalid_o,
output logic [OutW-1:0] rdata_o,
input logic rready_i,
output logic [DepthW:0] depth_o
);
localparam int unsigned WidthRatio = MaxW / MinW;
localparam bit [DepthW:0] FullDepth = WidthRatio[DepthW:0];
// signals
logic load_data;
logic clear_data;
logic clear_status;
// flops
logic [DepthW:0] depth_q, depth_d;
logic [MaxW-1:0] data_q, data_d;
logic clr_q, clr_d;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
depth_q <= '0;
data_q <= '0;
clr_q <= 1'b1;
end else begin
depth_q <= depth_d;
data_q <= data_d;
clr_q <= clr_d;
end
end
// flop for handling reset case for clr
assign clr_d = clr_i;
assign depth_o = depth_q;
if (InW < OutW) begin : gen_pack_mode
logic [MaxW-1:0] wdata_shifted;
assign wdata_shifted = {{OutW - InW{1'b0}}, wdata_i} << (depth_q*InW);
assign clear_status = (rready_i && rvalid_o) || clr_q;
assign clear_data = (ClearOnRead && clear_status) || clr_q;
assign load_data = wvalid_i && wready_o;
assign depth_d = clear_status ? '0 :
load_data ? depth_q+1 :
depth_q;
assign data_d = clear_data ? '0 :
load_data ? (wdata_shifted | (depth_q == 0 ? '0 : data_q)) :
data_q;
// set outputs
assign wready_o = !(depth_q == FullDepth) && !clr_q;
assign rdata_o = data_q;
assign rvalid_o = (depth_q == FullDepth) && !clr_q;
end else begin : gen_unpack_mode
logic [MaxW-1:0] rdata_shifted;
logic pull_data;
logic [DepthW:0] ptr_q, ptr_d;
logic [DepthW:0] lsb_is_one;
logic [DepthW:0] max_value;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
ptr_q <= '0;
end else begin
ptr_q <= ptr_d;
end
end
assign lsb_is_one = {{DepthW{1'b0}},1'b1};
assign max_value = FullDepth;
assign rdata_shifted = data_q >> ptr_q*OutW;
assign clear_status = (rready_i && (depth_q == lsb_is_one)) || clr_q;
assign clear_data = (ClearOnRead && clear_status) || clr_q;
assign load_data = wvalid_i && wready_o;
assign pull_data = rvalid_o && rready_i;
assign depth_d = clear_status ? '0 :
load_data ? max_value :
pull_data ? depth_q-1 :
depth_q;
assign ptr_d = clear_status ? '0 :
pull_data ? ptr_q+1 :
ptr_q;
assign data_d = clear_data ? '0 :
load_data ? wdata_i :
data_q;
// set outputs
assign wready_o = (depth_q == '0) && !clr_q;
assign rdata_o = rdata_shifted[OutW-1:0];
assign rvalid_o = !(depth_q == '0) && !clr_q;
// Avoid possible lint errors in case InW > OutW.
if (InW > OutW) begin : gen_unused
logic [MaxW-MinW-1:0] unused_rdata_shifted;
assign unused_rdata_shifted = rdata_shifted[MaxW-1:MinW];
end
end
//////////////////////////////////////////////
// Assertions, Assumptions, and Coverpoints //
//////////////////////////////////////////////
// If not acked, valid_o should keep asserting
`ASSERT(ValidOPairedWithReadyI_A,
rvalid_o && !rready_i && !clr_i |=> rvalid_o)
// If output port doesn't accept the data, the data should be stable
`ASSERT(DataOStableWhenPending_A,
##1 rvalid_o && $past(rvalid_o)
&& !$past(rready_i) && !$past(clr_i) |-> $stable(rdata_o))
endmodule