diff --git a/.gitmodules b/.gitmodules index ab3a5ebb..832f661c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "src-sv/common_cells"] - path = src/common_cells - url = https://github.com/pulp-platform/common_cells.git [submodule "src-sv/fpu_div_sqrt_mvp"] path = src/fpu_div_sqrt_mvp url = https://github.com/pulp-platform/fpu_div_sqrt_mvp.git diff --git a/Bender.yml b/Bender.yml index e11f6a40..bc5515a9 100644 --- a/Bender.yml +++ b/Bender.yml @@ -6,13 +6,15 @@ package: authors: ["Stefan Mach "] dependencies: - common_cells: {git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0} fpu_div_sqrt_mvp: {git: "https://github.com/pulp-platform/fpu_div_sqrt_mvp.git", version: 1.0.4} sources: - src/fpnew_pkg.sv - src/fpnew_cast_multi.sv - src/fpnew_classifier.sv + - vendor/common_cells/src/cf_math_pkg.sv + - vendor/common_cells/src/rr_arb_tree.sv + - vendor/common_cells/src/lzc.sv - vendor/opene906/E906_RTL_FACTORY/gen_rtl/clk/rtl/gated_clk_cell.v - vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_ctrl.v - vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_ff1.v diff --git a/vendor/common_cells.lock.hjson b/vendor/common_cells.lock.hjson new file mode 100644 index 00000000..6257e9ae --- /dev/null +++ b/vendor/common_cells.lock.hjson @@ -0,0 +1,14 @@ +// Copyright lowRISC contributors. +// 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: 6aeee85d0a34fedc06c14f04fd6363c9f7b4eeea + } +} diff --git a/vendor/common_cells.vendor.hjson b/vendor/common_cells.vendor.hjson new file mode 100644 index 00000000..84fe28b8 --- /dev/null +++ b/vendor/common_cells.vendor.hjson @@ -0,0 +1,32 @@ +// Copyright 2023 OpenHW Group +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +{ + name: "common_cells", + target_dir: "common_cells", + + upstream: { + url: "https://github.com/pulp-platform/common_cells.git", + rev: "6aeee85d0a34fedc06c14f04fd6363c9f7b4eeea", + }, + + exclude_from_upstream: [ + ".github", + ".gitignore", + ".gitlab-ci.yml", + ".travis.yml", + "ci/", + "Bender.yml", + "CHANGELOG.md", + "LICENSE", + "Makefile", + "README.md", + "ci", + "formal", + "ips_list.yml", + "lint", + "src_files.yml", + "test" + ] + +} \ No newline at end of file diff --git a/vendor/common_cells/common_cells.core b/vendor/common_cells/common_cells.core new file mode 100644 index 00000000..50c3120d --- /dev/null +++ b/vendor/common_cells/common_cells.core @@ -0,0 +1,99 @@ +CAPI=2: + +name : pulp-platform.org::common_cells:1.20.0 + +filesets: + rtl: + files: + - include/common_cells/registers.svh : {is_include_file : true, include_path : include} + # Source files grouped in levels. Files in level 0 have no dependencies on files in this package. + # Files in level 1 only depend on files in level 0, files in level 2 on files in levels 1 and 0, + # etc. Files within a level are ordered alphabetically. + # Level 0 + - src/binary_to_gray.sv + - src/cb_filter_pkg.sv + - src/cdc_2phase.sv + - src/cf_math_pkg.sv + - src/clk_div.sv + - src/delta_counter.sv + - src/ecc_pkg.sv + - src/edge_propagator_tx.sv + - src/exp_backoff.sv + - src/fifo_v3.sv + - src/gray_to_binary.sv + - src/isochronous_spill_register.sv + - src/lfsr.sv + - src/lfsr_16bit.sv + - src/lfsr_8bit.sv + - src/mv_filter.sv + - src/onehot_to_bin.sv + - src/plru_tree.sv + - src/popcount.sv + - src/rr_arb_tree.sv + - src/rstgen_bypass.sv + - src/serial_deglitch.sv + - src/shift_reg.sv + - src/spill_register.sv + - src/stream_demux.sv + - src/stream_filter.sv + - src/stream_fork.sv + - src/stream_intf.sv + - src/stream_join.sv + - src/stream_mux.sv + - src/sub_per_hash.sv + - src/sync.sv + - src/sync_wedge.sv + - src/unread.sv + # Level 1 + - src/addr_decode.sv + - src/cb_filter.sv + - src/cdc_fifo_2phase.sv + - src/cdc_fifo_gray.sv + - src/counter.sv + - src/ecc_decode.sv + - src/ecc_encode.sv + - src/edge_detect.sv + - src/lzc.sv + - src/max_counter.sv + - src/rstgen.sv + - src/stream_delay.sv + - src/stream_fifo.sv + - src/stream_fork_dynamic.sv + - src/stream_xbar.sv + # Level 2 + - src/fall_through_register.sv + - src/id_queue.sv + - src/stream_to_mem.sv + - src/stream_arbiter_flushable.sv + - src/stream_omega_net.sv + - src/stream_register.sv + # Level 3 + - src/stream_arbiter.sv + file_type : systemVerilogSource + + deprecated: + files: + # Deprecated modules + # Level 0 + - src/deprecated/clock_divider_counter.sv + - src/deprecated/find_first_one.sv + - src/deprecated/generic_LFSR_8bit.sv + - src/deprecated/generic_fifo.sv + - src/deprecated/prioarbiter.sv + - src/deprecated/pulp_sync.sv + - src/deprecated/pulp_sync_wedge.sv + - src/deprecated/rrarbiter.sv + # Level 1 + - src/deprecated/clock_divider.sv + - src/deprecated/fifo_v2.sv + # Level 2 + - src/deprecated/fifo_v1.sv + + # Depend on deprecated modules + - src/edge_propagator.sv + - src/edge_propagator_rx.sv + file_type : systemVerilogSource + +targets: + default: + filesets : [rtl, deprecated] diff --git a/vendor/common_cells/include/common_cells/assertions.svh b/vendor/common_cells/include/common_cells/assertions.svh new file mode 100644 index 00000000..b6b4b737 --- /dev/null +++ b/vendor/common_cells/include/common_cells/assertions.svh @@ -0,0 +1,201 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Macros and helper code for using assertions. +// - Provides default clk and rst options to simplify code +// - Provides boiler plate template for common assertions + +`ifndef PRIM_ASSERT_SV +`define PRIM_ASSERT_SV + +`ifdef UVM + // report assertion error with UVM if compiled + package assert_rpt_pkg; + import uvm_pkg::*; + `include "uvm_macros.svh" + function void assert_rpt(string msg); + `uvm_error("ASSERT FAILED", msg) + endfunction + endpackage +`endif + +/////////////////// +// Helper macros // +/////////////////// + +// local helper macro to reduce code clutter. undefined at the end of this file +`ifndef VERILATOR +`ifndef SYNTHESIS +`ifndef XSIM +`define INC_ASSERT +`endif +`endif +`endif + +// Converts an arbitrary block of code into a Verilog string +`define PRIM_STRINGIFY(__x) `"__x`" + +// ASSERT_RPT is available to change the reporting mechanism when an assert fails +`define ASSERT_RPT(__name) \ +`ifdef UVM \ + assert_rpt_pkg::assert_rpt($sformatf("[%m] %s (%s:%0d)", \ + __name, `__FILE__, `__LINE__)); \ +`else \ + $error("[ASSERT FAILED] [%m] %s (%s:%0d)", __name, `__FILE__, `__LINE__); \ +`endif + +/////////////////////////////////////// +// Simple assertion and cover macros // +/////////////////////////////////////// + +// Default clk and reset signals used by assertion macros below. +`define ASSERT_DEFAULT_CLK clk_i +`define ASSERT_DEFAULT_RST !rst_ni + +// Immediate assertion +// Note that immediate assertions are sensitive to simulation glitches. +`define ASSERT_I(__name, __prop) \ +`ifdef INC_ASSERT \ + __name: assert (__prop) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ +`endif + +// Assertion in initial block. Can be used for things like parameter checking. +`define ASSERT_INIT(__name, __prop) \ +`ifdef INC_ASSERT \ + initial begin \ + __name: assert (__prop) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ + end \ +`endif + +// Assertion in final block. Can be used for things like queues being empty +// at end of sim, all credits returned at end of sim, state machines in idle +// at end of sim. +`define ASSERT_FINAL(__name, __prop) \ +`ifdef INC_ASSERT \ + final begin \ + __name: assert (__prop || $test$plusargs("disable_assert_final_checks")) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ + end \ +`endif + +// Assert a concurrent property directly. +// It can be called as a module (or interface) body item. +`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + __name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ +`endif +// Note: Above we use (__rst !== '0) in the disable iff statements instead of +// (__rst == '1). This properly disables the assertion in cases when reset is X at +// the beginning of a simulation. For that case, (reset == '1) does not disable the +// assertion. + +// Assert a concurrent property NEVER happens +`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + __name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) not (__prop)) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ +`endif + +// Assert that signal has a known value (each bit is either '0' or '1') after reset. +// It can be called as a module (or interface) body item. +`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + `ASSERT(__name, !$isunknown(__sig), __clk, __rst) \ +`endif + +// Cover a concurrent property +`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + __name: cover property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)); \ +`endif + +////////////////////////////// +// Complex assertion macros // +////////////////////////////// + +// Assert that signal is an active-high pulse with pulse length of 1 clock cycle +`define ASSERT_PULSE(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + `ASSERT(__name, $rose(__sig) |=> !(__sig), __clk, __rst) \ +`endif + +// Assert that a property is true only when an enable signal is set. It can be called as a module +// (or interface) body item. +`define ASSERT_IF(__name, __prop, __enable, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + `ASSERT(__name, (__enable) |-> (__prop), __clk, __rst) \ +`endif + +// Assert that signal has a known value (each bit is either '0' or '1') after reset if enable is +// set. It can be called as a module (or interface) body item. +`define ASSERT_KNOWN_IF(__name, __sig, __enable, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + `ASSERT_KNOWN(__name``KnownEnable, __enable, __clk, __rst) \ + `ASSERT_IF(__name, !$isunknown(__sig), __enable, __clk, __rst) \ +`endif + +/////////////////////// +// Assumption macros // +/////////////////////// + +// Assume a concurrent property +`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef INC_ASSERT \ + __name: assume property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ +`endif + +// Assume an immediate property +`define ASSUME_I(__name, __prop) \ +`ifdef INC_ASSERT \ + __name: assume (__prop) \ + else begin \ + `ASSERT_RPT(`PRIM_STRINGIFY(__name)) \ + end \ +`endif + +////////////////////////////////// +// For formal verification only // +////////////////////////////////// + +// Note that the existing set of ASSERT macros specified above shall be used for FPV, +// thereby ensuring that the assertions are evaluated during DV simulations as well. + +// ASSUME_FPV +// Assume a concurrent property during formal verification only. +`define ASSUME_FPV(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef FPV_ON \ + `ASSUME(__name, __prop, __clk, __rst) \ +`endif + +// ASSUME_I_FPV +// Assume a concurrent property during formal verification only. +`define ASSUME_I_FPV(__name, __prop) \ +`ifdef FPV_ON \ + `ASSUME_I(__name, __prop) \ +`endif + +// COVER_FPV +// Cover a concurrent property during formal verification +`define COVER_FPV(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef FPV_ON \ + `COVER(__name, __prop, __clk, __rst) \ +`endif + +`endif // PRIM_ASSERT_SV diff --git a/vendor/common_cells/include/common_cells/registers.svh b/vendor/common_cells/include/common_cells/registers.svh new file mode 100644 index 00000000..c1975edc --- /dev/null +++ b/vendor/common_cells/include/common_cells/registers.svh @@ -0,0 +1,224 @@ +// 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. + +// Common register defines for RTL designs +`ifndef COMMON_CELLS_REGISTERS_SVH_ +`define COMMON_CELLS_REGISTERS_SVH_ + +// Abridged Summary of available FF macros: +// `FF: asynchronous active-low reset (implicit clock and reset) +// `FFAR: asynchronous active-high reset +// `FFARN: asynchronous active-low reset +// `FFSR: synchronous active-high reset +// `FFSRN: synchronous active-low reset +// `FFNR: without reset +// `FFL: load-enable and asynchronous active-low reset (implicit clock and reset) +// `FFLAR: load-enable and asynchronous active-high reset +// `FFLARN: load-enable and asynchronous active-low reset +// `FFLARNC: load-enable and asynchronous active-low reset and synchronous active-high clear +// `FFLSR: load-enable and synchronous active-high reset +// `FFLSRN: load-enable and synchronous active-low reset +// `FFLNR: load-enable without reset + + +// Flip-Flop with asynchronous active-low reset (implicit clock and reset) +// __q: Q output of FF +// __d: D input of FF +// __reset_value: value assigned upon reset +// Implicit: +// clk_i: clock input +// rst_ni: reset input (asynchronous, active low) +`define FF(__q, __d, __reset_value) \ + always_ff @(posedge clk_i or negedge rst_ni) begin \ + if (!rst_ni) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__d); \ + end \ + end + +// Flip-Flop with asynchronous active-high reset +// __q: Q output of FF +// __d: D input of FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __arst: asynchronous reset +`define FFAR(__q, __d, __reset_value, __clk, __arst) \ + always_ff @(posedge (__clk) or posedge (__arst)) begin \ + if (__arst) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__d); \ + end \ + end + +// Flip-Flop with asynchronous active-low reset +// __q: Q output of FF +// __d: D input of FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __arst_n: asynchronous reset +`define FFARN(__q, __d, __reset_value, __clk, __arst_n) \ + always_ff @(posedge (__clk) or negedge (__arst_n)) begin \ + if (!__arst_n) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__d); \ + end \ + end + +// Flip-Flop with synchronous active-high reset +// __q: Q output of FF +// __d: D input of FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __reset_clk: reset input +`define FFSR(__q, __d, __reset_value, __clk, __reset_clk) \ + `ifndef VERILATOR \ + /``* synopsys sync_set_reset `"__reset_clk`" *``/ \ + `endif \ + always_ff @(posedge (__clk)) begin \ + __q <= (__reset_clk) ? (__reset_value) : (__d); \ + end + +// Flip-Flop with synchronous active-low reset +// __q: Q output of FF +// __d: D input of FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __reset_n_clk: reset input +`define FFSRN(__q, __d, __reset_value, __clk, __reset_n_clk) \ + `ifndef VERILATOR \ + /``* synopsys sync_set_reset `"__reset_n_clk`" *``/ \ + `endif \ + always_ff @(posedge (__clk)) begin \ + __q <= (!__reset_n_clk) ? (__reset_value) : (__d); \ + end + +// Always-enable Flip-Flop without reset +// __q: Q output of FF +// __d: D input of FF +// __clk: clock input +`define FFNR(__q, __d, __clk) \ + always_ff @(posedge (__clk)) begin \ + __q <= (__d); \ + end + +// Flip-Flop with load-enable and asynchronous active-low reset (implicit clock and reset) +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __reset_value: value assigned upon reset +// Implicit: +// clk_i: clock input +// rst_ni: reset input (asynchronous, active low) +`define FFL(__q, __d, __load, __reset_value) \ + always_ff @(posedge clk_i or negedge rst_ni) begin \ + if (!rst_ni) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__load) ? (__d) : (__q); \ + end \ + end + +// Flip-Flop with load-enable and asynchronous active-high reset +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __arst: asynchronous reset +`define FFLAR(__q, __d, __load, __reset_value, __clk, __arst) \ + always_ff @(posedge (__clk) or posedge (__arst)) begin \ + if (__arst) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__load) ? (__d) : (__q); \ + end \ + end + +// Flip-Flop with load-enable and asynchronous active-low reset +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __arst_n: asynchronous reset +`define FFLARN(__q, __d, __load, __reset_value, __clk, __arst_n) \ + always_ff @(posedge (__clk) or negedge (__arst_n)) begin \ + if (!__arst_n) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__load) ? (__d) : (__q); \ + end \ + end + +// Flip-Flop with load-enable and synchronous active-high reset +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __reset_clk: reset input +`define FFLSR(__q, __d, __load, __reset_value, __clk, __reset_clk) \ + `ifndef VERILATOR \ + /``* synopsys sync_set_reset `"__reset_clk`" *``/ \ + `endif \ + always_ff @(posedge (__clk)) begin \ + __q <= (__reset_clk) ? (__reset_value) : ((__load) ? (__d) : (__q)); \ + end + +// Flip-Flop with load-enable and synchronous active-low reset +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __reset_n_clk: reset input +`define FFLSRN(__q, __d, __load, __reset_value, __clk, __reset_n_clk) \ + `ifndef VERILATOR \ + /``* synopsys sync_set_reset `"__reset_n_clk`" *``/ \ + `endif \ + always_ff @(posedge (__clk)) begin \ + __q <= (!__reset_n_clk) ? (__reset_value) : ((__load) ? (__d) : (__q)); \ + end + +// Flip-Flop with load-enable and asynchronous active-low reset and synchronous clear +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __clear: assign reset value into FF +// __reset_value: value assigned upon reset +// __clk: clock input +// __arst_n: asynchronous reset +`define FFLARNC(__q, __d, __load, __clear, __reset_value, __clk, __arst_n) \ + `ifndef VERILATOR \ + /``* synopsys sync_set_reset `"__clear`" *``/ \ + `endif \ + always_ff @(posedge (__clk) or negedge (__arst_n)) begin \ + if (!__arst_n) begin \ + __q <= (__reset_value); \ + end else begin \ + __q <= (__clear) ? (__reset_value) : (__load) ? (__d) : (__q); \ + end \ + end + +// Load-enable Flip-Flop without reset +// __q: Q output of FF +// __d: D input of FF +// __load: load d value into FF +// __clk: clock input +`define FFLNR(__q, __d, __load, __clk) \ + always_ff @(posedge (__clk)) begin \ + __q <= (__load) ? (__d) : (__q); \ + end + +`endif diff --git a/vendor/common_cells/src/addr_decode.sv b/vendor/common_cells/src/addr_decode.sv new file mode 100644 index 00000000..90a43a0d --- /dev/null +++ b/vendor/common_cells/src/addr_decode.sv @@ -0,0 +1,161 @@ +// Copyright 2019 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. + +// Author: Wolfgang Roenninger + +/// Address Decoder: Maps the input address combinatorially to an index. +/// The address map `addr_map_i` is a packed array of rule_t structs. +/// The ranges of any two rules may overlap. If so, the rule at the higher (more significant) +/// position in `addr_map_i` prevails. +/// +/// There can be an arbitrary number of address rules. There can be multiple +/// ranges defined for the same index. The start address has to be less than the end address. +/// +/// There is the possibility to add a default mapping: +/// `en_default_idx_i`: Driving this port to `1'b1` maps all input addresses +/// for which no rule in `addr_map_i` exists to the default index specified by +/// `default_idx_i`. In this case, `dec_error_o` is always `1'b0`. +/// +/// Assertions: The module checks every time there is a change in the address mapping +/// if the resulting map is valid. It fatals if `start_addr` is higher than `end_addr` +/// or if a mapping targets an index that is outside the number of allowed indices. +/// It issues warnings if the address regions of any two mappings overlap. +module addr_decode #( + /// Highest index which can happen in a rule. + parameter int unsigned NoIndices = 32'd0, + /// Total number of rules. + parameter int unsigned NoRules = 32'd0, + /// Address type inside the rules and to decode. + parameter type addr_t = logic, + /// Rule packed struct type. + /// The address decoder expects three fields in `rule_t`: + /// + /// typedef struct packed { + /// int unsigned idx; + /// addr_t start_addr; + /// addr_t end_addr; + /// } rule_t; + /// + /// - `idx`: index of the rule, has to be < `NoIndices` + /// - `start_addr`: start address of the range the rule describes, value is included in range + /// - `end_addr`: end address of the range the rule describes, value is NOT included in range + parameter type rule_t = logic, + /// Dependent parameter, do **not** overwite! + /// + /// Width of the `idx_o` output port. + parameter int unsigned IdxWidth = cf_math_pkg::idx_width(NoIndices), + /// Dependent parameter, do **not** overwite! + /// + /// Type of the `idx_o` output port. + parameter type idx_t = logic [IdxWidth-1:0] +) ( + /// Address to decode. + input addr_t addr_i, + /// Address map: rule with the highest array position wins on collision + input rule_t [NoRules-1:0] addr_map_i, + /// Decoded index. + output idx_t idx_o, + /// Decode is valid. + output logic dec_valid_o, + /// Decode is not valid, no matching rule found. + output logic dec_error_o, + /// Enable default port mapping. + /// + /// When not used, tie to `0`. + input logic en_default_idx_i, + /// Default port index. + /// + /// When `en_default_idx_i` is `1`, this will be the index when no rule matches. + /// + /// When not used, tie to `0`. + input idx_t default_idx_i +); + + logic [NoRules-1:0] matched_rules; // purely for address map debugging + + always_comb begin + // default assignments + matched_rules = '0; + dec_valid_o = 1'b0; + dec_error_o = (en_default_idx_i) ? 1'b0 : 1'b1; + idx_o = (en_default_idx_i) ? default_idx_i : '0; + + // match the rules + for (int unsigned i = 0; i < NoRules; i++) begin + if ((addr_i >= addr_map_i[i].start_addr) && (addr_i < addr_map_i[i].end_addr)) begin + matched_rules[i] = 1'b1; + dec_valid_o = 1'b1; + dec_error_o = 1'b0; + idx_o = idx_t'(addr_map_i[i].idx); + end + end + end + + // Assumptions and assertions + `ifndef VERILATOR + `ifndef XSIM + // pragma translate_off + initial begin : proc_check_parameters + assume ($bits(addr_i) == $bits(addr_map_i[0].start_addr)) else + $warning($sformatf("Input address has %d bits and address map has %d bits.", + $bits(addr_i), $bits(addr_map_i[0].start_addr))); + assume (NoRules > 0) else + $fatal(1, $sformatf("At least one rule needed")); + assume (NoIndices > 0) else + $fatal(1, $sformatf("At least one index needed")); + end + + assert final ($onehot0(matched_rules)) else + $warning("More than one bit set in the one-hot signal, matched_rules"); + + // These following assumptions check the validity of the address map. + // The assumptions gets generated for each distinct pair of rules. + // Each assumption is present two times, as they rely on one rules being + // effectively ordered. Only one of the rules with the same function is + // active at a time for a given pair. + // check_start: Enforces a smaller start than end address. + // check_idx: Enforces a valid index in the rule. + // check_overlap: Warns if there are overlapping address regions. + always @(addr_map_i) #0 begin : proc_check_addr_map + if (!$isunknown(addr_map_i)) begin + for (int unsigned i = 0; i < NoRules; i++) begin + check_start : assume (addr_map_i[i].start_addr < addr_map_i[i].end_addr) else + $fatal(1, $sformatf("This rule has a higher start than end address!!!\n\ + Violating rule %d.\n\ + Rule> IDX: %h START: %h END: %h\n\ + #####################################################", + i ,addr_map_i[i].idx, addr_map_i[i].start_addr, addr_map_i[i].end_addr)); + // check the SLV ids + check_idx : assume (addr_map_i[i].idx < NoIndices) else + $fatal(1, $sformatf("This rule has a IDX that is not allowed!!!\n\ + Violating rule %d.\n\ + Rule> IDX: %h START: %h END: %h\n\ + Rule> MAX_IDX: %h\n\ + #####################################################", + i, addr_map_i[i].idx, addr_map_i[i].start_addr, addr_map_i[i].end_addr, + (NoIndices-1))); + for (int unsigned j = i + 1; j < NoRules; j++) begin + // overlap check + check_overlap : assume (!((addr_map_i[j].start_addr < addr_map_i[i].end_addr) && + (addr_map_i[j].end_addr > addr_map_i[i].start_addr))) else + $warning($sformatf("Overlapping address region found!!!\n\ + Rule %d: IDX: %h START: %h END: %h\n\ + Rule %d: IDX: %h START: %h END: %h\n\ + #####################################################", + i, addr_map_i[i].idx, addr_map_i[i].start_addr, addr_map_i[i].end_addr, + j, addr_map_i[j].idx, addr_map_i[j].start_addr, addr_map_i[j].end_addr)); + end + end + end + end + // pragma translate_on + `endif + `endif +endmodule diff --git a/vendor/common_cells/src/binary_to_gray.sv b/vendor/common_cells/src/binary_to_gray.sv new file mode 100644 index 00000000..f4e4efd3 --- /dev/null +++ b/vendor/common_cells/src/binary_to_gray.sv @@ -0,0 +1,22 @@ +// 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. +// +// Fabian Schuiki + +/// A binary to gray code converter. +module binary_to_gray #( + parameter int N = -1 +)( + input logic [N-1:0] A, + output logic [N-1:0] Z +); + assign Z = A ^ (A >> 1); +endmodule diff --git a/vendor/common_cells/src/cb_filter.sv b/vendor/common_cells/src/cb_filter.sv new file mode 100644 index 00000000..9fbc5269 --- /dev/null +++ b/vendor/common_cells/src/cb_filter.sv @@ -0,0 +1,246 @@ +// Copyright (c) 2019 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. + +// Author: Wolfgang Roenninger + +// `cb_filter`: This module implements a counting bloom filter with parameterizable hash functions. +// +// Functionality: A counting bloom filter is a data structure to efficiently implement +// set lookups. It does so by hashing its data inputs onto multiple pointers +// which serve as indicators for an array of buckets. For lookups can be +// false positives, but no false negatives. +// - Seeding: The pseudo random generators need seeds at elaboration time to generate +// different hashes. In principle any combination of seeds can be used. +// But one should look that the hash outputs give sufficient different patterns, +// such that the resulting collision rate is low. The package `cb_filter_pkg` +// contains the struct for seeding the PRG's in the hash functions. +// - Lookup: +// - Ports: `look_data_i`, `look_valid_o` +// - Description: Lookup combinational, `look_valid_o` is high, when `look_data_i` was +// previously put into the filter. +// - Increment: +// - Ports: `incr_data_i`, `incr_valid_i` +// - Description: Put data into the counting bloom filter, when valid is high. +// - Decrement: +// - Ports: `decr_data_i`, `decr_valid_i` +// - Description: Remove data from the counting bloom filter. Only remove data that was +// previously put in, otherwise will go in a wrong state. +// - Status: +// - `filter_clear_i`: Clears the filter and sets all counters to 0. +// - `filter_ussage_o`: How many data items are currently in the filter. +// - `filter_full_o`: Filter is full, can no longer hold more items. +// - `filter_empty_o`: Filter is empty. +// - `filter_error_o`: One of the internal counters or buckets overflowed. + +/// This is a counting bloom filter +module cb_filter #( + parameter int unsigned KHashes = 32'd3, // Number of hash functions + parameter int unsigned HashWidth = 32'd4, // Number of counters is 2**HashWidth + parameter int unsigned HashRounds = 32'd1, // Number of permutation substitution rounds + parameter int unsigned InpWidth = 32'd32, // Input data width + parameter int unsigned BucketWidth = 32'd4, // Width of Bucket counters + // the seeds used for seeding the PRG's inside each hash, one `cb_seed_t` per hash function. + parameter cb_filter_pkg::cb_seed_t [KHashes-1:0] Seeds = cb_filter_pkg::EgSeeds +) ( + input logic clk_i, // Clock + input logic rst_ni, // Active low reset + // data lookup + input logic [InpWidth-1:0] look_data_i, + output logic look_valid_o, + // data increment + input logic [InpWidth-1:0] incr_data_i, + input logic incr_valid_i, + // data decrement + input logic [InpWidth-1:0] decr_data_i, + input logic decr_valid_i, + // status signals + input logic filter_clear_i, + output logic [HashWidth-1:0] filter_usage_o, + output logic filter_full_o, + output logic filter_empty_o, + output logic filter_error_o +); + + localparam int unsigned NoCounters = 2**HashWidth; + + // signal declarations + logic [NoCounters-1:0] look_ind; // hash function pointers + logic [NoCounters-1:0] incr_ind; // hash function pointers + logic [NoCounters-1:0] decr_ind; // hash function pointers + // bucket (counter signals) + logic [NoCounters-1:0] bucket_en; + logic [NoCounters-1:0] bucket_down; + logic [NoCounters-1:0] bucket_occupied; + logic [NoCounters-1:0] bucket_overflow; + logic [NoCounters-1:0] bucket_full; + logic [NoCounters-1:0] bucket_empty; + // membership lookup signals + logic [NoCounters-1:0] data_in_bucket; + // tot count signals (filter usage) + logic cnt_en; + logic cnt_down; + logic cnt_overflow; + + // ----------------------------------------- + // Lookup Hash - Membership Detection + // ----------------------------------------- + hash_block #( + .NoHashes ( KHashes ), + .InpWidth ( InpWidth ), + .HashWidth ( HashWidth ), + .NoRounds ( HashRounds ), + .Seeds ( Seeds ) + ) i_look_hashes ( + .data_i ( look_data_i ), + .indicator_o ( look_ind ) + ); + assign data_in_bucket = look_ind & bucket_occupied; + assign look_valid_o = (data_in_bucket == look_ind) ? 1'b1 : 1'b0; + + // ----------------------------------------- + // Increment Hash - Add Member to Set + // ----------------------------------------- + hash_block #( + .NoHashes ( KHashes ), + .InpWidth ( InpWidth ), + .HashWidth ( HashWidth ), + .NoRounds ( HashRounds ), + .Seeds ( Seeds ) + ) i_incr_hashes ( + .data_i ( incr_data_i ), + .indicator_o ( incr_ind ) + ); + + // ----------------------------------------- + // Decrement Hash - Remove Member from Set + // ----------------------------------------- + hash_block #( + .NoHashes ( KHashes ), + .InpWidth ( InpWidth ), + .HashWidth ( HashWidth ), + .NoRounds ( HashRounds ), + .Seeds ( Seeds ) + ) i_decr_hashes ( + .data_i ( decr_data_i ), + .indicator_o ( decr_ind ) + ); + + // ----------------------------------------- + // Control the incr/decr of buckets + // ----------------------------------------- + assign bucket_down = decr_valid_i ? decr_ind : '0; + + always_comb begin : proc_bucket_control + case ({incr_valid_i, decr_valid_i}) + 2'b00 : bucket_en = '0; + 2'b10 : bucket_en = incr_ind; + 2'b01 : bucket_en = decr_ind; + 2'b11 : bucket_en = incr_ind ^ decr_ind; + default: bucket_en = '0; // unreachable + endcase + end + + // ----------------------------------------- + // Counters + // ----------------------------------------- + for (genvar i = 0; i < NoCounters; i++) begin : gen_buckets + logic [BucketWidth-1:0] bucket_content; + counter #( + .WIDTH( BucketWidth ) + ) i_bucket ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( filter_clear_i ), + .en_i ( bucket_en[i] ), + .load_i ( '0 ), + .down_i ( bucket_down[i] ), + .d_i ( '0 ), + .q_o ( bucket_content ), + .overflow_o ( bucket_overflow[i]) + ); + assign bucket_full[i] = bucket_overflow[i] | (&bucket_content); + assign bucket_occupied[i] = |bucket_content; + assign bucket_empty[i] = ~bucket_occupied[i]; + end + + // ----------------------------------------- + // Filter tot item counter + // ----------------------------------------- + assign cnt_en = incr_valid_i ^ decr_valid_i; + assign cnt_down = decr_valid_i; + counter #( + .WIDTH ( HashWidth ) + ) i_tot_count ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( filter_clear_i ), + .en_i ( cnt_en ), + .load_i ( '0 ), + .down_i ( cnt_down ), + .d_i ( '0 ), + .q_o ( filter_usage_o ), + .overflow_o( cnt_overflow ) + ); + + // ----------------------------------------- + // Filter Output Flags + // ----------------------------------------- + assign filter_full_o = |bucket_full; + assign filter_empty_o = &bucket_empty; + assign filter_error_o = |bucket_overflow | cnt_overflow; +endmodule + +// gives out the or 'onehots' of all hash functions +module hash_block #( + parameter int unsigned NoHashes = 32'd3, + parameter int unsigned InpWidth = 32'd11, + parameter int unsigned HashWidth = 32'd5, + parameter int unsigned NoRounds = 32'd1, + parameter cb_filter_pkg::cb_seed_t [NoHashes-1:0] Seeds = cb_filter_pkg::EgSeeds +) ( + input logic [InpWidth-1:0] data_i, + output logic [2**HashWidth-1:0] indicator_o +); + + logic [NoHashes-1:0][2**HashWidth-1:0] hashes; + + for (genvar i = 0; i < NoHashes; i++) begin : gen_hashes + sub_per_hash #( + .InpWidth ( InpWidth ), + .HashWidth ( HashWidth ), + .NoRounds ( NoRounds ), + .PermuteKey ( Seeds[i].PermuteSeed ), + .XorKey ( Seeds[i].XorSeed ) + ) i_hash ( + .data_i ( data_i ), + .hash_o ( ), // not used, because we want the onehot + .hash_onehot_o ( hashes[i] ) + ); + end + + // output assignment + always_comb begin : proc_hash_or + indicator_o = '0; + for (int unsigned i = 0; i < (2**HashWidth); i++) begin + for (int unsigned j = 0; j < NoHashes; j++) begin + indicator_o[i] = indicator_o[i] | hashes[j][i]; + end + end + end + + // assertions + // pragma translate_off + initial begin + hash_conf: assume (InpWidth > HashWidth) else + $fatal(1, "%m:\nA Hash Function reduces the width of the input>\nInpWidth: %s\nOUT_WIDTH: %s", + InpWidth, HashWidth); + end + // pragma translate_on +endmodule diff --git a/vendor/common_cells/src/cb_filter_pkg.sv b/vendor/common_cells/src/cb_filter_pkg.sv new file mode 100644 index 00000000..97334475 --- /dev/null +++ b/vendor/common_cells/src/cb_filter_pkg.sv @@ -0,0 +1,26 @@ +// Copyright (c) 2019 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. + +// Author: Wolfgang Roenninger + +/// Package with the struct definition for the seeds and an example. +package cb_filter_pkg; + typedef struct packed { + int unsigned PermuteSeed; + int unsigned XorSeed; + } cb_seed_t; + + // example seeding struct + localparam cb_seed_t [2:0] EgSeeds = '{ + '{PermuteSeed: 32'd299034753, XorSeed: 32'd4094834 }, + '{PermuteSeed: 32'd19921030, XorSeed: 32'd995713 }, + '{PermuteSeed: 32'd294388, XorSeed: 32'd65146511 } + }; +endpackage diff --git a/vendor/common_cells/src/cdc_2phase.sv b/vendor/common_cells/src/cdc_2phase.sv new file mode 100644 index 00000000..8e770abf --- /dev/null +++ b/vendor/common_cells/src/cdc_2phase.sv @@ -0,0 +1,175 @@ +// 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. +// +// Fabian Schuiki + +/// A two-phase clock domain crossing. +/// +/// CONSTRAINT: Requires max_delay of min_period(src_clk_i, dst_clk_i) through +/// the paths async_req, async_ack, async_data. +/* verilator lint_off DECLFILENAME */ +module cdc_2phase #( + parameter type T = logic +)( + input logic src_rst_ni, + input logic src_clk_i, + input T src_data_i, + input logic src_valid_i, + output logic src_ready_o, + + input logic dst_rst_ni, + input logic dst_clk_i, + output T dst_data_o, + output logic dst_valid_o, + input logic dst_ready_i +); + + // Asynchronous handshake signals. + (* dont_touch = "true" *) logic async_req; + (* dont_touch = "true" *) logic async_ack; + (* dont_touch = "true" *) T async_data; + + // The sender in the source domain. + cdc_2phase_src #(.T(T)) i_src ( + .rst_ni ( src_rst_ni ), + .clk_i ( src_clk_i ), + .data_i ( src_data_i ), + .valid_i ( src_valid_i ), + .ready_o ( src_ready_o ), + .async_req_o ( async_req ), + .async_ack_i ( async_ack ), + .async_data_o ( async_data ) + ); + + // The receiver in the destination domain. + cdc_2phase_dst #(.T(T)) i_dst ( + .rst_ni ( dst_rst_ni ), + .clk_i ( dst_clk_i ), + .data_o ( dst_data_o ), + .valid_o ( dst_valid_o ), + .ready_i ( dst_ready_i ), + .async_req_i ( async_req ), + .async_ack_o ( async_ack ), + .async_data_i ( async_data ) + ); + +endmodule + + +/// Half of the two-phase clock domain crossing located in the source domain. +module cdc_2phase_src #( + parameter type T = logic +)( + input logic rst_ni, + input logic clk_i, + input T data_i, + input logic valid_i, + output logic ready_o, + output logic async_req_o, + input logic async_ack_i, + output T async_data_o +); + + (* dont_touch = "true" *) + logic req_src_q, ack_src_q, ack_q; + (* dont_touch = "true" *) + T data_src_q; + + // The req_src and data_src registers change when a new data item is accepted. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + req_src_q <= 0; + data_src_q <= '0; + end else if (valid_i && ready_o) begin + req_src_q <= ~req_src_q; + data_src_q <= data_i; + end + end + + // The ack_src and ack registers act as synchronization stages. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ack_src_q <= 0; + ack_q <= 0; + end else begin + ack_src_q <= async_ack_i; + ack_q <= ack_src_q; + end + end + + // Output assignments. + assign ready_o = (req_src_q == ack_q); + assign async_req_o = req_src_q; + assign async_data_o = data_src_q; + +endmodule + + +/// Half of the two-phase clock domain crossing located in the destination +/// domain. +module cdc_2phase_dst #( + parameter type T = logic +)( + input logic rst_ni, + input logic clk_i, + output T data_o, + output logic valid_o, + input logic ready_i, + input logic async_req_i, + output logic async_ack_o, + input T async_data_i +); + + (* dont_touch = "true" *) + (* async_reg = "true" *) + logic req_dst_q, req_q0, req_q1, ack_dst_q; + (* dont_touch = "true" *) + T data_dst_q; + + // The ack_dst register changes when a new data item is accepted. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ack_dst_q <= 0; + end else if (valid_o && ready_i) begin + ack_dst_q <= ~ack_dst_q; + end + end + + // The data_dst register changes when a new data item is presented. This is + // indicated by the async_req line changing levels. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + data_dst_q <= '0; + end else if (req_q0 != req_q1 && !valid_o) begin + data_dst_q <= async_data_i; + end + end + + // The req_dst and req registers act as synchronization stages. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + req_dst_q <= 0; + req_q0 <= 0; + req_q1 <= 0; + end else begin + req_dst_q <= async_req_i; + req_q0 <= req_dst_q; + req_q1 <= req_q0; + end + end + + // Output assignments. + assign valid_o = (ack_dst_q != req_q1); + assign data_o = data_dst_q; + assign async_ack_o = ack_dst_q; + +endmodule +/* verilator lint_on DECLFILENAME */ diff --git a/vendor/common_cells/src/cdc_fifo_2phase.sv b/vendor/common_cells/src/cdc_fifo_2phase.sv new file mode 100644 index 00000000..acbb7b0a --- /dev/null +++ b/vendor/common_cells/src/cdc_fifo_2phase.sv @@ -0,0 +1,134 @@ +// 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. +// +// Fabian Schuiki + +/// A clock domain crossing FIFO, using 2-phase hand shakes. +/// +/// This FIFO has its push and pop ports in two separate clock domains. Its size +/// can only be powers of two, which is why its depth is given as 2**LOG_DEPTH. +/// LOG_DEPTH must be at least 1. +/// +/// CONSTRAINT: See the constraints for `cdc_2phase`. An additional maximum +/// delay path needs to be specified from fifo_data_q to dst_data_o. +module cdc_fifo_2phase #( + /// The data type of the payload transported by the FIFO. + parameter type T = logic, + /// The FIFO's depth given as 2**LOG_DEPTH. + parameter int LOG_DEPTH = 3 +)( + input logic src_rst_ni, + input logic src_clk_i, + input T src_data_i, + input logic src_valid_i, + output logic src_ready_o, + + input logic dst_rst_ni, + input logic dst_clk_i, + output T dst_data_o, + output logic dst_valid_o, + input logic dst_ready_i +); + + // Check the invariants. + //pragma translate_off + initial begin + assert(LOG_DEPTH > 0); + end + //pragma translate_on + + localparam int PtrWidth = LOG_DEPTH+1; + typedef logic [PtrWidth-1:0] pointer_t; + typedef logic [LOG_DEPTH-1:0] index_t; + + localparam pointer_t PtrFull = (1 << LOG_DEPTH); + localparam pointer_t PtrEmpty = '0; + + // Allocate the registers for the FIFO memory with its separate write and read + // ports. The FIFO has the following ports: + // + // - write: fifo_widx, fifo_wdata, fifo_write, src_clk_i + // - read: fifo_ridx, fifo_rdata + index_t fifo_widx, fifo_ridx; + logic fifo_write; + T fifo_wdata, fifo_rdata; + T fifo_data_q [2**LOG_DEPTH]; + + assign fifo_rdata = fifo_data_q[fifo_ridx]; + + for (genvar i = 0; i < 2**LOG_DEPTH; i++) begin : g_word + always_ff @(posedge src_clk_i, negedge src_rst_ni) begin + if (!src_rst_ni) + fifo_data_q[i] <= '0; + else if (fifo_write && fifo_widx == i) + fifo_data_q[i] <= fifo_wdata; + end + end + + // Allocate the read and write pointers in the source and destination domain. + pointer_t src_wptr_q, dst_wptr, src_rptr, dst_rptr_q; + + always_ff @(posedge src_clk_i, negedge src_rst_ni) begin + if (!src_rst_ni) + src_wptr_q <= 0; + else if (src_valid_i && src_ready_o) + src_wptr_q <= src_wptr_q + 1; + end + + always_ff @(posedge dst_clk_i, negedge dst_rst_ni) begin + if (!dst_rst_ni) + dst_rptr_q <= 0; + else if (dst_valid_o && dst_ready_i) + dst_rptr_q <= dst_rptr_q + 1; + end + + // The pointers into the FIFO are one bit wider than the actual address into + // the FIFO. This makes detecting critical states very simple: if all but the + // topmost bit of rptr and wptr agree, the FIFO is in a critical state. If the + // topmost bit is equal, the FIFO is empty, otherwise it is full. + assign src_ready_o = ((src_wptr_q ^ src_rptr) != PtrFull); + assign dst_valid_o = ((dst_rptr_q ^ dst_wptr) != PtrEmpty); + + // Transport the read and write pointers across the clock domain boundary. + cdc_2phase #( .T(pointer_t) ) i_cdc_wptr ( + .src_rst_ni ( src_rst_ni ), + .src_clk_i ( src_clk_i ), + .src_data_i ( src_wptr_q ), + .src_valid_i ( 1'b1 ), + .src_ready_o ( ), + .dst_rst_ni ( dst_rst_ni ), + .dst_clk_i ( dst_clk_i ), + .dst_data_o ( dst_wptr ), + .dst_valid_o ( ), + .dst_ready_i ( 1'b1 ) + ); + + cdc_2phase #( .T(pointer_t) ) i_cdc_rptr ( + .src_rst_ni ( dst_rst_ni ), + .src_clk_i ( dst_clk_i ), + .src_data_i ( dst_rptr_q ), + .src_valid_i ( 1'b1 ), + .src_ready_o ( ), + .dst_rst_ni ( src_rst_ni ), + .dst_clk_i ( src_clk_i ), + .dst_data_o ( src_rptr ), + .dst_valid_o ( ), + .dst_ready_i ( 1'b1 ) + ); + + // Drive the FIFO write and read ports. + assign fifo_widx = src_wptr_q; + assign fifo_wdata = src_data_i; + assign fifo_write = src_valid_i && src_ready_o; + assign fifo_ridx = dst_rptr_q; + assign dst_data_o = fifo_rdata; + +endmodule diff --git a/vendor/common_cells/src/cdc_fifo_gray.sv b/vendor/common_cells/src/cdc_fifo_gray.sv new file mode 100644 index 00000000..802f2954 --- /dev/null +++ b/vendor/common_cells/src/cdc_fifo_gray.sv @@ -0,0 +1,269 @@ +// Copyright 2018-2019 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. +// +// Fabian Schuiki +// Florian Zaruba + +/// A clock domain crossing FIFO, using gray counters. +/// +/// # Architecture +/// +/// The design is split into two parts, each one being clocked and reset +/// separately. +/// 1. The data to be transferred over the clock domain boundary is +/// is stored in a FIFO. The corresponding write pointer is managed +/// (incremented) in the source clock domain. +/// 2. The entire FIFO content is exposed over the `async_data` port. +/// The destination clock domain increments its read pointer +/// in its destination clock domain. +/// +/// Read and write pointers are then gray coded, communicated +/// and synchronized using a classic multi-stage FF synchronizer +/// in the other clock domain. The gray coding ensures that only +/// one bit changes at each pointer increment, preventing the +/// synchronizer to accidentally latch an inconsistent state +/// on a multi-bit bus. +/// +/// The not full signal e.g. `src_ready_o` (on the sending side) +/// is generated using the local write pointer and the pessimistic +/// read pointer from the destination clock domain (pessimistic +/// because it is delayed at least two cycles because of the synchronizer +/// stages). This prevents the FIFO from overflowing. +/// +/// The not empty signal e.g. `dst_valid_o` is generated using +/// the pessimistic write pointer and the local read pointer in +/// the destination clock domain. This means the FIFO content +/// does not need to be synchronized as we are sure we are reading +/// data which has been written at least two cycles earlier. +/// Furthermore, the read select logic into the FIFO is completely +/// clocked by the destination clock domain which avoids +/// inefficient data synchronization. +/// +/// The FIFO size must be powers of two, which is why its depth is +/// given as 2**LOG_DEPTH. LOG_DEPTH must be at least 1. +/// +/// # Constraints +/// +/// We need to make sure that the propagation delay of the +/// data, read and write pointer is bound to the minimum of +/// either the sending or receiving clock period to prevent +/// an inconsistent state to be latched (if for example the one +/// bit of the read/write pointer have an excessive delay). +/// Furthermore, we should deactivate setup and hold checks on +/// the asynchronous signals. +/// +/// ``` +/// set_ungroup [get_designs cdc_fifo_gray*] false +/// set_boundary_optimization [get_designs cdc_fifo_gray*] false +/// set_max_delay min(T_src, T_dst) \ +/// -through [get_pins -hierarchical -filter async] \ +/// -through [get_pins -hierarchical -filter async] +/// set_false_path -hold \ +/// -through [get_pins -hierarchical -filter async] \ +/// -through [get_pins -hierarchical -filter async] +/// ``` + +`include "common_cells/registers.svh" + +(* no_ungroup *) +(* no_boundary_optimization *) +module cdc_fifo_gray #( + /// The width of the default logic type. + parameter int unsigned WIDTH = 1, + /// The data type of the payload transported by the FIFO. + parameter type T = logic [WIDTH-1:0], + /// The FIFO's depth given as 2**LOG_DEPTH. + parameter int LOG_DEPTH = 3, + /// The number of synchronization registers to insert on the async pointers. + parameter int SYNC_STAGES = 2 +) ( + input logic src_rst_ni, + input logic src_clk_i, + input T src_data_i, + input logic src_valid_i, + output logic src_ready_o, + + input logic dst_rst_ni, + input logic dst_clk_i, + output T dst_data_o, + output logic dst_valid_o, + input logic dst_ready_i +); + + T [2**LOG_DEPTH-1:0] async_data; + logic [LOG_DEPTH:0] async_wptr; + logic [LOG_DEPTH:0] async_rptr; + + cdc_fifo_gray_src #( + .T ( T ), + .LOG_DEPTH ( LOG_DEPTH ) + ) i_src ( + .src_rst_ni, + .src_clk_i, + .src_data_i, + .src_valid_i, + .src_ready_o, + + (* async *) .async_data_o ( async_data ), + (* async *) .async_wptr_o ( async_wptr ), + (* async *) .async_rptr_i ( async_rptr ) + ); + + cdc_fifo_gray_dst #( + .T ( T ), + .LOG_DEPTH ( LOG_DEPTH ) + ) i_dst ( + .dst_rst_ni, + .dst_clk_i, + .dst_data_o, + .dst_valid_o, + .dst_ready_i, + + (* async *) .async_data_i ( async_data ), + (* async *) .async_wptr_i ( async_wptr ), + (* async *) .async_rptr_o ( async_rptr ) + ); + + // Check the invariants. + // pragma translate_off + `ifndef VERILATOR + initial assert(LOG_DEPTH > 0); + initial assert(SYNC_STAGES >= 2); + `endif + // pragma translate_on + +endmodule + + +(* no_ungroup *) +(* no_boundary_optimization *) +module cdc_fifo_gray_src #( + parameter type T = logic, + parameter int LOG_DEPTH = 3, + parameter int SYNC_STAGES = 2 +)( + input logic src_rst_ni, + input logic src_clk_i, + input T src_data_i, + input logic src_valid_i, + output logic src_ready_o, + + output T [2**LOG_DEPTH-1:0] async_data_o, + output logic [LOG_DEPTH:0] async_wptr_o, + input logic [LOG_DEPTH:0] async_rptr_i +); + + localparam int PtrWidth = LOG_DEPTH+1; + localparam logic [PtrWidth-1:0] PtrFull = (1 << LOG_DEPTH); + + T [2**LOG_DEPTH-1:0] data_q; + logic [PtrWidth-1:0] wptr_q, wptr_d, wptr_bin, wptr_next, rptr, rptr_bin; + + // Data FIFO. + assign async_data_o = data_q; + for (genvar i = 0; i < 2**LOG_DEPTH; i++) begin : gen_word + `FFLNR(data_q[i], src_data_i, + src_valid_i & src_ready_o & (wptr_bin[LOG_DEPTH-1:0] == i), src_clk_i) + end + + // Read pointer. + for (genvar i = 0; i < PtrWidth; i++) begin : gen_sync + sync #(.STAGES(SYNC_STAGES)) i_sync ( + .clk_i ( src_clk_i ), + .rst_ni ( src_rst_ni ), + .serial_i ( async_rptr_i[i] ), + .serial_o ( rptr[i] ) + ); + end + gray_to_binary #(PtrWidth) i_rptr_g2b (.A(rptr), .Z(rptr_bin)); + + // Write pointer. + assign wptr_next = wptr_bin+1; + gray_to_binary #(PtrWidth) i_wptr_g2b (.A(wptr_q), .Z(wptr_bin)); + binary_to_gray #(PtrWidth) i_wptr_b2g (.A(wptr_next), .Z(wptr_d)); + `FFLARN(wptr_q, wptr_d, src_valid_i & src_ready_o, '0, src_clk_i, src_rst_ni) + assign async_wptr_o = wptr_q; + + // The pointers into the FIFO are one bit wider than the actual address into + // the FIFO. This makes detecting critical states very simple: if all but the + // topmost bit of rptr and wptr agree, the FIFO is in a critical state. If the + // topmost bit is equal, the FIFO is empty, otherwise it is full. + assign src_ready_o = ((wptr_bin ^ rptr_bin) != PtrFull); + +endmodule + + +(* no_ungroup *) +(* no_boundary_optimization *) +module cdc_fifo_gray_dst #( + parameter type T = logic, + parameter int LOG_DEPTH = 3, + parameter int SYNC_STAGES = 2 +)( + input logic dst_rst_ni, + input logic dst_clk_i, + output T dst_data_o, + output logic dst_valid_o, + input logic dst_ready_i, + + input T [2**LOG_DEPTH-1:0] async_data_i, + input logic [LOG_DEPTH:0] async_wptr_i, + output logic [LOG_DEPTH:0] async_rptr_o +); + + localparam int PtrWidth = LOG_DEPTH+1; + localparam logic [PtrWidth-1:0] PtrEmpty = '0; + + T dst_data; + logic [PtrWidth-1:0] rptr_q, rptr_d, rptr_bin, rptr_bin_d, rptr_next, wptr, wptr_bin; + logic dst_valid, dst_ready; + // Data selector and register. + assign dst_data = async_data_i[rptr_bin[LOG_DEPTH-1:0]]; + + // Read pointer. + assign rptr_next = rptr_bin+1; + gray_to_binary #(PtrWidth) i_rptr_g2b (.A(rptr_q), .Z(rptr_bin)); + binary_to_gray #(PtrWidth) i_rptr_b2g (.A(rptr_next), .Z(rptr_d)); + `FFLARN(rptr_q, rptr_d, dst_valid & dst_ready, '0, dst_clk_i, dst_rst_ni) + assign async_rptr_o = rptr_q; + + // Write pointer. + for (genvar i = 0; i < PtrWidth; i++) begin : gen_sync + sync #(.STAGES(SYNC_STAGES)) i_sync ( + .clk_i ( dst_clk_i ), + .rst_ni ( dst_rst_ni ), + .serial_i ( async_wptr_i[i] ), + .serial_o ( wptr[i] ) + ); + end + gray_to_binary #(PtrWidth) i_wptr_g2b (.A(wptr), .Z(wptr_bin)); + + // The pointers into the FIFO are one bit wider than the actual address into + // the FIFO. This makes detecting critical states very simple: if all but the + // topmost bit of rptr and wptr agree, the FIFO is in a critical state. If the + // topmost bit is equal, the FIFO is empty, otherwise it is full. + assign dst_valid = ((wptr_bin ^ rptr_bin) != PtrEmpty); + + // Cut the combinatorial path with a spill register. + spill_register #( + .T ( T ) + ) i_spill_register ( + .clk_i ( dst_clk_i ), + .rst_ni ( dst_rst_ni ), + .valid_i ( dst_valid ), + .ready_o ( dst_ready ), + .data_i ( dst_data ), + .valid_o ( dst_valid_o ), + .ready_i ( dst_ready_i ), + .data_o ( dst_data_o ) + ); + +endmodule diff --git a/vendor/common_cells/src/cf_math_pkg.sv b/vendor/common_cells/src/cf_math_pkg.sv new file mode 100644 index 00000000..9f35a44e --- /dev/null +++ b/vendor/common_cells/src/cf_math_pkg.sv @@ -0,0 +1,61 @@ +// Copyright 2016 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. + +/// cf_math_pkg: Constant Function Implementations of Mathematical Functions for HDL Elaboration +/// +/// This package contains a collection of mathematical functions that are commonly used when defining +/// the value of constants in HDL code. These functions are implemented as Verilog constants +/// functions. Introduced in Verilog 2001 (IEEE Std 1364-2001), a constant function (§ 10.3.5) is a +/// function whose value can be evaluated at compile time or during elaboration. A constant function +/// must be called with arguments that are constants. +package cf_math_pkg; + + /// Ceiled Division of Two Natural Numbers + /// + /// Returns the quotient of two natural numbers, rounded towards plus infinity. + function automatic integer ceil_div (input longint dividend, input longint divisor); + automatic longint remainder; + + // pragma translate_off + `ifndef VERILATOR + if (dividend < 0) begin + $fatal(1, "Dividend %0d is not a natural number!", dividend); + end + + if (divisor < 0) begin + $fatal(1, "Divisor %0d is not a natural number!", divisor); + end + + if (divisor == 0) begin + $fatal(1, "Division by zero!"); + end + `endif + // pragma translate_on + + remainder = dividend; + for (ceil_div = 0; remainder > 0; ceil_div++) begin + remainder = remainder - divisor; + end + endfunction + + /// Index width required to be able to represent up to `num_idx` indices as a binary + /// encoded signal. + /// Ensures that the minimum width if an index signal is `1`, regardless of parametrization. + /// + /// Sample usage in type definition: + /// As parameter: + /// `parameter type idx_t = logic[cf_math_pkg::idx_width(NumIdx)-1:0]` + /// As typedef: + /// `typedef logic [cf_math_pkg::idx_width(NumIdx)-1:0] idx_t` + function automatic integer unsigned idx_width (input integer unsigned num_idx); + return (num_idx > 32'd1) ? unsigned'($clog2(num_idx)) : 32'd1; + endfunction + +endpackage diff --git a/vendor/common_cells/src/clk_div.sv b/vendor/common_cells/src/clk_div.sv new file mode 100644 index 00000000..b1df809f --- /dev/null +++ b/vendor/common_cells/src/clk_div.sv @@ -0,0 +1,42 @@ +// 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. + +// Author: Florian Zaruba +// Description: Divides the clock by an integer factor +module clk_div #( + parameter int unsigned RATIO = 4 +)( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic testmode_i, // testmode + input logic en_i, // enable clock divider + output logic clk_o // divided clock out +); + logic [RATIO-1:0] counter_q; + logic clk_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + clk_q <= 1'b0; + counter_q <= '0; + end else begin + clk_q <= 1'b0; + if (en_i) begin + if (counter_q == (RATIO[RATIO-1:0] - 1)) begin + clk_q <= 1'b1; + end else begin + counter_q <= counter_q + 1; + end + end + end + end + // output assignment - bypass in testmode + assign clk_o = testmode_i ? clk_i : clk_q; +endmodule diff --git a/vendor/common_cells/src/counter.sv b/vendor/common_cells/src/counter.sv new file mode 100644 index 00000000..43392e4b --- /dev/null +++ b/vendor/common_cells/src/counter.sv @@ -0,0 +1,43 @@ +// 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. + +// Author: Florian Zaruba +// Description: Generic up/down counter + +module counter #( + parameter int unsigned WIDTH = 4, + parameter bit STICKY_OVERFLOW = 1'b0 +)( + input logic clk_i, + input logic rst_ni, + input logic clear_i, // synchronous clear + input logic en_i, // enable the counter + input logic load_i, // load a new value + input logic down_i, // downcount, default is up + input logic [WIDTH-1:0] d_i, + output logic [WIDTH-1:0] q_o, + output logic overflow_o +); + delta_counter #( + .WIDTH (WIDTH), + .STICKY_OVERFLOW (STICKY_OVERFLOW) + ) i_counter ( + .clk_i, + .rst_ni, + .clear_i, + .en_i, + .load_i, + .down_i, + .delta_i({{WIDTH-1{1'b0}}, 1'b1}), + .d_i, + .q_o, + .overflow_o + ); +endmodule diff --git a/vendor/common_cells/src/delta_counter.sv b/vendor/common_cells/src/delta_counter.sv new file mode 100644 index 00000000..90b5cffa --- /dev/null +++ b/vendor/common_cells/src/delta_counter.sv @@ -0,0 +1,74 @@ +// 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. + +// Up/down counter with variable delta + +module delta_counter #( + parameter int unsigned WIDTH = 4, + parameter bit STICKY_OVERFLOW = 1'b0 +)( + input logic clk_i, + input logic rst_ni, + input logic clear_i, // synchronous clear + input logic en_i, // enable the counter + input logic load_i, // load a new value + input logic down_i, // downcount, default is up + input logic [WIDTH-1:0] delta_i, + input logic [WIDTH-1:0] d_i, + output logic [WIDTH-1:0] q_o, + output logic overflow_o +); + logic [WIDTH:0] counter_q, counter_d; + if (STICKY_OVERFLOW) begin : gen_sticky_overflow + logic overflow_d, overflow_q; + always_ff @(posedge clk_i or negedge rst_ni) overflow_q <= ~rst_ni ? 1'b0 : overflow_d; + always_comb begin + overflow_d = overflow_q; + if (clear_i || load_i) begin + overflow_d = 1'b0; + end else if (!overflow_q && en_i) begin + if (down_i) begin + overflow_d = delta_i > counter_q[WIDTH-1:0]; + end else begin + overflow_d = counter_q[WIDTH-1:0] > ({WIDTH{1'b1}} - delta_i); + end + end + end + assign overflow_o = overflow_q; + end else begin : gen_transient_overflow + // counter overflowed if the MSB is set + assign overflow_o = counter_q[WIDTH]; + end + assign q_o = counter_q[WIDTH-1:0]; + + always_comb begin + counter_d = counter_q; + + if (clear_i) begin + counter_d = '0; + end else if (load_i) begin + counter_d = {1'b0, d_i}; + end else if (en_i) begin + if (down_i) begin + counter_d = counter_q - delta_i; + end else begin + counter_d = counter_q + delta_i; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + counter_q <= '0; + end else begin + counter_q <= counter_d; + end + end +endmodule diff --git a/vendor/common_cells/src/deprecated/clock_divider.sv b/vendor/common_cells/src/deprecated/clock_divider.sv new file mode 100644 index 00000000..343b0a23 --- /dev/null +++ b/vendor/common_cells/src/deprecated/clock_divider.sv @@ -0,0 +1,191 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +// // +// Company: Multitherman Laboratory @ DEIS - University of Bologna // +// Viale Risorgimento 2 40136 // +// Bologna - fax 0512093785 - // +// // +// Engineer: Antonio Pullini - pullinia@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 13/02/2013 // +// Design Name: ULPSoC // +// Module Name: clock_divider // +// Project Name: ULPSoC // +// Language: SystemVerilog // +// // +// Description: Clock Divider // +// // +// // +// Revision: // +// Revision v0.1 - File Created // +// Revision v0.2 - (19/03/2015) clock_gating swapped in pulp_clock_gating // +// // +// // +// // +// // +// // +// // +//////////////////////////////////////////////////////////////////////////////// + +module clock_divider +#( + parameter DIV_INIT = 0, + parameter BYPASS_INIT = 1 +) +( + input logic clk_i, + input logic rstn_i, + input logic test_mode_i, + input logic clk_gate_async_i, + input logic [7:0] clk_div_data_i, + input logic clk_div_valid_i, + output logic clk_div_ack_o, + output logic clk_o +); + + enum logic [1:0] {IDLE, STOP, WAIT, RELEASE} state, state_next; + + logic s_clk_out; + logic s_clock_enable; + logic s_clock_enable_gate; + logic s_clk_div_valid; + + logic [7:0] reg_clk_div; + logic s_clk_div_valid_sync; + + logic s_rstn_sync; + + logic [1:0] reg_ext_gate_sync; + + assign s_clock_enable_gate = s_clock_enable & reg_ext_gate_sync; + +`ifndef PULP_FPGA_EMUL + rstgen i_rst_gen + ( + // PAD FRAME SIGNALS + .clk_i(clk_i), + .rst_ni(rstn_i), //async signal coming from pads + + // TEST MODE + .test_mode_i(test_mode_i), + + // OUTPUT RESET + .rst_no(s_rstn_sync), + .init_no() //not used + ); + `else + assign s_rstn_sync = rstn_i; +`endif + + + //handle the handshake with the soc_ctrl. Interface is now async + pulp_sync_wedge i_edge_prop + ( + .clk_i(clk_i), + .rstn_i(s_rstn_sync), + .en_i(1'b1), + .serial_i(clk_div_valid_i), + .serial_o(clk_div_ack_o), + .r_edge_o(s_clk_div_valid_sync), + .f_edge_o() + ); + + clock_divider_counter + #( + .BYPASS_INIT(BYPASS_INIT), + .DIV_INIT(DIV_INIT) + ) + i_clkdiv_cnt + ( + .clk(clk_i), + .rstn(s_rstn_sync), + .test_mode(test_mode_i), + .clk_div(reg_clk_div), + .clk_div_valid(s_clk_div_valid), + .clk_out(s_clk_out) + ); + + pulp_clock_gating i_clk_gate + ( + .clk_i(s_clk_out), + .en_i(s_clock_enable_gate), + .test_en_i(test_mode_i), + .clk_o(clk_o) + ); + + always_comb + begin + case(state) + IDLE: + begin + s_clock_enable = 1'b1; + s_clk_div_valid = 1'b0; + if (s_clk_div_valid_sync) + state_next = STOP; + else + state_next = IDLE; + end + + STOP: + begin + s_clock_enable = 1'b0; + s_clk_div_valid = 1'b1; + state_next = WAIT; + end + + WAIT: + begin + s_clock_enable = 1'b0; + s_clk_div_valid = 1'b0; + state_next = RELEASE; + end + + RELEASE: + begin + s_clock_enable = 1'b0; + s_clk_div_valid = 1'b0; + state_next = IDLE; + end + endcase + end + + always_ff @(posedge clk_i or negedge s_rstn_sync) + begin + if (!s_rstn_sync) + state <= IDLE; + else + state <= state_next; + end + + //sample the data when valid has been sync and there is a rise edge + always_ff @(posedge clk_i or negedge s_rstn_sync) + begin + if (!s_rstn_sync) + reg_clk_div <= '0; + else if (s_clk_div_valid_sync) + reg_clk_div <= clk_div_data_i; + end + + //sample the data when valid has been sync and there is a rise edge + always_ff @(posedge clk_i or negedge s_rstn_sync) + begin + if (!s_rstn_sync) + reg_ext_gate_sync <= 2'b00; + else + reg_ext_gate_sync <= {clk_gate_async_i, reg_ext_gate_sync[1]}; + end + +endmodule diff --git a/vendor/common_cells/src/deprecated/clock_divider_counter.sv b/vendor/common_cells/src/deprecated/clock_divider_counter.sv new file mode 100644 index 00000000..e5c222af --- /dev/null +++ b/vendor/common_cells/src/deprecated/clock_divider_counter.sv @@ -0,0 +1,211 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////////// +// Company: Multitherman Laboratory @ DEIS - University of Bologna // +// Viale Risorgimento 2 40136 // +// Bologna - fax 0512093785 - // +// // +// Engineer: Antonio Pullini - pullinia@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 13/02/2013 // +// Design Name: ULPSoC // +// Module Name: clock_divider_counter // +// Project Name: ULPSoC // +// Language: SystemVerilog // +// // +// Description: clock_divider_counter // +// // +// // +// Revision: // +// Revision v0.1 - File Created // +// Revision v0.2 - (19/03/2015) clock_gating swapped in pulp_clock_gating // +// // +// // +// // +// // +// // +// // +//////////////////////////////////////////////////////////////////////////////// + + +module clock_divider_counter +#( + parameter BYPASS_INIT = 1, + parameter DIV_INIT = 'hFF +) +( + input logic clk, + input logic rstn, + input logic test_mode, + input logic [7:0] clk_div, + input logic clk_div_valid, + output logic clk_out +); + + logic [7:0] counter; + logic [7:0] counter_next; + logic [7:0] clk_cnt; + logic en1; + logic en2; + + logic is_odd; + + logic div1; + logic div2; + logic div2_neg_sync; + + logic [7:0] clk_cnt_odd; + logic [7:0] clk_cnt_odd_incr; + logic [7:0] clk_cnt_even; + logic [7:0] clk_cnt_en2; + + logic bypass; + + logic clk_out_gen; + logic clk_div_valid_reg; + + logic clk_inv_test; + logic clk_inv; + + // assign clk_cnt_odd_incr = clk_div + 1; + // assign clk_cnt_odd = {1'b0,clk_cnt_odd_incr[7:1]}; //if odd divider than clk_cnt = (clk_div+1)/2 + assign clk_cnt_odd = clk_div - 8'h1; //if odd divider than clk_cnt = clk_div - 1 + assign clk_cnt_even = (clk_div == 8'h2) ? 8'h0 : ({1'b0,clk_div[7:1]} - 8'h1); //if even divider than clk_cnt = clk_div/2 + assign clk_cnt_en2 = {1'b0,clk_cnt[7:1]} + 8'h1; + + always_comb + begin + if (counter == 'h0) + en1 = 1'b1; + else + en1 = 1'b0; + + if (clk_div_valid) + counter_next = 'h0; + else if (counter == clk_cnt) + counter_next = 'h0; + else + counter_next = counter + 1; + + if (clk_div_valid) + en2 = 1'b0; + else if (counter == clk_cnt_en2) + en2 = 1'b1; + else + en2 = 1'b0; + end + + always_ff @(posedge clk, negedge rstn) + begin + if (~rstn) + begin + counter <= 'h0; + div1 <= 1'b0; + bypass <= BYPASS_INIT; + clk_cnt <= DIV_INIT; + is_odd <= 1'b0; + clk_div_valid_reg <= 1'b0; + end + else + begin + if (!bypass) + counter <= counter_next; + + clk_div_valid_reg <= clk_div_valid; + if (clk_div_valid) + begin + if ((clk_div == 8'h0) || (clk_div == 8'h1)) + begin + bypass <= 1'b1; + clk_cnt <= 'h0; + is_odd <= 1'b0; + end + else + begin + bypass <= 1'b0; + if (clk_div[0]) + begin + is_odd <= 1'b1; + clk_cnt <= clk_cnt_odd; + end + else + begin + is_odd <= 1'b0; + clk_cnt <= clk_cnt_even; + end + end + div1 <= 1'b0; + end + else + begin + if (en1 && !bypass) + div1 <= ~div1; + end + end + end + + pulp_clock_inverter clk_inv_i + ( + .clk_i(clk), + .clk_o(clk_inv) + ); + +`ifndef PULP_FPGA_EMUL + `ifdef PULP_DFT + pulp_clock_mux2 clk_muxinv_i + ( + .clk0_i(clk_inv), + .clk1_i(clk), + .clk_sel_i(test_mode), + .clk_o(clk_inv_test) + ); + `else + assign clk_inv_test = clk_inv; + `endif +`else + assign clk_inv_test = clk_inv; +`endif + + always_ff @(posedge clk_inv_test or negedge rstn) + begin + if (!rstn) + begin + div2 <= 1'b0; + end + else + begin + if (clk_div_valid_reg) + div2 <= 1'b0; + else if (en2 && is_odd && !bypass) + div2 <= ~div2; + end + end // always_ff @ (posedge clk_inv_test or negedge rstn) + + pulp_clock_xor2 clock_xor_i + ( + .clk_o(clk_out_gen), + .clk0_i(div1), + .clk1_i(div2) + ); + + pulp_clock_mux2 clk_mux_i + ( + .clk0_i(clk_out_gen), + .clk1_i(clk), + .clk_sel_i(bypass || test_mode), + .clk_o(clk_out) + ); + +endmodule diff --git a/vendor/common_cells/src/deprecated/fifo_v1.sv b/vendor/common_cells/src/deprecated/fifo_v1.sv new file mode 100644 index 00000000..31295e80 --- /dev/null +++ b/vendor/common_cells/src/deprecated/fifo_v1.sv @@ -0,0 +1,57 @@ +// 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. + +// Author: Florian Zaruba + +/* verilator lint_off DECLFILENAME */ +module fifo #( + parameter bit FALL_THROUGH = 1'b0, // fifo is in fall-through mode + parameter int unsigned DATA_WIDTH = 32, // default data width if the fifo is of type logic + parameter int unsigned DEPTH = 8, // depth can be arbitrary from 0 to 2**32 + parameter int unsigned THRESHOLD = 1, // fill count until when to assert threshold_o + parameter type dtype = logic [DATA_WIDTH-1:0] +)( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, // flush the queue + input logic testmode_i, // test_mode to bypass clock gating + // status flags + output logic full_o, // queue is full + output logic empty_o, // queue is empty + output logic threshold_o, // the FIFO is above the specified threshold + // as long as the queue is not full we can push new data + input dtype data_i, // data to push into the queue + input logic push_i, // data is valid and can be pushed to the queue + // as long as the queue is not empty we can pop new elements + output dtype data_o, // output data + input logic pop_i // pop head from queue +); + fifo_v2 #( + .FALL_THROUGH ( FALL_THROUGH ), + .DATA_WIDTH ( DATA_WIDTH ), + .DEPTH ( DEPTH ), + .ALM_FULL_TH ( THRESHOLD ), + .dtype ( dtype ) + ) impl ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( flush_i ), + .testmode_i ( testmode_i ), + .full_o ( full_o ), + .empty_o ( empty_o ), + .alm_full_o ( threshold_o ), + .alm_empty_o ( ), + .data_i ( data_i ), + .push_i ( push_i ), + .data_o ( data_o ), + .pop_i ( pop_i ) + ); +endmodule +/* verilator lint_on DECLFILENAME */ diff --git a/vendor/common_cells/src/deprecated/fifo_v2.sv b/vendor/common_cells/src/deprecated/fifo_v2.sv new file mode 100644 index 00000000..9c87ed96 --- /dev/null +++ b/vendor/common_cells/src/deprecated/fifo_v2.sv @@ -0,0 +1,79 @@ +// 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. + +// Author: Florian Zaruba + +module fifo_v2 #( + parameter bit FALL_THROUGH = 1'b0, // fifo is in fall-through mode + parameter int unsigned DATA_WIDTH = 32, // default data width if the fifo is of type logic + parameter int unsigned DEPTH = 8, // depth can be arbitrary from 0 to 2**32 + parameter int unsigned ALM_EMPTY_TH = 1, // almost empty threshold (when to assert alm_empty_o) + parameter int unsigned ALM_FULL_TH = 1, // almost full threshold (when to assert alm_full_o) + parameter type dtype = logic [DATA_WIDTH-1:0], + // DO NOT OVERWRITE THIS PARAMETER + parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1 +)( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, // flush the queue + input logic testmode_i, // test_mode to bypass clock gating + // status flags + output logic full_o, // queue is full + output logic empty_o, // queue is empty + output logic alm_full_o, // FIFO fillstate >= the specified threshold + output logic alm_empty_o, // FIFO fillstate <= the specified threshold + // as long as the queue is not full we can push new data + input dtype data_i, // data to push into the queue + input logic push_i, // data is valid and can be pushed to the queue + // as long as the queue is not empty we can pop new elements + output dtype data_o, // output data + input logic pop_i // pop head from queue +); + + logic [ADDR_DEPTH-1:0] usage; + + // generate threshold parameters + if (DEPTH == 0) begin + assign alm_full_o = 1'b0; // that signal does not make any sense in a FIFO of depth 0 + assign alm_empty_o = 1'b0; // that signal does not make any sense in a FIFO of depth 0 + end else begin + assign alm_full_o = (usage >= ALM_FULL_TH[ADDR_DEPTH-1:0]); + assign alm_empty_o = (usage <= ALM_EMPTY_TH[ADDR_DEPTH-1:0]); + end + + fifo_v3 #( + .FALL_THROUGH ( FALL_THROUGH ), + .DATA_WIDTH ( DATA_WIDTH ), + .DEPTH ( DEPTH ), + .dtype ( dtype ) + ) i_fifo_v3 ( + .clk_i, + .rst_ni, + .flush_i, + .testmode_i, + .full_o, + .empty_o, + .usage_o (usage), + .data_i, + .push_i, + .data_o, + .pop_i + ); + + // pragma translate_off + `ifndef VERILATOR + initial begin + assert (ALM_FULL_TH <= DEPTH) else $error("ALM_FULL_TH can't be larger than the DEPTH."); + assert (ALM_EMPTY_TH <= DEPTH) else $error("ALM_EMPTY_TH can't be larger than the DEPTH."); + end + `endif + // pragma translate_on + +endmodule // fifo_v2 diff --git a/vendor/common_cells/src/deprecated/find_first_one.sv b/vendor/common_cells/src/deprecated/find_first_one.sv new file mode 100644 index 00000000..ee3ba20f --- /dev/null +++ b/vendor/common_cells/src/deprecated/find_first_one.sv @@ -0,0 +1,83 @@ +// 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. + +// Deprecated, use lzc unit instead. + +/// A leading-one finder / leading zero counter. +/// Set FLIP to 0 for find_first_one => first_one_o is the index of the first one (from the LSB) +/// Set FLIP to 1 for leading zero counter => first_one_o is the number of leading zeroes (from the MSB) +module find_first_one #( + /// The width of the input vector. + parameter int WIDTH = -1, + parameter int FLIP = 0 +)( + input logic [WIDTH-1:0] in_i, + output logic [$clog2(WIDTH)-1:0] first_one_o, + output logic no_ones_o +); + + localparam int NUM_LEVELS = $clog2(WIDTH); + + // pragma translate_off + initial begin + assert(WIDTH >= 0); + end + // pragma translate_on + + logic [WIDTH-1:0][NUM_LEVELS-1:0] index_lut; + logic [2**NUM_LEVELS-1:0] sel_nodes; + logic [2**NUM_LEVELS-1:0][NUM_LEVELS-1:0] index_nodes; + + logic [WIDTH-1:0] in_tmp; + + for (genvar i = 0; i < WIDTH; i++) begin + assign in_tmp[i] = FLIP ? in_i[WIDTH-1-i] : in_i[i]; + end + + for (genvar j = 0; j < WIDTH; j++) begin + assign index_lut[j] = j; + end + + for (genvar level = 0; level < NUM_LEVELS; level++) begin + + if (level < NUM_LEVELS-1) begin + for (genvar l = 0; l < 2**level; l++) begin + assign sel_nodes[2**level-1+l] = sel_nodes[2**(level+1)-1+l*2] | sel_nodes[2**(level+1)-1+l*2+1]; + assign index_nodes[2**level-1+l] = (sel_nodes[2**(level+1)-1+l*2] == 1'b1) ? + index_nodes[2**(level+1)-1+l*2] : index_nodes[2**(level+1)-1+l*2+1]; + end + end + + if (level == NUM_LEVELS-1) begin + for (genvar k = 0; k < 2**level; k++) begin + // if two successive indices are still in the vector... + if (k * 2 < WIDTH-1) begin + assign sel_nodes[2**level-1+k] = in_tmp[k*2] | in_tmp[k*2+1]; + assign index_nodes[2**level-1+k] = (in_tmp[k*2] == 1'b1) ? index_lut[k*2] : index_lut[k*2+1]; + end + // if only the first index is still in the vector... + if (k * 2 == WIDTH-1) begin + assign sel_nodes[2**level-1+k] = in_tmp[k*2]; + assign index_nodes[2**level-1+k] = index_lut[k*2]; + end + // if index is out of range + if (k * 2 > WIDTH-1) begin + assign sel_nodes[2**level-1+k] = 1'b0; + assign index_nodes[2**level-1+k] = '0; + end + end + end + end + + assign first_one_o = NUM_LEVELS > 0 ? index_nodes[0] : '0; + assign no_ones_o = NUM_LEVELS > 0 ? ~sel_nodes[0] : '1; + +endmodule diff --git a/vendor/common_cells/src/deprecated/generic_LFSR_8bit.sv b/vendor/common_cells/src/deprecated/generic_LFSR_8bit.sv new file mode 100644 index 00000000..fb0080ac --- /dev/null +++ b/vendor/common_cells/src/deprecated/generic_LFSR_8bit.sv @@ -0,0 +1,64 @@ +// 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. + +// Igor Loi + +module generic_LFSR_8bit + #( + parameter OH_WIDTH = 4, + parameter BIN_WIDTH = $clog2(OH_WIDTH), + parameter SEED = 8'b00000000 + ) + ( + output logic [OH_WIDTH-1:0] data_OH_o, // One hot encoding + output logic [BIN_WIDTH-1:0] data_BIN_o, // Binary encoding + input logic enable_i, // + input logic clk, // + input logic rst_n // + ); + + logic [7:0] out; + logic linear_feedback; + logic [BIN_WIDTH-1:0] temp_ref_way; + + + //-------------Code Starts Here------- + assign linear_feedback = !(out[7] ^ out[3] ^ out[2] ^ out[1]); // TAPS for XOR feedback + + assign data_BIN_o = temp_ref_way; + + always_ff @(posedge clk, negedge rst_n) + begin + if (rst_n == 1'b0) + begin + out <= SEED ; + end + else if (enable_i) + begin + out <= {out[6],out[5],out[4],out[3],out[2],out[1],out[0], linear_feedback}; + end + end + + generate + + if(OH_WIDTH == 2) + assign temp_ref_way = out[1]; + else + assign temp_ref_way = out[BIN_WIDTH:1]; + endgenerate + + // Bin to One Hot Encoder + always_comb + begin + data_OH_o = '0; + data_OH_o[temp_ref_way] = 1'b1; + end + +endmodule diff --git a/vendor/common_cells/src/deprecated/generic_fifo.sv b/vendor/common_cells/src/deprecated/generic_fifo.sv new file mode 100644 index 00000000..ece4aac7 --- /dev/null +++ b/vendor/common_cells/src/deprecated/generic_fifo.sv @@ -0,0 +1,274 @@ +// 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. + +// ============================================================================= // +// Company: Multitherman Laboratory @ DEIS - University of Bologna // +// Viale Risorgimento 2 40136 // +// Bologna - fax 0512093785 - // +// // +// Engineer: Igor Loi - igor.loi@unibo.it // +// // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 01/02/2014 // +// Design Name: MISC // +// Module Name: generic_fifo // +// Project Name: PULP // +// Language: SystemVerilog // +// // +// Description: A simple FIFO used in the D_address_decoder, and D_allocator // +// to store the destinations ports // +// // +// Revision: // +// Revision v0.1 - 01/02/2014 : File Created // +// Revision v0.2 - 02/09/2015 : Updated with a global CG cell // +// // +// ============================================================================= // + +module generic_fifo +#( + parameter int unsigned DATA_WIDTH = 32, + parameter int unsigned DATA_DEPTH = 8 +) +( + input logic clk, + input logic rst_n, + //PUSH SIDE + input logic [DATA_WIDTH-1:0] data_i, + input logic valid_i, + output logic grant_o, + //POP SIDE + output logic [DATA_WIDTH-1:0] data_o, + output logic valid_o, + input logic grant_i, + + input logic test_mode_i +); + + + // Local Parameter + localparam int unsigned ADDR_DEPTH = $clog2(DATA_DEPTH); + enum logic [1:0] { EMPTY, FULL, MIDDLE } CS, NS; + // Internal Signals + + logic gate_clock; + logic clk_gated; + + logic [ADDR_DEPTH-1:0] Pop_Pointer_CS, Pop_Pointer_NS; + logic [ADDR_DEPTH-1:0] Push_Pointer_CS, Push_Pointer_NS; + logic [DATA_WIDTH-1:0] FIFO_REGISTERS[DATA_DEPTH-1:0]; + int unsigned i; + + // Parameter Check + // synopsys translate_off + initial begin : parameter_check + integer param_err_flg; + param_err_flg = 0; + + if (DATA_WIDTH < 1) begin + param_err_flg = 1; + $display("ERROR: %m :\n Invalid value (%d) for parameter DATA_WIDTH (legal range: greater than 1)", DATA_WIDTH ); + end + + if (DATA_DEPTH < 1) begin + param_err_flg = 1; + $display("ERROR: %m :\n Invalid value (%d) for parameter DATA_DEPTH (legal range: greater than 1)", DATA_DEPTH ); + end + end + // synopsys translate_on + +`ifndef PULP_FPGA_EMUL + cluster_clock_gating cg_cell + ( + .clk_i ( clk ), + .en_i (~gate_clock ), + .test_en_i ( test_mode_i ), + .clk_o ( clk_gated ) + ); +`else + assign clk_gated = clk; +`endif + + // UPDATE THE STATE + always_ff @(posedge clk, negedge rst_n) + begin + if(rst_n == 1'b0) + begin + CS <= EMPTY; + Pop_Pointer_CS <= {ADDR_DEPTH {1'b0}}; + Push_Pointer_CS <= {ADDR_DEPTH {1'b0}}; + end + else + begin + CS <= NS; + Pop_Pointer_CS <= Pop_Pointer_NS; + Push_Pointer_CS <= Push_Pointer_NS; + end + end + + + // Compute Next State + always_comb + begin + gate_clock = 1'b0; + + case(CS) + + EMPTY: + begin + grant_o = 1'b1; + valid_o = 1'b0; + + case(valid_i) + 1'b0 : + begin + NS = EMPTY; + Push_Pointer_NS = Push_Pointer_CS; + Pop_Pointer_NS = Pop_Pointer_CS; + gate_clock = 1'b1; + end + + 1'b1: + begin + NS = MIDDLE; + Push_Pointer_NS = Push_Pointer_CS + 1'b1; + Pop_Pointer_NS = Pop_Pointer_CS; + end + + endcase + end//~EMPTY + + MIDDLE: + begin + grant_o = 1'b1; + valid_o = 1'b1; + + case({valid_i,grant_i}) + + 2'b01: + begin + gate_clock = 1'b1; + + if((Pop_Pointer_CS == Push_Pointer_CS -1 ) || ((Pop_Pointer_CS == DATA_DEPTH-1) && (Push_Pointer_CS == 0) )) + NS = EMPTY; + else + NS = MIDDLE; + + Push_Pointer_NS = Push_Pointer_CS; + + if(Pop_Pointer_CS == DATA_DEPTH-1) + Pop_Pointer_NS = 0; + else + Pop_Pointer_NS = Pop_Pointer_CS + 1'b1; + end + + 2'b00 : + begin + gate_clock = 1'b1; + NS = MIDDLE; + Push_Pointer_NS = Push_Pointer_CS; + Pop_Pointer_NS = Pop_Pointer_CS; + end + + 2'b11: + begin + NS = MIDDLE; + + if(Push_Pointer_CS == DATA_DEPTH-1) + Push_Pointer_NS = 0; + else + Push_Pointer_NS = Push_Pointer_CS + 1'b1; + + if(Pop_Pointer_CS == DATA_DEPTH-1) + Pop_Pointer_NS = 0; + else + Pop_Pointer_NS = Pop_Pointer_CS + 1'b1; + end + + 2'b10: + begin + if(( Push_Pointer_CS == Pop_Pointer_CS - 1) || ( (Push_Pointer_CS == DATA_DEPTH-1) && (Pop_Pointer_CS == 0) )) + NS = FULL; + else + NS = MIDDLE; + + if(Push_Pointer_CS == DATA_DEPTH - 1) + Push_Pointer_NS = 0; + else + Push_Pointer_NS = Push_Pointer_CS + 1'b1; + + Pop_Pointer_NS = Pop_Pointer_CS; + end + + endcase + end + + FULL: + begin + grant_o = 1'b0; + valid_o = 1'b1; + gate_clock = 1'b1; + + case(grant_i) + 1'b1: + begin + NS = MIDDLE; + + Push_Pointer_NS = Push_Pointer_CS; + + if(Pop_Pointer_CS == DATA_DEPTH-1) + Pop_Pointer_NS = 0; + else + Pop_Pointer_NS = Pop_Pointer_CS + 1'b1; + end + + 1'b0: + begin + NS = FULL; + Push_Pointer_NS = Push_Pointer_CS; + Pop_Pointer_NS = Pop_Pointer_CS; + end + endcase + + end // end of FULL + + default : + begin + gate_clock = 1'b1; + grant_o = 1'b0; + valid_o = 1'b0; + NS = EMPTY; + Pop_Pointer_NS = 0; + Push_Pointer_NS = 0; + end + + endcase + end + + always_ff @(posedge clk_gated, negedge rst_n) + begin + if(rst_n == 1'b0) + begin + for (i=0; i< DATA_DEPTH; i++) + FIFO_REGISTERS[i] <= {DATA_WIDTH {1'b0}}; + end + else + begin + if((grant_o == 1'b1) && (valid_i == 1'b1)) + FIFO_REGISTERS[Push_Pointer_CS] <= data_i; + end + end + + assign data_o = FIFO_REGISTERS[Pop_Pointer_CS]; + +endmodule // generic_fifo diff --git a/vendor/common_cells/src/deprecated/generic_fifo_adv.sv b/vendor/common_cells/src/deprecated/generic_fifo_adv.sv new file mode 100644 index 00000000..df6cc0d7 --- /dev/null +++ b/vendor/common_cells/src/deprecated/generic_fifo_adv.sv @@ -0,0 +1,264 @@ +// 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. + +// Igor Loi + +module generic_fifo_adv +#( + parameter int unsigned DATA_WIDTH = 32, + parameter int unsigned DATA_DEPTH = 8 + ) + ( + input logic clk, + input logic rst_n, + input logic clear_i, + + //PUSH SIDE + input logic [DATA_WIDTH-1:0] data_i, + input logic valid_i, + output logic grant_o, + + //POP SIDE + output logic [DATA_WIDTH-1:0] data_o, + output logic valid_o, + input logic grant_i, + + input logic test_mode_i + ); + + + // Local Parameter + localparam int unsigned ADDR_DEPTH = $clog2(DATA_DEPTH); + enum logic [1:0] { EMPTY, FULL, MIDDLE } CS, NS; + // Internal Signals + + logic gate_clock; + logic clk_gated; + + logic [ADDR_DEPTH-1:0] Pop_Pointer_CS, Pop_Pointer_NS; + logic [ADDR_DEPTH-1:0] Push_Pointer_CS, Push_Pointer_NS; + logic [DATA_WIDTH-1:0] FIFO_REGISTERS[DATA_DEPTH-1:0]; + int unsigned i; + + // Parameter Check + // synopsys translate_off + initial + begin : parameter_check + integer param_err_flg; + param_err_flg = 0; + + if (DATA_WIDTH < 1) + begin + param_err_flg = 1; + $display("ERROR: %m :\n Invalid value (%d) for parameter DATA_WIDTH (legal range: greater than 1)", DATA_WIDTH ); + end + + if (DATA_DEPTH < 1) + begin + param_err_flg = 1; + $display("ERROR: %m :\n Invalid value (%d) for parameter DATA_DEPTH (legal range: greater than 1)", DATA_DEPTH ); + end + end + // synopsys translate_on + +`ifndef PULP_FPGA_EMUL + cluster_clock_gating cg_cell + ( + .clk_i ( clk ), + .en_i (~gate_clock ), + .test_en_i ( test_mode_i ), + .clk_o ( clk_gated ) + ); +`else + assign clk_gated = clk; +`endif + + // UPDATE THE STATE + always_ff @(posedge clk, negedge rst_n) + begin + if(rst_n == 1'b0) + begin + CS <= EMPTY; + Pop_Pointer_CS <= {ADDR_DEPTH {1'b0}}; + Push_Pointer_CS <= {ADDR_DEPTH {1'b0}}; + end + else + begin + if(clear_i) + begin + CS <= EMPTY; + Pop_Pointer_CS <= {ADDR_DEPTH {1'b0}}; + Push_Pointer_CS <= {ADDR_DEPTH {1'b0}}; + end + else + begin + CS <= NS; + Pop_Pointer_CS <= Pop_Pointer_NS; + Push_Pointer_CS <= Push_Pointer_NS; + end + end + end + + + // Compute Next State + always_comb + begin + gate_clock = 1'b0; + + case(CS) + + EMPTY: + begin + grant_o = 1'b1; + valid_o = 1'b0; + + case(valid_i) + 1'b0 : + begin + NS = EMPTY; + Push_Pointer_NS = Push_Pointer_CS; + Pop_Pointer_NS = Pop_Pointer_CS; + gate_clock = 1'b1; + end + + 1'b1: + begin + NS = MIDDLE; + Push_Pointer_NS = Push_Pointer_CS + 1'b1; + Pop_Pointer_NS = Pop_Pointer_CS; + end + + endcase + end//~EMPTY + + MIDDLE: + begin + grant_o = 1'b1; + valid_o = 1'b1; + + case({valid_i,grant_i}) + + 2'b01: + begin + gate_clock = 1'b1; + + if((Pop_Pointer_CS == Push_Pointer_CS -1 ) || ((Pop_Pointer_CS == DATA_DEPTH-1) && (Push_Pointer_CS == 0) )) + NS = EMPTY; + else + NS = MIDDLE; + + Push_Pointer_NS = Push_Pointer_CS; + + if(Pop_Pointer_CS == DATA_DEPTH-1) + Pop_Pointer_NS = 0; + else + Pop_Pointer_NS = Pop_Pointer_CS + 1'b1; + end + + 2'b00 : + begin + gate_clock = 1'b1; + NS = MIDDLE; + Push_Pointer_NS = Push_Pointer_CS; + Pop_Pointer_NS = Pop_Pointer_CS; + end + + 2'b11: + begin + NS = MIDDLE; + + if(Push_Pointer_CS == DATA_DEPTH-1) + Push_Pointer_NS = 0; + else + Push_Pointer_NS = Push_Pointer_CS + 1'b1; + + if(Pop_Pointer_CS == DATA_DEPTH-1) + Pop_Pointer_NS = 0; + else + Pop_Pointer_NS = Pop_Pointer_CS + 1'b1; + end + + 2'b10: + begin + if(( Push_Pointer_CS == Pop_Pointer_CS - 1) || ( (Push_Pointer_CS == DATA_DEPTH-1) && (Pop_Pointer_CS == 0) )) + NS = FULL; + else + NS = MIDDLE; + + if(Push_Pointer_CS == DATA_DEPTH - 1) + Push_Pointer_NS = 0; + else + Push_Pointer_NS = Push_Pointer_CS + 1'b1; + + Pop_Pointer_NS = Pop_Pointer_CS; + end + + endcase + end + + FULL: + begin + grant_o = 1'b0; + valid_o = 1'b1; + gate_clock = 1'b1; + + case(grant_i) + 1'b1: + begin + NS = MIDDLE; + + Push_Pointer_NS = Push_Pointer_CS; + + if(Pop_Pointer_CS == DATA_DEPTH-1) + Pop_Pointer_NS = 0; + else + Pop_Pointer_NS = Pop_Pointer_CS + 1'b1; + end + + 1'b0: + begin + NS = FULL; + Push_Pointer_NS = Push_Pointer_CS; + Pop_Pointer_NS = Pop_Pointer_CS; + end + endcase + + end // end of FULL + + default : + begin + gate_clock = 1'b1; + grant_o = 1'b0; + valid_o = 1'b0; + NS = EMPTY; + Pop_Pointer_NS = 0; + Push_Pointer_NS = 0; + end + + endcase + end + + always_ff @(posedge clk_gated, negedge rst_n) + begin + if(rst_n == 1'b0) + begin + for (i=0; i< DATA_DEPTH; i++) + FIFO_REGISTERS[i] <= {DATA_WIDTH {1'b0}}; + end + else + begin + if((grant_o == 1'b1) && (valid_i == 1'b1)) + FIFO_REGISTERS[Push_Pointer_CS] <= data_i; + end + end + + assign data_o = FIFO_REGISTERS[Pop_Pointer_CS]; + +endmodule // generic_fifo diff --git a/vendor/common_cells/src/deprecated/prioarbiter.sv b/vendor/common_cells/src/deprecated/prioarbiter.sv new file mode 100644 index 00000000..730ceca4 --- /dev/null +++ b/vendor/common_cells/src/deprecated/prioarbiter.sv @@ -0,0 +1,89 @@ +// 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. +// +// Author: Florian Zaruba , ETH Zurich +// Date: 16.03.2019 +// Description: Priority arbiter with Lock in. Port 0 has priority over port 1, port 1 over port2 +// and so on. If the `LOCK_IN` feature is activated the arbitration decision is kept +// when the `en_i` is low. + +// Dependencies: relies on fast leading zero counter tree "onehot_to_bin" in common_cells +module prioarbiter #( + parameter int unsigned NUM_REQ = 13, + parameter int unsigned LOCK_IN = 0 +) ( + input logic clk_i, + input logic rst_ni, + + input logic flush_i, // clears the fsm and control signal registers + input logic en_i, // arbiter enable + input logic [NUM_REQ-1:0] req_i, // request signals + + output logic [NUM_REQ-1:0] ack_o, // acknowledge signals + output logic vld_o, // request ack'ed + output logic [$clog2(NUM_REQ)-1:0] idx_o // idx output +); + + localparam SEL_WIDTH = $clog2(NUM_REQ); + + logic [SEL_WIDTH-1:0] arb_sel_lock_d, arb_sel_lock_q; + logic lock_d, lock_q; + + logic [$clog2(NUM_REQ)-1:0] idx; + + // shared + assign vld_o = (|req_i) & en_i; + assign idx_o = (lock_q) ? arb_sel_lock_q : idx; + + // Arbiter + // Port 0 has priority over all other ports + assign ack_o[0] = (req_i[0]) ? en_i : 1'b0; + // check that the priorities + for (genvar i = 1; i < NUM_REQ; i++) begin : gen_arb_req_ports + // for every subsequent port check the priorities of the previous port + assign ack_o[i] = (req_i[i] & ~(|ack_o[i-1:0])) ? en_i : 1'b0; + end + + onehot_to_bin #( + .ONEHOT_WIDTH ( NUM_REQ ) + ) i_onehot_to_bin ( + .onehot ( ack_o ), + .bin ( idx ) + ); + + if (LOCK_IN) begin : gen_lock_in + // latch decision in case we got at least one req and no acknowledge + assign lock_d = (|req_i) & ~en_i; + assign arb_sel_lock_d = idx_o; + end else begin + // disable + assign lock_d = '0; + assign arb_sel_lock_d = '0; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + lock_q <= 1'b0; + arb_sel_lock_q <= '0; + end else begin + if (flush_i) begin + lock_q <= 1'b0; + arb_sel_lock_q <= '0; + end else begin + lock_q <= lock_d; + arb_sel_lock_q <= arb_sel_lock_d; + end + end + end + +endmodule : prioarbiter + + + diff --git a/vendor/common_cells/src/deprecated/pulp_sync.sv b/vendor/common_cells/src/deprecated/pulp_sync.sv new file mode 100644 index 00000000..2b436163 --- /dev/null +++ b/vendor/common_cells/src/deprecated/pulp_sync.sv @@ -0,0 +1,36 @@ +// 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. + +// Antonio Pullini + +module pulp_sync + #( + parameter STAGES = 2 + ) + ( + input logic clk_i, + input logic rstn_i, + input logic serial_i, + output logic serial_o + ); + + logic [STAGES-1:0] r_reg; + + always_ff @(posedge clk_i, negedge rstn_i) + begin + if(!rstn_i) + r_reg <= 'h0; + else + r_reg <= {r_reg[STAGES-2:0], serial_i}; + end + + assign serial_o = r_reg[STAGES-1]; + +endmodule diff --git a/vendor/common_cells/src/deprecated/pulp_sync_wedge.sv b/vendor/common_cells/src/deprecated/pulp_sync_wedge.sv new file mode 100644 index 00000000..66cee57d --- /dev/null +++ b/vendor/common_cells/src/deprecated/pulp_sync_wedge.sv @@ -0,0 +1,55 @@ +// 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. + +// Antonio Pullini + +module pulp_sync_wedge #( + parameter int unsigned STAGES = 2 +) ( + input logic clk_i, + input logic rstn_i, + input logic en_i, + input logic serial_i, + output logic r_edge_o, + output logic f_edge_o, + output logic serial_o +); + logic clk; + logic serial, serial_q; + + assign serial_o = serial_q; + assign f_edge_o = ~serial & serial_q; + assign r_edge_o = serial & ~serial_q; + + pulp_sync #( + .STAGES(STAGES) + ) i_pulp_sync ( + .clk_i, + .rstn_i, + .serial_i, + .serial_o ( serial ) + ); + + pulp_clock_gating i_pulp_clock_gating ( + .clk_i, + .en_i, + .test_en_i ( 1'b0 ), + .clk_o ( clk ) + ); + + always_ff @(posedge clk, negedge rstn_i) begin + if (!rstn_i) begin + serial_q <= 1'b0; + end else begin + serial_q <= serial; + end + end + +endmodule diff --git a/vendor/common_cells/src/deprecated/rrarbiter.sv b/vendor/common_cells/src/deprecated/rrarbiter.sv new file mode 100644 index 00000000..bf806c5e --- /dev/null +++ b/vendor/common_cells/src/deprecated/rrarbiter.sv @@ -0,0 +1,61 @@ +// 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. +// +// Author: Michael Schaffner , ETH Zurich +// Date: 16.08.2018 +// Description: Fair round robin arbiter with lock feature. +// +// The rrarbiter employs fair round robin arbitration - i.e. the priorities +// rotate each cycle. +// +// The lock-in feature prevents the arbiter from changing the arbitration +// decision when the arbiter is disabled. I.e., the index of the first request +// that wins the arbitration will be locked until en_i is asserted again. +// +// Dependencies: relies on rr_arb_tree from common_cells. + +module rrarbiter #( + parameter int unsigned NUM_REQ = 64, + parameter bit LOCK_IN = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + + input logic flush_i, // clears arbiter state + input logic en_i, // arbiter enable + input logic [NUM_REQ-1:0] req_i, // request signals + + output logic [NUM_REQ-1:0] ack_o, // acknowledge signals + output logic vld_o, // request ack'ed + output logic [$clog2(NUM_REQ)-1:0] idx_o // idx output +); + + logic req; + assign vld_o = (|req_i) & en_i; + + rr_arb_tree #( + .NumIn ( NUM_REQ ), + .DataWidth ( 1 ), + .LockIn ( LOCK_IN )) + i_rr_arb_tree ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .flush_i ( flush_i ), + .rr_i ( '0 ), + .req_i ( req_i ), + .gnt_o ( ack_o ), + .data_i ( '0 ), + .gnt_i ( en_i & req ), + .req_o ( req ), + .data_o ( ), + .idx_o ( idx_o ) + ); + +endmodule : rrarbiter diff --git a/vendor/common_cells/src/deprecated/sram.sv b/vendor/common_cells/src/deprecated/sram.sv new file mode 100644 index 00000000..fca1372b --- /dev/null +++ b/vendor/common_cells/src/deprecated/sram.sv @@ -0,0 +1,46 @@ +// Copyright 2017, 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. +// +// Date: 13.10.2017 +// Description: SRAM Behavioral Model + +module sram #( + int unsigned DATA_WIDTH = 64, + int unsigned NUM_WORDS = 1024 +)( + input logic clk_i, + + input logic req_i, + input logic we_i, + input logic [$clog2(NUM_WORDS)-1:0] addr_i, + input logic [DATA_WIDTH-1:0] wdata_i, + input logic [DATA_WIDTH-1:0] be_i, + output logic [DATA_WIDTH-1:0] rdata_o +); + localparam ADDR_WIDTH = $clog2(NUM_WORDS); + + logic [DATA_WIDTH-1:0] ram [NUM_WORDS-1:0]; + logic [ADDR_WIDTH-1:0] raddr_q; + + // 1. randomize array + // 2. randomize output when no request is active + always_ff @(posedge clk_i) begin + if (req_i) begin + if (!we_i) + raddr_q <= addr_i; + else + for (int i = 0; i < DATA_WIDTH; i++) + if (be_i[i]) ram[addr_i][i] <= wdata_i[i]; + end + end + + assign rdata_o = ram[raddr_q]; + +endmodule diff --git a/vendor/common_cells/src/ecc_decode.sv b/vendor/common_cells/src/ecc_decode.sv new file mode 100644 index 00000000..40687e90 --- /dev/null +++ b/vendor/common_cells/src/ecc_decode.sv @@ -0,0 +1,128 @@ +// 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. +// +// Author: Florian Zaruba +// +/// # ECC Decoder +/// +/// Implements SECDED (Single Error Correction, Double Error Detection) Hamming Code +/// with extended parity bit [1]. +/// The module receives a data word including parity bit and decodes it according to the +/// number of data and parity bit. +/// +/// 1. If no error has been detected, the syndrome will be zero and all flags will be zero. +/// 2. If a single error has been detected, the syndrome is non-zero, and `single_error_o` will be +/// asserted. The output word contains the corrected data. +/// 3. If the parity bit contained an error, the module will assert `parity_error_o`. +/// 4. In case of a double fault the syndrome is non-zero, `double_error_o` will be asserted. +/// All other status flags will be de-asserted. +/// +/// [1] https://en.wikipedia.org/wiki/Hamming_code + +module ecc_decode import ecc_pkg::*; #( + /// Data width of unencoded word. + parameter int unsigned DataWidth = 64, + // Do not change + parameter type data_t = logic [DataWidth-1:0], + parameter type parity_t = logic [get_parity_width(DataWidth)-1:0], + parameter type code_word_t = logic [get_cw_width(DataWidth)-1:0], + parameter type encoded_data_t = struct packed { + logic parity; + code_word_t code_word; + } + ) ( + /// Encoded data in + input encoded_data_t data_i, + /// Corrected data out + output data_t data_o, + /// Error syndrome indicates the erroneous bit position + output parity_t syndrome_o, + /// A single error occurred + output logic single_error_o, + /// Error received in parity bit (MSB) + output logic parity_error_o, + /// A double error occurred + output logic double_error_o +); + + logic parity; + data_t data_wo_parity; + parity_t syndrome; + logic syndrome_not_zero; + code_word_t correct_data; + + // Check parity bit. 0 = parity equal, 1 = different parity + assign parity = data_i.parity ^ (^data_i.code_word); + + ///! | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + ///! |p1 p2 d1 p4 d2 d3 d4 p8 d5 d6 d7 d8 d9 d10 d11 + ///! ---|---------------------------------------------- + ///! p1 | x x x x x x x x + ///! p2 | x x x x x x x x + ///! p4 | x x x x x x x x + ///! p8 | x x x x x x x x + + ///! 1. Parity bit 1 covers all bit positions which have the least significant bit + ///! set: bit 1 (the parity bit itself), 3, 5, 7, 9, etc. + ///! 2. Parity bit 2 covers all bit positions which have the second least + ///! significant bit set: bit 2 (the parity bit itself), 3, 6, 7, 10, 11, etc. + ///! 3. Parity bit 4 covers all bit positions which have the third least + ///! significant bit set: bits 4–7, 12–15, 20–23, etc. + ///! 4. Parity bit 8 covers all bit positions which have the fourth least + ///! significant bit set: bits 8–15, 24–31, 40–47, etc. + ///! 5. In general each parity bit covers all bits where the bitwise AND of the + ///! parity position and the bit position is non-zero. + always_comb begin : calculate_syndrome + syndrome = 0; + for (int unsigned i = 0; i < unsigned'($bits(parity_t)); i++) begin + for (int unsigned j = 0; j < unsigned'($bits(code_word_t)); j++) begin + if (|(unsigned'(2**i) & (j + 1))) syndrome[i] = syndrome[i] ^ data_i.code_word[j]; + end + end + end + + assign syndrome_not_zero = |syndrome; + + // correct the data word if the syndrome is non-zero + always_comb begin + correct_data = data_i.code_word; + if (syndrome_not_zero) begin + correct_data[syndrome - 1] = ~data_i.code_word[syndrome - 1]; + end + end + + ///! Syndrome | Overall Parity (MSB) | Error Type | Notes + ///! -------------------------------------------------------- + ///! 0 | 0 | No Error | + ///! /=0 | 1 | Single Error | Correctable. Syndrome holds incorrect bit position. + ///! 0 | 1 | Parity Error | Overall parity, MSB is in error and can be corrected. + ///! /=0 | 0 | Double Error | Not correctable. + assign single_error_o = parity & syndrome_not_zero; + assign parity_error_o = parity & ~syndrome_not_zero; + assign double_error_o = ~parity & syndrome_not_zero; + + // Extract data vector + always_comb begin + automatic int unsigned idx; // bit index + data_wo_parity = '0; + idx = 0; + + for (int unsigned i = 1; i < unsigned'($bits(code_word_t)) + 1; i++) begin + // if i is a power of two we are indexing a parity bit + if (unsigned'(2**$clog2(i)) != i) begin + data_wo_parity[idx] = correct_data[i - 1]; + idx++; + end + end + end + + assign data_o = data_wo_parity; + +endmodule diff --git a/vendor/common_cells/src/ecc_encode.sv b/vendor/common_cells/src/ecc_encode.sv new file mode 100644 index 00000000..8669a082 --- /dev/null +++ b/vendor/common_cells/src/ecc_encode.sv @@ -0,0 +1,78 @@ +// 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. +// +// Author: Florian Zaruba +/// # ECC Encoder +/// +/// Implements SECDED (Single Error Correction, Double Error Detection) Hamming Code +/// with extended parity bit [1]. +/// The module receives a data word and encodes it using above mentioned error +/// detection and correction code. The corresponding decode module +/// can be found in `ecc_decode.sv` +/// +/// [1] https://en.wikipedia.org/wiki/Hamming_code + +module ecc_encode import ecc_pkg::*; #( + /// Data width of unencoded word. + parameter int unsigned DataWidth = 64, + // Do not change + parameter type data_t = logic [DataWidth-1:0], + parameter type parity_t = logic [get_parity_width(DataWidth)-1:0], + parameter type code_word_t = logic [get_cw_width(DataWidth)-1:0], + parameter type encoded_data_t = struct packed { + logic parity; + code_word_t code_word; + } +) ( + /// Unencoded data in + input data_t data_i, + /// Encoded data out + output encoded_data_t data_o +); + + parity_t parity_code_word; + code_word_t data, codeword; + + // Expand incoming data to codeword width + always_comb begin : expand_data + automatic int unsigned idx; + data = '0; + idx = 0; + for (int unsigned i = 1; i < unsigned'($bits(code_word_t)) + 1; i++) begin + // if it is not a power of two word it is a normal data index + if (unsigned'(2**$clog2(i)) != i) begin + data[i - 1] = data_i[idx]; + idx++; + end + end + end + + // calculate code word + always_comb begin : calculate_syndrome + parity_code_word = 0; + for (int unsigned i = 0; i < unsigned'($bits(parity_t)); i++) begin + for (int unsigned j = 1; j < unsigned'($bits(code_word_t)) + 1; j++) begin + if (|(unsigned'(2**i) & j)) parity_code_word[i] = parity_code_word[i] ^ data[j - 1]; + end + end + end + + // fuse the final codeword + always_comb begin : generate_codeword + codeword = data; + for (int unsigned i = 0; i < unsigned'($bits(parity_t)); i++) begin + codeword[2**i-1] = parity_code_word[i]; + end + end + + assign data_o.code_word = codeword; + assign data_o.parity = ^codeword; + +endmodule diff --git a/vendor/common_cells/src/ecc_pkg.sv b/vendor/common_cells/src/ecc_pkg.sv new file mode 100644 index 00000000..fde9f782 --- /dev/null +++ b/vendor/common_cells/src/ecc_pkg.sv @@ -0,0 +1,31 @@ +// 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. +// +// Author: Florian Zaruba +// +/// Contains common ECC definitions and helper functions. + +package ecc_pkg; + + // Calculate required ECC parity width: + function automatic int unsigned get_parity_width (input int unsigned data_width); + // data_width + cw_width + 1 <= 2**cw_width + int unsigned cw_width = 2; + while (unsigned'(2**cw_width) < cw_width + data_width + 1) cw_width++; + return cw_width; + endfunction + + // Calculate required ECC codeword width: + function automatic int unsigned get_cw_width (input int unsigned data_width); + // data width + parity width + one additional parity bit (for double error detection) + return data_width + get_parity_width(data_width); + endfunction + +endpackage diff --git a/vendor/common_cells/src/edge_detect.sv b/vendor/common_cells/src/edge_detect.sv new file mode 100644 index 00000000..c6453ba5 --- /dev/null +++ b/vendor/common_cells/src/edge_detect.sv @@ -0,0 +1,32 @@ +// 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. + +// Author: Florian Zaruba +// Description: Edge detector, clock needs to oversample for proper edge detection + +module edge_detect ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic d_i, // data stream in + output logic re_o, // rising edge detected + output logic fe_o // falling edge detected +); + + sync_wedge i_sync_wedge ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .en_i ( 1'b1 ), + .serial_i ( d_i ), + .r_edge_o ( re_o ), + .f_edge_o ( fe_o ), + .serial_o ( ) + ); + +endmodule diff --git a/vendor/common_cells/src/edge_propagator.sv b/vendor/common_cells/src/edge_propagator.sv new file mode 100644 index 00000000..2e272831 --- /dev/null +++ b/vendor/common_cells/src/edge_propagator.sv @@ -0,0 +1,50 @@ +// 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. + +// Antonio Pullini + +module edge_propagator ( + input logic clk_tx_i, + input logic rstn_tx_i, + input logic edge_i, + input logic clk_rx_i, + input logic rstn_rx_i, + output logic edge_o +); + + logic [1:0] sync_a; + logic sync_b; + + logic r_input_reg; + logic s_input_reg_next; + + assign s_input_reg_next = edge_i | (r_input_reg & (~sync_a[0])); + + always @(negedge rstn_tx_i or posedge clk_tx_i) begin + if (~rstn_tx_i) begin + r_input_reg <= 1'b0; + sync_a <= 2'b00; + end else begin + r_input_reg <= s_input_reg_next; + sync_a <= {sync_b,sync_a[1]}; + end + end + + pulp_sync_wedge i_sync_clkb ( + .clk_i ( clk_rx_i ), + .rstn_i ( rstn_rx_i ), + .en_i ( 1'b1 ), + .serial_i ( r_input_reg ), + .r_edge_o ( edge_o ), + .f_edge_o ( ), + .serial_o ( sync_b ) + ); + +endmodule diff --git a/vendor/common_cells/src/edge_propagator_rx.sv b/vendor/common_cells/src/edge_propagator_rx.sv new file mode 100644 index 00000000..89532cc2 --- /dev/null +++ b/vendor/common_cells/src/edge_propagator_rx.sv @@ -0,0 +1,31 @@ +// 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. + +// Antonio Pullini + +module edge_propagator_rx ( + input logic clk_i, + input logic rstn_i, + input logic valid_i, + output logic ack_o, + output logic valid_o +); + + pulp_sync_wedge i_sync_clkb ( + .clk_i ( clk_i ), + .rstn_i ( rstn_i ), + .en_i ( 1'b1 ), + .serial_i ( valid_i ), + .r_edge_o ( valid_o ), + .f_edge_o ( ), + .serial_o ( ack_o ) + ); + +endmodule diff --git a/vendor/common_cells/src/edge_propagator_tx.sv b/vendor/common_cells/src/edge_propagator_tx.sv new file mode 100644 index 00000000..0274a433 --- /dev/null +++ b/vendor/common_cells/src/edge_propagator_tx.sv @@ -0,0 +1,40 @@ +// 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. + +// Antonio Pullini + +module edge_propagator_tx ( + input logic clk_i, + input logic rstn_i, + input logic valid_i, + input logic ack_i, + output logic valid_o +); + + logic [1:0] sync_a; + + logic r_input_reg; + logic s_input_reg_next; + + assign s_input_reg_next = valid_i | (r_input_reg & ~sync_a[0]); + + always @(negedge rstn_i or posedge clk_i) begin + if (~rstn_i) begin + r_input_reg <= 1'b0; + sync_a <= 2'b00; + end else begin + r_input_reg <= s_input_reg_next; + sync_a <= {ack_i,sync_a[1]}; + end + end + + assign valid_o = r_input_reg; + +endmodule diff --git a/vendor/common_cells/src/exp_backoff.sv b/vendor/common_cells/src/exp_backoff.sv new file mode 100644 index 00000000..91dccb07 --- /dev/null +++ b/vendor/common_cells/src/exp_backoff.sv @@ -0,0 +1,98 @@ +// Copyright 2019 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. +// +// Author: Michael Schaffner , ETH Zurich +// Date: 10.04.2019 +// Description: exponential backoff counter with randomization. +// +// For each failed trial (set_i pulsed), this unit exponentially increases the +// (average) backoff time by masking an LFSR with a shifted mask in order to +// create the backoff counter initial value. +// +// The shift register mask and the counter value are both reset to '0 in case of +// a successful trial (clr_i). +// + +module exp_backoff #( + /// Seed for 16bit LFSR + parameter int unsigned Seed = 'hffff, + /// 2**MaxExp-1 determines the maximum range from which random wait counts are drawn + parameter int unsigned MaxExp = 16 +) ( + input logic clk_i, + input logic rst_ni, + /// Sets the backoff counter (pulse) -> use when trial did not succeed + input logic set_i, + /// Clears the backoff counter (pulse) -> use when trial succeeded + input logic clr_i, + /// Indicates whether the backoff counter is equal to zero and a new trial can be launched + output logic is_zero_o +); + + // leave this constant + localparam int unsigned WIDTH = 16; + + logic [WIDTH-1:0] lfsr_d, lfsr_q, cnt_d, cnt_q, mask_d, mask_q; + logic lfsr; + + // generate random wait counts + // note: we use a flipped lfsr here to + // avoid strange correlation effects between + // the (left-shifted) mask and the lfsr + assign lfsr = lfsr_q[15-15] ^ + lfsr_q[15-13] ^ + lfsr_q[15-12] ^ + lfsr_q[15-10]; + + assign lfsr_d = (set_i) ? {lfsr, lfsr_q[$high(lfsr_q):1]} : + lfsr_q; + + // mask the wait counts with exponentially increasing mask (shift reg) + assign mask_d = (clr_i) ? '0 : + (set_i) ? {{(WIDTH-MaxExp){1'b0}},mask_q[MaxExp-2:0], 1'b1} : + mask_q; + + assign cnt_d = (clr_i) ? '0 : + (set_i) ? (mask_q & lfsr_q) : + (!is_zero_o) ? cnt_q - 1'b1 : '0; + + assign is_zero_o = (cnt_q=='0); + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + lfsr_q <= WIDTH'(Seed); + mask_q <= '0; + cnt_q <= '0; + end else begin + lfsr_q <= lfsr_d; + mask_q <= mask_d; + cnt_q <= cnt_d; + end + end + +/////////////////////////////////////////////////////// +// assertions +/////////////////////////////////////////////////////// + +//pragma translate_off +`ifndef VERILATOR + initial begin + // assert wrong parameterizations + assert (MaxExp>0) + else $fatal(1,"MaxExp must be greater than 0"); + assert (MaxExp<=16) + else $fatal(1,"MaxExp cannot be greater than 16"); + assert (Seed>0) + else $fatal(1,"Zero seed is not allowed for LFSR"); + end +`endif +//pragma translate_on + +endmodule // exp_backoff diff --git a/vendor/common_cells/src/fall_through_register.sv b/vendor/common_cells/src/fall_through_register.sv new file mode 100644 index 00000000..fcbbe31d --- /dev/null +++ b/vendor/common_cells/src/fall_through_register.sv @@ -0,0 +1,58 @@ +// 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. + +// Fall-through register with a simple stream-like ready/valid handshake. +// This register does not cut combinatorial paths on any signals: in case the module at its output +// is ready to accept data within the same clock cycle, they are forwarded. Use this module to get a +// 'default ready' behavior towards the input. +module fall_through_register #( + parameter type T = logic // Vivado requires a default value for type parameters. +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous active-low reset + input logic clr_i, // Synchronous clear + input logic testmode_i, // Test mode to bypass clock gating + // Input port + input logic valid_i, + output logic ready_o, + input T data_i, + // Output port + output logic valid_o, + input logic ready_i, + output T data_o +); + + logic fifo_empty, + fifo_full; + + fifo_v2 #( + .FALL_THROUGH (1'b1), + .DATA_WIDTH ($size(T)), + .DEPTH (1), + .dtype (T) + ) i_fifo ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (clr_i), + .testmode_i (testmode_i), + .full_o (fifo_full), + .empty_o (fifo_empty), + .alm_full_o ( ), + .alm_empty_o ( ), + .data_i (data_i), + .push_i (valid_i & ~fifo_full), + .data_o (data_o), + .pop_i (ready_i & ~fifo_empty) + ); + + assign ready_o = ~fifo_full; + assign valid_o = ~fifo_empty; + +endmodule diff --git a/vendor/common_cells/src/fifo_v3.sv b/vendor/common_cells/src/fifo_v3.sv new file mode 100644 index 00000000..e417a3e7 --- /dev/null +++ b/vendor/common_cells/src/fifo_v3.sv @@ -0,0 +1,154 @@ +// 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. + +// Author: Florian Zaruba + +module fifo_v3 #( + parameter bit FALL_THROUGH = 1'b0, // fifo is in fall-through mode + parameter int unsigned DATA_WIDTH = 32, // default data width if the fifo is of type logic + parameter int unsigned DEPTH = 8, // depth can be arbitrary from 0 to 2**32 + parameter type dtype = logic [DATA_WIDTH-1:0], + // DO NOT OVERWRITE THIS PARAMETER + parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1 +)( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, // flush the queue + input logic testmode_i, // test_mode to bypass clock gating + // status flags + output logic full_o, // queue is full + output logic empty_o, // queue is empty + output logic [ADDR_DEPTH-1:0] usage_o, // fill pointer + // as long as the queue is not full we can push new data + input dtype data_i, // data to push into the queue + input logic push_i, // data is valid and can be pushed to the queue + // as long as the queue is not empty we can pop new elements + output dtype data_o, // output data + input logic pop_i // pop head from queue +); + // local parameter + // FIFO depth - handle the case of pass-through, synthesizer will do constant propagation + localparam int unsigned FifoDepth = (DEPTH > 0) ? DEPTH : 1; + // clock gating control + logic gate_clock; + // pointer to the read and write section of the queue + logic [ADDR_DEPTH - 1:0] read_pointer_n, read_pointer_q, write_pointer_n, write_pointer_q; + // keep a counter to keep track of the current queue status + // this integer will be truncated by the synthesis tool + logic [ADDR_DEPTH:0] status_cnt_n, status_cnt_q; + // actual memory + dtype [FifoDepth - 1:0] mem_n, mem_q; + + assign usage_o = status_cnt_q[ADDR_DEPTH-1:0]; + + if (DEPTH == 0) begin : gen_pass_through + assign empty_o = ~push_i; + assign full_o = ~pop_i; + end else begin : gen_fifo + assign full_o = (status_cnt_q == FifoDepth[ADDR_DEPTH:0]); + assign empty_o = (status_cnt_q == 0) & ~(FALL_THROUGH & push_i); + end + // status flags + + // read and write queue logic + always_comb begin : read_write_comb + // default assignment + read_pointer_n = read_pointer_q; + write_pointer_n = write_pointer_q; + status_cnt_n = status_cnt_q; + data_o = (DEPTH == 0) ? data_i : mem_q[read_pointer_q]; + mem_n = mem_q; + gate_clock = 1'b1; + + // push a new element to the queue + if (push_i && ~full_o) begin + // push the data onto the queue + mem_n[write_pointer_q] = data_i; + // un-gate the clock, we want to write something + gate_clock = 1'b0; + // increment the write counter + if (write_pointer_q == FifoDepth[ADDR_DEPTH-1:0] - 1) + write_pointer_n = '0; + else + write_pointer_n = write_pointer_q + 1; + // increment the overall counter + status_cnt_n = status_cnt_q + 1; + end + + if (pop_i && ~empty_o) begin + // read from the queue is a default assignment + // but increment the read pointer... + if (read_pointer_n == FifoDepth[ADDR_DEPTH-1:0] - 1) + read_pointer_n = '0; + else + read_pointer_n = read_pointer_q + 1; + // ... and decrement the overall count + status_cnt_n = status_cnt_q - 1; + end + + // keep the count pointer stable if we push and pop at the same time + if (push_i && pop_i && ~full_o && ~empty_o) + status_cnt_n = status_cnt_q; + + // FIFO is in pass through mode -> do not change the pointers + if (FALL_THROUGH && (status_cnt_q == 0) && push_i) begin + data_o = data_i; + if (pop_i) begin + status_cnt_n = status_cnt_q; + read_pointer_n = read_pointer_q; + write_pointer_n = write_pointer_q; + end + end + end + + // sequential process + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + read_pointer_q <= '0; + write_pointer_q <= '0; + status_cnt_q <= '0; + end else begin + if (flush_i) begin + read_pointer_q <= '0; + write_pointer_q <= '0; + status_cnt_q <= '0; + end else begin + read_pointer_q <= read_pointer_n; + write_pointer_q <= write_pointer_n; + status_cnt_q <= status_cnt_n; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + mem_q <= '0; + end else if (!gate_clock) begin + mem_q <= mem_n; + end + end + +// pragma translate_off +`ifndef VERILATOR + initial begin + assert (DEPTH > 0) else $error("DEPTH must be greater than 0."); + end + + full_write : assert property( + @(posedge clk_i) disable iff (~rst_ni) (full_o |-> ~push_i)) + else $fatal (1, "Trying to push new data although the FIFO is full."); + + empty_read : assert property( + @(posedge clk_i) disable iff (~rst_ni) (empty_o |-> ~pop_i)) + else $fatal (1, "Trying to pop data although the FIFO is empty."); +`endif +// pragma translate_on + +endmodule // fifo_v3 diff --git a/vendor/common_cells/src/gray_to_binary.sv b/vendor/common_cells/src/gray_to_binary.sv new file mode 100644 index 00000000..b1ad46f1 --- /dev/null +++ b/vendor/common_cells/src/gray_to_binary.sv @@ -0,0 +1,23 @@ +// 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. +// +// Fabian Schuiki + +/// A gray code to binary converter. +module gray_to_binary #( + parameter int N = -1 +)( + input logic [N-1:0] A, + output logic [N-1:0] Z +); + for (genvar i = 0; i < N; i++) + assign Z[i] = ^A[N-1:i]; +endmodule diff --git a/vendor/common_cells/src/id_queue.sv b/vendor/common_cells/src/id_queue.sv new file mode 100644 index 00000000..fdb71713 --- /dev/null +++ b/vendor/common_cells/src/id_queue.sv @@ -0,0 +1,278 @@ +// 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. + +// ID Queue +// +// In an ID queue, every element has a numeric ID. Among all elements that have the same ID, the ID +// queue preserves FIFO order. +// +// This ID queue implementation allows to either push (through the `inp_*` signals) or pop (through +// the `oup_*` signals) one element per clock cycle. The `inp_` port has priority and grants a +// request iff the queue is not full. The `oup_` port dequeues an element iff `oup_pop_i` is +// asserted during an `oup_` handshake; otherwise, it performs a non-destructive read. `oup_data_o` +// is valid iff `oup_data_valid_o` is asserted during an `oup_` handshake. If `oup_data_valid_o` is +// not asserted, the queue did not contain an element with the provided ID. +// +// This ID queue additionally provides the `exists_` port, which searches for an element anywhere in +// the queue. The comparison performed during the search can be masked: for every bit that is +// asserted in `exists_mask_i`, the corresponding bit in the queue element and in `exists_data_i` +// must be equal for a match; the other bits are not compared. If masking is not required, tie +// `exists_mask_i_ to `'1` and the synthesizer should simplify the comparisons to unmasked ones. The +// `exists_` port operates independently of the `inp_` and `oup_` ports. If the `exists_` port is +// unused, tie `exists_req_i` to `1'b0` and the synthesizer should remove the internal comparators. +// +// This ID queue can store at most `CAPACITY` elements, independent of their ID. Let +// - C = `CAPACITY` +// - B = $bits(data_t) +// - I = 2**`ID_WIDTH` +// Then +// - the queue element storage requires O(C * (B + log2(C))) bit +// - the ID table requires O(H * log2(C)) bit, where H = min(C, I) +// +// Maintainers: +// - Andreas Kurth + +module id_queue #( + parameter int ID_WIDTH = 0, + parameter int CAPACITY = 0, + parameter type data_t = logic, + // Dependent parameters, DO NOT OVERRIDE! + localparam type id_t = logic[ID_WIDTH-1:0], + localparam type mask_t = logic[$bits(data_t)-1:0] +) ( + input logic clk_i, + input logic rst_ni, + + input id_t inp_id_i, + input data_t inp_data_i, + input logic inp_req_i, + output logic inp_gnt_o, + + input data_t exists_data_i, + input mask_t exists_mask_i, + input logic exists_req_i, + output logic exists_o, + output logic exists_gnt_o, + + input id_t oup_id_i, + input logic oup_pop_i, + input logic oup_req_i, + output data_t oup_data_o, + output logic oup_data_valid_o, + output logic oup_gnt_o +); + + // Capacity of the head-tail table, which associates an ID with corresponding head and tail + // indices. + localparam int NIds = 2**ID_WIDTH; + localparam int HtCapacity = (NIds <= CAPACITY) ? NIds : CAPACITY; + localparam int unsigned HtIdxWidth = cf_math_pkg::idx_width(HtCapacity); + localparam int unsigned LdIdxWidth = cf_math_pkg::idx_width(CAPACITY); + + // Type for indexing the head-tail table. + typedef logic [HtIdxWidth-1:0] ht_idx_t; + + // Type for indexing the lined data table. + typedef logic [LdIdxWidth-1:0] ld_idx_t; + + // Type of an entry in the head-tail table. + typedef struct packed { + id_t id; + ld_idx_t head, + tail; + logic free; + } head_tail_t; + + // Type of an entry in the linked data table. + typedef struct packed { + data_t data; + ld_idx_t next; + logic free; + } linked_data_t; + + head_tail_t [HtCapacity-1:0] head_tail_d, head_tail_q; + + linked_data_t [CAPACITY-1:0] linked_data_d, linked_data_q; + + logic full, + match_id_valid, + no_id_match; + + logic [HtCapacity-1:0] head_tail_free, + idx_matches_id; + + logic [CAPACITY-1:0] exists_match, + linked_data_free; + + id_t match_id; + + ht_idx_t head_tail_free_idx, + match_idx; + + ld_idx_t linked_data_free_idx; + + // Find the index in the head-tail table that matches a given ID. + for (genvar i = 0; i < HtCapacity; i++) begin: gen_idx_match + assign idx_matches_id[i] = match_id_valid && (head_tail_q[i].id == match_id) && + !head_tail_q[i].free; + end + assign no_id_match = !(|idx_matches_id); + onehot_to_bin #( + .ONEHOT_WIDTH (HtCapacity) + ) i_id_ohb ( + .onehot (idx_matches_id), + .bin (match_idx) + ); + + // Find the first free index in the head-tail table. + for (genvar i = 0; i < HtCapacity; i++) begin: gen_head_tail_free + assign head_tail_free[i] = head_tail_q[i].free; + end + lzc #( + .WIDTH (HtCapacity), + .MODE (0) // Start at index 0. + ) i_ht_free_lzc ( + .in_i (head_tail_free), + .cnt_o (head_tail_free_idx), + .empty_o () + ); + + // Find the first free index in the linked data table. + for (genvar i = 0; i < CAPACITY; i++) begin: gen_linked_data_free + assign linked_data_free[i] = linked_data_q[i].free; + end + lzc #( + .WIDTH (CAPACITY), + .MODE (0) // Start at index 0. + ) i_ld_free_lzc ( + .in_i (linked_data_free), + .cnt_o (linked_data_free_idx), + .empty_o () + ); + + // The queue is full if and only if there are no free items in the linked data structure. + assign full = !(|linked_data_free); + + assign inp_gnt_o = ~full; + always_comb begin + match_id = '0; + match_id_valid = 1'b0; + head_tail_d = head_tail_q; + linked_data_d = linked_data_q; + oup_gnt_o = 1'b0; + oup_data_o = data_t'('0); + oup_data_valid_o = 1'b0; + if (inp_req_i && !full) begin + match_id = inp_id_i; + match_id_valid = 1'b1; + // If the ID does not yet exist in the queue, add a new ID entry. + if (no_id_match) begin + head_tail_d[head_tail_free_idx] = '{ + id: inp_id_i, + head: linked_data_free_idx, + tail: linked_data_free_idx, + free: 1'b0 + }; + // Otherwise append it to the existing ID subqueue. + end else begin + linked_data_d[head_tail_q[match_idx].tail].next = linked_data_free_idx; + head_tail_d[match_idx].tail = linked_data_free_idx; + end + linked_data_d[linked_data_free_idx] = '{ + data: inp_data_i, + next: '0, + free: 1'b0 + }; + end else if (oup_req_i) begin + match_id = oup_id_i; + match_id_valid = 1'b1; + if (!no_id_match) begin + oup_data_o = data_t'(linked_data_q[head_tail_q[match_idx].head].data); + oup_data_valid_o = 1'b1; + if (oup_pop_i) begin + // Set free bit of linked data entry, all other bits are don't care. + linked_data_d[head_tail_q[match_idx].head] = '0; + linked_data_d[head_tail_q[match_idx].head][0] = 1'b1; + if (head_tail_q[match_idx].head == head_tail_q[match_idx].tail) begin + head_tail_d[match_idx] = '{free: 1'b1, default: '0}; + end else begin + head_tail_d[match_idx].head = + linked_data_q[head_tail_q[match_idx].head].next; + end + end + end + // Always grant the output request. If there was no match, the default, invalid entry + // will be returned. + oup_gnt_o = 1'b1; + end + end + + // Exists Lookup + for (genvar i = 0; i < CAPACITY; i++) begin: gen_lookup + mask_t exists_match_bits; + for (genvar j = 0; j < $bits(data_t); j++) begin: gen_mask + always_comb begin + if (linked_data_q[i].free) begin + exists_match_bits[j] = 1'b0; + end else begin + if (!exists_mask_i[j]) begin + exists_match_bits[j] = 1'b1; + end else begin + exists_match_bits[j] = (linked_data_q[i].data[j] == exists_data_i[j]); + end + end + end + end + assign exists_match[i] = (&exists_match_bits); + end + always_comb begin + exists_gnt_o = 1'b0; + exists_o = '0; + if (exists_req_i) begin + exists_gnt_o = 1'b1; + exists_o = (|exists_match); + end + end + + // Registers + for (genvar i = 0; i < HtCapacity; i++) begin: gen_ht_ffs + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + head_tail_q[i] <= '{free: 1'b1, default: '0}; + end else begin + head_tail_q[i] <= head_tail_d[i]; + end + end + end + for (genvar i = 0; i < CAPACITY; i++) begin: gen_data_ffs + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + // Set free bit of linked data entries, all other bits are don't care. + linked_data_q[i] <= '0; + linked_data_q[i][0] <= 1'b1; + end else begin + linked_data_q[i] <= linked_data_d[i]; + end + end + end + + // Validate parameters. +// pragma translate_off +`ifndef VERILATOR + initial begin: validate_params + assert (ID_WIDTH >= 1) + else $fatal("The ID must at least be one bit wide!"); + assert (CAPACITY >= 1) + else $fatal("The queue must have capacity of at least one entry!"); + end +`endif +// pragma translate_on + +endmodule diff --git a/vendor/common_cells/src/isochronous_spill_register.sv b/vendor/common_cells/src/isochronous_spill_register.sv new file mode 100644 index 00000000..c7b0453c --- /dev/null +++ b/vendor/common_cells/src/isochronous_spill_register.sv @@ -0,0 +1,87 @@ +// 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. +// +// Florian Zaruba + +`include "common_cells/registers.svh" + +/// A register with handshakes that completely cuts any combinatorial paths +/// between the input and output in isochronous clock domains. +/// +/// > Definition of isochronous: In telecommunication, an isochronous signal is a signal +/// > in which the time interval separating any two significant instants is equal to the +/// > unit interval or a multiple of the unit interval. +/// +/// The source and destination clock domains must be derived from the same clock +/// but can vary in frequency by a constant factor (e.g., double the frequency). +/// +/// The module is basically a two deep dual-clock fifo with read and write pointers +/// in different clock domains. As we know the static timing relationship between the +/// clock domains we can rely on static timing analysis (STA) to get the sampling windows +/// right and therefore don't need any synchronization. +module isochronous_spill_register #( + /// Data type of spill register. + parameter type T = logic, + /// Make this spill register transparent. + parameter bit Bypass = 1'b0 +) ( + /// Clock of source clock domain. + input logic src_clk_i, + /// Active low async reset in source domain. + input logic src_rst_ni, + /// Source input data is valid. + input logic src_valid_i, + /// Source is ready to accept. + output logic src_ready_o, + /// Source input data. + input T src_data_i, + /// Clock of destination clock domain. + input logic dst_clk_i, + /// Active low async reset in destination domain. + input logic dst_rst_ni, + /// Destination output data is valid. + output logic dst_valid_o, + /// Destination is ready to accept. + input logic dst_ready_i, + /// Destination output data. + output T dst_data_o +); + // Don't generate the spill register. + if (Bypass) begin : gen_bypass + assign dst_valid_o = src_valid_i; + assign src_ready_o = dst_ready_i; + assign dst_data_o = src_data_i; + // Generate the spill register + end else begin : gen_isochronous_spill_register + /// Read/write pointer are one bit wider than necessary. + /// We implicitly capture the full and empty state with the second bit: + /// If all but the topmost bit of `rd_pointer_q` and `wr_pointer_q` agree, the + /// FIFO is in a critical state. If the topmost bit is equal, the FIFO is + /// empty, otherwise it is full. + logic [1:0] rd_pointer_q, wr_pointer_q; + // Advance write pointer if we pushed a new item into the FIFO. (Source clock domain) + `FFLARN(wr_pointer_q, wr_pointer_q+1, (src_valid_i && src_ready_o), '0, src_clk_i, src_rst_ni) + // Advance read pointer if downstream consumed an item. (Destination clock domain) + `FFLARN(rd_pointer_q, rd_pointer_q+1, (dst_valid_o && dst_ready_i), '0, dst_clk_i, dst_rst_ni) + + T [1:0] mem_d, mem_q; + `FFLNR(mem_q, mem_d, (src_valid_i && src_ready_o), src_clk_i) + always_comb begin + mem_d = mem_q; + mem_d[wr_pointer_q[0]] = src_data_i; + end + + assign src_ready_o = (rd_pointer_q ^ wr_pointer_q) != 2'b10; + + assign dst_valid_o = (rd_pointer_q ^ wr_pointer_q) != '0; + assign dst_data_o = mem_q[rd_pointer_q[0]]; + end +endmodule diff --git a/vendor/common_cells/src/lfsr.sv b/vendor/common_cells/src/lfsr.sv new file mode 100644 index 00000000..aae2e2df --- /dev/null +++ b/vendor/common_cells/src/lfsr.sv @@ -0,0 +1,315 @@ +// Copyright 2019 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. +// +// Author: Michael Schaffner , ETH Zurich +// Date: 26.04.2019 +// +// Description: This is a parametric LFSR with precomputed coefficients for +// LFSR lengths from 4 to 64bit. + +// Additional block cipher layers can be instantiated to non-linearly transform +// the pseudo-random LFSR sequence at the output, and hence break the shifting +// patterns. The additional cipher layers can only be used for an LFSR width +// of 64bit, since the block cipher has been designed for that block length. + +module lfsr #( + parameter int unsigned LfsrWidth = 64, // [4,64] + parameter int unsigned OutWidth = 8, // [1,LfsrWidth] + parameter logic [LfsrWidth-1:0] RstVal = '1, // [1,2^LfsrWidth-1] + // 0: disabled, the present cipher uses 31, but just a few layers (1-3) are enough + // to break linear shifting patterns + parameter int unsigned CipherLayers = 0, + parameter bit CipherReg = 1'b1 // additional output reg after cipher +) ( + input logic clk_i, + input logic rst_ni, + input logic en_i, + output logic [OutWidth-1:0] out_o +); + +// Galois LFSR feedback masks +// Automatically generated with get_lfsr_masks.py +// Masks are from https://users.ece.cmu.edu/~koopman/lfsr/ +localparam logic [63:0] Masks [4:64] = '{64'hC, + 64'h1E, + 64'h39, + 64'h7E, + 64'hFA, + 64'h1FD, + 64'h3FC, + 64'h64B, + 64'hD8F, + 64'h1296, + 64'h2496, + 64'h4357, + 64'h8679, + 64'h1030E, + 64'h206CD, + 64'h403FE, + 64'h807B8, + 64'h1004B2, + 64'h2006A8, + 64'h4004B2, + 64'h800B87, + 64'h10004F3, + 64'h200072D, + 64'h40006AE, + 64'h80009E3, + 64'h10000583, + 64'h20000C92, + 64'h400005B6, + 64'h80000EA6, + 64'h1000007A3, + 64'h200000ABF, + 64'h400000842, + 64'h80000123E, + 64'h100000074E, + 64'h2000000AE9, + 64'h400000086A, + 64'h8000001213, + 64'h1000000077E, + 64'h2000000123B, + 64'h40000000877, + 64'h8000000108D, + 64'h100000000AE9, + 64'h200000000E9F, + 64'h4000000008A6, + 64'h80000000191E, + 64'h100000000090E, + 64'h2000000000FB3, + 64'h4000000000D7D, + 64'h80000000016A5, + 64'h10000000000B4B, + 64'h200000000010AF, + 64'h40000000000DDE, + 64'h8000000000181A, + 64'h100000000000B65, + 64'h20000000000102D, + 64'h400000000000CD5, + 64'h8000000000024C1, + 64'h1000000000000EF6, + 64'h2000000000001363, + 64'h4000000000000FCD, + 64'h80000000000019E2}; + +// this S-box and permutation P has been taken from the Present Cipher, +// a super lightweight block cipher. use the cipher layers to add additional +// non-linearity to the LFSR output. note one layer does not fully correspond +// to the present cipher round, since the key and rekeying function is not applied here. +// +// See also: +// "PRESENT: An Ultra-Lightweight Block Cipher", A. Bogdanov et al., Ches 2007 +// http://www.lightweightcrypto.org/present/present_ches2007.pdf + +// this is the sbox from the present cipher +localparam logic[15:0][3:0] Sbox4 = {4'h2, 4'h1, 4'h7, 4'h4, + 4'h8, 4'hF, 4'hE, 4'h3, + 4'hD, 4'hA, 4'h0, 4'h9, + 4'hB, 4'h6, 4'h5, 4'hC }; + +// these are the permutation indices of the present cipher +localparam logic[63:0][5:0] Perm = {6'd63, 6'd47, 6'd31, 6'd15, 6'd62, 6'd46, 6'd30, 6'd14, + 6'd61, 6'd45, 6'd29, 6'd13, 6'd60, 6'd44, 6'd28, 6'd12, + 6'd59, 6'd43, 6'd27, 6'd11, 6'd58, 6'd42, 6'd26, 6'd10, + 6'd57, 6'd41, 6'd25, 6'd09, 6'd56, 6'd40, 6'd24, 6'd08, + 6'd55, 6'd39, 6'd23, 6'd07, 6'd54, 6'd38, 6'd22, 6'd06, + 6'd53, 6'd37, 6'd21, 6'd05, 6'd52, 6'd36, 6'd20, 6'd04, + 6'd51, 6'd35, 6'd19, 6'd03, 6'd50, 6'd34, 6'd18, 6'd02, + 6'd49, 6'd33, 6'd17, 6'd01, 6'd48, 6'd32, 6'd16, 6'd00}; + + +function automatic logic [63:0] sbox4_layer(logic [63:0] in); + logic [63:0] out; + //for (logic [4:0] j = '0; j<16; j++) out[j*4 +: 4] = sbox4[in[j*4 +: 4]]; + // this simulates much faster than the loop + out[0*4 +: 4] = Sbox4[in[0*4 +: 4]]; + out[1*4 +: 4] = Sbox4[in[1*4 +: 4]]; + out[2*4 +: 4] = Sbox4[in[2*4 +: 4]]; + out[3*4 +: 4] = Sbox4[in[3*4 +: 4]]; + + out[4*4 +: 4] = Sbox4[in[4*4 +: 4]]; + out[5*4 +: 4] = Sbox4[in[5*4 +: 4]]; + out[6*4 +: 4] = Sbox4[in[6*4 +: 4]]; + out[7*4 +: 4] = Sbox4[in[7*4 +: 4]]; + + out[8*4 +: 4] = Sbox4[in[8*4 +: 4]]; + out[9*4 +: 4] = Sbox4[in[9*4 +: 4]]; + out[10*4 +: 4] = Sbox4[in[10*4 +: 4]]; + out[11*4 +: 4] = Sbox4[in[11*4 +: 4]]; + + out[12*4 +: 4] = Sbox4[in[12*4 +: 4]]; + out[13*4 +: 4] = Sbox4[in[13*4 +: 4]]; + out[14*4 +: 4] = Sbox4[in[14*4 +: 4]]; + out[15*4 +: 4] = Sbox4[in[15*4 +: 4]]; + return out; +endfunction : sbox4_layer + +function automatic logic [63:0] perm_layer(logic [63:0] in); + logic [63:0] out; + // for (logic [7:0] j = '0; j<64; j++) out[perm[j]] = in[j]; + // this simulates much faster than the loop + out[Perm[0]] = in[0]; + out[Perm[1]] = in[1]; + out[Perm[2]] = in[2]; + out[Perm[3]] = in[3]; + out[Perm[4]] = in[4]; + out[Perm[5]] = in[5]; + out[Perm[6]] = in[6]; + out[Perm[7]] = in[7]; + out[Perm[8]] = in[8]; + out[Perm[9]] = in[9]; + + out[Perm[10]] = in[10]; + out[Perm[11]] = in[11]; + out[Perm[12]] = in[12]; + out[Perm[13]] = in[13]; + out[Perm[14]] = in[14]; + out[Perm[15]] = in[15]; + out[Perm[16]] = in[16]; + out[Perm[17]] = in[17]; + out[Perm[18]] = in[18]; + out[Perm[19]] = in[19]; + + out[Perm[20]] = in[20]; + out[Perm[21]] = in[21]; + out[Perm[22]] = in[22]; + out[Perm[23]] = in[23]; + out[Perm[24]] = in[24]; + out[Perm[25]] = in[25]; + out[Perm[26]] = in[26]; + out[Perm[27]] = in[27]; + out[Perm[28]] = in[28]; + out[Perm[29]] = in[29]; + + out[Perm[30]] = in[30]; + out[Perm[31]] = in[31]; + out[Perm[32]] = in[32]; + out[Perm[33]] = in[33]; + out[Perm[34]] = in[34]; + out[Perm[35]] = in[35]; + out[Perm[36]] = in[36]; + out[Perm[37]] = in[37]; + out[Perm[38]] = in[38]; + out[Perm[39]] = in[39]; + + out[Perm[40]] = in[40]; + out[Perm[41]] = in[41]; + out[Perm[42]] = in[42]; + out[Perm[43]] = in[43]; + out[Perm[44]] = in[44]; + out[Perm[45]] = in[45]; + out[Perm[46]] = in[46]; + out[Perm[47]] = in[47]; + out[Perm[48]] = in[48]; + out[Perm[49]] = in[49]; + + out[Perm[50]] = in[50]; + out[Perm[51]] = in[51]; + out[Perm[52]] = in[52]; + out[Perm[53]] = in[53]; + out[Perm[54]] = in[54]; + out[Perm[55]] = in[55]; + out[Perm[56]] = in[56]; + out[Perm[57]] = in[57]; + out[Perm[58]] = in[58]; + out[Perm[59]] = in[59]; + + out[Perm[60]] = in[60]; + out[Perm[61]] = in[61]; + out[Perm[62]] = in[62]; + out[Perm[63]] = in[63]; + return out; +endfunction : perm_layer + +//////////////////////////////////////////////////////////////////////// +// lfsr +//////////////////////////////////////////////////////////////////////// + +logic [LfsrWidth-1:0] lfsr_d, lfsr_q; +assign lfsr_d = + (en_i) ? (lfsr_q>>1) ^ ({LfsrWidth{lfsr_q[0]}} & Masks[LfsrWidth][LfsrWidth-1:0]) : lfsr_q; + +always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + //$display("%b %h", en_i, lfsr_d); + if (!rst_ni) begin + lfsr_q <= LfsrWidth'(RstVal); + end else begin + lfsr_q <= lfsr_d; + end +end + +//////////////////////////////////////////////////////////////////////// +// block cipher layers +//////////////////////////////////////////////////////////////////////// + +if (CipherLayers > unsigned'(0)) begin : g_cipher_layers + logic [63:0] ciph_layer; + localparam int unsigned NumRepl = ((64+LfsrWidth)/LfsrWidth); + + always_comb begin : p_ciph_layer + automatic logic [63:0] tmp; + tmp = 64'({NumRepl{lfsr_q}}); + for(int unsigned k = 0; k < CipherLayers; k++) begin + tmp = perm_layer(sbox4_layer(tmp)); + end + ciph_layer = tmp; + end + + // additiona output reg after cipher + if (CipherReg) begin : g_cipher_reg + logic [OutWidth-1:0] out_d, out_q; + + assign out_d = (en_i) ? ciph_layer[OutWidth-1:0] : out_q; + assign out_o = out_q[OutWidth-1:0]; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + out_q <= '0; + end else begin + out_q <= out_d; + end + end + // no outreg + end else begin : g_no_out_reg + assign out_o = ciph_layer[OutWidth-1:0]; + end + +// no block cipher +end else begin : g_no_cipher_layers + assign out_o = lfsr_q[OutWidth-1:0]; +end + +//////////////////////////////////////////////////////////////////////// +// assertions +//////////////////////////////////////////////////////////////////////// + +// pragma translate_off +initial begin + // these are the LUT limits + assert(OutWidth <= LfsrWidth) else + $fatal(1,"OutWidth must be smaller equal the LfsrWidth."); + assert(RstVal > unsigned'(0)) else + $fatal(1,"RstVal must be nonzero."); + assert((LfsrWidth >= $low(Masks)) && (LfsrWidth <= $high(Masks))) else + $fatal(1,"Unsupported LfsrWidth."); + assert(Masks[LfsrWidth][LfsrWidth-1]) else + $fatal(1, "LFSR mask is not correct. The MSB must be 1." ); + assert((CipherLayers > 0) && (LfsrWidth == 64) || (CipherLayers == 0)) else + $fatal(1, "Use additional cipher layers only in conjunction with an LFSR width of 64 bit." ); +end + +`ifndef VERILATOR + all_zero: assert property ( + @(posedge clk_i) disable iff (!rst_ni) en_i |-> lfsr_d) + else $fatal(1,"Lfsr must not be all-zero."); +`endif +// pragma translate_on + +endmodule // lfsr diff --git a/vendor/common_cells/src/lfsr_16bit.sv b/vendor/common_cells/src/lfsr_16bit.sv new file mode 100644 index 00000000..3fc93c77 --- /dev/null +++ b/vendor/common_cells/src/lfsr_16bit.sv @@ -0,0 +1,68 @@ +// 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. + +// Author: Florian Zaruba, ETH Zurich +// Date: 5.11.2018 +// Description: 16-bit LFSR + +// -------------- +// 16-bit LFSR +// -------------- +// +// Description: Shift register +// +module lfsr_16bit #( + parameter logic [15:0] SEED = 8'b0, + parameter int unsigned WIDTH = 16 +)( + input logic clk_i, + input logic rst_ni, + input logic en_i, + output logic [WIDTH-1:0] refill_way_oh, + output logic [$clog2(WIDTH)-1:0] refill_way_bin +); + + localparam int unsigned LogWidth = $clog2(WIDTH); + + logic [15:0] shift_d, shift_q; + + + always_comb begin + + automatic logic shift_in; + shift_in = !(shift_q[15] ^ shift_q[12] ^ shift_q[5] ^ shift_q[1]); + + shift_d = shift_q; + + if (en_i) + shift_d = {shift_q[14:0], shift_in}; + + // output assignment + refill_way_oh = 'b0; + refill_way_oh[shift_q[LogWidth-1:0]] = 1'b1; + refill_way_bin = shift_q; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ + if(~rst_ni) begin + shift_q <= SEED; + end else begin + shift_q <= shift_d; + end + end + + //pragma translate_off + initial begin + assert (WIDTH <= 16) + else $fatal(1, "WIDTH needs to be less than 16 because of the 16-bit LFSR"); + end + //pragma translate_on + +endmodule diff --git a/vendor/common_cells/src/lfsr_8bit.sv b/vendor/common_cells/src/lfsr_8bit.sv new file mode 100644 index 00000000..60fdf19f --- /dev/null +++ b/vendor/common_cells/src/lfsr_8bit.sv @@ -0,0 +1,61 @@ +// 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. + +// Author: Igor Loi - University of Bologna +// Author: Florian Zaruba, ETH Zurich +// Date: 12.11.2017 +// Description: 8-bit LFSR + +/// 8 bit Linear Feedback Shift register +module lfsr_8bit #( + parameter logic [7:0] SEED = 8'b0, + parameter int unsigned WIDTH = 8 +) ( + input logic clk_i, + input logic rst_ni, + input logic en_i, + output logic [ WIDTH-1:0] refill_way_oh, + output logic [$clog2(WIDTH)-1:0] refill_way_bin +); + + localparam int unsigned LogWidth = $clog2(WIDTH); + + logic [7:0] shift_d, shift_q; + + always_comb begin + + automatic logic shift_in; + shift_in = !(shift_q[7] ^ shift_q[3] ^ shift_q[2] ^ shift_q[1]); + + shift_d = shift_q; + + if (en_i) shift_d = {shift_q[6:0], shift_in}; + + // output assignment + refill_way_oh = 'b0; + refill_way_oh[shift_q[LogWidth - 1:0]] = 1'b1; + refill_way_bin = shift_q; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_ + if (~rst_ni) begin + shift_q <= SEED; + end else begin + shift_q <= shift_d; + end + end + + //pragma translate_off + initial begin + assert (WIDTH <= 8) else $fatal(1, "WIDTH needs to be less than 8 because of the 8-bit LFSR"); + end + //pragma translate_on + +endmodule diff --git a/vendor/common_cells/src/lzc.sv b/vendor/common_cells/src/lzc.sv new file mode 100644 index 00000000..424eb2ef --- /dev/null +++ b/vendor/common_cells/src/lzc.sv @@ -0,0 +1,112 @@ +// Copyright (c) 2018 - 2019 ETH Zurich, University of Bologna +// All rights reserved. +// +// This code is under development and not yet released to the public. +// Until it is released, the code is under the copyright of ETH Zurich and +// the University of Bologna, and may contain confidential and/or unpublished +// work. Any reuse/redistribution is strictly forbidden without written +// permission from ETH Zurich. +// +// Bug fixes and contributions will eventually be released under the +// SolderPad open hardware license in the context of the PULP platform +// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the +// University of Bologna. + +/// A trailing zero counter / leading zero counter. +/// Set MODE to 0 for trailing zero counter => cnt_o is the number of trailing zeros (from the LSB) +/// Set MODE to 1 for leading zero counter => cnt_o is the number of leading zeros (from the MSB) +/// If the input does not contain a zero, `empty_o` is asserted. Additionally `cnt_o` contains +/// the maximum number of zeros - 1. For example: +/// in_i = 000_0000, empty_o = 1, cnt_o = 6 (mode = 0) +/// in_i = 000_0001, empty_o = 0, cnt_o = 0 (mode = 0) +/// in_i = 000_1000, empty_o = 0, cnt_o = 3 (mode = 0) +/// Furthermore, this unit contains a more efficient implementation for Verilator (simulation only). +/// This speeds up simulation significantly. +module lzc #( + /// The width of the input vector. + parameter int unsigned WIDTH = 2, + /// Mode selection: 0 -> trailing zero, 1 -> leading zero + parameter bit MODE = 1'b0, + /// Dependent parameter. Do **not** change! + /// + /// Width of the output signal with the zero count. + parameter int unsigned CNT_WIDTH = cf_math_pkg::idx_width(WIDTH) +) ( + /// Input vector to be counted. + input logic [WIDTH-1:0] in_i, + /// Count of the leading / trailing zeros. + output logic [CNT_WIDTH-1:0] cnt_o, + /// Counter is empty: Asserted if all bits in in_i are zero. + output logic empty_o +); + + if (WIDTH == 1) begin : gen_degenerate_lzc + + assign cnt_o[0] = !in_i[0]; + assign empty_o = !in_i[0]; + + end else begin : gen_lzc + + localparam int unsigned NumLevels = $clog2(WIDTH); + + // pragma translate_off + initial begin + assert(WIDTH > 0) else $fatal(1, "input must be at least one bit wide"); + end + // pragma translate_on + + logic [WIDTH-1:0][NumLevels-1:0] index_lut; + logic [2**NumLevels-1:0] sel_nodes; + logic [2**NumLevels-1:0][NumLevels-1:0] index_nodes; + + logic [WIDTH-1:0] in_tmp; + + // reverse vector if required + always_comb begin : flip_vector + for (int unsigned i = 0; i < WIDTH; i++) begin + in_tmp[i] = (MODE) ? in_i[WIDTH-1-i] : in_i[i]; + end + end + + for (genvar j = 0; unsigned'(j) < WIDTH; j++) begin : g_index_lut + assign index_lut[j] = (NumLevels)'(unsigned'(j)); + end + + for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : g_levels + if (unsigned'(level) == NumLevels - 1) begin : g_last_level + for (genvar k = 0; k < 2 ** level; k++) begin : g_level + // if two successive indices are still in the vector... + if (unsigned'(k) * 2 < WIDTH - 1) begin : g_reduce + assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2] | in_tmp[k * 2 + 1]; + assign index_nodes[2 ** level - 1 + k] = (in_tmp[k * 2] == 1'b1) + ? index_lut[k * 2] : + index_lut[k * 2 + 1]; + end + // if only the first index is still in the vector... + if (unsigned'(k) * 2 == WIDTH - 1) begin : g_base + assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2]; + assign index_nodes[2 ** level - 1 + k] = index_lut[k * 2]; + end + // if index is out of range + if (unsigned'(k) * 2 > WIDTH - 1) begin : g_out_of_range + assign sel_nodes[2 ** level - 1 + k] = 1'b0; + assign index_nodes[2 ** level - 1 + k] = '0; + end + end + end else begin : g_not_last_level + for (genvar l = 0; l < 2 ** level; l++) begin : g_level + assign sel_nodes[2 ** level - 1 + l] = + sel_nodes[2 ** (level + 1) - 1 + l * 2] | sel_nodes[2 ** (level + 1) - 1 + l * 2 + 1]; + assign index_nodes[2 ** level - 1 + l] = (sel_nodes[2 ** (level + 1) - 1 + l * 2] == 1'b1) + ? index_nodes[2 ** (level + 1) - 1 + l * 2] : + index_nodes[2 ** (level + 1) - 1 + l * 2 + 1]; + end + end + end + + assign cnt_o = NumLevels > unsigned'(0) ? index_nodes[0] : {($clog2(WIDTH)) {1'b0}}; + assign empty_o = NumLevels > unsigned'(0) ? ~sel_nodes[0] : ~(|in_i); + + end : gen_lzc + +endmodule : lzc diff --git a/vendor/common_cells/src/max_counter.sv b/vendor/common_cells/src/max_counter.sv new file mode 100644 index 00000000..0081fab1 --- /dev/null +++ b/vendor/common_cells/src/max_counter.sv @@ -0,0 +1,77 @@ +// 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. + +// Up/down counter that tracks its maximum value + +module max_counter #( + parameter int unsigned WIDTH = 4 +) ( + input logic clk_i, + input logic rst_ni, + input logic clear_i, // synchronous clear for counter + input logic clear_max_i, // synchronous clear for maximum value + input logic en_i, // enable the counter + input logic load_i, // load a new value + input logic down_i, // downcount, default is up + input logic [WIDTH-1:0] delta_i, // counter delta + input logic [WIDTH-1:0] d_i, + output logic [WIDTH-1:0] q_o, + output logic [WIDTH-1:0] max_o, + output logic overflow_o, + output logic overflow_max_o +); + logic [WIDTH-1:0] max_d, max_q; + logic overflow_max_d, overflow_max_q; + + delta_counter #( + .WIDTH (WIDTH), + .STICKY_OVERFLOW (1'b1) + ) i_counter ( + .clk_i, + .rst_ni, + .clear_i, + .en_i, + .load_i, + .down_i, + .delta_i, + .d_i, + .q_o, + .overflow_o + ); + + always_comb begin + max_d = max_q; + max_o = max_q; + overflow_max_d = overflow_max_q; + if (clear_max_i) begin + max_d = '0; + overflow_max_d = 1'b0; + end else if (q_o > max_q) begin + max_d = q_o; + max_o = q_o; + if (overflow_o) begin + overflow_max_d = 1'b1; + end + end + end + + assign overflow_max_o = overflow_max_q; + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + max_q <= '0; + overflow_max_q <= 1'b0; + end else begin + max_q <= max_d; + overflow_max_q <= overflow_max_d; + end + end + +endmodule diff --git a/vendor/common_cells/src/mv_filter.sv b/vendor/common_cells/src/mv_filter.sv new file mode 100644 index 00000000..ddb81f07 --- /dev/null +++ b/vendor/common_cells/src/mv_filter.sv @@ -0,0 +1,55 @@ +// 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. + +// Author: Florian Zaruba + +module mv_filter #( + parameter int unsigned WIDTH = 4, + parameter int unsigned THRESHOLD = 10 +)( + input logic clk_i, + input logic rst_ni, + input logic sample_i, + input logic clear_i, + input logic d_i, + output logic q_o +); + logic [WIDTH-1:0] counter_q, counter_d; + logic d, q; + + assign q_o = q; + + always_comb begin + counter_d = counter_q; + d = q; + + if (counter_q >= THRESHOLD[WIDTH-1:0]) begin + d = 1'b1; + end else if (sample_i && d_i) begin + counter_d = counter_q + 1; + end + + // sync reset + if (clear_i) begin + counter_d = '0; + d = 1'b0; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + counter_q <= '0; + q <= 1'b0; + end else begin + counter_q <= counter_d; + q <= d; + end + end +endmodule diff --git a/vendor/common_cells/src/onehot_to_bin.sv b/vendor/common_cells/src/onehot_to_bin.sv new file mode 100644 index 00000000..0c33f084 --- /dev/null +++ b/vendor/common_cells/src/onehot_to_bin.sv @@ -0,0 +1,38 @@ +// 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. + +// Franceco Conti + +module onehot_to_bin #( + parameter int unsigned ONEHOT_WIDTH = 16, + // Do Not Change + parameter int unsigned BIN_WIDTH = ONEHOT_WIDTH == 1 ? 1 : $clog2(ONEHOT_WIDTH) +) ( + input logic [ONEHOT_WIDTH-1:0] onehot, + output logic [BIN_WIDTH-1:0] bin +); + + for (genvar j = 0; j < BIN_WIDTH; j++) begin : jl + logic [ONEHOT_WIDTH-1:0] tmp_mask; + for (genvar i = 0; i < ONEHOT_WIDTH; i++) begin : il + logic [BIN_WIDTH-1:0] tmp_i; + assign tmp_i = i; + assign tmp_mask[i] = tmp_i[j]; + end + assign bin[j] = |(tmp_mask & onehot); + end + +// pragma translate_off +`ifndef VERILATOR + assert final ($onehot0(onehot)) else + $fatal(1, "[onehot_to_bin] More than two bit set in the one-hot signal"); +`endif +// pragma translate_on +endmodule diff --git a/vendor/common_cells/src/plru_tree.sv b/vendor/common_cells/src/plru_tree.sv new file mode 100644 index 00000000..78a0a843 --- /dev/null +++ b/vendor/common_cells/src/plru_tree.sv @@ -0,0 +1,120 @@ +// 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. +// +// Author: David Schaffenrath, TU Graz +// Author: Florian Zaruba, ETH Zurich +// +// Description: Pseudo Least Recently Used Tree (PLRU) +// See: https://en.wikipedia.org/wiki/Pseudo-LRU + +module plru_tree #( + parameter int unsigned ENTRIES = 16 +) ( + input logic clk_i, + input logic rst_ni, + input logic [ENTRIES-1:0] used_i, // element i was used (one hot) + output logic [ENTRIES-1:0] plru_o // element i is the least recently used (one hot) +); + + localparam int unsigned LogEntries = $clog2(ENTRIES); + + logic [2*(ENTRIES-1)-1:0] plru_tree_q, plru_tree_d; + + always_comb begin : plru_replacement + plru_tree_d = plru_tree_q; + // The PLRU-tree indexing: + // lvl0 0 + // / \ + // / \ + // lvl1 1 2 + // / \ / \ + // lvl2 3 4 5 6 + // / \ /\/\ /\ + // ... ... ... ... + // Just predefine which nodes will be set/cleared + // E.g. for a TLB with 8 entries, the for-loop is semantically + // equivalent to the following pseudo-code: + // unique case (1'b1) + // used_i[7]: plru_tree_d[0, 2, 6] = {1, 1, 1}; + // used_i[6]: plru_tree_d[0, 2, 6] = {1, 1, 0}; + // used_i[5]: plru_tree_d[0, 2, 5] = {1, 0, 1}; + // used_i[4]: plru_tree_d[0, 2, 5] = {1, 0, 0}; + // used_i[3]: plru_tree_d[0, 1, 4] = {0, 1, 1}; + // used_i[2]: plru_tree_d[0, 1, 4] = {0, 1, 0}; + // used_i[1]: plru_tree_d[0, 1, 3] = {0, 0, 1}; + // used_i[0]: plru_tree_d[0, 1, 3] = {0, 0, 0}; + // default: begin /* No hit */ end + // endcase + for (int unsigned i = 0; i < ENTRIES; i++) begin + automatic int unsigned idx_base, shift, new_index; + // we got a hit so update the pointer as it was least recently used + if (used_i[i]) begin + // Set the nodes to the values we would expect + for (int unsigned lvl = 0; lvl < LogEntries; lvl++) begin + idx_base = $unsigned((2**lvl)-1); + // lvl0 <=> MSB, lvl1 <=> MSB-1, ... + shift = LogEntries - lvl; + // to circumvent the 32 bit integer arithmetic assignment + new_index = ~((i >> (shift-1)) & 1); + plru_tree_d[idx_base + (i >> shift)] = new_index[0]; + end + end + end + // Decode tree to write enable signals + // Next for-loop basically creates the following logic for e.g. an 8 entry + // TLB (note: pseudo-code obviously): + // plru_o[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1} + // plru_o[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0} + // plru_o[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1} + // plru_o[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0} + // plru_o[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1} + // plru_o[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0} + // plru_o[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1} + // plru_o[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0} + // For each entry traverse the tree. If every tree-node matches, + // the corresponding bit of the entry's index, this is + // the next entry to replace. + for (int unsigned i = 0; i < ENTRIES; i += 1) begin + automatic logic en; + automatic int unsigned idx_base, shift, new_index; + en = 1'b1; + for (int unsigned lvl = 0; lvl < LogEntries; lvl++) begin + idx_base = $unsigned((2**lvl)-1); + // lvl0 <=> MSB, lvl1 <=> MSB-1, ... + shift = LogEntries - lvl; + // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1); + new_index = (i >> (shift-1)) & 1; + if (new_index[0]) begin + en &= plru_tree_q[idx_base + (i>>shift)]; + end else begin + en &= ~plru_tree_q[idx_base + (i>>shift)]; + end + end + plru_o[i] = en; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + plru_tree_q <= '0; + end else begin + plru_tree_q <= plru_tree_d; + end + end + +// pragma translate_off +`ifndef VERILATOR + initial begin + assert (ENTRIES == 2**LogEntries) else $error("Entries must be a power of two"); + end +`endif +// pragma translate_on + +endmodule diff --git a/vendor/common_cells/src/popcount.sv b/vendor/common_cells/src/popcount.sv new file mode 100644 index 00000000..72b9b71f --- /dev/null +++ b/vendor/common_cells/src/popcount.sv @@ -0,0 +1,60 @@ +// Copyright (C) 2013-2018 ETH Zurich, 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. + +// Author: Manuel Eggimann + +// Description: This module calculates the hamming weight (number of ones) in +// its input vector using a balanced binary adder tree. Recursive instantiation +// is used to build the tree. Any unsigned INPUT_WIDTH larger or equal 2 is +// legal. The module pads the signal internally to the next power of two. The +// output result width is ceil(log2(INPUT_WIDTH))+1. + +module popcount #( + parameter int unsigned INPUT_WIDTH = 256, + localparam int unsigned PopcountWidth = $clog2(INPUT_WIDTH)+1 +) ( + input logic [INPUT_WIDTH-1:0] data_i, + output logic [PopcountWidth-1:0] popcount_o +); + + localparam int unsigned PaddedWidth = 1 << $clog2(INPUT_WIDTH); + + logic [PaddedWidth-1:0] padded_input; + logic [PopcountWidth-2:0] left_child_result, right_child_result; + + //Zero pad the input to next power of two + always_comb begin + padded_input = '0; + padded_input[INPUT_WIDTH-1:0] = data_i; + end + + //Recursive instantiation to build binary adder tree + if (INPUT_WIDTH == 1) begin : single_node + assign left_child_result = 1'b0; + assign right_child_result = padded_input[0]; + end else if (INPUT_WIDTH == 2) begin : leaf_node + assign left_child_result = padded_input[1]; + assign right_child_result = padded_input[0]; + end else begin : non_leaf_node + popcount #(.INPUT_WIDTH(PaddedWidth / 2)) + left_child( + .data_i(padded_input[PaddedWidth-1:PaddedWidth/2]), + .popcount_o(left_child_result)); + + popcount #(.INPUT_WIDTH(PaddedWidth / 2)) + right_child( + .data_i(padded_input[PaddedWidth/2-1:0]), + .popcount_o(right_child_result)); + end + + //Output assignment + assign popcount_o = left_child_result + right_child_result; + +endmodule : popcount diff --git a/vendor/common_cells/src/rr_arb_tree.sv b/vendor/common_cells/src/rr_arb_tree.sv new file mode 100644 index 00000000..3b1d0ca2 --- /dev/null +++ b/vendor/common_cells/src/rr_arb_tree.sv @@ -0,0 +1,347 @@ +// Copyright 2019 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. +// +// Author: Michael Schaffner , ETH Zurich +// Wolfgang Roenninger , ETH Zurich +// Date: 02.04.2019 +// Description: logarithmic arbitration tree with round robin arbitration scheme. + +/// The rr_arb_tree employs non-starving round robin-arbitration - i.e., the priorities +/// rotate each cycle. +/// +/// ## Fair vs. unfair Arbitration +/// +/// This refers to fair throughput distribution when not all inputs have active requests. +/// This module has an internal state `rr_q` which defines the highest priority input. (When +/// `ExtPrio` is `1'b1` this state is provided from the outside.) The arbitration tree will +/// choose the input with the same index as currently defined by the state if it has an active +/// request. Otherwise a *random* other active input is selected. The parameter `FairArb` is used +/// to distinguish between two methods of calculating the next state. +/// * `1'b0`: The next state is calculated by advancing the current state by one. This leads to the +/// state being calculated without the context of the active request. Leading to an +/// unfair throughput distribution if not all inputs have active requests. +/// * `1'b1`: The next state jumps to the next unserved request with higher index. +/// This is achieved by using two trailing-zero-counters (`lzc`). The upper has the masked +/// `req_i` signal with all indices which will have a higher priority in the next state. +/// The trailing zero count defines the input index with the next highest priority after +/// the current one is served. When the upper is empty the lower `lzc` provides the +/// wrapped index if there are outstanding requests with lower or same priority. +/// The implication of throughput fairness on the module timing are: +/// * The trailing zero counter (`lzc`) has a loglog relation of input to output timing. This means +/// that in this module the input to register path scales with Log(Log(`NumIn`)). +/// * The `rr_arb_tree` data multiplexing scales with Log(`NumIn`). This means that the input to output +/// timing path of this module also scales scales with Log(`NumIn`). +/// This implies that in this module the input to output path is always longer than the input to +/// register path. As the output data usually also terminates in a register the parameter `FairArb` +/// only has implications on the area. When it is `1'b0` a static plus one adder is instantiated. +/// If it is `1'b1` two `lzc`, a masking logic stage and a two input multiplexer are instantiated. +/// However these are small in respect of the data multiplexers needed, as the width of the `req_i` +/// signal is usually less as than `DataWidth`. +module rr_arb_tree #( + /// Number of inputs to be arbitrated. + parameter int unsigned NumIn = 64, + /// Data width of the payload in bits. Not needed if `DataType` is overwritten. + parameter int unsigned DataWidth = 32, + /// Data type of the payload, can be overwritten with custom type. Only use of `DataWidth`. + parameter type DataType = logic [DataWidth-1:0], + /// The `ExtPrio` option allows to override the internal round robin counter via the + /// `rr_i` signal. This can be useful in case multiple arbiters need to have + /// rotating priorities that are operating in lock-step. If static priority arbitration + /// is needed, just connect `rr_i` to '0. + /// + /// Set to 1'b1 to enable. + parameter bit ExtPrio = 1'b0, + /// If `AxiVldRdy` is set, the req/gnt signals are compliant with the AXI style vld/rdy + /// handshake. Namely, upstream vld (req) must not depend on rdy (gnt), as it can be deasserted + /// again even though vld is asserted. Enabling `AxiVldRdy` leads to a reduction of arbiter + /// delay and area. + /// + /// Set to `1'b1` to treat req/gnt as vld/rdy. + parameter bit AxiVldRdy = 1'b0, + /// The `LockIn` option prevents the arbiter from changing the arbitration + /// decision when the arbiter is disabled. I.e., the index of the first request + /// that wins the arbitration will be locked in case the destination is not + /// able to grant the request in the same cycle. + /// + /// Set to `1'b1` to enable. + parameter bit LockIn = 1'b0, + /// When set, ensures that throughput gets distributed evenly between all inputs. + /// + /// Set to `1'b0` to disable. + parameter bit FairArb = 1'b1, + /// Dependent parameter, do **not** overwrite. + /// Width of the arbitration priority signal and the arbitrated index. + parameter int unsigned IdxWidth = (NumIn > 32'd1) ? unsigned'($clog2(NumIn)) : 32'd1, + /// Dependent parameter, do **not** overwrite. + /// Type for defining the arbitration priority and arbitrated index signal. + parameter type idx_t = logic [IdxWidth-1:0] +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Clears the arbiter state. Only used if `ExtPrio` is `1'b0` or `LockIn` is `1'b1`. + input logic flush_i, + /// External round-robin priority. Only used if `ExtPrio` is `1'b1.` + input idx_t rr_i, + /// Input requests arbitration. + input logic [NumIn-1:0] req_i, + /* verilator lint_off UNOPTFLAT */ + /// Input request is granted. + output logic [NumIn-1:0] gnt_o, + /* verilator lint_on UNOPTFLAT */ + /// Input data for arbitration. + input DataType [NumIn-1:0] data_i, + /// Output request is valid. + output logic req_o, + /// Output request is granted. + input logic gnt_i, + /// Output data. + output DataType data_o, + /// Index from which input the data came from. + output idx_t idx_o +); + + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + // Default SVA reset + default disable iff (!rst_ni || flush_i); + `endif + `endif + // pragma translate_on + + // just pass through in this corner case + if (NumIn == unsigned'(1)) begin : gen_pass_through + assign req_o = req_i[0]; + assign gnt_o[0] = gnt_i; + assign data_o = data_i[0]; + assign idx_o = '0; + // non-degenerate cases + end else begin : gen_arbiter + localparam int unsigned NumLevels = unsigned'($clog2(NumIn)); + + /* verilator lint_off UNOPTFLAT */ + idx_t [2**NumLevels-2:0] index_nodes; // used to propagate the indices + DataType [2**NumLevels-2:0] data_nodes; // used to propagate the data + logic [2**NumLevels-2:0] gnt_nodes; // used to propagate the grant to masters + logic [2**NumLevels-2:0] req_nodes; // used to propagate the requests to slave + /* lint_off */ + idx_t rr_q; + logic [NumIn-1:0] req_d; + + // the final arbitration decision can be taken from the root of the tree + assign req_o = req_nodes[0]; + assign data_o = data_nodes[0]; + assign idx_o = index_nodes[0]; + + if (ExtPrio) begin : gen_ext_rr + assign rr_q = rr_i; + assign req_d = req_i; + end else begin : gen_int_rr + idx_t rr_d; + + // lock arbiter decision in case we got at least one req and no acknowledge + if (LockIn) begin : gen_lock + logic lock_d, lock_q; + logic [NumIn-1:0] req_q; + + assign lock_d = req_o & ~gnt_i; + assign req_d = (lock_q) ? req_q : req_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg + if (!rst_ni) begin + lock_q <= '0; + end else begin + if (flush_i) begin + lock_q <= '0; + end else begin + lock_q <= lock_d; + end + end + end + + // pragma translate_off + `ifndef VERILATOR + lock: assert property( + @(posedge clk_i) LockIn |-> req_o && !gnt_i |=> idx_o == $past(idx_o)) else + $fatal (1, "Lock implies same arbiter decision in next cycle if output is not \ + ready."); + + logic [NumIn-1:0] req_tmp; + assign req_tmp = req_q & req_i; + lock_req: assume property( + @(posedge clk_i) LockIn |-> lock_d |=> req_tmp == req_q) else + $fatal (1, "It is disallowed to deassert unserved request signals when LockIn is \ + enabled."); + `endif + // pragma translate_on + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_req_regs + if (!rst_ni) begin + req_q <= '0; + end else begin + if (flush_i) begin + req_q <= '0; + end else begin + req_q <= req_d; + end + end + end + end else begin : gen_no_lock + assign req_d = req_i; + end + + if (FairArb) begin : gen_fair_arb + logic [NumIn-1:0] upper_mask, lower_mask; + idx_t upper_idx, lower_idx, next_idx; + logic upper_empty, lower_empty; + + for (genvar i = 0; i < NumIn; i++) begin : gen_mask + assign upper_mask[i] = (i > rr_q) ? req_d[i] : 1'b0; + assign lower_mask[i] = (i <= rr_q) ? req_d[i] : 1'b0; + end + + lzc #( + .WIDTH ( NumIn ), + .MODE ( 1'b0 ) + ) i_lzc_upper ( + .in_i ( upper_mask ), + .cnt_o ( upper_idx ), + .empty_o ( upper_empty ) + ); + + lzc #( + .WIDTH ( NumIn ), + .MODE ( 1'b0 ) + ) i_lzc_lower ( + .in_i ( lower_mask ), + .cnt_o ( lower_idx ), + .empty_o ( /*unused*/ ) + ); + + assign next_idx = upper_empty ? lower_idx : upper_idx; + assign rr_d = (gnt_i && req_o) ? next_idx : rr_q; + + end else begin : gen_unfair_arb + assign rr_d = (gnt_i && req_o) ? ((rr_q == idx_t'(NumIn-1)) ? '0 : rr_q + 1'b1) : rr_q; + end + + // this holds the highest priority + always_ff @(posedge clk_i or negedge rst_ni) begin : p_rr_regs + if (!rst_ni) begin + rr_q <= '0; + end else begin + if (flush_i) begin + rr_q <= '0; + end else begin + rr_q <= rr_d; + end + end + end + end + + assign gnt_nodes[0] = gnt_i; + + // arbiter tree + for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : gen_levels + for (genvar l = 0; l < 2**level; l++) begin : gen_level + // local select signal + logic sel; + // index calcs + localparam int unsigned Idx0 = 2**level-1+l;// current node + localparam int unsigned Idx1 = 2**(level+1)-1+l*2; + ////////////////////////////////////////////////////////////// + // uppermost level where data is fed in from the inputs + if (unsigned'(level) == NumLevels-1) begin : gen_first_level + // if two successive indices are still in the vector... + if (unsigned'(l) * 2 < NumIn-1) begin : gen_reduce + assign req_nodes[Idx0] = req_d[l*2] | req_d[l*2+1]; + + // arbitration: round robin + assign sel = ~req_d[l*2] | req_d[l*2+1] & rr_q[NumLevels-1-level]; + + assign index_nodes[Idx0] = idx_t'(sel); + assign data_nodes[Idx0] = (sel) ? data_i[l*2+1] : data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]) & ~sel; + assign gnt_o[l*2+1] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2+1]) & sel; + end + // if only the first index is still in the vector... + if (unsigned'(l) * 2 == NumIn-1) begin : gen_first + assign req_nodes[Idx0] = req_d[l*2]; + assign index_nodes[Idx0] = '0;// always zero in this case + assign data_nodes[Idx0] = data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]); + end + // if index is out of range, fill up with zeros (will get pruned) + if (unsigned'(l) * 2 > NumIn-1) begin : gen_out_of_range + assign req_nodes[Idx0] = 1'b0; + assign index_nodes[Idx0] = idx_t'('0); + assign data_nodes[Idx0] = DataType'('0); + end + ////////////////////////////////////////////////////////////// + // general case for other levels within the tree + end else begin : gen_other_levels + assign req_nodes[Idx0] = req_nodes[Idx1] | req_nodes[Idx1+1]; + + // arbitration: round robin + assign sel = ~req_nodes[Idx1] | req_nodes[Idx1+1] & rr_q[NumLevels-1-level]; + + assign index_nodes[Idx0] = (sel) ? + idx_t'({1'b1, index_nodes[Idx1+1][NumLevels-unsigned'(level)-2:0]}) : + idx_t'({1'b0, index_nodes[Idx1][NumLevels-unsigned'(level)-2:0]}); + + assign data_nodes[Idx0] = (sel) ? data_nodes[Idx1+1] : data_nodes[Idx1]; + assign gnt_nodes[Idx1] = gnt_nodes[Idx0] & ~sel; + assign gnt_nodes[Idx1+1] = gnt_nodes[Idx0] & sel; + end + ////////////////////////////////////////////////////////////// + end + end + + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + initial begin : p_assert + assert(NumIn) + else $fatal(1, "Input must be at least one element wide."); + assert(!(LockIn && ExtPrio)) + else $fatal(1,"Cannot use LockIn feature together with external ExtPrio."); + end + + hot_one : assert property( + @(posedge clk_i) $onehot0(gnt_o)) + else $fatal (1, "Grant signal must be hot1 or zero."); + + gnt0 : assert property( + @(posedge clk_i) |gnt_o |-> gnt_i) + else $fatal (1, "Grant out implies grant in."); + + gnt1 : assert property( + @(posedge clk_i) req_o |-> gnt_i |-> |gnt_o) + else $fatal (1, "Req out and grant in implies grant out."); + + gnt_idx : assert property( + @(posedge clk_i) req_o |-> gnt_i |-> gnt_o[idx_o]) + else $fatal (1, "Idx_o / gnt_o do not match."); + + req0 : assert property( + @(posedge clk_i) |req_i |-> req_o) + else $fatal (1, "Req in implies req out."); + + req1 : assert property( + @(posedge clk_i) req_o |-> |req_i) + else $fatal (1, "Req out implies req in."); + `endif + `endif + // pragma translate_on + end + +endmodule : rr_arb_tree diff --git a/vendor/common_cells/src/rstgen.sv b/vendor/common_cells/src/rstgen.sv new file mode 100644 index 00000000..a7dccc63 --- /dev/null +++ b/vendor/common_cells/src/rstgen.sv @@ -0,0 +1,30 @@ +// 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. + +// Davide Rossi + +module rstgen ( + input logic clk_i, + input logic rst_ni, + input logic test_mode_i, + output logic rst_no, + output logic init_no +); + + rstgen_bypass i_rstgen_bypass ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .rst_test_mode_ni ( rst_ni ), + .test_mode_i ( test_mode_i ), + .rst_no ( rst_no ), + .init_no ( init_no ) + ); + +endmodule diff --git a/vendor/common_cells/src/rstgen_bypass.sv b/vendor/common_cells/src/rstgen_bypass.sv new file mode 100644 index 00000000..c51ee835 --- /dev/null +++ b/vendor/common_cells/src/rstgen_bypass.sv @@ -0,0 +1,57 @@ +// 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. + +// Florian Zaruba +// Description: This module is a reset synchronizer with a dedicated reset bypass pin for testmode reset. +// Pro Tip: The wise Dr. Schaffner recommends at least 4 registers! + +module rstgen_bypass #( + parameter int unsigned NumRegs = 4 +) ( + input logic clk_i, + input logic rst_ni, + input logic rst_test_mode_ni, + input logic test_mode_i, + output logic rst_no, + output logic init_no +); + + // internal reset + logic rst_n; + + logic [NumRegs-1:0] synch_regs_q; + // bypass mode + always_comb begin + if (test_mode_i == 1'b0) begin + rst_n = rst_ni; + rst_no = synch_regs_q[NumRegs-1]; + init_no = synch_regs_q[NumRegs-1]; + end else begin + rst_n = rst_test_mode_ni; + rst_no = rst_test_mode_ni; + init_no = 1'b1; + end + end + + always @(posedge clk_i or negedge rst_n) begin + if (~rst_n) begin + synch_regs_q <= 0; + end else begin + synch_regs_q <= {synch_regs_q[NumRegs-2:0], 1'b1}; + end + end + // pragma translate_off + `ifndef VERILATOR + initial begin : p_assertions + if (NumRegs < 1) $fatal(1, "At least one register is required."); + end + `endif + // pragma translate_on +endmodule diff --git a/vendor/common_cells/src/serial_deglitch.sv b/vendor/common_cells/src/serial_deglitch.sv new file mode 100644 index 00000000..3302e1ae --- /dev/null +++ b/vendor/common_cells/src/serial_deglitch.sv @@ -0,0 +1,50 @@ +// 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. + +// Author: Florian Zaruba +// Description: Deglitches a serial line by taking multiple samples until +// asserting the output high/low. + +module serial_deglitch #( + parameter int unsigned SIZE = 4 +)( + input logic clk_i, // clock + input logic rst_ni, // asynchronous reset active low + input logic en_i, // enable + input logic d_i, // serial data in + output logic q_o // filtered data out +); + logic [SIZE-1:0] count_q; + logic q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + count_q <= '0; + q <= 1'b0; + end else begin + if (en_i) begin + if (d_i == 1'b1 && count_q != SIZE[SIZE-1:0]) begin + count_q <= count_q + 1; + end else if (d_i == 1'b0 && count_q != SIZE[SIZE-1:0]) begin + count_q <= count_q - 1; + end + end + end + end + + // output process + always_comb begin + if (count_q == SIZE[SIZE-1:0]) begin + q_o = 1'b1; + end else if (count_q == 0) begin + q_o = 1'b0; + end + end +endmodule diff --git a/vendor/common_cells/src/shift_reg.sv b/vendor/common_cells/src/shift_reg.sv new file mode 100644 index 00000000..7193fbcd --- /dev/null +++ b/vendor/common_cells/src/shift_reg.sv @@ -0,0 +1,53 @@ + +// 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. +// +// Author: +// +// Description: Simple shift register for arbitrary depth and types + +module shift_reg #( + parameter type dtype = logic, + parameter int unsigned Depth = 1 +)( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input dtype d_i, + output dtype d_o +); + + // register of depth 0 is a wire + if (Depth == 0) begin : gen_pass_through + assign d_o = d_i; + // register of depth 1 is a simple register + end else if (Depth == 1) begin : gen_register + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + d_o <= '0; + end else begin + d_o <= d_i; + end + end + // if depth is greater than 1 it becomes a shift register + end else if (Depth > 1) begin : gen_shift_reg + dtype [Depth-1:0] reg_d, reg_q; + assign d_o = reg_q[Depth-1]; + assign reg_d = {reg_q[Depth-2:0], d_i}; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + reg_q <= '0; + end else begin + reg_q <= reg_d; + end + end + end + +endmodule diff --git a/vendor/common_cells/src/spill_register.sv b/vendor/common_cells/src/spill_register.sv new file mode 100644 index 00000000..d6f8d306 --- /dev/null +++ b/vendor/common_cells/src/spill_register.sv @@ -0,0 +1,95 @@ +// 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. +// +// Fabian Schuiki + + +/// A register with handshakes that completely cuts any combinational paths +/// between the input and output. +module spill_register #( + parameter type T = logic, + parameter bit Bypass = 1'b0 // make this spill register transparent +) ( + input logic clk_i , + input logic rst_ni , + input logic valid_i , + output logic ready_o , + input T data_i , + output logic valid_o , + input logic ready_i , + output T data_o +); + + if (Bypass) begin : gen_bypass + assign valid_o = valid_i; + assign ready_o = ready_i; + assign data_o = data_i; + end else begin : gen_spill_reg + // The A register. + T a_data_q; + logic a_full_q; + logic a_fill, a_drain; + logic a_en, a_en_data; + + always_ff @(posedge clk_i or negedge rst_ni) begin : ps_a_data + if (!rst_ni) + a_data_q <= '0; + else if (a_fill) + a_data_q <= data_i; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : ps_a_full + if (!rst_ni) + a_full_q <= 0; + else if (a_fill || a_drain) + a_full_q <= a_fill; + end + + // The B register. + T b_data_q; + logic b_full_q; + logic b_fill, b_drain; + + always_ff @(posedge clk_i or negedge rst_ni) begin : ps_b_data + if (!rst_ni) + b_data_q <= '0; + else if (b_fill) + b_data_q <= a_data_q; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : ps_b_full + if (!rst_ni) + b_full_q <= 0; + else if (b_fill || b_drain) + b_full_q <= b_fill; + end + + // Fill the A register when the A or B register is empty. Drain the A register + // whenever it is full and being filled. + assign a_fill = valid_i && ready_o; + assign a_drain = a_full_q && !b_full_q; + + // Fill the B register whenever the A register is drained, but the downstream + // circuit is not ready. Drain the B register whenever it is full and the + // downstream circuit is ready. + assign b_fill = a_drain && !ready_i; + assign b_drain = b_full_q && ready_i; + + // We can accept input as long as register B is not full. + assign ready_o = !a_full_q || !b_full_q; + + // The unit provides output as long as one of the registers is filled. + assign valid_o = a_full_q | b_full_q; + + // We empty the spill register before the slice register. + assign data_o = b_full_q ? b_data_q : a_data_q; + end +endmodule diff --git a/vendor/common_cells/src/stream_arbiter.sv b/vendor/common_cells/src/stream_arbiter.sv new file mode 100644 index 00000000..c8ca2a87 --- /dev/null +++ b/vendor/common_cells/src/stream_arbiter.sv @@ -0,0 +1,49 @@ +// 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 arbiter: Arbitrates a parametrizable number of input streams (i.e., valid-ready +// handshaking with dependency rules as in AXI4) to a single output stream. Once `oup_valid_o` is +// asserted, `oup_data_o` remains invariant until the output handshake has occurred. The +// arbitration scheme is round-robin with "look ahead", see the `rrarbiter` for details. + +module stream_arbiter #( + parameter type DATA_T = logic, // Vivado requires a default value for type parameters. + parameter integer N_INP = -1, // Synopsys DC requires a default value for parameters. + parameter ARBITER = "rr" // "rr" or "prio" +) ( + input logic clk_i, + input logic rst_ni, + + input DATA_T [N_INP-1:0] inp_data_i, + input logic [N_INP-1:0] inp_valid_i, + output logic [N_INP-1:0] inp_ready_o, + + output DATA_T oup_data_o, + output logic oup_valid_o, + input logic oup_ready_i +); + + stream_arbiter_flushable #( + .DATA_T (DATA_T), + .N_INP (N_INP), + .ARBITER (ARBITER) + ) i_arb ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (1'b0), + .inp_data_i (inp_data_i), + .inp_valid_i (inp_valid_i), + .inp_ready_o (inp_ready_o), + .oup_data_o (oup_data_o), + .oup_valid_o (oup_valid_o), + .oup_ready_i (oup_ready_i) + ); + +endmodule diff --git a/vendor/common_cells/src/stream_arbiter_flushable.sv b/vendor/common_cells/src/stream_arbiter_flushable.sv new file mode 100644 index 00000000..32946e68 --- /dev/null +++ b/vendor/common_cells/src/stream_arbiter_flushable.sv @@ -0,0 +1,82 @@ +// 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 arbiter: Arbitrates a parametrizable number of input streams (i.e., valid-ready +// handshaking with dependency rules as in AXI4) to a single output stream. Once `oup_valid_o` is +// asserted, `oup_data_o` remains invariant until the output handshake has occurred. The +// arbitration scheme is fair round-robin tree, see `rr_arb_tree` for details. + +module stream_arbiter_flushable #( + parameter type DATA_T = logic, // Vivado requires a default value for type parameters. + parameter integer N_INP = -1, // Synopsys DC requires a default value for parameters. + parameter ARBITER = "rr" // "rr" or "prio" +) ( + input logic clk_i, + input logic rst_ni, + input logic flush_i, + + input DATA_T [N_INP-1:0] inp_data_i, + input logic [N_INP-1:0] inp_valid_i, + output logic [N_INP-1:0] inp_ready_o, + + output DATA_T oup_data_o, + output logic oup_valid_o, + input logic oup_ready_i +); + + if (ARBITER == "rr") begin : gen_rr_arb + rr_arb_tree #( + .NumIn (N_INP), + .DataType (DATA_T), + .ExtPrio (1'b0), + .AxiVldRdy (1'b1), + .LockIn (1'b1) + ) i_arbiter ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ('0), + .req_i (inp_valid_i), + .gnt_o (inp_ready_o), + .data_i (inp_data_i), + .gnt_i (oup_ready_i), + .req_o (oup_valid_o), + .data_o (oup_data_o), + .idx_o () + ); + + end else if (ARBITER == "prio") begin : gen_prio_arb + rr_arb_tree #( + .NumIn (N_INP), + .DataType (DATA_T), + .ExtPrio (1'b1), + .AxiVldRdy (1'b1), + .LockIn (1'b1) + ) i_arbiter ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ('0), + .req_i (inp_valid_i), + .gnt_o (inp_ready_o), + .data_i (inp_data_i), + .gnt_i (oup_ready_i), + .req_o (oup_valid_o), + .data_o (oup_data_o), + .idx_o () + ); + + end else begin : gen_arb_error + // pragma translate_off + $fatal(1, "Invalid value for parameter 'ARBITER'!"); + // pragma translate_on + end + +endmodule diff --git a/vendor/common_cells/src/stream_delay.sv b/vendor/common_cells/src/stream_delay.sv new file mode 100644 index 00000000..5051b6c2 --- /dev/null +++ b/vendor/common_cells/src/stream_delay.sv @@ -0,0 +1,132 @@ +// 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. + +// Author: Florian Zaruba, zarubaf@iis.ee.ethz.ch +// Description: Delay (or randomize) AXI-like handshaking + +module stream_delay #( + parameter bit StallRandom = 0, + parameter int FixedDelay = 1, + parameter type payload_t = logic +)( + input logic clk_i, + input logic rst_ni, + + input payload_t payload_i, + output logic ready_o, + input logic valid_i, + + output payload_t payload_o, + input logic ready_i, + output logic valid_o +); + + if (FixedDelay == 0 && !StallRandom) begin : gen_pass_through + assign ready_o = ready_i; + assign valid_o = valid_i; + assign payload_o = payload_i; + end else begin : gen_delay + + localparam int unsigned CounterBits = 4; + + typedef enum logic [1:0] { + Idle, Valid, Ready + } state_e; + + state_e state_d, state_q; + + logic load; + logic [3:0] count_out; + logic en; + + logic [CounterBits-1:0] counter_load; + + assign payload_o = payload_i; + + always_comb begin + state_d = state_q; + valid_o = 1'b0; + ready_o = 1'b0; + load = 1'b0; + en = 1'b0; + + unique case (state_q) + Idle: begin + if (valid_i) begin + load = 1'b1; + state_d = Valid; + // Just one cycle delay + if (FixedDelay == 1 || (StallRandom && counter_load == 1)) begin + state_d = Ready; + end + + if (StallRandom && counter_load == 0) begin + valid_o = 1'b1; + ready_o = ready_i; + if (ready_i) state_d = Idle; + else state_d = Ready; + end + end + end + Valid: begin + en = 1'b1; + if (count_out == 0) begin + state_d = Ready; + end + end + + Ready: begin + valid_o = 1'b1; + ready_o = ready_i; + if (ready_i) state_d = Idle; + end + default : /* default */; + endcase + + end + + if (StallRandom) begin : gen_random_stall + lfsr_16bit #( + .WIDTH ( 16 ) + ) i_lfsr_16bit ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .en_i ( load ), + .refill_way_oh ( ), + .refill_way_bin ( counter_load ) + ); + end else begin : gen_fixed_delay + assign counter_load = FixedDelay; + end + + counter #( + .WIDTH ( CounterBits ) + ) i_counter ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( 1'b0 ), + .en_i ( en ), + .load_i ( load ), + .down_i ( 1'b1 ), + .d_i ( counter_load ), + .q_o ( count_out ), + .overflow_o ( ) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + state_q <= Idle; + end else begin + state_q <= state_d; + end + end + end + +endmodule diff --git a/vendor/common_cells/src/stream_demux.sv b/vendor/common_cells/src/stream_demux.sv new file mode 100644 index 00000000..69ad3099 --- /dev/null +++ b/vendor/common_cells/src/stream_demux.sv @@ -0,0 +1,36 @@ +// 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. + +/// Connects the input stream (valid-ready) handshake to one of `N_OUP` output stream handshakes. +/// +/// This module has no data ports because stream data does not need to be demultiplexed: the data of +/// the input stream can just be applied at all output streams. +module stream_demux #( + /// Number of connected outputs. + parameter int unsigned N_OUP = 32'd1, + /// Dependent parameters, DO NOT OVERRIDE! + parameter int unsigned LOG_N_OUP = (N_OUP > 32'd1) ? unsigned'($clog2(N_OUP)) : 1'b1 +) ( + input logic inp_valid_i, + output logic inp_ready_o, + + input logic [LOG_N_OUP-1:0] oup_sel_i, + + output logic [N_OUP-1:0] oup_valid_o, + input logic [N_OUP-1:0] oup_ready_i +); + + always_comb begin + oup_valid_o = '0; + oup_valid_o[oup_sel_i] = inp_valid_i; + end + assign inp_ready_o = oup_ready_i[oup_sel_i]; + +endmodule diff --git a/vendor/common_cells/src/stream_fifo.sv b/vendor/common_cells/src/stream_fifo.sv new file mode 100644 index 00000000..e7c60e57 --- /dev/null +++ b/vendor/common_cells/src/stream_fifo.sv @@ -0,0 +1,66 @@ +// 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. + +// Author: Georg Rutishauser + +module stream_fifo #( + /// FIFO is in fall-through mode + parameter bit FALL_THROUGH = 1'b0, + /// Default data width if the fifo is of type logic + parameter int unsigned DATA_WIDTH = 32, + /// Depth can be arbitrary from 0 to 2**32 + parameter int unsigned DEPTH = 8, + parameter type T = logic [DATA_WIDTH-1:0], + // DO NOT OVERWRITE THIS PARAMETER + parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1 +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic flush_i, // flush the fifo + input logic testmode_i, // test_mode to bypass clock gating + output logic [ADDR_DEPTH-1:0] usage_o, // fill pointer + // input interface + input T data_i, // data to push into the fifo + input logic valid_i, // input data valid + output logic ready_o, // fifo is not full + // output interface + output T data_o, // output data + output logic valid_o, // fifo is not empty + input logic ready_i // pop head from fifo +); + + logic push, pop; + logic empty, full; + + assign push = valid_i & ~full; + assign pop = ready_i & ~empty; + assign ready_o = ~full; + assign valid_o = ~empty; + + fifo_v3 #( + .FALL_THROUGH (FALL_THROUGH), + .DATA_WIDTH (DATA_WIDTH), + .DEPTH (DEPTH), + .dtype(T) + ) fifo_i ( + .clk_i, + .rst_ni, + .flush_i, + .testmode_i, + .full_o (full), + .empty_o (empty), + .usage_o, + .data_i, + .push_i (push), + .data_o, + .pop_i (pop) + ); + +endmodule diff --git a/vendor/common_cells/src/stream_filter.sv b/vendor/common_cells/src/stream_filter.sv new file mode 100644 index 00000000..52a5835e --- /dev/null +++ b/vendor/common_cells/src/stream_filter.sv @@ -0,0 +1,26 @@ +// 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 filter: If `drop_i` is `1`, signal `ready` to the upstream regardless of the downstream, +// and do not propagate `valid` downstream. Otherwise, connect upstream to downstream. +module stream_filter ( + input logic valid_i, + output logic ready_o, + + input logic drop_i, + + output logic valid_o, + input logic ready_i +); + + assign valid_o = drop_i ? 1'b0 : valid_i; + assign ready_o = drop_i ? 1'b1 : ready_i; + +endmodule diff --git a/vendor/common_cells/src/stream_fork.sv b/vendor/common_cells/src/stream_fork.sv new file mode 100644 index 00000000..aebb0f5d --- /dev/null +++ b/vendor/common_cells/src/stream_fork.sv @@ -0,0 +1,133 @@ +// 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. + +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, WAIT} 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 = WAIT; + end + end else begin + ready_o = 1'b0; + end + end + WAIT: 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 = WAIT; + end + end else begin // No output handshake + oup_ready[i] = 1'b0; + end + end + end + WAIT: 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. + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_OUP >= 1) else $fatal("Number of outputs must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule diff --git a/vendor/common_cells/src/stream_fork_dynamic.sv b/vendor/common_cells/src/stream_fork_dynamic.sv new file mode 100644 index 00000000..e4720f70 --- /dev/null +++ b/vendor/common_cells/src/stream_fork_dynamic.sv @@ -0,0 +1,95 @@ +// 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 + +/// 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 ) + ); + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_OUP >= 1) else $fatal(1, "N_OUP must be at least 1!"); + end +`endif +// pragma translate_on +endmodule diff --git a/vendor/common_cells/src/stream_intf.sv b/vendor/common_cells/src/stream_intf.sv new file mode 100644 index 00000000..32f2d8b6 --- /dev/null +++ b/vendor/common_cells/src/stream_intf.sv @@ -0,0 +1,49 @@ +// 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. + +// Author: Florian Zaruba + +/// A stream interface with custom payload of type `payload_t`. +/// Handshaking rules as defined in the AXI standard. +interface STREAM_DV #( + /// Custom payload type. + parameter type payload_t = logic +)( + /// Interface clock. + input logic clk_i +); + payload_t data; + logic valid; + logic ready; + + modport In ( + output ready, + input valid, data + ); + + modport Out ( + output valid, data, + input ready + ); + + /// Passive modport for scoreboard and monitors. + modport Passive ( + input valid, ready, data + ); + + // Make sure that the handshake and payload is stable + // pragma translate_off + `ifndef VERILATOR + assert property (@(posedge clk_i) (valid && !ready |=> $stable(data))); + assert property (@(posedge clk_i) (valid && !ready |=> valid)); + `endif + // pragma translate_on +endinterface diff --git a/vendor/common_cells/src/stream_join.sv b/vendor/common_cells/src/stream_join.sv new file mode 100644 index 00000000..2f210bc7 --- /dev/null +++ b/vendor/common_cells/src/stream_join.sv @@ -0,0 +1,43 @@ +// 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 + +/// Stream join: Joins a parametrizable number of input streams (i.e., valid-ready handshaking with +/// dependency rules as in AXI4) to a single output stream. The output handshake happens only once +/// all inputs are valid. The data channel flows outside of this module. +module stream_join #( + /// 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, + /// Output stream valid handshake + output logic oup_valid_o, + /// Output stream ready handshake + input logic oup_ready_i +); + + assign oup_valid_o = (&inp_valid_i); + for (genvar i = 0; i < N_INP; i++) begin : gen_inp_ready + assign inp_ready_o[i] = oup_valid_o & oup_ready_i; + end + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_INP >= 1) else $fatal(1, "N_INP must be at least 1!"); + end +`endif +// pragma translate_on +endmodule diff --git a/vendor/common_cells/src/stream_mux.sv b/vendor/common_cells/src/stream_mux.sv new file mode 100644 index 00000000..156c572c --- /dev/null +++ b/vendor/common_cells/src/stream_mux.sv @@ -0,0 +1,46 @@ +// 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 multiplexer: connects the output to one of `N_INP` data streams with valid-ready +/// handshaking. + +module stream_mux #( + parameter type DATA_T = logic, // Vivado requires a default value for type parameters. + parameter integer N_INP = 0, // Synopsys DC requires a default value for value parameters. + /// Dependent parameters, DO NOT OVERRIDE! + parameter integer LOG_N_INP = $clog2(N_INP) +) ( + input DATA_T [N_INP-1:0] inp_data_i, + input logic [N_INP-1:0] inp_valid_i, + output logic [N_INP-1:0] inp_ready_o, + + input logic [LOG_N_INP-1:0] inp_sel_i, + + output DATA_T oup_data_o, + output logic oup_valid_o, + input logic oup_ready_i +); + + always_comb begin + inp_ready_o = '0; + inp_ready_o[inp_sel_i] = oup_ready_i; + end + assign oup_data_o = inp_data_i[inp_sel_i]; + assign oup_valid_o = inp_valid_i[inp_sel_i]; + +// pragma translate_off +`ifndef VERILATOR + initial begin: p_assertions + assert (N_INP >= 1) else $fatal ("The number of inputs must be at least 1!"); + end +`endif +// pragma translate_on + +endmodule diff --git a/vendor/common_cells/src/stream_omega_net.sv b/vendor/common_cells/src/stream_omega_net.sv new file mode 100644 index 00000000..ad8e11d8 --- /dev/null +++ b/vendor/common_cells/src/stream_omega_net.sv @@ -0,0 +1,301 @@ +// Copyright (c) 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. + +// Author: Wolfgang Roenninger + +/// Omega network using multiple `stream_xbar` as switches. +/// +/// An omega network is isomorphic to a butterfly network. +/// +/// Handshaking rules as defined by the `AMBA AXI` standard on default. +module stream_omega_net #( + /// Number of inputs into the network (`> 0`). + parameter int unsigned NumInp = 32'd0, + /// Number of outputs from the network (`> 0`). + parameter int unsigned NumOut = 32'd0, + /// Radix of the individual switch points of the network. + /// Currently supported are `32'd2` and `32'd4`. + parameter int unsigned Radix = 32'd2, + /// Data width of the stream. Can be overwritten by defining the type parameter `payload_t`. + parameter int unsigned DataWidth = 32'd1, + /// Payload type of the data ports, only usage of parameter `DataWidth`. + parameter type payload_t = logic [DataWidth-1:0], + /// Adds a spill register stage at each output. + parameter bit SpillReg = 1'b0, + /// Use external priority for the individual `rr_arb_trees`. + parameter int unsigned ExtPrio = 1'b0, + /// Use strict AXI valid ready handshaking. + /// To be protocol conform also the parameter `LockIn` has to be set. + parameter int unsigned AxiVldRdy = 1'b1, + /// Lock in the arbitration decision of the `rr_arb_tree`. + /// When this is set, valids have to be asserted until the corresponding transaction is indicated + /// by ready. + parameter int unsigned LockIn = 1'b1, + /// Derived parameter, do **not** overwrite! + /// + /// Width of the output selection signal. + parameter int unsigned SelWidth = (NumOut > 32'd1) ? unsigned'($clog2(NumOut)) : 32'd1, + /// Derived parameter, do **not** overwrite! + /// + /// Signal type definition for selecting the output at the inputs. + parameter type sel_oup_t = logic[SelWidth-1:0], + /// Derived parameter, do **not** overwrite! + /// + /// Width of the input index signal. + parameter int unsigned IdxWidth = (NumInp > 32'd1) ? unsigned'($clog2(NumInp)) : 32'd1, + /// Derived parameter, do **not** overwrite! + /// + /// Signal type definition indicating from which input the output came. + parameter type idx_inp_t = logic[IdxWidth-1:0] +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Flush the state of the internal `rr_arb_tree` modules. + /// If not used set to `0`. + /// Flush should only be used if there are no active `valid_i`, otherwise it will + /// not adhere to the AXI handshaking. + input logic flush_i, + /// Provide an external state for the `rr_arb_tree` models. + /// Will only do something if ExtPrio is `1` otherwise tie to `0`. + input idx_inp_t [NumOut-1:0] rr_i, + /// Input data ports. + /// Has to be stable as long as `valid_i` is asserted when parameter `AxiVldRdy` is set. + input payload_t [NumInp-1:0] data_i, + /// Selection of the output port where the data should be routed. + /// Has to be stable as long as `valid_i` is asserted and parameter `AxiVldRdy` is set. + input sel_oup_t [NumInp-1:0] sel_i, + /// Input is valid. + input logic [NumInp-1:0] valid_i, + /// Input is ready to accept data. + output logic [NumInp-1:0] ready_o, + /// Output data ports. Valid if `valid_o = 1` + output payload_t [NumOut-1:0] data_o, + /// Index of the input port where data came from. + output idx_inp_t [NumOut-1:0] idx_o, + /// Output is valid. + output logic [NumOut-1:0] valid_o, + /// Output can be accepted. + input logic [NumOut-1:0] ready_i +); + if (NumInp <= Radix && NumOut <= Radix) begin : gen_degenerate_omega_net + // If both Number of inputs and number of outputs are smaller or the same as the radix + // just instantiate a `stream_xbar`. + stream_xbar #( + .NumInp ( NumInp ), + .NumOut ( NumOut ), + .payload_t ( payload_t ), + .OutSpillReg ( SpillReg ), + .ExtPrio ( ExtPrio ), + .AxiVldRdy ( AxiVldRdy ), + .LockIn ( LockIn ) + ) i_stream_xbar ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ( rr_i ), + .data_i ( data_i ), + .sel_i ( sel_i ), + .valid_i ( valid_i ), + .ready_o ( ready_o ), + .data_o ( data_o ), + .idx_o ( idx_o ), + .valid_o ( valid_o ), + .ready_i ( ready_i ) + ); + end else begin : gen_omega_net + // Find the next power of radix of either the number of inputs or number of outputs. + // This normalizes the network to a power of the radix. Unused inputs and outputs are tied off. + // If the radix is poorly chosen with respect to the number of input/outputs ports + // will lead to an explosion of tied off lanes, which will be removed during optimization. + // Can lead however to RTL simulation overhead. + // Dividing through the log base 2 of `Radix` leads to a change of base. + localparam int unsigned NumLanes = (NumOut > NumInp) ? + unsigned'(Radix**(cf_math_pkg::ceil_div($clog2(NumOut), $clog2(Radix)))) : + unsigned'(Radix**(cf_math_pkg::ceil_div($clog2(NumInp), $clog2(Radix)))); + + // Find the number of routing levels needed. + localparam int unsigned NumLevels = unsigned'(($clog2(NumLanes)+$clog2(Radix)-1)/$clog2(Radix)); + + // Find the number of routes per network stage. Can use a normal division here, as + // `NumLanes % Radix == 0`. + localparam int unsigned NumRouters = NumLanes / Radix; + + // Define the type of sel signal to send through the network. It has to be sliced for the + // individual sel signals of a stage. This slicing has to align with `$clog2(Radix)`. + // For example `Radix = 4`, `NumOut = 17` will lead to the sel signal of an individual stage to + // be 2 bit wide, whereas signal `sel_i` of the module will be 5 bit wide. + // To prevent slicing into an undefined field the overall sel signal is then defined with + // width 6. + typedef logic [$clog2(NumLanes)-1:0] sel_dst_t; + + // Selection signal type of an individual router + localparam int unsigned SelW = unsigned'($clog2(Radix)); + initial begin : proc_selw + $display("SelW is: %0d", SelW); + $display("SelDstW is: %0d", $bits(sel_dst_t)); + end + typedef logic [SelW-1:0] sel_t; + + // Define the payload which should be routed through the network. + typedef struct packed { + sel_dst_t sel_oup; // Selection of output, where it should be routed + payload_t payload; // External payload data + idx_inp_t idx_inp; // Index of the input of this packet + } omega_data_t; + + // signal definitions + omega_data_t [NumLevels-1:0][NumRouters-1:0][Radix-1:0] inp_router_data; + logic [NumLevels-1:0][NumRouters-1:0][Radix-1:0] inp_router_valid, inp_router_ready; + omega_data_t [NumLevels-1:0][NumRouters-1:0][Radix-1:0] out_router_data; + logic [NumLevels-1:0][NumRouters-1:0][Radix-1:0] out_router_valid, out_router_ready; + + // Generate the shuffling between the routers + for (genvar i = 0; unsigned'(i) < NumLevels-1; i++) begin : gen_shuffle_levels + for (genvar j = 0; unsigned'(j) < NumRouters; j++) begin : gen_shuffle_routers + for (genvar k = 0; unsigned'(k) < Radix; k++) begin : gen_shuffle_radix + // This parameter is from `0` to `NumLanes-1` + localparam int unsigned IdxLane = Radix * j + k; + // Do the perfect shuffle + assign inp_router_data[i+1][IdxLane%NumRouters][IdxLane/NumRouters] = + out_router_data[i][j][k]; + + assign inp_router_valid[i+1][IdxLane%NumRouters][IdxLane/NumRouters] = + out_router_valid[i][j][k]; + + assign out_router_ready[i][j][k] = + inp_router_ready[i+1][IdxLane%NumRouters][IdxLane/NumRouters]; + + // Do the first input shuffle of layer 0. + // The inputs are connected in reverse. The reason is that then the optimization + // leaves then the biggest possible network diameter. + if (i == 0) begin : gen_shuffle_inp + // Reverse the order of the input ports + if ((NumLanes-IdxLane) <= NumInp) begin : gen_inp_ports + localparam int unsigned IdxInp = NumLanes - IdxLane - 32'd1; + assign inp_router_data[0][IdxLane%NumRouters][IdxLane/NumRouters] = '{ + sel_oup: sel_dst_t'(sel_i[IdxInp]), + payload: data_i[IdxInp], + idx_inp: idx_inp_t'(IdxInp) + }; + + assign inp_router_valid[0][IdxLane%NumRouters][IdxLane/NumRouters] = valid_i[IdxInp]; + assign ready_o[IdxInp] = inp_router_ready[0][IdxLane%NumRouters][IdxLane/NumRouters]; + + end else begin : gen_tie_off + assign inp_router_data[0][IdxLane%NumRouters][IdxLane/NumRouters] = '{ default: '0}; + assign inp_router_valid[0][IdxLane%NumRouters][IdxLane/NumRouters] = 1'b0; + end + end + end + end + end + + // Generate the `stream_xbar_routers` + for (genvar i = 0; unsigned'(i) < NumLevels; i++) begin : gen_router_levels + for (genvar j = 0; unsigned'(j) < NumRouters; j++) begin : gen_routers + sel_t [Radix-1:0] sel_router; + for (genvar k = 0; unsigned'(k) < Radix; k++) begin : gen_router_sel + // For the inter stage routing some bits of the overall selection are important. + // The `MSB` is for stage `0`, `MSB-1` for stage `1` and so on for the `Radix=2` case. + // For higher radices's a bit slice following the same pattern is used. + // This is the reason that the internal network is expanded to a power of two, so that + // the selection slicing always has a valid index. + assign sel_router[k] = inp_router_data[i][j][k].sel_oup[SelW*(NumLevels-i-1)+:SelW]; + end + + stream_xbar #( + .NumInp ( Radix ), + .NumOut ( Radix ), + .payload_t ( omega_data_t ), + .OutSpillReg ( SpillReg ), + .ExtPrio ( 1'b0 ), + .AxiVldRdy ( AxiVldRdy ), + .LockIn ( LockIn ) + ) i_stream_xbar ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ( '0 ), + .data_i ( inp_router_data[i][j] ), + .sel_i ( sel_router ), + .valid_i ( inp_router_valid[i][j] ), + .ready_o ( inp_router_ready[i][j] ), + .data_o ( out_router_data[i][j] ), + .idx_o ( /* not used */ ), + .valid_o ( out_router_valid[i][j] ), + .ready_i ( out_router_ready[i][j] ) + ); + end + end + + // outputs are on the last level + for (genvar i = 0; unsigned'(i) < NumLanes; i++) begin : gen_outputs + if (i < NumOut) begin : gen_connect + assign data_o[i] = out_router_data[NumLevels-1][i/Radix][i%Radix].payload; + assign idx_o[i] = out_router_data[NumLevels-1][i/Radix][i%Radix].idx_inp; + assign valid_o[i] = out_router_valid[NumLevels-1][i/Radix][i%Radix]; + assign out_router_ready[NumLevels-1][i/Radix][i%Radix] = ready_i[i]; + end else begin : gen_tie_off + assign out_router_ready[NumLevels-1][i/Radix][i%Radix] = 1'b0; + end + end + + initial begin : proc_debug_print + $display("NumInp: %0d", NumInp); + $display("NumOut: %0d", NumOut); + $display("Radix: %0d", Radix); + $display("NumLanes: %0d", NumLanes); + $display("NumLevels: %0d", NumLevels); + $display("NumRouters: %0d", NumRouters); + end + + // Assertions + // Make sure that the handshake and payload is stable + // pragma translate_off + `ifndef VERILATOR + default disable iff rst_ni; + for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_sel_assertions + assert property (@(posedge clk_i) (valid_i[i] |-> sel_i[i] < sel_oup_t'(NumOut))) else + $fatal(1, "Non-existing output is selected!"); + end + + if (AxiVldRdy) begin : gen_handshake_assertions + for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_inp_assertions + assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> $stable(data_i[i]))) else + $error("data_i is unstable at input: %0d", i); + assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> $stable(sel_i[i]))) else + $error("sel_i is unstable at input: %0d", i); + assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> valid_i[i])) else + $error("valid_i at input %0d has been taken away without a ready.", i); + end + for (genvar i = 0; unsigned'(i) < NumOut; i++) begin : gen_out_assertions + assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> $stable(data_o[i]))) else + $error("data_o is unstable at output: %0d Check that parameter LockIn is set.", i); + assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> $stable(idx_o[i]))) else + $error("idx_o is unstable at output: %0d Check that parameter LockIn is set.", i); + assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> valid_o[i])) else + $error("valid_o at output %0d has been taken away without a ready.", i); + end + end + + initial begin : proc_parameter_assertions + assert ((2**$clog2(Radix) == Radix) && (Radix > 32'd1)) else + $fatal(1, "Radix %0d is not power of two.", Radix); + assert (2**$clog2(NumRouters) == NumRouters) else + $fatal(1, "NumRouters %0d is not power of two.", NumRouters); + assert ($clog2(NumLanes) % SelW == 0) else + $fatal(1, "Bit slicing of the internal selection signal is broken."); + end + `endif + // pragma translate_on + end +endmodule diff --git a/vendor/common_cells/src/stream_register.sv b/vendor/common_cells/src/stream_register.sv new file mode 100644 index 00000000..f529d6a2 --- /dev/null +++ b/vendor/common_cells/src/stream_register.sv @@ -0,0 +1,57 @@ +// 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. + +/// Register with a simple stream-like ready/valid handshake. +/// This register does not cut combinatorial paths on all control signals; if you need a complete +/// cut, use the `spill_register`. +module stream_register #( + parameter type T = logic // Vivado requires a default value for type parameters. +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous active-low reset + input logic clr_i, // Synchronous clear + input logic testmode_i, // Test mode to bypass clock gating + // Input port + input logic valid_i, + output logic ready_o, + input T data_i, + // Output port + output logic valid_o, + input logic ready_i, + output T data_o +); + + logic fifo_empty, + fifo_full; + + fifo_v2 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH ($bits(T)), + .DEPTH (1), + .dtype (T) + ) i_fifo ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (clr_i), + .testmode_i (testmode_i), + .full_o (fifo_full), + .empty_o (fifo_empty), + .alm_full_o ( ), + .alm_empty_o ( ), + .data_i (data_i), + .push_i (valid_i & ~fifo_full), + .data_o (data_o), + .pop_i (ready_i & ~fifo_empty) + ); + + assign ready_o = ~fifo_full; + assign valid_o = ~fifo_empty; + +endmodule diff --git a/vendor/common_cells/src/stream_to_mem.sv b/vendor/common_cells/src/stream_to_mem.sv new file mode 100644 index 00000000..00c30863 --- /dev/null +++ b/vendor/common_cells/src/stream_to_mem.sv @@ -0,0 +1,134 @@ +// 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 + +/// `stream_to_mem`: Allows to use memories with flow control (`valid`/`ready`) for requests but without flow +/// control for output data to be used in streams. +`include "common_cells/registers.svh" +module stream_to_mem #( + /// Memory request payload type, usually write enable, write data, etc. + parameter type mem_req_t = logic, + /// Memory response payload type, usually read data + parameter type mem_resp_t = logic, + /// Number of buffered responses (fall-through, thus no additional latency). This defines the + /// maximum number of outstanding requests on the memory interface. If the attached memory + /// responds in the same cycle a request is applied, this MUST be 0. If the attached memory + /// responds at least one cycle after a request, this MUST be >= 1 and should be equal to the + /// response latency of the memory to saturate bandwidth. + parameter int unsigned BufDepth = 32'd1 +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset, active low + input logic rst_ni, + /// Request stream interface, payload + input mem_req_t req_i, + /// Request stream interface, payload is valid for transfer + input logic req_valid_i, + /// Request stream interface, payload can be accepted + output logic req_ready_o, + /// Response stream interface, payload + output mem_resp_t resp_o, + /// Response stream interface, payload is valid for transfer + output logic resp_valid_o, + /// Response stream interface, payload can be accepted + input logic resp_ready_i, + /// Memory request interface, payload + output mem_req_t mem_req_o, + /// Memory request interface, payload is valid for transfer + output logic mem_req_valid_o, + /// Memory request interface, payload can be accepted + input logic mem_req_ready_i, + /// Memory response interface, payload + input mem_resp_t mem_resp_i, + /// Memory response interface, payload is valid + input logic mem_resp_valid_i +); + + typedef logic [$clog2(BufDepth+1):0] cnt_t; + + cnt_t cnt_d, cnt_q; + logic buf_ready, + req_ready; + + if (BufDepth > 0) begin : gen_buf + // Count number of outstanding requests. + always_comb begin + cnt_d = cnt_q; + if (req_valid_i && req_ready_o) begin + cnt_d++; + end + if (resp_valid_o && resp_ready_i) begin + cnt_d--; + end + end + + // Can issue another request if the counter is not at its limit or a response is delivered in + // the current cycle. + assign req_ready = (cnt_q < BufDepth) | (resp_valid_o & resp_ready_i); + + // Control request and memory request interface handshakes. + assign req_ready_o = mem_req_ready_i & req_ready; + assign mem_req_valid_o = req_valid_i & req_ready; + + // Buffer responses. + stream_fifo #( + .FALL_THROUGH ( 1'b1 ), + .DEPTH ( BufDepth ), + .T ( mem_resp_t ) + ) i_resp_buf ( + .clk_i, + .rst_ni, + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + .data_i ( mem_resp_i ), + .valid_i ( mem_resp_valid_i ), + .ready_o ( buf_ready ), + .data_o ( resp_o ), + .valid_o ( resp_valid_o ), + .ready_i ( resp_ready_i ), + .usage_o ( /* unused */ ) + ); + + // Register + `FFARN(cnt_q, cnt_d, '0, clk_i, rst_ni) + + end else begin : gen_no_buf + // Control request, memory request, and response interface handshakes. + assign mem_req_valid_o = req_valid_i; + assign resp_valid_o = mem_req_valid_o & mem_req_ready_i & mem_resp_valid_i; + assign req_ready_o = resp_ready_i & resp_valid_o; + + // Forward responses. + assign resp_o = mem_resp_i; + end + + // Forward requests. + assign mem_req_o = req_i; + +// Assertions +// pragma translate_off +`ifndef VERILATOR + if (BufDepth > 0) begin : gen_buf_asserts + assert property (@(posedge clk_i) mem_resp_valid_i |-> buf_ready) + else $error("Memory response lost!"); + assert property (@(posedge clk_i) cnt_q == '0 |=> cnt_q != '1) + else $error("Counter underflowed!"); + assert property (@(posedge clk_i) cnt_q == BufDepth |=> cnt_q != BufDepth + 1) + else $error("Counter overflowed!"); + end else begin : gen_no_buf_asserts + assume property (@(posedge clk_i) mem_req_valid_o & mem_req_ready_i |-> mem_resp_valid_i) + else $error("Without BufDepth = 0, the memory must respond in the same cycle!"); + end +`endif +// pragma translate_on +endmodule diff --git a/vendor/common_cells/src/stream_xbar.sv b/vendor/common_cells/src/stream_xbar.sv new file mode 100644 index 00000000..95740068 --- /dev/null +++ b/vendor/common_cells/src/stream_xbar.sv @@ -0,0 +1,198 @@ +// Copyright (c) 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. + +// Author: Wolfgang Roenninger + +/// Fully connected stream crossbar. +/// +/// Handshaking rules as defined by the `AMBA AXI` standard on default. +module stream_xbar #( + /// Number of inputs into the crossbar (`> 0`). + parameter int unsigned NumInp = 32'd0, + /// Number of outputs from the crossbar (`> 0`). + parameter int unsigned NumOut = 32'd0, + /// Data width of the stream. Can be overwritten by defining the type parameter `payload_t`. + parameter int unsigned DataWidth = 32'd1, + /// Payload type of the data ports, only usage of parameter `DataWidth`. + parameter type payload_t = logic [DataWidth-1:0], + /// Adds a spill register stage at each output. + parameter bit OutSpillReg = 1'b0, + /// Use external priority for the individual `rr_arb_trees`. + parameter int unsigned ExtPrio = 1'b0, + /// Use strict AXI valid ready handshaking. + /// To be protocol conform also the parameter `LockIn` has to be set. + parameter int unsigned AxiVldRdy = 1'b1, + /// Lock in the arbitration decision of the `rr_arb_tree`. + /// When this is set, valids have to be asserted until the corresponding transaction is indicated + /// by ready. + parameter int unsigned LockIn = 1'b1, + /// Derived parameter, do **not** overwrite! + /// + /// Width of the output selection signal. + parameter int unsigned SelWidth = (NumOut > 32'd1) ? unsigned'($clog2(NumOut)) : 32'd1, + /// Derived parameter, do **not** overwrite! + /// + /// Signal type definition for selecting the output at the inputs. + parameter type sel_oup_t = logic[SelWidth-1:0], + /// Derived parameter, do **not** overwrite! + /// + /// Width of the input index signal. + parameter int unsigned IdxWidth = (NumInp > 32'd1) ? unsigned'($clog2(NumInp)) : 32'd1, + /// Derived parameter, do **not** overwrite! + /// + /// Signal type definition indicating from which input the output came. + parameter type idx_inp_t = logic[IdxWidth-1:0] +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Flush the state of the internal `rr_arb_tree` modules. + /// If not used set to `0`. + /// Flush should only be used if there are no active `valid_i`, otherwise it will + /// not adhere to the AXI handshaking. + input logic flush_i, + /// Provide an external state for the `rr_arb_tree` models. + /// Will only do something if ExtPrio is `1` otherwise tie to `0`. + input idx_inp_t [NumOut-1:0] rr_i, + /// Input data ports. + /// Has to be stable as long as `valid_i` is asserted when parameter `AxiVldRdy` is set. + input payload_t [NumInp-1:0] data_i, + /// Selection of the output port where the data should be routed. + /// Has to be stable as long as `valid_i` is asserted and parameter `AxiVldRdy` is set. + input sel_oup_t [NumInp-1:0] sel_i, + /// Input is valid. + input logic [NumInp-1:0] valid_i, + /// Input is ready to accept data. + output logic [NumInp-1:0] ready_o, + /// Output data ports. Valid if `valid_o = 1` + output payload_t [NumOut-1:0] data_o, + /// Index of the input port where data came from. + output idx_inp_t [NumOut-1:0] idx_o, + /// Output is valid. + output logic [NumOut-1:0] valid_o, + /// Output can be accepted. + input logic [NumOut-1:0] ready_i +); + typedef struct packed { + payload_t data; + idx_inp_t idx; + } spill_data_t; + + logic [NumInp-1:0][NumOut-1:0] inp_valid; + logic [NumInp-1:0][NumOut-1:0] inp_ready; + + payload_t [NumOut-1:0][NumInp-1:0] out_data; + logic [NumOut-1:0][NumInp-1:0] out_valid; + logic [NumOut-1:0][NumInp-1:0] out_ready; + + // Generate the input selection + for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_inps + stream_demux #( + .N_OUP ( NumOut ) + ) i_stream_demux ( + .inp_valid_i ( valid_i[i] ), + .inp_ready_o ( ready_o[i] ), + .oup_sel_i ( sel_i[i] ), + .oup_valid_o ( inp_valid[i] ), + .oup_ready_i ( inp_ready[i] ) + ); + + // Do the switching cross of the signals. + for (genvar j = 0; unsigned'(j) < NumOut; j++) begin : gen_cross + // Propagate the data from this input to all outputs. + assign out_data[j][i] = data_i[i]; + // switch handshaking + assign out_valid[j][i] = inp_valid[i][j]; + assign inp_ready[i][j] = out_ready[j][i]; + end + end + + // Generate the output arbitration. + for (genvar j = 0; unsigned'(j) < NumOut; j++) begin : gen_outs + spill_data_t arb; + logic arb_valid, arb_ready; + + rr_arb_tree #( + .NumIn ( NumInp ), + .DataType ( payload_t ), + .ExtPrio ( ExtPrio ), + .AxiVldRdy ( AxiVldRdy ), + .LockIn ( LockIn ) + ) i_rr_arb_tree ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ( rr_i[j] ), + .req_i ( out_valid[j] ), + .gnt_o ( out_ready[j] ), + .data_i ( out_data[j] ), + .req_o ( arb_valid ), + .gnt_i ( arb_ready ), + .data_o ( arb.data ), + .idx_o ( arb.idx ) + ); + + spill_data_t spill; + + spill_register #( + .T ( spill_data_t ), + .Bypass ( !OutSpillReg ) + ) i_spill_register ( + .clk_i, + .rst_ni, + .valid_i ( arb_valid ), + .ready_o ( arb_ready ), + .data_i ( arb ), + .valid_o ( valid_o[j] ), + .ready_i ( ready_i[j] ), + .data_o ( spill ) + ); + // Assign the outputs (deaggregate the data). + assign data_o[j] = spill.data; + assign idx_o[j] = spill.idx; + end + + // Assertions + // Make sure that the handshake and payload is stable + // pragma translate_off + `ifndef VERILATOR + default disable iff rst_ni; + for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_sel_assertions + assert property (@(posedge clk_i) (valid_i[i] |-> sel_i[i] < sel_oup_t'(NumOut))) else + $fatal(1, "Non-existing output is selected!"); + end + + if (AxiVldRdy) begin : gen_handshake_assertions + for (genvar i = 0; unsigned'(i) < NumInp; i++) begin : gen_inp_assertions + assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> $stable(data_i[i]))) else + $error("data_i is unstable at input: %0d", i); + assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> $stable(sel_i[i]))) else + $error("sel_i is unstable at input: %0d", i); + assert property (@(posedge clk_i) (valid_i[i] && !ready_o[i] |=> valid_i[i])) else + $error("valid_i at input %0d has been taken away without a ready.", i); + end + for (genvar i = 0; unsigned'(i) < NumOut; i++) begin : gen_out_assertions + assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> $stable(data_o[i]))) else + $error("data_o is unstable at output: %0d Check that parameter LockIn is set.", i); + assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> $stable(idx_o[i]))) else + $error("idx_o is unstable at output: %0d Check that parameter LockIn is set.", i); + assert property (@(posedge clk_i) (valid_o[i] && !ready_i[i] |=> valid_o[i])) else + $error("valid_o at output %0d has been taken away without a ready.", i); + end + end + + initial begin : proc_parameter_assertions + assert (NumInp > 32'd0) else $fatal(1, "NumInp has to be > 0!"); + assert (NumOut > 32'd0) else $fatal(1, "NumOut has to be > 0!"); + end + `endif + // pragma translate_on +endmodule diff --git a/vendor/common_cells/src/sub_per_hash.sv b/vendor/common_cells/src/sub_per_hash.sv new file mode 100644 index 00000000..d4938ad4 --- /dev/null +++ b/vendor/common_cells/src/sub_per_hash.sv @@ -0,0 +1,173 @@ +// Copyright (c) 2019 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. + +// Author: Wolfgang Roenninger + +// This module implements a fully parameterizable substitution-permutation hash +// function. The hash is structured in stages consisting of a shuffle of the input bits +// and then xoring for each bit 3 pseudo-random bits of the shuffeled vector. +// The hash function is NOT cryptographically secure! +// From the keys it computes a sequence of pseudo-random numbers, which determine the permutations +// and substitutions. As pseudo random generator a multiplicative linear congruential +// generator is used and uses different constants for the computation of the permutation +// and substitution respectively. +// The permutation shuffles the bits using a variant of the Fisher-Yates shuffle algorithm. +// The substitution per stage is the xor of 3 pseudo random bits of the previous stage. +// As shifting and xoring of a signal do not change its distribution, the distribution +// of the output hash is the same as the one of the input data. +// +// Parameters: +// - `InpWidth`: The input width of the vector `data_i`. +// - `HashWidth`: The output width of the substitution-permutation hash. +// - `NoRounds`: The amount of permutation, substitution stages generated. Translates +// into how many levels of xor's there will be before optimization. +// - `PermuteKey`: The Key for the pseudo-random generator used for determining the exact +// permutation (shuffled wiring between each xor stage) at compile/elaboration. +// Any `int unsigned` value can be used as key, however one should examine the +// output of the hash function. +// - `XorKey`: The Key for the pseudo-random generator used for determining the xor +// of bits between stages. The same principles as for `PermuteKey` applies, +// however one should look that both keys have a greatest common divisor of 1. + +module sub_per_hash #( + parameter int unsigned InpWidth = 32'd11, + parameter int unsigned HashWidth = 32'd5, + parameter int unsigned NoRounds = 32'd1, + parameter int unsigned PermuteKey = 32'd299034753, + parameter int unsigned XorKey = 32'd4094834 +) ( + // is purely combinational + input logic [InpWidth-1:0] data_i, + output logic [HashWidth-1:0] hash_o, + output logic [2**HashWidth-1:0] hash_onehot_o +); + + // typedefs and respective localparams + typedef int unsigned perm_lists_t [NoRounds][InpWidth]; + localparam perm_lists_t PERMUTATIONS = get_permutations(PermuteKey); + // encoding for inner most array: + // position 0 indicates the number of inputs, 2 or 3 + // the other positions 1 - 3 indicate the inputs + typedef int unsigned xor_stages_t [NoRounds][InpWidth][3]; + localparam xor_stages_t XorStages = get_xor_stages(XorKey); + + // stage signals + logic [NoRounds-1:0][InpWidth-1:0] permuted, xored; + + // for each round + for (genvar r = 0; r < NoRounds; r++) begin : gen_round + // for each bit + for (genvar i = 0; i < InpWidth ; i++) begin : gen_sub_per + + // assign the permutation + if (r == 0) begin : gen_input + assign permuted[r][i] = data_i[PERMUTATIONS[r][i]]; + end else begin : gen_permutation + assign permuted[r][i] = permuted[r-1][PERMUTATIONS[r][i]]; + end + + // assign the xor substitution + assign xored[r][i] = permuted[r][XorStages[r][i][0]] ^ + permuted[r][XorStages[r][i][1]] ^ + permuted[r][XorStages[r][i][2]]; + end + end + + // output assignment, take the bottom bits of the last round + assign hash_o = xored[NoRounds-1][HashWidth-1:0]; + // for onehot run trough a decoder + assign hash_onehot_o = 1 << hash_o; + + // PRG is MLCG (multiplicative linear congruential generator) + // Constant values the same as RtlUniform from Native API + // X(n+1) = (a*X(n)+c) mod m + // a: large prime + // c: increment + // m: range + // Shuffling is a variation of the Fisher-Yates shuffle algorithm + function automatic perm_lists_t get_permutations(input int unsigned seed); + perm_lists_t indices; + perm_lists_t perm_array; + longint unsigned A = 2147483629; + longint unsigned C = 2147483587; + longint unsigned M = 2**31 - 1; + longint unsigned index = 0; + longint unsigned advance = 0; + longint unsigned rand_number = (A * seed + C) % M; + + // do it for each round + for (int unsigned r = 0; r < NoRounds; r++) begin + // initialize the index array + for (int unsigned i = 0; i < InpWidth; i++) begin + indices[r][i] = i; + end + // do the shuffling + for (int unsigned i = 0; i < InpWidth; i++) begin + // get the 'random' number + if (i > 0) begin + rand_number = (A * rand_number + C) % M; + index = rand_number % i; + end + // do the shuffling + if (i != index) begin + perm_array[r][i] = perm_array[r][index]; + perm_array[r][index] = indices[r][i]; + end + end + // advance the PRG a bit + rand_number = (A * rand_number + C) % M; + advance = rand_number % NoRounds; + for (int unsigned i = 0; i < advance; i++) begin + rand_number = (A * rand_number + C) % M; + end + end + return perm_array; + endfunction : get_permutations + + // PRG is MLCG (multiplicative linear congruential generator) + // Constant values the same as Numerical Recipes + // X(n+1) = (a*X(n)+c) mod m + // a: large prime + // c: increment + // m: range + function automatic xor_stages_t get_xor_stages(input int unsigned seed); + xor_stages_t xor_array; + longint unsigned A = 1664525; + longint unsigned C = 1013904223; + longint unsigned M = 2**32; + longint unsigned index = 0; + // int unsigned even = 0; + longint unsigned advance = 0; + longint unsigned rand_number = (A * seed + C) % M; + + // fill the array with 'randon' inputs + // for each xor, a even random number is two input, uneven is tree + // for each round + for (int unsigned r = 0; r < NoRounds; r++) begin + // for each bit + for (int unsigned i = 0; i < InpWidth; i++) begin + rand_number = (A * rand_number + C) % M; + // even = rand_number[3]; + for (int unsigned j = 0; j < 3; j++) begin + rand_number = (A * rand_number + C) % M; + index = rand_number % InpWidth; + xor_array[r][i][j] = index; + end + end + // advance the PRG a bit + rand_number = (A * rand_number + C) % M; + advance = rand_number % NoRounds; + for (int unsigned i = 0; i < advance; i++) begin + rand_number = (A * rand_number + C) % M; + end + end + return xor_array; + endfunction : get_xor_stages +endmodule diff --git a/vendor/common_cells/src/sync.sv b/vendor/common_cells/src/sync.sv new file mode 100644 index 00000000..ff2ef5b5 --- /dev/null +++ b/vendor/common_cells/src/sync.sv @@ -0,0 +1,34 @@ +// 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. + +// Antonio Pullini + +module sync #( + parameter int unsigned STAGES = 2 +) ( + input logic clk_i, + input logic rst_ni, + input logic serial_i, + output logic serial_o +); + + logic [STAGES-1:0] reg_q; + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + reg_q <= 'h0; + end else begin + reg_q <= {reg_q[STAGES-2:0], serial_i}; + end + end + + assign serial_o = reg_q[STAGES-1]; + +endmodule diff --git a/vendor/common_cells/src/sync_wedge.sv b/vendor/common_cells/src/sync_wedge.sv new file mode 100644 index 00000000..58f12798 --- /dev/null +++ b/vendor/common_cells/src/sync_wedge.sv @@ -0,0 +1,56 @@ +// 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. + +// Antonio Pullini + +module sync_wedge #( + parameter int unsigned STAGES = 2 +) ( + input logic clk_i, + input logic rst_ni, + input logic en_i, + input logic serial_i, + output logic r_edge_o, + output logic f_edge_o, + output logic serial_o +); + logic clk; + logic serial, serial_q; + + assign serial_o = serial_q; + assign f_edge_o = (~serial) & serial_q; + assign r_edge_o = serial & (~serial_q); + + sync #( + .STAGES (STAGES) + ) i_sync ( + .clk_i, + .rst_ni, + .serial_i, + .serial_o ( serial ) + ); + + pulp_clock_gating i_pulp_clock_gating ( + .clk_i, + .en_i, + .test_en_i ( 1'b0 ), + .clk_o ( clk ) + ); + + always_ff @(posedge clk, negedge rst_ni) begin + if (!rst_ni) begin + serial_q <= 1'b0; + end else begin + if (en_i) begin + serial_q <= serial; + end + end + end +endmodule diff --git a/vendor/common_cells/src/unread.sv b/vendor/common_cells/src/unread.sv new file mode 100644 index 00000000..80e73562 --- /dev/null +++ b/vendor/common_cells/src/unread.sv @@ -0,0 +1,21 @@ +// 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. +// +// Author: Florian Zaruba, ETH Zurich +// Date: 29.10.2018 +// Description: Dummy circuit to mitigate Open Pin warnings + +/* verilator lint_off UNUSED */ +module unread ( + input logic d_i +); + +endmodule +/* verilator lint_on UNUSED */