diff --git a/ibex_pkg.core b/ibex_pkg.core index 17f47e08c..68a512543 100644 --- a/ibex_pkg.core +++ b/ibex_pkg.core @@ -9,6 +9,7 @@ filesets: files_rtl: files: - rtl/ibex_pkg.sv + - rtl/ibex_cheriot_pkg.sv file_type: systemVerilogSource targets: diff --git a/ibex_top.core b/ibex_top.core index 82edbe133..0bb94c2e3 100644 --- a/ibex_top.core +++ b/ibex_top.core @@ -19,10 +19,12 @@ filesets: - lowrisc:prim:onehot_check - lowrisc:prim:onehot - lowrisc:prim:util + - pulp-platform:common_cells:common_cells files: - rtl/ibex_register_file_ff.sv # generic FF-based - rtl/ibex_register_file_fpga.sv # FPGA - rtl/ibex_register_file_latch.sv # ASIC + - rtl/ibex_trvk.sv - rtl/ibex_lockstep.sv - rtl/ibex_top.sv file_type: systemVerilogSource diff --git a/rtl/ibex_cheriot_pkg.sv b/rtl/ibex_cheriot_pkg.sv new file mode 100644 index 000000000..d944edf43 --- /dev/null +++ b/rtl/ibex_cheriot_pkg.sv @@ -0,0 +1,56 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package ibex_cheriot_pkg; + + parameter int unsigned TOP_W = 9; + parameter int unsigned BOT_W = 9; + parameter int unsigned EXP_W = 5; + parameter int unsigned OTYPE_W = 3; + parameter int unsigned CPERMS_W = 6; + + + // Obtain 32-bit representation of top + function automatic logic[32:0] get_bound33(logic [TOP_W-1:0] top, logic [1:0] cor, + logic [EXP_W-1:0] exponent, logic [31:0] addr); + logic [32:0] t1, t2, mask, cor_val; + + if (cor[1]) + // negative sign extension + cor_val = {33{cor[1]}}; + else + cor_val = {32'h0, (~cor[1]) & cor[0]}; + + cor_val = (cor_val << exponent) << TOP_W; + mask = (33'h1_ffff_ffff << exponent) << TOP_W; + + // apply correction and truncate + t1 = ({1'b0, addr} & mask) + cor_val; + // extend to 32 bit + t2 = {24'h0, top}; + t1 = t1 | (t2 << exponent); + + return t1; + endfunction + + + // Update the top/base correction for a cap + function automatic logic [2:0] update_temp_fields(logic [TOP_W-1:0] top, logic [BOT_W-1:0] base, + logic [BOT_W-1:0] addrmi); + logic top_hi, addr_hi; + logic [2:0] res3; + + top_hi = (top < base); + addr_hi = (addrmi < base); + + // top_cor + res3[2:1] = (top_hi == addr_hi)? 2'b00 : ((top_hi && (!addr_hi))? 2'b01 : 2'b11); + + // base_cor + res3[0] = (addr_hi) ? 1'b1 : 1'b0; + + return res3; + endfunction + +endpackage diff --git a/rtl/ibex_trvk.sv b/rtl/ibex_trvk.sv new file mode 100644 index 000000000..b02835718 --- /dev/null +++ b/rtl/ibex_trvk.sv @@ -0,0 +1,383 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module ibex_trvk #( + // The number of outstanding transaction the IP supports + parameter int unsigned NumOutstanding = 32'd4, + // The width of the meta memory address space used to store the revocation bits + parameter int unsigned RevBitmapAddrWidth = 32'd9, + // The base address of the meta SRAM holding the revocation bitmap + parameter int unsigned RevBitmapBaseAddr = 32'h0000_0000 +)( + input clk_i, + input rst_ni, + + // The base address of the (heap) memory where to be revocable capabilities point to + input logic [31:0] heap_base_addr_i, + + // Upstream port + input logic upstream_req_i, + output logic upstream_gnt_o, + output logic upstream_rvalid_o, + input logic upstream_we_i, + input logic [3:0] upstream_be_i, + input logic [31:0] upstream_addr_i, + input logic [31:0] upstream_wdata_i, + input logic [6:0] upstream_wdata_intg_i, + output logic [31:0] upstream_rdata_o, + output logic [6:0] upstream_rdata_intg_o, + output logic upstream_err_o, + input logic upstream_tag_i, + output logic upstream_tag_o, + + // Downstream port + output logic downstream_req_o, + input logic downstream_gnt_i, + input logic downstream_rvalid_i, + output logic downstream_we_o, + output logic [3:0] downstream_be_o, + output logic [31:0] downstream_addr_o, + output logic [31:0] downstream_wdata_o, + output logic [6:0] downstream_wdata_intg_o, + input logic [31:0] downstream_rdata_i, + input logic [6:0] downstream_rdata_intg_i, + input logic downstream_err_i, + output logic downstream_tag_o, + input logic downstream_tag_i, + + // Revocation bitmap memory port + output logic revbm_req_o, + input logic revbm_gnt_i, + input logic revbm_rvalid_i, + output logic [31:0] revbm_addr_o, + input logic [31:0] revbm_rdata_i, + input logic [6:0] revbm_rdata_intg_i, + input logic revbm_err_i, + + // Error signals + output logic revbm_data_intg_error_o, + output logic revbm_device_error_o +); + + + /////////// + // Types // + /////////// + + // Expanded exponent (5 bit). Exponent 'd15 stored in memory is mapped to 'd24. + typedef logic [ibex_cheriot_pkg::EXP_W-1:0] cheriot_exp_t; + typedef logic [ibex_cheriot_pkg::BOT_W-1:0] cheriot_bot_t; + typedef logic [ibex_cheriot_pkg::OTYPE_W-1:0] cheriot_otype_t; + typedef logic [ibex_cheriot_pkg::CPERMS_W-1:0] cheriot_cperms_t; + + // Revocation bitmap word address type + typedef logic [RevBitmapAddrWidth-1:0] revbm_addr_t; + + // Local capability metadata type to facilitate parsing of the fields. + typedef struct packed { + cheriot_exp_t exponent; + cheriot_bot_t base; + cheriot_otype_t otype; + cheriot_cperms_t cperms; + } cap_meta_t; + + // Local downstream response type + typedef struct packed { + logic [31:0] data; + logic [6:0] intg; + logic tag; + logic err; + } ds_rsp_t; + + + ///////////// + // Signals // + ///////////// + + // Signals connecting the request to the alignment store. + logic align_fork_valid; + logic align_fork_ready; + + // Alignment store output + logic align_out; + logic align_out_valid; + logic align_out_ready; + + // Pointer store signals + logic [31:0] ptr_store_q; + logic ptr_store_valid_q; + logic ptr_store_enable; + + // Downstream response store output + ds_rsp_t downstream_rsp_in; + ds_rsp_t downstream_rsp_out; + logic downstream_rsp_out_valid; + logic downstream_rsp_out_ready; + + // Base (address) calculation + cap_meta_t cap_meta; + logic unused_cap_meta; + logic is_sealing_cap; + logic [32:0] cap_base_33; + logic unused_cap_base_33; + logic [31:0] cap_base; + cheriot_bot_t addr_mid; + logic [ 2:0] cap_correction; + logic unused_cap_correction; + + // Revocation bitmap addressing + logic [31:0] revbm_cap_addr; + logic [31:0] revbm_bit_addr; + logic [ 4:0] revbm_bit_select; + revbm_addr_t revbm_addr; + logic revbm_out_of_range; + + // Revocation bitmap signals + logic revbm_req_required; + logic revbm_rsp_ready; + logic revbm_outstanding_q; + logic revbm_revoked; + + // Bitmap response ECC signals + logic [1:0] revbm_rsp_data_intg_error; + + + ////////////////////////////// + // Host to Device Intercept // + ////////////////////////////// + + // Both the device and the alignment store need to handshake for + // the stream to advance. + stream_fork #( + .N_OUP(32'd2) + ) u_stream_fork_h2d ( + .clk_i, + .rst_ni, + .valid_i(upstream_req_i), + .ready_o(upstream_gnt_o), + .valid_o({align_fork_valid, downstream_req_o}), + .ready_i({align_fork_ready, downstream_gnt_i}) + ); + + // The host and on lookup the revocation bitmap must handshake to advance the stream + // We use `revbm_req_required` as a section signal here. If there is a bitmap lookup required, + // this signal gets asserted beyond the request handshake completion until the response arrives. + stream_join_dynamic #( + .N_INP(32'd2) + ) u_stream_join_dynamic_d2h ( + .inp_valid_i({revbm_rvalid_i, downstream_rsp_out_valid}), + .inp_ready_o({revbm_rsp_ready, downstream_rsp_out_ready}), + .sel_i ({revbm_req_required, 1'b1 }), + .oup_valid_o(upstream_rvalid_o), + .oup_ready_i(1'b1) + ); + + // Forward OBI payload between host and device + assign downstream_we_o = upstream_we_i; + assign downstream_be_o = upstream_be_i; + assign downstream_addr_o = upstream_addr_i; + assign downstream_wdata_o = upstream_wdata_i; + assign downstream_wdata_intg_o = upstream_wdata_intg_i; + assign upstream_rdata_o = downstream_rsp_out.data; + assign upstream_rdata_intg_o = downstream_rsp_out.intg; + assign upstream_err_o = downstream_rsp_out.err; + + // Forward host to device CHERIoT tag w/o changes + assign downstream_tag_o = upstream_tag_i; + + // Tag handling; always return tag except if we do a revocation bitmap lookup + assign upstream_tag_o = revbm_rvalid_i ? !revbm_revoked : downstream_rsp_out.tag; + + // 64-bit alignment store + prim_fifo_sync #( + .Width(32'd1), + .Pass(1'b0), + .Depth(NumOutstanding), + .NeverClears(1'b1), + .Secure(1'b0) + ) u_prim_fifo_sync_align ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(align_fork_valid), + .wready_o(align_fork_ready), + .wdata_i (upstream_addr_i[2]), + .rvalid_o(align_out_valid), + .rready_i(align_out_ready), + .rdata_o (align_out), + .full_o (), + .depth_o (), + .err_o () + ); + + // Element is consumed, if host handshakes response + assign align_out_ready = upstream_rvalid_o; + + + /////////////////// + // Pointer Store // + /////////////////// + + // Pointer buffer is filled iff tag valid & 64-bit aligned + assign ptr_store_enable = downstream_rsp_out.tag && !align_out && align_out_valid && + align_out_ready; + + // Pointer store + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_pointer_store + if(!rst_ni) begin + ptr_store_q <= '0; + end else begin + if (ptr_store_enable) begin + ptr_store_q <= downstream_rsp_out.data; + end + end + end + + // Pointer valid store + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_pointer_vaild_store + if(!rst_ni) begin + ptr_store_valid_q <= 1'b0; + end else begin + if (align_out_ready) begin + ptr_store_valid_q <= ptr_store_enable; + end + end + end + + + ////////////////////////////////// + // Downstream Response Latching // + ////////////////////////////////// + + // downstream response store + prim_fifo_sync #( + .Width($bits(ds_rsp_t)), + .Pass(1'b1), + .Depth(NumOutstanding), + .NeverClears(1'b1), + .Secure(1'b0) + ) u_prim_fifo_ds_rsp_store ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(downstream_rvalid_i), + .wready_o(), // Not connected as OBI is not configured to use rready + .wdata_i (downstream_rsp_in), + .rvalid_o(downstream_rsp_out_valid), + .rready_i(downstream_rsp_out_ready), + .rdata_o (downstream_rsp_out), + .full_o (), + .depth_o (), + .err_o () + ); + + assign downstream_rsp_in = '{ + data: downstream_rdata_i, + intg: downstream_rdata_intg_i, + tag: downstream_tag_i, + err: downstream_err_i + }; + + + //////////////////////////////// + // Bitmap Address Calculation // + //////////////////////////////// + + // Exponent is expanded (page 70, CHERIoT Architecture specification, Version 1.0) + assign cap_meta = '{ + base: downstream_rsp_out.data[8:0], + exponent: downstream_rsp_out.data[21:18] != 4'hf ? {1'b0, downstream_rsp_out.data[21:18]} : + 5'd24, + otype: downstream_rsp_out.data[24:22], + cperms: downstream_rsp_out.data[30:25] + }; + + // Not all fields are used + assign unused_cap_meta = ^{cap_meta.otype, cap_meta.cperms[5]}; + + // Check if cap is sealing cap + assign is_sealing_cap = (cap_meta.cperms[4:3] == 2'b00) && (|cap_meta.cperms[2:0]); + + // Extract the middle field from the pointer, bounds depend on exponent, width fixed + assign addr_mid = ptr_store_q[cap_meta.exponent +: ibex_cheriot_pkg::BOT_W]; + + // Fetch the correction values, we are only interested in the base correction value (1 bit) + // top-related inputs are set to zero, top-related outputs ignored + assign cap_correction = ibex_cheriot_pkg::update_temp_fields('0, cap_meta.base, addr_mid); + + // Calculate the base address of the capability as a 33-bit value + assign cap_base_33 = ibex_cheriot_pkg::get_bound33(cap_meta.base, {2{cap_correction[0]}}, + cap_meta.exponent, ptr_store_q); + + // We don't need the correction bits corresponding to the top address + assign unused_cap_correction = ^cap_correction[2:1]; + + // The MSB is unused in our case + assign {unused_cap_base_33, cap_base} = cap_base_33; + + // Address in the revocation bitmap + assign revbm_cap_addr = cap_base - heap_base_addr_i; + + // Bit address in the revocation bitmap (every bit corresponds to one 64-bit capability) + assign revbm_bit_addr = revbm_cap_addr >> 32'd3; + + // Word address of the revocation bitmap + assign revbm_addr = revbm_bit_addr[RevBitmapAddrWidth+5-1:5]; + + // Bit select + assign revbm_bit_select = revbm_bit_addr[4:0]; + + // Capability base is outside of the bitmap range + assign revbm_out_of_range = |(revbm_bit_addr[31:RevBitmapAddrWidth+5]); + + + ////////////////////// + // Bitmap Interface // + ////////////////////// + + // We have loaded valid capability pointer, now we see valid metadata, not a sealing cap, + // and are pointing into the revocation bitmap + assign revbm_req_required = !is_sealing_cap && // Not sealing cap + ptr_store_valid_q && // The base pointer stored is valid + downstream_rsp_out.tag && // We are looking at a capability + downstream_rsp_out_valid && // The stored response is valid + align_out && // We are on the second word of the cap + align_out_valid && // The alignment store is valid + !revbm_out_of_range; // We hit the heap range + + // Assemble read-only request + assign revbm_req_o = revbm_req_required && !revbm_outstanding_q; + assign revbm_addr_o = RevBitmapBaseAddr + {{32 - RevBitmapAddrWidth{1'b0}}, revbm_addr}; + + // Is the current capability marked as revoked? + assign revbm_revoked = revbm_rdata_i[revbm_bit_select] || revbm_err_i; + + // Did we receive a device error? + assign revbm_device_error_o = revbm_rvalid_i && revbm_err_i; + + // Check the bitmap response data integrity + prim_secded_inv_39_32_dec u_prim_secded_inv_39_32_dec_bm_rsp_data ( + .data_i ({revbm_rdata_intg_i, revbm_rdata_i}), + .data_o (), + .syndrome_o(), + .err_o (revbm_rsp_data_intg_error) + ); + + // Mask response integrity error if response is not being handshaked + assign revbm_data_intg_error_o = revbm_rvalid_i && (|revbm_rsp_data_intg_error); + + // One outstanding request + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_rev_req_store + if(!rst_ni) begin + revbm_outstanding_q <= 1'b0; + end else begin + if (revbm_rvalid_i && revbm_rsp_ready) begin + revbm_outstanding_q <= 1'b0; + end else if (revbm_req_o && revbm_gnt_i) begin + revbm_outstanding_q <= 1'b1; + end + end + end + +endmodule diff --git a/vendor/patches/pulp_common_cells/stream_fork_dynamic_sv/0001-replace-assertions.patch b/vendor/patches/pulp_common_cells/stream_fork_dynamic_sv/0001-replace-assertions.patch new file mode 100644 index 000000000..6d6b7533d --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_fork_dynamic_sv/0001-replace-assertions.patch @@ -0,0 +1,23 @@ +diff --git a/stream_fork_dynamic.sv b/stream_fork_dynamic.sv +index 8da5313d..1aa546b4 100644 +--- a/stream_fork_dynamic.sv ++++ b/stream_fork_dynamic.sv +@@ -11,7 +11,7 @@ + // Authors: + // - Andreas Kurth + +-`include "common_cells/assertions.svh" ++`include "prim_assert.sv" + + /// Dynamic stream fork: Connects the input stream (ready-valid) handshake to a combination of output + /// stream handshake. The combination is determined dynamically through another stream, which +@@ -87,7 +87,6 @@ module stream_fork_dynamic #( + .ready_i ( int_oup_ready ) + ); + +-`ifndef COMMON_CELLS_ASSERTS_OFF +- `ASSERT_INIT(n_oup_0, N_OUP >= 1, "N_OUP must be at least 1!") +-`endif ++ `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) ++ + endmodule diff --git a/vendor/patches/pulp_common_cells/stream_fork_sv/0001-replace-assertions.patch b/vendor/patches/pulp_common_cells/stream_fork_sv/0001-replace-assertions.patch new file mode 100644 index 000000000..408c39eca --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_fork_sv/0001-replace-assertions.patch @@ -0,0 +1,23 @@ +diff --git a/stream_fork.sv b/stream_fork.sv +index ebe65f9b..7ae27c1a 100644 +--- a/stream_fork.sv ++++ b/stream_fork.sv +@@ -16,7 +16,7 @@ + // This module has no data ports because stream data does not need to be forked: the data of the + // input stream can just be applied at all output streams. + +-`include "common_cells/assertions.svh" ++`include "prim_assert.sv" + + module stream_fork #( + parameter int unsigned N_OUP = 0 // Synopsys DC requires a default value for parameters. +@@ -124,8 +124,6 @@ module stream_fork #( + assign all_ones = '1; // Synthesis fix for Vivado, which does not correctly compute the width + // of the '1 literal when assigned to a port of parametrized width. + +-`ifndef COMMON_CELLS_ASSERTS_OFF +- `ASSERT_INIT(n_oup_0, N_OUP >= 1, "Number of outputs must be at least 1!") +-`endif ++ `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) + + endmodule diff --git a/vendor/patches/pulp_common_cells/stream_fork_sv/0002-rename-wait-state.patch b/vendor/patches/pulp_common_cells/stream_fork_sv/0002-rename-wait-state.patch new file mode 100644 index 000000000..b0e07fa66 --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_fork_sv/0002-rename-wait-state.patch @@ -0,0 +1,46 @@ +diff --git a/stream_fork.sv b/stream_fork.sv +index 7ae27c1a06..e27b6e4549 100644 +--- a/stream_fork.sv ++++ b/stream_fork.sv +@@ -29,7 +29,7 @@ module stream_fork #( + input logic [N_OUP-1:0] ready_i + ); + +- typedef enum logic {READY, WAIT} state_t; ++ typedef enum logic {READY, WAITING} state_t; + + logic [N_OUP-1:0] oup_ready, + all_ones; +@@ -50,13 +50,13 @@ module stream_fork #( + end else begin + ready_o = 1'b0; + // Otherwise, wait for inputs that did not handshake yet. +- inp_state_d = WAIT; ++ inp_state_d = WAITING; + end + end else begin + ready_o = 1'b0; + end + end +- WAIT: begin ++ WAITING: begin + if (valid_i && oup_ready == all_ones) begin + ready_o = 1'b1; + inp_state_d = READY; +@@ -94,14 +94,14 @@ module stream_fork #( + valid_o[i] = 1'b1; + if (ready_i[i]) begin // Output handshake + if (!ready_o) begin // No input handshake yet +- oup_state_d = WAIT; ++ oup_state_d = WAITING; + end + end else begin // No output handshake + oup_ready[i] = 1'b0; + end + end + end +- WAIT: begin ++ WAITING: begin + if (valid_i && ready_o) begin // Input handshake + oup_state_d = READY; + end diff --git a/vendor/patches/pulp_common_cells/stream_join_dynamic_sv/0001-replace-assertions.patch b/vendor/patches/pulp_common_cells/stream_join_dynamic_sv/0001-replace-assertions.patch new file mode 100644 index 000000000..76cbb735c --- /dev/null +++ b/vendor/patches/pulp_common_cells/stream_join_dynamic_sv/0001-replace-assertions.patch @@ -0,0 +1,23 @@ +diff --git a/stream_join_dynamic.sv b/stream_join_dynamic.sv +index 267990b9..0dec8875 100644 +--- a/stream_join_dynamic.sv ++++ b/stream_join_dynamic.sv +@@ -11,7 +11,7 @@ + // Authors: + // - Luca Colagrande + +-`include "common_cells/assertions.svh" ++`include "prim_assert.sv" + + // Stream join dynamic: Joins a parametrizable number of input streams (i.e. valid-ready + // handshaking with dependency rules as in AXI4) to a single output stream. The subset of streams +@@ -39,7 +39,6 @@ module stream_join_dynamic #( + assign inp_ready_o[i] = oup_valid_o & oup_ready_i & sel_i[i]; + end + +-`ifndef COMMON_CELLS_ASSERTS_OFF +- `ASSERT_INIT(n_inp_0, N_INP >= 1, "N_INP must be at least 1!") +-`endif ++ `ASSERT_INIT(NumInputLargerZero_A, N_INP >= 1) ++ + endmodule diff --git a/vendor/pulp_common_cells.core b/vendor/pulp_common_cells.core new file mode 100644 index 000000000..c877e01cb --- /dev/null +++ b/vendor/pulp_common_cells.core @@ -0,0 +1,21 @@ +CAPI=2: +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "pulp-platform:common_cells:common_cells:0.1" +description: "PULP Common Cells" + +filesets: + files_src: + depend: + - lowrisc:prim:assert + files: + - pulp_common_cells/rtl/stream_fork.sv + - pulp_common_cells/rtl/stream_fork_dynamic.sv + - pulp_common_cells/rtl/stream_join_dynamic.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_src diff --git a/vendor/pulp_common_cells.lock.hjson b/vendor/pulp_common_cells.lock.hjson new file mode 100644 index 000000000..df71f1191 --- /dev/null +++ b/vendor/pulp_common_cells.lock.hjson @@ -0,0 +1,14 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/pulp-platform/common_cells.git + rev: 63e1b679a70eca3a1d60d686bc1fa170ec08e1ab + } +} diff --git a/vendor/pulp_common_cells.vendor.hjson b/vendor/pulp_common_cells.vendor.hjson new file mode 100644 index 000000000..2dd98136b --- /dev/null +++ b/vendor/pulp_common_cells.vendor.hjson @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "pulp_common_cells", + target_dir: "pulp_common_cells", + patch_dir: "patches/pulp_common_cells", + + upstream: { + url: "https://github.com/pulp-platform/common_cells.git" + rev: "master" + }, + + mapping: [ + { + from: "src/stream_fork.sv", + to: "rtl/stream_fork.sv", + patch_dir: "stream_fork_sv"}, + { + from: "src/stream_fork_dynamic.sv", + to: "rtl/stream_fork_dynamic.sv", + patch_dir: "stream_fork_dynamic_sv"}, + { + from: "src/stream_join_dynamic.sv", + to: "rtl/stream_join_dynamic.sv", + patch_dir: "stream_join_dynamic_sv"}, + ] +} diff --git a/vendor/pulp_common_cells/rtl/stream_fork.sv b/vendor/pulp_common_cells/rtl/stream_fork.sv new file mode 100644 index 000000000..e27b6e454 --- /dev/null +++ b/vendor/pulp_common_cells/rtl/stream_fork.sv @@ -0,0 +1,129 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (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-0.51. 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. + +// Stream fork: Connects the input stream (ready-valid) handshake to *all* of `N_OUP` output stream +// handshakes. For each input stream handshake, every output stream handshakes exactly once. The +// input stream only handshakes when all output streams have handshaked, but the output streams do +// not have to handshake simultaneously. +// +// This module has no data ports because stream data does not need to be forked: the data of the +// input stream can just be applied at all output streams. + +`include "prim_assert.sv" + +module stream_fork #( + parameter int unsigned N_OUP = 0 // Synopsys DC requires a default value for parameters. +) ( + input logic clk_i, + input logic rst_ni, + input logic valid_i, + output logic ready_o, + output logic [N_OUP-1:0] valid_o, + input logic [N_OUP-1:0] ready_i +); + + typedef enum logic {READY, WAITING} state_t; + + logic [N_OUP-1:0] oup_ready, + all_ones; + + state_t inp_state_d, inp_state_q; + + // Input control FSM + always_comb begin + // ready_o = 1'b0; + inp_state_d = inp_state_q; + + unique case (inp_state_q) + READY: begin + if (valid_i) begin + if (valid_o == all_ones && ready_i == all_ones) begin + // If handshake on all outputs, handshake on input. + ready_o = 1'b1; + end else begin + ready_o = 1'b0; + // Otherwise, wait for inputs that did not handshake yet. + inp_state_d = WAITING; + end + end else begin + ready_o = 1'b0; + end + end + WAITING: begin + if (valid_i && oup_ready == all_ones) begin + ready_o = 1'b1; + inp_state_d = READY; + end else begin + ready_o = 1'b0; + end + end + default: begin + inp_state_d = READY; + ready_o = 1'b0; + end + endcase + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + inp_state_q <= READY; + end else begin + inp_state_q <= inp_state_d; + end + end + + // Output control FSM + for (genvar i = 0; i < N_OUP; i++) begin: gen_oup_state + state_t oup_state_d, oup_state_q; + + always_comb begin + oup_ready[i] = 1'b1; + valid_o[i] = 1'b0; + oup_state_d = oup_state_q; + + unique case (oup_state_q) + READY: begin + if (valid_i) begin + valid_o[i] = 1'b1; + if (ready_i[i]) begin // Output handshake + if (!ready_o) begin // No input handshake yet + oup_state_d = WAITING; + end + end else begin // No output handshake + oup_ready[i] = 1'b0; + end + end + end + WAITING: begin + if (valid_i && ready_o) begin // Input handshake + oup_state_d = READY; + end + end + default: begin + oup_state_d = READY; + end + endcase + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + oup_state_q <= READY; + end else begin + oup_state_q <= oup_state_d; + end + end + end + + assign all_ones = '1; // Synthesis fix for Vivado, which does not correctly compute the width + // of the '1 literal when assigned to a port of parametrized width. + + `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) + +endmodule diff --git a/vendor/pulp_common_cells/rtl/stream_fork_dynamic.sv b/vendor/pulp_common_cells/rtl/stream_fork_dynamic.sv new file mode 100644 index 000000000..1aa546b44 --- /dev/null +++ b/vendor/pulp_common_cells/rtl/stream_fork_dynamic.sv @@ -0,0 +1,92 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (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-0.51. 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. + +// Authors: +// - Andreas Kurth + +`include "prim_assert.sv" + +/// Dynamic stream fork: Connects the input stream (ready-valid) handshake to a combination of output +/// stream handshake. The combination is determined dynamically through another stream, which +/// provides a bitmask for the fork. For each input stream handshake, every output stream handshakes +/// exactly once. The input stream only handshakes when all output streams have handshaked, but the +/// output streams do not have to handshake simultaneously. +/// +/// This module has no data ports because stream data does not need to be forked: the data of the +/// input stream can just be applied at all output streams. +module stream_fork_dynamic #( + /// Number of output streams + parameter int unsigned N_OUP = 32'd0 // Synopsys DC requires a default value for parameters. +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Input stream valid handshake, + input logic valid_i, + /// Input stream ready handshake + output logic ready_o, + /// Selection mask for the output handshake + input logic [N_OUP-1:0] sel_i, + /// Selection mask valid + input logic sel_valid_i, + /// Selection mask ready + output logic sel_ready_o, + /// Output streams valid handshakes + output logic [N_OUP-1:0] valid_o, + /// Output streams ready handshakes + input logic [N_OUP-1:0] ready_i +); + + logic int_inp_valid, int_inp_ready; + logic [N_OUP-1:0] int_oup_valid, int_oup_ready; + + // Output handshaking + for (genvar i = 0; i < N_OUP; i++) begin : gen_oups + always_comb begin + valid_o[i] = 1'b0; + int_oup_ready[i] = 1'b0; + if (sel_valid_i) begin + if (sel_i[i]) begin + valid_o[i] = int_oup_valid[i]; + int_oup_ready[i] = ready_i[i]; + end else begin + int_oup_ready[i] = 1'b1; + end + end + end + end + + // Input handshaking + always_comb begin + int_inp_valid = 1'b0; + ready_o = 1'b0; + sel_ready_o = 1'b0; + if (sel_valid_i) begin + int_inp_valid = valid_i; + ready_o = int_inp_ready; + sel_ready_o = int_inp_ready; + end + end + + stream_fork #( + .N_OUP ( N_OUP ) + ) i_fork ( + .clk_i, + .rst_ni, + .valid_i ( int_inp_valid ), + .ready_o ( int_inp_ready ), + .valid_o ( int_oup_valid ), + .ready_i ( int_oup_ready ) + ); + + `ASSERT_INIT(NumOutputLargerZero_A, N_OUP >= 1) + +endmodule diff --git a/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv b/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv new file mode 100644 index 000000000..0dec8875e --- /dev/null +++ b/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv @@ -0,0 +1,44 @@ +// Copyright 2020 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (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-0.51. 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. + +// Authors: +// - Luca Colagrande + +`include "prim_assert.sv" + +// Stream join dynamic: Joins a parametrizable number of input streams (i.e. valid-ready +// handshaking with dependency rules as in AXI4) to a single output stream. The subset of streams +// to join can be configured dynamically via `sel_i`. The output handshake happens only after +// there has been a handshake. The data channel flows outside of this module. +module stream_join_dynamic #( + /// Number of input streams + parameter int unsigned N_INP = 32'd0 // Synopsys DC requires a default value for parameters. +) ( + /// Input streams valid handshakes + input logic [N_INP-1:0] inp_valid_i, + /// Input streams ready handshakes + output logic [N_INP-1:0] inp_ready_o, + /// Selection mask for the output handshake + input logic [N_INP-1:0] sel_i, + /// Output stream valid handshake + output logic oup_valid_o, + /// Output stream ready handshake + input logic oup_ready_i +); + + // Corner case when `sel_i` is all 0s should not generate valid + assign oup_valid_o = &(inp_valid_i | ~sel_i) && |sel_i; + for (genvar i = 0; i < N_INP; i++) begin : gen_inp_ready + assign inp_ready_o[i] = oup_valid_o & oup_ready_i & sel_i[i]; + end + + `ASSERT_INIT(NumInputLargerZero_A, N_INP >= 1) + +endmodule