Skip to content
Snippets Groups Projects
sras.sv 6.64 KiB
//Copyright (C) 2018 to present,
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 2.0 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-2.0. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Author: Florian Zaruba, ETH Zurich
// Date: 08.02.2018
// Migrated: Luis Vitorio Cargnini, IEEE
// Date: 09.06.2018

// segmented return address stack
module sras #(
    parameter int unsigned DEPTH = 2,
    parameter int unsigned SpecDepth = 16,
    parameter int unsigned SZ_OVF = 16
)(
    input  logic             clk_i,
    input  logic             rst_ni,
    input  logic             flush_i,
    input  logic             push_i,
    input  logic             pop_i,
    input  logic [riscv::VLEN-1:0]      data_i,
    input  logic             begin_spec_i,
    input  logic             valid_spec_i,
    input  logic             bad_spec_i,
    output ariane_pkg::ras_t data_o
);

    logic [$clog2(SpecDepth)-1:0] ptr_spec_d, ptr_spec_q;
    logic [$clog2(SpecDepth)-1:0] ptr_backup_d, ptr_backup_q;
    logic [SpecDepth-1:0][$clog2(DEPTH)-1:0] tos_d, tos_q;
    ariane_pkg::ras_t [SpecDepth-1:0][DEPTH-1:0] stack_d, stack_q;
    logic [SpecDepth-1:0][SZ_OVF-1:0] ovf_counter_d, ovf_counter_q;

    assign ptr_spec_d = (bad_spec_i) ? ptr_backup_q : (begin_spec_i) ? ptr_spec_q + 1'b1 : ptr_spec_q;
    assign ptr_backup_d = (valid_spec_i) ? ptr_backup_q + 1'b1 : ptr_backup_q;

    logic [$clog2(SpecDepth)-1:0] previous_tos_addr;
    logic [$clog2(DEPTH)-1:0] previous_tos, prev_plus_one, pp_plus_one, prev_minus_one;

    assign previous_tos_addr = (!bad_spec_i && begin_spec_i) ? ptr_spec_q : ptr_spec_d;
    assign previous_tos = tos_q[previous_tos_addr];

    assign prev_plus_one = previous_tos + 1'b1;
    assign pp_plus_one = tos_q[ptr_spec_q] + 1'b1;
    assign prev_minus_one = previous_tos - 1'b1;

    logic overflow;
    assign overflow = |ovf_counter_q[ptr_spec_q];

    always_comb begin
        tos_d = tos_q;
        ovf_counter_d = ovf_counter_q;

        if (flush_i) begin
            tos_d = '0;
            ovf_counter_d = '0;
        end else if (!bad_spec_i) begin
            if (push_i && !pop_i) begin
                if (overflow) begin
                    ovf_counter_d[ptr_spec_d] = ovf_counter_q[ptr_spec_q] + 1'b1;
                end else begin
                    if (prev_plus_one == '0 || pp_plus_one == '0) begin
                        ovf_counter_d[ptr_spec_d] = ovf_counter_q[ptr_spec_q] + 1'b1;
                        if (begin_spec_i) begin
                            ovf_counter_d[ptr_spec_q] = ovf_counter_q[ptr_spec_q] + 1'b1;
                        end
                    end else begin
                        tos_d[ptr_spec_d] = prev_plus_one;
                        if (begin_spec_i) begin
                            tos_d[ptr_spec_q] = pp_plus_one;
                        end
                    end
                end
            end else if (!push_i && pop_i) begin
                if (ovf_counter_q[ptr_spec_q] != '0) begin
                    ovf_counter_d[ptr_spec_d] = ovf_counter_q[ptr_spec_q] - 1'b1;
                end else begin
                    tos_d[ptr_spec_d] = prev_minus_one;
                end
            end else if (!bad_spec_i && begin_spec_i) begin
                tos_d[ptr_spec_d] = tos_q[ptr_spec_q];
                ovf_counter_d[ptr_spec_d] = ovf_counter_q[ptr_spec_q];
            end
        end
    end

    logic can_pop, can_push;
    assign can_pop = pop_i && ovf_counter_d[ptr_spec_q][SZ_OVF-1:1] == '0 && !bad_spec_i;
    assign can_push = push_i && ovf_counter_d[ptr_spec_q] == '0 && !bad_spec_i;

    assign data_o = stack_q[previous_tos_addr][previous_tos];

    ariane_pkg::ras_t to_push;
    assign to_push.ra = (can_push) ? data_i : 0;
    assign to_push.valid = can_push;

    ariane_pkg::ras_t [DEPTH-1:0] new_stack, prev_stack;

    always_comb begin
        new_stack = stack_q[ptr_spec_d];

        if (!bad_spec_i && begin_spec_i) begin
            new_stack = stack_q[ptr_spec_q];
        end

        if (can_pop) begin
            new_stack[previous_tos] = to_push;
        end

        if (can_push) begin
            new_stack[prev_plus_one] = to_push;
        end
    end

    always_comb begin
        prev_stack = stack_q[ptr_spec_q];

        if (can_push && begin_spec_i) begin
            prev_stack[pp_plus_one] = to_push;
        end
    end

    for (genvar i = 0; i < SpecDepth; i++) begin
        assign stack_d[i] = (flush_i) ? '0 :
                            (i == ptr_spec_d) ? new_stack :
                            (i == ptr_spec_q) ? prev_stack :
                            stack_q[i];
    end

    always_ff @(posedge clk_i or negedge rst_ni) begin
        if (~rst_ni) begin
            stack_q       <= '0;
            ptr_spec_q    <= '0;
            ptr_backup_q  <= '0;
            tos_q         <= '0;
            ovf_counter_q <= '0;
        end else begin
            stack_q       <= stack_d;
            ptr_spec_q    <= ptr_spec_d;
            ptr_backup_q  <= ptr_backup_d;
            tos_q         <= tos_d;
            ovf_counter_q <= ovf_counter_d;
        end
    end

    // pragma translate_off
    `ifndef VERILATOR
      initial begin
         assert (2 ** $clog2(SpecDepth) == SpecDepth) else $fatal(1,"[sras] SpecDepth is not a power of 2");
         assert (SpecDepth >= 2) else $fatal(1,"[sras] SpecDepth is lower than 2");
         assert (2 ** $clog2(DEPTH) == DEPTH) else $fatal(1,"[sras] DEPTH is not a power of 2");
      end

      // assert property (
      //   @(posedge clk_i) disable iff (!rst_ni) push_i |-> begin_spec_i)
      //     else $warning (1,"[sras] push_i & ~begin_spec_i");

      // assert property (
      //   @(posedge clk_i) disable iff (!rst_ni) (begin_spec_i & !(bad_spec_i)) |-> (ptr_spec_d != ptr_backup_d))
      //     else $warning (1,"[sras] speculation overflow");

      // assert property (
      //   @(posedge clk_i) disable iff (!rst_ni) (begin_spec_i & (!bad_spec_i) & (ptr_spec_d == ptr_backup_d)) |-> (~push_i))
      //     else $warning (1,"[sras] backup overwrite");

      // assert property (
      //   @(posedge clk_i) disable iff (!rst_ni) valid_spec_i |-> ((ptr_backup_q == ptr_spec_q) |-> (ptr_backup_d == ptr_spec_d)))
      //     else $fatal (1,"[sras] backup overtake");
    `endif
    // pragma translate_on
endmodule