blob: e860e2a16183cc16f85f59a55a7401eac97ba4af [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Simple stack parameterised on width and depth
*
* When a push and pop occur in the same cycle the pop is ordered before the push (so top_data_o
* reflects what was on top of the stack, which is retrieved by the pop, the push then immediately
* replaces this with a new piece of data). Internal checking is performed for full & empty
* conditions so a push on full/pop on empty is allowable, though meaningless. For a push on full
* the data will be dropped, for a pop no empty there is no valid data to pop. The exception is
* a combined push & pop on full, here the top is popped off and replaced with what is pushed, no
* data is dropped.
*/
module otbn_stack
import otbn_pkg::*;
#(
parameter int unsigned StackWidth = 32,
parameter int unsigned StackDepth = 4
) (
input clk_i,
input rst_ni,
output logic full_o, // Stack is full
input logic clear_i, // Clear all data
input logic push_i, // Push the data
input logic [StackWidth-1:0] push_data_i, // Data to push
input logic pop_i, // Pop top of the stack
output logic [StackWidth-1:0] top_data_o, // Data on top of the stack
output logic top_valid_o // Stack is non empty (`top_data_o` is valid)
);
localparam int unsigned StackDepthW = prim_util_pkg::vbits(StackDepth);
logic [StackWidth-1:0] stack_storage [StackDepth];
logic [StackDepthW:0] stack_wr_ptr_q, stack_wr_ptr_d;
logic [StackDepthW-1:0] stack_top_idx, stack_rd_idx, stack_wr_idx;
logic stack_empty;
logic stack_full;
logic stack_write;
logic stack_read;
assign stack_empty = stack_wr_ptr_q == '0;
assign stack_full = stack_wr_ptr_q == StackDepth[StackDepthW:0];
assign stack_write = push_i & (~full_o | pop_i);
assign stack_read = top_valid_o & pop_i;
assign stack_top_idx = stack_wr_ptr_q[StackDepthW-1:0];
assign stack_rd_idx = stack_top_idx - 1'b1;
assign stack_wr_idx = pop_i ? stack_rd_idx : stack_top_idx;
always_comb begin
stack_wr_ptr_d = stack_wr_ptr_q;
if (stack_write && !stack_read) begin
stack_wr_ptr_d = stack_wr_ptr_q + 1'b1;
end
if (!stack_write && stack_read) begin
stack_wr_ptr_d = stack_wr_ptr_q - 1'b1;
end
if (clear_i) begin
stack_wr_ptr_d = '0;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
stack_wr_ptr_q <= '0;
end else begin
stack_wr_ptr_q <= stack_wr_ptr_d;
end
end
always_ff @(posedge clk_i) begin
if (stack_write) begin
stack_storage[stack_wr_idx] <= push_data_i;
end
end
assign full_o = stack_full;
assign top_data_o = stack_storage[stack_rd_idx];
assign top_valid_o = ~stack_empty;
endmodule