diff --git a/Bender.yml b/Bender.yml
index f30fcff2d..460321999 100644
--- a/Bender.yml
+++ b/Bender.yml
@@ -32,8 +32,11 @@ sources:
   # levels 1 and 0, etc. Files within a level are ordered alphabetically.
   # Level 0
   - src/axi_pkg.sv
+  - src/ace/ace_pkg.sv
   # Level 1
   - src/axi_intf.sv
+  - src/ace/ace_intf.sv
+  - src/ace/snoop_intf.sv
   # Level 2
   - src/axi_atop_filter.sv
   - src/axi_burst_splitter.sv
diff --git a/include/ace/assign.svh b/include/ace/assign.svh
new file mode 100644
index 000000000..d37943c40
--- /dev/null
+++ b/include/ace/assign.svh
@@ -0,0 +1,510 @@
+// Copyright (c) 2014-2018 ETH Zurich, University of Bologna
+// Copyright (c) 2022 PlanV GmbH
+//
+// 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.
+//
+
+// Macros to assign ACE Interfaces and Structs
+
+`ifndef ACE_ASSIGN_SVH_
+`define ACE_ASSIGN_SVH_
+
+`include "axi/assign.svh"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Internal implementation for assigning one ACE struct or interface to another struct or interface.
+// The path to the signals on each side is defined by the `__sep*` arguments.  The `__opt_as`
+// argument allows to use this standalone (with `__opt_as = assign`) or in assignments inside
+// processes (with `__opt_as` void).
+`define __ACE_TO_AW(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)   \
+  __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;         \
+  __opt_as __lhs``__lhs_sep``addr   = __rhs``__rhs_sep``addr;       \
+  __opt_as __lhs``__lhs_sep``len    = __rhs``__rhs_sep``len;        \
+  __opt_as __lhs``__lhs_sep``size   = __rhs``__rhs_sep``size;       \
+  __opt_as __lhs``__lhs_sep``burst  = __rhs``__rhs_sep``burst;      \
+  __opt_as __lhs``__lhs_sep``lock   = __rhs``__rhs_sep``lock;       \
+  __opt_as __lhs``__lhs_sep``cache  = __rhs``__rhs_sep``cache;      \
+  __opt_as __lhs``__lhs_sep``prot   = __rhs``__rhs_sep``prot;       \
+  __opt_as __lhs``__lhs_sep``qos    = __rhs``__rhs_sep``qos;        \
+  __opt_as __lhs``__lhs_sep``region = __rhs``__rhs_sep``region;     \
+  __opt_as __lhs``__lhs_sep``atop   = __rhs``__rhs_sep``atop;       \
+  __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;       \
+  __opt_as __lhs``__lhs_sep``snoop   = __rhs``__rhs_sep``snoop; \
+  __opt_as __lhs``__lhs_sep``bar   = __rhs``__rhs_sep``bar;         \
+  __opt_as __lhs``__lhs_sep``domain   = __rhs``__rhs_sep``domain;   \
+  __opt_as __lhs``__lhs_sep``awunique   = __rhs``__rhs_sep``awunique;
+
+
+`define __ACE_TO_AR(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)   \
+  __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;         \
+  __opt_as __lhs``__lhs_sep``addr   = __rhs``__rhs_sep``addr;       \
+  __opt_as __lhs``__lhs_sep``len    = __rhs``__rhs_sep``len;        \
+  __opt_as __lhs``__lhs_sep``size   = __rhs``__rhs_sep``size;       \
+  __opt_as __lhs``__lhs_sep``burst  = __rhs``__rhs_sep``burst;      \
+  __opt_as __lhs``__lhs_sep``lock   = __rhs``__rhs_sep``lock;       \
+  __opt_as __lhs``__lhs_sep``cache  = __rhs``__rhs_sep``cache;      \
+  __opt_as __lhs``__lhs_sep``prot   = __rhs``__rhs_sep``prot;       \
+  __opt_as __lhs``__lhs_sep``qos    = __rhs``__rhs_sep``qos;        \
+  __opt_as __lhs``__lhs_sep``region = __rhs``__rhs_sep``region;     \
+  __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;       \
+  __opt_as __lhs``__lhs_sep``snoop = __rhs``__rhs_sep``snoop;   \
+  __opt_as __lhs``__lhs_sep``bar = __rhs``__rhs_sep``bar;           \
+  __opt_as __lhs``__lhs_sep``domain = __rhs``__rhs_sep``domain;
+`define __ACE_TO_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)    \
+  __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;         \
+  __opt_as __lhs``__lhs_sep``data   = __rhs``__rhs_sep``data;       \
+  __opt_as __lhs``__lhs_sep``resp   = __rhs``__rhs_sep``resp;       \
+  __opt_as __lhs``__lhs_sep``last   = __rhs``__rhs_sep``last;       \
+  __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;
+`define __ACE_TO_REQ(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)  \
+  `__ACE_TO_AW(__opt_as, __lhs.aw, __lhs_sep, __rhs.aw, __rhs_sep)  \
+  __opt_as __lhs.aw_valid = __rhs.aw_valid;                         \
+  `__AXI_TO_W(__opt_as, __lhs.w, __lhs_sep, __rhs.w, __rhs_sep)     \
+  __opt_as __lhs.w_valid = __rhs.w_valid;                           \
+  __opt_as __lhs.b_ready = __rhs.b_ready;                           \
+  `__ACE_TO_AR(__opt_as, __lhs.ar, __lhs_sep, __rhs.ar, __rhs_sep)  \
+  __opt_as __lhs.ar_valid = __rhs.ar_valid;                         \
+  __opt_as __lhs.r_ready = __rhs.r_ready;                           \
+  __opt_as __lhs.wack = __rhs.wack;                                 \
+  __opt_as __lhs.rack = __rhs.rack;
+`define __ACE_TO_RESP(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
+  __opt_as __lhs.aw_ready = __rhs.aw_ready;                         \
+  __opt_as __lhs.ar_ready = __rhs.ar_ready;                         \
+  __opt_as __lhs.w_ready = __rhs.w_ready;                           \
+  __opt_as __lhs.b_valid = __rhs.b_valid;                           \
+  `__AXI_TO_B(__opt_as, __lhs.b, __lhs_sep, __rhs.b, __rhs_sep)     \
+  __opt_as __lhs.r_valid = __rhs.r_valid;                           \
+  `__ACE_TO_R(__opt_as, __lhs.r, __lhs_sep, __rhs.r, __rhs_sep)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning one AXI4+ATOP interface to another, as if you would do `assign slv = mst;`
+//
+// The channel assignments `ACE_ASSIGN_XX(dst, src)` assign all payload and the valid signal of the
+// `XX` channel from the `src` to the `dst` interface and they assign the ready signal from the
+// `src` to the `dst` interface.
+// The interface assignment `AXI_ASSIGN(dst, src)` assigns all channels including handshakes as if
+// `src` was the master of `dst`.
+//
+// Usage Example:
+// `ACE_ASSIGN(slv, mst)
+// `ACE_ASSIGN_AW(dst, src)
+// `ACE_ASSIGN_R(dst, src)
+`define ACE_ASSIGN_AW(dst, src)               \
+  `__ACE_TO_AW(assign, dst.aw, _, src.aw, _)  \
+  assign dst.aw_valid = src.aw_valid;         \
+  assign src.aw_ready = dst.aw_ready;
+
+`define ACE_ASSIGN_AR(dst, src)               \
+  `__ACE_TO_AR(assign, dst.ar, _, src.ar, _)  \
+  assign dst.ar_valid = src.ar_valid;         \
+  assign src.ar_ready = dst.ar_ready;
+`define ACE_ASSIGN_R(dst, src)                \
+  `__ACE_TO_R(assign, dst.r, _, src.r, _)     \
+  assign dst.r_valid  = src.r_valid;          \
+  assign src.r_ready  = dst.r_ready;
+`define ACE_ASSIGN(slv, mst)  \
+  `ACE_ASSIGN_AW(slv, mst)    \
+  `AXI_ASSIGN_W(slv, mst)     \
+  `AXI_ASSIGN_B(mst, slv)     \
+  `ACE_ASSIGN_AR(slv, mst)    \
+  `ACE_ASSIGN_R(mst, slv)     \
+  assign slv.wack = mst.wack; \
+  assign slv.rack = mst.rack;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning a AXI4+ATOP interface to a monitor modport, as if you would do `assign mon = axi_if;`
+//
+// The channel assignment `ACE_ASSIGN_MONITOR(mon_dv, axi_if)` assigns all signals from `axi_if`
+// to the `mon_dv` interface.
+//
+// Usage Example:
+// `ACE_ASSIGN_MONITOR(mon_dv, axi_if)
+`define ACE_ASSIGN_MONITOR(mon_dv, axi_if)          \
+  `__ACE_TO_AW(assign, mon_dv.aw, _, axi_if.aw, _)  \
+  assign mon_dv.aw_valid  = axi_if.aw_valid;        \
+  assign mon_dv.aw_ready  = axi_if.aw_ready;        \
+  `__AXI_TO_W(assign, mon_dv.w, _, axi_if.w, _)     \
+  assign mon_dv.w_valid   = axi_if.w_valid;         \
+  assign mon_dv.w_ready   = axi_if.w_ready;         \
+  `__AXI_TO_B(assign, mon_dv.b, _, axi_if.b, _)     \
+  assign mon_dv.b_valid   = axi_if.b_valid;         \
+  assign mon_dv.b_ready   = axi_if.b_ready;         \
+  `__ACE_TO_AR(assign, mon_dv.ar, _, axi_if.ar, _)  \
+  assign mon_dv.ar_valid  = axi_if.ar_valid;        \
+  assign mon_dv.ar_ready  = axi_if.ar_ready;        \
+  `__ACE_TO_R(assign, mon_dv.r, _, axi_if.r, _)     \
+  assign mon_dv.r_valid   = axi_if.r_valid;         \
+  assign mon_dv.r_ready   = axi_if.r_ready;         \
+  assign mon_dv.wack   = axi_if.wack;               \
+  assign mon_dv.rack   = axi_if.rack;
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Setting an interface from channel or request/response structs inside a process.
+//
+// The channel macros `ACE_SET_FROM_XX(axi_if, xx_struct)` set the payload signals of the `axi_if`
+// interface from the signals in `xx_struct`.  They do not set the handshake signals.
+// The request macro `ACE_SET_FROM_REQ(axi_if, req_struct)` sets all request channels (AW, W, AR)
+// and the request-side handshake signals (AW, W, and AR valid and B and R ready) of the `axi_if`
+// interface from the signals in `req_struct`.
+// The response macro `ACE_SET_FROM_RESP(axi_if, resp_struct)` sets both response channels (B and R)
+// and the response-side handshake signals (B and R valid and AW, W, and AR ready) of the `axi_if`
+// interface from the signals in `resp_struct`.
+//
+// Usage Example:
+// always_comb begin
+//   `ACE_SET_FROM_REQ(my_if, my_req_struct)
+// end
+`define ACE_SET_FROM_AW(axi_if, aw_struct)      `__ACE_TO_AW(, axi_if.aw, _, aw_struct, .)
+`define ACE_SET_FROM_AR(axi_if, ar_struct)      `__ACE_TO_AR(, axi_if.ar, _, ar_struct, .)
+`define ACE_SET_FROM_R(axi_if, r_struct)        `__ACE_TO_R(, axi_if.r, _, r_struct, .)
+`define ACE_SET_FROM_REQ(axi_if, req_struct)    `__ACE_TO_REQ(, axi_if, _, req_struct, .)
+`define ACE_SET_FROM_RESP(axi_if, resp_struct)  `__ACE_TO_RESP(, axi_if, _, resp_struct, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning an interface from channel or request/response structs outside a process.
+//
+// The channel macros `ACE_ASSIGN_FROM_XX(axi_if, xx_struct)` assign the payload signals of the
+// `axi_if` interface from the signals in `xx_struct`.  They do not assign the handshake signals.
+// The request macro `ACE_ASSIGN_FROM_REQ(axi_if, req_struct)` assigns all request channels (AW, W,
+// AR) and the request-side handshake signals (AW, W, and AR valid and B and R ready) of the
+// `axi_if` interface from the signals in `req_struct`.
+// The response macro `ACE_ASSIGN_FROM_RESP(axi_if, resp_struct)` assigns both response channels (B
+// and R) and the response-side handshake signals (B and R valid and AW, W, and AR ready) of the
+// `axi_if` interface from the signals in `resp_struct`.
+//
+// Usage Example:
+// `ACE_ASSIGN_FROM_REQ(my_if, my_req_struct)
+`define ACE_ASSIGN_FROM_AW(axi_if, aw_struct)     `__ACE_TO_AW(assign, axi_if.aw, _, aw_struct, .)
+`define ACE_ASSIGN_FROM_AR(axi_if, ar_struct)     `__ACE_TO_AR(assign, axi_if.ar, _, ar_struct, .)
+`define ACE_ASSIGN_FROM_R(axi_if, r_struct)       `__ACE_TO_R(assign, axi_if.r, _, r_struct, .)
+`define ACE_ASSIGN_FROM_REQ(axi_if, req_struct)   `__ACE_TO_REQ(assign, axi_if, _, req_struct, .)
+`define ACE_ASSIGN_FROM_RESP(axi_if, resp_struct) `__ACE_TO_RESP(assign, axi_if, _, resp_struct, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Setting channel or request/response structs from an interface inside a process.
+//
+// The channel macros `ACE_SET_TO_XX(xx_struct, axi_if)` set the signals of `xx_struct` to the
+// payload signals of that channel in the `axi_if` interface.  They do not set the handshake
+// signals.
+// The request macro `ACE_SET_TO_REQ(axi_if, req_struct)` sets all signals of `req_struct` (i.e.,
+// request channel (AW, W, AR) payload and request-side handshake signals (AW, W, and AR valid and
+// B and R ready)) to the signals in the `axi_if` interface.
+// The response macro `ACE_SET_TO_RESP(axi_if, resp_struct)` sets all signals of `resp_struct`
+// (i.e., response channel (B and R) payload and response-side handshake signals (B and R valid and
+// AW, W, and AR ready)) to the signals in the `axi_if` interface.
+//
+// Usage Example:
+// always_comb begin
+//   `ACE_SET_TO_REQ(my_req_struct, my_if)
+// end
+`define ACE_SET_TO_AW(aw_struct, axi_if)     `__ACE_TO_AW(, aw_struct, ., axi_if.aw, _)
+`define ACE_SET_TO_AR(ar_struct, axi_if)     `__ACE_TO_AR(, ar_struct, ., axi_if.ar, _)
+`define ACE_SET_TO_R(r_struct, axi_if)       `__ACE_TO_R(, r_struct, ., axi_if.r, _)
+`define ACE_SET_TO_REQ(req_struct, axi_if)   `__ACE_TO_REQ(, req_struct, ., axi_if, _)
+`define ACE_SET_TO_RESP(resp_struct, axi_if) `__ACE_TO_RESP(, resp_struct, ., axi_if, _)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning channel or request/response structs from an interface outside a process.
+//
+// The channel macros `ACE_ASSIGN_TO_XX(xx_struct, axi_if)` assign the signals of `xx_struct` to the
+// payload signals of that channel in the `axi_if` interface.  They do not assign the handshake
+// signals.
+// The request macro `ACE_ASSIGN_TO_REQ(axi_if, req_struct)` assigns all signals of `req_struct`
+// (i.e., request channel (AW, W, AR) payload and request-side handshake signals (AW, W, and AR
+// valid and B and R ready)) to the signals in the `axi_if` interface.
+// The response macro `ACE_ASSIGN_TO_RESP(axi_if, resp_struct)` assigns all signals of `resp_struct`
+// (i.e., response channel (B and R) payload and response-side handshake signals (B and R valid and
+// AW, W, and AR ready)) to the signals in the `axi_if` interface.
+//
+// Usage Example:
+// `ACE_ASSIGN_TO_REQ(my_req_struct, my_if)
+`define ACE_ASSIGN_TO_AW(aw_struct, axi_if)     `__ACE_TO_AW(assign, aw_struct, ., axi_if.aw, _)
+`define ACE_ASSIGN_TO_AR(ar_struct, axi_if)     `__ACE_TO_AR(assign, ar_struct, ., axi_if.ar, _)
+`define ACE_ASSIGN_TO_R(r_struct, axi_if)       `__ACE_TO_R(assign, r_struct, ., axi_if.r, _)
+`define ACE_ASSIGN_TO_REQ(req_struct, axi_if)   `__ACE_TO_REQ(assign, req_struct, ., axi_if, _)
+`define ACE_ASSIGN_TO_RESP(resp_struct, axi_if) `__ACE_TO_RESP(assign, resp_struct, ., axi_if, _)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Setting channel or request/response structs from another struct inside a process.
+//
+// The channel macros `ACE_SET_XX_STRUCT(lhs, rhs)` set the fields of the `lhs` channel struct to
+// the fields of the `rhs` channel struct.  They do not set the handshake signals, which are not
+// part of channel structs.
+// The request macro `ACE_SET_REQ_STRUCT(lhs, rhs)` sets all fields of the `lhs` request struct to
+// the fields of the `rhs` request struct.  This includes all request channel (AW, W, AR) payload
+// and request-side handshake signals (AW, W, and AR valid and B and R ready).
+// The response macro `ACE_SET_RESP_STRUCT(lhs, rhs)` sets all fields of the `lhs` response struct
+// to the fields of the `rhs` response struct.  This includes all response channel (B and R) payload
+// and response-side handshake signals (B and R valid and AW, W, and R ready).
+//
+// Usage Example:
+// always_comb begin
+//   `AXI_SET_S_REQ_STRUCT(my_req_struct, another_req_struct)
+// end
+`define ACE_SET_AW_STRUCT(lhs, rhs)     `__ACE_TO_AW(, lhs, ., rhs, .)
+`define ACE_SET_AR_STRUCT(lhs, rhs)     `__ACE_TO_AR(, lhs, ., rhs, .)
+`define ACE_SET_R_STRUCT(lhs, rhs)       `__ACE_TO_R(, lhs, ., rhs, .)
+`define ACE_SET_REQ_STRUCT(lhs, rhs)   `__ACE_TO_REQ(, lhs, ., rhs, .)
+`define ACE_SET_RESP_STRUCT(lhs, rhs) `__ACE_TO_RESP(, lhs, ., rhs, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning channel or request/response structs from another struct outside a process.
+//
+// The channel macros `ACE_ASSIGN_XX_STRUCT(lhs, rhs)` assign the fields of the `lhs` channel struct
+// to the fields of the `rhs` channel struct.  They do not assign the handshake signals, which are
+// not part of the channel structs.
+// The request macro `ACE_ASSIGN_REQ_STRUCT(lhs, rhs)` assigns all fields of the `lhs` request
+// struct to the fields of the `rhs` request struct.  This includes all request channel (AW, W, AR)
+// payload and request-side handshake signals (AW, W, and AR valid and B and R ready).
+// The response macro `ACE_ASSIGN_RESP_STRUCT(lhs, rhs)` assigns all fields of the `lhs` response
+// struct to the fields of the `rhs` response struct.  This includes all response channel (B and R)
+// payload and response-side handshake signals (B and R valid and AW, W, and R ready).
+//
+// Usage Example:
+// `ACE_ASSIGN_REQ_STRUCT(my_req_struct, another_req_struct)
+`define ACE_ASSIGN_AW_STRUCT(lhs, rhs)     `__ACE_TO_AW(assign, lhs, ., rhs, .)
+`define ACE_ASSIGN_AR_STRUCT(lhs, rhs)     `__ACE_TO_AR(assign, lhs, ., rhs, .)
+`define ACE_ASSIGN_R_STRUCT(lhs, rhs)       `__ACE_TO_R(assign, lhs, ., rhs, .)
+`define ACE_ASSIGN_REQ_STRUCT(lhs, rhs)   `__ACE_TO_REQ(assign, lhs, ., rhs, .)
+`define ACE_ASSIGN_RESP_STRUCT(lhs, rhs) `__ACE_TO_RESP(assign, lhs, ., rhs, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Internal implementation for assigning one SNOOP struct or interface to another struct or interface.
+// The path to the signals on each side is defined by the `__sep*` arguments.  The `__opt_as`
+// argument allows to use this standalone (with `__opt_as = assign`) or in assignments inside
+// processes (with `__opt_as` void).
+`define __SNOOP_TO_AC(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)       \
+  __opt_as __lhs``__lhs_sep``addr      = __rhs``__rhs_sep``addr;          \
+  __opt_as __lhs``__lhs_sep``snoop   = __rhs``__rhs_sep``snoop;       \
+  __opt_as __lhs``__lhs_sep``prot    = __rhs``__rhs_sep``prot;
+`define __SNOOP_TO_CD(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)       \
+  __opt_as __lhs``__lhs_sep``data   = __rhs``__rhs_sep``data;             \
+  __opt_as __lhs``__lhs_sep``last   = __rhs``__rhs_sep``last;
+`define __SNOOP_TO_CR(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)       \
+  __opt_as __lhs``__lhs_sep``resp   = __rhs``__rhs_sep``resp;
+`define __SNOOP_TO_REQ(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)      \
+  `__SNOOP_TO_AC(__opt_as, __lhs.ac, __lhs_sep, __rhs.ac, __rhs_sep)      \
+  __opt_as __lhs.ac_valid = __rhs.ac_valid;                               \
+  __opt_as __lhs.cd_ready = __rhs.cd_ready;                               \
+  __opt_as __lhs.cr_ready = __rhs.cr_ready;
+`define __SNOOP_TO_RESP(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)     \
+  __opt_as __lhs.ac_ready = __rhs.ac_ready;                               \
+  __opt_as __lhs.cd_valid = __rhs.cd_valid;                               \
+  `__SNOOP_TO_CD(__opt_as, __lhs.cd, __lhs_sep, __rhs.cd, __rhs_sep)      \
+  __opt_as __lhs.cr_valid = __rhs.cr_valid;                               \
+  __opt_as __lhs.cr_resp = __rhs.cr_resp;
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning one SNOOP+ATOP interface to another, as if you would do `assign slv = mst;`
+//
+// The channel assignments `SNOOP_ASSIGN_XX(dst, src)` assign all payload and the valid signal of the
+// `XX` channel from the `src` to the `dst` interface and they assign the ready signal from the
+// `src` to the `dst` interface.
+// The interface assignment `SNOOP_ASSIGN(dst, src)` assigns all channels including handshakes as if
+// `src` was the master of `dst`.
+//
+// Usage Example:
+// `SNOOP_ASSIGN(slv, mst)
+// `SNOOP_ASSIGN_AC(dst, src)
+// `SNOOP_ASSIGN_Cd(dst, src)
+`define SNOOP_ASSIGN_AC(dst, src)               \
+  `__SNOOP_TO_AC(assign, dst.ac, _, src.ac, _)  \
+  assign dst.ac_valid = src.ac_valid;         \
+  assign src.ac_ready = dst.ac_ready;
+`define SNOOP_ASSIGN_CD(dst, src)                \
+  `__SNOOP_TO_CD(assign, dst.cd, _, src.cd, _)     \
+  assign dst.cd_valid  = src.cd_valid;          \
+  assign src.cd_ready  = dst.cd_ready;
+`define SNOOP_ASSIGN_CR(dst, src)                \
+  `__SNOOP_TO_CR(assign, dst.cr, _, src.cr, _)     \
+  assign dst.cr_valid  = src.cr_valid;          \
+  assign src.cr_ready  = dst.cr_ready;
+`define SNOOP_ASSIGN(slv, mst)  \
+  `SNOOP_ASSIGN_AC(slv, mst)    \
+  `SNOOP_ASSIGN_CD(mst, slv)    \
+  `SNOOP_ASSIGN_CR(mst, slv)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// The channel assignment `SNOOP_ASSIGN_MONITOR(mon_dv, snoop_if)` assigns all signals from `snoop_if`
+// to the `mon_dv` interface.
+//
+// Usage Example:
+// `SNOOP_ASSIGN_MONITOR(mon_dv, snoop_if)
+`define SNOOP_ASSIGN_MONITOR(mon_dv, snoop_if)          \
+  `__SNOOP_TO_AC(assign, mon_dv.ac, _, snoop_if.ac, _)  \
+  assign mon_dv.ac_valid  = snoop_if.ac_valid;        \
+  assign mon_dv.ac_ready  = snoop_if.ac_ready;        \
+  `__SNOOP_TO_CD(assign, mon_dv.cd, _, snoop_if.cd, _)     \
+  assign mon_dv.cd_valid   = snoop_if.cd_valid;         \
+  assign mon_dv.cd_ready   = snoop_if.cd_ready;         \
+  `__SNOOP_TO_CR(assign, mon_dv.cr, _, snoop_if.cr, _)     \
+  assign mon_dv.cr_ready   = snoop_if.cr_ready;         \
+  assign mon_dv.cr_valid   = snoop_if.cr_valid;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Setting an interface from channel or request/response structs inside a process.
+//
+// The channel macros `SNOOP_SET_FROM_XX(snoop_if, xx_struct)` set the payload signals of the `snoop_if`
+// interface from the signals in `xx_struct`.  They do not set the handshake signals.
+// The request macro `SNOOP_SET_FROM_REQ(snoop_if, req_struct)` sets all request channels (AC)
+// and the request-side handshake signals (AC valid, CD and CR ready) of the `snoop_if`
+// interface from the signals in `req_struct`.
+// The response macro `SNOOP_SET_FROM_RESP(snoop_if, resp_struct)` sets both response channels (CD and CR)
+// and the response-side handshake signals (CD and CR valid, AC ready) of the `snoop_if`
+// interface from the signals in `resp_struct`.
+//
+// Usage Example:
+// always_comb begin
+//   `SNOOP_SET_FROM_REQ(my_if, my_req_struct)
+// end
+`define SNOOP_SET_FROM_AC(snoop_if, ac_struct)      `__SNOOP_TO_AC(, snoop_if.ac, _, ac_struct, .)
+`define SNOOP_SET_FROM_CD(snoop_if, cd_struct)      `__SNOOP_TO_CD(, snoop_if.cd, _, cd_struct, .)
+`define SNOOP_SET_FROM_CR(snoop_if, cr_struct)        `__SNOOP_TO_CR(, snoop_if.cr, _, cr_struct, .)
+`define SNOOP_SET_FROM_REQ(snoop_if, req_struct)    `__SNOOP_TO_REQ(, snoop_if, _, req_struct, .)
+`define SNOOP_SET_FROM_RESP(snoop_if, resp_struct)  `__SNOOP_TO_RESP(, snoop_if, _, resp_struct, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning an interface from channel or request/response structs outside a process.
+//
+// The channel macros `SNOOP_ASSIGN_FROM_XX(snoop_if, xx_struct)` assign the payload signals of the
+// `snoop_if` interface from the signals in `xx_struct`.  They do not assign the handshake signals.
+// The request macro `SNOOP_ASSIGN_FROM_REQ(snoop_if, req_struct)` assigns all request channels (AC)
+// and the request-side handshake signals (AC valid and CD and CR ready) of the `snoop_if` interface
+// from the signals in `req_struct`.The response macro `SNOOP_ASSIGN_FROM_RESP(snoop_if, resp_struct)`
+// assigns both response channels (CD and CR) and the response-side handshake signals (CD and CR valid
+// and AC ready) of the `snoop_if` interface from the signals in `resp_struct`.
+//
+// Usage Example:
+// `SNOOP_ASSIGN_FROM_REQ(my_if, my_req_struct)
+`define SNOOP_ASSIGN_FROM_AC(snoop_if, ac_struct)     `__SNOOP_TO_AC(assign, snoop_if.ac, _, ac_struct, .)
+`define SNOOP_ASSIGN_FROM_CD(snoop_if, cd_struct)     `__SNOOP_TO_CD(assign, snoop_if.cd, _, cd_struct, .)
+`define SNOOP_ASSIGN_FROM_CR(snoop_if, cr_struct)       `__SNOOP_TO_CR(assign, snoop_if.cr, _, cr_struct, .)
+`define SNOOP_ASSIGN_FROM_REQ(snoop_if, req_struct)   `__SNOOP_TO_REQ(assign, snoop_if, _, req_struct, .)
+`define SNOOP_ASSIGN_FROM_RESP(snoop_if, resp_struct) `__SNOOP_TO_RESP(assign, snoop_if, _, resp_struct, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Setting channel or request/response structs from an interface inside a process.
+//
+// The channel macros `SNOOP_SET_TO_XX(xx_struct, snoop_if)` set the signals of `xx_struct` to the
+// payload signals of that channel in the `snoop_if` interface.  They do not set the handshake
+// signals.
+// The request macro `SNOOP_SET_TO_REQ(snoop_if, req_struct)` sets all signals of `req_struct` (i.e.,
+// request channel (AC) payload and request-side handshake signals (AC valid and
+// CD and CR ready)) to the signals in the `snoop_if` interface.
+// The response macro `SNOOP_SET_TO_RESP(snoop_if, resp_struct)` sets all signals of `resp_struct`
+// (i.e., response channel (CD and CR) payload and response-side handshake signals (CD and CR valid and
+// AC ready)) to the signals in the `snoop_if` interface.
+//
+// Usage Example:
+// always_comb begin
+//   `SNOOP_SET_TO_REQ(my_req_struct, my_if)
+// end
+`define SNOOP_SET_TO_AC(ac_struct, snoop_if)     `__SNOOP_TO_AC(, ac_struct, ., snoop_if.ac, _)
+`define SNOOP_SET_TO_CD(cd_struct, snoop_if)     `__SNOOP_TO_CD(, cd_struct, ., snoop_if.cd, _)
+`define SNOOP_SET_TO_CR(cr_struct, snoop_if)       `__SNOOP_TO_CR(, cr_struct, ., snoop_if.cr, _)
+`define SNOOP_SET_TO_REQ(req_struct, snoop_if)   `__SNOOP_TO_REQ(, req_struct, ., snoop_if, _)
+`define SNOOP_SET_TO_RESP(resp_struct, snoop_if) `__SNOOP_TO_RESP(, resp_struct, ., snoop_if, _)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning channel or request/response structs from an interface outside a process.
+//
+// The channel macros `SNOOP_ASSIGN_TO_XX(xx_struct, snoop_if)` assign the signals of `xx_struct` to the
+// payload signals of that channel in the `snoop_if` interface.  They do not assign the handshake
+// signals.
+// The request macro `SNOOP_ASSIGN_TO_REQ(snoop_if, req_struct)` assigns all signals of `req_struct`
+// (i.e., request channel (AC) payload and request-side handshake signals (AC valid and CD and CR ready))
+// to the signals in the `snoop_if` interface.
+// The response macro `SNOOP_ASSIGN_TO_RESP(snoop_if, resp_struct)` assigns all signals of `resp_struct`
+// (i.e., response channel (CD and CR) payload and response-side handshake signals (CD and CR valid and
+// AC ready)) to the signals in the `snoop_if` interface.
+//
+// Usage Example:
+// `SNOOP_ASSIGN_TO_REQ(my_req_struct, my_if)
+`define SNOOP_ASSIGN_TO_AC(aw_struct, snoop_if)     `__SNOOP_TO_AC(assign, aw_struct, ., snoop_if.aw, _)
+`define SNOOP_ASSIGN_TO_CD(ar_struct, snoop_if)     `__SNOOP_TO_CD(assign, ar_struct, ., snoop_if.ar, _)
+`define SNOOP_ASSIGN_TO_CR(r_struct, snoop_if)       `__SNOOP_TO_CR(assign, r_struct, ., snoop_if.r, _)
+`define SNOOP_ASSIGN_TO_REQ(req_struct, snoop_if)   `__SNOOP_TO_REQ(assign, req_struct, ., snoop_if, _)
+`define SNOOP_ASSIGN_TO_RESP(resp_struct, snoop_if) `__SNOOP_TO_RESP(assign, resp_struct, ., snoop_if, _)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Setting channel or request/response structs from another struct inside a process.
+//
+// The channel macros `SNOOP_SET_XX_STRUCT(lhs, rhs)` set the fields of the `lhs` channel struct to
+// the fields of the `rhs` channel struct.  They do not set the handshake signals, which are not
+// part of channel structs.
+// The request macro `SNOOP_SET_REQ_STRUCT(lhs, rhs)` sets all fields of the `lhs` request struct to
+// the fields of the `rhs` request struct.  This includes all request channel (AC) payload
+// and request-side handshake signals (AC valid and CD and CR ready).
+// The response macro `SNOOP_SET_RESP_STRUCT(lhs, rhs)` sets all fields of the `lhs` response struct
+// to the fields of the `rhs` response struct.  This includes all response channel (CD and CR) payload
+// and response-side handshake signals (CD and CR valid and AC ready).
+//
+// Usage Example:
+// always_comb begin
+//   `SNOOP_SET_S_REQ_STRUCT(my_req_struct, another_req_struct)
+// end
+`define SNOOP_SET_AC_STRUCT(lhs, rhs)     `__SNOOP_TO_AC(, lhs, ., rhs, .)
+`define SNOOP_SET_CD_STRUCT(lhs, rhs)     `__SNOOP_TO_CD(, lhs, ., rhs, .)
+`define SNOOP_SET_CR_STRUCT(lhs, rhs)       `__SNOOP_TO_CR(, lhs, ., rhs, .)
+`define SNOOP_SET_REQ_STRUCT(lhs, rhs)   `__SNOOP_TO_REQ(, lhs, ., rhs, .)
+`define SNOOP_SET_RESP_STRUCT(lhs, rhs) `__SNOOP_TO_RESP(, lhs, ., rhs, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Assigning channel or request/response structs from another struct outside a process.
+//
+// The channel macros `SNOOP_ASSIGN_XX_STRUCT(lhs, rhs)` assign the fields of the `lhs` channel struct
+// to the fields of the `rhs` channel struct.  They do not assign the handshake signals, which are
+// not part of the channel structs.
+// The request macro `SNOOP_ASSIGN_REQ_STRUCT(lhs, rhs)` assigns all fields of the `lhs` request
+// struct to the fields of the `rhs` request struct.  This includes all request channel (AW, W, AR)
+// payload and request-side handshake signals (AC valid and CD and CR ready).
+// The response macro `SNOOP_ASSIGN_RESP_STRUCT(lhs, rhs)` assigns all fields of the `lhs` response
+// struct to the fields of the `rhs` response struct.  This includes all response channel (CD and CR)
+// payload and response-side handshake signals (CD and CR valid and AC ready).
+//
+// Usage Example:
+// `SNOOP_ASSIGN_REQ_STRUCT(my_req_struct, another_req_struct)
+`define SNOOP_ASSIGN_AC_STRUCT(lhs, rhs)     `__SNOOP_TO_AC(assign, lhs, ., rhs, .)
+`define SNOOP_ASSIGN_CD_STRUCT(lhs, rhs)     `__SNOOP_TO_CD(assign, lhs, ., rhs, .)
+`define SNOOP_ASSIGN_CR_STRUCT(lhs, rhs)       `__SNOOP_TO_CR(assign, lhs, ., rhs, .)
+`define SNOOP_ASSIGN_REQ_STRUCT(lhs, rhs)   `__SNOOP_TO_REQ(assign, lhs, ., rhs, .)
+`define SNOOP_ASSIGN_RESP_STRUCT(lhs, rhs) `__SNOOP_TO_RESP(assign, lhs, ., rhs, .)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+`endif
diff --git a/include/ace/convert.svh b/include/ace/convert.svh
new file mode 100644
index 000000000..61f840b24
--- /dev/null
+++ b/include/ace/convert.svh
@@ -0,0 +1,104 @@
+`include "axi/assign.svh"
+
+`ifndef ACE_CONVERT_SVH_
+`define ACE_CONVERT_SVH_
+
+`define __ACE_TO_AXI_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)     \
+    __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;            \
+    __opt_as __lhs``__lhs_sep``data   = __rhs``__rhs_sep``data;          \
+    __opt_as __lhs``__lhs_sep``resp   = __rhs``__rhs_sep``resp[1:0];     \
+    __opt_as __lhs``__lhs_sep``last   = __rhs``__rhs_sep``last;          \
+    __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;
+`define __AXI_TO_ACE_AW(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
+    __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;         \
+    __opt_as __lhs``__lhs_sep``addr   = __rhs``__rhs_sep``addr;       \
+    __opt_as __lhs``__lhs_sep``len    = __rhs``__rhs_sep``len;        \
+    __opt_as __lhs``__lhs_sep``size   = __rhs``__rhs_sep``size;       \
+    __opt_as __lhs``__lhs_sep``burst  = __rhs``__rhs_sep``burst;      \
+    __opt_as __lhs``__lhs_sep``lock   = __rhs``__rhs_sep``lock;       \
+    __opt_as __lhs``__lhs_sep``cache  = __rhs``__rhs_sep``cache;      \
+    __opt_as __lhs``__lhs_sep``prot   = __rhs``__rhs_sep``prot;       \
+    __opt_as __lhs``__lhs_sep``qos    = __rhs``__rhs_sep``qos;        \
+    __opt_as __lhs``__lhs_sep``region = __rhs``__rhs_sep``region;     \
+    __opt_as __lhs``__lhs_sep``atop   = __rhs``__rhs_sep``atop;       \
+    __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;       \
+    __opt_as __lhs``__lhs_sep``snoop  = '0;                           \
+    __opt_as __lhs``__lhs_sep``bar    = '0;                           \
+    __opt_as __lhs``__lhs_sep``domain = '0;                           \
+    __opt_as __lhs``__lhs_sep``awunique = '0;
+`define __AXI_TO_ACE_AR(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
+    __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;         \
+    __opt_as __lhs``__lhs_sep``addr   = __rhs``__rhs_sep``addr;       \
+    __opt_as __lhs``__lhs_sep``len    = __rhs``__rhs_sep``len;        \
+    __opt_as __lhs``__lhs_sep``size   = __rhs``__rhs_sep``size;       \
+    __opt_as __lhs``__lhs_sep``burst  = __rhs``__rhs_sep``burst;      \
+    __opt_as __lhs``__lhs_sep``lock   = __rhs``__rhs_sep``lock;       \
+    __opt_as __lhs``__lhs_sep``cache  = __rhs``__rhs_sep``cache;      \
+    __opt_as __lhs``__lhs_sep``prot   = __rhs``__rhs_sep``prot;       \
+    __opt_as __lhs``__lhs_sep``qos    = __rhs``__rhs_sep``qos;        \
+    __opt_as __lhs``__lhs_sep``region = __rhs``__rhs_sep``region;     \
+    __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;       \
+    __opt_as __lhs``__lhs_sep``snoop  = '0;                           \
+    __opt_as __lhs``__lhs_sep``bar    = '0;                           \
+    __opt_as __lhs``__lhs_sep``domain = '0;
+`define __AXI_TO_ACE_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep)     \
+    __opt_as __lhs``__lhs_sep``id     = __rhs``__rhs_sep``id;            \
+    __opt_as __lhs``__lhs_sep``data   = __rhs``__rhs_sep``data;          \
+    __opt_as __lhs``__lhs_sep``resp   = {2'b00, __rhs``__rhs_sep``resp}; \
+    __opt_as __lhs``__lhs_sep``last   = __rhs``__rhs_sep``last;          \
+    __opt_as __lhs``__lhs_sep``user   = __rhs``__rhs_sep``user;
+
+`define ACE_TO_AXI_ASSIGN_R_STRUCT(dst, src) \
+    `__ACE_TO_AXI_R(assign, dst, ., src, .)
+
+`define AXI_TO_ACE_ASSIGN_AW_STRUCT(dst, src) \
+    `__AXI_TO_ACE_AW(assign, dst, ., src, .)
+
+`define AXI_TO_ACE_ASSIGN_AR_STRUCT(dst, src) \
+    `__AXI_TO_ACE_AR(assign, dst, ., src, .)
+
+`define AXI_TO_ACE_ASSIGN_R_STRUCT(dst, src) \
+    `__AXI_TO_ACE_R(assign, dst, ., src, .)
+
+
+`define ACE_TO_AXI_ASSIGN_REQ(dst, src)   \
+    `AXI_ASSIGN_AW_STRUCT(dst.aw, src.aw) \
+    `AXI_ASSIGN_AR_STRUCT(dst.ar, src.ar) \
+    `AXI_ASSIGN_W_STRUCT(dst.w, src.w)    \
+    assign dst.aw_valid = src.aw_valid;   \
+    assign dst.ar_valid = src.ar_valid;   \
+    assign dst.w_valid  = src.w_valid;    \
+    assign dst.b_ready  = src.b_ready;    \
+    assign dst.r_ready  = src.r_ready;
+
+`define ACE_TO_AXI_ASSIGN_RESP(dst, src)      \
+    `ACE_TO_AXI_ASSIGN_R_STRUCT(dst.r, src.r) \
+    `AXI_ASSIGN_B_STRUCT(dst.b, src.b)        \
+    assign dst.aw_ready = src.aw_ready;       \
+    assign dst.ar_ready = src.ar_ready;       \
+    assign dst.w_ready  = src.w_ready;        \
+    assign dst.b_valid  = src.b_valid;        \
+    assign dst.r_valid  = src.r_valid;
+
+`define AXI_TO_ACE_ASSIGN_REQ(dst, src)          \
+    `AXI_TO_ACE_ASSIGN_AW_STRUCT(dst.aw, src.aw) \
+    `AXI_TO_ACE_ASSIGN_AR_STRUCT(dst.ar, src.ar) \
+    `AXI_ASSIGN_W_STRUCT(dst.w, src.w)           \
+    assign dst.aw_valid = src.aw_valid;          \
+    assign dst.ar_valid = src.ar_valid;          \
+    assign dst.w_valid  = src.w_valid;           \
+    assign dst.b_ready  = src.b_ready;           \
+    assign dst.r_ready  = src.r_ready;
+
+
+`define AXI_TO_ACE_ASSIGN_RESP(dst, src)      \
+    `AXI_TO_ACE_ASSIGN_R_STRUCT(dst.r, src.r) \
+    `AXI_ASSIGN_B_STRUCT(dst.b, src.b)        \
+    assign dst.aw_ready = src.aw_ready;       \
+    assign dst.ar_ready = src.ar_ready;       \
+    assign dst.w_ready  = src.w_ready;        \
+    assign dst.b_valid  = src.b_valid;        \
+    assign dst.r_valid  = src.r_valid;
+
+
+`endif // ACE_CONVERT_SVH_
diff --git a/include/ace/domain.svh b/include/ace/domain.svh
new file mode 100644
index 000000000..cbc867373
--- /dev/null
+++ b/include/ace/domain.svh
@@ -0,0 +1,28 @@
+`ifndef ACE_DOMAIN_SVH_
+`define ACE_DOMAIN_SVH_
+
+  //////////////////
+  // Domain types //
+  //////////////////
+
+    `define DOMAIN_MASK_T(width)\
+        logic [width-1:0]
+    `define DOMAIN_SET_T           \
+        struct packed {    \
+          domain_mask_t initiator; \
+          domain_mask_t inner;     \
+          domain_mask_t outer;     \
+        }
+    `define DOMAIN_TYPEDEF_MASK_T(width) \
+        typedef logic [width-1:0] domain_mask_t;
+    `define DOMAIN_TYPEDEF_SET_T \
+        typedef  struct packed {    \
+          domain_mask_t initiator; \
+          domain_mask_t inner;     \
+          domain_mask_t outer;     \
+        } domain_set_t;
+    `define DOMAIN_TYPEDEF_ALL(width) \
+        `DOMAIN_TYPEDEF_MASK_T(width) \
+        `DOMAIN_TYPEDEF_SET_T
+
+`endif // ACE_DOMAIN_SVH_
diff --git a/include/ace/typedef.svh b/include/ace/typedef.svh
new file mode 100644
index 000000000..fb35d1cc5
--- /dev/null
+++ b/include/ace/typedef.svh
@@ -0,0 +1,167 @@
+// Copyright (c) 2019 ETH Zurich, University of Bologna
+// Copyright (c) 2022 PlanV GmbH
+//
+// 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.
+//
+
+// Macros to define ACE Channel and Request/Response Structs
+
+`ifndef ACE_TYPEDEF_SVH_
+`define ACE_TYPEDEF_SVH_
+
+`include "axi/typedef.svh"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// AXI4+ATOP Channel and Request/Response Structs (with snoop support)
+//
+// Usage Example:
+// `ACE_TYPEDEF_AW_CHAN_T(axi_aw_t, axi_addr_t, axi_id_t, axi_user_t)
+// `ACE_TYPEDEF_AR_CHAN_T(axi_ar_t, axi_addr_t, axi_id_t, axi_user_t)
+// `ACE_TYPEDEF_R_CHAN_T(axi_r_t, axi_data_t, axi_id_t, axi_user_t)
+// `ACE_TYPEDEF_REQ_T(axi_req_t, axi_aw_t, axi_w_t, axi_ar_t)
+// `ACE_TYPEDEF_RESP_T(axi_resp_t, axi_b_t, axi_r_t)
+`define ACE_TYPEDEF_AW_CHAN_T(aw_chan_t, addr_t, id_t, user_t)  \
+  typedef struct packed {                                       \
+    id_t                id;                                       \
+    addr_t              addr;                                     \
+    axi_pkg::len_t      len;                                      \
+    axi_pkg::size_t     size;                                     \
+    axi_pkg::burst_t    burst;                                    \
+    logic               lock;                                     \
+    axi_pkg::cache_t    cache;                                    \
+    axi_pkg::prot_t     prot;                                     \
+    axi_pkg::qos_t      qos;                                      \
+    axi_pkg::region_t   region;                                   \
+    axi_pkg::atop_t     atop;                                     \
+    user_t              user;                                     \
+    ace_pkg::awsnoop_t  snoop;                                  \
+    ace_pkg::axbar_t      bar;                                      \
+    ace_pkg::axdomain_t   domain;                                   \
+    ace_pkg::awunique_t awunique;                                 \
+  } aw_chan_t;
+`define ACE_TYPEDEF_AR_CHAN_T(ar_chan_t, addr_t, id_t, user_t)  \
+  typedef struct packed {                                         \
+    id_t                id;                                       \
+    addr_t              addr;                                     \
+    axi_pkg::len_t      len;                                      \
+    axi_pkg::size_t     size;                                     \
+    axi_pkg::burst_t    burst;                                    \
+    logic               lock;                                     \
+    axi_pkg::cache_t    cache;                                    \
+    axi_pkg::prot_t     prot;                                     \
+    axi_pkg::qos_t      qos;                                      \
+    axi_pkg::region_t   region;                                   \
+    user_t              user;                                     \
+    ace_pkg::arsnoop_t  snoop;                                  \
+    ace_pkg::axbar_t      bar;                                      \
+    ace_pkg::axdomain_t   domain;                                   \
+  } ar_chan_t;
+`define ACE_TYPEDEF_R_CHAN_T(r_chan_t, data_t, id_t, user_t)  \
+  typedef struct packed {                                        \
+    id_t              id;                                       \
+    data_t            data;                                     \
+    ace_pkg::rresp_t  resp;                                    \
+    logic             last;                                     \
+    user_t            user;                                     \
+  } r_chan_t;
+`define ACE_TYPEDEF_REQ_T(req_t, aw_chan_t, w_chan_t, ar_chan_t)  \
+  typedef struct packed {                                         \
+    aw_chan_t aw;                                             \
+    logic     aw_valid;                                           \
+    w_chan_t  w;                                                  \
+    logic     w_valid;                                            \
+    logic     b_ready;                                            \
+    ar_chan_t ar;                                             \
+    logic     ar_valid;                                           \
+    logic     r_ready;                                            \
+    logic     wack;                                               \
+    logic     rack;                                               \
+  } req_t;
+`define ACE_TYPEDEF_RESP_T(resp_t, b_chan_t, r_chan_t)  \
+  typedef struct packed {                               \
+    logic     aw_ready;                                 \
+    logic     ar_ready;                                 \
+    logic     w_ready;                                  \
+    logic     b_valid;                                  \
+    b_chan_t  b;                                        \
+    logic     r_valid;                                  \
+    r_chan_t  r;                                        \
+  } resp_t;
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// All AXI4+ATOP Channels and Request/Response Structs in One Macro (with snoop support)
+//
+// This can be used whenever the user is not interested in "precise" control of the naming of the
+// individual channels.
+//
+// Usage Example:
+// `AXI_TYPEDEF_ALL(axi, addr_t, id_t, data_t, strb_t, user_t)
+//
+// This defines `axi_req_t` and `axi_resp_t` request/response structs as well as `axi_aw_chan_t`,
+// `axi_w_chan_t`, `axi_b_chan_t`, `axi_ar_chan_t`, and `axi_r_chan_t` channel structs.
+`define ACE_TYPEDEF_ALL(__name, __addr_t, __id_t, __data_t, __strb_t, __user_t)                 \
+  `ACE_TYPEDEF_AW_CHAN_T(__name``_aw_chan_t, __addr_t, __id_t, __user_t)                    \
+  `AXI_TYPEDEF_W_CHAN_T(__name``_w_chan_t, __data_t, __strb_t, __user_t)                        \
+  `AXI_TYPEDEF_B_CHAN_T(__name``_b_chan_t, __id_t, __user_t)                                    \
+  `ACE_TYPEDEF_AR_CHAN_T(__name``_ar_chan_t, __addr_t, __id_t, __user_t)                    \
+  `ACE_TYPEDEF_R_CHAN_T(__name``_r_chan_t, __data_t, __id_t, __user_t)                      \
+  `ACE_TYPEDEF_REQ_T(__name``_req_t, __name``_aw_chan_t, __name``_w_chan_t, __name``_ar_chan_t) \
+  `ACE_TYPEDEF_RESP_T(__name``_resp_t, __name``_b_chan_t, __name``_r_chan_t)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Usage Example:
+// `SNOOP_TYPEDEF_AC_CHAN_T(snoop_ac_t, snoop_addr_t)
+// 'SNOOP_TYPEDEF_CD_CHAN_T(snoop_cd_t, snoop_data_t)
+// `SNOOP_TYPEDEF_REQ_T(snoop_req_t, snoop_ac_t)
+// `SNOOP_TYPEDEF_RESP_T(snoop_resp_t, snoop_cd_t, snoop_cr_t)
+`define SNOOP_TYPEDEF_AC_CHAN_T(ac_chan_t, addr_t)              \
+  typedef struct packed {                                       \
+    addr_t                addr;                                 \
+    ace_pkg::acsnoop_t  snoop;                              \
+    ace_pkg::acprot_t   prot;                               \
+  } ac_chan_t;
+`define SNOOP_TYPEDEF_CD_CHAN_T(cd_chan_t, data_t)              \
+  typedef struct packed {                                       \
+    data_t                data;                                 \
+    logic                 last;                                 \
+  } cd_chan_t;
+`define SNOOP_TYPEDEF_CR_CHAN_T(cr_chan_t)                      \
+   typedef ace_pkg::crresp_t     cr_chan_t;
+`define SNOOP_TYPEDEF_REQ_T(req_t, ac_chan_t)      \
+  typedef struct packed {                                       \
+    logic     ac_valid;                                         \
+    logic     cd_ready;                                         \
+    ac_chan_t ac;                                               \
+    logic     cr_ready;                                         \
+  } req_t;
+`define SNOOP_TYPEDEF_RESP_T(resp_t, cd_chan_t, cr_chan_t)      \
+  typedef struct packed {                                       \
+    logic     ac_ready;                                         \
+    logic     cd_valid;                                         \
+    cd_chan_t cd;                                               \
+    logic     cr_valid;                                         \
+    cr_chan_t cr_resp;                                          \
+  } resp_t;
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Usage Example:
+// `SNOOP_TYPEDEF_ALL(snoop, addr_t, data_t)
+//
+// This defines `snoop_req_t` and `snoop_resp_t` request/response structs as well as `snoop_ac_chan_t`,
+// `snoop_cd_chan_t` and `snoop_cr_chan_t` channel structs.
+  `define SNOOP_TYPEDEF_ALL(__name, __addr_t, __data_t)               \
+  `SNOOP_TYPEDEF_AC_CHAN_T(__name``_aw_chan_t, __addr_t)              \
+  `SNOOP_TYPEDEF_CR_CHAN_T(__name``_cr_chan_t)                        \
+  `SNOOP_TYPEDEF_CD_CHAN_T(__name``_cd_chan_t, __data_t)              \
+  `SNOOP_TYPEDEF_REQ_T(__name``_req_t, __name``_ac_chan_t)            \
+  `SNOOP_TYPEDEF_RESP_T(__name``_resp_t, __name``_cd_chan_t, __name``_cr_chan_t)
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+`endif
diff --git a/src/ace/ace_intf.sv b/src/ace/ace_intf.sv
new file mode 100644
index 000000000..ce79e57c1
--- /dev/null
+++ b/src/ace/ace_intf.sv
@@ -0,0 +1,277 @@
+// Copyright (c) 2014-2018 ETH Zurich, University of Bologna
+// Copyright (c) 2022 PlanV GmbH
+//
+// 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.
+
+interface CLK_IF (input clk_i);
+endinterface
+
+// ACE bus interafces
+interface ACE_BUS #(
+  parameter int unsigned AXI_ADDR_WIDTH = 0,
+  parameter int unsigned AXI_DATA_WIDTH = 0,
+  parameter int unsigned AXI_ID_WIDTH   = 0,
+  parameter int unsigned AXI_USER_WIDTH = 0
+);
+
+  localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
+
+  typedef logic [AXI_ID_WIDTH-1:0]   id_t;
+  typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
+  typedef logic [AXI_DATA_WIDTH-1:0] data_t;
+  typedef logic [AXI_STRB_WIDTH-1:0] strb_t;
+  typedef logic [AXI_USER_WIDTH-1:0] user_t;
+
+  id_t              aw_id;
+  addr_t            aw_addr;
+  axi_pkg::len_t    aw_len;
+  axi_pkg::size_t   aw_size;
+  axi_pkg::burst_t  aw_burst;
+  logic             aw_lock;
+  axi_pkg::cache_t  aw_cache;
+  axi_pkg::prot_t   aw_prot;
+  axi_pkg::qos_t    aw_qos;
+  axi_pkg::region_t aw_region;
+  axi_pkg::atop_t   aw_atop;
+  user_t            aw_user;
+  logic             aw_valid;
+  logic             aw_ready;
+  ace_pkg::awsnoop_t aw_snoop;
+  ace_pkg::axbar_t    aw_bar;
+  ace_pkg::axdomain_t aw_domain;
+  ace_pkg::awunique_t aw_awunique;
+
+  data_t            w_data;
+  strb_t            w_strb;
+  logic             w_last;
+  user_t            w_user;
+  logic             w_valid;
+  logic             w_ready;
+
+  id_t              b_id;
+  axi_pkg::resp_t   b_resp;
+  user_t            b_user;
+  logic             b_valid;
+  logic             b_ready;
+
+  id_t              ar_id;
+  addr_t            ar_addr;
+  axi_pkg::len_t    ar_len;
+  axi_pkg::size_t   ar_size;
+  axi_pkg::burst_t  ar_burst;
+  logic             ar_lock;
+  axi_pkg::cache_t  ar_cache;
+  axi_pkg::prot_t   ar_prot;
+  axi_pkg::qos_t    ar_qos;
+  axi_pkg::region_t ar_region;
+  user_t            ar_user;
+  logic             ar_valid;
+  logic             ar_ready;
+  ace_pkg::arsnoop_t ar_snoop;
+  ace_pkg::axbar_t    ar_bar;
+  ace_pkg::axdomain_t ar_domain;
+ 
+  id_t              r_id;
+  data_t            r_data;
+  ace_pkg::rresp_t   r_resp;
+  logic             r_last;
+  user_t            r_user;
+  logic             r_valid;
+  logic             r_ready;
+
+  logic             wack;
+  logic             rack;
+
+  modport Master (
+    output aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_snoop, aw_bar, aw_domain, aw_awunique, input aw_ready,
+    output w_data, w_strb, w_last, w_user, w_valid, input w_ready,
+    input b_id, b_resp, b_user, b_valid, output b_ready,
+    output ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_snoop, ar_bar, ar_domain, input ar_ready,
+    input r_id, r_data, r_resp, r_last, r_user, r_valid, output r_ready,
+    output wack, rack
+  );
+
+  modport Slave (
+    input  aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_snoop, aw_bar, aw_domain, aw_awunique, output aw_ready,
+    input  w_data, w_strb, w_last, w_user, w_valid, output w_ready,
+    output b_id, b_resp, b_user, b_valid, input b_ready,
+    input  ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_snoop, ar_bar, ar_domain, output ar_ready,
+    output r_id, r_data, r_resp, r_last, r_user, r_valid, input r_ready,
+    input wack, rack
+  );
+
+  modport Monitor (
+    input aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_ready, aw_snoop, aw_bar, aw_domain, aw_awunique,
+          w_data, w_strb, w_last, w_user, w_valid, w_ready,
+          b_id, b_resp, b_user, b_valid, b_ready,
+          ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_ready, ar_snoop, ar_bar, ar_domain,
+          r_id, r_data, r_resp, r_last, r_user, r_valid, r_ready,
+          wack, rack
+  );
+
+endinterface
+
+
+/// A clocked ACE interface for use in design verification.
+interface ACE_BUS_DV #(
+  parameter int unsigned AXI_ADDR_WIDTH = 0,
+  parameter int unsigned AXI_DATA_WIDTH = 0,
+  parameter int unsigned AXI_ID_WIDTH   = 0,
+  parameter int unsigned AXI_USER_WIDTH = 0
+)(
+  input logic clk_i
+);
+
+  localparam int unsigned AXI_STRB_WIDTH = AXI_DATA_WIDTH / 8;
+
+  typedef logic [AXI_ID_WIDTH-1:0]   id_t;
+  typedef logic [AXI_ADDR_WIDTH-1:0] addr_t;
+  typedef logic [AXI_DATA_WIDTH-1:0] data_t;
+  typedef logic [AXI_STRB_WIDTH-1:0] strb_t;
+  typedef logic [AXI_USER_WIDTH-1:0] user_t;
+
+  id_t                aw_id;
+  addr_t              aw_addr;
+  axi_pkg::len_t      aw_len;
+  axi_pkg::size_t     aw_size;
+  axi_pkg::burst_t    aw_burst;
+  logic               aw_lock;
+  axi_pkg::cache_t    aw_cache;
+  axi_pkg::prot_t     aw_prot;
+  axi_pkg::qos_t      aw_qos;
+  axi_pkg::region_t   aw_region;
+  axi_pkg::atop_t     aw_atop;
+  user_t              aw_user;
+  logic               aw_valid;
+  logic               aw_ready;
+  ace_pkg::awsnoop_t  aw_snoop;
+  ace_pkg::axbar_t    aw_bar;
+  ace_pkg::axdomain_t aw_domain;
+  ace_pkg::awunique_t aw_awunique;
+
+  data_t            w_data;
+  strb_t            w_strb;
+  logic             w_last;
+  user_t            w_user;
+  logic             w_valid;
+  logic             w_ready;
+
+  id_t              b_id;
+  axi_pkg::resp_t   b_resp;
+  user_t            b_user;
+  logic             b_valid;
+  logic             b_ready;
+
+  id_t                ar_id;
+  addr_t              ar_addr;
+  axi_pkg::len_t      ar_len;
+  axi_pkg::size_t     ar_size;
+  axi_pkg::burst_t    ar_burst;
+  logic               ar_lock;
+  axi_pkg::cache_t    ar_cache;
+  axi_pkg::prot_t     ar_prot;
+  axi_pkg::qos_t      ar_qos;
+  axi_pkg::region_t   ar_region;
+  user_t              ar_user;
+  logic               ar_valid;
+  logic               ar_ready;
+  ace_pkg::arsnoop_t  ar_snoop;
+  ace_pkg::axbar_t    ar_bar;
+  ace_pkg::axdomain_t ar_domain;
+
+  id_t             r_id;
+  data_t           r_data;
+  ace_pkg::rresp_t r_resp;
+  logic            r_last;
+  user_t           r_user;
+  logic            r_valid;
+  logic            r_ready;
+
+  logic            wack;
+  logic            rack;
+
+  modport Master (
+    output aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_snoop, aw_bar, aw_domain, aw_awunique, input aw_ready,
+    output w_data, w_strb, w_last, w_user, w_valid, input w_ready,
+    input b_id, b_resp, b_user, b_valid, output b_ready,
+    output ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_snoop, ar_bar,ar_domain, input ar_ready,
+    input r_id, r_data, r_resp, r_last, r_user, r_valid, output r_ready,
+    output wack, rack
+  );
+
+  modport Slave (
+    input  aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_snoop, aw_bar, aw_domain, aw_awunique, output aw_ready,
+    input  w_data, w_strb, w_last, w_user, w_valid, output w_ready,
+    output b_id, b_resp, b_user, b_valid, input b_ready,
+    input  ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_snoop, ar_bar, ar_domain, output ar_ready,
+    output r_id, r_data, r_resp, r_last, r_user, r_valid, input r_ready,
+    input wack, rack
+  );
+
+  modport Monitor (
+    input aw_id, aw_addr, aw_len, aw_size, aw_burst, aw_lock, aw_cache, aw_prot, aw_qos, aw_region, aw_atop, aw_user, aw_valid, aw_ready, aw_snoop, aw_bar, aw_domain, aw_awunique,
+          w_data, w_strb, w_last, w_user, w_valid, w_ready,
+          b_id, b_resp, b_user, b_valid, b_ready,
+          ar_id, ar_addr, ar_len, ar_size, ar_burst, ar_lock, ar_cache, ar_prot, ar_qos, ar_region, ar_user, ar_valid, ar_ready, ar_snoop, ar_bar, ar_domain,
+          r_id, r_data, r_resp, r_last, r_user, r_valid, r_ready, wack, rack
+  );
+
+  // pragma translate_off
+  `ifndef VERILATOR
+  // Single-Channel Assertions: Signals including valid must not change between valid and handshake.
+  // AW
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_id)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_addr)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_len)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_size)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_burst)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_lock)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_cache)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_prot)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_qos)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_region)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_atop)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> $stable(aw_user)));
+  assert property (@(posedge clk_i) (aw_valid && !aw_ready |=> aw_valid));
+  // W
+  assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_data)));
+  assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_strb)));
+  assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_last)));
+  assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> $stable(w_user)));
+  assert property (@(posedge clk_i) ( w_valid && ! w_ready |=> w_valid));
+  // B
+  assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> $stable(b_id)));
+  assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> $stable(b_resp)));
+  assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> $stable(b_user)));
+  assert property (@(posedge clk_i) ( b_valid && ! b_ready |=> b_valid));
+  // AR
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_id)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_addr)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_len)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_size)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_burst)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_lock)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_cache)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_prot)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_qos)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_region)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> $stable(ar_user)));
+  assert property (@(posedge clk_i) (ar_valid && !ar_ready |=> ar_valid));
+  // R
+  assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_id)));
+  assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_data)));
+  assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_resp)));
+  assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_last)));
+  assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> $stable(r_user)));
+  assert property (@(posedge clk_i) ( r_valid && ! r_ready |=> r_valid));
+  `endif
+  // pragma translate_on
+
+endinterface
diff --git a/src/ace/ace_pkg.sv b/src/ace/ace_pkg.sv
new file mode 100644
index 000000000..f27f526db
--- /dev/null
+++ b/src/ace/ace_pkg.sv
@@ -0,0 +1,124 @@
+// Copyright (c) 2014-2018 ETH Zurich, University of Bologna
+// Copyright (c) 2022 PlanV GmbH
+//
+// 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.
+
+
+//! ACE Package
+/// Contains all necessary type definitions, constants, and generally useful functions.
+package ace_pkg;
+
+  //////////////
+  // Typedefs //
+  //////////////
+
+  // Additional types for already existing AXI channels
+  typedef logic [3:0] arsnoop_t;
+  typedef logic [2:0] awsnoop_t;
+  typedef logic [1:0] axbar_t;
+  typedef logic [1:0] axdomain_t;
+  typedef logic [3:0] rresp_t;
+  typedef logic [0:0] awunique_t;
+
+  // Snoop related types
+  typedef logic [3:0] acsnoop_t;
+  typedef logic [2:0] acprot_t;
+
+  typedef struct packed {
+    logic WasUnique;
+    logic IsShared;
+    logic PassDirty;
+    logic Error;
+    logic DataTransfer;
+  } crresp_t;
+
+  typedef struct packed {
+    acsnoop_t snoop_trs;
+    logic accepts_dirty;
+    logic accepts_dirty_shared;
+    logic accepts_shared;
+  } snoop_info_t;
+
+  ///////////////
+  // Encodings //
+  ///////////////
+
+  // AxDOMAIN
+  localparam axdomain_t NonShareable   = 2'b00;
+  localparam axdomain_t InnerShareable = 2'b01;
+  localparam axdomain_t OuterShareable = 2'b10;
+  localparam axdomain_t System         = 2'b11;
+
+
+  // AxBAR
+  localparam axbar_t NormalAccessRespectingBarriers = 2'b00;
+  localparam axbar_t MemoryBarrier                  = 2'b01;
+  localparam axbar_t NormalAccessIgnoringBarriers   = 2'b10;
+  localparam axbar_t SynchronizationBarrier         = 2'b11;
+
+  // Uniquely defined here both for ARSNOOP and AWSNOOP
+  localparam int unsigned Barrier = 0;
+
+  // ARSNOOP
+  localparam arsnoop_t ReadNoSnoop        = 4'b0000;
+  localparam arsnoop_t ReadOnce           = 4'b0000;
+  localparam arsnoop_t ReadShared         = 4'b0001;
+  localparam arsnoop_t ReadClean          = 4'b0010;
+  localparam arsnoop_t ReadNotSharedDirty = 4'b0011;
+  localparam arsnoop_t ReadUnique         = 4'b0111;
+  localparam arsnoop_t CleanUnique        = 4'b1011;
+  localparam arsnoop_t MakeUnique         = 4'b1100;
+  localparam arsnoop_t CleanShared        = 4'b1000;
+  localparam arsnoop_t CleanInvalid       = 4'b1001;
+  localparam arsnoop_t MakeInvalid        = 4'b1101;
+  localparam arsnoop_t DVMComplete        = 4'b1110;
+  localparam arsnoop_t DVMMessage         = 4'b1111;
+  /* Barrier is already defined */
+
+  // AWSNOOP
+  localparam awsnoop_t WriteNoSnoop    = 3'b000;
+  localparam awsnoop_t WriteUnique     = 3'b000;
+  localparam awsnoop_t WriteLineUnique = 3'b001;
+  localparam awsnoop_t WriteClean      = 3'b010;
+  localparam awsnoop_t WriteBack       = 3'b011;
+  localparam awsnoop_t Evict           = 3'b100;
+  localparam awsnoop_t WriteEvict      = 3'b101;
+  /* Barrier is already defined */
+
+  // ACSNOOP
+  //
+  //  The encoding is shared with ARSNOOP transactions for the following cases:
+  //    - ReadOnce
+  //    - ReadShared
+  //    - ReadClean
+  //    - ReadNotSharedDirty
+  //    - ReadUnique
+  //    - CleanShared
+  //    - CleanInvalid
+  //    - MakeInvalid
+  //    - DVMComplete
+  //    - DVMMessage
+  //  Cast the parameters to acsnoop_t for consistency (but works anyway)
+
+  //////////////////
+  // Domain rules //
+  //////////////////
+
+  localparam int unsigned MaxMasterNum  = 16;
+  localparam int unsigned MasterIdxBits = $clog2(MaxMasterNum);
+
+  typedef struct packed {
+        logic [MasterIdxBits-1:0] InnerShareableNum;
+        logic [MaxMasterNum-1:0][MasterIdxBits-1:0] InnerShareableList;
+        logic [MasterIdxBits-1:0] OuterShareableNum;
+        logic [MaxMasterNum-1:0][MasterIdxBits-1:0] OuterShareableList;
+  } domain_rule_t;
+
+endpackage
diff --git a/src/ace/snoop_intf.sv b/src/ace/snoop_intf.sv
new file mode 100644
index 000000000..faeb22d98
--- /dev/null
+++ b/src/ace/snoop_intf.sv
@@ -0,0 +1,122 @@
+// Copyright (c) 2014-2018 ETH Zurich, University of Bologna
+// Copyright (c) 2022 PlanV GmbH
+//
+// 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.
+
+
+// Snoop bus interafces
+interface SNOOP_BUS #(
+  parameter int unsigned SNOOP_ADDR_WIDTH = 0,
+  parameter int unsigned SNOOP_DATA_WIDTH = 0
+);
+
+  typedef logic [SNOOP_ADDR_WIDTH-1:0] addr_t;
+  typedef logic [SNOOP_DATA_WIDTH-1:0] data_t;
+
+  addr_t                ac_addr;
+  ace_pkg::acprot_t   ac_prot;
+  ace_pkg::acsnoop_t  ac_snoop;
+  logic                 ac_valid;
+  logic                 ac_ready;
+
+  ace_pkg::crresp_t     cr_resp;
+  logic                 cr_valid;
+  logic                 cr_ready;
+
+  data_t                cd_data;
+  logic                 cd_last;
+  logic                 cd_valid;
+  logic                 cd_ready;
+
+  modport Master (
+    input   ac_addr, ac_prot, ac_snoop, ac_valid, output ac_ready,
+    input   cr_ready, output cr_valid, cr_resp,
+    input   cd_ready, output cd_data, cd_last, cd_valid
+  );
+
+ modport Slave (
+    output   ac_addr, ac_prot, ac_snoop, ac_valid, input ac_ready,
+    output   cr_ready, input cr_valid, cr_resp,
+    output   cd_ready, input cd_data, cd_last, cd_valid
+  );
+
+
+  modport Monitor (
+    input    ac_addr, ac_prot, ac_snoop, ac_valid, ac_ready,
+             cr_ready, cr_valid, cr_resp,
+             cd_ready, cd_data, cd_last, cd_valid
+  );
+
+endinterface
+
+/// A clocked SNOOP interface for use in design verification.
+interface SNOOP_BUS_DV #(
+  parameter int unsigned SNOOP_ADDR_WIDTH = 0,
+  parameter int unsigned SNOOP_DATA_WIDTH = 0
+)(
+  input clk_i
+);
+
+  typedef logic [SNOOP_ADDR_WIDTH-1:0] addr_t;
+  typedef logic [SNOOP_DATA_WIDTH-1:0] data_t;
+
+  addr_t                ac_addr;
+  ace_pkg::acprot_t   ac_prot;
+  ace_pkg::acsnoop_t  ac_snoop;
+  logic                 ac_valid;
+  logic                 ac_ready;
+
+  ace_pkg::crresp_t     cr_resp;
+  logic                 cr_valid;
+  logic                 cr_ready;
+
+  data_t                cd_data;
+  logic                 cd_last;
+  logic                 cd_valid;
+  logic                 cd_ready;
+
+  modport Master (
+    input   ac_addr, ac_prot, ac_snoop, ac_valid, output ac_ready,
+    input   cr_ready, output cr_valid, cr_resp,
+    input   cd_ready, output cd_data, cd_last, cd_valid
+  );
+
+ modport Slave (
+    output   ac_addr, ac_prot, ac_snoop, ac_valid, input ac_ready,
+    output   cr_ready, input cr_valid, cr_resp,
+    output   cd_ready, input cd_data, cd_last, cd_valid
+  );
+
+
+  modport Monitor (
+    input    ac_addr, ac_prot, ac_snoop, ac_valid, ac_ready,
+             cr_ready, cr_valid, cr_resp,
+             cd_ready, cd_data, cd_last, cd_valid
+  );
+
+  // pragma translate_off
+  `ifndef VERILATOR
+  // Single-Channel Assertions: Signals including valid must not change between valid and handshake.
+  // AC
+  assert property (@(posedge clk_i) (ac_valid && !ac_ready |=> $stable(ac_addr)));
+  assert property (@(posedge clk_i) (ac_valid && !ac_ready |=> $stable(ac_snoop)));
+  assert property (@(posedge clk_i) (ac_valid && !ac_ready |=> $stable(ac_prot)));
+  assert property (@(posedge clk_i) (ac_valid && !ac_ready |=> ac_valid));
+  // CR
+  assert property (@(posedge clk_i) (cr_valid && !cr_ready |=> $stable(cr_resp)));
+  assert property (@(posedge clk_i) (cr_valid && !cr_ready |=> cr_valid));
+  // CD
+  assert property (@(posedge clk_i) (cd_valid && !cd_ready |=> $stable(cd_data)));
+  assert property (@(posedge clk_i) (cd_valid && !cd_ready |=> $stable(cd_last)));
+  assert property (@(posedge clk_i) (cd_valid && !cd_ready |=> cd_valid));
+  `endif
+  // pragma translate_on
+
+endinterface