Vendorize pulp OBI specs at tag v0.1.3

This commit is contained in:
Yannick Casamatta 2024-07-23 11:19:13 +02:00 committed by JeanRochCoulon
parent 81426a4b85
commit 4a6b458011
25 changed files with 4319 additions and 0 deletions

1
vendor/pulp-platform/obi/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.bender

38
vendor/pulp-platform/obi/Bender.yml vendored Normal file
View file

@ -0,0 +1,38 @@
# Copyright 2023 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
package:
name: obi
authors:
- "Michael Rogenmoser <michaero@iis.ee.ethz.ch>"
dependencies:
common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.37.0 }
common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 }
export_include_dirs:
- include
sources:
# Level 1
- src/obi_pkg.sv
# Level 2
- src/obi_intf.sv
- src/obi_rready_converter.sv
# Level 3
- src/obi_atop_resolver.sv
- src/obi_demux.sv
- src/obi_err_sbr.sv
- src/obi_mux.sv
- src/obi_sram_shim.sv
# Level 4
- src/obi_xbar.sv
- target: test
files:
- src/test/obi_asserter.sv
- src/test/obi_test.sv
- src/test/obi_sim_mem.sv
- src/test/tb_obi_xbar.sv
- src/test/atop_golden_mem_pkg.sv
- src/test/tb_obi_atop_resolver.sv

34
vendor/pulp-platform/obi/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,34 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## Unreleased
## 0.1.3 - 2024-07-18
### Added
- Add `obi_rready_converter` to convert between manager requiring rready to subordinate not supporting it.
## 0.1.2 - 2024-03-11
### Added
- Add assertion module to check protocol constraints.
- Add additional typedefs.
## 0.1.1 - 2023-08-08
### Fixed
- `obi_mux`: Move `if` outside `always_comb` to enforce `generate` and remove compile warning.
## 0.1.0 - 2023-07-24
Initial release
### Added
- `obi_mux.sv`: A multiplexer IP for the OBI protocol.
- `obi_demux.sv`: A demultiplexer IP for the OBI protocol.
- `obi_xbar.sv`: A crossbar interconnect IP for the OBI protocol.
- `obi_err_sbr.sv`: A error subordinate, responding with the error bit set.
- `obi_sram_shim.sv`: An adapter for a standard sram.
- `obi_atop_resolver.sv`: An atomics filter, resolving atomic operations on an exclusive bus.
- Various support infrastructure for types and configurations in `obi_pkg.sv`, `obi_typedef.svh`, and `obi_assign.svh`.
- Initial testing infrastructure, testing `obi_xbar` and `obi_atop_resolver`.

176
vendor/pulp-platform/obi/LICENSE vendored Normal file
View file

