| // 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. |
| * |
| * The read and write pointers and read and write signals are exposed as `stack_rd_idx_i, |
| * `stack_wr_idx_o`, `stack_write_o` and `stack_read_o`. `next_top_data_o` and `next_top_valid_o` |
| * provide the top_data_o and top_valid_o output that will be seen in the following cycle. This is |
| * to enable users to extend the stack in case where it's not a simple matter of adding extra data |
| * bits (e.g. where this is a prim_count instance per stack entry). |
| */ |
| module otbn_stack |
| import otbn_pkg::*; |
| #( |
| parameter int unsigned StackWidth = 32, |
| parameter int unsigned StackDepth = 4, |
| |
| localparam int unsigned StackDepthW = prim_util_pkg::vbits(StackDepth) |
| ) ( |
| input clk_i, |
| input rst_ni, |
| |
| output logic full_o, // Stack is full |
| |
| output logic cnt_err_o, // Stack counters are wrong |
| |
| 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) |
| |
| output logic [StackDepthW-1:0] stack_wr_idx_o, |
| output logic stack_write_o, |
| output logic [StackDepthW-1:0] stack_rd_idx_o, |
| output logic stack_read_o, |
| |
| output logic [StackWidth-1:0] next_top_data_o, |
| output logic next_top_valid_o |
| ); |
| logic [StackWidth-1:0] stack_storage [StackDepth]; |
| logic [StackDepthW:0] stack_wr_ptr; |
| logic [StackDepthW-1:0] stack_rd_idx, stack_wr_idx; |
| logic [StackDepthW:0] next_stack_wr_ptr; |
| logic [StackDepthW-1:0] next_stack_rd_idx; |
| logic cnt_err, cnt_err_d, cnt_err_q; |
| |
| logic stack_empty; |
| logic stack_full; |
| |
| logic stack_write; |
| logic stack_read; |
| |
| assign stack_empty = stack_wr_ptr == '0; |
| assign stack_full = stack_wr_ptr == StackDepth[StackDepthW:0]; |
| |
| assign stack_write = push_i & (~full_o | pop_i); |
| assign stack_read = top_valid_o & pop_i; |
| |
| assign stack_rd_idx = stack_wr_ptr[StackDepthW-1:0] - 1'b1; |
| assign stack_wr_idx = pop_i ? stack_rd_idx : stack_wr_ptr[StackDepthW-1:0]; |
| |
| // SEC_CM: STACK_WR_PTR.CTR.REDUN |
| prim_count #( |
| .Width (StackDepthW+1) |
| ) u_stack_wr_ptr ( |
| .clk_i, |
| .rst_ni, |
| .clr_i (clear_i), |
| .set_i (1'b0), |
| .set_cnt_i ('0), |
| .incr_en_i (stack_write), |
| .decr_en_i (stack_read), |
| .step_i ((StackDepthW+1)'(1'b1)), |
| .cnt_o (stack_wr_ptr), |
| .cnt_next_o (next_stack_wr_ptr), |
| .err_o (cnt_err) |
| ); |
| |
| assign cnt_err_d = cnt_err_q | cnt_err; |
| |
| prim_flop #( |
| .Width(1), |
| .ResetValue('0) |
| ) u_cnt_err_flop ( |
| .clk_i, |
| .rst_ni, |
| |
| .d_i(cnt_err_d), |
| .q_o(cnt_err_q) |
| ); |
| |
| assign cnt_err_o = cnt_err_d; |
| |
| 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; |
| |
| assign stack_wr_idx_o = stack_wr_idx; |
| assign stack_rd_idx_o = stack_rd_idx; |
| assign stack_write_o = stack_write; |
| assign stack_read_o = stack_read; |
| |
| assign next_stack_rd_idx = next_stack_wr_ptr[StackDepthW-1:0] - 1'b1; |
| |
| assign next_top_data_o = stack_write ? push_data_i : stack_storage[next_stack_rd_idx]; |
| assign next_top_valid_o = next_stack_wr_ptr != '0; |
| |
| endmodule |