| // 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 |