@ -0,0 +1,176 @@
SOLDERPAD HARDWARE LICENSE version 0.51
This license is based closely on the Apache License Version 2.0, but is not
approved or endorsed by the Apache Foundation. A copy of the non-modified
Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
As this license is not currently OSI or FSF approved, the Licensor permits any
Work licensed under this License, at the option of the Licensee, to be treated
as licensed under the Apache License Version 2.0 (which is so approved).
This License is licensed under the terms of this License and in particular
clause 7 below (Disclaimer of Warranties) applies in relation to its use.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the Rights owner or entity authorized by the Rights owner
that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Rights" means copyright and any similar right including design right (whether
registered or unregistered), semiconductor topography (mask) rights and
database rights (but excluding Patents and Trademarks).
"Source" form shall mean the preferred form for making modifications, including
but not limited to source code, net lists, board layouts, CAD files,
documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, the instantiation of a hardware design and
conversions to other media types, including intermediate forms such as
bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask
works).
"Work" shall mean the work of authorship, whether in Source form or other
Object form, made available under the License, as indicated by a Rights notice
that is included in or attached to the work (an example is provided in the
Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) or physically connect to or interoperate with the interfaces of, the Work
and Derivative Works thereof.
"Contribution" shall mean any design or work of authorship, including the
original version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the Rights owner or by an individual or Legal Entity
authorized to submit on behalf of the Rights owner. For the purposes of this
definition, "submitted" means any form of electronic, verbal, or written
communication sent to the Licensor or its representatives, including but not
limited to communication on electronic mailing lists, source code control
systems, and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but excluding
communication that is conspicuously marked or otherwise designated in writing
by the Rights owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of License. Subject to the terms and conditions of this License, each
Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable license under the Rights to reproduce,
prepare Derivative Works of, publicly display, publicly perform, sublicense,
and distribute the Work and such Derivative Works in Source or Object form and
do anything in relation to the Work as if the Rights did not exist.
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those
patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with the
Work to which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent
licenses granted to You under this License for that Work shall terminate as of
the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and in
Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy
of this License; and
You must cause any modified files to carry prominent notices stating that
You changed the files; and
You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from
the Source form of the Work, excluding those notices that do not pertain to
any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then
any Derivative Works that You distribute must include a readable copy of
the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at
least one of the following places: within a NOTICE text file distributed as
part of the Derivative Works; within the Source form or documentation, if
provided along with the Derivative Works; or, within a display generated by
the Derivative Works, if and wherever such third-party notices normally
appear. The contents of the NOTICE file are for informational purposes only
and do not modify the License. You may add Your own attribution notices
within Derivative Works that You distribute, alongside or as an addendum to
the NOTICE text from the Work, provided that such additional attribution
notices cannot be construed as modifying the License. You may add Your own
copyright statement to Your modifications and may provide additional or
different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work
otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without any
additional terms or conditions. Notwithstanding the above, nothing herein shall
supersede or modify the terms of any separate license agreement you may have
executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to in
writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability to
use the Work (including but not limited to damages for loss of goodwill, work
stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the
possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or
Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree
to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

35
vendor/pulp-platform/obi/Makefile vendored Normal file
View file

@ -0,0 +1,35 @@
# Copyright 2023 ETH Zurich and University of Bologna.
# Solderpad Hardware License, Version 0.51, see LICENSE for details.
# SPDX-License-Identifier: SHL-0.51
BENDER ?= bender
VSIM ?= vsim
AVAILABLE_TESTBENCHES = tb_obi_xbar tb_obi_atop_resolver
scripts/compile.tcl:
mkdir -p scripts
$(BENDER) script vsim -t test --vlog-arg="-svinputport=compat" > $@
.PHONY: build
build: scripts/compile.tcl
$(VSIM) -c -do 'exit -code [source scripts/compile.tcl]'
.PHONY: $(AVAILABLE_TESTBENCHES)
$(AVAILABLE_TESTBENCHES): build
ifdef gui
$(VSIM) $@ -voptargs="+acc"
else
$(VSIM) -c $@ -do "run -all; quit -f"
endif
.PHONY: all
all: $(AVAILABLE_TESTBENCHES)
.PHONY: clean
clean:
rm -f scripts/compile.tcl
rm -rf work
rm -f modelsim.ini
rm -f transcript
rm -f vsim.wlf

25
vendor/pulp-platform/obi/Readme.md vendored Normal file
View file

@ -0,0 +1,25 @@
# OBI
The repository contains a collection of SystemVerilog IPs for the [OBI v1.6 standard](https://github.com/openhwgroup/obi/blob/072d9173c1f2d79471d6f2a10eae59ee387d4c6f/OBI-v1.6.0.pdf).
They are designed by PULP-platform and are available under the Solderpad v0.51 license (See [`LICENSE`](LICENSE)).
## Using the IPs
As the OBI protocol is very configurable, the IPs are designed to incorporate specific parameters for the design:
- `ObiCfg`: This specifies the configuration used for the OBI protocol being input or output from the link. A default config can be found in the `obi_pkg.sv`. This config should be aligned with the `req` and `rsp` structs.
- `obi_req_t`: The OBI request struct is designed to be generated with a macro available in the `include/obi/typedef.svh` include file and has fields for the handshake and a field for the *A* channel.
- `obi_rsp_t`: The OBI response struct is designed to be generated with a macro available in the `include/obi/typedef.svh` include file and has fields for the handshake and a filed for the *R* channel.
Most IPs will also support a SystemVerilog `interface` variant, also based on `ObiCfg`.
## Available IPs
- `obi_mux.sv`: A multiplexer IP for the OBI protocol.
- `obi_demux.sv`: A demultiplexer IP for the OBI protocol.
- `obi_xbar.sv`: A crossbar interconnect IP for the OBI protocol.
- `obi_err_sbr.sv`: A error subordinate, responding with the error bit set.
- `obi_sram_shim.sv`: An adapter for a standard sram.
- `obi_atop_resolver.sv`: An atomics filter, resolving atomic operations on an exclusive bus.
## License
Solderpad Hardware License, Version 0.51

View file

@ -0,0 +1,257 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
`ifndef OBI_ASSIGN_SVH
`define OBI_ASSIGN_SVH
////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal implementation for assigning one ONE 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 __OBI_TO_A(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``addr = __rhs``__rhs_sep``addr; \
__opt_as __lhs``__lhs_sep``we = __rhs``__rhs_sep``we; \
__opt_as __lhs``__lhs_sep``be = __rhs``__rhs_sep``be; \
__opt_as __lhs``__lhs_sep``wdata = __rhs``__rhs_sep``wdata; \
__opt_as __lhs``__lhs_sep``aid = __rhs``__rhs_sep``aid; \
__opt_as __lhs``__lhs_sep``a_optional = __rhs``__rhs_sep``a_optional;
`define __OBI_TO_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs``__lhs_sep``rdata = __rhs``__rhs_sep``rdata; \
__opt_as __lhs``__lhs_sep``rid = __rhs``__rhs_sep``rid; \
__opt_as __lhs``__lhs_sep``err = __rhs``__rhs_sep``err; \
__opt_as __lhs``__lhs_sep``r_optional = __rhs``__rhs_sep``r_optional;
`define __OBI_TO_REQ(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep, __lhscfg, __rhscfg) \
`__OBI_TO_A(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs.req = __rhs.req; \
if (__lhscfg.UseRReady) begin \
if (__rhscfg.UseRReady) begin \
__opt_as __lhs.rready = __rhs.rready; \
if (__lhscfg.Integrity) begin \
if (__rhscfg.Integrity) begin \
__opt_as __lhs.rreadypar = __rhs.rreadypar; \
end else begin \
__opt_as __lhs.rreadypar = ~__rhs.rready; \
end \
end \
end else begin \
__opt_as __lhs.rready = 1'b1; \
if (__lhscfg.Integrity) begin \
__opt_as __lhs.rreadypar = 1'b0; \
end \
end \
end else if (__rhscfg.UseRReady) begin \
$error("Incompatible Configs! Please assign manually!"); \
end \
if (__lhscfg.Integrity) begin \
if (__rhscfg.Integrity) begin \
__opt_as __lhs.reqpar = __rhs.reqpar; \
end else begin \
__opt_as __lhs.reqpar = ~__rhs.req; \
end \
end
`define __OBI_TO_RSP(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep, __lhscfg, __rhscfg) \
`__OBI_TO_R(__opt_as, __lhs, __lhs_sep, __rhs, __rhs_sep) \
__opt_as __lhs.gnt = __rhs.gnt; \
__opt_as __lhs.rvalid = __rhs.rvalid; \
if (__lhscfg.Integrity) begin \
if (__rhscfg.Integrity) begin \
__opt_as __lhs.gntpar = __rhs.gntpar; \
__opt_as __lhs.rvalidpar = __rhs.rvalidpar; \
end else begin \
__opt_as __lhs.gntpar = ~__rhs.gnt; \
__opt_as __lhs.rvalidpar = ~__rhs.rvalid; \
end \
end
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning one OBI interface to another, as if you would do `assign sbr = mgr;`
//
// The channel assignments `OBI_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 `OBI_ASSIGN(dst, src)` assigns all channels including handshakes as if
// `src` was the manager of `dst`.
//
// Usage Example:
// `OBI_ASSIGN(sbr, mgr)
// `OBI_ASSIGN_A(dst, src)
// `OBI_ASSIGN_R(dst, src)
`define OBI_ASSIGN_A(dst, src, dstcfg, srccfg) \
`__OBI_TO_A(assign, dst, ., src, .) \
assign dst.req = src.req; \
assign src.gnt = dst.gnt; \
if (dstcfg.Integrity && srccfg.Integrity) begin \
assign dst.reqpar = src.reqpar; \
assign src.gntpar = dst.gntpar; \
end else if (dstcfg.Integrity ^ srccfg.Integrity) begin \
$error("Incompatible Configs! Please assign manually!"); \
end
`define OBI_ASSIGN_R(dst, src, dstcfg, srccfg) \
`__OBI_TO_R(assign, dst, ., src, .) \
assign dst.rvalid = src.rvalid; \
if (dstcfg.Integrity && srccfg.Integrity) begin \
assign dst.rvalidpar = src.rvalidpar; \
end else if (dstcfg.Integrity ^ srccfg.Integrity) begin \
$error("Incompatible Configs! Please assign manually!"); \
end \
if (srccfg.UseRReady) begin \
if (dstcfg.UseRReady) begin \
assign src.rready = dst.rready; \
if (srccfg.Integrity && dstcfg.Integrity) begin \
assign src.rreadypar = dst.rreadypar; \
end \
end else begin \
assign src.rready = 1'b1; \
if (srccfg.Integrity) begin \
assign src.rreadypar = 1'b0; \
end \
end \
end else if (dstcfg.UseRReady) begin \
$error("Incompatible Configs! Please assign manually!"); \
end
`define OBI_ASSIGN(sbr, mgr, sbrcfg, mgrcfg) \
`OBI_ASSIGN_A(sbr, mgr, sbrcfg, mgrcfg) \
`OBI_ASSIGN_R(mgr, sbr, mgrcfg, sbrcfg)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting an interface from channel or request/response structs inside a process.
//
// The channel macros `OBI_SET_FROM_XX(obi_if, xx_struct)` set the payload signals of the `obi_if`
// interface from the signals in `xx_struct`. They do not set the handshake signals.
// The request macro `OBI_SET_FROM_REQ(obi_if, req_struct)` sets the request channel and the
// request-side handshake signals of the `obi_if` interface from the signals in `req_struct`.
// The response macro `OBI_SET_FROM_RESP(obi_if, rsp_struct)` sets the response channel and the
// response-side handshake signals of the `obi_if` interface from the signals in `resp_struct`.
//
// Usage Example:
// always_comb begin
// `OBI_SET_FROM_REQ(my_if, my_req_struct)
// end
`define OBI_SET_FROM_A(obi_if, a_struct) `__OBI_TO_A(, obi_if, ., a_struct, .)
`define OBI_SET_FROM_R(obi_if, r_struct) `__OBI_TO_R(, obi_if, ., r_struct, .)
`define OBI_SET_FROM_REQ(obi_if, req_struct, cfg) `__OBI_TO_REQ(, obi_if, ., req_struct, .a., cfg, cfg)
`define OBI_SET_FROM_RSP(obi_if, rsp_struct, cfg) `__OBI_TO_RSP(, obi_if, ., rsp_struct, .r., cfg, cfg)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning an interface from channel or request/response structs outside a process.
//
// The channel macros `OBI_ASSIGN_FROM_XX(obi_if, xx_struct)` assign the payload signals of the
// `obi_if` interface from the signals in `xx_struct`. They do not assign the handshake signals.
// The request macro `OBI_ASSIGN_FROM_REQ(obi_if, req_struct)` assigns the request channel and the
// request-side handshake signals of the `obi_if` interface from the signals in `req_struct`.
// The response macro `OBI_ASSIGN_FROM_RSP(obi_if, rsp_struct)` assigns the response channel and
// the response-side handshake signals of the `obi_if` interface from the signals in `rsp_struct`.
//
// Usage Example:
// `AXI_ASSIGN_FROM_REQ(my_if, my_req_struct)
`define OBI_ASSIGN_FROM_A(obi_if, a_struct) `__OBI_TO_A(assign, obi_if, ., a_struct, .)
`define OBI_ASSIGN_FROM_R(obi_if, r_struct) `__OBI_TO_R(assign, obi_if, ., r_struct, .)
`define OBI_ASSIGN_FROM_REQ(obi_if, req_struct, cfg) `__OBI_TO_REQ(assign, obi_if, ., req_struct, .a., cfg, cfg)
`define OBI_ASSIGN_FROM_RSP(obi_if, rsp_struct, cfg) `__OBI_TO_RSP(assign, obi_if, ., rsp_struct, .r., cfg, cfg)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting channel or request/response structs from an interface inside a process.
//
// The channel macros `OBI_SET_TO_XX(xx_struct, obi_if)` set the signals of `xx_struct` to the
// payload signals of that channel in the `obi_if` interface. They do not set the handshake
// signals.
// The request macro `OBI_SET_TO_REQ(obi_if, req_struct)` sets all signals of `req_struct` (i.e.,
// request channel payload and request-side handshake signals) to the signals in the `obi_if`
// interface.
// The response macro `OBI_SET_TO_RSP(obi_if, rsp_struct)` sets all signals of `rsp_struct`
// (i.e., response channel payload and response-side handshake signals) to the signals in the
// `obi_if` interface.
//
// Usage Example:
// always_comb begin
// `OBI_SET_TO_REQ(my_req_struct, my_if)
// end
`define OBI_SET_TO_A(a_struct, obi_if) `__OBI_TO_A(, a_struct, ., obi_if, .)
`define OBI_SET_TO_R(r_struct, obi_if) `__OBI_TO_R(, r_struct, ., obi_if, .)
`define OBI_SET_TO_REQ(req_struct, obi_if, cfg) `__OBI_TO_REQ(, req_struct, .a., obi_if, ., cfg, cfg)
`define OBI_SET_TO_RSP(rsp_struct, obi_if, cfg) `__OBI_TO_RSP(, rsp_struct, .r., obi_if, ., cfg, cfg)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from an interface outside a process.
//
// The channel macros `OBI_ASSIGN_TO_XX(xx_struct, obi_if)` assign the signals of `xx_struct` to the
// payload signals of that channel in the `obi_if` interface. They do not assign the handshake
// signals.
// The request macro `OBI_ASSIGN_TO_REQ(obi_if, req_struct)` assigns all signals of `req_struct`
// (i.e., request channel payload and request-side handshake signals) to the signals in the `obi_if`
// interface.
// The response macro `OBI_ASSIGN_TO_RSP(obi_if, rsp_struct)` assigns all signals of `rsp_struct`
// (i.e., response channel payload and response-side handshake signals) to the signals in the
// `obi_if` interface.
//
// Usage Example:
// `OBI_ASSIGN_TO_REQ(my_req_struct, my_if)
`define OBI_ASSIGN_TO_A(a_struct, obi_if) `__OBI_TO_A(assign, a_struct, ., obi_if, .)
`define OBI_ASSIGN_TO_R(r_struct, obi_if) `__OBI_TO_R(assign, r_struct, ., obi_if, .)
`define OBI_ASSIGN_TO_REQ(req_struct, obi_if, cfg) `__OBI_TO_REQ(assign, req_struct, .a., obi_if, ., cfg, cfg)
`define OBI_ASSIGN_TO_RSP(rsp_struct, obi_if, cfg) `__OBI_TO_RSP(assign, rsp_struct, .r., obi_if, ., cfg, cfg)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Setting channel or request/response structs from another struct inside a process.
//
// The channel macros `OBI_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 `OBI_SET_REQ_STRUCT(lhs, rhs)` sets all fields of the `lhs` request struct to
// the fields of the `rhs` request struct. This includes the request channel payload and
// request-side handshake signals.
// The response macro `OBI_SET_RSP_STRUCT(lhs, rhs)` sets all fields of the `lhs` response struct
// to the fields of the `rhs` response struct. This includes the response channel payload
// and response-side handshake signals.
//
// Usage Example:
// always_comb begin
// `OBI_SET_REQ_STRUCT(my_req_struct, another_req_struct)
// end
`define OBI_SET_A_STRUCT(lhs, rhs) `__OBI_TO_A(, lhs, ., rhs, .)
`define OBI_SET_R_STRUCT(lhs, rhs) `__OBI_TO_R(, lhs, ., rhs, .)
`define OBI_SET_REQ_STRUCT(lhs, rhs) `__OBI_TO_REQ(, lhs, .a., rhs, .a.)
`define OBI_SET_RSP_STRUCT(lhs, rhs) `__OBI_TO_RSP(, lhs, .r., rhs, .r.)
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Assigning channel or request/response structs from another struct outside a process.
//
// The channel macros `OBI_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 `OBI_ASSIGN_REQ_STRUCT(lhs, rhs)` assigns all fields of the `lhs` request
// struct to the fields of the `rhs` request struct. This includes the request channel payload and
// request-side handshake signals.
// The response macro `OBI_ASSIGN_RSP_STRUCT(lhs, rhs)` assigns all fields of the `lhs` response
// struct to the fields of the `rhs` response struct. This includes the response channel payload
// and response-side handshake signals.
//
// Usage Example:
// `OBI_ASSIGN_REQ_STRUCT(my_req_struct, another_req_struct)
`define OBI_ASSIGN_A_STRUCT(lhs, rhs) `__OBI_TO_A(assign, lhs, ., rhs, .)
`define OBI_ASSIGN_R_STRUCT(lhs, rhs) `__OBI_TO_R(assign, lhs, ., rhs, .)
`define OBI_ASSIGN_REQ_STRUCT(lhs, rhs) `__OBI_TO_REQ(assign, lhs, .a., rhs, .a.)
`define OBI_ASSIGN_RSP_STRUCT(lhs, rhs) `__OBI_TO_RSP(assign, lhs, .r., rhs, .r.)
////////////////////////////////////////////////////////////////////////////////////////////////////
`endif // OBI_ASSIGN_SVH

View file

@ -0,0 +1,122 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
`ifndef OBI_TYPEDEF_SVH
`define OBI_TYPEDEF_SVH
`define OBI_TYPEDEF_A_CHAN_T(a_chan_t, ADDR_WIDTH, DATA_WIDTH, ID_WIDTH, a_optional_t) \
typedef struct packed { \
logic [ ADDR_WIDTH-1:0] addr; \
logic we; \
logic [DATA_WIDTH/8-1:0] be; \
logic [ DATA_WIDTH-1:0] wdata; \
logic [ ID_WIDTH-1:0] aid; \
a_optional_t a_optional; \
} a_chan_t;
`define OBI_TYPEDEF_TYPE_A_CHAN_T(a_chan_t, addr_t, data_t, strb_t, id_t, a_optional_t) \
typedef struct packed { \
addr_t addr; \
logic we; \
strb_t be; \
data_t wdata; \
id_t aid; \
a_optional_t a_optional; \
} a_chan_t;
`define OBI_TYPEDEF_MINIMAL_A_OPTIONAL(a_optional_t) \
typedef logic a_optional_t;
`define OBI_TYPEDEF_ATOP_A_OPTIONAL(a_optional_t) \
typedef struct packed { \
obi_pkg::atop_t atop; \
} a_optional_t;
`define OBI_TYPEDEF_ALL_A_OPTIONAL(a_optional_t, AUSER_WIDTH, WUSER_WIDTH, MID_WIDTH, ACHK_WIDTH) \
typedef struct packed { \
logic [ AUSER_WIDTH-1:0] auser; \
logic [ WUSER_WIDTH-1:0] wuser; \
obi_pkg::atop_t atop; \
obi_pkg::memtype_t memtype; \
logic [ MID_WIDTH-1:0] mid; \
obi_pkg::prot_t prot; \
logic dbg; \
logic [ ACHK_WIDTH-1:0] achk; \
} a_optional_t;
`define OBI_TYPEDEF_R_CHAN_T(r_chan_t, RDATA_WIDTH, ID_WIDTH, r_optional_t) \
typedef struct packed { \
logic [RDATA_WIDTH-1:0] rdata; \
logic [ ID_WIDTH-1:0] rid; \
logic err; \
r_optional_t r_optional; \
} r_chan_t;
`define OBI_TYPEDEF_TYPE_R_CHAN_T(r_chan_t, data_t, id_t, r_optional_t) \
typedef struct packed { \
data_t rdata; \
id_t rid; \
logic err; \
r_optional_t r_optional; \
} r_chan_t;
`define OBI_TYPEDEF_MINIMAL_R_OPTIONAL(r_optional_t) \
typedef logic r_optional_t;
`define OBI_TYPEDEF_ALL_R_OPTIONAL(r_optional_t, RUSER_WIDTH, RCHK_WIDTH) \
typedef struct packed { \
logic [RUSER_WIDTH-1:0] ruser; \
logic exokay; \
logic [ RCHK_WIDTH-1:0] rchk; \
} r_optional_t;
`define OBI_TYPEDEF_DEFAULT_REQ_T(req_t, a_chan_t) \
typedef struct packed { \
a_chan_t a; \
logic req; \
} req_t;
`define OBI_TYPEDEF_REQ_T(req_t, a_chan_t) \
typedef struct packed { \
a_chan_t a; \
logic req; \
logic rready; \
} req_t;
`define OBI_TYPEDEF_RSP_T(rsp_t, r_chan_t) \
typedef struct packed { \
r_chan_t r; \
logic gnt; \
logic rvalid; \
} rsp_t;
`define OBI_TYPEDEF_INTEGRITY_REQ_T(req_t, a_chan_t) \
typedef struct packed { \
a_chan_t a; \
logic req; \
logic rready; \
logic reqpar; \
logic rreadypar; \
} req_t;
`define OBI_TYPEDEF_INTEGRITY_RSP_T(rsp_t, r_chan_t) \
typedef struct packed { \
r_chan_t r; \
logic gnt; \
logic gntpar; \
logic rvalid; \
logic rvalidpar; \
} rsp_t;
`define OBI_TYPEDEF_ALL(obi_t, cfg) \
`OBI_TYPEDEF_ALL_A_OPTIONAL(obi_t``_a_optional_t, cfg.OptionalCfg.AUserWidth, cfg.OptionalCfg.WUserWidth, cfg.OptionalCfg.MidWidth, cfg.OptionalCfg.AChkWidth) \
`OBI_TYPEDEF_A_CHAN_T(obi_t``_a_chan_t, cfg.AddrWidth, cfg.DataWidth, cfg.IdWidth, obi_t``_a_optional_t) \
`OBI_TYPEDEF_INTEGRITY_REQ_T(obi_t``_req_t, obi_t``_a_chan_t) \
`OBI_TYPEDEF_ALL_R_OPTIONAL(obi_t``_r_optional_t, cfg.OptionalCfg.RUserWidth, cfg.OptionalCfg.RChkWidth) \
`OBI_TYPEDEF_R_CHAN_T(obi_t``_r_chan_t, cfg.DataWidth, cfg.IdWidth, obi_t``_r_optional_t) \
`OBI_TYPEDEF_INTEGRITY_RSP_T(obi_t``_rsp_t, obi_t``_r_chan_t)
`endif // OBI_TYPEDEF_SVH

View file

@ -0,0 +1,509 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Samuel Riedel <sriedel@iis.ee.ethz.ch>
// Author: Michael Rogenmoser <michaero@iis.ee.ethz.ch>
`include "common_cells/registers.svh"
/// Handles atomics. Hence, it needs to be instantiated in front of a memory region over which the
/// bus has exclusive access.
module obi_atop_resolver import obi_pkg::*; #(
/// The configuration of the subordinate ports (input ports).
parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig,
/// The configuration of the manager port (output port).
parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg,
/// The request struct for the subordinate port (input ports).
parameter type sbr_port_obi_req_t = logic,
/// The response struct for the subordinate port (input ports).
parameter type sbr_port_obi_rsp_t = logic,
/// The request struct for the manager port (output port).
parameter type mgr_port_obi_req_t = sbr_port_obi_req_t,
/// The response struct for the manager ports (output ports).
parameter type mgr_port_obi_rsp_t = sbr_port_obi_rsp_t,
///
parameter type mgr_port_obi_a_optional_t = logic,
parameter type mgr_port_obi_r_optional_t = logic,
/// Enable LR & SC AMOS
parameter bit LrScEnable = 1,
/// Cut path between request and response at the cost of increased AMO latency
parameter bit RegisterAmo = 1'b0
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
input sbr_port_obi_req_t sbr_port_req_i,
output sbr_port_obi_rsp_t sbr_port_rsp_o,
output mgr_port_obi_req_t mgr_port_req_o,
input mgr_port_obi_rsp_t mgr_port_rsp_i
);
if (!SbrPortObiCfg.OptionalCfg.UseAtop) $fatal(1, "Atomics require atop to be enabled");
if (MgrPortObiCfg.OptionalCfg.UseAtop)
$fatal(1, "Filter requires atop to be disabled on manager port");
if (SbrPortObiCfg.Integrity || MgrPortObiCfg.Integrity) $error("Integrity not supported");
logic meta_valid, meta_ready;
logic rdata_valid, rdata_ready;
// read signal before register
logic [SbrPortObiCfg.DataWidth-1:0] out_rdata;
logic pop_resp;
logic last_amo_wb;
typedef enum logic [1:0] {
Idle, DoAMO, WriteBackAMO
} amo_state_e;
amo_state_e state_q, state_d;
logic load_amo;
obi_atop_e amo_op_q;
logic amo_wb;
logic [SbrPortObiCfg.DataWidth/8-1:0] be_expand;
logic [SbrPortObiCfg.AddrWidth-1:0] addr_q;
logic [SbrPortObiCfg.IdWidth-1:0] aid_q;
logic [31:0] amo_operand_a;
logic [31:0] amo_operand_a_q;
logic [31:0] amo_operand_b_q;
logic [31:0] amo_result, amo_result_q;
// Store the metadata at handshake
spill_register #(
.T (logic [SbrPortObiCfg.IdWidth-1:0]),
.Bypass(1'b0 )
) i_metadata_register (
.clk_i,
.rst_ni,
.valid_i ( sbr_port_req_i.req && sbr_port_rsp_o.gnt ),
.ready_o ( meta_ready ),
.data_i ( sbr_port_req_i.a.aid ),
.valid_o ( meta_valid ),
.ready_i ( pop_resp ),
.data_o ( sbr_port_rsp_o.r.rid )
);
// Store response if it's not accepted immediately
logic rdata_full, rdata_empty;
logic rdata_usage;
assign rdata_ready = !rdata_usage && !rdata_full;
assign rdata_valid = !rdata_empty;
logic sc_successful_or_lr_d, sc_successful_or_lr_q;
logic sc_q;
typedef struct packed {
logic [SbrPortObiCfg.DataWidth-1:0] data;
logic err;
logic exokay;
mgr_port_obi_r_optional_t optional;
} out_buffer_t;
out_buffer_t out_buf_fifo_in, out_buf_fifo_out;
assign out_buf_fifo_in = '{
data: out_rdata,
err: mgr_port_rsp_i.r.err,
exokay: sc_successful_or_lr_q,
optional: mgr_port_rsp_i.r.r_optional
};
assign sbr_port_rsp_o.r.rdata = out_buf_fifo_out.data;
assign sbr_port_rsp_o.r.err = out_buf_fifo_out.err;
assign sbr_port_rsp_o.r.r_optional.exokay = out_buf_fifo_out.exokay;
if (SbrPortObiCfg.OptionalCfg.RUserWidth) begin : gen_ruser
if (MgrPortObiCfg.OptionalCfg.RUserWidth) begin : gen_ruser_assign
always_comb begin
sbr_port_rsp_o.r.r_optional.ruser = '0;
sbr_port_rsp_o.r.r_optional.ruser = out_buf_fifo_in.optional.ruser;
end
end else begin : gen_no_ruser
assign sbr_port_rsp_o.r.r_optional.ruser = '0;
end
end
fifo_v3 #(
.FALL_THROUGH (1'b1 ),
.dtype (out_buffer_t),
.DEPTH (2 )
) i_rdata_fifo (
.clk_i,
.rst_ni,
.testmode_i,
.flush_i (1'b0 ),
.full_o (rdata_full ),
.empty_o (rdata_empty ),
.usage_o (rdata_usage ),
.data_i (out_buf_fifo_in ),
.push_i (~last_amo_wb & mgr_port_rsp_i.rvalid),
.data_o (out_buf_fifo_out ),
.pop_i (pop_resp & ~rdata_empty)
);
// In case of a SC we must forward SC result from the cycle earlier.
assign out_rdata = (sc_q && LrScEnable) ? $unsigned(!sc_successful_or_lr_q) :
mgr_port_rsp_i.r.rdata;
// Ready to output data if both meta and read data
// are available (the read data will always be last)
assign sbr_port_rsp_o.rvalid = meta_valid & rdata_valid;
// Only pop the data from the registers once both registers are ready
if (SbrPortObiCfg.UseRReady) begin : gen_pop_rready
assign pop_resp = sbr_port_rsp_o.rvalid & sbr_port_req_i.rready;
end else begin : gen_pop_norready
assign pop_resp = sbr_port_rsp_o.rvalid;
end
// Buffer amo_wb signal to ensure wb rdata is not used
`FFL(last_amo_wb, amo_wb, mgr_port_req_o.req, 1'b0, clk_i, rst_ni);
// ----------------
// LR/SC
// ----------------
if (LrScEnable) begin : gen_lrsc
// unique requester identifier, does not necessarily match core_id
logic [SbrPortObiCfg.IdWidth-1:0] unique_requester_id;
typedef struct packed {
/// Is the reservation valid.
logic valid;
/// On which address is the reservation placed.
/// This address is aligned to the memory size
/// implying that the reservation happen on a set size
/// equal to the word width of the memory (32 or 64 bit).
logic [SbrPortObiCfg.AddrWidth-1:0] addr;
/// Which requester made this reservation. Important to
/// track the reservations from different requesters and
/// to prevent any live-locking.
logic [SbrPortObiCfg.IdWidth-1:0] requester;
} reservation_t;
reservation_t reservation_d, reservation_q;
`FF(sc_successful_or_lr_q, sc_successful_or_lr_d, 1'b0, clk_i, rst_ni);
`FF(reservation_q, reservation_d, 1'b0, clk_i, rst_ni);
`FF(sc_q, sbr_port_req_i.req &
sbr_port_rsp_o.gnt &
(obi_atop_e'(sbr_port_req_i.a.a_optional.atop) == ATOPSC), 1'b0, clk_i, rst_ni);
always_comb begin
unique_requester_id = sbr_port_req_i.a.aid;
reservation_d = reservation_q;
sc_successful_or_lr_d = 1'b0;
// new valid transaction
if (sbr_port_req_i.req && sbr_port_rsp_o.gnt) begin
// An SC can only pair with the most recent LR in program order.
// Place a reservation on the address if there isn't already a valid reservation.
// We prevent a live-lock by don't throwing away the reservation of a hart unless
// it makes a new reservation in program order or issues any SC.
if (obi_atop_e'(sbr_port_req_i.a.a_optional.atop) == ATOPLR &&
(!reservation_q.valid || reservation_q.requester == unique_requester_id)) begin
reservation_d.valid = 1'b1;
reservation_d.addr = sbr_port_req_i.a.addr;
reservation_d.requester = unique_requester_id;
sc_successful_or_lr_d = 1'b1;
end
// An SC may succeed only if no store from another hart (or other device) to
// the reservation set can be observed to have occurred between
// the LR and the SC, and if there is no other SC between the
// LR and itself in program order.
// check whether another requester has made a write attempt
if ((unique_requester_id != reservation_q.requester) &&
(sbr_port_req_i.a.addr == reservation_q.addr) &&
(!((obi_atop_e'(sbr_port_req_i.a.a_optional.atop) inside {ATOPLR, ATOPSC}) ||
!sbr_port_req_i.a.a_optional.atop[5]) || sbr_port_req_i.a.we)) begin
reservation_d.valid = 1'b0;
end
// An SC from the same hart clears any pending reservation.
if (reservation_q.valid && obi_atop_e'(sbr_port_req_i.a.a_optional.atop) == ATOPSC
&& reservation_q.requester == unique_requester_id) begin
reservation_d.valid = 1'b0;
sc_successful_or_lr_d = (reservation_q.addr == sbr_port_req_i.a.addr);
end
end
end // always_comb
end else begin : gen_disable_lrcs
assign sc_q = 1'b0;
assign sc_successful_or_lr_d = 1'b0;
assign sc_successful_or_lr_q = 1'b0;
end
// ----------------
// Atomics
// ----------------
mgr_port_obi_a_optional_t a_optional;
if (MgrPortObiCfg.OptionalCfg.AUserWidth) begin : gen_auser
if (SbrPortObiCfg.OptionalCfg.AUserWidth) begin : gen_auser_assign
always_comb begin
a_optional.auser = '0;
a_optional.auser = sbr_port_req_i.a.a_optional.auser;
end
end else begin : gen_no_auser
assign a_optional.auser = '0;
end
end
if (MgrPortObiCfg.OptionalCfg.WUserWidth) begin : gen_wuser
if (SbrPortObiCfg.OptionalCfg.WUserWidth) begin : gen_wuser_assign
always_comb begin
a_optional.wuser = '0;
a_optional.wuser = sbr_port_req_i.a.a_optional.wuser;
end
end else begin : gen_no_wuser
assign a_optional.wuser = '0;
end
end
if (MgrPortObiCfg.OptionalCfg.UseProt) begin : gen_prot
if (SbrPortObiCfg.OptionalCfg.UseProt) begin : gen_prot_assign
assign a_optional.prot = sbr_port_req_i.a.a_optional.prot;
end else begin : gen_no_prot
assign a_optional.prot = obi_pkg::DefaultProt;
end
end
if (MgrPortObiCfg.OptionalCfg.UseMemtype) begin : gen_memtype
if (SbrPortObiCfg.OptionalCfg.UseMemtype) begin : gen_memtype_assign
assign a_optional.memtype = sbr_port_req_i.a.a_optional.memtype;
end else begin : gen_no_memtype
assign a_optional.memtype = obi_pkg::DefaultMemtype;
end
end
if (MgrPortObiCfg.OptionalCfg.MidWidth) begin : gen_mid
if (SbrPortObiCfg.OptionalCfg.MidWidth) begin : gen_mid_assign
always_comb begin
a_optional.mid = '0;
a_optional.mid = sbr_port_req_i.a.a_optional.mid;
end
end else begin : gen_no_mid
assign a_optional.mid = '0;
end
end
if (MgrPortObiCfg.OptionalCfg.UseDbg) begin : gen_dbg
if (SbrPortObiCfg.OptionalCfg.UseDbg) begin : gen_dbg_assign
assign a_optional.dbg = sbr_port_req_i.a.a_optional.dbg;
end else begin : gen_no_dbg
assign a_optional.dbg = '0;
end
end
if (!MgrPortObiCfg.OptionalCfg.AUserWidth &&
!MgrPortObiCfg.OptionalCfg.WUserWidth &&
!MgrPortObiCfg.OptionalCfg.UseProt &&
!MgrPortObiCfg.OptionalCfg.UseMemtype &&
!MgrPortObiCfg.OptionalCfg.MidWidth &&
!MgrPortObiCfg.OptionalCfg.UseDbg) begin : gen_no_optional
assign a_optional = '0;
end
always_comb begin
// feed-through
sbr_port_rsp_o.gnt = rdata_ready & mgr_port_rsp_i.gnt;
mgr_port_req_o.req = sbr_port_req_i.req & rdata_ready;
mgr_port_req_o.a.addr = sbr_port_req_i.a.addr;
mgr_port_req_o.a.we = obi_atop_e'(sbr_port_req_i.a.a_optional.atop) != ATOPSC ?
sbr_port_req_i.a.we : sc_successful_or_lr_d;
mgr_port_req_o.a.wdata = sbr_port_req_i.a.wdata;
mgr_port_req_o.a.be = sbr_port_req_i.a.be;
mgr_port_req_o.a.aid = sbr_port_req_i.a.aid;
mgr_port_req_o.a.a_optional = a_optional;
state_d = state_q;
load_amo = 1'b0;
amo_wb = 1'b0;
unique case (state_q)
Idle: begin
if (sbr_port_req_i.req &
sbr_port_rsp_o.gnt &
!((obi_atop_e'(sbr_port_req_i.a.a_optional.atop) inside {ATOPLR, ATOPSC}) ||
!sbr_port_req_i.a.a_optional.atop[5])) begin
load_amo = 1'b1;
state_d = DoAMO;
if (obi_atop_e'(sbr_port_req_i.a.a_optional.atop) inside {AMOSWAP, AMOADD, AMOXOR,
AMOAND, AMOOR, AMOMIN, AMOMAX,
AMOMINU, AMOMAXU}) begin
mgr_port_req_o.a.we = 1'b0;
end
end
end
// Claim the memory interface
DoAMO, WriteBackAMO: begin
sbr_port_rsp_o.gnt = 1'b0;
if (mgr_port_rsp_i.gnt) begin
state_d = (RegisterAmo && state_q != WriteBackAMO) ? WriteBackAMO : Idle;
end
// Commit AMO
amo_wb = 1'b1;
mgr_port_req_o.req = 1'b1;
mgr_port_req_o.a.we = 1'b1;
mgr_port_req_o.a.addr = addr_q;
mgr_port_req_o.a.aid = aid_q;
mgr_port_req_o.a.be = {SbrPortObiCfg.DataWidth/8{1'b1}};
// serve from register if we cut the path
if (RegisterAmo) begin
mgr_port_req_o.a.wdata = amo_result_q;
end else begin
mgr_port_req_o.a.wdata = amo_result;
end
end
default:;
endcase
end
if (RegisterAmo) begin : gen_amo_slice
`FFLNR(amo_result_q, amo_result, (state_q == DoAMO), clk_i)
end else begin : gen_amo_slice
assign amo_result_q = '0;
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= Idle;
amo_op_q <= obi_atop_e'('0);
addr_q <= '0;
amo_operand_b_q <= '0;
aid_q <= '0;
end else begin
state_q <= state_d;
if (load_amo) begin
amo_op_q <= obi_atop_e'(sbr_port_req_i.a.a_optional.atop);
addr_q <= sbr_port_req_i.a.addr;
aid_q <= sbr_port_req_i.a.aid;
amo_operand_b_q <= sbr_port_req_i.a.wdata;
end else begin
amo_op_q <= ATOPNONE;
end
end
end
// ----------------
// AMO ALU
// ----------------
logic [33:0] adder_sum;
logic [32:0] adder_operand_a, adder_operand_b;
`FFL(amo_operand_a_q, mgr_port_rsp_i.r.rdata, mgr_port_rsp_i.rvalid, '0, clk_i, rst_ni)
assign amo_operand_a = mgr_port_rsp_i.rvalid ? mgr_port_rsp_i.r.rdata : amo_operand_a_q;
assign adder_sum = adder_operand_a + adder_operand_b;
/* verilator lint_off WIDTH */
always_comb begin : amo_alu
adder_operand_a = $signed(amo_operand_a);
adder_operand_b = $signed(amo_operand_b_q);
amo_result = amo_operand_b_q;
unique case (amo_op_q)
// the default is to output operand_b
AMOSWAP:;
AMOADD: amo_result = adder_sum[31:0];
AMOAND: amo_result = amo_operand_a & amo_operand_b_q;
AMOOR: amo_result = amo_operand_a | amo_operand_b_q;
AMOXOR: amo_result = amo_operand_a ^ amo_operand_b_q;
AMOMAX: begin
adder_operand_b = -$signed(amo_operand_b_q);
amo_result = adder_sum[32] ? amo_operand_b_q : amo_operand_a;
end
AMOMIN: begin
adder_operand_b = -$signed(amo_operand_b_q);
amo_result = adder_sum[32] ? amo_operand_a : amo_operand_b_q;
end
AMOMAXU: begin
adder_operand_a = $unsigned(amo_operand_a);
adder_operand_b = -$unsigned(amo_operand_b_q);
amo_result = adder_sum[32] ? amo_operand_b_q : amo_operand_a;
end
AMOMINU: begin
adder_operand_a = $unsigned(amo_operand_a);
adder_operand_b = -$unsigned(amo_operand_b_q);
amo_result = adder_sum[32] ? amo_operand_a : amo_operand_b_q;
end
default: amo_result = '0;
endcase
end
// pragma translate_off
// Check for unsupported parameters
if (SbrPortObiCfg.DataWidth != 32 || MgrPortObiCfg.DataWidth != 32) begin : gen_datawidth_err
$error($sformatf({"Module currently only supports DataWidth = 32. ",
"DataWidth is currently set to: %0d"}, DataWidth));
end
`ifndef VERILATOR
assert_rdata_full : assert property(
@(posedge clk_i) disable iff (~rst_ni) (sbr_port_rsp_o.gnt |-> !rdata_full))
else $fatal (1, "Trying to push new data although the i_rdata_register is not ready.");
`endif
// pragma translate_on
endmodule
`include "obi/typedef.svh"
`include "obi/assign.svh"
module obi_atop_resolver_intf import obi_pkg::*; #(
/// The configuration of the subordinate ports (input ports).
parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig,
/// The configuration of the manager port (output port).
parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg,
/// Enable LR & SC AMOS
parameter bit LrScEnable = 1,
/// Cut path between request and response at the cost of increased AMO latency
parameter bit RegisterAmo = 1'b0
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
OBI_BUS.Subordinate sbr_port,
OBI_BUS.Manager mgr_port
);
`OBI_TYPEDEF_ALL(sbr_port_obi, SbrPortObiCfg)
`OBI_TYPEDEF_ALL(mgr_port_obi, MgrPortObiCfg)
sbr_port_obi_req_t sbr_port_req;
sbr_port_obi_rsp_t sbr_port_rsp;
mgr_port_obi_req_t mgr_port_req;
mgr_port_obi_rsp_t mgr_port_rsp;
`OBI_ASSIGN_TO_REQ(sbr_port_req, sbr_port, SbrPortObiCfg)
`OBI_ASSIGN_FROM_RSP(sbr_port, sbr_port_rsp, SbrPortObiCfg)
`OBI_ASSIGN_FROM_REQ(mgr_port, mgr_port_req, MgrPortObiCfg)
`OBI_ASSIGN_TO_RSP(mgr_port_rsp, mgr_port, MgrPortObiCfg)
obi_atop_resolver #(
.SbrPortObiCfg ( SbrPortObiCfg ),
.MgrPortObiCfg ( MgrPortObiCfg ),
.sbr_port_obi_req_t ( sbr_port_obi_req_t ),
.sbr_port_obi_rsp_t ( sbr_port_obi_rsp_t ),
.mgr_port_obi_req_t ( mgr_port_obi_req_t ),
.mgr_port_obi_rsp_t ( mgr_port_obi_rsp_t ),
.mgr_port_obi_a_optional_t( mgr_port_obi_a_optional_t),
.mgr_port_obi_r_optional_t( mgr_port_obi_r_optional_t),
.LrScEnable ( LrScEnable ),
.RegisterAmo ( RegisterAmo )
) i_obi_atop_resolver (
.clk_i,
.rst_ni,
.testmode_i,
.sbr_port_req_i(sbr_port_req),
.sbr_port_rsp_o(sbr_port_rsp),
.mgr_port_req_o(mgr_port_req),
.mgr_port_rsp_i(mgr_port_rsp)
);
endmodule

View file

@ -0,0 +1,164 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
module obi_demux #(
/// The OBI configuration for all ports.
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
/// The request struct for all ports.
parameter type obi_req_t = logic,
/// The response struct for all ports.
parameter type obi_rsp_t = logic,
/// The number of manager ports.
parameter int unsigned NumMgrPorts = 32'd0,
/// The maximum number of outstanding transactions.
parameter int unsigned NumMaxTrans = 32'd0,
/// The type of the port select signal.
parameter type select_t = logic [$clog2(NumMgrPorts)-1:0]
) (
input logic clk_i,
input logic rst_ni,
input select_t sbr_port_select_i,
input obi_req_t sbr_port_req_i,
output obi_rsp_t sbr_port_rsp_o,
output obi_req_t [NumMgrPorts-1:0] mgr_ports_req_o,
input obi_rsp_t [NumMgrPorts-1:0] mgr_ports_rsp_i
);
if (ObiCfg.Integrity) begin : gen_integrity_err
$fatal(1, "unimplemented");
end
// stall requests to ensure in-order behavior (could be handled differently with rready)
localparam int unsigned CounterWidth = cf_math_pkg::idx_width(NumMaxTrans);
logic cnt_up, cnt_down, overflow;
logic [CounterWidth-1:0] in_flight;
logic sbr_port_rready;
select_t select_d, select_q;
always_comb begin : proc_req
select_d = select_q;
cnt_up = 1'b0;
for (int i = 0; i < NumMgrPorts; i++) begin
mgr_ports_req_o[i].req = 1'b0;
mgr_ports_req_o[i].a = '0;
end
if (!overflow) begin
if (sbr_port_select_i == select_q || in_flight == '0 || (in_flight == 1 && cnt_down)) begin
mgr_ports_req_o[sbr_port_select_i].req = sbr_port_req_i.req;
mgr_ports_req_o[sbr_port_select_i].a = sbr_port_req_i.a;
end
end
if (mgr_ports_req_o[sbr_port_select_i].req && mgr_ports_rsp_i[sbr_port_select_i].gnt) begin
select_d = sbr_port_select_i;
cnt_up = 1'b1;
end
end
assign sbr_port_rsp_o.gnt = mgr_ports_rsp_i[sbr_port_select_i].gnt;
assign sbr_port_rsp_o.r = mgr_ports_rsp_i[select_q].r;
assign sbr_port_rsp_o.rvalid = mgr_ports_rsp_i[select_q].rvalid;
if (ObiCfg.UseRReady) begin : gen_rready
assign sbr_port_rready = sbr_port_req_i.rready;
for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_rready
assign mgr_ports_req_o[i].rready = sbr_port_req_i.rready;
end
end else begin : gen_no_rready
assign sbr_port_rready = 1'b1;
end
assign cnt_down = mgr_ports_rsp_i[select_q].rvalid && sbr_port_rready;
delta_counter #(
.WIDTH ( CounterWidth ),
.STICKY_OVERFLOW ( 1'b0 )
) i_counter (
.clk_i,
.rst_ni,
.clear_i ( 1'b0 ),
.en_i ( cnt_up ^ cnt_down ),
.load_i ( 1'b0 ),
.down_i ( cnt_down ),
.delta_i ( {{CounterWidth-1{1'b0}}, 1'b1} ),
.d_i ( '0 ),
.q_o ( in_flight ),
.overflow_o( overflow )
);
always_ff @(posedge clk_i or negedge rst_ni) begin : proc_select
if(!rst_ni) begin
select_q <= '0;
end else begin
select_q <= select_d;
end
end
endmodule
`include "obi/typedef.svh"
`include "obi/assign.svh"
module obi_demux_intf #(
/// The OBI configuration for all ports.
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
/// The number of manager ports.
parameter int unsigned NumMgrPorts = 32'd0,
/// The maximum number of outstanding transactions.
parameter int unsigned NumMaxTrans = 32'd0,
/// The type of the port select signal.
parameter type select_t = logic [$clog2(NumMgrPorts)-1:0]
) (
input logic clk_i,
input logic rst_ni,
input select_t sbr_port_select_i,
OBI_BUS.Subordinate sbr_port,
OBI_BUS.Manager mgr_ports [NumMgrPorts]
);
`OBI_TYPEDEF_ALL(obi, ObiCfg)
obi_req_t sbr_port_req;
obi_rsp_t sbr_port_rsp;
obi_req_t [NumMgrPorts-1:0] mgr_ports_req;
obi_rsp_t [NumMgrPorts-1:0] mgr_ports_rsp;
`OBI_ASSIGN_TO_REQ(sbr_port_req, sbr_port, ObiCfg)
`OBI_ASSIGN_FROM_RSP(sbr_port, sbr_port_rsp, ObiCfg)
for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_mgr_ports_assign
`OBI_ASSIGN_FROM_REQ(mgr_ports[i], mgr_ports_req[i], ObiCfg)
`OBI_ASSIGN_TO_RSP(mgr_ports_rsp[i], mgr_ports[i], ObiCfg)
end
obi_demux #(
.ObiCfg ( ObiCfg ),
.obi_req_t ( obi_req_t ),
.obi_rsp_t ( obi_rsp_t ),
.NumMgrPorts ( NumMgrPorts ),
.NumMaxTrans ( NumMaxTrans ),
.select_t ( select_t )
) i_obi_demux (
.clk_i,
.rst_ni,
.sbr_port_select_i,
.sbr_port_req_i ( sbr_port_req ),
.sbr_port_rsp_o ( sbr_port_rsp ),
.mgr_ports_req_o ( mgr_ports_req ),
.mgr_ports_rsp_i ( mgr_ports_rsp )
);
endmodule

View file

@ -0,0 +1,107 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
module obi_err_sbr #(
/// The OBI configuration for all ports.
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
/// The request struct.
parameter type obi_req_t = logic,
/// The response struct.
parameter type obi_rsp_t = logic,
/// Numper of transactions accepted before stalling if UseRReady
parameter int unsigned NumMaxTrans = 1,
/// Data to respond with from error subordinate
parameter logic [ObiCfg.DataWidth-1:0] RspData = 32'hBADCAB1E
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
input obi_req_t obi_req_i,
output obi_rsp_t obi_rsp_o
);
logic [ObiCfg.IdWidth-1:0] rid;
logic fifo_full, fifo_empty, fifo_pop;
always_comb begin
obi_rsp_o.r.rdata = '0;
obi_rsp_o.r.rdata = RspData;
obi_rsp_o.r.rid = rid;
obi_rsp_o.r.err = 1'b1;
obi_rsp_o.r.r_optional = '0;
obi_rsp_o.gnt = ~fifo_full;
obi_rsp_o.rvalid = ~fifo_empty;
end
if (ObiCfg.UseRReady) begin : gen_pop_rready
assign fifo_pop = obi_rsp_o.rvalid && obi_req_i.rready;
end else begin : gen_pop_default
assign fifo_pop = obi_rsp_o.rvalid;
end
fifo_v3 #(
.DEPTH ( ObiCfg.UseRReady ? NumMaxTrans : 1 ),
.FALL_THROUGH ( 1'b0 ),
.DATA_WIDTH ( ObiCfg.IdWidth )
) i_id_fifo (
.clk_i,
.rst_ni,
.testmode_i,
.flush_i ( '0 ),
.full_o ( fifo_full ),
.empty_o ( fifo_empty ),
.usage_o (),
.data_i ( obi_req_i.a.aid ),
.push_i ( obi_req_i.req && obi_rsp_o.gnt ),
.data_o ( rid ),
.pop_i ( fifo_pop )
);
endmodule
`include "obi/typedef.svh"
`include "obi/assign.svh"
module obi_err_sbr_intf #(
/// The OBI configuration for all ports.
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
/// Numper of transactions accepted before stalling if UseRReady
parameter int unsigned NumMaxTrans = 1,
/// Data to respond with from error subordinate
parameter logic [ObiCfg.DataWidth-1:0] RspData = 32'hBADCAB1E
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
OBI_BUS.Subordinate sbr_port
);
`OBI_TYPEDEF_ALL(obi, ObiCfg)
obi_req_t obi_req;
obi_rsp_t obi_rsp;
`OBI_ASSIGN_TO_REQ(obi_req, sbr_port, ObiCfg)
`OBI_ASSIGN_FROM_RSP(sbr_port, obi_rsp, ObiCfg)
obi_err_sbr #(
.ObiCfg ( ObiCfg ),
.obi_req_t ( obi_req_t ),
.obi_rsp_t ( obi_rsp_t ),
.NumMaxTrans ( NumMaxTrans ),
.RspData ( RspData )
) i_err_sbr (
.clk_i,
.rst_ni,
.testmode_i,
.obi_req_i ( obi_req ),
.obi_rsp_o ( obi_rsp )
);
endmodule

241
vendor/pulp-platform/obi/src/obi_intf.sv vendored Normal file
View file

@ -0,0 +1,241 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
interface OBI_BUS #(
parameter obi_pkg::obi_cfg_t OBI_CFG = obi_pkg::ObiDefaultConfig,
parameter type obi_a_optional_t = logic,
parameter type obi_r_optional_t = logic
) ();
logic req;
logic reqpar;
logic gnt;
logic gntpar;
logic [ OBI_CFG.AddrWidth-1:0] addr;
logic we;
logic [OBI_CFG.DataWidth/8-1:0] be;
logic [ OBI_CFG.DataWidth-1:0] wdata;
logic [ OBI_CFG.IdWidth-1:0] aid;
obi_a_optional_t a_optional;
logic rvalid;
logic rvalidpar;
logic rready;
logic rreadypar;
logic [ OBI_CFG.DataWidth-1:0] rdata;
logic [ OBI_CFG.IdWidth-1:0] rid;
logic err;
obi_r_optional_t r_optional;
modport Manager (
output req,
output reqpar,
input gnt,
input gntpar,
output addr,
output we,
output be,
output wdata,
output aid,
output a_optional,
input rvalid,
input rvalidpar,
output rready,
output rreadypar,
input rdata,
input rid,
input err,
input r_optional
);
modport Subordinate (
input req,
input reqpar,
output gnt,
output gntpar,
input addr,
input we,
input be,
input wdata,
input aid,
input a_optional,
output rvalid,
output rvalidpar,
input rready,
input rreadypar,
output rdata,
output rid,
output err,
output r_optional
);
modport Monitor (
input req,
input reqpar,
input gnt,
input gntpar,
input addr,
input we,
input be,
input wdata,
input aid,
input a_optional,
input rvalid,
input rvalidpar,
input rready,
input rreadypar,
input rdata,
input rid,
input err,
input r_optional
);
endinterface
interface OBI_BUS_DV #(
parameter obi_pkg::obi_cfg_t OBI_CFG = obi_pkg::ObiDefaultConfig,
parameter type obi_a_optional_t = logic,
parameter type obi_r_optional_t = logic
) (
input logic clk_i,
input logic rst_ni
);
logic req;
logic reqpar;
logic gnt;
logic gntpar;
logic [ OBI_CFG.AddrWidth-1:0] addr;
logic we;
logic [OBI_CFG.DataWidth/8-1:0] be;
logic [ OBI_CFG.DataWidth-1:0] wdata;
logic [ OBI_CFG.IdWidth-1:0] aid;
obi_a_optional_t a_optional;
logic rvalid;
logic rvalidpar;
logic rready;
logic rreadypar;
logic [ OBI_CFG.DataWidth-1:0] rdata;
logic [ OBI_CFG.IdWidth-1:0] rid;
logic err;
obi_r_optional_t r_optional;
modport Manager (
output req,
output reqpar,
input gnt,
input gntpar,
output addr,
output we,
output be,
output wdata,
output aid,
output a_optional,
input rvalid,
input rvalidpar,
output rready,
output rreadypar,
input rdata,
input rid,
input err,
input r_optional
);
modport Subordinate (
input req,
input reqpar,
output gnt,
output gntpar,
input addr,
input we,
input be,
input wdata,
input aid,
input a_optional,
output rvalid,
output rvalidpar,
input rready,
input rreadypar,
output rdata,
output rid,
output err,
output r_optional
);
modport Monitor (
input req,
input reqpar,
input gnt,
input gntpar,
input addr,
input we,
input be,
input wdata,
input aid,
input a_optional,
input rvalid,
input rvalidpar,
input rready,
input rreadypar,
input rdata,
input rid,
input err,
input r_optional
);
// pragma translate_off
`ifndef VERILATOR
// A channel
// OBI spec R3.1
assert property (@(posedge clk_i) disable iff (!rst_ni) req |-> ##[1:$] gnt);
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> $stable(addr)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> $stable(we)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> $stable(be)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> $stable(wdata)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> $stable(aid)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> $stable(a_optional)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && !gnt |=> req));
if (OBI_CFG.Integrity) begin : gen_integrity_req_check
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && ~reqpar));
assert property (@(posedge clk_i) disable iff (!rst_ni) (gnt && ~gntpar));
// TODO: achk?
end
// R channel
if (OBI_CFG.UseRReady) begin : gen_rready_checks
// OBI spec R4.1
assert property (@(posedge clk_i) disable iff (!rst_ni) rvalid |-> ##[1:$] rready);
assert property (@(posedge clk_i) disable iff (!rst_ni) (rvalid && !rready |=> $stable(rdata)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (rvalid && !rready |=> $stable(rid)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (rvalid && !rready |=> $stable(err)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (rvalid && !rready |=>
$stable(r_optional)));
assert property (@(posedge clk_i) disable iff (!rst_ni) (rvalid && !rready |=> rvalid));
end
if (OBI_CFG.Integrity) begin : gen_integrity_rsp_check
assert property (@(posedge clk_i) disable iff (!rst_ni) (rvalid && ~rvalidpar));
if (OBI_CFG.UseRReady) begin : gen_rready_integrity_check
assert property (@(posedge clk_i) disable iff (!rst_ni) (rready && ~rreadypar));
end
end
if (!OBI_CFG.BeFull) begin : gen_be_checks
// OBI spec R7
assert property (@(posedge clk_i) disable iff (!rst_ni) (req && gnt && we |-> be != '0));
// TODO R7: assert be contiguous
end
`endif
// pragma translate_on
endinterface

228
vendor/pulp-platform/obi/src/obi_mux.sv vendored Normal file
View file

@ -0,0 +1,228 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
`include "obi/assign.svh"
/// An OBI multiplexer.
module obi_mux #(
/// The configuration of the subordinate ports (input ports).
parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig,
/// The configuration of the manager port (output port).
parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg,
/// The request struct for the subordinate ports (input ports).
parameter type sbr_port_obi_req_t = logic,
/// The A channel struct for the subordinate ports (input ports).
parameter type sbr_port_a_chan_t = logic,
/// The response struct for the subordinate ports (input ports).
parameter type sbr_port_obi_rsp_t = logic,
/// The R channel struct for the subordinate ports (input ports).
parameter type sbr_port_r_chan_t = logic,
/// The request struct for the manager port (output port).
parameter type mgr_port_obi_req_t = sbr_port_obi_req_t,
/// The response struct for the manager ports (output ports).
parameter type mgr_port_obi_rsp_t = sbr_port_obi_rsp_t,
/// The number of subordinate ports (input ports).
parameter int unsigned NumSbrPorts = 32'd0,
/// The maximum number of outstanding transactions.
parameter int unsigned NumMaxTrans = 32'd0,
/// Use the extended ID field (aid & rid) to route the response
parameter bit UseIdForRouting = 1'b0
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
input sbr_port_obi_req_t [NumSbrPorts-1:0] sbr_ports_req_i,
output sbr_port_obi_rsp_t [NumSbrPorts-1:0] sbr_ports_rsp_o,
output mgr_port_obi_req_t mgr_port_req_o,
input mgr_port_obi_rsp_t mgr_port_rsp_i
);
if (NumSbrPorts <= 1) begin : gen_NumSbrPorts_err
$fatal(1, "unimplemented");
end
localparam int unsigned RequiredExtraIdWidth = $clog2(NumSbrPorts);
logic [NumSbrPorts-1:0] sbr_ports_req, sbr_ports_gnt;
sbr_port_a_chan_t [NumSbrPorts-1:0] sbr_ports_a;
for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sbr_assign
assign sbr_ports_req[i] = sbr_ports_req_i[i].req;
assign sbr_ports_a[i] = sbr_ports_req_i[i].a;
assign sbr_ports_rsp_o[i].gnt = sbr_ports_gnt[i];
end
sbr_port_a_chan_t mgr_port_a_in_sbr;
logic [RequiredExtraIdWidth-1:0] selected_id, response_id;
logic mgr_port_req, fifo_full, fifo_pop;
rr_arb_tree #(
.NumIn ( NumSbrPorts ),
.DataType ( sbr_port_a_chan_t ),
.AxiVldRdy ( 1'b1 ),
.LockIn ( 1'b1 )
) i_rr_arb (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.rr_i ( '0 ),
.req_i ( sbr_ports_req ),
.gnt_o ( sbr_ports_gnt ),
.data_i ( sbr_ports_a ),
.req_o ( mgr_port_req ),
.gnt_i ( mgr_port_rsp_i.gnt && ~fifo_full ),
.data_o ( mgr_port_a_in_sbr ),
.idx_o ( selected_id )
);
assign mgr_port_req_o.req = mgr_port_req && ~fifo_full;
if (MgrPortObiCfg.IdWidth > 0 &&
(MgrPortObiCfg.IdWidth >= SbrPortObiCfg.IdWidth + RequiredExtraIdWidth)) begin : gen_aid_extend
always_comb begin
mgr_port_req_o.a.aid = '0;
`OBI_SET_A_STRUCT(mgr_port_req_o.a, mgr_port_a_in_sbr)
mgr_port_req_o.a.aid[SbrPortObiCfg.IdWidth + RequiredExtraIdWidth-1:0] =
{selected_id, mgr_port_a_in_sbr.aid};
end
end else begin : gen_aid_consistent
always_comb begin
mgr_port_req_o.a.aid = '0;
`OBI_SET_A_STRUCT(mgr_port_req_o.a, mgr_port_a_in_sbr)
end
end
logic [SbrPortObiCfg.IdWidth-1:0] rsp_rid;
if (UseIdForRouting) begin : gen_id_assign
if (!(MgrPortObiCfg.IdWidth > 0 &&
(MgrPortObiCfg.IdWidth >= SbrPortObiCfg.IdWidth + RequiredExtraIdWidth)))
$fatal(1, "UseIdForRouting requires MgrPort IdWidth to increase with log2(NumSbrPorts)");
assign {response_id, rsp_rid} =
mgr_port_rsp_i.r.rid[SbrPortObiCfg.IdWidth + RequiredExtraIdWidth-1:0];
assign fifo_full = 1'b0;
end else begin : gen_no_id_assign
fifo_v3 #(
.FALL_THROUGH( 1'b0 ),
.DATA_WIDTH ( RequiredExtraIdWidth ),
.DEPTH ( NumMaxTrans )
) i_fifo (
.clk_i,
.rst_ni,
.flush_i ('0),
.testmode_i,
.full_o ( fifo_full ),
.empty_o (),
.usage_o (),
.data_i ( selected_id ),
.push_i ( mgr_port_req_o.req && mgr_port_rsp_i.gnt ),
.data_o ( response_id ),
.pop_i ( fifo_pop )
);
end
if (MgrPortObiCfg.UseRReady) begin : gen_rready_connect
assign mgr_port_req_o.rready = sbr_ports_req_i[response_id].rready;
end
logic [NumSbrPorts-1:0] sbr_rsp_rvalid;
sbr_port_r_chan_t [NumSbrPorts-1:0] sbr_rsp_r;
always_comb begin : proc_sbr_rsp
for (int i = 0; i < NumSbrPorts; i++) begin
sbr_rsp_r[i] = '0;
sbr_rsp_rvalid[i] = '0;
end
`OBI_SET_R_STRUCT(sbr_rsp_r[response_id], mgr_port_rsp_i.r);
sbr_rsp_rvalid[response_id] = mgr_port_rsp_i.rvalid;
end
for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sbr_rsp_assign
assign sbr_ports_rsp_o[i].r = sbr_rsp_r[i];
assign sbr_ports_rsp_o[i].rvalid = sbr_rsp_rvalid[i];
end
if (MgrPortObiCfg.UseRReady) begin : gen_fifo_pop
assign fifo_pop = mgr_port_rsp_i.rvalid && mgr_port_req_o.rready;
end else begin : gen_fifo_pop
assign fifo_pop = mgr_port_rsp_i.rvalid;
end
endmodule
`include "obi/typedef.svh"
module obi_mux_intf #(
/// The configuration of the subordinate ports (input ports).
parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig,
/// The configuration of the manager port (output port).
parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg,
/// The number of subordinate ports (input ports).
parameter int unsigned NumSbrPorts = 32'd0,
/// The maximum number of outstanding transactions.
parameter int unsigned NumMaxTrans = 32'd0,
/// Use the extended ID field (aid & rid) to route the response
parameter bit UseIdForRouting = 1'b0
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
OBI_BUS.Subordinate sbr_ports [NumSbrPorts],
OBI_BUS.Manager mgr_port
);
`OBI_TYPEDEF_ALL(sbr_port_obi, SbrPortObiCfg)
`OBI_TYPEDEF_ALL(mgr_port_obi, MgrPortObiCfg)
sbr_port_obi_req_t [NumSbrPorts-1:0] sbr_ports_req;
sbr_port_obi_rsp_t [NumSbrPorts-1:0] sbr_ports_rsp;
mgr_port_obi_req_t mgr_port_req;
mgr_port_obi_rsp_t mgr_port_rsp;
for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sbr_ports_assign
`OBI_ASSIGN_TO_REQ(sbr_ports_req[i], sbr_ports[i], SbrPortObiCfg)
`OBI_ASSIGN_FROM_RSP(sbr_ports[i], sbr_ports_rsp[i], SbrPortObiCfg)
end
`OBI_ASSIGN_FROM_REQ(mgr_port, mgr_port_req, MgrPortObiCfg)
`OBI_ASSIGN_TO_RSP(mgr_port_rsp, mgr_port, MgrPortObiCfg)
obi_mux #(
.SbrPortObiCfg ( SbrPortObiCfg ),
.MgrPortObiCfg ( MgrPortObiCfg ),
.sbr_port_obi_req_t ( sbr_port_obi_req_t ),
.sbr_port_a_chan_t ( sbr_port_obi_a_chan_t ),
.sbr_port_obi_rsp_t ( sbr_port_obi_rsp_t ),
.sbr_port_r_chan_t ( sbr_port_obi_r_chan_t ),
.mgr_port_obi_req_t ( mgr_port_obi_req_t ),
.mgr_port_obi_rsp_t ( mgr_port_obi_rsp_t ),
.NumSbrPorts ( NumSbrPorts ),
.NumMaxTrans ( NumMaxTrans ),
.UseIdForRouting ( UseIdForRouting )
) i_obi_mux (
.clk_i,
.rst_ni,
.testmode_i,
.sbr_ports_req_i ( sbr_ports_req ),
.sbr_ports_rsp_o ( sbr_ports_rsp ),
.mgr_port_req_o ( mgr_port_req ),
.mgr_port_rsp_i ( mgr_port_rsp )
);
endmodule

136
vendor/pulp-platform/obi/src/obi_pkg.sv vendored Normal file
View file

@ -0,0 +1,136 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
package obi_pkg;
/// The OBI atomics type, to be expanded.
typedef logic [5:0] atop_t;
/// The OBI memtype type, to be expanded.
typedef logic [1:0] memtype_t;
/// The OBI prot type, to be expanded.
typedef logic [2:0] prot_t;
localparam atop_t DefaultAtop = 6'b000000;
localparam memtype_t DefaultMemtype = 2'b00;
localparam prot_t DefaultProt = 3'b111;
/// The config type for OBI bus optional fields.
typedef struct packed {
bit UseAtop;
bit UseMemtype;
bit UseProt;
bit UseDbg;
int unsigned AUserWidth;
int unsigned WUserWidth;
int unsigned RUserWidth;
int unsigned MidWidth;
int unsigned AChkWidth;
int unsigned RChkWidth;
} obi_optional_cfg_t;
localparam obi_optional_cfg_t ObiMinimalOptionalConfig = '{
UseAtop: 1'b0,
UseMemtype: 1'b0,
UseProt: 1'b0,
UseDbg: 1'b0,
AUserWidth: 0,
WUserWidth: 0,
RUserWidth: 0,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
};
localparam obi_optional_cfg_t ObiAtopOptionalConfig = '{
UseAtop: 1'b1,
UseMemtype: 1'b0,
UseProt: 1'b0,
UseDbg: 1'b0,
AUserWidth: 0,
WUserWidth: 0,
RUserWidth: 0,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
};
function automatic obi_optional_cfg_t obi_all_optional_config(int unsigned AUserWidth,
int unsigned WUserWidth, int unsigned RUserWidth, int unsigned MidWidth,
int unsigned AChkWidth, int unsigned RChkWidth);
obi_all_optional_config = '{
UseAtop: 1'b1,
UseMemtype: 1'b1,
UseProt: 1'b1,
UseDbg: 1'b1,
AUserWidth: AUserWidth,
WUserWidth: WUserWidth,
RUserWidth: RUserWidth,
MidWidth: MidWidth,
AChkWidth: AChkWidth,
RChkWidth: RChkWidth
};
endfunction
/// The OBI bus config type.
typedef struct packed {
bit UseRReady;
bit CombGnt;
int unsigned AddrWidth;
int unsigned DataWidth;
int unsigned IdWidth;
bit Integrity;
bit BeFull;
obi_optional_cfg_t OptionalCfg;
} obi_cfg_t;
function automatic obi_cfg_t obi_default_cfg(int unsigned AddrWidth, int unsigned DataWidth,
int unsigned IdWidth, obi_optional_cfg_t OptionalCfg);
obi_default_cfg = '{
UseRReady: 1'b0,
CombGnt: 1'b0,
AddrWidth: AddrWidth,
DataWidth: DataWidth,
IdWidth: IdWidth,
Integrity: 1'b0,
BeFull: 1'b1,
OptionalCfg: OptionalCfg
};
endfunction
/// The default OBI bus config.
localparam obi_cfg_t ObiDefaultConfig = obi_default_cfg(32, 32, 1, ObiMinimalOptionalConfig);
function automatic obi_cfg_t mux_grow_cfg(obi_cfg_t ObiCfgIn, int unsigned NumManagers);
mux_grow_cfg = '{
UseRReady: ObiCfgIn.UseRReady,
CombGnt: ObiCfgIn.CombGnt,
AddrWidth: ObiCfgIn.AddrWidth,
DataWidth: ObiCfgIn.DataWidth,
IdWidth: ObiCfgIn.IdWidth + cf_math_pkg::idx_width(NumManagers),
Integrity: ObiCfgIn.Integrity,
BeFull: ObiCfgIn.BeFull,
OptionalCfg: ObiCfgIn.OptionalCfg
};
endfunction
typedef enum atop_t {
ATOPLR = 6'h22,
ATOPSC = 6'h23,
AMOSWAP = 6'h21,
AMOADD = 6'h20,
AMOXOR = 6'h24,
AMOAND = 6'h2C,
AMOOR = 6'h28,
AMOMIN = 6'h30,
AMOMAX = 6'h34,
AMOMINU = 6'h38,
AMOMAXU = 6'h3C,
ATOPNONE = 6'h0
} obi_atop_e;
endpackage

View file

@ -0,0 +1,72 @@
// Copyright 2024 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Georg Rutishauser <georgr@iis.ee.ethz.ch>
module obi_rready_converter #(
parameter type obi_a_chan_t = logic,
parameter type obi_r_chan_t = logic,
parameter int unsigned Depth = 1,
parameter bit CombRspReq = 1'b1
) (
input logic clk_i,
input logic rst_ni,
input logic test_mode_i,
input obi_a_chan_t sbr_a_chan_i,
input logic req_i,
output logic gnt_o,
output obi_r_chan_t sbr_r_chan_o,
output logic rvalid_o,
input logic rready_i,
output obi_a_chan_t mgr_a_chan_o,
output logic req_o,
input logic gnt_i,
input obi_r_chan_t mgr_r_chan_i,
input logic rvalid_i
);
logic fifo_ready, credit_left;
stream_fifo #(
.FALL_THROUGH ( 1'b1 ),
.DEPTH ( Depth ),
.T ( obi_r_chan_t )
) response_fifo_i (
.clk_i,
.rst_ni,
.flush_i ( 1'b0 ),
.testmode_i ( test_mode_i ),
.usage_o (),
.data_i ( mgr_r_chan_i ),
.valid_i ( rvalid_i ),
.ready_o ( fifo_ready ),
.data_o ( sbr_r_chan_o ),
.valid_o ( rvalid_o ),
.ready_i ( rready_i )
);
credit_counter #(
.NumCredits ( Depth ),
.InitCreditEmpty ( 1'b0 )
) credit_cntr_i (
.clk_i,
.rst_ni,
.credit_o (),
.credit_give_i ( rvalid_o & rready_i ),
.credit_take_i ( req_o & gnt_i ),
.credit_init_i ( 1'b0 ),
.credit_left_o ( credit_left ),
.credit_crit_o (),
.credit_full_o ()
);
// only transmit requests if we have credits left or free a space in the FIFO
assign req_o = req_i & (credit_left | (CombRspReq & rready_i & rvalid_o));
// only grant requests if we have credits left or free a space in the FIFO
assign gnt_o = gnt_i & (credit_left | (CombRspReq & rready_i & rvalid_o));
assign mgr_a_chan_o = sbr_a_chan_i;
endmodule

View file

@ -0,0 +1,66 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
module obi_sram_shim #(
/// The OBI configuration for all ports.
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
/// The request struct for all ports.
parameter type obi_req_t = logic,
/// The response struct for all ports.
parameter type obi_rsp_t = logic
) (
input logic clk_i,
input logic rst_ni,
input obi_req_t obi_req_i,
output obi_rsp_t obi_rsp_o,
output logic req_o,
output logic we_o,
output logic [ ObiCfg.AddrWidth-1:0] addr_o,
output logic [ ObiCfg.DataWidth-1:0] wdata_o,
output logic [ObiCfg.DataWidth/8-1:0] be_o,
input logic gnt_i,
input logic [ ObiCfg.DataWidth-1:0] rdata_i
);
if (ObiCfg.OptionalCfg.UseAtop) $error("Please use an ATOP resolver before sram shim.");
if (ObiCfg.UseRReady) $error("Please use an RReady Fifo before sram shim.");
if (ObiCfg.Integrity) $error("Integrity not yet supported, WIP");
if (ObiCfg.OptionalCfg.UseProt) $warning("Prot not checked!");
if (ObiCfg.OptionalCfg.UseMemtype) $warning("Memtype not checked!");
logic rvalid_d, rvalid_q;
logic [ObiCfg.IdWidth-1:0] id_d, id_q;
assign req_o = obi_req_i.req;
assign we_o = obi_req_i.a.we;
assign addr_o = obi_req_i.a.addr;
assign wdata_o = obi_req_i.a.wdata;
assign be_o = obi_req_i.a.be;
assign obi_rsp_o.gnt = gnt_i;
assign obi_rsp_o.rvalid = rvalid_q;
assign obi_rsp_o.r.rdata = rdata_i;
assign obi_rsp_o.r.rid = id_q;
assign obi_rsp_o.r.err = 1'b0;
assign obi_rsp_o.r.r_optional = '0;
assign rvalid_d = obi_req_i.req & obi_rsp_o.gnt;
assign id_d = obi_req_i.a.aid;
always_ff @(posedge clk_i or negedge rst_ni) begin : proc_sram_state
if(!rst_ni) begin
rvalid_q <= 1'b0;
id_q <= '0;
end else begin
rvalid_q <= rvalid_d;
id_q <= id_d;
end
end
endmodule

242
vendor/pulp-platform/obi/src/obi_xbar.sv vendored Normal file
View file

@ -0,0 +1,242 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
/// An OBI crossbar interconnect.
module obi_xbar #(
/// The OBI configuration for the subordinate ports (input ports).
parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig,
/// The OBI configuration for the manager ports (ouput ports).
parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg,
/// The request struct for the subordinate ports (input ports).
parameter type sbr_port_obi_req_t = logic,
/// The A channel struct for the subordinate ports (input ports).
parameter type sbr_port_a_chan_t = logic,
/// The response struct for the subordinate ports (input ports).
parameter type sbr_port_obi_rsp_t = logic,
/// The R channel struct for the subordinate ports (input ports).
parameter type sbr_port_r_chan_t = logic,
/// The request struct for the manager ports (output ports).
parameter type mgr_port_obi_req_t = sbr_port_obi_req_t,
/// The response struct for the manager ports (output ports).
parameter type mgr_port_obi_rsp_t = sbr_port_obi_rsp_t,
/// The number of subordinate ports (input ports).
parameter int unsigned NumSbrPorts = 32'd0,
/// The number of manager ports (output ports).
parameter int unsigned NumMgrPorts = 32'd0,
/// The maximum number of outstanding transactions.
parameter int unsigned NumMaxTrans = 32'd0,
/// The number of address rules.
parameter int unsigned NumAddrRules = 32'd0,
/// The address map rule type.
parameter type addr_map_rule_t = logic,
/// Use the extended ID field (aid & rid) to route the response
parameter bit UseIdForRouting = 1'b0,
/// Connectivity matrix to disable certain paths.
parameter bit [NumSbrPorts-1:0][NumMgrPorts-1:0] Connectivity = '1
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
input sbr_port_obi_req_t [NumSbrPorts-1:0] sbr_ports_req_i,
output sbr_port_obi_rsp_t [NumSbrPorts-1:0] sbr_ports_rsp_o,
output mgr_port_obi_req_t [NumMgrPorts-1:0] mgr_ports_req_o,
input mgr_port_obi_rsp_t [NumMgrPorts-1:0] mgr_ports_rsp_i,
input addr_map_rule_t [NumAddrRules-1:0] addr_map_i,
input logic [NumSbrPorts-1:0] en_default_idx_i,
input logic [NumSbrPorts-1:0][$clog2(NumMgrPorts)-1:0] default_idx_i
);
logic [NumSbrPorts-1:0][$clog2(NumMgrPorts)-1:0] sbr_port_select;
// Signals from the demuxes
sbr_port_obi_req_t [NumSbrPorts-1:0][NumMgrPorts-1:0] sbr_reqs;
sbr_port_obi_rsp_t [NumSbrPorts-1:0][NumMgrPorts-1:0] sbr_rsps;
// Signals to the muxes
sbr_port_obi_req_t [NumMgrPorts-1:0][NumSbrPorts-1:0] mgr_reqs;
sbr_port_obi_rsp_t [NumMgrPorts-1:0][NumSbrPorts-1:0] mgr_rsps;
for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_demux
addr_decode #(
.NoIndices ( NumMgrPorts ),
.NoRules ( NumAddrRules ),
.addr_t ( logic [MgrPortObiCfg.AddrWidth-1:0] ),
.rule_t ( addr_map_rule_t )
) i_addr_decode (
.addr_i ( sbr_ports_req_i[i].a.addr ),
.addr_map_i ( addr_map_i ),
.idx_o ( sbr_port_select[i] ),
.dec_valid_o (),
.dec_error_o (),
.en_default_idx_i( en_default_idx_i[i] ),
.default_idx_i ( default_idx_i[i] )
);
obi_demux #(
.ObiCfg ( SbrPortObiCfg ),
.obi_req_t ( sbr_port_obi_req_t ),
.obi_rsp_t ( sbr_port_obi_rsp_t ),
.NumMgrPorts ( NumMgrPorts ),
.NumMaxTrans ( NumMaxTrans )
) i_demux (
.clk_i,
.rst_ni,
.sbr_port_select_i ( sbr_port_select[i] ),
.sbr_port_req_i ( sbr_ports_req_i[i] ),
.sbr_port_rsp_o ( sbr_ports_rsp_o[i] ),
.mgr_ports_req_o ( sbr_reqs[i] ),
.mgr_ports_rsp_i ( sbr_rsps[i] )
);
end
for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_interco_sbr
for (genvar j = 0; j < NumMgrPorts; j++) begin : gen_interco_mgr
if (Connectivity[i][j]) begin : gen_connected
assign mgr_reqs[j][i] = sbr_reqs[i][j];
assign sbr_rsps[i][j] = mgr_rsps[j][i];
end else begin : gen_err_sbr
assign mgr_reqs[j][i].req = 1'b0;
if (MgrPortObiCfg.UseRReady) begin : gen_rready
assign mgr_reqs[j][i].rready = 1'b0;
end
assign mgr_reqs[j][i].a = '0;
if (MgrPortObiCfg.Integrity) begin : gen_integrity
assign mgr_reqs[j][i].reqpar = 1'b1;
if (MgrPortObiCfg.UseRReady) begin : gen_int_rready
assign mgr_reqs[j][i].rreadypar = 1'b1;
end
end
obi_err_sbr #(
.ObiCfg ( SbrPortObiCfg ),
.obi_req_t ( sbr_port_obi_req_t ),
.obi_rsp_t ( sbr_port_obi_rsp_t ),
.NumMaxTrans ( NumMaxTrans ),
.RspData ( 32'hBADCAB1E )
) i_err_sbr (
.clk_i,
.rst_ni,
.testmode_i,
.obi_req_i (sbr_reqs[i][j]),
.obi_rsp_o (sbr_rsps[i][j])
);
end
end
end
for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_mux
obi_mux #(
.SbrPortObiCfg ( SbrPortObiCfg ),
.MgrPortObiCfg ( MgrPortObiCfg ),
.sbr_port_obi_req_t ( sbr_port_obi_req_t ),
.sbr_port_a_chan_t ( sbr_port_a_chan_t ),
.sbr_port_obi_rsp_t ( sbr_port_obi_rsp_t ),
.sbr_port_r_chan_t ( sbr_port_r_chan_t ),
.mgr_port_obi_req_t ( mgr_port_obi_req_t ),
.mgr_port_obi_rsp_t ( mgr_port_obi_rsp_t ),
.NumSbrPorts ( NumSbrPorts ),
.NumMaxTrans ( NumMaxTrans ),
.UseIdForRouting ( UseIdForRouting )
) i_mux (
.clk_i,
.rst_ni,
.testmode_i,
.sbr_ports_req_i ( mgr_reqs[i] ),
.sbr_ports_rsp_o ( mgr_rsps[i] ),
.mgr_port_req_o ( mgr_ports_req_o[i] ),
.mgr_port_rsp_i ( mgr_ports_rsp_i[i] )
);
end
endmodule
`include "obi/typedef.svh"
`include "obi/assign.svh"
module obi_xbar_intf #(
/// The OBI configuration for the subordinate ports (input ports).
parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig,
/// The OBI configuration for the manager ports (ouput ports).
parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg,
/// The number of subordinate ports (input ports).
parameter int unsigned NumSbrPorts = 32'd0,
/// The number of manager ports (output ports).
parameter int unsigned NumMgrPorts = 32'd0,
/// The maximum number of outstanding transactions.
parameter int unsigned NumMaxTrans = 32'd0,
/// The number of address rules.
parameter int unsigned NumAddrRules = 32'd0,
/// The address map rule type.
parameter type addr_map_rule_t = logic,
/// Use the extended ID field (aid & rid) to route the response
parameter bit UseIdForRouting = 1'b0,
/// Connectivity matrix to disable certain paths.
parameter bit [NumSbrPorts-1:0][NumMgrPorts-1:0] Connectivity = '1
) (
input logic clk_i,
input logic rst_ni,
input logic testmode_i,
OBI_BUS.Subordinate sbr_ports [NumSbrPorts],
OBI_BUS.Manager mgr_ports [NumMgrPorts],
input addr_map_rule_t [NumAddrRules-1:0] addr_map_i,
input logic [NumSbrPorts-1:0] en_default_idx_i,
input logic [NumSbrPorts-1:0][$clog2(NumMgrPorts)-1:0] default_idx_i
);
`OBI_TYPEDEF_ALL(sbr_port_obi, SbrPortObiCfg)
`OBI_TYPEDEF_ALL(mgr_port_obi, MgrPortObiCfg)
sbr_port_obi_req_t [NumSbrPorts-1:0] sbr_ports_req;
sbr_port_obi_rsp_t [NumSbrPorts-1:0] sbr_ports_rsp;
mgr_port_obi_req_t [NumMgrPorts-1:0] mgr_ports_req;
mgr_port_obi_rsp_t [NumMgrPorts-1:0] mgr_ports_rsp;
for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sbr_ports_assign
`OBI_ASSIGN_TO_REQ(sbr_ports_req[i], sbr_ports[i], SbrPortObiCfg)
`OBI_ASSIGN_FROM_RSP(sbr_ports[i], sbr_ports_rsp[i], SbrPortObiCfg)
end
for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_mgr_ports_assign
`OBI_ASSIGN_FROM_REQ(mgr_ports[i], mgr_ports_req[i], MgrPortObiCfg)
`OBI_ASSIGN_TO_RSP(mgr_ports_rsp[i], mgr_ports[i], MgrPortObiCfg)
end
obi_xbar #(
.SbrPortObiCfg ( SbrPortObiCfg ),
.MgrPortObiCfg ( MgrPortObiCfg ),
.sbr_port_obi_req_t ( sbr_port_obi_req_t ),
.sbr_port_a_chan_t ( sbr_port_obi_a_chan_t ),
.sbr_port_obi_rsp_t ( sbr_port_obi_rsp_t ),
.sbr_port_r_chan_t ( sbr_port_obi_r_chan_t ),
.mgr_port_obi_req_t ( mgr_port_obi_req_t ),
.mgr_port_obi_rsp_t ( mgr_port_obi_rsp_t ),
.NumSbrPorts ( NumSbrPorts ),
.NumMgrPorts ( NumMgrPorts ),
.NumMaxTrans ( NumMaxTrans ),
.NumAddrRules ( NumAddrRules ),
.addr_map_rule_t ( addr_map_rule_t ),
.UseIdForRouting ( UseIdForRouting ),
.Connectivity ( Connectivity )
) i_obi_xbar (
.clk_i,
.rst_ni,
.testmode_i,
.sbr_ports_req_i ( sbr_ports_req ),
.sbr_ports_rsp_o ( sbr_ports_rsp ),
.mgr_ports_req_o ( mgr_ports_req ),
.mgr_ports_rsp_i ( mgr_ports_rsp ),
.addr_map_i ( addr_map_i ),
.en_default_idx_i ( en_default_idx_i ),
.default_idx_i ( default_idx_i )
);
endmodule

View file

@ -0,0 +1,245 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Michael Rogenmoser <michaero@iis.ee.ethz.ch>
interface OBI_ATOP_MONITOR_BUS #(
parameter int unsigned AddrWidth = 0,
parameter int unsigned DataWidth = 0,
parameter int unsigned IdWidth = 0,
parameter int unsigned UserWidth = 0
) (
input logic clk_i
);
typedef logic [IdWidth-1:0] id_t;
typedef logic [AddrWidth-1:0] addr_t;
typedef logic [DataWidth-1:0] data_t;
typedef logic [DataWidth/8-1:0] be_t;
typedef logic [UserWidth-1:0] user_t;
logic valid;
logic we;
addr_t addr;
data_t data;
be_t be;
id_t id;
user_t user;
modport Manager (output valid, we, addr, data, be, id, user);
modport Subordinate (input valid, we, addr, data, be, id, user);
endinterface
package atop_golden_mem_pkg;
class atop_golden_mem #(
parameter int unsigned ObiAddrWidth = 32,
parameter int unsigned ObiDataWidth = 32,
parameter int unsigned ObiIdWidthM = 5,
parameter int unsigned ObiIdWidthS = 8,
parameter int unsigned ObiUserWidth = 5,
parameter int unsigned NumMgrWidth = 2,
parameter time ApplDelay = 0ns,
parameter time AcqDelay = 0ns
);
typedef logic [ObiAddrWidth-1:0] mem_addr_t;
static logic [7:0] memory [mem_addr_t];
virtual OBI_ATOP_MONITOR_BUS #(
.AddrWidth(ObiAddrWidth),
.DataWidth(ObiDataWidth),
.IdWidth (ObiIdWidthS),
.UserWidth(ObiUserWidth)
) monitor;
function new(virtual OBI_ATOP_MONITOR_BUS #(
.AddrWidth(ObiAddrWidth),
.DataWidth(ObiDataWidth),
.IdWidth (ObiIdWidthS),
.UserWidth(ObiUserWidth)
) monitor
);
this.monitor = monitor;
assert (randomize(memory));
endfunction
function automatic logic [ObiIdWidthS-1:0] subordinate_id(logic [ObiIdWidthM-1:0] mgr_id,
logic [NumMgrWidth-1:0] mgr_channel);
subordinate_id = '0;
if (ObiIdWidthS > ObiIdWidthM) begin
subordinate_id |= (mgr_channel) << ObiIdWidthM;
end
subordinate_id[ObiIdWidthM-1:0] = mgr_id;
endfunction
task set_memory(logic [ObiAddrWidth-1:0] addr, logic [ObiDataWidth-1:0] data,
logic [ObiDataWidth/8-1:0] be);
#(ApplDelay);
// $display("set 0x%x at 0x%x",data, addr);
for (int i = 0; i < ObiDataWidth/8; i++) begin
if (be[i]) begin
memory[addr+i] = data[i*8+:8];
end
end
endtask
function logic [ObiDataWidth-1:0] get_memory(logic [ObiAddrWidth-1:0] addr);
get_memory = '0;
for (int i = 0; i < ObiDataWidth/8; i++) begin
get_memory[i*8+:8] = memory[addr+i];
end
// $display("got 0x%x at 0x%x",get_memory, addr);
endfunction
task write(
input logic [ ObiAddrWidth-1:0] addr,
input logic [ ObiDataWidth-1:0] wdata,
input logic [ObiDataWidth/8-1:0] be,
input logic [ ObiIdWidthM-1:0] m_id,
// input logic [ ObiUserWidth-1:0] user,
input logic [ NumMgrWidth-1:0] manager = 0,
input obi_pkg::atop_t atop = '0,
output logic [ ObiDataWidth-1:0] rdata,
output logic err,
output logic exokay
);
automatic logic [ObiIdWidthS-1:0] res_id = subordinate_id(m_id, manager);
automatic logic unsigned [ObiDataWidth-1:0] data_ui = $unsigned(wdata);
automatic logic unsigned [ObiDataWidth-1:0] data_uo = 0;
automatic logic signed [ObiDataWidth-1:0] data_si = $signed(wdata);
automatic logic signed [ObiDataWidth-1:0] data_so = 0;
// $display("Writing 0x%x to 0x%x and atop 0x%x", wdata, addr, atop);
if (atop == obi_pkg::ATOPSC) begin
// TODO
end else if (atop == obi_pkg::ATOPNONE) begin
wait_write_rsp(res_id);
set_memory(addr, wdata, be);
rdata = '0;
err = '0;
exokay = '0;
end else if (atop == obi_pkg::AMOSWAP) begin
wait_write_rsp(res_id);
rdata = get_memory(addr);
set_memory(addr, wdata, be);
err = '0;
exokay = '0;
end else if (atop == obi_pkg::AMOADD || atop == obi_pkg::AMOXOR ||
atop == obi_pkg::AMOAND || atop == obi_pkg::AMOOR ||
atop == obi_pkg::AMOMIN || atop == obi_pkg::AMOMAX ||
atop == obi_pkg::AMOMINU || atop == obi_pkg::AMOMAXU ) begin
wait_write_rsp(res_id);
data_uo = $unsigned(get_memory(addr));
data_so = $unsigned(get_memory(addr));
rdata = data_uo;
unique case (atop)
obi_pkg::AMOADD: begin
set_memory(addr, data_uo + data_ui, be);
end
obi_pkg::AMOXOR: begin
set_memory(addr, data_uo ^ data_ui, be);
end
obi_pkg::AMOAND: begin
set_memory(addr, data_uo & data_ui, be);
end
obi_pkg::AMOOR: begin
set_memory(addr, data_uo | data_ui, be);
end
obi_pkg::AMOMIN: begin
set_memory(addr, data_so > data_si ? data_si : data_so, be);
end
obi_pkg::AMOMAX: begin
set_memory(addr, data_so > data_si ? data_so : data_si, be);
end
obi_pkg::AMOMINU: begin
set_memory(addr, data_uo > data_ui ? data_ui : data_uo, be);
end
obi_pkg::AMOMAXU: begin
set_memory(addr, data_uo > data_ui ? data_uo : data_ui, be);
end
default: begin
set_memory(addr, data_uo, be);
end
endcase
err = '0;
exokay = '0;
end else begin
err = 1'b1;
exokay = 1'b0;
rdata = '0;
end
endtask
task read(
input logic [ObiAddrWidth-1:0] addr,
input logic [ ObiIdWidthM-1:0] m_id,
// input logic [ObiUserWidth-1:0] user,
input logic [ NumMgrWidth-1:0] manager = 0,
input obi_pkg::atop_t atop = '0,
output logic [ObiDataWidth-1:0] rdata,
output logic err,
output logic exokay
);
automatic logic [ObiIdWidthS-1:0] res_id = subordinate_id(m_id, manager);
wait_read(res_id);
rdata = get_memory(addr);
err = '0;
if (atop == obi_pkg::ATOPLR) begin
end
exokay = '0;
endtask
task wait_write_rsp(
input logic [ObiIdWidthS-1:0] id
);
forever begin
@(posedge monitor.clk_i)
#(AcqDelay);
if (monitor.valid && monitor.we && monitor.id == id) begin
break;
end
end
endtask
task wait_read(
input logic [ObiIdWidthS-1:0] id
);
forever begin
@(posedge monitor.clk_i)
#(AcqDelay);
if (monitor.valid && !monitor.we && monitor.id == id) begin
break;
end
end
endtask
endclass
endpackage

View file

@ -0,0 +1,42 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Authors:
// - Tobias Senti <tsenti@ethz.ch>
`include "common_cells/assertions.svh"
/// Checks for compliance with the OBI spec !!!Not complete!!!
module obi_asserter #(
parameter type obi_req_t = logic,
parameter type obi_rsp_t = logic
) (
input logic clk_i,
input logic rst_ni,
input obi_req_t obi_req_i,
input obi_rsp_t obi_rsp_i
);
//R-2.1
`ASSERT(OBIAReqLowDuringReset, !rst_ni |-> !obi_req_i.a_req, clk_i, 1'b0)
//R-2.2
`ASSERT(OBIRValidLowDuringReset, !rst_ni |-> !obi_rsp_i.r_valid, clk_i, 1'b0)
//R-3.1 - Stable during address phase
`ASSERT(OBIReadStableDuringAddressPhase,
((obi_req_i.a_req && !obi_req_i.a.we && !obi_rsp_i.a_gnt) |=>
$stable({obi_req_i.a_req, obi_req_i.a.we, obi_req_i.a.addr, obi_req_i.a.be})),
clk_i, !rst_ni)
`ASSERT(OBIWriteStableDuringAddressPhase,
((obi_req_i.a_req && obi_req_i.a.we && !obi_rsp_i.a_gnt) |=>
$stable({obi_req_i.a_req, obi_req_i.a})), clk_i, !rst_ni)
//R-4.1 - Stable during response phase
`ASSERT(OBIStableDuringResponsePhase, ((obi_rsp_i.r_valid && !obi_req_i.r_ready) |=>
$stable({obi_rsp_i.r_valid, obi_rsp_i.r})), clk_i, !rst_ni)
//R-5 - Response phase should only be sent after the corresponding address phase has ended
endmodule

View file

@ -0,0 +1,201 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Michael Rogenmoser <michaero@iis.ee.ethz.ch>
module obi_sim_mem import obi_pkg::*; #(
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
parameter type obi_req_t = logic,
parameter type obi_rsp_t = logic,
parameter type obi_r_chan_t = logic,
parameter bit WarnUninitialized = 1'b0,
parameter bit ClearErrOnAccess = 1'b0,
parameter time ApplDelay = 0ps,
parameter time AcqDelay = 0ps
) (
input logic clk_i,
input logic rst_ni,
input obi_req_t obi_req_i,
output obi_rsp_t obi_rsp_o,
output logic mon_valid_o,
output logic mon_we_o,
output logic [ ObiCfg.AddrWidth-1:0] mon_addr_o,
output logic [ ObiCfg.DataWidth-1:0] mon_wdata_o,
output logic [ObiCfg.DataWidth/8-1:0] mon_be_o,
output logic [ ObiCfg.IdWidth-1:0] mon_id_o
);
if (ObiCfg.OptionalCfg.UseAtop) $error("Please use an ATOP resolver before sim mem.");
if (ObiCfg.Integrity) $error("Integrity not supported");
if (ObiCfg.OptionalCfg.UseProt) $warning("Prot not checked!");
if (ObiCfg.OptionalCfg.UseMemtype) $warning("Memtype not checked!");
typedef logic [ObiCfg.AddrWidth-1:0] addr_t;
obi_r_chan_t rsp_queue[$];
logic [7:0] mem[addr_t];
logic rsp_ready;
if (ObiCfg.UseRReady) begin : gen_rready
assign rsp_ready = obi_req_i.rready;
end else begin : gen_no_rready
assign rsp_ready = 1'b1;
end
logic mon_valid;
logic mon_we;
logic [ObiCfg.AddrWidth-1:0] mon_addr;
logic [ObiCfg.DataWidth-1:0] mon_wdata;
logic [ObiCfg.DataWidth/8-1:0] mon_be;
logic [ObiCfg.IdWidth-1:0] mon_id;
assign mon_we = obi_req_i.a.we;
assign mon_addr = obi_req_i.a.addr;
assign mon_wdata = obi_req_i.a.wdata;
assign mon_be = obi_req_i.a.be;
assign mon_id = obi_req_i.a.aid;
initial begin
fork
// Request Handling
forever begin
// Start cycle
@(posedge clk_i);
#(ApplDelay);
// Indicate ready
obi_rsp_o.gnt = 1'b1;
mon_valid = 1'b0;
// End of cycle
#(AcqDelay-ApplDelay);
// If requesting
if (obi_req_i.req) begin
mon_valid = 1'b1;
if (obi_req_i.a.we) begin
automatic obi_r_chan_t write_rsp;
// write memory
for (int i = 0; i < ObiCfg.DataWidth/8; i++) begin
if (obi_req_i.a.be[i]) begin
mem[obi_req_i.a.addr+i] = obi_req_i.a.wdata[8*i+:8];
end
end
// write response
write_rsp = 'x;
write_rsp.rid = obi_req_i.a.aid;
write_rsp.err = 1'b0;
write_rsp.r_optional = '0;
rsp_queue.push_back(write_rsp);
end else begin
// read response
automatic obi_r_chan_t read_rsp = 'x;
if (!mem.exists(obi_req_i.a.addr)) begin
if (WarnUninitialized) begin
$warning("%t - Access to non-initialized address at 0x%016x by ID 0x%x.",
$realtime, obi_req_i.a.addr, obi_req_i.a.aid);
end
end else begin
for (int i = 0; i < ObiCfg.DataWidth/8; i++) begin
read_rsp.rdata[8*i+:8] = mem[obi_req_i.a.addr+i];
end
end
read_rsp.rid = obi_req_i.a.aid;
read_rsp.err = 1'b0;
read_rsp.r_optional = '0;
rsp_queue.push_back(read_rsp);
end
end
end
// Response Handling
forever begin
// Start cycle
@(posedge clk_i);
#(ApplDelay);
obi_rsp_o.rvalid = 1'b0;
if (rsp_queue.size() != 0) begin
obi_rsp_o.r = rsp_queue[0];
obi_rsp_o.rvalid = 1'b1;
// End of cycle
#(AcqDelay-ApplDelay);
if (rsp_ready) begin
void'(rsp_queue.pop_front());
end
end
end
join
end
initial begin
mon_valid_o = '0;
mon_we_o = '0;
mon_addr_o = '0;
mon_wdata_o = '0;
mon_be_o = '0;
mon_id_o = '0;
wait (rst_ni);
forever begin
@(posedge clk_i);
mon_valid_o <= #(ApplDelay) mon_valid;
mon_we_o <= #(ApplDelay) mon_we;
mon_addr_o <= #(ApplDelay) mon_addr;
mon_wdata_o <= #(ApplDelay) mon_wdata;
mon_be_o <= #(ApplDelay) mon_be;
mon_id_o <= #(ApplDelay) mon_id;
end
end
endmodule
`include "obi/typedef.svh"
`include "obi/assign.svh"
module obi_sim_mem_intf import obi_pkg::*; #(
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig,
parameter bit WarnUninitialized = 1'b0,
parameter bit ClearErrOnAccess = 1'b0,
parameter time ApplDelay = 0ps,
parameter time AcqDelay = 0ps
) (
input logic clk_i,
input logic rst_ni,
OBI_BUS.Subordinate obi_sbr,
output logic mon_valid_o,
output logic mon_we_o,
output logic [ ObiCfg.AddrWidth-1:0] mon_addr_o,
output logic [ ObiCfg.DataWidth-1:0] mon_wdata_o,
output logic [ObiCfg.DataWidth/8-1:0] mon_be_o,
output logic [ ObiCfg.IdWidth-1:0] mon_id_o
);
`OBI_TYPEDEF_ALL(obi, ObiCfg)
obi_req_t obi_req;
obi_rsp_t obi_rsp;
`OBI_ASSIGN_TO_REQ(obi_req, obi_sbr, ObiCfg)
`OBI_ASSIGN_FROM_RSP(obi_sbr, obi_rsp, ObiCfg)
obi_sim_mem #(
.ObiCfg (ObiCfg),
.obi_req_t (obi_req_t),
.obi_rsp_t (obi_rsp_t),
.obi_r_chan_t (obi_r_chan_t),
.WarnUninitialized(WarnUninitialized),
.ClearErrOnAccess (ClearErrOnAccess),
.ApplDelay (ApplDelay),
.AcqDelay (AcqDelay)
) i_obi_sim_mem (
.clk_i,
.rst_ni,
.obi_req_i(obi_req),
.obi_rsp_o(obi_rsp),
.mon_valid_o,
.mon_we_o,
.mon_addr_o,
.mon_wdata_o,
.mon_be_o,
.mon_id_o
);
endmodule

View file

@ -0,0 +1,398 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
package obi_test;
import obi_pkg::*;
class obi_driver #(
parameter obi_cfg_t ObiCfg = ObiDefaultConfig,
parameter type obi_a_optional_t = logic,
parameter type obi_r_optional_t = logic,
parameter time TA = 0ns,
parameter time TT = 0ns
);
virtual OBI_BUS_DV #(
.OBI_CFG ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
.obi_r_optional_t ( obi_r_optional_t )
) obi;
function new(
virtual OBI_BUS_DV #(
.OBI_CFG ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
.obi_r_optional_t ( obi_r_optional_t )
) obi
);
this.obi = obi;
endfunction
function void reset_manager();
obi.req <= '0;
obi.reqpar <= '1;
obi.addr <= '0;
obi.we <= '0;
obi.be <= '0;
obi.wdata <= '0;
obi.aid <= '0;
obi.a_optional <= '0;
obi.rready <= '0;
obi.rreadypar <= '1;
endfunction
function void reset_subordinate();
obi.gnt <= '0;
obi.gntpar <= '1;
obi.rvalid <= '0;
obi.rvalidpar <= '1;
obi.rdata <= '0;
obi.rid <= '0;
obi.r_optional <= '0;
endfunction
task cycle_start;
#TT;
endtask
task cycle_end;
@(posedge obi.clk_i);
endtask
task send_a (
input logic [ ObiCfg.AddrWidth-1:0] addr,
input logic we,
input logic [ObiCfg.DataWidth/8-1:0] be,
input logic [ ObiCfg.DataWidth-1:0] wdata,
input logic [ ObiCfg.IdWidth-1:0] aid,
input obi_a_optional_t a_optional
);
obi.req <= #TA 1'b1;
obi.reqpar <= #TA 1'b0;
obi.addr <= #TA addr;
obi.we <= #TA we;
obi.be <= #TA be;
obi.wdata <= #TA wdata;
obi.aid <= #TA aid;
obi.a_optional <= #TA a_optional;
cycle_start();
while (obi.gnt != 1'b1) begin cycle_end(); cycle_start(); end
cycle_end();
obi.req <= #TA 1'b0;
obi.reqpar <= #TA 1'b1;
obi.addr <= #TA '0;
obi.we <= #TA '0;
obi.be <= #TA '0;
obi.wdata <= #TA '0;
obi.aid <= #TA '0;
obi.a_optional <= #TA '0;
endtask
task send_r (
input logic [ObiCfg.DataWidth-1:0] rdata,
input logic [ ObiCfg.IdWidth-1:0] rid,
input obi_r_optional_t r_optional
);
obi.rvalid <= #TA 1'b1;
obi.rvalidpar <= #TA 1'b0;
obi.rdata <= #TA rdata;
obi.rid <= #TA rid;
obi.r_optional <= #TA r_optional;
cycle_start();
if (ObiCfg.UseRReady) begin
while (obi.rready != 1'b1) begin cycle_end(); cycle_start(); end
end
cycle_end();
obi.rvalid <= #TA 1'b0;
obi.rvalidpar <= #TA 1'b1;
obi.rdata <= #TA '0;
obi.rid <= #TA '0;
obi.r_optional <= #TA '0;
endtask
task recv_a (
output logic [ ObiCfg.AddrWidth-1:0] addr,
output logic we,
output logic [ObiCfg.DataWidth/8-1:0] be,
output logic [ ObiCfg.DataWidth-1:0] wdata,
output logic [ ObiCfg.IdWidth-1:0] aid,
output obi_a_optional_t a_optional
);
obi.gnt <= #TA 1'b1;
obi.gntpar <= #TA 1'b0;
cycle_start();
while (obi.req != 1'b1) begin cycle_end(); cycle_start(); end
addr = obi.addr;
we = obi.we;
be = obi.be;
wdata = obi.wdata;
aid = obi.aid;
a_optional = obi.a_optional;
cycle_end();
obi.gnt <= #TA 1'b0;
obi.gntpar <= #TA 1'b1;
endtask
task recv_r (
output logic [ObiCfg.DataWidth-1:0] rdata,
output logic [ ObiCfg.IdWidth-1:0] rid,
output logic err,
output obi_r_optional_t r_optional
);
obi.rready <= #TA 1'b1;
obi.rreadypar <= #TA 1'b0;
cycle_start();
while (obi.rvalid != 1'b1) begin cycle_end(); cycle_start(); end
rdata = obi.rdata;
rid = obi.rid;
err = obi.err;
r_optional = obi.r_optional;
cycle_end();
if (ObiCfg.UseRReady) begin
obi.rready <= #TA 1'b0;
obi.rreadypar <= #TA 1'b1;
end
endtask
endclass
class obi_rand_manager #(
// Obi Parameters
parameter obi_cfg_t ObiCfg = ObiDefaultConfig,
parameter type obi_a_optional_t = logic,
parameter type obi_r_optional_t = logic,
// Stimuli Parameters
parameter time TA = 2ns,
parameter time TT = 8ns,
// Manager Parameters
parameter int unsigned MinAddr = 32'h0000_0000,
parameter int unsigned MaxAddr = 32'hffff_ffff,
// Wait Parameters
parameter int unsigned AMinWaitCycles = 0,
parameter int unsigned AMaxWaitCycles = 100,
parameter int unsigned RMinWaitCycles = 0,
parameter int unsigned RMaxWaitCycles = 100
);
typedef obi_test::obi_driver #(
.ObiCfg ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
.obi_r_optional_t ( obi_r_optional_t ),
.TA ( TA ),
.TT ( TT )
) obi_driver_t;
typedef logic [ObiCfg.AddrWidth-1:0] addr_t;
string name;
obi_driver_t drv;
addr_t a_queue[$];
logic r_queue[$];
function new(
virtual OBI_BUS_DV #(
.OBI_CFG ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
.obi_r_optional_t ( obi_r_optional_t )
) obi,
input string name
);
this.drv = new(obi);
this.name = name;
assert(ObiCfg.AddrWidth != 0) else $fatal(1, "ObiCfg.AddrWidth must be non-zero!");
assert(ObiCfg.DataWidth != 0) else $fatal(1, "ObiCfg.DataWidth must be non-zero!");
endfunction
function void reset();
drv.reset_manager();
endfunction
task automatic rand_wait(input int unsigned min, input int unsigned max);
int unsigned rand_success, cycles;
rand_success = std::randomize(cycles) with {
cycles >= min;
cycles <= max;
};
assert (rand_success) else $error("Failed to randomize wait cycles!");
repeat (cycles) @(posedge this.drv.obi.clk_i);
endtask
task automatic send_as(input int unsigned n_reqs);
automatic addr_t a_addr;
automatic logic a_we;
automatic logic [ObiCfg.DataWidth/8-1:0] a_be;
automatic logic [ ObiCfg.DataWidth-1:0] a_wdata;
automatic logic [ ObiCfg.IdWidth-1:0] a_aid;
automatic obi_a_optional_t a_optional;
repeat (n_reqs) begin
rand_wait(AMinWaitCycles, AMaxWaitCycles);
a_addr = addr_t'($urandom_range(MinAddr, MaxAddr));
a_we = $urandom() % 2;
a_be = $urandom() % (1 << (ObiCfg.DataWidth/8));
a_wdata = $urandom() % (1 << ObiCfg.DataWidth);
a_aid = $urandom() % (1 << ObiCfg.IdWidth);
a_optional = obi_a_optional_t'($urandom());
this.a_queue.push_back(a_addr);
this.drv.send_a(a_addr, a_we, a_be, a_wdata, a_aid, a_optional);
end
endtask
task automatic recv_rs(input int unsigned n_rsps);
automatic addr_t a_addr;
automatic logic [ObiCfg.DataWidth-1:0] r_rdata;
automatic logic [ ObiCfg.IdWidth-1:0] r_rid;
automatic logic r_err;
automatic obi_r_optional_t r_optional;
repeat (n_rsps) begin
wait (a_queue.size() > 0);
a_addr = this.a_queue.pop_front();
rand_wait(RMinWaitCycles, RMaxWaitCycles);
drv.recv_r(r_rdata, r_rid, r_err, r_optional);
end
endtask
task automatic run(int unsigned n_reqs);
$display("Run for Reqs: %0d", n_reqs);
fork
this.send_as(n_reqs);
this.recv_rs(n_reqs);
join
endtask
task automatic write(
input addr_t addr,
input logic [ObiCfg.DataWidth/8-1:0] be,
input logic [ ObiCfg.DataWidth-1:0] wdata,
input logic [ ObiCfg.IdWidth-1:0] aid,
input obi_a_optional_t a_optional,
output logic [ ObiCfg.DataWidth-1:0] r_rdata,
output logic [ ObiCfg.IdWidth-1:0] r_rid,
output logic r_err,
output obi_r_optional_t r_optional
);
this.drv.send_a(addr, 1'b1, be, wdata, aid, a_optional);
this.drv.recv_r(r_rdata, r_rid, r_err, r_optional);
endtask
task automatic read(
input addr_t addr,
input logic [ ObiCfg.IdWidth-1:0] aid,
input obi_a_optional_t a_optional,
output logic [ObiCfg.DataWidth-1:0] r_rdata,
output logic [ ObiCfg.IdWidth-1:0] r_rid,
output logic r_err,
output obi_r_optional_t r_optional
);
this.drv.send_a(addr, 1'b0, '1, '0, aid, a_optional);
this.drv.recv_r(r_rdata, r_rid, r_err, r_optional);
endtask
endclass
class obi_rand_subordinate #(
// Obi Parameters
parameter obi_cfg_t ObiCfg = ObiDefaultConfig,
parameter type obi_a_optional_t = logic,
parameter type obi_r_optional_t = logic,
// Stimuli Parameters
parameter time TA = 2ns,
parameter time TT = 8ns,
// Wait Parameters
parameter int unsigned AMinWaitCycles = 0,
parameter int unsigned AMaxWaitCycles = 100,
parameter int unsigned RMinWaitCycles = 0,
parameter int unsigned RMaxWaitCycles = 100
);
typedef obi_test::obi_driver #(
.ObiCfg ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
.obi_r_optional_t ( obi_r_optional_t ),
.TA ( TA ),
.TT ( TT )
) obi_driver_t;
typedef logic [ObiCfg.AddrWidth-1:0] addr_t;
typedef logic [ ObiCfg.IdWidth-1:0] id_t;
string name;
obi_driver_t drv;
addr_t a_queue[$];
id_t id_queue[$];
logic r_queue[$];
function new(
virtual OBI_BUS_DV #(
.OBI_CFG ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
.obi_r_optional_t ( obi_r_optional_t )
) obi,
input string name
);
this.drv = new(obi);
this.name = name;
assert(ObiCfg.AddrWidth != 0) else $fatal(1, "ObiCfg.AddrWidth must be non-zero!");
assert(ObiCfg.DataWidth != 0) else $fatal(1, "ObiCfg.DataWidth must be non-zero!");
endfunction
function void reset();
drv.reset_subordinate();
endfunction
task automatic rand_wait(input int unsigned min, input int unsigned max);
int unsigned rand_success, cycles;
rand_success = std::randomize(cycles) with {
cycles >= min;
cycles <= max;
};
assert (rand_success) else $error("Failed to randomize wait cycles!");
repeat (cycles) @(posedge this.drv.obi.clk_i);
endtask
task automatic recv_as();
forever begin
automatic addr_t a_addr;
automatic logic [ObiCfg.DataWidth/8-1:0] a_be;
automatic logic a_we;
automatic logic [ ObiCfg.DataWidth-1:0] a_wdata;
automatic logic [ ObiCfg.IdWidth-1:0] a_aid;
automatic obi_a_optional_t a_optional;
this.drv.recv_a(a_addr, a_we, a_be, a_wdata, a_aid, a_optional);
this.a_queue.push_back(a_addr);
this.id_queue.push_back(a_aid);
end
endtask
task automatic send_rs();
forever begin
automatic logic rand_success;
automatic addr_t a_addr;
automatic logic [ObiCfg.DataWidth-1:0] r_rdata;
automatic logic [ ObiCfg.IdWidth-1:0] r_rid;
automatic obi_r_optional_t r_optional;
wait (a_queue.size() > 0);
wait (id_queue.size() > 0);
a_addr = this.a_queue.pop_front();
r_rid = this.id_queue.pop_front();
rand_success = std::randomize(r_rdata); assert(rand_success);
rand_success = std::randomize(r_optional); assert(rand_success);
this.drv.send_r(r_rdata, r_rid, r_optional);
end
endtask
task automatic run();
fork
recv_as();
send_rs();
join
endtask
endclass
endpackage

View file

@ -0,0 +1,706 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Author: Samuel Riedel <sriedel@iis.ee.ethz.ch>
// Author: Michael Rogenmoser <michaero@iis.ee.ethz.ch>
`include "obi/typedef.svh"
`include "obi/assign.svh"
module tb_obi_atop_resolver;
import obi_pkg::*;
localparam int unsigned MaxTimeout = 10000;
localparam int unsigned NumManagers = 32'd10;
localparam int unsigned NumMaxTrans = 32'd8;
localparam int unsigned AddrWidth = 32;
localparam int unsigned DataWidth = 32;
localparam int unsigned MgrIdWidth = 5;
localparam int unsigned SbrIdWidth = MgrIdWidth+$clog2(NumManagers);
localparam int unsigned AUserWidth = 4;
localparam int unsigned WUserWidth = 2;
localparam int unsigned RUserWidth = 3;
localparam time CyclTime = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
localparam obi_pkg::obi_cfg_t MgrConfig = '{
UseRReady: 1'b0,
CombGnt: 1'b0,
AddrWidth: AddrWidth,
DataWidth: DataWidth,
IdWidth: MgrIdWidth,
Integrity: 1'b0,
BeFull: 1'b1,
OptionalCfg: '{
UseAtop: 1'b1,
UseMemtype: 1'b0,
UseProt: 1'b0,
UseDbg: 1'b0,
AUserWidth: AUserWidth,
WUserWidth: WUserWidth,
RUserWidth: RUserWidth,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
}
};
`OBI_TYPEDEF_ALL_A_OPTIONAL(mgr_a_optional_t, AUserWidth, WUserWidth, 0, 0)
`OBI_TYPEDEF_ALL_R_OPTIONAL(mgr_r_optional_t, RUserWidth, 0)
typedef obi_test::obi_rand_manager #(
.ObiCfg ( MgrConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t ),
.TA ( ApplTime ),
.TT ( TestTime ),
.MinAddr (32'h0000_0000),
.MaxAddr (32'h0001_3000)
) rand_manager_t;
localparam obi_pkg::obi_cfg_t MgrMuxedConfig = '{
UseRReady: 1'b0,
CombGnt: 1'b0,
AddrWidth: AddrWidth,
DataWidth: DataWidth,
IdWidth: SbrIdWidth,
Integrity: 1'b0,
BeFull: 1'b1,
OptionalCfg: '{
UseAtop: 1'b1,
UseMemtype: 1'b0,
UseProt: 1'b0,
UseDbg: 1'b0,
AUserWidth: AUserWidth,
WUserWidth: WUserWidth,
RUserWidth: RUserWidth,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
}
};
localparam obi_pkg::obi_cfg_t SbrConfig = '{
UseRReady: 1'b0,
CombGnt: 1'b0,
AddrWidth: AddrWidth,
DataWidth: DataWidth,
IdWidth: SbrIdWidth,
Integrity: 1'b0,
BeFull: 1'b1,
OptionalCfg: '{
UseAtop: 1'b0,
UseMemtype: 1'b0,
UseProt: 1'b0,
UseDbg: 1'b0,
AUserWidth: AUserWidth,
WUserWidth: WUserWidth,
RUserWidth: RUserWidth,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
}
};
`OBI_TYPEDEF_ALL_A_OPTIONAL(sbr_a_optional_t, AUserWidth, WUserWidth, 0, 0)
`OBI_TYPEDEF_ALL_R_OPTIONAL(sbr_r_optional_t, RUserWidth, 0)
logic clk, rst_n;
logic [NumManagers-1:0] end_of_sim;
int unsigned num_errors = 0;
OBI_BUS_DV #(
.OBI_CFG ( MgrConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t )
) mgr_bus_dv [NumManagers] (
.clk_i ( clk ),
.rst_ni ( rst_n )
);
OBI_BUS #(
.OBI_CFG ( MgrConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t )
) mgr_bus [NumManagers] ();
OBI_BUS #(
.OBI_CFG ( MgrMuxedConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t )
) mgr_bus_muxed ();
rand_manager_t obi_rand_managers[NumManagers];
for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_drivers
initial begin
obi_rand_managers[i] = new ( mgr_bus_dv[i], $sformatf("MGR_%0d",i));
end_of_sim[i] <= 1'b0;
obi_rand_managers[i].reset();
end
`OBI_ASSIGN(mgr_bus[i], mgr_bus_dv[i], MgrConfig, MgrConfig)
end
OBI_BUS #(
.OBI_CFG ( SbrConfig ),
.obi_a_optional_t ( sbr_a_optional_t ),
.obi_r_optional_t ( sbr_r_optional_t )
) sbr_bus ();
OBI_ATOP_MONITOR_BUS #(
.DataWidth ( DataWidth ),
.AddrWidth ( AddrWidth ),
.IdWidth ( SbrIdWidth ),
.UserWidth ( AUserWidth )
) mem_monitor_dv (
.clk_i ( clk )
);
clk_rst_gen #(
.ClkPeriod ( CyclTime ),
.RstClkCycles ( 5 )
) i_clk_gen (
.clk_o ( clk ),
.rst_no ( rst_n )
);
obi_mux_intf #(
.SbrPortObiCfg ( MgrConfig ),
.MgrPortObiCfg ( MgrMuxedConfig ),
.NumSbrPorts ( NumManagers ),
.NumMaxTrans ( 2 ),
.UseIdForRouting ( 1'b0 )
) i_obi_mux (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.testmode_i ( 1'b0 ),
.sbr_ports ( mgr_bus ),
.mgr_port ( mgr_bus_muxed )
);
obi_atop_resolver_intf #(
.SbrPortObiCfg ( MgrMuxedConfig ),
.MgrPortObiCfg ( SbrConfig ),
.LrScEnable ( 1 ),
.RegisterAmo ( 1'b0 )
) i_atop_resolver (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.testmode_i ( '0 ),
.sbr_port ( mgr_bus_muxed ),
.mgr_port ( sbr_bus )
);
obi_sim_mem_intf #(
.ObiCfg ( SbrConfig ),
.ClearErrOnAccess ( 1'b0 ),
.WarnUninitialized ( 1'b0 ),
.ApplDelay ( ApplTime ),
.AcqDelay ( TestTime )
) i_sim_mem (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.obi_sbr ( sbr_bus ),
.mon_valid_o ( mem_monitor_dv.valid ),
.mon_we_o ( mem_monitor_dv.we ),
.mon_addr_o ( mem_monitor_dv.addr ),
.mon_wdata_o ( mem_monitor_dv.data ),
.mon_be_o ( mem_monitor_dv.be ),
.mon_id_o ( mem_monitor_dv.id )
);
atop_golden_mem_pkg::atop_golden_mem #(
.ObiAddrWidth ( AddrWidth ),
.ObiDataWidth ( DataWidth ),
.ObiIdWidthM ( MgrIdWidth ),
.ObiIdWidthS ( SbrIdWidth ),
.ObiUserWidth ( AUserWidth ),
.NumMgrWidth ( $clog2(NumManagers) ),
.ApplDelay ( ApplTime ),
.AcqDelay ( TestTime )
) golden_memory = new(mem_monitor_dv);
assign mem_monitor_dv.user = '0;
/*====================================================================
= Main =
====================================================================*/
initial begin
wait (rst_n);
@(posedge clk);
// Run tests!
test_all_amos();
test_same_address();
test_amo_write_consistency();
// // test_interleaving();
test_atomic_counter();
// random_amo();
// overtake_r();
test_lr_sc();
end_of_sim <= '1;
end
/*====================================================================
= Timeout =
====================================================================*/
initial begin
automatic int unsigned timeout = 0;
automatic logic [1:0] handshake = 2'b00;
@(posedge clk);
wait (rst_n);
fork
while (timeout < MaxTimeout) begin
handshake = {sbr_bus.req, sbr_bus.gnt};
@(posedge clk);
if (handshake != {sbr_bus.req, sbr_bus.gnt}) begin
timeout = 0;
end else begin
timeout += 1;
end
end
wait (&end_of_sim);
join_any
if (&end_of_sim && num_errors == 0) begin
$display("\nSUCCESS\n");
end else if (&end_of_sim) begin
$display("\nFINISHED\n");
if (num_errors > 0) begin
$fatal(1, "Encountered %d errors.", num_errors);
end else begin
$display("All tests passed.");
end
end else begin
$fatal(1, "TIMEOUT");
end
$stop;
end
/*====================================================================
= Hand crafted tests =
====================================================================*/
task automatic test_all_amos();
automatic logic [AddrWidth-1:0] address;
automatic logic [DataWidth-1:0] data_init;
automatic logic [DataWidth-1:0] data_amo;
automatic atop_t atop;
$display("%t - Test all possible amos with a single thread...\n", $realtime);
for (int j = 0; j < 9; j++) begin
// Go through standard AMOs
if (j == 0) atop = AMOSWAP;
if (j == 1) atop = AMOADD;
if (j == 2) atop = AMOXOR;
if (j == 3) atop = AMOAND;
if (j == 4) atop = AMOOR;
if (j == 5) atop = AMOMIN;
if (j == 6) atop = AMOMAX;
if (j == 7) atop = AMOMINU;
if (j == 8) atop = AMOMAXU;
assert (randomize(address));
assert (randomize(data_init));
assert (randomize(data_amo));
write_amo_read_cycle(0, address, data_init, data_amo, 0, 0, atop);
end
endtask
// Test if the adapter protects the atomic region correctly
task automatic test_same_address();
parameter int unsigned NumIterations = 64;
parameter logic [AddrWidth-1:0] Address = 'h01004000;
automatic logic [ AddrWidth-1:0] address = Address;
automatic logic [ DataWidth-1:0] rdata_init;
automatic logic [MgrIdWidth-1:0] rid_init;
automatic logic err_init;
automatic mgr_r_optional_t r_optional_init;
automatic logic [ DataWidth-1:0] exp_data_init;
automatic logic exp_err_init;
automatic logic exp_exokay_init;
$display("%t - Test random accesses to the same memory location...\n", $realtime);
// Initialize memory with 0
fork
obi_rand_managers[0].write(address, '1, '0, '0, '0, rdata_init, rid_init, err_init,
r_optional_init);
golden_memory.write(address, '0, '1, '0, 0, '0, exp_data_init, exp_err_init,
exp_exokay_init);
join
for (int i = 0; i < NumManagers; i++) begin
automatic int m = i;
automatic logic [MgrIdWidth-1:0] id;
automatic logic [SbrIdWidth-1:0] s_id;
automatic logic [ DataWidth-1:0] wdata;
automatic mgr_a_optional_t a_optional;
automatic logic [ DataWidth-1:0] rdata;
automatic logic [ DataWidth-1:0] exp_data;
automatic logic [MgrIdWidth-1:0] rid;
automatic logic [MgrIdWidth-1:0] exp_rid;
automatic logic err;
automatic logic exp_err;
automatic mgr_r_optional_t r_optional;
automatic logic exp_exokay;
automatic atop_t atop;
fork
for (int j = 0; j < NumIterations; j++) begin
assert (randomize(id));
assert (randomize(wdata));
do begin
assert (randomize(atop));
end while (!(obi_atop_e'(atop) inside {AMOSWAP, AMOADD, AMOXOR, AMOAND, AMOOR, AMOMIN,
AMOMAX, AMOMINU, AMOMAXU, ATOPNONE}));
a_optional.atop = atop;
fork
obi_rand_managers[m].write(address, '1, wdata, id, a_optional, rdata, rid, err,
r_optional);
golden_memory.write(address, wdata, '1, id, m, atop, exp_data, exp_err, exp_exokay);
join
assert (err == exp_err && r_optional.exokay == exp_exokay) else begin
$warning("%t - Response codes did not match! got: 0x%b, exp: 0x%b", $realtime,
{err, r_optional.exokay}, {exp_err, exp_exokay});
num_errors += 1;
end
if (atop != ATOPNONE) begin
assert (rdata == exp_data) else begin
$warning("%t - ATOP data did not match! got: 0x%x, exp: 0x%x with op 0x%x",
$realtime, rdata, exp_data, atop);
num_errors += 1;
end
end
end
join_none
end
wait fork;
#1000ns;
endtask
// Test if the adapter protects the atomic region correctly
task automatic test_amo_write_consistency();
parameter int unsigned NumIterations = 64;
parameter logic [AddrWidth-1:0] Address = 'h01004000;
automatic logic [ AddrWidth-1:0] address = Address;
$display("%t - Test write consistency...\n", $realtime);
// Initialize to 0
write_amo_read_cycle(0, address, '0, '0, '0, 0, '0);
for (int i = 0; i < NumManagers; i++) begin
automatic int m = i;
automatic logic [MgrIdWidth-1:0] id;
automatic logic [ DataWidth-1:0] data;
automatic logic [ DataWidth-1:0] data_amo;
automatic atop_t atop;
fork
for (int j = 0; j < NumIterations; j++) begin
do begin
assert (randomize(atop));
end while (!(obi_atop_e'(atop) inside {AMOSWAP, AMOADD, AMOXOR, AMOAND, AMOOR, AMOMIN,
AMOMAX, AMOMINU, AMOMAXU, ATOPNONE}));
assert (randomize(data));
assert (randomize(data_amo));
assert (randomize(id));
write_amo_read_cycle(m, address, data, data_amo, id, 0, atop);
end
join_none
end
wait fork;
endtask
// Test multiple atomic accesses to the same address
task automatic test_atomic_counter();
parameter int unsigned NumIterations = 64;
parameter logic [AddrWidth-1:0] Address = 'h01002000;
automatic logic [ AddrWidth-1:0] address = Address;
automatic logic [ DataWidth-1:0] rdata;
automatic logic [MgrIdWidth-1:0] rid;
automatic logic err;
automatic mgr_r_optional_t r_optional;
$display("%t - Test atomic counter...\n", $realtime);
// Initialize to 0
obi_rand_managers[0].write(address, '1, '0, '0, '0, rdata, rid, err, r_optional);
for (int i = 0; i < NumManagers; i++) begin
automatic int m = i;
fork
for (int j = 0; j < NumIterations; j++) begin
obi_rand_managers[m].write(address, '1, 1, '0, '{atop: AMOADD, default: '0}, rdata, rid,
err, r_optional);
end
join_none
end
wait fork;
obi_rand_managers[0].read(address, '0, '0, rdata, rid, err, r_optional);
if (rdata == NumIterations*NumManagers) begin
$display("%t - Adder result correct: %d", $realtime, rdata);
end else begin
$display("%t - Adder result wrong: %d (Expected: %d)", $realtime, rdata,
NumIterations*NumManagers);
num_errors += 1;
end
endtask
// Test LR/SC accesses
task automatic test_lr_sc();
automatic logic [ AddrWidth-1:0] address;
automatic logic [ AddrWidth-1:0] address_2;
automatic logic [ DataWidth-1:0] data;
automatic logic [ DataWidth-1:0] data_2;
automatic logic [MgrIdWidth-1:0] trans_id;
automatic mgr_a_optional_t a_optional = '0;
automatic logic [ DataWidth-1:0] rdata;
automatic logic [MgrIdWidth-1:0] rid;
automatic logic err;
automatic mgr_r_optional_t r_optional;
$display("%t - Test LR/SC...\n", $realtime);
// SC without acquiring lock -> !exokay
///////////////////////////////////////
assert (randomize(address));
address[$clog2(DataWidth/8)-1:0] = '0;
assert (randomize(data));
assert (randomize(trans_id));
// Initialize address
obi_rand_managers[0].write(address, '1, '0, trans_id, a_optional, rdata, rid, err, r_optional);
a_optional.atop = ATOPSC;
obi_rand_managers[0].write(address, '1, data, trans_id, a_optional, rdata, rid, err,
r_optional);
assert (r_optional.exokay == 1'b0 && rdata != '0) else begin
$warning("%t - SC without LR returned exokay", $realtime);
num_errors += 1;
end
// Ensure SC did not store
a_optional.atop = ATOPNONE;
obi_rand_managers[0].read(address, trans_id, a_optional, rdata, rid, err, r_optional);
assert (rdata == '0 && rdata != data) else begin
$warning("%t - SC without LR stored data 0x%x", $realtime, rdata);
num_errors += 1;
end
// LR/SC sequence -> exokay
///////////////////////////
assert (randomize(address));
address[$clog2(DataWidth/8)-1:0] = '0;
assert (randomize(trans_id));
// Initialize address
obi_rand_managers[0].write(address, '1, '0, trans_id, a_optional, rdata, rid, err, r_optional);
a_optional.atop = ATOPLR;
obi_rand_managers[0].read(address, trans_id, a_optional, rdata, rid, err, r_optional);
assert (r_optional.exokay == 1'b1) else begin
$warning("%t - LR did not return exokay", $realtime);
num_errors += 1;
end
assert (randomize(data));
a_optional.atop = ATOPSC;
obi_rand_managers[0].write(address, '1, data, trans_id, a_optional, rdata, rid, err,
r_optional);
assert (r_optional.exokay == 1'b1) else begin
$warning("%t - SC with LR did not return exokay", $realtime);
num_errors += 1;
end
// Ensure SC did store
a_optional.atop = ATOPNONE;
obi_rand_managers[0].read(address, trans_id, a_optional, rdata, rid, err, r_optional);
assert (rdata == data) else begin
$warning("%t - SC with LR did not store data", $realtime);
num_errors += 1;
end
// LR then different SC -> !exokay
//////////////////////////////////
assert (randomize(address));
address[$clog2(DataWidth/8)-1:0] = '0;
assert (randomize(trans_id));
// Initialize address
obi_rand_managers[0].write(address, '1, '0, trans_id, a_optional, rdata, rid, err, r_optional);
a_optional.atop = ATOPLR;
obi_rand_managers[0].read(address, trans_id, a_optional, rdata, rid, err, r_optional);
assert (r_optional.exokay == 1'b1) else begin
$warning("%t - LR did not return exokay", $realtime);
num_errors += 1;
end
do begin
assert (randomize(address_2));
address_2[$clog2(DataWidth/8)-1:0] = '0;
end while (address_2 == address);
assert (randomize(data));
a_optional.atop = ATOPSC;
obi_rand_managers[0].write(address_2, '1, data, trans_id, a_optional, rdata, rid, err,
r_optional);
assert (r_optional.exokay == 1'b0) else begin
$warning("%t - SC with LR to different address returned exokay", $realtime);
num_errors += 1;
end
// LR, other core store, SC -> !exokay
//////////////////////////////////////
assert (randomize(address));
address[$clog2(DataWidth/8)-1:0] = '0;
assert (randomize(trans_id));
// Initialize address
obi_rand_managers[0].write(address, '1, '0, trans_id, a_optional, rdata, rid, err, r_optional);
a_optional.atop = ATOPLR;
obi_rand_managers[0].read(address, trans_id, a_optional, rdata, rid, err, r_optional);
assert (r_optional.exokay == 1'b1) else begin
$warning("%t - LR did not return exokay", $realtime);
num_errors += 1;
end
assert (randomize(data));
a_optional.atop = ATOPNONE;
obi_rand_managers[1].write(address, '1, data, trans_id, a_optional, rdata, rid, err,
r_optional);
assert (randomize(data_2));
a_optional.atop = ATOPSC;
obi_rand_managers[0].write(address, '1, data_2, trans_id, a_optional, rdata, rid, err,
r_optional);
assert (r_optional.exokay == 1'b0) else begin
$warning("%t - SC with LR and injected store returned exokay", $realtime);
num_errors += 1;
end
// Ensure SC did not store
a_optional.atop = ATOPNONE;
obi_rand_managers[0].read(address, trans_id, a_optional, rdata, rid, err, r_optional);
assert (rdata == data && rdata != data_2) else begin
$warning("%t - SC without LR stored data (stored: 0x%x, SC: 0x%x, rdata: 0x%x)", $realtime,
data, data_2, rdata);
num_errors += 1;
end
endtask
/*====================================================================
= Helper Functions =
====================================================================*/
task automatic write_amo_read_cycle(
input int unsigned driver,
input logic [ AddrWidth-1:0] address,
input logic [ DataWidth-1:0] data_init,
input logic [ DataWidth-1:0] data_amo,
input logic [MgrIdWidth-1:0] id,
input logic [AUserWidth-1:0] user,
input atop_t atop
);
automatic logic [MgrIdWidth-1:0] trans_id = id;
automatic logic [ DataWidth-1:0] rdata;
automatic logic [ DataWidth-1:0] exp_data;
automatic logic [ DataWidth-1:0] act_data;
automatic logic err;
automatic logic exokay;
automatic logic exp_err;
automatic logic exp_exokay;
automatic logic [MgrIdWidth-1:0] rid;
automatic mgr_a_optional_t a_optional = '0;
automatic mgr_r_optional_t r_optional;
a_optional.atop = '0;
exokay = r_optional.exokay;
if (!id) begin
assert (randomize(trans_id));
end
// Preload data
fork
obi_rand_managers[driver].write(address, '1, data_init, trans_id, a_optional, rdata, rid,
err, r_optional);
golden_memory.write(address, data_init, '1, trans_id, driver, '0, exp_data, exp_err,
exp_exokay);
join
if (!id) begin
assert (randomize(trans_id));
end
// Execute AMO
a_optional.atop = atop;
fork
obi_rand_managers[driver].write(address, '1, data_amo, trans_id, a_optional, rdata, rid, err,
r_optional);
golden_memory.write(address, data_amo, '1, trans_id, driver, atop, exp_data, exp_err,
exp_exokay);
join
exokay = r_optional.exokay;
assert (err == exp_err && exokay == exp_exokay) else begin
$warning("%t - Response codes did not match! got: 0x%b, exp: 0x%b", $realtime, {err, exokay},
{exp_err, exp_exokay});
num_errors += 1;
end
if (atop != '0) begin
assert (rdata == exp_data) else begin
$warning("%t - ATOP data did not match! got: 0x%x, exp: 0x%x at addr: 0x%x with op 0x%x",
$realtime, rdata, exp_data, address, atop);
num_errors += 1;
end
end
if (!id) begin
assert (randomize(trans_id));
end
// Check stored data
a_optional.atop = '0;
fork
obi_rand_managers[driver].read(address, trans_id, a_optional, act_data, rid, err, r_optional);
golden_memory.read(address, trans_id, driver, '0, exp_data, exp_err, exp_exokay);
join
assert(act_data == exp_data) else begin
$warning("%t - Stored data did not match! got: 0x%x, exp: 0x%x at addr: 0x%x with op 0x%x",
$realtime, act_data, exp_data, address, atop);
num_errors += 1;
end
endtask
endmodule

View file

@ -0,0 +1,229 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
// Michael Rogenmoser <michaero@iis.ee.ethz.ch>
`include "obi/typedef.svh"
`include "obi/assign.svh"
module tb_obi_xbar;
localparam int unsigned NumManagers = 32'd6;
localparam int unsigned NumSubordinates = 32'd8;
localparam bit UseIdForRouting = 1'b0;
localparam int unsigned NumMaxTrans = 32'd8;
localparam int unsigned AddrWidth = 32;
localparam int unsigned DataWidth = 32;
localparam int unsigned MgrIdWidth = 5;
localparam int unsigned SbrIdWidth = MgrIdWidth+$clog2(NumManagers);
localparam int unsigned AUserWidth = 4;
localparam int unsigned WUserWidth = 2;
localparam int unsigned RUserWidth = 3;
// TODO CHK!
localparam int unsigned NumRequests = 32'd10000;
localparam time CyclTime = 10ns;
localparam time ApplTime = 2ns;
localparam time TestTime = 8ns;
localparam obi_pkg::obi_cfg_t MgrConfig = '{
UseRReady: 1'b1,
CombGnt: 1'b0,
AddrWidth: AddrWidth,
DataWidth: DataWidth,
IdWidth: MgrIdWidth,
Integrity: 1'b0,
BeFull: 1'b1,
OptionalCfg: '{
UseAtop: 1'b1,
UseMemtype: 1'b1,
UseProt: 1'b1,
UseDbg: 1'b1,
AUserWidth: AUserWidth,
WUserWidth: WUserWidth,
RUserWidth: RUserWidth,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
}
};
`OBI_TYPEDEF_ALL_A_OPTIONAL(mgr_a_optional_t, AUserWidth, WUserWidth, 0, 0)
`OBI_TYPEDEF_ALL_R_OPTIONAL(mgr_r_optional_t, RUserWidth, 0)
typedef obi_test::obi_rand_manager #(
.ObiCfg ( MgrConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t ),
.TA ( ApplTime ),
.TT ( TestTime ),
.MinAddr (32'h0000_0000),
.MaxAddr (32'h0001_3000)
) rand_manager_t;
localparam obi_pkg::obi_cfg_t SbrConfig = '{
UseRReady: 1'b1,
CombGnt: 1'b0,
AddrWidth: AddrWidth,
DataWidth: DataWidth,
IdWidth: SbrIdWidth,
Integrity: 1'b0,
BeFull: 1'b1,
OptionalCfg: '{
UseAtop: 1'b1,
UseMemtype: 1'b1,
UseProt: 1'b1,
UseDbg: 1'b1,
AUserWidth: AUserWidth,
WUserWidth: WUserWidth,
RUserWidth: RUserWidth,
MidWidth: 0,
AChkWidth: 0,
RChkWidth: 0
}
};
`OBI_TYPEDEF_ALL_A_OPTIONAL(sbr_a_optional_t, AUserWidth, WUserWidth, 0, 0)
`OBI_TYPEDEF_ALL_R_OPTIONAL(sbr_r_optional_t, RUserWidth, 0)
typedef obi_test::obi_rand_subordinate #(
.ObiCfg ( SbrConfig ),
.obi_a_optional_t ( sbr_a_optional_t ),
.obi_r_optional_t ( sbr_r_optional_t ),
.TA ( ApplTime ),
.TT ( TestTime )
) rand_subordinate_t;
localparam int unsigned NumRules = 8;
typedef struct packed {
int unsigned idx;
logic [AddrWidth-1:0] start_addr;
logic [AddrWidth:0] end_addr;
} rule_t;
localparam rule_t [NumRules-1:0] AddrMap = '{
'{idx: 32'd7, start_addr: 32'h0001_0000, end_addr: 32'h0001_1000},
'{idx: 32'd6, start_addr: 32'h0000_9000, end_addr: 32'h0001_0000},
'{idx: 32'd5, start_addr: 32'h0000_8000, end_addr: 32'h0000_9000},
'{idx: 32'd4, start_addr: 32'h0000_7000, end_addr: 32'h0000_8000},
'{idx: 32'd3, start_addr: 32'h0000_6300, end_addr: 32'h0000_7000},
'{idx: 32'd2, start_addr: 32'h0000_4000, end_addr: 32'h0000_6300},
'{idx: 32'd1, start_addr: 32'h0000_3000, end_addr: 32'h0000_4000},
'{idx: 32'd0, start_addr: 32'h0000_0000, end_addr: 32'h0000_3000}
};
logic clk, rst_n;
logic [NumManagers-1:0] end_of_sim;
OBI_BUS_DV #(
.OBI_CFG ( MgrConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t )
) mgr_bus_dv [NumManagers] (
.clk_i ( clk ),
.rst_ni ( rst_n )
);
OBI_BUS #(
.OBI_CFG ( MgrConfig ),
.obi_a_optional_t ( mgr_a_optional_t ),
.obi_r_optional_t ( mgr_r_optional_t )
) mgr_bus [NumManagers] ();
for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_drivers
initial begin
automatic rand_manager_t obi_rand_manager = new ( mgr_bus_dv[i], $sformatf("MGR_%0d",i));
automatic logic [ MgrConfig.DataWidth-1:0] r_rdata = '0;
automatic logic [ MgrConfig.IdWidth-1:0] r_rid = '0;
automatic logic r_err = '0;
automatic mgr_r_optional_t r_optional = '0;
end_of_sim[i] <= 1'b0;
obi_rand_manager.reset();
@(posedge rst_n);
obi_rand_manager.write(32'h0000_1100, 4'hF, 32'hDEAD_BEEF, 2,
'{auser: '0,
wuser: '0,
atop: '0,
memtype: obi_pkg::memtype_t'('0),
mid: '0,
prot: obi_pkg::prot_t'('0),
dbg: '0,
achk: '0}, r_rdata, r_rid, r_err, r_optional);
obi_rand_manager.read(32'h0000_e100, 2, '{auser: '0,
wuser: '0,
atop: '0,
memtype: obi_pkg::memtype_t'('0),
mid: '0,
prot: obi_pkg::prot_t'('0),
dbg: '0,
achk: '0}, r_rdata, r_rid, r_err, r_optional);
obi_rand_manager.run(NumRequests);
end_of_sim[i] <= 1'b1;
end
`OBI_ASSIGN(mgr_bus[i], mgr_bus_dv[i], MgrConfig, MgrConfig)
end
OBI_BUS_DV #(
.OBI_CFG ( SbrConfig ),
.obi_a_optional_t ( sbr_a_optional_t ),
.obi_r_optional_t ( sbr_r_optional_t )
) sbr_bus_dv [NumSubordinates] (
.clk_i ( clk ),
.rst_ni ( rst_n )
);
OBI_BUS #(
.OBI_CFG ( SbrConfig ),
.obi_a_optional_t ( sbr_a_optional_t ),
.obi_r_optional_t ( sbr_r_optional_t )
) sbr_bus [NumSubordinates] ();
for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_drivers
initial begin
automatic rand_subordinate_t obi_rand_subordinate =
new ( sbr_bus_dv[i], $sformatf("SBR_%0d",i));
obi_rand_subordinate.reset();
@(posedge rst_n);
obi_rand_subordinate.run();
end
`OBI_ASSIGN(sbr_bus_dv[i], sbr_bus[i], SbrConfig, SbrConfig)
end
clk_rst_gen #(
.ClkPeriod ( CyclTime ),
.RstClkCycles ( 5 )
) i_clk_gen (
.clk_o ( clk ),
.rst_no ( rst_n )
);
// DUT
obi_xbar_intf #(
.SbrPortObiCfg ( MgrConfig ),
.MgrPortObiCfg ( SbrConfig ),
.NumSbrPorts ( NumManagers ),
.NumMgrPorts ( NumSubordinates ),
.NumMaxTrans ( NumMaxTrans ),
.NumAddrRules ( NumRules ),
.addr_map_rule_t ( rule_t ),
.UseIdForRouting ( UseIdForRouting )
) i_dut (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.testmode_i ( 1'b0 ),
.sbr_ports ( mgr_bus ),
.mgr_ports ( sbr_bus ),
.addr_map_i ( AddrMap ),
.en_default_idx_i ( '0 ),
.default_idx_i ( '0 )
);
initial begin
wait(&end_of_sim);
repeat (1000) @(posedge clk);
$display("Simulation stopped as all Masters transferred their data, Success.",);
$stop();
end
endmodule

13
vendor/pulp-platform_obi.lock.hjson vendored Normal file
View file

@ -0,0 +1,13 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2024 Thales DIS France SAS
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
// Original Author: Yannick Casamatta
{
upstream:
{
url: https://github.com/pulp-platform/obi.git
rev: c2141a653c755461ff44f61d12aeb5d99fc8e760
}
}

32
vendor/pulp-platform_obi.vendor.hjson vendored Normal file
View file

@ -0,0 +1,32 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2024 Thales DIS France SAS
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
// Original Author: Yannick Casamatta
{
// Name of the project
name: "pulp_obi",
// Target directory: relative to the location of this script.
target_dir: "pulp-platform/obi",
// Upstream repository
upstream: {
// URL
url: "https://github.com/pulp-platform/obi.git",
// revision
rev: "v0.1.3",
}
// Patch dir for local changes
patch_dir: "patches/pulp-platform/obi",
// Exclusions from upstream content
exclude_from_upstream: [
".ci",
".github",
".gitlab-ci.d",
".gitlab-ci.yml",
]
}