diff --git a/ibex_pkg.core b/ibex_pkg.core index 17f47e08c4..68a512543a 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 82edbe133d..0424dff65e 100644 --- a/ibex_top.core +++ b/ibex_top.core @@ -11,6 +11,7 @@ filesets: - lowrisc:ibex:ibex_pkg - lowrisc:ibex:ibex_core - lowrisc:prim:and2 + - lowrisc:prim:assert - lowrisc:prim:buf - lowrisc:prim:clock_mux2 - lowrisc:prim:count @@ -19,10 +20,14 @@ filesets: - lowrisc:prim:onehot_check - lowrisc:prim:onehot - lowrisc:prim:util + - lowrisc:prim:fifo + - lowrisc:prim:secded + - 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 0000000000..f25ddb8ff3 --- /dev/null +++ b/rtl/ibex_cheriot_pkg.sv @@ -0,0 +1,71 @@ +// Copyright lowRISC contributors. +// Copyright Microsoft Corporation +// 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; + + + // Expand the compressed 4-bit exponent field stored in a capability to its + // 5-bit internal representation.(page 70, CHERIoT Architecture specification, Version 1.0). + function automatic logic [EXP_W-1:0] get_exponent(logic [3:0] exponent_stored); + return (exponent_stored != 4'hf) ? {1'b0, exponent_stored} : 5'd24; + endfunction + + + // Check if a capability's permissions correspond to a sealing capability. + // Only the low 5 bits of the (6-bit) compressed permissions field are decoded here. + function automatic logic is_sealing_cap(logic [4:0] cperms); + return (cperms[4:3] == 2'b00) && (|cperms[2:0]); + endfunction + + + // Obtain 33-bit representation of top or bottom depending on the inputs. + 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 0000000000..81c665e4e5 --- /dev/null +++ b/rtl/ibex_trvk.sv @@ -0,0 +1,421 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +// Assumes a tagged capability load always arrives downstream as exactly two consecutive, +// uninterrupted 32-bit responses from a single, in-order requester. +module ibex_trvk #( + // The number of outstanding transaction the IP supports + parameter int unsigned NumOutstanding = 32'd4, + // The width of the meta memory byte address space used to store the revocation bits + parameter int unsigned RevBitmapAddrWidth = 32'd11, + // The base byte address of the meta SRAM holding the revocation bitmap + parameter int unsigned RevBitmapBaseAddr = 32'h0000_0000, + // Enable ECC checking on the revocation bitmap memory port + parameter bit MemECC = 1'b1 +)( + input logic clk_i, + input logic 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 // + /////////// + + localparam int unsigned RevBitmapWordAddrWidth = RevBitmapAddrWidth - 32'd2; + + // Expanded exponent (5 bit) + 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 [RevBitmapWordAddrWidth-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 misalign_flag_out; + logic misalign_flag_out_valid; + logic misalign_flag_out_ready; + + // Pointer store signals + logic [31:0] ptr_storage_q; + logic ptr_storage_valid_q; + logic ptr_storage_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; + logic downstream_rsp_wready; + + // 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; + + + ////////////////////////////////////// + // Upstream to Downstream Intercept // + ////////////////////////////////////// + + // Both the downstream port and the alignment store need to handshake for + // the stream to advance. + stream_fork #( + .N_OUP(32'd2) + ) u_stream_fork_us2ds ( + .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 upstream port 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_ds2us ( + .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 upstream and downstream + 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 upstream to downstream CHERIoT tag without 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 : 1'b1) & 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(misalign_flag_out_valid), + .rready_i(misalign_flag_out_ready), + .rdata_o (misalign_flag_out), + .full_o (), + .depth_o (), + .err_o () + ); + + // Element is consumed, if upstream handshakes response + assign misalign_flag_out_ready = upstream_rvalid_o; + + + /////////////////// + // Pointer Store // + /////////////////// + + // Pointer valid store. Pointer, `ptr` refers here to the lower word of a capability, which + // corresponds to the C pointer. The 32-bit OBI interconnect first passes the pointer, which + // is stored in `ptr_storage_q` with a valid signal in `ptr_storage_valid_q`. + + // Pointer buffer could be filled iff tag valid & 64-bit aligned. + assign ptr_storage_enable = downstream_rsp_out.tag && !misalign_flag_out && + misalign_flag_out_valid; + + // Pointer store + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_pointer_store + if(!rst_ni) begin + ptr_storage_q <= '0; + end else begin + if (misalign_flag_out_ready && ptr_storage_enable) begin + ptr_storage_q <= downstream_rsp_out.data; + end + end + end + + // Pointer valid store + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_pointer_valid_store + if(!rst_ni) begin + ptr_storage_valid_q <= 1'b0; + end else begin + if (misalign_flag_out_ready) begin + ptr_storage_valid_q <= ptr_storage_enable; + end + end + end + + + ////////////////////////////////// + // Downstream Response Latching // + ////////////////////////////////// + + assign downstream_rsp_in = '{ + data: downstream_rdata_i, + intg: downstream_rdata_intg_i, + tag: downstream_tag_i, + err: downstream_err_i + }; + + // 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(downstream_rsp_wready), + .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 () + ); + + + //////////////////////////////// + // Bitmap Address Calculation // + //////////////////////////////// + + assign cap_meta = '{ + base: downstream_rsp_out.data[8:0], + exponent: ibex_cheriot_pkg::get_exponent(downstream_rsp_out.data[21:18]), + 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 = ibex_cheriot_pkg::is_sealing_cap(cap_meta.cperms[4:0]); + + // Extract the middle field from the pointer, bounds depend on exponent, width fixed + assign addr_mid = cheriot_bot_t'(ptr_storage_q >> cap_meta.exponent); + + // 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_storage_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 >> $clog2(64/8); + + // Word address of the revocation bitmap + assign revbm_addr = revbm_bit_addr[RevBitmapWordAddrWidth+$clog2(32)-1:$clog2(32)]; + + // Bit select + assign revbm_bit_select = revbm_bit_addr[$clog2(32)-1:0]; + + // Capability base is outside of the bitmap range + assign revbm_out_of_range = |(revbm_bit_addr[31:RevBitmapWordAddrWidth+$clog2(32)]); + + + ////////////////////// + // 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_storage_valid_q && // The pointer stored is valid + downstream_rsp_out.tag && // We are looking at a capability + downstream_rsp_out_valid && // The stored response is valid + misalign_flag_out && // We are on the second word of the cap + misalign_flag_out_valid && // The latched alignment bits are 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 - RevBitmapWordAddrWidth - 2{1'b0}}, revbm_addr, 2'b00}; + + // Is the current capability marked as revoked? + assign revbm_revoked = revbm_rdata_i[revbm_bit_select] || revbm_err_i || + (|revbm_rsp_data_intg_error); + + // Did we receive a device error? + assign revbm_device_error_o = revbm_rvalid_i && revbm_err_i; + + // Check the bitmap response data integrity + if (MemECC) begin : gen_revbm_intg_check + 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); + end else begin : gen_no_revbm_intg_check + logic unused_revbm_rdata_intg; + assign unused_revbm_rdata_intg = ^revbm_rdata_intg_i; + + assign revbm_rsp_data_intg_error = 2'b00; + assign revbm_data_intg_error_o = 1'b0; + end + + // 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 + + + //////////////// + // Assertions // + //////////////// + + // Every delivered response has its alignment information available. + `ASSERT(AlignValidOnRsp_A, upstream_rvalid_o |-> misalign_flag_out_valid) + // The downstream response store never overflows. + `ASSERT(DsRspFifoNoOverflow_A, downstream_rvalid_i |-> downstream_rsp_wready) + // No unsolicited bitmap response; this is what keeps `upstream_tag_o` safe. + `ASSERT(RevbmRspOnlyWhenOutstanding_A, revbm_rvalid_i |-> revbm_outstanding_q) + // OBI request stability on the bitmap port. + `ASSERT(RevbmReqStable_A, revbm_req_o && !revbm_gnt_i |=> revbm_req_o && $stable(revbm_addr_o)) + `ASSERT_INIT(RevBitmapWordAddrWidthRange_A, RevBitmapWordAddrWidth inside {[1:26]}) + `ASSERT_INIT(RevBitmapBaseAddrAligned_A, RevBitmapBaseAddr[1:0] == 2'b00) + `ASSERT(HeapBaseAligned_A, heap_base_addr_i[2:0] == 3'b0) + +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 0000000000..408c39eca7 --- /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 0000000000..b0e07fa66c --- /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 0000000000..76cbb735ca --- /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 0000000000..1d9c8a007d --- /dev/null +++ b/vendor/pulp_common_cells.core @@ -0,0 +1,20 @@ +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_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 0000000000..df71f11911 --- /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 0000000000..152e0cc1cf --- /dev/null +++ b/vendor/pulp_common_cells.vendor.hjson @@ -0,0 +1,24 @@ +// 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_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 0000000000..e27b6e4549 --- /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_join_dynamic.sv b/vendor/pulp_common_cells/rtl/stream_join_dynamic.sv new file mode 100644 index 0000000000..0dec8875e9 --- /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